<?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[Tech Bytes]]></title><description><![CDATA[Tech Bytes]]></description><link>https://blog.abhinavtb.com</link><generator>RSS for Node</generator><lastBuildDate>Fri, 16 Oct 2026 12:23:23 GMT</lastBuildDate><atom:link href="https://blog.abhinavtb.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[The Preparation Trap]]></title><description><![CDATA[For a long time, I thought I had a consistency problem. I’d start with energy, map out a learning plan, collect resources, and build a clean roadmap. On most days, that felt responsible. It felt like ]]></description><link>https://blog.abhinavtb.com/the-preparation-trap</link><guid isPermaLink="true">https://blog.abhinavtb.com/the-preparation-trap</guid><dc:creator><![CDATA[Abhinav T B]]></dc:creator><pubDate>Sun, 31 May 2026 20:54:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6061f1687a123b754262daf6/23a18568-297d-4a4f-9b82-4a9606027496.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For a long time, I thought I had a consistency problem. I’d start with energy, map out a learning plan, collect resources, and build a clean roadmap. On most days, that felt responsible. It felt like discipline.</p>
<p>But when I looked at outcomes, the pattern was obvious: I had become excellent at preparing and inconsistent at executing.</p>
<p>That’s the trap.</p>
<p>Preparation is useful, but it can also become a socially acceptable way to delay exposure. Instead of shipping one rough thing, we polish our inputs. We save another article, queue another video, revise the plan one more time, and tell ourselves we’re “still working on it.” Technically true. Practically stagnant.</p>
<p>The hardest part is that this behavior doesn’t feel like procrastination. It feels smart. It feels thoughtful. It feels like you’re reducing risk.</p>
<p>In reality, you’re often postponing feedback.</p>
<p>And feedback is the only thing that makes you better.</p>
<p>I like thinking about this in ML terms. A book, a course, or a framework is like a high-quality model trained on someone else’s data. Useful, absolutely. But your life is the production environment. Your constraints, your timing, your pressure, your edge cases none of that is in the training set. Until you deploy in your own environment, you don’t know what survives contact with reality.</p>
<p>No deployment means no signal. No signal means no improvement.</p>
<p>What changed things for me was a small but strict rule: every preparation session has to end with one concrete output. Not better notes. Not a cleaner plan. Output.</p>
<p>If I studied system design, I had to publish one architecture sketch. If I watched an LLM tutorial, I had to run one tiny experiment. If I planned a side project, I had to ship one narrow slice that same day.</p>
<p>The output could be small and imperfect. It just couldn’t be imaginary.</p>
<p>That one constraint exposes everything. It shows whether I’m learning to apply or learning to avoid. It also makes momentum real, because execution compounds in a way preparation never does.</p>
<p>I still plan. I still read. I still research deeply. But now I treat preparation as support work, not the main work.</p>
<p>If you feel stuck, try this for a month: prepare less than you execute. Keep learning, but force conversion. The goal is not to know more in private. The goal is to become effective in public.</p>
<p>Because in the end, unread books, saved threads, and polished plans don’t change your trajectory.</p>
<p>Shipped work does.</p>
]]></content:encoded></item><item><title><![CDATA[VaultClip: Building Private Video Q&A Fully in the Browser]]></title><description><![CDATA[I built VaultClip because I wanted a simple thing that still feels surprisingly hard to get today: upload a video, ask questions about it, and get useful answers ,without sending the video to someone ]]></description><link>https://blog.abhinavtb.com/vaultclip-building-private-video-q-a-fully-in-the-browser</link><guid isPermaLink="true">https://blog.abhinavtb.com/vaultclip-building-private-video-q-a-fully-in-the-browser</guid><category><![CDATA[AI]]></category><category><![CDATA[webgpu]]></category><category><![CDATA[browser AI]]></category><category><![CDATA[privacy]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[Abhinav T B]]></dc:creator><pubDate>Sat, 23 May 2026 20:03:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6061f1687a123b754262daf6/4a2e6ffa-c414-4185-afd8-d617452a418d.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I built <strong>VaultClip</strong> because I wanted a simple thing that still feels surprisingly hard to get today: upload a video, ask questions about it, and get useful answers ,without sending the video to someone else's server.</p>
<p>The GitHub repo is here: <a href="https://github.com/abhinav-TB/VaultClip">https://github.com/abhinav-TB/VaultClip</a></p>
<p>Live demo: <a href="https://vaultclip.gamezdev.workers.dev">https://vaultclip.gamezdev.workers.dev</a></p>
<p>VaultClip is an open-source, browser-only video Q&amp;A app. You select a video or audio file, the app extracts the audio locally, transcribes it locally, builds searchable context locally, and lets you chat with the media using local AI. No uploads. No accounts. No backend. No analytics pipeline quietly collecting your files.</p>
<p>That constraint, <strong>everything happens in the browser</strong> shaped almost every technical decision in the project.</p>
<h2>Why build this?</h2>
<p>A lot of useful information is trapped inside videos: lectures, meeting recordings, interviews, demos, tutorials, walkthroughs, research calls, debugging sessions, and personal notes. The usual way to find something is still manual scrubbing: drag the timeline, listen for a few seconds, miss it, go back, try again.</p>
<p>Cloud AI tools can solve that problem, but they often require uploading the file. That is fine for public content, but it is a bad fit for private recordings. A meeting recording, an interview, a classroom lecture, or an unreleased demo may contain sensitive information. Even when a provider has good policies, the user still has to trust an external system with the media.</p>
<p>VaultClip takes the opposite approach:</p>
<ul>
<li><p>the file stays on your machine</p>
</li>
<li><p>inference runs in your browser</p>
</li>
<li><p>temporary data stays in browser memory</p>
</li>
<li><p>after the initial model download, the app can work offline</p>
</li>
<li><p>the code is open source and auditable</p>
</li>
</ul>
<p>The goal was not just to make video Q&amp;A work. The goal was to make it work while preserving the user's control over the media.</p>
<h2>The product idea</h2>
<p>The user flow is intentionally small:</p>
<ol>
<li><p>Pick a video or audio file.</p>
</li>
<li><p>Preview it in the browser.</p>
</li>
<li><p>Let VaultClip extract and transcribe the audio.</p>
</li>
<li><p>Ask natural-language questions.</p>
</li>
<li><p>Get answers grounded in timestamped transcript context.</p>
</li>
</ol>
<p>Examples of questions:</p>
<ul>
<li><p>"What were the main decisions in this meeting?"</p>
</li>
<li><p>"When does the speaker explain the architecture?"</p>
</li>
<li><p>"Summarize the action items."</p>
</li>
<li><p>"Find the part where they talk about latency."</p>
</li>
<li><p>"What are the objections raised in this interview?"</p>
</li>
</ul>
<p>The important part is that the app does not need to upload the file to answer these questions.</p>
<h2>Architecture at a high level</h2>
<p>VaultClip is a React + TypeScript single-page app built with Vite. The heavier work is moved out of the UI thread into Web Workers, because media processing and model inference can easily freeze the browser if handled directly in React components.</p>
<p>The pipeline looks like this:</p>
<pre><code class="language-text">User selects media
        ↓
Validate file size, duration, and format
        ↓
Preview the file locally
        ↓
Extract audio with ffmpeg.wasm in a Web Worker
        ↓
Transcribe audio chunks with Gemma via Transformers.js + WebGPU
        ↓
Create timestamped transcript segments
        ↓
Build local retrieval context
        ↓
