<?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[Ashish's Tech Blogs]]></title><description><![CDATA[Sharing the experiences I had with AI]]></description><link>https://tech-talk.getwithashish.yahodu.info</link><generator>RSS for Node</generator><lastBuildDate>Fri, 24 Apr 2026 11:41:55 GMT</lastBuildDate><atom:link href="https://tech-talk.getwithashish.yahodu.info/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[GraphQL with Python Strawberry]]></title><description><![CDATA[Imagine this: You walk into your favorite grocery store, ready to fill your cart with the essentials. You’re in a hurry, so you only want specific items—some organic strawberries, a loaf of sourdough, and almond milk. Now, instead of wandering throug...]]></description><link>https://tech-talk.getwithashish.yahodu.info/graphql-with-python-strawberry</link><guid isPermaLink="true">https://tech-talk.getwithashish.yahodu.info/graphql-with-python-strawberry</guid><category><![CDATA[GraphQL]]></category><category><![CDATA[strawberry]]></category><category><![CDATA[Python]]></category><category><![CDATA[FastAPI]]></category><category><![CDATA[pydantic]]></category><category><![CDATA[REST API]]></category><category><![CDATA[Apollo GraphQL]]></category><category><![CDATA[APIs]]></category><dc:creator><![CDATA[Ashish Sam T George]]></dc:creator><pubDate>Mon, 16 Sep 2024 18:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728390414169/bdcf96cd-8beb-4924-b280-813663e63f34.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine this: You walk into your favorite grocery store, ready to fill your cart with the essentials. You’re in a hurry, so you only want specific items—some organic strawberries, a loaf of sourdough, and almond milk. Now, instead of wandering through every aisle and picking things manually (like in traditional APIs), you simply hand your list to the store manager, who collects exactly what you need and delivers it directly to you. Efficient, right?</p>
<p>This is the beauty of GraphQL. Rather than pulling all possible data from an endpoint like in REST APIs, GraphQL allows you to ask for <strong>only what you need</strong>. No extra data, no unnecessary overload. And what makes this experience even better is <strong>Python Strawberry</strong>, an easy-to-use library that allows you to set up GraphQL servers with Python.</p>
<p>In this extended journey, we’ll explore the core concepts of Strawberry, delve into some practical use cases. Let’s not only learn how to install and use Strawberry, but also dive deeper into the technical aspects of creating, mutating, and handling real-time data.</p>
<h2 id="heading-step-1-setting-the-scene-installing-the-essentials">Step 1: Setting the Scene - Installing the Essentials</h2>
<p>Before you embark on this seamless grocery shopping experience, you need the right tools. Just like how you wouldn't start grocery shopping without a cart, we need to install a few key packages to get Strawberry and GraphQL up and running.</p>
<p>To install the required packages, you’ll need to have <strong>Python 3.10+</strong> installed. After that, Strawberry can be installed using pip:</p>
<pre><code class="lang-bash">pip install strawberry-graphql
</code></pre>
<p>If you want to integrate Strawberry with <strong>FastAPI</strong> (which we will later in the article), install the FastAPI package too:</p>
<pre><code class="lang-bash">pip install fastapi
pip install strawberry-graphql[fastapi]
</code></pre>
<p>You’ll also need <strong>Uvicorn</strong>, an ASGI server to run your FastAPI app:</p>
<pre><code class="lang-bash">pip install uvicorn
</code></pre>
<p>Now you’re all set to begin building with Strawberry!</p>
<h3 id="heading-gathering-the-right-ingredients">Gathering the Right Ingredients</h3>
<p>Think of installing these packages like gathering the ingredients you need for a recipe. You wouldn’t start cooking without the basics like flour, eggs, and milk. Similarly, you need these libraries to kick off your GraphQL adventure. Once you have them in place, you’re ready to start cooking up some awesome APIs.</p>
<hr />
<h2 id="heading-step-2-understanding-graphql-basics">Step 2: Understanding GraphQL Basics</h2>
<p>Before diving into coding, let’s take a moment to appreciate why GraphQL is so powerful. Think of your data as a grocery store. In the traditional <strong>REST API</strong> world, you walk down every aisle (or hit every endpoint) to collect your items, but you often end up grabbing things you don’t need, like that family-size bag of chips (unwanted data). The REST API doesn’t let you be selective—either you get everything in the aisle, or nothing.</p>
<p><strong>GraphQL</strong> flips this concept on its head. Instead of walking through every aisle, you simply hand over a precise shopping list (your query) to the store manager (the GraphQL server). The manager retrieves only the specific items you requested, down to the last strawberry. It’s efficient, precise, and saves you the headache of sifting through a pile of unnecessary data.</p>
<p>So, how do you get started with this neat querying system? Let’s jump into our first example.</p>
<hr />
<h2 id="heading-step-3-creating-your-first-query-ask-for-exactly-what-you-need">Step 3: Creating Your First Query - Ask for Exactly What You Need</h2>
<p>Let’s say you want to retrieve information about a particular fruit from a database (or your hypothetical grocery store). You’re only interested in a <strong>strawberry</strong> and its level of sweetness, not all the fruits available in the store. This is where GraphQL shines—you define what data you need and get exactly that.</p>
<p>Here's how you create a <strong>Strawberry Schema</strong> in Python to query specific fruit details:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> strawberry

<span class="hljs-meta">@strawberry.type</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Fruit</span>:</span>
    name: str
    sweetness: int

<span class="hljs-meta">@strawberry.type</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Query</span>:</span>
<span class="hljs-meta">    @strawberry.field</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_fruit</span>(<span class="hljs-params">self</span>) -&gt; Fruit:</span>
        <span class="hljs-keyword">return</span> Fruit(name=<span class="hljs-string">"Strawberry"</span>, sweetness=<span class="hljs-number">8</span>)

schema = strawberry.Schema(query=Query)
</code></pre>
<p>In this example, we define a <strong>Fruit</strong> type with two fields: <code>name</code> and <code>sweetness</code>. Then, we create a <code>Query</code> type that fetches a specific fruit. Here, the query always returns a <strong>strawberry</strong> with a sweetness level of 8.</p>
<p>Now that we’ve set up the schema, we can query it by executing a GraphQL request like this:</p>
<pre><code class="lang-graphql">{
  getFruit {
    name
    sweetness
  }
}
</code></pre>
<p>The response:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"data"</span>: {
    <span class="hljs-attr">"getFruit"</span>: {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Strawberry"</span>,
      <span class="hljs-attr">"sweetness"</span>: <span class="hljs-number">8</span>
    }
  }
}
</code></pre>
<h3 id="heading-customizing-your-grocery-list">Customizing Your Grocery List</h3>
<p>This is like telling the store manager, "I need <strong>only strawberries</strong> and I want them to have a sweetness level of <strong>8</strong>." You’re being very specific with what you need, and the manager delivers exactly that. In contrast, if you had used a REST API, you might have received a whole fruit basket, with apples and bananas, even though you only wanted one type of fruit.</p>
<hr />
<h2 id="heading-step-4-expanding-queries-with-nested-fields">Step 4: Expanding Queries with Nested Fields</h2>
<p>What if you want to know not only the fruit details but also information about the farm it came from? With GraphQL, you can use <strong>nested fields</strong> to get more detailed information in a single query. You’ll ask for strawberries, and also request details about the farm where they were grown.</p>
<pre><code class="lang-python"><span class="hljs-meta">@strawberry.type</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Farm</span>:</span>
    name: str
    location: str

<span class="hljs-meta">@strawberry.type</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Fruit</span>:</span>
    name: str
    sweetness: int
    farm: Farm

<span class="hljs-meta">@strawberry.type</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Query</span>:</span>
<span class="hljs-meta">    @strawberry.field</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_fruit</span>(<span class="hljs-params">self</span>) -&gt; Fruit:</span>
        <span class="hljs-keyword">return</span> Fruit(
            name=<span class="hljs-string">"Strawberry"</span>,
            sweetness=<span class="hljs-number">8</span>,
            farm=Farm(name=<span class="hljs-string">"Sunny Farms"</span>, location=<span class="hljs-string">"California"</span>)
        )

schema = strawberry.Schema(query=Query)
</code></pre>
<p>Now, when you query for a fruit, you can also request information about the farm:</p>
<pre><code class="lang-graphql">{
  getFruit {
    name
    sweetness
    farm {
      name
      location
    }
  }
}
</code></pre>
<p>The Response:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"data"</span>: {
    <span class="hljs-attr">"getFruit"</span>: {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Strawberry"</span>,
      <span class="hljs-attr">"sweetness"</span>: <span class="hljs-number">8</span>,
      <span class="hljs-attr">"farm"</span>: {
        <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Sunny Farms"</span>,
        <span class="hljs-attr">"location"</span>: <span class="hljs-string">"California"</span>
      }
    }
  }
}
</code></pre>
<h3 id="heading-asking-for-more-details">Asking for More Details</h3>
<p>Imagine you walk up to the store manager and say, “I’d like to know more about these strawberries—where are they from?” The manager provides you with not only the strawberries but also the name of the farm and its location. This <strong>nested query</strong> feature in GraphQL helps you get exactly what you want, without the extra clutter.</p>
<hr />
<h2 id="heading-step-5-mutations-making-changes-to-your-grocery-list">Step 5: Mutations - Making Changes to Your Grocery List</h2>
<p>What if you want to <strong>add a new item</strong> to your cart while shopping? In GraphQL, this is called a <strong>mutation</strong>. Mutations allow you to modify data on the server, such as adding, updating, or deleting records.</p>
<p>Here’s an example of adding a new fruit using a mutation:</p>
<pre><code class="lang-python"><span class="hljs-meta">@strawberry.type</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Mutation</span>:</span>
<span class="hljs-meta">    @strawberry.mutation</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">add_fruit</span>(<span class="hljs-params">self, name: str, sweetness: int</span>) -&gt; Fruit:</span>
        <span class="hljs-keyword">return</span> Fruit(name=name, sweetness=sweetness)

schema = strawberry.Schema(query=Query, mutation=Mutation)
</code></pre>
<p>To execute the mutation:</p>
<pre><code class="lang-graphql"><span class="hljs-keyword">mutation</span> {
  addFruit(<span class="hljs-symbol">name:</span> <span class="hljs-string">"Blueberry"</span>, <span class="hljs-symbol">sweetness:</span> <span class="hljs-number">6</span>) {
    name
    sweetness
  }
}
</code></pre>
<p>The Response:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"data"</span>: {
    <span class="hljs-attr">"addFruit"</span>: {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Blueberry"</span>,
      <span class="hljs-attr">"sweetness"</span>: <span class="hljs-number">6</span>
    }
  }
}
</code></pre>
<h3 id="heading-changing-your-grocery-list-on-the-fly">Changing Your Grocery List on the Fly</h3>
<p>This is like telling the store manager, “Hey, I want to <strong>add some blueberries</strong> to my cart.” The manager doesn’t hesitate—they promptly update your cart with the new item. Similarly, mutations allow you to adjust data in real-time, adding or changing items as needed.</p>
<hr />
<h2 id="heading-step-6-real-time-updates-with-subscriptions">Step 6: Real-Time Updates with Subscriptions</h2>
<p>Let’s say you’re waiting for fresh strawberries to be restocked at the store. Wouldn’t it be great if you could get notified the moment new strawberries are available? This is where <strong>GraphQL subscriptions</strong> come in handy. Subscriptions allow you to listen for real-time updates from the server.</p>
<p>In Strawberry, setting up a subscription is easy:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> asyncio

<span class="hljs-meta">@strawberry.type</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Subscription</span>:</span>
<span class="hljs-meta">    @strawberry.subscription</span>
    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">new_fruit</span>(<span class="hljs-params">self</span>) -&gt; Fruit:</span>
        <span class="hljs-keyword">await</span> asyncio.sleep(<span class="hljs-number">1</span>)
        <span class="hljs-keyword">return</span> Fruit(name=<span class="hljs-string">"Strawberry"</span>, sweetness=<span class="hljs-number">9</span>)

schema = strawberry.Schema(query=Query, subscription=Subscription)
</code></pre>
<p>You can subscribe to this real-time data by executing a GraphQL subscription query:</p>
<pre><code class="lang-graphql"><span class="hljs-keyword">subscription</span> {
  newFruit {
    name
    sweetness
  }
}
</code></pre>
<p>This setup will notify you when a new fruit is added or updated.</p>
<h3 id="heading-waiting-for-fresh-stock">Waiting for Fresh Stock</h3>
<p>Imagine you’re waiting for the latest batch of <strong>fresh strawberries</strong> to arrive. Instead of constantly checking the produce section, the store manager promises to notify you the moment they’re available. Similarly, with GraphQL subscriptions, you can wait for real-time updates and be notified immediately when changes occur.</p>
<hr />
<h2 id="heading-step-7-integrating-strawberry-with-fastapi-a-seamless-checkout">Step 7: Integrating Strawberry with FastAPI - A Seamless Checkout</h2>
<p>What good is a grocery trip without a seamless checkout? <strong>FastAPI</strong> is like the lightning-fast cashier, ensuring that your requests are processed with minimal wait time. Integrating Strawberry with FastAPI is incredibly simple, and it combines the power of GraphQL with the speed and efficiency of FastAPI.</p>
<p>Here’s how you can set it up:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> strawberry
<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI
<span class="hljs-keyword">from</span> strawberry.fastapi <span class="hljs-keyword">import</span> GraphQLRouter

<span class="hljs-meta">@strawberry.type</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Query</span>:</span>
<span class="hljs-meta">    @strawberry.field</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_fruit</span>(<span class="hljs-params">self</span>) -&gt; str:</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Strawberry"</span>

schema = strawberry.Schema(query=Query)
graphql_app = GraphQLRouter(schema)

app = FastAPI()
app.include_router(graphql_app, prefix=<span class="hljs-string">"/graphql"</span>)
</code></pre>
<h3 id="heading-smooth-checkout">Smooth Checkout</h3>
<p>At the end of your grocery run, you want to ensure the checkout is fast and painless. FastAPI, in this analogy, is the cashier who scans your items with lightning speed, ensuring that you get in and out efficiently. Strawberry, integrated with FastAPI, ensures your data requests are processed just as quickly and smoothly.</p>
<h2 id="heading-conclusion-the-perfect-grocery-store-experience-with-strawberry-and-graphql">Conclusion: The Perfect Grocery Store Experience with Strawberry and GraphQL</h2>
<p>By the time you walk out of the store, you're carrying the <strong>exact items you wanted</strong>, nothing more, nothing less. And this is the magic of <strong>Python Strawberry</strong>—it gives you precision, flexibility, and control over your API requests, ensuring efficient data handling every step of the way.</p>
<p>From querying to mutations, real-time subscriptions to error handling, Strawberry simplifies the complex world of GraphQL with elegance. And when paired with FastAPI, it’s like having the ultimate grocery store experience—efficient, flexible, and always responsive to your needs.</p>
<p>So, the next time you’re thinking about API design, imagine yourself in that grocery store, requesting only what you need, when you need it.</p>
]]></content:encoded></item><item><title><![CDATA[Python Beanie and FastAPI: Building a Data Empire]]></title><description><![CDATA[Imagine you’re the proud ruler of a kingdom. Not just any kingdom, though—a data kingdom where every brick in the castle represents a piece of information, and your citizens (users) are always hungry for efficient services. As the monarch of this emp...]]></description><link>https://tech-talk.getwithashish.yahodu.info/python-beanie-and-fastapi-building-a-data-empire</link><guid isPermaLink="true">https://tech-talk.getwithashish.yahodu.info/python-beanie-and-fastapi-building-a-data-empire</guid><category><![CDATA[beanie]]></category><category><![CDATA[Python]]></category><category><![CDATA[FastAPI]]></category><category><![CDATA[ODM]]></category><category><![CDATA[MongoDB]]></category><category><![CDATA[pydantic]]></category><category><![CDATA[motor]]></category><category><![CDATA[uvicorn]]></category><dc:creator><![CDATA[Ashish Sam T George]]></dc:creator><pubDate>Tue, 13 Aug 2024 18:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728366530013/f1c780a7-2900-4d64-be8f-b17543f299dc.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine you’re the proud ruler of a kingdom. Not just any kingdom, though—a <strong>data kingdom</strong> where every brick in the castle represents a piece of information, and your citizens (users) are always hungry for efficient services. As the monarch of this empire, you need a way to handle all your resources (data), communicate with your citizens, and build systems that grow as your kingdom expands. This is where <strong>Python Beanie</strong> and <strong>FastAPI</strong> come in, turning your modest village into a thriving metropolis.</p>
<h3 id="heading-the-kings-vision-why-choose-beanie">The King’s Vision: Why Choose Beanie?</h3>
<p>Every great kingdom needs a clear vision. You wouldn’t want your kingdom’s libraries (databases) to be cluttered, your roads (data flow) blocked, or your citizens waiting in line for ages (latency). This is where <strong>Beanie</strong> shines. Beanie is your kingdom’s <strong>wise advisor</strong>, managing data effectively, without the headaches of manual bookkeeping (CRUD operations).</p>
<p>Why Beanie?</p>
<ul>
<li><p><strong>Asynchronous</strong>: Like a team of knights who can joust, guard, and entertain the crowd all at once, Beanie handles multiple database operations simultaneously without slowing down.</p>
</li>
<li><p><strong>Pydantic-powered models</strong>: Beanie ensures that the data entering the kingdom follows the correct protocols (data validation), so no one sneaks in with invalid passports (bad data).</p>
</li>
<li><p><strong>Schema migrations</strong>: Need to add a new wing to your castle? No problem! Beanie allows you to expand your data model seamlessly without disturbing the foundation.</p>
</li>
</ul>
<p>Now that we know what Beanie can do for your kingdom, let’s start building!</p>
<h3 id="heading-step-1-building-the-castles-foundation-setting-up-beanie-and-fastapi">Step 1: Building the Castle’s Foundation (Setting Up Beanie and FastAPI)</h3>
<p>No kingdom can function without a castle. In our case, the <strong>castle</strong> is your backend—built using Beanie, MongoDB, and FastAPI. But before we lay the first brick, we need to install the right tools to ensure our kingdom runs like a well-oiled machine.</p>
<pre><code class="lang-bash">pip install fastapi beanie motor uvicorn
</code></pre>
<ul>
<li><p><strong>FastAPI</strong> is the front gate of your castle, allowing citizens to interact with your kingdom through API endpoints.</p>
</li>
<li><p><strong>Beanie</strong> is the architect, keeping the data organized and accessible.</p>
</li>
<li><p><strong>Motor</strong> is the messenger, ensuring communication between your kingdom (app) and MongoDB.</p>
</li>
<li><p><strong>Uvicorn</strong> is the herald, broadcasting your kingdom’s greatness to the world (by running the development server).</p>
</li>
</ul>
<h3 id="heading-step-2-drafting-the-blueprints-modeling-with-beanie">Step 2: Drafting the Blueprints (Modeling with Beanie)</h3>
<p>Before you start constructing new buildings in your kingdom, you need blueprints for each structure. These blueprints will ensure that all your houses (data documents) are built correctly and are easy to maintain.</p>
<p>In Beanie, blueprints are created using <strong>Pydantic models</strong>. Let’s design the <strong>citizens</strong> of your kingdom (user profiles):</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> beanie <span class="hljs-keyword">import</span> Document, init_beanie
<span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseModel
<span class="hljs-keyword">from</span> motor.motor_asyncio <span class="hljs-keyword">import</span> AsyncIOMotorClient
<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI
<span class="hljs-keyword">import</span> asyncio

