coderouter-cli 1.9.0__tar.gz → 1.10.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-1.9.0 → coderouter_cli-1.10.0}/CHANGELOG.md +535 -1
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/PKG-INFO +25 -8
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/README.en.md +24 -7
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/README.md +24 -7
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/config/schemas.py +154 -1
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/doctor.py +1 -1
- coderouter_cli-1.10.0/coderouter/guards/backend_health.py +208 -0
- coderouter_cli-1.10.0/coderouter/guards/memory_pressure.py +210 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/logging.py +352 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/metrics/collector.py +86 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/metrics/prometheus.py +84 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/routing/auto_router.py +118 -13
- coderouter_cli-1.10.0/coderouter/routing/budget.py +191 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/routing/fallback.py +594 -39
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/llamacpp-direct.en.md +1 -1
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/llamacpp-direct.md +1 -1
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/lmstudio-direct.en.md +1 -1
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/lmstudio-direct.md +1 -1
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/pyproject.toml +1 -1
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_auto_router.py +303 -0
- coderouter_cli-1.10.0/tests/test_backend_health.py +413 -0
- coderouter_cli-1.10.0/tests/test_budget.py +351 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_fallback_cache_observed.py +147 -18
- coderouter_cli-1.10.0/tests/test_memory_pressure.py +362 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/.gitignore +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/LICENSE +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/__init__.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/__main__.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/adapters/__init__.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/adapters/anthropic_native.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/adapters/base.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/adapters/openai_compat.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/adapters/registry.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/cli.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/cli_stats.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/config/__init__.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/config/capability_registry.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/config/env_file.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/config/loader.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/cost.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/data/__init__.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/data/model-capabilities.yaml +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/doctor_apply.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/env_security.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/errors.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/guards/__init__.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/guards/tool_loop.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/ingress/__init__.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/ingress/anthropic_routes.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/ingress/app.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/ingress/dashboard_routes.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/ingress/metrics_routes.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/ingress/openai_routes.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/metrics/__init__.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/output_filters.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/routing/__init__.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/routing/adaptive.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/routing/capability.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/translation/__init__.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/translation/anthropic.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/translation/convert.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/coderouter/translation/tool_repair.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/assets/dashboard-demo.png +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/designs/v1.5-dashboard-mockup.html +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/designs/v1.6-auto-router-verification.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/designs/v1.6-auto-router.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/free-tier-guide.en.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/free-tier-guide.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/hf-ollama-models.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/openrouter-roster/README.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/openrouter-roster/latest.json +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/quickstart.en.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/quickstart.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/retrospectives/v0.4.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/retrospectives/v0.5-verify.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/retrospectives/v0.5.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/retrospectives/v0.6.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/retrospectives/v0.7.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/retrospectives/v1.0-verify.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/retrospectives/v1.0.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/security.en.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/security.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/troubleshooting.en.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/troubleshooting.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/usage-guide.en.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/usage-guide.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/when-do-i-need-coderouter.en.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/docs/when-do-i-need-coderouter.md +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/examples/.env.example +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/examples/providers.auto-custom.yaml +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/examples/providers.auto.yaml +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/examples/providers.note-2026.yaml +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/examples/providers.nvidia-nim.yaml +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/examples/providers.yaml +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/scripts/demo_traffic.sh +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/scripts/openrouter_roster_diff.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/scripts/verify_v0_5.sh +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/scripts/verify_v1_0.sh +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/__init__.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/conftest.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_adapter_anthropic.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_capability.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_capability_degraded_payload.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_capability_registry.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_capability_registry_cache_control.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_claude_code_suitability.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_cli.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_cli_stats.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_config.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_dashboard_endpoint.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_doctor.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_doctor_apply.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_doctor_cache_probe.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_env_file.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_env_security.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_errors.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_examples_yaml.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_fallback.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_fallback_anthropic.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_fallback_cache_control.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_fallback_misconfig_warn.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_fallback_paid_gate.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_fallback_thinking.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_guards_tool_loop.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_ingress_anthropic.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_ingress_profile.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_metrics_cache.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_metrics_collector.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_metrics_cost.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_metrics_endpoint.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_metrics_jsonl.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_metrics_prometheus.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_metrics_prometheus_cache.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_openai_compat.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_openrouter_roster_diff.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_output_filters.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_output_filters_adapters.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_reasoning_strip.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_routing_adaptive.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_setup_sh.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_tool_repair.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_translation_anthropic.py +0 -0
- {coderouter_cli-1.9.0 → coderouter_cli-1.10.0}/tests/test_translation_reverse.py +0 -0
|
@@ -6,6 +6,540 @@ versioning follows [SemVer](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [v1.10.0] — 2026-05-01 (Umbrella tag — Cost enforcement + Long-run reliability completion + Auto-router feature complete)
|
|
10
|
+
|
|
11
|
+
**Theme: 「観測 → 理解 → 行動」を 3 軸で完成、Vision pillar P2/P3 が揃う。** v1.9.1 (patch) で取り切った 2 機能 (v1.9-B2 streaming usage 集約 + per-model auto-routing) は事実上 v1.10 backlog の助走、本 v1.10.0 で残り 3 機能を minor として束ねて出荷。CodeRouter は **「Local LLM で agent を長時間回すための信頼性層」** という Vision の v1.x 担当分が完成 — context overflow (L1) と quality drift (L4) を除く 4 系統障害 (L2/L3/L5/L6) を体系的に対処、auto-router の declarative 6 matcher も揃い、cost 系は観測 (v1.9-D) → enforcement (v1.10) で経路が閉じた。
|
|
12
|
+
|
|
13
|
+
含まれる出荷 5 件 (`docs/inside/future.md §6.6` の v1.10 着手順序、本 release で全完了):
|
|
14
|
+
|
|
15
|
+
| # | sub-release | テーマ | LOC | tests | 出荷先 |
|
|
16
|
+
|---|---|---|---|---|---|
|
|
17
|
+
| 1 | **v1.9-B2** | streaming 経路の usage 集約 (`_StreamUsageAccumulator`、placeholder→観測値) | ~150 | +3 | v1.9.1 |
|
|
18
|
+
| 2 | **per-model auto-routing** | `RuleMatcher.model_pattern` (Opus/Sonnet/Haiku 分岐、free-claude-code 由来) | ~120 | +5 | v1.9.1 |
|
|
19
|
+
| 3 | **provider 月次予算上限** | `BudgetTracker` + `cost.monthly_budget_usd` (LiteLLM 由来 / v1.9-D 累積版) | ~250 | +8 | **v1.10.0** |
|
|
20
|
+
| 4 | **v1.9-E phase 2 (L2/L5)** | Memory pressure detector + Backend health 状態機械 (Vision pillar 完成) | ~370 | +27 | **v1.10.0** |
|
|
21
|
+
| 5 | **longContext auto-switch** | `RuleMatcher.content_token_count_min` (claude-code-router 由来) | ~80 | +5 | **v1.10.0** |
|
|
22
|
+
|
|
23
|
+
- Tests: 838 (v1.9.1) → **871** (+33: 本 minor 単独 +27 from v1.9-E phase 2 + 8 budget + 5 longContext から、v1.10.0 で純増 +33)
|
|
24
|
+
- Runtime deps: 5 → 5 (**33 sub-release 連続据え置き**) — 最初から守ってきた `fastapi / uvicorn / httpx / pydantic / pyyaml` のみ
|
|
25
|
+
- pyproject version: 1.9.1 → 1.10.0
|
|
26
|
+
|
|
27
|
+
### Pillar 別の達成
|
|
28
|
+
|
|
29
|
+
#### P2 Long-run Reliability (v1.9-E 系) — Vision の核心が揃う
|
|
30
|
+
|
|
31
|
+
6 系統障害体系 (`docs/inside/future.md §1`) のうち v1.x で取りに行くと宣言した分が完成:
|
|
32
|
+
|
|
33
|
+
| # | 障害 | v1.x 担当 | 状態 |
|
|
34
|
+
|---|---|---|---|
|
|
35
|
+
| **L1** | Context overflow | (v2.0-F) | ⏳ |
|
|
36
|
+
| **L2** | Memory pressure | v1.9-E phase 2 | ✅ v1.10.0 |
|
|
37
|
+
| **L3** | Tool loop | v1.9-E phase 1 | ✅ v1.9.0 |
|
|
38
|
+
| **L4** | Quality drift | (v2.0-G) | ⏳ |
|
|
39
|
+
| **L5** | Backend crash / health | v1.9-E phase 2 | ✅ v1.10.0 |
|
|
40
|
+
| **L6** | Mid-stream interrupt | v0.3-A 既存 + (v2.0-H 強化) | ✅ baseline |
|
|
41
|
+
|
|
42
|
+
L2/L3/L5 の 3 兄弟が `coderouter/guards/` 配下に並び、それぞれ `MemoryPressureGuard` / `_apply_tool_loop_guard` / `BackendHealthMonitor` が pure module として独立。engine 統合は `_observe_provider_failure` / `_observe_provider_success` の 2 chokepoint で済む綺麗な設計に着地。
|
|
43
|
+
|
|
44
|
+
#### Cost pillar (v1.9-D 系) — 観測 → 制約の経路が閉じる
|
|
45
|
+
|
|
46
|
+
| 段階 | sub-release | 役割 |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| **観測** | v1.9-A | `cache-observed` log + cache hit/miss 4-class outcome |
|
|
49
|
+
| **観測のカバレッジ** | v1.9-B2 (v1.9.1) | streaming 経路まで完全カバー、placeholder ゼロ化 |
|
|
50
|
+
| **理解** | v1.9-D | per-provider USD cost + cache savings 別計算 (LiteLLM 既存品が落とす精度) |
|
|
51
|
+
| **制約** | **v1.10.0** | `monthly_budget_usd` で per-provider 月次 cap、UTC 暦月 in-memory bucketing |
|
|
52
|
+
|
|
53
|
+
v1.9.0 GA 時点で「観測の 4-class 精度」「LiteLLM 既存品より精度高い cost 計算」が CodeRouter の差別化軸として確立、v1.10.0 でそれを enforcement に活用できる経路が閉じた。
|
|
54
|
+
|
|
55
|
+
#### Auto-router (v1.6 系) — 6 matcher で feature complete
|
|
56
|
+
|
|
57
|
+
| # | matcher | 由来 | 出荷 |
|
|
58
|
+
|---|---|---|---|
|
|
59
|
+
| 1 | `has_image` | v1.6-A bundled | v1.6.0 |
|
|
60
|
+
| 2 | `code_fence_ratio_min` | v1.6-A bundled | v1.6.0 |
|
|
61
|
+
| 3 | `content_contains` | v1.6-A user-defined | v1.6.0 |
|
|
62
|
+
| 4 | `content_regex` | v1.6-A user-defined | v1.6.0 |
|
|
63
|
+
| 5 | `model_pattern` | free-claude-code 由来 | v1.9.1 |
|
|
64
|
+
| 6 | `content_token_count_min` | claude-code-router 由来 | **v1.10.0** |
|
|
65
|
+
|
|
66
|
+
declarative routing が「latest message の content / 画像 (per-turn signal) + request 全体の model id / token count (request-shape signal)」で完備、competitive analysis で抽出した v1.10 候補の取り込みは打ち止め、これ以降の追加は要望ドリブンで再開する想定。
|
|
67
|
+
|
|
68
|
+
### Migration
|
|
69
|
+
|
|
70
|
+
不要。**v1.9.1 / v1.9.0 / v1.9.0a* からの自然なアップグレード**:
|
|
71
|
+
|
|
72
|
+
- `coderouter` コマンド名 / Python import 名 / providers.yaml の format / env 変数 / ingress URL すべて完全に同じ
|
|
73
|
+
- 新しい schema field (`cost.monthly_budget_usd` / `memory_pressure_*` / `backend_health_*` / `content_token_count_min`) は全部 optional + 安全側 default (`monthly_budget_usd: None` / action は `warn` か `off`)、未指定 deployment は v1.9.x と挙動完全一致
|
|
74
|
+
- 新しい log event (`skip-budget-exceeded` / `chain-budget-exceeded` / `memory-pressure-detected` / `skip-memory-pressure` / `chain-memory-pressure-blocked` / `backend-health-changed` / `demote-unhealthy-provider`) は既存 `cache-observed` / `provider-failed` / etc. と同じ JSON 形式、外部 consumer は受信側に dispatch 追加するだけで対応可能
|
|
75
|
+
|
|
76
|
+
### Out of scope (v2.0 以降)
|
|
77
|
+
|
|
78
|
+
- **L1 Context overflow** → v2.0-F (semantic compression、context budget per-mode)
|
|
79
|
+
- **L4 Quality drift detection** → v2.0-G (response 品質 rolling window 観測)
|
|
80
|
+
- **L6 Mid-stream stitching 強化** → v2.0-H (resumable continuation)
|
|
81
|
+
- **Continuous probing** → v2.0-I (毎時/毎日 model ヘルスチェック、HF dataset 公開)
|
|
82
|
+
- **Persistent budget state** (sqlite / Redis) — 5-deps 不変原則で v1.x 範囲では却下
|
|
83
|
+
- **L5 active probing** (60s 間隔の能動 GET /api/version) — v2.0-I の領域、passive で 80% カバーできているため後回し
|
|
84
|
+
- **tiktoken / SentencePiece による正確なトークンカウント** — 5-deps 不変原則で却下
|
|
85
|
+
|
|
86
|
+
詳細は `docs/inside/future.md §7` を参照。
|
|
87
|
+
|
|
88
|
+
### Files touched
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
A coderouter/guards/backend_health.py
|
|
92
|
+
A coderouter/guards/memory_pressure.py
|
|
93
|
+
A coderouter/routing/budget.py
|
|
94
|
+
A tests/test_backend_health.py
|
|
95
|
+
A tests/test_budget.py
|
|
96
|
+
A tests/test_memory_pressure.py
|
|
97
|
+
M CHANGELOG.md
|
|
98
|
+
M coderouter/config/schemas.py
|
|
99
|
+
M coderouter/logging.py
|
|
100
|
+
M coderouter/metrics/collector.py
|
|
101
|
+
M coderouter/metrics/prometheus.py
|
|
102
|
+
M coderouter/routing/auto_router.py
|
|
103
|
+
M coderouter/routing/fallback.py
|
|
104
|
+
M docs/inside/future.md
|
|
105
|
+
M plan.md
|
|
106
|
+
M pyproject.toml
|
|
107
|
+
M tests/test_auto_router.py
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
### v1.10 候補 #5: longContext auto-switch (claude-code-router 由来)
|
|
113
|
+
|
|
114
|
+
**Theme: コンテキスト窓圧迫の自動逃がし。** 長文プロンプト (会話ヒストリーの累積、コードベース貼り付け等) が来た時、context window の小さいモデル (200K Anthropic) ではなく 1M ctx の Gemini Flash 系へ自動切替する仕組み。`auto_router.rules[].if.content_token_count_min` を 6 番目の matcher として追加、既存 5 種と同じ "exactly one" 規約を継承。
|
|
115
|
+
|
|
116
|
+
ユースケース例:
|
|
117
|
+
|
|
118
|
+
```yaml
|
|
119
|
+
auto_router:
|
|
120
|
+
rules:
|
|
121
|
+
- if: { content_token_count_min: 32000 }
|
|
122
|
+
route_to: longcontext
|
|
123
|
+
default_rule_profile: writing
|
|
124
|
+
|
|
125
|
+
profiles:
|
|
126
|
+
- name: longcontext
|
|
127
|
+
providers:
|
|
128
|
+
- openrouter-gemini-flash-free # 1M ctx
|
|
129
|
+
- anthropic-haiku-direct # 200K ctx
|
|
130
|
+
- name: writing
|
|
131
|
+
providers: [anthropic-sonnet-direct]
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
agent が短いやり取りを 100 ターン続けて context が膨らんだ時、自動で 1M ctx チェーンに切り替わる。`free-claude-code` / `claude-code-router` 由来のニーズを CodeRouter の declarative auto_router framework に取り込んだ形。
|
|
135
|
+
|
|
136
|
+
#### 設計判断: char/4 ヒューリスティック vs tiktoken
|
|
137
|
+
|
|
138
|
+
token カウントは `len(text) // 4` の素朴ヒューリスティック (OpenAI 公式の rule of thumb)。**5-deps 不変原則** (`plan.md §5.4`) を守るため tiktoken / SentencePiece は導入しない。トレードオフ:
|
|
139
|
+
|
|
140
|
+
- **English 散文 / コード**: char/4 はやや緩い (実際は ~3.5/token)、`min` 比較なので大きい threshold で安全側に倒せる
|
|
141
|
+
- **CJK (日本語/中国語/韓国語)**: char/4 は **保守的にカウント不足** (実際は ~1.5-2 char/token)、つまり 100k 文字の日本語プロンプトを ~25k tokens と過小評価。これは積極的に context overflow を引き起こす方向ではないので fail-safe な誤差
|
|
142
|
+
- **トレードオフ判断**: tiktoken なら正確だが 100MB 級の依存追加、SentencePiece でも 50MB 級。CodeRouter は「個人開発者用の signal-based router」なので、operator が threshold を実機運用フィードバックで調整する前提のヒューリスティックで十分
|
|
143
|
+
|
|
144
|
+
#### Other matchers との違い
|
|
145
|
+
|
|
146
|
+
`content_contains` / `content_regex` / `has_image` は **latest user message** に対して評価 (per-turn signal)、`content_token_count_min` は **request 全体 (system + 全 messages)** を walk して合算 (request-shape signal)。context-window pressure はリクエスト全体の性質なので latest-only では誤検出する。
|
|
147
|
+
|
|
148
|
+
- Tests: 866 → **871** (+5: long-prompt match / 短文 fallthrough / 全 messages walk / 負値 reject / first-match-wins precedence)
|
|
149
|
+
- Runtime deps: 5 → 5 (33 sub-release 連続据え置き)
|
|
150
|
+
- Backward compat: 完全互換、既存 `auto_router` rule は何も変わらない
|
|
151
|
+
|
|
152
|
+
#### Changes
|
|
153
|
+
|
|
154
|
+
- `coderouter/config/schemas.py`:
|
|
155
|
+
- `RuleMatcher` に `content_token_count_min: int | None = None` (`ge=1`) を追加、`_MATCHER_FIELDS` に登録 (既存の "exactly one" バリデータが自動適用、`ge=1` で 0/負値は schema load で reject)。
|
|
156
|
+
- docstring の Variants セクションに 6 番目として明記、char/4 ヒューリスティック + 全 messages 対象 (latest-only の他 matcher と差別化) + 5-deps トレードオフを文書化。
|
|
157
|
+
|
|
158
|
+
- `coderouter/routing/auto_router.py`:
|
|
159
|
+
- `_estimate_total_tokens(body)` ヘルパを新設 — `body["system"]` (str / list-of-blocks 両対応) と `body["messages"]` の全 message を walk、`_extract_text` で text を抽出、char 合算を `_CHARS_PER_TOKEN_HEURISTIC=4` で除して token 推定。image / non-text blocks は 0 寄与。
|
|
160
|
+
- `_match_rule` に `estimated_tokens: int` パラメータを追加、6 番目の分岐として `content_token_count_min` 比較を実装。
|
|
161
|
+
- `classify(...)` 内で `_estimate_total_tokens(body)` を 1 回計算、ルール評価ループに流す。`_emit_resolved` / `_emit_fallthrough` の signals payload に `estimated_tokens` を追記、dashboard / Prometheus exporter から「何トークン推定でその profile に流れたか」が見える。
|
|
162
|
+
|
|
163
|
+
- `tests/test_auto_router.py` Group 7 を新設、5 ケース:
|
|
164
|
+
- `test_classify_long_prompt_routes_to_longcontext` — 200,000 chars (~50,000 tokens) → 32,000 threshold を超えて longcontext profile。
|
|
165
|
+
- `test_classify_short_prompt_below_threshold_falls_through` — 1,000 chars (~250 tokens) → default_rule_profile (writing) に落ちる。
|
|
166
|
+
- `test_classify_long_context_walks_all_messages_not_just_latest` — 長い会話 history + 短い最新ユーザー msg、latest-only matcher なら拾えないケースを longContext は拾うことを pin。
|
|
167
|
+
- `test_content_token_count_min_rejects_non_positive_at_load` — `0` / `-5` を `RuleMatcher` 構築時に reject (pydantic `ge=1`)。
|
|
168
|
+
- `test_long_context_first_match_wins_over_later_image_rule` — token-count rule を先に置けば長文+画像 body でも longcontext が勝つ、先勝順序を pin。
|
|
169
|
+
|
|
170
|
+
#### Files touched
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
M CHANGELOG.md
|
|
174
|
+
M coderouter/config/schemas.py
|
|
175
|
+
M coderouter/routing/auto_router.py
|
|
176
|
+
M tests/test_auto_router.py
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
#### Why now
|
|
180
|
+
|
|
181
|
+
`docs/inside/future.md §6.6` の v1.10 着手順序 **#5 (最終)**。実装規模 ~80 LOC + tests ~150 LOC、半日工数 (見積 ~150-200 LOC / 3-5 日より大幅短縮 — auto_router の matcher 追加パターンが per-model auto-routing で確立済み、全 messages walk 用の `_estimate_total_tokens` ヘルパだけ新設で済んだ)。
|
|
182
|
+
|
|
183
|
+
これで **v1.10 候補 5 件全完了** (#1 v1.9-B2 / #2 per-model auto-routing は v1.9.1、#3 monthly budget / #4 v1.9-E phase 2 / #5 longContext auto-switch は本 [Unreleased] umbrella)。次回 PyPI publish 時に **v1.10.0 minor として umbrella tag 化**できる位置 (Vision pillar 完成 + auto-router 全 6 matcher 揃 + cost enforcement 完成)。
|
|
184
|
+
|
|
185
|
+
#### Out of scope (v2.0 以降 / 将来の精緻化)
|
|
186
|
+
|
|
187
|
+
- **tiktoken / SentencePiece による正確なトークンカウント** — 5-deps 不変原則で却下。実機運用で threshold tuning が困難になったら再検討。
|
|
188
|
+
- **Provider 別 context-window 自動推測** — `model-capabilities.yaml` に `max_context_tokens` を加えれば自動推測できる方向もあるが、operator の運用シナリオ次第なので明示宣言で十分。
|
|
189
|
+
- **動的 threshold (chain の最小 max_context_tokens に応じて)** — 同上、明示宣言で十分。
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
### v1.10 候補 #4: v1.9-E phase 2 (L2 memory pressure + L5 backend health) — Vision 完成
|
|
194
|
+
|
|
195
|
+
**Theme: 「8 時間 agent ループでも止まらない」を約束する Long-run Reliability pillar (P2) を完成させる。** v1.9.0 で L3 (tool-loop guard) を phase 1 として先行出荷したが、Vision で謳う 6 系統障害体系のうち **L2 (Memory pressure)** と **L5 (Backend crash / health)** が phase 2 として残っていた。本 release で両方を opt-in guard として実装、`coderouter/guards/` 配下に並ぶ 3 兄弟 (tool_loop / memory_pressure / backend_health) で長時間運用の中核 3 障害をカバーする。
|
|
196
|
+
|
|
197
|
+
#### L2: Memory pressure detection + cooldown
|
|
198
|
+
|
|
199
|
+
ローカル backend (Ollama / LM Studio / llama.cpp) が VRAM 枯渇で 5xx を返す時、エラー本文に `out of memory` / `CUDA out of memory` / `insufficient memory` / `model requires more system memory` 等の OOM フレーズが入る。L2 はこれを観察して当該 provider をクールダウンに入れ、次の chain resolve から `memory_pressure_cooldown_s` 秒間 skip する:
|
|
200
|
+
|
|
201
|
+
```yaml
|
|
202
|
+
profiles:
|
|
203
|
+
- name: default
|
|
204
|
+
providers: [ollama-large, ollama-small, openrouter-fallback]
|
|
205
|
+
memory_pressure_action: skip # off / warn / skip (default: warn)
|
|
206
|
+
memory_pressure_cooldown_s: 120 # default 120s, 10〜3600 s
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
`action=skip` の時、ollama-large が OOM → 120 秒間 ollama-large を chain から除外、ollama-small や openrouter-fallback に流れる → クールダウン明けで再度 ollama-large を試す。`action=warn` (default) は log のみ、`off` は完全に無効化 (zero overhead)。
|
|
210
|
+
|
|
211
|
+
#### L5: Backend health (consecutive failure state machine)
|
|
212
|
+
|
|
213
|
+
backend が突発 crash した時の defacto demote。`BackendHealthMonitor` が provider ごとに consecutive failure 数を数え、`backend_health_threshold` (default 3) で `HEALTHY → DEGRADED`、`2 x threshold` で `DEGRADED → UNHEALTHY` に遷移。1 回の成功で即 HEALTHY 復帰。`backend_health_action: demote` の時、UNHEALTHY な provider は chain 末尾に降格 (skip ではなく **demote** — 死活確認の 1 リクエストは飛ばす、best-effort principle):
|
|
214
|
+
|
|
215
|
+
```yaml
|
|
216
|
+
profiles:
|
|
217
|
+
- name: default
|
|
218
|
+
providers: [ollama-local, anthropic-fallback]
|
|
219
|
+
backend_health_action: demote # off / warn / demote (default: warn)
|
|
220
|
+
backend_health_threshold: 3
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
v1.9-C の `adaptive` (rolling-window 連続観測 + debounce) とは直交関係 — adaptive が「徐々に遅くなった」勾配ケース、L5 が「突発 crash」二値ケース。両者重ね掛け可能で、両方 enable した chain では「latency 劣化 → adaptive demote」「crash → L5 demote」の両方の信号で並べ替え。
|
|
224
|
+
|
|
225
|
+
#### Numbers
|
|
226
|
+
|
|
227
|
+
- Tests: 839 → **866** (+27 累積、L2 +19 / L5 +8)
|
|
228
|
+
- Runtime deps: 5 → 5 (32 sub-release 連続据え置き)
|
|
229
|
+
- Backward compat: 完全互換、両 `*_action` のデフォルトは `warn` (= log のみ、行動変化なし)。`off` で完全無効化。既存 v1.9.x deployment は yaml 変更なしで自然継続
|
|
230
|
+
|
|
231
|
+
#### Changes
|
|
232
|
+
|
|
233
|
+
- `coderouter/guards/memory_pressure.py` 新設 (~170 LOC):
|
|
234
|
+
- `is_memory_pressure_error(exc)` — 純関数、9 種の OOM フレーズに対する case-insensitive substring match (Ollama / LM Studio / llama.cpp / 汎用 CUDA / Metal の実観測パターン)。
|
|
235
|
+
- `MemoryPressureGuard` — per-provider TTL cooldown tracker、`mark_pressured` / `is_pressured` / `pressured_until` API、`time.monotonic` ベースで wall-clock skew 耐性、tests は `now=` 引数で deterministic 注入。
|
|
236
|
+
|
|
237
|
+
- `coderouter/guards/backend_health.py` 新設 (~200 LOC):
|
|
238
|
+
- `BackendHealthMonitor` — per-provider 状態機械 (HEALTHY / DEGRADED / UNHEALTHY)、`record_attempt(success, threshold)` で観測、状態遷移時のみ `HealthTransition` を返す (no log spam on stable state)、threshold は per-call で profile 違いに対応。
|
|
239
|
+
- 状態機械の遷移ルール: 失敗 N 回 (= threshold) で DEGRADED、2N 回で UNHEALTHY、1 回成功で即 HEALTHY 復帰。
|
|
240
|
+
|
|
241
|
+
- `coderouter/config/schemas.py`:
|
|
242
|
+
- `FallbackChain` に `memory_pressure_action` / `memory_pressure_cooldown_s` (L2) と `backend_health_action` / `backend_health_threshold` (L5) を追加。L3 (`tool_loop_*`) と同じ命名 + 同じ "off / warn / 行動" tri-state パターン。
|
|
243
|
+
- 各 field に `Literal` 型 + range 制約 + 詳細 docstring (どの障害を見るか、L2/L5 の使い分け、v1.9-C adaptive との関係)。
|
|
244
|
+
|
|
245
|
+
- `coderouter/logging.py`:
|
|
246
|
+
- L2: `log_memory_pressure_detected` / `log_skip_memory_pressure` / `log_chain_memory_pressure_blocked` ヘルパ + 3 つの TypedDict payload。paid-gate / budget-gate と完全 symmetric。
|
|
247
|
+
- L5: `log_backend_health_changed` (state transition、payload に old_state/new_state/consecutive_failures) / `log_demote_unhealthy_provider` ヘルパ + 2 つの TypedDict。
|
|
248
|
+
|
|
249
|
+
- `coderouter/routing/fallback.py`:
|
|
250
|
+
- `FallbackEngine` に `_memory_pressure` / `_backend_health` lazy property を追加 (`_adaptive` / `_budget` と同じパターン、`__new__` 経由 legacy tests 対応)。
|
|
251
|
+
- `_observe_provider_failure(provider, exc, profile)` ヘルパ — L2 OOM 検出 + L5 失敗カウンタを single chokepoint で dispatch、6 つの failure site (4 entry point × non-stream/mid-stream) から呼ぶ。
|
|
252
|
+
- `_observe_provider_success(provider, profile)` 新設 — L5 状態機械の成功遷移を 4 success site (provider-ok 時) から呼ぶ。
|
|
253
|
+
- `_resolve_chain` を 4-pass に拡張: paid → budget → **L2 pressure skip** → L5 demote。L2 は filter (skip)、L5 は reorder (demote)、両者の役割分担を明確化。L5 demote は `unhealthy and healthy` 両方ある時のみ実施 (uniformly UNHEALTHY chain は no-op、log spam 抑制)。
|
|
254
|
+
|
|
255
|
+
- `coderouter/metrics/collector.py`:
|
|
256
|
+
- `_provider_skipped_memory_pressure: Counter` + `_chain_memory_pressure_blocked_total: int` (L2)。
|
|
257
|
+
- `_provider_demoted_unhealthy: Counter` + `_backend_health_transitions: dict[str, Counter]` (L5、transition を destination state でキー)。
|
|
258
|
+
- `skip-memory-pressure` / `chain-memory-pressure-blocked` / `backend-health-changed` / `demote-unhealthy-provider` event の dispatch + snapshot/reset 配線。
|
|
259
|
+
|
|
260
|
+
- `coderouter/metrics/prometheus.py`:
|
|
261
|
+
- `coderouter_provider_skipped_total{reason="memory_pressure"}` を既存 `paid` / `unknown` / `budget` と同 counter に同居。
|
|
262
|
+
- `coderouter_provider_demoted_unhealthy_total{provider}` (L5)、`coderouter_backend_health_transitions_total{provider, state}` (L5)、`coderouter_chain_memory_pressure_blocked_total` (L2) を追加。
|
|
263
|
+
|
|
264
|
+
- `tests/test_memory_pressure.py` 新設 (~360 LOC、+19 tests):
|
|
265
|
+
- **Group 1 (detector)**: 8 種の OOM フレーズを parameterize で網羅、5 種の非 OOM 失敗で false 確認。
|
|
266
|
+
- **Group 2 (guard)**: TTL cooldown / lazy expiry / re-mark 拡張。
|
|
267
|
+
- **Group 3 (engine)**: action=warn は log only / action=skip は cooldown 中 chain skip + fallback / action=off は完全無効 / 全 provider pressured で `chain-memory-pressure-blocked` warn + `NoProvidersAvailableError`。
|
|
268
|
+
|
|
269
|
+
- `tests/test_backend_health.py` 新設 (~340 LOC、+8 tests):
|
|
270
|
+
- **Group 1 (monitor)**: 初期状態 HEALTHY、threshold/2x threshold での状態遷移、success で UNHEALTHY → HEALTHY 即復帰、stable state で transition 返さない。
|
|
271
|
+
- **Group 2 (engine action)**: warn は log only / demote で chain reorder (try-provider 順序検証) / off で監視ゼロ / UNHEALTHY → HEALTHY recovery transition log。
|
|
272
|
+
- **Group 3 (chain reorder)**: 全 provider UNHEALTHY 時は demote no-op (log spam なし、best-effort 続行)。
|
|
273
|
+
|
|
274
|
+
#### Files touched
|
|
275
|
+
|
|
276
|
+
```
|
|
277
|
+
A coderouter/guards/backend_health.py
|
|
278
|
+
A coderouter/guards/memory_pressure.py
|
|
279
|
+
A tests/test_backend_health.py
|
|
280
|
+
A tests/test_memory_pressure.py
|
|
281
|
+
M CHANGELOG.md
|
|
282
|
+
M coderouter/config/schemas.py
|
|
283
|
+
M coderouter/logging.py
|
|
284
|
+
M coderouter/metrics/collector.py
|
|
285
|
+
M coderouter/metrics/prometheus.py
|
|
286
|
+
M coderouter/routing/fallback.py
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
#### Why now
|
|
290
|
+
|
|
291
|
+
`docs/inside/future.md §6.6` v1.10 着手順序 #4 — **Vision の核心**。v1.9.0 GA で「v1.10 候補」と整理した backlog で唯一 ~900 LOC スケールの Vision-critical pillar。v1.9.1 の monthly-budget で cost 軸の運用が見えるようになった上に、L2/L5 で **「6 系統障害のうち L2/L3/L5 を体系的に対処」** が完成。`L1 Context overflow` / `L4 Quality drift` / `L6 Mid-stream interrupt 強化` は v2.0-F/G/H の領域、v1.x で cover する long-run reliability の到達点として位置付け。
|
|
292
|
+
|
|
293
|
+
#### Out of scope (v2.0 以降)
|
|
294
|
+
|
|
295
|
+
- **L5 active probing** (60s 間隔の能動 GET /api/version) — 受動 observation で十分カバーできる範囲、active probe を加えると httpx の lifecycle / mocking の複雑度が増えるため v2.0-I (`continuous probing` pillar 拡張) で再検討。
|
|
296
|
+
- **L2 thresholding (count of OOM events before mark)** — single OOM = mark の素朴実装で十分。複数 OOM 観測でしか mark しないという調整は実機運用 feedback が来てから。
|
|
297
|
+
- **HEALTHY/DEGRADED/UNHEALTHY の 4 段階以上化** — 3 段階で十分、運用 feedback が来てから検討。
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
### v1.10 候補 #3: provider 月次予算上限 (LiteLLM 由来 / v1.9-D の累積版)
|
|
302
|
+
|
|
303
|
+
**Theme: v1.9-D で「いくら使ったか」が見えるようになった所に、「これ以上使うな」を宣言できる gate を足す。** v1.9-D の `cost_total_usd` は process-lifetime cumulative なので billing-cycle 上限としては使えない (再起動で消える + 月境界で reset しない)。本機能は **per-provider monthly USD cap** を `cost.monthly_budget_usd` で宣言できるようにし、UTC 暦月単位の running total が cap に達した provider を chain resolver が skip するようにする。
|
|
304
|
+
|
|
305
|
+
ユースケース例:
|
|
306
|
+
|
|
307
|
+
```yaml
|
|
308
|
+
providers:
|
|
309
|
+
- name: anthropic-direct
|
|
310
|
+
kind: anthropic
|
|
311
|
+
base_url: https://api.anthropic.com
|
|
312
|
+
model: claude-sonnet-4-6
|
|
313
|
+
cost:
|
|
314
|
+
input_tokens_per_million: 3.0
|
|
315
|
+
output_tokens_per_million: 15.0
|
|
316
|
+
monthly_budget_usd: 5.0 # ← v1.10 新フィールド
|
|
317
|
+
- name: ollama-local
|
|
318
|
+
base_url: http://localhost:11434/v1
|
|
319
|
+
model: qwen3.6:35b-a3b
|
|
320
|
+
# 無料 / cost 未設定 = 無制限 (skip 対象外)
|
|
321
|
+
profiles:
|
|
322
|
+
- name: default
|
|
323
|
+
providers: [anthropic-direct, ollama-local] # paid → free fallback
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
`anthropic-direct` が今月 5 USD 消費した時点で chain resolver が skip し、`ollama-local` (無料) に fall through する。`skip-budget-exceeded` info + (全 provider が cap に達した時のみ) `chain-budget-exceeded` warn が emit される。
|
|
327
|
+
|
|
328
|
+
**Persistence の意図的な制限**: in-memory only。プロセス再起動で running total が 0 にリセットされる。**5-deps 不変原則** (`plan.md §5.4`) を守るため sqlite / Redis / disk は導入しない。durable な月次 enforcement が必要なオペレータは v1.9-D の `cost_total_usd` panel を外部監視ツール (Prometheus alertmanager / Grafana threshold) で受ければ十分カバー可能。
|
|
329
|
+
|
|
330
|
+
- Tests: 831 → **839** (+8: BudgetTracker pure 3 / CostConfig schema 2 / engine integration 3)
|
|
331
|
+
- Runtime deps: 5 → 5 (31 sub-release 連続据え置き)
|
|
332
|
+
- Backward compat: 完全互換、`monthly_budget_usd` 未設定 deployment は挙動完全一致 (opt-in feature)
|
|
333
|
+
|
|
334
|
+
#### Changes
|
|
335
|
+
|
|
336
|
+
- `coderouter/config/schemas.py`:
|
|
337
|
+
- `CostConfig` に `monthly_budget_usd: float | None = None` を追加 (`ge=0.0`、None = 無制限)。
|
|
338
|
+
- docstring で UTC calendar-month + in-memory only persistence を明示、5-deps 不変原則との整合 (no sqlite/Redis) を文書化。
|
|
339
|
+
|
|
340
|
+
- `coderouter/routing/budget.py` 新設 (~190 LOC):
|
|
341
|
+
- `BudgetTracker` クラス — per-provider current-month USD running total を `dict[str, float]` で保持、`threading.RLock` 配下。月境界判定は `_utc_month_key` ヘルパ (UTC `datetime.now()` 経由、tests は `now=` 引数で deterministic に注入可能)。
|
|
342
|
+
- 公開 API: `record(provider, cost_usd)` / `is_over_budget(provider, budget_usd)` / `current_month()` / `total_for_provider(provider)` / `reset()`。
|
|
343
|
+
- **Lazy month rollover**: 各 public call の入口で `_roll_if_needed` を呼び、cached month と current UTC month が違えば `_totals` を clear してから query に答える。background timer 不要。
|
|
344
|
+
- `is_over_budget` は `>=` 比較 — exact-hit の "5.00 USD" は exhausted と判定 (conservative: 次の call は bill しない)。
|
|
345
|
+
|
|
346
|
+
- `coderouter/logging.py`:
|
|
347
|
+
- `SkipBudgetExceededPayload` / `ChainBudgetExceededPayload` TypedDict + `log_skip_budget_exceeded` / `log_chain_budget_exceeded` ヘルパを追加。`log_chain_paid_gate_blocked` のパターンを完全 mirror、payload に `month` (YYYY-MM UTC bucket) を含める。
|
|
348
|
+
|
|
349
|
+
- `coderouter/routing/fallback.py`:
|
|
350
|
+
- `FallbackEngine.__init__` に `_budget_tracker: BudgetTracker = BudgetTracker()` を追加、`_adaptive` と同じ lazy property pattern で `_budget` を露出 (legacy tests の `__new__` 経路でも空 tracker が返る)。
|
|
351
|
+
- `_resolve_chain` を 2-pass に refactor: pass 1 が paid-gate (既存ロジック)、pass 2 が **budget-gate** (新規)。budget-gate は `provider_cfg.cost.monthly_budget_usd` が set されている provider のみ check、`is_over_budget` ならば `skip-budget-exceeded` info を emit して候補から除外。chain が空になった時の aggregate warn は `blocked_by_budget` を優先 (paid-gate より後段で filter したため)、`chain-budget-exceeded` を fire。
|
|
352
|
+
- `_emit_cache_observed` / `_emit_cache_observed_streaming` に `budget: BudgetTracker | None = None` 引数を追加、`compute_cost_for_attempt` の結果が positive な時に `budget.record(provider, cost.total_usd)` を呼ぶ。engine 側 2 callsite (`generate_anthropic` / `stream_anthropic`) で `budget=self._budget` を渡す配線。
|
|
353
|
+
|
|
354
|
+
- `coderouter/metrics/collector.py`:
|
|
355
|
+
- `_provider_skipped_budget: Counter[str]` + `_chain_budget_exceeded_total: int` を追加、`_provider_skipped_paid` / `_chain_paid_gate_blocked_total` の対称配置。
|
|
356
|
+
- `_dispatch` に `skip-budget-exceeded` / `chain-budget-exceeded` event の handler を追加。`reset()` / `snapshot()` も両 counter を含むように拡張。
|
|
357
|
+
- module docstring の event inventory に v1.10 行 2 件追記。
|
|
358
|
+
|
|
359
|
+
- `coderouter/metrics/prometheus.py`:
|
|
360
|
+
- `coderouter_provider_skipped_total{provider, reason="budget"}` を既存の `paid` / `unknown` と同じ counter に同居 (dashboards が reason 別 stack できるように)。
|
|
361
|
+
- `coderouter_chain_budget_exceeded_total` scalar counter を新設 (`coderouter_chain_paid_gate_blocked_total` の対称配置)。
|
|
362
|
+
|
|
363
|
+
- `tests/test_budget.py` 新設 (~340 LOC、+8 tests):
|
|
364
|
+
- **Group 1 (BudgetTracker pure)**: record 蓄積 / is_over_budget の `>=` boundary semantics / 月境界 rollover (`now=` 引数で April→May 跨ぎを deterministic に検証)。
|
|
365
|
+
- **Group 2 (CostConfig schema)**: `monthly_budget_usd: 5.0` 受理、負値 reject (pydantic `ge=0.0`)。
|
|
366
|
+
- **Group 3 (engine integration)**: pre-loaded budget でも primary skip + fallback 経由 (warn なし) / 全 provider cap で `NoProvidersAvailableError` + `chain-budget-exceeded` warn 1 回 / 実 attempt の cost が `BudgetTracker` に蓄積されて 3 回目で skip されることを確認 (real wiring の end-to-end test)。
|
|
367
|
+
|
|
368
|
+
#### Files touched
|
|
369
|
+
|
|
370
|
+
```
|
|
371
|
+
A coderouter/routing/budget.py
|
|
372
|
+
A tests/test_budget.py
|
|
373
|
+
M CHANGELOG.md
|
|
374
|
+
M coderouter/config/schemas.py
|
|
375
|
+
M coderouter/logging.py
|
|
376
|
+
M coderouter/metrics/collector.py
|
|
377
|
+
M coderouter/metrics/prometheus.py
|
|
378
|
+
M coderouter/routing/fallback.py
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
#### Why now
|
|
382
|
+
|
|
383
|
+
`docs/inside/future.md §6.6` の v1.10 着手順序 #3。v1.9-D で観測の基盤ができた直後に **enforcement** を足す自然な順序、cost-aware ユーザー (paid backend を組み込む operator) にとって最も価値の高い v1.10 候補。LiteLLM が同等機能を `litellm[proxy]` の中で重実装 (Redis 必須) しているのに対し、CodeRouter は in-memory + 5-deps 維持で「個人開発者用の budget guard」として割り切ることで構造的負債を避ける。
|
|
384
|
+
|
|
385
|
+
#### Out of scope
|
|
386
|
+
|
|
387
|
+
- **Persistent budget state** (sqlite / Redis / disk-backed) — 5-deps 不変原則により未対応。durable enforcement 必要なケースは v1.9-D dashboard を外部 alerting に繋ぐ運用で代替。
|
|
388
|
+
- **Rolling 30-day window** — UTC calendar month で十分 (typical billing cycle と一致、月境界判定の rollover 実装が単純)。rolling window は `_utc_month_key` を date-windowed key に差し替えれば追加できるが、operator request が来てから判断。
|
|
389
|
+
- **Per-profile budget** (vs per-provider) — provider 単位で十分。同じ provider を複数 profile が共有する場合 budget は共有されるべき (実コストの帰属先は provider なので) という意味的にも provider 帰属が正しい。
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## [v1.9.1] — 2026-05-01 (Patch — v1.10 候補から quick win 2 件先行刈取り)
|
|
394
|
+
|
|
395
|
+
**Theme: v1.9.0 GA で「v1.10 候補」と整理した backlog のうち、構造的負債を伴わない quick win 2 件 (streaming cache 観測の完成形 + agent-driven model 識別子で profile 分岐) を patch として束ねる。** 観測ループの埋め残しと、Claude Code / Cursor 等 agent 側の設定 (Opus / Sonnet / Haiku 使い分け) が CodeRouter の declarative routing に反映できる経路を、完全互換で追加。両機能とも v1.9.0 既存 framework (`cache-observed` log / `auto_router.rules`) の延長線で、新 framework / 依存追加なし。
|
|
396
|
+
|
|
397
|
+
含まれる出荷 2 件 (`docs/inside/future.md §6.6` の v1.10 着手順序 #1, #2):
|
|
398
|
+
|
|
399
|
+
| # | sub-release | テーマ | LOC | tests |
|
|
400
|
+
|---|---|---|---|---|
|
|
401
|
+
| 1 | **v1.9-B2** | streaming 経路の usage 集約 — `_StreamUsageAccumulator` + `_emit_cache_observed_streaming` で `outcome=unknown` placeholder を観測値に置換 | ~150 | +3 |
|
|
402
|
+
| 2 | **per-model auto-routing** | `RuleMatcher.model_pattern` 5 番目 matcher 追加、`re.fullmatch` で body model id を評価 (free-claude-code 由来) | ~120 | +5 |
|
|
403
|
+
|
|
404
|
+
- Tests: 830 → **838** (+8 累積、v1.9-B2 +3 / per-model +5)
|
|
405
|
+
- Runtime deps: 5 → 5 (30 sub-release 連続据え置き)
|
|
406
|
+
- Backward compat: 完全互換、既存 yaml / API / log payload 全部既存と同じ schema、新フィールド (`model_pattern`) を使わない deployment は挙動完全一致
|
|
407
|
+
- pyproject version: 1.9.0 → 1.9.1
|
|
408
|
+
|
|
409
|
+
### Migration
|
|
410
|
+
|
|
411
|
+
不要。**v1.9.0 / v1.9.0a* からの自然なアップグレード**:
|
|
412
|
+
|
|
413
|
+
- `coderouter` コマンド名 / Python import 名 / providers.yaml の format / env 変数 / ingress URL すべて完全に同じ
|
|
414
|
+
- streaming で `cache-observed` log を読んでいる外部 consumer (例: dashboard / Prometheus / 自前 JSONL parser) には、v1.9.0a6 までゼロ固定だった `cache_read_input_tokens` / `cache_creation_input_tokens` / `input_tokens` / `output_tokens` / `outcome` / `cost_usd` / `cost_savings_usd` が観測値に置き換わる。consumer 側は **値が増えた** だけで schema は同じ、ロジック変更不要
|
|
415
|
+
- `auto_router.rules[].if.model_pattern` を使い始めるには yaml に 1 行足すだけ、既存 rule に影響なし
|
|
416
|
+
|
|
417
|
+
### Out of scope (v1.10 / v1.9.x 続編)
|
|
418
|
+
|
|
419
|
+
[v1.9.0] GA ノートと `docs/inside/future.md §6.6` で示した v1.10 候補から残り 3 件:
|
|
420
|
+
|
|
421
|
+
- **provider 月次予算上限** (LiteLLM 由来、v1.9-D の累積版) — `monthly_budget_usd` で provider 単位の running total + 超過時 skip + log。~400 LOC、3-5 日。
|
|
422
|
+
- **v1.9-E phase 2** — L2 Memory pressure (LM Studio / ollama backend OOM 検知) / L5 Backend health (continuous probe + chain reorder)。**Vision の核心 (8 時間 agent ループでも止まらない)** を完成させる pillar。~900 LOC、1-2 週間。
|
|
423
|
+
- **longContext auto-switch** — `auto_router` rule type 5 として `content_token_count_min` matcher 追加 (claude-code-router task-based 取込)。~200 LOC、3-5 日。
|
|
424
|
+
|
|
425
|
+
これら 3 件は構造拡張を伴うため v1.9.1 patch ではなく v1.10.0 minor で個別 sub-release にして出荷する想定。
|
|
426
|
+
|
|
427
|
+
### Files touched
|
|
428
|
+
|
|
429
|
+
```
|
|
430
|
+
M CHANGELOG.md
|
|
431
|
+
M coderouter/config/schemas.py
|
|
432
|
+
M coderouter/routing/auto_router.py
|
|
433
|
+
M coderouter/routing/fallback.py
|
|
434
|
+
M docs/inside/future.md
|
|
435
|
+
M plan.md
|
|
436
|
+
M pyproject.toml
|
|
437
|
+
M tests/test_auto_router.py
|
|
438
|
+
M tests/test_fallback_cache_observed.py
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
### per-model auto-routing (v1.10 候補 #2、free-claude-code 由来)
|
|
444
|
+
|
|
445
|
+
**Theme: agent が送ってきた `model` フィールドそのものを auto_router の判定軸に追加。** Claude Code / Cursor 等の agent 側設定 (Opus / Sonnet / Haiku の使い分け) を、CodeRouter 側 profile chain の選択にも反映できるようにする。`auto_router.rules[].if.model_pattern` を 5 番目の matcher として導入、既存 4 種 (`has_image` / `code_fence_ratio_min` / `content_contains` / `content_regex`) と同じ "exactly one" 規約と eager regex compile (typo は startup で fast-fail) を継承。
|
|
446
|
+
|
|
447
|
+
ユースケース例:
|
|
448
|
+
|
|
449
|
+
```yaml
|
|
450
|
+
auto_router:
|
|
451
|
+
rules:
|
|
452
|
+
- if: { model_pattern: "claude-3-5-haiku.*" }
|
|
453
|
+
route_to: lightweight
|
|
454
|
+
- if: { model_pattern: "claude-3-5-sonnet.*" }
|
|
455
|
+
route_to: coding
|
|
456
|
+
default_rule_profile: writing
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
agent 側で「モデルの使い分けは決まってる」状況に CodeRouter が綺麗に乗れる。`free-claude-code` repo の同様機能を CodeRouter の declarative auto_router framework に取り込んだ形。
|
|
460
|
+
|
|
461
|
+
- Tests: 833 → **838** (+5: Sonnet→coding / Haiku→lightweight / no-model field → fallthrough / 不正 regex は schema load で fast-fail / model_pattern と content rule の first-match-wins precedence)
|
|
462
|
+
- Runtime deps: 5 → 5 (30 sub-release 連続据え置き)
|
|
463
|
+
- Backward compat: 完全互換、既存 `auto_router` rule は何も変わらない、`model_pattern` を使わない deployment は挙動完全一致
|
|
464
|
+
|
|
465
|
+
#### Changes
|
|
466
|
+
|
|
467
|
+
- `coderouter/config/schemas.py`:
|
|
468
|
+
- `RuleMatcher` に `model_pattern: str | None = None` を追加、`_MATCHER_FIELDS` tuple に追加 (zero/multiple-fields の "exactly one" バリデータが自動適用)。
|
|
469
|
+
- `_compile_regex_eagerly` バリデータを `model_pattern` も覆うよう拡張、不正な regex は schema load で `ValueError("Invalid regex for model_pattern ...")` を発火 (`content_regex` と同じ fast-fail パターン)。
|
|
470
|
+
- docstring の Variants セクションに 5 番目として `model_pattern` を追記、`re.fullmatch` semantics と `content_regex` の `re.search` との違い (model 識別子は "structured tokens" であり全体描写型) を明示。
|
|
471
|
+
|
|
472
|
+
- `coderouter/routing/auto_router.py`:
|
|
473
|
+
- `_extract_model(body)` ヘルパを新設 — 両 ingress (Anthropic `/v1/messages` / OpenAI `/v1/chat/completions`) で body の top-level `model` field を 1 ヶ所で抽出、空文字列 / 非 str は None 扱い。
|
|
474
|
+
- `_match_rule(rule, message, text, model)` シグネチャに `model: str | None` を追加、`model_pattern` matcher を 5 番目の分岐として実装。`re.fullmatch` で評価 (model id は構造的 token なので部分一致より全体記述型の方が直観に合う)。`model is None` の時は False を返して fallthrough させる (空 body などの test fixtures 対策)。
|
|
475
|
+
- `classify(...)` 内で `_extract_model(body)` を一度だけ呼び、`_match_rule` に流す。`user_msg is None` でも `model_pattern` rule は評価する (空 messages でも model 経路で route 可能)。
|
|
476
|
+
- `_emit_resolved` / `_emit_fallthrough` の `signals` payload に `model` を追記、auto-router-resolved log で何の model id で routing 判断したかが dashboard / Prometheus exporter から見える。
|
|
477
|
+
|
|
478
|
+
- `tests/test_auto_router.py` Group 6 (per-model auto-routing) を新設、5 ケース:
|
|
479
|
+
- `test_classify_model_pattern_sonnet_routes_to_coding` — 基本ケース、`claude-3-5-sonnet.*` → coding profile。content は writing 寄りでも model rule が勝つ。
|
|
480
|
+
- `test_classify_model_pattern_haiku_routes_to_lightweight` — 4-profile fixture (`_model_pattern_config` で lightweight 追加)、Haiku id → lightweight profile。
|
|
481
|
+
- `test_classify_model_pattern_no_model_field_falls_through` — body に `model` field がない時、`r".+"` でも match せず default_rule_profile に落ちる (fixtures / test harness 用 robustness)。
|
|
482
|
+
- `test_model_pattern_invalid_regex_fast_fails_at_load` — `r"([unclosed"` → `RuleMatcher` 構築時に `ValueError(model_pattern)` (`content_regex` と同じ eager compile path)。
|
|
483
|
+
- `test_model_pattern_first_match_wins_over_later_content_rule` — model_pattern rule を content_contains rule より前に置くと、両方 match する body でも先勝、global "first match wins" を pin。
|
|
484
|
+
|
|
485
|
+
#### Files touched
|
|
486
|
+
|
|
487
|
+
```
|
|
488
|
+
M CHANGELOG.md
|
|
489
|
+
M coderouter/config/schemas.py
|
|
490
|
+
M coderouter/routing/auto_router.py
|
|
491
|
+
M tests/test_auto_router.py
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
#### Why now
|
|
495
|
+
|
|
496
|
+
`docs/inside/future.md §6.6` の v1.10 着手順序で 2 番目に推奨されていた quick win。実装規模 ~120 LOC (見積 ~150-200 LOC を下回って収束)、tests +5、半日工数。既存 auto_router framework の 1 matcher 追加なので構造的負債なし、`free-claude-code` 由来要望を CodeRouter の declarative 思想を崩さずに取り込めた。次の v1.10 候補 (provider 月次予算 / longContext auto-switch / v1.9-E phase 2) の前段階として位置付け。
|
|
497
|
+
|
|
498
|
+
---
|
|
499
|
+
|
|
500
|
+
### v1.9-B2: streaming 経路の usage 集約 (v1.10 候補 #1)
|
|
501
|
+
|
|
502
|
+
**Theme: v1.9.0 で意図的に v1.10 候補へ繰り越した quick win を回収。** v1.9.0a6 で「streaming パスでも `cache-observed` log を emit する」ところまでは揃えたが、token 数は `outcome=unknown` + ゼロ固定の placeholder だった。本 patch は `message_start.message.usage` + 終端 `message_delta.usage` を accumulator で max-merge 集約し、非 streaming (`generate_anthropic`) と同じ outcome 分類 + cost 計算 + ログ payload 形状に揃える。`/dashboard` / Prometheus / MetricsCollector 側は branch 不要で streaming 経路の数字が取れるようになる。
|
|
503
|
+
|
|
504
|
+
- Tests: 830 → **833** (+3: cache_hit / cache_creation / no_cache の streaming 集約 — `tests/test_fallback_cache_observed.py`)
|
|
505
|
+
- Runtime deps: 5 → 5 (29 sub-release 連続据え置き)
|
|
506
|
+
- Backward compat: 完全互換、log payload は v1.9-A と同じ schema、`streaming=true` flag のみ意味的に "観測値" になる (ゼロ placeholder ではなくなる)
|
|
507
|
+
|
|
508
|
+
#### Changes
|
|
509
|
+
|
|
510
|
+
- `coderouter/routing/fallback.py`:
|
|
511
|
+
- `_StreamUsageAccumulator` を新設 — `message_start.message.usage` と `message_delta.usage` から `input_tokens` / `output_tokens` / `cache_read_input_tokens` / `cache_creation_input_tokens` を per-field max-merge で集約。`output_tokens` は終端 `message_delta` で最終値が決まるため max が安全、cache fields は API minor version によって `message_start` / `message_delta` どちらにも現れる可能性があるため両方を観測。`usage_present` は「upstream が空 dict も含めて usage を返したか」を保持し、何も流れてこなかった streaming は引き続き `outcome=unknown` に分類。
|
|
512
|
+
- `_emit_cache_observed_streaming(...)` を追加 — accumulator 値を `classify_cache_outcome` / `compute_cost_for_attempt` に通して `log_cache_observed` を呼ぶ。非 streaming `_emit_cache_observed` と同じ outcome 分類 + cost 計算ロジック。
|
|
513
|
+
- `stream_anthropic(...)` 内のループで `acc = _StreamUsageAccumulator()` を初期化、`first` および後続 `event_iter` の各 event に `acc.observe(...)` を呼ぶ。完了時の `log_cache_observed(..., outcome="unknown", *=0)` を `_emit_cache_observed_streaming(acc, ..., provider_config=adapter.config)` に置換。
|
|
514
|
+
- `_emit_cache_observed` の docstring を更新 — `streaming=True` arg は openai_compat 経路 (downgrade で 1 つの response に collapse される) 用に残す説明に改訂。
|
|
515
|
+
|
|
516
|
+
- `tests/test_fallback_cache_observed.py`:
|
|
517
|
+
- `_CacheAnthropicAdapter.stream_anthropic` を constructor 引数駆動に変更 (`message_start.message.usage` に input_tokens + cache 系、`message_delta.usage` に input_tokens + output_tokens を流す、ゼロ時は空 dict を出して "usage 一切なし" を再現可能)。
|
|
518
|
+
- 既存 `test_cache_observed_fires_on_streaming_with_unknown_outcome` の docstring を v1.9-B2 文脈に更新 (上流から usage が 1 件も流れない時の `unknown` 床をピン留め)。
|
|
519
|
+
- 新規 3 ケース:
|
|
520
|
+
- `test_streaming_aggregates_cache_hit_usage` — `cache_read_input_tokens=2048` を含む stream → `outcome=cache_hit` + 入出力カウンタ集約。
|
|
521
|
+
- `test_streaming_aggregates_cache_creation_usage` — `cache_creation_input_tokens=1500` の stream → `outcome=cache_creation`。
|
|
522
|
+
- `test_streaming_aggregates_no_cache_outcome` — non-zero usage + cache fields なし → `outcome=no_cache` (本番最頻 case、v1.9.0a6 の placeholder では拾えていなかった)。
|
|
523
|
+
|
|
524
|
+
#### Why now
|
|
525
|
+
|
|
526
|
+
v1.9.0 GA ノートで明示した「v1.10 候補」のうち最も短期に取れる quick win。実装サイズ ~150 LOC、半日工数で `outcome=unknown` placeholder を観測値に置き換えられるため、cost dashboard / cache-hit rate panel の streaming 経路カバレッジが完成する。`v1.9-E phase 2` (L2/L5) や per-model auto-routing といった上位 priority 作業の前段で済ませておくと、その後の adaptive routing / Vision pillar 完成度が上がる。
|
|
527
|
+
|
|
528
|
+
#### Out of scope
|
|
529
|
+
|
|
530
|
+
- ChatRequest.stream() 経路 (OpenAI-shaped streaming) は対象外 — `stream_anthropic` の sibling であり、Anthropic 経由の cache observation は未対応の領域。Anthropic prompt cache を利用する client は実質 `/v1/messages` 経由なので影響範囲は限定的。
|
|
531
|
+
- v1.9.0a6 で論じた "downgrade 後の synthesize_anthropic_stream_from_response" 経路 — 元になる AnthropicResponse から `message_start` event が usage 付きで再構築されるため、accumulator が自動でカバーする (追加実装不要)。
|
|
532
|
+
|
|
533
|
+
#### Files touched
|
|
534
|
+
|
|
535
|
+
```
|
|
536
|
+
M CHANGELOG.md
|
|
537
|
+
M coderouter/routing/fallback.py
|
|
538
|
+
M tests/test_fallback_cache_observed.py
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
---
|
|
542
|
+
|
|
9
543
|
## [v1.9.0] — 2026-04-29 (Umbrella tag — Cache observability + Adaptive routing + Cost-aware + Long-run reliability)
|
|
10
544
|
|
|
11
545
|
**Theme: 「観測 → 理解 → 行動 → 信頼性」を 1 minor で揃える、observability pillar の成熟。** v1.9.0 は 6 sub-release (v1.9-A〜E) を通じて、CodeRouter を「動いてはいるが何が起きているか分からない」状態から、「**何にいくら使った / どこで遅くなった / 何で詰まった**」が運用ログ 1 行で分かる状態に押し上げる。具体的には:
|
|
@@ -841,7 +1375,7 @@ M coderouter/doctor.py
|
|
|
841
1375
|
M pyproject.toml
|
|
842
1376
|
M plan.md
|
|
843
1377
|
M docs/troubleshooting.md
|
|
844
|
-
M docs/articles/note-v1-8-1-reality-check.md (or new file v1-8-2)
|
|
1378
|
+
M docs/articles/v1-saga/note-1-v1-8-1-reality-check.md (or new file v1-8-2)
|
|
845
1379
|
M tests/test_doctor.py
|
|
846
1380
|
```
|
|
847
1381
|
|