<?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: Tilde A. Thurium</title>
    <description>The latest articles on DEV Community by Tilde A. Thurium (@annthurium).</description>
    <link>https://dev.to/annthurium</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F192035%2Fdc1598b5-946e-4e13-81ca-916ec4e3f918.png</url>
      <title>DEV Community: Tilde A. Thurium</title>
      <link>https://dev.to/annthurium</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/annthurium"/>
    <language>en</language>
    <item>
      <title>Harness engineering doesn't mean building your own harness</title>
      <dc:creator>Tilde A. Thurium</dc:creator>
      <pubDate>Tue, 15 Sep 2026 01:04:48 +0000</pubDate>
      <link>https://dev.to/annthurium/harness-engineering-doesnt-mean-building-your-own-harness-16pk</link>
      <guid>https://dev.to/annthurium/harness-engineering-doesnt-mean-building-your-own-harness-16pk</guid>
      <description>&lt;p&gt;&lt;strong&gt;It means giving the agent enough context to do the right thing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For this week's Agent Factory podcast, I interviewed &lt;a href="https://www.linkedin.com/in/ryanlopopolo/" rel="noopener noreferrer"&gt;Ryan Lopopolo&lt;/a&gt;, the engineer who coined the term "agentic harness." &lt;/p&gt;

&lt;h3&gt;
  
  
  What's in the video
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;How to Stop Writing Code Manually:&lt;/strong&gt; Ryan explains how to sculpt smaller feedback loops into long-horizon agentic workflows via harness engineering&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Harness, Deconstructed:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/billyjacobson/" rel="noopener noreferrer"&gt;Billy Jacobson&lt;/a&gt; delivers a code walkthrough of a simple harness, exploring linear and closed-loop styles. &lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The Three-Layer Stack:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/smithakolan/" rel="noopener noreferrer"&gt;Smitha Kolan&lt;/a&gt; breaks down how to use &lt;a href="https://blog.google/innovation-and-ai/models-and-research/gemini-models/3-8-flash-and-3-8-flash-cyber/" rel="noopener noreferrer"&gt;Gemini 3.8 Flash&lt;/a&gt;, &lt;a href="https://antigravity.google/docs/boost/" rel="noopener noreferrer"&gt;Antigravity Boost&lt;/a&gt;, and the &lt;a href="https://github.com/google/skills" rel="noopener noreferrer"&gt;Google Skills GitHub repo&lt;/a&gt; to configure memory, tools, and guardrails.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The thing that struck me was how many best practices for scaling software engineering teams also apply to coding agents.&lt;/p&gt;

&lt;p&gt;How much code are you writing manually these days? Let me know in the comments! &lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>automation</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>is Graph Engineering just reinventing systems architecture for the AI age?</title>
      <dc:creator>Tilde A. Thurium</dc:creator>
      <pubDate>Thu, 10 Sep 2026 19:23:39 +0000</pubDate>
      <link>https://dev.to/googleai/is-graph-engineering-just-reinventing-systems-architecture-for-the-ai-age-2427</link>
      <guid>https://dev.to/googleai/is-graph-engineering-just-reinventing-systems-architecture-for-the-ai-age-2427</guid>
      <description>&lt;p&gt;&lt;strong&gt;Bringing backend predictability to multi-agent systems&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I've been hearing the phrase "graph engineering" thrown around a lot lately. I originally assumed it had something to do with knowledge graphs, or maybe GraphRAG. Not quite!&lt;/p&gt;

&lt;p&gt;I sat down with &lt;a href="https://www.linkedin.com/in/anniewangtech/" rel="noopener noreferrer"&gt;Annie Wang&lt;/a&gt; and asked her to break it down from the ground up: what graph engineering is, and how it gives developers control over complex AI systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's in the video
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Demystifying the terms:&lt;/strong&gt; The actual differences between an agent harness, a loop, and a graph.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;A real-world example:&lt;/strong&gt; Walking through an automated pull request (PR) review pipeline using fan-out, join, and conditional router patterns.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;What it isn't:&lt;/strong&gt; Why graph engineering is fundamentally different from knowledge graphs and agent swarms.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Shared state:&lt;/strong&gt; How to orchestrate shared state and strict schemas using Google's Agent Development Kit (ADK).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The point that stuck with me is that graph engineering basically feels like modern backend architecture —like microservices— applied to agents. Instead of letting LLMs wander in a black-box loop and hoping for the best, you treat nodes with strict schemas and fan-out/join pipelines. It gives us back the predictability and debuggability that software engineers actually need.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;(You can also follow along with the hands-on &lt;a href="https://g.dev/ai/adk-2" rel="noopener noreferrer"&gt;ADK Codelab&lt;/a&gt;).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Did your mental model of "graph engineering" match up? Are you still running straight loops for your agents, or have you started breaking them into graph patterns? Let me know in the comments!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>graphengineering</category>
      <category>adk</category>
    </item>
    <item>
      <title>4 pitfalls of loop engineering (and how to fix them)</title>
      <dc:creator>Tilde A. Thurium</dc:creator>
      <pubDate>Wed, 09 Sep 2026 17:37:13 +0000</pubDate>
      <link>https://dev.to/googleai/4-pitfalls-of-loop-engineering-and-how-to-fix-them-1ji2</link>
      <guid>https://dev.to/googleai/4-pitfalls-of-loop-engineering-and-how-to-fix-them-1ji2</guid>
      <description>&lt;p&gt;Perhaps you've heard the term &lt;em&gt;Loop Engineering&lt;/em&gt;: instead of solving a problem by hand, you build a system, set a measurable goal, and let an agent keep iterating until it gets there.&lt;/p&gt;

&lt;p&gt;It sounds great until something goes wrong.&lt;/p&gt;

&lt;p&gt;So I sat down with &lt;a href="https://www.linkedin.com/in/anniewangtech/" rel="noopener noreferrer"&gt;Annie Wang&lt;/a&gt; to talk through the four most common ways Loop Engineering breaks down, and how to fix each one.&lt;/p&gt;

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


&lt;div class="crayons-card c-embed"&gt;

  

&lt;h2&gt;
  
  
  What's in the video
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What Loop Engineering actually means: building an agentic system that retries toward a definable goal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure #1 - runaway loops:&lt;/strong&gt; you need a hard stop rule because tokens cost real $$$. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure #2 - unverified autonomy:&lt;/strong&gt; why letting an agent grade its own work is like asking a kindergartner to grade its own homework, and why you want agent A checking agent B's work instead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure #3 - vague or uncheckable goals:&lt;/strong&gt; why "make this better" breaks an LLM, and how to write criteria that are actually non-negotiable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure #4- complexity overflow:&lt;/strong&gt; when a single loop chokes on a big task, and why that's the moment to move from Loop Engineering to Graph Engineering

&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;



