Skip to content

feat(skeleton): surface gateway routing/fallback, never double-retry it (#309) - #378

Merged
tommasobolis merged 4 commits into
developfrom
feat/309-gateway-routing-fallback
Sep 15, 2026
Merged

tommasobolis merged 4 commits into
developfrom
feat/309-gateway-routing-fallback

Conversation

@tommasobolis

Copy link
Copy Markdown
Collaborator

Closes #309.

What

Surfaces what the Omni Gateway actually did with each request — served provider/model, routing strategy, and whether it was a fallback — and stops the SDK from stacking a retry on top of a failover the gateway already performed. The five x-llm-proxy-* routing headers are already live-verified (responses.success.headers.txt); this is a consumption task, not a verification one.

Per the #362 precedent, routing fields hang off the existing donkey.last_call record (not a new donkey.last_routing), beside the identity/usage fields.

Changes

  • core/lastcall.py — parse routing-type / routing-fallback / llm-provider / llm-model onto LastCall (served_provider, served_model, routing_type, fallback, plus a substituted property). fallback is tri-state (None when the header is absent — never a default False, §0.3); is_fallback() is definitive-True-only, for the retry gate.
  • core/transport.py — thread the requested model onto the record; emit gen_ai.response.model / donkey.routing.type / donkey.routing.fallback span attrs; and do not retry a 502/503/504 the gateway already marked a fallback (must not fight the retry policy, errors: prove policy refusals are terminal and openai's built-in 429 retry never fires #183).
  • core/telemetry.py — pinned gen_ai.response.model + stable donkey.routing.* keys, allowlisted; build_genai_attributes emits fallback=False but drops None.
  • core/config.py + donkey.py — on_model_substitution ("off" | "raise"), resolved kwarg → env → toml → default (off); Donkey.from_env(on_model_substitution=…); ModelSubstituted typed error (public export) raised on served≠requested.
  • Docs (cross-surface lockstep) — verified-apis.md records the fallback + routing headers and the skeleton: surface gateway-side routing and fallback, and never double-retry it #309 consumption citing the fixture; telemetry.mdx gains a Routing & resilience section and flags TypeScript parity as Phase 5.

Acceptance (all met)

  • All five routing headers parsed from the captured live fixture.
  • Missing headers report None — a non-proxy / simulated response does not crash.
  • Span attrs emitted for served provider, served model, fallback.
  • on_model_substitution defaults off; "raise" raises a typed error.
  • A test asserts a fallback response is not additionally retried.

Gate

pytest -q tests/unit → 384 passed, 8 skipped · mypy --strict clean · ruff check . clean · lint-imports 4/4 kept.

🤖 Generated with Claude Code

tommasobolis and others added 2 commits September 15, 2026 00:08
…ry it (#309)

Consume the five live-verified x-llm-proxy routing headers (already captured in
responses.success.headers.txt) off the shared transport's _on_response hook, so
the raw client benefits with no framework required (BG §1.1):

- core/lastcall.py: parse routing-type/-fallback/-llm-provider/-llm-model onto
  LastCall (served_provider/served_model/routing_type/fallback, plus a
  substituted property); tri-state fallback (None when absent, never a default
  False, §0.3) and a definitive-True-only is_fallback() for the retry gate.
- core/transport.py: thread the requested model onto the record; emit
  gen_ai.response.model / donkey.routing.type / donkey.routing.fallback span
  attrs; and DO NOT retry a 502/503/504 the gateway already marked a fallback
  (must not fight the retry policy in #183).
- core/telemetry.py: pinned gen_ai.response.model + stable donkey.routing.*
  keys, allowlisted; build_genai_attributes emits fallback=False, drops None.
- core/config.py + donkey.py: on_model_substitution ("off"|"raise"), resolved
  kwarg → env → toml → default (off); Donkey.from_env(on_model_substitution=…);
  ModelSubstituted typed error (public export) raised on served≠requested.

Docs (cross-surface lockstep): verified-apis.md records the fallback + routing
headers and the #309 consumption citing the fixture; telemetry.mdx gains a
Routing & resilience section (last_call fields, span attrs, no-double-retry,
on_model_substitution) and flags TypeScript parity as Phase 5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
tommasobolis and others added 2 commits September 15, 2026 00:23
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…outing-fallback

# Conflicts:
#	docs/verified-apis.md
#	python/src/donkey_kit/core/lastcall.py
#	python/src/donkey_kit/core/telemetry.py
#	python/src/donkey_kit/core/transport.py
#	python/src/donkey_kit/donkey.py
#	python/tests/unit/test_lastcall.py
#	python/tests/unit/test_telemetry.py
#	website/pages/telemetry.mdx
#	website/public/llms-full.txt
#	website/public/telemetry.md
@tommasobolis
tommasobolis merged commit 457813f into develop Sep 15, 2026
7 checks passed
@tommasobolis
tommasobolis deleted the feat/309-gateway-routing-fallback branch September 15, 2026 07:39
tommasobolis added a commit that referenced this pull request Sep 15, 2026
#382)

Release: promote develop → main (donkey.last_call skeleton, #376–#378)
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