<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lynkr</title>
    <description>The latest articles on DEV Community by Lynkr (@lynkr).</description>
    <link>https://dev.to/lynkr</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3645387%2F794ced23-25c9-41ed-863a-401839a48d59.png</url>
      <title>DEV Community: Lynkr</title>
      <link>https://dev.to/lynkr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lynkr"/>
    <language>en</language>
    <item>
      <title>LLM Inference Is Reinventing the Operating System</title>
      <dc:creator>Lynkr</dc:creator>
      <pubDate>Fri, 04 Sep 2026 22:54:56 +0000</pubDate>
      <link>https://dev.to/lynkr/llm-inference-is-reinventing-the-operating-system-10a2</link>
      <guid>https://dev.to/lynkr/llm-inference-is-reinventing-the-operating-system-10a2</guid>
      <description>&lt;p&gt;Operating systems solved a problem decades ago:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How do you make a limited amount of fast memory appear much larger to applications?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;LLM inference is now facing a surprisingly similar problem.&lt;/p&gt;

&lt;p&gt;Except instead of CPU processes and memory pages, we have &lt;strong&gt;inference requests and KV-cache blocks&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CPU → RAM → Disk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we increasingly have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GPU HBM → CPU DRAM → NVMe → Remote Memory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And instead of simply executing instructions, the system must continuously decide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What state should stay in GPU memory?&lt;/li&gt;
&lt;li&gt;What can be shared?&lt;/li&gt;
&lt;li&gt;What should be evicted?&lt;/li&gt;
&lt;li&gt;What should be prefetched?&lt;/li&gt;
&lt;li&gt;Which request should run next?&lt;/li&gt;
&lt;li&gt;Where should its state live?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is how LLM inference is turning into an operating-system problem.&lt;/p&gt;




&lt;h1&gt;
  
  
  1. The KV Cache Turns Inference Into a Memory-Management Problem
&lt;/h1&gt;

&lt;p&gt;To understand everything else, we first need to understand the &lt;strong&gt;KV cache&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Transformers use self-attention.&lt;/p&gt;

&lt;p&gt;For every token, attention produces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a Query (Q)&lt;/li&gt;
&lt;li&gt;a Key (K)&lt;/li&gt;
&lt;li&gt;a Value (V)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;During autoregressive generation, previously generated tokens don't need to have their Keys and Values recomputed every time.&lt;/p&gt;

&lt;p&gt;So inference engines store them.&lt;/p&gt;

&lt;p&gt;That's the KV cache.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Token 1 → K1, V1
Token 2 → K2, V2
Token 3 → K3, V3
Token 4 → K4, V4
...
Token N → KN, VN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When generating token &lt;code&gt;N+1&lt;/code&gt;, the model can reuse:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;K1...KN
V1...VN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead of recomputing them.&lt;/p&gt;

&lt;p&gt;This dramatically reduces computation.&lt;/p&gt;

&lt;p&gt;But it creates a new problem:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The longer the context, the more memory the KV cache consumes.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A simplified KV-cache memory calculation is:&lt;/p&gt;

&lt;p&gt;Mₖᵥ = 2 × L × T × Hₖᵥ × D × B&lt;/p&gt;

&lt;p&gt;where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(L) = number of transformer layers&lt;/li&gt;
&lt;li&gt;(T) = number of tokens&lt;/li&gt;
&lt;li&gt;(H_{KV}) = number of KV heads&lt;/li&gt;
&lt;li&gt;(D) = head dimension&lt;/li&gt;
&lt;li&gt;(B) = bytes per element&lt;/li&gt;
&lt;li&gt;2 = Key + Value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice the important variable:&lt;/p&gt;

&lt;p&gt;Double the context length and you roughly double the KV memory.&lt;/p&gt;

&lt;p&gt;Now imagine thousands of simultaneous requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request A → 10K tokens
Request B → 50K tokens
Request C → 2K tokens
Request D → 100K tokens
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model weights may be static.&lt;/p&gt;

&lt;p&gt;The KV cache is &lt;strong&gt;dynamic memory&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And that is where the operating-system analogy begins.&lt;/p&gt;




&lt;h1&gt;
  
  
  2. PagedAttention Turns KV Cache Into Pages
&lt;/h1&gt;

&lt;p&gt;A naive inference engine might allocate one large contiguous region of memory for every request.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GPU Memory

┌─────────┬─────────┬─────────┬─────────┐
│ Request │ Request │ Request │ Request │
│    A    │    B    │    C    │    D    │
└─────────┴─────────┴─────────┴─────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But requests don't behave nicely.&lt;/p&gt;

&lt;p&gt;Request A may finish.&lt;/p&gt;

&lt;p&gt;Request C may grow.&lt;/p&gt;

&lt;p&gt;Request D may be paused.&lt;/p&gt;

&lt;p&gt;A new request may arrive.&lt;/p&gt;

&lt;p&gt;Eventually memory becomes fragmented.&lt;/p&gt;

&lt;p&gt;You can have plenty of free memory but not enough contiguous memory for a large allocation.&lt;/p&gt;

&lt;p&gt;Operating systems solved a similar problem with &lt;strong&gt;paging&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of requiring a process to occupy one contiguous region:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Process A:

[A][A][A][A][A]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the OS divides it into pages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[A0][A1][A2][A3][A4]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and stores those pages wherever free physical frames are available:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Physical memory:

[A0][C2][B1][A3][D4][A1][FREE][A2]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A page table maps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Virtual page → Physical frame
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modern LLM serving systems use a similar idea for KV cache.&lt;/p&gt;

&lt;p&gt;The KV cache is divided into blocks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request A:

[A0][A1][A2][A3][A4]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those blocks can be placed independently in GPU memory.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A0 → GPU block 17
A1 → GPU block 42
A2 → GPU block 5
A3 → GPU block 91
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The original vLLM/PagedAttention work explicitly uses the virtual-memory analogy to manage KV cache efficiently.&lt;/p&gt;

&lt;p&gt;The important idea is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Logical token state doesn't need to correspond to one contiguous physical memory region.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That single change makes KV memory dramatically easier to manage.&lt;/p&gt;




&lt;h1&gt;
  
  
  3. KV Cache Sharing Turns Into Shared Memory
&lt;/h1&gt;

&lt;p&gt;Now consider multiple requests with the same prefix.&lt;/p&gt;

&lt;p&gt;For example, thousands of users might send requests beginning with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are an expert financial analyst.

Here is the company's annual report:
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The beginning of every request is identical.&lt;/p&gt;

&lt;p&gt;Without sharing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request A:
[P][P][P][A]

Request B:
[P][P][P][B]

Request C:
[P][P][P][C]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same prefix is stored three times.&lt;/p&gt;

&lt;p&gt;That's wasteful.&lt;/p&gt;

&lt;p&gt;Instead, we can share the prefix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              ┌── Request A
              │
[P][P][P] ────┼── Request B
              │
              └── Request C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One physical set of KV blocks.&lt;/p&gt;

&lt;p&gt;Multiple logical requests.&lt;/p&gt;

&lt;p&gt;This is conceptually similar to &lt;strong&gt;shared memory&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It also resembles copy-on-write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Shared prefix
      │
 ┌────┼────┐
 A    B    C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As long as requests only read the shared blocks, they can reuse them.&lt;/p&gt;

&lt;p&gt;If they diverge, new blocks can be allocated.&lt;/p&gt;

&lt;p&gt;Systems such as SGLang's RadixAttention exploit prefix reuse to avoid recomputing common KV state.&lt;/p&gt;

&lt;p&gt;Now the inference runtime isn't simply asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How much memory does this request need?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It can also ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How much of this request can I reuse from memory I already have?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is a much more sophisticated memory-management problem.&lt;/p&gt;




&lt;h1&gt;
  
  
  4. KV Eviction Turns Into a Cache-Management Problem
&lt;/h1&gt;

&lt;p&gt;GPU HBM is limited.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HBM

████████████████████
100% FULL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Something must leave.&lt;/p&gt;

&lt;p&gt;This introduces &lt;strong&gt;KV-cache eviction&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Suppose we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[A][B][C][D][E][F][G][H]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and a new request needs another block.&lt;/p&gt;

&lt;p&gt;Which one should we remove?&lt;/p&gt;

&lt;p&gt;Possible policies include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LRU
LFU
FIFO
TTL
Priority
Reuse probability
Cost-aware eviction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The simplest approach is something like LRU:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Evict the least recently used block.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But LLM workloads are more complicated.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request A → active
Request B → paused
Request C → active
Request D → paused
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Request B might not have been used recently.&lt;/p&gt;

&lt;p&gt;But perhaps B is an agent waiting for a database query.&lt;/p&gt;

&lt;p&gt;In two seconds it may resume and need its entire KV history.&lt;/p&gt;

&lt;p&gt;So blindly evicting B could be expensive.&lt;/p&gt;

&lt;p&gt;There are now multiple possible costs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;KEEP
 ↓
consume valuable HBM

EVICT
 ↓
reload later

RECOMPUTE
 ↓
use GPU compute

TRANSFER
 ↓
move from another memory tier
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The runtime is therefore solving something like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;br&gt;
Bottleneck = min(Cₘₑₘₒᵣᵧ, Cᵣₑₗₒₐd, Cᵣₑcₒₘₚᵤₜₑ, Cₜᵣₐₙₛfₑᵣ)&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is no longer simply "run the neural network."&lt;/p&gt;

&lt;p&gt;It is &lt;strong&gt;cache management&lt;/strong&gt;.&lt;/p&gt;


&lt;h1&gt;
  
  
  5. Hierarchical KV Cache Creates a Memory Hierarchy
&lt;/h1&gt;

&lt;p&gt;Once HBM becomes insufficient, we need somewhere else to put KV state.&lt;/p&gt;

&lt;p&gt;Modern systems can increasingly use multiple memory tiers.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                  GPU
                   │
             ┌─────▼─────┐
             │    HBM    │
             │   HOT     │
             └─────┬─────┘
                   │
             ┌─────▼─────┐
             │   DRAM    │
             │   WARM    │
             └─────┬─────┘
                   │
             ┌─────▼─────┐
             │   NVMe    │
             │   COLD    │
             └─────┬─────┘
                   │
             ┌─────▼──────┐
             │   Remote    │
             │   Storage   │
             └─────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This looks remarkably similar to a computer memory hierarchy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CPU cache
   ↓
RAM
   ↓
SSD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Except now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GPU HBM
   ↓
CPU DRAM
   ↓
NVMe
   ↓
Network
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each level has different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;latency&lt;/li&gt;
&lt;li&gt;bandwidth&lt;/li&gt;
&lt;li&gt;capacity&lt;/li&gt;
&lt;li&gt;cost&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the runtime needs to decide:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Where should this KV block live?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hot blocks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HBM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Warm blocks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DRAM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cold blocks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NVMe / remote storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SGLang's HiCache is an example of a system explicitly exploring multi-level KV-cache management.&lt;/p&gt;

&lt;p&gt;At this point, LLM inference is starting to look less like a simple GPU program and more like a &lt;strong&gt;memory hierarchy manager&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  6. KV Prefetching Turns Into an I/O Problem
&lt;/h1&gt;

&lt;p&gt;Moving KV state between memory tiers can be expensive.&lt;/p&gt;

&lt;p&gt;Suppose a request needs a block currently stored in CPU memory.&lt;/p&gt;

&lt;p&gt;A naive implementation does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Need KV
   ↓
Request transfer
   ↓
Wait
   ↓
KV arrives
   ↓
Compute
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The GPU is waiting.&lt;/p&gt;

&lt;p&gt;A better system tries to predict what will be needed next.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;               COMPUTE
                  │
                  │
                  ▼
             ┌─────────┐
             │   GPU   │
             └────┬────┘
                  ▲
                  │
              PREFETCH
                  │
                  │
             ┌────┴────┐
             │  DRAM   │
             └─────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While the GPU is computing, the runtime asynchronously moves future KV blocks closer.&lt;/p&gt;

&lt;p&gt;This is classic &lt;strong&gt;prefetching&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The objective is to hide:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;br&gt;
T_{memory}&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;behind:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;br&gt;
T_{compute}&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Ideally:&lt;/p&gt;

&lt;p&gt;T_effective ≈ max(T_compute, T_memory)&lt;/p&gt;

&lt;p&gt;rather than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;T_{effective}= T_{compute} + T_{memory}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modern inference research is actively exploring asynchronous KV-cache prefetching because memory movement increasingly becomes a bottleneck.&lt;/p&gt;

&lt;p&gt;The inference engine now needs to answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What will this request need next?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a prediction problem.&lt;/p&gt;

&lt;p&gt;And prediction determines memory performance.&lt;/p&gt;




&lt;h1&gt;
  
  
  7. Cache-Aware Scheduling Turns Into an OS Scheduler Problem
&lt;/h1&gt;

&lt;p&gt;Traditional request scheduling might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Queue:

A
B
C
D
E
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The scheduler chooses which request runs next.&lt;/p&gt;

&lt;p&gt;But with KV caching, requests aren't independent.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A shares prefix with B
A shares nothing with C
A shares nothing with D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A → B
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;might be much more efficient than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A → C → D → B
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;because A and B can reuse the same KV state.&lt;/p&gt;

&lt;p&gt;So the scheduler now needs to consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GPU utilization
+
request latency
+
batch size
+
KV residency
+
prefix reuse
+
memory pressure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The scheduling problem becomes:&lt;/p&gt;

&lt;p&gt;``&lt;br&gt;
Choose(request)= f(latency,compute,memory,cache reuse)&lt;/p&gt;

&lt;p&gt;``&lt;/p&gt;

&lt;p&gt;This is very similar to an OS scheduler making decisions based on resource availability.&lt;/p&gt;

&lt;p&gt;But now the scheduler is also trying to maximize &lt;strong&gt;memory locality&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's a major conceptual shift.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The inference scheduler isn't just scheduling compute. It's scheduling data locality.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h1&gt;
  
  
  8. Prefill/Decode Disaggregation Turns Into Resource Scheduling
&lt;/h1&gt;

&lt;p&gt;LLM inference has two very different phases.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prefill
&lt;/h2&gt;

&lt;p&gt;The model processes the user's prompt.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10,000 input tokens
        ↓
parallel processing
        ↓
KV cache generated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tends to be relatively compute-intensive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decode
&lt;/h2&gt;

&lt;p&gt;The model generates output one token at a time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Token 1
   ↓
Token 2
   ↓
Token 3
   ↓
Token 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Decode is much more sensitive to memory bandwidth and latency.&lt;/p&gt;

&lt;p&gt;So the same GPU is being asked to serve two fundamentally different workloads.&lt;/p&gt;

&lt;p&gt;This leads to &lt;strong&gt;prefill/decode disaggregation&lt;/strong&gt;.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                Requests
                    │
             ┌──────┴──────┐
             │             │
          PREFILL        DECODE
             │             │
             ▼             ▼
        GPU cluster    GPU cluster
             │             │
             └──────┬──────┘
                    │
                 KV state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the system can optimize the two phases separately.&lt;/p&gt;

&lt;p&gt;You can dedicate resources to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Prefill:
compute throughput
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Decode:
memory bandwidth
latency
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is another classic systems principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Different workloads should not necessarily compete for the same resource pool.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is similar to how operating systems and distributed systems isolate different classes of work.&lt;/p&gt;




&lt;h1&gt;
  
  
  9. Agentic Workloads Turn KV Management Into a Process-Lifetime Problem
&lt;/h1&gt;

&lt;p&gt;This is where things get really interesting.&lt;/p&gt;

&lt;p&gt;A normal chatbot 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;Request
   ↓
