Skip to content

feat(routing): same routing surface on all four clients — Solana, message lists, virtual ids - #52

Merged
VickyXAI merged 1 commit into
mainfrom
feat/routing-parity-all-clients
Aug 19, 2026
Merged

feat(routing): same routing surface on all four clients — Solana, message lists, virtual ids#52
VickyXAI merged 1 commit into
mainfrom
feat/routing-parity-all-clients

Conversation

@VickyXAI

Copy link
Copy Markdown
Contributor

1.11.0 put Router Core in the SDK but wired it into exactly one place: the Base sync client's single-prompt path. This closes the three gaps that left.

What was missing

before after
SolanaLLMClient / AsyncSolanaLLMClient no routing at all route(), smart_chat(), smart_chat_completion()
AsyncLLMClient no routing same three
Message-list routing none smart_chat_completion() on all four
blockrun/auto | eco | premium inert strings select a routing profile from chat() / chat_completion()
Solana chat() / chat_completion() no fallback_models walks the ranked chain

A Solana user got no model selection at all, while the TypeScript SDK has offered it on both chains since 3.12.0.

Why smart_chat_completion matters separately

smart_chat routes a prompt string. The decisions that actually need routing are the ones a prompt string cannot express:

  • tools + tool_choice → a tool-agent decision, and a model that cannot call tools is filtered out before scoring.
  • response_format → forces the structured-output tier.
  • image parts → forces a vision model.
  • capacity is checked against the whole transcript, not the last message. An agent conversation can be 100x its final turn, and a context overflow is a non-transient error no fallback chain rescues.

Cross-chain equivalence

Both chains run one engine over one catalog, so the same request picks the same model. Only the x402 floor in the cost metadata differs — $0.002 Base, $0.001 Solana. Pinned by tests that route the same prompts through both clients and compare model, tier, task type and the full candidate chain.

Fixed along the way: a 429 killed the call

Both clients counted only 5xx as retriable, so a saturated upstream ended the request with capable models still in the chain. This showed up in live testing: a rate-limited free model answered 429 and the three remaining free models were never tried. The TypeScript adapter has always treated 429 as transient — this upstream is saturated, the next model is a different upstream. Settled payments and permanent payment failures are still refused before the status check, so no call can pay twice.

Also

/v1/models → pricing-map conversion moved into router_adapter.build_model_pricing(), shared by all four clients rather than written out per client. Catalog rows marked available: false are now skipped everywhere (1.11.0 did that only in the Base sync client).

Verification

  • 608 unit tests pass (45 new in test_routing_parity.py: surface parity across the four clients, virtual-id resolution, cross-chain decision equality, the free-profile guarantee on Solana, and the retriable-status matrix).
  • ruff + black clean; new modules clean under mypy strict; compiles on Python 3.9 (the CI floor).
  • Live Base gateway: chat_completion("blockrun/auto", ...) routed to google/gemini-2.5-flash and answered; a tool-required message list routed to openai/gpt-5-mini with task_type: tool_agent; the free profile through the message-list path landed on nvidia/step-3.7-flash at $0.
  • Live Solana gateway: route() returned deepseek/deepseek-v4-pro (REASONING, portfolio) and smart_chat(routing_profile="free") was served by nvidia/step-3.7-flash at $0 with a three-model chain behind it.

Version bumped to 1.12.0.

…sage lists, virtual ids

1.11.0 put Router Core in the Python SDK but wired it only into the Base sync
client's single-prompt path. Three gaps came out of that:

- SolanaLLMClient and AsyncSolanaLLMClient had no routing at all. A Solana user
  got no model selection, while the TypeScript SDK has offered it on both chains
  since 3.12.0.
- There was no way to route a message list, so an agent transcript — the case
  where tools, response_format and transcript size actually drive the decision —
  could not be routed at all.
- `blockrun/auto` | `blockrun/eco` | `blockrun/premium` did nothing in Python;
  in the TS SDK they select a routing profile from ordinary chat calls.

All four clients now expose route(), smart_chat() and smart_chat_completion().
Both chains run the same engine over the same catalog, so the same request picks
the same model; only the x402 floor in the cost metadata differs ($0.002 Base,
$0.001 Solana).

smart_chat_completion routes on the whole request, not a prompt string: tools
and tool_choice make it a tool-agent decision, response_format forces the
structured-output tier, image parts force vision, and capacity is checked
against the entire transcript rather than the last message — an agent
conversation can be 100x its final turn and a context overflow is a
non-transient error no fallback chain rescues.

Solana chat()/chat_completion() gain fallback_models. The parameter existed only
on the Solana streaming path, so a routed Solana call carried a recovery chain it
could not walk. The walk reuses _should_fallback_solana, which refuses anything
already tagged as settled — the next model cannot sign a second transfer for one
call.

The /v1/models -> pricing conversion moved to router_adapter.build_model_pricing
so the four clients cannot drift; unavailable catalog rows are now skipped
everywhere, not just in the Base sync client.

Also fixes a 429 ending a call outright: both clients counted only 5xx as
retriable, so a saturated upstream failed the request with capable models still
in the chain. Found live — a rate-limited free model answered 429 and the three
remaining free models were never tried. The TS adapter has always treated 429 as
transient. Settled and permanently-failed payments are still refused first.
@VickyXAI
VickyXAI merged commit b02ef7c into main Aug 19, 2026
4 checks passed
@VickyXAI
VickyXAI deleted the feat/routing-parity-all-clients branch August 19, 2026 04:10
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