coderouter-cli 2.9.2__tar.gz → 2.9.4__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.
Files changed (279) hide show
  1. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/.gitignore +1 -0
  2. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/CHANGELOG.md +181 -0
  3. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/PKG-INFO +2 -2
  4. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/config/schemas.py +91 -6
  5. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/ingress/launcher_routes.py +856 -6
  6. coderouter_cli-2.9.4/coderouter/launcher_devices.py +518 -0
  7. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/launcher_swap.py +100 -12
  8. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/translation/anthropic.py +26 -5
  9. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/backends/launcher-quickstart.en.md +2 -0
  10. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/backends/launcher-quickstart.md +2 -0
  11. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/backends/launcher.en.md +165 -2
  12. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/backends/launcher.md +164 -2
  13. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/designs/orchestration-companion.md +8 -7
  14. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/openrouter-roster/CHANGES.md +10 -0
  15. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/openrouter-roster/latest.json +6 -6
  16. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/examples/providers.llamacpp-vllm.yaml +38 -0
  17. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/pyproject.toml +2 -2
  18. coderouter_cli-2.9.4/tests/test_launcher_config_bench.py +160 -0
  19. coderouter_cli-2.9.4/tests/test_launcher_devices.py +538 -0
  20. coderouter_cli-2.9.4/tests/test_launcher_devices_detect.py +196 -0
  21. coderouter_cli-2.9.4/tests/test_launcher_devices_routes.py +633 -0
  22. coderouter_cli-2.9.4/tests/test_launcher_gui_devices.py +492 -0
  23. coderouter_cli-2.9.4/tests/test_launcher_gui_readiness.py +659 -0
  24. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_launcher_swap.py +277 -8
  25. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_launcher_swap_review.py +4 -2
  26. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_role_normalization.py +50 -13
  27. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/LICENSE +0 -0
  28. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/README.en.md +0 -0
  29. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/README.md +0 -0
  30. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/__init__.py +0 -0
  31. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/__main__.py +0 -0
  32. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/adapters/__init__.py +0 -0
  33. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/adapters/anthropic_native.py +0 -0
  34. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/adapters/base.py +0 -0
  35. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/adapters/openai_compat.py +0 -0
  36. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/adapters/registry.py +0 -0
  37. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/cli.py +0 -0
  38. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/cli_stats.py +0 -0
  39. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/config/__init__.py +0 -0
  40. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/config/capability_registry.py +0 -0
  41. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/config/env_file.py +0 -0
  42. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/config/loader.py +0 -0
  43. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/cost.py +0 -0
  44. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/data/__init__.py +0 -0
  45. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/data/model-capabilities.yaml +0 -0
  46. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/doctor.py +0 -0
  47. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/doctor_apply.py +0 -0
  48. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/env_security.py +0 -0
  49. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/errors.py +0 -0
  50. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/gguf_introspect.py +0 -0
  51. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/guards/__init__.py +0 -0
  52. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/guards/_fingerprint.py +0 -0
  53. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/guards/backend_health.py +0 -0
  54. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/guards/context_budget.py +0 -0
  55. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/guards/continuous_probe.py +0 -0
  56. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/guards/drift_actions.py +0 -0
  57. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/guards/drift_detection.py +0 -0
  58. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/guards/memory_budget.py +0 -0
  59. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/guards/memory_pressure.py +0 -0
  60. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/guards/self_healing.py +0 -0
  61. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/guards/tool_loop.py +0 -0
  62. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/hardware.py +0 -0
  63. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/ingress/__init__.py +0 -0
  64. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/ingress/anthropic_routes.py +0 -0
  65. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/ingress/app.py +0 -0
  66. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/ingress/dashboard_routes.py +0 -0
  67. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/ingress/metrics_routes.py +0 -0
  68. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/ingress/openai_routes.py +0 -0
  69. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/language_tax.py +0 -0
  70. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/launcher_speculative.py +0 -0
  71. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/logging.py +0 -0
  72. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/metrics/__init__.py +0 -0
  73. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/metrics/collector.py +0 -0
  74. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/metrics/prometheus.py +0 -0
  75. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/output_filters.py +0 -0
  76. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/plugins/__init__.py +0 -0
  77. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/plugins/base.py +0 -0
  78. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/plugins/loader.py +0 -0
  79. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/plugins/registry.py +0 -0
  80. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/routing/__init__.py +0 -0
  81. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/routing/adaptive.py +0 -0
  82. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/routing/auto_router.py +0 -0
  83. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/routing/budget.py +0 -0
  84. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/routing/capability.py +0 -0
  85. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/routing/fallback.py +0 -0
  86. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/state/__init__.py +0 -0
  87. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/state/audit_log.py +0 -0
  88. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/state/replay.py +0 -0
  89. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/state/request_log.py +0 -0
  90. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/state/store.py +0 -0
  91. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/state/suggest_rules.py +0 -0
  92. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/token_estimation.py +0 -0
  93. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/token_estimation_accurate.py +0 -0
  94. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/translation/__init__.py +0 -0
  95. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/translation/convert.py +0 -0
  96. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/coderouter/translation/tool_repair.py +0 -0
  97. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/README.en.md +0 -0
  98. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/README.md +0 -0
  99. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/assets/dashboard-demo.png +0 -0
  100. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/backends/claude-code-llamacpp-vllm.en.md +0 -0
  101. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/backends/claude-code-llamacpp-vllm.md +0 -0
  102. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/backends/external-agents.en.md +0 -0
  103. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/backends/external-agents.md +0 -0
  104. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/backends/gguf_dl.en.md +0 -0
  105. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/backends/gguf_dl.md +0 -0
  106. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/backends/hf-ollama-models.en.md +0 -0
  107. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/backends/hf-ollama-models.md +0 -0
  108. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/backends/install-backends.en.md +0 -0
  109. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/backends/install-backends.md +0 -0
  110. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/backends/llamacpp-direct.en.md +0 -0
  111. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/backends/llamacpp-direct.md +0 -0
  112. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/backends/lmstudio-direct.en.md +0 -0
  113. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/backends/lmstudio-direct.md +0 -0
  114. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/backends/verify-ollama-0.23.1.md +0 -0
  115. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/concepts/architecture.en.md +0 -0
  116. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/concepts/architecture.md +0 -0
  117. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/concepts/context-budget.en.md +0 -0
  118. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/concepts/context-budget.md +0 -0
  119. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/concepts/continuous-probing.en.md +0 -0
  120. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/concepts/continuous-probing.md +0 -0
  121. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/concepts/drift-detection.en.md +0 -0
  122. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/concepts/drift-detection.md +0 -0
  123. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/concepts/partial-stitch.en.md +0 -0
  124. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/concepts/partial-stitch.md +0 -0
  125. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/designs/agent-cli-plugin-extraction.md +0 -0
  126. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/designs/external-agents-adapter.md +0 -0
  127. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/designs/launcher-model-swap.md +0 -0
  128. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/designs/v1.5-dashboard-mockup.html +0 -0
  129. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/designs/v1.6-auto-router-verification.md +0 -0
  130. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/designs/v1.6-auto-router.md +0 -0
  131. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/guides/free-tier-guide.en.md +0 -0
  132. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/guides/free-tier-guide.md +0 -0
  133. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/guides/language-tax.en.md +0 -0
  134. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/guides/language-tax.md +0 -0
  135. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/guides/remote-access.en.md +0 -0
  136. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/guides/remote-access.md +0 -0
  137. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/guides/security.en.md +0 -0
  138. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/guides/security.md +0 -0
  139. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/guides/subagent-routing.en.md +0 -0
  140. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/guides/subagent-routing.md +0 -0
  141. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/guides/troubleshooting.en.md +0 -0
  142. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/guides/troubleshooting.md +0 -0
  143. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/guides/usage-guide.en.md +0 -0
  144. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/guides/usage-guide.md +0 -0
  145. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/low-memory-integration.en.md +0 -0
  146. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/low-memory-integration.md +0 -0
  147. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/openrouter-roster/README.md +0 -0
  148. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/retrospectives/v0.4.md +0 -0
  149. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/retrospectives/v0.5-verify.md +0 -0
  150. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/retrospectives/v0.5.md +0 -0
  151. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/retrospectives/v0.6.md +0 -0
  152. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/retrospectives/v0.7.md +0 -0
  153. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/retrospectives/v1.0-verify.md +0 -0
  154. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/retrospectives/v1.0.md +0 -0
  155. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/start/quickstart.en.md +0 -0
  156. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/start/quickstart.md +0 -0
  157. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/start/when-do-i-need-coderouter.en.md +0 -0
  158. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/docs/start/when-do-i-need-coderouter.md +0 -0
  159. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/examples/.env.example +0 -0
  160. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/examples/README.md +0 -0
  161. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/examples/providers-agent-cli.yaml +0 -0
  162. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/examples/providers-multiagent.yaml +0 -0
  163. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/examples/providers.context-budget-test.yaml +0 -0
  164. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/examples/providers.nvidia-nim.yaml +0 -0
  165. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/examples/providers.ollama-auto-custom.yaml +0 -0
  166. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/examples/providers.ollama-auto.yaml +0 -0
  167. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/examples/providers.ollama-free-chain.yaml +0 -0
  168. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/examples/providers.raspberrypi.yaml +0 -0
  169. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/examples/providers.swap.yaml +0 -0
  170. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/examples/providers.yaml +0 -0
  171. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/scripts/demo_traffic.sh +0 -0
  172. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/scripts/openrouter_roster_diff.py +0 -0
  173. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/scripts/smoke_v2_2.sh +0 -0
  174. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/scripts/verify-providers.yaml +0 -0
  175. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/scripts/verify_ollama_0_23.py +0 -0
  176. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/scripts/verify_v0_5.sh +0 -0
  177. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/scripts/verify_v1_0.sh +0 -0
  178. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/__init__.py +0 -0
  179. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/conftest.py +0 -0
  180. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_adapter_anthropic.py +0 -0
  181. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_agent_cli.py +0 -0
  182. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_audit_log.py +0 -0
  183. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_auto_router.py +0 -0
  184. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_auto_router_cjk.py +0 -0
  185. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_backend_health.py +0 -0
  186. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_budget.py +0 -0
  187. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_capability.py +0 -0
  188. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_capability_degraded_payload.py +0 -0
  189. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_capability_registry.py +0 -0
  190. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_capability_registry_cache_control.py +0 -0
  191. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_capability_tool_choice.py +0 -0
  192. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_claude_code_suitability.py +0 -0
  193. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_cli.py +0 -0
  194. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_cli_bind_warning.py +0 -0
  195. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_cli_stats.py +0 -0
  196. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_config.py +0 -0
  197. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_config_shim_actions.py +0 -0
  198. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_context_budget.py +0 -0
  199. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_continuous_probe.py +0 -0
  200. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_dashboard_endpoint.py +0 -0
  201. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_doctor.py +0 -0
  202. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_doctor_apply.py +0 -0
  203. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_doctor_cache_probe.py +0 -0
  204. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_drift_actions.py +0 -0
  205. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_drift_detection.py +0 -0
  206. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_drift_detection_integration.py +0 -0
  207. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_env_file.py +0 -0
  208. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_env_security.py +0 -0
  209. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_errors.py +0 -0
  210. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_examples_yaml.py +0 -0
  211. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fallback.py +0 -0
  212. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fallback_anthropic.py +0 -0
  213. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fallback_cache_control.py +0 -0
  214. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fallback_cache_observed.py +0 -0
  215. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fallback_cache_strip.py +0 -0
  216. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fallback_empty_response.py +0 -0
  217. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fallback_misconfig_warn.py +0 -0
  218. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fallback_paid_gate.py +0 -0
  219. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fallback_thinking.py +0 -0
  220. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fallback_tool_choice_emulate.py +0 -0
  221. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fix_h1_prometheus_drift.py +0 -0
  222. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fix_h2_h6_translation.py +0 -0
  223. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fix_h3_client_reuse.py +0 -0
  224. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fix_h4_h5_guards.py +0 -0
  225. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fix_h7_probe_restore.py +0 -0
  226. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fix_h8_launcher_auth.py +0 -0
  227. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fix_mA_routing.py +0 -0
  228. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fix_mB_adapters.py +0 -0
  229. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fix_mC_convert.py +0 -0
  230. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fix_mD_logging_metrics.py +0 -0
  231. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fix_mE_config_validation.py +0 -0
  232. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_fix_mF_ingress.py +0 -0
  233. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_gguf_introspect.py +0 -0
  234. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_guards_tool_loop.py +0 -0
  235. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_hardware.py +0 -0
  236. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_ingress_anthropic.py +0 -0
  237. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_ingress_count_tokens.py +0 -0
  238. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_ingress_profile.py +0 -0
  239. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_language_tax.py +0 -0
  240. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_language_tax_integration.py +0 -0
  241. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_launcher_mtp.py +0 -0
  242. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_launcher_mtp_fallback.py +0 -0
  243. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_launcher_provider_sync.py +0 -0
  244. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_launcher_readiness_restart.py +0 -0
  245. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_memory_budget.py +0 -0
  246. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_memory_pressure.py +0 -0
  247. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_metrics_cache.py +0 -0
  248. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_metrics_collector.py +0 -0
  249. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_metrics_cost.py +0 -0
  250. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_metrics_endpoint.py +0 -0
  251. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_metrics_jsonl.py +0 -0
  252. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_metrics_prometheus.py +0 -0
  253. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_metrics_prometheus_cache.py +0 -0
  254. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_models_passthrough.py +0 -0
  255. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_openai_compat.py +0 -0
  256. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_openrouter_roster_diff.py +0 -0
  257. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_output_filters.py +0 -0
  258. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_output_filters_adapters.py +0 -0
  259. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_partial_stitch.py +0 -0
  260. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_plugin_adapter.py +0 -0
  261. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_plugins_integration.py +0 -0
  262. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_plugins_loader.py +0 -0
  263. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_plugins_registry.py +0 -0
  264. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_reasoning_strip.py +0 -0
  265. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_repair_byte_fallback.py +0 -0
  266. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_request_log.py +0 -0
  267. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_routing_adaptive.py +0 -0
  268. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_self_healing.py +0 -0
  269. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_setup_sh.py +0 -0
  270. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_state_store.py +0 -0
  271. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_token_estimation.py +0 -0
  272. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_token_estimation_accurate.py +0 -0
  273. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_tokens_saved_metric.py +0 -0
  274. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_tool_repair.py +0 -0
  275. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_tool_repair_lenient.py +0 -0
  276. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_tool_repair_r4.py +0 -0
  277. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_toolrepair_bench.py +0 -0
  278. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_translation_anthropic.py +0 -0
  279. {coderouter_cli-2.9.2 → coderouter_cli-2.9.4}/tests/test_translation_reverse.py +0 -0