Answer questions in a local chat interface
</code></pre>
<p>The current repository is here:</p>
<p><a href="https://github.com/abhinav-TB/VaultClip">https://github.com/abhinav-TB/VaultClip</a></p>
<p>The app is deployed as a static app on Cloudflare Workers, but the deployed app is still not a backend for user files. The Worker serves the frontend assets and handles app routing. The actual media processing happens in the user's browser.</p>
<h2>Why browser-only AI is interesting</h2>
<p>For a long time, "AI app" almost automatically meant "send data to an API." That is still the right architecture for many products, especially when models are large or collaboration matters. But browsers have become much more capable.</p>
<p>With WebGPU, Web Workers, WebAssembly, and libraries like Transformers.js, it is now realistic to run useful local AI workflows inside a normal web app. The browser can:</p>
<ul>
<li><p>decode and preview media</p>
</li>
<li><p>run ffmpeg.wasm for audio extraction</p>
</li>
<li><p>use WebGPU for accelerated inference</p>
</li>
<li><p>cache model files locally</p>
</li>
<li><p>manage temporary data in memory</p>
</li>
<li><p>provide a familiar UI without installation</p>
</li>
</ul>
<p>This makes a new class of apps possible: local-first AI tools that feel like web apps but behave more like private desktop utilities.</p>
<p>VaultClip is an experiment in that direction.</p>
<h2>Keeping media private</h2>
<p>Privacy in VaultClip is not just a sentence in the README. It is a design constraint.</p>
<p>The selected media file is stored through the browser's <code>File</code> API. It is not serialized into Redux, not uploaded to a server, and not persisted to a remote database. The extracted audio is also kept locally, and object URLs are cleaned up when they are no longer needed.</p>
<p>The app has no backend API for media. The only unavoidable network step is the initial download of model assets. Once the model is cached, the workflow can run locally.</p>
<p>This matters because video files are often high-context. They can include faces, voices, screenshares, credentials accidentally visible in a demo, internal project names, customer details, or personal conversations. A privacy-first architecture should not ask for that data unless it truly needs it.</p>
<p>VaultClip's answer is: for this use case, the app does not need it.</p>
<h2>The local processing pipeline</h2>
<p>The first technical challenge is turning an arbitrary media file into something the model can use.</p>
<p>Videos are large, varied, and browser support depends on codecs. VaultClip starts with guardrails: file size limits, duration limits, supported formats, and browser preview checks. These constraints are not glamorous, but they make the app much more reliable. Browser memory is finite, and local inference gets expensive quickly.</p>
<p>Once the user confirms the file, VaultClip extracts audio using <code>ffmpeg.wasm</code> inside a Web Worker. That keeps the UI responsive while the app does CPU-heavy work. The extracted audio is converted into a transcription-friendly format, such as WAV at 16 kHz.</p>
<p>The transcription step processes audio in chunks. Chunking matters because long media files can exceed practical generation limits, and smaller chunks make progress easier to track. VaultClip keeps timestamp ranges for transcript segments so answers can point back to where the information came from.</p>
<p>After transcription, the app prepares local context for question answering. Instead of sending the whole transcript to a server, VaultClip builds a local retrieval layer and feeds relevant context into the chat flow.</p>
<h2>Running Gemma in the browser</h2>
<p>VaultClip uses Gemma through Transformers.js with WebGPU acceleration. The model lifecycle has a few stages:</p>
<ol>
<li><p>load or fetch model assets</p>
</li>
<li><p>initialize the runtime</p>
</li>
<li><p>warm up inference</p>
</li>
<li><p>process transcription and chat tasks</p>
</li>
<li><p>surface progress and failure states in the UI</p>
</li>
</ol>
<p>This is very different from calling a hosted API. With an API, the hard work is hidden behind an HTTP request. In the browser, the app has to care about model download size, browser support, GPU availability, memory pressure, initialization time, and failure recovery.</p>
<p>That tradeoff is worth it for VaultClip because the privacy model becomes much stronger. The user's media does not leave the device just because they want an AI summary.</p>
<h2>State management and workers</h2>
<p>One lesson from the project: do not put large browser objects directly into global app state.</p>
<p>VaultClip uses Redux Toolkit for serializable UI and workflow state, but the actual <code>File</code> objects and raw audio bytes live in separate registries. Redux stores metadata like file name, size, duration, object URL, processing status, transcript segments, and model state.</p>
<p>That separation keeps the app easier to debug and avoids pushing non-serializable heavy objects through the normal React state flow.</p>
<p>The Web Worker boundary is also important. The UI should not know too much about how ffmpeg or the model runtime works. Instead, a worker client provides a promise-based interface for long-running tasks. This keeps the app structure cleaner:</p>
<ul>
<li><p>React handles the user experience</p>
</li>
<li><p>Redux tracks state transitions</p>
</li>
<li><p>workers handle expensive compute</p>
</li>
<li><p>small registry modules own large in-memory objects</p>
</li>
<li><p>shared types define the contracts between pieces</p>
</li>
</ul>
<p>That architecture made the app easier to grow without turning every component into an orchestration layer.</p>
<h2>The hard parts</h2>
<p>The hardest parts were not the UI screens. The hard parts were browser limitations and edge cases.</p>
<p>Browser memory is not straightforward. Web APIs do not give a reliable cross-browser way to know total system memory or GPU memory. That means the app needs conservative limits and clear failure states instead of pretending it can process every file.</p>
<p>WebGPU support is also still uneven. Chrome and Edge are the best targets right now. Firefox and Safari are improving, but for this kind of app, WebGPU support is a real requirement.</p>
<p>Another subtle issue is transcription coverage. Longer chunks can produce incomplete coverage or weaker timestamp behavior, so VaultClip defaults to smaller chunk sizes. The app is designed around practical reliability instead of theoretical maximum throughput.</p>
<p>There is also the first-run experience: downloading a local model can be large. That is not as seamless as an API call, but the upside is that the model can be cached and reused locally.</p>
<h2>What VaultClip is good for</h2>
<p>VaultClip is useful when privacy matters more than cloud-scale convenience.</p>
<p>Some examples:</p>
<ul>
<li><p>students reviewing long lectures</p>
</li>
<li><p>engineers searching through demo recordings</p>
</li>
<li><p>researchers analyzing interviews</p>
</li>
<li><p>creators reviewing raw footage</p>
</li>
<li><p>professionals summarizing meeting recordings</p>
</li>
<li><p>anyone who wants Q&amp;A over media without uploading it</p>
</li>
</ul>
<p>It is not trying to replace every cloud transcription or video intelligence product. It is designed for the local-first case: give me useful AI over my file, but keep the file with me.</p>
<h2>Current limitations</h2>
<p>The current version is still an MVP. The main limitations are:</p>
<ul>
<li><p>WebGPU is required for the best experience</p>
</li>
<li><p>first-time model download is large</p>
</li>
<li><p>browser memory limits constrain file size and duration</p>
</li>
<li><p>transcript timestamps are segment-level, not word-level</p>
</li>
<li><p>the app focuses on one active media session at a time</p>
</li>
<li><p>very long recordings still need better chunking and persistence strategies</p>
</li>
</ul>
<p>These constraints are acceptable for the first version because they keep the privacy model simple and the architecture understandable.</p>
<h2>Why open source matters here</h2>
<p>For privacy-focused tools, open source is not just a nice extra. It is part of the trust model.</p>
<p>If an app claims "your files never leave your device," users should be able to inspect how that is implemented. VaultClip's code is available here:</p>
<p><a href="https://github.com/abhinav-TB/VaultClip">https://github.com/abhinav-TB/VaultClip</a></p>
<p>The repo includes the architecture, worker pipeline, model runtime integration, media guardrails, deployment setup, and local development instructions.</p>
<p>If you want to run it locally:</p>
<pre><code class="language-bash">git clone https://github.com/abhinav-TB/VaultClip.git
cd VaultClip
npm install
npm run dev -- --host 127.0.0.1
</code></pre>
<p>Then open <code>http://127.0.0.1:5173</code> in a WebGPU-capable browser like Chrome or Edge.</p>
<h2>What I learned</h2>
<p>The biggest lesson from building VaultClip is that browser AI is no longer just a toy category. It still has rough edges, but it is useful enough for real workflows when the scope is chosen carefully.</p>
<p>The second lesson is that privacy-first design needs to happen at the architecture level. You cannot bolt it on at the end. If the core workflow assumes uploads, then privacy becomes policy language. If the core workflow keeps data local, privacy becomes a property of the system.</p>
<p>VaultClip is my attempt to build the second kind of app.</p>
<h2>Closing thoughts</h2>
<p>We are used to thinking of the browser as a thin client for server-side intelligence. But for many personal AI tasks, especially ones involving private files, the browser can be the compute environment.</p>
<p>VaultClip shows that a modern web app can process media, run local models, build retrieval context, and provide useful Q&amp;A — all without asking the user to hand over their video.</p>
<p>If you are interested in local-first AI, browser ML, WebGPU, or privacy-preserving media tools, check out the project here:</p>
<p><a href="https://github.com/abhinav-TB/VaultClip">https://github.com/abhinav-TB/VaultClip</a></p>
<p>And try the live demo here:</p>
<p><a href="https://vaultclip.gamezdev.workers.dev">https://vaultclip.gamezdev.workers.dev</a></p>
<p>If you find the idea useful or want to support more local-first browser AI experiments, please consider giving the GitHub repo a star. It helps the project reach more people who care about private AI tools.</p>
]]></content:encoded></item><item><title><![CDATA[What an AI Assistant Should Actually Remember]]></title><description><![CDATA[One of the easiest mistakes to make when building an AI assistant is assuming that “more memory” automatically means “better assistant.”
It sounds obvious at first: if the assistant remembers everythi]]></description><link>https://blog.abhinavtb.com/what-an-ai-assistant-should-actually-remember</link><guid isPermaLink="true">https://blog.abhinavtb.com/what-an-ai-assistant-should-actually-remember</guid><category><![CDATA[ai agents]]></category><category><![CDATA[LLM's ]]></category><category><![CDATA[openclaw]]></category><category><![CDATA[agent-memory]]></category><category><![CDATA[personal ai]]></category><dc:creator><![CDATA[Abhinav T B]]></dc:creator><pubDate>Wed, 29 Apr 2026 14:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6061f1687a123b754262daf6/193b2f16-d3eb-4824-b876-d35ea96e02a2.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>One of the easiest mistakes to make when building an AI assistant is assuming that “more memory” automatically means “better assistant.”</p>
<p>It sounds obvious at first: if the assistant remembers everything, it should become more useful over time. It should know your preferences, your projects, your writing style, your tools, your schedule, and all the tiny details that usually get lost between conversations.</p>
<p>But in practice, an assistant that remembers everything becomes noisy.</p>
<p>It starts carrying around stale assumptions. It overfits to one-off conversations. It treats temporary preferences like permanent truth. It may remember the exact wrong thing with too much confidence.</p>
<p>The real question is not: <strong>how do we make the assistant remember more?</strong></p>
<p>The better question is: <strong>what should an assistant actually remember?</strong></p>
<p>And just as importantly: <strong>when should something become memory, when should it become a skill, and when should it simply be forgotten?</strong></p>
<h2>Memory is not a transcript</h2>
<p>A useful assistant should not treat memory as a giant chat log.</p>
<p>A transcript is what happened. Memory is what remains useful later.</p>
<p>Those are different things.</p>
<p>If I tell an assistant, “I’m debugging a Next.js issue right now,” that may be useful for the current session. But it probably does not need to become long-term memory.</p>
<p>If I tell it, “When I write technical notes, I prefer concept-first explanations with concrete examples,” that is durable. It should remember that.</p>
<p>If I say, “Use this folder for my Obsidian notes,” that is also durable. Forgetting it causes repeated friction.</p>
<p>The assistant should not store every message. It should extract stable facts, preferences, workflows, and decisions that reduce future back-and-forth.</p>
<p>Good memory feels invisible. You notice it because the assistant stops asking the same question twice.</p>
<p>Bad memory feels creepy or annoying. You notice it because the assistant brings up irrelevant details or acts like yesterday’s temporary context is now your identity.</p>
<h2>The three buckets: context, memory, and skills</h2>
<p>I think assistant knowledge should be split into three buckets.</p>
<h3>1. Temporary context</h3>
<p>This is the working set for the current task.</p>
<p>Examples:</p>
<ul>
<li><p>the bug we are debugging right now</p>
</li>
<li><p>the article draft currently open</p>
</li>
<li><p>the command output from five minutes ago</p>
</li>
<li><p>the user’s latest instruction in this thread</p>
</li>
<li><p>a design choice that only matters for this feature branch</p>
</li>
</ul>
<p>Temporary context should be easy to use and easy to discard. Not everything needs ceremony. Most things belong here.</p>
<h3>2. Long-term memory</h3>
<p>This is durable information that will probably be useful across many future sessions.</p>
<p>Examples:</p>
<ul>
<li><p>the user’s preferred name and timezone</p>
</li>
<li><p>canonical folders, repos, and tools</p>
</li>
<li><p>writing style preferences</p>
</li>
<li><p>recurring project goals</p>
</li>
<li><p>important constraints like “do not send emails without asking”</p>
</li>
<li><p>personal workflows that are stable but not complex enough to become a skill</p>
</li>
</ul>
<p>Memory should answer the question: <strong>will this help the assistant behave better next week?</strong></p>
<p>If the answer is no, it probably does not belong there.</p>
<h3>3. Skills</h3>
<p>Skills are different from memory.</p>
<p>Memory says: <strong>remember this fact or preference.</strong></p>
<p>A skill says: <strong>when this kind of task appears, follow this repeatable procedure.</strong></p>
<p>That distinction matters.</p>
<p>For example, “Abhi prefers detailed beginner-friendly notes” is memory.</p>
<p>But “when Abhi asks to create a tutorial note, save it to the canonical Obsidian folder, use this structure, include examples, and run a Nextcloud scan afterward” is a skill.</p>
<p>The skill is not just remembering a preference. It is packaging a workflow.</p>
<h2>When repeatable work becomes a skill</h2>
<p>Some agent systems make this workflow explicit. In OpenClaw, for example, a skill is a small instruction package the agent can load when a certain kind of task appears. Hermes-style systems use a similar idea: repeated work should not live only in the model’s memory or in scattered chat history. It should become a reusable workflow.</p>
<p>The idea is simple: if an agent repeatedly performs the same kind of task, capture the procedure as a skill. The next time that task appears, the agent does not have to rediscover the process from scratch.</p>
<p>That is extremely useful.</p>
<p>But it also creates a temptation: turning everything into a skill.</p>
<p>I do not think that is the right move.</p>
<p>If every preference, tiny habit, and one-off instruction becomes a skill, the system becomes heavy. The assistant spends more time deciding which skill applies than actually helping. Skills start overlapping. Some become stale. Others encode workflows that were only useful once.</p>
<p>A skill should exist when there is a repeatable pattern with enough structure to justify it.</p>
<p>Good candidates for skills:</p>
<ul>
<li><p>writing a Hashnode-ready blog draft</p>
</li>
<li><p>generating a tailored resume from a job description</p>
</li>
<li><p>creating a tutorial-style Obsidian note</p>
</li>
<li><p>triaging GitHub issues</p>
</li>
<li><p>running a health check on a server</p>
</li>
<li><p>using a specific browser automation flow</p>
</li>
<li><p>preparing a release checklist</p>
</li>
</ul>
<p>Bad candidates for skills:</p>
<ul>
<li><p>“I like concise replies”</p>
</li>
<li><p>“Use this nickname for me”</p>
</li>
<li><p>“This project is important this week”</p>
</li>
<li><p>“Remember that I prefer Python examples”</p>
</li>
<li><p>“For this one article, use a slightly more opinionated tone”</p>
</li>
</ul>
<p>Those belong in memory or temporary context.</p>
<p>A good rule of thumb:</p>
<blockquote>
<p>If it is a stable preference, store it in memory. If it is a repeatable procedure, make it a skill. If it only matters right now, keep it in context.</p>
</blockquote>
<h2>OpenClaw’s approach: tell the agent what to store or create</h2>
<p>In OpenClaw, this distinction becomes practical.</p>
<p>You can tell the agent to remember things. You can also tell it to create or update skills for repeatable workflows.</p>
<p>That gives the user more control over the assistant’s evolution.</p>
<p>Instead of hoping the model magically guesses what matters, the system can support explicit instructions like:</p>
<pre><code class="language-text">Remember that I prefer technical notes with detailed examples.
</code></pre>
<p>or:</p>
<pre><code class="language-text">Create a skill for writing my blog posts so future drafts follow this structure.
</code></pre>
<p>or:</p>
<pre><code class="language-text">Update the resume tailoring skill with this new rule.
</code></pre>
<p>This is important because users should not have to re-explain stable preferences forever, but they also should not have every random message fossilized into permanent memory.</p>
<p>The assistant should be proactive, but not presumptuous.</p>
<p>A strong agent should sometimes say:</p>
<blockquote>
<p>This looks like a repeatable workflow. Want me to turn it into a skill?</p>
</blockquote>
<p>Or:</p>
<blockquote>
<p>This seems like a durable preference. I’ll remember it for next time.</p>
</blockquote>
<p>That small distinction makes the assistant feel much more trustworthy.</p>
<h2>What should actually go into memory?</h2>
<p>Here is my personal filter.</p>
<p>An assistant should remember things that are:</p>
<h3>Durable</h3>
<p>They remain true beyond the current session.</p>
<p>“Use my Nextcloud Obsidian vault for notes” is durable. “I am currently comparing two blog titles” is not.</p>
<h3>Actionable</h3>
<p>They change how the assistant behaves.</p>
<p>“User likes practical examples” is actionable. “User once mentioned React in passing” is probably not.</p>
<h3>User-approved or clearly implied</h3>
<p>Some things should only be stored when the user asks. Personal details, sensitive context, and preferences that could be wrong should not be silently collected.</p>
<p>The assistant can infer lightweight working context, but durable memory should be handled carefully.</p>
<h3>Small enough to stay useful</h3>
<p>Memory should be compressed.</p>
<p>Do not store the whole conversation. Store the lesson.</p>
<p>Instead of:</p>
<blockquote>
<p>On Tuesday, the user asked me to write a note about web workers, then corrected the examples, then said they like this style...</p>
</blockquote>
<p>Store:</p>
<blockquote>
<p>User prefers technical notes that are concept-first, sectioned, and include detailed examples in the style of the web-workers note.</p>
</blockquote>
<p>That is useful.</p>
<h2>What should become a skill?</h2>
<p>A workflow should become a skill when it has triggers, steps, and quality checks.</p>
<p>For example, “write a blog post” is not just a preference. It has a shape:</p>
<ul>
<li><p>detect the topic</p>
</li>
<li><p>create a strong title and slug</p>
</li>
<li><p>include frontmatter</p>
</li>
<li><p>write in the user’s style</p>
</li>
<li><p>save to the right folder</p>
</li>
<li><p>run any required indexing step</p>
</li>
<li><p>respond with the saved path</p>
</li>
</ul>
<p>That deserves a skill.</p>
<p>The same applies to job applications, note generation, browser automation, server health checks, inbox triage, and other repeatable workflows.</p>
<p>The key is that a skill should reduce future cognitive load. It should make the agent more reliable, not more bureaucratic.</p>
<p>If a skill does not improve reliability, it is probably just documentation wearing a costume.</p>
<h2>My take: memory should be boring, skills should be opinionated</h2>
<p>This is the part I care about most.</p>
<p>Memory should be boring.</p>
<p>It should store stable facts, preferences, constraints, and pointers. It should not be dramatic. It should not try to model the user’s soul from three messages. It should not turn every interaction into permanent identity.</p>
<p>Skills, on the other hand, should be opinionated.</p>
<p>A good skill should encode a clear way of doing a task. It should have defaults. It should know the destination folder. It should know the expected format. It should know what “done” means.</p>
<p>Memory helps the assistant know you.</p>
<p>Skills help the assistant do things for you.</p>
<p>Those are related, but they are not the same.</p>
<h2>The danger of too much automatic memory</h2>
<p>A lot of AI products talk about memory like it is magic. But automatic memory has real failure modes.</p>
<p>It can store stale information. It can create privacy problems. It can make the assistant overconfident. It can also make users feel watched instead of helped.</p>
<p>There is a subtle difference between:</p>
<blockquote>
<p>“Nice, it remembered how I like my notes structured.”</p>
</blockquote>
<p>and:</p>
<blockquote>
<p>“Why did it bring up that random thing I said two months ago?”</p>
</blockquote>
<p>The first feels useful. The second feels invasive.</p>
<p>This is why I prefer memory systems where the assistant can write things down, but the user can also explicitly say what should be remembered, forgotten, or promoted into a skill.</p>
<p>The assistant should not be a hoarder. It should be a careful librarian.</p>
<h2>A practical framework</h2>
<p>When deciding what to do with new information, the agent can ask three questions.</p>
<p>First:</p>
<blockquote>
<p>Will this matter after the current task?</p>
</blockquote>
<p>If no, keep it in temporary context.</p>
<p>Second:</p>
<blockquote>
<p>Is this a stable fact, preference, or constraint?</p>
</blockquote>
<p>If yes, store it in memory.</p>
<p>Third:</p>
<blockquote>
<p>Is this a repeatable workflow with steps and quality checks?</p>
</blockquote>
<p>If yes, create or update a skill.</p>
<p>That is the whole system.</p>
<p>Context is for now.</p>
<p>Memory is for later.</p>
<p>Skills are for doing it better next time.</p>
<h2>Closing thoughts</h2>
<p>The best AI assistant is not the one that remembers the most.</p>
<p>It is the one that remembers the right things, forgets the noise, and turns repeated work into reliable workflows.</p>
<p>That is the difference between a chatbot with a long transcript and an actual assistant that compounds over time.</p>
<p>For OpenClaw, I think the sweet spot is clear: let the user tell the agent what to remember, let the agent suggest when something should become a skill, and keep the boundary between memory and skills sharp.</p>
<p>Not every detail deserves permanence.</p>
<p>Not every workflow deserves a skill.</p>
<p>But when the assistant gets the distinction right, it starts to feel less like software you operate and more like a teammate who actually learns how to work with you.</p>
]]></content:encoded></item><item><title><![CDATA[Transformers and Attention Mechanisms: From Basics to GPTMini]]></title><description><![CDATA[Transformers have revolutionized natural language processing by using attention mechanisms to model long-range dependencies. In this post, we’ll journey from the origins of attention to building a mini GPT model (“GPTMini”) from scratch. We’ll start ...]]></description><link>https://blog.abhinavtb.com/transformers-and-attention-mechanisms-from-basics-to-gptmini</link><guid isPermaLink="true">https://blog.abhinavtb.com/transformers-and-attention-mechanisms-from-basics-to-gptmini</guid><category><![CDATA[transformers]]></category><category><![CDATA[GPT 3]]></category><category><![CDATA[gpt]]></category><category><![CDATA[pytorch]]></category><dc:creator><![CDATA[Abhinav T B]]></dc:creator><pubDate>Sun, 01 Jun 2025 19:35:58 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/nYSdjVD2ayo/upload/05f13b61084fff628fcb28ec05c27aab.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Transformers have revolutionized natural language processing by using <strong>attention mechanisms</strong> to model long-range dependencies. In this post, we’ll journey from the origins of attention to building a mini GPT model (“GPTMini”) from scratch. We’ll start with how attention arose to overcome RNN limitations, then break down the Transformer architecture, including self-attention, positional encoding, multi-head attention, feed-forward layers, and residual connections, in simple terms. Finally, we’ll walk through implementing a GPTMini project with a custom tokenizer, WikiText dataset, Transformer decoder model, training loop, and text generation.</p>
<h2 id="heading-a-brief-history-from-rnns-to-attention-is-all-you-need">A Brief History: From RNNs to “Attention Is All You Need”</h2>
<p>Recurrent Neural Networks (RNNs) and their gated variants (LSTMs) once dominated sequence modeling, but they struggled with long-range relationships due to sequential processing and vanishing gradients. In 2014, Bahdanau et al. introduced the attention mechanism to help RNN-based translation models focus on relevant parts of the input sequence at each decoding step. Instead of encoding an entire sentence into a single vector (a “context vector”) as in vanilla sequence-to-sequence models, attention allowed the decoder to dynamically look at (“attend to”) specific encoder states (words) when producing each output word. This alignment mechanism significantly improved translation quality by bypassing the fixed-length bottleneck of RNNs.</p>
<p>Fast-forward to 2017: Vaswani et al.’s paper “Attention Is All You Need” dispensed with recurrence altogether and proposed the Transformer – an architecture built entirely on attention layers and feed-forward networks. The Transformer’s breakthrough was showing that you can achieve state-of-the-art results in translation (and soon many other tasks) using only attention mechanisms in a highly parallelizable model, making training faster than RNNs. The Transformer introduced several key ideas we’ll unpack below: self-attention, multi-head attention, positional encoding, and residual connections. These innovations enabled models to capture global context and long-term dependencies more effectively than RNNs</p>
<h2 id="heading-transformer-fundamentals-attention-and-friends">Transformer Fundamentals: Attention and Friends</h2>
<p>Before building our mini GPT, let’s understand the core concepts of the Transformer architecture in an approachable way.</p>
<h3 id="heading-self-attention-paying-attention-to-context">Self-Attention: Paying Attention to Context</h3>
<p>At the heart of Transformers is the <strong>self-attention</strong> mechanism. In simple terms, self-attention lets every token (word or subword) in a sequence look at every other token and decide how much attention to give to each. This means each word can gather useful information from the whole sequence when computing its representation, rather than only from nearby words or previous words. As a result, the model can capture relationships like long-distance dependencies (e.g. pronoun-reference links, subject-verb agreements) much better than an RNN, which processes words one by one</p>
<p>How does self-attention work? Imagine we have a sentence, and we want to compute new representations for each word that include contextual information. The Transformer does this by creating three vectors for each token: a Query vector (Q), a Key vector (K), and a Value vector (V). You can think of this like an information retrieval system: each token’s Query is like a question, and each other token has a Key (an identifying summary) and a Value (the content info). To figure out how much one word should pay attention to another, we compute a similarity score between the Query of the first word and the Key of the second word (for all pairs). These scores (often scaled dot-products) form an attention matrix of size n×n (for n tokens). We then apply a softmax to turn these scores into attention weights that sum to 1 for each query word. Finally, each word’s output representation is computed as a weighted sum of all Value vectors, using these attention weights.</p>
<p>In essence, self-attention produces a context-aware representation for each token. If word A’s Query aligns strongly with word B’s Key, then word A’s representation will include a lot of word B’s information (Value). For example, in the sentence “The animal didn’t cross the road because it was tired,” the pronoun “it” might have a high attention weight on “animal”, effectively letting “it” incorporate context from “animal” into its representation to clarify the reference</p>
<p>This mechanism is powerful because it’s order-agnostic (any token can attend to any other, regardless of position) and parallelizable (all token relationships can be computed simultaneously). It gives Transformers the flexibility to capture both local and long-distance relationships in a sequence. Notably, Vaswani et al. used a specific form called scaled dot-product attention: they compute scores as dot products of Q and K, scale by √d<sub>k</sub> (the vector dimension) to stabilize gradients, and softmax</p>
<h3 id="heading-positional-encoding-adding-order-to-sequences">Positional Encoding: Adding Order to Sequences</h3>
<p>One catch: since self-attention doesn’t inherently know the positions of tokens (unlike RNNs, which process in order), Transformers need a way to inject sequence order information. This is done via positional encoding. In the original Transformer, they added a deterministic positional embedding to token embeddings using sine and cosine functions of varying frequencies for each position Intuitively, these positional encodings produce a unique pattern for each position; the model can learn to infer relative positions (e.g. one position’s encoding dot-product another’s indicates how far apart they are). In practice, some implementations use learned positional embeddings (trainable vectors for each position index) instead of sinusoidal formulas, both work to give the model a sense of word order.</p>
<p>The result is that the input to the Transformer is enriched with positional information: <strong>TokenEmbedding + PositionEmbedding</strong> for each token. This allows the self-attention layer to be aware of sequence ordering (so it doesn’t treat a sentence as a bag of words).</p>
<h3 id="heading-multi-head-attention-many-perspectives-at-once">Multi-Head Attention: Many Perspectives at Once</h3>
<p>A single self-attention head (one set of Q, K, V projections) is great, but the Transformer improves on this with <strong>multi-head attention</strong>. Instead of computing one attention distribution, the Transformer uses multiple attention “heads” in parallel. Each head operates on the same input but with its own learned projection matrices, so it might focus on different aspects of the sequence. For example, one head could attend to syntactic relationships (e.g., noun-adjective), another to long-range dependencies, and yet another to anaphora resolution. The idea is that multiple heads allow the model to capture diverse types of relationships simultaneously.</p>
<p>In practice, if the model’s hidden size is \(d_{model}\), and we choose \(h\) heads, we project the input into \(h\) different subspaces of dimension \(d_k = d_{model}/h\). Each head yields its own attention output (size \(n \times d_v\); often \(d_v\) is chosen equal to \(d_k\)). We then concatenate the outputs of all heads (getting back \(n \times d_{model}\)), and pass it through a final linear layer so that multi-head attention still produces an output of the same dimension \(d_{model}\).</p>
<p>Formally, if \(head_i(Q,K,V)\) is the output of the \(i\)-th attention head, then:</p>
<p>\[MultiHead(Q,K,V) = Concat(head_1, ..., head_h) W^O\]</p><p>where \(W^O\) is a learned projection matrix.</p>
<p>Don’t worry if the details sound complex; the key takeaway is that each head can attend to different patterns or parts of the sequence, and the model merges this information. Empirically, this improves the model’s capacity and performance.</p>
<h3 id="heading-feed-forward-networks-and-residual-connections">Feed-Forward Networks and Residual Connections</h3>
<p>After the attention mechanism, each Transformer layer includes a simple <strong>position-wise feed-forward network (FFN)</strong>. This is just an MLP applied to each token’s vector independently: typically a linear layer expanding the dimension (e.g., from \(d_{model}\) to some larger \(d_{ff}\)), a non-linearity (ReLU), then a linear layer back to \(d_{model}\). For example, in Vaswani et al., \(d_{model}=512\) and \(d_{ff}=2048\) – so a token’s 512-dim representation is converted to 2048-dim, non-linearly transformed, then back to 512-dim. This FFN gives the model extra transformation power at each position (learning higher-level features or mixing the information that attention collected). Importantly, because the feed-forward is applied to each position separately (no interactions across tokens), it does not destroy the contextual mixing done by attention; instead, it processes each token’s enriched representation in isolation. This also means it can be parallelized for all tokens.</p>
<p>Critically, Transformers employ <strong>residual connections (skip connections)</strong> and <strong>layer normalization</strong> around both the attention sub-layer and the FFN sub-layer. In each layer, the input to the sub-layer is added to its output (“adding the skip”) and then normalized. These residual connections help gradients flow and allow the model to retain the original input features while the sub-layer learns a refinement. Layer normalization stabilizes training by normalizing the output at each layer. In simple terms, the computation in one Transformer layer looks like:</p>
<h3 id="heading-self-attention-layer">Self-Attention layer:</h3>
<p>\[X_{\text{attn}} = \text{SelfAttn}(\text{LayerNorm}(X_{\text{in}}))\]</p><p>Then:</p>
<p>\[X_{\text{mid}} = X_{\text{in}} + X_{\text{attn}}\]</p><p>(This applies attention to normalized input, then adds it back to the input).</p>
<h3 id="heading-feed-forward-layer">Feed-Forward layer:</h3>
<p>\[X_{\text{ffn}} = \text{FFN}(\text{LayerNorm}(X_{\text{mid}}))\]</p><p>Then:</p>
<p>\[X_{\text{out}} = X_{\text{mid}} + X_{\text{ffn}}\]</p><p>This "add &amp; normalize" structure is repeated in every layer. It allows deep stacking of layers without losing the original signal or causing training to diverge.</p>
<h3 id="heading-transformer-architecture-recap">Transformer Architecture Recap</h3>
<p>Putting it all together, the original Transformer architecture consists of an <strong>Encoder</strong> (stack of N identical layers, each with self-attention + FFN) and a <strong>Decoder</strong> (stack of N layers, each with self-attention, <strong>encoder-decoder attention,</strong> aka cross-attention, and FFN). The encoder-decoder attention in each decoder layer allows the decoder to attend to the encoder’s output (source sentence) in addition to attending to previous decoder tokens. This is crucial for tasks like translation, where the decoder needs to focus on the source sequence. In purely generative settings (like GPT language models), we don’t use an encoder at all; we only have decoder blocks with self-attention.</p>
<p>One important detail for <strong>decoder self-attention</strong> (used in language generation) is <strong>masking</strong>. The decoder operates autoregressively: when predicting the next token, it should not peek at later positions. Thus, decoder self-attention uses a <strong>causal mask</strong> to zero out attention weights to future tokens. This mask is a triangular matrix that prevents information flow from position <em>j</em> to <em>i</em> if <em>j &gt; i</em>. In training, this masking ensures the model can’t trivially see the ground-truth next word; at inference, it naturally only attends to already generated tokens.</p>
<p>Now that we’ve covered the theory, let’s get our hands dirty with a practical implementation of these ideas in a mini GPT model.</p>
<h2 id="heading-building-gptmini-an-end-to-end-example">Building GPTMini: An End-to-End Example</h2>
<p>Our project, <strong>GPTMini,</strong> is a scaled-down GPT-like language model. We will outline each component: a custom Byte-Pair Encoding tokenizer, preparing the WikiText dataset, defining the Transformer (decoder-only) architecture in code, training the model, and finally generating text. The goal is to demystify how these pieces fit together in code, step by step.</p>
<h3 id="heading-byte-pair-encoding-bpe-tokenizer">Byte-Pair Encoding (BPE) Tokenizer</h3>
<p>Real-world Transformer models don’t typically operate on characters or whole words directly – they use subword tokenization. Byte-Pair Encoding (BPE) is a popular subword tokenization algorithm used by GPT-2, GPT-3, BERT, and others. BPE was originally a data compression technique (Gage, 1994) where the most frequent pair of bytes in a text is merged into a single symbol, iteratively. In NLP tokenization, we start with all characters (or bytes) as initial tokens and repeatedly merge the most frequent adjacent token pair to form a new token until we reach a desired vocabulary size. This allows common multi-character sequences (like “ing” or “tion”) to become one token, while rare sequences remain split into smaller pieces. The result is a vocabulary of subword units that balance vocabulary size and representation of word parts, handling unknown words gracefully (any word can be composed from subword pieces).</p>
<p>In GPTMini, we implemented a custom BPE tokenizer from scratch. First, we gather the corpus text and initialize the vocabulary with all unique characters. Then we count character pair frequencies and merge the most frequent pair into a new token, update the corpus by replacing that pair everywhere, and repeat. For example, suppose the corpus has “low” and “lowest” frequently – BPE might merge “l” + “o” -&gt; “lo”, then perhaps “lo” + “w” -&gt; “low”, and eventually “low” + “est” -&gt; “lowest”. After enough merges, common words or chunks become single tokens.</p>
<p>Here’s a simplified snippet (for illustration) of how one might learn BPE merges in Python:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> collections <span class="hljs-keyword">import</span> Counter

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">learn_bpe_merges</span>(<span class="hljs-params">corpus_tokens, num_merges</span>):</span>
    <span class="hljs-comment"># corpus_tokens is a list of lists of tokens (start with char tokens per word)</span>
    <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(num_merges):
        <span class="hljs-comment"># Count all adjacent token pairs in the corpus</span>
        pair_counts = Counter()
        <span class="hljs-keyword">for</span> tokens <span class="hljs-keyword">in</span> corpus_tokens:
            <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(len(tokens)<span class="hljs-number">-1</span>):
                pair = (tokens[i], tokens[i+<span class="hljs-number">1</span>])
                pair_counts[pair] += <span class="hljs-number">1</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> pair_counts: 
            <span class="hljs-keyword">break</span>
        <span class="hljs-comment"># Find the most frequent pair</span>
        best_pair = max(pair_counts, key=pair_counts.get)
        <span class="hljs-comment"># Merge the best pair in all token sequences</span>
        new_token = best_pair[<span class="hljs-number">0</span>] + best_pair[<span class="hljs-number">1</span>]
        <span class="hljs-keyword">for</span> i, tokens <span class="hljs-keyword">in</span> enumerate(corpus_tokens):
            corpus_tokens[i] = merge_tokens(tokens, best_pair, new_token)
        <span class="hljs-comment"># Add new_token to vocabulary (and remove old tokens if needed)</span>
        vocab.add(new_token)
    <span class="hljs-keyword">return</span> vocab

<span class="hljs-comment"># Helper to merge a specific pair in one token list</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">merge_tokens</span>(<span class="hljs-params">token_list, pair, merged_token</span>):</span>
    i = <span class="hljs-number">0</span>
    merged_list = []
    <span class="hljs-keyword">while</span> i &lt; len(token_list):
        <span class="hljs-keyword">if</span> i &lt; len(token_list)<span class="hljs-number">-1</span> <span class="hljs-keyword">and</span> (token_list[i], token_list[i+<span class="hljs-number">1</span>]) == pair:
            merged_list.append(merged_token)
            i += <span class="hljs-number">2</span>  <span class="hljs-comment"># skip the merged pair</span>
        <span class="hljs-keyword">else</span>:
            merged_list.append(token_list[i])
            i += <span class="hljs-number">1</span>
    <span class="hljs-keyword">return</span> merged_list
</code></pre>
<p>In practice, one would also handle spaces or use byte-level BPE (operating on raw bytes to include all characters, which GPT-2 did for robustness. After training BPE on our WikiText corpus, we save the merge rules and vocabulary. Then we can use it to encode any text into a sequence of token IDs and decode IDs back to text.</p>
<p>For example, after training, encoding a word like “transformers” might yield tokens <code>["trans", "form", "ers"]</code> if those subwords are in the vocab. The tokenizer also appends special tokens as needed (like an end-of-sequence or padding token, though GPT usually doesn’t use start/end tokens explicitly, just an end-of-text token).</p>
<p><strong>Why BPE?</strong> It provides a balanced vocabulary where common words are one token and uncommon words are split into meaningful pieces. This drastically reduces out-of-vocabulary issues compared to word-level tokenization and is more efficient than character-level (which would make sequences very long). BPE is used by GPT-2 and GPT-3, for instance, to achieve a vocabulary of around 50,000 tokens that can encode text from a variety of languages</p>
<h3 id="heading-dataset-preparation-with-wikitext">Dataset Preparation with WikiText</h3>
<p>For training GPTMini, we need a large text corpus. We chose the WikiText dataset, a popular benchmark for language modeling. WikiText-2 (the smaller version) contains about 2 million tokens of Wikipedia articles (extracted from “Good” and “Featured” quality articles), and WikiText-103 (the full version) has over 100 million tokens. These are clean, curated Wikipedia texts that are great for training a language model from scratch.</p>
<p>We downloaded the WikiText corpus and applied our BPE tokenizer to it, converting the text into a stream of token IDs. Since language models are usually trained to predict the next token in a sequence, we formatted the data as sequences of a fixed length (say, 128 tokens long). Each training sample is a snippet of the tokenized text (128 tokens), and the target is the same sequence shifted one position to the left (so at each position, the model learns to predict the next token). We slide this window through the entire corpus to generate many training examples. Alternatively, one can randomly sample chunks of text of length 128 each epoch.</p>
<p>Here’s how we can create training sequences in code:</p>
<pre><code class="lang-python">token_ids = tokenizer.encode(full_text)  <span class="hljs-comment"># convert entire text to list of token IDs</span>
seq_length = <span class="hljs-number">128</span>
train_data = []
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">0</span>, len(token_ids) - seq_length):
    input_seq = token_ids[i : i+seq_length]
    target_seq = token_ids[i+<span class="hljs-number">1</span> : i+seq_length+<span class="hljs-number">1</span>]
    train_data.append((input_seq, target_seq))
</code></pre>
<p>In practice, we wouldn’t explicitly materialize train_data as a list of all sequences (which could be huge); instead, we use a Dataset object that reads batches on the fly. For example, in PyTorch, you might create a torch.utils.data.Dataset that wraps the token list and yields slices of length 128. We also shuffle the data or randomize starting positions for each epoch to alleviate fixed segmentation.</p>
<p>After this preparation, we have a dataset of input-target pairs of token sequences. Now we’re ready to build the Transformer model that will learn to predict tokens.</p>
<h3 id="heading-the-gptmini-model-transformer-decoder-architecture">The GPTMini Model: Transformer Decoder Architecture</h3>
<p>GPTMini’s architecture is a <strong>Transformer decoder,</strong> essentially the same building blocks as the original Transformer’s decoder part, but possibly with fewer layers or smaller dimensions for simplicity. Conceptually, our model will do the following:</p>
<p><em>Take a sequence of input tokens, embed them, add positional encodings, pass them through a stack of Transformer decoder blocks (with self-attention and feed-forward sublayers), and output a probability distribution for the next token at each position.</em> Because we train it with next-token prediction, when we feed a sequence, the model’s output at position <em>t</em> is trying to predict the token at <em>t+1</em>.</p>
<p>Let’s break down the components in code. We’ll use PyTorch-like pseudo-code for clarity:</p>
<p><strong>Embeddings:</strong> We need an embedding layer for token IDs, and we’ll add positional encodings. If we use learnable position embeddings, we can just have a second embedding table for positions.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> torch.nn <span class="hljs-keyword">as</span> nn

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GPTMini</span>(<span class="hljs-params">nn.Module</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, vocab_size, d_model, max_seq_len, n_layers, n_heads, d_ff</span>):</span>
        super().__init__()
        self.token_emb = nn.Embedding(vocab_size, d_model)
        self.pos_emb   = nn.Embedding(max_seq_len, d_model)
        self.layers    = nn.ModuleList([
            DecoderBlock(d_model, n_heads, d_ff) <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(n_layers)
        ])
        self.norm      = nn.LayerNorm(d_model)
        self.out_head  = nn.Linear(d_model, vocab_size)
</code></pre>
<p>Here <code>DecoderBlock</code> will be a class implementing one Transformer decoder layer (self-attention + feed-forward). We pass in <code>n_heads</code> and <code>d_ff</code> (hidden size of feed-forward) as well. We also have a final linear <code>out_head</code> layer that maps the final hidden state of each position to vocabulary logits for prediction.</p>
<p><strong>Self-Attention Sub-layer:</strong> We implement this as part of the DecoderBlock. We need to compute multi-head masked self-attention. Let’s define a single attention head first, then generalize.</p>
<p>A simplified single-head attention (without batching for brevity) could be:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">single_head_attention</span>(<span class="hljs-params">Q, K, V, mask=None</span>):</span>
    scores = Q @ K.T              <span class="hljs-comment"># (T_q × T_k) raw attention scores</span>
    scores = scores / math.sqrt(Q.size(<span class="hljs-number">1</span>))  <span class="hljs-comment"># scale by √d_k</span>
    <span class="hljs-keyword">if</span> mask <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">None</span>:
        scores.masked_fill_(mask == <span class="hljs-number">0</span>, float(<span class="hljs-string">'-inf'</span>))  <span class="hljs-comment"># mask out future positions</span>
    weights = scores.softmax(dim=<span class="hljs-number">-1</span>)   <span class="hljs-comment"># (T_q × T_k) attention weights</span>
    <span class="hljs-keyword">return</span> weights @ V                <span class="hljs-comment"># weighted sum of values (T_q × d_v)</span>
</code></pre>
<p>In a multi-head setting, we have <code>n_heads</code> different projection matrices for Q, K, V. In code, we often combine them for efficiency. For example, we project the input <code>X</code> (of shape <code>[batch_size, T, d_model]</code>) into Q, K, V of shapes <code>[batch_size, T, d_model]</code> each, then split those into <code>[batch_size, n_heads, T, d_k]</code>. Each head then performs the above operation in parallel. After that, we concatenate the heads and project back to <code>d_model</code>. Here’s how a multi-head self-attention forward might look:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MultiheadSelfAttention</span>(<span class="hljs-params">nn.Module</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, d_model, n_heads</span>):</span>
        super().__init__()
        <span class="hljs-keyword">assert</span> d_model % n_heads == <span class="hljs-number">0</span>
        self.d_k = d_model // n_heads
        self.n_heads = n_heads
        <span class="hljs-comment"># Projection layers for Q, K, V (we use one big weight for all heads for each)</span>
        self.W_q = nn.Linear(d_model, d_model)
        self.W_k = nn.Linear(d_model, d_model)
        self.W_v = nn.Linear(d_model, d_model)
        <span class="hljs-comment"># Output projection</span>
        self.W_o = nn.Linear(d_model, d_model)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">forward</span>(<span class="hljs-params">self, X, mask=None</span>):</span>
        B, T, C = X.shape  <span class="hljs-comment"># batch, seq_length, d_model</span>
        <span class="hljs-comment"># Project inputs to multi-head Q, K, V</span>
        Q = self.W_q(X)  <span class="hljs-comment"># shape (B, T, C)</span>
        K = self.W_k(X)
        V = self.W_v(X)
        <span class="hljs-comment"># Split into heads: reshape to (B, T, n_heads, d_k) then transpose</span>
        Q = Q.view(B, T, self.n_heads, self.d_k).transpose(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>)  <span class="hljs-comment"># (B, n_heads, T, d_k)</span>
        K = K.view(B, T, self.n_heads, self.d_k).transpose(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>)  <span class="hljs-comment"># (B, n_heads, T, d_k)</span>
        V = V.view(B, T, self.n_heads, self.d_k).transpose(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>)  <span class="hljs-comment"># (B, n_heads, T, d_k)</span>
        <span class="hljs-comment"># Compute scaled dot-product attention for each head</span>
        <span class="hljs-comment"># (We transpose K to (B, n_heads, d_k, T) for matmul)</span>
        scores = torch.matmul(Q, K.transpose(<span class="hljs-number">-2</span>, <span class="hljs-number">-1</span>)) / math.sqrt(self.d_k)  <span class="hljs-comment"># (B, n_heads, T, T)</span>
        <span class="hljs-keyword">if</span> mask <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">None</span>:
            scores = scores.masked_fill(mask[:, <span class="hljs-literal">None</span>, <span class="hljs-literal">None</span>, :] == <span class="hljs-number">0</span>, <span class="hljs-number">-1e9</span>)
        weights = torch.softmax(scores, dim=<span class="hljs-number">-1</span>)  <span class="hljs-comment"># (B, n_heads, T, T)</span>
        attn_output = torch.matmul(weights, V)   <span class="hljs-comment"># (B, n_heads, T, d_k)</span>
        <span class="hljs-comment"># Concatenate heads back together</span>
        attn_output = attn_output.transpose(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>).contiguous().view(B, T, C)  <span class="hljs-comment"># (B, T, d_model)</span>
        <span class="hljs-keyword">return</span> self.W_o(attn_output)  <span class="hljs-comment"># final linear proj</span>
</code></pre>
<p>Don’t worry if the tensor manipulations are a bit much, the idea is we computed attention for each head and recombined. The <code>mask</code> we pass will be a tensor that is 0 where we want to mask out future tokens (for positions beyond the current timestep in the decoder) and 1 elsewhere. This ensures the model only attends to positions ≤ current position.</p>
<p><strong>Decoder Block:</strong> Now we create the <code>DecoderBlock</code> using the attention and a feed-forward sub-layer:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DecoderBlock</span>(<span class="hljs-params">nn.Module</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, d_model, n_heads, d_ff</span>):</span>
        super().__init__()
        self.self_attn = MultiheadSelfAttention(d_model, n_heads)
        self.feed_forward = nn.Sequential(
            nn.Linear(d_model, d_ff),
            nn.ReLU(),
            nn.Linear(d_ff, d_model),
        )
        self.ln1 = nn.LayerNorm(d_model)
        self.ln2 = nn.LayerNorm(d_model)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">forward</span>(<span class="hljs-params">self, x, mask=None</span>):</span>
        <span class="hljs-comment"># Self-attention sub-layer with residual connection</span>
        attn_out = self.self_attn(self.ln1(x), mask=mask)
        x = x + attn_out
        <span class="hljs-comment"># Feed-forward sub-layer with residual connection</span>
        ff_out = self.feed_forward(self.ln2(x))
        x = x + ff_out
        <span class="hljs-keyword">return</span> x
</code></pre>
<p>Each DecoderBlock applies layer norm to the input (<code>ln1(x)</code>), performs masked self-attention, adds it back, then layer norm again and feed-forward, and adds it back. We don’t show an encoder-decoder attention here because GPTMini is decoder-only (no encoder to attend to).</p>
<p>Now we can complete the <code>GPTMini.forward</code> method:</p>
<pre><code class="lang-python">    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">forward</span>(<span class="hljs-params">self, idx</span>):</span>
        B, T = idx.shape  <span class="hljs-comment"># idx is input token indices (batch, seq_length)</span>
        <span class="hljs-comment"># Create causal mask for attention (shape T×T, lower triangular ones)</span>
        device = idx.device
        mask = torch.tril(torch.ones(T, T, device=device)).bool()  <span class="hljs-comment"># True where allowed</span>
        <span class="hljs-comment"># Embed tokens and positions</span>
        token_embeddings = self.token_emb(idx)            <span class="hljs-comment"># (B, T, d_model)</span>
        positions = torch.arange(<span class="hljs-number">0</span>, T, device=device).unsqueeze(<span class="hljs-number">0</span>)  <span class="hljs-comment"># (1, T)</span>
        pos_embeddings = self.pos_emb(positions)          <span class="hljs-comment"># (1, T, d_model)</span>
        x = token_embeddings + pos_embeddings             <span class="hljs-comment"># combine token + position</span>
        <span class="hljs-comment"># Apply each Transformer decoder layer</span>
        <span class="hljs-keyword">for</span> layer <span class="hljs-keyword">in</span> self.layers:
            x = layer(x, mask=mask)
        <span class="hljs-comment"># Final layer norm (often used at end of Transformer decoder stack, e.g. GPT-2)</span>
        x = self.norm(x)  <span class="hljs-comment"># (B, T, d_model)</span>
        <span class="hljs-comment"># Output logits for each position and token in vocab</span>
        logits = self.out_head(x)  <span class="hljs-comment"># (B, T, vocab_size)</span>
        <span class="hljs-keyword">return</span> logits
</code></pre>
<p>A few things to note: we constructed a <code>mask</code> using <code>torch.tril</code> (lower-triangular matrix of ones) which will be broadcast to shape (B, n_heads, T, T) inside attention to mask out future positions. We add token and positional embeddings to get our initial <em>x</em>. After the Transformer layers, <code>logits</code> contains scores for each vocabulary token at each sequence position.</p>
<p>During <strong>training</strong>, we will call <code>model(input_ids)</code> to get <code>logits</code>, which has shape <code>[batch_size, seq_length, vocab_size]</code>. We then compare it to the <code>target_ids</code> (the ground-truth next tokens) to compute loss. Typically we use CrossEntropy loss applied to each position. In PyTorch, we’d flatten the predictions and targets to shape <code>[batch_size*seq_length]</code> when computing <code>F.cross_entropy(logits.view(-1, vocab_size), targets.view(-1))</code>.</p>
<p>To recap, GPTMini’s architecture includes everything we discussed: token + positional embeddings, multi-head <em>masked</em> self-attention in each layer, residual connections with layer norm, feed-forward networks, and an output projection. It’s essentially a small GPT model.</p>
<h3 id="heading-training-loop">Training Loop</h3>
<p>With the model and data ready, we train GPTMini using a next-token prediction objective (language modeling). We iterate over the dataset of input-target sequences, and for each batch, do the following:</p>
<ol>
<li><p><strong>Forward pass:</strong> Compute <code>logits = model(input_ids)</code>.</p>
</li>
<li><p><strong>Loss computation:</strong> Compare <code>logits</code> to <code>target_ids</code>. We use cross-entropy loss, which is perfect for classification over the vocabulary for each position. In code: <code>loss = F.cross_entropy(logits.view(-1, vocab_size), target_ids.view(-1))</code>. This calculates the average negative log-likelihood of the correct tokens.</p>
</li>
<li><p><strong>Backward pass:</strong> <a target="_blank" href="http://optimizer.zero"><code>optimizer.zero</code></a><code>_grad(); loss.backward(); optimizer.step()</code>. We typically use the Adam optimizer (often with weight decay and a learning rate schedule) as used in the original Transformer paper.</p>
</li>
</ol>
<p>A simplified training loop:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> torch
<span class="hljs-keyword">import</span> torch.nn.functional <span class="hljs-keyword">as</span> F

model = GPTMini(vocab_size, d_model=<span class="hljs-number">384</span>, max_seq_len=<span class="hljs-number">128</span>, n_layers=<span class="hljs-number">6</span>, n_heads=<span class="hljs-number">6</span>, d_ff=<span class="hljs-number">1024</span>)
optimizer = torch.optim.AdamW(model.parameters(), lr=<span class="hljs-number">1e-3</span>)

<span class="hljs-keyword">for</span> epoch <span class="hljs-keyword">in</span> range(num_epochs):
    <span class="hljs-keyword">for</span> input_ids, target_ids <span class="hljs-keyword">in</span> train_loader:
        logits = model(input_ids)              <span class="hljs-comment"># (batch, T, vocab_size)</span>
        loss = F.cross_entropy(logits.view(<span class="hljs-number">-1</span>, vocab_size), target_ids.view(<span class="hljs-number">-1</span>))
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    print(<span class="hljs-string">f"Epoch <span class="hljs-subst">{epoch}</span> loss: <span class="hljs-subst">{loss.item():<span class="hljs-number">.4</span>f}</span>"</span>)
</code></pre>
<p>We also validate the model on a held-out validation set (WikiText provides train/validation/test splits) to ensure it’s not overfitting and to track its performance (often measured in perplexity, which is \(\exp(\text{loss})\)). Perplexity tells us how well the model predicts the sample on average the lower, the better.</p>
<h3 id="heading-text-generation-with-gptmini">Text Generation with GPTMini</h3>
<p>Once trained, we can use GPTMini to <strong>generate text</strong> by sampling from the model’s output distribution iteratively. Here’s the general procedure for text generation (also known as <strong>autoregressive decoding</strong>):</p>
<ol>
<li><p>Start with a <strong>prompt</strong> (some initial text). Tokenize it to get input IDs.</p>
</li>
<li><p>Feed the input through the model to get output logits for the next token.</p>
</li>
<li><p>Convert logits to probabilities (e.g., apply softmax, possibly with a temperature to control randomness).</p>
</li>
<li><p>Sample a token from the probability distribution (or pick the highest probability token for greedy decoding).</p>
</li>
<li><p>Append the sampled token to the input sequence.</p>
</li>
<li><p>Repeat steps 2–5 until you reach a desired length or an end-of-sequence token.</p>
</li>
</ol>
<p>For example, suppose we want GPTMini to generate text after the prompt <em>"Once upon a time"</em>:</p>
<pre><code class="lang-python">prompt = <span class="hljs-string">"Once upon a time"</span>
input_ids = tokenizer.encode(prompt)  <span class="hljs-comment"># list of token IDs</span>
<span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(<span class="hljs-number">50</span>):  <span class="hljs-comment"># generate 50 tokens</span>
    idx_tensor = torch.tensor(input_ids)[<span class="hljs-literal">None</span>, :]  <span class="hljs-comment"># shape (1, seq_len)</span>
    logits = model(idx_tensor)  <span class="hljs-comment"># forward pass</span>
    next_token_logits = logits[<span class="hljs-number">0</span>, <span class="hljs-number">-1</span>, :]          <span class="hljs-comment"># logits for the last token</span>
    <span class="hljs-comment"># Optionally apply temperature scaling and/or top-k filtering:</span>
    probs = torch.softmax(next_token_logits / temperature, dim=<span class="hljs-number">-1</span>)
    next_id = torch.multinomial(probs, num_samples=<span class="hljs-number">1</span>).item()  <span class="hljs-comment"># sample next token</span>
    input_ids.append(next_id)
</code></pre>
<p>After this loop, <code>input_ids</code> will contain the original prompt tokens plus 50 newly generated tokens. We can then decode <code>input_ids</code> back to text with <code>tokenizer.decode(input_ids)</code>.</p>
<p>To improve quality, one often uses strategies like <strong>top-k</strong> or <strong>top-p (nucleus) sampling</strong> to avoid low-probability tokens and encourage more plausible outputs. For instance, top-k sampling picks from the <em>k</em> most probable tokens (e.g., k=50) instead of the entire distribution, which reduces the chance of odd words. Top-p sampling chooses the smallest set of tokens whose cumulative probability exceeds a threshold p (e.g,. 0.9), and samples from that set.</p>
<p>GPTMini, being small, won’t produce Shakespearean prose, but it will learn basic English structure and generate somewhat coherent sentences related to Wikipedia topics in the training data. For example, if prompted with “Once upon a time”, it might continue with something like “in a small village, a young girl named Alice was reading about kings and queens.” (The output will vary each time due to randomness in sampling.)</p>
<h3 id="heading-putting-it-all-together">Putting It All Together</h3>
<p>We’ve now gone from theoretical foundations to a working miniature GPT model:</p>
<ul>
<li><p><strong>Attention mechanisms</strong> enabled the model to handle dependencies without RNNs, examining all words at once</p>
</li>
<li><p>The <strong>Transformer architecture</strong> uses stacked layers of multi-head self-attention and feed-forward networks with residual connections, allowing deep, parallel computation.</p>
</li>
<li><p>We built a <strong>BPE tokenizer</strong> to preprocess text into subword units, which is exactly how real GPT models handle open-vocabulary input</p>
</li>
<li><p>We prepared the <strong>WikiText</strong> dataset as a training corpus, a standard benchmark with millions of tokens from Wikipedia</p>
</li>
<li><p>We defined the <strong>GPTMini model</strong> in code, including masked multi-head self-attention and all the trimmings of a Transformer decoder.</p>
</li>
<li><p>We ran a <strong>training loop</strong> optimizing next-word prediction, and then used the trained model to <strong>generate text</strong> with a simple sampling strategy.</p>
</li>
</ul>
<h2 id="heading-key-takeaways-and-future-directions">Key Takeaways and Future Directions</h2>
<p>Transformers and attention mechanisms have transformed (pun intended) the field of NLP. By letting models attend to all parts of the input, we overcome the bottlenecks of older architectures and achieve greater parallelism and context integration. The self-attention mechanism is the backbone of modern large language models, enabling them to capture meaning across entire documents. In our GPTMini, we saw how these ideas materialize in code, from tokenization to model forward pass.</p>
<p>Going further, one could experiment with improvements and extensions: for example, adding <strong>Dropout</strong> in the model (the original Transformer used dropout in various places) for regularization, using <strong>adaptive softmax</strong> or tied input-output embeddings for efficiency, scaling up the model size, or pretraining on a larger corpus. Modern large language models also introduce techniques like <strong>transformer-decoder block scaling, learning rate warmups, gradient clipping</strong>, and more – but the essence remains the architecture we’ve built.</p>
<p>Transformers aren’t limited to text; they’re used in vision (ViT – Vision Transformer), audio, and multi-modal models. The versatility of attention mechanisms means they can be applied wherever we have sequential or set data that benefits from global interactions.</p>
<p>We hope this guided tour – from the intuition of attention to coding a mini GPT – has demystified how these powerful models work. With these fundamentals, you can better understand breakthroughs like GPT-3 and beyond, or even build your own Transformer models for fun and learning. Happy experimenting with attention and Transformers, sometimes, a little attention goes a long way!</p>
]]></content:encoded></item><item><title><![CDATA[How To Build A BlockChain Using Python]]></title><description><![CDATA[To create a blockchain using Python, you will need to do the following:

Import the necessary modules and libraries. This will typically include the hashlib library for generating hashes and the datetime library for managing timestamps.

Copy codeimp...]]></description><link>https://blog.abhinavtb.com/how-to-build-a-blockchain-using-python</link><guid isPermaLink="true">https://blog.abhinavtb.com/how-to-build-a-blockchain-using-python</guid><category><![CDATA[Blockchain]]></category><category><![CDATA[Web3]]></category><dc:creator><![CDATA[Abhinav T B]]></dc:creator><pubDate>Mon, 12 Dec 2022 05:21:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1670822478238/STdQk1yUUF.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>To create a blockchain using Python, you will need to do the following:</p>
<ol>
<li>Import the necessary modules and libraries. This will typically include the <code>hashlib</code> library for generating hashes and the <code>datetime</code> library for managing timestamps.</li>
</ol>
<pre><code class="lang-python">Copy codeimport hashlib
<span class="hljs-keyword">import</span> datetime
</code></pre>
<ol>
<li>Define a <code>Block</code> the class that will represent each block in the chain. This class should have the following attributes:</li>
</ol>
<ul>
<li><p><code>timestamp</code>: a timestamp representing the time at which the block was created</p>
</li>
<li><p><code>data</code>: the data that the block will store (e.g. a transaction record)</p>
</li>
<li><p><code>previous_hash</code>: a hash of the previous block in the chain</p>
</li>
<li><p><code>hash</code>: a hash of the current block</p>
</li>
</ul>
<pre><code class="lang-python">Copy codeclass Block:
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, timestamp, data, previous_hash</span>):</span>
    self.timestamp = timestamp
    self.data = data
    self.previous_hash = previous_hash
    self.hash = self.calculate_hash()

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">calculate_hash</span>(<span class="hljs-params">self</span>):</span>
    <span class="hljs-comment"># Generate the hash of the block using the SHA-256 algorithm</span>
    sha = hashlib.sha256()
    sha.update(self.timestamp.encode(<span class="hljs-string">'utf-8'</span>) + self.data.encode(<span class="hljs-string">'utf-8'</span>) + self.previous_hash.encode(<span class="hljs-string">'utf-8'</span>))
    <span class="hljs-keyword">return</span> sha.hexdigest()
