<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Krishaay's Blog]]></title><description><![CDATA[Krishaay's Blog]]></description><link>https://blog.krishaay.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 14 May 2026 09:41:56 GMT</lastBuildDate><atom:link href="https://blog.krishaay.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Demystifying AI Generated Art | Stable Diffusion]]></title><description><![CDATA[Generative AI has made progress in leaps and bounds in the past year with the release of LLMs (large language models) like ChatGPT from OpenAI and LLama2 from Meta as well as image generation models like DALL-E from OpenAI and Stable Diffusion from s...]]></description><link>https://blog.krishaay.dev/demystifying-ai-generated-art-stable-diffusion</link><guid isPermaLink="true">https://blog.krishaay.dev/demystifying-ai-generated-art-stable-diffusion</guid><category><![CDATA[stable diffusion]]></category><category><![CDATA[generative ai]]></category><category><![CDATA[Text2Image]]></category><dc:creator><![CDATA[Krishaay Jois]]></dc:creator><pubDate>Fri, 08 Sep 2023 10:52:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/pPDed_gBfuA/upload/bbdd60867e55a761679bbcb2c9307bb3.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Generative AI has made progress in leaps and bounds in the past year with the release of LLMs (large language models) like ChatGPT from OpenAI and LLama2 from Meta as well as image generation models like DALL-E from OpenAI and Stable Diffusion from <a target="_blank" href="http://stability.ai">stability.ai</a>.</p>
<p>These image generation models can convert a simple image description like</p>
<blockquote>
<p><strong>“A car on a road, sci-fi style”</strong></p>
</blockquote>
<p>into beautiful photo-realistic photographs like this</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694169224769/5ed4d697-0f9e-474a-b381-b9e355d21e2f.png" alt /></p>
<p>In this article, we are going to take a deep dive into how the stable diffusion (which is the current state-of-the-art image generation AI) model works.</p>
<h1 id="heading-how-do-diffusion-models-work">How do diffusion models work?</h1>
<p>Diffusion Models are a type of generative model which means they are built to generate outputs similar to ones they have been trained on.</p>
<p><strong>T</strong>here are two different types of diffusion processes:</p>
<h2 id="heading-forward-diffusion">Forward Diffusion</h2>
<p>This process progressively adds noise to the image until it is converted into <strong>uncharacteristic</strong> noise. Uncharacteristic noise means you can’t tell whether the original image was a dog, a cat or maybe even a car. This is a very important step in the process.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694169186582/e4db7a2f-c9d9-4b50-ab83-c3fcc7a91941.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-reverse-diffusion">Reverse Diffusion</h2>
<p>This is the fun part! The reverse diffusion process tries to reconstruct the original training image from the noisy image we got from <strong>Forward Diffusion</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694169168562/e8937370-4c8c-496e-aec0-369f6f3cab66.png" alt class="image--center mx-auto" /></p>
<p>The reconstructed image isn’t always the same as the original as there is randomness that comes into play here. Now we know what the model needs to do. But the question is <strong>HOW</strong> 🤔</p>
<h2 id="heading-training-process">Training Process</h2>
<p>To reconstruct the diffusion process, we need to find out how much noise was added to the image. To achieve this we train a neural network to predict the noise that was added. It is called a <strong>Noise Predictor</strong> which is a U-Net Model.</p>
<p><strong>The steps are as follows:</strong></p>
<ul>
<li><p>Pick a random training image</p>
</li>
<li><p>Generate some noise</p>
</li>
<li><p>Add this noise to the training image for a certain number of steps</p>
</li>
<li><p>Teach the noise predictor how much noise was added</p>
</li>
</ul>
<blockquote>
<p>🔥 Now we have a fully trained noise predictor</p>
</blockquote>
<h2 id="heading-inference">Inference</h2>
<p>To use this noise predictor, we generate a new noisy image. The noise predictor estimates how much noise was added and then removes the noise from the image. We repeat this process for a specified number of sampling steps.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694169112702/3baf4db1-7f31-4754-822e-3bdf91fbc8f7.png" alt="Noise removal visualisation" class="image--center mx-auto" /></p>
<p>The above process we discussed is running in <strong>pixel space</strong> (holds data for the red, green and blue channels for every single pixel). A single image of resolution 512x512 has <strong>786,432</strong> dimensions to it. Running these computations in pixel space is very, very slow and requires several GPUs at a minimum to run 🤯. But, Stable Diffusion has a trick up its sleeve 👇</p>
<h1 id="heading-stable-diffusion">Stable Diffusion</h1>
<h2 id="heading-latent-space">Latent Space</h2>
<p>To overcome the computational speed issues, we have what are called <strong>Latent Diffusion Models</strong> such as the Stable Diffusion family of models which compress the high dimensional space from <strong>pixel space</strong> into something called <strong>latent space</strong>.</p>
<p>Latent Space is <strong>48x</strong> smaller than pixel space which makes it exponentially faster to run which unlocks the ability to run inference on a single GPU with decent speeds.</p>
<h2 id="heading-variational-autoencoder-vae">Variational Autoencoder (VAE)</h2>
<p>To perform the compression we use a technique called the variational autoencoder (VAE). It consists of two parts:</p>
<ol>
<li><p><strong>Encoder -</strong> It handles compressing the image from pixel space to latent space</p>
</li>
<li><p><strong>Decoder -</strong> It handles converting the image from latent space back to pixel space</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694169105982/a7c210e2-4e2a-469b-ae28-5d1f5e239609.png" alt class="image--center mx-auto" /></p>
</li>
</ol>
<p>The image is compressed into latent space without any loss of information. This is possible since natural images are <strong>not random.</strong> For example, faces follow a certain spatial relationship between eyes, nose and other features. You can read more about this here → <a target="_blank" href="https://en.wikipedia.org/wiki/Manifold_hypothesis">Manifold Hypothesis</a>.</p>
<h2 id="heading-inference-in-latent-space">Inference in Latent Space</h2>
<p>The inference in latent space is mostly the same as the one in pixel space except, a random latent space matrix is used instead of the generated noise image. An additional <strong>VAE Decoder</strong> step is also added after inference completes to convert the latent matrix back into a regular image (pixel space) which is our final generated image.</p>
<h2 id="heading-conditioning">Conditioning</h2>
<p>In the steps we discussed above, we never specified what we wanted the model to generate. Telling or <strong><em>“conditioning”</em></strong> the model to generate a certain kind of desired result is known as <strong>conditioning</strong>.</p>
<p>There are several types of conditioning such as:</p>
<ul>
<li><p>Text conditioning (aka prompting)</p>
</li>
<li><p>inpainting</p>
</li>
<li><p>outpainting</p>
</li>
<li><p>controlnets</p>
</li>
<li><p>and more….</p>
</li>
</ul>
<p>In this article, we will only look into text conditioning which is the most widely used conditioning method and it is also used in several other conditioning methods.</p>
<h3 id="heading-text-conditioning">Text Conditioning</h3>
<p>Here is a high-level view of how the text prompts are processed and fed into the noise predictor (U-NET). This might look familiar to some who know about the transformer model architecture for LLMs.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694169033447/60b4663b-3204-41f9-870f-721bec5c1611.png" alt class="image--center mx-auto" /></p>
<p><strong>Tokenizer</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694169022513/0d49057b-b4f7-4280-b3d0-5ffd6283673f.png" alt class="image--center mx-auto" /></p>
<p>The text prompt is <strong>tokenized</strong> using the <a target="_blank" href="https://openai.com/research/clip">CLIP</a> tokenizer. Tokenization allows the model to understand the prompt without having to understand “words”. Each word <strong>doesn’t</strong> always correspond to a single token. One word may consist of multiple tokens as well.</p>
<p><strong>Embedding Model</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694169030288/5ec9bbe6-c137-4c28-a7e7-3dfe81fa174e.png" alt class="image--center mx-auto" /></p>
<p>The embedding model converts these tokens into vectors. Each vector has a unique fixed vector embedding which is learned by the embedding model when it was trained. Vector embedding allows computers to understand how semantically similar two tokens using the distance between any two vectors. Stable Diffusion used OpenAI’s ViT-L/14 CLIP Model.</p>
<p><strong>Text Transformer</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694169018451/4bcb69e3-4d5f-489a-8705-6280ebadf9a2.png" alt class="image--center mx-auto" /></p>
<p>The Text Transformer is the final step in the pipeline for processing the text prompt. It serves as an adapter for other conditioning methods. The inputs to the transformer are not limited to text, it can include images, depth maps and a variety of other conditioning inputs.</p>
<p><strong>Cross-Attention Mechanism</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694168873168/632da9ca-ded0-42e1-9c4b-bc3411421431.png" alt class="image--center mx-auto" /></p>
<p>The Noise Predictor (U-NET) ingests the output of the text transformer via a cross-attention mechanism. It has two parts to it:</p>
<p><strong>(a) Self Attention (within the prompt)</strong></p>
<p>Assume the text prompt is as follows:</p>
<blockquote>
<p><strong><em>“A blue car on the road”</em></strong></p>
</blockquote>
<p>The self-attention mechanism pairs up <strong>“blue”</strong> and “<strong><em>car”</em></strong> so the model generates images with a <strong>“blue car”</strong> and not a <strong>“blue road”</strong>. For an in-depth look into this, read the <a target="_blank" href="https://arxiv.org/abs/1706.03762">Attention is all you need</a> paper.</p>
<p><strong>(b) Cross Attention (between prompt and image)</strong></p>
<p>The model then uses the information from <strong>(a)</strong> to guide the reverse diffusion process to generate the images containing blue cards.</p>
<p>This is a very important part of the conditioning pipeline, so much so that modifying its functionality can change the style of the generated images. Modifying these to fine-tune model outputs is known as Hypernetworks, you can read about them <a target="_blank" href="https://blog.novelai.net/novelai-improvements-on-stable-diffusion-e10d38db82ac">here</a>.</p>
<h2 id="heading-end-to-end-pipeline-overview">End-to-End Pipeline Overview</h2>
<p>Based on everything we have discussed above, here is what the finished pipeline looks like.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1694170003584/b78cdd44-f2ee-4be3-8dca-eb467643e151.png" alt class="image--center mx-auto" /></p>
<p>and this is a visualization of how the noise is converted into an image.</p>
<p><img src="https://i0.wp.com/stable-diffusion-art.com/wp-content/uploads/2022/12/cat_euler_15.gif?resize=512%2C512&amp;ssl=1" alt="stable diffusion euler" class="image--center mx-auto" /></p>
<p>If you made it till here, 👏. Thank you for reading the article. I hope you found the information useful. Please post any feedback you have in the comments below. Peace ✌️.</p>
<p><img src="https://y.yarn.co/4b40d729-6ee0-4245-8f97-97904651c279_text.gif" alt class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[Building a GPT Powered PokeDex App]]></title><description><![CDATA[OpenAI’s Large Language Models like ChatGPT and GPT-3 have been used to build a variety of powerful applications. In this post, I’ll walk you through how I built my first LLM powered application, OpenDex.
Step 1: Create OpenAI API Key

⚠️ If you don’...]]></description><link>https://blog.krishaay.dev/building-a-gpt-powered-pokedex-app</link><guid isPermaLink="true">https://blog.krishaay.dev/building-a-gpt-powered-pokedex-app</guid><category><![CDATA[pokemon]]></category><category><![CDATA[GPT 3]]></category><category><![CDATA[openai]]></category><category><![CDATA[langchain]]></category><dc:creator><![CDATA[Krishaay Jois]]></dc:creator><pubDate>Tue, 23 May 2023 11:10:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1684839240099/98d360ce-535a-414f-8b3f-e1e656ea1315.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>OpenAI’s Large Language Models like <strong>ChatGPT</strong> and <strong>GPT-3</strong> have been used to build a variety of powerful applications. In this post, I’ll walk you through how I built my first LLM powered application, <strong>OpenDex</strong>.</p>
<h1 id="heading-step-1-create-openai-api-key">Step 1: Create OpenAI API Key</h1>
<blockquote>
<p>⚠️ If you don’t have an OpenAI account, create one and then follow the steps below.</p>
</blockquote>
<h3 id="heading-1-open-dashboard">1) Open Dashboard</h3>
<p>Dashboard Link → <a target="_blank" href="https://platform.openai.com/account/api-keys">https://platform.openai.com/account/api-keys</a></p>
<p>You should see this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1684839747930/c06b8674-d68d-4e67-a50f-d0c581a3ac90.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-2-create-a-new-secret-key">2) Create a new secret key</h3>
<p>Enter <code>OpenDex</code> as the key name and create it</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1684839999451/76561091-355e-4380-a9da-f329265dd35e.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-3-save-the-key">3) Save the key</h3>
<p>Copy the key it gives you and save it on notepad for now, you will need it later!</p>
<p>Click on <code>Done</code> and you can close the tab</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1684839764945/9e53a4ad-6fb7-4efb-bdd0-11a38e54f53c.png" alt class="image--center mx-auto" /></p>
<p>✅ <strong>You now have your API Key</strong></p>
<h1 id="heading-step-2-find-a-database-of-all-the-pokemon-and-their-attributes">Step 2: Find a database of all the pokemon and their attributes</h1>
<p><strong>I found a pokemon dataset on kaggle which had pokemons up to the 8th Generation.</strong></p>
<p><a target="_blank" href="https://www.kaggle.com/datasets/mariotormo/complete-pokemon-dataset-updated-090420?select=pokedex_%28Update_04.21%29.csv">https://www.kaggle.com/datasets/mariotormo/complete-pokemon-dataset-updated-090420?select=pokedex_(Update_04.21).csv</a></p>
<p><strong>You can use other pokemon databases/datasets if you wish to do so.</strong></p>
<blockquote>
<p>⚠️ Download this dataset into a folder called <strong>datasets</strong> in your working directory so your folder looks like this.  </p>
<p>Here <code>dataset_file_name.csv</code> is a placeholder name</p>
<pre><code class="lang-plaintext">.
└── datasets/
    └── dataset_file_name.csv