&lt;p&gt;Have you hit any of these failure modes yourself? Tell me which one (or more) got you.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Building a viral Imax ticketing app that never crashes</title>
      <dc:creator>Tilde A. Thurium</dc:creator>
      <pubDate>Thu, 20 Aug 2026 21:29:37 +0000</pubDate>
      <link>https://dev.to/googleai/building-a-viral-imax-ticketing-app-that-never-crashes-4d58</link>
      <guid>https://dev.to/googleai/building-a-viral-imax-ticketing-app-that-never-crashes-4d58</guid>
      <description>&lt;p&gt;When 150,000 tickets went on sale for &lt;em&gt;The Odyssey&lt;/em&gt; in 70mm IMAX, they sold out almost instantly. But plans change, cancellations happen, and good seats randomly open up at odd hours.&lt;/p&gt;

&lt;p&gt;To solve this, Andrew Baker from Temporal built &lt;a href="https://imaxxing.io/" rel="noopener noreferrer"&gt;&lt;strong&gt;IMAXXING&lt;/strong&gt;&lt;/a&gt;: a service that monitors every 70mm IMAX showing across the US and alerts subscribers the moment great seats become available. What started as a fun weekend project quickly scaled, now over 9,000 users.&lt;/p&gt;

&lt;p&gt;I sat down with Andrew to break down the architecture: how durable execution keeps long-running workflows alive, how to debounce alerts so you don't spam users, and how serverless workers on Google Cloud Run handle sudden spikes in demand without overprovisioning.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/1LInqYY2m9Y"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;




&lt;h3&gt;
  
  
  What's in the video
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Durable execution 101:&lt;/strong&gt; How Temporal allows you to rewind history to the point of failure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Entity Workflow pattern:&lt;/strong&gt; Why there is one persistent workflow per user subscription and separate monitoring workflows per showing across the country.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signals &amp;amp; smart debouncing:&lt;/strong&gt; How showing workflows send signals to wake up subscription workflows, and how a 60-second in-workflow timer batches multiple theater alerts into a single digest—without consuming active CPU while sleeping.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serverless workers on &lt;a href="https://cloud.google.com/run" rel="noopener noreferrer"&gt;Cloud Run&lt;/a&gt;:&lt;/strong&gt; How running &lt;a href="https://temporal.io" rel="noopener noreferrer"&gt;Temporal&lt;/a&gt; workers as serverless containers lets compute autoscale directly with task queue depth rather than generic CPU metrics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI agents for ops:&lt;/strong&gt; How modern coding agents paired with Terraform and the &lt;code&gt;gcloud&lt;/code&gt; CLI accelerated the deployment and operational dashboard setup.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;The point that stuck with me is how durable execution fundamentally changes how you think about long-lived state and retries. Instead of building complex cron jobs, custom retry databases, and alert queues, the workflow state itself &lt;em&gt;is&lt;/em&gt; the queue and the timer.&lt;/p&gt;

&lt;p&gt;Have you experimented with entity workflows or running workflow workers on serverless infrastructure? How do you handle debouncing and noisy downstream APIs in your own apps?&lt;/p&gt;

</description>
      <category>googlecloud</category>
      <category>temporal</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Skills vs MCP: How AI tools have evolved</title>
      <dc:creator>Tilde A. Thurium</dc:creator>
      <pubDate>Thu, 30 Jul 2026 17:50:23 +0000</pubDate>
      <link>https://dev.to/googleai/skills-vs-mcp-how-ai-tools-have-evolved-3pmk</link>
      <guid>https://dev.to/googleai/skills-vs-mcp-how-ai-tools-have-evolved-3pmk</guid>
      <description>&lt;p&gt;Eighteen months ago, MCP was &lt;em&gt;the&lt;/em&gt; thing. Every demo and chatbot connector was running on MCP under the hood. When I started seeing everyone talk about &lt;em&gt;Skills&lt;/em&gt;, I got curious. How are MCP and Skills connected? Are they different lenses on the same problem?&lt;/p&gt;

&lt;p&gt;I brought these questions to &lt;a href="https://dev.to/greggyb"&gt;@greggyb&lt;/a&gt; again in our latest video.&lt;/p&gt;


&lt;div class="crayons-card c-embed"&gt;

  
&lt;h2&gt;
  
  
  What's in the video
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What MCP actually does: giving a model programmatic access to APIs and the file system through natural language&lt;/li&gt;
&lt;li&gt;Downsides of MCP: context bloat from loading hundreds of lines of tool definitions for tools you're not even using&lt;/li&gt;
&lt;li&gt;What a Skill actually is (spoiler: pretty much a markdown file containing a reusable prompt)&lt;/li&gt;
&lt;li&gt;Progressive disclosure: why an agent can "know about" 20 skills without loading the full instructions for all of them&lt;/li&gt;
&lt;li&gt;Why MCP and Skills aren't competitors, and how Skills can even teach your agent &lt;em&gt;how&lt;/em&gt; to use MCP tools

&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;



&lt;p&gt;My favorite line from this one: the hottest programming language right now is English. Skills let non-developers write, iterate on, and share instructions that make an entire team's agents faster, no code required.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/T6PQIPMlj3E"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Are you using Skills yet? Curious where everyone's at.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>mcp</category>
      <category>agentskills</category>
    </item>
    <item>
      <title>AI builder essentials: tokens, context windows and RAG 101</title>
      <dc:creator>Tilde A. Thurium</dc:creator>
      <pubDate>Fri, 24 Jul 2026 19:16:22 +0000</pubDate>
      <link>https://dev.to/googleai/ai-builder-essentials-tokens-context-windows-and-rag-101-12bn</link>
      <guid>https://dev.to/googleai/ai-builder-essentials-tokens-context-windows-and-rag-101-12bn</guid>
      <description>&lt;p&gt;Tokens are an important foundational concept that underlie how we interact with LLMs. &lt;a class="mentioned-user" href="https://dev.to/greggyb"&gt;@greggyb&lt;/a&gt; and myself recorded a quick video to demystify what a token actually is and unpack several other related concepts.&lt;/p&gt;


&lt;div class="crayons-card c-embed"&gt;

  
&lt;h2&gt;
  
  
  What's in the video
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What is a token? Tokens are the basic unit of input and output that LLMs operate on. &lt;/li&gt;
&lt;li&gt;What is retrieval-augmented generation (AKA RAG)? Providing a model with extra data that isn't already in the model's training dataset, and is relevant to a specific query.&lt;/li&gt;
&lt;li&gt;What are context windows? Context windows are how many tokens a model can use for input and output. Context window size varies from model to model, but it has increased a lot in recent years.&lt;/li&gt;
&lt;li&gt;What is tokenmaxxing? Does using more tokens really mean you're being more productive? (Spoiler alert: no.)

&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;



&lt;p&gt;To paint a bigger picture: more context you give a large language model, the better your responses are likely to be. The design patterns for providing that context have evolved at a rapid pace, and are likely to continue doing so.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/BnEhq2lPRz4"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;What AI terms should we demystify next? Let us know!&lt;/p&gt;

</description>
      <category>tokenomics</category>
      <category>rag</category>
      <category>tokenmaxxing</category>
      <category>ai</category>
    </item>
    <item>
      <title>What is an "agentic harness," actually?</title>
      <dc:creator>Tilde A. Thurium</dc:creator>
      <pubDate>Thu, 16 Jul 2026 21:19:20 +0000</pubDate>
      <link>https://dev.to/googleai/what-is-an-agentic-harness-actually-4oie</link>
      <guid>https://dev.to/googleai/what-is-an-agentic-harness-actually-4oie</guid>
      <description>&lt;p&gt;I've been hearing the word "harness" thrown around a lot lately. I assumed it just meant &lt;em&gt;"the IDE"&lt;/em&gt; or &lt;em&gt;"whatever app is running your agent."&lt;/em&gt; Turns out, it goes a little deeper than that.&lt;/p&gt;

&lt;p&gt;So I sat down with &lt;a class="mentioned-user" href="https://dev.to/greggyb"&gt;@greggyb&lt;/a&gt; and asked him to explain it from the ground up: what an LLM actually does, what turns it into an agent, and where the harness fits into all of it.&lt;/p&gt;


&lt;div class="crayons-card c-embed"&gt;

  
&lt;h2&gt;
  
  
  What's in the video
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Simon Willison's definition of an agent: an LLM with tools, running in a loop to accomplish a goal&lt;/li&gt;
&lt;li&gt;What "tools" really means under the hood, and how function calling lets an LLM pull in context it wasn't trained on&lt;/li&gt;
&lt;li&gt;The loop part: how an agent programmatically checks its own output to decide if it's actually done&lt;/li&gt;
&lt;li&gt;Why the harness is everything &lt;em&gt;after&lt;/em&gt; the LLM, and why that's a different thing than the interface&lt;/li&gt;
&lt;li&gt;Why you can swap interfaces without touching the underlying harness, and why some agents don't need a UI at all

&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;



&lt;p&gt;The point that stuck with me is the harness isn't the app you're looking at. It's the invisible plumbing deciding whether the agent keeps going or calls it done.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/W9BX0jyzd2k"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Did your mental model of "harness" match up? Or were you picturing something else too?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>discuss</category>
      <category>llm</category>
    </item>
    <item>
      <title>I built a native Android app in an afternoon, and I've never written a line of Kotlin</title>
      <dc:creator>Tilde A. Thurium</dc:creator>
      <pubDate>Wed, 01 Jul 2026 18:32:34 +0000</pubDate>
      <link>https://dev.to/googleai/i-built-a-native-android-app-in-an-afternoon-and-ive-never-written-a-line-of-kotlin-20d6</link>
      <guid>https://dev.to/googleai/i-built-a-native-android-app-in-an-afternoon-and-ive-never-written-a-line-of-kotlin-20d6</guid>
      <description>&lt;p&gt;I’ve always thought building a mobile app required climbing a massive learning curve just to get a basic environment set up. To test that theory, I tried building my very first Android app using &lt;a href="https://aistudio.google.com" rel="noopener noreferrer"&gt;Google AI Studio&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Five minutes later, I had a working prototype.&lt;/p&gt;

&lt;p&gt;The coolest part about this isn't just the speed: it’s that &lt;em&gt;anyone&lt;/em&gt; can do this. The traditional barriers to building software are disappearing, making it incredibly easy to just start creating.&lt;/p&gt;

&lt;p&gt;I recorded the whole 5-minute process here if you want to see what it looks like in practice:&lt;/p&gt;


&lt;div class="crayons-card c-embed"&gt;

  
&lt;h2&gt;
  
  
  What's in the video
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Prompting AI Studio to build a native Android app from scratch&lt;/li&gt;
&lt;li&gt;Progressive Webapp (PWA) vs Android Native App in 2026: feature comparison&lt;/li&gt;
&lt;li&gt;Sideloading the app onto an Android device via USB-C cable. No Play Store required&lt;/li&gt;
&lt;li&gt;What happens when the AI gets something wrong? Fixing bugs in a vibe coded app

&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;



&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/Vl9HYB9tmdw"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>ai</category>
      <category>android</category>
      <category>vibecoding</category>
      <category>mobile</category>
    </item>
    <item>
      <title>3 takeaways from the IO '26 developer keynote</title>
      <dc:creator>Tilde A. Thurium</dc:creator>
      <pubDate>Tue, 19 May 2026 21:53:04 +0000</pubDate>
      <link>https://dev.to/googleai/3-takeaways-from-the-io-26-developer-keynote-11b2</link>
      <guid>https://dev.to/googleai/3-takeaways-from-the-io-26-developer-keynote-11b2</guid>
      <description>&lt;p&gt;My top 3 takeaways from the IO 26’ developer keynote, in no particular order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://goo.gle/gemini-3-5" rel="noopener noreferrer"&gt;&lt;strong&gt;Gemini 3.5&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;Flash GA is a breakthrough for developers worldwide.&lt;/strong&gt; In plain English, this model offers flagship-tier coding brains that run at lightning speeds for a fraction of the cost of frontier models. This is a massive win for international devs, as the ultra-low cost levels the playing field against steep USD exchange rates, and the raw speed easily offsets regional network latency. I tested it out myself by vibe coding a vaporwave-themed pet store app with Antigravity. I was impressed with how snappy the code generation felt.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0lpwztv62ir072i66wp7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0lpwztv62ir072i66wp7.png" alt="Screenshot of Vaporpaws web app, dark themed with a ridiculous neon cat food generated image and too many bells and whistles to describe in words." width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Google AI Studio &lt;a href="https://goo.gle/ai-studio-IO-2026" rel="noopener noreferrer"&gt;new features&lt;/a&gt; can make anyone a builder.&lt;/strong&gt; One-click deployment to Firebase and Cloud Run makes it easier than ever to go from demo to serving real users. Custom asset generation with Nano Banana makes your vibe coded apps look sharp. And when you’re ready to add next-level complexity, now it’s easier than ever to export your project to Antigravity.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://goo.gle/agy-io-2026" rel="noopener noreferrer"&gt;&lt;strong&gt;Antigravity 2.0&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;unlocks full-stack agentic development&lt;/strong&gt;.  You can now use always-on agents that build for you while you sleep, while the new Antigravity SDK lets you richly customize your agents and deploy them to private infrastructure.  &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For more details, check out the official &lt;a href="https://goo.gle/io2026-keynote" rel="noopener noreferrer"&gt;announcement blog&lt;/a&gt; and stay tuned for upcoming &lt;a href="https://bit.ly/3Pvpyrt" rel="noopener noreferrer"&gt;Dev.To challenges&lt;/a&gt; where you can build with our new models.  What are you most excited about? &lt;/p&gt;

</description>
      <category>antigravity</category>
      <category>gemini</category>
      <category>googleio</category>
      <category>nanobanana</category>
    </item>
    <item>
      <title>How to use your network to get a job</title>
      <dc:creator>Tilde A. Thurium</dc:creator>
      <pubDate>Mon, 13 Oct 2025 17:26:06 +0000</pubDate>
      <link>https://dev.to/launchdarkly/how-to-use-your-network-to-get-a-job-4j08</link>
      <guid>https://dev.to/launchdarkly/how-to-use-your-network-to-get-a-job-4j08</guid>
      <description>&lt;p&gt;Common advice for job seekers is to &lt;em&gt;"use your network"&lt;/em&gt; but what does that mean, exactly? Let's break it down.&lt;/p&gt;

