coderouter-cli 1.10.0__tar.gz → 2.0.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.10.0 → coderouter_cli-2.0.0}/CHANGELOG.md +196 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/PKG-INFO +10 -8
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/README.en.md +8 -7
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/README.md +9 -7
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/cli_stats.py +48 -1
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/config/schemas.py +111 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/data/model-capabilities.yaml +79 -0
- coderouter_cli-2.0.0/coderouter/guards/context_budget.py +376 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/ingress/anthropic_routes.py +19 -2
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/logging.py +89 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/metrics/collector.py +49 -2
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/metrics/prometheus.py +71 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/routing/auto_router.py +54 -47
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/routing/fallback.py +196 -0
- coderouter_cli-2.0.0/coderouter/token_estimation.py +161 -0
- coderouter_cli-2.0.0/docs/context-budget.md +175 -0
- coderouter_cli-2.0.0/docs/gguf_dl.md +190 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/llamacpp-direct.md +8 -4
- coderouter_cli-2.0.0/docs/openrouter-roster/CHANGES.md +24 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/openrouter-roster/latest.json +37 -7
- coderouter_cli-2.0.0/examples/providers.raspberrypi.yaml +298 -0
- coderouter_cli-2.0.0/examples/providers.v2-context-budget.yaml +111 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/examples/providers.yaml +9 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/pyproject.toml +1 -1
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_auto_router.py +216 -0
- coderouter_cli-2.0.0/tests/test_context_budget.py +404 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_ingress_anthropic.py +133 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_metrics_collector.py +66 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_metrics_prometheus.py +30 -0
- coderouter_cli-2.0.0/tests/test_token_estimation.py +135 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/.gitignore +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/LICENSE +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/__init__.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/__main__.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/adapters/__init__.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/adapters/anthropic_native.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/adapters/base.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/adapters/openai_compat.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/adapters/registry.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/cli.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/config/__init__.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/config/capability_registry.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/config/env_file.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/config/loader.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/cost.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/data/__init__.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/doctor.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/doctor_apply.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/env_security.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/errors.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/guards/__init__.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/guards/backend_health.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/guards/memory_pressure.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/guards/tool_loop.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/ingress/__init__.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/ingress/app.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/ingress/dashboard_routes.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/ingress/metrics_routes.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/ingress/openai_routes.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/metrics/__init__.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/output_filters.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/routing/__init__.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/routing/adaptive.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/routing/budget.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/routing/capability.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/translation/__init__.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/translation/anthropic.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/translation/convert.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/coderouter/translation/tool_repair.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/assets/dashboard-demo.png +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/designs/v1.5-dashboard-mockup.html +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/designs/v1.6-auto-router-verification.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/designs/v1.6-auto-router.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/free-tier-guide.en.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/free-tier-guide.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/hf-ollama-models.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/llamacpp-direct.en.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/lmstudio-direct.en.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/lmstudio-direct.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/openrouter-roster/README.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/quickstart.en.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/quickstart.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/retrospectives/v0.4.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/retrospectives/v0.5-verify.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/retrospectives/v0.5.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/retrospectives/v0.6.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/retrospectives/v0.7.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/retrospectives/v1.0-verify.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/retrospectives/v1.0.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/security.en.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/security.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/troubleshooting.en.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/troubleshooting.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/usage-guide.en.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/usage-guide.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/when-do-i-need-coderouter.en.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/docs/when-do-i-need-coderouter.md +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/examples/.env.example +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/examples/providers.auto-custom.yaml +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/examples/providers.auto.yaml +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/examples/providers.note-2026.yaml +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/examples/providers.nvidia-nim.yaml +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/scripts/demo_traffic.sh +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/scripts/openrouter_roster_diff.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/scripts/verify_v0_5.sh +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/scripts/verify_v1_0.sh +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/__init__.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/conftest.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_adapter_anthropic.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_backend_health.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_budget.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_capability.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_capability_degraded_payload.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_capability_registry.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_capability_registry_cache_control.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_claude_code_suitability.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_cli.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_cli_stats.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_config.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_dashboard_endpoint.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_doctor.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_doctor_apply.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_doctor_cache_probe.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_env_file.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_env_security.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_errors.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_examples_yaml.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_fallback.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_fallback_anthropic.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_fallback_cache_control.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_fallback_cache_observed.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_fallback_misconfig_warn.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_fallback_paid_gate.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_fallback_thinking.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_guards_tool_loop.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_ingress_profile.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_memory_pressure.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_metrics_cache.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_metrics_cost.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_metrics_endpoint.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_metrics_jsonl.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_metrics_prometheus_cache.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_openai_compat.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_openrouter_roster_diff.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_output_filters.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_output_filters_adapters.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_reasoning_strip.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_routing_adaptive.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_setup_sh.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_tool_repair.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_translation_anthropic.py +0 -0
- {coderouter_cli-1.10.0 → coderouter_cli-2.0.0}/tests/test_translation_reverse.py +0 -0
|
@@ -6,6 +6,202 @@ versioning follows [SemVer](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [v2.0.0] — 2026-05-05 (Context Budget Management — L1 overflow 防止)
|
|
10
|
+
|
|
11
|
+
**Theme: 長時間 agent session の context overflow を未然に防止する guard を実装。** Claude Code / Cline / OpenClaw 等の agentic session が 8 時間超え loop で動くと messages が context window に漸近し、backend が 400 / truncation を返して session 死亡する問題 (L1) を根本解決。warn (80%) → auto trim (90%) の 2 段階 guard で overflow をゼロに。
|
|
12
|
+
|
|
13
|
+
| 機能 | 説明 |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `estimate_context_usage()` | char/4 heuristic で request の context 充填率を推定 (5-deps 不変) |
|
|
16
|
+
| `trim_to_budget()` | 古い messages を先頭から削除、tool_use/tool_result ペアを tool_use_id ベースで atomic 保全 |
|
|
17
|
+
| `context_budget_action: off/warn/trim` | profile 単位で guard 有効化 (default: off) |
|
|
18
|
+
| `X-CodeRouter-Context-Budget` header | response header で warn/trimmed ステータスを通知 (streaming 対応) |
|
|
19
|
+
| Prometheus metrics | `coderouter_context_budget_warnings_total`, `coderouter_context_budget_trims_total`, `coderouter_context_budget_usage_ratio` |
|
|
20
|
+
| `coderouter stats` TUI | Fallback & Gates パネルに context budget warn/trim count + latest ratio 表示 |
|
|
21
|
+
| model-capabilities.yaml | 主要モデル (Claude 200K, Qwen3/3.5/3.6 32-131K, Gemma4 131K, DeepSeek 131K 等) の max_context_tokens bundled |
|
|
22
|
+
|
|
23
|
+
- Tests: 878 → **~930** (+50, token_estimation 13 + context_budget 22 + ingress header 5 + metrics 6 + prometheus 3)
|
|
24
|
+
- Runtime deps: 5 → 5 (**35 sub-release 連続据え置き**)
|
|
25
|
+
- Backward compat: 完全互換、`context_budget_action` default は `"off"` — opt-in するまで既存挙動完全一致
|
|
26
|
+
|
|
27
|
+
### 設定例
|
|
28
|
+
|
|
29
|
+
```yaml
|
|
30
|
+
profiles:
|
|
31
|
+
- name: long-session
|
|
32
|
+
providers: [ollama-qwen3]
|
|
33
|
+
context_budget_action: trim # off | warn | trim
|
|
34
|
+
context_budget_warn_threshold: 0.80 # ratio で警告
|
|
35
|
+
context_budget_trim_threshold: 0.90 # ratio で自動 trim
|
|
36
|
+
context_budget_trim_target: 0.75 # trim 後の目標充填率
|
|
37
|
+
context_budget_preserve_last_n: 4 # 直近 N messages は必ず保持
|
|
38
|
+
|
|
39
|
+
providers:
|
|
40
|
+
- name: ollama-qwen3
|
|
41
|
+
base_url: http://localhost:11434/v1
|
|
42
|
+
model: qwen3:30b-a3b
|
|
43
|
+
max_context_tokens: 32768 # 明示 (registry に乗ってない場合)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Files touched (主要)
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
A coderouter/token_estimation.py
|
|
50
|
+
A coderouter/guards/context_budget.py
|
|
51
|
+
M coderouter/config/schemas.py
|
|
52
|
+
M coderouter/routing/fallback.py
|
|
53
|
+
M coderouter/routing/auto_router.py
|
|
54
|
+
M coderouter/ingress/anthropic_routes.py
|
|
55
|
+
M coderouter/logging.py
|
|
56
|
+
M coderouter/metrics/collector.py
|
|
57
|
+
M coderouter/metrics/prometheus.py
|
|
58
|
+
M coderouter/cli_stats.py
|
|
59
|
+
M coderouter/data/model-capabilities.yaml
|
|
60
|
+
A tests/test_token_estimation.py
|
|
61
|
+
A tests/test_context_budget.py
|
|
62
|
+
M tests/test_ingress_anthropic.py
|
|
63
|
+
M tests/test_metrics_collector.py
|
|
64
|
+
M tests/test_metrics_prometheus.py
|
|
65
|
+
A docs/inside/v2.0-F-context-budget-plan.md
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## [v1.10.1] — 2026-05-04 (Patch — tool-aware auto routing + Raspberry Pi starter)
|
|
71
|
+
|
|
72
|
+
**Theme: 「ローカル小型モデルでは tool calling できないので tool-laden な request だけクラウドに逃がしたい」というユースケース (OpenClaw + Pi 8GB シナリオ) を declarative に解決。** v1.10.0 で feature complete を宣言した auto_router の 6 matcher を 7 matcher に拡張、`has_tools` を追加して「tools[] を宣言したリクエストか否か」で profile を分岐できるように。併せて Raspberry Pi 8GB 向けの starter YAML (`examples/providers.raspberrypi.yaml`) を同梱、SBC 上で OpenClaw / Claude Code 互換 agent を回すユーザーが yaml 1 個 copy するだけで動く状態にした。
|
|
73
|
+
|
|
74
|
+
含まれる出荷 2 件:
|
|
75
|
+
|
|
76
|
+
| # | sub-release | テーマ | LOC | tests |
|
|
77
|
+
|---|---|---|---|---|
|
|
78
|
+
| 1 | **has_tools matcher** | `RuleMatcher.has_tools` 7 番目 matcher 追加、OpenAI/Anthropic `tools[]` + OpenAI legacy `functions[]` を一括認識 (OpenClaw + Pi 由来) | ~80 | +7 |
|
|
79
|
+
| 2 | **Raspberry Pi starter** | `examples/providers.raspberrypi.yaml` 新規、Ollama 小型モデル (≤4B) + OpenRouter free 系 + `has_tools` ベースの tool-aware profile 振り分け | YAML のみ | (loader 検証で +0 直接、既存 parametric test に乗る) |
|
|
80
|
+
|
|
81
|
+
- Tests: 871 → **878** (+7、has_tools matcher の 6 シナリオ + `has_tools: false` の "set 扱いだがマッチしない" 安全網テスト)
|
|
82
|
+
- Runtime deps: 5 → 5 (**34 sub-release 連続据え置き**)
|
|
83
|
+
- Backward compat: 完全互換、既存 yaml / API / log payload schema 完全に同じ、新フィールド (`has_tools`) を使わない deployment は挙動完全一致
|
|
84
|
+
- pyproject version: 1.10.0 → 1.10.1
|
|
85
|
+
|
|
86
|
+
### Migration
|
|
87
|
+
|
|
88
|
+
不要。**v1.10.0 からの自然なアップグレード**:
|
|
89
|
+
|
|
90
|
+
- `coderouter` コマンド名 / Python import 名 / providers.yaml の format / env 変数 / ingress URL すべて完全に同じ
|
|
91
|
+
- 既存 `auto_router.rules[]` は何も変わらない、`has_tools` matcher を使い始めるには yaml に 1 行足すだけ
|
|
92
|
+
- v1.10.0 で v1.6 系 auto_router を「6 matcher で feature complete」と宣言した直後の追加だが、同じ宣言型 framework の延長線で構造変更なし — 「7 matcher で改めて feature complete」と読み替えて差し支えない
|
|
93
|
+
|
|
94
|
+
### Out of scope (v1.11 以降)
|
|
95
|
+
|
|
96
|
+
- **Provider capability gate for tools** — `capabilities.tools=false` を fallback chain の skip ゲートとして機能させる案。本 patch は profile レベルで振り分ける方針 (router で chain を切り替える) で `has_tools` matcher を採用、provider レベルの skip ゲートは別 issue。CodeRouter の chain semantics (順次フォールバック + downgrade) の互換性検討が必要なため、必要性が確認できてから着手。
|
|
97
|
+
- **小型ローカルモデルの tool-call repair 強化** — 現状 `tool_repair.py` は `<tool_call>{...}</tool_call>` ラッパ形式の救済を行うが、1-4B モデルが返す自由形式の text からの推測救済は別領域 (`tool_emulation`)。プロンプトテンプレ書き換えで誘導する手段もあり、設計検討は v2.0 後送り。
|
|
98
|
+
|
|
99
|
+
### Files touched
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
A examples/providers.raspberrypi.yaml
|
|
103
|
+
M CHANGELOG.md
|
|
104
|
+
M coderouter/config/schemas.py
|
|
105
|
+
M coderouter/routing/auto_router.py
|
|
106
|
+
M pyproject.toml
|
|
107
|
+
M tests/test_auto_router.py
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
### has_tools matcher (OpenClaw + Raspberry Pi 由来)
|
|
113
|
+
|
|
114
|
+
**Theme: tools[] を宣言したリクエストだけクラウドに振り分け、ローカル小型モデルは tool 不要の素朴な chat に専念させる。** Raspberry Pi 8GB / Jetson Nano クラスの SBC で OpenClaw 等の tool-aware agent を動かしたい時、CPU 推論で実用域に入る Ollama モデル (≤4B) は tool calling が苦手 (`finish_reason: tool_calls` を返さない / 引数 JSON が壊れる / 自由形式 text に bury される) で、結果として agent 側からは「tool 呼び出しが起きてない」状態になる。`auto_router.rules[].if.has_tools` を 7 番目の matcher として追加することで、profile レベルで「tools あり → クラウド (Qwen3-Coder/gpt-oss/Gemini-Flash の OpenRouter free)」「tools 無し → ローカル小型」を declarative に切り替えられる。
|
|
115
|
+
|
|
116
|
+
ユースケース例 (Raspberry Pi 8GB starter `examples/providers.raspberrypi.yaml` から抜粋):
|
|
117
|
+
|
|
118
|
+
```yaml
|
|
119
|
+
auto_router:
|
|
120
|
+
rules:
|
|
121
|
+
- id: user:has-tools-go-cloud
|
|
122
|
+
profile: with-tools # OpenRouter free 系のみ
|
|
123
|
+
match:
|
|
124
|
+
has_tools: true
|
|
125
|
+
- id: user:image-go-cloud
|
|
126
|
+
profile: vision # Gemini Flash 1M ctx
|
|
127
|
+
match:
|
|
128
|
+
has_image: true
|
|
129
|
+
- id: user:longcontext-go-cloud
|
|
130
|
+
profile: longcontext
|
|
131
|
+
match:
|
|
132
|
+
content_token_count_min: 32000
|
|
133
|
+
default_rule_profile: local-chat # qwen3.5:2b/4b / gemma3:1b ローカル
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
OpenClaw (毎ターン Bash/Read/Write 等の tool を declare する agent) を `OPENAI_BASE_URL=http://<pi-ip>:8088/v1` で繋ぐと、tool-laden traffic は自動でクラウドに飛び、軽い chat だけが Pi 上のローカルで処理される。OPENROUTER_API_KEY のみ設定すればよく、有料 API は不要 (`ALLOW_PAID=false` がデフォルト)。
|
|
137
|
+
|
|
138
|
+
#### なぜ provider レベルの capability gate ではなく profile レベルなのか
|
|
139
|
+
|
|
140
|
+
`ProviderConfig.capabilities.tools=false` フラグは既存 (v0.x からある) だが、現状は `coderouter doctor` の診断表示と `model-capabilities.yaml` registry の解決に使うだけで、fallback chain における skip ゲートには接続されていない。`thinking` / `cache_control` には `will_degrade` ゲート (capability.py の `provider_supports_*`) があるが、tools には同等の skip 機構がない。これは既存の v0.3-D 「downgrade path」(non-native + tools[] あり → 非ストリーミング + tool_repair) に依存していて、provider が tools を返せなくても adapter エラーは出ず、上流から見ると success (空 tool_calls) で chain が fallthrough せず止まってしまう (= 観測症状: 「tool call されてない」)。
|
|
141
|
+
|
|
142
|
+
provider レベルの skip ゲートを後付けするのは chain semantics に踏み込む変更で互換性検討が要るため、本 patch では **profile レベルの宣言型 lever** に留める方針を採用。chain semantics を変えず、auto_router rule の追加で同じ効果を得られ、かつ既存の 6 matcher と完全に同じ規約 (exactly one + first match wins + fast-fail at load) で導入できる。
|
|
143
|
+
|
|
144
|
+
- Tests: 871 → **878** (+7: OpenAI tools[] / Anthropic tools[] / OpenAI legacy functions[] / no-tools fallthrough / 空リスト fallthrough / has_tools rule が code-fence rule より優先 / `has_tools: false` の "set 扱いだがマッチしない" 安全網)
|
|
145
|
+
- Runtime deps: 5 → 5 (34 sub-release 連続据え置き)
|
|
146
|
+
- Backward compat: 完全互換、既存 `auto_router` rule は何も変わらない、`has_tools` を使わない deployment は挙動完全一致
|
|
147
|
+
|
|
148
|
+
#### Changes
|
|
149
|
+
|
|
150
|
+
- `coderouter/config/schemas.py`:
|
|
151
|
+
- `RuleMatcher` に `has_tools: bool | None = None` を追加、`_MATCHER_FIELDS` tuple に追加 (zero/multiple-fields の "exactly one" バリデータが自動適用)。
|
|
152
|
+
- docstring の Variants セクションに 7 番目として `has_tools` を追記、boolean 形状が `has_image` と同じである理由 (`True` のみ意味を持ち、`False` は "set" 扱いだが `_match_rule` の `is True` チェックでマッチしない安全網) と、provider レベルの `capabilities.tools` flag との違い (前者は profile-level routing、後者は doctor の診断補助で chain skip ゲートではない) を明示。
|
|
153
|
+
|
|
154
|
+
- `coderouter/routing/auto_router.py`:
|
|
155
|
+
- `_has_tools_in_body(body)` ヘルパを新設 — body の top-level `tools[]` (OpenAI Chat Completions / Anthropic Messages API 共通) と `functions[]` (OpenAI legacy、deprecated だが pinned SDK で残存) を一括認識、空リスト / None は False (lazy init 対応)。
|
|
156
|
+
- `_match_rule(rule, message, text, model, estimated_tokens, has_tools)` シグネチャに `has_tools: bool` を追加、`has_tools is True` 分岐を 7 番目として実装。
|
|
157
|
+
- `classify(...)` 内で `_has_tools_in_body(body)` を一度だけ呼んで rule iteration に渡す。`user_msg is None` でも `has_tools` rule は評価する (system-only prompt + tools[] declaration の構成にも対応)。
|
|
158
|
+
- `_emit_resolved` / `_emit_fallthrough` の `signals` payload に `has_tools` を追記、`auto-router-resolved` log で「tools あり判定で routing したか」が dashboard / Prometheus exporter から見える。
|
|
159
|
+
|
|
160
|
+
- `tests/test_auto_router.py` Group 8 (tool-aware routing) を新設、7 ケース:
|
|
161
|
+
- `test_classify_request_with_openai_tools_routes_to_with_tools` — 基本ケース、OpenAI 形式 `tools[].function` → `with-tools` profile。
|
|
162
|
+
- `test_classify_request_with_anthropic_tools_routes_to_with_tools` — Anthropic 形式 `tools[].input_schema` も同じ top-level `tools` キーなので、単一 matcher で両 ingress カバー。
|
|
163
|
+
- `test_classify_request_with_legacy_functions_routes_to_with_tools` — OpenAI legacy `functions[]` (deprecated だが pinned SDK で残存) も tool-laden 扱い。
|
|
164
|
+
- `test_classify_request_without_tools_falls_through` — 逆ケース、tools 宣言なしの plain chat は `default_rule_profile` (Pi の場合は `local-chat`) に落ちる。
|
|
165
|
+
- `test_classify_empty_tools_list_treated_as_no_tools` — `tools: []` / `functions: []` (lazy init shape) は False 扱い、no-spurious-match property を pin。
|
|
166
|
+
- `test_classify_has_tools_first_match_wins_over_later_content_rule` — has_tools rule が code_fence rule より前に置かれた時、両方マッチする body でも先勝、global "first match wins" を新 matcher にも適用。
|
|
167
|
+
- `test_has_tools_false_rejected_at_load` — `has_tools: False` が `_exactly_one` を通過するが `_match_rule` の `is True` チェックでマッチしない安全網を文書化、誤設定時もデフォルト経路に落ちることを保証。
|
|
168
|
+
|
|
169
|
+
#### Files touched
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
M CHANGELOG.md
|
|
173
|
+
M coderouter/config/schemas.py
|
|
174
|
+
M coderouter/routing/auto_router.py
|
|
175
|
+
M pyproject.toml
|
|
176
|
+
M tests/test_auto_router.py
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
### Raspberry Pi 8GB starter (`examples/providers.raspberrypi.yaml`)
|
|
182
|
+
|
|
183
|
+
**Theme: SBC で OpenClaw を動かす最小構成を yaml 1 個に集約。** v1.10.1 で追加した `has_tools` matcher を主役にした starter で、`coderouter serve` 1 発で Pi 上のローカル ollama (qwen3.5:2b/4b、qwen2.5:1.5b、gemma3:1b) と OpenRouter free 系 (qwen3-coder:free / gpt-oss-120b:free / gemini-2.5-flash:free) が tool-aware に振り分けられる。OPENROUTER_API_KEY のみ要設定、有料 API キー不要 (`ALLOW_PAID=false` がデフォルト)。
|
|
184
|
+
|
|
185
|
+
#### 設計の要点
|
|
186
|
+
|
|
187
|
+
- **ローカル全部 `tools: false`** — Pi 8GB に乗る ≤4B モデルは tool_calls を安定して返せないため capability で明示的に `false`。これは doctor 診断用の宣言で、実 routing は `has_tools` matcher が profile レベルで振り分けるので二重防御になる。
|
|
188
|
+
- **`num_ctx: 8192` + `num_predict: 1024` 制限** — Pi の CPU 推論は context 縮めた方が prefill が現実的、デフォルト ollama の 2048 だと OpenClaw の system prompt で詰む & 2048 から 32K に上げると prefill が分単位になるので 8K が現実的中間点。
|
|
189
|
+
- **画像 / 長尺 (32K+) もクラウドへ** — Pi では Gemma 4 E4B (vision capable だが 9.6GB で 8GB Pi に乗らない) の代わりに、`has_image` rule で OpenRouter Gemini Flash (1M ctx + vision native) に逃がす。
|
|
190
|
+
- **OpenRouter free 3 モデルで vendor diversity** — qwen-coder / gpt-oss / gemini-flash の 3 ベンダーを並べて、daily cap (~200 req/day per model per account) 当たり時の rate-limit 逃げ場を確保。
|
|
191
|
+
- **`output_filters: [strip_thinking, strip_stop_markers]` を Qwen 系で常時適用** — Pi で動かす Qwen 3.5 系は `<think>...</think>` リーク + `<|im_end|>` 漏れの両方を観測、両方 strip。
|
|
192
|
+
|
|
193
|
+
#### Tests
|
|
194
|
+
|
|
195
|
+
`tests/test_examples_yaml.py::test_example_yaml_loads` が `examples/providers*.yaml` を parametric にカバーしているため、`providers.raspberrypi.yaml` も自動でこの test に乗る。新たに pin したい invariant (例: ローカル全部 `tools: false`、`has_tools` rule の存在、auto_router default が `local-chat` 等) があれば後続 patch で個別 test 追加可能だが、本 patch では parametric の loader-clean property のみ確保。
|
|
196
|
+
|
|
197
|
+
#### Files touched
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
A examples/providers.raspberrypi.yaml
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
9
205
|
## [v1.10.0] — 2026-05-01 (Umbrella tag — Cost enforcement + Long-run reliability completion + Auto-router feature complete)
|
|
10
206
|
|
|
11
207
|
**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) で経路が閉じた。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: coderouter-cli
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
4
4
|
Summary: Local-first, free-first, fallback-built-in LLM router. Claude Code / OpenAI compatible.
|
|
5
5
|
Project-URL: Homepage, https://github.com/zephel01/CodeRouter
|
|
6
6
|
Project-URL: Repository, https://github.com/zephel01/CodeRouter
|
|
@@ -60,7 +60,7 @@ Description-Content-Type: text/markdown
|
|
|
60
60
|
<p align="center">
|
|
61
61
|
<a href="https://github.com/zephel01/CodeRouter/actions/workflows/ci.yml"><img src="https://github.com/zephel01/CodeRouter/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI"></a>
|
|
62
62
|
<a href=""><img src="https://img.shields.io/badge/status-stable-brightgreen" alt="status"></a>
|
|
63
|
-
<a href=""><img src="https://img.shields.io/badge/version-
|
|
63
|
+
<a href=""><img src="https://img.shields.io/badge/version-2.0.0-blue" alt="version"></a>
|
|
64
64
|
<a href=""><img src="https://img.shields.io/badge/python-3.12%2B-blue" alt="python"></a>
|
|
65
65
|
<a href=""><img src="https://img.shields.io/badge/runtime%20deps-5-brightgreen" alt="deps"></a>
|
|
66
66
|
<a href=""><img src="https://img.shields.io/badge/license-MIT-yellow" alt="license"></a>
|
|
@@ -90,7 +90,8 @@ Description-Content-Type: text/markdown
|
|
|
90
90
|
- v1.9.0 から **adaptive routing** で「いま遅い provider」を自動降格(profile に `adaptive: true` を付けるだけ)、**tool-loop guard** で stuck loop を検出(`warn` / `inject` / `break` の 3 段階 policy)
|
|
91
91
|
- **v1.10.0 で Long-run reliability pillar が完成**: `cost.monthly_budget_usd` で provider 月次 USD 予算を強制、**L2 memory pressure detector**(Ollama / LM Studio が VRAM 切れで OOM になった時に自動クールダウン)、**L5 backend health 状態機械**(連続失敗で UNHEALTHY → chain 末尾に降格、1 回成功で即復帰)
|
|
92
92
|
- **v1.10.0 で auto-router が 6 matcher に揃う**: `has_image` / `code_fence_ratio_min` / `content_contains` / `content_regex` / `model_pattern`(Opus/Sonnet/Haiku 分岐)/ `content_token_count_min`(長文 → 1M ctx Gemini Flash 等へ自動切替)
|
|
93
|
-
-
|
|
93
|
+
- **v2.0.0 で Context Budget Management (L1 overflow 防止) を搭載**: 長時間 agent session で messages が context window に漸近 → backend 400 エラーで session 死亡する問題を根本解決。warn (80%) → auto trim (90%) の 2 段階 guard で **context overflow ゼロ**を実現。tool_use / tool_result ペアは atomic 保全、`X-CodeRouter-Context-Budget` ヘッダで状態通知、Prometheus メトリクス完備
|
|
94
|
+
- ランタイム依存 5 個(`fastapi` / `uvicorn` / `httpx` / `pydantic` / `pyyaml`)— 純 Python、MIT、テスト 930 本緑
|
|
94
95
|
|
|
95
96
|
→ **Claude Code / gemini-cli / codex + Ollama / llama.cpp / NVIDIA NIM で、破綻しない local-first agent が組める**
|
|
96
97
|
|
|
@@ -102,11 +103,12 @@ Description-Content-Type: text/markdown
|
|
|
102
103
|
| **使いこなす** | [利用ガイド](./docs/usage-guide.md) | HW 別モデル選定・チューニング既定値・OS ごとの起動フロー・`doctor` / `verify` の読み方 |
|
|
103
104
|
| **無料で回す** | [無料枠ガイド](./docs/free-tier-guide.md) | NVIDIA NIM 40 req/min × OpenRouter 無料枠の使い分け・live 検証済みモデル表・地雷 5 点 |
|
|
104
105
|
| **要るか判断する** | [要否判定ガイド](./docs/when-do-i-need-coderouter.md) | エージェント × モデルの詳細マトリクスで「そもそも自分に必要か」を決める |
|
|
106
|
+
| **長時間 session** | [Context Budget](./docs/context-budget.md) | v2.0.0 新機能。長時間 agent session の context overflow を自動防止する guard の設定・仕組み・可観測性 |
|
|
105
107
|
| **詰まったとき** | [トラブルシューティング](./docs/troubleshooting.md) | `doctor` の使い方、`.env` の export 必須、Ollama サイレント失敗 5 症状、Claude Code 連携の罠 |
|
|
106
108
|
| **llama.cpp 直叩き** | [llama.cpp 直叩きガイド](./docs/llamacpp-direct.md) | Qwen3.6 を Ollama 詰みから救出する経路。`llama.cpp` build → Unsloth GGUF → `llama-server` → CodeRouter 接続を 7 step で(v1.8.3 実機検証済)|
|
|
107
109
|
| **LM Studio 直接** | [LM Studio 直接ガイド](./docs/lmstudio-direct.md) | `qwen35` / `qwen35moe` を救う第 2 経路。LM Studio 0.4.12+ Local Server 経由で OpenAI 互換 + Anthropic 互換 (`/v1/messages`) 両対応、prompt caching 透過(v1.8.4 実機検証済)|
|
|
108
110
|
| **安全に使う** | [セキュリティ方針](./docs/security.md) | 脅威モデル・秘密情報の扱い・脆弱性報告経路 |
|
|
109
|
-
| **履歴** | [CHANGELOG](./CHANGELOG.md) | 全リリース履歴(最新:
|
|
111
|
+
| **履歴** | [CHANGELOG](./CHANGELOG.md) | 全リリース履歴(最新: v2.0.0 — Context Budget Management (L1 overflow 防止) で長時間 agent session の安定性を根本改善) |
|
|
110
112
|
| **設計を追う** | [plan.md](./plan.md) | 設計不変項・マイルストーン・今後のロードマップ |
|
|
111
113
|
|
|
112
114
|
English versions: [Quickstart](./docs/quickstart.en.md) · [Usage guide](./docs/usage-guide.en.md) · [Free-tier guide](./docs/free-tier-guide.en.md) · [When you need it](./docs/when-do-i-need-coderouter.en.md) · [Troubleshooting](./docs/troubleshooting.en.md) · [llama.cpp direct](./docs/llamacpp-direct.en.md) · [LM Studio direct](./docs/lmstudio-direct.en.md) · [Security](./docs/security.en.md)
|
|
@@ -122,7 +124,7 @@ CodeRouter は、コーディングエージェント(Claude Code / gemini-cli
|
|
|
122
124
|
- **うっかり課金しない。** `ALLOW_PAID=false` が既定。有料プロバイダをチェーンから外したときは理由を 1 行ログに出すので、なぜ使われなかったかが後で grep できます。
|
|
123
125
|
- **ローカル Ollama の上で Claude Code / gemini-cli / codex が動く。** Claude Code は Anthropic のワイアフォーマット、Ollama / llama.cpp / LM Studio は OpenAI。CodeRouter が双方向に変換し、小さいローカルモデルがテキストで吐いてしまう `{"name":..., "arguments":...}` を tool_use ブロックへ復元してからエージェントに渡します。
|
|
124
126
|
- **「なぜか動かない」の原因を教えてくれる。** `coderouter doctor --check-model <provider>` が 6 種類の典型的な失敗モード(コンテキスト切り詰め / ストリーム早期終了 / ツール呼び出し欠落 / reasoning フィールド漏れ / 認証 / Anthropic `thinking`)を実地プローブし、コピペ可能な YAML パッチを出します。
|
|
125
|
-
- **監査しやすい。** ランタイム依存 5 個(LiteLLM は 100+)。Pure Python、MIT、テスト
|
|
127
|
+
- **監査しやすい。** ランタイム依存 5 個(LiteLLM は 100+)。Pure Python、MIT、テスト 930 本緑。
|
|
126
128
|
|
|
127
129
|
```
|
|
128
130
|
クライアント (Claude Code / OpenAI SDK / gemini-cli / codex / curl)
|
|
@@ -196,7 +198,7 @@ CodeRouter / Voice Bridge ともに独立した repo で進化していて、HTT
|
|
|
196
198
|
|
|
197
199
|
## クイックスタート(3 コマンド)
|
|
198
200
|
|
|
199
|
-
**
|
|
201
|
+
**v2.0.0 で Context Budget Management (L1 overflow 防止) を搭載** — 長時間 agent session が context window を使い切って死ぬ問題を根本解決。`uvx` 一発で動きます (Python 3.12 以上必須):
|
|
200
202
|
|
|
201
203
|
```bash
|
|
202
204
|
# 1. サンプル設定を置く
|
|
@@ -264,9 +266,9 @@ CodeRouter 自体は純 Python 3.12+ で、実質的な OS 対応範囲は `min(
|
|
|
264
266
|
|
|
265
267
|
注意点や「ローカル GPU なし」向けレシピを含むフル版マトリクス: [利用ガイド §1](./docs/usage-guide.md#1-os-互換性)
|
|
266
268
|
|
|
267
|
-
## ステータス —
|
|
269
|
+
## ステータス — v2.0.0 (2026-05)
|
|
268
270
|
|
|
269
|
-
**テスト
|
|
271
|
+
**テスト 930 本通過。ランタイム依存 5 個 (36 sub-release 連続据え置き)。macOS / Linux / Windows WSL2 で動作。** v2.0.0 で **Context Budget Management (L1 overflow 防止)** を搭載 — 長時間 agent session が context window を使い切って死ぬ問題を根本解決。v1.10.0 までに **Long-run Reliability** (L2/L3/L5)、**Cost pillar**、**Auto-router 6 matcher** が完成済み。v1.0 の総まとめは [`docs/retrospectives/v1.0.md`](./docs/retrospectives/v1.0.md)。
|
|
270
272
|
|
|
271
273
|
今日の CodeRouter が届ける価値:
|
|
272
274
|
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
<p align="center">
|
|
21
21
|
<a href="https://github.com/zephel01/CodeRouter/actions/workflows/ci.yml"><img src="https://github.com/zephel01/CodeRouter/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI"></a>
|
|
22
22
|
<a href=""><img src="https://img.shields.io/badge/status-stable-brightgreen" alt="status"></a>
|
|
23
|
-
<a href=""><img src="https://img.shields.io/badge/version-
|
|
23
|
+
<a href=""><img src="https://img.shields.io/badge/version-2.0.0-blue" alt="version"></a>
|
|
24
24
|
<a href=""><img src="https://img.shields.io/badge/python-3.12%2B-blue" alt="python"></a>
|
|
25
25
|
<a href=""><img src="https://img.shields.io/badge/runtime%20deps-5-brightgreen" alt="deps"></a>
|
|
26
26
|
<a href=""><img src="https://img.shields.io/badge/license-MIT-yellow" alt="license"></a>
|
|
@@ -49,7 +49,8 @@
|
|
|
49
49
|
- v1.9.0 ships **adaptive routing** that auto-demotes a temporarily-slow provider (set `adaptive: true` on a profile) and a **tool-loop guard** that catches stuck-loop patterns with a 3-tier policy (`warn` / `inject` / `break`)
|
|
50
50
|
- **v1.10.0 completes the long-run reliability pillar**: `cost.monthly_budget_usd` enforces a per-provider monthly USD cap, the **L2 memory-pressure detector** automatically cools down a backend when Ollama / LM Studio reports VRAM exhaustion, and the **L5 backend-health state machine** demotes UNHEALTHY providers to the back of the chain (consecutive-failure threshold, single-success recovery)
|
|
51
51
|
- **v1.10.0 brings the auto-router to 6 matchers**: `has_image` / `code_fence_ratio_min` / `content_contains` / `content_regex` / `model_pattern` (Opus/Sonnet/Haiku branching) / `content_token_count_min` (long prompts → 1M-ctx Gemini Flash, etc.)
|
|
52
|
-
-
|
|
52
|
+
- **v2.0.0 ships Context Budget Management (L1 overflow prevention)**: long-running agent sessions approaching the context window are automatically trimmed — warn at 80%, auto-trim at 90%, tool_use/tool_result pairs preserved atomically, `X-CodeRouter-Context-Budget` response header, Prometheus metrics included
|
|
53
|
+
- Five runtime dependencies (`fastapi` / `uvicorn` / `httpx` / `pydantic` / `pyyaml`) — pure Python, MIT, 930 tests green
|
|
53
54
|
|
|
54
55
|
→ **Claude Code / gemini-cli / codex on top of Ollama / llama.cpp / NVIDIA NIM, without the agent falling apart.**
|
|
55
56
|
|
|
@@ -65,7 +66,7 @@
|
|
|
65
66
|
| **llama.cpp direct** | [llama.cpp direct guide](./docs/llamacpp-direct.en.md) | Rescue path for Qwen3.6 (Ollama is brittle). 7-step recipe: `llama.cpp` build → Unsloth GGUF → `llama-server` → CodeRouter wiring. Real-machine verified in v1.8.3. |
|
|
66
67
|
| **LM Studio direct** | [LM Studio direct guide](./docs/lmstudio-direct.en.md) | Second rescue path for `qwen35` / `qwen35moe`. LM Studio 0.4.12+ Local Server with both OpenAI-compatible and Anthropic-compatible (`/v1/messages`) routes — prompt caching survives end-to-end. Real-machine verified in v1.8.4. |
|
|
67
68
|
| **Operate safely** | [Security](./docs/security.en.md) | Threat model, secret handling, vulnerability reporting |
|
|
68
|
-
| **History** | [CHANGELOG](./CHANGELOG.md) | All releases (latest:
|
|
69
|
+
| **History** | [CHANGELOG](./CHANGELOG.md) | All releases (latest: v2.0.0 — Context Budget Management (L1 overflow prevention) for rock-solid long-running agent sessions) |
|
|
69
70
|
| **Track the design** | [plan.md](./plan.md) | Design invariants, milestones, roadmap |
|
|
70
71
|
|
|
71
72
|
日本語版: [Quickstart](./docs/quickstart.md) · [利用ガイド](./docs/usage-guide.md) · [無料枠ガイド](./docs/free-tier-guide.md) · [要否判定](./docs/when-do-i-need-coderouter.md) · [トラブルシューティング](./docs/troubleshooting.md) · [LM Studio 直接](./docs/lmstudio-direct.md) · [Security](./docs/security.md)
|
|
@@ -81,7 +82,7 @@ Concretely, it takes care of things most beginners hit the hard way:
|
|
|
81
82
|
- **No surprise bill.** `ALLOW_PAID=false` is the default; when CodeRouter drops a paid provider from the chain it logs one clear line so you can see why.
|
|
82
83
|
- **Use Claude Code / gemini-cli / codex on top of local Ollama.** Claude Code speaks Anthropic wire format, Ollama / llama.cpp / LM Studio speak OpenAI. CodeRouter translates both directions, and repairs the malformed `{"name":..., "arguments":...}` JSON that small local models emit as plain text.
|
|
83
84
|
- **Know *why* your local model is acting weird.** `coderouter doctor --check-model <provider>` probes six common failure modes (context truncation, streaming cutoff, missing tool-use, reasoning leaks, auth, Anthropic `thinking`) and prints a copy-paste YAML patch.
|
|
84
|
-
- **Auditable.** 5 runtime dependencies (vs. 100+ for LiteLLM). Pure Python, MIT,
|
|
85
|
+
- **Auditable.** 5 runtime dependencies (vs. 100+ for LiteLLM). Pure Python, MIT, 930 tests passing.
|
|
85
86
|
|
|
86
87
|
```
|
|
87
88
|
Client (Claude Code / OpenAI SDK / gemini-cli / codex / curl)
|
|
@@ -155,7 +156,7 @@ CodeRouter and Voice Bridge live in separate repos and evolve independently, con
|
|
|
155
156
|
|
|
156
157
|
## Quickstart (2 commands)
|
|
157
158
|
|
|
158
|
-
**
|
|
159
|
+
**v2.0.0 ships Context Budget Management (L1 overflow prevention)** — long-running agent sessions that approach the context window are automatically trimmed to prevent session death. `uvx` installs and runs in one shot (Python 3.12+ required):
|
|
159
160
|
|
|
160
161
|
```bash
|
|
161
162
|
# 1. Drop a sample config
|
|
@@ -224,9 +225,9 @@ CodeRouter is pure Python 3.12+; OS support is effectively `min(coderouter, olla
|
|
|
224
225
|
|
|
225
226
|
Full matrix with caveats and the "no local GPU" recipe: [usage guide §1](./docs/usage-guide.en.md#1-os-compatibility).
|
|
226
227
|
|
|
227
|
-
## Status —
|
|
228
|
+
## Status — v2.0.0 (2026-05)
|
|
228
229
|
|
|
229
|
-
**
|
|
230
|
+
**930 tests pass. 5 runtime dependencies (36 sub-releases held to the same 5).** Works on macOS / Linux / Windows WSL2. v2.0.0 ships **Context Budget Management (L1 overflow prevention)** — the final piece for rock-solid long-running agent sessions. Previous milestones: **Long-run Reliability** (L2/L3/L5), **Cost pillar**, **Auto-router 6 matchers**. The v1.0 wrap-up is in [`docs/retrospectives/v1.0.md`](./docs/retrospectives/v1.0.md).
|
|
230
231
|
|
|
231
232
|
What CodeRouter can do for you today:
|
|
232
233
|
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
<p align="center">
|
|
20
20
|
<a href="https://github.com/zephel01/CodeRouter/actions/workflows/ci.yml"><img src="https://github.com/zephel01/CodeRouter/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI"></a>
|
|
21
21
|
<a href=""><img src="https://img.shields.io/badge/status-stable-brightgreen" alt="status"></a>
|
|
22
|
-
<a href=""><img src="https://img.shields.io/badge/version-
|
|
22
|
+
<a href=""><img src="https://img.shields.io/badge/version-2.0.0-blue" alt="version"></a>
|
|
23
23
|
<a href=""><img src="https://img.shields.io/badge/python-3.12%2B-blue" alt="python"></a>
|
|
24
24
|
<a href=""><img src="https://img.shields.io/badge/runtime%20deps-5-brightgreen" alt="deps"></a>
|
|
25
25
|
<a href=""><img src="https://img.shields.io/badge/license-MIT-yellow" alt="license"></a>
|
|
@@ -49,7 +49,8 @@
|
|
|
49
49
|
- v1.9.0 から **adaptive routing** で「いま遅い provider」を自動降格(profile に `adaptive: true` を付けるだけ)、**tool-loop guard** で stuck loop を検出(`warn` / `inject` / `break` の 3 段階 policy)
|
|
50
50
|
- **v1.10.0 で Long-run reliability pillar が完成**: `cost.monthly_budget_usd` で provider 月次 USD 予算を強制、**L2 memory pressure detector**(Ollama / LM Studio が VRAM 切れで OOM になった時に自動クールダウン)、**L5 backend health 状態機械**(連続失敗で UNHEALTHY → chain 末尾に降格、1 回成功で即復帰)
|
|
51
51
|
- **v1.10.0 で auto-router が 6 matcher に揃う**: `has_image` / `code_fence_ratio_min` / `content_contains` / `content_regex` / `model_pattern`(Opus/Sonnet/Haiku 分岐)/ `content_token_count_min`(長文 → 1M ctx Gemini Flash 等へ自動切替)
|
|
52
|
-
-
|
|
52
|
+
- **v2.0.0 で Context Budget Management (L1 overflow 防止) を搭載**: 長時間 agent session で messages が context window に漸近 → backend 400 エラーで session 死亡する問題を根本解決。warn (80%) → auto trim (90%) の 2 段階 guard で **context overflow ゼロ**を実現。tool_use / tool_result ペアは atomic 保全、`X-CodeRouter-Context-Budget` ヘッダで状態通知、Prometheus メトリクス完備
|
|
53
|
+
- ランタイム依存 5 個(`fastapi` / `uvicorn` / `httpx` / `pydantic` / `pyyaml`)— 純 Python、MIT、テスト 930 本緑
|
|
53
54
|
|
|
54
55
|
→ **Claude Code / gemini-cli / codex + Ollama / llama.cpp / NVIDIA NIM で、破綻しない local-first agent が組める**
|
|
55
56
|
|
|
@@ -61,11 +62,12 @@
|
|
|
61
62
|
| **使いこなす** | [利用ガイド](./docs/usage-guide.md) | HW 別モデル選定・チューニング既定値・OS ごとの起動フロー・`doctor` / `verify` の読み方 |
|
|
62
63
|
| **無料で回す** | [無料枠ガイド](./docs/free-tier-guide.md) | NVIDIA NIM 40 req/min × OpenRouter 無料枠の使い分け・live 検証済みモデル表・地雷 5 点 |
|
|
63
64
|
| **要るか判断する** | [要否判定ガイド](./docs/when-do-i-need-coderouter.md) | エージェント × モデルの詳細マトリクスで「そもそも自分に必要か」を決める |
|
|
65
|
+
| **長時間 session** | [Context Budget](./docs/context-budget.md) | v2.0.0 新機能。長時間 agent session の context overflow を自動防止する guard の設定・仕組み・可観測性 |
|
|
64
66
|
| **詰まったとき** | [トラブルシューティング](./docs/troubleshooting.md) | `doctor` の使い方、`.env` の export 必須、Ollama サイレント失敗 5 症状、Claude Code 連携の罠 |
|
|
65
67
|
| **llama.cpp 直叩き** | [llama.cpp 直叩きガイド](./docs/llamacpp-direct.md) | Qwen3.6 を Ollama 詰みから救出する経路。`llama.cpp` build → Unsloth GGUF → `llama-server` → CodeRouter 接続を 7 step で(v1.8.3 実機検証済)|
|
|
66
68
|
| **LM Studio 直接** | [LM Studio 直接ガイド](./docs/lmstudio-direct.md) | `qwen35` / `qwen35moe` を救う第 2 経路。LM Studio 0.4.12+ Local Server 経由で OpenAI 互換 + Anthropic 互換 (`/v1/messages`) 両対応、prompt caching 透過(v1.8.4 実機検証済)|
|
|
67
69
|
| **安全に使う** | [セキュリティ方針](./docs/security.md) | 脅威モデル・秘密情報の扱い・脆弱性報告経路 |
|
|
68
|
-
| **履歴** | [CHANGELOG](./CHANGELOG.md) | 全リリース履歴(最新:
|
|
70
|
+
| **履歴** | [CHANGELOG](./CHANGELOG.md) | 全リリース履歴(最新: v2.0.0 — Context Budget Management (L1 overflow 防止) で長時間 agent session の安定性を根本改善) |
|
|
69
71
|
| **設計を追う** | [plan.md](./plan.md) | 設計不変項・マイルストーン・今後のロードマップ |
|
|
70
72
|
|
|
71
73
|
English versions: [Quickstart](./docs/quickstart.en.md) · [Usage guide](./docs/usage-guide.en.md) · [Free-tier guide](./docs/free-tier-guide.en.md) · [When you need it](./docs/when-do-i-need-coderouter.en.md) · [Troubleshooting](./docs/troubleshooting.en.md) · [llama.cpp direct](./docs/llamacpp-direct.en.md) · [LM Studio direct](./docs/lmstudio-direct.en.md) · [Security](./docs/security.en.md)
|
|
@@ -81,7 +83,7 @@ CodeRouter は、コーディングエージェント(Claude Code / gemini-cli
|
|
|
81
83
|
- **うっかり課金しない。** `ALLOW_PAID=false` が既定。有料プロバイダをチェーンから外したときは理由を 1 行ログに出すので、なぜ使われなかったかが後で grep できます。
|
|
82
84
|
- **ローカル Ollama の上で Claude Code / gemini-cli / codex が動く。** Claude Code は Anthropic のワイアフォーマット、Ollama / llama.cpp / LM Studio は OpenAI。CodeRouter が双方向に変換し、小さいローカルモデルがテキストで吐いてしまう `{"name":..., "arguments":...}` を tool_use ブロックへ復元してからエージェントに渡します。
|
|
83
85
|
- **「なぜか動かない」の原因を教えてくれる。** `coderouter doctor --check-model <provider>` が 6 種類の典型的な失敗モード(コンテキスト切り詰め / ストリーム早期終了 / ツール呼び出し欠落 / reasoning フィールド漏れ / 認証 / Anthropic `thinking`)を実地プローブし、コピペ可能な YAML パッチを出します。
|
|
84
|
-
- **監査しやすい。** ランタイム依存 5 個(LiteLLM は 100+)。Pure Python、MIT、テスト
|
|
86
|
+
- **監査しやすい。** ランタイム依存 5 個(LiteLLM は 100+)。Pure Python、MIT、テスト 930 本緑。
|
|
85
87
|
|
|
86
88
|
```
|
|
87
89
|
クライアント (Claude Code / OpenAI SDK / gemini-cli / codex / curl)
|
|
@@ -155,7 +157,7 @@ CodeRouter / Voice Bridge ともに独立した repo で進化していて、HTT
|
|
|
155
157
|
|
|
156
158
|
## クイックスタート(3 コマンド)
|
|
157
159
|
|
|
158
|
-
**
|
|
160
|
+
**v2.0.0 で Context Budget Management (L1 overflow 防止) を搭載** — 長時間 agent session が context window を使い切って死ぬ問題を根本解決。`uvx` 一発で動きます (Python 3.12 以上必須):
|
|
159
161
|
|
|
160
162
|
```bash
|
|
161
163
|
# 1. サンプル設定を置く
|
|
@@ -223,9 +225,9 @@ CodeRouter 自体は純 Python 3.12+ で、実質的な OS 対応範囲は `min(
|
|
|
223
225
|
|
|
224
226
|
注意点や「ローカル GPU なし」向けレシピを含むフル版マトリクス: [利用ガイド §1](./docs/usage-guide.md#1-os-互換性)
|
|
225
227
|
|
|
226
|
-
## ステータス —
|
|
228
|
+
## ステータス — v2.0.0 (2026-05)
|
|
227
229
|
|
|
228
|
-
**テスト
|
|
230
|
+
**テスト 930 本通過。ランタイム依存 5 個 (36 sub-release 連続据え置き)。macOS / Linux / Windows WSL2 で動作。** v2.0.0 で **Context Budget Management (L1 overflow 防止)** を搭載 — 長時間 agent session が context window を使い切って死ぬ問題を根本解決。v1.10.0 までに **Long-run Reliability** (L2/L3/L5)、**Cost pillar**、**Auto-router 6 matcher** が完成済み。v1.0 の総まとめは [`docs/retrospectives/v1.0.md`](./docs/retrospectives/v1.0.md)。
|
|
229
231
|
|
|
230
232
|
今日の CodeRouter が届ける価値:
|
|
231
233
|
|
|
@@ -112,6 +112,10 @@ class GatesSummary:
|
|
|
112
112
|
degraded_breakdown: dict[str, int] # capability → count
|
|
113
113
|
filters_applied_total: int
|
|
114
114
|
filters_breakdown: dict[str, int] # filter name → count
|
|
115
|
+
# v2.0-F (L1): context budget guard summary
|
|
116
|
+
context_budget_warnings: int = 0
|
|
117
|
+
context_budget_trims: int = 0
|
|
118
|
+
context_budget_latest_ratio: dict[str, float] | None = None
|
|
115
119
|
|
|
116
120
|
|
|
117
121
|
@dataclass(frozen=True)
|
|
@@ -252,6 +256,8 @@ def build_gates_summary(snapshot: dict[str, Any]) -> GatesSummary:
|
|
|
252
256
|
)
|
|
253
257
|
degraded_breakdown = dict(counters.get("capability_degraded", {}) or {})
|
|
254
258
|
filters_breakdown = dict(counters.get("output_filter_applied", {}) or {})
|
|
259
|
+
# v2.0-F (L1): context budget guard counters
|
|
260
|
+
ctx_budget_latest = counters.get("context_budget_latest_ratio") or {}
|
|
255
261
|
return GatesSummary(
|
|
256
262
|
total_requests=total_requests,
|
|
257
263
|
total_failed=total_failed,
|
|
@@ -261,6 +267,13 @@ def build_gates_summary(snapshot: dict[str, Any]) -> GatesSummary:
|
|
|
261
267
|
degraded_breakdown=degraded_breakdown,
|
|
262
268
|
filters_applied_total=sum(filters_breakdown.values()),
|
|
263
269
|
filters_breakdown=filters_breakdown,
|
|
270
|
+
context_budget_warnings=int(
|
|
271
|
+
counters.get("context_budget_warnings_total", 0)
|
|
272
|
+
),
|
|
273
|
+
context_budget_trims=int(
|
|
274
|
+
counters.get("context_budget_trims_total", 0)
|
|
275
|
+
),
|
|
276
|
+
context_budget_latest_ratio=ctx_budget_latest if ctx_budget_latest else None,
|
|
264
277
|
)
|
|
265
278
|
|
|
266
279
|
|
|
@@ -397,6 +410,19 @@ def format_text(snapshot: dict[str, Any], *, width: int = 80) -> str:
|
|
|
397
410
|
else ""
|
|
398
411
|
)
|
|
399
412
|
)
|
|
413
|
+
# v2.0-F (L1): context budget guard stats
|
|
414
|
+
if gates.context_budget_warnings or gates.context_budget_trims:
|
|
415
|
+
ratio_str = ""
|
|
416
|
+
if gates.context_budget_latest_ratio:
|
|
417
|
+
top_profile = max(
|
|
418
|
+
gates.context_budget_latest_ratio,
|
|
419
|
+
key=gates.context_budget_latest_ratio.get, # type: ignore[arg-type]
|
|
420
|
+
)
|
|
421
|
+
ratio_str = f" (latest: {gates.context_budget_latest_ratio[top_profile]:.0%} {top_profile})"
|
|
422
|
+
lines.append(
|
|
423
|
+
f" context-budget warn: {gates.context_budget_warnings} "
|
|
424
|
+
f"trim: {gates.context_budget_trims}{ratio_str}"
|
|
425
|
+
)
|
|
400
426
|
lines.append("")
|
|
401
427
|
lines.append("Recent")
|
|
402
428
|
if not recent:
|
|
@@ -633,7 +659,28 @@ def _draw_frame( # pragma: no cover - curses-only
|
|
|
633
659
|
+ (f" ({_fmt_breakdown(gates.filters_breakdown)})" if gates.filters_breakdown else ""),
|
|
634
660
|
width,
|
|
635
661
|
)
|
|
636
|
-
row +=
|
|
662
|
+
row += 1
|
|
663
|
+
# v2.0-F (L1): context budget guard line
|
|
664
|
+
if gates.context_budget_warnings or gates.context_budget_trims:
|
|
665
|
+
ratio_str = ""
|
|
666
|
+
if gates.context_budget_latest_ratio:
|
|
667
|
+
top_profile = max(
|
|
668
|
+
gates.context_budget_latest_ratio,
|
|
669
|
+
key=gates.context_budget_latest_ratio.get, # type: ignore[arg-type]
|
|
670
|
+
)
|
|
671
|
+
ratio_str = f" (latest: {gates.context_budget_latest_ratio[top_profile]:.0%} {top_profile})"
|
|
672
|
+
budget_line = (
|
|
673
|
+
f" context-budget warn: {gates.context_budget_warnings} "
|
|
674
|
+
f"trim: {gates.context_budget_trims}{ratio_str}"
|
|
675
|
+
)
|
|
676
|
+
budget_color = (
|
|
677
|
+
_COLOR_YELLOW_PAIR
|
|
678
|
+
if gates.context_budget_trims == 0
|
|
679
|
+
else _COLOR_RED_PAIR
|
|
680
|
+
)
|
|
681
|
+
stdscr.addnstr(row, 0, budget_line, width, int(curses.color_pair(budget_color)))
|
|
682
|
+
row += 1
|
|
683
|
+
row += 1
|
|
637
684
|
|
|
638
685
|
if row >= height - 2:
|
|
639
686
|
return
|