coderouter-cli 2.17.0__tar.gz → 2.19.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/CHANGELOG.md +104 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/PKG-INFO +3 -5
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/README.en.md +2 -3
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/README.md +2 -4
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/adapters/anthropic_native.py +4 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/adapters/base.py +3 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/adapters/openai_compat.py +6 -0
- coderouter_cli-2.19.0/coderouter/adapters/slot_affinity.py +54 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/cli.py +2 -1
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/config/schemas.py +36 -1
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/doctor_claude_code.py +12 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/ingress/anthropic_routes.py +8 -2
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/ingress/app.py +3 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/ingress/openai_routes.py +7 -2
- coderouter_cli-2.19.0/coderouter/ingress/responses_routes.py +269 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/routing/auto_router.py +46 -2
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/routing/capability.py +4 -1
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/translation/anthropic.py +3 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/translation/convert.py +4 -0
- coderouter_cli-2.19.0/coderouter/translation/responses.py +686 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/vscode_init.py +10 -1
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/backends/llamacpp-direct.en.md +21 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/backends/llamacpp-direct.md +21 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/guides/subagent-routing.en.md +19 -2
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/guides/subagent-routing.md +22 -2
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/start/quickstart.en.md +27 -16
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/start/quickstart.md +27 -16
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/start/when-do-i-need-coderouter.en.md +1 -1
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/start/when-do-i-need-coderouter.md +1 -1
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/pyproject.toml +1 -1
- coderouter_cli-2.19.0/tests/test_auto_router_hints.py +99 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_context_budget.py +30 -16
- coderouter_cli-2.19.0/tests/test_responses_api.py +398 -0
- coderouter_cli-2.19.0/tests/test_slot_affinity.py +147 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/.gitignore +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/LICENSE +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/__init__.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/__main__.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/adapters/__init__.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/adapters/registry.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/cli_stats.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/config/__init__.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/config/capability_registry.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/config/env_file.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/config/loader.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/cost.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/credentials.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/data/__init__.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/data/model-capabilities.yaml +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/doctor.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/doctor_apply.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/env_security.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/errors.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/gguf_introspect.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/guards/__init__.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/guards/_fingerprint.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/guards/backend_health.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/guards/context_budget.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/guards/continuous_probe.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/guards/drift_actions.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/guards/drift_detection.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/guards/memory_budget.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/guards/memory_pressure.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/guards/self_healing.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/guards/tool_loop.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/hardware.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/ingress/__init__.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/ingress/anthropic_errors.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/ingress/dashboard_routes.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/ingress/launcher_routes.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/ingress/metrics_routes.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/ingress/tool_search.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/ingress/ui_auth.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/language_tax.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/launcher_devices.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/launcher_speculative.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/launcher_swap.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/logging.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/metrics/__init__.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/metrics/collector.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/metrics/prometheus.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/output_filters.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/plugins/__init__.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/plugins/base.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/plugins/loader.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/plugins/registry.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/py.typed +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/rollback.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/routing/__init__.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/routing/adaptive.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/routing/budget.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/routing/fallback.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/routing/fallback_trace.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/secret_redaction.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/state/__init__.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/state/audit_log.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/state/replay.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/state/request_log.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/state/store.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/state/suggest_rules.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/token_estimation.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/token_estimation_accurate.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/translation/__init__.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/coderouter/translation/tool_repair.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/README.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/README.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/assets/dashboard-demo.png +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/backends/claude-code-llamacpp-vllm.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/backends/claude-code-llamacpp-vllm.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/backends/external-agents.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/backends/external-agents.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/backends/gguf_dl.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/backends/gguf_dl.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/backends/hf-ollama-models.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/backends/hf-ollama-models.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/backends/install-backends.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/backends/install-backends.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/backends/launcher-quickstart.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/backends/launcher-quickstart.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/backends/launcher.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/backends/launcher.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/backends/lmstudio-direct.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/backends/lmstudio-direct.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/backends/verify-ollama-0.23.1.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/concepts/architecture.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/concepts/architecture.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/concepts/commit-point.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/concepts/commit-point.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/concepts/context-budget.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/concepts/context-budget.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/concepts/continuous-probing.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/concepts/continuous-probing.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/concepts/drift-detection.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/concepts/drift-detection.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/concepts/partial-stitch.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/concepts/partial-stitch.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/concepts/stream-truncation.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/concepts/stream-truncation.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/designs/agent-cli-plugin-extraction.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/designs/external-agents-adapter.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/designs/launcher-model-swap.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/designs/launcher-multi-build.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/designs/orchestration-companion.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/designs/v1.5-dashboard-mockup.html +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/designs/v1.6-auto-router-verification.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/designs/v1.6-auto-router.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/guides/free-tier-guide.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/guides/free-tier-guide.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/guides/language-tax.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/guides/language-tax.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/guides/remote-access.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/guides/remote-access.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/guides/security.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/guides/security.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/guides/troubleshooting.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/guides/troubleshooting.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/guides/usage-guide.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/guides/usage-guide.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/guides/vscode.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/low-memory-integration.en.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/low-memory-integration.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/openrouter-roster/CHANGES.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/openrouter-roster/README.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/openrouter-roster/latest.json +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/retrospectives/v0.4.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/retrospectives/v0.5-verify.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/retrospectives/v0.5.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/retrospectives/v0.6.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/retrospectives/v0.7.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/retrospectives/v1.0-verify.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/docs/retrospectives/v1.0.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/examples/.env.example +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/examples/README.md +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/examples/providers-agent-cli.yaml +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/examples/providers-multiagent.yaml +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/examples/providers.cli-session.yaml +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/examples/providers.context-budget-test.yaml +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/examples/providers.llamacpp-vllm.yaml +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/examples/providers.nvidia-nim.yaml +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/examples/providers.ollama-auto-custom.yaml +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/examples/providers.ollama-auto.yaml +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/examples/providers.ollama-free-chain.yaml +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/examples/providers.opusplan.yaml +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/examples/providers.raspberrypi.yaml +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/examples/providers.swap.yaml +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/examples/providers.yaml +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/scripts/demo_traffic.sh +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/scripts/openrouter_roster_diff.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/scripts/smoke_v2_2.sh +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/scripts/verify-providers.yaml +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/scripts/verify_ollama_0_23.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/scripts/verify_v0_5.sh +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/scripts/verify_v1_0.sh +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/__init__.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/conftest.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_adapter_anthropic.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_agent_cli.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_anthropic_errors.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_audit_log.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_auto_router.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_auto_router_cjk.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_backend_health.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_budget.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_capability.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_capability_degraded_payload.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_capability_registry.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_capability_registry_cache_control.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_capability_tool_choice.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_claude_code_suitability.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_cli.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_cli_bind_warning.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_cli_stats.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_config.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_config_cwd_warning.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_config_shim_actions.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_continuous_probe.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_credentials.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_dashboard_endpoint.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_doctor.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_doctor_apply.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_doctor_cache_probe.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_doctor_claude_code.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_drift_actions.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_drift_detection.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_drift_detection_integration.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_env_file.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_env_security.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_errors.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_examples_yaml.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_examples_yaml_roundtrip.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fallback.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fallback_anthropic.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fallback_cache_control.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fallback_cache_observed.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fallback_cache_strip.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fallback_empty_response.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fallback_misconfig_warn.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fallback_paid_gate.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fallback_reason.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fallback_thinking.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fallback_tool_choice_emulate.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fallback_tool_integrity.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fix_h1_prometheus_drift.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fix_h2_h6_translation.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fix_h3_client_reuse.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fix_h4_h5_guards.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fix_h7_probe_restore.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fix_h8_launcher_auth.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fix_mA_routing.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fix_mB_adapters.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fix_mC_convert.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fix_mD_logging_metrics.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fix_mE_config_validation.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fix_mF_ingress.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_fix_v213_launcher_hardening.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_gguf_introspect.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_guards_tool_loop.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_hardware.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_import_hygiene.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_ingress_anthropic.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_ingress_count_tokens.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_ingress_profile.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_language_tax.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_language_tax_integration.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_launcher_backend_variants.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_launcher_config_bench.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_launcher_devices.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_launcher_devices_detect.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_launcher_devices_routes.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_launcher_gui_devices.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_launcher_gui_events.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_launcher_gui_readiness.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_launcher_gui_sweep_close.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_launcher_mtp.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_launcher_mtp_fallback.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_launcher_provider_sync.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_launcher_readiness_restart.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_launcher_swap.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_launcher_swap_review.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_launcher_variant_config.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_launcher_variant_gui.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_launcher_variant_routes.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_launcher_variant_sweep.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_memory_budget.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_memory_pressure.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_metrics_cache.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_metrics_collector.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_metrics_cost.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_metrics_endpoint.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_metrics_jsonl.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_metrics_prometheus.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_metrics_prometheus_cache.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_models_passthrough.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_openai_compat.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_openrouter_roster_diff.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_output_filters.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_output_filters_adapters.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_packaging.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_partial_stitch.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_plugin_adapter.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_plugins_integration.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_plugins_loader.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_plugins_registry.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_reasoning_strip.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_repair_byte_fallback.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_request_log.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_role_normalization.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_rollback.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_routing_adaptive.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_secret_redaction.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_self_healing.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_setup_sh.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_state_store.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_stop_reason_forward_compat.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_stream_truncation.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_token_estimation.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_token_estimation_accurate.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_tokens_saved_metric.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_tool_repair.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_tool_repair_differential.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_tool_repair_lenient.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_tool_repair_r4.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_tool_repair_scanners.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_tool_search.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_toolrepair_bench.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_translation_anthropic.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_translation_reverse.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_ttft_timeout.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_ui_auth.py +0 -0
- {coderouter_cli-2.17.0 → coderouter_cli-2.19.0}/tests/test_vscode_init.py +0 -0
|
@@ -9,6 +9,110 @@ are kept verbatim where the Japanese text itself is the subject).
|
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
+
## [v2.19.0] — 2026-10-24 (OpenAI Responses API for the Codex CLI)
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **`POST /v1/responses`** — the OpenAI Responses API, so the current Codex
|
|
17
|
+
CLI (Responses-only since 2026-02) can use CodeRouter as a custom
|
|
18
|
+
provider (`wire_api = "responses"`, `base_url = http://127.0.0.1:8088/v1`;
|
|
19
|
+
see `docs/start/quickstart.md` Pattern B). The request is translated to an
|
|
20
|
+
Anthropic Messages request and runs through the same fallback engine as
|
|
21
|
+
`/v1/messages`, so fallback, tool-call repair, the guards, the TTFT
|
|
22
|
+
cut-off and the chain-failure error mapping all apply; the result is
|
|
23
|
+
translated back (`coderouter/translation/responses.py`). Built against
|
|
24
|
+
the wire contract in openai/codex `970111f` (2026-10-23):
|
|
25
|
+
- `developer` / `system` messages (Codex sends its system prompt as the
|
|
26
|
+
first `developer` input item, not `instructions`) become Anthropic
|
|
27
|
+
`system` text; `instructions` is honoured for other clients.
|
|
28
|
+
- Tools: `function` → `function_call` with a JSON-string `arguments`;
|
|
29
|
+
`custom` grammar tools such as `apply_patch` → `custom_tool_call` with
|
|
30
|
+
the raw `input` string (Codex rejects a `function_call` for those; the
|
|
31
|
+
grammar is passed to the model in the tool description); `namespace`
|
|
32
|
+
(MCP) tools are flattened per request and answered with `namespace` +
|
|
33
|
+
the inner `name`. Hosted tools (`web_search`, …) are dropped.
|
|
34
|
+
- History: `function_call` / `custom_tool_call` and their outputs become
|
|
35
|
+
`tool_use` / `tool_result`; orphan outputs are dropped and calls with no
|
|
36
|
+
output get a synthetic `"aborted"` result so the Anthropic pairing rules
|
|
37
|
+
hold. `reasoning` and hosted-tool items are dropped.
|
|
38
|
+
- Stream: `response.created` → per item `output_item.added` (text deltas
|
|
39
|
+
only inside an open item) → `output_item.done` → `response.completed`
|
|
40
|
+
with `response.id` and complete usage details (`cached_tokens`,
|
|
41
|
+
`reasoning_tokens`). `max_tokens` stops become `response.incomplete`
|
|
42
|
+
(`max_output_tokens`).
|
|
43
|
+
- Errors: pre-content failures get an OpenAI-style error body with a real
|
|
44
|
+
status (400 / 429 with `Retry-After`, which Codex needs to retry a 429 /
|
|
45
|
+
502). A context overflow on a streaming request — upstream overflow or
|
|
46
|
+
`context_budget_action: reject` — becomes `response.failed` with code
|
|
47
|
+
`context_length_exceeded`, the only overflow signal Codex recognizes. A
|
|
48
|
+
failure after the stream started is `response.failed` (`server_error`),
|
|
49
|
+
which Codex retries.
|
|
50
|
+
- Profile selection as on the other routes (body `profile` >
|
|
51
|
+
`X-CodeRouter-Profile` > `X-CodeRouter-Mode` > auto router > default);
|
|
52
|
+
Codex's `session-id` header feeds llama-server slot affinity.
|
|
53
|
+
- Non-streaming JSON responses are supported for other clients.
|
|
54
|
+
- Tool outputs that carry images (Codex `view_image`, MCP image results)
|
|
55
|
+
become image blocks, not base64 text. Usage reports OpenAI semantics —
|
|
56
|
+
`input_tokens` includes cache reads and writes — so Codex's
|
|
57
|
+
context-window accounting (and its auto-compaction) sees the real
|
|
58
|
+
prompt size. A stream that ends without `message_stop`, or with a block
|
|
59
|
+
still open, is reported as `response.failed` (retryable) rather than a
|
|
60
|
+
`response.completed` that would silently drop the partial item.
|
|
61
|
+
Flattened tool names are capped at 64 characters (hash suffix) for
|
|
62
|
+
strict OpenAI-compatible backends, and a custom-tool call whose
|
|
63
|
+
arguments hold a single string under another key still reaches
|
|
64
|
+
`apply_patch` as raw text.
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
|
|
68
|
+
- Docs: quickstart Pattern B (ja/en) now configures Codex through
|
|
69
|
+
CodeRouter; README and the "do I need CodeRouter?" guide updated.
|
|
70
|
+
|
|
71
|
+
### Known limits
|
|
72
|
+
|
|
73
|
+
- Reasoning items are not produced or round-tripped; hosted tools are not
|
|
74
|
+
emulated; `previous_response_id` (server-side conversation state) is not
|
|
75
|
+
supported — Codex does not use it on HTTP.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## [v2.18.0] — 2026-10-24 (route on Claude Code's own request hints)
|
|
80
|
+
|
|
81
|
+
### Added
|
|
82
|
+
|
|
83
|
+
- **auto_router matchers on Claude Code's gateway hint headers.** With
|
|
84
|
+
`CLAUDE_CODE_GATEWAY_HINT_HEADERS=1` (Claude Code 2.1.273+), every request
|
|
85
|
+
says what it is. Three new `RuleMatcher` fields read those headers:
|
|
86
|
+
- `request_class` — `x-claude-code-request-class` (`main` / `subagent` /
|
|
87
|
+
`workflow` / `compaction` / `auxiliary`), case-insensitive;
|
|
88
|
+
- `agent_type` — `x-claude-code-agent-type` (e.g. `Explore`),
|
|
89
|
+
case-insensitive;
|
|
90
|
+
- `compaction: true` — `x-claude-code-compaction` is truthy (`false` is
|
|
91
|
+
rejected at load, like the other boolean matchers).
|
|
92
|
+
Typical use: auto-mode classifier / title calls (`auxiliary`) to the
|
|
93
|
+
smallest local model, compaction to a long-context profile, `Explore`
|
|
94
|
+
sub-agents to a fast one. Without the headers these matchers never match,
|
|
95
|
+
so existing rule sets behave exactly as before. `classify()` gained an
|
|
96
|
+
optional `hints` argument; both `/v1/messages` and `/v1/chat/completions`
|
|
97
|
+
pass the headers through. Docs: `docs/guides/subagent-routing.md` / `.en.md`.
|
|
98
|
+
- **`slot_affinity_slots` (provider; llama.cpp only).** For
|
|
99
|
+
`llama-server --parallel N`: requests carrying a conversation key
|
|
100
|
+
(`x-claude-code-session-id`) get `id_slot = sha256(key) % N`, so every
|
|
101
|
+
turn of a conversation reuses the same slot's prompt cache instead of
|
|
102
|
+
re-prefilling on whichever slot is free. Stable across restarts; an
|
|
103
|
+
explicit `id_slot` in `extra_body` wins; off by default. Works on both the
|
|
104
|
+
native (`kind: anthropic`) and translated (`kind: openai_compat`) paths,
|
|
105
|
+
and survives every internal request rebuild (thinking strip for backends
|
|
106
|
+
without thinking, `tool_choice` emulation, `cache_control` strip,
|
|
107
|
+
OpenAI→Anthropic translation). The main conversation and its compaction
|
|
108
|
+
share a key; sub-agents and auxiliary calls get `<session>:<class>:<agent>`
|
|
109
|
+
so they do not queue on — or evict — the main conversation's slot.
|
|
110
|
+
Docs: `docs/backends/llamacpp-direct.md` / `.en.md`.
|
|
111
|
+
- **`vscode-init` also writes `CLAUDE_CODE_GATEWAY_HINT_HEADERS=1`**, and
|
|
112
|
+
`doctor --check-claude-code` reports (as info) when it is missing.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
12
116
|
## [v2.17.0] — 2026-10-24 (time-to-first-token cut-off, retry signals)
|
|
13
117
|
|
|
14
118
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: coderouter-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.19.0
|
|
4
4
|
Summary: Local-first, free-first, fallback-built-in LLM router. Claude Code / OpenAI compatible.
|
|
5
5
|
Project-URL: Homepage, https://github.com/zephel01/CodeRouter
|
|
6
6
|
Project-URL: Repository, https://github.com/zephel01/CodeRouter
|
|
@@ -66,7 +66,7 @@ Description-Content-Type: text/markdown
|
|
|
66
66
|
## 何ができるか — 30 秒で
|
|
67
67
|
|
|
68
68
|
```
|
|
69
|
-
あなたのエージェント (Claude Code /
|
|
69
|
+
あなたのエージェント (Claude Code / codex / agy)
|
|
70
70
|
│
|
|
71
71
|
▼
|
|
72
72
|
┌─ CodeRouter ─┐
|
|
@@ -76,8 +76,6 @@ Description-Content-Type: text/markdown
|
|
|
76
76
|
└──────────────┘
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
※ 現行の codex CLI は Responses API(`/v1/responses`)専用になったため、CodeRouter 経由ではまだ使えません(対応予定)。直結の代替手順 → [quickstart Pattern B](./docs/start/quickstart.md#pattern-b-codex-cli-で使う)
|
|
80
|
-
|
|
81
79
|
**やってくれること:**
|
|
82
80
|
|
|
83
81
|
- ローカルモデルが壊した tool calling を Claude Code に届く前に修復する
|
|
@@ -147,7 +145,7 @@ ANTHROPIC_BASE_URL=http://localhost:8088 ANTHROPIC_AUTH_TOKEN=dummy claude
|
|
|
147
145
|
| Claude Code + ローカル Ollama で tool calling が壊れる | **必須** — tool 修復 (+ 必要なら wire 変換) |
|
|
148
146
|
| Claude Code + ローカルで長時間回すと止まる | **必須級** — 6 系統ガード + self-healing |
|
|
149
147
|
| Ollama v0.14+ / LM Studio にネイティブ直結で動いてる | **便利** — 直結に無い fallback / ガード / 診断を追加 (passthrough で翻訳ゼロ) |
|
|
150
|
-
| codex / agy + Ollama 直繋ぎで動いてる | オプション — フォールバックが欲しいなら(codex は `/v1/responses`
|
|
148
|
+
| codex / agy + Ollama 直繋ぎで動いてる | オプション — フォールバックが欲しいなら(codex は v2.19.0 の `/v1/responses` 経由) |
|
|
151
149
|
| Claude API を直接叩いてて問題ない | 不要 |
|
|
152
150
|
|
|
153
151
|
詳細は → [要否判定ガイド](./docs/start/when-do-i-need-coderouter.md)
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
## What it does — in 30 seconds
|
|
22
22
|
|
|
23
23
|
```
|
|
24
|
-
Your agent (Claude Code /
|
|
24
|
+
Your agent (Claude Code / codex / agy)
|
|
25
25
|
│
|
|
26
26
|
▼
|
|
27
27
|
┌─ CodeRouter ──┐
|
|
@@ -31,7 +31,6 @@ Your agent (Claude Code / agy, etc. *)
|
|
|
31
31
|
└────────────────┘
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
\* Current codex CLI speaks only the Responses API (`/v1/responses`), so it cannot go through CodeRouter yet (planned). Direct-connect workaround → [quickstart Pattern B](./docs/start/quickstart.en.md#pattern-b-use-it-with-the-codex-cli)
|
|
35
34
|
|
|
36
35
|
**What it does for you:**
|
|
37
36
|
|
|
@@ -83,7 +82,7 @@ That's it. Claude Code works as usual, but your local Ollama is answering behind
|
|
|
83
82
|
|---|---|
|
|
84
83
|
| Claude Code + local Ollama, tool calling breaks | **Yes** — wire translation + tool repair |
|
|
85
84
|
| Claude Code + local, dies after long sessions | **Helpful** — 6 guards + self-healing |
|
|
86
|
-
| codex / agy + Ollama works fine | Optional — if you want fallback (codex:
|
|
85
|
+
| codex / agy + Ollama works fine | Optional — if you want fallback (codex: via `/v1/responses`, v2.19.0+) |
|
|
87
86
|
| Using Claude API directly, no issues | Not needed |
|
|
88
87
|
|
|
89
88
|
Full decision matrix → [Do I need CodeRouter?](./docs/start/when-do-i-need-coderouter.en.md)
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
## 何ができるか — 30 秒で
|
|
22
22
|
|
|
23
23
|
```
|
|
24
|
-
あなたのエージェント (Claude Code /
|
|
24
|
+
あなたのエージェント (Claude Code / codex / agy)
|
|
25
25
|
│
|
|
26
26
|
▼
|
|
27
27
|
┌─ CodeRouter ─┐
|
|
@@ -31,8 +31,6 @@
|
|
|
31
31
|
└──────────────┘
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
※ 現行の codex CLI は Responses API(`/v1/responses`)専用になったため、CodeRouter 経由ではまだ使えません(対応予定)。直結の代替手順 → [quickstart Pattern B](./docs/start/quickstart.md#pattern-b-codex-cli-で使う)
|
|
35
|
-
|
|
36
34
|
**やってくれること:**
|
|
37
35
|
|
|
38
36
|
- ローカルモデルが壊した tool calling を Claude Code に届く前に修復する
|
|
@@ -102,7 +100,7 @@ ANTHROPIC_BASE_URL=http://localhost:8088 ANTHROPIC_AUTH_TOKEN=dummy claude
|
|
|
102
100
|
| Claude Code + ローカル Ollama で tool calling が壊れる | **必須** — tool 修復 (+ 必要なら wire 変換) |
|
|
103
101
|
| Claude Code + ローカルで長時間回すと止まる | **必須級** — 6 系統ガード + self-healing |
|
|
104
102
|
| Ollama v0.14+ / LM Studio にネイティブ直結で動いてる | **便利** — 直結に無い fallback / ガード / 診断を追加 (passthrough で翻訳ゼロ) |
|
|
105
|
-
| codex / agy + Ollama 直繋ぎで動いてる | オプション — フォールバックが欲しいなら(codex は `/v1/responses`
|
|
103
|
+
| codex / agy + Ollama 直繋ぎで動いてる | オプション — フォールバックが欲しいなら(codex は v2.19.0 の `/v1/responses` 経由) |
|
|
106
104
|
| Claude API を直接叩いてて問題ない | 不要 |
|
|
107
105
|
|
|
108
106
|
詳細は → [要否判定ガイド](./docs/start/when-do-i-need-coderouter.md)
|
|
@@ -46,6 +46,7 @@ from coderouter.adapters.base import (
|
|
|
46
46
|
StreamChunk,
|
|
47
47
|
StreamTruncatedError,
|
|
48
48
|
)
|
|
49
|
+
from coderouter.adapters.slot_affinity import apply_slot_affinity
|
|
49
50
|
from coderouter.credentials import resolve_provider_credential
|
|
50
51
|
from coderouter.logging import (
|
|
51
52
|
get_logger,
|
|
@@ -243,6 +244,9 @@ class AnthropicAdapter(BaseAdapter):
|
|
|
243
244
|
body.update(dumped)
|
|
244
245
|
body["model"] = self.config.model
|
|
245
246
|
body["stream"] = stream
|
|
247
|
+
apply_slot_affinity(
|
|
248
|
+
body, slots=self.config.slot_affinity_slots, key=req.session_key
|
|
249
|
+
)
|
|
246
250
|
return body
|
|
247
251
|
|
|
248
252
|
# ------------------------------------------------------------------
|
|
@@ -65,6 +65,9 @@ class ChatRequest(BaseModel):
|
|
|
65
65
|
|
|
66
66
|
# CodeRouter-specific extension (not sent upstream)
|
|
67
67
|
profile: str | None = Field(default=None, exclude=True)
|
|
68
|
+
# v2.18.0: conversation key for KV-slot affinity (see
|
|
69
|
+
# ``ProviderConfig.slot_affinity_slots``). Never serialized.
|
|
70
|
+
session_key: str | None = Field(default=None, exclude=True)
|
|
68
71
|
|
|
69
72
|
|
|
70
73
|
class ChatResponse(BaseModel):
|
|
@@ -30,6 +30,7 @@ from coderouter.adapters.base import (
|
|
|
30
30
|
StreamChunk,
|
|
31
31
|
StreamTruncatedError,
|
|
32
32
|
)
|
|
33
|
+
from coderouter.adapters.slot_affinity import apply_slot_affinity
|
|
33
34
|
from coderouter.credentials import resolve_provider_credential
|
|
34
35
|
from coderouter.logging import (
|
|
35
36
|
get_logger,
|
|
@@ -182,6 +183,11 @@ class OpenAICompatAdapter(BaseAdapter):
|
|
|
182
183
|
value = getattr(request, field, None)
|
|
183
184
|
if value is not None:
|
|
184
185
|
body[field] = value
|
|
186
|
+
apply_slot_affinity(
|
|
187
|
+
body,
|
|
188
|
+
slots=self.config.slot_affinity_slots,
|
|
189
|
+
key=getattr(request, "session_key", None),
|
|
190
|
+
)
|
|
185
191
|
if stream:
|
|
186
192
|
# Request a terminal usage chunk. Providers that honor this
|
|
187
193
|
# (OpenAI, OpenRouter, Ollama >=0.x) will send one extra chunk
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""v2.18.0: KV-slot affinity for llama-server (``id_slot``).
|
|
2
|
+
|
|
3
|
+
llama-server started with ``--parallel N`` serves N slots, each holding its
|
|
4
|
+
own prompt cache. Without a hint it picks a free slot, so consecutive turns
|
|
5
|
+
of one conversation can land on different slots and re-prefill the whole
|
|
6
|
+
prompt. Sending ``id_slot`` pins a conversation to one slot.
|
|
7
|
+
|
|
8
|
+
The slot is ``sha256(key) % N`` — stable across processes and restarts
|
|
9
|
+
(Python's ``hash`` is salted per process).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import hashlib
|
|
15
|
+
from collections.abc import Mapping
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
# Request classes whose prompt shares the main conversation's prefix: they
|
|
19
|
+
# belong on the main conversation's slot. Everything else (sub-agents,
|
|
20
|
+
# auxiliary calls such as the auto-mode classifier or titles, workflows) has
|
|
21
|
+
# its own prompt and gets its own key, so it neither waits on the main slot
|
|
22
|
+
# nor evicts its cache.
|
|
23
|
+
_MAIN_CLASSES = frozenset({"", "main", "compaction"})
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def affinity_key(hints: Mapping[str, str]) -> str | None:
|
|
27
|
+
"""Conversation key for slot affinity, from Claude Code's hint headers.
|
|
28
|
+
|
|
29
|
+
``None`` when there is no session id. ``main`` / ``compaction`` (and a
|
|
30
|
+
missing class) share ``<session>``; other classes get
|
|
31
|
+
``<session>:<class>:<agent_type>``.
|
|
32
|
+
"""
|
|
33
|
+
session = hints.get("session_id")
|
|
34
|
+
if not session:
|
|
35
|
+
return None
|
|
36
|
+
request_class = hints.get("request_class", "").lower()
|
|
37
|
+
if request_class in _MAIN_CLASSES:
|
|
38
|
+
return session
|
|
39
|
+
return f"{session}:{request_class}:{hints.get('agent_type', '').lower()}"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def slot_for(key: str, slots: int) -> int:
|
|
43
|
+
digest = hashlib.sha256(key.encode("utf-8")).digest()
|
|
44
|
+
return int.from_bytes(digest[:8], "big") % slots
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def apply_slot_affinity(body: dict[str, Any], *, slots: int | None, key: str | None) -> None:
|
|
48
|
+
"""Set ``body["id_slot"]`` when affinity is configured and a key exists.
|
|
49
|
+
|
|
50
|
+
An ``id_slot`` already present (e.g. from ``extra_body``) wins.
|
|
51
|
+
"""
|
|
52
|
+
if not slots or not key or "id_slot" in body:
|
|
53
|
+
return
|
|
54
|
+
body["id_slot"] = slot_for(key, slots)
|
|
@@ -483,7 +483,8 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
483
483
|
help=(
|
|
484
484
|
"v2.16.0: do not write the Claude Code gateway settings "
|
|
485
485
|
"(ENABLE_TOOL_SEARCH=false, CLAUDE_CODE_AUTO_MODE_SERVER=0, "
|
|
486
|
-
"and --max-context-tokens).
|
|
486
|
+
"CLAUDE_CODE_GATEWAY_HINT_HEADERS=1 and --max-context-tokens). "
|
|
487
|
+
"Use when the chain is "
|
|
487
488
|
"api.anthropic.com only."
|
|
488
489
|
),
|
|
489
490
|
)
|
|
@@ -562,6 +562,23 @@ class ProviderConfig(BaseModel):
|
|
|
562
562
|
# withholds those opening events from the client. ``None`` (default)
|
|
563
563
|
# disables it. Typical use: a local llama-server still loading a model
|
|
564
564
|
# or chewing through a long prompt while a cloud fallback could answer.
|
|
565
|
+
# v2.18.0: KV-slot affinity for llama-server started with
|
|
566
|
+
# ``--parallel N``. When set to N and the request carries a conversation
|
|
567
|
+
# key (Claude Code's ``x-claude-code-session-id`` hint header), the body
|
|
568
|
+
# gets ``id_slot = stable_hash(key) % N`` so every turn of a conversation
|
|
569
|
+
# lands on the same slot and reuses its prompt cache instead of
|
|
570
|
+
# re-prefilling on whichever slot is free. llama.cpp only (other servers
|
|
571
|
+
# ignore or reject ``id_slot``); ``None`` (default) sends nothing.
|
|
572
|
+
slot_affinity_slots: int | None = Field(
|
|
573
|
+
default=None,
|
|
574
|
+
ge=1,
|
|
575
|
+
le=256,
|
|
576
|
+
description=(
|
|
577
|
+
"v2.18.0: llama-server --parallel N. Pins each conversation to "
|
|
578
|
+
"slot hash(session) % N via id_slot so its KV cache is reused. "
|
|
579
|
+
"llama.cpp only. None disables."
|
|
580
|
+
),
|
|
581
|
+
)
|
|
565
582
|
ttft_timeout_s: float | None = Field(
|
|
566
583
|
default=None,
|
|
567
584
|
gt=0.0,
|
|
@@ -1639,6 +1656,21 @@ class RuleMatcher(BaseModel):
|
|
|
1639
1656
|
# declares any tools); ``False`` is rejected by ``_exactly_one``
|
|
1640
1657
|
# since a "no-tools" rule would shadow the default fall-through.
|
|
1641
1658
|
has_tools: bool | None = None
|
|
1659
|
+
# v2.18.0: Claude Code gateway hint headers. Claude Code 2.1.273+ sends
|
|
1660
|
+
# these to a custom ANTHROPIC_BASE_URL when
|
|
1661
|
+
# ``CLAUDE_CODE_GATEWAY_HINT_HEADERS=1`` is set (``coderouter
|
|
1662
|
+
# vscode-init`` sets it). They say what a request *is* — something the
|
|
1663
|
+
# body heuristics above can only guess:
|
|
1664
|
+
# * ``request_class`` — ``x-claude-code-request-class``: ``main`` /
|
|
1665
|
+
# ``subagent`` / ``workflow`` / ``compaction`` / ``auxiliary``
|
|
1666
|
+
# (auto-mode classifier, titles, …). Case-insensitive exact match.
|
|
1667
|
+
# * ``agent_type`` — ``x-claude-code-agent-type`` (e.g. ``Explore``),
|
|
1668
|
+
# case-insensitive exact match.
|
|
1669
|
+
# * ``compaction: True`` — ``x-claude-code-compaction`` is truthy.
|
|
1670
|
+
# Without the headers these matchers simply never match.
|
|
1671
|
+
request_class: str | None = None
|
|
1672
|
+
agent_type: str | None = None
|
|
1673
|
+
compaction: bool | None = None
|
|
1642
1674
|
|
|
1643
1675
|
_MATCHER_FIELDS: tuple[str, ...] = (
|
|
1644
1676
|
"has_image",
|
|
@@ -1649,12 +1681,15 @@ class RuleMatcher(BaseModel):
|
|
|
1649
1681
|
"content_token_count_min",
|
|
1650
1682
|
"has_tools",
|
|
1651
1683
|
"cjk_ratio_min",
|
|
1684
|
+
"request_class",
|
|
1685
|
+
"agent_type",
|
|
1686
|
+
"compaction",
|
|
1652
1687
|
)
|
|
1653
1688
|
|
|
1654
1689
|
# Boolean matcher fields carry meaning only at ``True`` — the runtime
|
|
1655
1690
|
# evaluator uses ``is True`` — so a ``False`` value is a dead rule that
|
|
1656
1691
|
# never matches. ``_exactly_one`` rejects it explicitly (see below).
|
|
1657
|
-
_BOOL_MATCHER_FIELDS: tuple[str, ...] = ("has_image", "has_tools")
|
|
1692
|
+
_BOOL_MATCHER_FIELDS: tuple[str, ...] = ("has_image", "has_tools", "compaction")
|
|
1658
1693
|
|
|
1659
1694
|
@model_validator(mode="after")
|
|
1660
1695
|
def _exactly_one(self) -> Self:
|
|
@@ -125,6 +125,18 @@ def check(version: Version | None, env: Mapping[str, str]) -> list[ClaudeCodeFin
|
|
|
125
125
|
)
|
|
126
126
|
)
|
|
127
127
|
|
|
128
|
+
if env.get("CLAUDE_CODE_GATEWAY_HINT_HEADERS", "").lower() in ("1", "true"):
|
|
129
|
+
out.append(ClaudeCodeFinding("ok", "CLAUDE_CODE_GATEWAY_HINT_HEADERS=1"))
|
|
130
|
+
else:
|
|
131
|
+
out.append(
|
|
132
|
+
ClaudeCodeFinding(
|
|
133
|
+
"info",
|
|
134
|
+
"CLAUDE_CODE_GATEWAY_HINT_HEADERS is not set: auto_router rules "
|
|
135
|
+
"on request_class / agent_type / compaction (v2.18.0) never "
|
|
136
|
+
"match without it.",
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
|
|
128
140
|
if env.get("CLAUDE_CODE_AUTO_MODE_SERVER") == "0":
|
|
129
141
|
out.append(ClaudeCodeFinding("ok", "CLAUDE_CODE_AUTO_MODE_SERVER=0"))
|
|
130
142
|
else:
|
|
@@ -31,6 +31,7 @@ from fastapi import APIRouter, Header, HTTPException, Request
|
|
|
31
31
|
from fastapi.responses import JSONResponse, Response, StreamingResponse
|
|
32
32
|
|
|
33
33
|
from coderouter.adapters.base import StreamTruncatedError
|
|
34
|
+
from coderouter.adapters.slot_affinity import affinity_key
|
|
34
35
|
from coderouter.guards.context_budget import ContextBudgetRejectError
|
|
35
36
|
from coderouter.guards.tool_loop import ToolCountExceededError, ToolLoopBreakError
|
|
36
37
|
from coderouter.ingress.anthropic_errors import (
|
|
@@ -49,7 +50,7 @@ from coderouter.routing import (
|
|
|
49
50
|
MidStreamError,
|
|
50
51
|
NoProvidersAvailableError,
|
|
51
52
|
)
|
|
52
|
-
from coderouter.routing.auto_router import RESERVED_PROFILE_NAME, classify
|
|
53
|
+
from coderouter.routing.auto_router import RESERVED_PROFILE_NAME, classify, extract_gateway_hints
|
|
53
54
|
from coderouter.routing.fallback_trace import (
|
|
54
55
|
SSE_FALLBACK_EVENT,
|
|
55
56
|
current_fallback_trace,
|
|
@@ -226,6 +227,11 @@ async def messages(
|
|
|
226
227
|
if anthropic_beta:
|
|
227
228
|
anth_req.anthropic_beta = anthropic_beta
|
|
228
229
|
|
|
230
|
+
# v2.18.0: Claude Code gateway hint headers — routing matchers below,
|
|
231
|
+
# and the conversation key for llama-server KV-slot affinity.
|
|
232
|
+
gateway_hints = extract_gateway_hints(request.headers)
|
|
233
|
+
anth_req.session_key = affinity_key(gateway_hints)
|
|
234
|
+
|
|
229
235
|
# Profile selection — body field wins over header (same policy as OpenAI route).
|
|
230
236
|
if anth_req.profile is None and x_coderouter_profile:
|
|
231
237
|
anth_req.profile = x_coderouter_profile
|
|
@@ -253,7 +259,7 @@ async def messages(
|
|
|
253
259
|
# AnthropicRequest), so both OpenAI and Anthropic ingress use the same
|
|
254
260
|
# classifier without a shared request shim.
|
|
255
261
|
if anth_req.profile is None and config.default_profile == RESERVED_PROFILE_NAME:
|
|
256
|
-
anth_req.profile = classify(payload, config)
|
|
262
|
+
anth_req.profile = classify(payload, config, gateway_hints)
|
|
257
263
|
|
|
258
264
|
if anth_req.profile is not None:
|
|
259
265
|
try:
|
|
@@ -19,6 +19,7 @@ from coderouter.ingress.dashboard_routes import router as dashboard_router
|
|
|
19
19
|
from coderouter.ingress.launcher_routes import router as launcher_router
|
|
20
20
|
from coderouter.ingress.metrics_routes import router as metrics_router
|
|
21
21
|
from coderouter.ingress.openai_routes import router as openai_router
|
|
22
|
+
from coderouter.ingress.responses_routes import router as responses_router
|
|
22
23
|
from coderouter.logging import configure_logging, get_logger
|
|
23
24
|
from coderouter.metrics import install_collector
|
|
24
25
|
from coderouter.plugins import discover_and_load
|
|
@@ -491,6 +492,8 @@ def create_app(config_path: str | None = None) -> FastAPI:
|
|
|
491
492
|
|
|
492
493
|
app.include_router(openai_router, prefix="/v1", tags=["openai-compat"])
|
|
493
494
|
app.include_router(anthropic_router, prefix="/v1", tags=["anthropic-compat"])
|
|
495
|
+
# v2.19.0: OpenAI Responses API (Codex CLI).
|
|
496
|
+
app.include_router(responses_router, prefix="/v1", tags=["openai-responses"])
|
|
494
497
|
# v1.5-A: /metrics.json sits at the root (no /v1 prefix) — metrics are not
|
|
495
498
|
# part of the OpenAI / Anthropic API surface, and Prometheus-style
|
|
496
499
|
# endpoints conventionally live at the root in v1.5-B.
|
|
@@ -32,9 +32,10 @@ from fastapi import APIRouter, Header, HTTPException, Request
|
|
|
32
32
|
from fastapi.responses import JSONResponse, StreamingResponse
|
|
33
33
|
|
|
34
34
|
from coderouter.adapters.base import ChatRequest
|
|
35
|
+
from coderouter.adapters.slot_affinity import affinity_key
|
|
35
36
|
from coderouter.logging import get_logger
|
|
36
37
|
from coderouter.routing import FallbackEngine, NoProvidersAvailableError
|
|
37
|
-
from coderouter.routing.auto_router import RESERVED_PROFILE_NAME, classify
|
|
38
|
+
from coderouter.routing.auto_router import RESERVED_PROFILE_NAME, classify, extract_gateway_hints
|
|
38
39
|
from coderouter.routing.fallback_trace import current_fallback_trace
|
|
39
40
|
|
|
40
41
|
router = APIRouter()
|
|
@@ -199,6 +200,10 @@ async def chat_completions(
|
|
|
199
200
|
except Exception as exc: # pydantic.ValidationError, etc.
|
|
200
201
|
raise HTTPException(status_code=422, detail=str(exc)) from exc
|
|
201
202
|
|
|
203
|
+
# v2.18.0: gateway hint headers (routing matchers + KV-slot affinity key).
|
|
204
|
+
gateway_hints = extract_gateway_hints(request.headers)
|
|
205
|
+
chat_req.session_key = affinity_key(gateway_hints)
|
|
206
|
+
|
|
202
207
|
# Header-based override (body wins if both are set — see module docstring)
|
|
203
208
|
if chat_req.profile is None and x_coderouter_profile:
|
|
204
209
|
chat_req.profile = x_coderouter_profile
|
|
@@ -226,7 +231,7 @@ async def chat_completions(
|
|
|
226
231
|
# When inactive, the engine still falls through to
|
|
227
232
|
# ``config.default_profile`` on its own — same semantics as pre-v1.6.
|
|
228
233
|
if chat_req.profile is None and config.default_profile == RESERVED_PROFILE_NAME:
|
|
229
|
-
chat_req.profile = classify(payload, config)
|
|
234
|
+
chat_req.profile = classify(payload, config, gateway_hints)
|
|
230
235
|
|
|
231
236
|
# Validate profile exists before we kick off any upstream call
|
|
232
237
|
if chat_req.profile is not None:
|