</code></pre>
</blockquote>
<h1 id="heading-step-3-setup-the-backend">Step 3: Setup the backend</h1>
<h3 id="heading-step-1-define-the-environment-variables">Step 1: Define the environment variables</h3>
<p>Create a file <code>.env</code> in your working directory so the file tree looks like this</p>
<pre><code class="lang-plaintext">.
├── datasets/
│   └── dataset_file_name.csv
└── .env
</code></pre>
<p>and paste the following into the file</p>
<pre><code class="lang-plaintext">OPENAI_APK_KEY="&lt;key&gt;"
</code></pre>
<p>Replace <code>&lt;key&gt;</code> with your secret key you noted down earlier and save the file</p>
<h3 id="heading-step-2-install-python-dependencies">Step 2: Install python dependencies</h3>
<p>Create a file <code>requirements.txt</code> in your working directory so you file tree looks like this</p>
<pre><code class="lang-plaintext">.
├── datasets/
│   └── dataset_file_name.csv
├── .env
└── requirements.txt
</code></pre>
<p>Add the dependencies to the file</p>
<pre><code class="lang-plaintext">flask
langchain
openai
python-dotenv
flask-cors
flask-restful
pandas
</code></pre>
<p>Save the file.</p>
<p>Install the dependencies using pip using: <code>pip install -r requirements.txt</code></p>
<h3 id="heading-step-3-building-the-flask-server">Step 3: Building the flask server</h3>
<p>Create a new file <code>app.py</code> in the working directory so you file tree looks like this:</p>
<pre><code class="lang-plaintext">.
├── datasets/
│   └── dataset_file_name.csv
├── .env
├── requirements.txt
└── app.py
</code></pre>
<p><strong>We’ll start by importing all the modules we will be using</strong></p>
<pre><code class="lang-python"><span class="hljs-comment"># Flask API</span>
<span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, render_template, request
<span class="hljs-keyword">from</span> flask_restful <span class="hljs-keyword">import</span> Api, Resource
<span class="hljs-keyword">from</span> flask_cors <span class="hljs-keyword">import</span> CORS

