Skip to content

feat(router): port Router Core into the Python SDK - #50

Merged
VickyXAI merged 1 commit into
mainfrom
feat/router-core-port
Aug 16, 2026
Merged

feat(router): port Router Core into the Python SDK#50
VickyXAI merged 1 commit into
mainfrom
feat/router-core-port

Conversation

@VickyXAI

Copy link
Copy Markdown
Contributor

Why

The Python SDK routed on its own hand-maintained tier tables and a 14-dimension scorer, while the TypeScript SDK and the gateway had both moved to @blockrun/router-core. The same request could pick different models depending on which SDK you called, and the Python tables drifted on their own schedule.

router-core ships as an npm tarball pinned to a commit, so npm can't install it into a Python package — parity requires a port.

What

  • blockrun_llm/router_core/ — the port, at upstream commit 18bf4ab (one commit ahead of the TS SDK's pin d430804, which predates the deepseek-v4-flash NVIDIA EOL).
    • config.py is machine-transpiled from config.ts with comments preserved; the transpiled object was asserted equal to a JSON parse of the TS source, so no value was hand-copied.
    • Ported regexes compile through _js.py:js_regex, which sets re.ASCII so \b/\w/\d keep JavaScript semantics — without it patterns like \b(?:fast)\b silently stop matching inside CJK text. _js.py also ports Number.toFixed rounding and Date.parse, both of which show up in decision output.
  • blockrun_llm/router_adapter.py — port of the TS SDK's src/router-adapter.ts: free/*nvidia/* catalog id resolution, x402 payment floors, capacity filtering against the full conversation.
  • blockrun_llm/router.py — now a back-compat shim. route(), classify_by_rules() and the decision keys still work.
  • client.route(prompt, ...) — returns the routing decision without making or paying for a call (TS SDK parity).

Capabilities Python didn't have before

before after
Selection tier lookup, first priced model wins portfolio (V3) ranking on affinity / cost / speed / reliability
Capability limits none hard filters: context, output length, tools, vision
Task shape not modeled code_agent, tool_agent, reasoning_math, long_context, extraction, …
Explainability reasoning string + candidates, candidate_scores, task_type, profile, router_version
Tier config separate hand-maintained tables shared with the TS SDK and the gateway

Bugs this surfaced

  • The free profile pointed at retired models. Its table led with nvidia/deepseek-v4-flash (HTTP 410 since 2026-08-12) and fell back to nvidia/llama-4-maverick and nvidia/qwen3-coder-480b (also EOL), so free routing worked only because the gateway redirects pinned callers. It now routes the live free lineup, and the adapter drops any candidate the catalog doesn't price at $0 — a paid model can't leak into a free-profile call.
  • Catalog rows marked available: false entered the pricing map, so routing could pick a model every call to which fails non-transiently. Now skipped (matches the TS client).

Breaking-ish

routing.method is now "portfolio" by default — "rules" only for the free profile and the config-only V2 rollback. Code asserting method == "rules" needs updating. Everything else on RoutingDecision is additive.

Verification

  • 88 parity tests in tests/unit/test_router_core.py — a 1:1 port of all four upstream vitest suites (portfolio, selector, strategy, tool-intent). These are the guard that Python and TypeScript keep choosing the same model; re-port them whenever upstream is re-synced.
  • 22 adapter tests covering the host layer: id resolution, payment floor, capacity filtering, free-profile guarantees.
  • Full suite: 563 passed. black / ruff clean; mypy strict clean for the new modules.
  • Imported and routed under Python 3.9 (the CI floor) — identical decisions.
  • End-to-end against the live gateway: local decision, then a routing_profile="free" call served by nvidia/step-3.7-flash at $0.
  • Wheel build confirmed model_profiles.generated.json ships.

Version bumped to 1.11.0 (all three declarations), CHANGELOG / README / CLAUDE.md updated.

…al tier tables

The Python SDK routed on its own hand-maintained tier tables and a 14-dimension
scorer while the TypeScript SDK and the gateway had both moved to
@blockrun/router-core. The same request could pick different models on each
SDK, and the Python tables drifted independently.

router-core ships as an npm tarball pinned to a commit, so parity requires a
port: blockrun_llm/router_core/ is that port, at upstream commit 18bf4ab (one
ahead of the TS pin, which predates the deepseek-v4-flash NVIDIA EOL).
blockrun_llm/router_adapter.py ports the TS SDK's src/router-adapter.ts, and
router.py becomes a back-compat shim.

What Python did not have before: portfolio (V3) ranking instead of tier lookup,
hard capability filtering (a model that cannot hold the conversation, emit the
requested max_tokens, call tools or read images is dropped before scoring),
task classification, and explainable decisions (candidates, candidate_scores,
task_type, reasoning). Adds client.route() for a dry-run decision.

Two live bugs fell out of the port:
- The free profile pointed at models NVIDIA has retired (deepseek-v4-flash,
  410 on 2026-08-12; llama-4-maverick; qwen3-coder-480b), so free routing
  depended entirely on the gateway's redirect. It now routes the live free
  lineup, and the adapter drops any candidate the catalog does not price at $0.
- Catalog rows marked available: false no longer enter the pricing map; every
  smart call to one would have failed with a non-transient error.

Behavior change: routing.method is now "portfolio" by default ("rules" for the
free profile and the config-only V2 rollback).

tests/unit/test_router_core.py ports all four upstream vitest suites (88 cases)
as the parity guard; test_router_adapter.py covers the host layer. Verified on
Python 3.9 (the CI floor) and end-to-end against the live gateway.
@VickyXAI
VickyXAI merged commit 0fcec98 into main Aug 16, 2026
4 checks passed
@VickyXAI
VickyXAI deleted the feat/router-core-port branch August 16, 2026 00:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant