<?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>Forem: Stephen Collins</title>
    <description>The latest articles on Forem by Stephen Collins (@stephenc222).</description>
    <link>https://forem.com/stephenc222</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F99002%2F5f931e74-9594-4a85-ab35-6f7c96ce649b.png</url>
      <title>Forem: Stephen Collins</title>
      <link>https://forem.com/stephenc222</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/stephenc222"/>
    <language>en</language>
    <item>
      <title>AI Agents Can Pass Tests. They Still Can't Maintain Systems.</title>
      <dc:creator>Stephen Collins</dc:creator>
      <pubDate>Thu, 12 Mar 2026 13:43:00 +0000</pubDate>
      <link>https://forem.com/stephenc222/ai-agents-can-pass-tests-they-still-cant-maintain-systems-2004</link>
      <guid>https://forem.com/stephenc222/ai-agents-can-pass-tests-they-still-cant-maintain-systems-2004</guid>
      <description>&lt;p&gt;AI coding tools have made writing software dramatically easier.&lt;/p&gt;

&lt;p&gt;Cursor can scaffold entire modules from a prompt. Claude can produce working CLIs in minutes. Copilot fills in entire implementations as you type.&lt;/p&gt;

&lt;p&gt;Code production is no longer the primary bottleneck in software engineering.&lt;/p&gt;

&lt;p&gt;But a new research benchmark from Alibaba highlights something important:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Writing code is not the same as maintaining a system.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The SWE-CI Benchmark
&lt;/h2&gt;

&lt;p&gt;Most AI coding benchmarks test one thing: can a model produce working code once?&lt;/p&gt;

&lt;p&gt;Examples include HumanEval, MBPP, and SWE-bench. These evaluate a single moment in time. A model writes code, tests pass, and the task is complete.&lt;/p&gt;

&lt;p&gt;Real software does not work that way.&lt;/p&gt;

&lt;p&gt;Codebases evolve for years. Features are added. Requirements change. Dependencies shift. Bugs appear in unexpected places.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;&lt;a href="https://arxiv.org/abs/2603.03823" rel="noopener noreferrer"&gt;SWE-CI benchmark&lt;/a&gt;&lt;/strong&gt; was designed to simulate this reality.&lt;/p&gt;

&lt;p&gt;Instead of a single coding task, SWE-CI runs models through a simulated &lt;strong&gt;continuous integration loop across real repository history&lt;/strong&gt;. Each task includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a real open source repository&lt;/li&gt;
&lt;li&gt;a starting commit&lt;/li&gt;
&lt;li&gt;dozens of historical changes&lt;/li&gt;
&lt;li&gt;evolving test suites&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On average, each task spans &lt;strong&gt;233 days of evolution&lt;/strong&gt; across &lt;strong&gt;71 commits&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Agents must repeatedly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;read the repository&lt;/li&gt;
&lt;li&gt;analyze failing tests&lt;/li&gt;
&lt;li&gt;modify the code&lt;/li&gt;
&lt;li&gt;run CI&lt;/li&gt;
&lt;li&gt;avoid breaking existing behavior&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This process repeats over many iterations. The goal is not just to make code work once. The goal is to &lt;strong&gt;maintain a system over time&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Happens to Most AI Agents
&lt;/h2&gt;

&lt;p&gt;The results are revealing.&lt;/p&gt;

&lt;p&gt;Across 18 models from 8 providers, most agents struggled to maintain stability under continuous evolution. The most common failure pattern looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fix failing test
↓
Break another module
↓
Patch that module
↓
Break something else
↓
System becomes unstable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Researchers observed several recurring failure modes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Local Patch Myopia
&lt;/h3&gt;

&lt;p&gt;Agents apply minimal fixes to the failing test without understanding broader dependencies. The result is a cascading sequence of regressions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interface Drift
&lt;/h3&gt;

&lt;p&gt;Function signatures change but callers are not updated across the codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architectural Erosion
&lt;/h3&gt;

&lt;p&gt;Over many iterations, the codebase accumulates duplicated logic, temporary helpers, and inconsistent abstractions. In other words: &lt;strong&gt;AI agents generate technical debt.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Test Misinterpretation
&lt;/h3&gt;

&lt;p&gt;Models often treat tests as constraints to bypass rather than signals about system invariants. Passing the test becomes more important than preserving the intended behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Even the Best Models Struggle
&lt;/h2&gt;

&lt;p&gt;One model family stood out: Claude Opus produced significantly fewer regressions than other models.&lt;/p&gt;

&lt;p&gt;But even the best-performing agents still broke existing behavior in roughly &lt;strong&gt;half of the maintenance iterations&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That is a critical observation.&lt;/p&gt;

&lt;p&gt;AI models can often produce working implementations. But maintaining a complex system requires something deeper:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;understanding dependencies&lt;/li&gt;
&lt;li&gt;preserving architectural structure&lt;/li&gt;
&lt;li&gt;predicting change impact&lt;/li&gt;
&lt;li&gt;managing technical debt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are &lt;strong&gt;system comprehension problems&lt;/strong&gt;, not code generation problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottleneck Has Moved
&lt;/h2&gt;

&lt;p&gt;For decades, writing code was the limiting factor in software development. AI is rapidly removing that constraint.&lt;/p&gt;

&lt;p&gt;But SWE-CI highlights the next bottleneck: &lt;strong&gt;understanding large systems and how they evolve.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Two implementations may both pass tests today. Only one will remain stable as the system changes. Those differences only emerge over time — and that's exactly what the benchmark is designed to surface.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Rise of Meta Tooling
&lt;/h2&gt;

&lt;p&gt;As AI increases the rate of code production, the importance of tools that help engineers understand systems will grow.&lt;/p&gt;

&lt;p&gt;A new category of tooling is emerging around:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;repository evolution&lt;/li&gt;
&lt;li&gt;architectural risk&lt;/li&gt;
&lt;li&gt;change impact&lt;/li&gt;
&lt;li&gt;technical debt concentration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are tools that help engineers answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which files actually matter in this codebase?&lt;/li&gt;
&lt;li&gt;Where is complexity accumulating?&lt;/li&gt;
&lt;li&gt;Which parts of the system are most fragile?
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────┐
│  Layer 3 - Codebase Intelligence                    │
│  Hotspot analysis · architecture evolution ·        │
│  system risk signals                                │
├─────────────────────────────────────────────────────┤
│  Layer 2 - Code Validation                          │
│  Linters · type systems · static analysis ·         │
│  security scanners                                  │
├─────────────────────────────────────────────────────┤
│  Layer 1 - Code Creation                            │
│  IDEs · Copilot · Cursor · AI coding agents         │
└─────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the motivation behind &lt;a href="https://hotspots.dev" rel="noopener noreferrer"&gt;Hotspots&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Hotspots analyzes repository history and code structure to identify the small set of files that account for most maintenance risk. In large systems, these hotspots often represent a tiny fraction of the codebase that drives most engineering effort.&lt;/p&gt;

&lt;p&gt;Understanding those areas is essential for both humans and AI systems trying to maintain software over time. The SWE-CI failure modes tend to concentrate in the same places hotspot analysis flags: files with high churn and high complexity are exactly where cascading regressions and architectural erosion accumulate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Future of AI-Assisted Engineering
&lt;/h2&gt;

&lt;p&gt;AI will keep getting better at generating code.&lt;/p&gt;

&lt;p&gt;But software engineering has always been more than generation. It is about managing complexity, evolving architecture, and maintaining invariants across thousands of changes.&lt;/p&gt;

&lt;p&gt;The SWE-CI benchmark shows that these challenges remain significant for current AI agents. Which means the next generation of developer tooling will likely focus less on &lt;strong&gt;writing code&lt;/strong&gt; and more on &lt;strong&gt;understanding systems&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The developers who will be most effective in an AI-accelerated world won't just be the ones who can generate code fastest. They'll be the ones who can reason clearly about systems — who understand which parts matter, where risk lives, and where to focus attention.&lt;/p&gt;

&lt;p&gt;If you're curious where risk concentrates in your own codebase, &lt;a href="https://hotspots.dev" rel="noopener noreferrer"&gt;hotspots.dev&lt;/a&gt; is a good place to start.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>developertools</category>
      <category>codehealth</category>
      <category>engineering</category>
    </item>
    <item>
      <title>AI Made Code Cheap. The Bottleneck Is Now Understanding Systems.</title>
      <dc:creator>Stephen Collins</dc:creator>
      <pubDate>Mon, 09 Mar 2026 13:51:00 +0000</pubDate>
      <link>https://forem.com/stephenc222/ai-made-code-cheap-the-bottleneck-is-now-understanding-systems-34p6</link>
      <guid>https://forem.com/stephenc222/ai-made-code-cheap-the-bottleneck-is-now-understanding-systems-34p6</guid>
      <description>&lt;p&gt;&lt;em&gt;This post was originally published on &lt;a href="https://hotspots.dev/blog/ai-made-code-cheap/" rel="noopener noreferrer"&gt;hotspots.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Software used to be expensive to produce. Developers were constrained by how fast they could write code, which meant teams had to be deliberate about what they built. Every feature was an investment.&lt;/p&gt;

&lt;p&gt;That constraint is eroding quickly. AI tools can now generate large amounts of working software from relatively small prompts. Cursor scaffolds full modules from a description. Claude Code produces working CLIs in minutes. Copilot fills in entire implementations as you type. One prompt can produce hundreds of lines of code — complete API handlers, even full prototypes.&lt;/p&gt;

&lt;p&gt;Code production is no longer the bottleneck.&lt;/p&gt;

&lt;p&gt;The constraint has moved somewhere else.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottleneck Has Moved
&lt;/h2&gt;

&lt;p&gt;When the cost of producing something drops dramatically, volume increases. Software is no exception.&lt;/p&gt;

&lt;p&gt;As code becomes cheaper to produce, the hard problem shifts from &lt;em&gt;writing&lt;/em&gt; to &lt;em&gt;understanding&lt;/em&gt;. Developers increasingly spend their time asking different questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which parts of this codebase actually matter?&lt;/li&gt;
&lt;li&gt;Where are bugs most likely to appear?&lt;/li&gt;
&lt;li&gt;Which modules are fragile?&lt;/li&gt;
&lt;li&gt;Where should we direct engineering effort?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are questions about the system itself, not individual files or functions. And they get harder to answer as systems grow, something AI accelerates.&lt;/p&gt;

&lt;p&gt;The engineering bottleneck used to be code creation. Now it's system comprehension.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Structural Effects of Cheap Code
&lt;/h2&gt;

&lt;p&gt;When something becomes dramatically cheaper to produce, predictable effects follow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Codebases grow faster.&lt;/strong&gt; AI removes friction from writing new components. Developers spin up modules they previously would have avoided, scaffold services that once took days, and generate helper libraries on the fly. The number of files, abstractions, and moving parts increases. Systems get larger.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Systems change faster.&lt;/strong&gt; AI also accelerates commit velocity. Refactors become cheaper. Experiments are easier to try. Iteration cycles compress. This is broadly positive — faster feedback loops are good — but it also means the codebase changes at a rate that can outpace a team's ability to reason about it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Architectural entropy increases.&lt;/strong&gt; AI tools are optimized for local correctness — does this function do what I asked? — not for global coherence. Over time, the first two effects compound. Systems still work, but duplicated patterns, inconsistent abstractions, sprawling modules, and accidental complexity pile up.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Existing Tooling Misses
&lt;/h2&gt;

&lt;p&gt;Most developer tooling falls into two categories.&lt;/p&gt;

&lt;p&gt;There are tools that help you &lt;em&gt;write&lt;/em&gt; code: IDEs, autocomplete, and AI assistants like Copilot or Cursor. These help developers produce code faster. They've gotten remarkably good at this.&lt;/p&gt;

&lt;p&gt;There are tools that help you &lt;em&gt;check&lt;/em&gt; code: linters, static analyzers, type systems, security scanners. These validate correctness — does the code follow rules, does it compile, does it have obvious vulnerabilities.&lt;/p&gt;

&lt;p&gt;What's mostly missing is a third category: tools that answer the question &lt;em&gt;what parts of the system matter most?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Today, developers answer this question through intuition and tribal knowledge. Someone on the team "just knows" that the auth module is fragile, or that the payments service has accumulated a lot of technical debt. That knowledge lives in people's heads, doesn't transfer well, and becomes harder to maintain as systems grow and teams change.&lt;/p&gt;

&lt;p&gt;As AI accelerates codebase growth, relying on intuition becomes increasingly untenable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Meta Tool Layer
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────┐
│  Layer 3 - Codebase Intelligence                    │
│  Hotspot analysis · architecture evolution ·        │
│  system risk signals                                │
├─────────────────────────────────────────────────────┤
│  Layer 2 - Code Validation                          │
│  Linters · type systems · static analysis ·         │
│  security scanners                                  │
├─────────────────────────────────────────────────────┤
│  Layer 1 - Code Creation                            │
│  IDEs · Copilot · Cursor · AI coding agents         │
└─────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One way to think about this missing category is &lt;strong&gt;meta tools&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Where conventional tools analyze individual files — this function is too complex, this import is unused — meta tools analyze the codebase itself. They examine system structure, change patterns, and architectural behavior over time. They answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where does change concentrate?&lt;/li&gt;
&lt;li&gt;Where does complexity accumulate?&lt;/li&gt;
&lt;li&gt;Which modules are stable, which are volatile?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Meta tools sit above the code.\&lt;br&gt;
They don't tell you how to fix a specific bug.\&lt;br&gt;
They tell you where to look.&lt;/p&gt;
&lt;h2&gt;
  
  
  Hotspot Analysis as a Concrete Example
&lt;/h2&gt;

&lt;p&gt;One useful approach combines two signals already present in almost every repository: commit history and complexity.&lt;/p&gt;

&lt;p&gt;Some files change constantly. Some files are extremely complex. When those two traits overlap, you have a problem — a function that is both hard to understand &lt;em&gt;and&lt;/em&gt; being actively modified is a high-probability site for bugs, regressions, and compounding technical debt.&lt;/p&gt;

&lt;p&gt;The core heuristic is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;risk ≈ change frequency × complexity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In practice, this surface area is small. In most systems, perhaps 5–10% of the codebase accounts for the majority of the structural risk. Identifying that slice lets engineering teams make better decisions about where to invest time — what to refactor first, what to review most carefully, where to add test coverage.&lt;/p&gt;

