coderouter-cli 2.8.1__py3-none-any.whl → 2.9.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/adapters/registry.py +31 -48
- coderouter/config/schemas.py +555 -8
- coderouter/doctor.py +58 -0
- coderouter/ingress/app.py +29 -0
- coderouter/ingress/launcher_routes.py +476 -147
- coderouter/launcher_swap.py +466 -0
- coderouter/routing/fallback.py +225 -5
- {coderouter_cli-2.8.1.dist-info → coderouter_cli-2.9.1.dist-info}/METADATA +5 -3
- {coderouter_cli-2.8.1.dist-info → coderouter_cli-2.9.1.dist-info}/RECORD +12 -12
- coderouter/adapters/agent_cli.py +0 -1168
- {coderouter_cli-2.8.1.dist-info → coderouter_cli-2.9.1.dist-info}/WHEEL +0 -0
- {coderouter_cli-2.8.1.dist-info → coderouter_cli-2.9.1.dist-info}/entry_points.txt +0 -0
- {coderouter_cli-2.8.1.dist-info → coderouter_cli-2.9.1.dist-info}/licenses/LICENSE +0 -0
coderouter/routing/fallback.py
CHANGED
|
@@ -25,6 +25,7 @@ Dual entry points (v0.3.x-1):
|
|
|
25
25
|
from __future__ import annotations
|
|
26
26
|
|
|
27
27
|
import asyncio
|
|
28
|
+
import contextlib
|
|
28
29
|
import time
|
|
29
30
|
from collections.abc import AsyncIterator
|
|
30
31
|
from contextvars import ContextVar
|
|
@@ -1178,6 +1179,107 @@ class FallbackEngine:
|
|
|
1178
1179
|
self._pending_recovery_rearm: dict[str, str] = {}
|
|
1179
1180
|
# v2.0-K: persistent state store (None = in-memory only).
|
|
1180
1181
|
self._state_store: StateStore | None = None
|
|
1182
|
+
# launcher-model-swap.md §4.1: optional on-demand swap
|
|
1183
|
+
# coordinator (coderouter.launcher_swap.SwapManager), wired via
|
|
1184
|
+
# attach_swap_manager() from ingress/app.py's lifespan when
|
|
1185
|
+
# ``launcher.swap.enabled``. None (default / legacy engines) —
|
|
1186
|
+
# every dispatch entry point's swap hook is then a cheap no-op.
|
|
1187
|
+
self._swap_manager: Any | None = None
|
|
1188
|
+
|
|
1189
|
+
def attach_swap_manager(self, manager: Any) -> None:
|
|
1190
|
+
"""Wire a SwapManager for on-demand model-swap dispatch (Phase 1).
|
|
1191
|
+
|
|
1192
|
+
Optional — omit entirely for deployments that don't use
|
|
1193
|
+
``launcher.swap``; the dispatch hooks then short-circuit on
|
|
1194
|
+
``manager is None`` and behave exactly as before. See
|
|
1195
|
+
coderouter/launcher_swap.py and
|
|
1196
|
+
docs/designs/launcher-model-swap.md.
|
|
1197
|
+
"""
|
|
1198
|
+
self._swap_manager = manager
|
|
1199
|
+
|
|
1200
|
+
async def _swap_ensure_loaded(self, request: Any) -> Any | None:
|
|
1201
|
+
"""Phase 1 on-demand swap dispatch hook — see each call site's
|
|
1202
|
+
docstring (generate / stream / generate_anthropic / stream_anthropic).
|
|
1203
|
+
|
|
1204
|
+
Review fix M-2/M-3: keyed on the RESOLVED routing target, not
|
|
1205
|
+
on ``request.model``. By the time the engine runs, the ingress
|
|
1206
|
+
has already applied the full precedence chain (body profile >
|
|
1207
|
+
headers > auto_router — including the injected ``swap:<name>``
|
|
1208
|
+
rules), so ``request.profile or default_profile`` IS the final
|
|
1209
|
+
destination:
|
|
1210
|
+
|
|
1211
|
+
* resolved profile is a swap model's dedicated profile
|
|
1212
|
+
(``launcher-swap-<name>``) → acquire that model's lease,
|
|
1213
|
+
regardless of what the ``model`` field says (M-3: an aliased
|
|
1214
|
+
model name routed here explicitly was previously left
|
|
1215
|
+
lease-less and could be TTL-evicted mid-request);
|
|
1216
|
+
* otherwise, if the resolved chain explicitly lists a swap
|
|
1217
|
+
provider (``launcher-swap-<name>`` wired into a custom chain),
|
|
1218
|
+
acquire that;
|
|
1219
|
+
* otherwise no-op — a request whose model name merely *matches*
|
|
1220
|
+
the catalog but routes elsewhere no longer triggers a wasted
|
|
1221
|
+
spawn (M-2).
|
|
1222
|
+
|
|
1223
|
+
Returns ``None`` (no-op) when no SwapManager is attached or the
|
|
1224
|
+
routing target has no swap involvement — the common case,
|
|
1225
|
+
costing one ``getattr`` + one dict lookup. Otherwise spawns (or
|
|
1226
|
+
waits for a concurrent spawn of) the backend and returns the
|
|
1227
|
+
:class:`~coderouter.launcher_swap.Lease` the caller must release
|
|
1228
|
+
via :meth:`_swap_release`.
|
|
1229
|
+
"""
|
|
1230
|
+
# getattr (not self._swap_manager directly): some legacy tests
|
|
1231
|
+
# construct the engine via ``FallbackEngine.__new__``, bypassing
|
|
1232
|
+
# __init__ entirely (same rationale as the ``_adaptive`` /
|
|
1233
|
+
# ``_budget`` lazy properties above).
|
|
1234
|
+
manager = getattr(self, "_swap_manager", None)
|
|
1235
|
+
if manager is None:
|
|
1236
|
+
return None
|
|
1237
|
+
chosen = request.profile or self.config.default_profile
|
|
1238
|
+
spec = manager.spec_for_profile(chosen)
|
|
1239
|
+
if spec is None:
|
|
1240
|
+
try:
|
|
1241
|
+
chain = self.config.profile_by_name(chosen)
|
|
1242
|
+
except KeyError:
|
|
1243
|
+
return None # unknown / "auto" sentinel — nothing to swap
|
|
1244
|
+
for prov_name in chain.providers:
|
|
1245
|
+
spec = manager.spec_for_provider(prov_name)
|
|
1246
|
+
if spec is not None:
|
|
1247
|
+
break
|
|
1248
|
+
if spec is None:
|
|
1249
|
+
return None
|
|
1250
|
+
return await manager.ensure_loaded(spec.name)
|
|
1251
|
+
|
|
1252
|
+
async def _swap_ensure_loaded_or_raise(self, request: Any) -> Any | None:
|
|
1253
|
+
"""``_swap_ensure_loaded`` wrapper that fails the SAME way an
|
|
1254
|
+
exhausted provider chain does.
|
|
1255
|
+
|
|
1256
|
+
The swap hook runs *before* chain resolution, so a spawn/
|
|
1257
|
+
readiness failure here can't be caught by ``_generate_impl``'s
|
|
1258
|
+
own per-provider ``except AdapterError`` loop (there's no chain
|
|
1259
|
+
yet). Converting it to :class:`NoProvidersAvailableError` here
|
|
1260
|
+
keeps the observable failure mode identical either way — a 502
|
|
1261
|
+
with the per-provider error trail — instead of leaking an
|
|
1262
|
+
unhandled 500 out through the ingress. §6.4's "never poison"
|
|
1263
|
+
guarantee is unaffected: the next request re-tries ``idle`` from
|
|
1264
|
+
scratch regardless of which exception type carried the failure.
|
|
1265
|
+
"""
|
|
1266
|
+
try:
|
|
1267
|
+
return await self._swap_ensure_loaded(request)
|
|
1268
|
+
except AdapterError as exc:
|
|
1269
|
+
profile = request.profile or self.config.default_profile
|
|
1270
|
+
raise NoProvidersAvailableError(profile=profile, errors=[exc]) from exc
|
|
1271
|
+
|
|
1272
|
+
async def _swap_release(self, lease: Any) -> None:
|
|
1273
|
+
"""Release a swap lease acquired by :meth:`_swap_ensure_loaded`.
|
|
1274
|
+
|
|
1275
|
+
Never raises — a release failure must not mask the (already
|
|
1276
|
+
complete) response, and the TTL sweeper degrades gracefully
|
|
1277
|
+
(worst case: the model just doesn't idle-unload this cycle).
|
|
1278
|
+
"""
|
|
1279
|
+
manager = getattr(self, "_swap_manager", None)
|
|
1280
|
+
if manager is not None:
|
|
1281
|
+
with contextlib.suppress(Exception):
|
|
1282
|
+
await manager.release_lease(lease)
|
|
1181
1283
|
|
|
1182
1284
|
def register_provider(
|
|
1183
1285
|
self, provider: ProviderConfig, profile_name: str = "launcher"
|
|
@@ -1253,6 +1355,46 @@ class FallbackEngine:
|
|
|
1253
1355
|
"persisted": False, # in-memory only, by design (see docstring)
|
|
1254
1356
|
}
|
|
1255
1357
|
|
|
1358
|
+
async def deregister_provider(
|
|
1359
|
+
self, provider_name: str, profile_name: str = "launcher"
|
|
1360
|
+
) -> bool:
|
|
1361
|
+
"""Remove a provider previously added by :meth:`register_provider`.
|
|
1362
|
+
|
|
1363
|
+
The counterpart :meth:`register_provider` never needed — until
|
|
1364
|
+
the on-demand swap TTL sweeper (SwapManager, §6.6 known-trap
|
|
1365
|
+
#5) needs to unwind a launcher-started backend after it ages
|
|
1366
|
+
out: leaving it in the chain would route requests to a port
|
|
1367
|
+
that no longer answers. In-memory only, same as
|
|
1368
|
+
``register_provider`` (no providers.yaml rewrite).
|
|
1369
|
+
|
|
1370
|
+
Idempotent — removing an already-absent provider / chain entry
|
|
1371
|
+
is a silent no-op (returns False) rather than an error, since a
|
|
1372
|
+
sweeper race (two code paths tearing down the same model) must
|
|
1373
|
+
never raise.
|
|
1374
|
+
"""
|
|
1375
|
+
removed = False
|
|
1376
|
+
with contextlib.suppress(KeyError):
|
|
1377
|
+
chain = self.config.profile_by_name(profile_name)
|
|
1378
|
+
if provider_name in chain.providers:
|
|
1379
|
+
chain.providers.remove(provider_name)
|
|
1380
|
+
removed = True
|
|
1381
|
+
self.config.providers = [
|
|
1382
|
+
p for p in self.config.providers if p.name != provider_name
|
|
1383
|
+
]
|
|
1384
|
+
adapter = self._adapters.pop(provider_name, None)
|
|
1385
|
+
if adapter is not None:
|
|
1386
|
+
removed = True
|
|
1387
|
+
with contextlib.suppress(Exception):
|
|
1388
|
+
await adapter.aclose()
|
|
1389
|
+
if removed:
|
|
1390
|
+
logger.info(
|
|
1391
|
+
"launcher provider sync: deregistered %s (profile %r)",
|
|
1392
|
+
provider_name,
|
|
1393
|
+
profile_name,
|
|
1394
|
+
extra={"provider": provider_name, "profile": profile_name},
|
|
1395
|
+
)
|
|
1396
|
+
return removed
|
|
1397
|
+
|
|
1256
1398
|
@property
|
|
1257
1399
|
def plugins(self) -> PluginRegistry:
|
|
1258
1400
|
"""Return the plugin registry, lazily building an empty one if absent.
|
|
@@ -2219,7 +2361,23 @@ class FallbackEngine:
|
|
|
2219
2361
|
async def generate(self, request: ChatRequest) -> ChatResponse:
|
|
2220
2362
|
"""Non-streaming OpenAI-shaped generation with sequential fallback.
|
|
2221
2363
|
|
|
2222
|
-
|
|
2364
|
+
launcher-model-swap.md §4.1: the on-demand swap dispatch hook
|
|
2365
|
+
runs first — when the resolved profile (or its chain) targets a
|
|
2366
|
+
swap model it spawns/awaits the backend (or waits for a
|
|
2367
|
+
concurrent spawn) before chain resolution, so ``_generate_impl``
|
|
2368
|
+
always sees an already-loaded backend. A no-op (cheap ``is
|
|
2369
|
+
None`` check) when no SwapManager is attached.
|
|
2370
|
+
"""
|
|
2371
|
+
lease = await self._swap_ensure_loaded_or_raise(request)
|
|
2372
|
+
try:
|
|
2373
|
+
return await self._generate_impl(request)
|
|
2374
|
+
finally:
|
|
2375
|
+
if lease is not None:
|
|
2376
|
+
await self._swap_release(lease)
|
|
2377
|
+
|
|
2378
|
+
async def _generate_impl(self, request: ChatRequest) -> ChatResponse:
|
|
2379
|
+
"""Walks the chain in order, returning the first provider's response.
|
|
2380
|
+
|
|
2223
2381
|
On retryable :class:`AdapterError` (transport failure, rate
|
|
2224
2382
|
limit, upstream 5xx, etc.) the loop advances; on non-retryable
|
|
2225
2383
|
errors it breaks immediately. When every provider has been tried
|
|
@@ -2295,7 +2453,23 @@ class FallbackEngine:
|
|
|
2295
2453
|
async def stream(self, request: ChatRequest) -> AsyncIterator[StreamChunk]:
|
|
2296
2454
|
"""Stream from the first provider that successfully starts streaming.
|
|
2297
2455
|
|
|
2298
|
-
|
|
2456
|
+
launcher-model-swap.md §4.1 / §6.6 known-trap #1: the swap lease
|
|
2457
|
+
(if any) is held across the *entire* generator — including every
|
|
2458
|
+
yielded chunk — and released in ``finally`` so it survives client
|
|
2459
|
+
disconnect / cancellation (``GeneratorExit`` still runs the
|
|
2460
|
+
``finally``) without ever leaking an ``in_flight`` count that
|
|
2461
|
+
would block the TTL sweeper forever.
|
|
2462
|
+
"""
|
|
2463
|
+
lease = await self._swap_ensure_loaded_or_raise(request)
|
|
2464
|
+
try:
|
|
2465
|
+
async for chunk in self._stream_impl(request):
|
|
2466
|
+
yield chunk
|
|
2467
|
+
finally:
|
|
2468
|
+
if lease is not None:
|
|
2469
|
+
await self._swap_release(lease)
|
|
2470
|
+
|
|
2471
|
+
async def _stream_impl(self, request: ChatRequest) -> AsyncIterator[StreamChunk]:
|
|
2472
|
+
"""Important: once we begin yielding chunks from an adapter, we cannot
|
|
2299
2473
|
fall back mid-stream (the client has already received partial content).
|
|
2300
2474
|
We only fall through if the *initial* response is an error.
|
|
2301
2475
|
"""
|
|
@@ -2591,6 +2765,30 @@ class FallbackEngine:
|
|
|
2591
2765
|
)
|
|
2592
2766
|
|
|
2593
2767
|
async def generate_anthropic(self, request: AnthropicRequest) -> AnthropicResponse:
|
|
2768
|
+
"""Non-streaming Anthropic request, per-provider dispatch.
|
|
2769
|
+
|
|
2770
|
+
launcher-model-swap.md §4.1: the swap dispatch hook runs first,
|
|
2771
|
+
keyed on the resolved profile (see ``_swap_ensure_loaded``).
|
|
2772
|
+
When it fires (spawns or waits for a
|
|
2773
|
+
just-loaded swap backend), the M11 ingress-prepared dispatch
|
|
2774
|
+
(``apply_context_budget``, which resolves the chain *before*
|
|
2775
|
+
this method — and therefore before the swap process/provider
|
|
2776
|
+
existed) is deliberately bypassed so ``_generate_anthropic_impl``
|
|
2777
|
+
re-resolves the chain fresh and actually finds the newly
|
|
2778
|
+
registered provider.
|
|
2779
|
+
"""
|
|
2780
|
+
lease = await self._swap_ensure_loaded_or_raise(request)
|
|
2781
|
+
try:
|
|
2782
|
+
return await self._generate_anthropic_impl(
|
|
2783
|
+
request, skip_prepared_dispatch=lease is not None
|
|
2784
|
+
)
|
|
2785
|
+
finally:
|
|
2786
|
+
if lease is not None:
|
|
2787
|
+
await self._swap_release(lease)
|
|
2788
|
+
|
|
2789
|
+
async def _generate_anthropic_impl(
|
|
2790
|
+
self, request: AnthropicRequest, *, skip_prepared_dispatch: bool = False
|
|
2791
|
+
) -> AnthropicResponse:
|
|
2594
2792
|
"""Non-streaming Anthropic request, per-provider dispatch."""
|
|
2595
2793
|
# v1.9-E (L3): tool-loop guard runs before chain dispatch so the
|
|
2596
2794
|
# `inject` action's mutated request flows into the chain
|
|
@@ -2609,7 +2807,9 @@ class FallbackEngine:
|
|
|
2609
2807
|
# guard nor the input filters replaced the request object (the
|
|
2610
2808
|
# common no-plugin path). This avoids a second chain resolution
|
|
2611
2809
|
# (incl. adaptive/drift reorder) and a second full token estimate.
|
|
2612
|
-
|
|
2810
|
+
# ``skip_prepared_dispatch`` (swap) forces a fresh resolution —
|
|
2811
|
+
# see ``generate_anthropic``'s docstring.
|
|
2812
|
+
prepared = None if skip_prepared_dispatch else self._take_prepared_dispatch(request)
|
|
2613
2813
|
if prepared is not None:
|
|
2614
2814
|
chain = prepared.chain
|
|
2615
2815
|
request = prepared.request
|
|
@@ -2879,7 +3079,26 @@ class FallbackEngine:
|
|
|
2879
3079
|
) -> AsyncIterator[AnthropicStreamEvent]:
|
|
2880
3080
|
"""Streaming Anthropic request, per-provider dispatch.
|
|
2881
3081
|
|
|
2882
|
-
|
|
3082
|
+
launcher-model-swap.md §4.1 / §6.6 known-trap #1: same swap
|
|
3083
|
+
lease treatment as :meth:`stream` — held across the whole
|
|
3084
|
+
generator, released in ``finally``. See :meth:`generate_anthropic`
|
|
3085
|
+
for why the M11 prepared-dispatch cache is bypassed when swap
|
|
3086
|
+
fires.
|
|
3087
|
+
"""
|
|
3088
|
+
lease = await self._swap_ensure_loaded_or_raise(request)
|
|
3089
|
+
try:
|
|
3090
|
+
async for ev in self._stream_anthropic_impl(
|
|
3091
|
+
request, skip_prepared_dispatch=lease is not None
|
|
3092
|
+
):
|
|
3093
|
+
yield ev
|
|
3094
|
+
finally:
|
|
3095
|
+
if lease is not None:
|
|
3096
|
+
await self._swap_release(lease)
|
|
3097
|
+
|
|
3098
|
+
async def _stream_anthropic_impl(
|
|
3099
|
+
self, request: AnthropicRequest, *, skip_prepared_dispatch: bool = False
|
|
3100
|
+
) -> AsyncIterator[AnthropicStreamEvent]:
|
|
3101
|
+
"""For non-native providers with tools declared, we use the v0.3-D
|
|
2883
3102
|
downgrade path (run the request non-streaming internally, repair
|
|
2884
3103
|
tool calls, then synthesize an Anthropic SSE event sequence) —
|
|
2885
3104
|
the same logic that used to live in the ingress. Consolidating
|
|
@@ -2898,7 +3117,8 @@ class FallbackEngine:
|
|
|
2898
3117
|
# M11: reuse the ingress-prepared chain + trimmed request when the
|
|
2899
3118
|
# guard / filters were no-ops (identity match). Mirrors the
|
|
2900
3119
|
# non-streaming path — avoids a redundant resolution + estimate.
|
|
2901
|
-
|
|
3120
|
+
# ``skip_prepared_dispatch`` (swap) forces a fresh resolution.
|
|
3121
|
+
prepared = None if skip_prepared_dispatch else self._take_prepared_dispatch(request)
|
|
2902
3122
|
if prepared is not None:
|
|
2903
3123
|
chain = prepared.chain
|
|
2904
3124
|
request = prepared.request
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: coderouter-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.9.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
|
|
@@ -66,7 +66,7 @@ Description-Content-Type: text/markdown
|
|
|
66
66
|
## 何ができるか — 30 秒で
|
|
67
67
|
|
|
68
68
|
```
|
|
69
|
-
あなたのエージェント (Claude Code /
|
|
69
|
+
あなたのエージェント (Claude Code / codex / agy)
|
|
70
70
|
│
|
|
71
71
|
▼
|
|
72
72
|
┌─ CodeRouter ─┐
|
|
@@ -141,7 +141,7 @@ ANTHROPIC_BASE_URL=http://localhost:8088 ANTHROPIC_AUTH_TOKEN=dummy claude
|
|
|
141
141
|
| Claude Code + ローカル Ollama で tool calling が壊れる | **必須** — tool 修復 (+ 必要なら wire 変換) |
|
|
142
142
|
| Claude Code + ローカルで長時間回すと止まる | **必須級** — 6 系統ガード + self-healing |
|
|
143
143
|
| Ollama v0.14+ / LM Studio にネイティブ直結で動いてる | **便利** — 直結に無い fallback / ガード / 診断を追加 (passthrough で翻訳ゼロ) |
|
|
144
|
-
| codex /
|
|
144
|
+
| codex / agy + Ollama 直繋ぎで動いてる | オプション — フォールバックが欲しいなら |
|
|
145
145
|
| Claude API を直接叩いてて問題ない | 不要 |
|
|
146
146
|
|
|
147
147
|
詳細は → [要否判定ガイド](./docs/start/when-do-i-need-coderouter.md)
|
|
@@ -311,6 +311,8 @@ English: [Quickstart](./docs/start/quickstart.en.md) · [Usage guide](./docs/gui
|
|
|
311
311
|
- **テスト**: 1,500+ 本(ランタイム依存 5 個は v1 系から不変)
|
|
312
312
|
- **対応 OS**: macOS (Apple Silicon 推奨) / Linux / Windows WSL2
|
|
313
313
|
- **対応 backend**: Ollama / llama.cpp / LM Studio / vLLM / MLX-LM / OpenRouter / NVIDIA NIM / Anthropic API
|
|
314
|
+
- **外部エージェント CLI**: `agent_cli` provider として Claude Code / codex / grok / antigravity の4種を束ねて呼び出せる(要 `coderouter-plugin-agents`。詳細 → [external-agents ガイド](./docs/backends/external-agents.md))
|
|
315
|
+
- **プラグイン**: compress / memory / agents の3種を opt-in で追加可能(コアの依存は増えない。一覧・導入方法 → [docs/README.md](./docs/README.md#対応プラグイン--plugins))
|
|
314
316
|
- **ライセンス**: MIT
|
|
315
317
|
|
|
316
318
|
---
|
|
@@ -3,7 +3,7 @@ coderouter/__main__.py,sha256=-LCgxJnvgUV240HjQKv7ly-mn2NuKHpC4nCpvTHjeSU,130
|
|
|
3
3
|
coderouter/cli.py,sha256=7zxt6riKPTpHGiDms9SVaPbe-n4ytfCrhIVtuPII-oU,30173
|
|
4
4
|
coderouter/cli_stats.py,sha256=CCjzc1G4hTRHZ2gG1XhxhDpUkJnnl3NXbcbp1T18jpg,29894
|
|
5
5
|
coderouter/cost.py,sha256=32h6uzb4nxh2eA5d2Hn3kD9yJbtis6CFDAbeIy5KRkM,7431
|
|
6
|
-
coderouter/doctor.py,sha256=
|
|
6
|
+
coderouter/doctor.py,sha256=x0OqWUMGPzYaX2poWSH2UA90sEr4Z69-gVYLXYQFCFU,85483
|
|
7
7
|
coderouter/doctor_apply.py,sha256=r_J6xbu5-HivofPNriw4_vjNYs_VRs7GsGTS0oMEX10,24209
|
|
8
8
|
coderouter/env_security.py,sha256=FEBZnXfJ0xE39kmMMn39zk0W_DRRnmcB_REmP9f4xWo,14796
|
|
9
9
|
coderouter/errors.py,sha256=Xmq67lheyw8iv3Ox39jh2c4tvNI5RcUR4QkoxVDN6l4,1130
|
|
@@ -11,21 +11,21 @@ coderouter/gguf_introspect.py,sha256=KeE00CfbYRa4gSTrzuwC6zVuHi20IzLC4nUQa98BEXI
|
|
|
11
11
|
coderouter/hardware.py,sha256=gn3_9qbVcGRR81yKMn1lJE_8-YDRau0LxIH_M-f7pxE,8356
|
|
12
12
|
coderouter/language_tax.py,sha256=LTbE3tIfoJuV2O3T0NixRKhzq_dEOTUuPEerJv2q9uk,9360
|
|
13
13
|
coderouter/launcher_speculative.py,sha256=kWaHNmhzBsbWuM_lRGDlbWkgJ1suuxBWQNTKhJOC-yg,12644
|
|
14
|
+
coderouter/launcher_swap.py,sha256=Oc9itCKzjSFbE_RWxqcjIJyiYax8o9zZo7czmEI2s-U,19150
|
|
14
15
|
coderouter/logging.py,sha256=5rfi4_R4aoeeUYDc642dgs61wA0aBX7wvubyB-k3Uyo,58074
|
|
15
16
|
coderouter/output_filters.py,sha256=0ry_rPiS_kC-FnHgaNVP6v7e6Al2djxzu9vBzZ8kEkE,25314
|
|
16
17
|
coderouter/token_estimation.py,sha256=iz22vZEEW2P7uKLB2pYvPNpIbZGbgXRO5MtfkS_-9Sk,7531
|
|
17
18
|
coderouter/token_estimation_accurate.py,sha256=GTfzrBVnvAGjeVzmzAeUdOYZvWZKLAxcxPpFiJGlzjk,4609
|
|
18
19
|
coderouter/adapters/__init__.py,sha256=7dIDSZ-FE_0iSqLSDc_lK1idRdLTKcM2hP9tCJipgPI,463
|
|
19
|
-
coderouter/adapters/agent_cli.py,sha256=0g0V92drEQLJACXq-Dn0hE-J6e3vmsuBr5UdnAkXJyk,52430
|
|
20
20
|
coderouter/adapters/anthropic_native.py,sha256=CT9Hitun-c3z83YHT2zZXgwZpY3_t6eRnOfAmNud2aw,23610
|
|
21
21
|
coderouter/adapters/base.py,sha256=ykKMaiIVVGr23oFFXilK2iP_YhI8-CtsEiJyFXaC9bE,10396
|
|
22
22
|
coderouter/adapters/openai_compat.py,sha256=wcrd_UpV7N6ISpUyKvdFPGU-YjCrX5oMqGpjAFEeWfg,20356
|
|
23
|
-
coderouter/adapters/registry.py,sha256=
|
|
23
|
+
coderouter/adapters/registry.py,sha256=XNc1wmFv_oZlqKS0rA693TRDdbq1sZ-OIcofXOVOtq0,3680
|
|
24
24
|
coderouter/config/__init__.py,sha256=FODEn74fN-qZnt4INPSHswqhOlEgpL6-_onxsitSx8g,274
|
|
25
25
|
coderouter/config/capability_registry.py,sha256=RZgzHF54dXy0CCDocRCz2ee21qXGbbgTSKTkRI9iLL4,16936
|
|
26
26
|
coderouter/config/env_file.py,sha256=CoMK27fuAXm-NtoLzXb8yN2E-wDFjHQuFwiIlmgTBQw,10356
|
|
27
27
|
coderouter/config/loader.py,sha256=FUEe8m4Tnmj_aul0vSctD8vKvNW-oLRoMRbTpSKqSmc,4077
|
|
28
|
-
coderouter/config/schemas.py,sha256=
|
|
28
|
+
coderouter/config/schemas.py,sha256=U7AGOm88gtCqUYsRNYc6KVaiP-Cdg5yd_oOzYiiR3rs,114340
|
|
29
29
|
coderouter/data/__init__.py,sha256=uNyfD9jaCvTWsBAWtaw1Fr25OSxzv3psGMfBjT1z0Cc,328
|
|
30
30
|
coderouter/data/model-capabilities.yaml,sha256=S9jt6SC6-3s2-icZ_n-a14iEMnc2yB1C2R6q-N_tZWQ,19309
|
|
31
31
|
coderouter/guards/__init__.py,sha256=5qliYBqygvVPneej7nx0uSjxDKsz7t8VzvrDgVBJlvU,1170
|
|
@@ -41,9 +41,9 @@ coderouter/guards/self_healing.py,sha256=Qene73S1kqMJn8HYzHO-WT8hhWxKbo2v_JtXtEg
|
|
|
41
41
|
coderouter/guards/tool_loop.py,sha256=EzeMcmU7BLeTW2jsRVevU81l5rhWcn1oUr7EpzgXjVM,15209
|
|
42
42
|
coderouter/ingress/__init__.py,sha256=WQsCH2CGJCAhy0mS6GSEdeYZRkkQu2OHDsP4CJWTLug,155
|
|
43
43
|
coderouter/ingress/anthropic_routes.py,sha256=1ThXzx8VgxRV7UCi3NF5ey7kbr0ADPKTXyQuYS5gK1w,27063
|
|
44
|
-
coderouter/ingress/app.py,sha256=
|
|
44
|
+
coderouter/ingress/app.py,sha256=6e_qNlYZIyjLKhY20t0ZNL-x5Fv8R8oaIH_HCdxa9Ic,20921
|
|
45
45
|
coderouter/ingress/dashboard_routes.py,sha256=1SqNNrVTfQiZcx5n2MvjAzWirittX0e61tCt-MKp4Ag,25617
|
|
46
|
-
coderouter/ingress/launcher_routes.py,sha256=
|
|
46
|
+
coderouter/ingress/launcher_routes.py,sha256=kVorBfnWL9Q0a6h-gad2YxgeoZjHFHbL8C4DcHtkigg,82031
|
|
47
47
|
coderouter/ingress/metrics_routes.py,sha256=M22dwOGn24P05Ge4W3c7d7mYytSGWjIR-pPSPOAiHJY,3965
|
|
48
48
|
coderouter/ingress/openai_routes.py,sha256=TrugsQNJfNPgKKLzfT1aXzrs4emWYGE4XebEBuaZoWk,12505
|
|
49
49
|
coderouter/metrics/__init__.py,sha256=7Es351DPS7yLM0yVF_F0eesmiD83n7Zzhie44chht38,1465
|
|
@@ -58,7 +58,7 @@ coderouter/routing/adaptive.py,sha256=G2o377twGSjbUh65wiIFx6klnpFGjsD_nI3oDvcBwh
|
|
|
58
58
|
coderouter/routing/auto_router.py,sha256=y4v0c8u5F9f98Vmhx1vRcKPiOgAvpzbFqr6TIh058h0,13341
|
|
59
59
|
coderouter/routing/budget.py,sha256=PblmVKJGs_BwNa9uDHAA8hmZ4XIVKv38mHAeU0V3OMs,8451
|
|
60
60
|
coderouter/routing/capability.py,sha256=rRQhzTgQYFTFDNYcSLKvq3xjdw6B1w7T-3jS7PTMI14,27864
|
|
61
|
-
coderouter/routing/fallback.py,sha256=
|
|
61
|
+
coderouter/routing/fallback.py,sha256=DDH3kvnWqXOD28ENWi7kgpaBQOJo6_LRIj1a43ZWXP0,155655
|
|
62
62
|
coderouter/state/__init__.py,sha256=XoGcPmmBQSiZWML2S0juSveQ78xfhtdeCliNnVyzu7E,1088
|
|
63
63
|
coderouter/state/audit_log.py,sha256=n7vuDsTfd5iuNUJhlRQPakJ2tMSsT9EfgPCzs1GEaac,10941
|
|
64
64
|
coderouter/state/replay.py,sha256=Z_YHKroTKZdrL8qObFxcoLOAQWWXZvXFdLfxzvBhEJg,11230
|
|
@@ -69,8 +69,8 @@ coderouter/translation/__init__.py,sha256=PYXN7XVEwpG1uC8RLy6fvnGbzEZhhrEuUapH8I
|
|
|
69
69
|
coderouter/translation/anthropic.py,sha256=aZkcYH4x82b0x7efJgJb9RWn9Hbyc9pEOthXe4vjUdU,11113
|
|
70
70
|
coderouter/translation/convert.py,sha256=VV4kpu4dvLmGBmJfQnCLY5ryy_AnQcV2NuwjqRtaR8Q,52633
|
|
71
71
|
coderouter/translation/tool_repair.py,sha256=DGOKArM53yfVbIfiAveoul1vGq9NEKmjagf43Qh4IkM,57630
|
|
72
|
-
coderouter_cli-2.
|
|
73
|
-
coderouter_cli-2.
|
|
74
|
-
coderouter_cli-2.
|
|
75
|
-
coderouter_cli-2.
|
|
76
|
-
coderouter_cli-2.
|
|
72
|
+
coderouter_cli-2.9.1.dist-info/METADATA,sha256=yfgVyK-eoyV3fde8w8Ngm9TvXw3h61CI3epBZOggdRI,15994
|
|
73
|
+
coderouter_cli-2.9.1.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
74
|
+
coderouter_cli-2.9.1.dist-info/entry_points.txt,sha256=-dnLfD1YZ2WjH2zSdNCvlO65wYltM9bsHt9Fhg3yGss,51
|
|
75
|
+
coderouter_cli-2.9.1.dist-info/licenses/LICENSE,sha256=wkEzoR86jFw33jvfOHjULqmkGEfxTFMgMaJnpR8mPRw,1065
|
|
76
|
+
coderouter_cli-2.9.1.dist-info/RECORD,,
|