&lt;h2&gt;
  
  
  In 2025, job referrals are &lt;em&gt;everything&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;As a hiring manager, I got hundreds of applicants within 24 hours of posting a new job. Most of the applications were AI slop. Recruiters and managers are overwhelmed. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A personalized referral from someone you know will get you further with a hiring manager than the perfect resume.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Use your network to get referrals. By &lt;em&gt;network&lt;/em&gt;, I mean people you already have a relationship with, who can vouch for your awesome work.&lt;/p&gt;

&lt;p&gt;Going to meetups to chat up strangers who are probably also job seeking is likely not the highest-value use of your time.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to ask for referrals
&lt;/h2&gt;

&lt;p&gt;People want to help you. But first, you need to ask. To the LinkedIn mobile, Batman. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Identify jobs that are a good fit with what you're seeking.&lt;/li&gt;
&lt;li&gt;Do some LinkedIn snooping to see if anyone you know works at those companies. &lt;/li&gt;
&lt;li&gt;Write those people a quick message asking if they'd be comfortable referring you. &lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Isn't it weird to ask for a favor when you haven't seen someone in years?
&lt;/h3&gt;

&lt;p&gt;In a &lt;em&gt;professional context&lt;/em&gt;, it's totally okay to message an old co-worker you haven't talked to since 2019 and ask for a referral.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example note to ask for a referral
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Hi Tilde!&lt;/p&gt;

&lt;p&gt;Long time no see, hope you're doing well.&lt;/p&gt;

&lt;p&gt;I saw that LaunchDarkly is hiring for a Backend Engineer, Metrics. [INSERT LINK TO SPECIFIC POSTING]. I have 2 years of experience working on Go backends in my previous company. Would you mind referring me for this role? My resume is attached. Thanks so much!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What works about this letter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's brief&lt;/li&gt;
&lt;li&gt;It specifies why you're a good fit for the role&lt;/li&gt;
&lt;li&gt;It includes all the information needed to make the referral. Nobody has time for a scavenger hunt.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;p&gt;Using your network is much harder for early-career engineers. There are many less junior positions available than there used to be. Also, juniors generally have smaller networks to begin with.&lt;/p&gt;

&lt;p&gt;Norms vary geographically. I've seen this approach work in North America and European companies. Outside those zones, your mileage may vary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parting thoughts
&lt;/h2&gt;

&lt;p&gt;The best time to grow your network is before you need it. If you are currently employed, take the time to celebrate your teammates' wins. Ask people how they are doing. Talk about the excellent work you are doing. Smash that connect button on LinkedIn. Small actions pay off over time in the long run.&lt;/p&gt;

&lt;p&gt;The job market is incredibly tough, but people want to help. Go forth and ask.&lt;/p&gt;

&lt;p&gt;P.S. &lt;a href="https://launchdarkly.com/careers/" rel="noopener noreferrer"&gt;LaunchDarkly is hiring.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>hiring</category>
      <category>career</category>
      <category>networking</category>
    </item>
    <item>
      <title>Snowflake Cortex Completion API + LaunchDarkly SDK Integration</title>
      <dc:creator>Tilde A. Thurium</dc:creator>
      <pubDate>Mon, 25 Aug 2025 18:18:23 +0000</pubDate>
      <link>https://dev.to/launchdarkly/snowflake-cortex-completion-api-launchdarkly-sdk-integration-5021</link>
      <guid>https://dev.to/launchdarkly/snowflake-cortex-completion-api-launchdarkly-sdk-integration-5021</guid>
      <description>&lt;p&gt;This tutorial walks through an integration between the Snowflake Cortex Completion API and &lt;a href="https://launchdarkly.com/docs/sdk/ai#ai-sdks" rel="noopener noreferrer"&gt;LaunchDarkly's AI SDKs&lt;/a&gt;. We'll be using a Snowflake Personal Access Token to query the Cortex API and receive completion responses. &lt;/p&gt;

&lt;p&gt;Leveraging Snowflake's gateway approach to completions alongside LaunchDarkly's ability to make runtime changes to AI Configs, you can update the models, prompts, and parameters that you're using in the Snowflake endpoints in real-time. &lt;/p&gt;

&lt;p&gt;This tutorial is presented in TypeScript, but since we're using Snowflake's REST API it's universal to any language in which you can access Snowflake. Snowflake additionally has a Python package that can be used to access their AI and ML functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authenticate with Snowflake
&lt;/h2&gt;

&lt;p&gt;If you are new to Snowflake, there is some setup you'll need to do to get an application running, like setting up a user that is able to access the API.&lt;/p&gt;

