coderouter-cli 2.6.0__py3-none-any.whl → 2.6.1__py3-none-any.whl
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/ingress/dashboard_routes.py +23 -0
- coderouter/metrics/collector.py +60 -0
- {coderouter_cli-2.6.0.dist-info → coderouter_cli-2.6.1.dist-info}/METADATA +32 -1
- {coderouter_cli-2.6.0.dist-info → coderouter_cli-2.6.1.dist-info}/RECORD +7 -7
- {coderouter_cli-2.6.0.dist-info → coderouter_cli-2.6.1.dist-info}/WHEEL +0 -0
- {coderouter_cli-2.6.0.dist-info → coderouter_cli-2.6.1.dist-info}/entry_points.txt +0 -0
- {coderouter_cli-2.6.0.dist-info → coderouter_cli-2.6.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -184,6 +184,21 @@ _DASHBOARD_HTML = r"""<!doctype html>
|
|
|
184
184
|
</div>
|
|
185
185
|
</div>
|
|
186
186
|
<div id="language-tax-by-provider" class="text-xs text-slate-400 tabnum mt-3"></div>
|
|
187
|
+
<!-- Token Savings: trim (core budget guard) + compress (plugin) -->
|
|
188
|
+
<div class="grid grid-cols-3 gap-3 mt-3">
|
|
189
|
+
<div class="rounded-md bg-slate-800/50 p-3">
|
|
190
|
+
<div class="text-xs text-slate-400">Tokens saved · trim</div>
|
|
191
|
+
<div class="text-2xl font-semibold tabnum text-sky-400" data-bind="tokens_saved_trim">0</div>
|
|
192
|
+
</div>
|
|
193
|
+
<div class="rounded-md bg-slate-800/50 p-3">
|
|
194
|
+
<div class="text-xs text-slate-400">Tokens saved · compress</div>
|
|
195
|
+
<div class="text-2xl font-semibold tabnum text-sky-400" data-bind="tokens_saved_compress">0</div>
|
|
196
|
+
</div>
|
|
197
|
+
<div class="rounded-md bg-slate-800/50 p-3">
|
|
198
|
+
<div class="text-xs text-slate-400">Tokens saved · total</div>
|
|
199
|
+
<div class="text-2xl font-semibold tabnum text-green-400" data-bind="tokens_saved_total">0</div>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
187
202
|
</section>
|
|
188
203
|
<section class="bg-slate-900/60 border border-slate-800 rounded-lg p-4">
|
|
189
204
|
<h2 class="text-sm font-semibold uppercase tracking-wider text-slate-400 mb-3">Usage Mix</h2>
|
|
@@ -474,6 +489,14 @@ _DASHBOARD_HTML = r"""<!doctype html>
|
|
|
474
489
|
rows.map(([n, v]) =>
|
|
475
490
|
'<span class="mr-4"><span class="text-slate-500">' + escapeHTML(n) +
|
|
476
491
|
'</span> ' + usd(v) + '</span>').join("");
|
|
492
|
+
// token-savings tiles. trim comes from the core budget guard;
|
|
493
|
+
// compress from the optional plugin. Collector zero-fills, so an
|
|
494
|
+
// install with neither still renders "0" cleanly.
|
|
495
|
+
const ts = c.tokens_saved_by_mechanism || {};
|
|
496
|
+
const intfmt = (x) => (Number(x) || 0).toLocaleString();
|
|
497
|
+
setBind("tokens_saved_trim", intfmt(ts.trim));
|
|
498
|
+
setBind("tokens_saved_compress", intfmt(ts.compress));
|
|
499
|
+
setBind("tokens_saved_total", intfmt(c.tokens_saved_total));
|
|
477
500
|
};
|
|
478
501
|
|
|
479
502
|
const renderSnapshot = (snap) => {
|
coderouter/metrics/collector.py
CHANGED
|
@@ -197,6 +197,16 @@ class MetricsCollector(logging.Handler):
|
|
|
197
197
|
self._language_tax_usd: dict[str, float] = {}
|
|
198
198
|
self._language_tax_usd_aggregate: float = 0.0
|
|
199
199
|
|
|
200
|
+
# token-savings accounting. Tokens that never reached the backend
|
|
201
|
+
# because a reduction mechanism shrank or dropped them. Two
|
|
202
|
+
# mechanisms feed this: ``trim`` (core context-budget guard, derived
|
|
203
|
+
# from the existing ``context-budget-trimmed`` event) and
|
|
204
|
+
# ``compress`` (the optional compress plugin, via the neutral
|
|
205
|
+
# ``tokens-saved`` event). Owned by core so the figure exists even
|
|
206
|
+
# when no plugin is installed.
|
|
207
|
+
self._tokens_saved_total: int = 0
|
|
208
|
+
self._tokens_saved_by_mechanism: Counter[str] = Counter()
|
|
209
|
+
|
|
200
210
|
# v2.0-F (L1): context budget guard counters. Per-profile counts
|
|
201
211
|
# of warnings (over warn threshold) and trims (messages removed).
|
|
202
212
|
# The ``latest_usage_ratio`` dict records the most recent ratio
|
|
@@ -425,6 +435,41 @@ class MetricsCollector(logging.Handler):
|
|
|
425
435
|
profile = _str(extras.get("profile"))
|
|
426
436
|
self._context_budget_trims_total += 1
|
|
427
437
|
self._context_budget_trims_by_profile[profile] += 1
|
|
438
|
+
# token-savings: the trim already carries the estimated
|
|
439
|
+
# before/after token counts, so fold the delta into the
|
|
440
|
+
# shared savings buckets (mechanism="trim").
|
|
441
|
+
before = extras.get("estimated_tokens_before")
|
|
442
|
+
after = extras.get("estimated_tokens_after")
|
|
443
|
+
if isinstance(before, int | float) and isinstance(
|
|
444
|
+
after, int | float
|
|
445
|
+
):
|
|
446
|
+
saved = max(0, int(before) - int(after))
|
|
447
|
+
if saved:
|
|
448
|
+
self._tokens_saved_total += saved
|
|
449
|
+
self._tokens_saved_by_mechanism["trim"] += saved
|
|
450
|
+
elif event == "tokens-saved":
|
|
451
|
+
# Neutral token-savings event emitted by non-core reduction
|
|
452
|
+
# mechanisms (e.g. the compress plugin). Carries a
|
|
453
|
+
# ``mechanism`` label plus either a precomputed
|
|
454
|
+
# ``tokens_saved`` or a ``tokens_before`` / ``tokens_after``
|
|
455
|
+
# pair. Negative deltas clamp to zero. No core import needed
|
|
456
|
+
# by the emitter — it just logs this record name.
|
|
457
|
+
mechanism = _str(extras.get("mechanism")) or "unknown"
|
|
458
|
+
saved_raw = extras.get("tokens_saved")
|
|
459
|
+
before = extras.get("tokens_before")
|
|
460
|
+
after = extras.get("tokens_after")
|
|
461
|
+
if isinstance(saved_raw, int | float):
|
|
462
|
+
saved = max(0, int(saved_raw))
|
|
463
|
+
elif isinstance(before, int | float) and isinstance(
|
|
464
|
+
after, int | float
|
|
465
|
+
):
|
|
466
|
+
saved = max(0, int(before) - int(after))
|
|
467
|
+
else:
|
|
468
|
+
saved = 0
|
|
469
|
+
if saved:
|
|
470
|
+
self._tokens_saved_total += saved
|
|
471
|
+
self._tokens_saved_by_mechanism[mechanism] += saved
|
|
472
|
+
self._push_recent(event, extras, record)
|
|
428
473
|
elif event == "drift-detected":
|
|
429
474
|
# v2.0-G (L4): drift detection fired.
|
|
430
475
|
provider = _str(extras.get("provider"))
|
|
@@ -624,6 +669,11 @@ class MetricsCollector(logging.Handler):
|
|
|
624
669
|
"language_tax_usd_aggregate": round(
|
|
625
670
|
self._language_tax_usd_aggregate, 6
|
|
626
671
|
),
|
|
672
|
+
# token-savings: total + per-mechanism (trim / compress).
|
|
673
|
+
"tokens_saved_total": self._tokens_saved_total,
|
|
674
|
+
"tokens_saved_by_mechanism": dict(
|
|
675
|
+
self._tokens_saved_by_mechanism
|
|
676
|
+
),
|
|
627
677
|
# v2.0-F (L1): context budget guard aggregate counters.
|
|
628
678
|
"context_budget_warnings_total": self._context_budget_warnings_total,
|
|
629
679
|
"context_budget_trims_total": self._context_budget_trims_total,
|
|
@@ -684,6 +734,10 @@ class MetricsCollector(logging.Handler):
|
|
|
684
734
|
"chain_budget_exceeded_total": self._chain_budget_exceeded_total,
|
|
685
735
|
"chain_memory_pressure_blocked_total": self._chain_memory_pressure_blocked_total,
|
|
686
736
|
"chain_uniform_auth_failure_total": self._chain_uniform_auth_failure_total,
|
|
737
|
+
"tokens_saved_total": self._tokens_saved_total,
|
|
738
|
+
"tokens_saved_by_mechanism": dict(
|
|
739
|
+
self._tokens_saved_by_mechanism
|
|
740
|
+
),
|
|
687
741
|
"probe_rounds_total": self._probe_rounds_total,
|
|
688
742
|
}
|
|
689
743
|
|
|
@@ -724,6 +778,9 @@ class MetricsCollector(logging.Handler):
|
|
|
724
778
|
self._language_tax_usd_aggregate += float(
|
|
725
779
|
state.get("language_tax_usd_aggregate", 0.0)
|
|
726
780
|
)
|
|
781
|
+
self._tokens_saved_total += int(state.get("tokens_saved_total", 0))
|
|
782
|
+
for k, v in (state.get("tokens_saved_by_mechanism") or {}).items():
|
|
783
|
+
self._tokens_saved_by_mechanism[k] += int(v)
|
|
727
784
|
self._chain_paid_gate_blocked_total += int(
|
|
728
785
|
state.get("chain_paid_gate_blocked_total", 0)
|
|
729
786
|
)
|
|
@@ -782,6 +839,9 @@ class MetricsCollector(logging.Handler):
|
|
|
782
839
|
# v2.6
|
|
783
840
|
self._language_tax_usd.clear()
|
|
784
841
|
self._language_tax_usd_aggregate = 0.0
|
|
842
|
+
# token-savings
|
|
843
|
+
self._tokens_saved_total = 0
|
|
844
|
+
self._tokens_saved_by_mechanism.clear()
|
|
785
845
|
# v2.0-H (L6)
|
|
786
846
|
self._partial_stitch_surfaced_total = 0
|
|
787
847
|
# v2.0-I
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: coderouter-cli
|
|
3
|
-
Version: 2.6.
|
|
3
|
+
Version: 2.6.1
|
|
4
4
|
Summary: Local-first, free-first, fallback-built-in LLM router. Claude Code / OpenAI compatible.
|
|
5
5
|
Project-URL: Homepage, https://github.com/zephel01/CodeRouter
|
|
6
6
|
Project-URL: Repository, https://github.com/zephel01/CodeRouter
|
|
@@ -161,6 +161,36 @@ ANTHROPIC_BASE_URL=http://localhost:8088 ANTHROPIC_AUTH_TOKEN=dummy claude
|
|
|
161
161
|
| **`coderouter replay`** | provider 切替の効果を統計比較 (A/B 分析) / `--suggest-rules` でルール最適化提案 |
|
|
162
162
|
| **Continuous Probe** | idle 時も定期的に backend を監視 |
|
|
163
163
|
|
|
164
|
+
### 言語税トラッキング — v2.6.0
|
|
165
|
+
|
|
166
|
+
日本語などの CJK テキストは、クラウドのトークナイザだと「同じ意味の英語」より多くのトークンを消費します(**実測: GPT-4o 系 o200k で平均 1.6 倍、GPT-4 系 cl100k で平均 2.0 倍**)。ローカル LLM は課金されないので、この「言語税」はクラウド利用時だけ効いてきます。CodeRouter v2.6.0 はこれを **計測・ルーティング回避・可視化** します。
|
|
167
|
+
|
|
168
|
+
| 機能 | 何をしてくれるか |
|
|
169
|
+
|---|---|
|
|
170
|
+
| **言語税の計測** | プロバイダに `tokenizer_path`(ローカルの `tokenizer.json`)を指定すると、char/4 ヒューリスティック比の実トークン倍率と割増 USD を算出(ネットワーク不要・未設定なら無効) |
|
|
171
|
+
| **`cjk_ratio_min` ルーティング** | CJK 比率が高いリクエストを自動でローカル LLM(課金ゼロ)へ。コードや英語はクラウドへ |
|
|
172
|
+
| **ダッシュボード可視化** | `/dashboard` の「Cost & Language Tax」パネルで総支出・キャッシュ節約・言語税をリアルタイム表示 |
|
|
173
|
+
|
|
174
|
+
```yaml
|
|
175
|
+
# providers.yaml — CJK 多めのターンはローカルへ自動回避
|
|
176
|
+
auto_router:
|
|
177
|
+
rules:
|
|
178
|
+
- match: { cjk_ratio_min: 0.3 } # 日本語が3割以上 → ローカル
|
|
179
|
+
profile: local
|
|
180
|
+
- match: { has_tools: true } # ツール使用 → クラウド
|
|
181
|
+
profile: cloud
|
|
182
|
+
default_rule_profile: cloud
|
|
183
|
+
|
|
184
|
+
providers:
|
|
185
|
+
- name: cloud-sonnet
|
|
186
|
+
kind: anthropic
|
|
187
|
+
base_url: https://api.anthropic.com
|
|
188
|
+
model: claude-sonnet-4-6
|
|
189
|
+
tokenizer_path: ~/.coderouter/tokenizers/sonnet.json # 言語税の正確計測(任意)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
詳細 → [言語税ガイド](./docs/guides/language-tax.md)
|
|
193
|
+
|
|
164
194
|
### Launcher — llama.cpp / vllm 起動 UI
|
|
165
195
|
|
|
166
196
|
`http://localhost:8088/launcher` で開けるブラウザ UI。llama.cpp や vllm を GUI で起動・管理できます。
|
|
@@ -229,6 +259,7 @@ providers:
|
|
|
229
259
|
| 使いこなす | [利用ガイド](./docs/guides/usage-guide.md) |
|
|
230
260
|
| 無料で回す | [無料枠ガイド](./docs/guides/free-tier-guide.md) |
|
|
231
261
|
| llama.cpp / vllm を GUI で起動 | [Launcher ガイド](./docs/backends/launcher.md) |
|
|
262
|
+
| 言語税を計測・回避する | [言語税ガイド](./docs/guides/language-tax.md) |
|
|
232
263
|
| 詰まった | [トラブルシューティング](./docs/guides/troubleshooting.md) |
|
|
233
264
|
| 設計を知りたい | [アーキテクチャ詳細](./docs/concepts/architecture.md) |
|
|
234
265
|
| 全リリース履歴 | [CHANGELOG](./CHANGELOG.md) |
|
|
@@ -40,12 +40,12 @@ coderouter/guards/tool_loop.py,sha256=EzeMcmU7BLeTW2jsRVevU81l5rhWcn1oUr7EpzgXjV
|
|
|
40
40
|
coderouter/ingress/__init__.py,sha256=WQsCH2CGJCAhy0mS6GSEdeYZRkkQu2OHDsP4CJWTLug,155
|
|
41
41
|
coderouter/ingress/anthropic_routes.py,sha256=It2f7XGe3fgKQX01J2F5JOCoZr96t_Tx_kY2om99MVo,16894
|
|
42
42
|
coderouter/ingress/app.py,sha256=PcuTvUFNjr04EbsUOu8qdyKTdBzxkIJYB4xpz8dFfMo,12635
|
|
43
|
-
coderouter/ingress/dashboard_routes.py,sha256=
|
|
43
|
+
coderouter/ingress/dashboard_routes.py,sha256=1SqNNrVTfQiZcx5n2MvjAzWirittX0e61tCt-MKp4Ag,25617
|
|
44
44
|
coderouter/ingress/launcher_routes.py,sha256=Jh-E6qFmHnr7ON4W6QanafxQIoojT4F034mybLvhTyQ,47548
|
|
45
45
|
coderouter/ingress/metrics_routes.py,sha256=M22dwOGn24P05Ge4W3c7d7mYytSGWjIR-pPSPOAiHJY,3965
|
|
46
46
|
coderouter/ingress/openai_routes.py,sha256=Zw1efPw9DI6GgV8ZcLrzS6Cda0KLrFkKn2GBZWSe6Vo,6322
|
|
47
47
|
coderouter/metrics/__init__.py,sha256=7Es351DPS7yLM0yVF_F0eesmiD83n7Zzhie44chht38,1465
|
|
48
|
-
coderouter/metrics/collector.py,sha256=
|
|
48
|
+
coderouter/metrics/collector.py,sha256=F6C19DgZDqEKq8Hidlo99cfev5vRQO63p4WMOrvUeqQ,52975
|
|
49
49
|
coderouter/metrics/prometheus.py,sha256=YRqyT931s40zVkIj07D-M2UNfDhIEElVFRz3izdJcnQ,24419
|
|
50
50
|
coderouter/plugins/__init__.py,sha256=76hMLe5dV_ilripHXzWn3HSYoIALjzlw4EJVyI-GyIM,1974
|
|
51
51
|
coderouter/plugins/base.py,sha256=n9hsck2NCSqi6oeHIumKC5zhQ8JGwCXUz7J5AZQCQss,5772
|
|
@@ -67,8 +67,8 @@ coderouter/translation/__init__.py,sha256=PYXN7XVEwpG1uC8RLy6fvnGbzEZhhrEuUapH8I
|
|
|
67
67
|
coderouter/translation/anthropic.py,sha256=aZkcYH4x82b0x7efJgJb9RWn9Hbyc9pEOthXe4vjUdU,11113
|
|
68
68
|
coderouter/translation/convert.py,sha256=-qyzFzmmr9hhQV6_Sg75kJnvCZvHe3n7vRdaZtk_JqQ,47269
|
|
69
69
|
coderouter/translation/tool_repair.py,sha256=Ok2PF947Liegc5oaytfptv5MWMkpfJYQie-zdP1y3cY,9946
|
|
70
|
-
coderouter_cli-2.6.
|
|
71
|
-
coderouter_cli-2.6.
|
|
72
|
-
coderouter_cli-2.6.
|
|
73
|
-
coderouter_cli-2.6.
|
|
74
|
-
coderouter_cli-2.6.
|
|
70
|
+
coderouter_cli-2.6.1.dist-info/METADATA,sha256=3zw3ZZGT71GYD8F9qeYIyMQB-ZN4jmRRoYOB4XD8eIo,13534
|
|
71
|
+
coderouter_cli-2.6.1.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
72
|
+
coderouter_cli-2.6.1.dist-info/entry_points.txt,sha256=-dnLfD1YZ2WjH2zSdNCvlO65wYltM9bsHt9Fhg3yGss,51
|
|
73
|
+
coderouter_cli-2.6.1.dist-info/licenses/LICENSE,sha256=wkEzoR86jFw33jvfOHjULqmkGEfxTFMgMaJnpR8mPRw,1065
|
|
74
|
+
coderouter_cli-2.6.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|