<span class="hljs-comment"># Langchain SDK</span>
<span class="hljs-keyword">from</span> langchain.llms <span class="hljs-keyword">import</span> OpenAI
<span class="hljs-keyword">from</span> langchain.agents <span class="hljs-keyword">import</span> create_csv_agent

<span class="hljs-comment"># Environment Variables</span>
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
</code></pre>
<p><strong>Now we’ll load in the environment variables</strong></p>
<pre><code class="lang-python">load_dotenv()
</code></pre>
<p><strong>To work with our dataset we’ll be using the CSV Agent included with the langchain SDK</strong></p>
<pre><code class="lang-python">agent = create_csv_agent(OpenAI(temperature=<span class="hljs-number">0.1</span>),
                         <span class="hljs-string">'datasets/dataset_file_name.csv'</span>, verbose=<span class="hljs-literal">True</span>)
</code></pre>
<p><em>make sure you replace</em> <code>dataset_file_name.csv</code> <em>with the filename for your dataset</em></p>
<p><strong>Now, create the flask and add CORS middleware</strong></p>
<pre><code class="lang-python"><span class="hljs-comment"># Create Flask A</span>
app = Flask(__name__)
<span class="hljs-comment"># Add CORS middleware</span>
CORS(app)
api = Api(app)
</code></pre>
<p><strong>We’ll return our html document at the</strong> <code>/</code> path</p>
<pre><code class="lang-python"><span class="hljs-comment"># return index.html at / path</span>
<span class="hljs-meta">@app.route('/')</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span>():</span>
        <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">'index.html'</span>)