&lt;p&gt;Head into your Snowflake instance and follow the guide provided by Snowflake for &lt;a href="https://docs.snowflake.com/en/developer-guide/snowflake-rest-api/authentication" rel="noopener noreferrer"&gt;authenticating against the REST API&lt;/a&gt;, and the guide for &lt;a href="https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-rest-api#setting-up-authentication" rel="noopener noreferrer"&gt;authenticating against the Cortex REST API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pay attention to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's recommended to create a new user to access the API so that its permissions, privileges and access can be limited to the necessary scope&lt;/li&gt;
&lt;li&gt;Make sure to grant the role of the user you're authenticating as a &lt;code&gt;SNOWFLAKE.CORTEX_USER&lt;/code&gt; database role if it's not already present&lt;/li&gt;
&lt;li&gt;If you are using a &lt;a href="https://docs.snowflake.com/en/developer-guide/snowflake-rest-api/authentication#label-sfrest-authenticating-pat" rel="noopener noreferrer"&gt;Personal Access Token&lt;/a&gt;, make sure to apply a &lt;a href="https://docs.snowflake.com/en/user-guide/network-policies" rel="noopener noreferrer"&gt;Network Policy&lt;/a&gt; that allow-lists the IP you'll be accessing from&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Admin Privileges Required for Network Policies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You need admin privileges to create Network Policies for Personal Access Token authentication. If you don't have admin access:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a fresh Snowflake trial account (where you'll have admin access)&lt;/li&gt;
&lt;li&gt;Or contact your Snowflake administrator for help with authentication setup&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enterprise/work accounts typically don't grant these privileges to regular users&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Capture your account identifier, which can be found by accessing the lower-left button on the UI that contains your name and account role. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click your name&lt;/li&gt;
&lt;li&gt;Hover over your active account&lt;/li&gt;
&lt;li&gt;In the popover menu, select "View account details"&lt;/li&gt;
&lt;li&gt;Copy the field labeled "Account/Server URL"&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Set up an AI Config
&lt;/h2&gt;

&lt;p&gt;Before we write any code, we'll go into LaunchDarkly and create an AI Config to be used in the integration.&lt;/p&gt;

&lt;p&gt;Navigate to your LaunchDarkly instance and follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to "AI Configs" and click "Create AI Config" on the top-right side&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;Give your Config a name, and then select "Cortex" from the provider dropdown.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;Now that Cortex is selected, the model dropdown will be filtered to the available models. For this first variation, we'll select &lt;code&gt;claude-3-5-sonnet&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Make sure that the region you're accessing from has support for the model you select. You can view model availability on &lt;a href="https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-rest-api#model-availability" rel="noopener noreferrer"&gt;this page&lt;/a&gt;.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;Add your messages for the completion. We'll add a single system message, as well as a template for where the user message will go:&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;&lt;code&gt;{{variables}}&lt;/code&gt; signify a variable that will be replaced at the time you retrieve your config. This is how you provide dynamic content to your Configs such as contextual user information&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click "Review and save". You'll be given a chance to review your changes before committing them.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;Your AI Config is now saved so it's time to serve our new variation to our users. Click the "Targeting" tab on the top of the AI Config:&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;By default your Config will be serving the &lt;code&gt;disabled&lt;/code&gt; variation which is used to signal that a Config is turned off. We'll revisit this aspect later in the code.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;Click "Edit" on the default rule and select your variation from the dropdown, click "Review and save", and then confirm the changes:&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;You've now targeted a variation which can be served in the SDK. We'll come back to this later once we've got some code set up. For now, just copy the key in your sidebar for later:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Set up the server
&lt;/h2&gt;

&lt;p&gt;Next, we'll set up our sample application so that we can see LaunchDarkly AI Configs and the Snowflake REST API interacting in real-time. This section assumes some knowledge with TypeScript and the NodeJS ecosystem, but can be accomplished in any language with &lt;a href="https://launchdarkly.com/docs/sdk/ai#ai-sdks" rel="noopener noreferrer"&gt;AI SDK support&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For the following sections, these are instructions to set it up as a new application. If you're not concerned about which piece does what or having a clean slate, you can also just clone this repository and run &lt;code&gt;npm install&lt;/code&gt; and then &lt;code&gt;npm run start&lt;/code&gt; after filling out the &lt;code&gt;.env&lt;/code&gt; section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set up an ExpressJS application
&lt;/h3&gt;

&lt;p&gt;Follow the &lt;a href="https://expressjs.com/en/starter/installing.html" rel="noopener noreferrer"&gt;ExpressJS installation guide&lt;/a&gt; to set up a new project leveraging Express.&lt;/p&gt;

&lt;h4&gt;
  
  
  Basic setup
&lt;/h4&gt;

&lt;p&gt;Let's create some of the structure we'll need for the app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;app views
&lt;span class="nb"&gt;touch &lt;/span&gt;index.ts package.json views/index.html app/launchdarklyClient.ts app/completions.ts
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;code&gt;.env&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;The last command created a &lt;code&gt;.env&lt;/code&gt; file that we'll use to register our secrets so they can be securely loaded by the application.&lt;/p&gt;

&lt;p&gt;Within this file, fill out the following values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SNOWFLAKE_ACCOUNT_IDENTIFIER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;Snowflake account identifier&amp;gt;.snowflakecomputing.com
&lt;span class="nv"&gt;SNOWFLAKE_PAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;Snowflake Personal Access Token&amp;gt;

&lt;span class="nv"&gt;LAUNCHDARKLY_SDK_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;LaunchDarkly SDK key&amp;gt;
&lt;span class="nv"&gt;LAUNCHDARKLY_AI_CONFIG_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;LaunchDarkly AI Config key&amp;gt;
&lt;span class="nv"&gt;PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Snowflake account identifier and Personal Access Token should be available from following the authentication instructions for Snowflake.&lt;/p&gt;

&lt;p&gt;If you do not know how to get your LaunchDarkly SDK key, you can follow &lt;a href="https://launchdarkly.com/docs/sdk/concepts/getting-started" rel="noopener noreferrer"&gt;this guide&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;package.json&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Grab the contents of the &lt;code&gt;package.json&lt;/code&gt; file from the repository and replace your local &lt;code&gt;package.json&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Now run &lt;code&gt;npm install&lt;/code&gt; to install our dependencies. Once that finishes, run &lt;code&gt;typescript --init&lt;/code&gt; from the project folder to create a &lt;code&gt;tsconfig.json&lt;/code&gt; file. You'll need the dependencies in here to process TypeScript files and run your local application.&lt;/p&gt;

&lt;p&gt;The dependencies in this file do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add TypeScript support to ExpressJS (&lt;code&gt;@types/express&lt;/code&gt;, &lt;code&gt;typescript&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Add utilities to run the application (&lt;code&gt;nodemon&lt;/code&gt;, &lt;code&gt;ts-node&lt;/code&gt;, &lt;code&gt;dotenv&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Initialize the LaunchDarkly SDKs (&lt;code&gt;@launchdarkly/node-server-sdk&lt;/code&gt;, &lt;code&gt;@launchdarkly/server-sdk-ai&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We are using default TypeScript settings. Feel free to edit these to match your project's needs.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;index.ts&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;index.ts&lt;/code&gt; file is responsible for initializing the application. We'll be including two routes; one to render an HTML page and one to respond to the completion.&lt;/p&gt;

&lt;p&gt;Grab the &lt;code&gt;index.ts&lt;/code&gt; file from the repo and replace this content. The file has comments explaining the functionality.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;index.html&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Replace the &lt;code&gt;index.html&lt;/code&gt; file in &lt;code&gt;views/index.html&lt;/code&gt; with the same content from the repository, or use it as a guideline to build your own interface for the chat. This file is also commented, but outside of the HTML structure, you'll want to pay attention to the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; at the bottom of the page which handles making the API call.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make our completion calls
&lt;/h3&gt;

&lt;p&gt;Now that we have an application, we can start wiring up LaunchDarkly to the Snowflake API.&lt;/p&gt;

&lt;p&gt;We'll set up the LaunchDarkly clients in &lt;code&gt;app/launchdarklyClient.ts&lt;/code&gt;:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LDClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@launchdarkly/node-server-sdk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LDAIClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initAi&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@launchdarkly/server-sdk-ai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;ldClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LDClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getSDKClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ldClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;ldClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LAUNCHDARKLY_SDK_KEY&lt;/span&gt;&lt;span class="o"&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;return&lt;/span&gt; &lt;span class="nx"&gt;ldClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;aiClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LDAIClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAIClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;aiClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;aiClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;initAi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getSDKClient&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;aiClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Initialize and return the LaunchDarkly client&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getLaunchDarklyClients&lt;/span&gt;&lt;span class="p"&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;ldClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getSDKClient&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;aiClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getAIClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;try&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;ldClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForInitialization&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// log your error.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ldClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;aiClient&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;closeLaunchDarklyClients&lt;/span&gt;&lt;span class="p"&gt;()&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;ldClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;ldClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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;The LaunchDarkly AI SDK allows you to use the features of AI Configs within the LaunchDarkly SDK.&lt;/p&gt;

&lt;p&gt;Within &lt;code&gt;app/completions.ts&lt;/code&gt; let's go ahead and set up the Snowflake call:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getLaunchDarklyClients&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./launchdarklyClient&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Base URL&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SNOWFLAKE_BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SNOWFLAKE_ACCOUNT_IDENTIFIER&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="c1"&gt;// Completion endpoint&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SNOWFLAKE_COMPLETE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;SNOWFLAKE_BASE_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/v2/cortex/inference:complete`&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;snowflakeCompletionClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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;any&lt;/span&gt;&lt;span class="o"&gt;&amp;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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// Includes the authorization token on the request&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SNOWFLAKE_PAT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Accept&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Run a fetch on the Snowflake completion URL&lt;/span&gt;
    &lt;span class="k"&gt;return&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;SNOWFLAKE_COMPLETE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// We are not going to stream our responses, so pass `stream:false` to all instances of this invocation&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({...&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;stream&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="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runSnowflakeCompletion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userInput&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;span class="c1"&gt;// Retrieve the AI Client from LaunchDarkly&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;aiClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getLaunchDarklyClients&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// Set up the user's context; this can be used to control which variations the users receive. You can target against any attribute passed in this context.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John Doe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`user-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Retrieve the AI Config from the LD SDK&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&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;aiClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="c1"&gt;// This is the key of our config&lt;/span&gt;
        &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LAUNCHDARKLY_AI_CONFIG_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="c1"&gt;// Context&lt;/span&gt;
        &lt;span class="nx"&gt;userContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="c1"&gt;// Defaults - can be left empty unless you want to provide&lt;/span&gt;
        &lt;span class="c1"&gt;// default values if the AI Config is not found.&lt;/span&gt;
        &lt;span class="p"&gt;{},&lt;/span&gt; 
        &lt;span class="c1"&gt;// These variables are automatically interpolated into the &lt;/span&gt;
        &lt;span class="c1"&gt;// messages returned from the SDK. Here, we're providing the&lt;/span&gt;
        &lt;span class="c1"&gt;// user's actual query, but this can be used for other runtime data augmentation purposes&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// Check that the config is enabled and conforms to the shape we would expect.&lt;/span&gt;
    &lt;span class="c1"&gt;// Some calls may omit things like the `messages` array when just changing models.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enabled&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Malformed config&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="c1"&gt;// Make the call to the Snowflake API&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;snowflakeCompletionClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="c1"&gt;// The model name is provided dynamically, which means we&lt;/span&gt;
        &lt;span class="c1"&gt;// can change this at runtime with AI Configs!&lt;/span&gt;
        &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&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;result&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;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// Extract the top choice of message and return it to the client&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No response from Snowflake&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;h3&gt;
  
  
  Run the app
&lt;/h3&gt;

&lt;p&gt;Navigate to your root directory and run &lt;code&gt;npm run start&lt;/code&gt; to start the application.&lt;/p&gt;

&lt;p&gt;When you navigate to &lt;code&gt;localhost:3000&lt;/code&gt; (or whichever port you changed it to) you should see a simple screen that looks like this:&lt;/p&gt;

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

&lt;p&gt;Enter a query into the textarea, such as "How do I create a user in Snowflake?" and after a few moments a response will be generated:&lt;/p&gt;

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

&lt;p&gt;We can see in the response that it lists the model we're using to generate the completion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make runtime changes
&lt;/h2&gt;

&lt;p&gt;Now that we have a completion endpoint set up, let's create a new variation and change the model at runtime. You can minimize your code editor for now; we'll only need to make changes in the LaunchDarkly UI!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate back to your AI Config in the LaunchDarkly UI&lt;/li&gt;
&lt;li&gt;Click "Add another variation" and then repeat the steps from earlier, but this time select a different model. We'll use &lt;code&gt;llama3.1-8b&lt;/code&gt; and edit the system message slightly for tone:&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;Click save and head back over to targeting&lt;/li&gt;
&lt;li&gt;On the targeting page, edit the default rule and select the new variation you created:&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;Click save and confirm the changes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, let's head back over to our app on the &lt;code&gt;localhost&lt;/code&gt; URL. Without refreshing the page or restarting the server, go ahead and re-submit the request.&lt;/p&gt;

&lt;p&gt;Our output was now generated by our &lt;code&gt;llama3.1-8b&lt;/code&gt; model rather than the Sonnet model we were leveraging earlier:&lt;/p&gt;

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

&lt;p&gt;And that's it! We've now made a runtime model change on an active AI Config. &lt;/p&gt;

&lt;p&gt;We didn't have to change any code to change the model, and there's more we can tweak such as the model's parameters and messages. We can add messages and have them automatically inserted at runtime, test different models on different customers, and tweak parameters in a live system to see how the models respond in real-world conditions. &lt;/p&gt;

&lt;p&gt;The last point we'll touch on is how we can optionally capture data about our AI Config invocations and send those over to LaunchDarkly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up monitoring
&lt;/h3&gt;

&lt;p&gt;To set up monitoring, we need to extract the &lt;code&gt;tracker&lt;/code&gt; object from our AI Config and call some &lt;code&gt;track&lt;/code&gt; methods that will communicate the metrics to LaunchDarkly.&lt;/p&gt;

&lt;p&gt;Let's update the &lt;code&gt;app/completions.ts&lt;/code&gt; file to include tracking:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LDAIConfigTracker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@launchdarkly/server-sdk-ai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getLaunchDarklyClients&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./launchdarklyClient&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Base URL&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SNOWFLAKE_BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SNOWFLAKE_ACCOUNT_IDENTIFIER&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="c1"&gt;// Completion endpoint&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SNOWFLAKE_COMPLETE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;SNOWFLAKE_BASE_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/v2/cortex/inference:complete`&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;snowflakeCompletionClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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;any&lt;/span&gt;&lt;span class="o"&gt;&amp;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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// Includes the authorization token on the request&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SNOWFLAKE_PAT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Accept&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Run a fetch on the Snowflake completion URL&lt;/span&gt;
    &lt;span class="k"&gt;return&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;SNOWFLAKE_COMPLETE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// We are not going to stream our responses, so pass `stream:false` to all instances of this invocation&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({...&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;stream&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="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runSnowflakeCompletion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userInput&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;span class="c1"&gt;// Retrieve the AI Client from LaunchDarkly&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;aiClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getLaunchDarklyClients&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// Set up the user's context; this can be used to control which variations the users receive. You can target against any attribute passed in this context.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John Doe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`user-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Retrieve the AI Config from the LD SDK&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&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;aiClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="c1"&gt;// This is the key of our config&lt;/span&gt;
        &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LAUNCHDARKLY_AI_CONFIG_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="c1"&gt;// Context&lt;/span&gt;
        &lt;span class="nx"&gt;userContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="c1"&gt;// Defaults - can be left empty unless you want to provide&lt;/span&gt;
        &lt;span class="c1"&gt;// default values if the AI Config is not found.&lt;/span&gt;
        &lt;span class="p"&gt;{},&lt;/span&gt; 
        &lt;span class="c1"&gt;// These variables are automatically interpolated into the &lt;/span&gt;
        &lt;span class="c1"&gt;// messages returned from the SDK. Here, we're providing the&lt;/span&gt;
        &lt;span class="c1"&gt;// user's actual query, but this can be used for other runtime data augmentation purposes&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// Extract the tracker from the AI Config&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tracker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// Check that the config is enabled and conforms to the shape we would expect.&lt;/span&gt;
    &lt;span class="c1"&gt;// Some calls may omit things like the `messages` array when just changing models.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enabled&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Track an error if the config is not enabled or does not conform to the shape we would expect.&lt;/span&gt;
        &lt;span class="nx"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trackError&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Malformed config&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="k"&gt;try&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;durationStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Make the call to the Snowflake API&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;snowflakeCompletionClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="c1"&gt;// The model name is provided dynamically, which means we&lt;/span&gt;
            &lt;span class="c1"&gt;/// can change this at runtime with AI Configs!&lt;/span&gt;
            &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&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;durationEnd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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;result&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;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// Track a successful completion&lt;/span&gt;
        &lt;span class="nx"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trackSuccess&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// Track the duration of the completion&lt;/span&gt;
        &lt;span class="nx"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trackDuration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;durationEnd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;durationStart&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// Track the tokens used in the completion&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trackTokens&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prompt_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completion_tokens&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;span class="c1"&gt;// Extract the top choice of message and return it to the client&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No response from Snowflake&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// An error occurred while making the completion call&lt;/span&gt;
        &lt;span class="nx"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trackError&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&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;We now capture a duration by setting a timer before and after the call, and use the &lt;code&gt;usage&lt;/code&gt; parameter returned from Snowflake to capture the token usage. These metrics will appear in your dashboard for your AI Config under the "Monitoring" tab:&lt;/p&gt;

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

&lt;p&gt;You'll receive additional information on this page about when different variations released and when changes were made to the configs so that you can keep track of what changes have what impact on your completions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;This is a simple example but demonstrates how you can use the power of Snowflake's Cortex completion gateway in conjunction with AI Configs. Together, they allow you to tweak models in real-time by selecting any models available in your region and having them update seamlessly without requiring any code changes. &lt;/p&gt;

&lt;p&gt;Additionally, with the power of &lt;a href="https://launchdarkly.com/blog/beginners-guide-to-targeting-with-feature-flags/" rel="noopener noreferrer"&gt;LaunchDarkly's targeting&lt;/a&gt; you can serve different models and prompts to different users, and even &lt;a href="https://launchdarkly.com/blog/introducing-ai-experiments-and-ai-versioning/" rel="noopener noreferrer"&gt;run experiments&lt;/a&gt; against your AI Configs to see which model and prompts best fit your features.&lt;/p&gt;

&lt;p&gt;To get started with AI Configs, &lt;a href="https://app.launchdarkly.com/signup" rel="noopener noreferrer"&gt;sign up for a free trial&lt;/a&gt;. You can also contact us at &lt;code&gt;aiproduct@launchdarkly.com&lt;/code&gt; with any questions.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published by Andrew Klatzke on docs.launchdarkly.com.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>snowflake</category>
      <category>launchdarkly</category>
    </item>
    <item>
      <title>Using LaunchDarkly's AI Configs to review database changes</title>
      <dc:creator>Tilde A. Thurium</dc:creator>
      <pubDate>Thu, 21 Aug 2025 19:43:01 +0000</pubDate>
      <link>https://dev.to/launchdarkly/using-launchdarklys-ai-configs-to-review-database-changes-1cfj</link>
      <guid>https://dev.to/launchdarkly/using-launchdarklys-ai-configs-to-review-database-changes-1cfj</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;At LaunchDarkly, we’re constantly pushing the boundaries of what it means to move fast without breaking things. We ship frequently, serve &lt;strong&gt;quadrillions of events per day&lt;/strong&gt;, and operate with &lt;strong&gt;zero tolerance for downtime&lt;/strong&gt;. To keep pace, we need systems that help us ship confidently—even when change is happening at a rapid, “vibe coding” pace.&lt;/p&gt;

&lt;p&gt;But what happens when that rapid change reaches your database?&lt;/p&gt;

&lt;p&gt;From an SRE’s perspective, the database is sacred. It’s the source of truth—and one of the riskiest areas to touch without deep context. Even if your code is reviewed in a pull request:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does the reviewer understand the query access patterns?&lt;/li&gt;
&lt;li&gt;Could this schema change hurt index performance?&lt;/li&gt;
&lt;li&gt;Is the change touching critical production tables?&lt;/li&gt;
&lt;li&gt;Will the new model scale with usage?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With LaunchDarkly AI Configs, we finally have a way to automate this kind of insight — &lt;em&gt;reviewing database changes before they become production issues&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To follow along, you'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A LaunchDarkly account with &lt;a href="https://dev.to/home/ai-configs/quickstart"&gt;AI Configs&lt;/a&gt; enabled. &lt;a href="https://app.launchdarkly.com/signup" rel="noopener noreferrer"&gt;Sign up for free here.&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Access to a database system. These  code samples are written to be compatible with a database using the PostgreSQL wire protocol. However, you could adapt them to suit other flavors of databases.&lt;/li&gt;
&lt;li&gt;Basic familiarity with SQL and database schema management&lt;/li&gt;
&lt;li&gt;A development environment where you can test database changes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What are AI Configs?
&lt;/h2&gt;

&lt;p&gt;LaunchDarkly &lt;a href="https://dev.to/home/ai-configs"&gt;AI Configs&lt;/a&gt; allow you to customize, test, and roll out new large language models (LLMs) within your generative AI applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 What the AI Reviewer Checks
&lt;/h2&gt;

&lt;p&gt;Our system uses LaunchDarkly’s internal AI Configs to analyze your schema and query changes directly from a CI build. It checks for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are the &lt;strong&gt;queries&lt;/strong&gt; optimized for access patterns?&lt;/li&gt;
&lt;li&gt;Are the right indexes in place?&lt;/li&gt;
&lt;li&gt;Are we modifying high-risk tables?&lt;/li&gt;
&lt;li&gt;Will the schema scale and evolve over time?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isn’t just a linter. It’s an AI-powered reviewer trained on your environment.&lt;/p&gt;

&lt;p&gt;If you want to skip right to reading the code, &lt;a href="https://github.com/launchdarkly-labs/lddbai/blob/main/.github/workflows/test.yml" rel="noopener noreferrer"&gt;a complete example can be found here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;## 🛠 Step 1: Collect the Right Data&lt;/p&gt;

&lt;p&gt;The AI needs a complete snapshot of your system to make a meaningful review:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full schema (full-schema.json)&lt;/li&gt;
&lt;li&gt;Schema diff (schema-diff.json)&lt;/li&gt;
&lt;li&gt;Full set of SQL queries (sql-queries.json)&lt;/li&gt;
&lt;li&gt;Query diff (queries-diff.json)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  📦 sql-proxy container
&lt;/h3&gt;

&lt;p&gt;To evaluate database changes, we need to observe real SQL queries your application runs during CI.&lt;/p&gt;

&lt;p&gt;We do this by inserting a lightweight PostgreSQL proxy between your app and the database. It logs and deduplicates queries, then exposes them via an API for analysis.&lt;/p&gt;

&lt;p&gt;Here’s the setup in Docker Compose / GitHub Action Service Container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5432:5432&lt;/span&gt;

  &lt;span class="na"&gt;proxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/${{ github.repository_owner }}/sql-proxy:latest&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;LISTEN_PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5433&lt;/span&gt;
      &lt;span class="na"&gt;BACKEND_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
      &lt;span class="na"&gt;BACKEND_PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5432&lt;/span&gt;
      &lt;span class="na"&gt;API_PORT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
      &lt;span class="na"&gt;DB_CONNECTION_STRING&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;host=postgres port=5432 user=postgres password=postgres sslmode=disable&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5433:5433&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8080:8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;​&lt;br&gt;
Every query is deduplicated and exposed via:&lt;br&gt;
&lt;code&gt;GET http://localhost:8080/queries&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Connecting to the database via 5433 will now pass the queries through the proxy.&lt;/p&gt;
&lt;h3&gt;
  
  
  🏗 Dumping the Schema
&lt;/h3&gt;

&lt;p&gt;To give the AI full context, we also need a snapshot of the database schema—including table definitions, columns, indexes, and relationships.&lt;/p&gt;

&lt;p&gt;This can be triggered early in the CI pipeline to run in parallel with your other steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Migrate Database&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;cd schema_test&lt;/span&gt;
    &lt;span class="s"&gt;go run .&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Start Schema Dump&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;curl -X POST http://localhost:8080/schema_dump&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⏱️ &lt;strong&gt;Note&lt;/strong&gt;: For large schemas, this can take a minute or two. Triggering it early(but after the migrations) avoids blocking downstream jobs.&lt;/p&gt;

&lt;h2&gt;
  
  
  📊 Step 2: Compare Against Main
&lt;/h2&gt;

&lt;p&gt;After your tests or migrations run through the proxy, it now holds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;full set of SQL queries&lt;/strong&gt; the app executed&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;current state of the database schema&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this data captured, you can run a GitHub Action that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calls the proxy’s API to fetch the captured data&lt;/li&gt;
&lt;li&gt;Downloads artifacts from the main branch (last known good state)

&lt;ul&gt;
&lt;li&gt;Note: it needs to run on the main branch at least once to have generated a proper artifact for comparison&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Compares current vs. main to generate:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;schema-diff.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;queries-diff.json&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s what that looks like in CI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get SQL Data&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;droptableifexists/recon@main&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;get-sql-data&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;SQL_PROXY_API_ADDRESS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost:8080&lt;/span&gt;
    &lt;span class="na"&gt;GITHUB_REPOSITORY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.repository }}&lt;/span&gt;
    &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🤖 Step 3: Run the AI Review
&lt;/h2&gt;

&lt;p&gt;After you have the four key files, you pass them into the AI Config system.&lt;/p&gt;

&lt;p&gt;Save input to a file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Save SQL data to file&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;cat &amp;lt;&amp;lt; EOF &amp;gt; analysis_input.json&lt;/span&gt;
    &lt;span class="s"&gt;{&lt;/span&gt;
      &lt;span class="s"&gt;"sql_queries": ${{  toJSON(steps.get-sql-data.outputs.sql-queries) }},&lt;/span&gt;
      &lt;span class="s"&gt;"queries_diff": ${{  toJSON(steps.get-sql-data.outputs.queries-diff) }},&lt;/span&gt;
      &lt;span class="s"&gt;"schema": ${{  toJSON(steps.get-sql-data.outputs.schema) }},&lt;/span&gt;
      &lt;span class="s"&gt;"schema_diff": ${{  toJSON(steps.get-sql-data.outputs.schema-diff) }}&lt;/span&gt;
    &lt;span class="s"&gt;}&lt;/span&gt;
    &lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the DB analysis tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Database Analysis&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;launchdarkly-labs/lddbai@main&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Analyzing&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;database&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;changes..."&lt;/span&gt;
    &lt;span class="na"&gt;input_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;analysis_input.json&lt;/span&gt;
    &lt;span class="na"&gt;openai_api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.OPENAI_API_KEY }}&lt;/span&gt;
    &lt;span class="na"&gt;launchdarkly_sdk_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.LAUNCHDARKLY_SDK_KEY }}&lt;/span&gt;
    &lt;span class="na"&gt;github_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
    &lt;span class="na"&gt;pull_request_number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event.pull_request.number }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the hood, here's what the code looks like:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_ai_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&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="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;AIConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LDAIConfigTracker&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
   &lt;span class="n"&gt;aiclient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Deps&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;get_launchdarkly_ai&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cockroachdb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;database&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cockroachdb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="n"&gt;fallback_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AIConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="n"&gt;enabled&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="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;ModelConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&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;parameters&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;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;
       &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;LDMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&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;provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;ProviderConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-default-provider&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;return&lt;/span&gt; &lt;span class="n"&gt;aiclient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;evaluate-database-changes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fallback_value&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;schema&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;schema&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;schema_diff&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;schema_diff&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;sql_queries&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sql_queries&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;queries_diff&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;queries_diff&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="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to get the AI’s recommendation:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_ai_recommendation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&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="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="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_ai_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&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;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;track_openai_metrics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&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="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_dict&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;messages&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;span class="nc"&gt;Deps&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;get_launchdarkly&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;flush&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&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;h2&gt;
  
  
  🧠 Step 4: Add Context That Only You Know