LLM
   ↓
Response
   ↓
Done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An agent looks more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LLM
 ↓
Tool call
 ↓
Wait
 ↓
LLM
 ↓
Database
 ↓
Wait
 ↓
LLM
 ↓
Browser
 ↓
LLM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The request can be inactive for seconds or minutes.&lt;/p&gt;

&lt;p&gt;But inactive does not mean finished.&lt;/p&gt;

&lt;p&gt;Suppose the agent has accumulated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;50,000 tokens
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and is now waiting for a tool.&lt;/p&gt;

&lt;p&gt;Should we keep all 50K tokens in HBM?&lt;/p&gt;

&lt;p&gt;Maybe.&lt;/p&gt;

&lt;p&gt;But if thousands of agents are simultaneously waiting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Agent A → waiting
Agent B → waiting
Agent C → waiting
...
Agent 10,000 → waiting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we cannot keep everything in HBM.&lt;/p&gt;

&lt;p&gt;So we need to understand &lt;strong&gt;request lifetime&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This starts looking like process management.&lt;/p&gt;

&lt;p&gt;Traditional OS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RUNNING
   ↓
WAITING
   ↓
RUNNABLE
   ↓
RUNNING
   ↓
TERMINATED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GENERATING
   ↓
TOOL WAIT
   ↓
READY
   ↓
GENERATING
   ↓
TOOL WAIT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And memory policy now needs to understand those states.&lt;/p&gt;

&lt;p&gt;A request that is waiting might have a high probability of becoming active again.&lt;/p&gt;

&lt;p&gt;Therefore:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;br&gt;
P(\text{reuse soon})&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;becomes relevant to eviction.&lt;/p&gt;

&lt;p&gt;This is fundamentally different from simple LRU.&lt;/p&gt;

&lt;p&gt;Agentic AI is therefore pushing inference toward &lt;strong&gt;application-aware memory management&lt;/strong&gt;.&lt;/p&gt;


&lt;h1&gt;
  
  
  10. Distributed KV Turns Inference Into a Distributed Operating System
&lt;/h1&gt;

&lt;p&gt;Now scale beyond one GPU.&lt;/p&gt;

&lt;p&gt;A production system may have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GPU 0 ─── GPU 1
 │          │
 │          │
GPU 2 ─── GPU 3
      │
      │
   CPU RAM
      │
      │
   Network
      │
      ▼
Distributed storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now KV cache might exist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GPU HBM
CPU DRAM
another GPU
another machine
NVMe
remote storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The runtime needs to understand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;capacity
latency
bandwidth
topology
locality
contention
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Moving a KV block from local HBM might be cheap.&lt;/p&gt;

&lt;p&gt;Moving it from another machine could be much more expensive.&lt;/p&gt;

&lt;p&gt;So:&lt;/p&gt;

&lt;p&gt;``&lt;br&gt;
Cost(block) =f(distance,bandwidth,latency,contention)&lt;/p&gt;

&lt;p&gt;``&lt;/p&gt;

&lt;p&gt;Now memory management and network scheduling become connected.&lt;/p&gt;

&lt;p&gt;This resembles distributed operating systems and NUMA systems, where memory isn't uniformly accessible.&lt;/p&gt;

&lt;p&gt;The runtime must answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Where should this state live, and where should computation happen?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is a distributed-systems problem.&lt;/p&gt;


&lt;h1&gt;
  
  
  The Bigger Picture
&lt;/h1&gt;

&lt;p&gt;Put all ten layers together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                  LLM APPLICATION
                         │
                       AGENT
                         │
                INFERENCE RUNTIME
                         │
          ┌──────────────┴──────────────┐
          │                             │
      SCHEDULER                    MEMORY MANAGER
          │                             │
          │                     ┌───────┴───────┐
          │                     │               │
          │                    HBM             DRAM
          │                     │               │
          │                    NVMe          Network
          │                     │               │
          └──────────────┬──────┴───────────────┘
                         │
                      GPU
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the ten transformations are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. KV Cache
   ↓
   Dynamic memory

2. PagedAttention
   ↓
   Paging

3. Prefix sharing
   ↓
   Shared memory

4. KV eviction
   ↓
   Cache management

5. Hierarchical KV
   ↓
   Memory hierarchy

6. KV prefetching
   ↓
   I/O management

7. Cache-aware scheduling
   ↓
   OS-style scheduling

8. Prefill/decode disaggregation
   ↓
   Resource scheduling

9. Agentic workloads
   ↓
   Process lifetime management

10. Distributed KV
    ↓
    Distributed memory management
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is why I think the phrase &lt;strong&gt;“inference OS”&lt;/strong&gt; isn't as crazy as it initially sounds.&lt;/p&gt;

&lt;p&gt;We are building increasingly sophisticated software to manage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;compute&lt;/li&gt;
&lt;li&gt;memory&lt;/li&gt;
&lt;li&gt;state&lt;/li&gt;
&lt;li&gt;scheduling&lt;/li&gt;
&lt;li&gt;locality&lt;/li&gt;
&lt;li&gt;data movement&lt;/li&gt;
&lt;li&gt;sharing&lt;/li&gt;
&lt;li&gt;eviction&lt;/li&gt;
&lt;li&gt;prefetching&lt;/li&gt;
&lt;li&gt;distributed resources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model is only one component.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Most Important Difference
&lt;/h1&gt;

&lt;p&gt;There is one major difference between traditional operating systems and LLM inference.&lt;/p&gt;

&lt;p&gt;Operating systems mostly manage &lt;strong&gt;passive data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;LLMs generate their own working state.&lt;/p&gt;

&lt;p&gt;Every token can create more KV cache:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Token
  ↓
KV state
  ↓
More memory
  ↓
More pressure
  ↓
Eviction / transfer
  ↓
Potential recomputation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the workload itself continuously changes the memory requirements.&lt;/p&gt;

&lt;p&gt;That's unusual.&lt;/p&gt;

&lt;p&gt;And it means the inference runtime must understand both:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;computation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;memory.&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;We spent decades optimizing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CPU → Memory → Storage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then AI gave us:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GPU → HBM → CPU Memory → Storage → Network&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And now the most interesting part of LLM infrastructure may not be the model itself.&lt;/p&gt;

&lt;p&gt;It may be the system managing the model's working state.&lt;/p&gt;

&lt;p&gt;Paging.&lt;/p&gt;

&lt;p&gt;Caching.&lt;/p&gt;

&lt;p&gt;Prefetching.&lt;/p&gt;

&lt;p&gt;Eviction.&lt;/p&gt;

&lt;p&gt;Scheduling.&lt;/p&gt;

&lt;p&gt;Memory hierarchies.&lt;/p&gt;

&lt;p&gt;Shared state.&lt;/p&gt;

&lt;p&gt;Resource allocation.&lt;/p&gt;

&lt;p&gt;Distributed memory.&lt;/p&gt;

&lt;p&gt;These are all classic operating-system and systems concepts.&lt;/p&gt;

&lt;p&gt;But they're being applied to something new:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;the runtime state of a neural network.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The model generates tokens.&lt;/p&gt;

&lt;p&gt;The tokens generate state.&lt;/p&gt;

&lt;p&gt;The state consumes memory.&lt;/p&gt;

&lt;p&gt;The memory determines scheduling.&lt;/p&gt;

&lt;p&gt;The scheduler determines throughput.&lt;/p&gt;

&lt;p&gt;And the entire system feeds back into the model's performance.&lt;/p&gt;

&lt;p&gt;So perhaps the next generation of AI infrastructure won't just be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;better models + faster GPUs&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It will be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;better inference operating systems.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The model is the intelligence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The runtime is the machine that makes that intelligence usable.&lt;/strong&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to run Claude Desktop and ChatGPT desktop app for free with llama.cpp and other local LLMs.</title>
      <dc:creator>Lynkr</dc:creator>
      <pubDate>Sun, 30 Aug 2026 07:11:41 +0000</pubDate>
      <link>https://dev.to/lynkr/how-we-added-claude-desktop-and-chatgptapp-support-to-one-llm-gateway-4llg</link>
      <guid>https://dev.to/lynkr/how-we-added-claude-desktop-and-chatgptapp-support-to-one-llm-gateway-4llg</guid>
      <description>&lt;p&gt;Most desktop AI apps still assume the backend is fixed.&lt;/p&gt;

&lt;p&gt;Claude Desktop assumes Anthropic. ChatGPT.app assumes OpenAI.&lt;/p&gt;

&lt;p&gt;That works until you want to route the same desktop app across local and cloud models, keep one gateway for caching and fallback, and pin stronger or cheaper models without changing frontend UX.&lt;/p&gt;

&lt;p&gt;That is the problem we just solved in Lynkr.&lt;/p&gt;

&lt;p&gt;Lynkr is an open-source LLM gateway. In the latest code changes, we added native support for two desktop surfaces that normally do not give you much backend control: &lt;strong&gt;Claude Desktop&lt;/strong&gt; and &lt;strong&gt;ChatGPT.app (Codex Desktop)&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude Desktop: using the hidden third-party gateway path
&lt;/h2&gt;

&lt;p&gt;Claude Desktop does not expose a normal bring-your-own-provider flow. The new Lynkr support works by using Claude Desktop's undocumented third-party gateway mode — the same general mechanism Ollama's Claude Desktop launcher relies on.&lt;/p&gt;

&lt;p&gt;The new install flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lynkr desktop-token &amp;lt;oauth-token&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the hood, Lynkr edits config in &lt;code&gt;~/Library/Application Support/&lt;/code&gt; and manages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Claude/claude_desktop_config.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Claude-3p/claude_desktop_config.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Claude-3p/configLibrary/_meta.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Claude-3p/configLibrary/&amp;lt;uuid&amp;gt;.json&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The installer also writes &lt;code&gt;Claude-3p/configLibrary/.lynkr-backup.json&lt;/code&gt;, which means restore is not a blind delete. Lynkr saves the original deployment mode and applied profile, then restores them exactly later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lynkr desktop-token &lt;span class="nt"&gt;--restore&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That restore command removes the Lynkr profile, resets deployment bookkeeping, and puts Claude Desktop back into its previous mode.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Claude Desktop needs a token
&lt;/h2&gt;

&lt;p&gt;Once Desktop is switched into third-party gateway mode (&lt;code&gt;deploymentMode: "3p"&lt;/code&gt;), it is no longer talking directly to Anthropic. Lynkr becomes the API layer receiving Desktop traffic.&lt;/p&gt;

&lt;p&gt;That is why the install command expects a Claude OAuth token. The current implementation explicitly validates that the token starts with &lt;code&gt;sk-ant-oat&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Lynkr does not mint or refresh that token. It installs the token into Claude Desktop's gateway profile so Desktop can send it to Lynkr, and Lynkr can forward it when a request routes to an Anthropic-backed tier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude Desktop's model picker becomes a tier selector
&lt;/h2&gt;

&lt;p&gt;Instead of treating the picker as cosmetic, Lynkr uses it as a routing control surface.&lt;/p&gt;

&lt;p&gt;The docs describe fixed picker entries that map to Lynkr tiers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auto -&amp;gt; no pin, normal content-based scoring&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;claude-opus-5&lt;/code&gt; -&amp;gt; &lt;code&gt;REASONING&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;claude-sonnet-5&lt;/code&gt; -&amp;gt; &lt;code&gt;COMPLEX&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;claude-sonnet-4-6&lt;/code&gt; -&amp;gt; &lt;code&gt;MEDIUM&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;claude-haiku-4-5-20251001&lt;/code&gt; -&amp;gt; &lt;code&gt;SIMPLE&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because Claude Desktop validates model ids against its own catalog, Lynkr cannot advertise made-up ids like &lt;code&gt;lynkr-medium&lt;/code&gt;. Instead, it reuses real Claude-family ids and treats them as tier pins.&lt;/p&gt;

&lt;p&gt;So the contract becomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Auto&lt;/strong&gt; = gateway decides&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;specific model&lt;/strong&gt; = user decides&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the user picks anything other than Auto, Lynkr bypasses classifier-based routing for that request and pins the tier directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude Desktop detection is gated, not global
&lt;/h2&gt;

&lt;p&gt;Lynkr does not force this behavior onto every Anthropic-compatible caller. The Claude Desktop gateway logic only intercepts callers that look like Desktop specifically — for example requests carrying an &lt;code&gt;anthropic-version&lt;/code&gt; header or an explicit &lt;code&gt;?format=anthropic&lt;/code&gt; path.&lt;/p&gt;

&lt;p&gt;Other Anthropic-format clients still fall through to the normal model list behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  ChatGPT.app: one backend for Desktop and CLI
&lt;/h2&gt;

&lt;p&gt;ChatGPT.app required a different strategy.&lt;/p&gt;

&lt;p&gt;The key detail is that Codex Desktop and Codex CLI share the same config surface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.codex/config.toml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means Lynkr can be added once as a provider block and then back both interfaces with the same gateway configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  ChatGPT.app exposes a real routing signal: &lt;code&gt;reasoning.effort&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The most interesting part of the ChatGPT.app work is that the model picker sends structured routing hints on the wire.&lt;/p&gt;

&lt;p&gt;The latest code adds a dedicated mapping module in &lt;code&gt;src/routing/openai-model-slots.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That file documents the behavior Lynkr now relies on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Codex sends a &lt;strong&gt;real OpenAI model id&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;it also sends &lt;code&gt;reasoning.effort&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Lynkr maps that pair into a routing tier&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The current effort-to-tier mapping is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;minimal -&amp;gt; SIMPLE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;low -&amp;gt; MEDIUM&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;medium -&amp;gt; COMPLEX&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;high -&amp;gt; REASONING&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was confirmed live from a captured request.&lt;/p&gt;

&lt;p&gt;One verified path from the implementation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UI label: &lt;strong&gt;Light&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;wire value: &lt;code&gt;reasoning.effort = "low"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Lynkr routing tier: &lt;code&gt;MEDIUM&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;classifier path: bypassed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the app already tells the gateway how much reasoning the user wants, the gateway should respect it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pin is intentionally conservative
&lt;/h2&gt;

&lt;p&gt;If Lynkr sees an unrecognized model or effort value, it falls back to normal content-based routing instead of guessing.&lt;/p&gt;

&lt;p&gt;There is also an important scope note in the code: the model/effort pin only governs the &lt;strong&gt;first model call of a turn&lt;/strong&gt;. If the turn becomes multi-step — tool calls, follow-up steps, more agent iterations — later steps can still be rescored by content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why these integrations matter
&lt;/h2&gt;

&lt;p&gt;The reason these integrations matter is not just that Lynkr now supports two more apps.&lt;/p&gt;

&lt;p&gt;It is that both apps normally own the backend relationship:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Desktop assumes Anthropic&lt;/li&gt;
&lt;li&gt;ChatGPT.app assumes OpenAI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once both route through Lynkr, the control plane moves out of the app and into the gateway:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Claude Desktop / ChatGPT.app
-&amp;gt; Lynkr
-&amp;gt; local + cloud models
-&amp;gt; tier routing + token optimization + fallback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At that point:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the app owns the UX&lt;/li&gt;
&lt;li&gt;Lynkr owns routing logic&lt;/li&gt;
&lt;li&gt;providers become interchangeable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the real story in these latest Lynkr changes.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>claude</category>
      <category>chatgpt</category>
    </item>
    <item>
      <title>Lynkr vs LiteLLM's New `complexity_router`</title>
      <dc:creator>Lynkr</dc:creator>
      <pubDate>Wed, 05 Aug 2026 22:17:24 +0000</pubDate>
      <link>https://dev.to/lynkr/lynkr-vs-litellms-new-complexityrouter-464p</link>
      <guid>https://dev.to/lynkr/lynkr-vs-litellms-new-complexityrouter-464p</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclosure up front:&lt;/strong&gt; this benchmark was designed, written, and run by Lynkr's&lt;br&gt;
