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.
@@ -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
- Walks the chain in order, returning the first provider's response.
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
- Important: once we begin yielding chunks from an adapter, we cannot
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
- prepared = self._take_prepared_dispatch(request)
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
- For non-native providers with tools declared, we use the v0.3-D
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
- prepared = self._take_prepared_dispatch(request)
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.8.1
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 / gemini-cli / codex)
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 / gemini-cli + Ollama 直繋ぎで動いてる | オプション — フォールバックが欲しいなら |
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=2luNk6BHSRvpQStJnHcqzNvNi-SKdOuKV0WZdorZhVk,82854
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=hwMhdBg-wurjJKa4MJIuDoMn1ClOOsorGppJ-s5pnF0,4311
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=AJIJ-zrEgI5czTlcD6YxWQe9rnJFsr6AcKSwwlCaLk8,89447
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=h6s-UWsbGP22rku4pPVlvRdrtbMj4KoEEUZoQQOrTTw,19370
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=8EY3a6O_vbA45h2uhp_wc37bP_DJFqVVjsd68d_bwtk,66179
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=x3Y_LHA1NZhW4L1e7E0okuYpQn6-HNw5An7f7HX547M,145111
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.8.1.dist-info/METADATA,sha256=qRLxuyz0gjzAwHy4eSWMXl_E23g7fwG6AYuVonimdlg,15546
73
- coderouter_cli-2.8.1.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
74
- coderouter_cli-2.8.1.dist-info/entry_points.txt,sha256=-dnLfD1YZ2WjH2zSdNCvlO65wYltM9bsHt9Fhg3yGss,51
75
- coderouter_cli-2.8.1.dist-info/licenses/LICENSE,sha256=wkEzoR86jFw33jvfOHjULqmkGEfxTFMgMaJnpR8mPRw,1065
76
- coderouter_cli-2.8.1.dist-info/RECORD,,
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,,