@@ -7,6 +7,7 @@
7
7
  !.env.example
8
8
  secrets.env
9
9
  ~/.coderouter/
10
+ CLAUDE.md
10
11
 
11
12
  # ============================================================
12
13
  # Python
@@ -9,6 +9,187 @@ are kept verbatim where the Japanese text itself is the subject).
9
9
 
10
10
  ---
11
11
 
12
+ ## [v2.9.4] — 2026-07-19 (launcher device selection & bench sweep, cache-stable system prompt)
13
+
14
+ ### Added
15
+
16
+ - **llama.cpp device selection (`--device` / `--tensor-split`) in the
17
+ Launcher.** Both the desktop GUI and the Web edition can now detect
18
+ available `llama-server` devices (via `--list-devices`), display their
19
+ VRAM, and let the operator pick which one(s) to offload to instead of
20
+ always relying on llama.cpp's own default placement. New shared module
21
+ `coderouter/launcher_devices.py` (dataclasses + pure functions only, no
22
+ pydantic, so the standalone GUI can import it without the `coderouter`
23
+ package) owns detection/caching (`detect_llama_devices`,
24
+ `parse_list_devices`, a 60s TTL cache keyed by binary path),
25
+ CLI-fragment building (`DeviceSelection.to_cli_args` — empty selection
26
+ emits nothing, preserving today's launch command byte-for-byte),
27
+ VRAM-ratio tensor-split suggestion (`suggest_tensor_split`), and backend
28
+ grouping (`backend_of` / `group_by_backend` / `selectable_devices`) so a
29
+ GPU listed twice under different backends (e.g. `CUDA0` and `Vulkan1`
30
+ for the same physical card) is never double-counted and cross-backend
31
+ configs are never auto-generated. Devices reporting `0 MiB` (e.g. macOS
32
+ `BLAS: Accelerate`) are still shown for information but excluded from
33
+ selection/suggestion. `StartRequest` gained `device_ids` / `tensor_split`
34
+ (both default empty — existing Web clients are unaffected), and
35
+ `GET /api/launcher/devices` (`?backend=`, `?refresh=1`) returns the
36
+ probe plus a per-backend `suggested_tensor_split` and `auto_configs`.
37
+ `launcher_gui.py`'s LAUNCH form gained a device checklist (🔍 Detect,
38
+ falling back to a manual comma-separated entry on detection failure)
39
+ and a tensor-split field that auto-hides when one or fewer devices are
40
+ detected. See `tests/test_launcher_devices.py`,
41
+ `tests/test_launcher_devices_detect.py`,
42
+ `tests/test_launcher_devices_routes.py`,
43
+ `tests/test_launcher_gui_devices.py`, and the new "Device selection"
44
+ section in `docs/backends/launcher.md` / `launcher.en.md`.
45
+ - **Bench sweep: automated device-configuration benchmarking against an
46
+ external `llmbench`.** Both Launcher editions can now drive a list of
47
+ device configurations (e.g. `CUDA0` alone / `CUDA1` alone / a
48
+ multi-GPU split) through start → wait for readiness → run an external
49
+ bench command → stop → advance to the next configuration, then compare
50
+ the results. `coderouter/launcher_devices.py` supplies the shared
51
+ `SweepPlan` / `SweepStep` / `SweepState` state machine,
52
+ `build_auto_sweep_configs` (one config per selectable device plus one
53
+ per multi-device backend group), `render_bench_command` (template
54
+ placeholders `{port}` `{config}` `{base_url}` `{results_dir}` `{runs}`,
55
+ expanded via plain string substitution rather than `str.format` so JSON
56
+ braces in the command aren't misinterpreted, then `shlex`-split with
57
+ `posix=False` on Windows to keep backslash paths intact), and
58
+ `load_latest_results` / `summarize_results` (best-effort, alias-tolerant
59
+ extraction of `tokens_per_sec` / `ttft_ms` / `latency_ms` from the
60
+ newest `llmbench` results JSON). New `LauncherBenchConfig`
61
+ (`command_template` / `runs` / `results_dir` / `readiness_timeout_s`,
62
+ read from an optional `launcher.bench:` block — omitted entirely by
63
+ default, so existing `providers.yaml` files are unaffected) supplies
64
+ the sweep's defaults. The Web edition gained
65
+ `POST /api/launcher/sweep/start` (token-gated; 409 if a sweep is
66
+ already running, 400 for empty `configs` or a busy/unavailable port),
67
+ `GET /api/launcher/sweep/status`, `POST /api/launcher/sweep/abort`
68
+ (token-gated), and `GET /api/launcher/sweep/logs`, backed by a new
69
+ `_SweepRunner` that reuses the existing `spawn_process` /
70
+ `stop_process` / `proc.ready` readiness primitives. `launcher_gui.py`
71
+ gained a "📊 Bench sweep" button opening a separate `SweepWindow` with
72
+ its own `_SweepWorker` thread, reusing `poll_until_ready` for readiness
73
+ and driving the same external bench subprocess model. See
74
+ `tests/test_launcher_config_bench.py` and the new "Bench sweep" section
75
+ in `docs/backends/launcher.md` / `launcher.en.md`.
76
+
77
+ ### Fixed
78
+
79
+ - **Mid-conversation `role:"system"` messages are no longer hoisted into
80
+ the top-level `system` field.** (#75, contributed by @firelzrd.)
81
+ Claude Code CLI ≥ 2.1.154 emits volatile system reminders as
82
+ `role:"system"` messages *inside* `messages[]`; hoisting them meant the
83
+ serialized prompt's head changed every turn, so llama.cpp / LM Studio
84
+ style prefix-matching KV caches were invalidated wholesale (a captured
85
+ real session measured only 11.8 % reusable prefix between consecutive
86
+ requests). `normalize_message_roles()` now splits by position: leading
87
+ system messages (before any emitted turn) are hoisted as before — the
88
+ legitimate OpenAI-style system prompt — while mid-conversation ones are
89
+ coerced to `user` in place, keeping `messages` append-only and the
90
+ prompt head stable as the conversation grows. See
91
+ `tests/test_role_normalization.py`
92
+ (`test_leading_system_is_hoisted_but_mid_conversation_is_not`,
93
+ `test_system_prompt_is_stable_as_conversation_grows`).
94
+
95
+ ### Security
96
+
97
+ - **Dependency bumps for two fresh advisories** (lockfile-only; both
98
+ verified with a clean `uv sync --frozen` + full test suite, 1905
99
+ passed): `click` 8.3.2 → 8.4.2 (PYSEC-2026-2132, fix ≥ 8.3.3;
100
+ transitive via uvicorn) and `json-repair` 0.59.10 → 0.61.5
101
+ (GHSA-xf7x-x43h-rpqh, CVSS 7.5, fix ≥ 0.60.1). The `json-repair`
102
+ floor in `pyproject.toml` was raised from `>=0.30` to `>=0.60.1` so
103
+ fresh installs can never resolve a vulnerable version; `uv.lock` also
104
+ records the new lockfile revision 3 format (upload-time metadata)
105
+ emitted by current uv.
106
+
107
+ ---
108
+
109
+ ## [v2.9.3] — 2026-07-12 (GUI/Web parity, swap polish, launcher UI fixes)
110
+
111
+ ### Added
112
+
113
+ - **Per-model TTL override for `launcher.swap`.** `SwapModelSpec.ttl_seconds`
114
+ (new, optional, `None` by default) lets a catalog entry override the
115
+ global `launcher.swap.ttl_seconds` for just that one model — `None`
116
+ keeps following the global value, `0` unloads the model as soon as
117
+ its last in-flight lease releases (same meaning as the global field's
118
+ `0`, just scoped to one entry). `SwapManager.sweep_once` now resolves
119
+ each model's effective TTL individually, and the sweeper starts
120
+ whenever either the global TTL or at least one catalog entry's
121
+ override is set (previously gated solely on the global value). See
122
+ `tests/test_launcher_swap.py` (`test_ttl_override_spec_wins_over_global`,
123
+ `test_ttl_override_unset_falls_back_to_global`,
124
+ `test_ttl_override_zero_unloads_even_when_global_ttl_disabled`) and the
125
+ updated `launcher.swap` field table in `docs/backends/launcher.md` /
126
+ `launcher.en.md`.
127
+ - **Configurable ephemeral-port retry count for `launcher.swap`.**
128
+ `LauncherSwapConfig.port_retry_attempts` (new, default `2`, range
129
+ 0–5) replaces the previously hard-coded single retry for catalog
130
+ entries that leave `port` unset — the swap manager now attempts up
131
+ to `1 + port_retry_attempts` spawns (each on a freshly picked
132
+ ephemeral port) before giving up, versus the previous fixed 2 total
133
+ attempts. Fixed-port entries are unaffected (still exactly one
134
+ attempt — a second try on the same port would just collide again).
135
+ This narrows the practical impact of the known pick-then-bind TOCTOU
136
+ window documented on `SwapModelSpec.port` / `_pick_ephemeral_port`
137
+ but does not close it outright; a fixed `port` remains the only way
138
+ to eliminate the race entirely. See
139
+ `tests/test_launcher_swap.py`
140
+ (`test_port_none_retries_default_port_retry_attempts`,
141
+ `test_port_retry_attempts_exhausted_raises`,
142
+ `test_port_retry_attempts_configurable_to_zero`).
143
+ - **`/launcher` UI now shows which processes are swap-managed.**
144
+ `GET /api/launcher/processes` includes `swap_managed` (bool) and
145
+ `swap_model` (the swap catalog model name, or `null`) for every
146
+ process; `spawn_process` gained a `swap_model` kwarg that
147
+ `SwapManager._spawn` now passes through. The process table renders a
148
+ small "swap" badge next to the name of any swap-managed process
149
+ (title shows the backing catalog model when known). Manually-started
150
+ processes are unaffected (`swap_managed: false`, `swap_model: null`).
151
+ See `tests/test_launcher_swap.py::test_i1_on_demand_spawn_reaches_200`.
152
+
153
+ ### Fixed
154
+
155
+ - **Swap-managed "stopped" processes no longer accumulate in the launcher
156
+ registry.** `stop_process` only sets `status="stopped"` and never
157
+ removes the entry — deliberate for manually started processes (the
158
+ stopped row is visible history in the /launcher UI, with logs and an
159
+ explicit ✕ delete button), but swap-managed processes went through the
160
+ same path, so every failed readiness attempt left one permanent
161
+ "stopped" row (`1 + port_retry_attempts` rows per failed load — 3 with
162
+ the defaults) and every TTL unload left another, all growing
163
+ `GET /api/launcher/processes` and the UI without bound. `SwapManager`
164
+ now removes its own processes from the registry after stopping them,
165
+ in both the failed-readiness cleanup (`_spawn_with_retry`) and the TTL
166
+ unload (`_unload_locked`), guarded on `ManagedProcess.swap_managed` so
167
+ a manual process is never swept up. Crash leftovers (a swap process
168
+ that died on its own) are deliberately kept — their log tail is the
169
+ only crash forensics an operator has. See `tests/test_launcher_swap.py`
170
+ (`test_failed_load_leaves_no_registry_litter`,
171
+ `test_ttl_unload_removes_registry_entry`,
172
+ `test_registry_removal_skips_non_swap_processes`; adapted from the
173
+ review repro with its assertions inverted).
174
+ - **/launcher UI no longer 404-polls a removed process's logs forever.**
175
+ The log panel's poller (`poll()` → `refreshLogs()`, every 3s) never
176
+ checked the response status: once the polled id left the registry —
177
+ a `coderouter serve` restart under a still-open browser tab, a delete
178
+ issued from another client, or (with the fix above) a swap TTL unload
179
+ removing the entry mid-view — the 404 body has no `.logs`, the
180
+ resulting TypeError landed in the generic catch, and `selectedLogId`
181
+ was never cleared, so the tab kept hitting
182
+ `GET /api/launcher/logs/<stale-id>` and spamming `404 Not Found` into
183
+ the serve log indefinitely. The UI now stops polling as soon as the
184
+ selected id disappears from the periodic `/api/launcher/processes`
185
+ refresh (usually before a single 404 is even issued), treats a logs
186
+ 404 as terminal (shows "(process removed)" instead of retrying), and
187
+ the server keeps answering 404 for unknown ids — that behavior is
188
+ correct and now pinned by
189
+ `tests/test_launcher_swap.py::test_logs_unknown_proc_id_is_404`.
190
+
191
+ ---
192
+
12
193
  ## [v2.9.2] — 2026-07-12 (config: no more dummy provider for swap-only setups)
13
194
 
14
195
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coderouter-cli
3
- Version: 2.9.2
3
+ Version: 2.9.4
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
@@ -40,7 +40,7 @@ Requires-Dist: types-pyyaml>=6.0.12; extra == 'dev'
40
40
  Provides-Extra: doctor
41
41
  Requires-Dist: ruamel-yaml>=0.18.6; extra == 'doctor'
42
42
  Provides-Extra: repair
43
- Requires-Dist: json-repair>=0.30; extra == 'repair'
43
+ Requires-Dist: json-repair>=0.60.1; extra == 'repair'
44
44
  Description-Content-Type: text/markdown
45
45
 
46
46
  <h1 align="center">CodeRouter</h1>
@@ -1460,6 +1460,49 @@ class LauncherOptionProfile(BaseModel):
1460
1460
  )