&lt;p&gt;Hotspot analysis is one concrete example of a meta tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hotspots.dev" rel="noopener noreferrer"&gt;Hotspots&lt;/a&gt; applies this idea to any git repository, ranking functions by activity-weighted risk and surfacing the small portion of the codebase that dominates engineering effort. The goal isn't to tell you your code is bad. It's to give you a map.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters More in the AI Era
&lt;/h2&gt;

&lt;p&gt;AI tools make hotspot signals stronger, not weaker.&lt;/p&gt;

&lt;p&gt;When developers iterate faster, commit velocity increases — and high-velocity files accumulate churn signals more quickly. When AI generates code that gets repeatedly revised, rewritten, or extended, those integration points show up clearly in the analysis. When architectural entropy increases, complexity metrics climb.&lt;/p&gt;

&lt;p&gt;The problem hotspot analysis solves — identifying where risk concentrates — becomes more valuable as the forces that create risk accelerate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Codebase Observability
&lt;/h2&gt;

&lt;p&gt;The broader idea behind this goes beyond hotspot analysis.&lt;/p&gt;

&lt;p&gt;Modern production systems have strong observability. We measure logs, metrics, traces, error rates, latency. We have dashboards for system health. When something goes wrong in production, we have tools to understand why.&lt;/p&gt;

&lt;p&gt;We don't have equivalent tooling for the codebase itself.&lt;/p&gt;

&lt;p&gt;We don't routinely measure how architecture evolves, where complexity accumulates over time, which modules are trending toward instability, or how AI-generated code changes the system's structural properties. That information exists in git history and static analysis — it just hasn't been assembled into something useful.&lt;/p&gt;

&lt;p&gt;The next step is &lt;em&gt;codebase observability&lt;/em&gt;: treating the evolution of software systems as something worth measuring, monitoring, and understanding over time. Not just "is the code correct?" but "how is the system changing, and where is that change creating risk?"&lt;/p&gt;

&lt;h2&gt;
  
  
  What Comes Next
&lt;/h2&gt;

&lt;p&gt;AI is making code abundant.&lt;/p&gt;

&lt;p&gt;System understanding is becoming the scarce skill.&lt;/p&gt;

&lt;p&gt;The developers who will be most effective in an AI-accelerated world won't just be the ones who can generate code fastest. They'll be the ones who can reason clearly about systems — who understand which parts matter, where risk lives, and where to focus attention. Tools that help with that problem will become increasingly important.&lt;/p&gt;

&lt;p&gt;Tooling for this problem is still early.\&lt;br&gt;
But it's the right problem to be solving.&lt;/p&gt;

&lt;p&gt;If you're curious where risk concentrates in your own codebase, &lt;a href="https://hotspots.dev" rel="noopener noreferrer"&gt;hotspots.dev&lt;/a&gt; is a good place to start.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>developertools</category>
      <category>codehealth</category>
      <category>engineering</category>
    </item>
    <item>
      <title>I Built a CLI to Find the Riskiest Code in Any Repo — Introducing Hotspots</title>
      <dc:creator>Stephen Collins</dc:creator>
      <pubDate>Tue, 03 Mar 2026 13:38:00 +0000</pubDate>
      <link>https://forem.com/stephenc222/i-built-a-cli-to-find-the-riskiest-code-in-any-repo-introducing-hotspots-19ob</link>
      <guid>https://forem.com/stephenc222/i-built-a-cli-to-find-the-riskiest-code-in-any-repo-introducing-hotspots-19ob</guid>
      <description>&lt;p&gt;Every team has those files. The ones everyone knows are dangerous. The ones where a "simple" change takes three days of careful testing. The ones that keep showing up in postmortems.&lt;/p&gt;

&lt;p&gt;I spent a long time noticing that pattern—the same 10% of a codebase causing 80% of the pain—and wondering why our tooling didn't just &lt;em&gt;show&lt;/em&gt; us where that 10% was. ESLint can tell you a function has high cyclomatic complexity. But that doesn't tell you whether it's actively being changed, who's touching it, or whether it's likely to bite you in the next sprint.&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://github.com/Stephen-Collins-tech/hotspots" rel="noopener noreferrer"&gt;Hotspots&lt;/a&gt; — a Rust CLI that finds risky code by combining structural complexity with real git activity.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Core Idea: Complexity × Change
&lt;/h3&gt;

&lt;p&gt;Raw complexity metrics are useful, but they're incomplete. A gnarly function that hasn't been touched in two years isn't your emergency today. The &lt;em&gt;real&lt;/em&gt; danger is complexity &lt;strong&gt;plus&lt;/strong&gt; active change — functions that are structurally hard to reason about &lt;em&gt;and&lt;/em&gt; are being modified regularly.&lt;/p&gt;

&lt;p&gt;Hotspots computes a risk score for every function in your codebase by combining:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Structural signals:&lt;/strong&gt; cyclomatic complexity (CC), nesting depth (ND), fan-out (FO), non-structured exits (NS)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Activity signals:&lt;/strong&gt; git churn in the last 30 days, touch frequency (commit count), recency, call-graph influence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is an &lt;strong&gt;activity-weighted risk score&lt;/strong&gt; — a prioritized list of functions that are both dangerous and active. Not functions you should eventually clean up. Functions you should care about &lt;em&gt;this sprint&lt;/em&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;p&gt;Install with a single curl:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/Stephen-Collins-tech/hotspots/main/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point it at any repo and run a snapshot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hotspots analyze &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--mode&lt;/span&gt; snapshot &lt;span class="nt"&gt;--format&lt;/span&gt; text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll get a ranked output grouped into risk bands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Critical (risk ≥ 9.0):
  processPlanUpgrade    src/api/billing.ts:142    risk 12.4  CC 15  ND 4  FO 8

High (6.0 ≤ risk &amp;lt; 9.0):
  validateSession       src/auth/session.ts:67    risk 9.8   CC 11  ND 3  FO 7
  applySchema           src/db/migrations.ts:203  risk 8.1   CC 10  ND 2  FO 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a shareable HTML report:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hotspots analyze &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--mode&lt;/span&gt; snapshot &lt;span class="nt"&gt;--format&lt;/span&gt; html &lt;span class="nt"&gt;--output&lt;/span&gt; report.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Explain Mode: Why Is This Function Risky?
&lt;/h3&gt;

&lt;p&gt;Once you have the list, you want to know &lt;em&gt;why&lt;/em&gt; something ranked high — and what to do about it. Pass &lt;code&gt;--explain-patterns&lt;/code&gt; and Hotspots annotates each function with antipattern labels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hotspots analyze &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--mode&lt;/span&gt; snapshot &lt;span class="nt"&gt;--format&lt;/span&gt; json &lt;span class="nt"&gt;--explain-patterns&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; snapshot.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In v1.2.0, Hotspots detects two tiers of patterns:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tier 1 — Structural:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;complex_branching&lt;/code&gt;, &lt;code&gt;deeply_nested&lt;/code&gt;, &lt;code&gt;exit_heavy&lt;/code&gt;, &lt;code&gt;long_function&lt;/code&gt;, &lt;code&gt;god_function&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tier 2 — Relational &amp;amp; Temporal:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;hub_function&lt;/code&gt;, &lt;code&gt;cyclic_hub&lt;/code&gt;, &lt;code&gt;middle_man&lt;/code&gt;, &lt;code&gt;neighbor_risk&lt;/code&gt;, &lt;code&gt;stale_complex&lt;/code&gt;, &lt;code&gt;churn_magnet&lt;/code&gt;, &lt;code&gt;shotgun_target&lt;/code&gt;, &lt;code&gt;volatile_god&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A function tagged &lt;code&gt;god_function&lt;/code&gt; + &lt;code&gt;cyclic_hub&lt;/code&gt; is both monolithic &lt;em&gt;and&lt;/em&gt; at the center of a dependency cycle — a very different refactoring situation than one tagged &lt;code&gt;exit_heavy&lt;/code&gt; + &lt;code&gt;long_function&lt;/code&gt;. The labels make the action obvious.&lt;/p&gt;


&lt;h3&gt;
  
  
  CI Policy Checks: Stop Regressions Before They Merge
&lt;/h3&gt;

&lt;p&gt;Identifying hotspots is useful. Preventing new ones from landing is better. Hotspots has a delta mode that compares the current branch against the baseline and applies configurable policy checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hotspots analyze &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--mode&lt;/span&gt; delta &lt;span class="nt"&gt;--policy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Policies you can configure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Critical Introduction&lt;/strong&gt; — fail if a new function lands in the critical band&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Excessive Regression&lt;/strong&gt; — fail if a function's risk score jumps by a large delta&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rapid Growth&lt;/strong&gt; — flag unusually fast complexity growth&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch/Attention&lt;/strong&gt; — warn when functions approach thresholds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add it to CI and complexity regressions fail the PR before review. Start it in warn-only mode, get the team used to the signal, then flip to blocking when you're ready.&lt;/p&gt;

&lt;p&gt;Here's a minimal GitHub Actions step:&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;Hotspots policy check&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hotspots analyze . --mode delta --policy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exit code 1 if any blocking policy fails. Zero if clean.&lt;/p&gt;




&lt;h3&gt;
  
  
  hotspots.dev — Automated OSS Analysis
&lt;/h3&gt;

&lt;p&gt;Alongside the CLI, I've been building &lt;a href="https://hotspots.dev" rel="noopener noreferrer"&gt;hotspots.dev&lt;/a&gt; — a blog that runs automated Hotspots analyses on trending open-source repos every night.&lt;/p&gt;

&lt;p&gt;The pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A GitHub Actions crawl job selects a fresh trending repo&lt;/li&gt;
&lt;li&gt;Hotspots analyzes it and extracts the top functions and antipatterns&lt;/li&gt;
&lt;li&gt;An AI draft gets generated and opened as a PR for review&lt;/li&gt;
&lt;li&gt;Once merged, it deploys automatically&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's a real-world showcase of what the tool surfaces in popular codebases — eslint, Flowise, and more. The HTML reports are hosted at &lt;code&gt;reports.hotspots.dev&lt;/code&gt; so you can drill into the full ranked analysis for any repo.&lt;/p&gt;

&lt;p&gt;I find it genuinely interesting to run Hotspots on codebases I use every day and see where the structural risk actually lives. It's rarely where you'd guess.&lt;/p&gt;




&lt;h3&gt;
  
  
  Try It
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/Stephen-Collins-tech/hotspots/main/install.sh | sh

&lt;span class="c"&gt;# Snapshot your current repo&lt;/span&gt;
hotspots analyze &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--mode&lt;/span&gt; snapshot &lt;span class="nt"&gt;--format&lt;/span&gt; text

&lt;span class="c"&gt;# Full JSON with pattern labels&lt;/span&gt;
hotspots analyze &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--mode&lt;/span&gt; snapshot &lt;span class="nt"&gt;--format&lt;/span&gt; json &lt;span class="nt"&gt;--explain-patterns&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; snapshot.json

&lt;span class="c"&gt;# CI policy check&lt;/span&gt;
hotspots analyze &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--mode&lt;/span&gt; delta &lt;span class="nt"&gt;--policy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;📖 Docs: &lt;a href="https://docs.hotspots.dev" rel="noopener noreferrer"&gt;docs.hotspots.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🔬 Live analyses: &lt;a href="https://hotspots.dev" rel="noopener noreferrer"&gt;hotspots.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🦀 Source: &lt;a href="https://github.com/Stephen-Collins-tech/hotspots" rel="noopener noreferrer"&gt;github.com/Stephen-Collins-tech/hotspots&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's MIT licensed, written in Rust, and works on any language — since it operates on git history and AST-level metrics rather than language-specific rules.&lt;/p&gt;

&lt;p&gt;If you've ever looked at a PR and thought "this feels risky but I can't explain why" — run Hotspots. It'll tell you why.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Complexity metrics alone miss the point. Hotspots combines structural analysis with real git activity to surface the functions that are both hard to change &lt;em&gt;and&lt;/em&gt; actively being changed. It's a Rust CLI, it works on any language, and it can gate PRs in CI. &lt;a href="https://github.com/Stephen-Collins-tech/hotspots" rel="noopener noreferrer"&gt;Try it.&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>codequality</category>
      <category>developertools</category>
      <category>techdebt</category>
      <category>staticanalysis</category>
    </item>
    <item>
      <title>Merkle Trees in SQLite with Python: A Practical Tutorial</title>
      <dc:creator>Stephen Collins</dc:creator>
      <pubDate>Mon, 22 Sep 2025 20:45:00 +0000</pubDate>
      <link>https://forem.com/stephenc222/merkle-trees-in-sqlite-with-python-a-practical-tutorial-5d04</link>
      <guid>https://forem.com/stephenc222/merkle-trees-in-sqlite-with-python-a-practical-tutorial-5d04</guid>
      <description>&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%2Fynuznw0m906ejppltaat.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%2Fynuznw0m906ejppltaat.webp" alt="Blog Post Cover Image" width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Merkle trees are one of those concepts that quietly power some of the most important technologies we use today—Git, blockchains, and peer-to-peer systems like IPFS. They provide a compact way to prove that a piece of data belongs to a larger dataset, without needing to reveal or send everything.&lt;/p&gt;

&lt;p&gt;But Merkle trees are often introduced only in the context of massive distributed systems. What if you just want to play with them locally, on your laptop, using tools you already know? That's where SQLite comes in.&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll build a Merkle tree in Python and persist it to SQLite. By the end, you'll have a working project that can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build a Merkle tree from arbitrary strings.&lt;/li&gt;
&lt;li&gt;Store all nodes—leaves and internal hashes—in a relational database.&lt;/li&gt;
&lt;li&gt;Query inclusion proofs for any leaf.&lt;/li&gt;
&lt;li&gt;Verify proofs against the stored root.&lt;/li&gt;
&lt;li&gt;Inspect and visualize the tree with SQL or helper scripts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This combination gives you a &lt;strong&gt;verifiable, tamper-evident, and queryable data structure&lt;/strong&gt; inside a single &lt;code&gt;.db&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;All the code is available on &lt;a href="https://github.com/stephenc222/example-merkle-trees-in-sqlite" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why SQLite?
&lt;/h2&gt;

