Skip to main content
The Davenporter
Essays Code Arcade Now
Theme
Palette
Jason Davenport

I write about agents doing real engineering work: source control, memory, telemetry, and the human side of AI.

Subscribe

Elsewhere

  • Substack
  • LinkedIn
  • GitHub
  • X
  • RSS
  1. Home
  2. Essays
  3. WIBTD/W/M: Routing AI responses between local runners

WIBTD/W/M: Routing AI responses between local runners

Jun 04, 2026 · 5 min read · Original on Substack ↗

Extending smartrouter to support calling local AI models that are a part of a pool of workers, learnings and tribulations, and things I'm planning to do next. I’ve been fascinated with local models more and more recently. These are the open models you can download and run on your laptop.

Cover image for WIBTD/W/M: Routing AI responses between local runners
Cover image from the original post.

I’ve been fascinated with local models more and more recently. These are the open models you can download and run on your laptop. It’s been really cool starting to use these with things like LMStudio on my Mac, but I’ve been thinking more also about how you can run and distribute LLM requests to these models across different machines and fleets, and it’s a little different than just running a cluster or service.

So my goal this past week was to build a service that could take requests centrally, route them out to local models, and pull them back to the user. Why? There’s a ton of examples out there calling a local model directly, but when you have a fleet of models and agents / users that can call them, you need some way to manage the chaos and deployments. Smartrouter provides logging and monitoring for these calls, and we can extend it to support ‘local AI’.

So how did I build it? First, research about how I should run local AI.

Considerations for running local AI

This is by no means an exhaustive list but incorporate some of the things I’ve thought about and read up on as I’m getting up to speed on local AI, keeping in mind the goal is concurrency and increasing the number of requests a cluster could serve for a particular model type.

First thing I’ve been reading up on is how to run local AI in clusters. This isn’t so much an activity like database sharding, more like how you can run multiple services. And there’s complexity here where if you try to run a large model, you need to take into account how to shard a model across things like different GPU’s based on vRAM size. In the example of concurrency, we can scale up here with faster GPU’s or storage like Lustre for model loading. But if we’re constrained by something like cost (so no GPU usage), or limited to the devices we have (I have 2 AMD Ryzen 5 NUC’s), that doesn’t really work. I’m looking to scale out.

So, in this example, what I really want is a fleet or pool of workers that take items, work them, and put them back. And there are a number of great existing examples of using queues and workers to complete tasks. So I decided to implement this pattern in smartrouter for local routing.

How the local cluster works in smartrouter

Smartrouter allows you to register machine runners into a ‘local cluster’, which is a logical service representation. This contains a queue for worker requests, the models available to serve, and any additional queueing or execution limitations.

Next, you can use the existing router architecture to route requests through smartrouter to the local clusters. This includes dynamic routing.

Here’s an example of a request that does a few things: first, it routes to a local model. Next, the router actually changes which model the request gets sent to based on the routing rules. Then the runner picks up the request from the queue, works it, and publishes it.

A runner in smartrouter is like a machine agent. It registers itself with smartrouter and uses llama.cpp to pull down the model to run. I’ve created an example using Kubernetes pods that also allow you to run pure CPU or GPU inference, and I’m working on a Mac and Linux version next (the Mac code exists but I haven’t been able to give it a thorough working through yet).

We can also test smartrouter local agents by creating a key just like any other app registered and routing to the appropriate ‘model’:

curl -s -X POST "https://gemini-smart-router-your-url-hash-uc.a.run.app/v1/models/gemma2:2b:generateContent" \
      -H "x-goog-api-key: gr_local_cluster_verify_key" \
      -H "X-Client-App-ID: app-local-verify" \
      -H "Content-Type: application/json" \
      -d '{"contents": [{"parts": [{"text": "Write a 3-word quote about coding."}]}], "generationConfig": {"maxOutputTokens": 10}}'

And get a result back:

Target Endpoint: https://gemini-smart-router-your-url-hash-uc.a.run.app
------------------------------------------------------------
💬 Gemma: "Code, debug, deploy." 
💬 Gemma: **Code, create, solve.** 
💬 Gemma: "Code it, fix it, thrive." 
💬 Gemma: **"Code, Create, Connect."** 
💬 Gemma: "Code makes reality." 
💬 Gemma: **Lines, logic, build.** 
💬 Gemma: **Code. Create. Conquer.** 
💬 Gemma: **Write code, build dreams.** 

Feats and pain using AI for development