</code></pre>
<ol>
<li>Define a <code>Blockchain</code> class that will manage the blocks in the chain. This class should have the following attributes and methods:</li>
</ol>
<ul>
<li><p><code>chain</code>: a list of blocks in the chain</p>
</li>
<li><p><code>create_genesis_block()</code>: a method that creates the first block in the chain (also called the "genesis block")</p>
</li>
<li><p><code>latest_block()</code>: a method that returns the latest block in the chain</p>
</li>
<li><p><code>add_block(data)</code>: a method that adds a new block to the chain</p>
</li>
</ul>
<pre><code class="lang-python">Copy codeclass Blockchain:
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
    self.chain = [self.create_genesis_block()]

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_genesis_block</span>(<span class="hljs-params">self</span>):</span>
    <span class="hljs-comment"># Create the first block in the chain</span>
    timestamp = datetime.datetime.now()
    data = <span class="hljs-string">"Genesis Block"</span>
    previous_hash = <span class="hljs-string">"0"</span>
    <span class="hljs-keyword">return</span> Block(timestamp, data, previous_hash)

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">latest_block</span>(<span class="hljs-params">self</span>):</span>
    <span class="hljs-comment"># Return the latest block in the chain</span>
    <span class="hljs-keyword">return</span> self.chain[<span class="hljs-number">-1</span>]

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">add_block</span>(<span class="hljs-params">self, data</span>):</span>
    <span class="hljs-comment"># Create a new block and add it to the chain</span>
    timestamp = datetime.datetime.now()
    previous_hash = self.latest_block().hash
    new_block = Block(timestamp, data, previous_hash)
    self.chain.append(new_block)
