<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Austin Vance</title>
    <description>The latest articles on DEV Community by Austin Vance (@austinbv).</description>
    <link>https://dev.to/austinbv</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F305023%2Fc978f899-9fa5-4b1e-9ad9-e3f60313fd65.jpeg</url>
      <title>DEV Community: Austin Vance</title>
      <link>https://dev.to/austinbv</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/austinbv"/>
    <language>en</language>
    <item>
      <title>Everybody Tests</title>
      <dc:creator>Austin Vance</dc:creator>
      <pubDate>Tue, 03 Feb 2026 19:43:37 +0000</pubDate>
      <link>https://dev.to/focused_dot_io/everybody-tests-126k</link>
      <guid>https://dev.to/focused_dot_io/everybody-tests-126k</guid>
      <description>&lt;h1&gt;
  
  
  Everybody Tests
&lt;/h1&gt;

&lt;p&gt;Everybody tests. Every developer. Every team. The debate isn't about whether you test. It's about whether you automate it.&lt;/p&gt;

&lt;p&gt;I hear the pushback all the time. "We don't do TDD." "We don't have time for tests." "Tests slow us down." Fine. But watch what happens when that same developer finishes a feature. They open the browser. They click around. They fill out a form. They check the button. They verify the data.&lt;/p&gt;

&lt;p&gt;That's a test.&lt;/p&gt;

&lt;p&gt;When QA maintains a spreadsheet of scenarios to run before each release... that's a test suite. When you demo to stakeholders and walk through the happy path... that's acceptance testing. When you "just quickly check" that your refactor didn't break anything... that's regression testing.&lt;/p&gt;

&lt;p&gt;The tests exist. They always have. They're just manual. Trapped in someone's head.&lt;/p&gt;

&lt;h2&gt;
  
  
  You already think test-first
&lt;/h2&gt;

&lt;p&gt;Most developers already think test-first. They just don't write it down.&lt;/p&gt;

&lt;p&gt;Before you write a function, what do you do? You think about what it should do. You imagine calling it with some input. You picture what comes out. You might even sketch a few edge cases in your head, on a post-it, on a whiteboard. What if the list is empty? What if the user isn't authenticated?&lt;/p&gt;

&lt;p&gt;That's the test. You've written it. It exists. It's just in your brain instead of in code.&lt;/p&gt;

&lt;p&gt;Then you implement. Then you verify it does what you imagined.&lt;/p&gt;

&lt;p&gt;Congratulations, you already did the hardest part of test-first development.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cost nobody talks about
&lt;/h2&gt;

&lt;p&gt;Manual testing isn't free. It's a subscription.&lt;/p&gt;

&lt;p&gt;Every time you click through your app to verify a change, you're paying. Every time someone else on your team does the same verification, that's another payment. Before every release. After every refactor. When something breaks in production. When a new hire needs to understand what the system does.&lt;/p&gt;

&lt;p&gt;That 30-second click-through? You'll do it a hundred times. So will everyone else. Multiply by every feature, every edge case, every browser.&lt;/p&gt;

&lt;p&gt;The math is brutal. An automated test takes minutes to write and milliseconds to run. A manual test takes seconds to run, but you'll run it thousands of times.&lt;/p&gt;

&lt;h2&gt;
  
  
  The teams that say they don't have time
&lt;/h2&gt;

&lt;p&gt;I've seen teams who are adamant they "don't do TDD" spend entire days before a release running through test scripts. They hire QA teams whose job, their actual full-time job, is clicking the same buttons over and over.&lt;/p&gt;

&lt;p&gt;And then they tell me they don't have time to write tests.&lt;/p&gt;

&lt;p&gt;What they mean is this: they don't have time to write the test once. But they have infinite time to run it manually forever.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same satisfying satisfying, all over again
&lt;/h2&gt;

&lt;p&gt;We're about to make the exact same mistake with AI.&lt;/p&gt;

&lt;p&gt;Watch what happens when someone builds an agent. They tweak a prompt. They run it. They look at the output. They squint. They decide if it's good enough. They tweak again. They run it again. They squint harder.&lt;/p&gt;

&lt;p&gt;That's an eval. That's literally an eval. You're evaluating the output of your system against some criteria in your head.&lt;/p&gt;

&lt;p&gt;Everybody evals. The question is whether you automate it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Eval Driven Development
&lt;/h2&gt;

&lt;p&gt;The parallel to TDD is exact. Before you write an agent, you think about what it should do. You imagine the inputs. You picture what a good output looks like. You might even think through some edge cases, what if the context is empty? What if the user asks something adversarial?&lt;/p&gt;

&lt;p&gt;That's the eval. You've already written it. It's in your brain. Write it down.&lt;/p&gt;

&lt;p&gt;Define what good looks like before you start prompting. Capture the examples. Capture the edge cases. Automate the judgment, whether that's exact match, semantic similarity, or LLM-as-judge.&lt;/p&gt;

&lt;p&gt;Then iterate. Change the prompt, run the evals, see what breaks. Change the model, run the evals, see what improves. Swap in a new retriever, run the evals, know immediately if you're moving forward or backward.&lt;/p&gt;

&lt;p&gt;This is Eval Driven Development. It's just TDD for the age of AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vibes don't ship
&lt;/h2&gt;

&lt;p&gt;With traditional software, at least the output is deterministic. You click the button, you get the same result.&lt;/p&gt;

&lt;p&gt;With agents? The output is stochastic. You run the same prompt twice, you get different results. Manual review is useful for exploration. It’s useless as a measurement system. You can't trust your own squinting because you're sampling a distribution. That one good output you saw could happen 90% of the time. Or maybe 20% of the time. You have no idea.&lt;/p&gt;

&lt;p&gt;This is why the teams building AI systems have eval suites, they have regression sets, they run benchmarks on every commit. They know their success rates because they measure them.&lt;/p&gt;

&lt;p&gt;The teams that don't? They're shipping on vibes. And I've seen where that ends. It ends with an agent in production that works great in the demo and fails catastrophically on real traffic. It ends with prompt changes that "seemed fine" but tanked accuracy by 15%. It ends with executives asking why the AI feature is so unreliable, and engineers shrugging because they never actually knew how reliable it was in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  The choice is the same as it always was
&lt;/h2&gt;

&lt;p&gt;Twenty years ago, the industry had to learn that manual QA doesn't scale. That you can't hire enough people to click through every scenario before every release. That automated tests aren't a luxury.&lt;/p&gt;

&lt;p&gt;Some teams learned that lesson. Some are still running through spreadsheets the night before launch.&lt;/p&gt;

&lt;p&gt;Now we get to make the same choice again. You can build agents by squinting at outputs and hoping for the best. Or you can write down what good looks like, automate the evaluation, and actually know whether your system works.&lt;/p&gt;

&lt;p&gt;Everybody tests. Everybody evals. The only question is whether you're going to keep doing it by hand, or whether you're finally going to let the computer do its job.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If your team is building agents without evals—or still stuck in manual testing on the traditional side—drop me a note or check out &lt;a href="https://focused.io" rel="noopener noreferrer"&gt;focused.io&lt;/a&gt;. We've helped teams escape the spreadsheet and the squint.&lt;/em&gt;  &lt;/p&gt;

</description>
      <category>testing</category>
      <category>cleancode</category>
      <category>programming</category>
      <category>ai</category>
    </item>
    <item>
      <title>Context Will Replace Your Design</title>
      <dc:creator>Austin Vance</dc:creator>
      <pubDate>Tue, 27 Jan 2026 03:34:12 +0000</pubDate>
      <link>https://dev.to/focused_dot_io/context-will-replace-your-design-l94</link>
      <guid>https://dev.to/focused_dot_io/context-will-replace-your-design-l94</guid>
      <description>&lt;p&gt;There's a particular irony in typing a Slack DM to an AI assistant that will help me write about why interfaces don't matter anymore... There’s just words in a box (or in this case,a series of manic voice notes), and a system that understands what I need.&lt;/p&gt;

&lt;p&gt;I've been running &lt;a href="https://clawd.bot" rel="noopener noreferrer"&gt;Clawdbot&lt;/a&gt;, a persistent AI assistant, for a few days (yeah only days) now. It reads my email, manages my calendar, browses the web on my behalf, and remembers conversations across sessions. It does everything my current VA does. It's changed how I think about software design. Clawd has no interface, no web page for interaction, nothing... and the absence of a traditional interface hasn't diminished the experience. If anything, it's clarified something we've been dancing around since agents have started to take shape: most interface design exists to compensate for computers not understanding what users want.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Translation Layer We Built
&lt;/h3&gt;