</code></pre>
<p><strong>Now the core section which is the api endpoint which takes a prompt as input and returns the response from the LLM</strong></p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OpenDexApi</span>(<span class="hljs-params">Resource</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">post</span>(<span class="hljs-params">self</span>):</span>
                <span class="hljs-comment"># data from the frontend</span>
        jsonData = request.get_json(force=<span class="hljs-literal">True</span>)
                <span class="hljs-comment"># user's prompt</span>
        prompt = jsonData[<span class="hljs-string">'prompt'</span>]

                <span class="hljs-comment"># return an error if the prompt is empty</span>
                <span class="hljs-keyword">if</span> (prompt == <span class="hljs-literal">None</span>):
            <span class="hljs-keyword">return</span> {<span class="hljs-string">'error'</span>: <span class="hljs-string">'prompt is required'</span>}, <span class="hljs-number">400</span>
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">try</span>:
                <span class="hljs-comment"># execute the prompt using langchain csv agent</span>
                                op = agent.run(prompt)
                                <span class="hljs-comment"># respond with the response from the agent</span>
                <span class="hljs-keyword">return</span> {<span class="hljs-string">'prompt'</span>: prompt, <span class="hljs-string">'output'</span>: op}, <span class="hljs-number">200</span>
            <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
                <span class="hljs-comment"># return error data if there is an issue in execution</span>
                <span class="hljs-keyword">return</span> {<span class="hljs-string">'error'</span>: str(e)}, <span class="hljs-number">500</span>
</code></pre>
<p><strong>Map the resource we created to the</strong> <code>/api</code> path</p>
<pre><code class="lang-python">api.add_resource(OpenDexApi, <span class="hljs-string">'/api'</span>)
</code></pre>
<p><strong>Run the server</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    app.run(debug=<span class="hljs-literal">True</span>)
</code></pre>
<p><strong>Here is all the code in one code block</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, render_template, request
<span class="hljs-keyword">from</span> flask_restful <span class="hljs-keyword">import</span> Api, Resource
<span class="hljs-keyword">from</span> flask_cors <span class="hljs-keyword">import</span> CORS
<span class="hljs-keyword">from</span> langchain.llms <span class="hljs-keyword">import</span> OpenAI
<span class="hljs-keyword">from</span> langchain.agents <span class="hljs-keyword">import</span> create_csv_agent
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
load_dotenv()
agent = create_csv_agent(OpenAI(temperature=<span class="hljs-number">0.1</span>),
                         <span class="hljs-string">'datasets/dataset_file_name.csv'</span>, verbose=<span class="hljs-literal">True</span>)
app = Flask(__name__)
CORS(app)
api = Api(app)

<span class="hljs-meta">@app.route('/')</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span>():</span>
    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">'index.html'</span>)

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OpenDexApi</span>(<span class="hljs-params">Resource</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">post</span>(<span class="hljs-params">self</span>):</span>
        jsonData = request.get_json(force=<span class="hljs-literal">True</span>)
        prompt = jsonData[<span class="hljs-string">'prompt'</span>]
        <span class="hljs-keyword">if</span> (prompt == <span class="hljs-literal">None</span>):
            <span class="hljs-keyword">return</span> {<span class="hljs-string">'error'</span>: <span class="hljs-string">'prompt is required'</span>}, <span class="hljs-number">400</span>
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">try</span>:
                op = agent.run(prompt)
                <span class="hljs-keyword">return</span> {<span class="hljs-string">'prompt'</span>: prompt, <span class="hljs-string">'output'</span>: op}, <span class="hljs-number">200</span>
            <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
                <span class="hljs-keyword">return</span> {<span class="hljs-string">'error'</span>: str(e)}, <span class="hljs-number">500</span>

api.add_resource(OpenDexApi, <span class="hljs-string">'/api'</span>)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    app.run(debug=<span class="hljs-literal">True</span>)
</code></pre>
<h3 id="heading-step-4-add-the-files-for-the-frontend">Step 4: Add the files for the frontend</h3>
<p><strong>1) HTML Template</strong></p>
<p>Create a folder <code>templates</code> and create a file <code>index.html</code> inside it</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>OpenDex<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/static/style.css"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"logo"</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>OpenDex<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-container"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">form</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"prompt"</span>&gt;</span>Question<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"prompt"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"prompt"</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Ask OpenDex<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"loader"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"loader"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- add a section to display the response --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"response-div"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"response"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/static/index.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p><strong>2) Stylesheet and Javascript</strong></p>
<p>Create a folder <code>static</code> and create a file <code>index.js</code></p>
<pre><code class="lang-jsx"><span class="hljs-comment">// references to html elements</span>
<span class="hljs-keyword">const</span> submitButton = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'submit'</span>);
<span class="hljs-keyword">const</span> loader = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'loader'</span>);
<span class="hljs-keyword">const</span> responseDiv = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'response'</span>);