maintainer, using Lynkr's own regression harness. The scenario set was written to&lt;br&gt;
test routing behaviors Lynkr considers important — that is a real selection bias&lt;br&gt;
and it is not hand-waved away here (see Limitations). Mitigations:&lt;br&gt;
both proxies route over &lt;strong&gt;identical backends&lt;/strong&gt;, LiteLLM ran its &lt;strong&gt;out-of-the-box&lt;br&gt;
defaults&lt;/strong&gt; exactly as Lynkr did, every scenario ran twice, the full config and&lt;br&gt;
harness are in this repo, and the results include the scenarios where LiteLLM&lt;br&gt;
wins and where Lynkr shows drift. Reproduction steps are at the bottom.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why this benchmark exists
&lt;/h2&gt;

&lt;p&gt;In early August 2026, LiteLLM shipped &lt;code&gt;complexity_router&lt;/code&gt; in core — a rule-based&lt;br&gt;
tier router that classifies each request as SIMPLE / MEDIUM / COMPLEX / REASONING&lt;br&gt;
(the same four-tier taxonomy Lynkr uses) via weighted keyword scoring across 7&lt;br&gt;
dimensions, with zero API calls and sub-millisecond decisions. It ships alongside&lt;br&gt;
&lt;code&gt;adaptive_router&lt;/code&gt; (Thompson-sampling bandits) and a savings baseline.&lt;/p&gt;

&lt;p&gt;This is the category leader validating tier-based cost routing as a core proxy&lt;br&gt;
feature. We benchmarked it the week it shipped, on the same backends Lynkr routes&lt;br&gt;
to in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Methodology
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Systems under test
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;version&lt;/th&gt;
&lt;th&gt;routing decision mechanism&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lynkr&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;git working tree, 2026-08-05&lt;/td&gt;
&lt;td&gt;embedding-anchor intent score (local, no LLM call) + risk analyzer + agentic detector + session pins + calibrated thresholds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LiteLLM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.95.0 (PyPI; latest release)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;complexity_router&lt;/code&gt;, heuristic classifier, default weights/boundaries, untouched&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Note: LiteLLM git-main (1.97.0) could not be run — the git wheel is missing&lt;br&gt;
&lt;code&gt;proxy_server.py&lt;/code&gt; and current main is incompatible with fastapi ≥0.140&lt;br&gt;
(&lt;code&gt;get_flat_dependant&lt;/code&gt; removed). Both are being reported upstream. 1.95.0 is the&lt;br&gt;
latest installable release and includes &lt;code&gt;complexity_router&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Identical backends, only the decision differs
&lt;/h3&gt;

&lt;p&gt;Both proxies map their four tiers to the same deployments (mirroring Lynkr's live&lt;br&gt;
&lt;code&gt;.env&lt;/code&gt; at test time):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;tier&lt;/th&gt;
&lt;th&gt;backend&lt;/th&gt;
&lt;th&gt;cost class&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SIMPLE&lt;/td&gt;
&lt;td&gt;ollama &lt;code&gt;minimax-m3:cloud&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MEDIUM&lt;/td&gt;
&lt;td&gt;ollama &lt;code&gt;minimax-m3:cloud&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;COMPLEX&lt;/td&gt;
&lt;td&gt;azure &lt;code&gt;gpt-5.6-sol&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;paid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;REASONING&lt;/td&gt;
&lt;td&gt;moonshot &lt;code&gt;kimi-k3&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;paid&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;LiteLLM config: &lt;code&gt;litellm-autorouter-v2.yaml&lt;/code&gt; (in-repo), using the shipped&lt;br&gt;
&lt;code&gt;auto_router/complexity_router&lt;/code&gt; schema with &lt;code&gt;tiers&lt;/code&gt; mapping and &lt;code&gt;default_model&lt;/code&gt;,&lt;br&gt;
nothing else configured. Each tier deployment carries &lt;code&gt;model_info.id&lt;/code&gt; so the&lt;br&gt;
&lt;code&gt;x-litellm-model-id&lt;/code&gt; response header reveals which tier served each request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Harness
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;benchmark-tier-routing.js&lt;/code&gt; (in-repo), &lt;code&gt;MODE=routing RUNS=2&lt;/code&gt;. Each scenario&lt;br&gt;
declares an &lt;strong&gt;acceptable-tier set&lt;/strong&gt;; a proxy is routing-correct when the tier that&lt;br&gt;
served the request is in the set. &lt;code&gt;RUNS=2&lt;/code&gt; means a scenario passes only if &lt;strong&gt;both&lt;/strong&gt;&lt;br&gt;
runs pass — this catches non-deterministic classifiers. Stateful scenarios embed a&lt;br&gt;
per-run nonce so session pins and caches can't leak between runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Scoreboard: 11 scenarios, acceptable-tier judging
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ID&lt;/th&gt;
&lt;th&gt;scenario (gist)&lt;/th&gt;
&lt;th&gt;acceptable&lt;/th&gt;
&lt;th&gt;Lynkr&lt;/th&gt;
&lt;th&gt;LiteLLM&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S1&lt;/td&gt;
&lt;td&gt;"What does git stash do?"&lt;/td&gt;
&lt;td&gt;SIMPLE|MEDIUM&lt;/td&gt;
&lt;td&gt;✓ MEDIUM&lt;/td&gt;
&lt;td&gt;✓ SIMPLE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;R1&lt;/td&gt;
&lt;td&gt;JWT security trade-offs for a banking app, step by step&lt;/td&gt;
&lt;td&gt;COMPLEX|REASONING&lt;/td&gt;
&lt;td&gt;✓ REASONING&lt;/td&gt;
&lt;td&gt;✗ MEDIUM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;F1&lt;/td&gt;
&lt;td&gt;"Refactor the entire ingestion pipeline, give me the plan"&lt;/td&gt;
&lt;td&gt;COMPLEX|REASONING&lt;/td&gt;
&lt;td&gt;✓ COMPLEX&lt;/td&gt;
&lt;td&gt;✗ MEDIUM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;F2&lt;/td&gt;
&lt;td&gt;"Fix the null check in &lt;code&gt;src/auth/middleware.ts&lt;/code&gt; + tests"&lt;/td&gt;
&lt;td&gt;COMPLEX|REASONING&lt;/td&gt;
&lt;td&gt;✓ REASONING&lt;/td&gt;
&lt;td&gt;✗ SIMPLE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RS1&lt;/td&gt;
&lt;td&gt;"17+25" wrapped in injected &lt;code&gt;&amp;lt;system-reminder&amp;gt;&lt;/code&gt; security noise&lt;/td&gt;
&lt;td&gt;SIMPLE|MEDIUM&lt;/td&gt;
&lt;td&gt;✓ MEDIUM&lt;/td&gt;
&lt;td&gt;✓ SIMPLE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SR1&lt;/td&gt;
&lt;td&gt;Suggestion-mode side request over a prior security conversation&lt;/td&gt;
&lt;td&gt;SIMPLE|MEDIUM&lt;/td&gt;
&lt;td&gt;✓ SIMPLE&lt;/td&gt;
&lt;td&gt;✓ SIMPLE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A1&lt;/td&gt;
&lt;td&gt;"Work autonomously: run tests, fix, iterate until green"&lt;/td&gt;
&lt;td&gt;COMPLEX|REASONING&lt;/td&gt;
&lt;td&gt;✓ REASONING&lt;/td&gt;
&lt;td&gt;✗ SIMPLE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P1&lt;/td&gt;
&lt;td&gt;"hi, you there?" (fresh session)&lt;/td&gt;
&lt;td&gt;SIMPLE|MEDIUM&lt;/td&gt;
&lt;td&gt;✓ MEDIUM&lt;/td&gt;
&lt;td&gt;✓ SIMPLE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P2&lt;/td&gt;
&lt;td&gt;same session, then "architecture review of the routing module"&lt;/td&gt;
&lt;td&gt;COMPLEX|REASONING&lt;/td&gt;
&lt;td&gt;✓ COMPLEX&lt;/td&gt;
&lt;td&gt;✗ SIMPLE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IV1&lt;/td&gt;
&lt;td&gt;"Review this retry helper for bugs" (bare)&lt;/td&gt;
&lt;td&gt;MEDIUM|COMPLEX&lt;/td&gt;
&lt;td&gt;✓ MEDIUM&lt;/td&gt;
&lt;td&gt;✗ SIMPLE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IV2&lt;/td&gt;
&lt;td&gt;identical ask + ~600 tokens of harness/IDE envelope noise&lt;/td&gt;
&lt;td&gt;MEDIUM|COMPLEX&lt;/td&gt;
&lt;td&gt;✓ MEDIUM&lt;/td&gt;
&lt;td&gt;✗ SIMPLE&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Lynkr: 11/11 · LiteLLM: 4/11.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The sharper cut: the 11 scenarios split into 4 "stay cheap" tests and 7&lt;br&gt;
"escalate correctly" tests. &lt;strong&gt;LiteLLM went 4/4 on stay-cheap and 0/7 on&lt;br&gt;
escalation.&lt;/strong&gt; A router biased toward the cheap tier passes every stay-cheap test&lt;br&gt;
by construction — the escalation tests are where routing is actually hard.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failure analysis: three clusters, not seven random misses
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Semantics beyond keywords (R1, F1, A1).&lt;/strong&gt; "Security trade-offs for a
banking application" and "work autonomously until the tests are green" carry
complexity in &lt;em&gt;meaning&lt;/em&gt;. The 7-dimension keyword sum doesn't reach the 0.35
COMPLEX boundary, so reasoning-grade work lands on the free tier. This is the
published, predicted weakness of ex-ante lexical scoring on real traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No risk model (F2).&lt;/strong&gt; "Fix the null check in &lt;code&gt;src/auth/middleware.ts&lt;/code&gt;" is
lexically trivial — and it is exactly the request you never send to an
uncontrolled free model. LiteLLM has no concept of protected paths; Lynkr's
risk analyzer forces auth/middleware edits to the governance tier regardless
of complexity score.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No session or envelope awareness (P2, IV1, IV2).&lt;/strong&gt; After a "hi" opener,
"now do an architecture review" stayed on SIMPLE — no pin-escape mechanism.
The invariance pair is subtler: LiteLLM was technically invariant (SIMPLE both
times) but invariantly &lt;em&gt;wrong&lt;/em&gt; — a code-review ask never accumulated enough
keyword weight in either form.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The cost table pathology (read before quoting)
&lt;/h3&gt;

&lt;p&gt;The harness's raw cost line shows LiteLLM at &lt;strong&gt;$0.00 — "100% cheaper."&lt;/strong&gt; That is&lt;br&gt;
the failure, not a win: it spent nothing because it routed nearly everything —&lt;br&gt;
including the 7 escalation-grade requests — to the free tier. A router that sends&lt;br&gt;
everything to the cheapest model is always the "cheapest" router and always the&lt;br&gt;
worst one. Cost numbers for a router are only meaningful &lt;strong&gt;conditional on routing&lt;br&gt;
correctness.&lt;/strong&gt; Any quotation of this benchmark that includes the cost table&lt;br&gt;
without this caveat is misleading, in either direction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lynkr's own warts (strict-expectation view)
&lt;/h3&gt;

&lt;p&gt;The harness also checks stricter per-scenario expectations than the acceptable&lt;br&gt;
sets. Lynkr drifted on 3 of 11:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;R1, F2:&lt;/strong&gt; over-routed COMPLEX → REASONING — correct-but-expensive (kimi-k3
spent where gpt-5.6-sol would do). Likely threshold drift after recent tier
remapping; the nightly calibration should re-fit, being watched.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;P1:&lt;/strong&gt; SIMPLE → MEDIUM pin drift on the trivial opener.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Over-routing burns money silently; it is the mirror image of LiteLLM's failure&lt;br&gt;
mode and it deserves the same scrutiny.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where LiteLLM is better
&lt;/h2&gt;

