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/doctor.py
CHANGED
|
@@ -1834,6 +1834,56 @@ async def _probe_cache(
|
|
|
1834
1834
|
)
|
|
1835
1835
|
|
|
1836
1836
|
|
|
1837
|
+
# ---------------------------------------------------------------------------
|
|
1838
|
+
# Phase 2c migration guard (docs/designs/agent-cli-plugin-extraction.md §5.2,
|
|
1839
|
+
# §7 "2c" row): a config-level pre-check, not an HTTP probe. Core removed
|
|
1840
|
+
# its in-core agent_cli adapter in Phase 2c — kind="agent_cli" now resolves
|
|
1841
|
+
# ONLY via the coderouter-plugin-agents adapter plugin
|
|
1842
|
+
# (coderouter/adapters/registry.py's build_adapter). A provider that still
|
|
1843
|
+
# declares kind="agent_cli" without the plugin enabled will fail at engine
|
|
1844
|
+
# startup with a ValueError; surfacing it here lets `coderouter doctor`
|
|
1845
|
+
# catch it ahead of `coderouter serve`, with a copy-paste-able fix.
|
|
1846
|
+
# ---------------------------------------------------------------------------
|
|
1847
|
+
|
|
1848
|
+
|
|
1849
|
+
def _check_agent_cli_plugin_migration(
|
|
1850
|
+
config: CodeRouterConfig, provider: ProviderConfig
|
|
1851
|
+
) -> ProbeResult | None:
|
|
1852
|
+
"""Warn when ``provider`` needs the agent_cli plugin but it isn't enabled.
|
|
1853
|
+
|
|
1854
|
+
Returns ``None`` (no finding) when ``provider.kind`` isn't
|
|
1855
|
+
``"agent_cli"``, or when ``"agents"`` is already listed in
|
|
1856
|
+
``plugins.enabled`` — i.e. nothing actionable to report.
|
|
1857
|
+
"""
|
|
1858
|
+
if provider.kind != "agent_cli":
|
|
1859
|
+
return None
|
|
1860
|
+
enabled = config.plugins.enabled if config.plugins is not None else []
|
|
1861
|
+
if "agents" in enabled:
|
|
1862
|
+
return None
|
|
1863
|
+
return ProbeResult(
|
|
1864
|
+
name="agent-cli-plugin-migration",
|
|
1865
|
+
verdict=ProbeVerdict.NEEDS_TUNING,
|
|
1866
|
+
detail=(
|
|
1867
|
+
f"provider {provider.name!r} declares kind='agent_cli', but the "
|
|
1868
|
+
"coderouter-plugin-agents adapter plugin is not enabled. As of "
|
|
1869
|
+
"v2.9.0 (Phase 2c), Core no longer ships an in-core agent_cli "
|
|
1870
|
+
"adapter — this provider cannot serve requests until the "
|
|
1871
|
+
"plugin is installed and listed in plugins.enabled."
|
|
1872
|
+
),
|
|
1873
|
+
target_file="providers.yaml",
|
|
1874
|
+
suggested_patch=(
|
|
1875
|
+
"# install the plugin:\n"
|
|
1876
|
+
'pip install "coderouter-plugin-agents @ '
|
|
1877
|
+
'git+https://github.com/zephel01/coderouter-plugin-agents"\n'
|
|
1878
|
+
"\n"
|
|
1879
|
+
"# providers.yaml — enable it (merge into any existing list):\n"
|
|
1880
|
+
"plugins:\n"
|
|
1881
|
+
" enabled:\n"
|
|
1882
|
+
" - agents\n"
|
|
1883
|
+
),
|
|
1884
|
+
)
|
|
1885
|
+
|
|
1886
|
+
|
|
1837
1887
|
# ---------------------------------------------------------------------------
|
|
1838
1888
|
# Orchestration
|
|
1839
1889
|
# ---------------------------------------------------------------------------
|
|
@@ -1872,6 +1922,14 @@ async def check_model(
|
|
|
1872
1922
|
resolved_caps=resolved,
|
|
1873
1923
|
)
|
|
1874
1924
|
|
|
1925
|
+
migration = _check_agent_cli_plugin_migration(config, provider)
|
|
1926
|
+
if migration is not None:
|
|
1927
|
+
# Nothing to probe over HTTP — the provider has no adapter until
|
|
1928
|
+
# the plugin is enabled. Report the finding and stop here rather
|
|
1929
|
+
# than sending HTTP probes at a (kind-wise meaningless) base_url.
|
|
1930
|
+
report.results.append(migration)
|
|
1931
|
+
return report
|
|
1932
|
+
|
|
1875
1933
|
auth_result = await _probe_auth_and_basic_chat(provider)
|
|
1876
1934
|
report.results.append(auth_result)
|
|
1877
1935
|
|
coderouter/ingress/app.py
CHANGED
|
@@ -305,6 +305,13 @@ def create_app(config_path: str | None = None) -> FastAPI:
|
|
|
305
305
|
},
|
|
306
306
|
)
|
|
307
307
|
|
|
308
|
+
# launcher-model-swap.md §6.6 known-trap #9: the TTL sweeper is a
|
|
309
|
+
# background task like continuous-probe above — start it once the
|
|
310
|
+
# event loop is actually running, not at app-construction time.
|
|
311
|
+
swap_manager = getattr(app.state, "swap", None)
|
|
312
|
+
if swap_manager is not None:
|
|
313
|
+
await swap_manager.start()
|
|
314
|
+
|
|
308
315
|
yield
|
|
309
316
|
|
|
310
317
|
# Graceful shutdown of probe task
|
|
@@ -313,6 +320,14 @@ def create_app(config_path: str | None = None) -> FastAPI:
|
|
|
313
320
|
with contextlib.suppress(Exception):
|
|
314
321
|
await probe_task
|
|
315
322
|
|
|
323
|
+
# Cancel the swap TTL sweeper *before* shutdown_launcher tears down
|
|
324
|
+
# every ManagedProcess below — shutdown_launcher already stops
|
|
325
|
+
# swap-spawned processes too (they're in the same registry), so
|
|
326
|
+
# this only needs to stop the sweeper loop itself, not race it.
|
|
327
|
+
if swap_manager is not None:
|
|
328
|
+
with contextlib.suppress(Exception):
|
|
329
|
+
await swap_manager.stop()
|
|
330
|
+
|
|
316
331
|
# Launcher: stop child llama.cpp / vllm processes so they don't orphan.
|
|
317
332
|
from coderouter.ingress.launcher_routes import shutdown_launcher
|
|
318
333
|
|
|
@@ -371,6 +386,20 @@ def create_app(config_path: str | None = None) -> FastAPI:
|
|
|
371
386
|
app.state.engine = engine
|
|
372
387
|
app.state.config = config
|
|
373
388
|
|
|
389
|
+
# Phase 1 on-demand model swap (docs/designs/launcher-model-swap.md).
|
|
390
|
+
# None (default) leaves the engine's swap dispatch hooks as cheap
|
|
391
|
+
# no-ops — zero behavior change for deployments that don't opt in.
|
|
392
|
+
# Constructed here (needs the real ``app`` for app.state.launcher /
|
|
393
|
+
# app.state.engine) but the TTL sweeper task itself is only started
|
|
394
|
+
# from the lifespan below, once the event loop is actually running.
|
|
395
|
+
swap_cfg = config.launcher.swap if config.launcher is not None else None
|
|
396
|
+
if swap_cfg is not None and swap_cfg.enabled:
|
|
397
|
+
from coderouter.launcher_swap import SwapManager
|
|
398
|
+
|
|
399
|
+
swap_manager = SwapManager(app, swap_cfg, config.launcher)
|
|
400
|
+
app.state.swap = swap_manager
|
|
401
|
+
engine.attach_swap_manager(swap_manager)
|
|
402
|
+
|
|
374
403
|
# H8: DNS-rebinding protection. Applied to every route so a hostname an
|
|
375
404
|
# attacker controls cannot be pointed at loopback and used to drive the
|
|
376
405
|
# local API from a victim's browser. Extra hostnames for deliberate
|