&lt;p&gt;Before we dive into code, let's answer the obvious question: why store a Merkle tree in SQLite at all?&lt;/p&gt;

&lt;p&gt;SQLite is a lightweight, embeddable database. You don't need a server, a cluster, or even an internet connection—just a file. That makes it ideal for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tamper-evident logs&lt;/strong&gt;&lt;br&gt;
Store logs or events as leaves, and use the Merkle root to prove no row was modified. If someone changes even one character in the database, the root hash won't match.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lightweight blockchain-like systems&lt;/strong&gt;&lt;br&gt;
You can experiment with blockchain concepts without spinning up heavy infrastructure. Exchange Merkle roots and proofs between peers instead of full tables.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Efficient synchronization&lt;/strong&gt;&lt;br&gt;
Two devices with the same dataset can exchange just their root hashes. If they differ, you can reconcile branch by branch instead of copying everything.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Verifiable queries&lt;/strong&gt;&lt;br&gt;
SQL gives you speed and structure; Merkle trees give you proofs. Together, you can run a query and provide a cryptographic guarantee that the results belong to a specific database state.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Education and demos&lt;/strong&gt;&lt;br&gt;
SQLite makes the concept tangible. You can see every node in a table, inspect it with SQL, and visualize the tree.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So while it may seem unusual, storing Merkle trees in SQLite is a neat way to bridge cryptographic structures with the databases we already use.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Merkle Trees Work
&lt;/h2&gt;

&lt;p&gt;A Merkle tree is a binary tree where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Leaves&lt;/strong&gt; contain the hash of the raw data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal nodes&lt;/strong&gt; contain the hash of the concatenation of their children.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;root hash&lt;/strong&gt; commits to the entire dataset.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to prove that “gamma” is part of the dataset, you don't need to show every other value. Instead, you provide its sibling hashes along the path to the root. The verifier recomputes and checks if it matches the stored root hash.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Project Structure
&lt;/h2&gt;