<span class="hljs-comment"># Define a Document model with Beanie</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Citizen</span>(<span class="hljs-params">Document</span>):</span>
    name: str
    profession: str
    loyalty_points: int = <span class="hljs-number">0</span>  <span class="hljs-comment"># Every citizen starts out loyal... for now.</span>

    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Settings</span>:</span>
        collection = <span class="hljs-string">"citizens"</span>

<span class="hljs-comment"># FastAPI app initialization</span>
app = FastAPI()

<span class="hljs-comment"># Database connection and Beanie initialization</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">init_db</span>():</span>
    client = AsyncIOMotorClient(<span class="hljs-string">"mongodb://localhost:27017"</span>)
    database = client[<span class="hljs-string">"kingdom_db"</span>]
    <span class="hljs-keyword">await</span> init_beanie(database, document_models=[Citizen])

<span class="hljs-meta">@app.on_event("startup")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">on_startup</span>():</span>
    <span class="hljs-keyword">await</span> init_db()
</code></pre>
<h4 id="heading-breaking-it-down">Breaking It Down:</h4>
<ul>
<li><p><strong>Citizen</strong> is your user profile model—a blueprint for each citizen in your kingdom. It contains attributes like <code>name</code>, <code>profession</code>, and <code>loyalty_points</code>.</p>
</li>
<li><p>The <strong>Settings</strong> class specifies which collection these citizens will be stored in (<code>citizens</code>). Think of it as deciding whether to put your citizens in the town square or the royal archives.</p>
</li>
<li><p><strong>init_db</strong> is the magic that connects your kingdom (app) to MongoDB, ensuring all data is organized neatly.</p>
</li>
</ul>
<h3 id="heading-step-3-making-royal-decrees-creating-fastapi-endpoints">Step 3: Making Royal Decrees (Creating FastAPI Endpoints)</h3>
<p>Every monarch needs a way to communicate with their people. In this case, your royal decrees are <strong>API endpoints</strong> that allow users to interact with the kingdom.</p>
<p>Let’s create a decree for adding new citizens to the kingdom and checking on their status:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Endpoint to create a new citizen</span>
<span class="hljs-meta">@app.post("/citizen")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_citizen</span>(<span class="hljs-params">citizen: Citizen</span>):</span>
    <span class="hljs-keyword">await</span> citizen.insert()
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"message"</span>: <span class="hljs-string">"Citizen has been added to the kingdom!"</span>}

<span class="hljs-comment"># Endpoint to get a citizen’s information</span>
<span class="hljs-meta">@app.get("/citizen/{name}")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_citizen</span>(<span class="hljs-params">name: str</span>):</span>
    citizen = <span class="hljs-keyword">await</span> Citizen.find_one(Citizen.name == name)
    <span class="hljs-keyword">if</span> citizen:
        <span class="hljs-keyword">return</span> citizen
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"error"</span>: <span class="hljs-string">"Citizen not found in the archives!"</span>}
</code></pre>
<h4 id="heading-the-royal-analogy">The Royal Analogy:</h4>
<ul>
<li><p><strong>create_citizen</strong> is like opening the castle gates and letting a new citizen into the kingdom. They are added to the royal records (database).</p>
</li>
<li><p><strong>get_citizen</strong> is like sending a scribe to check on a specific citizen’s details in the royal archives. If they exist, the scribe brings back their information; if not, the scribe returns empty-handed.</p>
</li>
</ul>
<h3 id="heading-step-4-running-the-kingdom-smoothly-handling-complex-queries">Step 4: Running the Kingdom Smoothly (Handling Complex Queries)</h3>
<p>As your kingdom grows, you’ll need a more efficient way to manage resources and citizens. You don’t want to check each one manually, right? Let’s automate some of the royal duties by adding an endpoint to retrieve all loyal citizens.</p>
<pre><code class="lang-python"><span class="hljs-meta">@app.get("/citizens/loyal")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_loyal_citizens</span>():</span>
    loyal_citizens = <span class="hljs-keyword">await</span> Citizen.find(Citizen.loyalty_points &gt;= <span class="hljs-number">50</span>).to_list()
    <span class="hljs-keyword">return</span> loyal_citizens
</code></pre>
<h4 id="heading-the-royal-analogy-1">The Royal Analogy:</h4>
<p>Imagine you’re organizing a grand feast, but you only want to invite citizens who’ve been particularly loyal. Instead of checking every name in the archives, you send out an automated decree to retrieve only those who have <code>loyalty_points</code> above a certain number. This is your royal banquet guest list.</p>
<h3 id="heading-step-5-expanding-the-castle-schema-migrations">Step 5: Expanding the Castle (Schema Migrations)</h3>
<p>As your kingdom prospers, you might want to add new buildings, like a library or a blacksmith’s forge. Similarly, in Beanie, you’ll eventually need to expand your data models without breaking existing functionality.</p>
<p>Let’s say you want to track each citizen’s favorite tavern. Easy! Just add a new field to your <strong>Citizen</strong> model:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Citizen</span>(<span class="hljs-params">Document</span>):</span>
    name: str
    profession: str
    loyalty_points: int = <span class="hljs-number">0</span>
    favorite_tavern: Optional[str] = <span class="hljs-literal">None</span>

    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Settings</span>:</span>
        collection = <span class="hljs-string">"citizens"</span>
</code></pre>
<h4 id="heading-the-royal-analogy-2">The Royal Analogy:</h4>
<p>This is like adding a new wing to your castle. You don’t need to demolish the whole building—just add the new feature and continue as usual. Beanie handles these “renovations” with grace, ensuring no data is lost during the process.</p>
<h3 id="heading-step-6-royal-predictions">Step 6: Royal Predictions</h3>
<p>As a wise ruler, you want to stay ahead of the game. How can you predict which citizens are most likely to stay loyal? By analyzing past behavior, of course! With Beanie’s querying capabilities, you can predict future trends based on historical data.</p>
<p>Here’s how to fetch the top 5 most loyal citizens:</p>
<pre><code class="lang-python"><span class="hljs-meta">@app.get("/citizens/top_loyal")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">predict_loyalty</span>():</span>
    loyal_citizens = <span class="hljs-keyword">await</span> Citizen.find().sort(<span class="hljs-string">"-loyalty_points"</span>).limit(<span class="hljs-number">5</span>).to_list()
    <span class="hljs-keyword">return</span> loyal_citizens
</code></pre>
<h4 id="heading-the-royal-analogy-3">The Royal Analogy:</h4>
<p>Imagine your kingdom’s knights taking a vote on who’s most likely to defend the castle in a crisis. You can look at past battles (loyalty points) to predict which citizens will rise to the occasion. Similarly, this query pulls the top citizens based on loyalty, giving you a shortlist of who’s most loyal.</p>
<h3 id="heading-step-7-protecting-the-realm-ensuring-data-consistency">Step 7: Protecting the Realm (Ensuring Data Consistency)</h3>
<p>No kingdom is complete without proper defenses! You wouldn’t let just anyone into the castle, would you? Similarly, you need to protect your data with validation and consistency checks to keep things in order.</p>
<p>Here’s how to validate that every new citizen has a proper profession:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Citizen</span>(<span class="hljs-params">Document</span>):</span>
    name: str
    profession: str
    loyalty_points: int = <span class="hljs-number">0</span>
    favorite_tavern: Optional[str] = <span class="hljs-literal">None</span>

<span class="hljs-meta">    @root_validator</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">validate_profession</span>(<span class="hljs-params">cls, values</span>):</span>
        profession = values.get(<span class="hljs-string">"profession"</span>)
        <span class="hljs-keyword">if</span> profession <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> [<span class="hljs-string">"Knight"</span>, <span class="hljs-string">"Mage"</span>, <span class="hljs-string">"Peasant"</span>]:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">"Invalid profession!"</span>)
        <span class="hljs-keyword">return</span> values