&lt;p&gt;Symmetry matters, so plainly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Decision latency.&lt;/strong&gt; Sub-millisecond, allocation-free keyword scoring vs
Lynkr's embedding lookup (~10–50 ms warm, more on classifier cold start). For
latency-critical single-model workloads that difference is real.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero infrastructure for the decision.&lt;/strong&gt; No embedding model, no classifier
model, no state. Lynkr's anchor scorer wants a local embedding model available.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Determinism and auditability.&lt;/strong&gt; A weighted keyword sum is trivially
explainable to a compliance reviewer; an embedding similarity is not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity of config.&lt;/strong&gt; One YAML block. Lynkr's routing intelligence has
meaningfully more surface to understand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tunability from a known base.&lt;/strong&gt; Weights, boundaries, and keyword lists are
all exposed; a team willing to tune per-workload could close some of the gap
(out-of-the-box was tested here, on both sides).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ecosystem and adoption.&lt;/strong&gt; LiteLLM is the default proxy of the ecosystem, and
the same release train ships &lt;code&gt;adaptive_router&lt;/code&gt; (bandit-based) — the direction
of travel is serious.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where Lynkr is better
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Escalation correctness: 7/7 vs 0/7&lt;/strong&gt; — the entire hard half of the problem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Risk-aware routing&lt;/strong&gt; — protected-path and instruction-risk signals route
security-relevant work to governed tiers independent of complexity score.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session pinning with escape&lt;/strong&gt; — sticky sessions that release when the work
outgrows the pin (P2), instead of trapping or ignoring session state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payload invariance by construction&lt;/strong&gt; — the intent score is computed on
cleaned user text, so harness envelopes, system-reminders, and IDE noise don't
move the decision (IV1/IV2, RS1).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A verification cascade behind the decision&lt;/strong&gt; (not exercised in this routing-
only benchmark): cheap-tier answers are structurally verified and escalated on
failure, so a mis-route down is a recoverable event rather than a silent
quality loss. LiteLLM's architecture is predict-then-commit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Third-party evaluation&lt;/strong&gt; — RouterArena (ICLR 2026): 67.65 arena / 68.41%
accuracy / $0.29 per 1K queries / 92.38 robustness, leaderboard #15.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Every one of these is a genuine limitation of this benchmark:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;N=11, author-selected.&lt;/strong&gt; The scenarios encode Lynkr's view of what routing
should do. A LiteLLM-authored scenario set would probably look different.
(Counterpoint: the escalation scenarios are not exotic — auth-file edits,
refactor plans, and agentic loops are everyday coding-agent traffic.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Defaults vs defaults.&lt;/strong&gt; LiteLLM's router is configurable; a tuned
configuration was not tested. Neither was a tuned Lynkr.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One day, one version pair.&lt;/strong&gt; LiteLLM 1.95.0; main moves fast and the
&lt;code&gt;adaptive_router&lt;/code&gt; (which may compensate for complexity_router misses over
time via bandit feedback) was not enabled or tested.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Routing-only.&lt;/strong&gt; Response quality, verification, compression, and caching
were all out of scope; the cost figures are therefore not end-to-end claims.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;RUNS=2&lt;/code&gt;&lt;/strong&gt; is enough to catch gross nondeterminism, not enough for
statistics. Both systems were deterministic across runs on every scenario.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reproduction
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# LiteLLM (Python ≥3.10 venv)&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"litellm[proxy]==1.95.0"&lt;/span&gt; &lt;span class="s2"&gt;"fastapi&amp;lt;0.140"&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'^(MOONSHOT_API_KEY|AZURE_OPENAI_API_KEY)='&lt;/span&gt; .env | xargs&lt;span class="si"&gt;)&lt;/span&gt;
litellm &lt;span class="nt"&gt;--port&lt;/span&gt; 8082 &lt;span class="nt"&gt;--config&lt;/span&gt; litellm-autorouter-v2.yaml

&lt;span class="c"&gt;# Lynkr&lt;/span&gt;
node index.js   &lt;span class="c"&gt;# serves :8081 per .env&lt;/span&gt;

&lt;span class="c"&gt;# head-to-head, routing-only, 2 runs per scenario&lt;/span&gt;
&lt;span class="nv"&gt;MODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;routing &lt;span class="nv"&gt;RUNS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nv"&gt;LITELLM_MASTER_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sk-1234 node benchmark-tier-routing.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scenario definitions: &lt;code&gt;benchmark-tier-routing.js&lt;/code&gt;. LiteLLM config:&lt;br&gt;
&lt;code&gt;litellm-autorouter-v2.yaml&lt;/code&gt;. Raw logs from this run: &lt;code&gt;/tmp/litellm-bench.log&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;/tmp/lynkr-bench.log&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow-ups queued
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Paraphrase-pair robustness scenarios (same intent, different surface — the
lexical scorer's structural weakness deserves its own measured number).&lt;/li&gt;
&lt;li&gt;Re-run with LiteLLM &lt;code&gt;adaptive_router&lt;/code&gt; enabled on top of &lt;code&gt;complexity_router&lt;/code&gt;,
with enough traffic for its bandit to update — the fairest version of this
comparison over time.&lt;/li&gt;
&lt;li&gt;File the two upstream LiteLLM bugs found while setting up (git wheel missing
&lt;code&gt;proxy_server.py&lt;/code&gt;; fastapi ≥0.140 incompatibility).&lt;/li&gt;
&lt;li&gt;Investigate Lynkr's R1/F2 over-routing drift after tier remapping.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>litellm</category>
    </item>
    <item>
      <title>Loop Engineering Is Mostly Papering Over a Model That Won't Converge</title>
      <dc:creator>Lynkr</dc:creator>
      <pubDate>Thu, 30 Jul 2026 07:25:49 +0000</pubDate>
      <link>https://dev.to/lynkr/loop-engineering-is-mostly-papering-over-a-model-that-wont-converge-4kh2</link>
      <guid>https://dev.to/lynkr/loop-engineering-is-mostly-papering-over-a-model-that-wont-converge-4kh2</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%2Fraw.githubusercontent.com%2FFast-Editor%2FLynkr%2Fmain%2F.hermes-temp%2Floop-engineering-infographic.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%2Fraw.githubusercontent.com%2FFast-Editor%2FLynkr%2Fmain%2F.hermes-temp%2Floop-engineering-infographic.jpg" alt="Loop engineering infographic" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I maintain &lt;a href="https://github.com/Fast-Editor/Lynkr" rel="noopener noreferrer"&gt;Lynkr&lt;/a&gt;, an open-source LLM gateway with loop-guard middleware, so I have a stake in this argument. Read this as a thesis to challenge, not a neutral survey.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Agent discourse has shifted from prompts to loops: the read-act-observe cycle that keeps running until the model decides it's done. My contrarian take is that most so-called &lt;em&gt;loop engineering&lt;/em&gt; is optimizing the wrong variable. &lt;strong&gt;A longer, richer loop is usually a convergence failure wearing a costume.&lt;/strong&gt; The well-engineered loop is the short one that knows when to stop.&lt;/p&gt;

&lt;h2&gt;
  
  
  The consensus I'm arguing against
&lt;/h2&gt;

&lt;p&gt;When an agent fails, the default move is to give the loop &lt;em&gt;more&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More turns before it gives up.&lt;/li&gt;
&lt;li&gt;More context stuffed into the window.&lt;/li&gt;
&lt;li&gt;More scaffolding: planners, critics, reflection steps, sub-agents spawning sub-agents.&lt;/li&gt;
&lt;li&gt;More retries with more feedback.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The implicit theory is simple: intelligence accumulates across iterations, so more turns should mean more convergence. Sometimes that works. Mostly, on real traffic, it doesn't — and the cost structure is brutal even when it does.&lt;/p&gt;

&lt;p&gt;A concrete example from coding agents: give an underpowered model a repo-wide refactor and it usually doesn't "reason longer" into success. It misreads one dependency, anchors on the wrong file, and spends the next dozen turns elaborating the mistake. The loop isn't rescuing the model. It's financing the failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why longer loops compound the wrong things
&lt;/h2&gt;

&lt;p&gt;Two things compound across a loop, and neither is intelligence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Loop cost grows fast.&lt;/strong&gt; Every turn re-sends accumulated context. A 20-turn session isn't just 20 single calls back-to-back; each later turn drags earlier tokens back through the model. In many agentic workloads, the loop becomes the most expensive object in the serving budget, and "add more turns" becomes the most expensive knob you can turn.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Reliability collapses across dependent steps.&lt;/strong&gt; Even with optimistic per-step reliability, multi-step chains degrade quickly. Long loops don't average out mistakes; they multiply the chance of staying on the correct trajectory. The agent that "just needs more turns" is often an agent walking deeper into the wrong branch with more conviction, because each turn conditions on its own earlier errors as if they were facts.&lt;/p&gt;

&lt;p&gt;We wrote earlier about how stuffing the context window can make agents worse, not smarter: attention dilutes, retrieval anchors on the wrong chunk, and signal gets buried. Loop length is the same failure on the time axis. More is not more.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a long loop is actually signaling
&lt;/h2&gt;

&lt;p&gt;When an agent needs 30 turns to do something, that's usually not the harness being clever. It's a signal of one of two failures:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The task was routed to a model that can't do it.&lt;/strong&gt; The loop is that model thrashing. Scaffolding rarely fixes an underpowered model decision; it usually just makes the failure more elaborate and more expensive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The task genuinely has many steps.&lt;/strong&gt; In that case, the engineering problem is not "how do I allow more turns" but "how do I verify each step cheaply so the wrong branch gets caught at turn 3 instead of compounding to turn 30?"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both point the same direction: either allocate the step to a better model, or verify the step before proceeding. Neither says: add turns and hope.&lt;/p&gt;

&lt;h2&gt;
  
  
  What loop engineering should actually be
&lt;/h2&gt;

&lt;p&gt;If loops are where cost and error compound, then good loop engineering should minimize compounding, not maximize capability-per-turn.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Cap the loop hard.&lt;/strong&gt; Every loop needs a turn ceiling and a tool-call ceiling that it cannot exceed, full stop. Not as an emergency fallback — as a first-class design constraint. A loop that &lt;em&gt;can&lt;/em&gt; run forever will, on some input, cost you a fortune to produce garbage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Verify each step cheaply.&lt;/strong&gt; The highest-leverage addition to a loop is usually not another reasoning stage. It's a fast, deterministic check on the step output that catches the failure modes you actually see: truncation, malformed tool calls, empty answers, degeneration loops, repeated echoes. Detection beats prediction because you already have the output in hand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Escalate the failing step, not the whole session.&lt;/strong&gt; When a step fails verification, redo &lt;em&gt;that step&lt;/em&gt; on a better tier. Don't grant the entire loop more turns to flail. Escalation is targeted; more-turns is a blank check.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part where this bit us too
&lt;/h2&gt;

&lt;p&gt;We're not exempt. In Lynkr, we shipped a terse mode meant to save output tokens by pushing models toward shorter answers. Our verifier then misread some of those short answers as low-effort failures and escalated trivial requests up a tier into a longer, more expensive path.&lt;/p&gt;

&lt;p&gt;The fix was one predicate. The broader lesson wasn't. &lt;strong&gt;A check inside a loop is itself part of the loop's cost and failure surface.&lt;/strong&gt; "Add verification" is not free virtue; a bad verifier can extend loops as eagerly as a naive retry does. Verification has to be engineered with the same suspicion as any other turn.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thesis, plainly
&lt;/h2&gt;

&lt;p&gt;A lot of popular loop engineering optimizes capability-per-turn while treating turn count as free. That's backwards. On real traffic, turn count is often the dominant cost and a major source of compounded error.&lt;/p&gt;

&lt;p&gt;If a task needs more intelligence, buy more intelligence for that step. If a task needs more reliability, verify the step. If a loop keeps growing, treat that as a failure signal, not sophistication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make the loop shorter. Make it stop. That's the engineering.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Lynkr is Apache-2.0 and self-hosted. The loop-guard middleware and step-level escalation ideas behind this post are in the repo: &lt;a href="https://github.com/Fast-Editor/Lynkr" rel="noopener noreferrer"&gt;github.com/Fast-Editor/Lynkr&lt;/a&gt;. If you've seen a loop run too long and know why, I'd love to hear the war story.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>agents</category>
      <category>devtools</category>
    </item>
    <item>
      <title>Your LLM Bill Is a Misallocation Problem, Not a Model Problem</title>
      <dc:creator>Lynkr</dc:creator>
      <pubDate>Thu, 30 Jul 2026 06:50:28 +0000</pubDate>
      <link>https://dev.to/lynkr/your-llm-bill-is-a-misallocation-problem-not-a-model-problem-18dg</link>
      <guid>https://dev.to/lynkr/your-llm-bill-is-a-misallocation-problem-not-a-model-problem-18dg</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I maintain &lt;a href="https://github.com/Fast-Editor/Lynkr" rel="noopener noreferrer"&gt;Lynkr&lt;/a&gt;, an open-source LLM router, so this post argues a thesis my own product embodies. The mitigation: every claim below is either published research, a July 2026 release you can verify, or a measurement you can reproduce — including the part where the obvious approach (and an earlier version of ours) doesn't work.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The dominant inefficiency in LLM serving is no longer kernel speed or quantization. It's &lt;strong&gt;per-request compute misallocation&lt;/strong&gt; — and the research says you can't fix it with prediction. You fix it with verification.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup: the gap closed, the dispersion didn't
&lt;/h2&gt;

&lt;p&gt;Three things became true more or less simultaneously in mid-2026:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Open-weight models sit within single-digit percentage points of frontier models on everyday work.&lt;/strong&gt; GLM-5.2 (744B MoE, 40B active, MIT license) leads several long-horizon coding benchmarks outright. Kimi K3 shipped in July at 2.8T parameters with ~1.8% expert activation and a 1M-token context — open weights, hosted access.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The cost of that near-frontier capability is 4–10× lower.&lt;/strong&gt; Hosted GLM-class models run at a fraction of frontier per-token prices; aggressive hosts (Meituan's LongCat 2.0 launched at $0.30/$1.20 per million) keep pushing the floor down.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Price-per-capability dispersion across providers has never been wider.&lt;/strong&gt; The same request can cost $0.10 or $15 per million output tokens depending on where you send it, for a quality difference that on most traffic is inside the noise.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Put those together and the largest line item in a serving budget is no longer "the model is slow" or "the quant is fat." It's &lt;strong&gt;every request routed to a frontier model that a cheaper tier would have cleared.&lt;/strong&gt; That's pure deadweight loss, and at current dispersion it usually dominates everything a kernel engineer can save you.&lt;/p&gt;

&lt;p&gt;I say that as someone who ships kernel PRs: a +13–15% decode win on a CPU inference engine (measured, merged, byte-identical output) saves you 13% &lt;em&gt;on one backend&lt;/em&gt;. Routing 60% of traffic down two price tiers saves ~90% &lt;em&gt;across the fleet&lt;/em&gt;. Same engineering effort. The leverage moved up the stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  The naive fix fails: you can't predict difficulty from the prompt
&lt;/h2&gt;

&lt;p&gt;The obvious architecture is ex-ante routing: classify the prompt, pick the model, done. Every "smart routing" product demo shows this.&lt;/p&gt;

&lt;p&gt;It doesn't survive contact with real traffic. &lt;strong&gt;Ex-ante difficulty prediction consistently fails to beat trivial baselines on agentic coding traffic.&lt;/strong&gt; When we ran the deep-research pass for Lynkr's cascade design (July 2025 literature, SWE-Bench-style workloads), the pattern was blunt: prompt surface features don't carry enough signal about downstream difficulty. A one-line ask ("fix the flaky test") can require repo-wide reasoning. A 3,000-token prompt can be boilerplate a 9B model handles. The information you need to route correctly mostly doesn't exist until &lt;em&gt;after&lt;/em&gt; a model has attempted the task.&lt;/p&gt;

&lt;p&gt;If your router's accuracy story is a difficulty classifier — however fancy the embeddings — you've built a system that's confidently wrong in both directions: burning frontier tokens on easy requests and serving garbage on hard ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  What works: try cheap, verify, escalate
&lt;/h2&gt;

&lt;p&gt;The architecture that actually gains is the &lt;strong&gt;cascade&lt;/strong&gt;: route to the cheapest plausible tier, run &lt;em&gt;deterministic verification&lt;/em&gt; on the answer, escalate only on failure. Published cascade results show up to &lt;strong&gt;+14% over static assignment&lt;/strong&gt; — and the gain lives in the verifier, because the cascade converts routing from a prediction problem (hard, information-starved) into a &lt;strong&gt;detection problem&lt;/strong&gt; (tractable, information-rich: you have the actual answer in hand).&lt;/p&gt;

&lt;p&gt;The verifier doesn't need to be an LLM judge. Ours is two layers of pure functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Structural checks&lt;/strong&gt; targeting the cheap-model failure modes we actually observed live: language drift (CJK tokens appearing mid-English answer), degeneration loops, truncation, malformed tool calls, empty/echo responses. High precision — flag only what is definitely broken.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A coarse effort score&lt;/strong&gt;: substantive ask answered with a stub, structure requested but not delivered, code requested but not delivered. Conservative threshold; only clear failures escalate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No LLM call in the loop. The verification cost is microseconds, which matters because a router that spends tokens deciding where to send tokens is eating the margin it exists to create.&lt;/p&gt;

&lt;h3&gt;
  
  
  A war story about verifiers
&lt;/h3&gt;

&lt;p&gt;One production bug worth sharing because it generalizes: we shipped a brevity mode that injects "answer tersely" into system prompts to cut output tokens. The verifier's effort-scorer then read the resulting short answers as &lt;em&gt;low-effort&lt;/em&gt; and started cascading trivial greetings up to the frontier tier — burning the exact money the brevity mode saved, and then some.&lt;/p&gt;

&lt;p&gt;The fix was one predicate (skip the effort check when the brevity instruction is present; keep the structural checks), but the lesson is bigger: &lt;strong&gt;a verifier must know what the model was asked to be.&lt;/strong&gt; Any answer-quality check that ignores the instructions that produced the answer will misfire exactly when your prompt engineering works.&lt;/p&gt;

&lt;h2&gt;
  
  
  The second-order effect: verification failures are free training data
&lt;/h2&gt;

&lt;p&gt;Here's where cascades stop being a cost trick and become a learning system.&lt;/p&gt;

&lt;p&gt;Every verification failure is a &lt;strong&gt;labeled hard negative&lt;/strong&gt;: "this request, routed to this tier, produced a detectably bad answer." Every escalation that then passes is a label: "requests like this outgrow the cheap tier." Feed those back into the routing decision — we treat it as a contextual bandit with verifier-generated rewards, logging propensities at decision time so the feedback isn't biased by the router's own choices — and the router learns the difficulty structure of &lt;em&gt;your&lt;/em&gt; traffic, from &lt;em&gt;your&lt;/em&gt; outcomes.&lt;/p&gt;

&lt;p&gt;No benchmark in the loop. That mattered more than usual this month: &lt;strong&gt;one of the field's most-cited coding benchmarks was retracted as broken.&lt;/strong&gt; Every router configured against it inherited the error silently, for months, with nothing in the loop that could detect it. Ground truth from your own traffic doesn't get retracted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Receipts
&lt;/h2&gt;

&lt;p&gt;This is the architecture behind Lynkr (Apache-2.0, self-hosted): deterministic embedding-anchor tier classification (zero LLM calls to route), structural verification on cheap-tier answers only (never second-guess the expensive tier — the loss is asymmetric), cascade escalation, reward pipeline with propensity logging.&lt;/p&gt;

&lt;p&gt;On &lt;a href="https://arxiv.org/abs/2510.00202" rel="noopener noreferrer"&gt;RouterArena&lt;/a&gt; (ICLR 2026, 8,400 queries, evaluation-only rules — no component tuned on their data), our submitted configuration measured &lt;strong&gt;67.65 arena score / 68.41% accuracy at $0.29 per 1K queries, robustness 92.38&lt;/strong&gt; — above GPT-5's built-in router and NotDiamond at a fraction of the per-decision cost. The &lt;a href="https://github.com/RouteWorks/RouterArena/pull/167" rel="noopener noreferrer"&gt;submission PR&lt;/a&gt; has the full config, prediction files, and the disclosures, including the metrics where we're mediocre (routing concentration is real; read the PR).&lt;/p&gt;

&lt;h2&gt;
  
  
  The thesis, restated
&lt;/h2&gt;

&lt;p&gt;For a decade the way to cut inference cost was to make the model cheaper: quantize, distill, fuse kernels. That work still matters — I still do it — but it's optimizing the &lt;em&gt;price of a decision that's already been made&lt;/em&gt;. In a world where capability is a commodity spectrum with 50× price dispersion and single-digit quality spread, the decision itself is where the money is.&lt;/p&gt;

&lt;p&gt;The gap between open and frontier closed. &lt;strong&gt;Allocation is the frontier now.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Lynkr is Apache-2.0 and self-hosted: &lt;a href="https://github.com/Fast-Editor/Lynkr" rel="noopener noreferrer"&gt;github.com/Fast-Editor/Lynkr&lt;/a&gt; · &lt;a href="https://www.npmjs.com/package/lynkr" rel="noopener noreferrer"&gt;npm&lt;/a&gt;. If you route production LLM traffic and have cascade/verification war stories, I want to hear them — the failure modes section of our verifier is built from exactly those.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>machinelearning</category>
      <category>devtools</category>
    </item>
    <item>
      <title>We Put Our Router on an Academic Benchmark. Here Are the Numbers We'd Rather Hide.</title>
      <dc:creator>Lynkr</dc:creator>
      <pubDate>Mon, 27 Jul 2026 07:36:49 +0000</pubDate>
      <link>https://dev.to/lynkr/we-put-our-router-on-an-academic-benchmark-here-are-the-numbers-wed-rather-hide-1c83</link>
      <guid>https://dev.to/lynkr/we-put-our-router-on-an-academic-benchmark-here-are-the-numbers-wed-rather-hide-1c83</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I maintain &lt;a href="https://github.com/Fast-Editor/Lynkr" rel="noopener noreferrer"&gt;Lynkr&lt;/a&gt;, the router being benchmarked, so read everything here with that in mind. The mitigation: every number in this post comes from &lt;a href="https://arxiv.org/abs/2510.00202" rel="noopener noreferrer"&gt;RouterArena&lt;/a&gt;'s automated evaluation pipeline, run by their CI on their infrastructure, not by me — including the numbers that make us look mediocre.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Every LLM router's README — ours included — makes the same claim: it sends easy queries to cheap models and hard queries to good ones, and saves you money without hurting quality. Almost none of them attach evidence. The numbers that do exist are self-reported, measured on datasets the vendor picked, with baselines the vendor chose.&lt;/p&gt;

&lt;p&gt;So when RouterArena showed up — an open, standardized benchmark for LLM routers out of the RouteWorks group (&lt;a href="https://arxiv.org/abs/2510.00202" rel="noopener noreferrer"&gt;paper&lt;/a&gt;, &lt;a href="https://routeworks.github.io/" rel="noopener noreferrer"&gt;leaderboard&lt;/a&gt;) — we submitted Lynkr to it. This post is what we learned, including the metrics where we did badly, because a benchmark result you only quote selectively is just marketing with extra steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  What RouterArena actually measures
&lt;/h2&gt;

&lt;p&gt;RouterArena evaluates a router over &lt;strong&gt;8,400 queries spanning 9 domains and 44 categories&lt;/strong&gt; at three difficulty levels, and scores it on five axes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Arena Score&lt;/strong&gt; — the headline number, a combined accuracy-vs-cost tradeoff.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accuracy&lt;/strong&gt; — did the model your router picked answer correctly?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost&lt;/strong&gt; — average $ per 1K queries, at real provider prices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimality&lt;/strong&gt; — three sub-metrics against the oracle: how often you picked the &lt;em&gt;cheapest correct&lt;/em&gt; model (Opt.Sel), how close your spend was to optimal (Opt.Cost), and how close your accuracy was to the best achievable with your pool (Opt.Acc).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Robustness&lt;/strong&gt; — do trivial rephrasings of the same query flip your routing decision?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One rule matters more than the metrics: &lt;strong&gt;RouterArena is evaluation-only.&lt;/strong&gt; Any router component trained, fitted, or tuned on their data or labels gets rejected, and violations found later get withdrawn. Keep that rule in mind — it comes back at the end of this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we submitted
&lt;/h2&gt;

&lt;p&gt;We submitted Lynkr's &lt;strong&gt;untuned production default&lt;/strong&gt; — the same complexity scorer and the same tier boundaries that ship in the repo. The adapter is &lt;a href="https://github.com/RouteWorks/RouterArena/pull/167" rel="noopener noreferrer"&gt;~70 lines of Python&lt;/a&gt; that sends each query to a live Lynkr instance's &lt;code&gt;/routing/analyze&lt;/code&gt; endpoint and maps the returned tier to a model. No benchmark-special code path: the endpoint runs the exact intent scorer the live proxy uses (local embeddings, no LLM call in the routing decision itself).&lt;/p&gt;

&lt;p&gt;The model pool, all via OpenRouter:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lynkr tier&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SIMPLE&lt;/td&gt;
&lt;td&gt;gpt-oss-120b&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MEDIUM&lt;/td&gt;
&lt;td&gt;Qwen3-235B-A22B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;COMPLEX / REASONING&lt;/td&gt;
&lt;td&gt;GLM-4.7&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Running all 8,400 queries cost &lt;strong&gt;$2.46 total&lt;/strong&gt;. That's the entire eval bill, which says something on its own about where inference prices are in mid-2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  The results
&lt;/h2&gt;

&lt;p&gt;From RouterArena's automated evaluation on our submission PR:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Lynkr&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Arena Score&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;67.65&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accuracy&lt;/td&gt;
&lt;td&gt;68.41%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost per 1K queries&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.29&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Robustness&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;92.38&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Opt.Acc (accuracy vs. optimal)&lt;/td&gt;
&lt;td&gt;84.48&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Opt.Cost (cost efficiency vs. optimal)&lt;/td&gt;
&lt;td&gt;16.08&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Opt.Sel (optimal model selection)&lt;/td&gt;
&lt;td&gt;10.97&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On the official leaderboard (our submission &lt;a href="https://github.com/RouteWorks/RouterArena/pull/167" rel="noopener noreferrer"&gt;merged 2026-07-23&lt;/a&gt;), that lands mid-table — &lt;strong&gt;15th of 27 routers&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The parts we'll be quoting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;We beat GPT-5 used as a router — at 34× lower cost.&lt;/strong&gt; The leaderboard includes GPT-5 itself as a routing baseline: Arena score 64.32 at &lt;strong&gt;$10.02 per 1K queries&lt;/strong&gt;. Lynkr scores 67.65 at &lt;strong&gt;$0.29 per 1K&lt;/strong&gt;. A local intent scorer making the decision without any LLM call outperforms asking a frontier model to route — which is the entire thesis Lynkr is built on, so we're relieved the benchmark agrees. Several well-known systems (NotDiamond at 57.29, RouteLLM at 48.07, both RouterBench baselines) also land below us.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Robustness 92.38 is top-five territory.&lt;/strong&gt; Most leaderboard routers sit between 22 and 72 — meaning a rephrased query frequently flips their model choice. Lynkr's routing decision survives rephrasing 92% of the time. For an interactive tool where users iterate on prompts, decision stability is arguably worth more than a couple of accuracy points: a router that sends your reworded follow-up to a different model mid-conversation is a router you turn off.&lt;/p&gt;

&lt;h2&gt;
  
  
  The parts we'd rather hide (but won't)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Opt.Sel is 10.97.&lt;/strong&gt; When multiple models in our pool could answer a query correctly, we picked the &lt;em&gt;cheapest&lt;/em&gt; correct one about 11% of the time. Lynkr routes conservatively — when in doubt, it escalates a tier. That's a deliberate live-serving bias (a wrong cheap answer costs more user trust than an unnecessarily good one costs dollars), and $0.29/1K shows the absolute spend stays low. But the oracle comparison is unambiguous: there was money on the table we didn't pick up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The accuracy gap to the top is real.&lt;/strong&gt; Cross-Router leads at 78.14% accuracy; we're at 68.41%. Part of that is the pool (three self-hostable open-weight models — no frontier closed models to escalate to), but part is genuinely the router. Mid-table is where we are, not where we'd spin it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the benchmark taught us — and why we won't act on all of it
&lt;/h2&gt;

&lt;p&gt;Reading our failure cases was the most valuable part of the exercise. The clearest pattern: our SIMPLE→MEDIUM boundary is conservative — a chunk of queries scored just past the boundary and got a mid-tier model when the cheap tier would have answered correctly.&lt;/p&gt;

&lt;p&gt;So we prototyped a shifted boundary and ran it on a ~10% subsample locally: Arena score 69.93, accuracy 71.07%, Opt.Sel jumping from ~11 to ~66. A two-point arena gain from moving one threshold.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That config will never appear on the leaderboard.&lt;/strong&gt; It was diagnosed from RouterArena's own failure cases, which makes it exactly what their evaluation-only rule exists to prevent — a router fitted to the eval. The leaderboard entry is the untuned default, and stays that way. The honest generalization claim is much narrower: the benchmark showed us &lt;em&gt;which knob&lt;/em&gt; matters, and we'll validate new defaults on our own traffic, not on theirs.&lt;/p&gt;

&lt;p&gt;If you maintain a router: this is the trap. The moment a public benchmark exists, the incentive is to tune against it, and every point you gain that way is a point of overfitting you ship to your actual users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveats, so you can weigh this properly
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;RouterArena is single-turn Q&amp;amp;A-style evaluation. Lynkr's primary workload is multi-turn coding agents, which RouterArena doesn't measure — agentic routing (tool-call density, context accumulation) is a different problem than classifying one prompt.&lt;/li&gt;
&lt;li&gt;Our score depends on our chosen pool. A pool with a frontier model on top would score differently in both directions (higher ceiling, higher cost).&lt;/li&gt;
&lt;li&gt;Leaderboard positions move; check the &lt;a href="https://routeworks.github.io/" rel="noopener noreferrer"&gt;live leaderboard&lt;/a&gt; rather than trusting this snapshot.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're evaluating routers — including ours — ask every vendor for their RouterArena number. It costs a few dollars and a PR to get one. "We haven't submitted" is also an answer.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt; &lt;a href="https://arxiv.org/abs/2510.00202" rel="noopener noreferrer"&gt;RouterArena paper (arXiv:2510.00202)&lt;/a&gt; · &lt;a href="https://routeworks.github.io/" rel="noopener noreferrer"&gt;leaderboard&lt;/a&gt; · &lt;a href="https://github.com/RouteWorks/RouterArena/pull/167" rel="noopener noreferrer"&gt;our submission PR&lt;/a&gt; · &lt;a href="https://github.com/Fast-Editor/Lynkr" rel="noopener noreferrer"&gt;Lynkr&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>benchmarking</category>
      <category>devtools</category>
    </item>
    <item>
      <title>Stuffing the Context Window Is Making Your Agent Dumber: What the Research Says</title>
      <dc:creator>Lynkr</dc:creator>
      <pubDate>Fri, 17 Jul 2026 01:10:15 +0000</pubDate>
      <link>https://dev.to/lynkr/stuffing-the-context-window-is-making-your-agent-dumber-what-the-research-says-2063</link>
      <guid>https://dev.to/lynkr/stuffing-the-context-window-is-making-your-agent-dumber-what-the-research-says-2063</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I maintain &lt;a href="https://github.com/Fast-Editor/Lynkr" rel="noopener noreferrer"&gt;Lynkr&lt;/a&gt;, an open-source gateway that (among other things) compresses agent tool outputs — so I have a horse in this race. This piece, though, is about the research, and every number in it is cited to its primary source.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There's an intuition almost every LLM user shares: more context = better answers. Million-token context windows are marketed as a capability. We paste in whole files "just in case." Our coding agents accumulate every grep result, every file read, every test log, on the theory that the model might need it.&lt;/p&gt;

&lt;p&gt;The research says this intuition is not just wrong — it's &lt;em&gt;backwards&lt;/em&gt;, and the size of the effect is embarrassing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The result that should change how you build
&lt;/h2&gt;

&lt;p&gt;The cleanest demonstration comes from the Hindsight memory system (&lt;a href="https://arxiv.org/abs/2512.12818" rel="noopener noreferrer"&gt;arXiv:2512.12818&lt;/a&gt;, demo at ACL 2026). On LongMemEval — a benchmark of questions over long conversational histories — the same open-source 20B model scores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;39.0%&lt;/strong&gt; when handed the &lt;em&gt;full context&lt;/em&gt; — everything, the whole history&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;83.6%&lt;/strong&gt; when handed a &lt;em&gt;curated slice&lt;/em&gt; selected by a structured memory system&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same model. Same available information. The difference is that one setup made the model read everything, and the other selected what mattered. &lt;strong&gt;+44.6 points from subtraction.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It gets more uncomfortable: that 20B model with curated context also &lt;strong&gt;beats full-context GPT-4o&lt;/strong&gt;, which scores 60.2% on the same benchmark. A model a fraction of the size, winning because someone cleaned its desk. As the authors put it, the memory architecture — not model scale — drives the performance.&lt;/p&gt;

&lt;p&gt;One benchmark, one paper? No — this is a pile-on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"Lost in the middle"&lt;/strong&gt; (Liu et al., 2023) established the shape of the problem early: models attend well to the start and end of long contexts and poorly to the middle — exactly where your agent's fifteenth tool result lives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context-rot studies&lt;/strong&gt; (notably Chroma's 2025 report) showed performance degrading as context grows &lt;em&gt;even when the added tokens are relevant&lt;/em&gt;, and degrading faster when they're distractors.&lt;/li&gt;
&lt;li&gt;A whole 2026 research wave now treats context as a resource to be &lt;strong&gt;managed, not maximized&lt;/strong&gt;: "Agentic Context Engineering" was accepted at ICLR 2026, active context compression systems prune their own working memory (&lt;a href="https://arxiv.org/abs/2601.07190" rel="noopener noreferrer"&gt;arXiv:2601.07190&lt;/a&gt;), and two consolidating surveys (&lt;a href="https://arxiv.org/abs/2512.13564" rel="noopener noreferrer"&gt;arXiv:2512.13564&lt;/a&gt;, &lt;a href="https://arxiv.org/abs/2603.07670" rel="noopener noreferrer"&gt;arXiv:2603.07670&lt;/a&gt;) formalize memory as a write–&lt;em&gt;manage&lt;/em&gt;–read loop — with "manage" doing the heavy lifting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The marketing said "bigger window." The research says "better librarian."&lt;/p&gt;

&lt;h2&gt;
  
  
  Why more context makes things worse
&lt;/h2&gt;

&lt;p&gt;Three mechanisms, all well-documented:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Attention is a budget, not a spotlight.&lt;/strong&gt; Every token in context competes for attention mass. Pack in 50k tokens of tool output and the three lines that matter are now competing with 49,900 tokens of noise. Needle-in-a-haystack benchmarks — the ones vendors publish — test &lt;em&gt;retrieval&lt;/em&gt; of a planted string, which models are good at. Real tasks require &lt;em&gt;reasoning over&lt;/em&gt; the context, which degrades much faster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Distractors don't just dilute — they actively mislead.&lt;/strong&gt; The context-rot findings show semantically-similar-but-irrelevant content is worse than random filler. Your agent's context is &lt;em&gt;full&lt;/em&gt; of this: old versions of the file it's editing, error messages from an already-fixed bug, grep hits from a deprecated module. Each is a plausible-looking wrong answer sitting one attention head away.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Position effects compound over turns.&lt;/strong&gt; Agents append. Every turn pushes the important early material (the task! the constraints!) toward the middle of the context — the attention dead zone — while burying the recent signal under boilerplate tool output. A long agent session is a machine for constructing worst-case attention layouts.&lt;/p&gt;

&lt;p&gt;Coding agents are the pathological case of all three at once: they generate enormous, distractor-dense, structurally-repetitive context (JSON tool results, file dumps, test logs) at machine speed, across dozens of turns.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the research says works instead
&lt;/h2&gt;

&lt;p&gt;The successful systems in the literature share a shape — they spend compute &lt;em&gt;deciding what the model sees&lt;/em&gt; instead of showing it everything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Selection over inclusion.&lt;/strong&gt; Hindsight's four memory networks (facts vs. experiences vs. summaries vs. beliefs) exist so retrieval pulls the &lt;em&gt;right kind&lt;/em&gt; of memory for each question. The general lesson: retrieval into a small context beats residence in a big one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compression as a first-class operation.&lt;/strong&gt; Active-context-compression agents treat "shrink my working set" as an action the agent itself takes, on par with tool calls. Summarize the resolved, drop the superseded, keep the live.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structure beats soup.&lt;/strong&gt; Tabular, labeled, deduplicated context consistently outperforms raw dumps of the same information — the model spends attention on content, not parsing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What you can do about it today
&lt;/h2&gt;

&lt;p&gt;You don't need a research memory system to benefit:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Treat context as a liability with interest, not an asset.&lt;/strong&gt; Every tool result you leave in the window is re-read (and re-billed) every subsequent turn, while making each turn slightly dumber.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compact aggressively and early.&lt;/strong&gt; Whatever your agent's compaction/clear mechanism is (&lt;code&gt;/compact&lt;/code&gt;, &lt;code&gt;/clear&lt;/code&gt;, fresh sessions per task), use it &lt;em&gt;before&lt;/em&gt; quality degrades — by the time you notice the agent going in circles, the context has been hurting you for many turns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope sessions to tasks.&lt;/strong&gt; One task, one session. The 40-turn omnibus session is the exact scenario the position-effect research warns about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolate research from execution.&lt;/strong&gt; Subagents (or separate sessions) that read a lot and report a little are context firewalls: the summary crosses over; the 30k tokens of grep output don't.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compress tool outputs before they enter context.&lt;/strong&gt; Test logs, JSON blobs, and directory listings compress 40–90% with zero information the model actually needs lost. Whether you do it with a proxy layer (this is the part where I mention that's what &lt;a href="https://github.com/Fast-Editor/Lynkr" rel="noopener noreferrer"&gt;Lynkr&lt;/a&gt; does), a harness setting, or a wrapper script — do it somewhere.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The next time a model launch leads with context-window size, remember the 20B model with a good librarian beating GPT-4o with a hoard. Capacity isn't capability. Curation is.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Primary sources:&lt;/strong&gt; &lt;a href="https://arxiv.org/abs/2512.12818" rel="noopener noreferrer"&gt;Hindsight (arXiv:2512.12818)&lt;/a&gt; · &lt;a href="https://github.com/vectorize-io/hindsight-benchmarks" rel="noopener noreferrer"&gt;benchmark data&lt;/a&gt; · &lt;a href="https://arxiv.org/abs/2307.03172" rel="noopener noreferrer"&gt;Lost in the Middle (Liu et al.)&lt;/a&gt; · &lt;a href="https://arxiv.org/abs/2601.07190" rel="noopener noreferrer"&gt;Active Context Compression (arXiv:2601.07190)&lt;/a&gt; · &lt;a href="https://arxiv.org/abs/2512.13564" rel="noopener noreferrer"&gt;Memory in the Age of AI Agents — survey (arXiv:2512.13564)&lt;/a&gt; · &lt;a href="https://arxiv.org/abs/2603.07670" rel="noopener noreferrer"&gt;Memory for Autonomous LLM Agents — survey (arXiv:2603.07670)&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>machinelearning</category>
      <category>devtools</category>
    </item>
    <item>
      <title>How We Built an Agentic-Task Detector for LLM Routing</title>
      <dc:creator>Lynkr</dc:creator>
      <pubDate>Tue, 14 Jul 2026 04:18:47 +0000</pubDate>
      <link>https://dev.to/lynkr/how-we-built-an-agentic-task-detector-for-llm-routing-4eb4</link>
      <guid>https://dev.to/lynkr/how-we-built-an-agentic-task-detector-for-llm-routing-4eb4</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I maintain &lt;a href="https://github.com/Fast-Editor/Lynkr" rel="noopener noreferrer"&gt;Lynkr&lt;/a&gt;, the open-source LLM router whose agentic detector this post dissects. Every snippet below is real, shipping code — &lt;a href="https://github.com/Fast-Editor/Lynkr/blob/main/src/routing/agentic-detector.js" rel="noopener noreferrer"&gt;read the whole file here&lt;/a&gt;, it's 350 lines.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;"Fix the auth bug in session.js."&lt;/p&gt;