1461
1461
 
1462
1462
 
1463
+ class LauncherBenchConfig(BaseModel):
1464
+ """The ``launcher.bench:`` block — defaults for the bench sweep feature.
1465
+
1466
+ 設計 §4.1。デバイス構成スイープ(起動→readiness→外部ベンチ→停止→次)の
1467
+ 既定値を providers.yaml から供給する。未設定(``LauncherConfig.bench`` が
1468
+ None)ならスイープ UI はハードコード既定を使う(完全後方互換)。すべての
1469
+ フィールドに ``default`` があるので、既存 YAML は無改変で通過する。
1470
+ """
1471
+
1472
+ model_config = ConfigDict(extra="forbid")
1473
+
1474
+ command_template: str = Field(
1475
+ default="llmbench run --model local-openai --runs {runs}",
1476
+ description=(
1477
+ "外部ベンチコマンドのテンプレ。``{port}`` ``{config}`` "
1478
+ "``{base_url}`` ``{results_dir}`` ``{runs}`` を単純置換で展開して "
1479
+ "argv 化する(``str.format`` ではないので JSON 波括弧で誤爆しない)。"
1480
+ ),
1481
+ )
1482
+ runs: int = Field(
1483
+ default=5,
1484
+ ge=1,
1485
+ le=1000,
1486
+ description="1 構成あたりのベンチ実行回数。``{runs}`` に展開される。",
1487
+ )
1488
+ results_dir: str | None = Field(
1489
+ default=None,
1490
+ description=(
1491
+ "llmbench の results/ ディレクトリ。相対はサーバ CWD 基準。"
1492
+ "設定時のみスイープ結果 JSON を読み比較サマリを付与する。"
1493
+ ),
1494
+ )
1495
+ readiness_timeout_s: float = Field(
1496
+ default=300.0,
1497
+ ge=5.0,
1498
+ le=3600.0,
1499
+ description=(
1500
+ "スイープの各構成でサーバが ready になるのを待つ最大秒数。"
1501
+ "大きな GGUF ロードを考慮した既定 5 分。"
1502
+ ),
1503
+ )
1504
+
1505
+
1463
1506
  class SwapModelSpec(BaseModel):