</code></pre>
<ol>
<li>Create an instance of the <code>Blockchain</code> class, add some data to it, and print the resulting chain to verify that it is working as expected.</li>
</ol>
<pre><code class="lang-python">Copy codeblockchain = Blockchain()
blockchain.add_block(<span class="hljs-string">"Transaction Data 1"</span>)
blockchain.add_block(<span class="hljs-string">"Transaction Data 2"</span>)

<span class="hljs-keyword">for</span> block <span class="hljs-keyword">in</span> blockchain.chain:
  print(<span class="hljs-string">f"Timestamp: <span class="hljs-subst">{block.timestamp}</span>"</span>)
  print(<span class="hljs-string">f"Data: <span class="hljs-subst">{block.data}</span>"</span>)
</code></pre>
<p>To complete the implementation of the blockchain, you will need to add some additional functionality to the <code>Block</code> and <code>Blockchain</code> classes.</p>
<p>For the <code>Block</code> class, you will need to add a method that verifies the integrity of the block by checking that the calculated <code>hash</code> of the block matches the <code>hash</code> stored in the block itself. You can do this by adding the following method to the <code>Block</code> class:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">is_valid</span>(<span class="hljs-params">self</span>):</span>
    <span class="hljs-comment"># Check if the calculated hash of the block matches the stored hash</span>
    <span class="hljs-keyword">return</span> self.calculate_hash() == self.hash
