Cory Policht
← Back to the Devlog
DEVLOG · 2026-07-13 .. 2026-07-18 · RELEASE

LLM Proxy hits version three.

Nine days after releasing 2.0, the proxy cuts v3.0.1. The rapid major bump marks the complete retirement and deletion of the 14,000-line Node.js monolith in favor of the high-throughput .NET implementation.

WINDOW
Start2026-07-13
End2026-07-18
Kindrelease
Projectsllm-proxy-gateway
Delivered
  • llm-proxy-gateway
    Node.js gateway retirement and full cutover to .NET
    Deletes the legacy JavaScript gateway and test suite (-13,971 lines), promoting the .NET service to the primary gateway implementation and tagging v3.0.1.
    llm-proxy · #60llm-proxy · c475835llm-proxy · v3.0.1
  • llm-proxy-gateway
    CI workflow consolidation and container pipeline simplification
    Retires edge and multiarch build lanes, collapses redundant container workflows into a single unified publish path, and fixes runner MTU issues.
    llm-proxy · #61llm-proxy · 16c5a93
  • llm-proxy-gateway
    Unique routing decision ID minting per request
    Mints a routing_decision_id on every incoming request (IP-AAT-4), threading it through downstream headers and telemetry logs to correlate decisions with trace spans.
    llm-proxy · #66llm-proxy · 12583af
  • llm-proxy-gateway
    Escalation adoption and measurement harness
    Implements role-based escalation measurement (IP-LLMP-9) to monitor when and why local worker models defer to upstream advisor models.
    llm-proxy · #67llm-proxy · a9ad615
  • llm-proxy-gateway
    Agent thread attribution contracts and fixtures
    Lands wave-1 contract definitions and test fixtures for agent thread attribution (IP-17) across proxy dispatch paths.
    llm-proxy · #69llm-proxy · d697338

Nine days after shipping v2.0.0, llm-proxy published v3.0.1. Cutting two major versions in less than a fortnight is usually a sign of churn; here, it was a deliberate, clean-slate transition.

When v2.0.0 shipped, it carried both the established Node.js proxy and the early scaffold of a compiled .NET port. Maintaining two runtimes in parallel quickly generated friction: duplicate GitHub Actions / Gitea CI workflows, parallel Dockerfile configurations, conflicting runner cache keys, and diverging unit test suites. Instead of living with dual implementations through a prolonged deprecation cycle, PR #60 executed a clean cut.

In a single pull request, nearly 14,000 lines of JavaScript were deleted. The Node.js codebase (src/*.js) and its associated test harness were wiped, the root Dockerfile was switched over to compile the .NET solution, and the .NET runtime took over all production traffic. Because the internal architecture, configuration schema, and container boundaries shifted completely, cutting a major semantic version bump to v3.0.1 accurately reflected the break.

With the legacy runtime removed, the deployment pipeline received a much-needed pruning (#61). Multi-architecture build matrixes and speculative edge lanes were dismantled in favor of a single, hardened container publish job, eliminating MTU packet drop issues that had plagued the self-hosted runners.

The newly crowned .NET gateway then received core observability upgrades. Under PR #66 (IP-AAT-4), the gateway began minting a unique routing_decision_id at the perimeter of every request. Rather than treating proxy routing as a black box, this ID is stamped on response headers and passed downstream to telemetry collectors, correlating incoming agent requests with exact model targets and failover attempts.

Finally, PR #67 established an escalation measurement harness (IP-LLMP-9), giving the platform empirical insight into how often worker models trigger the call_advisor escape hatch versus handling turns directly on local hardware.

Lessons learned

  • Rip out parallel legacy runtimes in a clean break rather than maintaining dual stacks. Carrying both Node.js and .NET implementations meant doubled CI runs, divergent tests, and split focus. Deleting the legacy codebase in one coordinated motion cleared substantial operational baggage.
  • Mint and propagate routing decision IDs at the gateway perimeter. Attaching a unique decision ID to incoming requests before any routing logic runs allows traces, logs, and billing meters to link back to the exact routing branch chosen, even if the connection aborts mid-stream.
  • Consolidate multi-architecture container build pipelines early. Maintaining complex multi-arch build lanes for a private cluster running on homogeneous x86-64 hardware wasted runner resources and generated recurring MTU network errors during image layer uploads.