&lt;p&gt;For fifty years, interface design has solved a specific problem: we humans think in fuzzy concepts and intentions, while computers operate on instructions and data. Going from "I want to book a flight to Chicago next Tuesday" to the seventeen form fields, dropdowns, and confirmation screens required to book a flight is what interface designers do.&lt;/p&gt;

&lt;p&gt;There are disciplines around this translation work. Information architecture. Visual hierarchy. Affordances and signifiers. Progressive disclosure. All this design exists to help people navigate the rigid structures that computers require. Click here, not there. Fill this field before that one. Read the error message, understand what went wrong, try again.&lt;/p&gt;

&lt;p&gt;Design makes speaking “computer” effortless, drawing on the premise that humans adapt to the system.&lt;/p&gt;

&lt;h3&gt;
  
  
  When the System Adapts to You
&lt;/h3&gt;

&lt;p&gt;Over the weekend, I sent a voice memo to my assistant while skiing that says, "draft a 2,000-word essay about the future of design in a post-agentic world, use Clawdbot as a case study, make it sound like my writing style," and... it did a pretty mediocre job on the essay…  but the interaction was perfect. I used WhatsApp and Slack, used voice notes while driving and skiing, it read my other articles on focused.io and austinbv.com. It blew me away. It's a new paradigm for design.&lt;/p&gt;

&lt;p&gt;The agent, clawdbot, did all the input work for me. It didn't need maliciously designed input fields, instead the LLM can parse my natural language, and it doesn’t want visual hierarchy to guide attention because it just  asks clarifying questions. There’s no more progressive disclosure because it can handle complexity directly.&lt;/p&gt;

&lt;p&gt;So, what does design become when the translation layer is the system itself?&lt;/p&gt;

&lt;h3&gt;
  
  
  The Clawdbot Case Study
&lt;/h3&gt;

&lt;p&gt;My current setup involves an AI assistant (clawdbot) that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lives in WhatsApp and Slack (messaging interfaces I already use)&lt;/li&gt;
&lt;li&gt;Maintains persistent memory across conversations&lt;/li&gt;
&lt;li&gt;Has access to my email, calendar, and files&lt;/li&gt;
&lt;li&gt;Can browse the web and interact with services&lt;/li&gt;
&lt;li&gt;Runs scheduled tasks and proactive check-ins&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The "interface" is the chat apps, but the beauty is the tools. Clawdbot connects to other APIs and handles tasks that would require dedicated applications and logins and passwords and and 2fa and have websites with carefully designed interfaces: email clients, calendar apps, travel booking sites, task managers, note-taking tools.&lt;/p&gt;

&lt;p&gt;It feels like noise now that I think about it... Designers in an agentic world should be thinking as the Agent, as the user, not a person. You can then frame it as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Context structures&lt;/strong&gt; - What information does the AI need to understand my preferences, constraints, and goals?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory systems&lt;/strong&gt; - How does the assistant maintain continuity across sessions without drowning in irrelevant history?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool interfaces&lt;/strong&gt; - How do we expose capabilities (email, calendar, web browsing) in ways that AI can reliably invoke?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure modes&lt;/strong&gt; - What happens when the AI misunderstands? How do we make correction natural?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The design becomes decoupled from the interface and more about “How it works” rather than “how it looks.” Isn’t that what designers wanted all along?&lt;/p&gt;

&lt;h3&gt;
  
  
  Context Engineering: The New Discipline
&lt;/h3&gt;

&lt;p&gt;There’s a new term for what's emerging: &lt;em&gt;context engineering&lt;/em&gt;. It's the discipline of designing the information environments in which AI agents operate.&lt;/p&gt;

&lt;p&gt;Traditional interface design asks: "How do we present information so humans can find and act on it?"&lt;/p&gt;

&lt;p&gt;Context engineering asks: "How do we structure information so AI can understand and act on it appropriately?"&lt;/p&gt;

&lt;p&gt;Sound similar? They are, but lead to radically different practices. Visual hierarchy doesn't matter to an AI, it can process information in any order. Color and iconography don't matter. Animation and micro-interactions don't matter.&lt;/p&gt;

&lt;p&gt;What matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Information completeness&lt;/strong&gt; - Does the AI have everything it needs to make good decisions?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic clarity&lt;/strong&gt; - Is the information structured in ways the AI can reliably parse?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Constraint specification&lt;/strong&gt; - Are the boundaries of acceptable action clearly defined?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context efficiency&lt;/strong&gt; - Can we convey what's needed without wasting tokens (and money, and latency)?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point deserves emphasis. In the current paradigm, loading a webpage or json or whatever costs the user attention and costs the provider bandwidth, both relatively cheap. In an agentic paradigm, loading context costs tokens, which cost real money at scale, add latency that compounds, and confuse agents across multi-step tasks.&lt;/p&gt;

&lt;p&gt;The entire economics of information presentation flips. Dense, structured, machine-readable information beats sparse, visual, human-readable information.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Agents Need
&lt;/h3&gt;

&lt;p&gt;When I watch Clawdbot browse the web on my behalf, it’s painful. It loads pages designed for humans, rich with images, navigation, whitespace, branding, and extracts the tiny fraction of information it needs. A flight search that a human might complete in five minutes of visual scanning takes the AI multiple page loads, screenshots, and navigation of UIs designed to be intuitive for people who look rather than read.&lt;/p&gt;

&lt;p&gt;This is obviously transitional. As agents become more prevalent, services will increasingly expose information in agent-friendly formats. I don’t think this is traditional APIs (they are too noisy), but something more flexible, semantic interfaces that can adapt to varied requests while remaining token-efficient.&lt;/p&gt;

&lt;p&gt;And a new design challenge emerges, going from "how do we make this look good to humans" to "how do we make this legible to agents while remaining interpretable by humans for oversight?" Yup, with oversight.&lt;/p&gt;

&lt;p&gt;It’s harder than it sounds. Pure API endpoints are good for agents but opaque to human oversight. Visual interfaces are easy for humans to monitor but wasteful for agents. The emerging discipline will be the middle ground: interfaces that are simultaneously agent-efficient and human-auditable. Until the agent just gets what it needs and builds the interface on the fly.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Skills That Transfer
&lt;/h3&gt;

&lt;p&gt;If you're a designer reading this with anxiety about obsolescence. I think your core skills transfer, building APIs had users, the systems were users, the developers were users. Now the Agents are users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understanding user intent&lt;/strong&gt; remains crucial. You're solving for what people want, even when the interface between the user and the system is an agent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Information architecture&lt;/strong&gt; evolves. Structuring information so an agent can navigate it requires the same thinking about relationships, hierarchies, and access patterns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Systems thinking&lt;/strong&gt; becomes more important. Agent-based systems involve more moving pieces, more failure modes, more need for coherent components.&lt;/p&gt;

&lt;p&gt;What won’t transfer is the visual craft, the micro-interactions, the animation choreography.&lt;/p&gt;

&lt;h3&gt;
  
  
  Design Becomes Invisible
&lt;/h3&gt;

&lt;p&gt;There's a Marshall McLuhan quality to what's happening. The interfaces that are everywhere in our visual landscape, the screens, buttons, forms, and menus, they aren’t the point. They are necessary infrastructure for the pre-AI era when computers couldn't understand intent and humans had to translate, or the interface had to force it.&lt;/p&gt;

&lt;p&gt;As that scaffolding comes down, design becomes the structure of context and the shape of information. It’s the logic of systems that we interact with but no longer see.&lt;/p&gt;

&lt;p&gt;So, the best interface is no interface, as the cliché goes. And we're about to find out if we meant it.&lt;/p&gt;

&lt;p&gt;My AI assistant doesn't have a color scheme. It doesn't have a logo animation or a distinctive illustrative style. It lives in generic chat windows with access to multiple platforms. And it's the most thoughtfully designed software I’ve interacted with, because the design is in what it knows, how it reasons, and how it adapts to what I need.&lt;/p&gt;

&lt;p&gt;That's the future of design in an agentic world.&lt;/p&gt;