</code></pre>
<p>For the <code>Blockchain</code> class, you will need to add a method that verifies the integrity of the entire chain by checking that the <code>hash</code> of each block matches the previous block's <code>hash</code>, and that each block is valid according to the <code>is_valid()</code> method of the <code>Block</code> class. You can do this by adding the following method to the <code>Blockchain</code> class:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">is_chain_valid</span>(<span class="hljs-params">self</span>):</span>
    <span class="hljs-comment"># Check if each block in the chain is valid</span>
    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">1</span>, len(self.chain)):
        current_block = self.chain[i]
        previous_block = self.chain[i<span class="hljs-number">-1</span>]
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> current_block.is_valid():
            <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>
        <span class="hljs-keyword">if</span> current_block.previous_hash != previous_block.hash:
            <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>
</code></pre>
<p>Once you have added these methods, your blockchain should be complete and ready for use. You can test it by adding some data to the chain and verifying that it is valid according to the <code>is_chain_valid()</code> method.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create a new blockchain</span>
blockchain = Blockchain()

<span class="hljs-comment"># Add some data to the blockchain</span>
blockchain.add_block(<span class="hljs-string">"Transaction Data 1"</span>)
blockchain.add_block(<span class="hljs-string">"Transaction Data 2"</span>)

<span class="hljs-comment"># Check if the chain is valid</span>
<span class="hljs-keyword">if</span> blockchain.is_chain_valid():
  print(<span class="hljs-string">"The blockchain is valid."</span>)
<span class="hljs-keyword">else</span>:
  print(<span class="hljs-string">"The blockchain is not valid."</span>)
</code></pre>
<p>This is just a basic implementation of a blockchain using Python. In a real-world scenario, you would need to add additional functionality to make it more robust and secure, such as the ability to handle multiple transactions in each block and the ability to handle multiple chains (i.e. a "fork" in the blockchain).</p>
]]></content:encoded></item><item><title><![CDATA[Natural Language Processing]]></title><description><![CDATA[Natural Language Processing (NLP) is a field of artificial intelligence that focuses on enabling computers to understand and process human language. NLP techniques are used in a wide range of applications, including language translation, text summari...]]></description><link>https://blog.abhinavtb.com/natural-language-processing</link><guid isPermaLink="true">https://blog.abhinavtb.com/natural-language-processing</guid><category><![CDATA[nlp]]></category><category><![CDATA[Machine Learning]]></category><category><![CDATA[natural language processing]]></category><dc:creator><![CDATA[Abhinav T B]]></dc:creator><pubDate>Fri, 09 Dec 2022 10:35:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1670582095222/6qWX1ZTY1.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Natural Language Processing (NLP) is a field of artificial intelligence that focuses on enabling computers to understand and process human language. NLP techniques are used in a wide range of applications, including language translation, text summarization, sentiment analysis, and question-answering.</p>
<p>One of the key challenges in NLP is the fact that human language is incredibly complex and varied. There are thousands of languages, dialects, and accents, and even within a single language there can be a wide range of vocabulary, syntax, and grammar. This makes it difficult for computers to accurately process and understand natural language inputs.</p>
<p>To overcome this challenge, NLP researchers and practitioners use a variety of techniques and algorithms. These include:</p>
<ul>
<li><p>Part-of-speech tagging: This is the process of identifying the part of speech (e.g. noun, verb, adjective) for each word in a sentence. Part-of-speech tagging is important for understanding the structure and meaning of a sentence.</p>
</li>
<li><p>Named entity recognition: This is the process of identifying and classifying named entities (e.g. people, organizations, locations) in a text. Named entity recognition is important for extracting useful information from a text.</p>
</li>
<li><p>Sentiment analysis: This is the process of determining the sentiment (e.g. positive, negative, neutral) of a piece of text. Sentiment analysis is often used to gauge the public opinion of a product, service, or event.</p>
</li>
<li><p>Machine translation: This is the process of automatically translating text from one language to another. Machine translation has improved dramatically in recent years, but it still faces challenges in dealing with complex syntax and cultural differences.</p>
</li>
</ul>
<p>One of the most promising areas of NLP research is the development of deep learning algorithms. These algorithms use neural networks to learn the underlying patterns and structures of language and can achieve impressive results in tasks such as language translation and text summarization.</p>
<p>In conclusion, NLP is a fascinating and rapidly-evolving field of artificial intelligence that has a wide range of applications. By using sophisticated algorithms and techniques, NLP researchers and practitioners are making impressive progress in enabling computers to understand and process human language.</p>
]]></content:encoded></item><item><title><![CDATA[Block Chain For Web Developers]]></title><description><![CDATA[The word Blockchain is pretty popular among developers, and many are curious to know more about it. Most articles on the internet are scaring people with a lot of technical jargon. In this article, I aim to simplify it for existing web developers.
Le...]]></description><link>https://blog.abhinavtb.com/block-chain-for-web-developers</link><guid isPermaLink="true">https://blog.abhinavtb.com/block-chain-for-web-developers</guid><category><![CDATA[Blockchain]]></category><category><![CDATA[Web3]]></category><dc:creator><![CDATA[Abhinav T B]]></dc:creator><pubDate>Wed, 30 Nov 2022 11:44:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1670823089250/qsOSoJ-OQ.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The word Blockchain is pretty popular among developers, and many are curious to know more about it. Most articles on the internet are scaring people with a lot of technical jargon. In this article, I aim to simplify it for existing web developers.</p>
<p>Let's first understand how a typical web application works without blockchain. A website, after coding, is uploaded to a server, which serves the user's request. This server also connects to the database to fetch and dynamically deliver data to the user.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669808247303/323bfCRKy.png" alt /></p>
<p>Now let us see how blockchain is different from this former setup. A web app built using blockchain is called a web3 application, and most of the other web applications we use today without blockchain are called web3 applications. As a web2 application has a front end and a back end, a blockchain application has two parts. The major components of a web3 app are the front end and smart contracts. So as you might have guessed, the front-end parts of a web2 and web3 application are similar.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669808283556/dEPxhgfWF.png" alt /></p>
<p>Smart Contracts are code written a particular programming language which varies depending on the blockchain we choose. For Ethereum, the programming language used is solidity. These smart contracts will have functions to add and remove data from the blockchain. These smart contracts are then published to the blockchain, a chain of nodes(computers) that serves the function of a backend server but in a decentralized manner. When published, smart contracts will generate an ABI(Application Binary Interface) and return an address.</p>
<p>The front end uses the ABI and the address to interact with the blockchain. The connection to the blockchain is made using a wallet. Any user can execute different functions in the smart contract using a UI interface in the front end if the function being executed changes the data in the blockchain, some fees need to be paid. These fees are called gas fees, which are paid using a wallet connected to the application.</p>
]]></content:encoded></item><item><title><![CDATA[Step By Step Explanation To Pytorch For Deep Learning]]></title><description><![CDATA[Pytorch is a young Deep learning framework that has achieved tremendous popularity in both research and industry. In this blog post, I will explain how to train a simple feed-forward neural network in PyTorch, It would cover most of the important con...]]></description><link>https://blog.abhinavtb.com/step-by-step-explanation-to-pytorch-for-deep-learning</link><guid isPermaLink="true">https://blog.abhinavtb.com/step-by-step-explanation-to-pytorch-for-deep-learning</guid><category><![CDATA[Deep Learning]]></category><category><![CDATA[Python]]></category><category><![CDATA[Machine Learning]]></category><category><![CDATA[neural networks]]></category><dc:creator><![CDATA[Abhinav T B]]></dc:creator><pubDate>Fri, 28 Jan 2022 06:31:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643351366418/0k8c_J6iK.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Pytorch is a young Deep learning framework that has achieved tremendous popularity in both research and industry. In this blog post, I will explain how to train a simple feed-forward neural network in PyTorch, It would cover most of the important concepts in PyTorch and will be a stepping stone for your future endeavors with PyTorch.</p>
<h2 id="heading-code-walk-through">Code Walk Through</h2>
<h3 id="heading-library-imports">Library imports</h3>
<pre><code class="lang-python"><span class="hljs-comment"># Necessary Library imports</span>
<span class="hljs-keyword">import</span> torch
<span class="hljs-keyword">from</span> torch.utils.data <span class="hljs-keyword">import</span> Dataset , DataLoader , random_split
<span class="hljs-keyword">import</span> torch.nn.functional <span class="hljs-keyword">as</span> F
<span class="hljs-keyword">import</span> torch.nn <span class="hljs-keyword">as</span> nn
</code></pre>
<p>The following are the major import statements we need to build our demo app,we can discuss the use of each of these libraries when we use them in the upcoming sections.</p>
<h3 id="heading-creating-a-dataset">Creating a dataset</h3>
<h4 id="heading-tensors">Tensors</h4>
<pre><code class="lang-python">Nos = <span class="hljs-number">1000</span>
no_of_features = <span class="hljs-number">5</span>
x = torch.randn(Nos,no_of_features) <span class="hljs-comment">#taking a random sample from a normal distribution</span>
y1 = torch.ones(Nos//<span class="hljs-number">2</span>)        <span class="hljs-comment"># creating first half of the binary output</span>
y2 = torch.zeros(Nos//<span class="hljs-number">2</span>)      <span class="hljs-comment"># creating second half of the binary output</span>
y = torch.cat((y1,y2))        <span class="hljs-comment">#final output array</span>
shuffled_y = [y[i] <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> torch.randperm(len(y))] <span class="hljs-comment">#shuffling output tensor</span>
y = torch.tensor(shuffled_y)
</code></pre>
<p>We are not using any premade/complicated dataset for this particular tutorial instead we can make a dataset of our own using some PyTorch utilities. The basic building blocks of PyTorch are tensors, they are n-dimensional arrays that are similar to NumPy arrays but have the capability to perform operations in a GPU. If you are familiar with NumPy most of the methods in tensors will be similar.</p>
<p>Our independent variable x has 1000 items defined with 5 features, torch.randn() takes in the shape of the tensor to be created a returns a tensor of the required dimension, actually, there are two functions to create a random tensor rand() and randn() , the only difference is radn() creates a tensor whose elements follow a normal distribution. To create the dependent variable y which act as the prediction we create two nos/2 dimensional vectors y1 and y2 , which contain only ones and zeros respectively and the corresponding functions used to generate them are self-explanatory. Then we use torch.cat to concatenate these two tensors and as a final step we shuffle these ones and zeros and the final output is stored in y.</p>
<h4 id="heading-dataset-class">Dataset class</h4>
<pre><code class="lang-python"><span class="hljs-comment"># creating a custom dataset class inheriting from torch.utils.data.Dataset class</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyDataset</span>(<span class="hljs-params">Dataset</span>):</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self,x,y</span>) -&gt; <span class="hljs-keyword">None</span>:</span>
    self.x = x
    self.y = y

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__len__</span>(<span class="hljs-params">self</span>):</span>
    <span class="hljs-keyword">return</span> len(self.y)

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getitem__</span>(<span class="hljs-params">self,idx</span>):</span>
    <span class="hljs-keyword">return</span> self.x[idx] , self.y[idx]

dataset = MyDataset(x,y)

<span class="hljs-comment"># spliting the dataset for training and testing</span>
train_set , test_set = random_split(dataset, [<span class="hljs-number">800</span>, <span class="hljs-number">200</span>], generator=torch.Generator().manual_seed(<span class="hljs-number">42</span>))
</code></pre>
<p>Now we have our data as a tensor, but to use our data with a PyTorch model we need to convert it to a child class of superclass called Dataset, which can be imported from torch.utils.data.Dataset.The child class should implement <strong>init</strong>() , <strong>len</strong>() , <strong>getitem</strong>() functions to make the class valid for future uses . The <strong>init</strong>() function is the constructor to our class this is where we do all the preprocessing required for our dataset, here it takes two arguments that are x and y tensors which we created earlier. The <strong>len</strong>() function returns the length of the dataset and the <strong>getitem</strong>() function takes an index as an argument and returns the feature and output tensor at that particular index. Finally, we split the dataset into two,train_set and test_test which can be used for training and testing respectively.</p>
<h4 id="heading-dataloaders">Dataloaders</h4>
<pre><code class="lang-python"><span class="hljs-comment"># creating dataloaders for train and test set</span>
train_loader = DataLoader(train_set,batch_size = <span class="hljs-number">16</span>,shuffle = <span class="hljs-literal">True</span>)
test_loader = DataLoader(test_set,batch_size = <span class="hljs-number">16</span>,shuffle = <span class="hljs-literal">True</span>)
sample_x, sample_y = next(iter(train_loader))
sample_x.shape , sample_y.shape
</code></pre>
<p>A Dataloader class in PyTorch helps to load the data in batches and shuffle them during each epoch of training for better randomization. The above code creates two data loaders for both train and test sets. Pytorch dataloaders are basically python iterators and the next elements can be extracted using the next() in python</p>
<h3 id="heading-defining-a-model">Defining a Model</h3>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyModel</span>(<span class="hljs-params">nn.Module</span>):</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
    super(MyModel,self).__init__()
    self.layer1 = nn.Linear(<span class="hljs-number">5</span>,<span class="hljs-number">300</span>)
    self.layer2 = nn.Linear(<span class="hljs-number">300</span>,<span class="hljs-number">1</span>)
    self.relu = nn.ReLU()
    self.sigmoid = nn.Sigmoid()

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">forward</span>(<span class="hljs-params">self,x</span>):</span>
    x = self.layer1(x)
    x = self.relu(x)
    x = self.layer2(x)
    x = self.sigmoid(x)
    <span class="hljs-keyword">return</span> x

model = MyModel()
print(model)

<span class="hljs-keyword">for</span> parameter <span class="hljs-keyword">in</span> model.parameters(): 
  print(parameter.data.shape)
</code></pre>
<p>Now we have our dataset and data loaders ready, next let us define our model architecture. In PyTorch, we represent a Model through a class that inherits from the base class nn.Module. The class should implement two functions <strong>init</strong>() and the forward . The init() function defines the layers for our model with help of a neural network sub-module called nn . In the above code snippet, we define a linear layer with the function nn.Linear() which takes in two parameters which are the input and output layer dimensions. The forward function is the function that gets executed when we call the model in the training loop it is called inside a <strong>call</strong>() function inside the model class , this is the place where the forward pass takes place</p>
<p>Notice that the input to the first layer is 5 this is the number of features we have in our dataset and the output of our 2nd/final layer is 1 since we are doing a binary classification problem and the output is either 0/1 based on the probability in this final layer. We can also see a non-linear activation function like ReLu which does not change the dimension of our layers</p>
<p>You might be wondering where are the weights and bias of our model stored, this is automatically implemented by PyTorch as we use the base class nn.Module. We can use the parameters() in the model class to get all the parameters in our model with the initialiased values. In our case our model will have 4 matrices of parameters with the following dimensions torch.Size([300, 5]), torch.Size([300]), torch.Size([1, 300]), torch.Size([1]), We can see that the first and third ones are weights and the 2nd and last ones are biases of our model.</p>
<h3 id="heading-training-and-testing-loops">Training and Testing Loops</h3>
<pre><code class="lang-python"><span class="hljs-comment"># Optimising the model parameters</span>

loss_fn = F.binary_cross_entropy
optimizer = torch.optim.SGD(model.parameters(),lr = <span class="hljs-number">1e-4</span>)

<span class="hljs-comment"># training loop for train set</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">train</span>(<span class="hljs-params">train_loader,model , loss_fn,optimizer</span>):</span>

  <span class="hljs-keyword">for</span> batch,(x,y) <span class="hljs-keyword">in</span> enumerate(train_loader):

    pred = model(x)
    loss = loss_fn(pred,y.unsqueeze(<span class="hljs-number">1</span>))

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    <span class="hljs-keyword">if</span> batch % <span class="hljs-number">50</span> == <span class="hljs-number">0</span>:
      loss , current = loss.item(),batch * len(x)
      print(<span class="hljs-string">f"loos : <span class="hljs-subst">{loss}</span>, record no <span class="hljs-subst">{current}</span> "</span>)

<span class="hljs-comment">#testing loop</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test</span>(<span class="hljs-params">test_loader,loss_fn</span>):</span>
  size = len(test_loader.dataset)

  model.eval()
  test_loss , accuracy = <span class="hljs-number">0</span> , <span class="hljs-number">0</span>
  <span class="hljs-keyword">for</span> batch,(x,y) <span class="hljs-keyword">in</span> enumerate(train_loader):
    pred = model(x)
    loss = loss_fn(pred,y.unsqueeze(<span class="hljs-number">1</span>))
    test_loss += loss.item()
    accuracy += (pred.round()==sample_y.unsqueeze(<span class="hljs-number">1</span>)).sum()

  print(<span class="hljs-string">f"test_loss = <span class="hljs-subst">{loss/<span class="hljs-number">16</span>}</span>"</span>)
  print(<span class="hljs-string">f"test accuracy = <span class="hljs-subst">{(accuracy/Nos)}</span>"</span>)


epochs = <span class="hljs-number">10</span>

<span class="hljs-keyword">for</span> t <span class="hljs-keyword">in</span> range(epochs):
  print(<span class="hljs-string">f"starting epoch <span class="hljs-subst">{t}</span>"</span>)
  train(train_loader,model,loss_fn,optimizer)
  test(test_loader, loss_fn)
</code></pre>
<p>Let's train the model with the dataset we created. Training is a fancy term for updating the weights and biases of our model such that the output predicted by our model is close to the original output, a loss function is a metric that tells us how different are our prediction and output, here we use a loss function called binary_cross_entropy which we import from the functional package. An optimizer updates the weights to minimize the loss function here we use SGD from the torch.optim package with a learning rate of 1e-4.</p>
<p>After we define our optimizer and loss function we define our training loop which loops the data loader and in each iteration it passes the x(i) value to the model to generate predictions, this prediction along with the actual y label is passed to loss function to generate loss , here we unsqueeze y to add a single dimension to it so that it matches the predicted value's dimension, An important thing to note is that a PyTorch model class will only accept values in batches and it returns the prediction of each element in the batch, here the dimension of the tensor pred is [16,1] as our batch size was 16.</p>
<p>Pytorch uses dynamic computation graphs which will help us to calculate gradients easily without an extra step, this makes PyTorch more pythonic than other frameworks. optimizer.zero_grad() clears the calculated gradients from the previous step and loss.backwords() calculate the gradients of the loss function with respect to the X .Optimiser.step() updates the weights and biases accordingly.</p>
<p>Now let's talk about the testing loop. It is very much similar to the training loop but we are not updating the weights but simply validating the training hence we add model.eval() to tell the Pytorch the same, the default mode is model.train() so we don't have to add it explicitly in the training loop. For each epoch, we find the accuracy and loss with the training set. Now lets us closely examine how loss and accuracy are calculated in each epoch. For every batch prediction in an epoch, we obtain an avg loss for the batch from the loss function we sum it up and divide it by the batch size to find the avg loss for the entire dataset for that epoch. For each batch prediction, we round it off, compare it with its label and add the total number of correct predictions to the accuracy variable at the end of the epoch we divide the number of correct predictions by the total size of our dataset to find out the accuracy after that epoch.</p>
<h3 id="heading-conclution">Conclution</h3>
<p>Phew!, congrats if you could make it to the end, all these might seem a bit overwhelming in the beginning but you will get used to it soon. Please note that the dataset we used is just for educational purposes and we did not try to make any best predictions here. That's all folks, if you want me to explain any other topic do comment it out.</p>
]]></content:encoded></item><item><title><![CDATA[Introduction To Docker]]></title><description><![CDATA[Introduction
Before understanding what is docker let's try to find the answer to the question "why Docker". Any software developed is for solving a particular problem statement. Let's consider a situation in which you developed an application and sen...]]></description><link>https://blog.abhinavtb.com/introduction-to-docker</link><guid isPermaLink="true">https://blog.abhinavtb.com/introduction-to-docker</guid><category><![CDATA[Docker]]></category><category><![CDATA[Devops]]></category><category><![CDATA[production]]></category><dc:creator><![CDATA[Abhinav T B]]></dc:creator><pubDate>Thu, 27 Jan 2022 03:05:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/HjBOmBPbi9k/upload/v1643252111668/NNCXG7Xcd.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Before understanding what is docker let's try to find the answer to the question "why Docker". Any software developed is for solving a particular problem statement. Let's consider a situation in which you developed an application and send your source files to your manager for production or a friend to test it out. Even though your application works fine on your machine when your friend or manager runs it on their machine or in a production server it just didn't work. There can be many reasons for this, lets say your application was build using nodejs 14 but the person who ran that has node 12 in their system or you developed the app in a Linux environment but the person who ran it might have used windows and it crashed the app. This problem of "it works on my machine" is what docker tries to solve. So what if there is a way to ship your machine to production rather than shipping your source code, this will ensure that the program works where ever it is made to run. Dockerising an application helps us to do this, in the upcoming sections we will try to see how to do it.</p>
<h2 id="heading-installation">Installation</h2>
<p>Installing docker is pretty straightforward, you can follow these instructions according to your operating system 
https://docs.docker.com/get-docker/.
It's a good thing to note that in all these installation setups docker is installed inside a Linux machine or a Linux virtual machine(Docker Desktop), that is because docker only runs operating systems with the same kernel. If you don't understand this do not worry, you will get this once you use are familiarised with docker.</p>
<h2 id="heading-dockerfile-docker-images-docker-containers">Dockerfile, Docker Images, Docker Containers</h2>
<p>Now let's understand some important terms in Docker. Let's start with Docker Images, as the name suggests it is a snapshot of the machine you need to ship to production and a docker file is a blueprint to configure how your image should be built. Once we build a docker file and create a docker image, we can run the image to create a container which is a process that runs our applications in the specified environment</p>
<h2 id="heading-containerizing-a-node-js-application">Containerizing a Node Js Application</h2>
<p>Let's try to use all the things we learnt till now and containerise a simple Nodejs Application. You can find the starter code <a target="_blank" href="https://github.com/abhinav-TB/Docker-Demo">here</a>. Once you are ready with the starter nodejs application let's begin by creating a DockerFile in the root directory of the application.</p>
<pre><code>FROM node:<span class="hljs-number">12</span>