<span class="hljs-comment">// add click callback for the submit button</span>
submitButton.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
        <span class="hljs-comment">// turn on loading animation</span>
    loader.style.display = <span class="hljs-string">'block'</span>;
        <span class="hljs-comment">// hide submit button</span>
    submitButton.style.display = <span class="hljs-string">'none'</span>;
    <span class="hljs-comment">// clear previous references</span>
    responseDiv.innerHTML = <span class="hljs-string">''</span>;
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'submit button clicked'</span>);
        <span class="hljs-comment">// prevent default form submission</span>
        event.preventDefault();
    <span class="hljs-comment">// get user's prompt from the input element</span>
    <span class="hljs-keyword">const</span> prompt = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'prompt'</span>).value;
    <span class="hljs-comment">// create request</span>
    <span class="hljs-keyword">const</span> xhr = <span class="hljs-keyword">new</span> XMLHttpRequest();
    xhr.open(<span class="hljs-string">'POST'</span>, <span class="hljs-string">'/api'</span>);
    xhr.responseType = <span class="hljs-string">'json'</span>;
    xhr.setRequestHeader(<span class="hljs-string">'Content-Type'</span>, <span class="hljs-string">'application/json'</span>);
        <span class="hljs-comment">// define callback for the api request</span>
    xhr.onload = <span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">const</span> response = xhr.response;

        <span class="hljs-keyword">if</span> (xhr.status === <span class="hljs-number">200</span>) { <span class="hljs-comment">// if request was completed without any issues</span>
            <span class="hljs-built_in">console</span>.log(response);
                        <span class="hljs-comment">// display the response from the backend to the user</span>
            responseDiv.innerHTML = response.output;
        } <span class="hljs-keyword">else</span> { <span class="hljs-comment">// if there was an issue while processing the request</span>
            responseDiv.innerHTML = <span class="hljs-string">'Error\n'</span> + response.error;
            <span class="hljs-built_in">console</span>.error(response);
        }
        <span class="hljs-comment">// stop the loading animation</span>
        loader.style.display = <span class="hljs-string">'none'</span>;
        <span class="hljs-comment">// show the submit button</span>
        submitButton.style.display = <span class="hljs-string">'block'</span>;
    };
    <span class="hljs-comment">// send the api request</span>
    xhr.send(<span class="hljs-built_in">JSON</span>.stringify({ <span class="hljs-attr">prompt</span>: prompt }));
});
</code></pre>
<p><strong>Inside the same</strong> <code>static</code> folder create another file <code>style.css</code></p>
<pre><code class="lang-css"><span class="hljs-keyword">@import</span> url(<span class="hljs-string">'https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@400;500;700&amp;display=swap'</span>);