&lt;p&gt;If you like this and are thinking about design and agents check out &lt;a href="https://focused.io" rel="noopener noreferrer"&gt;Focused&lt;/a&gt; we build AI agents that actually work. We're a LangChain consulting partner and help teams move from prototypes to production... context engineering included.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Customizing Memory in LangGraph Agents for Better Conversations</title>
      <dc:creator>Austin Vance</dc:creator>
      <pubDate>Wed, 16 Jul 2025 17:24:47 +0000</pubDate>
      <link>https://dev.to/focused_dot_io/customizing-memory-in-langgraph-agents-for-better-conversations-3l10</link>
      <guid>https://dev.to/focused_dot_io/customizing-memory-in-langgraph-agents-for-better-conversations-3l10</guid>
      <description>&lt;p&gt;Right now everyone is building conversational agents and having them remember past interactions is crucial for creating natural, engaging user experiences. &lt;a href="https://www.langchain.com" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt;, a powerful framework for developing LLM-based applications, has evolved its memory management. Recently, in v0.3.x they deprecated indivdual memory management classes, the recommended approach for memory in agents is to use LangGraph persistence. This tutorial dives into customizing memory using &lt;a href="https://www.langchain.com/langgraph" rel="noopener noreferrer"&gt;LangGraph&lt;/a&gt;, addressing common challenges like maintaining persistent chat history and optimizing for better conversations. Whether you're building chatbots or intelligent assistants, mastering LangGraph memory will enhance your agent's intelligence and make the UX feel more seamless across interactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview of Memory Types in LangChain
&lt;/h2&gt;

&lt;p&gt;As of LangChain v0.3.1, several legacy memory types have been deprecated in favor of more robust persistence via LangGraph. Here's a quick overview of the deprecated types and the migration path:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ConversationBufferMemory&lt;/code&gt;: Deprecated. Previously stored entire conversation history.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ConversationBufferWindowMemory&lt;/code&gt;: Deprecated. Limited to recent messages.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ConversationSummaryMemory&lt;/code&gt;: Deprecated. Summarized interactions.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ConversationEntityMemory&lt;/code&gt;: Deprecated. Extracted and stored entities.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these have been replaced by &lt;a href="https://langchain-ai.github.io/langgraph/concepts/persistence/" rel="noopener noreferrer"&gt;LangGraph's checkpointing system&lt;/a&gt;, which provides built-in persistence, support for multiple threads, and advanced features like time travel. LangGraph uses checkpointers (e.g., &lt;a href="https://langchain-ai.github.io/langgraph/reference/checkpoints/#langgraph.checkpoint.memory.InMemorySaver" rel="noopener noreferrer"&gt;InMemorySaver&lt;/a&gt; for in-memory, &lt;a href="https://langchain-ai.github.io/langgraph/reference/checkpoints/#langgraph.checkpoint.memory.InMemorySaver" rel="noopener noreferrer"&gt;SqliteSaver&lt;/a&gt; for persistent storage) to manage state across conversations.&lt;/p&gt;

&lt;p&gt;For more on the migration, refer to the &lt;a href="https://python.langchain.com/docs/versions/migrating_memory/" rel="noopener noreferrer"&gt;official migration guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this langchain memory tutorial, we'll start with simple setups using LangGraph and progress to custom persistent implementations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Simple Memory Buffers
&lt;/h2&gt;

&lt;p&gt;Let's begin by setting up a basic conversational agent with memory using LangGraph and &lt;code&gt;InMemorySaver&lt;/code&gt;. This provides simple, in-memory persistence across interactions within the same thread.&lt;/p&gt;

&lt;p&gt;First, ensure you have the necessary dependencies installed. We'll use LangChain version 0.3.26 (the latest as of July 2025), LangGraph, and OpenAI for the LLM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv add langchain langchain-openai langgraph langchain-community &lt;span class="nt"&gt;--bounds&lt;/span&gt; lower
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, here's a Python example to create an agent with simple memory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_core.tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langgraph.prebuilt&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_react_agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langgraph.checkpoint.memory&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MemorySaver&lt;/span&gt;

&lt;span class="c1"&gt;# Initialize LLM (replace with your API key)
&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Define a simple tool (e.g., for math calculations)
&lt;/span&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
   &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Multiplies two numbers.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;

&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Set up memory with MemorySaver
&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MemorySaver&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Create the agent
&lt;/span&gt;&lt;span class="n"&gt;agent_executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_react_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkpointer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Interact with the agent using a thread_id for memory
&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;configurable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;thread_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chat1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;

&lt;span class="n"&gt;response1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent_executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What is 3 times 4?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}]},&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Output: 12
&lt;/span&gt;
&lt;span class="n"&gt;response2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent_executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What was the result of the previous calculation?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}]},&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Output: The previous calculation result
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup uses MemorySaver to store and recall chat history within the same thread, making your langchain agents more context-aware.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom Memory Implementation
&lt;/h2&gt;

&lt;p&gt;For more advanced scenarios, such as persistent storage across restarts, use a persistent checkpointer like SqliteSaver. First, install the required package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv add langgraph-checkpoint-sqlite &lt;span class="nt"&gt;--bounds&lt;/span&gt; lower
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's how to create a persistent memory using SqliteSaver, ensuring conversations survive restarts—perfect for production chat apps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_core.tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langgraph.prebuilt&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_react_agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langgraph.checkpoint.sqlite&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SqliteSaver&lt;/span&gt;

&lt;span class="c1"&gt;# Initialize LLM (replace with your API key)
&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Define a simple tool
&lt;/span&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
   &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Multiplies two numbers.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;

&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Set up persistent memory with SqliteSaver
&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;checkpoints.db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check_same_thread&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;memory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SqliteSaver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create the agent
&lt;/span&gt;&lt;span class="n"&gt;agent_executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_react_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkpointer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Interact with the agent using a thread_id for memory
&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;configurable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;thread_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chat1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;

&lt;span class="n"&gt;response1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent_executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What is 3 times 4?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}]},&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Output: 12
&lt;/span&gt;
&lt;span class="n"&gt;response2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent_executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What was the result of the previous calculation?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}]},&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Output: The previous calculation result
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This implementation uses &lt;code&gt;SqliteSaver&lt;/code&gt; for file-based persistence (in "checkpoints.db"), providing true long-term conversation memory in LangGraph.&lt;/p&gt;

&lt;p&gt;For fully custom behavior, you can subclass BaseCheckpointSaver to create your own checkpointer, tailoring persistence (e.g., to JSON files or other databases).&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: Persistent Chat History
&lt;/h2&gt;

&lt;p&gt;Building on the persistent memory above, let's apply it to a real-world example: a persistent chatbot for customer support. The agent remembers user details across sessions and restarts, improving personalization.&lt;/p&gt;

&lt;p&gt;In the code snippet provided, after invoking with "Hi, I'm Bob.", stopping and restarting the script, then asking "Who am I?" should recall the name due to the SQLite storage. This addresses pain points in custom memory LangChain agents, ensuring seamless experiences across many sessions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimization Techniques
&lt;/h2&gt;

&lt;p&gt;To make your memory-efficient in LangGraph:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Limit History Length: Use message trimming functions like &lt;code&gt;trim_messages&lt;/code&gt; to avoid token limits.&lt;/li&gt;
&lt;li&gt;Summarization: Implement summary nodes in your graph to condense long histories.&lt;/li&gt;
&lt;li&gt;Entity Extraction: Add tools for entity extraction and store in a separate memory store for focused recall.&lt;/li&gt;
&lt;li&gt;Async Persistence: Use async checkpointers like AsyncSqliteSaver for high-traffic apps to prevent bottlenecks.&lt;/li&gt;
&lt;li&gt;Monitoring: Leverage LangSmith to trace memory usage and optimize graphs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These techniques optimize LLM agents memory, preventing issues like context overflow in extended conversations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: Enhancing Agent Intelligence
&lt;/h2&gt;

&lt;p&gt;Customizing memory with LangGraph transforms basic chatbots into intelligent, context-aware systems. By leveraging checkpointers for persistence, you can build robust applications that remember and adapt across sessions. This not only improves user engagement but also positions your projects for scalability.&lt;/p&gt;