&lt;p&gt;Eight words. Every token-count heuristic on earth routes this to the small, cheap model — it's &lt;em&gt;short&lt;/em&gt;. And every one of them is wrong, because those eight words are about to unleash a grep → read → edit → test loop with exact-string file edits, the precise workload where small models fumble tool calls and kill sessions.&lt;/p&gt;

&lt;p&gt;The inverse request — three paragraphs asking for a detailed comparison of locking strategies — &lt;em&gt;looks&lt;/em&gt; expensive and routes safely to a free local model, because it's pure text generation. Size and stakes are nearly uncorrelated in coding-agent traffic. So the router's real job is detecting &lt;strong&gt;agentic intent&lt;/strong&gt;, and this post is a tour of how Lynkr's detector does it: the signals, the weights, the classification ladder — and the embarrassing false positive that almost made the whole thing useless.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not "agentic: yes/no" — a ladder
&lt;/h2&gt;

&lt;p&gt;The first design decision: agentic-ness isn't boolean. The detector classifies requests into four types, each with a minimum tier floor and a score boost fed into the &lt;a href="https://dev.to/lynkr/how-a-13-dimension-complexity-scorer-decides-which-model-gets-your-request-e95"&gt;complexity scorer&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AGENT_TYPES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;SINGLE_SHOT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;minTier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SIMPLE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="na"&gt;scoreBoost&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="c1"&gt;// request-response, no tools&lt;/span&gt;
  &lt;span class="na"&gt;TOOL_CHAIN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;minTier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MEDIUM&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="na"&gt;scoreBoost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;// read -&amp;gt; edit -&amp;gt; test&lt;/span&gt;
  &lt;span class="na"&gt;ITERATIVE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;minTier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;COMPLEX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="na"&gt;scoreBoost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;// retry loops, debugging cycles&lt;/span&gt;
  &lt;span class="na"&gt;AUTONOMOUS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;minTier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;REASONING&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;scoreBoost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;// "figure it out", full autonomy&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;minTier&lt;/code&gt; is a floor, not a suggestion: even if every other dimension scores low, an ITERATIVE request cannot route below the COMPLEX tier. Mid-debugging-loop is the worst possible moment to hand the session to a 7B model.&lt;/p&gt;