</code></pre>
<h4 id="heading-the-royal-analogy-4">The Royal Analogy:</h4>
<p>This is your kingdom’s security guard. Every time someone tries to enter, the guard checks their credentials to ensure they belong. No imposters allowed! The validation ensures every new citizen has a valid profession, keeping the kingdom orderly.</p>
<hr />
<h3 id="heading-conclusion-building-your-data-kingdom-with-beanie-and-fastapi">Conclusion: Building Your Data Kingdom with Beanie and FastAPI</h3>
<p>By now, you've gone from ruling over a small village to commanding a vast, data-driven kingdom—all thanks to the powers of <strong>Beanie</strong> and <strong>FastAPI</strong>. From creating blueprints (data models) to managing citizens (documents) efficiently, you’ve built a kingdom that runs like clockwork.</p>
<p>Just like any great kingdom, your app is scalable, flexible, and ready to grow. Beanie’s asynchronous power means that no matter how many new citizens arrive, you’re always ready to handle them—without breaking a sweat.</p>
<p>As a proud ruler, I’d like to share that I have developed an application that interacts with a fine-tuned model (or any other model) using Python Beanie and FastAPI. You can explore the code and its functionalities by visiting my <a target="_blank" href="https://github.com/getwithashish/AICA">GitHub repository</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Python Tutorial: How to Chat with Gemini AI - Part 1]]></title><description><![CDATA[Introduction
You have probably heard about Gemini AI already. Gemini is a family of generative AI models that can generate content and solve problems. There are different models with their own set of capabilities. We will be focusing on the Gemini 1....]]></description><link>https://tech-talk.getwithashish.yahodu.info/python-tutorial-how-to-chat-with-gemini-ai-part-1</link><guid isPermaLink="true">https://tech-talk.getwithashish.yahodu.info/python-tutorial-how-to-chat-with-gemini-ai-part-1</guid><category><![CDATA[generative ai]]></category><category><![CDATA[gemini]]></category><category><![CDATA[RAG ]]></category><category><![CDATA[Python]]></category><category><![CDATA[PostgreSQL]]></category><category><![CDATA[Docker]]></category><category><![CDATA[AI]]></category><category><![CDATA[Google]]></category><category><![CDATA[Flask Framework]]></category><dc:creator><![CDATA[Ashish Sam T George]]></dc:creator><pubDate>Wed, 17 Jul 2024 13:07:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728132891907/499b7c30-81a0-4e26-afd2-4681e2d40f92.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>You have probably heard about Gemini AI already. Gemini is a family of generative AI models that can generate content and solve problems. There are different models with their own set of capabilities. We will be focusing on the <code>Gemini 1.0 Pro</code> model. Throughout this tutorial, we will be using the name <code>gemini-pro</code>. It is an alias for <code>Gemini 1.0 Pro</code>. To know more about gemini models, <a target="_blank" href="https://ai.google.dev/gemini-api/docs/models/gemini">Refer Here</a>.</p>
<p>We will be creating an AI Chat Bot powered by Gemini AI, with JWT authentication using python.</p>
<p>You can find the complete code on my Github repo: <a target="_blank" href="https://github.com/getwithashish/Converse3.0">Converse3.0</a></p>
<h1 id="heading-tech-stack">Tech Stack</h1>
<ul>
<li><p>Python</p>
</li>
<li><p>Flask</p>
</li>
<li><p>PostgreSQL</p>
</li>
<li><p>Docker</p>
</li>
</ul>
<h1 id="heading-lets-start">Let's Start</h1>
<h2 id="heading-obtain-api-key-to-use-gemini">Obtain API Key to use Gemini</h2>
<ol>
<li><p>Go to <a target="_blank" href="https://aistudio.google.com/">Google AI Studio</a></p>
</li>
<li><p>Login with google account</p>
</li>
<li><p>Create API Key</p>
</li>
</ol>
<h2 id="heading-install-postgresql">Install PostgreSQL</h2>
<p>Download and install postgreSQL, from <a target="_blank" href="https://www.postgresql.org/download/">here</a>. Go with the latest version.</p>
<blockquote>
<p>If you are using linux, make sure that you install libpq-dev</p>
<ul>
<li><p>Linux (with apt package manager)</p>
<pre><code class="lang-bash">  sudo apt-get install libpq-dev
</code></pre>
</li>
</ul>
</blockquote>
<h2 id="heading-install-docker-optional">Install Docker (Optional)</h2>
<p>Installing docker is completely optional. Having the Dockerfile and docker compose setup will help in faster deployment.</p>
<p>You can also spin up a docker container for postgreSQL instead of installing it in your system.</p>
<pre><code class="lang-bash">docker run -d \
  --name gemini_postgres \
  -p 5432:5432 \
  -e POSTGRES_USER=postgres \
  -e POSTGRES_PASSWORD=password\
  -e POSTGRES_DB=postgres\
  postgres:latest
</code></pre>
<p>Postgres now accessible on port 5432.</p>
<h2 id="heading-setup-environment">Setup Environment</h2>
<h3 id="heading-create-virtual-environment">Create Virtual Environment</h3>
<pre><code class="lang-bash">python -m venv venv
</code></pre>
<h3 id="heading-activate-virtual-environment">Activate Virtual Environment</h3>
<ul>
<li><p>Windows (CMD)</p>
<pre><code class="lang-bash">  venv\Scripts\activate
</code></pre>
</li>
<li><p>Mac/Linux</p>
<pre><code class="lang-bash">  <span class="hljs-built_in">source</span> venv/bin/activate
</code></pre>
</li>
</ul>
<h3 id="heading-install-python-packages">Install Python Packages</h3>
<p>With the virtual environment activated, you can install Python packages using <code>pip</code>.</p>
<pre><code class="lang-bash">pip install Flask
pip install Flask-JWT-Extended
pip install Flask-SQLAlchemy
pip install Flask-Cors
</code></pre>
<ul>
<li><p>Flask is a lightweight and flexible web framework for Python</p>
</li>
<li><p>Flask-JWT-Extended - provides support for JSON Web Tokens (JWTs). We will be using JWTs for implementing authentication and authorization in this web application.</p>
</li>
<li><p>Flask-SQLAlchemy - integrates SQLAlchemy with Flask. We will be using SQLAlchemy to interact with PostgreSQL.</p>
</li>
<li><p>Flask-Cors - enables Cross-Origin Resource Sharing (CORS)</p>
</li>
</ul>
<pre><code class="lang-bash">pip install psycopg2
</code></pre>
<ul>
<li>Psycopg2 is a PostgreSQL database adapter</li>
</ul>
<pre><code class="lang-bash">pip install python-decouple
</code></pre>
<ul>
<li>Decouple helps in loading values from .env files</li>
</ul>
<pre><code class="lang-bash">pip install jsonpickle
</code></pre>
<ul>
<li>jsonpickle helps in the conversion of complex python object to JSON and vice-versa. We will use this library to convert and store the chat history.</li>
</ul>
<h4 id="heading-install-python-sdk-for-gemini-api">Install Python SDK for Gemini API</h4>
<pre><code class="lang-bash">pip install google-generativeai
</code></pre>
<h2 id="heading-start-coding">Start Coding</h2>
<h4 id="heading-configure-python-decouple">Configure Python Decouple</h4>
<p>Python decouple automatically picks up <code>.env</code> file. But, since we are also planning to deploy the application into production, we need it to pick up a different <code>.env</code> when in production environment. So, rather than using the default <code>config</code> provided by decouple, we will create a method to return a decouple <code>config</code> which includes our additional logic.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> decouple
<span class="hljs-keyword">from</span> decouple <span class="hljs-keyword">import</span> RepositoryEnv
<span class="hljs-keyword">import</span> pathlib


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DecoupleConfigUtil</span>:</span>

<span class="hljs-meta">    @classmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_env_config</span>(<span class="hljs-params">cls</span>) -&gt; decouple.Config:</span>
        <span class="hljs-string">"""
        Creates and returns a Config object based on the environment setting.
        It uses .env for development and .prod.env for production.
        """</span>

        ENVIRONMENT = os.getenv(<span class="hljs-string">"ENVIRONMENT"</span>, default=<span class="hljs-string">"DEVELOPMENT"</span>)

        env_files = {
            <span class="hljs-string">"DEVELOPMENT"</span>: <span class="hljs-string">".env"</span>,
            <span class="hljs-string">"PRODUCTION"</span>: <span class="hljs-string">".env.prod"</span>,
        }

        app_dir_path = pathlib.Path(__file__).resolve().parent.parent
        env_file_name = env_files.get(ENVIRONMENT, <span class="hljs-string">".env"</span>)
        file_path = app_dir_path / env_file_name

        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> file_path.is_file():
            <span class="hljs-keyword">raise</span> FileNotFoundError(<span class="hljs-string">f"Environment file not found: <span class="hljs-subst">{file_path}</span>"</span>)

        <span class="hljs-keyword">return</span> decouple.Config(RepositoryEnv(file_path))
</code></pre>
<p>We create this class inside <code>utils</code> package. This would check for an environment variable called "ENVIRONMENT" in the system. According to the variable's value, it would decide from which <code>.env</code>, it should take the configurations from.</p>
<blockquote>
<p>In the production environment, it would take values from <code>.env.prod</code>. So, make sure that you have this file when deploying the application to production.</p>
</blockquote>
<p>You can add these values to the <code>.env</code> file:</p>
<pre><code class="lang-bash">APP_SECRET_KEY=this_is_my_secret_key
JWT_SECRET_KEY=this_is_my_secret_jwt_secret_key

CORS_ORIGINS=http://localhost:5173,http://localhost:4173

DATABASE_URI=postgresql://postgres:password@localhost:5432/postgres

HOST=0.0.0.0
PORT=8000

GOOGLE_API_KEY=AIzaSyDTzAF3jNsbktskJLC_EIBz0_QKPFdnHds
</code></pre>
<h4 id="heading-create-file-for-storing-static-messages">Create file for storing static messages</h4>
<p>We create a <code>messages.py</code> file to store all the static messages we need.</p>
<pre><code class="lang-python">USERNAME_REQUIRED = <span class="hljs-string">"Username is required"</span>
PASSWORD_REQUIRED = <span class="hljs-string">"Password is required"</span>
USERNAME_EXISTS = <span class="hljs-string">"Username already exists"</span>
USER_REGISTRATION_SUCCESSFUL = <span class="hljs-string">"User has been successfully registered"</span>

INVALID_USERNAME_PASSWORD = <span class="hljs-string">"Username or Password is invalid"</span>

INVALID_PROMPT = <span class="hljs-string">"Provided prompt is invalid"</span>

CHAT_HISTORY_LIST_RETRIEVED = <span class="hljs-string">"Chat history list has been successfully retrieved"</span>
CHAT_HISTORY_RETRIEVED = <span class="hljs-string">"Chat history has been successfully retrieved"</span>
CHAT_HISTORY_UNAVAILABLE = <span class="hljs-string">"Chat history is not available"</span>

