coderouter-cli 2.5.5__tar.gz → 2.6.1__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.5.5 → coderouter_cli-2.6.1}/.gitignore +1 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/CHANGELOG.md +116 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/PKG-INFO +32 -1
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/README.en.md +31 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/README.md +31 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/config/schemas.py +31 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/cost.py +32 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/ingress/dashboard_routes.py +65 -0
- coderouter_cli-2.6.1/coderouter/language_tax.py +244 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/logging.py +8 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/metrics/collector.py +105 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/routing/auto_router.py +7 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/routing/fallback.py +30 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/token_estimation.py +47 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/README.md +38 -1
- coderouter_cli-2.6.1/docs/backends/claude-code-llamacpp-vllm.md +172 -0
- coderouter_cli-2.6.1/docs/guides/language-tax.en.md +138 -0
- coderouter_cli-2.6.1/docs/guides/language-tax.md +139 -0
- coderouter_cli-2.6.1/examples/README.md +68 -0
- coderouter_cli-2.6.1/examples/providers-multiagent.yaml +188 -0
- coderouter_cli-2.5.5/examples/providers.v2-context-budget.yaml → coderouter_cli-2.6.1/examples/providers.context-budget-test.yaml +2 -1
- coderouter_cli-2.5.5/examples/providers.llama-cpp-vllm.yaml → coderouter_cli-2.6.1/examples/providers.llamacpp-vllm.yaml +19 -9
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/examples/providers.nvidia-nim.yaml +1 -0
- coderouter_cli-2.5.5/examples/providers.auto-custom.yaml → coderouter_cli-2.6.1/examples/providers.ollama-auto-custom.yaml +6 -5
- coderouter_cli-2.5.5/examples/providers.auto.yaml → coderouter_cli-2.6.1/examples/providers.ollama-auto.yaml +2 -1
- coderouter_cli-2.5.5/examples/providers.note-2026.yaml → coderouter_cli-2.6.1/examples/providers.ollama-free-chain.yaml +3 -2
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/examples/providers.raspberrypi.yaml +1 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/examples/providers.yaml +1 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/pyproject.toml +1 -1
- coderouter_cli-2.6.1/tests/test_auto_router_cjk.py +99 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_dashboard_endpoint.py +6 -0
- coderouter_cli-2.6.1/tests/test_language_tax.py +147 -0
- coderouter_cli-2.6.1/tests/test_language_tax_integration.py +215 -0
- coderouter_cli-2.6.1/tests/test_tokens_saved_metric.py +87 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/LICENSE +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/__init__.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/__main__.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/adapters/__init__.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/adapters/anthropic_native.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/adapters/base.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/adapters/openai_compat.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/adapters/registry.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/cli.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/cli_stats.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/config/__init__.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/config/capability_registry.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/config/env_file.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/config/loader.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/data/__init__.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/data/model-capabilities.yaml +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/doctor.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/doctor_apply.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/env_security.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/errors.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/gguf_introspect.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/guards/__init__.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/guards/_fingerprint.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/guards/backend_health.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/guards/context_budget.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/guards/continuous_probe.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/guards/drift_actions.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/guards/drift_detection.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/guards/memory_budget.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/guards/memory_pressure.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/guards/self_healing.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/guards/tool_loop.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/hardware.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/ingress/__init__.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/ingress/anthropic_routes.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/ingress/app.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/ingress/launcher_routes.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/ingress/metrics_routes.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/ingress/openai_routes.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/metrics/__init__.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/metrics/prometheus.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/output_filters.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/plugins/__init__.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/plugins/base.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/plugins/loader.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/plugins/registry.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/routing/__init__.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/routing/adaptive.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/routing/budget.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/routing/capability.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/state/__init__.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/state/audit_log.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/state/replay.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/state/request_log.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/state/store.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/state/suggest_rules.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/token_estimation_accurate.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/translation/__init__.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/translation/anthropic.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/translation/convert.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/coderouter/translation/tool_repair.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/assets/dashboard-demo.png +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/backends/gguf_dl.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/backends/hf-ollama-models.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/backends/install-backends.en.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/backends/install-backends.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/backends/launcher-quickstart.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/backends/launcher.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/backends/llamacpp-direct.en.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/backends/llamacpp-direct.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/backends/lmstudio-direct.en.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/backends/lmstudio-direct.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/backends/verify-ollama-0.23.1.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/concepts/architecture.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/concepts/context-budget.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/concepts/continuous-probing.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/concepts/drift-detection.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/concepts/partial-stitch.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/designs/v1.5-dashboard-mockup.html +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/designs/v1.6-auto-router-verification.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/designs/v1.6-auto-router.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/guides/free-tier-guide.en.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/guides/free-tier-guide.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/guides/security.en.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/guides/security.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/guides/troubleshooting.en.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/guides/troubleshooting.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/guides/usage-guide.en.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/guides/usage-guide.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/low-memory-integration.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/openrouter-roster/CHANGES.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/openrouter-roster/README.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/openrouter-roster/latest.json +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/retrospectives/v0.4.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/retrospectives/v0.5-verify.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/retrospectives/v0.5.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/retrospectives/v0.6.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/retrospectives/v0.7.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/retrospectives/v1.0-verify.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/retrospectives/v1.0.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/start/quickstart.en.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/start/quickstart.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/start/when-do-i-need-coderouter.en.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/docs/start/when-do-i-need-coderouter.md +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/examples/.env.example +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/scripts/demo_traffic.sh +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/scripts/openrouter_roster_diff.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/scripts/smoke_v2_2.sh +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/scripts/verify-providers.yaml +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/scripts/verify_ollama_0_23.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/scripts/verify_v0_5.sh +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/scripts/verify_v1_0.sh +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/__init__.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/conftest.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_adapter_anthropic.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_audit_log.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_auto_router.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_backend_health.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_budget.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_capability.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_capability_degraded_payload.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_capability_registry.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_capability_registry_cache_control.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_claude_code_suitability.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_cli.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_cli_stats.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_config.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_context_budget.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_continuous_probe.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_doctor.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_doctor_apply.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_doctor_cache_probe.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_drift_actions.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_drift_detection.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_drift_detection_integration.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_env_file.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_env_security.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_errors.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_examples_yaml.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_fallback.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_fallback_anthropic.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_fallback_cache_control.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_fallback_cache_observed.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_fallback_misconfig_warn.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_fallback_paid_gate.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_fallback_thinking.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_gguf_introspect.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_guards_tool_loop.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_hardware.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_ingress_anthropic.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_ingress_profile.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_memory_budget.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_memory_pressure.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_metrics_cache.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_metrics_collector.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_metrics_cost.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_metrics_endpoint.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_metrics_jsonl.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_metrics_prometheus.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_metrics_prometheus_cache.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_openai_compat.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_openrouter_roster_diff.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_output_filters.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_output_filters_adapters.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_partial_stitch.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_plugins_integration.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_plugins_loader.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_plugins_registry.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_reasoning_strip.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_repair_byte_fallback.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_request_log.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_role_normalization.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_routing_adaptive.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_self_healing.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_setup_sh.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_state_store.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_token_estimation.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_token_estimation_accurate.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_tool_repair.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_translation_anthropic.py +0 -0
- {coderouter_cli-2.5.5 → coderouter_cli-2.6.1}/tests/test_translation_reverse.py +0 -0
|
@@ -6,6 +6,122 @@ versioning follows [SemVer](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [v2.6.1] — 2026-06-28 (Token-savings accounting)
|
|
10
|
+
|
|
11
|
+
Patch release: surfaces **token-savings accounting** in the metrics layer
|
|
12
|
+
and dashboard. The figure is owned by core, so it appears even when no
|
|
13
|
+
plugin is installed (trim savings from the context-budget guard), and the
|
|
14
|
+
optional `compress` plugin adds to the same total via a neutral
|
|
15
|
+
`tokens-saved` log event. **No new core dependency**, **no behavioral
|
|
16
|
+
change** to existing paths, and **fully backward compatible** — existing
|
|
17
|
+
counters and events are untouched; the new buckets are additive.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **Token-savings buckets in `MetricsCollector`**
|
|
22
|
+
(`coderouter/metrics/collector.py`). `tokens_saved_total` plus a
|
|
23
|
+
per-mechanism breakdown (`tokens_saved_by_mechanism`). Two feeds
|
|
24
|
+
aggregate under one schema: `trim` (derived from the existing
|
|
25
|
+
`context-budget-trimmed` event's before/after token estimate) and
|
|
26
|
+
`compress` (the neutral `tokens-saved` event emitted by the compress
|
|
27
|
+
plugin — no core import). Wired through `snapshot()`, `save_state()`,
|
|
28
|
+
`load_state()`, and `reset()`.
|
|
29
|
+
- **Dashboard Token Savings tiles**
|
|
30
|
+
(`coderouter/ingress/dashboard_routes.py`). Three tiles — trim /
|
|
31
|
+
compress / total — in the "Cost & Language Tax" panel, zero-filled so a
|
|
32
|
+
fresh or local-only deployment renders cleanly.
|
|
33
|
+
|
|
34
|
+
### Docs / examples
|
|
35
|
+
|
|
36
|
+
- Reorganized provider samples under `examples/` with a category index.
|
|
37
|
+
- Added the language-tax guide (JA/EN) and a Claude Code +
|
|
38
|
+
llama.cpp/vllm backend guide.
|
|
39
|
+
|
|
40
|
+
### Tests
|
|
41
|
+
|
|
42
|
+
- `tests/test_tokens_saved_metric.py` — trim/compress accounting,
|
|
43
|
+
negative-delta clamping, combined aggregate, persistence round-trip,
|
|
44
|
+
and reset. Full suite: **1263 passed, 1 skipped**.
|
|
45
|
+
|
|
46
|
+
### Companion
|
|
47
|
+
|
|
48
|
+
- `coderouter-plugin-compress` emits the `tokens-saved` event (plugin
|
|
49
|
+
branch `feat/tokens-saved-emit`, v0.2.0). CodeRouter core works
|
|
50
|
+
standalone — trim savings show without the plugin.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## [v2.6.0] — 2026-06-20 (Language Tax: measure, route, visualize)
|
|
55
|
+
|
|
56
|
+
Minor release: makes the CJK **"language tax"** — cloud tokenizers bill
|
|
57
|
+
Japanese/Chinese/Korean text ~1.2–1.5× more tokens per character than
|
|
58
|
+
English, while local models are unaffected — measurable, routable, and
|
|
59
|
+
visible. Built entirely on existing infrastructure; **no new core
|
|
60
|
+
dependency** (the accurate tokenizer is the existing optional `accuracy`
|
|
61
|
+
extra), **no network** (local `tokenizer.json` only), and **fully
|
|
62
|
+
backward compatible** — the feature is inert until a provider declares
|
|
63
|
+
`tokenizer_path`.
|
|
64
|
+
|
|
65
|
+
### Added
|
|
66
|
+
|
|
67
|
+
- **Language-tax measurement (`coderouter/language_tax.py`).** A leaf
|
|
68
|
+
module exposing `cjk_char_ratio`, `estimate_language_tax`,
|
|
69
|
+
`LanguageTaxBreakdown`, and `language_tax_usd`. CJK detection is
|
|
70
|
+
stdlib-only (Unicode range checks); the accurate token count is
|
|
71
|
+
delegated to the optional `accuracy` (`tokenizers`) backend with a
|
|
72
|
+
char/4 fallback. The tax multiplier is `tokens_accurate /
|
|
73
|
+
tokens_heuristic` — ~1.0 for English/code, ~2.0–4.0 for pure CJK.
|
|
74
|
+
|
|
75
|
+
- **End-to-end cost integration.** `CostBreakdown` gains
|
|
76
|
+
`language_tax_multiplier` / `language_tax_usd`;
|
|
77
|
+
`compute_cost_for_attempt` accepts an optional `language_tax=`. Both
|
|
78
|
+
`cache-observed` emit sites in `routing/fallback.py` (streaming +
|
|
79
|
+
non-streaming) build a `LanguageTaxBreakdown` **only when the provider
|
|
80
|
+
declares `tokenizer_path`**, so the hot path is untouched by default.
|
|
81
|
+
The `cache-observed` log line now carries `language_tax_usd` /
|
|
82
|
+
`language_tax_multiplier`, and `MetricsCollector` aggregates per-provider
|
|
83
|
+
+ total language-tax spend (mirroring the cost-savings aggregation).
|
|
84
|
+
|
|
85
|
+
- **`ProviderConfig.tokenizer_path`** — optional path to a local
|
|
86
|
+
`tokenizer.json` for accurate (language-tax) token counting. Local-file
|
|
87
|
+
only; never contacts the HuggingFace Hub. Inert when unset.
|
|
88
|
+
|
|
89
|
+
- **`cjk_ratio_min` auto-route matcher.** A new `RuleMatcher` variant that
|
|
90
|
+
routes turns whose latest user message CJK ratio ≥ threshold to a
|
|
91
|
+
(typically local, tax-free) profile, while ASCII/code turns fall through
|
|
92
|
+
to the cloud chain. Per-turn property mirroring `code_fence_ratio_min`.
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
auto_router:
|
|
96
|
+
rules:
|
|
97
|
+
- match: { cjk_ratio_min: 0.3 } # JA-heavy turns → local
|
|
98
|
+
profile: local
|
|
99
|
+
- match: { has_tools: true }
|
|
100
|
+
profile: cloud
|
|
101
|
+
default_rule_profile: cloud
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
- **Dashboard "Cost & Language Tax" panel** on `/dashboard`: total spend,
|
|
105
|
+
cache savings, and CJK language-tax spend (aggregate + per-provider).
|
|
106
|
+
Also surfaces the previously-hidden cost aggregates.
|
|
107
|
+
|
|
108
|
+
- **`token_estimation.extract_text_from_anthropic_request()`** — pulls the
|
|
109
|
+
concatenated request text for the accurate tokenizer leg.
|
|
110
|
+
|
|
111
|
+
### Security
|
|
112
|
+
|
|
113
|
+
- **Bump starlette 1.0.1 → 1.3.1**, clearing four advisories
|
|
114
|
+
(CVE-2026-48817 / CVE-2026-48818 / CVE-2026-54282 / CVE-2026-54283) that
|
|
115
|
+
failed the `cve-audit` CI job (`pip-audit --strict`).
|
|
116
|
+
|
|
117
|
+
### Notes
|
|
118
|
+
|
|
119
|
+
- 38 new tests (`test_language_tax`, `test_language_tax_integration`,
|
|
120
|
+
`test_auto_router_cjk`, extended dashboard contract). Full suite:
|
|
121
|
+
**1250 passed, 8 skipped**. ruff clean. The 5-deps invariant is intact.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
9
125
|
## [v2.5.5] — 2026-06-06 (Claude Code >= 2.1.154 `system` role normalization)
|
|
10
126
|
|
|
11
127
|
Patch release: ingress-side workaround for a Claude Code CLI regression.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: coderouter-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.1
|
|
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
|
|
@@ -161,6 +161,36 @@ ANTHROPIC_BASE_URL=http://localhost:8088 ANTHROPIC_AUTH_TOKEN=dummy claude
|
|
|
161
161
|
| **`coderouter replay`** | provider 切替の効果を統計比較 (A/B 分析) / `--suggest-rules` でルール最適化提案 |
|
|
162
162
|
| **Continuous Probe** | idle 時も定期的に backend を監視 |
|
|
163
163
|
|
|
164
|
+
### 言語税トラッキング — v2.6.0
|
|
165
|
+
|
|
166
|
+
日本語などの CJK テキストは、クラウドのトークナイザだと「同じ意味の英語」より多くのトークンを消費します(**実測: GPT-4o 系 o200k で平均 1.6 倍、GPT-4 系 cl100k で平均 2.0 倍**)。ローカル LLM は課金されないので、この「言語税」はクラウド利用時だけ効いてきます。CodeRouter v2.6.0 はこれを **計測・ルーティング回避・可視化** します。
|
|
167
|
+
|
|
168
|
+
| 機能 | 何をしてくれるか |
|
|
169
|
+
|---|---|
|
|
170
|
+
| **言語税の計測** | プロバイダに `tokenizer_path`(ローカルの `tokenizer.json`)を指定すると、char/4 ヒューリスティック比の実トークン倍率と割増 USD を算出(ネットワーク不要・未設定なら無効) |
|
|
171
|
+
| **`cjk_ratio_min` ルーティング** | CJK 比率が高いリクエストを自動でローカル LLM(課金ゼロ)へ。コードや英語はクラウドへ |
|
|
172
|
+
| **ダッシュボード可視化** | `/dashboard` の「Cost & Language Tax」パネルで総支出・キャッシュ節約・言語税をリアルタイム表示 |
|
|
173
|
+
|
|
174
|
+
```yaml
|
|
175
|
+
# providers.yaml — CJK 多めのターンはローカルへ自動回避
|
|
176
|
+
auto_router:
|
|
177
|
+
rules:
|
|
178
|
+
- match: { cjk_ratio_min: 0.3 } # 日本語が3割以上 → ローカル
|
|
179
|
+
profile: local
|
|
180
|
+
- match: { has_tools: true } # ツール使用 → クラウド
|
|
181
|
+
profile: cloud
|
|
182
|
+
default_rule_profile: cloud
|
|
183
|
+
|
|
184
|
+
providers:
|
|
185
|
+
- name: cloud-sonnet
|
|
186
|
+
kind: anthropic
|
|
187
|
+
base_url: https://api.anthropic.com
|
|
188
|
+
model: claude-sonnet-4-6
|
|
189
|
+
tokenizer_path: ~/.coderouter/tokenizers/sonnet.json # 言語税の正確計測(任意)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
詳細 → [言語税ガイド](./docs/guides/language-tax.md)
|
|
193
|
+
|
|
164
194
|
### Launcher — llama.cpp / vllm 起動 UI
|
|
165
195
|
|
|
166
196
|
`http://localhost:8088/launcher` で開けるブラウザ UI。llama.cpp や vllm を GUI で起動・管理できます。
|
|
@@ -229,6 +259,7 @@ providers:
|
|
|
229
259
|
| 使いこなす | [利用ガイド](./docs/guides/usage-guide.md) |
|
|
230
260
|
| 無料で回す | [無料枠ガイド](./docs/guides/free-tier-guide.md) |
|
|
231
261
|
| llama.cpp / vllm を GUI で起動 | [Launcher ガイド](./docs/backends/launcher.md) |
|
|
262
|
+
| 言語税を計測・回避する | [言語税ガイド](./docs/guides/language-tax.md) |
|
|
232
263
|
| 詰まった | [トラブルシューティング](./docs/guides/troubleshooting.md) |
|
|
233
264
|
| 設計を知りたい | [アーキテクチャ詳細](./docs/concepts/architecture.md) |
|
|
234
265
|
| 全リリース履歴 | [CHANGELOG](./CHANGELOG.md) |
|
|
@@ -116,6 +116,36 @@ Full decision matrix → [Do I need CodeRouter?](./docs/start/when-do-i-need-cod
|
|
|
116
116
|
| **`coderouter replay`** | Compare providers statistically (A/B analysis) / `--suggest-rules` for automated rule suggestions |
|
|
117
117
|
| **Continuous Probe** | Background health monitoring even during idle |
|
|
118
118
|
|
|
119
|
+
### Language Tax tracking — v2.6.0
|
|
120
|
+
|
|
121
|
+
CJK text (Japanese / Chinese / Korean) costs more tokens on cloud tokenizers than the same meaning in English (**measured: ~1.6× on average with GPT-4o-era o200k, ~2.0× with GPT-4-era cl100k**). Local models bill nothing per token, so this "language tax" only bites on the cloud leg. CodeRouter v2.6.0 makes it **measurable, routable, and visible**.
|
|
122
|
+
|
|
123
|
+
| Feature | What it does |
|
|
124
|
+
|---|---|
|
|
125
|
+
| **Language-tax measurement** | Set a provider's `tokenizer_path` (a local `tokenizer.json`) to compute the real token multiplier vs the char/4 heuristic and the extra USD. No network; inert when unset. |
|
|
126
|
+
| **`cjk_ratio_min` routing** | Auto-route CJK-heavy turns to a local (tax-free) model; code/English falls through to the cloud chain. |
|
|
127
|
+
| **Dashboard panel** | The `/dashboard` "Cost & Language Tax" panel shows total spend, cache savings, and language-tax spend live. |
|
|
128
|
+
|
|
129
|
+
```yaml
|
|
130
|
+
# providers.yaml — steer CJK-heavy turns to local
|
|
131
|
+
auto_router:
|
|
132
|
+
rules:
|
|
133
|
+
- match: { cjk_ratio_min: 0.3 } # >=30% CJK chars -> local
|
|
134
|
+
profile: local
|
|
135
|
+
- match: { has_tools: true } # tool use -> cloud
|
|
136
|
+
profile: cloud
|
|
137
|
+
default_rule_profile: cloud
|
|
138
|
+
|
|
139
|
+
providers:
|
|
140
|
+
- name: cloud-sonnet
|
|
141
|
+
kind: anthropic
|
|
142
|
+
base_url: https://api.anthropic.com
|
|
143
|
+
model: claude-sonnet-4-6
|
|
144
|
+
tokenizer_path: ~/.coderouter/tokenizers/sonnet.json # accurate language-tax (optional)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Details → [Language Tax guide](./docs/guides/language-tax.en.md)
|
|
148
|
+
|
|
119
149
|
### Launcher — llama.cpp / vllm GUI
|
|
120
150
|
|
|
121
151
|
Browser UI at `http://localhost:8088/launcher` for starting and managing local inference backends.
|
|
@@ -184,6 +214,7 @@ More detail → [Usage guide](./docs/guides/usage-guide.en.md) · [Architecture]
|
|
|
184
214
|
| Use it well | [Usage guide](./docs/guides/usage-guide.en.md) |
|
|
185
215
|
| Run for free | [Free-tier guide](./docs/guides/free-tier-guide.en.md) |
|
|
186
216
|
| Launch llama.cpp / vllm via GUI | [Launcher guide](./docs/backends/launcher.md) |
|
|
217
|
+
| Measure & avoid the language tax | [Language Tax guide](./docs/guides/language-tax.en.md) |
|
|
187
218
|
| Stuck? | [Troubleshooting](./docs/guides/troubleshooting.en.md) |
|
|
188
219
|
| Understand the design | [Architecture](./docs/concepts/architecture.md) |
|
|
189
220
|
| Full release history | [CHANGELOG](./CHANGELOG.md) |
|
|
@@ -116,6 +116,36 @@ ANTHROPIC_BASE_URL=http://localhost:8088 ANTHROPIC_AUTH_TOKEN=dummy claude
|
|
|
116
116
|
| **`coderouter replay`** | provider 切替の効果を統計比較 (A/B 分析) / `--suggest-rules` でルール最適化提案 |
|
|
117
117
|
| **Continuous Probe** | idle 時も定期的に backend を監視 |
|
|
118
118
|
|
|
119
|
+
### 言語税トラッキング — v2.6.0
|
|
120
|
+
|
|
121
|
+
日本語などの CJK テキストは、クラウドのトークナイザだと「同じ意味の英語」より多くのトークンを消費します(**実測: GPT-4o 系 o200k で平均 1.6 倍、GPT-4 系 cl100k で平均 2.0 倍**)。ローカル LLM は課金されないので、この「言語税」はクラウド利用時だけ効いてきます。CodeRouter v2.6.0 はこれを **計測・ルーティング回避・可視化** します。
|
|
122
|
+
|
|
123
|
+
| 機能 | 何をしてくれるか |
|
|
124
|
+
|---|---|
|
|
125
|
+
| **言語税の計測** | プロバイダに `tokenizer_path`(ローカルの `tokenizer.json`)を指定すると、char/4 ヒューリスティック比の実トークン倍率と割増 USD を算出(ネットワーク不要・未設定なら無効) |
|
|
126
|
+
| **`cjk_ratio_min` ルーティング** | CJK 比率が高いリクエストを自動でローカル LLM(課金ゼロ)へ。コードや英語はクラウドへ |
|
|
127
|
+
| **ダッシュボード可視化** | `/dashboard` の「Cost & Language Tax」パネルで総支出・キャッシュ節約・言語税をリアルタイム表示 |
|
|
128
|
+
|
|
129
|
+
```yaml
|
|
130
|
+
# providers.yaml — CJK 多めのターンはローカルへ自動回避
|
|
131
|
+
auto_router:
|
|
132
|
+
rules:
|
|
133
|
+
- match: { cjk_ratio_min: 0.3 } # 日本語が3割以上 → ローカル
|
|
134
|
+
profile: local
|
|
135
|
+
- match: { has_tools: true } # ツール使用 → クラウド
|
|
136
|
+
profile: cloud
|
|
137
|
+
default_rule_profile: cloud
|
|
138
|
+
|
|
139
|
+
providers:
|
|
140
|
+
- name: cloud-sonnet
|
|
141
|
+
kind: anthropic
|
|
142
|
+
base_url: https://api.anthropic.com
|
|
143
|
+
model: claude-sonnet-4-6
|
|
144
|
+
tokenizer_path: ~/.coderouter/tokenizers/sonnet.json # 言語税の正確計測(任意)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
詳細 → [言語税ガイド](./docs/guides/language-tax.md)
|
|
148
|
+
|
|
119
149
|
### Launcher — llama.cpp / vllm 起動 UI
|
|
120
150
|
|
|
121
151
|
`http://localhost:8088/launcher` で開けるブラウザ UI。llama.cpp や vllm を GUI で起動・管理できます。
|
|
@@ -184,6 +214,7 @@ providers:
|
|
|
184
214
|
| 使いこなす | [利用ガイド](./docs/guides/usage-guide.md) |
|
|
185
215
|
| 無料で回す | [無料枠ガイド](./docs/guides/free-tier-guide.md) |
|
|
186
216
|
| llama.cpp / vllm を GUI で起動 | [Launcher ガイド](./docs/backends/launcher.md) |
|
|
217
|
+
| 言語税を計測・回避する | [言語税ガイド](./docs/guides/language-tax.md) |
|
|
187
218
|
| 詰まった | [トラブルシューティング](./docs/guides/troubleshooting.md) |
|
|
188
219
|
| 設計を知りたい | [アーキテクチャ詳細](./docs/concepts/architecture.md) |
|
|
189
220
|
| 全リリース履歴 | [CHANGELOG](./CHANGELOG.md) |
|
|
@@ -185,6 +185,19 @@ class ProviderConfig(BaseModel):
|
|
|
185
185
|
)
|
|
186
186
|
timeout_s: float = Field(default=30.0, ge=1.0, le=600.0)
|
|
187
187
|
|
|
188
|
+
# v2.6 language-tax track: path to a LOCAL ``tokenizer.json`` for this
|
|
189
|
+
# provider's model, used to measure the CJK over-count vs the char/4
|
|
190
|
+
# baseline (see ``coderouter.language_tax``). Loaded local-file-only —
|
|
191
|
+
# never contacts the HuggingFace Hub. When unset, language-tax falls
|
|
192
|
+
# back to char/4 (multiplier 1.0) and the feature is silently inert.
|
|
193
|
+
tokenizer_path: str | None = Field(
|
|
194
|
+
default=None,
|
|
195
|
+
description=(
|
|
196
|
+
"Local tokenizer.json for accurate (language-tax) token "
|
|
197
|
+
"counting. No network access. Requires the 'accuracy' extra."
|
|
198
|
+
),
|
|
199
|
+
)
|
|
200
|
+
|
|
188
201
|
# Provider-specific extras merged into the outbound request body.
|
|
189
202
|
# Use for non-standard fields like Ollama's `think: false`, `keep_alive`,
|
|
190
203
|
# `options.num_ctx`, or any vendor-specific toggle. User-supplied request
|
|
@@ -763,6 +776,16 @@ class RuleMatcher(BaseModel):
|
|
|
763
776
|
``request.tools`` set). The ``has_tools`` matcher is the
|
|
764
777
|
profile-level lever for steering tool-laden traffic to the right
|
|
765
778
|
chain entirely.
|
|
779
|
+
|
|
780
|
+
Variants (v2.6 / language-tax routing):
|
|
781
|
+
|
|
782
|
+
- ``cjk_ratio_min: 0.3`` — CJK character ratio of the latest user
|
|
783
|
+
message is ``>=`` this threshold. Routes CJK-heavy turns (which
|
|
784
|
+
pay the cloud "language tax" of ~1.2-1.5x more tokens) to a local
|
|
785
|
+
model that bills nothing per token, while ASCII/code turns fall
|
|
786
|
+
through to the cloud chain. Per-turn property like
|
|
787
|
+
``code_fence_ratio_min``; see
|
|
788
|
+
:func:`coderouter.language_tax.cjk_char_ratio`.
|
|
766
789
|
"""
|
|
767
790
|
|
|
768
791
|
model_config = ConfigDict(extra="forbid")
|
|
@@ -773,6 +796,13 @@ class RuleMatcher(BaseModel):
|
|
|
773
796
|
content_regex: str | None = None
|
|
774
797
|
model_pattern: str | None = None
|
|
775
798
|
content_token_count_min: int | None = Field(default=None, ge=1)
|
|
799
|
+
# v2.6 language-tax routing: CJK character ratio of the latest user
|
|
800
|
+
# message >= this threshold. Lets operators steer CJK-heavy traffic
|
|
801
|
+
# (which carries the cloud language tax) to a local model that bills
|
|
802
|
+
# nothing per token. Operates on the latest user message like
|
|
803
|
+
# ``code_fence_ratio_min`` (a per-turn property), not the whole
|
|
804
|
+
# request. See ``coderouter.language_tax.cjk_char_ratio``.
|
|
805
|
+
cjk_ratio_min: float | None = Field(default=None, ge=0.0, le=1.0)
|
|
776
806
|
# [Unreleased]: tool-aware routing (OpenClaw + Raspberry Pi 由来).
|
|
777
807
|
# See class docstring "Variants ([Unreleased] / tool-aware routing)"
|
|
778
808
|
# above for the full rationale. Boolean shape mirrors ``has_image`` —
|
|
@@ -789,6 +819,7 @@ class RuleMatcher(BaseModel):
|
|
|
789
819
|
"model_pattern",
|
|
790
820
|
"content_token_count_min",
|
|
791
821
|
"has_tools",
|
|
822
|
+
"cjk_ratio_min",
|
|
792
823
|
)
|
|
793
824
|
|
|
794
825
|
@model_validator(mode="after")
|
|
@@ -58,9 +58,13 @@ in the cost calc.
|
|
|
58
58
|
from __future__ import annotations
|
|
59
59
|
|
|
60
60
|
from dataclasses import dataclass
|
|
61
|
+
from typing import TYPE_CHECKING
|
|
61
62
|
|
|
62
63
|
from coderouter.config.schemas import CostConfig
|
|
63
64
|
|
|
65
|
+
if TYPE_CHECKING: # avoid an import cycle at runtime; used only for typing
|
|
66
|
+
from coderouter.language_tax import LanguageTaxBreakdown
|
|
67
|
+
|
|
64
68
|
|
|
65
69
|
@dataclass(frozen=True)
|
|
66
70
|
class CostBreakdown:
|
|
@@ -82,6 +86,12 @@ class CostBreakdown:
|
|
|
82
86
|
chart. ``input_usd`` is "fresh input only" (does not
|
|
83
87
|
include cache buckets); cache_read_usd / cache_creation_usd
|
|
84
88
|
are the post-discount / post-premium values.
|
|
89
|
+
language_tax_multiplier: ``tokens_accurate / tokens_heuristic``
|
|
90
|
+
for the request text (v2.6 language-tax track). 1.0 when no
|
|
91
|
+
tax is measurable (English/code, or no accurate tokenizer).
|
|
92
|
+
language_tax_usd: USD share of ``total_usd`` attributable to the
|
|
93
|
+
CJK over-count vs CodeRouter's char/4 English baseline.
|
|
94
|
+
0.0 for free / local providers. See :mod:`coderouter.language_tax`.
|
|
85
95
|
"""
|
|
86
96
|
|
|
87
97
|
total_usd: float = 0.0
|
|
@@ -90,6 +100,10 @@ class CostBreakdown:
|
|
|
90
100
|
output_usd: float = 0.0
|
|
91
101
|
cache_read_usd: float = 0.0
|
|
92
102
|
cache_creation_usd: float = 0.0
|
|
103
|
+
# v2.6 language-tax track (additive; defaults keep pre-v2.6 behaviour
|
|
104
|
+
# and equality with a bare ``CostBreakdown()``).
|
|
105
|
+
language_tax_multiplier: float = 1.0
|
|
106
|
+
language_tax_usd: float = 0.0
|
|
93
107
|
|
|
94
108
|
|
|
95
109
|
_PER_MILLION: float = 1_000_000.0
|
|
@@ -102,6 +116,7 @@ def compute_cost_for_attempt(
|
|
|
102
116
|
output_tokens: int,
|
|
103
117
|
cache_read_input_tokens: int,
|
|
104
118
|
cache_creation_input_tokens: int,
|
|
119
|
+
language_tax: LanguageTaxBreakdown | None = None,
|
|
105
120
|
) -> CostBreakdown:
|
|
106
121
|
"""Translate per-attempt token counts into a USD :class:`CostBreakdown`.
|
|
107
122
|
|
|
@@ -144,6 +159,21 @@ def compute_cost_for_attempt(
|
|
|
144
159
|
full_rate_for_cache_read = safe_read * input_rate
|
|
145
160
|
savings_usd = full_rate_for_cache_read - cache_read_usd
|
|
146
161
|
|
|
162
|
+
# v2.6 language tax: the share of fresh-input spend attributable to
|
|
163
|
+
# the CJK over-count vs the char/4 English baseline. Defaults to a
|
|
164
|
+
# 1.0 multiplier / $0 when no LanguageTaxBreakdown is supplied, so
|
|
165
|
+
# the pre-v2.6 call shape is unchanged.
|
|
166
|
+
lt_multiplier = 1.0
|
|
167
|
+
lt_usd = 0.0
|
|
168
|
+
if language_tax is not None:
|
|
169
|
+
lt_multiplier = language_tax.tax_multiplier
|
|
170
|
+
from coderouter.language_tax import language_tax_usd
|
|
171
|
+
|
|
172
|
+
lt_usd = language_tax_usd(
|
|
173
|
+
language_tax.extra_tokens,
|
|
174
|
+
input_tokens_per_million=cost_config.input_tokens_per_million,
|
|
175
|
+
)
|
|
176
|
+
|
|
147
177
|
return CostBreakdown(
|
|
148
178
|
total_usd=total_usd,
|
|
149
179
|
savings_usd=max(savings_usd, 0.0),
|
|
@@ -151,4 +181,6 @@ def compute_cost_for_attempt(
|
|
|
151
181
|
output_usd=output_usd,
|
|
152
182
|
cache_read_usd=cache_read_usd,
|
|
153
183
|
cache_creation_usd=cache_creation_usd,
|
|
184
|
+
language_tax_multiplier=lt_multiplier,
|
|
185
|
+
language_tax_usd=lt_usd,
|
|
154
186
|
)
|
|
@@ -165,6 +165,41 @@ _DASHBOARD_HTML = r"""<!doctype html>
|
|
|
165
165
|
</main>
|
|
166
166
|
|
|
167
167
|
<footer class="max-w-7xl mx-auto px-4 md:px-6 pb-8">
|
|
168
|
+
<!-- Panel: Cost & Language Tax (v2.6) -->
|
|
169
|
+
<section class="bg-slate-900/60 border border-slate-800 rounded-lg p-4 mb-4">
|
|
170
|
+
<h2 class="text-sm font-semibold uppercase tracking-wider text-slate-400 mb-3">Cost & Language Tax</h2>
|
|
171
|
+
<div class="grid grid-cols-3 gap-3">
|
|
172
|
+
<div class="rounded-md bg-slate-800/50 p-3">
|
|
173
|
+
<div class="text-xs text-slate-400">Total spend</div>
|
|
174
|
+
<div class="text-2xl font-semibold tabnum" data-bind="cost_total">$0.00</div>
|
|
175
|
+
</div>
|
|
176
|
+
<div class="rounded-md bg-slate-800/50 p-3">
|
|
177
|
+
<div class="text-xs text-slate-400">Cache savings</div>
|
|
178
|
+
<div class="text-2xl font-semibold tabnum text-green-400" data-bind="cost_savings">$0.00</div>
|
|
179
|
+
</div>
|
|
180
|
+
<div class="rounded-md bg-slate-800/50 p-3">
|
|
181
|
+
<div class="text-xs text-slate-400">Language tax (CJK)</div>
|
|
182
|
+
<div class="text-2xl font-semibold tabnum text-amber-400" data-bind="language_tax_total">$0.00</div>
|
|
183
|
+
<div class="text-xs text-slate-500" data-bind="language_tax_hint">no tokenizer configured</div>
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
<div id="language-tax-by-provider" class="text-xs text-slate-400 tabnum mt-3"></div>
|
|
187
|
+
<!-- Token Savings: trim (core budget guard) + compress (plugin) -->
|
|
188
|
+
<div class="grid grid-cols-3 gap-3 mt-3">
|
|
189
|
+
<div class="rounded-md bg-slate-800/50 p-3">
|
|
190
|
+
<div class="text-xs text-slate-400">Tokens saved · trim</div>
|
|
191
|
+
<div class="text-2xl font-semibold tabnum text-sky-400" data-bind="tokens_saved_trim">0</div>
|
|
192
|
+
</div>
|
|
193
|
+
<div class="rounded-md bg-slate-800/50 p-3">
|
|
194
|
+
<div class="text-xs text-slate-400">Tokens saved · compress</div>
|
|
195
|
+
<div class="text-2xl font-semibold tabnum text-sky-400" data-bind="tokens_saved_compress">0</div>
|
|
196
|
+
</div>
|
|
197
|
+
<div class="rounded-md bg-slate-800/50 p-3">
|
|
198
|
+
<div class="text-xs text-slate-400">Tokens saved · total</div>
|
|
199
|
+
<div class="text-2xl font-semibold tabnum text-green-400" data-bind="tokens_saved_total">0</div>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
</section>
|
|
168
203
|
<section class="bg-slate-900/60 border border-slate-800 rounded-lg p-4">
|
|
169
204
|
<h2 class="text-sm font-semibold uppercase tracking-wider text-slate-400 mb-3">Usage Mix</h2>
|
|
170
205
|
<div id="usage-bar" class="flex h-3 rounded-full overflow-hidden bg-slate-800" role="img" aria-label="usage mix"></div>
|
|
@@ -435,6 +470,35 @@ _DASHBOARD_HTML = r"""<!doctype html>
|
|
|
435
470
|
{"&": "&", "<": "<", ">": ">", '"': """, "'": "'"}[c]
|
|
436
471
|
));
|
|
437
472
|
|
|
473
|
+
// v2.6: cost + language-tax panel. The collector zero-fills these, so
|
|
474
|
+
// a fresh/local-only deployment shows $0.00 across the board.
|
|
475
|
+
const renderCostTax = (snap) => {
|
|
476
|
+
const c = snap.counters || {};
|
|
477
|
+
const usd = (x) => "$" + (Number(x) || 0).toFixed(4);
|
|
478
|
+
setBind("cost_total", usd(c.cost_total_usd_aggregate));
|
|
479
|
+
setBind("cost_savings", usd(c.cost_savings_usd_aggregate));
|
|
480
|
+
const taxTotal = Number(c.language_tax_usd_aggregate) || 0;
|
|
481
|
+
setBind("language_tax_total", usd(taxTotal));
|
|
482
|
+
setBind("language_tax_hint",
|
|
483
|
+
taxTotal > 0 ? "extra paid for CJK vs char/4 baseline"
|
|
484
|
+
: "no tax measured (set provider tokenizer_path)");
|
|
485
|
+
const byProv = c.language_tax_usd || {};
|
|
486
|
+
const el = document.getElementById("language-tax-by-provider");
|
|
487
|
+
const rows = Object.entries(byProv).filter(([, v]) => Number(v) > 0);
|
|
488
|
+
el.innerHTML = rows.length === 0 ? "" :
|
|
489
|
+
rows.map(([n, v]) =>
|
|
490
|
+
'<span class="mr-4"><span class="text-slate-500">' + escapeHTML(n) +
|
|
491
|
+
'</span> ' + usd(v) + '</span>').join("");
|
|
492
|
+
// token-savings tiles. trim comes from the core budget guard;
|
|
493
|
+
// compress from the optional plugin. Collector zero-fills, so an
|
|
494
|
+
// install with neither still renders "0" cleanly.
|
|
495
|
+
const ts = c.tokens_saved_by_mechanism || {};
|
|
496
|
+
const intfmt = (x) => (Number(x) || 0).toLocaleString();
|
|
497
|
+
setBind("tokens_saved_trim", intfmt(ts.trim));
|
|
498
|
+
setBind("tokens_saved_compress", intfmt(ts.compress));
|
|
499
|
+
setBind("tokens_saved_total", intfmt(c.tokens_saved_total));
|
|
500
|
+
};
|
|
501
|
+
|
|
438
502
|
const renderSnapshot = (snap) => {
|
|
439
503
|
const startup = snap.startup || {};
|
|
440
504
|
const cfg = snap.config || {};
|
|
@@ -451,6 +515,7 @@ _DASHBOARD_HTML = r"""<!doctype html>
|
|
|
451
515
|
renderSparkline(snap);
|
|
452
516
|
renderRecent(snap);
|
|
453
517
|
renderUsageMix(snap);
|
|
518
|
+
renderCostTax(snap);
|
|
454
519
|
};
|
|
455
520
|
|
|
456
521
|
const renderError = (msg) => {
|