&lt;p&gt;Ready to implement advanced conversation memory in LangGraph? If you need help with custom LangChain agents or optimizations, &lt;a href="https://focused.io/contact" rel="noopener noreferrer"&gt;contact us at Focused.io&lt;/a&gt;, we're here to help you build agents that work!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>langchain</category>
      <category>programming</category>
      <category>python</category>
    </item>
    <item>
      <title>Migrating Classic LangChain Agents to LangGraph a How To</title>
      <dc:creator>Austin Vance</dc:creator>
      <pubDate>Wed, 16 Jul 2025 15:46:31 +0000</pubDate>
      <link>https://dev.to/focused_dot_io/migrating-classic-langchain-agents-to-langgraph-a-how-to-nea</link>
      <guid>https://dev.to/focused_dot_io/migrating-classic-langchain-agents-to-langgraph-a-how-to-nea</guid>
      <description>&lt;p&gt;&lt;strong&gt;Takeaway&lt;/strong&gt;: You can swap a &lt;code&gt;legacy AgentExecutor&lt;/code&gt; for a &lt;code&gt;LangGraph&lt;/code&gt; node in a single commit. The payoff is lower overhead, deterministic routing, and native persistence.&lt;/p&gt;

&lt;h2&gt;
  
  
  WHY MIGRATE NOW?
&lt;/h2&gt;

&lt;p&gt;LangChain announced that with LangChain 0.2 the original agent helpers (&lt;code&gt;initialize_agent&lt;/code&gt;, &lt;code&gt;AgentExecutor&lt;/code&gt;) are deprecated and will only receive critical fixes. LangChain recommends moving to LangGraph’s node‑based approach for better control flow, built‑in persistence, and the ability to use multi‑actor workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  WHAT CHANGED IN LANGCHAIN 0.2 AND LATER?
&lt;/h2&gt;

&lt;p&gt;Legacy pattern (langchain &amp;lt; 0.2) versus current pattern (langchain 0.2 or newer):&lt;/p&gt;

&lt;p&gt;• Agent entry point – legacy: initialize_agent; current: graph node created with LangGraph helpers.&lt;br&gt;
• Configuration – legacy: many function kwargs that are hard to extend; current: typed graph state and composable nodes.&lt;br&gt;
• Persistence – legacy: DIY pickling or a custom DB; current: checkpoint helpers built into LangGraph. ￼&lt;br&gt;
• Deprecation status – legacy helpers are deprecated; the LangGraph approach is the long‑term, fully supported path.&lt;/p&gt;
&lt;h2&gt;
  
  
  CODE DIFF: FROM &lt;code&gt;initialize_agent&lt;/code&gt; TO A LANGGRAPH NODE
&lt;/h2&gt;

&lt;p&gt;Below is a minimal ReAct agent that calls a calculator tool—first the legacy way, then the LangGraph way.&lt;/p&gt;

&lt;p&gt;Before (legacy agent)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AgentExecutor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AgentType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initialize_agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;load_tools&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.chat_models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_core.tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;


&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;capitalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Capitalize the text.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;initialize_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;capitalize&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;AgentType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ZERO_SHOT_REACT_DESCRIPTION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;can you capitalize this text: hello world&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After (LangGraph)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langgraph.prebuilt&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_react_agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langgraph.graph&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;END&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StateGraph&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Calculator&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;add_messages&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;capitalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Capitalize the text.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Capitalizing text: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;capitalize&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create the ReAct agent node
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;agent_node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_react_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Build a simple single‑node graph
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StateGraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;react_agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;agent_node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_entry_point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;react_agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;react_agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;END&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;agent_executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent_executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;can you capitalize this text: hello world&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}]})&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The functional behavior is identical, but you gain an explicit state object, the ability to add router or guardrail nodes later without refactoring the agent itself, and full compatibility with LangGraph’s checkpoint and observability APIs. ￼ ￼&lt;/p&gt;

&lt;h2&gt;
  
  
  UPDATING TESTS AND CALLBACKS
&lt;/h2&gt;

&lt;p&gt;Unit tests with Pytest and LangSmith&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;rom&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;agent_executor&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_capitalize_text&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent_executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;can you capitalize this text: hello world&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HELLO WORLD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For richer coverage, use LangSmith’s pytest plugin to log run trees and score outputs with metrics instead of brittle string matches. ￼ ￼&lt;/p&gt;