CHAT_RESPONSE_NOT_SAFE = <span class="hljs-string">"Chat response is not safe to be viewed publicly"</span>
CHAT_NOT_FOUND = <span class="hljs-string">"Chat not found. Please provide correct details"</span>
</code></pre>
<h3 id="heading-create-models">Create Models</h3>
<p>We create models using SQLAlchemy and place these models inside the <code>models</code> package.</p>
<h5 id="heading-modelspy">models.py</h5>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask_sqlalchemy <span class="hljs-keyword">import</span> SQLAlchemy


db = SQLAlchemy()


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">bulk_save_objects</span>(<span class="hljs-params">objects</span>):</span>
    db.session.bulk_save_objects(objects)
    db.session.commit()


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">delete_objects</span>(<span class="hljs-params">objects</span>):</span>
    <span class="hljs-keyword">for</span> obj <span class="hljs-keyword">in</span> objects:
        db.session.delete(obj)

    db.session.commit()
</code></pre>
<p>Here, we initialize an instance of SQLAlchemy. This instance will be used to create further models. We also define two methods to bulk save objects and bulk delete objects.</p>
<h5 id="heading-userpy">user.py</h5>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> models.models <span class="hljs-keyword">import</span> db
<span class="hljs-keyword">from</span> models.document <span class="hljs-keyword">import</span> Document
<span class="hljs-keyword">from</span> werkzeug.security <span class="hljs-keyword">import</span> generate_password_hash, check_password_hash


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span>(<span class="hljs-params">db.Model</span>):</span>
    id = db.Column(db.Integer, primary_key=<span class="hljs-literal">True</span>)
    username = db.Column(db.String(<span class="hljs-number">80</span>), unique=<span class="hljs-literal">True</span>, nullable=<span class="hljs-literal">False</span>)
    password_hash = db.Column(db.String(<span class="hljs-number">256</span>), nullable=<span class="hljs-literal">False</span>)

    documents = db.relationship(Document, back_populates=<span class="hljs-string">"user"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__repr__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">"&lt;User %r&gt;"</span> % self.username

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">set_password</span>(<span class="hljs-params">self, password</span>):</span>
        self.password_hash = generate_password_hash(password)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">check_password</span>(<span class="hljs-params">self, password</span>):</span>
        <span class="hljs-keyword">return</span> check_password_hash(self.password_hash, password)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">save</span>(<span class="hljs-params">self</span>):</span>
        db.session.add(self)
        db.session.commit()
</code></pre>
<p>We specify relationship between <code>User</code> model and <code>Document</code> model.</p>
<h5 id="heading-documentpy">document.py</h5>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> models.models <span class="hljs-keyword">import</span> db


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Document</span>(<span class="hljs-params">db.Model</span>):</span>
    id = db.Column(db.Integer, primary_key=<span class="hljs-literal">True</span>)
    document_name = db.Column(db.String(<span class="hljs-number">80</span>), nullable=<span class="hljs-literal">False</span>)
    user_id = db.Column(db.Integer, db.ForeignKey(<span class="hljs-string">"user.id"</span>), nullable=<span class="hljs-literal">False</span>)

    user = db.relationship(<span class="hljs-string">"User"</span>, back_populates=<span class="hljs-string">"documents"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__repr__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">"&lt;UploadedDocuments %r&gt;"</span> % self.document_name

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">save</span>(<span class="hljs-params">self</span>):</span>
        db.session.add(self)
        db.session.commit()
</code></pre>
<p>The <code>Document</code> model has a one-one relationship with the <code>User</code> model.</p>
<h5 id="heading-normalchathistorypy">normal_chat_history.py</h5>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> models.models <span class="hljs-keyword">import</span> db
<span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NormalChatHistory</span>(<span class="hljs-params">db.Model</span>):</span>
    id = db.Column(db.Integer, primary_key=<span class="hljs-literal">True</span>)
    chat_history = db.Column(db.JSON)
    started_at = db.Column(db.DateTime, default=datetime.utcnow)
    user_id = db.Column(db.Integer, db.ForeignKey(<span class="hljs-string">"user.id"</span>), nullable=<span class="hljs-literal">False</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__repr__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">"&lt;NormalChatHistory %r&gt;"</span> % self.id + <span class="hljs-string">"-"</span> + self.user_id

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">save</span>(<span class="hljs-params">self</span>):</span>
        db.session.add(self)
        db.session.commit()
</code></pre>
<p>The chat history is stored in JSON format. <code>NormalChatHistory</code> model has a one-one relationship with the <code>User</code> model.</p>
<h3 id="heading-create-auth-controllers">Create Auth Controllers</h3>
<h4 id="heading-user-registration">User Registration</h4>
<p>We place the controllers inside the <code>controllers</code> package.</p>
<p><strong>register.py</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> request

<span class="hljs-keyword">from</span> utils.common_response <span class="hljs-keyword">import</span> CommonResponse
<span class="hljs-keyword">from</span> service.user_registration <span class="hljs-keyword">import</span> UserRegistration


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">register</span>():</span>
    username = request.json.get(<span class="hljs-string">"username"</span>, <span class="hljs-literal">None</span>)
    password = request.json.get(<span class="hljs-string">"password"</span>, <span class="hljs-literal">None</span>)

    user_registration = UserRegistration()
    message, status = user_registration.register(username, password)

    <span class="hljs-keyword">return</span> CommonResponse(message, status).format_response()
</code></pre>
<p>We also create custom response files to structure the response as we want. Create a file called <code>common_response.py</code> in <code>utils</code> package.</p>
<p><strong>common_response.py</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> jsonify


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CommonResponse</span>:</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, message, status, data=None</span>) -&gt; <span class="hljs-keyword">None</span>:</span>
        self.message = message
        self.status = status
        self.data = data

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">format_response</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> (
            jsonify({<span class="hljs-string">"message"</span>: self.message, <span class="hljs-string">"data"</span>: self.data}),
            self.status,
        )
</code></pre>
<h4 id="heading-user-login">User Login</h4>
<p>We place it in the <code>controllers</code> package.</p>
<p><strong>login.py</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> request

<span class="hljs-keyword">from</span> utils.login_response <span class="hljs-keyword">import</span> LoginResponse
<span class="hljs-keyword">from</span> utils.common_response <span class="hljs-keyword">import</span> CommonResponse
<span class="hljs-keyword">from</span> service.user_login <span class="hljs-keyword">import</span> UserLogin


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">login</span>():</span>
    username = request.json.get(<span class="hljs-string">"username"</span>, <span class="hljs-literal">None</span>)
    password = request.json.get(<span class="hljs-string">"password"</span>, <span class="hljs-literal">None</span>)

    user_login = UserLogin()
    response, status = user_login.login(username, password)
    <span class="hljs-keyword">if</span> status != <span class="hljs-number">200</span>:
        <span class="hljs-keyword">return</span> CommonResponse(response, status)
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">return</span> LoginResponse(response, status)
</code></pre>
<p>Create a file called <code>login_response.py</code> in <code>utils</code> package.</p>
<p><strong>login_response.py</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> jsonify


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LoginResponse</span>:</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, access_token, status</span>) -&gt; <span class="hljs-keyword">None</span>:</span>
        self.access_token = access_token
        self.status = status

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">format_response</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> (
            jsonify({<span class="hljs-string">"access_token"</span>: self.access_token}),
            self.status,
        )
</code></pre>
<h3 id="heading-create-auth-services">Create Auth Services</h3>
<h4 id="heading-user-registration-1">User Registration</h4>
<p>We place the service classes inside the <code>services</code> package. This file will contain all the logic required for registering a new user.</p>
<p><strong>user_registration.py</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> models.user <span class="hljs-keyword">import</span> User
<span class="hljs-keyword">from</span> messages <span class="hljs-keyword">import</span> (
    PASSWORD_REQUIRED,
    USER_REGISTRATION_SUCCESSFUL,
    USERNAME_EXISTS,
    USERNAME_REQUIRED,
)


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserRegistration</span>:</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">register</span>(<span class="hljs-params">self, username, password</span>):</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> username:
            message = USERNAME_REQUIRED
            status = <span class="hljs-number">400</span>
        <span class="hljs-keyword">elif</span> <span class="hljs-keyword">not</span> password:
            message = PASSWORD_REQUIRED
            status = <span class="hljs-number">400</span>
        <span class="hljs-keyword">elif</span> User.query.filter_by(username=username).first():
            message = USERNAME_EXISTS
            status = <span class="hljs-number">400</span>
        <span class="hljs-keyword">else</span>:
            new_user = User(username=username)
            new_user.set_password(password)
            new_user.save()
            message = USER_REGISTRATION_SUCCESSFUL
            status = <span class="hljs-number">201</span>

        <span class="hljs-keyword">return</span> message, status
</code></pre>
<h4 id="heading-user-login-1">User Login</h4>
<p>This file will contain all the logic required for successfully logging in a valid user.</p>
<p><strong>user_login.py</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask_jwt_extended <span class="hljs-keyword">import</span> create_access_token

<span class="hljs-keyword">from</span> messages <span class="hljs-keyword">import</span> INVALID_USERNAME_PASSWORD
<span class="hljs-keyword">from</span> models.user <span class="hljs-keyword">import</span> User


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserLogin</span>:</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">login</span>(<span class="hljs-params">self, username, password</span>):</span>
        user = User.query.filter_by(username=username).first()
        <span class="hljs-keyword">if</span> user <span class="hljs-keyword">and</span> user.check_password(password):
            additional_claims = {<span class="hljs-string">"user_id"</span>: user.id}
            access_token = create_access_token(
                identity=username, additional_claims=additional_claims
            )
            <span class="hljs-keyword">return</span> access_token, <span class="hljs-number">200</span>
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">return</span> INVALID_USERNAME_PASSWORD, <span class="hljs-number">400</span>
</code></pre>
<p>Add these lines to <code>main.py</code>:</p>
<p><strong>main.py</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask
<span class="hljs-keyword">from</span> flask_cors <span class="hljs-keyword">import</span> CORS
<span class="hljs-keyword">from</span> flask_jwt_extended <span class="hljs-keyword">import</span> JWTManager