&lt;h2&gt;
  
  
  The six signals
&lt;/h2&gt;

&lt;p&gt;Each request accumulates a score from six independent signals. The interesting part is &lt;em&gt;why&lt;/em&gt; each one exists:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Tool count&lt;/strong&gt; (up to +25). Many tools attached usually means the client is prepared for multi-step work. &lt;em&gt;Usually.&lt;/em&gt; This signal is also the source of the great false positive — hold that thought.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Agentic tools specifically&lt;/strong&gt; (up to +25). Not all tools are equal evidence. &lt;code&gt;Bash&lt;/code&gt;, &lt;code&gt;Write&lt;/code&gt;, &lt;code&gt;Edit&lt;/code&gt;, &lt;code&gt;Task&lt;/code&gt;, git and test runners form an explicit set — these mutate state, and their presence signals mutation work. A request that can only &lt;code&gt;Read&lt;/code&gt;/&lt;code&gt;Grep&lt;/code&gt;/&lt;code&gt;WebSearch&lt;/code&gt; sits in a separate read-only set and earns nothing here. Two requests with five tools each can be night and day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Prior tool results&lt;/strong&gt; (up to +30 — the heaviest signal). If the conversation already contains &lt;code&gt;tool_result&lt;/code&gt; blocks, you're not predicting an agentic loop — you're &lt;em&gt;inside&lt;/em&gt; one. More than five results means a deep loop with accumulated exact state (file contents, error strings); downgrading the model now throws away the context discipline keeping that loop convergent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Language patterns&lt;/strong&gt; (up to +25 each). Regexes over the last user message:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;tool-chain&lt;/em&gt;: "then use", "after that", "step 2"&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;iterative&lt;/em&gt;: "keep trying", "until", "retry", "debug"&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;autonomous&lt;/em&gt;: "figure out", "make it work", "on your own", "whatever it takes"&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;multi-file&lt;/em&gt;: "across the codebase", "refactor entire", "everywhere"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plus a combination rule: "implement" alone is +10-ish planning noise, but "implement" &lt;em&gt;and&lt;/em&gt; "test/verify/make sure" in the same request is +15 — build-and-verify phrasing is a reliable tell of real work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Conversation depth&lt;/strong&gt; (up to +20). Fifteen-plus messages means established context and momentum.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Prompt length&lt;/strong&gt; (+10). The weakest signal, deliberately — see the opening paragraph.&lt;/p&gt;

&lt;p&gt;Score ≥ 25 → the request is agentic. The classification ladder then applies both thresholds &lt;em&gt;and&lt;/em&gt; signal combinations — AUTONOMOUS needs score ≥ 60, or an explicit autonomous phrase with score ≥ 40. A phrase alone doesn't do it; a high score without autonomous language doesn't either, unless it's overwhelming.&lt;/p&gt;

&lt;h2&gt;
  
  
  The false positive that almost sank it
&lt;/h2&gt;

&lt;p&gt;Early versions had a humiliating problem: &lt;strong&gt;every single Claude Code request scored agentic.&lt;/strong&gt; Including "hello."&lt;/p&gt;

&lt;p&gt;Why? Claude Code attaches its full tool loadout — Read, Write, Edit, Bash, Grep, Glob, Task, and friends — to &lt;em&gt;every&lt;/em&gt; request, even a greeting. Signals 1 and 2 saw 11+ tools, four of them mutating, on everything. Every request cleared the threshold, every request routed to expensive tiers, and the router's entire value proposition — savings — evaporated. The detector was technically working and practically useless.&lt;/p&gt;

&lt;p&gt;The fix ships as &lt;strong&gt;client profiles&lt;/strong&gt;: known harnesses (Claude Code, Cursor, Codex CLI) have documented baseline loadouts, and the tool-count signals score only the tools &lt;em&gt;beyond&lt;/em&gt; that baseline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Signals 1 &amp;amp; 2 score only tools BEYOND the harness's baseline loadout —&lt;/span&gt;
&lt;span class="c1"&gt;// Claude Code's 11 always-attached tools shouldn't count as "agentic&lt;/span&gt;
&lt;span class="c1"&gt;// intent" on their own.&lt;/span&gt;
&lt;span class="nx"&gt;toolsForScoring&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;clientProfiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;effectiveTools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Crucially, signals 3–6 still use the full payload — prior tool results and conversational language are &lt;em&gt;genuine&lt;/em&gt; evidence regardless of which harness sent them. Only the tool-presence signals get the subtraction, because only they are polluted by the harness's constant.&lt;/p&gt;

&lt;p&gt;And for traffic from harnesses we've never seen? A guard: if &lt;em&gt;every&lt;/em&gt; attached tool looks like a standard baseline and there are 10+ of them, the tool-count signals zero out rather than fire:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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;clientProfiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allToolsAreBaseline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;rawTools&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;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Unknown harness that looks like Claude Code / Cursor / Codex —&lt;/span&gt;
  &lt;span class="c1"&gt;// zero out the tool-count signals to avoid the same trap.&lt;/span&gt;
  &lt;span class="nx"&gt;toolsForScoring&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nx"&gt;scoringNote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unknown_harness_guard&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;Better to under-detect and lean on the five uncorrupted signals than to re-create the everything-is-agentic bug for unknown clients.&lt;/p&gt;

&lt;p&gt;One subtle consequence, preserved as a comment in the source: with the baseline subtracted, tool counts rarely reach the AUTONOMOUS threshold on their own — so the &lt;em&gt;autonomous phrase pattern&lt;/em&gt; becomes the primary path to the top classification. The signal design acknowledges its own post-fix physics.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it still gets wrong
&lt;/h2&gt;