Feat: Once we defined the desired architecture, LLM’s are pretty good at writing kubernetes specs in particular and running a playbook. When we got to this point, the process was a lot easier.

Feat: The rework cycle is dramatically shorter. I explored some earlier concepts about clusters and runners, and while they worked, the data model needed some heavy revisions to support scaling. LLM’s make this work significantly faster, to the point that you sometimes have to make sure you didn’t throw too much out the window.

Pain: Definitions really matter. In the first iteration of my local implementation, I called the machine agent an ‘agent’. And that created a number of comprehension problems for the LLM where changes would be made both on the frontend of smartrouter and in the machine agent, which I obviously did not want. After a few rounds of this, I went back to the drawing board and defined very clear and different terms in the application’s terminology. This helped the LLM generate better context for each of my reuqests going forward.

Pain: LLM’s still like to tell you it worked even though it didn’t. When I do iterative development, I like to use test driven design, but even that has flaws with the human. The tests look fine, but LLM’s I’ve found have a tendency to try to mock services to pass tests rather than run the request to the ground. I also ran into a few instances where a ‘200’ was not in fact a success. My learning here is to continue to build out my agent skills on testing so its clear what my expectations are, in this case no mocks in this code and real logs to prove it.

Next steps

Download and try smartrouter here: https://github.com/davenportjw/smartrouter

Let me know what you find / excites you / frustrates you!

I’m planning to extend smartrouter next week to support more dynamic routing requests and implement more analytic capabilities for routing. Cost in particular is a vector I hear a lot about, and mining current information for future learning is top of mind in a number of areas. Look for more on this in the next few weeks.

Also, I’m exploring cool concepts others have in this space, for example Matthew Tracy’s implementation of a router here: https://github.com/MatthewTracy/yagami

There’s a lot of good stuff here.

Thanks for reading The Davenporter! Subscribe for free to receive new posts and support my work.

Code for this essay

smartrouter

Routes agent prompts dynamically between Google Cloud Vertex AI (Gemini 3.8 Flash / Gemini 3.5 Flash-Lite) and local Gemma 4 runners based on complexity score, context token budget, and data residency policy.

Open in Code

Sample output from a local run
$ uv run python -m smartrouter.cli --prompt 'Refactor AST graph lock lease expiry' --slo-ms 850
ROUTE DECISION: vertex-ai/gemini-3.8-flash (project=davenport-boutique, location=us-central1)
REASON: multi-symbol code mutation (complexity=0.82 > threshold=0.65)
AUTH MODE: google-cloud-adc (zero API key) · EST. LATENCY: 410 ms
← Newer essay With AI, what's old is new in telemetry again Older essay → What's worth learning in an AI era?

Get new posts by email

I send new essays by email through Substack. Subscribe there and they'll land in your inbox.

Subscribe on Substack ↗
Subscribe on Substack · RSS · Privacy ·
Page metadata

Social card and structured data

The share image, feeds and Schema.org JSON-LD that search engines and AI tools read for this page.

Social card

Title card for WIBTD/W/M: Routing AI responses between local runners
Card URL: /og/wibtdml-routing-ai-responses-between.svg

Machine feeds

/llms.txt /llms-full.txt /sitemap.xml /api/agent/spec

Structured data