WORKDIR <span class="hljs-operator">/</span>app

COPY package.json ./

RUN npm install

COPY . .

ENV PORT<span class="hljs-operator">=</span><span class="hljs-number">8080</span>

EXPOSE <span class="hljs-number">8080</span>

CMD [ <span class="hljs-string">"npm"</span>, <span class="hljs-string">"start"</span> ]
</code></pre><p>Now copy and paste the following contents into the Dockerfile file. Let's break down each of these statements. The FROM command specifies the base image or the starting image that we are using , it can be ubuntu, centos or something particular like node which already have node js installed inside that. next, we specified the working directory, this is where all our files should be stored. the copy command copies the package.json to the working directory then the RUN command runs  'npm install' which installs the necessary packages needed. then we copy the rest of the app to the working directory. The ENV sets an Environment variable to 'port' then we Expose the port and finally starts the application using the CMD command</p>
<p>Now let's build an image using the docker file we made. Use the following command to run the docker file.</p>
<pre><code>docker build <span class="hljs-operator">-</span>t node<span class="hljs-operator">-</span>docker<span class="hljs-operator">-</span>demo:v1 .
</code></pre><p>This command builds our docker image.</p>
<pre><code><span class="hljs-attribute">docker</span> images ps
</code></pre><p>the above command can be used to show all the docker images in our system</p>
<p>Now lets the run the image in a container </p>
<pre><code> docker run <span class="hljs-operator">-</span>p <span class="hljs-number">8080</span>:<span class="hljs-number">8080</span> <span class="hljs-operator">-</span>d node<span class="hljs-operator">-</span>docker<span class="hljs-operator">-</span>demo:v1
</code></pre><p>the above command starts and the container runs our application, then expose the port 8080 to the host machine. Now you can visit http://localhost:8080/ in your browser to view our app.</p>
<p>Congrats on running your first containerized application, now we can stop the container<br />Run the following command to get the list of containers currently running in our machine</p>
<pre><code><span class="hljs-attribute">docker</span> ps
</code></pre><p>copy the id of the container that we started and run the following command to stop it.</p>
<pre><code>docker stop <span class="hljs-operator">&lt;</span>container_id<span class="hljs-operator">&gt;</span>
</code></pre><h2 id="heading-conclusions">Conclusions</h2>
<p>That was a small introduction to docker, if you are more curious checkout more commands and options in the <a target="_blank" href="https://docs.docker.com/"> official documentation</a> 
Follow me for more content like this</p>
]]></content:encoded></item><item><title><![CDATA[Different Ways To Make Network Requests in Javascript]]></title><description><![CDATA[In this article, I will be discussing the different methods to make a network request starting from the old XMLHttp browser API to the modern libraries like Axios.
Before getting into these let us understand what exactly is meant by a network request...]]></description><link>https://blog.abhinavtb.com/different-ways-to-make-network-requests-in-javascript</link><guid isPermaLink="true">https://blog.abhinavtb.com/different-ways-to-make-network-requests-in-javascript</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[http]]></category><category><![CDATA[network]]></category><category><![CDATA[axios]]></category><category><![CDATA[fetch]]></category><dc:creator><![CDATA[Abhinav T B]]></dc:creator><pubDate>Tue, 24 Aug 2021 13:11:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1629810594147/Lt1wVGeik.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this article, I will be discussing the different methods to make a network request starting from the old XMLHttp browser API to the modern libraries like Axios.</p>
<p>Before getting into these let us understand what exactly is meant by a network request.
In simple terms, a network request is a request made by a client-side application to a server for exchanging different types of resources like images, webpages, JSON etc. We are going to see three different types of methods to make a network request which includes XMLHttp, fetch and Axios. Let's try to understand each of these methods one by one.</p>
<h2 id="xmlhttp">XMLHttp</h2>
<pre><code><span class="hljs-keyword">const</span> getdata = <span class="hljs-function">() =&gt;</span>{
    <span class="hljs-keyword">var</span> xhr = <span class="hljs-keyword">new</span> XMLHttpRequest();
    xhr.open(<span class="hljs-string">"GET"</span>, <span class="hljs-string">"https://reqres.in/api/products/1"</span>);
    xhr.onload = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
        <span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">JSON</span>.parse(xhr.response));
    };
    xhr.send();
}
getdata()
</code></pre><p>The code consists of a simple js function making using of XMLHttp for getting data from an API endpoint. Now let's break down the code one by one. The XMLHttpRequest() object is a built-in method in the browser it initializes an XMLHttp object . The open method in the xhr object primarily takes in two arguments. An HTTP method and the URL to the resource, this method initializes a request. The onload method can be assigned a function that can be fired when the request receives some data. The data we receive will not be in JSON format, but we can parse it to JSON using the inbuild JSON object in the browser and finally the send() method sends the request to the server </p>
<p>This method of doing this is fine but since it's asynchronous, we can wrap the function with a promise.</p>
<pre><code><span class="hljs-keyword">const</span> getdata = <span class="hljs-function">() =&gt;</span>{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
        <span class="hljs-keyword">var</span> xhr = <span class="hljs-keyword">new</span> XMLHttpRequest();
        xhr.open(<span class="hljs-string">"GET"</span>, <span class="hljs-string">"https://reqres.in/api/products/1"</span>);
        xhr.onload = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
            resolve(<span class="hljs-built_in">JSON</span>.parse(xhr.response));
        };
        xhr.send();
    })

}
getdata()
.then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(data);
})
</code></pre><p>This is a quite vague implementation, it doesn't handle the rejected case but I just wanted to show you that promises could be used alongside with XMLHttp requests. A common doubt you must have now is XMLHttp requests have XML in its name but we are using JSON data, this is a very genuine question, the answer to this question is we could use both JSON and XML data with XMLHttp, it was named so because XML was a common standard in earlier days. So that's all we have for XMLHttp you could read more about it in the  <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest">MDN docs</a>.</p>
<h2 id="fetch-api">Fetch API</h2>
<p>XMLHttp requests work fine but, but too much code for a single request, fetch simplifies this process by implementing promises under the hood so we don't have to do it manually.</p>
<pre><code>fetch(<span class="hljs-string">'https://reqres.in/api/products/1'</span>)
.then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span><span class="hljs-built_in">console</span>.log(res))
</code></pre><p>Wow that was short, but there is a small problem, if you check the response body it will be a readable stream and not any form of usable data. To convert it to any other format there are methods provided by the fetch API, one of these is res.json() which will convert the response to JSON and returns another promise which can be resolved to get the data. The following code does what we have mentioned bellow</p>
<pre><code>fetch(<span class="hljs-string">'https://reqres.in/api/products/1'</span>)
.then(<span class="hljs-function">(<span class="hljs-params">res</span>)=&gt;</span>{
    <span class="hljs-keyword">return</span> res.json();
})
.then(<span class="hljs-function"><span class="hljs-params">data</span>=&gt;</span>{
    <span class="hljs-built_in">console</span>.log(data);
})
</code></pre><p> you could read more about it in the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API">MDN docs</a> </p>
<h2 id="axios">Axios</h2>
<p>Unlike the other two methods, we discussed above Axios is not a native browser API it's a javascript package that can either be installed using npm or used with a CDN.
Let's see a sample GET request using Axios that fetches the same data as we did before</p>
<pre><code>axios.get(<span class="hljs-string">'https://reqres.in/api/products/1'</span>)
.then(<span class="hljs-function"><span class="hljs-params">res</span>=&gt;</span>{
    <span class="hljs-built_in">console</span>.log(res)
})
</code></pre><p>you can notice it does not return a readable stream instead returns the entire data, unlike the fetch API. So is that the only difference, the answer is no. Axios provide a lot of things under the hood like XSRF protection, the ability to intercept HTTP requests etc.</p>
<h2 id="conclusion">Conclusion</h2>
<p>That's all for this post, hope you got a foundation on the different methods to make a network request in js so that you could practise and learn more about these soon.
If you loved my content consider following me for more of these and if you need a blog post any related content do mention it in the comments</p>
]]></content:encoded></item><item><title><![CDATA[Writing Your Node.js Apps Using ES6]]></title><description><![CDATA[INTRODUCTION
ECMAScript 2015 or ES6 was the second major revision to JavaScript. Many of you probably might be using it with popular front-end libraries such as react. Even though ES6 syntax is much better and as developers we loved to use it quite o...]]></description><link>https://blog.abhinavtb.com/writing-your-nodejs-apps-using-es6</link><guid isPermaLink="true">https://blog.abhinavtb.com/writing-your-nodejs-apps-using-es6</guid><category><![CDATA[Node.js]]></category><category><![CDATA[ES6]]></category><category><![CDATA[es2015]]></category><category><![CDATA[Babel]]></category><category><![CDATA[Express]]></category><dc:creator><![CDATA[Abhinav T B]]></dc:creator><pubDate>Sun, 08 Aug 2021 11:07:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1629810984140/HupUMxdhX.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="introduction">INTRODUCTION</h1>
<p>ECMAScript 2015 or ES6 was the second major revision to JavaScript. Many of you probably might be using it with popular front-end libraries such as react. Even though ES6 syntax is much better and as developers we loved to use it quite often, there are few versions of browsers that won't support ES6. Due to this reason we use babel a javascript transpiler to compile the ES6 code to lower versions. This is done in tools such as create react app.</p>
<p>Now let's come to Nodejs a javascript runtime that runs javascript outside the browser. Let's see how a simple express app would look while using ES6 and ES6</p>
<pre><code><span class="hljs-comment">// ES5 version</span>
<span class="hljs-keyword">var</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">var</span> app = express();