<span class="hljs-keyword">from</span> messages <span class="hljs-keyword">import</span> CHAT_NOT_FOUND, CHAT_RESPONSE_NOT_SAFE
<span class="hljs-keyword">from</span> exceptions <span class="hljs-keyword">import</span> ChatNotFoundException, SafetyException
<span class="hljs-keyword">from</span> models.models <span class="hljs-keyword">import</span> db
<span class="hljs-keyword">from</span> utils.decouple_config_util <span class="hljs-keyword">import</span> DecoupleConfigUtil
<span class="hljs-keyword">from</span> utils.common_response <span class="hljs-keyword">import</span> CommonResponse
<span class="hljs-keyword">from</span> controllers.register <span class="hljs-keyword">import</span> register
<span class="hljs-keyword">from</span> controllers.login <span class="hljs-keyword">import</span> login
<span class="hljs-keyword">from</span> controllers.normal_ai_chat <span class="hljs-keyword">import</span> (
    NormalAiChat,
    NormalAiChatHistory,
    NormalAiChatHistoryList,
    deleteNormalAiChatHistory,
)


config = DecoupleConfigUtil.get_env_config()

app = Flask(__name__)
app.secret_key = config(<span class="hljs-string">"APP_SECRET_KEY"</span>)

app.config[<span class="hljs-string">"SQLALCHEMY_DATABASE_URI"</span>] = config(<span class="hljs-string">"DATABASE_URI"</span>)
app.config[<span class="hljs-string">"JWT_SECRET_KEY"</span>] = config(<span class="hljs-string">"JWT_SECRET_KEY"</span>)

db.init_app(app)
jwt = JWTManager(app)

CORS(
    app,
    origins=config(
        <span class="hljs-string">"CORS_ORIGINS"</span>, cast=<span class="hljs-keyword">lambda</span> v: [item.strip() <span class="hljs-keyword">for</span> item <span class="hljs-keyword">in</span> v.split(<span class="hljs-string">","</span>)]
    ),
)

<span class="hljs-keyword">with</span> app.app_context():
    db.create_all()


app.route(<span class="hljs-string">"/register"</span>, methods=[<span class="hljs-string">"POST"</span>])(register)
app.route(<span class="hljs-string">"/login"</span>, methods=[<span class="hljs-string">"POST"</span>])(login)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    app.run(host=config(<span class="hljs-string">"HOST"</span>), port=config(<span class="hljs-string">"PORT"</span>))
</code></pre>
<ul>
<li><p>Initialize the <code>Flask</code> application</p>
</li>
<li><p>Initialize the DB</p>
</li>
<li><p>Specify the API routes</p>
</li>
</ul>
<h3 id="heading-chat-with-ai">Chat with AI</h3>
<h4 id="heading-custom-exceptions">Custom Exceptions</h4>
<p>Create custom exceptions and place it in <code>exceptions.py</code>.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SafetyException</span>(<span class="hljs-params">Exception</span>):</span>
    <span class="hljs-keyword">pass</span>


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ChatNotFoundException</span>(<span class="hljs-params">Exception</span>):</span>
    <span class="hljs-keyword">pass</span>
</code></pre>
<p>We create an abstract class in <code>client/ai_models</code>. This abstract class will serve as the parent class for the other AI model classes.</p>
<p><strong>ai_model.py</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> abc <span class="hljs-keyword">import</span> ABC, abstractmethod


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AIModel</span>(<span class="hljs-params">ABC</span>):</span>

<span class="hljs-meta">    @abstractmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">chat</span>(<span class="hljs-params">chat_history</span>):</span>
        <span class="hljs-keyword">pass</span>

<span class="hljs-meta">    @abstractmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_chat_history</span>():</span>
        <span class="hljs-keyword">pass</span>
</code></pre>
<h4 id="heading-gemini-ai-model">Gemini AI Model</h4>
<p>Since we are using Gemini as our AI model, we will put all the Gemini related files inside <code>client/ai_models/gemini</code>.</p>
<p>First, lets setup the required configurations in <code>config.py</code>.</p>
<p><strong>config.py</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> utils.decouple_config_util <span class="hljs-keyword">import</span> DecoupleConfigUtil


config = DecoupleConfigUtil.get_env_config()


GOOGLE_API_KEY = config(<span class="hljs-string">"GOOGLE_API_KEY"</span>)
GEMINI_MODEL_NAME = config(<span class="hljs-string">"GEMINI_MODEL_NAME"</span>)
</code></pre>
<p><strong>gemini_ai_model.py</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> google.generativeai <span class="hljs-keyword">as</span> genai
<span class="hljs-keyword">from</span> google.generativeai.types.generation_types <span class="hljs-keyword">import</span> StopCandidateException
<span class="hljs-keyword">import</span> logging

<span class="hljs-keyword">from</span> client.ai_models.ai_model <span class="hljs-keyword">import</span> AIModel
<span class="hljs-keyword">from</span> client.ai_models.gemini.config <span class="hljs-keyword">import</span> GEMINI_MODEL_NAME, GOOGLE_API_KEY
<span class="hljs-keyword">from</span> exceptions <span class="hljs-keyword">import</span> SafetyException


logging.basicConfig(
    filename=<span class="hljs-string">"app.log"</span>,
    level=logging.DEBUG,
    format=<span class="hljs-string">"%(asctime)s %(levelname)s %(name)s %(threadName)s : %(message)s"</span>,
)

logger = logging.getLogger(__name__)


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GeminiAIModel</span>(<span class="hljs-params">AIModel</span>):</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, model_name=GEMINI_MODEL_NAME</span>) -&gt; <span class="hljs-keyword">None</span>:</span>
        genai.configure(api_key=GOOGLE_API_KEY, transport=<span class="hljs-string">"rest"</span>)
        self.model = genai.GenerativeModel(model_name)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">chat</span>(<span class="hljs-params">self, prompt, chat_history</span>):</span>
        <span class="hljs-keyword">try</span>:
            self.chat = self.model.start_chat(history=chat_history)
            response = self.chat.send_message(prompt)
            content_parts = response.candidates[<span class="hljs-number">0</span>].content.parts[<span class="hljs-number">0</span>]
            <span class="hljs-keyword">return</span> content_parts.text
        <span class="hljs-keyword">except</span> StopCandidateException <span class="hljs-keyword">as</span> sce:
            logger.error(sce)
            <span class="hljs-keyword">raise</span> SafetyException()

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_chat_history</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">if</span> self.chat:
            <span class="hljs-keyword">return</span> self.chat.history
        <span class="hljs-keyword">return</span> []
</code></pre>
<ul>
<li><p>Initializes the Google AI python SDK with the necessary values to interact with Gemini</p>
</li>
<li><p><code>chat</code> method to chat with the AI model</p>
</li>
<li><p>Raises the custom exception <code>SafetyException</code> if the <code>StopCandidateException</code> is caught</p>
</li>
<li><p><code>get_chat_history</code> method to get the entire history of that chat</p>
</li>
</ul>
<h4 id="heading-create-ai-chat-controllers">Create AI Chat Controllers</h4>
<p>In the controllers package, create file called <code>normal_ai_chat.py</code>.</p>
<p><strong>normal_ai_chat.py</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> request
<span class="hljs-keyword">from</span> flask_jwt_extended <span class="hljs-keyword">import</span> get_jwt, jwt_required

<span class="hljs-keyword">from</span> client.ai_models.gemini.gemini_ai_model <span class="hljs-keyword">import</span> GeminiAIModel
<span class="hljs-keyword">from</span> utils.common_response <span class="hljs-keyword">import</span> CommonResponse
<span class="hljs-keyword">from</span> messages <span class="hljs-keyword">import</span> (
    CHAT_HISTORY_LIST_RETRIEVED,
    CHAT_HISTORY_RETRIEVED,
    CHAT_HISTORY_UNAVAILABLE,
    INVALID_PROMPT,
)
<span class="hljs-keyword">from</span> service.normal_ai_chatter <span class="hljs-keyword">import</span> NormalAiChatter
<span class="hljs-keyword">from</span> utils.chat_response <span class="hljs-keyword">import</span> ChatResponse


<span class="hljs-meta">@jwt_required()</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">NormalAiChat</span>():</span>
    prompt = request.json.get(<span class="hljs-string">"prompt"</span>, <span class="hljs-literal">None</span>)
    chat_id = request.json.get(<span class="hljs-string">"chat_id"</span>, <span class="hljs-literal">None</span>)
    user_id = get_jwt().get(<span class="hljs-string">"user_id"</span>, <span class="hljs-literal">None</span>)

    <span class="hljs-keyword">if</span> prompt:
        gemini_ai_model = GeminiAIModel()
        normal_chatter = NormalAiChatter(gemini_ai_model)
        ai_response, chat_id = normal_chatter.chat(prompt, chat_id, user_id)
        status = <span class="hljs-number">200</span>
    <span class="hljs-keyword">else</span>:
        ai_response = INVALID_PROMPT
        status = <span class="hljs-number">400</span>

    <span class="hljs-keyword">return</span> ChatResponse(ai_response, chat_id, status).format_response()
</code></pre>
<ul>
<li>Calls the Gemini model with the user prompt</li>
</ul>
<pre><code class="lang-python"><span class="hljs-meta">@jwt_required()</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">NormalAiChatHistoryList</span>():</span>
    user_id = get_jwt().get(<span class="hljs-string">"user_id"</span>, <span class="hljs-literal">None</span>)

    normal_chatter = NormalAiChatter()
    chat_list = normal_chatter.get_chat_history_list(user_id)
    status = <span class="hljs-number">200</span>

    <span class="hljs-keyword">return</span> CommonResponse(CHAT_HISTORY_LIST_RETRIEVED, status, chat_list).format_response()