&lt;p&gt;Our Python project is minimal and dependency-free. The main files are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── main.py            # Example usage
├── merkle_tree.py     # Core MerkleTree class
├── print_proof.py     # CLI to print and verify proofs
├── visualize_tree.py  # CLI to render tree as ASCII or Graphviz
├── pyproject.toml     # Metadata and scripts
└── README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything uses only the Python standard library (&lt;code&gt;hashlib&lt;/code&gt;, &lt;code&gt;sqlite3&lt;/code&gt;, &lt;code&gt;pathlib&lt;/code&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  The SQLite Schema
&lt;/h2&gt;

&lt;p&gt;The heart of persistence is a single table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;merkle_nodes&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;parent_id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;left_child_id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;right_child_id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;level&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;is_leaf&lt;/span&gt; &lt;span class="nb"&gt;BOOLEAN&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;merkle_nodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left_child_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;merkle_nodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;right_child_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;merkle_nodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&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;Each row is one node. Leaves carry original &lt;code&gt;data&lt;/code&gt;; internal nodes have only &lt;code&gt;hash&lt;/code&gt;. Parent and child links let you traverse the tree in either direction.&lt;/p&gt;




&lt;h2&gt;
  
  
  The &lt;code&gt;MerkleTree&lt;/code&gt; Class
&lt;/h2&gt;

&lt;p&gt;The core logic lives in &lt;code&gt;merkle_tree.py&lt;/code&gt;. It's wrapped in a class that manages both hashing and database storage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initialization and Context Manager
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;MerkleTree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;merkle_tree.db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;root_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build_tree&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alpha&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;beta&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gamma&lt;/span&gt;&lt;span class="sh"&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 class opens and closes its SQLite connection automatically. Using a &lt;code&gt;with&lt;/code&gt; block ensures resources are cleaned up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the Tree
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;build_tree(data_blocks)&lt;/code&gt; does three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hashes the leaves with SHA-256.&lt;/li&gt;
&lt;li&gt;Iteratively pairs them, hashing concatenated child hashes until a root is formed. If there's an odd number, the last hash is duplicated (Bitcoin-style).&lt;/li&gt;
&lt;li&gt;Inserts each node into the database and links parent-child relationships.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The method returns the root node's ID for convenience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generating Proofs
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;get_proof(leaf_id)&lt;/code&gt; walks from a leaf up to the root, collecting &lt;strong&gt;sibling hashes&lt;/strong&gt; along the way. Each step also records whether the &lt;em&gt;current node&lt;/em&gt; was a left or right child. That orientation matters because the concatenation order of hashes changes the result.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the current node was the &lt;strong&gt;left child&lt;/strong&gt;, the sibling is on the right → &lt;code&gt;(sibling_hash, True)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If the current node was the &lt;strong&gt;right child&lt;/strong&gt;, the sibling is on the left → &lt;code&gt;(sibling_hash, False)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This way, the verifier knows whether to compute &lt;code&gt;sha256(current + sibling)&lt;/code&gt; or &lt;code&gt;sha256(sibling + current)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Example proof path:&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="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;hash_of_right_sibling&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;   &lt;span class="c1"&gt;# current node was left
&lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hash_of_left_sibling&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;   &lt;span class="c1"&gt;# current node was right
&lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hash_of_right_sibling&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# current node was left
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During verification, you start from the leaf's hash, then fold in each sibling in the correct order until you recompute the root.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verification
&lt;/h3&gt;

&lt;p&gt;Finally, &lt;code&gt;verify_proof(data_item, proof_path, expected_root)&lt;/code&gt; recomputes the path from a leaf to the root.&lt;/p&gt;

&lt;p&gt;It works like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start with the SHA-256 hash of the original data.&lt;/li&gt;
&lt;li&gt;For each &lt;code&gt;(sibling_hash, is_left_child)&lt;/code&gt; in the proof path:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If &lt;code&gt;is_left_child == True&lt;/code&gt;, the current node was the &lt;strong&gt;left child&lt;/strong&gt;, so compute:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sibling_hash&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If &lt;code&gt;is_left_child == False&lt;/code&gt;, the current node was the &lt;strong&gt;right child&lt;/strong&gt;, so compute:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;sibling_hash&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

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

&lt;ol&gt;
&lt;li&gt;When you reach the top, compare the result to the stored root.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&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;sibling_hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;is_left_child&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;proof_path&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;is_left_child&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sibling_hash&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;sibling_hash&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected_root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# True if inclusion is valid
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This guarantees that only the correct leaf, combined with the right sequence of sibling hashes, can reconstruct the root.&lt;/p&gt;




&lt;h2&gt;
  
  
  Running the Demo
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;main.py&lt;/code&gt; ties everything together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv run main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Root node ID: 31
Leaf: 3 5f9a...
Proof for 'gamma': [('4f4a...', True), ('0cb0...', False), ('673e...', True)]
Verification: True
Merkle tree stored in /absolute/path/merkle_tree.db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The root node was built.&lt;/li&gt;
&lt;li&gt;A proof for &lt;code&gt;"gamma"&lt;/code&gt; was generated.&lt;/li&gt;
&lt;li&gt;Verification succeeded.&lt;/li&gt;
&lt;li&gt;The full tree is persisted in &lt;code&gt;merkle_tree.db&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Inspecting with SQL
&lt;/h2&gt;

&lt;p&gt;Because everything is in SQLite, you can poke around yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sqlite3 merkle_tree.db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;View schema and sample nodes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;schema&lt;/span&gt; &lt;span class="n"&gt;merkle_nodes&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;is_leaf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;merkle_nodes&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find the root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;merkle_nodes&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;parent_id&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can even extract inclusion proofs with a recursive CTE.&lt;/p&gt;




&lt;h2&gt;
  
  
  Printing and Verifying Proofs
&lt;/h2&gt;

&lt;p&gt;The helper script &lt;code&gt;print_proof.py&lt;/code&gt; lets you fetch proofs from an existing database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv run python print_proof.py gamma
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Data: gamma
DB: merkle_tree.db
Leaf: id=3 hash=be9d...
Root: afc48e...
Proof (sibling_hash, is_left_child):
   ('4f4a94...', True)
   ('0cb030...', False)
   ('673e72...', True)
Verification: True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Visualizing the Tree
&lt;/h2&gt;

&lt;p&gt;The optional &lt;code&gt;visualize_tree.py&lt;/code&gt; script shows the tree in ASCII or Graphviz DOT:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv run python visualize_tree.py &lt;span class="nt"&gt;--format&lt;/span&gt; ascii
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Root: id=31 hash=abc123
Level 0: [N id=31 h=abc123]
Level 1: [N id=25 h=1122aa]  [N id=30 h=99ee77]
Level 2: [L id=3 h=5f9a... data='gamma']  [L id=4 h=...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  How This Compares to Git and Bitcoin
&lt;/h2&gt;

&lt;p&gt;It's worth noting how our simple SQLite-backed Merkle tree relates to real-world systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Git&lt;/strong&gt; uses Merkle trees to track file versions. Every commit points to a tree object (directory), which points to blobs (files). Hashes are stored in a content-addressable database (&lt;code&gt;.git/objects&lt;/code&gt;). Our implementation is simpler but follows the same principle: content → hash → parent hash → root.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Bitcoin&lt;/strong&gt; uses Merkle trees to commit to all transactions in a block. Each leaf is a transaction hash; the Merkle root is included in the block header. Our example uses strings like &lt;code&gt;"alpha"&lt;/code&gt; instead of transactions, but the process—pairing, hashing, and duplicating odd leaves—is identical.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The difference is scale and purpose. Git and Bitcoin optimize for billions of objects or high-security consensus. Our SQLite example is educational: small, transparent, and easy to query.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;This little project highlights some powerful ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cryptography meets databases&lt;/strong&gt;: By storing Merkle trees in SQLite, you blend verifiability with queryability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proofs are compact&lt;/strong&gt;: Verifying inclusion doesn't require downloading everything—just log(N) sibling hashes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQLite is versatile&lt;/strong&gt;: It's more than a toy database; you can model even cryptographic structures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Educational clarity&lt;/strong&gt;: Inspecting the tree in SQL makes the concept far less abstract.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Where to Go Next
&lt;/h2&gt;

&lt;p&gt;This is a minimal, educational implementation. Real-world extensions could include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Append-only logs for tamper-evident auditing.&lt;/li&gt;
&lt;li&gt;Multiple independent Merkle trees in one database.&lt;/li&gt;
&lt;li&gt;Performance tuning for large datasets.&lt;/li&gt;
&lt;li&gt;More advanced hash strategies (domain separation, byte-level concatenation).&lt;/li&gt;
&lt;li&gt;Integration with APIs that require verifiable responses.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Merkle trees are often taught as abstract cryptographic structures behind big systems like Bitcoin. But as this tutorial shows, you can implement them locally with just Python and SQLite.&lt;/p&gt;

&lt;p&gt;The result is a verifiable, tamper-evident database in a single &lt;code&gt;.db&lt;/code&gt; file—no servers, no dependencies. Whether you're prototyping, teaching, or just exploring, it's a powerful way to make Merkle trees tangible.&lt;/p&gt;

</description>
      <category>merkletrees</category>
      <category>sqlite</category>
      <category>python</category>
      <category>cryptography</category>
    </item>
    <item>
      <title>The Technical Challenges I've Faced in Interviews (and Given as an Interviewer)</title>
      <dc:creator>Stephen Collins</dc:creator>
      <pubDate>Sat, 13 Sep 2025 15:50:35 +0000</pubDate>
      <link>https://forem.com/stephenc222/the-technical-challenges-ive-faced-in-interviews-and-given-as-an-interviewer-4hj9</link>
      <guid>https://forem.com/stephenc222/the-technical-challenges-ive-faced-in-interviews-and-given-as-an-interviewer-4hj9</guid>
      <description>&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%2Fll60tagvb41ik4vqst49.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%2Fll60tagvb41ik4vqst49.webp" alt="Blog post cover image" width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've been on both sides of the technical interview table. Sometimes I'm the one sweating through a coding challenge, other times I'm the one deciding how to evaluate a candidate. Along the way, I've seen a few clear patterns in what works (and what doesn't).&lt;/p&gt;

&lt;p&gt;Here's what that looks like from both perspectives.&lt;/p&gt;




&lt;h2&gt;
  
  
  As an Interviewer
&lt;/h2&gt;

&lt;p&gt;When I've run interviews, I've leaned heavily on &lt;strong&gt;technical conversations&lt;/strong&gt; rather than puzzles or trick questions.&lt;/p&gt;

&lt;p&gt;That means sitting down with someone and asking them to talk through what they know, what they don't know, and what they actually &lt;em&gt;like&lt;/em&gt; doing. I'll often dig into past projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What trade-offs did you make?&lt;/li&gt;
&lt;li&gt;What would you do differently if you had twice the time?&lt;/li&gt;
&lt;li&gt;What parts of the system frustrated you?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm not looking for “perfect” answers. I'm looking for signals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can you clearly explain your reasoning?&lt;/li&gt;
&lt;li&gt;Do you know your own limits?&lt;/li&gt;
&lt;li&gt;Do you light up when talking about certain problems, and shrink away from others?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The job isn't about memorizing solutions-it's about how you think, communicate, and make trade-offs. These conversations often reveal more than any contrived exercise ever could.&lt;/p&gt;

&lt;p&gt;And honestly, they're more enjoyable for both sides. Instead of watching someone struggle through a binary search tree implementation, we get to talk shop. That's a better predictor of whether I'd want to work with them every day.&lt;/p&gt;




&lt;h2&gt;
  
  
  As an Interviewee
&lt;/h2&gt;

&lt;p&gt;I've had my fair share of experiences across the spectrum. Some good, some less so.&lt;/p&gt;

&lt;h3&gt;
  
  
  Esoteric algorithm challenges
&lt;/h3&gt;

&lt;p&gt;These are the “can you do dynamic programming on a whiteboard in 20 minutes?” style questions. I'll be honest: I still bomb these. They're mostly about drilling patterns until you can regurgitate them under pressure.&lt;/p&gt;

&lt;p&gt;I once got asked to implement Conway's Game of Life that, with time, I probably could've solved-but in the moment, I froze. Unless it's two-sum (which I've got memorized), I'm not your whiteboard hero.&lt;/p&gt;

&lt;p&gt;I understand why these persist-they're standardized, and you can compare candidates on a consistent axis. But they rarely map to the day-to-day work of building software.&lt;/p&gt;




&lt;h3&gt;
  
  
  Real-world coding challenges
&lt;/h3&gt;

&lt;p&gt;These are my favorite. I've been asked to build or fix a simple web app or API server, usually in TypeScript or Go.&lt;/p&gt;

&lt;p&gt;Sometimes it's something as small as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add an endpoint that calculates a running total.&lt;/li&gt;
&lt;li&gt;Debug why a POST route is returning the wrong status code.&lt;/li&gt;
&lt;li&gt;Wire up a simple front end to call the backend you just wrote.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These exercises feel much closer to reality. They show how you name variables, how you structure code, whether you remember to handle edge cases, and how you test. In short-they reveal how you actually work.&lt;/p&gt;

&lt;p&gt;The best part? Even if you don't finish, the interviewer can still learn a ton by watching how you approach the problem.&lt;/p&gt;




&lt;h3&gt;
  
  
  Technical conversations
&lt;/h3&gt;

&lt;p&gt;And then there are the conversational ones, just like I give when I'm the interviewer. Some of the best interviews I've had were simply about discussing trade-offs, high-level design, or walking through my past decisions.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“If you were designing this feature, how would you balance speed vs reliability?”&lt;/li&gt;
&lt;li&gt;“What's an architecture decision you regret, and why?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No IDE, no stopwatch-just talking through real engineering problems. These feel collaborative, like a design session you might have on the job. And they've consistently left me with the best impression of both the company and the role.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Means For You
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Don't panic if you're not an algorithm wizard.&lt;/strong&gt; Many real-world interviews weigh practical coding and conversation more heavily. Passing or failing a graph problem doesn't define your career.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Get comfortable narrating your thinking.&lt;/strong&gt; Whether you're debugging code or sketching a system design, talking through your decisions is half the game. Interviewers want to see how you approach problems, not just whether you land on the “right” answer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;As an interviewer, think about fit, not filters.&lt;/strong&gt; You'll learn far more by understanding someone's preferences and reasoning than by seeing if they can invert a binary tree under pressure. Hiring is about building teams, not grading exams.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Final Reflection
&lt;/h2&gt;

&lt;p&gt;The truth is, there's no single “right” way to run a technical interview. Every format has trade-offs. But after being on both sides, I've learned that the best interviews aren't about tricking someone into an answer-they're about creating space to see how they think, what they value, and whether they'd thrive in your environment.&lt;/p&gt;

&lt;p&gt;That's ultimately the point of an interview: not to test for perfection, but to test for fit.&lt;/p&gt;

</description>
      <category>technicalinterviews</category>
      <category>interviewprep</category>
      <category>softwareengineering</category>
      <category>careeradvice</category>
    </item>
    <item>
      <title>How to Use PydanticAI for Multimodal LLMs</title>
      <dc:creator>Stephen Collins</dc:creator>
      <pubDate>Tue, 26 Aug 2025 20:56:00 +0000</pubDate>
      <link>https://forem.com/stephenc222/how-to-use-pydanticai-for-multimodal-llms-2l9p</link>
      <guid>https://forem.com/stephenc222/how-to-use-pydanticai-for-multimodal-llms-2l9p</guid>
      <description>&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%2Fmci6lip5khhn82v96ihv.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%2Fmci6lip5khhn82v96ihv.webp" alt="Blog post cover image" width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Large multimodal models like &lt;strong&gt;Google Gemma 3&lt;/strong&gt; and &lt;strong&gt;Claude Opus 4&lt;/strong&gt; can now reason over text and images. But if you've looked at the docs, it's easy to get lost in agents, tools, and structured outputs before you even get to "Hello, World."&lt;/p&gt;

&lt;p&gt;This post is the &lt;strong&gt;short version&lt;/strong&gt;—how to pass an image (or URL) into a PydanticAI agent in just a few lines.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is the newer, simplified guide from PydanticAI's updated version. For the original comprehensive version with structured outputs, agents, and testing, see my &lt;a href="/posts/how-to-use-pydantic-ai-for-structured-outputs-with-multimodal-llms"&gt;&lt;/a&gt;&lt;a href="https://stephencollins.tech/posts/how-to-use-pydantic-ai-for-structured-outputs-with-multimodal-llms" rel="noopener noreferrer"&gt;complete PydanticAI guide&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;All the code is available on &lt;a href="https://github.com/stephenc222/example-pydantic-ai-multi-modal-updated" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Setup&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you don't already have &lt;a href="https://docs.astral.sh/uv/" rel="noopener noreferrer"&gt;&lt;code&gt;uv&lt;/code&gt;&lt;/a&gt;, install it first (it's a fast Python package manager):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-LsSf&lt;/span&gt; https://astral.sh/uv/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then install PydanticAI and dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv add pydantic-ai python-dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll also need an OpenRouter API key. Sign up at &lt;a href="https://openrouter.ai/" rel="noopener noreferrer"&gt;OpenRouter&lt;/a&gt; and get your API key.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; file:&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;OPENROUTER_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YOUR_OPENROUTER_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you're ready to run the examples.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Comprehensive Image Analysis Example&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here's a complete example that demonstrates both remote and local image handling with PydanticAI:&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
Comprehensive Image Analysis Example
Demonstrates both remote and local image handling with pydantic-ai
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_ai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ImageUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BinaryContent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_ai.models.openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAIModel&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_ai.providers.openrouter&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenRouterProvider&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;

&lt;span class="c1"&gt;# Load environment variables
&lt;/span&gt;&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Initialize the model
&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;OpenAIModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model_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;google/gemma-3-4b-it&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;OpenRouterProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENROUTER_API_KEY&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="c1"&gt;# Create the agent
&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&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;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;analyze_remote_image&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Example: Analyze a remote image using ImageUrl&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=== Remote Image Analysis ===&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_sync&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What company is this logo from?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;ImageUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://iili.io/3Hs4FMg.png&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Remote image analysis result: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;analyze_local_image&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Example: Analyze a local image using BinaryContent&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=== Local Image Analysis ===&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Read local image file
&lt;/span&gt;    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;images/invoice_sample.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;image_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_sync&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What company is this logo from?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;BinaryContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;image_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;media_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image/png&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Local image analysis result: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Run both examples&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Pydantic AI Image Analysis Examples&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Analyze remote image
&lt;/span&gt;    &lt;span class="nf"&gt;analyze_remote_image&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Analyze local image
&lt;/span&gt;    &lt;span class="nf"&gt;analyze_local_image&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Analysis complete!&lt;/span&gt;&lt;span class="sh"&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;__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;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example shows the two main ways to handle images:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Remote images&lt;/strong&gt; using &lt;code&gt;ImageUrl&lt;/code&gt; - perfect for web-hosted images&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local images&lt;/strong&gt; using &lt;code&gt;BinaryContent&lt;/code&gt; - ideal for files on your system&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Key Takeaways&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ImageUrl&lt;/code&gt; → fastest way to use image URLs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BinaryContent&lt;/code&gt; → for local image files or when you want to control upload&lt;/li&gt;
&lt;li&gt;Works across &lt;strong&gt;OpenAI, Anthropic, Google Vertex, OpenRouter&lt;/strong&gt;, and more&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenRouter&lt;/strong&gt; provides access to multiple models through a single API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Gemma 3&lt;/strong&gt; offers excellent image analysis capabilities at competitive pricing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it - no complex agents, no long schemas. Just image input in a few lines of code.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>pydanticai</category>
      <category>multimodalllm</category>
    </item>
    <item>
      <title>intent-kit v0.4.0: Lessons in Reliable, Testable LLM Workflow Engineering</title>
      <dc:creator>Stephen Collins</dc:creator>
      <pubDate>Fri, 25 Jul 2025 14:24:00 +0000</pubDate>
      <link>https://forem.com/stephenc222/intent-kit-v040-lessons-in-reliable-testable-llm-workflow-engineering-4cka</link>
      <guid>https://forem.com/stephenc222/intent-kit-v040-lessons-in-reliable-testable-llm-workflow-engineering-4cka</guid>
      <description>&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%2Fuxf4xpe2djusvxqv6z3n.jpg" 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%2Fuxf4xpe2djusvxqv6z3n.jpg" alt="Blog post cover image" width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;intent-kit v0.4.0: Key Concepts and Lessons from This Release&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The latest &lt;a href="https://github.com/Stephen-Collins-tech/intent-kit" rel="noopener noreferrer"&gt;intent-kit release (v0.4.0)&lt;/a&gt; is a good opportunity to highlight a few patterns and practices that make LLM-driven systems more robust and maintainable. Here’s a look at the most relevant improvements and why they matter if you’re building or maintaining workflow automation around large language models.&lt;/p&gt;




&lt;h3&gt;
  
  
  Environment Variable Support for Configuration
&lt;/h3&gt;

&lt;p&gt;Hard-coding API keys and model settings is a common early mistake. By supporting environment variables for LLM config, intent-kit now encourages a best practice: &lt;strong&gt;separating configuration from code&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Why this matters:&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Makes local/dev/test/prod separation clean and secure
&lt;/li&gt;
&lt;li&gt;Reduces the risk of accidental credential leaks
&lt;/li&gt;
&lt;li&gt;Enables “12-factor app” style deployment, where code is portable and config lives outside the repo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're working in any kind of cloud, CI/CD, or containerized setup, environment variable config is almost always the right move.&lt;/p&gt;




&lt;h3&gt;
  
  
  Performance Monitoring with PerfUtil
&lt;/h3&gt;

&lt;p&gt;If you chain together LLM calls or complex workflows, performance can quickly become unpredictable.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;PerfUtil&lt;/strong&gt; is a small utility added in this release that lets you track how long nodes or actions take to run.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Why it matters:&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pinpoints slow steps or bottlenecks
&lt;/li&gt;
&lt;li&gt;Helps with profiling and optimization
&lt;/li&gt;
&lt;li&gt;Makes it easier to set expectations for user-facing latency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A lesson here: Add performance hooks &lt;em&gt;early&lt;/em&gt;. Even a lightweight timer pays off when things get complex.&lt;/p&gt;




&lt;h3&gt;
  
  
  Stronger Type Safety with NodeType Enum
&lt;/h3&gt;

&lt;p&gt;Workflows built as graphs or trees are prone to “stringly-typed” bugs—where node types are just strings passed around.&lt;br&gt;&lt;br&gt;
By introducing a &lt;strong&gt;NodeType enum&lt;/strong&gt;, intent-kit shifts toward explicit, predictable node handling.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Why this matters:&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduces silent failures from typos or refactoring
&lt;/li&gt;
&lt;li&gt;Lets your IDE catch mistakes
&lt;/li&gt;
&lt;li&gt;Makes the codebase more self-documenting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're designing your own workflow engine, prefer enums (or constants) for node kinds rather than freeform strings.&lt;/p&gt;




&lt;h3&gt;
  
  
  Comprehensive Testing for ActionNode
&lt;/h3&gt;

&lt;p&gt;Automated tests for the core node—&lt;strong&gt;ActionNode&lt;/strong&gt;—were expanded in v0.4.0.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Why this matters:&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tests clarify how the node is supposed to behave
&lt;/li&gt;
&lt;li&gt;New contributors can make changes with confidence
&lt;/li&gt;
&lt;li&gt;Edge cases are less likely to break your system later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lesson: Write tests for the real “workhorse” objects in your system, especially those representing actions or state transitions.&lt;/p&gt;




&lt;h3&gt;
  
  
  Dev Hygiene: Pre-commit Hooks, Coverage, and Cleanups
&lt;/h3&gt;

&lt;p&gt;Little things add up:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pre-commit hooks&lt;/strong&gt; help keep your changelog and versions consistent
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CodeCov integration&lt;/strong&gt; lets everyone see where tests are lacking
&lt;/li&gt;
&lt;li&gt;Cleaning out unused dependencies avoids bitrot&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your project is growing, investing in these practices saves time and confusion for both you and your future contributors.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Intent-kit v0.4.0 bakes in practices that any modern Python/LLM project should consider: secure config, performance hooks, type safety, targeted tests, and dev workflow automation. Each is small on its own, but together they add up to a codebase that's much easier to reason about, extend, and trust.&lt;/p&gt;




&lt;p&gt;Want to see these ideas in action? Check out the code or try running a workflow with the new release.&lt;/p&gt;

&lt;p&gt;Any and all feedback is welcome!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>llms</category>
      <category>workflowautomation</category>
    </item>
    <item>
      <title>Introducing intent-kit: Universal, Deterministic Intent Workflows for Python</title>
      <dc:creator>Stephen Collins</dc:creator>
      <pubDate>Thu, 10 Jul 2025 14:57:00 +0000</pubDate>
      <link>https://forem.com/stephenc222/introducing-intent-kit-universal-deterministic-intent-workflows-for-python-59gg</link>
      <guid>https://forem.com/stephenc222/introducing-intent-kit-universal-deterministic-intent-workflows-for-python-59gg</guid>
      <description>&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%2Fj9ugdl49nyt1bl6ec89r.jpg" 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%2Fj9ugdl49nyt1bl6ec89r.jpg" alt="Blog post cover image" width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you've spent any time trying to build reliable LLM-powered tools, you know the pain:&lt;/p&gt;

&lt;p&gt;Most startups and teams re-invent the wheel every time they want to build a chatbot, automation, or AI-powered workflow that's both smart and deterministic.&lt;/p&gt;

&lt;p&gt;LLMs are powerful-but they're unpredictable, hard to constrain, and most libraries either go "all in" on black-box AI or fall back to brittle, rule-based matching. When you want &lt;strong&gt;composability, reliability, and auditability&lt;/strong&gt; (especially in a product you're delivering to someone else), you're left cobbling together hacks.&lt;br&gt;
That's why I built &lt;strong&gt;intent-kit&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  What is intent-kit?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;intent-kit&lt;/strong&gt; is a universal Python framework for building intent-driven classification and execution systems-chatbots, automation tools, or custom workflow apps-using any combination of rule-based logic, LLMs, or custom classifiers.&lt;br&gt;
It's designed for developers and product teams who need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define all capabilities, constraints, and dependencies up front&lt;/li&gt;
&lt;li&gt;Mix and match classic and AI-driven routing in the same workflow&lt;/li&gt;
&lt;li&gt;Build systems that are &lt;strong&gt;predictable, testable, and production-safe&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Escape both "spaghetti rules" and "LLM guesswork"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;intent-kit&lt;/strong&gt; lets you wire up hierarchical "intent graphs," route inputs through any number of classifiers (from simple keywords to the latest LLMs), and always know exactly what's happening at every step.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why does intent-kit exist?
&lt;/h3&gt;

&lt;p&gt;After seeing team after team run into the same problems-&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Overly complex, one-off intent routers&lt;/li&gt;
&lt;li&gt;Rule-based systems that don't scale&lt;/li&gt;
&lt;li&gt;Messy, untestable parameter extraction&lt;/li&gt;
&lt;li&gt;Product demos that &lt;strong&gt;never&lt;/strong&gt; become maintainable products&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;-I realized there was a missing piece:&lt;br&gt;
A framework that's flexible enough for AI, reliable enough for enterprise, and open-ended enough to be used for any workflow.&lt;br&gt;
Not just another chatbot framework or hacky script, but something composable, deterministic, and actually production-ready.&lt;/p&gt;




&lt;h3&gt;
  
  
  How is intent-kit different?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;intent-kit&lt;/strong&gt; isn't just another "prompt router" or chatbot SDK. It's:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Universal:&lt;/strong&gt;
Works with any classification method-keyword, regex, custom ML, or LLM-zero dependencies by default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic &amp;amp; Composable:&lt;/strong&gt;
You define all valid intents, parameters, and context up front. No surprises, no hallucinations, no "magic."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extensible:&lt;/strong&gt;
Add LLMs only if/when you need them. Plug in your own business logic, APIs, or classifiers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testable &amp;amp; Debuggable:&lt;/strong&gt;
Parameter extraction, routing, and context flow are all inspectable, unit-testable, and production-ready.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production Focused:&lt;/strong&gt;
Built for building tools &lt;em&gt;for other companies&lt;/em&gt;-not just for demos, but for products you need to maintain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most importantly: you're always in control.&lt;br&gt;
&lt;strong&gt;intent-kit&lt;/strong&gt; doesn't do anything you didn't define-so you can deliver products that are both "AI-powered" &lt;em&gt;and&lt;/em&gt; robust enough for real users.&lt;/p&gt;




&lt;h3&gt;
  
  
  Eval API: Test Your Workflows with Real Data
&lt;/h3&gt;

&lt;p&gt;A powerful feature of intent-kit is its built-in &lt;strong&gt;Eval API&lt;/strong&gt;.&lt;br&gt;
You can supply your own datasets-structured as YAML or JSON examples (see &lt;a href="https://github.com/Stephen-Collins-tech/intent-kit/tree/main/examples" rel="noopener noreferrer"&gt;repo examples&lt;/a&gt;)-to automatically test and validate your entire workflow against a wide range of user inputs and edge cases.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No more hand-testing&lt;/strong&gt;: Evaluate how your graph/classifiers perform on real data, not just your best guesses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customize your evals&lt;/strong&gt;: Bring your own domain-specific cases and regression tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actionable reporting&lt;/strong&gt;: Quickly spot gaps, errors, or unintended behaviors &lt;em&gt;before&lt;/em&gt; you ship.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means you can confidently ship deterministic, AI-powered workflows-knowing exactly how your system will respond in the wild.&lt;/p&gt;




&lt;h3&gt;
  
  
  Learn More / Get Started
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Read the docs:&lt;/strong&gt; &lt;a href="https://docs.intentkit.io" rel="noopener noreferrer"&gt;docs.intentkit.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Get the code:&lt;/strong&gt; &lt;a href="https://github.com/Stephen-Collins-tech/intent-kit" rel="noopener noreferrer"&gt;github.com/Stephen-Collins-tech/intent-kit&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;See examples and demos&lt;/strong&gt; in the docs, or try the package locally with:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  pip &lt;span class="nb"&gt;install &lt;/span&gt;intent-kit
  pip &lt;span class="nb"&gt;install &lt;/span&gt;intent-kit[openai] &lt;span class="c"&gt;# to use OpenAI's SDK&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Questions or feedback?&lt;/strong&gt; Open a GitHub issue or DM me on &lt;a href="https://www.linkedin.com/in/stephen-collins-2b8207aa/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Stop gluing together broken workflows.&lt;br&gt;
Start building with intent.&lt;/p&gt;

</description>
      <category>llms</category>
      <category>workflowautomation</category>
      <category>intentclassification</category>
      <category>python</category>
    </item>
    <item>
      <title>Track UI Events and Network Activity in Windows Using Rust + C#</title>
      <dc:creator>Stephen Collins</dc:creator>
      <pubDate>Sun, 13 Apr 2025 15:59:00 +0000</pubDate>
      <link>https://forem.com/stephenc222/track-ui-events-and-network-activity-in-windows-using-rust-c-22i7</link>
      <guid>https://forem.com/stephenc222/track-ui-events-and-network-activity-in-windows-using-rust-c-22i7</guid>
      <description>&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%2F303l2llhplf9gl80vsv6.jpg" 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%2F303l2llhplf9gl80vsv6.jpg" alt="Blog Post Cover Image" width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have you ever clicked a button in a Windows app and wondered &lt;em&gt;exactly&lt;/em&gt; what it triggered? What if you could log not only which button was clicked, but also which network requests that click initiated—down to the TCP connection and PID (process ID)?&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll build a system that does exactly that. It's a two-part project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A C# WinForms app with clickable buttons that send HTTP requests.&lt;/li&gt;
&lt;li&gt;A Rust-based watcher that hooks into Windows APIs to:

&lt;ul&gt;
&lt;li&gt;Capture mouse clicks&lt;/li&gt;
&lt;li&gt;Inspect the UI element clicked&lt;/li&gt;
&lt;li&gt;Track all active TCP connections&lt;/li&gt;
&lt;li&gt;Correlate them with the application responsible&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The full code is available &lt;a href="https://github.com/stephenc222/example-network-ui-event-tracking-windows/tree/main" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;, and this post walks through how it works—and how you can build and run it yourself.&lt;/p&gt;

&lt;p&gt;Want the same thing on macOS? &lt;a href="https://stephencollins.tech/posts/macos-ui-network-monitoring-rust-swiftui" rel="noopener noreferrer"&gt;Here's the macOS version&lt;/a&gt; of this tutorial.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;We'll build a minimal, reproducible testbed for observing real-time interactions between a Windows app's UI and its network behavior. The repository is split into two folders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── ExampleWindowsApp/        # C# .NET WinForms application
├── windows-watcher/          # Rust application using system APIs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The WinForms app has three buttons. Each triggers a GET request to a placeholder API. The Rust watcher runs in the background, logging every mouse click and network connection made by any app—and then filters those logs to focus on the target application.&lt;/p&gt;

&lt;p&gt;This is a useful starting point for learning system programming, building diagnostic tools, or even prototyping lightweight telemetry systems.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: The Example C# Application
&lt;/h2&gt;

&lt;p&gt;Our first component is a WinForms application in .NET 8.0.&lt;/p&gt;

&lt;p&gt;Here's the high-level structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainForm&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Form&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Button&lt;/span&gt; &lt;span class="n"&gt;buttonA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buttonB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buttonC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;TextBox&lt;/span&gt; &lt;span class="n"&gt;responseTextBox&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;MainForm&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ... layout code omitted ...&lt;/span&gt;

        &lt;span class="n"&gt;buttonA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Click&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;FetchTodoAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;buttonB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Click&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;FetchTodoAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;buttonC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Click&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;FetchTodoAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;FetchTodoAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetStringAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;$"https://jsonplaceholder.typicode.com/todos/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;responseTextBox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&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;This app does two things well:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It exposes a basic GUI for user input.&lt;/li&gt;
&lt;li&gt;It generates real HTTP traffic based on button clicks.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This makes it ideal for testing UI-to-network flow.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: UI and Input Monitoring in Rust
&lt;/h2&gt;

&lt;p&gt;Our Rust application uses Win32 APIs to hook into system-wide mouse and keyboard events. When the left mouse button is pressed, it uses &lt;strong&gt;UI Automation (UIA)&lt;/strong&gt; to inspect the UI element under the cursor.&lt;/p&gt;

&lt;p&gt;Here's a simplified breakdown:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;automation&lt;/span&gt;&lt;span class="nf"&gt;.ElementFromPoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;POINT&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="nf"&gt;.CurrentName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;automation_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="nf"&gt;.CurrentAutomationId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="nf"&gt;.CurrentProcessId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;em&gt;visible name&lt;/em&gt; of the button&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;automation ID&lt;/em&gt; (e.g. &lt;code&gt;"ButtonA"&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;PID of the owning application&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From there, we can resolve the process name, and log all of that information to a file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[2025-04-10 15:13:07.775] Element: App='ExampleWindowsApp.exe', Name='Button A', AutomationID='ButtonA'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also listen for the &lt;code&gt;ESC&lt;/code&gt; key or &lt;code&gt;Ctrl+C&lt;/code&gt; combo to gracefully shut down the tool.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Monitoring TCP Connections
&lt;/h2&gt;

&lt;p&gt;In parallel with UI monitoring, the Rust watcher spawns a second thread that polls &lt;code&gt;GetExtendedTcpTable&lt;/code&gt;, a Windows API that returns all active TCP connections along with the owning process ID (PID).&lt;/p&gt;

&lt;p&gt;For each connection, we log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[2025-04-10 15:13:07.882] TCP: 10.0.0.5:56832 → 104.21.64.1:47872, PID=10792, STATE=ESTABLISHED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We filter for only active or connecting sockets (&lt;code&gt;SYN_SENT&lt;/code&gt;, &lt;code&gt;ESTABLISHED&lt;/code&gt;, etc.) and store a deduplicated list of observed connections to avoid re-logging old entries.&lt;/p&gt;

&lt;p&gt;Internally, we map each row from the TCP table to a &lt;code&gt;TcpConnection&lt;/code&gt; struct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;TcpConnection&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IpAddr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;u16&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;remote&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IpAddr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;u16&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&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;Then we write human-readable summaries to the log.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Correlating UI Events with Network Activity
&lt;/h2&gt;

&lt;p&gt;Here's where the two systems come together.&lt;/p&gt;

&lt;p&gt;Every time the user clicks a button in the WinForms app, the Rust tool logs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The exact button clicked&lt;/li&gt;
&lt;li&gt;The owning PID&lt;/li&gt;
&lt;li&gt;Any new TCP connections opened by that PID&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This lets us correlate frontend actions with backend effects—without modifying the application's source code.&lt;/p&gt;

&lt;p&gt;A typical flow might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[15:13:07.775] UI Click: App='ExampleWindowsApp.exe', Name='Button A', AutomationID='ButtonA'
[15:13:07.882] TCP: 10.0.0.5:56832 → 104.21.64.1:47872, PID=10792, STATE=ESTABLISHED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From this, we know that "Button A" initiated a network request, and we can trace its destination.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Running the System
&lt;/h2&gt;

&lt;p&gt;To run the entire setup:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Build and run the C# app
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;ExampleWindowsApp
start ExampleWindowsApp.sln
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build it in Visual Studio, then run it.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Build and run the Rust watcher
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;windows-watcher
cargo run &lt;span class="nt"&gt;--release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The watcher will start logging immediately. Try clicking buttons in the app and observe the log updates.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Log file location
&lt;/h3&gt;

&lt;p&gt;Output logs are stored at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%LOCALAPPDATA%\WindowsWatcher\windows_watcher.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also view a sample session in &lt;a href="https://github.com/stephenc222/example-network-ui-event-tracking-windows/blob/main/windows-watcher/example_output.txt" rel="noopener noreferrer"&gt;&lt;code&gt;example_output.txt&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What You've Learned
&lt;/h2&gt;

&lt;p&gt;This project walks through a powerful debugging pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hook into &lt;strong&gt;user input&lt;/strong&gt; at the system level&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;UIA&lt;/strong&gt; to extract app-specific context&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;network introspection&lt;/strong&gt; to log outgoing connections&lt;/li&gt;
&lt;li&gt;Combine them to create a real-time window into app behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No packet sniffing. No admin access required. Just clean, observable data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Exercises for the Reader (Next Steps)
&lt;/h2&gt;

&lt;p&gt;Want to go deeper? Here are a few exercises to take this further:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Add screenshots&lt;/strong&gt; of the UI element clicked using &lt;code&gt;PrintWindow&lt;/code&gt; or GDI APIs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stream logs remotely&lt;/strong&gt; via WebSocket or HTTP server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor specific ports&lt;/strong&gt; or domains (e.g., blocklist detection).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filter by foreground window only&lt;/strong&gt; to reduce noise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export logs to CSV or SQLite&lt;/strong&gt; for time-series analysis.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Or: port it to macOS. (Spoiler: I already did — &lt;a href="https://stephencollins.tech/posts/macos-ui-network-monitoring-rust-swiftui" rel="noopener noreferrer"&gt;read it here&lt;/a&gt;.)&lt;/p&gt;

</description>
      <category>systemsprogramming</category>
      <category>windowsapi</category>
      <category>rust</category>
      <category>applicationmonitoring</category>
    </item>
    <item>
      <title>Track UI Events and Network Activity in macOS Using Rust + SwiftUI</title>
      <dc:creator>Stephen Collins</dc:creator>
      <pubDate>Sun, 13 Apr 2025 15:54:00 +0000</pubDate>
      <link>https://forem.com/stephenc222/track-ui-events-and-network-activity-in-macos-using-rust-swiftui-3p0b</link>
      <guid>https://forem.com/stephenc222/track-ui-events-and-network-activity-in-macos-using-rust-swiftui-3p0b</guid>
      <description>&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%2F88ok4ug0ty6i8pke4wl2.jpg" 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%2F88ok4ug0ty6i8pke4wl2.jpg" alt="Blog Post Cover Image" width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most macOS apps are black boxes. You click a button, something happens—maybe a network request, maybe some system interaction—but it's hard to know what unless you own the source code.&lt;/p&gt;

&lt;p&gt;So I built a tool to watch.&lt;/p&gt;

&lt;p&gt;This post walks through a hybrid SwiftUI + Rust project that logs &lt;strong&gt;every button click&lt;/strong&gt; and &lt;strong&gt;tracks network traffic&lt;/strong&gt; tied to the clicked app. It combines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A SwiftUI frontend to simulate normal GUI interaction&lt;/li&gt;
&lt;li&gt;A Rust backend using &lt;code&gt;CGEventTap&lt;/code&gt; and the &lt;strong&gt;macOS Accessibility API&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Per-process network inspection using the undocumented but powerful &lt;code&gt;nettop&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No kernel extensions. No root. Just clever usage of system APIs and a bit of FFI.&lt;/p&gt;

&lt;p&gt;The full source code is available &lt;a href="https://github.com/stephenc222/example-network-ui-event-tracking-macos" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why build this?
&lt;/h2&gt;

&lt;p&gt;Because reverse-engineering app behavior shouldn't require a debugger or Wireshark session. I wanted to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Trace which UI elements trigger which network activity&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Understand what apps do in response to input&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Have a working reference for macOS Accessibility APIs in Rust&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Windows version of this tool relied on Win32 hooks and low-level TCP inspection via GetExtendedTcpTable.&lt;br&gt;
&lt;a href="https://stephencollins.tech/posts/windows-ui-network-monitoring-rust-csharp" rel="noopener noreferrer"&gt;You can read that walkthrough here →&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This macOS build takes the same spirit to Apple's ecosystem—same goals, different APIs.&lt;/p&gt;


&lt;h2&gt;
  
  
  Demo: From Click to Packet
&lt;/h2&gt;

&lt;p&gt;When a user clicks &lt;strong&gt;Button A&lt;/strong&gt;, here's what gets logged:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📡 example-mac-app.47727 ↑ 6092 B ↓ 0 B (Δ ↑ 6092 ↓ 0)
[INFO] Button Clicked: App='example-mac-app', PID=47727, ID='ButtonA', Label='Button A'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The app name&lt;/li&gt;
&lt;li&gt;The PID&lt;/li&gt;
&lt;li&gt;The element's accessibility label and identifier&lt;/li&gt;
&lt;li&gt;A delta of bytes sent/received from that process (captured via &lt;code&gt;nettop&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+----------------------+       +-----------------------+
|   SwiftUI Mac App    | ---&amp;gt;  |   Rust macos-watcher  |
| (3 Buttons + Network)|       | (Event Tap + nettop)  |
+----------------------+       +-----------------------+
              ⬑ Accessibility API (AXUIElement)
                        ⬐ CGEventTap mouse/keyboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Frontend: SwiftUI Playground
&lt;/h2&gt;

&lt;p&gt;The Xcode app has a simple interface with three buttons (&lt;code&gt;ButtonA&lt;/code&gt;, &lt;code&gt;ButtonB&lt;/code&gt;, &lt;code&gt;ButtonC&lt;/code&gt;). When clicked, each makes a network request using &lt;code&gt;URLSession&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kt"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Button A"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;fetchProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accessibilityIdentifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ButtonA"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is setting &lt;code&gt;.accessibilityIdentifier()&lt;/code&gt; so we can extract metadata from the Rust process using the Accessibility API.&lt;/p&gt;




&lt;h2&gt;
  
  
  Backend: Rust Input + Network Logger
&lt;/h2&gt;

&lt;p&gt;This is where the real work happens.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔹 Event Hooking
&lt;/h3&gt;

&lt;p&gt;We use &lt;code&gt;CGEventTap&lt;/code&gt; to hook into global input events like left-clicks and key presses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;event_mask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;K_CG_EVENT_LEFT_MOUSE_DOWN&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="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;K_CG_EVENT_KEY_DOWN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;event_tap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;CGEventTapCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_mask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🔹 Accessibility API
&lt;/h3&gt;

&lt;p&gt;On every click, we use &lt;code&gt;AXUIElementCopyElementAtPosition&lt;/code&gt; to resolve which UI element is under the cursor. We extract:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PID of the owning app&lt;/li&gt;
&lt;li&gt;Accessibility identifier (if set)&lt;/li&gt;
&lt;li&gt;Role (e.g. &lt;code&gt;AXButton&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Optional description or label
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ax_ui_element_copy_element_at_position&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;system_wide_element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;element_ref&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ax_ui_element_get_pid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element_ref&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ax_ui_element_copy_attribute_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element_ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;K_AX_ROLE_ATTRIBUTE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🔹 Network Activity via &lt;code&gt;nettop&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;After identifying the UI element and its app PID, we shell out to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nettop &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="nt"&gt;-J&lt;/span&gt; bytes_in,bytes_out &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We parse the output for lines matching the PID (e.g. &lt;code&gt;example-mac-app.47727&lt;/code&gt;), and log any change in byte counts. This gives us a primitive—but real—view into per-process network activity.&lt;/p&gt;




&lt;h2&gt;
  
  
  Log Output Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📡 example-mac-app.47727 ↑ 6092 B ↓ 0 B (Δ ↑ 6092 ↓ 0)
[INFO] Button Clicked: App='example-mac-app', PID=47727, ID='ButtonA', Label='Button A'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each input event is logged with as much context as we can scrape. You can see which button triggered the traffic, and how much was sent/received.&lt;/p&gt;




&lt;h2&gt;
  
  
  Installation &amp;amp; Permissions
&lt;/h2&gt;

&lt;p&gt;To run the watcher, build the Rust CLI and grant it accessibility permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--proto&lt;/span&gt; &lt;span class="s1"&gt;'=https'&lt;/span&gt; &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 &lt;span class="nt"&gt;-sSf&lt;/span&gt; https://sh.rustup.rs | sh
&lt;span class="nb"&gt;cd &lt;/span&gt;macos-watcher
cargo build &lt;span class="nt"&gt;--release&lt;/span&gt;
./target/release/macos-watcher
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;macOS will prompt you to allow the binary under:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;System Settings → Privacy &amp;amp; Security → Accessibility&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once granted, you'll see logs at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/macos_watcher.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also run it persistently in the background using the included &lt;code&gt;.plist&lt;/code&gt; and &lt;code&gt;install_daemon.sh&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dev Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Built using &lt;a href="https://docs.rs/core-foundation" rel="noopener noreferrer"&gt;core-foundation&lt;/a&gt;, &lt;a href="https://docs.rs/objc" rel="noopener noreferrer"&gt;objc&lt;/a&gt;, and &lt;code&gt;simplelog&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;All UI inspection is done using C-level bindings to &lt;code&gt;AXUIElement*&lt;/code&gt; APIs&lt;/li&gt;
&lt;li&gt;There's no NSAccessibility or AppKit dependency in the Rust half&lt;/li&gt;
&lt;li&gt;SwiftUI code is sandboxed and declarative. No extra work needed to simulate real-world interaction&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt; - See if random apps are sending packets when buttons are clicked&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing&lt;/strong&gt; - Ensure your app only hits the network when expected&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debugging&lt;/strong&gt; - Tie UI behavior to system-level effects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Education&lt;/strong&gt; - Learn how accessibility and input work on macOS&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Requires GUI permission to run (can't be run headless)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nettop&lt;/code&gt; output is undocumented and may change&lt;/li&gt;
&lt;li&gt;Packet contents aren't inspected—this is about attribution, not introspection&lt;/li&gt;
&lt;li&gt;Only tracks visible, clickable elements (not gestures, background jobs, etc.)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Cross-Platform Parity
&lt;/h2&gt;

&lt;p&gt;We now have both Windows and macOS versions of this tool, each using the platform's native APIs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Windows&lt;/th&gt;
&lt;th&gt;macOS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Input Hooking&lt;/td&gt;
&lt;td&gt;Win32 Low-Level Hooks&lt;/td&gt;
&lt;td&gt;CGEventTap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI Element Metadata&lt;/td&gt;
&lt;td&gt;UIAutomation / IAccessible&lt;/td&gt;
&lt;td&gt;AXUIElement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network Tracking&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GetExtendedTcpTable&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nettop&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Language&lt;/td&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;Rust + SwiftUI&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Future Work
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Add &lt;strong&gt;packet inspection&lt;/strong&gt; (via &lt;code&gt;libpcap&lt;/code&gt;) for HTTP tracing&lt;/li&gt;
&lt;li&gt;Tag UI actions with timestamps to correlate more precisely with network&lt;/li&gt;
&lt;li&gt;Export events in JSON or send to external observability tools&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;This project taught me a lot about how macOS input and accessibility work under the hood. The fact that you can hook into system-wide mouse events, pull out UI metadata, and match it to network traffic—all with a user-space Rust binary—is kind of wild.&lt;/p&gt;

&lt;p&gt;If you want to analyze how apps behave without reverse-engineering them from scratch, this tool gives you a solid start.&lt;/p&gt;

&lt;p&gt;Code is available &lt;a href="https://github.com/stephenc222/example-network-ui-event-tracking-macos" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>systemsprogramming</category>
      <category>macosapi</category>
      <category>rust</category>
      <category>applicationmonitoring</category>
    </item>
    <item>
      <title>How sqlite-vec Works for Storing and Querying Vector Embeddings</title>
      <dc:creator>Stephen Collins</dc:creator>
      <pubDate>Mon, 31 Mar 2025 21:07:00 +0000</pubDate>
      <link>https://forem.com/stephenc222/how-sqlite-vec-works-for-storing-and-querying-vector-embeddings-2g9b</link>
      <guid>https://forem.com/stephenc222/how-sqlite-vec-works-for-storing-and-querying-vector-embeddings-2g9b</guid>
      <description>&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%2Fcd7gaxbdabjpgoubv5b4.jpg" 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%2Fcd7gaxbdabjpgoubv5b4.jpg" alt="Blog Post Cover Image" width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vector search has become a foundational tool for modern applications — from powering recommendation engines to enabling semantic search in LLM pipelines. Traditionally, developers reached for dedicated vector databases like FAISS, Annoy, or Pinecone. But what if you could bring vector search directly into your favorite embedded database?&lt;/p&gt;

&lt;p&gt;Enter &lt;a href="https://github.com/asg017/sqlite-vec" rel="noopener noreferrer"&gt;&lt;code&gt;sqlite-vec&lt;/code&gt;&lt;/a&gt;: a powerful SQLite extension that lets you store, manipulate, and query vector data — right inside SQLite. It brings K-Nearest Neighbor (KNN) search, multiple distance metrics, and SIMD-accelerated performance into a portable, dependency-free package.&lt;/p&gt;

&lt;p&gt;In this post, I'll explore how &lt;code&gt;sqlite-vec&lt;/code&gt; enables efficient vector search, its supported formats and distance functions, and how it can be integrated into AI or semantic search pipelines without leaving the SQLite ecosystem.&lt;/p&gt;




&lt;p&gt;👉 New to &lt;code&gt;sqlite-vec&lt;/code&gt;? Check out &lt;a href="https://dev.to/posts/how-to-use-sqlite-vec-to-store-and-query-vector-embeddings"&gt;this step-by-step tutorial&lt;/a&gt; where I walk through how to store real embeddings and run semantic search using Node.js.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is &lt;code&gt;sqlite-vec&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;sqlite-vec&lt;/code&gt; extends SQLite with native vector support. It introduces a new vector data type and adds a suite of functions for working with vectors. Think of it as embedding a minimal vector database engine directly into your local &lt;code&gt;.db&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;You get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Native vector types: &lt;code&gt;float32&lt;/code&gt;, &lt;code&gt;int8&lt;/code&gt;, and &lt;code&gt;bit&lt;/code&gt; (binary vectors)&lt;/li&gt;
&lt;li&gt;Distance metrics: L2 (&lt;a href="https://en.wikipedia.org/wiki/Euclidean_distance" rel="noopener noreferrer"&gt;Euclidean&lt;/a&gt;), L1 (&lt;a href="https://en.wikipedia.org/wiki/Taxicab_geometry" rel="noopener noreferrer"&gt;Manhattan&lt;/a&gt;), &lt;a href="https://en.wikipedia.org/wiki/Cosine_similarity" rel="noopener noreferrer"&gt;cosine similarity&lt;/a&gt;, and &lt;a href="https://en.wikipedia.org/wiki/Hamming_distance" rel="noopener noreferrer"&gt;Hamming&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SQL functions for manipulating vectors&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm" rel="noopener noreferrer"&gt;KNN search&lt;/a&gt; via virtual tables&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/SIMD" rel="noopener noreferrer"&gt;SIMD&lt;/a&gt; acceleration with AVX and NEON&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes &lt;code&gt;sqlite-vec&lt;/code&gt; ideal for use cases like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Embedding-based semantic search&lt;/li&gt;
&lt;li&gt;Local AI-powered search engines&lt;/li&gt;
&lt;li&gt;Lightweight ML applications&lt;/li&gt;
&lt;li&gt;Offline recommender systems&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Core Feature: Vector Search
&lt;/h2&gt;

&lt;p&gt;At its heart, &lt;code&gt;sqlite-vec&lt;/code&gt; enables fast vector similarity search directly in SQLite. This is accomplished through three main pieces:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Vector Storage and Types&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Vectors can be stored as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;float32&lt;/code&gt; blobs (&lt;code&gt;REAL[]&lt;/code&gt;-like arrays)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;int8&lt;/code&gt; blobs for quantized vectors (smaller footprint)&lt;/li&gt;
&lt;li&gt;Bit vectors (&lt;code&gt;bit[]&lt;/code&gt;) for binary operations and Hamming distance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each vector column tracks its type using SQLite's "subtype" feature, a low-level tagging system that &lt;code&gt;sqlite-vec&lt;/code&gt; uses to enforce type safety. For example, if you try to compare a &lt;code&gt;float32&lt;/code&gt; vector with an &lt;code&gt;int8&lt;/code&gt; vector, you'll get a helpful error — not undefined behavior.&lt;/p&gt;

&lt;p&gt;You can ingest vectors using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JSON arrays (&lt;code&gt;[0.1, 0.3, 0.5]&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Raw blobs (&lt;code&gt;BLOB&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;SQL functions like &lt;code&gt;vec_f32()&lt;/code&gt; and &lt;code&gt;vec_int8()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Behind the scenes, &lt;code&gt;sqlite-vec&lt;/code&gt; performs custom JSON parsing with character-level control — it doesn't rely on SQLite's JSON functions. If your input is malformed (e.g., missing brackets or a non-numeric value), the parser returns clear SQL errors like &lt;code&gt;"invalid JSON array"&lt;/code&gt; or &lt;code&gt;"NaN is not allowed"&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Distance Metrics&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The magic of vector search lies in distance calculations. &lt;code&gt;sqlite-vec&lt;/code&gt; provides fast, SIMD-accelerated implementations of key metrics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;L2 (Euclidean Distance)&lt;/strong&gt;
For &lt;code&gt;float32&lt;/code&gt; and &lt;code&gt;int8&lt;/code&gt;, with AVX/NEON support.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;vec_distance_l2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;L1 (Manhattan Distance)&lt;/strong&gt;
Fast for quantized &lt;code&gt;int8&lt;/code&gt; vectors and compact float vectors.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;vec_distance_l1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cosine Similarity&lt;/strong&gt;
Normalize vectors and compute angular similarity.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;vec_distance_cosine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hamming Distance&lt;/strong&gt;
Only available for &lt;code&gt;bit&lt;/code&gt; vectors.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;vec_distance_hamming&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each function dynamically dispatches the correct implementation based on the vector type and dimension — which are validated before the calculation begins. This enforcement prevents accidental misuse and guarantees consistent results.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;K-Nearest Neighbor Search (KNN)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The core of vector search is KNN — finding the &lt;code&gt;k&lt;/code&gt; closest vectors to a query vector. &lt;code&gt;sqlite-vec&lt;/code&gt; provides this via virtual tables (&lt;code&gt;vec0_vtab&lt;/code&gt;), which support SQL queries like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;my_vectors&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt; &lt;span class="k"&gt;MATCH&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This scans the &lt;code&gt;vector&lt;/code&gt; column, computes distances to the query vector, and returns the 5 nearest rows, sorted by distance.&lt;/p&gt;

&lt;p&gt;The implementation uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Memory-efficient chunked vector storage&lt;/li&gt;
&lt;li&gt;Indexed rowid buffers for filtering and sorting&lt;/li&gt;
&lt;li&gt;Optimized metric dispatch (L2, cosine, etc.)&lt;/li&gt;
&lt;li&gt;Optional metadata filters (&lt;code&gt;WHERE label = 'cat'&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Real World Example: Semantic Search
&lt;/h2&gt;

&lt;p&gt;Let's say you're building a local semantic search tool. First, you embed text into vectors using OpenAI, Mistral, or a local model. Then you store the vectors in SQLite:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;VIRTUAL&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;embeddings&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;vec0&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt; &lt;span class="k"&gt;ANY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;embeddings&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'doc1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vec_f32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'[0.01, 0.42, 0.5, ...]'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'doc2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vec_f32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'[0.12, 0.21, 0.3, ...]'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run semantic search like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vec_distance_cosine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vec_f32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'[0.05, 0.41, 0.49, ...]'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;embeddings&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use KNN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;embeddings&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt; &lt;span class="k"&gt;MATCH&lt;/span&gt; &lt;span class="n"&gt;vec_f32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'[...]'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of this runs in embedded SQLite — no server, no dependencies.&lt;/p&gt;




&lt;h2&gt;
  
  
  Performance Under the Hood
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;sqlite-vec&lt;/code&gt; uses smart low-level tricks to stay fast:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SIMD&lt;/strong&gt;: AVX (x86) and NEON (ARM) intrinsics for L2/L1&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chunked Storage&lt;/strong&gt;: Vectors are grouped into chunks, reducing memory fragmentation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bitmaps&lt;/strong&gt;: Validity and metadata filters are bitmask-accelerated&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Safety&lt;/strong&gt;: Cleanup functions ensure no leaks in vector operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, the AVX-accelerated L2 distance routine processes 16 float32 elements per loop using &lt;code&gt;_mm256_loadu_ps&lt;/code&gt;, &lt;code&gt;_mm256_sub_ps&lt;/code&gt;, and &lt;code&gt;_mm256_mul_ps&lt;/code&gt;. Final results are summed and square-rooted for the true Euclidean distance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Other Goodies
&lt;/h2&gt;

&lt;p&gt;Beyond vector search, &lt;code&gt;sqlite-vec&lt;/code&gt; gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vector math&lt;/strong&gt;: Add, subtract, slice, normalize
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;vec_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vec1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vec2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;vec_normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vec1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type conversion&lt;/strong&gt;: Quantize float32 → int8 or binary (bit)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;vec_quantize_int8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vec_f32&lt;/span&gt;&lt;span class="p"&gt;(...),&lt;/span&gt; &lt;span class="s1"&gt;'unit'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;vec_quantize_binary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vec_f32&lt;/span&gt;&lt;span class="p"&gt;(...))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful when you want smaller vector footprints — e.g., for mobile apps or when storing millions of vectors. The &lt;code&gt;'unit'&lt;/code&gt; option scales vectors to the [-1, 1] range before quantization, preserving cosine relationships.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JSON I/O&lt;/strong&gt;: Convert vectors to/from JSON for logging and debugging
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;vec_to_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Metadata filtering&lt;/strong&gt;: Attach extra columns like tags, timestamps, or labels and filter before distance is computed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Helpful Errors&lt;/strong&gt;: If you pass mismatched types, malformed JSON, or vectors of different dimensions, &lt;code&gt;sqlite-vec&lt;/code&gt; will return readable SQLite-native error messages — not just crash or return null.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Advanced Architecture Highlights
&lt;/h2&gt;

&lt;p&gt;While &lt;code&gt;sqlite-vec&lt;/code&gt; feels simple to use at the SQL level, under the hood it's a robust system engineered for performance and modularity. Here are a few of the architectural systems that make it tick:&lt;/p&gt;

&lt;h3&gt;
  
  
  Virtual Table Engine (&lt;code&gt;vec0_vtab&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;All vector storage is handled via a custom SQLite virtual table module called &lt;code&gt;vec0_vtab&lt;/code&gt;. This acts like a dynamic table interface that manages vector columns, metadata, partitions, and even auxiliary fields. It controls everything from row layout to how queries are planned and executed. When you write &lt;code&gt;SELECT ... FROM vec0(...)&lt;/code&gt;, you're using this engine.&lt;/p&gt;

&lt;p&gt;It also enables dynamic query plans — like switching between full scans, point queries, or optimized KNN searches — based on what the SQL planner sees.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chunk-Based Vector Storage
&lt;/h3&gt;

&lt;p&gt;Vectors aren't just stored in rows — they're stored in &lt;em&gt;chunks&lt;/em&gt;. These are memory-efficient, fixed-size blocks that group vectors by partition key or table layout. Chunking improves locality, reduces memory fragmentation, and allows fine-grained row tracking with bitmaps.&lt;/p&gt;

&lt;p&gt;If you insert a new vector, it's placed into an available chunk; if a vector is deleted, the bitmap marks its slot invalid. This system scales surprisingly well and supports in-place updates and fast scans.&lt;/p&gt;

&lt;h3&gt;
  
  
  Metadata Filtering
&lt;/h3&gt;

&lt;p&gt;Each table can include custom metadata columns (e.g., &lt;code&gt;label&lt;/code&gt;, &lt;code&gt;timestamp&lt;/code&gt;, &lt;code&gt;language&lt;/code&gt;). These are indexed internally and can be used to filter KNN results &lt;em&gt;before&lt;/em&gt; distance calculations happen — saving time and compute.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;embeddings&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'cat'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt; &lt;span class="k"&gt;MATCH&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the hood, a metadata filter bitmap intersects with the chunk validity bitmap to skip irrelevant rows early — like having a WHERE clause that's KNN-aware.&lt;/p&gt;

&lt;h3&gt;
  
  
  Transaction Handling and Consistency
&lt;/h3&gt;

&lt;p&gt;The entire system is designed to respect SQLite's transactional semantics. Vector inserts, updates, and deletes are atomic and journaled properly. Virtual table methods hook into SQLite's transaction lifecycle (&lt;code&gt;xBegin&lt;/code&gt;, &lt;code&gt;xSync&lt;/code&gt;, &lt;code&gt;xRollback&lt;/code&gt;, &lt;code&gt;xCommit&lt;/code&gt;), ensuring consistency across both vector chunks and metadata.&lt;/p&gt;

&lt;p&gt;This means you can use &lt;code&gt;sqlite-vec&lt;/code&gt; safely in write-heavy or multi-threaded environments — rollbacks and savepoints just work.&lt;/p&gt;




&lt;h2&gt;
  
  
  When to Use This
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;sqlite-vec&lt;/code&gt; is perfect when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want lightweight, embedded vector search&lt;/li&gt;
&lt;li&gt;You already use SQLite and want to avoid external dependencies&lt;/li&gt;
&lt;li&gt;You're building AI-powered apps on the edge or mobile&lt;/li&gt;
&lt;li&gt;You want reproducible, portable vector pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It may &lt;em&gt;not&lt;/em&gt; be ideal for large-scale distributed search across millions of high-dimensional vectors — but for local, embedded, or small-medium applications, it's shockingly capable.&lt;/p&gt;




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

&lt;p&gt;With &lt;code&gt;sqlite-vec&lt;/code&gt;, vector search becomes a SQL-native feature. You can run ANN-style queries, compute distances, manipulate vectors, and serialize them — all using familiar SQL. It's fast, flexible, and portable.&lt;/p&gt;

&lt;p&gt;From embedded AI to offline semantic search to quantized mobile experiences, &lt;code&gt;sqlite-vec&lt;/code&gt; unlocks a whole new layer of vector intelligence in SQLite. If you're building search interfaces, AI notebooks, or on-device intelligence, give it a spin — no external services required.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>sqlite</category>
      <category>vectorsearch</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>How to use sqlite-vec to store and query vector embeddings</title>
      <dc:creator>Stephen Collins</dc:creator>
      <pubDate>Wed, 05 Mar 2025 16:20:00 +0000</pubDate>
      <link>https://forem.com/stephenc222/how-to-use-sqlite-vec-to-store-and-query-vector-embeddings-58mf</link>
      <guid>https://forem.com/stephenc222/how-to-use-sqlite-vec-to-store-and-query-vector-embeddings-58mf</guid>
      <description>&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%2Fs5579lxqk64of04n8nzr.jpg" 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%2Fs5579lxqk64of04n8nzr.jpg" alt="Blog Post Cover Image" width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vector search is a game-changer for LLM-based applications, but what if you could do it without setting up a dedicated vector database?&lt;/p&gt;

&lt;p&gt;With &lt;a href="https://github.com/asg017/sqlite-vec" rel="noopener noreferrer"&gt;&lt;code&gt;sqlite-vec&lt;/code&gt;&lt;/a&gt; (the successor to &lt;a href="https://github.com/asg017/sqlite-vss" rel="noopener noreferrer"&gt;&lt;code&gt;sqlite-vss&lt;/code&gt;&lt;/a&gt;), you can bring powerful vector search directly into SQLite, eliminating the need for additional infrastructure like Pinecone, Weaviate, or FAISS. This means simpler deployments, fewer dependencies, and all the power of AI-driven search inside a lightweight, file-based database.&lt;/p&gt;

&lt;p&gt;In this post, I'll show you how we can use &lt;code&gt;sqlite-vec&lt;/code&gt; to store and query vector embeddings inside SQLite, and walk you through a practical example: an AI-powered job matching system that stores and searches embeddings inside SQLite.&lt;/p&gt;

&lt;p&gt;👉 If you just want the code, check out my GitHub repository &lt;a href="https://github.com/stephenc222/example-sqlite-vec-tutorial" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Otherwise, let's get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use SQLite for Vector Search?
&lt;/h2&gt;

&lt;p&gt;Many developers default to dedicated vector databases for semantic search, but SQLite is a surprisingly strong alternative—especially for small to mid-scale applications. Here's why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity&lt;/strong&gt; - No extra services, infrastructure, or cloud dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Efficiency&lt;/strong&gt; - Embedded, fast, and optimized for local and lightweight use cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt; - Handles tens of thousands of embeddings efficiently on a single machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt; - &lt;code&gt;sqlite-vec&lt;/code&gt; extends SQLite to support nearest neighbor search just like dedicated vector databases.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With &lt;code&gt;sqlite-vec&lt;/code&gt;, you get the best of both worlds: the simplicity of SQLite combined with efficient AI-powered search—all without leaving SQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview of the Example Project
&lt;/h2&gt;

&lt;p&gt;The example project demonstrates how to use &lt;code&gt;sqlite-vec&lt;/code&gt; and &lt;a href="https://www.npmjs.com/package/@xenova/transformers" rel="noopener noreferrer"&gt;Xenova Transformers&lt;/a&gt; (which can run in the browser or Node.js) to generate embeddings for resumes and job descriptions, and then use those embeddings to perform similarity searches to match candidates with job postings via cosine similarity. &lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Embeddings Generation&lt;/strong&gt;: Uses the &lt;code&gt;Xenova/gte-base&lt;/code&gt; model to convert text into vector embeddings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database Setup&lt;/strong&gt;: Initializes an SQLite database with &lt;code&gt;sqlite-vec&lt;/code&gt; to store and query embeddings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Job Matching&lt;/strong&gt;: Performs cosine similarity searches to find the best job matches for resumes and vice versa.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic Enhancements&lt;/strong&gt;: Incorporates structured metadata, job title variations, and industry context to improve match accuracy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Demonstrations&lt;/strong&gt;: Shows high-similarity matches (90%+ cosine similarity) and negative examples to verify correct filtering.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting Up the Project
&lt;/h2&gt;

&lt;p&gt;Before we jump into code, let's make sure you have everything set up.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 1: Install Node.js 22.x&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you don't already have Node.js 22.x, install it using &lt;a href="https://github.com/nvm-sh/nvm" rel="noopener noreferrer"&gt;nvm&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nvm &lt;span class="nb"&gt;install &lt;/span&gt;22
nvm use 22
nvm &lt;span class="nb"&gt;alias &lt;/span&gt;default 22
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Step 2: Clone the Repository&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Grab the example project from GitHub:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone git@github.com:stephenc222/example-sqlite-vec-tutorial.git
&lt;span class="nb"&gt;cd &lt;/span&gt;example-sqlite-vec-tutorial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Step 3: Install Dependencies&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you're ready to start working with &lt;code&gt;sqlite-vec&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating Embeddings with Xenova Transformers
&lt;/h2&gt;

&lt;p&gt;This blog post's tutorial project uses the &lt;code&gt;Xenova/gte-base&lt;/code&gt; model (which is based on the &lt;a href="https://huggingface.co/thenlper/gte-base" rel="noopener noreferrer"&gt;thelnper/gte-base&lt;/a&gt; model with ONNX weights, more information down below) to create vector embeddings. The following function initializes the model:&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;pipeline&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;@xenova/transformers&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;embedder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&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;setupEmbeddings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;embedder&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;pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;feature-extraction&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="s2"&gt;Xenova/gte-base&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;NOTE: A production-grade implementation could use the &lt;a href="https://en.wikipedia.org/wiki/Singleton_pattern" rel="noopener noreferrer"&gt;singleton pattern&lt;/a&gt; to ensure the model is only initialized once, and protect access to the model instance.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To create an embedding from text:&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&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="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;embedder&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;Embedding model not initialized&lt;/span&gt;&lt;span class="dl"&gt;"&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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;embedder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// The `pooling` option specifies the method used to aggregate the token embeddings into a single vector.&lt;/span&gt;
    &lt;span class="na"&gt;pooling&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mean&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// The `normalize` option scales the embeddings to have unit length.&lt;/span&gt;
    &lt;span class="na"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nx"&gt;data&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;h2&gt;
  
  
  Storing and Querying Embeddings in SQLite
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Database Schema
&lt;/h3&gt;

&lt;p&gt;We define tables for resumes and jobs, each with an embedding column:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;resumes&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="n"&gt;AUTOINCREMENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;candidate_name&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;seniority&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;skills&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;industry&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;resume_text&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="nb"&gt;BLOB&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;VIRTUAL&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;vss_resumes&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;vec0&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;jobs&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="n"&gt;AUTOINCREMENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;job_title&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;seniority&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;required_skills&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;industry&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;job_description&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="nb"&gt;BLOB&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;VIRTUAL&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;vss_jobs&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;vec0&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Serializing and Storing Embeddings
&lt;/h3&gt;

&lt;p&gt;Since embeddings are arrays of floats, we serialize them into buffers for storage:&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;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;serializeEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Float32Array&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Buffer&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;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for &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;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&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="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFloatLE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&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;buffer&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;deserializeEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Float32Array&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;new&lt;/span&gt; &lt;span class="nc"&gt;Float32Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for &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;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&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="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFloatLE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&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;result&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;
  
  
  &lt;strong&gt;Inserting and Updating Vector Embeddings&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When adding resumes and job listings to the database, we enrich the text before embedding it. This improves the &lt;strong&gt;semantic relevance&lt;/strong&gt; of search queries.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Storing Resume Embeddings&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Each resume is structured with key details before generating its embedding.&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;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;createOrUpdateResumeEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;candidate_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;resume_text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;seniority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;skills&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="nx"&gt;industry&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Check if the resume already exists&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;existingResume&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT id FROM resumes WHERE candidate_name = ?&lt;/span&gt;&lt;span class="dl"&gt;"&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="nx"&gt;candidate_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Format the resume with structured information&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formattedResume&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
  Candidate Profile: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;candidate_name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
  Experience Level: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;seniority&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
  Core Skills: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;skills&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;
  Industry Experience: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;industry&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;

  Summary:
  &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;resume_text&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;// Generate embedding from the structured resume text&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;embedding&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;createResumeEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formattedResume&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="nx"&gt;existingResume&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Update existing resume&lt;/span&gt;
    &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
      UPDATE resumes 
      SET embedding = ?, seniority = ?, skills = ?, industry = ?, resume_text = ?
      WHERE candidate_name = ?
    `&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;seniority&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;skills&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;industry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resume_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;candidate_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Update vector search index&lt;/span&gt;
    &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DELETE FROM vss_resumes WHERE rowid = ?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existingResume&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;INSERT INTO vss_resumes (rowid, embedding) VALUES (?, ?)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existingResume&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Updated resume: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;candidate_name&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Insert new resume&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
      INSERT INTO resumes (candidate_name, seniority, skills, industry, resume_text, embedding)
      VALUES (?, ?, ?, ?, ?, ?)
    `&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;candidate_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;seniority&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;skills&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;industry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resume_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;INSERT INTO vss_resumes (rowid, embedding) VALUES (?, ?)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastInsertRowid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Added resume: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;candidate_name&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Storing Job Embeddings&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Like resumes, job descriptions are formatted before embedding.&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;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;createOrUpdateJobEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;job_title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;job_description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;seniority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;required_skills&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="nx"&gt;industry&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Select the top 3 most relevant skills&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;primarySkills&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;required_skills&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Check if the job listing already exists&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;existingJob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT id FROM jobs WHERE job_title = ?&lt;/span&gt;&lt;span class="dl"&gt;"&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="nx"&gt;job_title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Format job details into a structured description&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formattedJobDescription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
  JOB LISTING: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;job_title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
  Experience Level: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;seniority&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
  Industry: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;industry&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;

  Required Skills: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;primarySkills&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;
  Job Description:
  &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;job_description&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;// Generate embedding&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;embedding&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;createJobEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;job_title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;formattedJobDescription&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="nx"&gt;existingJob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Update existing job&lt;/span&gt;
    &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
      UPDATE jobs 
      SET embedding = ?, seniority = ?, required_skills = ?, industry = ?, job_description = ?
      WHERE job_title = ?
    `&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;seniority&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;required_skills&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;industry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;job_description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;job_title&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Update vector search index&lt;/span&gt;
    &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DELETE FROM vss_jobs WHERE rowid = ?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existingJob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;INSERT INTO vss_jobs (rowid, embedding) VALUES (?, ?)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existingJob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Updated job listing: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;job_title&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Insert new job&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
      INSERT INTO jobs (job_title, seniority, required_skills, industry, job_description, embedding)
      VALUES (?, ?, ?, ?, ?, ?)
    `&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;job_title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;seniority&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;required_skills&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;industry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;job_description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;INSERT INTO vss_jobs (rowid, embedding) VALUES (?, ?)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastInsertRowid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Added job listing: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;job_title&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="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;
  
  
  &lt;strong&gt;Key Takeaways&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pre-formatting text improves search accuracy.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Updates replace outdated embeddings seamlessly.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;sqlite-vec&lt;/code&gt; extension enables fast similarity queries.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This structure ensures &lt;strong&gt;efficient AI-powered job matching&lt;/strong&gt;, making SQLite a viable option for vector search.&lt;/p&gt;

&lt;h3&gt;
  
  
  Querying for Similar Matches
&lt;/h3&gt;

&lt;p&gt;To find the top-matching resumes for a job:&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;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;findMatchingResumes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;job_title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&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="nl"&gt;similarity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT id, embedding FROM jobs WHERE job_title = ?&lt;/span&gt;&lt;span class="dl"&gt;"&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="nx"&gt;job_title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;embedding&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="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&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;job&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;similarResumes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
    SELECT rowid, distance FROM vss_resumes WHERE embedding MATCH ? ORDER BY distance LIMIT ?
  `&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;rowid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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;similarResumes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;rowid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;distance&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;resume&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT candidate_name FROM resumes WHERE id = ?&lt;/span&gt;&lt;span class="dl"&gt;"&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="nx"&gt;rowid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;candidate_name&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidate_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;similarity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;distance&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// Convert distance to similarity&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 find the top-matching jobs for a resume:&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;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;findMatchingJobs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;candidate_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&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="nl"&gt;similarity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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;resume&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT id, skills, embedding FROM resumes WHERE candidate_name = ?&lt;/span&gt;&lt;span class="dl"&gt;"&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="nx"&gt;candidate_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;skills&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="nl"&gt;embedding&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="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&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;resume&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;similarJobs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
    SELECT rowid, distance FROM vss_jobs WHERE embedding MATCH ? ORDER BY distance LIMIT ?
  `&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;rowid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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;jobMatches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;similarJobs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;rowid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;distance&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;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT job_title, required_skills FROM jobs WHERE id = ?&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rowid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;job_title&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="nl"&gt;required_skills&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseSimilarity&lt;/span&gt; &lt;span class="o"&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;max&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="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Clamp between 0-1&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;skillOverlap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;skills&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;skill&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;required_skills&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;skill&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;length&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="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;job_title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;similarity&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;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;baseSimilarity&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;skillOverlap&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// Ensure it never exceeds 1&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;jobMatches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;similarity&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;similarity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Sort by highest similarity&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Improving Semantic Matching
&lt;/h2&gt;

&lt;p&gt;To improve match accuracy, we enhance embeddings with:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Industry Context&lt;/strong&gt;: Broadens potential matches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skill Weighting&lt;/strong&gt;: Prioritizes core competencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Experience Weighting&lt;/strong&gt;: Prioritizes experience over education.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In our example project, we create an embedding for an ambitious resume for a perfect match for a senior software engineer position:&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;await&lt;/span&gt; &lt;span class="nf"&gt;createOrUpdateResumeEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resume-perfect-match-1&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="s2"&gt;Senior software engineer with 8+ years of full-stack development experience, specializing in TypeScript, React, Node.js, and AWS. Led development of scalable web applications serving millions of users. Strong expertise in CI/CD pipelines, clean architecture, and performance optimization. Proven ability to mentor junior developers and implement modern front-end and back-end best practices.&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="s2"&gt;Senior&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TypeScript&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="s2"&gt;React&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="s2"&gt;Node.js&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="s2"&gt;AWS&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="s2"&gt;CI/CD&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="s2"&gt;Full-Stack Development&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="s2"&gt;Technology&lt;/span&gt;&lt;span class="dl"&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 we create an embedding for a deliberately mismatched resume, with no experience in software development for a senior software engineer position:&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;await&lt;/span&gt; &lt;span class="nf"&gt;createOrUpdateResumeEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resume-105&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="s2"&gt;pastry chef with no experience in creating gourmet desserts and pastries. Doesn't know anything about French patisserie techniques, chocolate tempering, and sugar art. Doesn't know anything about pastry. Doesn't know anything about culinary arts. Doesn't know anything about desserts. Doesn't know anything about pastries.&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="s2"&gt;No Experience&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Culinary Arts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running the Job Matching Demo
&lt;/h2&gt;

&lt;p&gt;To see the system in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Insert sample resume and job embeddings into the sqlite database.&lt;/li&gt;
&lt;li&gt;Run similarity searches through the &lt;code&gt;sqlite-vec&lt;/code&gt; extension.&lt;/li&gt;
&lt;li&gt;Output results with similarity scores with a "star" emoji rating.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Example Output
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✅ Matches for job-perfect-match-1:
1. John Doe (Similarity: 92%) - ⭐⭐⭐ PERFECT MATCH
2. Jane Smith (Similarity: 85%) - ⭐⭐ EXCELLENT MATCH
3. Bob Johnson (Similarity: 78%) - ⭐ GOOD MATCH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Using &lt;code&gt;sqlite-vec&lt;/code&gt; with SQLite makes AI-powered vector search accessible without the complexity of dedicated vector databases.&lt;/p&gt;

&lt;p&gt;This example focused on job matching, but you can apply the same principles to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Personalized recommendations (e.g., e-commerce, news, or learning platforms)&lt;/li&gt;
&lt;li&gt;AI-powered search in customer support systems&lt;/li&gt;
&lt;li&gt;Semantic search in personal knowledge bases&lt;/li&gt;
&lt;li&gt;Retrieval-augmented generation (RAG) for LLM applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're already using SQLite, &lt;code&gt;sqlite-vec&lt;/code&gt; is an easy, powerful upgrade—and if you're new to vector search, this is a great place to start.&lt;/p&gt;

&lt;p&gt;Try it out, and if you build something cool, I'd love to hear about it!&lt;/p&gt;

&lt;h3&gt;
  
  
  Next Steps
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Experiment with different embedding models.&lt;/li&gt;
&lt;li&gt;Learn about SQLite optimizations like &lt;a href="https://www.sqlite.org/wal.html" rel="noopener noreferrer"&gt;WAL mode&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;sqlite-vec&lt;/code&gt; extension for more vector search applications like recommendations, RAG, and more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a dive into the predecessor to &lt;code&gt;sqlite-vec&lt;/code&gt;, check out my &lt;a href="https://stephencollins.tech/posts/how-to-use-sqLite-to-store-and-query-vector-embeddings" rel="noopener noreferrer"&gt;previous blog post on SQLite vector embeddings with &lt;code&gt;sqlite-vss&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Further Reading&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you're interested in exploring &lt;code&gt;sqlite-vec&lt;/code&gt; further or optimizing SQLite for vector search, here are a few resources you might find useful:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Learn More About &lt;code&gt;sqlite-vec&lt;/code&gt;&lt;/strong&gt; - Check out the &lt;a href="https://github.com/asg017/sqlite-vec" rel="noopener noreferrer"&gt;official GitHub repository&lt;/a&gt; to see how it works under the hood.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Understand the Evolution&lt;/strong&gt; - If you're curious about the predecessor to &lt;code&gt;sqlite-vec&lt;/code&gt;, take a look at &lt;a href="https://github.com/asg017/sqlite-vss" rel="noopener noreferrer"&gt;&lt;code&gt;sqlite-vss&lt;/code&gt;&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimize Your SQLite Database&lt;/strong&gt; - &lt;a href="https://www.sqlite.org/wal.html" rel="noopener noreferrer"&gt;WAL mode&lt;/a&gt; can improve write performance for higher traffic workloads.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explore Embedding Models&lt;/strong&gt; - The &lt;a href="https://huggingface.co/Xenova/gte-base" rel="noopener noreferrer"&gt;&lt;code&gt;Xenova/gte-base&lt;/code&gt;&lt;/a&gt; model is a great starting point for generating embeddings.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consider Model Optimization&lt;/strong&gt; - If you want to deploy AI models efficiently, &lt;a href="https://onnx.ai/" rel="noopener noreferrer"&gt;ONNX&lt;/a&gt; helps with cross-platform compatibility and speed.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check out cosine similarity&lt;/strong&gt; - &lt;a href="https://en.wikipedia.org/wiki/Cosine_similarity" rel="noopener noreferrer"&gt;Cosine similarity&lt;/a&gt; is a great way to compare embeddings for semantic similarity.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These should give you a solid foundation for building your own AI-powered search applications with SQLite.&lt;/p&gt;

</description>
      <category>sqlite</category>
      <category>vectorsearch</category>
      <category>ai</category>
      <category>embeddings</category>
    </item>
  </channel>
</rss>