&lt;p&gt;Honesty section. Known limitations, from the code itself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It reads only the last user message.&lt;/strong&gt; "Do what I described above" carries the intent of an earlier message the regexes never see. Conversation-depth and tool-result signals partially compensate — but pattern detection is myopic by design (scanning full history was too noisy).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regexes can't tell mention from intent.&lt;/strong&gt; "Why did the retry loop break?" trips the iterative pattern despite being a read-only question. In practice this fails &lt;em&gt;safe&lt;/em&gt; — over-routing a question up-tier costs cents, under-routing an edit session down-tier costs the session — but it's still a false positive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;English only.&lt;/strong&gt; The patterns are English regexes; agentic intent in other languages leans entirely on the structural signals.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every detection returns its full evidence — score, signal list with weights, classification, and a &lt;code&gt;scoringNote&lt;/code&gt; explaining any baseline subtraction — so when the router misjudges, the telemetry shows exactly which signal lied. Debuggability was a design requirement: a routing layer you can't interrogate is a routing layer you'll eventually rip out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways if you're building anything similar
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Subtract the constant before reading the signal.&lt;/strong&gt; Whatever your equivalent of "the harness always attaches 11 tools" is — find it and remove it, or every request looks the same.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate "prepared for tools" from "already using tools."&lt;/strong&gt; Attached tools are weak evidence; &lt;code&gt;tool_result&lt;/code&gt; blocks in the conversation are near-proof.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fail toward the expensive model.&lt;/strong&gt; Asymmetric costs mean your threshold should be calibrated so mistakes over-spend pennies rather than break sessions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make the detector explain itself.&lt;/strong&gt; A score without a signal list is a black box you'll never be able to tune.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The whole detector is 350 lines of dependency-free JavaScript: &lt;a href="https://github.com/Fast-Editor/Lynkr/blob/main/src/routing/agentic-detector.js" rel="noopener noreferrer"&gt;src/routing/agentic-detector.js&lt;/a&gt;. Steal it, or tell me which of your prompts it would misjudge — the failure cases are the roadmap.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>opensource</category>
      <category>node</category>
    </item>
    <item>
      <title>Choosing a Local Tier for Your Coding Agent (July 2026 Edition)</title>
      <dc:creator>Lynkr</dc:creator>
      <pubDate>Wed, 08 Jul 2026 00:14:16 +0000</pubDate>
      <link>https://dev.to/lynkr/choosing-a-local-tier-for-your-coding-agent-july-2026-edition-2196</link>
      <guid>https://dev.to/lynkr/choosing-a-local-tier-for-your-coding-agent-july-2026-edition-2196</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I maintain &lt;a href="https://github.com/Fast-Editor/Lynkr" rel="noopener noreferrer"&gt;Lynkr&lt;/a&gt;, the open-source router used in the config examples. The benchmark figures below are third-party or vendor-reported (flagged where vendor-only) — I haven't independently benchmarked these models yet; the point of this post is to help you match models to request classes and test on your own workload.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;June 2026 was the busiest month for open-weight coding models in recent memory: GLM-5.2, MiniMax M3, Kimi K2.7 Code, Gemma 4, and NVIDIA's Nemotron 3 Ultra all landed within weeks. If you route your coding agent's simple requests to a local model — the "cloud architect, local coder" pattern — your options just changed meaningfully.&lt;/p&gt;

&lt;p&gt;Here's how I'd map the current field onto routing tiers, by hardware budget and by what each model can &lt;em&gt;safely&lt;/em&gt; own.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, the trap: "best open model" ≠ "your local tier"
&lt;/h2&gt;

&lt;p&gt;The headline model of the month, &lt;a href="https://techsy.io/en/blog/best-open-source-llms-2026" rel="noopener noreferrer"&gt;GLM-5.2&lt;/a&gt;, scores 62.1% on SWE-bench Pro — above GPT-5.5. It is also a 744B-parameter MoE whose 2-bit quant alone wants ~245 GB of memory. That's an open-&lt;em&gt;weight&lt;/em&gt; model, not a local model; for self-hosters it's a $40k-rig proposition (&lt;a href="https://aiweekly.co/node/5306" rel="noopener noreferrer"&gt;one published build&lt;/a&gt; runs it on four RTX PRO 6000s). The same goes for DeepSeek-V4 Pro and MiniMax M3: superb models you'll realistically consume via API, where they belong in your COMPLEX/REASONING tiers, not your local one.&lt;/p&gt;