{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "Person",
      "@id": "https://davenporter.com/#jason-davenport",
      "name": "Jason Davenport",
      "alternateName": "@davenportjw",
      "jobTitle": "AI & Distributed Systems Architect",
      "description": "Exploring agent-first source control (cosm), trajectory-to-lineage observability, Spanner-backed agent memory consolidation (Electric Sheep), hybrid local/cloud LLM routing, and real-time multiplayer browser simulations on Google Cloud Run.",
      "url": "https://davenporter.com",
      "sameAs": [
        "https://davenporter.substack.com",
        "https://www.linkedin.com/in/davenportjw",
        "https://github.com/davenportjw",
        "https://x.com/davenportjw"
      ]
    },
    {
      "@type": "WebSite",
      "@id": "https://davenporter.com/#website",
      "url": "https://davenporter.com",
      "name": "The Davenporter",
      "description": "Learnings about software, technology, agentic systems, and occasionally home renovation.",
      "publisher": {
        "@id": "https://davenporter.com/#jason-davenport"
      },
      "inLanguage": "en-US"
    },
    {
      "@type": "ImageObject",
      "@id": "https://davenporter.com/writings/wibtdml-routing-ai-responses-between#primaryimage",
      "url": "https://davenporter.com/og/wibtdml-routing-ai-responses-between.png",
      "contentUrl": "https://davenporter.com/og/wibtdml-routing-ai-responses-between.png",
      "width": 1200,
      "height": 630,
      "caption": "Title card for WIBTD/W/M: Routing AI responses between local runners",
      "representativeOfPage": true,
      "creator": {
        "@id": "https://davenporter.com/#jason-davenport"
      },
      "license": "https://creativecommons.org/licenses/by/4.0/",
      "acquireLicensePage": "https://davenporter.com/writings"
    },
    {
      "@type": "WebPage",
      "@id": "https://davenporter.com/writings/wibtdml-routing-ai-responses-between#webpage",
      "url": "https://davenporter.substack.com/p/wibtdml-routing-ai-responses-between",
      "name": "WIBTD/W/M: Routing AI responses between local runners | The Davenporter",
      "description": "Extending smartrouter to support calling local AI models that are a part of a pool of workers, learnings and tribulations, and things I'm planning to do next. I\u2019ve been fascinated with local models more and more recently. These are the open models you can download and run on your laptop.",
      "isPartOf": {
        "@id": "https://davenporter.com/#website"
      },
      "about": {
        "@id": "https://davenporter.com/#jason-davenport"
      },
      "primaryImageOfPage": {
        "@id": "https://davenporter.com/writings/wibtdml-routing-ai-responses-between#primaryimage"
      },
      "inLanguage": "en-US"
    },
    {
      "@type": "FAQPage",
      "@id": "https://davenporter.com/writings/wibtdml-routing-ai-responses-between#faq",
      "mainEntity": [
        {
          "@type": "Question",
          "name": "What is \u201cWIBTD/W/M: Routing AI responses between local runners\u201d about?",
          "acceptedAnswer": {
            "@type": "Answer",
            "text": "Extending smartrouter to support calling local AI models that are a part of a pool of workers, learnings and tribulations, and things I'm planning to do next. I\u2019ve been fascinated with local models more and more recently. These are the open models you can download and run on your laptop."
          }
        }
      ]
    },
    {
      "@type": "BlogPosting",
      "@id": "https://davenporter.com/writings/wibtdml-routing-ai-responses-between#article",
      "mainEntityOfPage": {
        "@id": "https://davenporter.com/writings/wibtdml-routing-ai-responses-between#webpage",
        "url": "https://davenporter.substack.com/p/wibtdml-routing-ai-responses-between"
      },
      "headline": "WIBTD/W/M: Routing AI responses between local runners",
      "alternativeHeadline": "Extending smartrouter to support calling local AI models that are a part of a pool of workers, learnings and tribulations, and things I'm planning to do next",
      "description": "Extending smartrouter to support calling local AI models that are a part of a pool of workers, learnings and tribulations, and things I'm planning to do next. I\u2019ve been fascinated with local models more and more recently. These are the open models you can download and run on your laptop.",
      "datePublished": "2026-06-04T20:38:51Z",
      "dateModified": "2026-06-04T20:38:51Z",
      "wordCount": 1108,
      "keywords": "WIBTM, Source Control, Model Routing",
      "author": {
        "@id": "https://davenporter.com/#jason-davenport"
      },
      "publisher": {
        "@id": "https://davenporter.com/#jason-davenport"
      },
      "image": {
        "@id": "https://davenporter.com/writings/wibtdml-routing-ai-responses-between#primaryimage"
      },
      "url": "https://davenporter.substack.com/p/wibtdml-routing-ai-responses-between"
    },
    {
      "@type": "BreadcrumbList",
      "@id": "https://davenporter.com/writings/wibtdml-routing-ai-responses-between#breadcrumb",
      "itemListElement": [
        {
          "@type": "ListItem",
          "position": 1,
          "name": "Home",
          "item": "https://davenporter.com/"
        },
        {
          "@type": "ListItem",
          "position": 2,
          "name": "Essays",
          "item": "https://davenporter.com/writings"
        },
        {
          "@type": "ListItem",
          "position": 3,
          "name": "WIBTD/W/M: Routing AI responses between local runners",
          "item": "https://davenporter.com/writings/wibtdml-routing-ai-responses-between"
        }
      ]
    }
  ]
}

Ask The Davenporter

● Checking local AI… How it works →

Chrome can download Gemini Nano so answers run on your device.

0%
Ask anything about the essays, projects, or games on this site.