<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ahmad Farhan Ishraq</title>
    <description>The latest articles on DEV Community by Ahmad Farhan Ishraq (@ahmad_farhanishraq_74b52).</description>
    <link>https://dev.to/ahmad_farhanishraq_74b52</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2167158%2Ffc994762-b84d-4ad6-b4a8-120615513d79.png</url>
      <title>DEV Community: Ahmad Farhan Ishraq</title>
      <link>https://dev.to/ahmad_farhanishraq_74b52</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ahmad_farhanishraq_74b52"/>
    <language>en</language>
    <item>
      <title>Building a No Code AI Platform and the BFS Algorithm</title>
      <dc:creator>Ahmad Farhan Ishraq</dc:creator>
      <pubDate>Sat, 28 Dec 2024 00:43:17 +0000</pubDate>
      <link>https://dev.to/ahmad_farhanishraq_74b52/building-a-no-code-platform-and-the-bfs-algorithm-6d1</link>
      <guid>https://dev.to/ahmad_farhanishraq_74b52/building-a-no-code-platform-and-the-bfs-algorithm-6d1</guid>
      <description>&lt;p&gt;For the past couple of months I've been building out a low code/no code AI/ML &lt;a href="https://otto-m8.com/" rel="noopener noreferrer"&gt;Platform&lt;/a&gt; that lets users deploy their ML models as an API. Imagine the following scenario: you've trained a deep learning model and pushed it to Huggingface. While the model is great for what you trained it for, it lives inside a jupyter notebook but you need a deployable API where users can directly interact with your model. Or even better, you want to hook your model up with one of the LLM's out there but you don't want to spend time coding up the API, think about integration code, etc. That's essentially what I'm trying to solve.&lt;/p&gt;

&lt;p&gt;From the user point of view, you see a flowchart like tool, where you configure your blocks(in the case of Huggingface, you'd paste in your model card), connect them to their input blocks(they essentially dictate whether your model accepts a text or image input or both), and hit create, which then deploys a docker container running a FastAPI server for you to then query. Under the hood, there is a graph being created with each node in the graph loading up the resources needed to run your workload. And in this article, I want to talk about the breadth first search algorithm and how it powers my project.&lt;/p&gt;

&lt;p&gt;Now consider the following workflow that runs a Financial Bert model for Sentiment Analysis. You have 3 blocks - the input block which will take your input text, the ML block and the output block which will display the end result.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwrpuvb6bddivc1hxvcpv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwrpuvb6bddivc1hxvcpv.png" alt="Image description" width="723" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first step, would be to take this UI and translate it into format that would speak to the backend. This could look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Input_Block"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"run_config"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"All user based config"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"connections"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"process.Financial_BERT"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"payload"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;Insert Positive news for financial BERT&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"implementation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Input_Implementation_Class()&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"process"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Financial_BERT"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"run_config"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"All user based config"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"connections"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"output.Output_Block"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"payload"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;Insert Positive news for financial BERT&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"implementation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;HF_MODEL_Class()&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Output_Block"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"run_config"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"All user based config"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"connections"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step would be taking the payload of one block, passing it into the implementation of another, and you'd know the next block via the connections list. Now, naively you could implement an iterative loop that starts at the input block, traverses its connection by passing the payload around. It would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;start_node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;input&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Input_Block&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;start_node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;connection&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="c1"&gt;# pass payload to process.Financial_BERT.implementation
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach would work only if we had a linear workflow, where 1 block connects to only one block. But in the real world, one could have N blocks connecting to 1 block, wherein the following block expects inputs from all N blocks before running itself.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2xv4ocz55z0ruy5sxsh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2xv4ocz55z0ruy5sxsh.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The orange block needs inputs A,B and C as params so it can run.So how do we ensure inputs from block A,B and C is passed altogether in the orange block? This where the breadth first search comes to the rescue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftxda46kvagyecp8x0vnz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftxda46kvagyecp8x0vnz.png" alt="Image description" width="500" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://commons.wikimedia.org/wiki/File:Breadth-First-Search-Algorithm.gif" rel="noopener noreferrer"&gt;https://commons.wikimedia.org/wiki/File:Breadth-First-Search-Algorithm.gif&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The way the BFS algorithm works is by starting at some node, source, it first looks at all its neighbors and traverses the graph in a layer wise fashion. Here's an excerpt from a Hackerearth article that sums it up best:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqodqe3ck4zgimbi9177s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqodqe3ck4zgimbi9177s.png" alt="Image description" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://www.hackerearth.com/practice/algorithms/graphs/breadth-first-search/tutorial/" rel="noopener noreferrer"&gt;https://www.hackerearth.com/practice/algorithms/graphs/breadth-first-search/tutorial/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So coming back to our use case, we can do something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F002kh0ehg20of0ll23b4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F002kh0ehg20of0ll23b4.png" alt="Image description" width="800" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can start at some dummy node, look at all it's immediate neighbors (A,B, and C) and process them, and only when all 3 are processed is when we move on the next layer where the orange block is and pass the 3 block's output as the orange block's input. The way we implement this is through a queue that processes nodes in the order they arrived in. We'll also keep track of all the nodes we already visited and processed so we don't end up reprocessing them. Below is a step by step flow of how its implemented.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftqbbt2eeetrcxdoii278.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftqbbt2eeetrcxdoii278.png" alt="Image description" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step by step flow of how I used BFS to process nodes&lt;/p&gt;

&lt;p&gt;Here's the code block that I used to incorporate BFS in my project:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Froe5h2mzvxoxftv0jew0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Froe5h2mzvxoxftv0jew0.png" alt="Image description" width="800" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://github.com/farhan0167/otto-m8" rel="noopener noreferrer"&gt;https://github.com/farhan0167/otto-m8&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if you notice, it looks a lot like what you'd find on Wikipedia on the BFS Algorithm:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmd87ln6f5gkit23ozlst.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmd87ln6f5gkit23ozlst.png" alt="Image description" width="800" height="282"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://en.wikipedia.org/wiki/Breadth-first_search" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/Breadth-first_search&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have a way to pass payloads from one block to another, there's a whole world of possibilities of what can be implemented. At the moment otto-m8 (which translates to automate), aims to provide all these blocks which implement some form of Deep Learning model. It supports LLM's(OpenAI and Ollama) and Hugging Face models. When you create a workflow, you get a deployable Docker container with an exposed REST endpoint. And at its core (obviously an oversimplification) the BFS algorithm paves the way for a user input to pass through all the chained models in your workflow.&lt;br&gt;
Thank you for reading, especially if you've followed along all the way. Please feel free to try out otto-m8 which can be found here on Github: &lt;a href="https://github.com/farhan0167/otto-m8" rel="noopener noreferrer"&gt;https://github.com/farhan0167/otto-m8&lt;/a&gt;, accompanied by the documentation here at &lt;a href="https://otto-m8.com" rel="noopener noreferrer"&gt;https://otto-m8.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>lowcode</category>
      <category>ai</category>
      <category>deeplearning</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