* {
    <span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Lexend Deca'</span>, sans-serif;
}

<span class="hljs-selector-class">.logo</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">32px</span>;
    <span class="hljs-attribute">font-weight</span>: bold;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#333</span>;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">text-align</span>: center;
}

<span class="hljs-selector-class">.logo</span> <span class="hljs-selector-tag">img</span> {
    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">height</span>: auto;
}

<span class="hljs-selector-class">.form-container</span> {
    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">500px</span>;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ccc</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
    <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">10px</span> <span class="hljs-number">#ccc</span>;
}

<span class="hljs-selector-class">.form-container</span> <span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"text"</span>]</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">10px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ccc</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
    <span class="hljs-attribute">box-sizing</span>: border-box;
}

<span class="hljs-selector-class">.form-container</span> <span class="hljs-selector-tag">button</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"submit"</span>]</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#333</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span> <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">border</span>: none;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
    <span class="hljs-attribute">cursor</span>: pointer;
}

<span class="hljs-selector-class">.form-container</span> <span class="hljs-selector-tag">button</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"submit"</span>]</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#555</span>;
}

<span class="hljs-comment">/* add some styling for the dropdown */</span>
<span class="hljs-selector-class">.dropdown</span> {
    <span class="hljs-attribute">position</span>: relative;
    <span class="hljs-attribute">display</span>: inline-block;
}

<span class="hljs-selector-class">.form-container</span> <span class="hljs-selector-tag">select</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">10px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ccc</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
    <span class="hljs-attribute">box-sizing</span>: border-box;
}