</code></pre>
<ul>
<li>Retrieves the list of chats the user has made with the model</li>
</ul>
<pre><code class="lang-python"><span class="hljs-meta">@jwt_required()</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">NormalAiChatHistory</span>():</span>
    user_id = get_jwt().get(<span class="hljs-string">"user_id"</span>, <span class="hljs-literal">None</span>)
    chat_id = request.args.get(<span class="hljs-string">"chat_id"</span>, <span class="hljs-literal">None</span>)

    normal_chatter = NormalAiChatter()
    chat_history = normal_chatter.get_chat_history(user_id, chat_id)
    <span class="hljs-keyword">if</span> chat_history:
        status = <span class="hljs-number">200</span>
        <span class="hljs-keyword">return</span> CommonResponse(CHAT_HISTORY_RETRIEVED, status, chat_history).format_response()

    status = <span class="hljs-number">404</span>
    <span class="hljs-keyword">return</span> CommonResponse(CHAT_HISTORY_UNAVAILABLE, status, <span class="hljs-literal">None</span>).format_response()
</code></pre>
<ul>
<li>Retrieves all the contents of a particular chat</li>
</ul>
<pre><code class="lang-python"><span class="hljs-meta">@jwt_required()</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">deleteNormalAiChatHistory</span>():</span>
    user_id = get_jwt().get(<span class="hljs-string">"user_id"</span>, <span class="hljs-literal">None</span>)
    chat_id = request.args.get(<span class="hljs-string">"chat_id"</span>, <span class="hljs-literal">None</span>)

    normal_chatter = NormalAiChatter()
    normal_chatter.delete_chat_history(user_id, chat_id)

    status = <span class="hljs-number">204</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">""</span>, status
</code></pre>
<ul>
<li>Delete chats based on <code>user_id</code> and <code>chat_id</code></li>
</ul>
<p>Create a file called <code>chat_response.py</code> in <code>utils</code> package.</p>
<p><strong>chat_response.py</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> jsonify


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ChatResponse</span>:</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, ai_response, chat_id, status</span>) -&gt; <span class="hljs-keyword">None</span>:</span>
        self.ai_response = ai_response
        self.chat_id = chat_id
        self.status = status

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">format_response</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> (
            jsonify(
                {
                    <span class="hljs-string">"ai_response"</span>: self.ai_response,
                    <span class="hljs-string">"chat_id"</span>: self.chat_id,
                }
            ),
            self.status,
        )
</code></pre>
<h4 id="heading-create-ai-chat-service">Create AI Chat Service</h4>
<p>We will create a file in the <code>services</code> package that handles chatting with the AI model.</p>
<p><strong>normal_ai_chatter.py</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> jsonpickle

<span class="hljs-keyword">from</span> exceptions <span class="hljs-keyword">import</span> ChatNotFoundException
<span class="hljs-keyword">from</span> models.models <span class="hljs-keyword">import</span> delete_objects
<span class="hljs-keyword">from</span> models.normal_chat_history <span class="hljs-keyword">import</span> NormalChatHistory


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NormalAiChatter</span>:</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, model=None</span>) -&gt; <span class="hljs-keyword">None</span>:</span>
        self.model = model

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">chat</span>(<span class="hljs-params">self, prompt, chat_id, user_id</span>):</span>
        chat_history = []
        <span class="hljs-keyword">if</span> chat_id:
            normal_chat_history = (
                NormalChatHistory.query.filter_by(user_id=user_id)
                .filter_by(id=chat_id)
                .first()
            )
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> normal_chat_history:
                <span class="hljs-keyword">raise</span> ChatNotFoundException()
            chat_history = jsonpickle.decode(normal_chat_history.chat_history)
        <span class="hljs-keyword">else</span>:
            normal_chat_history = NormalChatHistory(
                chat_history=chat_history, user_id=user_id
            )
            normal_chat_history.save()
            chat_id = normal_chat_history.id

        ai_response = self.model.chat(prompt, chat_history)
        self.save_chat_history(normal_chat_history)

        <span class="hljs-keyword">return</span> ai_response, chat_id

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">save_chat_history</span>(<span class="hljs-params">self, normal_chat_history</span>):</span>
        chat_history = self.model.get_chat_history()
        chat_history_json_string = jsonpickle.encode(chat_history)
        normal_chat_history.chat_history = chat_history_json_string
        normal_chat_history.save()

        <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>
</code></pre>
<ul>
<li><p>If there is a <code>chat_id</code>, then it will fetch the chat history corresponding to that <code>chat_id</code>. Otherwise, it would create a new chat.</p>
</li>
<li><p>After getting the response from the model, it would save the entire chat history.</p>
</li>
</ul>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_chat_history_list</span>(<span class="hljs-params">self, user_id</span>):</span>
        chat_history_list = NormalChatHistory.query.filter_by(user_id=user_id).all()
        chat_list = [
            {<span class="hljs-string">"chat_id"</span>: chat.id, <span class="hljs-string">"chat_name"</span>: chat.started_at}
            <span class="hljs-keyword">for</span> chat <span class="hljs-keyword">in</span> chat_history_list
        ]

        <span class="hljs-keyword">return</span> chat_list
</code></pre>
<ul>
<li>Get the list of all the chats of the particular user</li>
</ul>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_chat_history</span>(<span class="hljs-params">self, user_id, chat_id</span>):</span>
        normal_chat_history = (
            NormalChatHistory.query.filter_by(user_id=user_id)
            .filter_by(id=chat_id)
            .first()
        )

        <span class="hljs-keyword">if</span> normal_chat_history:
            chat_history = jsonpickle.decode(normal_chat_history.chat_history)
            chat_history_data = {
                <span class="hljs-string">"chat_id"</span>: chat_id,
                <span class="hljs-string">"chat_history"</span>: [
                    {<span class="hljs-string">"text"</span>: chat.parts[<span class="hljs-number">0</span>].text, <span class="hljs-string">"role"</span>: chat.role}
                    <span class="hljs-keyword">for</span> chat <span class="hljs-keyword">in</span> chat_history
                ],
            }
            <span class="hljs-keyword">return</span> chat_history_data

        <span class="hljs-keyword">return</span> []
</code></pre>
<ul>
<li>Get all the contents of a particular chat of that user</li>
</ul>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">delete_chat_history</span>(<span class="hljs-params">self, user_id, chat_id</span>):</span>
        <span class="hljs-keyword">if</span> chat_id:
            normal_chat_history = (
                NormalChatHistory.query.filter_by(user_id=user_id)
                .filter_by(id=chat_id)
                .all()
            )
        <span class="hljs-keyword">else</span>:
            normal_chat_history = NormalChatHistory.query.filter_by(
                user_id=user_id
            ).all()

        delete_objects(normal_chat_history)

        <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>
</code></pre>
<ul>
<li><p>Delete a particular chat of the user if the <code>chat_id</code> is provided</p>
</li>
<li><p>Delete all the chats of the user if the <code>chat_id</code> is not provided</p>
</li>
</ul>
<h4 id="heading-add-chat-routes">Add Chat Routes</h4>
<p>Update <code>main.py</code> with the following routes.</p>
<p><strong>main.py</strong></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> controllers.normal_ai_chat <span class="hljs-keyword">import</span> (
    NormalAiChat,
    NormalAiChatHistory,
    NormalAiChatHistoryList,
    deleteNormalAiChatHistory,
)


app.route(<span class="hljs-string">"/normal_chat_with_ai"</span>, methods=[<span class="hljs-string">"POST"</span>])(NormalAiChat)
app.route(<span class="hljs-string">"/normal_chat_history_list"</span>, methods=[<span class="hljs-string">"GET"</span>])(NormalAiChatHistoryList)
app.route(<span class="hljs-string">"/normal_chat_history"</span>, methods=[<span class="hljs-string">"GET"</span>])(NormalAiChatHistory)
app.route(<span class="hljs-string">"/normal_chat_history_list"</span>, methods=[<span class="hljs-string">"DELETE"</span>])(deleteNormalAiChatHistory)
</code></pre>
<h4 id="heading-handle-custom-exceptions">Handle Custom Exceptions</h4>
<p>Update <code>main.py</code> with the following code.</p>
<p><strong>main.py</strong></p>
<pre><code class="lang-python"><span class="hljs-meta">@app.errorhandler(SafetyException)</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">handle_safety_exception</span>(<span class="hljs-params">error</span>):</span>
    status = <span class="hljs-number">403</span>
    <span class="hljs-keyword">return</span> CommonResponse(CHAT_RESPONSE_NOT_SAFE, status, <span class="hljs-literal">None</span>).format_response()


<span class="hljs-meta">@app.errorhandler(ChatNotFoundException)</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">handle_chat_not_found_exception</span>(<span class="hljs-params">error</span>):</span>
    status = <span class="hljs-number">404</span>
    <span class="hljs-keyword">return</span> CommonResponse(CHAT_NOT_FOUND, status, <span class="hljs-literal">None</span>).format_response()
</code></pre>
<h2 id="heading-run-the-application">Run the Application</h2>
<p>We can now run the application by executing:</p>
<pre><code class="lang-bash">python main.py
</code></pre>
<ul>
<li><p>Make sure that PostgreSQL is already up and configured correctly</p>
</li>
<li><p>Ensure that the correct details are provided in the <code>.env</code> file</p>
</li>
<li><p>You can access the application on <code>http://localhost:8000</code></p>
</li>
</ul>
<h3 id="heading-dockerize-the-application-optional">Dockerize the Application (Optional)</h3>
<p>Add these lines to the <code>Dockerfile</code>.</p>
<p><strong>Dockerfile</strong></p>
<pre><code class="lang-dockerfile"><span class="hljs-keyword">FROM</span> python:<span class="hljs-number">3.10</span>-bookworm