app.get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">req, res</span>) </span>{
   res.send(<span class="hljs-string">'Hello World'</span>);
})

<span class="hljs-keyword">var</span> server = app.listen(<span class="hljs-number">8081</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
   <span class="hljs-keyword">var</span> host = server.address().address
   <span class="hljs-keyword">var</span> port = server.address().port

   <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Example app listening at http://%s:%s"</span>, host, port)
})
</code></pre><pre><code><span class="hljs-regexp">//</span> ES6 version
<span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;
var app = express();

app.get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-params">(req, res)</span>=&gt;</span> {
    res.send(<span class="hljs-string">'Hello World'</span>);
})

app.listen(<span class="hljs-number">3000</span>, <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Example app listening at port 3000"</span>)
})
</code></pre><p>Some of the changes we can observe here are,the use of ES6 imports and arrow functions but it's not limited to that, you can check  <a target="_blank" href="http://es6-features.org/#ClassInheritance">here</a>  for more differences and additions.</p>
<h1 id="methods-to-write-es6-code">METHODS TO WRITE ES6 CODE</h1>
<h2 id="method-1">METHOD 1</h2>
<p>From nodejs 13+ ES6 has been added as an experimental feature. You can enable this by adding type = "module" to your package.json.</p>
<pre><code>{
  "name": "demo",
  "version": "1.0.0",
  "type":"module",  &lt;<span class="hljs-comment">--</span>
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: <span class="hljs-keyword">no</span> test specified\" &amp;&amp; exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1"
  }
}
</code></pre><h2 id="method-2">METHOD 2</h2>
<p>Here if use babel to transpile our ES6 code to ES5 code. We need to install the following packages to do that.</p>
<pre><code> <span class="hljs-built_in">npm</span> install --save-dev @babel/cli @babel/core @babel/node @babel/preset-env
</code></pre><p>We can also install an additional dependency called nodemon to restart our server automatically when we make changes</p>
<pre><code>npm <span class="hljs-keyword">install</span> <span class="hljs-comment">--save-dev nodemon</span>
</code></pre><p>Now lets us configure our package.json to use babel to transpile our ES5 code to ES6
Create a folder called src at the root of the project and move the index.js(the file with your express app) file into it</p>
<p>Let's add the following scripts to our package.json</p>
<pre><code> <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"build"</span>: <span class="hljs-string">"babel ./src --out-dir dist"</span>,
    <span class="hljs-string">"start"</span>: <span class="hljs-string">"nodemon  --exec babel-node src/index.js "</span>
  },
</code></pre><p>Run the following code to start the development server and to transpile your code respectively.</p>
<pre><code>npm <span class="hljs-keyword">start</span>  // starts a development <span class="hljs-keyword">server</span>

npm run <span class="hljs-keyword">build</span>  //transpile the code <span class="hljs-keyword">to</span> ES6 <span class="hljs-keyword">and</span> <span class="hljs-keyword">store</span> it <span class="hljs-keyword">in</span> a <span class="hljs-keyword">directory</span> called dist
</code></pre><p>The final project structure would look like the one bellow</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1628418626452/e0CHY0MUN.png" alt="image.png" />
The final package.json will look the like the one bellow </p>
<pre><code>{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"demo"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"main"</span>: <span class="hljs-string">"index.js"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"babel ./src --out-dir dist"</span>,
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"nodemon  --exec babel-node src/index.js "</span>
  },
  <span class="hljs-attr">"author"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"license"</span>: <span class="hljs-string">"ISC"</span>,
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"express"</span>: <span class="hljs-string">"^4.17.1"</span>
  },
  <span class="hljs-attr">"devDependencies"</span>: {
    <span class="hljs-attr">"@babel/cli"</span>: <span class="hljs-string">"^7.14.8"</span>,
    <span class="hljs-attr">"@babel/core"</span>: <span class="hljs-string">"^7.15.0"</span>,
    <span class="hljs-attr">"@babel/node"</span>: <span class="hljs-string">"^7.14.9"</span>,
    <span class="hljs-attr">"@babel/preset-env"</span>: <span class="hljs-string">"^7.15.0"</span>
  }
}
</code></pre><p>That's it , you are now ready to get up and running with ES6 🎉. If you have any queries reach out to me via  <a target="_blank" href="https://twitter.com/abhiGamez">Twitter</a> .</p>
]]></content:encoded></item><item><title><![CDATA[Understanding Cors]]></title><description><![CDATA[INTRODUCTION
Most of us have heard the term CORS from an error message, popping up while doing a network request. Before getting into solving this error let's understand what does CORS exactly mean and check whether it's really bad as you think. 
In ...]]></description><link>https://blog.abhinavtb.com/understanding-cors</link><guid isPermaLink="true">https://blog.abhinavtb.com/understanding-cors</guid><category><![CDATA[CORS]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Express]]></category><dc:creator><![CDATA[Abhinav T B]]></dc:creator><pubDate>Sat, 24 Jul 2021 15:02:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1627138789925/V-LUGTWtj.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="introduction">INTRODUCTION</h1>
<p>Most of us have heard the term CORS from an error message, popping up while doing a network request. Before getting into solving this error let's understand what does CORS exactly mean and check whether it's really bad as you think. </p>
<p>In the olden days of the web, we were only allowed to share resources between the same origin, here origin can be considered as a single domain or a server. This was fine until microservice architecture became widely adopted and there was a need for the browser to communicate to different domains. This is where CORS come into the picture.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1627138646748/J-iGvveAJ.png" alt="cors1.png" /></p>
<h1 id="cors">CORS</h1>
<p>It is an HTTP-header based mechanism that allows a server to indicate any other origins (domain, scheme, or port) than its own from which a browser should permit loading of resources.  CORS also relies on a mechanism by which browsers make a “preflight” request to the server hosting the cross-origin resource, in order to check that the server will permit the actual request. In that preflight, the browser sends headers that indicate the HTTP method and headers that will be used in the actual request.</p>
<p>There are two types of cross-origin requests:</p>
<ol>
<li>Safe requests.</li>
<li>All the others.</li>
</ol>
<p>Safe Requests are simpler to make, so let’s start with them.</p>
<p>A request is safe if it satisfies two conditions:</p>
<ol>
<li>Safe method: GET, POST or HEAD</li>
<li><p>Safe headers – the only allowed custom headers are:</p>
<ul>
<li><p>Accept,</p>
</li>
<li><p>Accept-Language,</p>
</li>
<li><p>Content-Language,</p>
</li>
<li><p>Content-Type with the value application/x-www-form-urlencoded, multipart/form-data or text/plain.</p>
</li>
</ul>
</li>
</ol>
<p>Any other request is considered “unsafe”. For instance, a request with a PUT method or with an API-Key HTTP header does not fit the limitations</p>
<h1 id="handling-cors-errors">Handling CORS Errors</h1>
<p>The reason for you to get a CORS error is that your request does not come under the safe request category, one way of fixing this error would be adding a proxy server, this makes use of the property that CORS restrictions are not there in server to server communications. If you are working on a react app build using create-react-app, the react-scripts provide you with a proxy server, but this is only for development purposes so it's not a proper solution.</p>
<p>If you have control over the API then the best solution would be to add the CORS headers before sending the response to your request.</p>
<pre><code><span class="hljs-attr">Access-Control-Allow-Origin:</span> <span class="hljs-string">*</span>
<span class="hljs-attr">Access-Control-Allow-Origin:</span> <span class="hljs-string">&lt;origin&gt;</span>
<span class="hljs-attr">Access-Control-Allow-Origin:</span> <span class="hljs-literal">null</span>
</code></pre><p>The above headers can be used to allow some or all certain origins  to access the server resources. Similarly, there are more headers that you can add to allow different content-Types, methods etc. You can learn more about the different headers  <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#cors">here</a>.</p>
<p>Hopefully, you are now able to understand what caused this error and a possible fix, now let's try to implement this solution using code, I am going to use express js for this particular demo.</p>
<p>The following code shows a simple express app</p>
<pre><code><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);

<span class="hljs-keyword">const</span> app = express();

app.use(express.json());

app.listen(<span class="hljs-number">3000</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`listening on port 3000`</span>)
})

<span class="hljs-comment">// routes</span>
app.get(<span class="hljs-string">'/'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> res.render(<span class="hljs-string">'home'</span>));
app.put(<span class="hljs-string">'/books'</span>,  <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  books = updatebook() <span class="hljs-comment">// some function to update books</span>
  res.send(<span class="hljs-string">"books updated"</span>)
});
</code></pre><p>Then the browser is requesting for the /books resource from another origin there will be a CORS error as we are not attaching any CORS header to the response hence the preflight request will fail to raise the CORS Error. Now let's see how to add a simple express middleware that can add this necessary header to the responses.</p>
<pre><code>const express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);

const app = express();

app.use(express.json());

app.use(<span class="hljs-function"><span class="hljs-params">(req, res, next)</span> =&gt;</span> {
  res.header(<span class="hljs-string">'Access-Control-Allow-Origin'</span>, <span class="hljs-string">'*'</span>);
  res.header(<span class="hljs-string">'Access-Control-Allow-Headers'</span>,<span class="hljs-string">'*'</span>);
  next();
});

app.listen(<span class="hljs-number">3000</span>, <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(`<span class="javascript">listening on port <span class="hljs-number">3000</span></span>`)
})

<span class="hljs-regexp">//</span> routes
app.get(<span class="hljs-string">'/'</span>, <span class="hljs-function"><span class="hljs-params">(req, res)</span> =&gt;</span> res.render(<span class="hljs-string">'home'</span>));
app.put(<span class="hljs-string">'/books'</span>,  <span class="hljs-function"><span class="hljs-params">(req, res)</span> =&gt;</span> {
  books = updatebook() <span class="hljs-regexp">//</span> some function to update books
  res.send(<span class="hljs-string">"books updated"</span>)
});
</code></pre><p>app. use applied this middleware to all requests in our server, you can read more about express middleware  <a target="_blank" href="http://expressjs.com/en/guide/using-middleware.html#using-middleware">here</a>.</p>
<p>So from the next request, our server adds 'Access-Control-Allow-Origin' and 'Access-Control-Allow-Headers' to our requests so this will allow the browser to accept the resources from our server and CORS error will not be there. Note that the headers you need to include are the ones needed according to your question, do refer to the link I shared before for reading more about the other headers available</p>
<p>Before wrapping up I would like to add one more thing, that is about the different modes available in browsers fetch API, it is just a way to control how the browser should attempt to cross-origin requests and will not help in accessing cors restricted resources.</p>
<p>The different mode options available are the following:</p>
<ul>
<li><em>"cors"</em> – the default, cross-origin requests are allowed, as described in Fetch: Cross-Origin Requests,</li>
<li><em>"same-origin"</em> – cross-origin requests are forbidden,</li>
<li><em>"no-cors"</em> – only safe cross-origin requests are allowed.
This option may be useful when the URL for fetch comes from a 3rd-party, and we want a “power off switch” to limit cross-origin capabilities.</li>
</ul>
]]></content:encoded></item></channel></rss>