&lt;p&gt;Callbacks&lt;br&gt;
If you previously passed callbacks into &lt;code&gt;initialize_agent(..., callbacks=[StdOutCallbackHandler()]),&lt;/code&gt; move them to the graph compile step (or to individual nodes for fine‑grained tracing):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_core.callbacks.base&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseCallbackHandler&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrintCallbackHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseCallbackHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_llm_start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serialized&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;any&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;prompts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LLM start: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;prompts&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent_executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;can you capitalize this text: hello world&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}]},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;callbacks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;PrintCallbackHandler&lt;/span&gt;&lt;span class="p"&gt;()]})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  PRODUCTION ROLLOUT CHECKLIST
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Freeze versions: pin langchain&amp;gt;=0.2,&amp;lt;0.3, then move to 0.3 and the latest stable langgraph.&lt;/li&gt;
&lt;li&gt;Refactor imports: search‑and‑replace initialize_agent( with create_react_agent(.&lt;/li&gt;
&lt;li&gt;Compile once: cache the compiled graph (agent_executor) at application start to avoid cold‑start overhead.&lt;/li&gt;
&lt;li&gt;State schema: define a TypedDict or Pydantic model for your graph state to catch breaking changes early.&lt;/li&gt;
&lt;li&gt;Health probes: invoke the graph with {“message”: {role: {“user”: “ping”}}} and expect "pong" so orchestration platforms detect failures.&lt;/li&gt;
&lt;li&gt;Checkpoint storage: configure S3, Redis, or SQLite persistence before rolling to production if your flows exceed a single request. ￼&lt;/li&gt;
&lt;li&gt;Observability: enable LANGCHAIN_TRACING_V2=true and send traces to LangSmith.&lt;/li&gt;
&lt;li&gt;Canary deploy: route a slice of traffic to the new executor and compare latency and error rate against the legacy path.&lt;/li&gt;
&lt;li&gt;Retire legacy code: delete deprecated agent imports when metrics hit parity.&lt;/li&gt;
&lt;li&gt;Document the graph: export a GraphViz diagram and commit it so new teammates can visualize the flow.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  FINAL WORD
&lt;/h2&gt;

&lt;p&gt;Upgrading to LangGraph is not a risky rewrite; it is a surgical swap that positions your agent for reliable scale, granular observability, and future multi‑actor magic. Make the jump today and own the graph—before the graph owns you.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>langchain</category>
      <category>python</category>
      <category>programming</category>
    </item>
    <item>
      <title>Why Big Classes Get Bigger: Understanding Preferential Attachment in Your Code</title>
      <dc:creator>Austin Vance</dc:creator>
      <pubDate>Thu, 19 Dec 2024 16:15:53 +0000</pubDate>
      <link>https://dev.to/focused_dot_io/why-big-classes-get-bigger-understanding-preferential-attachment-in-your-code-47ib</link>
      <guid>https://dev.to/focused_dot_io/why-big-classes-get-bigger-understanding-preferential-attachment-in-your-code-47ib</guid>
      <description>&lt;h1&gt;
  
  
  The Law of Preferential attachment: Why your User class keeps growing
&lt;/h1&gt;

&lt;p&gt;Software often models the natural world. One pattern I have been thinking about is why our biggest components continue to grow. In natural systems there is a phenomenon known as preferential attachment, which describes how new elements in a system tend to collect at the most connected nodes in the network. Zoologists observed this phenomenon in taxonomy where the largest class, order, family, etc collect newly named organisms.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pull of the common class
&lt;/h2&gt;

&lt;p&gt;This is exactly what happens in our codebases. Someone once said there are two hard things in computer science: “Naming things, cache-invalidation, and off by one errors.” I love the joke, but it hurts every time. Poorly named components create a gravitational pull forcing the next developer between a rock and a hard place: either comb through the component to understand what its purpose is and then refactor the name to be more specific, or take the path of least resistance and add new functionality further expanding the responsibility of the component. This decision creates a feedback loop attracting more and more “like” functionality.&lt;/p&gt;

&lt;p&gt;Consider a component named &lt;code&gt;UserHandler&lt;/code&gt;. The original intent could be to manage a user's authentication, but because of the vague name, the component could also logically handle anything user related. The &lt;code&gt;UserHandler&lt;/code&gt; soon contains preferences, notifications, and social connections along with the original intention, authentication. Each addition makes new complexity even easier to add, moving it closer and closer to the “god class”.&lt;/p&gt;

&lt;h2&gt;
  
  
  Yes there’s math
&lt;/h2&gt;

&lt;p&gt;There’s actually some math to back this up. Preferential attachment follows a power law distribution where the probability of new functionality being added to a component is proportional to the existing functionality of the component. We can express this mathematically if &lt;code&gt;F(c)&lt;/code&gt; represents the functionality in component &lt;code&gt;c&lt;/code&gt;, then the probability &lt;code&gt;P&lt;/code&gt; of adding complexity to that component is:&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;P(c)=F(c)α∑iF(i)α
P(c) = \frac{F(c)^\alpha}{\sum_{i} F(i)^\alpha}
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;c&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mop"&gt;&lt;span class="mop op-symbol small-op"&gt;∑&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;F&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;i&lt;/span&gt;&lt;span class="mclose"&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;α&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;F&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;c&lt;/span&gt;&lt;span class="mclose"&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;α&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;Where 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;α\alpha &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;α&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 represents the strength of the preferential attachment.&lt;/p&gt;

&lt;p&gt;Imagine a system where we have three components&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* A - 100 lines of code
* B - 200 lines of code
* C - 1000 lines of code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And the strength of preferential attachment is 2 (which is a pretty common value in the power law) then we end up with&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;P(A)=0.006=1002(100+200+1000)2
P(A) = 0.006 = \frac{100^2}{(100+200+1000)^2}
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;A&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;0.006&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;100&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;200&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1000&lt;/span&gt;&lt;span class="mclose"&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;10&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;



&lt;p&gt;Vs&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;P(C)=0.591=10002(100+200+1000)2
P(C) = 0.591 = \frac{1000^2}{(100+200+1000)^2}
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;C&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;0.591&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;100&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;200&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1000&lt;/span&gt;&lt;span class="mclose"&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;100&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;The probability of adding to C is not 10x but 100x more likely. Which makes it more clear why some classes continue to grow while most remain small.&lt;/p&gt;

&lt;h2&gt;
  
  
  There is no strength in numbers
&lt;/h2&gt;

&lt;p&gt;If it is not painfully obvious, the larger the component the more impact that that component has on the efficiency of future development. Large components become immovable objects in architectures shaping the systems evolution. This shape has a significant cost on the ability of a system to evolve. &lt;/p&gt;

&lt;p&gt;Consider testing. As a component attracts more and more functionality its dependencies grow as well creating more and more potential interactions that must be validated every time the component changes. Changing one part of a system will eventually cascade to the “God Component” and then the changes to that component will require testing and updates to other seemingly unrelated parts of the code.&lt;/p&gt;

&lt;p&gt;The answer to preferential attachment is more simply said than done, just name things better. It’s always easier to bring components together than it is to untangle them and refactor them apart.&lt;/p&gt;

&lt;p&gt;Using the &lt;code&gt;UserHandler&lt;/code&gt; example above&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;UserHandler&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Credentials&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;validateSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;revokeAccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Credentials&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;validateSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;revokeAccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;fetchPreferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;UserPreferences&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;updatePreferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;preferences&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserPreferences&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;UserPreferences&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;getDefaultPreferences&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;UserPreferences&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;getNotificationSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;NotificationSettings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;updateNotificationSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NotificationSettings&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;NotificationSettings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;addSocial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;connectionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;removeSocial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;connectionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;listSocial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This component clearly does a lot and if each method is implemented it could be hundreds of lines long with dependencies on databases, caches, and other whole components&lt;/p&gt;

&lt;h2&gt;
  
  
  The only constant is change
&lt;/h2&gt;

&lt;p&gt;There are four clear concerns here that each could have their own class or component, creating more clear naming and a more clear place for new functionality to go&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;AuthenticationService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Credentials&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;validateSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;revokeAccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;PreferenceService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;UserPreferences&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;preferences&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserPreferences&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;UserPreferences&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;getDefaults&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;UserPreferences&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;NotificationService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;getSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;NotificationSettings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;updateSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NotificationSettings&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;NotificationSettings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;SocialService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;addSocial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;connectionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;removeSocial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;connectionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;listSocial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This form is more clear and gives clear indication of the behavior for each component and makes it easy to test and easy for developers to grok the context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build for change
&lt;/h2&gt;

&lt;p&gt;Our job as developers is to build code that enables change in a system and by creating clear boundaries with naming we avoid the pull of preferential attachment making new components easier to build an new functionality easier to change old.&lt;/p&gt;

&lt;p&gt;Just as natural systems tend toward entropy, software systems tend toward complexity, and the science supports it. Our role is to manage that complexity by introducing patterns and constraints ensuring sustainable and predictable growth of the system over time.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Shameless plug&lt;/strong&gt;&lt;br&gt;
If you've got a "god class" or "god service" and need help decomposing and untangling your largest components or systems drop me a note or check out focused.io. We love working with legacy code and helping it welcome the future!&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>coding</category>
      <category>programming</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Unlock the Power of LangChain: Deploying to Production Made Easy</title>
      <dc:creator>Austin Vance</dc:creator>
      <pubDate>Wed, 14 Feb 2024 20:33:45 +0000</pubDate>
      <link>https://dev.to/focused_dot_io/unlock-the-power-of-langchain-deploying-to-production-made-easy-3lmn</link>
      <guid>https://dev.to/focused_dot_io/unlock-the-power-of-langchain-deploying-to-production-made-easy-3lmn</guid>
      <description>&lt;p&gt;In this tutorial, Austin Vance, CEO and co-founder of Focused Labs, will guide you through deploying a PDF RAG with LangChain to production! &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/CbBIwVxjdP8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In this captivating video, we will dive deep into the process of deploying a rag to production, taking you on an informative and engaging step-by-step journey. Throughout this tutorial, we will explore the intricacies of transforming a local rag into a powerful and accessible resource on the digital ocean app platform. Don't miss out on this highly informative and exciting adventure - make sure to subscribe now to stay updated with all the latest content!&lt;br&gt;
Don't forget to subscribe for more tutorials like this.&lt;/p&gt;




&lt;p&gt;Just to remember what happened so far:&lt;/p&gt;

&lt;p&gt;In Part One You will Learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a new app using LangChain's LangServe&lt;/li&gt;
&lt;li&gt;ingestion of PDFs using unstructuredio
&lt;/li&gt;
&lt;li&gt;Chunking of documents via LangChain's SemanticChunker&lt;/li&gt;
&lt;li&gt;Embedding chunks using OpenAI's embeddings API&lt;/li&gt;
&lt;li&gt;Storing embedded chunks into a PGVector a vector database&lt;/li&gt;
&lt;li&gt;Build a LCEL Chain for LangServe that uses PGVector as a retriever&lt;/li&gt;
&lt;li&gt;Use the LangServe playground as a way to test our RAG&lt;/li&gt;
&lt;li&gt;Stream output including document sources to a future front end.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Part 2 we will focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating a front end with Typescript, React, and Tailwind&lt;/li&gt;
&lt;li&gt;Display sources of information along with the LLM output&lt;/li&gt;
&lt;li&gt;Stream to the frontend with Server Sent Events&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Part 3 we will focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploying the Backend application to @DigitalOcean&lt;/li&gt;
&lt;li&gt;Deploying the frontend to &lt;a class="mentioned-user" href="https://dev.to/digitalocean_staff"&gt;@digitalocean_staff&lt;/a&gt; App Platform&lt;/li&gt;
&lt;li&gt;Use a managed Postgres Database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Part 4 we will focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding Memory to the  @LangChain  Chain with PostgreSQL&lt;/li&gt;
&lt;li&gt;Add Multiquery to the chain for better breadth of search&lt;/li&gt;
&lt;li&gt;Add sessions to the Chat History&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Github repo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/focused-labs/pdf_rag" rel="noopener noreferrer"&gt;https://github.com/focused-labs/pdf_rag&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>tutorial</category>
      <category>python</category>
    </item>
    <item>
      <title>Chat With Your PDFs: Part 2 - Frontend - An End to End LangChain Tutorial. Build A RAG with OpenAI.</title>
      <dc:creator>Austin Vance</dc:creator>
      <pubDate>Mon, 29 Jan 2024 23:35:58 +0000</pubDate>
      <link>https://dev.to/focused_dot_io/chat-with-your-pdfs-part-2-frontend-an-end-to-end-langchain-tutorial-build-a-rag-with-openai-e0p</link>
      <guid>https://dev.to/focused_dot_io/chat-with-your-pdfs-part-2-frontend-an-end-to-end-langchain-tutorial-build-a-rag-with-openai-e0p</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/xFWllDS6ZRw"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In this video we are going to dive into part two of building and deploying a fully custom RAG with @LangChain and   @OpenAI. In this tutorial, code with me, video we will take the LangServe pipeline we developed in Part 1 and build out a fully functioning React &amp;amp; Typescript frontend using TailwindCSS.&lt;/p&gt;

&lt;p&gt;The video did end up getting pretty long so we will deploy the app to  &lt;a class="mentioned-user" href="https://dev.to/digitalocean_staff"&gt;@digitalocean_staff&lt;/a&gt;  and to  @LangChain in Part 3!&lt;/p&gt;




&lt;p&gt;Just to remember what happened so far:&lt;/p&gt;

&lt;p&gt;In Part One You will Learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a new app using @LangChain's LangServe&lt;/li&gt;
&lt;li&gt;ingestion of PDFs using @unstructuredio&lt;/li&gt;
&lt;li&gt;Chunking of documents via @LangChain's SemanticChunker&lt;/li&gt;
&lt;li&gt;Embedding chunks using @OpenAI's embeddings API&lt;/li&gt;
&lt;li&gt;Storing embedded chunks into a PGVector a vector database&lt;/li&gt;
&lt;li&gt;Build a LCEL Chain for LangServe that uses PGVector as a retriever&lt;/li&gt;
&lt;li&gt;Use the LangServe playground as a way to test our RAG&lt;/li&gt;
&lt;li&gt;Stream output including document sources to a future front end.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Part 2 we will focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating a front end with Typescript, React, and Tailwind&lt;/li&gt;
&lt;li&gt;Display sources of information along with the LLM output&lt;/li&gt;
&lt;li&gt;Stream to the frontend with Server Sent Events&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Part 3 we will focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploying the Backend application to @DigitalOcean &amp;amp;   @LangChain's LangServe hosted platform to compare&lt;/li&gt;
&lt;li&gt;Add LangSmith Integrations&lt;/li&gt;
&lt;li&gt;Deploying the frontend to &lt;a class="mentioned-user" href="https://dev.to/digitalocean_staff"&gt;@digitalocean_staff&lt;/a&gt;'s App Platform&lt;/li&gt;
&lt;li&gt;Use a managed Postgres Database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Part 4 we will focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding Memory to the @LangChain Chain with PostgreSQL&lt;/li&gt;
&lt;li&gt;Add Multiquery to the chain for better breadth of search&lt;/li&gt;
&lt;li&gt;Add sessions to the Chat History&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Github repo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/focused-labs/pdf_rag" rel="noopener noreferrer"&gt;https://github.com/focused-labs/pdf_rag&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>react</category>
    </item>
    <item>
      <title>Chat With Your PDFs: Part 1 - An End to End LangChain Tutorial For Building A Custom RAG with OpenAI.</title>
      <dc:creator>Austin Vance</dc:creator>
      <pubDate>Wed, 24 Jan 2024 17:48:57 +0000</pubDate>
      <link>https://dev.to/focused_dot_io/chat-with-your-pdfs-an-end-to-end-langchain-tutorial-for-building-a-custom-rag-with-openai-part-1-3oi3</link>
      <guid>https://dev.to/focused_dot_io/chat-with-your-pdfs-an-end-to-end-langchain-tutorial-for-building-a-custom-rag-with-openai-part-1-3oi3</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/UwgZmrRAgQ4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;A common use case for developing AI chat bots is ingesting PDF documents and allowing users to ask questions, inspect the documents, and learn from them. In this tutorial we will start with a 100% blank project and build an end to end chat application that allows users to chat about the Epic Games vs Apple Lawsuit. &lt;/p&gt;

&lt;p&gt;There's a lot of content packed into this one video so please ask questions in the comments and I will do my best to help you get past any hurdles. &lt;/p&gt;

&lt;p&gt;In Part One You will Learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a new app using  @LangChain  's LangServe&lt;/li&gt;
&lt;li&gt;ingestion of PDFs using  @unstructuredio &lt;/li&gt;
&lt;li&gt;Chunking of documents via  @LangChain  's SemanticChunker&lt;/li&gt;
&lt;li&gt;Embedding chunks using  @OpenAI  's embeddings API&lt;/li&gt;
&lt;li&gt;Storing embedded chunks into a PGVector a vector database&lt;/li&gt;
&lt;li&gt;Build a LCEL Chain for LangServe that uses PGVector as a retriever&lt;/li&gt;
&lt;li&gt;Use the LangServe playground as a way to test our RAG&lt;/li&gt;
&lt;li&gt;Stream output including document sources to a future front end.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Part 2 we will focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating a front end with Typescript, React, and Tailwind&lt;/li&gt;
&lt;li&gt;Display sources of information along with the LLM output&lt;/li&gt;
&lt;li&gt;Stream to the frontend with Server Sent Events&lt;/li&gt;
&lt;li&gt;Deploying the Backend application to  @DigitalOcean  &amp;amp;  @LangChain 's LangServe hosted platform to compare&lt;/li&gt;
&lt;li&gt;Deploying the frontend to  @DigitalOcean 's App Platform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Part 3 we will focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding Memory to the  @LangChain  Chain with PostgreSQL&lt;/li&gt;
&lt;li&gt;Add Multiquery to the chain for better breadth of search&lt;/li&gt;
&lt;li&gt;Add sessions to the Chat History&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Github repo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/focused-labs/pdf_rag" rel="noopener noreferrer"&gt;https://github.com/focused-labs/pdf_rag&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>tutorial</category>
      <category>video</category>
      <category>python</category>
    </item>
    <item>
      <title>Beyond a JPEG: NFT as a Primary Key</title>
      <dc:creator>Austin Vance</dc:creator>
      <pubDate>Wed, 12 Jan 2022 14:17:10 +0000</pubDate>
      <link>https://dev.to/focused_dot_io/beyond-a-jpeg-nft-as-a-primary-key-1nfo</link>
      <guid>https://dev.to/focused_dot_io/beyond-a-jpeg-nft-as-a-primary-key-1nfo</guid>
      <description>&lt;p&gt;These days, we most often see NFTs used to record the ownership information of &lt;a href="https://www.bloomberg.com/news/articles/2021-03-11/beeple-everydays-nft-sells-at-art-auction-for-60-million-paid-in-ether" rel="noopener noreferrer"&gt;artwork&lt;/a&gt;. There are the pie-in-the-sky folks who think NFTs will become a new standard for ownership for everything from &lt;a href="https://www.forbes.com/sites/nataliakarayaneva/2021/11/24/real-estate-nfts-how-it-began/?sh=1f3f8d033b12" rel="noopener noreferrer"&gt;houses&lt;/a&gt;, to &lt;a href="https://cointelegraph.com/news/rapper-nas-to-let-fans-own-part-of-his-music-through-nfts" rel="noopener noreferrer"&gt;music&lt;/a&gt;, to &lt;a href="https://www.bbc.com/news/technology-59568929" rel="noopener noreferrer"&gt;in-game items&lt;/a&gt; - only time will tell.&lt;/p&gt;

&lt;p&gt;NFTs are powerful, but they ​get a bad rap. I don't have strong opinions about the disruptive nature of NFTs or how they affect art, games, or anything else. I do see that the "&lt;em&gt;Non-Fungible&lt;/em&gt;" attribute of an NFT has some remarkable properties that allow smart contract developers to store state in a permissionless manner and enable that state to be transferred or sold.&lt;br&gt;
​&lt;br&gt;
Ok, I admit that's rather abstract and confusing. Since this is a dev blog, let's build an app that uses an NFT contract and holds information specific to a user.&lt;/p&gt;

&lt;p&gt;Before we dive in, this is not revolutionary. Several projects already do this. The &lt;a href="https://mai.finance" rel="noopener noreferrer"&gt;QiDAO&lt;/a&gt; uses &lt;a href="https://docs.mai.finance/functions/smart-contract-functions" rel="noopener noreferrer"&gt;NFTs to manage loan ownership&lt;/a&gt; and &lt;a href="https://uniswap.org/blog/uniswap-v3" rel="noopener noreferrer"&gt;Uniswap V3&lt;/a&gt; uses NFTs to manage a Liquidity Position. Before the release of V3, Uniswap recorded Liquidity by issuing the liquidity provider an LP Token. These tokens are an &lt;a href="https://ethereum.org/en/developers/docs/standards/tokens/erc-20/" rel="noopener noreferrer"&gt;ERC-20&lt;/a&gt; token, and ERC-20's are fungible. Fungibility means that one token is the same as another. Think if someone hands you a 1€ note. That note is the same as the other 1€'s in your wallet. The ERC-20's are 100% transferable, farmable, and sellable, but they cannot store any secondary information about a Liquidity Position.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NFTs allow for encapsulation and transfer and sale of metadata in a permissionless manner&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Uniswap V3 enables the addition of metadata to a liquidity position. Instead of issuing an ERC-20 token to represent a liquidity position, they issue an &lt;a href="https://ethereum.org/en/developers/docs/standards/tokens/erc-721/" rel="noopener noreferrer"&gt;ERC-721 (NFT)&lt;/a&gt;. With that ERC-721, Uniswap can now add unique features (like impermanent loss protection) to each liquidity position.&lt;/p&gt;

&lt;p&gt;How cool is that! But how does it work? Let's build it.&lt;/p&gt;



&lt;p&gt;Without giving too much background, at Focused Labs, we offer a blog bounty and a bonus for blogging streaks. The more frequently the company blogs, the larger the blog bounty becomes.&lt;/p&gt;

&lt;p&gt;I want to move this bounty from a spreadsheet to the blockchain and use an NFT to identify wallets contributing a blog to the streak.&lt;/p&gt;

&lt;p&gt;We will need an ERC-721 contract. Let's use &lt;a href="https://openzeppelin.com/" rel="noopener noreferrer"&gt;OpenZeppelin&lt;/a&gt; to enforce the correct interface for our NFT.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// contracts/FocusedBlogPost.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract FocusedBlogPost is ERC721 {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    const 
    constructor() ERC721("FocusedBlogPost", "FCSD") {}

    function publishBlog(address blogger)
        public
        returns (uint256)
    {
        _tokenIds.increment();

        uint256 newItemId = _tokenIds.current();
        _mint(blogger, newItemId);

        return newItemId;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whenever we call &lt;code&gt;publishBlog()&lt;/code&gt; with an address, we will mint and transfer an NFT to the blogger.&lt;/p&gt;

&lt;p&gt;A new blog must be published every two weeks to earn a streak. Each week at least one new blog post goes out our streak counter increases. This streak counter is a multiple on our Blog Bounty! Now let’s set up our contract to add logic around a streak.&lt;/p&gt;

&lt;p&gt;Let's start by tracking when new posts are published.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contract FocusedBlogPost is ERC721 {
    // ...

    struct BlogPost {
        string postUri;
        uint256 publishedAt;
        address originalAuthor;
    }

    // ...

    // two weeks in seconds
    uint constant twoWeeks = 60 * 60 * 24 * 14;

    // map of NFT ids to Blog Posts
    mapping(uint256 =&amp;gt; BlogPost) public blogPosts;

    function publishBlog(address blogger, string memory postUri)
    public
    returns (uint256)
    {
        // ...
        _mint(player, newPostId);

        blogPosts[newPostId] = BlogPost({
            postUri: postUri,
            publishedAt: block.timestamp,
            originalAuthor: blogger
        });

        // ...
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now every time a new blog post is published, we not only create an NFT for the post, but we record when it was published and keep track of the original author.&lt;/p&gt;

&lt;p&gt;We still don't track a streak, though, so let's add a new method to our token contract.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contract FocusedBlogPost is ERC721Enumerable {
    using SafeMath for uint256;

    // ...

     function getCurrentStreak() public view returns (uint) {
        uint streak = 0;
        if (totalSupply() == 0 || totalSupply() == 1) {
            return streak;
        }

        for (uint256 i = totalSupply().sub(1); i &amp;gt; 0; i--) {
            BlogPost memory currentBlog = blogPosts[tokenByIndex(i)];
            BlogPost memory previousBlog = blogPosts[tokenByIndex(i).sub(1)];

            if (currentBlog.publishedAt - previousBlog.publishedAt &amp;gt;= twoWeeks) {
                break;
            }

            streak++;
        }
        return streak;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use OpenZeppelin's &lt;code&gt;ERC721Enumerable&lt;/code&gt;; this gives us a few new methods to loop through each NFT we have minted. Then we can check the timestamp of each &lt;code&gt;BlogPosts.publishedAt&lt;/code&gt;. Pretty easy, right?!&lt;/p&gt;

&lt;p&gt;In a future part of this series, we will continue to add features to the NFT, allowing for payouts to streak contributors and adding validations like only increasing a streak if the author isn't in the current streak.&lt;/p&gt;




&lt;p&gt;Although this example is a bit contrived, honestly, why would someone want to transfer their streak? I think the practical applications are straightforward.&lt;/p&gt;

&lt;p&gt;The NFT can act as a unique identifier, recording metadata about an event or individual actions. A dApp can then use that NFT to make decisions like providing access to a "secret" website or paying a dividend for contributing a blogpost.&lt;/p&gt;

&lt;p&gt;Source code is available on &lt;a href="https://github.com/focused-labs/NFTs-for-more-than-just-JPEGs" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; where there is a complete example with passing tests.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>blockchain</category>
      <category>programming</category>
      <category>testing</category>
    </item>
    <item>
      <title>Testing Interactions with other Smart Contracts</title>
      <dc:creator>Austin Vance</dc:creator>
      <pubDate>Tue, 04 Jan 2022 00:34:51 +0000</pubDate>
      <link>https://dev.to/focused_dot_io/testing-interactions-with-other-smart-contracts-2m7c</link>
      <guid>https://dev.to/focused_dot_io/testing-interactions-with-other-smart-contracts-2m7c</guid>
      <description>&lt;p&gt;Developing on the blockchain is an incredible experience. The ecosystem is open and permissionless; each project becoming a lego brick in whatever idea a developer has in mind. Because of the open nature of the blockchain, it's not uncommon to have your smart contracts interact with another project’s contracts. It may be a &lt;a href="https://chain.link/" rel="noopener noreferrer"&gt;Chainlink Oracle&lt;/a&gt;, a Dex like &lt;a href="https://uniswap.org/" rel="noopener noreferrer"&gt;Uniswap&lt;/a&gt;, or a Lending platform like the &lt;a href="https://mai.finance/" rel="noopener noreferrer"&gt;QiDAO&lt;/a&gt;, or maybe you interact with all three in a single contract?&lt;/p&gt;

&lt;p&gt;But how do you test your contract based on responses and interactions with these external contracts?&lt;/p&gt;

&lt;p&gt;There are two ways: you can deploy "mock contracts" or you can use a mocking library. There are tradeoffs, but for this post I am going to focus on using a &lt;a href="https://smock.readthedocs.io/en/latest/index.html" rel="noopener noreferrer"&gt;Smock's&lt;/a&gt; mocking library to put external contracts into a place for testing.&lt;/p&gt;

&lt;p&gt;Smock depends on &lt;a href="https://hardhat.org/" rel="noopener noreferrer"&gt;Hardhat&lt;/a&gt; so you need to have a Hardhat project. For the sake of this post let's write and test a smart contract that can liquidate a loan on the &lt;a href="https://mai.finance/" rel="noopener noreferrer"&gt;QiDAO&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://mai.finance/" rel="noopener noreferrer"&gt;QiDAO&lt;/a&gt; contracts can be found in their &lt;a href="https://docs.mai.finance/functions/smart-contract-addresses" rel="noopener noreferrer"&gt;docs&lt;/a&gt; and the source can be found on their github.&lt;/p&gt;

&lt;p&gt;Specifically we will be using the &lt;a href="https://github.com/0xlaozi/qidao/blob/main/contracts/erc20Stablecoin/erc20Stablecoin.sol" rel="noopener noreferrer"&gt;erc20Stablecoin&lt;/a&gt; contract deployed for &lt;a href="https://polygonscan.com/address/0x61167073E31b1DAd85a3E531211c7B8F1E5cAE72" rel="noopener noreferrer"&gt;LINK - 0x61167073E31b1DAd85a3E531211c7B8F1E5cAE72&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our simple liquidation contract looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contract LoanLiquidator {
  address const vaultAddress = 0x61167073E31b1DAd85a3E531211c7B8F1E5cAE72
  function liquidate(uint256 vaultId) external {
    erc20Stablecoin vault = erc20Stablecoin(vaultAddress);
    require(vault.checkLiquidation(vaultId), "Vault not below liquidation threshold");

    vault.liquidateVault(vaultId);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For simplicity let's test the two cases of &lt;code&gt;checkLiquidation&lt;/code&gt; as &lt;code&gt;liquidateVault&lt;/code&gt; doesn't return anything. First we will test  but there is a gotcha. We can get into that later!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LoanLiquidator&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#liquidate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should revert if the vault cannot be liquidated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;call the vaults liquidateVault if the loan can be liquidated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we aren't using Smock then this is pretty difficult. I would either need to inject a contract address into the &lt;code&gt;LoanLiquidator&lt;/code&gt; and then have that address implement &lt;code&gt;erc20Stablecoin&lt;/code&gt;'s interface. That's for another blog post.&lt;/p&gt;

&lt;p&gt;In this post it's a lot simpler because we will use Smock, but there are limitations. First let's focus on &lt;code&gt;it("should revert if the vault cannot be liquidated")&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should revert if the vault cannot be liquidated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;VAULT_ADDRESS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0x61167073E31b1DAd85a3E531211c7B8F1E5cAE72&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;I&lt;/span&gt; &lt;span class="nx"&gt;am&lt;/span&gt; &lt;span class="nx"&gt;using&lt;/span&gt; &lt;span class="nx"&gt;Typechain&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;generate&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;erc20Stablecoin&lt;/span&gt; &lt;span class="nx"&gt;ABI&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fake&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;smock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Erc20QiStablecoin&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;Erc20QiStablecoin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;abi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;VAULT_ADDRESS&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkLiquidation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LoanLiquidatorFactory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContractFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LoanLiquidatator&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;LoanLiquidator__factory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loanLiquidator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;LoanLiquidatatorFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;loanLiquidator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deployed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loanLiquidator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;liquidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;revertedWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Vault not below liquidation threshold&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;liquidateVault&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;been&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;called&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The magic here lies in the &lt;code&gt;opts&lt;/code&gt; for Smock's &lt;code&gt;#fake()&lt;/code&gt; method. You can pass an existing contract address to &lt;code&gt;#fake()&lt;/code&gt; and Smock will use Hardhat's &lt;code&gt;[hardhat_setCode](https://hardhat.org/hardhat-network/reference/#hardhat-setcode)&lt;/code&gt; rpc call to replace the contract at the address given with Smock's fake implementation of the contract.&lt;/p&gt;

&lt;p&gt;Next lets test &lt;code&gt;it("call the vaults liquidateVault if the loan can be liquidated")&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;call the vaults liquidateVault if the loan can be liquidated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;VAULT_ADDRESS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0x61167073E31b1DAd85a3E531211c7B8F1E5cAE72&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;I&lt;/span&gt; &lt;span class="nx"&gt;am&lt;/span&gt; &lt;span class="nx"&gt;using&lt;/span&gt; &lt;span class="nx"&gt;Typechain&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;generate&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;erc20Stablecoin&lt;/span&gt; &lt;span class="nx"&gt;ABI&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fake&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;smock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Erc20QiStablecoin&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;Erc20QiStablecoin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;abi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;VAULT_ADDRESS&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkLiquidation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LoanLiquidatorFactory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContractFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LoanLiquidatator&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;LoanLiquidator__factory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loanLiquidator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;LoanLiquidatorFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;loanLiquidator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deployed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loanLiquidator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;liquidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reverted&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;liquidateVault&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;been&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;called&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case you'll get green lights and you can keep on coding. In the real world there's a gotcha. When you fake a contract you fake &lt;em&gt;all&lt;/em&gt; of it. By default, functions will now return their Zero value. If you have calls to later in your implementation that require non-zero values.&lt;/p&gt;

&lt;p&gt;A clear example of this is if we add the method &lt;code&gt;#getVaultAddress()&lt;/code&gt; to our &lt;code&gt;LoanLiquidator&lt;/code&gt; contract:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function getVaultAddress() public view returns (address) {
  return vaultAddress;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now in test, after faking, if you call &lt;code&gt;#getVaultAddress()&lt;/code&gt; you will get the zero address &lt;code&gt;0x0000000000000000000000000000000000000000&lt;/code&gt; If you had code that used the returned address you may see an error like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Error: Transaction reverted: function call to a non-contract account&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This just scratches the surface of what's possible with Smock and Solidity. The Web3 space is one of the most test driven development friendly and open ecosystems I have ever encountered.&lt;/p&gt;

&lt;p&gt;If you're interested in TDD, writing great software, and developing cutting edge technology, don't hesitate to check out our &lt;a href="//notion://www.notion.so/careers"&gt;careers page&lt;/a&gt;. Or if you’re looking for a partner to help build your next dApp, backend, or frontend and up-skill your  team please reach out to us at &lt;a href="mailto:work@withfocus.com"&gt;work@withfocus.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>tutorial</category>
      <category>javascript</category>
      <category>testing</category>
    </item>
    <item>
      <title>TIL: the Git -p flag!</title>
      <dc:creator>Austin Vance</dc:creator>
      <pubDate>Fri, 15 Jan 2021 19:03:17 +0000</pubDate>
      <link>https://dev.to/focused_dot_io/til-the-git-p-flag-2b1g</link>
      <guid>https://dev.to/focused_dot_io/til-the-git-p-flag-2b1g</guid>
      <description>&lt;p&gt;Today I learned about the &lt;code&gt;-p&lt;/code&gt; flag in git and now I want to use it everywhere!&lt;/p&gt;

&lt;p&gt;According to the man page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ man git-add

...
-p, --patch
Use the interactive patch selection interface to chose which changes to commit. See git-add(1) for details
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use the &lt;code&gt;-p&lt;/code&gt; flag with &lt;code&gt;checkout&lt;/code&gt;, &lt;code&gt;add&lt;/code&gt;, &lt;code&gt;commit&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsdunby7qyg13pqr4f0go.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsdunby7qyg13pqr4f0go.gif" alt="Git Patch!!!!!" width="800" height="524"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foozpjm9qveeg3fed6jbz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foozpjm9qveeg3fed6jbz.gif" alt="How cool" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>bloggolf</category>
      <category>todayilearned</category>
      <category>commandline</category>
    </item>
    <item>
      <title>Blog Golf: Smallest Post Wins</title>
      <dc:creator>Austin Vance</dc:creator>
      <pubDate>Fri, 15 Jan 2021 15:01:24 +0000</pubDate>
      <link>https://dev.to/focused_dot_io/blog-post-golf-smallest-post-wins-3f4b</link>
      <guid>https://dev.to/focused_dot_io/blog-post-golf-smallest-post-wins-3f4b</guid>
      <description>&lt;p&gt;At Focused Labs we have been really trying to emphasize blogging. So, our Editorial Board sat down and brainstormed. The goal of the Editorial Board was to make blogging fun, show that it doesn't take tons of time to write good posts, and get people to post! &lt;/p&gt;

&lt;p&gt;They came up with an awesome idea, like &lt;a href="https://codegolf.stackexchange.com/" rel="noopener noreferrer"&gt;code golf&lt;/a&gt;, why don't we try Blog Golf!&lt;/p&gt;

&lt;p&gt;Here are the rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write a blog post in 15 minutes or less&lt;/li&gt;
&lt;li&gt;Smallest blog post wins&lt;/li&gt;
&lt;li&gt;Code samples are judged on size of code not words&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The winner will win a &lt;em&gt;life changing prize&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;Watch out for a series of posts each in Blog Golf and try yourself by tagging &lt;code&gt;#bloggolf&lt;/code&gt; to guarantee some 🦄🦄🦄&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vlimnxtciq3w3ou59kz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vlimnxtciq3w3ou59kz.gif" alt="Golf" width="300" height="155"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>culture</category>
      <category>bloggolf</category>
      <category>contributorswanted</category>
      <category>series</category>
    </item>
  </channel>
</rss>