<span class="hljs-keyword">ENV</span> PYTHONDONTWRITEBYTECODE <span class="hljs-number">1</span>
<span class="hljs-keyword">ENV</span> PYTHONUNBUFFERED <span class="hljs-number">1</span>
<span class="hljs-keyword">ENV</span> ENVIRONMENT DEVELOPMENT

<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>

<span class="hljs-keyword">RUN</span><span class="bash"> apt-get update -y </span>
<span class="hljs-keyword">RUN</span><span class="bash"> apt-get install -y git ca-certificates</span>
<span class="hljs-keyword">RUN</span><span class="bash"> apt-get update &amp;&amp; \
    apt-get install -y \
    python3-dev \
    libpq-dev \
    libmariadb-dev \
    build-essential \
    cmake \
    swig \
    pkgconf</span>

<span class="hljs-keyword">RUN</span><span class="bash"> apt-get install curl</span>
<span class="hljs-keyword">RUN</span><span class="bash"> curl -k -L https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz | tar -C /usr/<span class="hljs-built_in">local</span>/bin -xzv</span>

<span class="hljs-keyword">COPY</span><span class="bash"> requirements.txt /app/</span>
<span class="hljs-keyword">RUN</span><span class="bash"> pip install wheel</span>
<span class="hljs-keyword">RUN</span><span class="bash"> pip install --upgrade pip</span>
<span class="hljs-keyword">RUN</span><span class="bash"> pip install pyarrow</span>
<span class="hljs-keyword">RUN</span><span class="bash"> pip install pyOpenSSL</span>
<span class="hljs-keyword">RUN</span><span class="bash"> pip install -r requirements.txt</span>

<span class="hljs-keyword">COPY</span><span class="bash"> . /app/</span>

<span class="hljs-keyword">EXPOSE</span> <span class="hljs-number">5000</span>
<span class="hljs-keyword">EXPOSE</span> <span class="hljs-number">8000</span>

<span class="hljs-keyword">RUN</span><span class="bash"> chmod +x deploy.sh</span>

<span class="hljs-keyword">CMD</span><span class="bash"> [<span class="hljs-string">"python"</span>, <span class="hljs-string">"main.py"</span>]</span>
</code></pre>
<h3 id="heading-run-in-production">Run in Production</h3>
<p>To run the application in production, use a WSGI server like <code>Gunicorn</code> to serve the Flask application.</p>
<blockquote>
<p>Add the configurations in <code>.prod.env</code> instead of <code>.env</code> when in production</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Chaumian Blinding: The Magic Trick for Anonymous Transactions]]></title><description><![CDATA[ℹ
This article is an updated version of my previous piece on Chaumian Blinding, incorporating new insights. Read on to explore refined explanations of this fascinating technique!


Imagine you're a magician performing a trick where you’re about to ma...]]></description><link>https://tech-talk.getwithashish.yahodu.info/chaumian-blinding-the-magic-trick-for-anonymous-transactions</link><guid isPermaLink="true">https://tech-talk.getwithashish.yahodu.info/chaumian-blinding-the-magic-trick-for-anonymous-transactions</guid><category><![CDATA[anonymity]]></category><category><![CDATA[privacy]]></category><category><![CDATA[encryption]]></category><category><![CDATA[finance]]></category><dc:creator><![CDATA[Ashish Sam T George]]></dc:creator><pubDate>Tue, 18 Jun 2024 18:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728121678645/3df9d52a-8adf-467a-b2db-c12cadb8a753.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div data-node-type="callout">
<div data-node-type="callout-emoji">ℹ</div>
<div data-node-type="callout-text"><em>This article is an updated version of my previous piece on </em><a target="_blank" href="https://www.geeksforgeeks.org/chaumian-blinding/"><em>Chaumian Blinding</em></a><em>, incorporating new insights. Read on to explore refined explanations of this fascinating technique!</em></div>
</div>

<p>Imagine you're a magician performing a trick where you’re about to make a card disappear, but not just for the audience—the card will vanish from the view of <em>everyone</em>, including yourself, the magician. It sounds impossible, right? Yet, in the cryptographic world, Chaumian Blinding pulls off a similar trick, but with financial transactions. It keeps the who, what, and when of the transaction a mystery to even the central authority (like a bank) involved.</p>
<p>Developed by the cryptographer David Lee Chaum in his paper <em>“</em><a target="_blank" href="http://www.hit.bme.hu/~buttyan/courses/BMEVIHIM219/2009/Chaum.BlindSigForPayment.1982.PDF"><em>Blind Signature for Untraceable Payments</em></a><em>”</em>, this technique essentially makes transactions anonymous to even the very people who approve them! Imagine doing something and not knowing exactly what you did, yet still getting it right—how wild is that?</p>
<h3 id="heading-the-players-adrian-becky-and-the-trusting-bank">The Players: Adrian, Becky, and the Trusting Bank</h3>
<p>Let’s take a normal bank transaction between Adrian and Becky. Without any fancy tech, the bank knows who sent money to whom, how much was sent, and when. So much for privacy, huh?</p>
<p>Enter Chaumian Blinding. With this clever technique, Adrian can send money to Becky, and the bank won’t know a thing about <em>who</em> sent it or <em>how much</em> was involved. It’s like Adrian sent an invisible package through the bank—one they approve, but can’t open.</p>
<p>So, how do we make that happen?</p>
<h3 id="heading-the-secret-sauce-functions-and-trust">The Secret Sauce: Functions and Trust</h3>
<p>Chaumian Blinding revolves around a few special functions, each playing a role in our little magic act:</p>
<ol>
<li><p><strong>S (The Signing Function):</strong> Known only to the bank, S is like the bank’s magic seal that says, “Yep, this is legit.” Its inverse, <strong>S'</strong>, is publicly known and used to verify the bank’s seal.</p>
</li>
<li><p><strong>C (The Commuting Function):</strong> Adrian’s personal encryption trick. It scrambles his certificate (which contains the money and serial number) into cipher text. Only Adrian knows how to scramble and, more importantly, unscramble with <strong>C’</strong>.</p>
</li>
<li><p><strong>r (Redundancy Check):</strong> Let’s not let any funny business slip through. This checks if the certificate makes sense. Think of it like spell-check, but for transactions.</p>
</li>
</ol>
<h3 id="heading-the-magic-act-begins">The Magic Act Begins</h3>
<p>So here’s how the trick unfolds:</p>
<p>Adrian wants to pay Becky. To do that, he needs the bank to approve his transaction by signing a certificate. This certificate includes the amount to be debited and a serial number. But here’s the thing: Adrian doesn’t want the bank snooping on his personal details.</p>
<p>So, what does he do? He encrypts the certificate using <strong>C</strong>, turning the details into gibberish.</p>
<p><em>Adrian’s encrypted certificate (Cipher text) -&gt; Sent to the bank</em></p>
<p>Now, the bank sees this encrypted mumbo-jumbo but, surprisingly, they’re cool with it. They’ve entered into what’s called <strong>blind trust</strong> with Adrian. More on that in a bit.</p>
<p>The bank then uses their signing function <strong>S</strong> on the encrypted data:</p>
<p><em>S(Certificate, but scrambled) -&gt; Signed by the bank</em></p>
<p>At this point, Adrian receives back his now-bank-approved certificate, still encrypted. But here’s the clever bit: Adrian applies his <strong>C’</strong> function (the unscrambling one), and—presto!—he’s got a legitimate, bank-signed certificate without ever revealing the original details to the bank.</p>
<h3 id="heading-ta-da-becky-gets-paid">Ta-da! Becky Gets Paid</h3>
<p>Adrian sends this freshly signed certificate to Becky. Becky, being the practical one in this whole ordeal, uses <strong>S’</strong> (the public verification function) to check that everything’s in order:</p>
<p><em>Signed Certificate -&gt; Verified by Becky -&gt; Becky sends it to the bank</em></p>
<p>The bank then credits Becky’s account, all without ever knowing who sent the payment. It’s like Becky got an anonymous tip, but in cold hard cash.</p>
<h3 id="heading-blind-trust-the-foolproof-magic-trick">Blind Trust: The Foolproof Magic Trick</h3>
<p>Now, how does the bank make sure Adrian’s not pulling a fast one? The answer lies in <strong>blind trust</strong>.</p>
<p>Imagine the bank says, “Adrian, send me 1,000 copies of the certificate you want me to sign. They should all have the same amount of money, but different serial numbers.”</p>
<p>Adrian, who’s all in on this deal, sends over 1,000 certificates (one of which is the real one). The bank, in turn, picks 999 of them at random and says, “Show me the keys to decrypt these.”</p>
<p>Adrian obliges. The bank checks, and if all 999 decrypted certificates have the same amount of money, it signs the remaining one—blindly trusting that it, too, contains the same amount.</p>
<h3 id="heading-chaumian-blinding-in-real-life">Chaumian Blinding in Real Life</h3>
<p>Think of this process like a “choose-your-own-adventure” story, but every page is scrambled, and the person approving the story can only read random snippets. They trust that, based on the snippets they’ve seen, the rest of the story makes sense. It’s a leap of faith—wrapped in math.</p>
<p>In essence, Chaumian Blinding allows us to create an anonymous system where privacy is protected not by hiding information but by ensuring the approving party never gets to see the full details in the first place.</p>
<p>And that’s the beauty of it: It’s magic, but with cryptography instead of sleight of hand.</p>
]]></content:encoded></item></channel></rss>