&lt;p&gt;Your local tier is decided by a harsher question: &lt;strong&gt;what fits in your VRAM and still makes reliable tool calls?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The local field, by hardware budget
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;~16 GB RAM (ordinary laptop): Gemma 4 12B.&lt;/strong&gt; Released June 3 as a dense 12B that genuinely fits consumer RAM (&lt;a href="https://www.sitepoint.com/local-llms-are-getting-easier-the-complete-guide-2026/" rel="noopener noreferrer"&gt;SitePoint's guide&lt;/a&gt;). Apache-2.0-class licensing with no usage clauses. This is a SIMPLE-tier model: explanations, one-liners, commit messages, "what does this error mean." I would not hand it an Edit tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;24 GB GPU (RTX 3090/4090 class): Qwen3.6-27B — still the default answer.&lt;/strong&gt; The community's consensus "local Claude" since April: within a few points of frontier models on SWE-bench Verified (77.2 reported vs Claude's 80.9 — &lt;a href="https://codersera.com/blog/qwen-3-6-as-local-claude-code-replacement-2026/" rel="noopener noreferrer"&gt;analysis&lt;/a&gt;), Apache-2.0, runs quantized on a single 24 GB card or a ~$2k build. Its known weakness is exactly the one that matters for agents: tool-call reliability drifts in long contexts — fine as a &lt;em&gt;supervised&lt;/em&gt; MEDIUM tier, risky as an unsupervised COMPLEX one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agentic multi-file edits on similar hardware: Devstral Small 2.&lt;/strong&gt; Purpose-built for multi-file, tool-driven coding rather than chat (&lt;a href="https://www.kdnuggets.com/top-7-coding-models-you-can-run-locally-in-2026" rel="noopener noreferrer"&gt;KDnuggets roundup&lt;/a&gt;). If your traffic is edit-heavy, it can arguably take MEDIUM-tier mutation requests that I'd keep away from general chat models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Autocomplete-shaped work: Codestral 22B&lt;/strong&gt; is fast and good at it — but mind the non-commercial license before using it for work.&lt;/p&gt;

&lt;p&gt;One rule that keeps proving out (&lt;a href="https://pinggy.io/blog/best_open_source_self_hosted_llms_for_coding/" rel="noopener noreferrer"&gt;Pinggy's guide&lt;/a&gt;): &lt;strong&gt;within the same memory budget, a bigger model at Q4 usually beats a smaller one at Q8.&lt;/strong&gt; Quantization choice matters nearly as much as model family.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mapping to tiers
&lt;/h2&gt;

&lt;p&gt;Putting that together into a routing config (Lynkr shown; the mapping logic applies to any router):&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="c"&gt;# 24 GB GPU + API keys for the hard stuff&lt;/span&gt;
&lt;span class="nv"&gt;TIER_SIMPLE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ollama:gemma4:12b            &lt;span class="c"&gt;# trivia, explanations, greetings&lt;/span&gt;
&lt;span class="nv"&gt;TIER_MEDIUM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ollama:qwen3.6:27b           &lt;span class="c"&gt;# code questions, supervised edits&lt;/span&gt;
&lt;span class="nv"&gt;TIER_COMPLEX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;deepseek:deepseek-v4-flash  &lt;span class="c"&gt;# tool-heavy mutations, via API&lt;/span&gt;
&lt;span class="nv"&gt;TIER_REASONING&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;deepseek:deepseek-v4      &lt;span class="c"&gt;# architecture, multi-step planning&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why V4 Flash for COMPLEX: it's the first open-weight model teams report dropping into real agentic pipelines as a frontier substitute &lt;em&gt;on price&lt;/em&gt; (&lt;a href="https://openrouter.ai/blog/insights/the-open-weight-models-that-matter-june-2026/" rel="noopener noreferrer"&gt;OpenRouter's June analysis&lt;/a&gt;) — the cheapest "won't break the session" option right now. Kimi K2.7 Code (vendor-reported 58.6% SWE-bench Pro at ~30% fewer reasoning tokens) and GLM-5.2 are strong API-tier alternatives; all the June day-one numbers are vendor-reported, so treat them as directional until LiveBench catches up.&lt;/p&gt;

&lt;p&gt;The key discipline: &lt;strong&gt;the boundary between MEDIUM and COMPLEX should not be "how big is the request" but "will tools mutate state."&lt;/strong&gt; Local models in this class handle read-and-explain reliably; exact-match edits and bash execution are where they still break sessions — I wrote up those failure modes &lt;a href="https://dev.to/lynkr/routing-down-is-easy-knowing-when-not-to-is-hard-why-cheap-models-break-your-coding-agent-4g33"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed vs three months ago
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The floor rose.&lt;/strong&gt; A 16 GB laptop now runs a genuinely useful SIMPLE tier (Gemma 4). Six months ago that tier meant 3B models that couldn't be trusted with a paragraph.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The open-weight ceiling now beats proprietary on some coding benchmarks&lt;/strong&gt; (GLM-5.2 &amp;gt; GPT-5.5 on SWE-bench Pro) — but at server scale, which &lt;em&gt;strengthens&lt;/em&gt; the hybrid pattern: open models via cheap APIs up top, small open models on your metal below.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MoE won.&lt;/strong&gt; Every serious June release is Mixture-of-Experts. For self-hosters this cuts both ways: better quality-per-active-param, but total memory footprints that keep the top tier out of reach.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Licensing is consolidating&lt;/strong&gt; around MIT (DeepSeek) and Apache-2.0 (Qwen, Gemma) for the models you'd actually build on.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Test on your traffic, not on benchmarks
&lt;/h2&gt;

&lt;p&gt;Every number above is someone else's workload. The honest way to pick your local tier: route a week of your real traffic through whatever candidates fit your hardware, and count &lt;em&gt;session survival&lt;/em&gt; — how often the local model's tool calls held up — not just benchmark deltas. That's a one-line config change per candidate, and your own telemetry will contradict at least one thing this post told you.&lt;/p&gt;

&lt;p&gt;Lynkr is Apache-2.0, self-hosted, and treats every model above as a first-class routing tier: &lt;a href="https://github.com/Fast-Editor/Lynkr" rel="noopener noreferrer"&gt;github.com/Fast-Editor/Lynkr&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>opensource</category>
      <category>ollama</category>
    </item>
    <item>
      <title>Routing Down Is Easy. Knowing When Not To Is Hard: Why Cheap Models Break Your Coding Agent</title>
      <dc:creator>Lynkr</dc:creator>
      <pubDate>Wed, 08 Jul 2026 00:08:36 +0000</pubDate>
      <link>https://dev.to/lynkr/routing-down-is-easy-knowing-when-not-to-is-hard-why-cheap-models-break-your-coding-agent-4g33</link>
      <guid>https://dev.to/lynkr/routing-down-is-easy-knowing-when-not-to-is-hard-why-cheap-models-break-your-coding-agent-4g33</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I maintain &lt;a href="https://github.com/Fast-Editor/Lynkr" rel="noopener noreferrer"&gt;Lynkr&lt;/a&gt;, an open-source router whose design decisions this post explains. The failure modes described are patterns widely reported across router issue trackers and local-LLM forums — the examples are representative reconstructions, not captured transcripts. The problem is real either way; ask anyone who's routed a coding agent to a 7B model.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Everyone who gets their first LLM router working does the same thing within the hour: point the expensive coding agent at a free local model and watch the bill drop to zero.&lt;/p&gt;

&lt;p&gt;Then the agent tries to edit a file.&lt;/p&gt;

&lt;h2&gt;
  
  
  The graveyard of downgraded sessions
&lt;/h2&gt;

&lt;p&gt;If you browse the issue tracker of any Claude Code router — or r/LocalLLaMA on any given week — you'll find the same story in a hundred variations. The routing works perfectly. The &lt;em&gt;session&lt;/em&gt; dies anyway. The killers, in rough order of frequency:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Malformed tool arguments.&lt;/strong&gt; The agent decides to call &lt;code&gt;Edit&lt;/code&gt;, and the model produces arguments that are &lt;em&gt;almost&lt;/em&gt; JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"file_path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/auth.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"old_string"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"if (token) {"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"new_string"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"if (token &amp;amp;&amp;amp; !expired) {"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One missing brace. The harness rejects the call, the model retries, produces a different malformation, and you're three turns deep into fixing nothing. Frontier models emit structurally valid tool calls with boring reliability; sub-10B models do it &lt;em&gt;most&lt;/em&gt; of the time — and "most of the time," at 30 tool calls per session, means every session breaks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Stale string matching.&lt;/strong&gt; &lt;code&gt;Edit&lt;/code&gt;-style tools require the &lt;code&gt;old_string&lt;/code&gt; to match the file exactly. Small models paraphrase from memory instead of quoting — they'll "remember" the line as &lt;code&gt;if (token) {&lt;/code&gt; when the file says &lt;code&gt;if (accessToken) {&lt;/code&gt;. The edit fails, the model re-reads the file, burns 2,000 tokens, tries again with a different paraphrase. This is the single most reported failure, because it &lt;em&gt;looks&lt;/em&gt; like the router's fault and is actually a capability cliff.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Hallucinated context.&lt;/strong&gt; Ask a small model to run tests and it may confidently call &lt;code&gt;Bash&lt;/code&gt; with &lt;code&gt;npm test -- --grep "auth"&lt;/code&gt; in a repo that uses pytest. It's not being stupid — it's pattern-completing from training data instead of the conversation, because instruction-following degrades faster than fluency as models shrink.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. The infinite loop.&lt;/strong&gt; The subtlest one: the model calls &lt;code&gt;Read&lt;/code&gt; on the same file five times in a row, or greps, reads, greps the same term again. Weak models lose the thread of &lt;em&gt;what they already know&lt;/em&gt; in long agentic contexts. Nothing errors — the session just stops converging while tokens burn.&lt;/p&gt;

&lt;p&gt;Here's the uncomfortable part: &lt;strong&gt;none of these are the router's bug, and all of them are the router's fault.&lt;/strong&gt; The router made a judgment — "this request is cheap-model-safe" — and the judgment was wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the obvious heuristics misjudge
&lt;/h2&gt;

&lt;p&gt;Most routing setups decide with static rules: token thresholds, keyword lists, scenario slots. These fail in a specific, predictable way: &lt;strong&gt;they measure the request's size, not its stakes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"Fix the auth bug in session.js" is eight words. Every token-based rule on earth routes it to the small model. But those eight words unleash a read-grep-edit-test loop — the exact workload where small models faceplant. Meanwhile, "explain the difference between optimistic and pessimistic locking, with examples" looks expensive (long answer, technical vocabulary) and is actually &lt;em&gt;perfectly&lt;/em&gt; cheap-model-safe: it's pure text generation, no tool calls, no exact string matching, nothing to break.&lt;/p&gt;

&lt;p&gt;Size and stakes are almost uncorrelated in agentic traffic. That's the whole problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "stakes-aware" routing looks like
&lt;/h2&gt;

&lt;p&gt;When I built &lt;a href="https://github.com/Fast-Editor/Lynkr" rel="noopener noreferrer"&gt;Lynkr&lt;/a&gt;'s router, most of the design ended up being about &lt;em&gt;when not to save money&lt;/em&gt;. The parts that matter:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Weight the tools, not just their count.&lt;/strong&gt; A request where &lt;code&gt;Grep&lt;/code&gt; and &lt;code&gt;Read&lt;/code&gt; are in play is research — paraphrase-tolerant, failure-tolerant, ideal for a local model. A request where &lt;code&gt;Bash&lt;/code&gt;, &lt;code&gt;Write&lt;/code&gt;, or &lt;code&gt;Edit&lt;/code&gt; will fire is a mutation with exact-match requirements. Lynkr assigns each tool a risk weight (&lt;code&gt;Bash&lt;/code&gt; 0.9, &lt;code&gt;Write&lt;/code&gt; 0.8, &lt;code&gt;Edit&lt;/code&gt; 0.7 … &lt;code&gt;Grep&lt;/code&gt; 0.2) and scores the request's &lt;em&gt;effective&lt;/em&gt; toolset. Two requests with five tools each can land tiers apart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Treat mid-session as a signal.&lt;/strong&gt; If the conversation already contains three tool results, you're inside an agentic flow with accumulated exact-state (file contents, error strings). Downgrading the model mid-flow throws away the one thing that was keeping the loop convergent. Prior tool usage and conversation depth push requests &lt;em&gt;up&lt;/em&gt;-tier even when the latest message is short.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Subtract the harness baseline.&lt;/strong&gt; Claude Code ships ~14 tool schemas with every request — including "hello." Count them naively and everything looks agentic, so nothing ever routes local and you save nothing. Score only the tools the request could plausibly use, and the safe majority routes down while the risky minority stays up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Some patterns override everything.&lt;/strong&gt; Greetings and "what does X do" questions force-route local, always. Security-sensitive analysis force-routes to the strong tier, always — a JWT architecture question is short, toolless, and precisely the wrong place to save four cents.&lt;/p&gt;

&lt;p&gt;The result on my own traffic: 70–90% of requests route to free local models — but they're the &lt;em&gt;right&lt;/em&gt; 70–90%, which is the entire difference between "my bill dropped" and "my agent broke."&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways, router-agnostic
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Route research down, mutations up.&lt;/strong&gt; If your router can't tell a &lt;code&gt;Grep&lt;/code&gt; request from an &lt;code&gt;Edit&lt;/code&gt; request, it isn't routing — it's gambling on which sessions break.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never downgrade mid-loop.&lt;/strong&gt; Model consistency across an agentic sequence is worth more than the marginal savings of one cheap turn.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure session survival, not just cost.&lt;/strong&gt; A routing setup that saves 60% and breaks one session in five is more expensive than the bill it replaced — you're paying in re-runs and rage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The ceiling is rising.&lt;/strong&gt; Local models' tool-calling improves every quarter; the set of safely-downgradable requests grows with it. A router with per-tool judgment gets to expand that set gradually. A token threshold has to guess again from scratch.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The router's job was never "pick the cheapest model." It's "pick the cheapest model &lt;em&gt;that won't break the session&lt;/em&gt;" — and those five extra words are where all the engineering lives.&lt;/p&gt;

&lt;p&gt;The scorer described here is ~1,000 lines of readable Apache-2.0 JavaScript: &lt;a href="https://github.com/Fast-Editor/Lynkr/blob/main/src/routing/complexity-analyzer.js" rel="noopener noreferrer"&gt;src/routing/complexity-analyzer.js&lt;/a&gt;. Steal the design, or file an issue telling me where it misjudges — the failure cases are the interesting part.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>opensource</category>
      <category>devtools</category>
    </item>
    <item>
      <title>The 5% Router Tax: What Hosted LLM Gateways Charge For (and How to Self-Host It)</title>
      <dc:creator>Lynkr</dc:creator>
      <pubDate>Sun, 05 Jul 2026 08:48:56 +0000</pubDate>
      <link>https://dev.to/lynkr/the-5-router-tax-what-hosted-llm-gateways-charge-for-and-how-to-self-host-it-513</link>
      <guid>https://dev.to/lynkr/the-5-router-tax-what-hosted-llm-gateways-charge-for-and-how-to-self-host-it-513</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I maintain &lt;a href="https://github.com/Fast-Editor/Lynkr" rel="noopener noreferrer"&gt;Lynkr&lt;/a&gt;, the self-hosted gateway discussed in the second half. OpenRouter and Requesty are good products — this post is about understanding what you're paying for so you can decide whether you need to.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Hosted LLM routers had a huge 2026 — OpenRouter alone pushes 25 trillion tokens a week. The pitch is real: one API key, 400+ models, automatic failover. The price is a &lt;strong&gt;~5% fee on every token you route&lt;/strong&gt; (5.5% on OpenRouter credits, 5% on Requesty), plus a subtler cost: every prompt, every file your coding agent reads, every secret that leaks into a context window transits their infrastructure.&lt;/p&gt;

&lt;p&gt;For a hobby project, 5% of a small bill is nothing and the convenience wins. For an agentic coding workload — where teams routinely spend $500–$2,000 per engineer per month — 5% is real money, and the data-transit question stops being academic. So it's worth asking precisely: &lt;strong&gt;what does the hosted router actually do for that fee, and which parts can you self-host?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What the fee buys
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Unified API across providers&lt;/strong&gt; — one format in, translated per-provider out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failover&lt;/strong&gt; — a provider 500s, your request retries elsewhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model marketplace&lt;/strong&gt; — new models available the day they launch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consolidated billing&lt;/strong&gt; — one invoice instead of six provider accounts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;(Sometimes) smart routing&lt;/strong&gt; — OpenRouter's &lt;code&gt;auto&lt;/code&gt; router picks a model per-request.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Items 1, 2, and 5 are software. Items 3 and 4 are genuinely hard to self-host — if you want day-one access to every new model with zero account setup, the marketplace earns its fee. But most coding workloads use a handful of models, not four hundred.&lt;/p&gt;

&lt;h2&gt;
  
  
  The parts a hosted router structurally can't give you
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Local models as a tier.&lt;/strong&gt; No hosted router will route your easy requests to the Ollama instance on your own machine — free, private, zero latency to first byte on cached weights. For coding traffic, where (in my instrumented sessions) 70–90% of requests are simple enough for a good local model, this is the single biggest cost lever, and it's only available to something running on your side of the wire.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your data staying home.&lt;/strong&gt; Self-hosted means prompts, code, and keys never transit a third party. For anyone with a compliance requirement — or code they'd rather not ship to a router's logs — this isn't a preference, it's a prerequisite.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token optimization before the bill.&lt;/strong&gt; A hosted router bills you for the tokens you send it — it has no incentive to shrink them. A self-hosted proxy can strip unusable tool schemas (measured: −53% on tool-heavy requests) and compress JSON tool results (measured: 3,458 → 427 tokens on a grep result) &lt;em&gt;before&lt;/em&gt; any provider bills you. That's not a routing saving; it stacks on top of routing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No availability dependency.&lt;/strong&gt; Hosted routers go down (OpenRouter's outages have their own HN threads) and offer no SLA at consumer tiers. A local proxy fails independently of anyone's status page.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What self-hosting costs you
&lt;/h2&gt;

&lt;p&gt;Honesty cuts both ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You run a process.&lt;/strong&gt; &lt;code&gt;npm install -g lynkr &amp;amp;&amp;amp; lynkr init &amp;amp;&amp;amp; lynkr start&lt;/code&gt; — but it's yours now: updates, logs, the works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You manage provider accounts.&lt;/strong&gt; Two or three API keys instead of one. The consolidated invoice is genuinely gone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model lag.&lt;/strong&gt; A new provider means waiting for support (or a PR) instead of it appearing in a dropdown.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nobody to email.&lt;/strong&gt; Self-hosted support is a GitHub issue tracker.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If those trade-offs read as "fine," the math is straightforward: the 5% fee disappears, the local-tier routing removes the easy majority of requests from your bill entirely, and compression shrinks what's left.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hybrid that actually makes sense
&lt;/h2&gt;

&lt;p&gt;This isn't either/or. A pattern I see working:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Coding tool → self-hosted proxy (Lynkr)
                ├─ SIMPLE/MEDIUM  → local Ollama/llama.cpp   (free)
                ├─ COMPLEX        → direct provider API keys  (no fee)
                └─ exotic models  → OpenRouter               (5% on the long tail only)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep a hosted router as &lt;em&gt;one backend&lt;/em&gt; for the long tail of models you rarely need, route the bulk directly or locally, and let the proxy's classifier decide per-request. You get the marketplace when you want it without paying the tax on your entire volume.&lt;/p&gt;

&lt;p&gt;Lynkr is Apache-2.0, self-hosted, supports 13 providers including Ollama, llama.cpp, LM Studio, Bedrock, Azure, Databricks — and OpenRouter itself as a tier: &lt;a href="https://github.com/Fast-Editor/Lynkr" rel="noopener noreferrer"&gt;github.com/Fast-Editor/Lynkr&lt;/a&gt;. Benchmarks with methodology are in the repo; run them on your own workload before believing anyone's percentages, including mine.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>selfhosted</category>
      <category>llm</category>
    </item>
    <item>
      <title>How a 13-Dimension Complexity Scorer Decides Which Model Gets Your Request</title>
      <dc:creator>Lynkr</dc:creator>
      <pubDate>Sun, 05 Jul 2026 08:48:40 +0000</pubDate>
      <link>https://dev.to/lynkr/how-a-13-dimension-complexity-scorer-decides-which-model-gets-your-request-e95</link>
      <guid>https://dev.to/lynkr/how-a-13-dimension-complexity-scorer-decides-which-model-gets-your-request-e95</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I'm the author of &lt;a href="https://github.com/Fast-Editor/Lynkr" rel="noopener noreferrer"&gt;Lynkr&lt;/a&gt;, the open-source proxy whose internals this post walks through. All code shown is real and Apache-2.0 — &lt;a href="https://github.com/Fast-Editor/Lynkr/blob/main/src/routing/complexity-analyzer.js" rel="noopener noreferrer"&gt;read it here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The most expensive default in AI coding tools is that &lt;strong&gt;model choice is a setting, not a decision&lt;/strong&gt;. You pick a model once; every request — "what does git stash do?" and "refactor this auth module" alike — goes there. Routing each request to the cheapest model that can actually handle it is worth 50%+ of most bills, but it only works if the "can actually handle it" judgment is reliable. Get it wrong downward and a small model fumbles your file edits; get it wrong upward and you've saved nothing.&lt;/p&gt;

&lt;p&gt;Here's how Lynkr makes that judgment, in enough detail that you could reimplement it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not just count tokens?
&lt;/h2&gt;

&lt;p&gt;The obvious heuristics fail in both directions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"Long request → big model"&lt;/strong&gt; fails on a 60k-token context that's mostly grep output around a trivial question.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Short request → small model"&lt;/strong&gt; fails catastrophically on "fix the auth bug in session.js" — eight words that unleash a tool-heavy agentic session a 7B model will faceplant on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Token count is &lt;em&gt;one&lt;/em&gt; signal. The failure cases all come from treating it as the only one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture: weighted dimensions, then overrides
&lt;/h2&gt;

&lt;p&gt;Every request gets a 0–100 score from 13 dimensions in four groups. The weights are configurable; these are the defaults:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DIMENSION_WEIGHTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Content Analysis (35%)&lt;/span&gt;
  &lt;span class="na"&gt;tokenCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.08&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;promptComplexity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// avg sentence length/structure&lt;/span&gt;
  &lt;span class="na"&gt;technicalDepth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// technical keyword density&lt;/span&gt;
  &lt;span class="na"&gt;domainSpecificity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.07&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// security/ML/distributed/db/frontend/devops&lt;/span&gt;
  &lt;span class="c1"&gt;// Tool Analysis (25%)&lt;/span&gt;
  &lt;span class="na"&gt;toolCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.08&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;toolComplexity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// which tools, not how many&lt;/span&gt;
  &lt;span class="na"&gt;toolChainPotential&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.07&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// "first...then", "step 2", sequencing language&lt;/span&gt;
  &lt;span class="c1"&gt;// Reasoning Requirements (25%)&lt;/span&gt;
  &lt;span class="na"&gt;multiStepReasoning&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;codeGeneration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.08&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;analysisDepth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.07&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// trade-off/comparison markers&lt;/span&gt;
  &lt;span class="c1"&gt;// Context Factors (15%)&lt;/span&gt;
  &lt;span class="na"&gt;conversationDepth&lt;/span&gt;&lt;span class="p"&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="na"&gt;priorToolUsage&lt;/span&gt;&lt;span class="p"&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;// tool_results already in the conversation&lt;/span&gt;
  &lt;span class="na"&gt;ambiguity&lt;/span&gt;&lt;span class="p"&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few design decisions worth stealing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not all tools are equal.&lt;/strong&gt; A request that can &lt;code&gt;Grep&lt;/code&gt; is not like a request that can &lt;code&gt;Bash&lt;/code&gt;. Each tool carries a hand-tuned risk weight — &lt;code&gt;Bash&lt;/code&gt; 0.9, &lt;code&gt;Write&lt;/code&gt; 0.8, &lt;code&gt;Edit&lt;/code&gt; 0.7, down to &lt;code&gt;Grep&lt;/code&gt; at 0.2. A request whose available toolset averages 0.8 is an agentic mutation session; one averaging 0.25 is read-only research. Same tool &lt;em&gt;count&lt;/em&gt;, completely different stakes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Subtract the harness baseline.&lt;/strong&gt; Claude Code ships ~14 tool schemas with &lt;em&gt;every&lt;/em&gt; request, including "hello". If you count them naively, everything looks agentic and nothing routes local. The scorer subtracts the client's constant baseline and scores only the &lt;em&gt;effective&lt;/em&gt; tools the request could plausibly use — one of those fixes that sounds trivial and changed everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conversation history is a signal.&lt;/strong&gt; Three &lt;code&gt;tool_result&lt;/code&gt; blocks already in the conversation means you're mid-agentic-flow — this is not the moment to downgrade models and break the session's momentum. &lt;code&gt;priorToolUsage&lt;/code&gt; and &lt;code&gt;conversationDepth&lt;/code&gt; push mid-session requests up-tier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ambiguity cuts the other way.&lt;/strong&gt; "file X, line 42, this error" is specific — a small model can act on it. "Something feels slow sometimes" needs interpretation before action. Specificity markers (paths, line numbers, error strings) &lt;em&gt;lower&lt;/em&gt; the score.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overrides: the classifier knows what it can't know
&lt;/h2&gt;

&lt;p&gt;Two pattern lists short-circuit the whole scoring pipeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Force-local:&lt;/strong&gt; greetings, acknowledgments, "what does X do" one-liners. Score 0, never leave the machine, no cloud tokens ever.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Force-cloud:&lt;/strong&gt; security-critical analysis, architecture decisions, anything matching high-risk patterns. Straight to the top tier regardless of how cheap it looks. A JWT-vs-cookies security question is short and toolless — every naive heuristic routes it local. This is the wrong request to save $0.004 on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On top of the regex dimensions, an AST pass (tree-sitter) scores actual code structure in the payload — cyclomatic signals beat keyword counting when real code is present.&lt;/p&gt;

&lt;h2&gt;
  
  
  From score to model
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;score &amp;lt; threshold        → SIMPLE   (e.g. ollama:qwen2.5:7b, free)
threshold..~65           → MEDIUM   (e.g. ollama:qwen2.5-coder, free)
above                    → COMPLEX  (your API key: Sonnet, GPT-4o...)
reasoning markers heavy  → REASONING (o3, DeepSeek R1...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The threshold moves with a single mode switch — &lt;code&gt;aggressive&lt;/code&gt; (60) routes more local, &lt;code&gt;conservative&lt;/code&gt; (25) routes more to the cloud, default is 40. Multi-turn conversations score with a recency-weighted sliding window, so a short follow-up ("now add tests") inherits the complexity of the work it refers to instead of scoring as a trivial one-liner.&lt;/p&gt;

&lt;p&gt;Crucially, &lt;strong&gt;the classifier only chooses among models you listed&lt;/strong&gt;. It's not an autonomous agent picking providers — you define the tiers, it picks the tier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does it work?
&lt;/h2&gt;

&lt;p&gt;In my instrumented sessions, 70–90% of requests score SIMPLE or MEDIUM and run free on local models, while tool-heavy and security-flagged requests reliably escalate. The failure mode everyone fears — cheap model breaking an agentic session — is exactly what the tool weights, baseline subtraction, and prior-tool-usage dimensions exist to prevent.&lt;/p&gt;

&lt;p&gt;Is 13 hand-weighted dimensions the optimal design? Almost certainly not — a learned router trained on outcome data would beat it eventually. But it's transparent (every routing decision logs its per-dimension breakdown), it's tunable, it runs in-process in microseconds, and it never sends your prompts to a third-party classifier API.&lt;/p&gt;

&lt;p&gt;The whole thing is readable in one sitting: &lt;a href="https://github.com/Fast-Editor/Lynkr/blob/main/src/routing/complexity-analyzer.js" rel="noopener noreferrer"&gt;src/routing/complexity-analyzer.js&lt;/a&gt;. Steal the design or use the proxy — either outcome means fewer frontier-model tokens spent on &lt;code&gt;git stash&lt;/code&gt; questions.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>node</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