<span class="hljs-selector-class">.loader</span> {
    <span class="hljs-attribute">border</span>: <span class="hljs-number">5px</span> solid <span class="hljs-number">#f3f3f3</span>;
    <span class="hljs-comment">/* visibility: hidden; */</span>
    <span class="hljs-attribute">display</span>: none;
    <span class="hljs-comment">/* Light grey */</span>
    <span class="hljs-attribute">border-top</span>: <span class="hljs-number">5px</span> solid <span class="hljs-number">#ed7777</span>;
    <span class="hljs-comment">/* Blue */</span>
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">30px</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">30px</span>;
    <span class="hljs-attribute">animation</span>: spin <span class="hljs-number">1s</span> linear infinite;
}

<span class="hljs-keyword">@keyframes</span> spin {
    0% {
        <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate</span>(<span class="hljs-number">0deg</span>);
    }

    100% {
        <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate</span>(<span class="hljs-number">360deg</span>);
    }
}
</code></pre>
<p><strong>Now your file tree should end up looking like this</strong></p>
<pre><code class="lang-plaintext">.
├── datasets/
│   └── dataset_file_name.csv
├── templates/
│   └── index.html
├── static/
│   ├── style.css
│   └── index.js
├── .env
├── requirements.txt
└── app.py
</code></pre>
<h3 id="heading-step-5-run-the-app">Step 5: Run the app</h3>
<pre><code class="lang-plaintext">python app.py
</code></pre>
<h3 id="heading-step-6-open-the-website">Step 6: Open the Website</h3>
<p>When you run the server you should see output similar to this</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1684839929237/799353d9-1d77-4560-a446-aa0d1c7c5075.png" alt class="image--center mx-auto" /></p>
<p>Open the IP address which was logged in your web browser and you should be able to see the OpenDex Page.</p>
<h1 id="heading-source-code">Source Code</h1>
<p>The source code for this project is available on Github at the link below 👇</p>
<p><a target="_blank" href="https://github.com/kry0sc0pic/OpenDex">https://github.com/kry0sc0pic/OpenDex</a></p>
<p><img src="https://media.giphy.com/media/kshYlR6jUxesJDUWZj/giphy.gif" alt="https://media.giphy.com/media/kshYlR6jUxesJDUWZj/giphy.gif" /></p>
<h1 id="heading-thanks-for-reading">Thanks for reading!</h1>
<p><strong>If you enjoy content like this, consider following so you don’t miss out.</strong></p>
]]></content:encoded></item><item><title><![CDATA[How I single-handedly reduced developer onboarding from 40 hours to 2 hours]]></title><description><![CDATA[Introduction
In today's fast-paced and competitive tech industry, time is of the essence, and every minute counts. This is especially true when it comes to developer onboarding, where the time it takes to get new hires up to speed can have a signific...]]></description><link>https://blog.krishaay.dev/how-i-single-handedly-reduced-developer-onboarding-from-40-hours-to-2-hours</link><guid isPermaLink="true">https://blog.krishaay.dev/how-i-single-handedly-reduced-developer-onboarding-from-40-hours-to-2-hours</guid><category><![CDATA[Productivity]]></category><category><![CDATA[Devops]]></category><category><![CDATA[software development]]></category><category><![CDATA[Collaboration]]></category><category><![CDATA[documentation]]></category><dc:creator><![CDATA[Krishaay Jois]]></dc:creator><pubDate>Sat, 11 Mar 2023 17:45:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/BXOXnQ26B7o/upload/9af8795024a726de44a7e3d9a2aa8af1.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-introduction">Introduction</h3>
<p>In today's fast-paced and competitive tech industry, time is of the essence, and every minute counts. This is especially true when it comes to developer onboarding, where the time it takes to get new hires up to speed can have a significant impact on project timelines and deadlines. Team leads are faced with tough choices - make the deadline or spend time on documentation. The choices determine the ability of the team to scale up. Team leads will most often sacrifice documentation to meet project deadlines. This results in higher onboarding times of new developers down the line which delays projects to which the developer may be assigned. Documentation is an investment that pays itself back many times over.</p>
<p>After struggling with a lengthy and frustrating onboarding process that took up to 40 hours across 4 days, I decided to take matters into my own hands and find ways to streamline the process. In this article, I will share the strategies that helped reduce this time to just under 2 hours.</p>
<h3 id="heading-the-problem">The Problem</h3>
<p>For a bit of background, I joined the team as a new developer and decided to take the time to document this process as I went through it. The team was geographically spread across various time zones as well. This amplified the impact of the problem as well.</p>
<p>The existing documentation/wikis:</p>
<ol>
<li><p>Were not comprehensive</p>
</li>
<li><p>Were not up to date</p>
</li>
<li><p>Did not serve as a knowledge base</p>
</li>
</ol>
<h3 id="heading-problem-impact">Problem Impact</h3>
<ol>
<li><p>Lower developer productivity</p>
</li>
<li><p>Increased development costs</p>
</li>
<li><p>Detrimental to team morale and motivation</p>
</li>
<li><p>Delayed project and product timelines</p>
</li>
</ol>
<h3 id="heading-solution-approach">Solution Approach</h3>
<p>Some of the important considerations while designing the new onboarding process were:</p>
<ol>
<li><p>The documentation should be experience and skill-agnostic</p>
</li>
<li><p>Minimize the need for communication with other developers during the onboarding process</p>
</li>
</ol>
<h3 id="heading-the-solution">The Solution</h3>
<ol>
<li><p>Having complete, in-depth, and <strong>up-to-date</strong> documentation that caters to developers with any experience level. The documentation should at a minimum include:</p>
<ul>
<li><p>Environment setup guide</p>
</li>
<li><p>A brief overview of the codebase and repositories</p>
</li>
<li><p>Additional information such as commit formats, standards</p>
</li>
<li><p>A document owner early on in the process</p>
</li>
</ul>
</li>
<li><p>Update documents frequently</p>
<ul>
<li><p>Review and update just after each release</p>
</li>
<li><p>Review and update after migrations or upgrades (a lot of issues tend to pop up post and during these processes so it is essential to do this)</p>
</li>
<li><p>Ensure all developers are contributing ideas and information to the document</p>
</li>
</ul>
</li>
<li><p>Build and maintain a well-structured knowledge base. This is should ideally include</p>
<ul>
<li><p>Solutions to frequent issues faced by team members</p>
</li>
<li><p>Traps, pitfalls, and quirks of the tools and frameworks being used</p>
</li>
<li><p>Any other relevant details and resources</p>
</li>
</ul>
</li>
</ol>
<p>It is also very effective to have a blog-like log section for developers to quickly note down any issues they may encounter or notice as well as solutions to those. These tidbits of information can be collated and refined to further expand the knowledge base.</p>
<p>I would suggest adding walkthrough videos to complement relevant text guides to further improve productivity.</p>
<blockquote>
<p><strong>One of the most important things to keep in mind is to keep the documentation up to date with solutions to issues and new documentation as and when needed.</strong></p>
<p><strong>It is a common occurrence where the documentation is left untouched after the initial version or a few updates and eventually becomes outdated.</strong></p>
</blockquote>
<h3 id="heading-tldr">TLDR;</h3>
<ul>
<li><p>Create comprehensive and up-to-date documentation for the onboarding process that includes an environment setup guide, codebase and repository overview, solutions to frequently faced issues, and additional information such as commit formats and standards</p>
</li>
<li><p>Review and update documentation regularly</p>
</li>
<li><p>Establish a document owner and ensure all developers contribute to the document.</p>
</li>
<li><p>Build and maintain a well-structured knowledge base that includes solutions to frequent issues faced by team members, traps, pitfalls, and quirks of the tools and frameworks being used</p>
</li>
</ul>
<h3 id="heading-bonus-tools">Bonus → Tools</h3>
<p>I used these tools in building the documentation and still use them regularly.</p>
<ol>
<li><p><strong>Video Recording →</strong> <a target="_blank" href="https://www.loom.com/"><strong>Loom</strong></a></p>
<p> Loom is a simple and powerful tool to record videos. I used it to record the video guides for the onboarding process. I also regularly use it to demonstrate features and share ideas or concepts.</p>
</li>
<li><p><strong>Building Guides →</strong> <a target="_blank" href="https://scribehow.com/"><strong>Scribe</strong></a></p>
<p> Scribe is a powerful tool that makes building detailed step-by-step guides a walk in the park for anyone. It’s just as simple as hitting record, doing all the steps you would like to detail, and hitting save. That’s it.</p>
</li>
<li><p><strong>Wikis/Docs →</strong> <a target="_blank" href="https://www.notion.so/"><strong>Notion</strong></a></p>
<p> Notion is one of the best and most versatile productivity tools out there and is excellent for building wikis, guides, and managing projects.</p>
</li>
</ol>
<p><strong>I hope these strategies and tools are helpful for you and your team in streamlining your onboarding process. Remember, documentation, and communication are key to ensuring new developers can quickly get up to speed and start contributing to your projects as soon as possible.</strong></p>
]]></content:encoded></item></channel></rss>