&lt;/h2&gt;

&lt;p&gt;Once the model has your queries and schema, you can make it smarter by adding business context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database engine and version 🧱&lt;/li&gt;
&lt;li&gt;Critical tables to tread carefully around 🚨&lt;/li&gt;
&lt;li&gt;Average query volume and server specs 📈 &lt;/li&gt;
&lt;li&gt;Team-specific data modeling principles 📐&lt;/li&gt;
&lt;li&gt;History of past incidents or patterns 📜&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This context transforms the AI from a generic reviewer into a tailored risk advisor for your system.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft4q71z3c3cevibs3u8kz.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft4q71z3c3cevibs3u8kz.webp" alt="Screenshot showing the LaunchDarkly UI for creating an AI Config to manage database changes." width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Why This Matters
&lt;/h2&gt;

&lt;p&gt;With AI Configs, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Catch performance and scaling issues before they hit production&lt;/li&gt;
&lt;li&gt;Share SRE intuition across your whole engineering team&lt;/li&gt;
&lt;li&gt;Shorten feedback loops without blocking deploys&lt;/li&gt;
&lt;li&gt;Scale database expertise without bottlenecks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’re no longer at the mercy of “who reviewed the PR.” Every change gets a consistent, context-aware review.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏁 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Database changes don’t have to be scary anymore.&lt;/p&gt;

&lt;p&gt;By plugging into LaunchDarkly AI Configs, you can automate reviews, enforce data modeling best practices, and de-risk your deploys—without slowing anyone down. To get started, &lt;a href="//aiproduct@launchdarkly.com"&gt;sign up for a free trial today&lt;/a&gt; or email us at &lt;code&gt;aiproduct@launchdarkly.com&lt;/code&gt; if you have questions. &lt;/p&gt;

&lt;p&gt;So yeah, go ahead. Vibe out. Ship confidently. And let the AI handle the hard stuff. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally &lt;a href="https://launchdarkly.com/docs/tutorials" rel="noopener noreferrer"&gt;published by Kevin Kruger on the LaunchDarkly blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>ai</category>
      <category>postgres</category>
      <category>codereview</category>
    </item>
  </channel>
</rss>