1464
1507
  """One entry in ``launcher.swap.models`` (docs/designs/launcher-model-swap.md).
1465
1508
 
@@ -1518,10 +1561,25 @@ class SwapModelSpec(BaseModel):
1518
1561
  le=65535,
1519
1562
  description=(
1520
1563
  "Fixed port (recommended — §10 Q2). When unset, the swap "
1521
- "manager picks an OS-assigned ephemeral port and retries "
1522
- "once on a different port if the backend fails to become "
1523
- "ready. Best-effort only — no strong TOCTOU guarantee "
1524
- "(§6.6 known-trap #4)."
1564
+ "manager picks an OS-assigned ephemeral port and retries, "
1565
+ "each time on a freshly picked port, up to "
1566
+ "``LauncherSwapConfig.port_retry_attempts`` additional "
1567
+ "times if the backend fails to become ready. Best-effort "
1568
+ "only — no strong TOCTOU guarantee (§6.6 known-trap #4)."
1569
+ ),
1570
+ )
1571
+ ttl_seconds: float | None = Field(
1572
+ default=None,
1573
+ ge=0.0,
1574
+ description=(
1575
+ "[Unreleased] Per-model override of "
1576
+ "``LauncherSwapConfig.ttl_seconds``. ``None`` (default) = "
1577
+ "use the global value. ``0`` = unload as soon as the last "
1578
+ "in-flight lease for THIS model releases — same meaning as "
1579
+ "the global field's ``0``, just scoped to one catalog "
1580
+ "entry. Lets a large/expensive model unload sooner (or "
1581
+ "stay resident longer) than the rest of the catalog "
1582
+ "without changing the global default."
1525
1583
  ),
1526
1584
  )
1527
1585
  option_profile: str | None = Field(
@@ -1611,8 +1669,10 @@ class LauncherSwapConfig(BaseModel):
1611
1669
  "Seconds of no in-flight requests after which an idle swap "
1612
1670
  "process is auto-stopped. None = TTL disabled (runs until "
1613
1671
  "explicitly stopped). 0 = unload as soon as the last "
1614
- "in-flight lease releases. §10 Q1: one GLOBAL value for "
1615
- "Phase 1 — no per-model override yet."
1672
+ "in-flight lease releases. §10 Q1: this is the GLOBAL "
1673
+ "default; a catalog entry's ``SwapModelSpec.ttl_seconds`` "
1674
+ "overrides it for that one model when set "
1675
+ "([Unreleased] per-model TTL override)."
1616
1676
  ),
1617
1677
  )
1618
1678
  readiness_timeout_s: float = Field(
@@ -1631,6 +1691,24 @@ class LauncherSwapConfig(BaseModel):
1631
1691
  le=600.0,
1632
1692
  description="How often the TTL sweeper background task scans for idle processes.",
1633
1693
  )
1694
+ port_retry_attempts: int = Field(
1695
+ default=2,
1696
+ ge=0,
1697
+ le=5,
1698
+ description=(
1699
+ "[Unreleased] Number of ADDITIONAL spawn attempts (each on a "
1700
+ "freshly picked ephemeral port) after the first, used only "
1701
+ "when a catalog entry's ``port`` is unset and the previous "
1702
+ "attempt fails to become ready. 0 = no retry. Ignored when "
1703
+ "``port`` is set — a fixed port never retries (a second "
1704
+ "attempt would just collide on the same port again). This "
1705
+ "does not close the pick-then-bind TOCTOU window (see "
1706
+ "``coderouter.launcher_swap._pick_ephemeral_port``); it only "
1707
+ "bounds how many times the swap manager re-rolls the dice. "
1708
+ "A fixed ``port`` remains the recommended way to eliminate "
1709
+ "the race entirely (§10 Q2)."
1710
+ ),
1711
+ )
1634
1712
  inject_auto_router_rules: bool = Field(
1635
1713
  default=True,
1636
1714
  description=(
@@ -1828,6 +1906,13 @@ class LauncherConfig(BaseModel):
1828
1906
  "behavior."
1829
1907
  ),
1830
1908
  )
1909
+ bench: LauncherBenchConfig | None = Field(
1910
+ default=None,
1911
+ description=(
1912
+ "デバイス構成ベンチスイープ(設計 §4)の既定値。None(既定)なら "
1913
+ "スイープ UI はハードコード既定を使う=完全後方互換。"
1914
+ ),
1915
+ )
1831
1916
 
1832
1917
  @model_validator(mode="after")
1833
1918
  def _check_auto_restart_backoff_ordered(self) -> LauncherConfig: