coderouter-cli 2.10.0__tar.gz → 2.11.0__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 (288) hide show
  1. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/CHANGELOG.md +103 -0
  2. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/PKG-INFO +2 -1
  3. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/README.en.md +3 -0
  4. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/README.md +1 -0
  5. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/config/schemas.py +143 -9
  6. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/ingress/launcher_routes.py +289 -22
  7. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/launcher_devices.py +188 -4
  8. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/launcher_speculative.py +5 -1
  9. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/launcher_swap.py +11 -2
  10. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/vscode_init.py +4 -2
  11. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/README.md +2 -0
  12. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/backends/launcher-quickstart.en.md +2 -0
  13. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/backends/launcher-quickstart.md +2 -0
  14. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/backends/launcher.en.md +114 -1
  15. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/backends/launcher.md +114 -1
  16. coderouter_cli-2.11.0/docs/designs/launcher-multi-build.md +464 -0
  17. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/examples/README.md +1 -1
  18. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/examples/providers.llamacpp-vllm.yaml +22 -0
  19. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/pyproject.toml +1 -1
  20. coderouter_cli-2.11.0/tests/test_launcher_backend_variants.py +403 -0
  21. coderouter_cli-2.11.0/tests/test_launcher_variant_config.py +387 -0
  22. coderouter_cli-2.11.0/tests/test_launcher_variant_gui.py +222 -0
  23. coderouter_cli-2.11.0/tests/test_launcher_variant_routes.py +411 -0
  24. coderouter_cli-2.11.0/tests/test_launcher_variant_sweep.py +270 -0
  25. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_vscode_init.py +0 -3
  26. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/.gitignore +0 -0
  27. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/LICENSE +0 -0
  28. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/__init__.py +0 -0
  29. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/__main__.py +0 -0
  30. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/adapters/__init__.py +0 -0
  31. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/adapters/anthropic_native.py +0 -0
  32. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/adapters/base.py +0 -0
  33. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/adapters/openai_compat.py +0 -0
  34. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/adapters/registry.py +0 -0
  35. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/cli.py +0 -0
  36. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/cli_stats.py +0 -0
  37. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/config/__init__.py +0 -0
  38. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/config/capability_registry.py +0 -0
  39. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/config/env_file.py +0 -0
  40. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/config/loader.py +0 -0
  41. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/cost.py +0 -0
  42. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/data/__init__.py +0 -0
  43. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/data/model-capabilities.yaml +0 -0
  44. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/doctor.py +0 -0
  45. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/doctor_apply.py +0 -0
  46. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/env_security.py +0 -0
  47. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/errors.py +0 -0
  48. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/gguf_introspect.py +0 -0
  49. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/guards/__init__.py +0 -0
  50. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/guards/_fingerprint.py +0 -0
  51. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/guards/backend_health.py +0 -0
  52. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/guards/context_budget.py +0 -0
  53. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/guards/continuous_probe.py +0 -0
  54. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/guards/drift_actions.py +0 -0
  55. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/guards/drift_detection.py +0 -0
  56. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/guards/memory_budget.py +0 -0
  57. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/guards/memory_pressure.py +0 -0
  58. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/guards/self_healing.py +0 -0
  59. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/guards/tool_loop.py +0 -0
  60. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/hardware.py +0 -0
  61. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/ingress/__init__.py +0 -0
  62. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/ingress/anthropic_routes.py +0 -0
  63. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/ingress/app.py +0 -0
  64. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/ingress/dashboard_routes.py +0 -0
  65. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/ingress/metrics_routes.py +0 -0
  66. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/ingress/openai_routes.py +0 -0
  67. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/language_tax.py +0 -0
  68. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/logging.py +0 -0
  69. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/metrics/__init__.py +0 -0
  70. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/metrics/collector.py +0 -0
  71. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/metrics/prometheus.py +0 -0
  72. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/output_filters.py +0 -0
  73. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/plugins/__init__.py +0 -0
  74. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/plugins/base.py +0 -0
  75. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/plugins/loader.py +0 -0
  76. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/plugins/registry.py +0 -0
  77. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/routing/__init__.py +0 -0
  78. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/routing/adaptive.py +0 -0
  79. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/routing/auto_router.py +0 -0
  80. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/routing/budget.py +0 -0
  81. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/routing/capability.py +0 -0
  82. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/routing/fallback.py +0 -0
  83. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/state/__init__.py +0 -0
  84. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/state/audit_log.py +0 -0
  85. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/state/replay.py +0 -0
  86. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/state/request_log.py +0 -0
  87. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/state/store.py +0 -0
  88. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/state/suggest_rules.py +0 -0
  89. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/token_estimation.py +0 -0
  90. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/token_estimation_accurate.py +0 -0
  91. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/translation/__init__.py +0 -0
  92. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/translation/anthropic.py +0 -0
  93. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/translation/convert.py +0 -0
  94. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/coderouter/translation/tool_repair.py +0 -0
  95. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/README.en.md +0 -0
  96. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/assets/dashboard-demo.png +0 -0
  97. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/backends/claude-code-llamacpp-vllm.en.md +0 -0
  98. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/backends/claude-code-llamacpp-vllm.md +0 -0
  99. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/backends/external-agents.en.md +0 -0
  100. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/backends/external-agents.md +0 -0
  101. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/backends/gguf_dl.en.md +0 -0
  102. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/backends/gguf_dl.md +0 -0
  103. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/backends/hf-ollama-models.en.md +0 -0
  104. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/backends/hf-ollama-models.md +0 -0
  105. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/backends/install-backends.en.md +0 -0
  106. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/backends/install-backends.md +0 -0
  107. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/backends/llamacpp-direct.en.md +0 -0
  108. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/backends/llamacpp-direct.md +0 -0
  109. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/backends/lmstudio-direct.en.md +0 -0
  110. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/backends/lmstudio-direct.md +0 -0
  111. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/backends/verify-ollama-0.23.1.md +0 -0
  112. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/concepts/architecture.en.md +0 -0
  113. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/concepts/architecture.md +0 -0
  114. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/concepts/context-budget.en.md +0 -0
  115. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/concepts/context-budget.md +0 -0
  116. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/concepts/continuous-probing.en.md +0 -0
  117. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/concepts/continuous-probing.md +0 -0
  118. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/concepts/drift-detection.en.md +0 -0
  119. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/concepts/drift-detection.md +0 -0
  120. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/concepts/partial-stitch.en.md +0 -0
  121. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/concepts/partial-stitch.md +0 -0
  122. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/designs/agent-cli-plugin-extraction.md +0 -0
  123. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/designs/external-agents-adapter.md +0 -0
  124. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/designs/launcher-model-swap.md +0 -0
  125. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/designs/orchestration-companion.md +0 -0
  126. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/designs/v1.5-dashboard-mockup.html +0 -0
  127. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/designs/v1.6-auto-router-verification.md +0 -0
  128. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/designs/v1.6-auto-router.md +0 -0
  129. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/guides/free-tier-guide.en.md +0 -0
  130. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/guides/free-tier-guide.md +0 -0
  131. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/guides/language-tax.en.md +0 -0
  132. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/guides/language-tax.md +0 -0
  133. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/guides/remote-access.en.md +0 -0
  134. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/guides/remote-access.md +0 -0
  135. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/guides/security.en.md +0 -0
  136. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/guides/security.md +0 -0
  137. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/guides/subagent-routing.en.md +0 -0
  138. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/guides/subagent-routing.md +0 -0
  139. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/guides/troubleshooting.en.md +0 -0
  140. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/guides/troubleshooting.md +0 -0
  141. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/guides/usage-guide.en.md +0 -0
  142. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/guides/usage-guide.md +0 -0
  143. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/guides/vscode.md +0 -0
  144. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/low-memory-integration.en.md +0 -0
  145. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/low-memory-integration.md +0 -0
  146. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/openrouter-roster/CHANGES.md +0 -0
  147. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/openrouter-roster/README.md +0 -0
  148. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/openrouter-roster/latest.json +0 -0
  149. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/retrospectives/v0.4.md +0 -0
  150. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/retrospectives/v0.5-verify.md +0 -0
  151. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/retrospectives/v0.5.md +0 -0
  152. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/retrospectives/v0.6.md +0 -0
  153. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/retrospectives/v0.7.md +0 -0
  154. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/retrospectives/v1.0-verify.md +0 -0
  155. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/retrospectives/v1.0.md +0 -0
  156. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/start/quickstart.en.md +0 -0
  157. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/start/quickstart.md +0 -0
  158. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/start/when-do-i-need-coderouter.en.md +0 -0
  159. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/docs/start/when-do-i-need-coderouter.md +0 -0
  160. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/examples/.env.example +0 -0
  161. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/examples/providers-agent-cli.yaml +0 -0
  162. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/examples/providers-multiagent.yaml +0 -0
  163. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/examples/providers.context-budget-test.yaml +0 -0
  164. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/examples/providers.nvidia-nim.yaml +0 -0
  165. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/examples/providers.ollama-auto-custom.yaml +0 -0
  166. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/examples/providers.ollama-auto.yaml +0 -0
  167. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/examples/providers.ollama-free-chain.yaml +0 -0
  168. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/examples/providers.raspberrypi.yaml +0 -0
  169. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/examples/providers.swap.yaml +0 -0
  170. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/examples/providers.yaml +0 -0
  171. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/scripts/demo_traffic.sh +0 -0
  172. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/scripts/openrouter_roster_diff.py +0 -0
  173. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/scripts/smoke_v2_2.sh +0 -0
  174. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/scripts/verify-providers.yaml +0 -0
  175. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/scripts/verify_ollama_0_23.py +0 -0
  176. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/scripts/verify_v0_5.sh +0 -0
  177. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/scripts/verify_v1_0.sh +0 -0
  178. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/__init__.py +0 -0
  179. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/conftest.py +0 -0
  180. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_adapter_anthropic.py +0 -0
  181. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_agent_cli.py +0 -0
  182. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_audit_log.py +0 -0
  183. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_auto_router.py +0 -0
  184. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_auto_router_cjk.py +0 -0
  185. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_backend_health.py +0 -0
  186. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_budget.py +0 -0
  187. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_capability.py +0 -0
  188. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_capability_degraded_payload.py +0 -0
  189. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_capability_registry.py +0 -0
  190. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_capability_registry_cache_control.py +0 -0
  191. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_capability_tool_choice.py +0 -0
  192. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_claude_code_suitability.py +0 -0
  193. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_cli.py +0 -0
  194. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_cli_bind_warning.py +0 -0
  195. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_cli_stats.py +0 -0
  196. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_config.py +0 -0
  197. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_config_shim_actions.py +0 -0
  198. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_context_budget.py +0 -0
  199. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_continuous_probe.py +0 -0
  200. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_dashboard_endpoint.py +0 -0
  201. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_doctor.py +0 -0
  202. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_doctor_apply.py +0 -0
  203. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_doctor_cache_probe.py +0 -0
  204. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_drift_actions.py +0 -0
  205. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_drift_detection.py +0 -0
  206. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_drift_detection_integration.py +0 -0
  207. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_env_file.py +0 -0
  208. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_env_security.py +0 -0
  209. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_errors.py +0 -0
  210. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_examples_yaml.py +0 -0
  211. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fallback.py +0 -0
  212. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fallback_anthropic.py +0 -0
  213. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fallback_cache_control.py +0 -0
  214. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fallback_cache_observed.py +0 -0
  215. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fallback_cache_strip.py +0 -0
  216. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fallback_empty_response.py +0 -0
  217. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fallback_misconfig_warn.py +0 -0
  218. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fallback_paid_gate.py +0 -0
  219. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fallback_thinking.py +0 -0
  220. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fallback_tool_choice_emulate.py +0 -0
  221. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fix_h1_prometheus_drift.py +0 -0
  222. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fix_h2_h6_translation.py +0 -0
  223. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fix_h3_client_reuse.py +0 -0
  224. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fix_h4_h5_guards.py +0 -0
  225. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fix_h7_probe_restore.py +0 -0
  226. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fix_h8_launcher_auth.py +0 -0
  227. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fix_mA_routing.py +0 -0
  228. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fix_mB_adapters.py +0 -0
  229. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fix_mC_convert.py +0 -0
  230. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fix_mD_logging_metrics.py +0 -0
  231. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fix_mE_config_validation.py +0 -0
  232. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_fix_mF_ingress.py +0 -0
  233. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_gguf_introspect.py +0 -0
  234. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_guards_tool_loop.py +0 -0
  235. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_hardware.py +0 -0
  236. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_ingress_anthropic.py +0 -0
  237. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_ingress_count_tokens.py +0 -0
  238. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_ingress_profile.py +0 -0
  239. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_language_tax.py +0 -0
  240. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_language_tax_integration.py +0 -0
  241. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_launcher_config_bench.py +0 -0
  242. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_launcher_devices.py +0 -0
  243. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_launcher_devices_detect.py +0 -0
  244. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_launcher_devices_routes.py +0 -0
  245. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_launcher_gui_devices.py +0 -0
  246. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_launcher_gui_readiness.py +0 -0
  247. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_launcher_mtp.py +0 -0
  248. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_launcher_mtp_fallback.py +0 -0
  249. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_launcher_provider_sync.py +0 -0
  250. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_launcher_readiness_restart.py +0 -0
  251. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_launcher_swap.py +0 -0
  252. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_launcher_swap_review.py +0 -0
  253. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_memory_budget.py +0 -0
  254. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_memory_pressure.py +0 -0
  255. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_metrics_cache.py +0 -0
  256. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_metrics_collector.py +0 -0
  257. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_metrics_cost.py +0 -0
  258. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_metrics_endpoint.py +0 -0
  259. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_metrics_jsonl.py +0 -0
  260. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_metrics_prometheus.py +0 -0
  261. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_metrics_prometheus_cache.py +0 -0
  262. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_models_passthrough.py +0 -0
  263. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_openai_compat.py +0 -0
  264. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_openrouter_roster_diff.py +0 -0
  265. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_output_filters.py +0 -0
  266. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_output_filters_adapters.py +0 -0
  267. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_partial_stitch.py +0 -0
  268. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_plugin_adapter.py +0 -0
  269. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_plugins_integration.py +0 -0
  270. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_plugins_loader.py +0 -0
  271. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_plugins_registry.py +0 -0
  272. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_reasoning_strip.py +0 -0
  273. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_repair_byte_fallback.py +0 -0
  274. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_request_log.py +0 -0
  275. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_role_normalization.py +0 -0
  276. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_routing_adaptive.py +0 -0
  277. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_self_healing.py +0 -0
  278. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_setup_sh.py +0 -0
  279. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_state_store.py +0 -0
  280. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_token_estimation.py +0 -0
  281. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_token_estimation_accurate.py +0 -0
  282. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_tokens_saved_metric.py +0 -0
  283. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_tool_repair.py +0 -0
  284. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_tool_repair_lenient.py +0 -0
  285. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_tool_repair_r4.py +0 -0
  286. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_toolrepair_bench.py +0 -0
  287. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_translation_anthropic.py +0 -0
  288. {coderouter_cli-2.10.0 → coderouter_cli-2.11.0}/tests/test_translation_reverse.py +0 -0
@@ -9,6 +9,109 @@ are kept verbatim where the Japanese text itself is the subject).
9
9
 
10
10
  ---
11
11
 
12
+ ## [v2.11.0] — 2026-07-28 (launcher backend variants)
13
+
14
+ ### Added
15
+
16
+ - **Backend variants — pick which llama.cpp build to launch.**
17
+ `launcher.backends` now accepts keys of the form `<base>-<variant>`
18
+ (`llama.cpp-cuda`, `llama.cpp-vulkan`, `llama.cpp-rocm`), each naming
19
+ an additional build of the same backend with its own `binary` path.
20
+ Declared variants appear as extra entries in the Launcher's backend
21
+ select (GUI and Web) marked `⚙`, and each one gets its own
22
+ `--list-devices` probe, its own `option_profiles`, and can be pinned
23
+ per model in `launcher.swap`. Motivation: on a machine with mixed
24
+ GPUs the visible devices differ per build — the CUDA build enumerates
25
+ `CUDA0`/`CUDA1` (RTX 5090 + 3090) while the Vulkan build also
26
+ enumerates `Vulkan2` (Radeon 8060S) and the ROCm build only the
27
+ Radeon — so the best build varies by model. Previously `binary` held
28
+ a single path and switching meant editing `providers.yaml` and
29
+ restarting. Docs: `docs/backends/launcher.md` "特化ビルドの切り替え",
30
+ design: `docs/designs/launcher-multi-build.md`.
31
+ - **`option_profiles` inheritance for variants.** A variant key
32
+ inherits the base backend's presets and appends its own; a preset
33
+ whose `name` collides replaces the inherited one *in place* (order
34
+ stays stable, no duplicate at the tail). Shared presets no longer
35
+ need duplicating under every variant key. Shared implementation:
36
+ `launcher_devices.resolve_option_profiles` (used by the Web routes,
37
+ the Tk GUI and `launcher_swap`).
38
+ - **Cross-build bench sweep.** With two or more llama.cpp builds
39
+ declared, the sweep panel shows a "⚙ ビルド横断" button that probes
40
+ every build and generates configurations labelled
41
+ `cuda / CUDA0 単体`, `vulkan / Vulkan2 単体`, … Running the sweep
42
+ launches the same model on each build in turn, so one sweep answers
43
+ "which build is fastest for this model". `SweepStep.backend` /
44
+ `SweepConfigItem.backend` carry the per-step build; `None` keeps the
45
+ previous plan-wide behaviour. New
46
+ `launcher_devices.build_cross_variant_sweep_configs`. Configurations
47
+ never mix devices across builds — one process runs one executable.
48
+ - **Device-id namespace guard.** Device ids are build-specific
49
+ (`CUDA0` and `Vulkan0` are not the same GPU). Switching backend in
50
+ either launcher now clears the device selection, and
51
+ `POST /api/launcher/start` / `sweep/start` reject ids that do not
52
+ exist in the chosen build with a 400 instead of letting
53
+ `--device CUDA0` reach a Vulkan build and fail at startup. Skipped
54
+ when `--list-devices` itself fails (best-effort, per the
55
+ `hardware.py` invariant). New `launcher_devices.unknown_device_ids`.
56
+
57
+ ### Changed
58
+
59
+ - **BREAKING: `launcher.backends` keys are now validated.** A key must
60
+ be `llama.cpp` / `vllm` / `mlx` or one of those with a
61
+ `-<variant>` suffix (variant matching `[a-z0-9][a-z0-9._-]*`).
62
+ Previously any key was accepted and silently ignored, because the
63
+ backend list was a hard-coded set of three; a typo such as
64
+ `llamacpp:` therefore had no effect and no warning. It is now a
65
+ config-load error. Check key spelling if startup fails after
66
+ upgrading.
67
+ - **A backend variant must set `binary`.** Allowing it to be omitted
68
+ would fall back to the base default on PATH, meaning an operator who
69
+ selected `llama.cpp-cuda` could silently get the plain build — the
70
+ hardest failure of this feature to notice. Rejected at config load.
71
+ Base keys keep `binary` optional (PATH resolution), unchanged.
72
+ - **The Launcher's backend list is now config-derived.** Both launchers
73
+ build the select from the three base backends plus whatever variants
74
+ `launcher.backends` declares, instead of a hard-coded list. Declaring
75
+ no variants yields exactly the previous three entries, so specialized
76
+ builds stay invisible to operators who have not opted in.
77
+ - `SwapModelSpec.backend` relaxed from
78
+ `Literal["llama.cpp","vllm","mlx"]` to a validated `str` so it can
79
+ name a variant; the three previous values still validate unchanged.
80
+ When a variant is named, config load verifies it is declared in
81
+ `launcher.backends`.
82
+ - `GET /api/launcher/backends` entries gained `base` and `variant`
83
+ keys. All pre-existing keys (`resolved` / `configured` / `default` /
84
+ `is_custom` / `found`) are unchanged.
85
+
86
+ ### Fixed
87
+
88
+ - **Backend-name branches are normalized through a single helper.**
89
+ Backend name drove behaviour in ten places, several of which would
90
+ have failed *open* for a variant name rather than raising. Two
91
+ mattered: `_MODEL_FLAGS.get(backend, frozenset())` would have
92
+ returned an empty set and **disabled the H8 model-override guard**
93
+ (letting `options` / `extra_args` re-specify `-m` and load an
94
+ arbitrary model), and `_backend_ready`'s
95
+ `backend in ("llama.cpp","vllm")` would have degraded readiness to a
96
+ bare TCP connect — re-introducing the bug readiness gating was added
97
+ to fix (provider registered before the model finished loading). All
98
+ branches now go through `launcher_devices.base_backend()`, in
99
+ `launcher_routes.py`, `launcher_speculative.py`, `launcher_gui.py`
100
+ and the inline SPA JS. `_assert_no_model_override` additionally
101
+ became fail-closed: an unrecognized base uses the union of every
102
+ banned flag set rather than an empty one.
103
+
104
+ ### Tests
105
+
106
+ - 4 new files, 150 new cases (`test_launcher_backend_variants.py`,
107
+ `test_launcher_variant_config.py`, `test_launcher_variant_routes.py`,
108
+ `test_launcher_variant_sweep.py`, `test_launcher_variant_gui.py`).
109
+ The fail-open branches above each have a 1:1 regression test, plus
110
+ byte-identical-argv and unchanged-API-payload tests for configs
111
+ without variants. Suite: 2098 passed, ruff clean.
112
+
113
+ ---
114
+
12
115
  ## [v2.10.0] — 2026-07-20 (VSCode workspace scaffolder)
13
116
 
14
117
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coderouter-cli
3
- Version: 2.10.0
3
+ Version: 2.11.0
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
@@ -224,6 +224,7 @@ providers:
224
224
  | **ログビューア** | 各プロセスの stdout/stderr をブラウザ内でリアルタイム確認 |
225
225
  | **provider 自動同期** (v2.7.4) | 起動したバックエンドを provider として自動登録(`launcher-llamacpp-8085` 等)。providers.yaml 無編集で `X-CodeRouter-Profile: launcher` からルーティング可能。メモリ内のみ・serve と同寿命 |
226
226
  | **モデル名パススルー** (v2.7.4) | `model: ""` の provider は `/v1/models` が上流のロード中モデル ID(gguf 名)をそのまま返す。gguf を差し替えても config 編集不要 — 外部ベンチからモデルを識別できる |
227
+ | **特化ビルドの切り替え** (v2.11.0) | `llama.cpp-cuda` / `-vulkan` / `-rocm` を `backends` に登録すると、起動ごとにどのビルドの `llama-server` を使うか選べる。ビルド別にデバイス検出・option_profiles・ベンチスイープが独立(書かなければ従来どおり) |
227
228
 
228
229
  ```yaml
229
230
  # providers.yaml に追記するだけで有効になる
@@ -69,6 +69,8 @@ ANTHROPIC_BASE_URL=http://localhost:8088 ANTHROPIC_AUTH_TOKEN=dummy claude
69
69
 
70
70
  That's it. Claude Code works as usual, but your local Ollama is answering behind the scenes.
71
71
 
72
+ **From VSCode's integrated terminal**, avoid the env-var footgun with `coderouter vscode-init`. Run it once at your project root and it merges `terminal.integrated.env.*` into `.vscode/settings.json`, so `claude` in VSCode's terminal just works from then on. Cheat-sheet snippets for Cline / Roo Code / Continue.dev are printed at the end of the same run → [VSCode integration guide](./docs/guides/vscode.md) (JP only for now).
73
+
72
74
  ---
73
75
 
74
76
  ## Do you need it?
@@ -217,6 +219,7 @@ More detail → [Usage guide](./docs/guides/usage-guide.en.md) · [Architecture]
217
219
  | Run for free | [Free-tier guide](./docs/guides/free-tier-guide.en.md) |
218
220
  | Launch llama.cpp / vllm via GUI | [Launcher guide](./docs/backends/launcher.md) |
219
221
  | Measure & avoid the language tax | [Language Tax guide](./docs/guides/language-tax.en.md) |
222
+ | Use from VSCode / Cline / Continue | [VSCode integration guide](./docs/guides/vscode.md) (JP) |
220
223
  | Reach it safely from another machine | [Remote access guide](./docs/guides/remote-access.en.md) |
221
224
  | Stuck? | [Troubleshooting](./docs/guides/troubleshooting.en.md) |
222
225
  | Understand the design | [Architecture](./docs/concepts/architecture.md) |
@@ -179,6 +179,7 @@ providers:
179
179
  | **ログビューア** | 各プロセスの stdout/stderr をブラウザ内でリアルタイム確認 |
180
180
  | **provider 自動同期** (v2.7.4) | 起動したバックエンドを provider として自動登録(`launcher-llamacpp-8085` 等)。providers.yaml 無編集で `X-CodeRouter-Profile: launcher` からルーティング可能。メモリ内のみ・serve と同寿命 |
181
181
  | **モデル名パススルー** (v2.7.4) | `model: ""` の provider は `/v1/models` が上流のロード中モデル ID(gguf 名)をそのまま返す。gguf を差し替えても config 編集不要 — 外部ベンチからモデルを識別できる |
182
+ | **特化ビルドの切り替え** (v2.11.0) | `llama.cpp-cuda` / `-vulkan` / `-rocm` を `backends` に登録すると、起動ごとにどのビルドの `llama-server` を使うか選べる。ビルド別にデバイス検出・option_profiles・ベンチスイープが独立(書かなければ従来どおり) |
182
183
 
183
184
  ```yaml
184
185
  # providers.yaml に追記するだけで有効になる
@@ -17,7 +17,14 @@ import re
17
17
  import warnings
18
18
  from typing import Any, Literal, Self
19
19
 
20
- from pydantic import BaseModel, ConfigDict, Field, HttpUrl, model_validator
20
+ from pydantic import (
21
+ BaseModel,
22
+ ConfigDict,
23
+ Field,
24
+ HttpUrl,
25
+ field_validator,
26
+ model_validator,
27
+ )
21
28
 
22
29
 
23
30
  class Capabilities(BaseModel):
@@ -1414,6 +1421,29 @@ class LauncherBackendConfig(BaseModel):
1414
1421
  binary: ~/llama.cpp/build/bin/llama-server
1415
1422
  vllm:
1416
1423
  binary: ~/.venv/bin/python
1424
+
1425
+ **Backend variants** (v2.11.0+, docs/designs/launcher-multi-build.md):
1426
+ a key may also be ``<base>-<variant>`` to register an additional build of
1427
+ the same backend — typically llama.cpp compiled for a specific GPU
1428
+ runtime. Each variant appears as its own entry in the Launcher's backend
1429
+ select, gets its own ``--list-devices`` probe (device IDs differ per
1430
+ build: ``CUDA0`` and ``Vulkan0`` are not the same GPU) and can carry its
1431
+ own ``option_profiles``. ``binary`` is **required** for a variant::
1432
+
1433
+ backends:
1434
+ llama.cpp: # 素のビルド (binary 省略可 = PATH)
1435
+ binary: ~/llm/apps/llama.cpp/build/bin/llama-server
1436
+ llama.cpp-cuda:
1437
+ binary: ~/llm/apps/llama.cpp/build-cuda/bin/llama-server
1438
+ llama.cpp-vulkan:
1439
+ binary: ~/llm/apps/llama.cpp/build-vulkan/bin/llama-server
1440
+ llama.cpp-rocm:
1441
+ binary: ~/llm/apps/llama.cpp/build-rocm/bin/llama-server
1442
+
1443
+ Variants are an advanced option: the matching GPU runtime (CUDA Toolkit /
1444
+ Vulkan runtime + ICD / ROCm) must already be installed by the operator.
1445
+ CodeRouter never selects a variant implicitly — omit them and both the UI
1446
+ and the resulting argv are identical to before.
1417
1447
  """
1418
1448
 
1419
1449
  model_config = ConfigDict(extra="forbid")
@@ -1544,9 +1574,39 @@ class SwapModelSpec(BaseModel):
1544
1574
  "only widens *catalog* matching for ``SwapManager.match``."
1545
1575
  ),
1546
1576
  )
1547
- backend: Literal["llama.cpp", "vllm", "mlx"] = Field(
1548
- ..., description="Same backend set as the manual launcher UI / _build_cmd.",
1577
+ backend: str = Field(
1578
+ ...,
1579
+ description=(
1580
+ "Same backend set as the manual launcher UI / _build_cmd: "
1581
+ "'llama.cpp' / 'vllm' / 'mlx', optionally with a '-<variant>' "
1582
+ "suffix naming a specific build declared in launcher.backends "
1583
+ "(e.g. 'llama.cpp-cuda' to pin this model to the CUDA build). "
1584
+ "Validated by the field validator below plus "
1585
+ "LauncherConfig._check_swap_backends_declared."
1586
+ ),
1549
1587
  )
1588
+
1589
+ @field_validator("backend")
1590
+ @classmethod
1591
+ def _check_backend_name(cls, v: str) -> str:
1592
+ """基底名 or 既知基底名 + 妥当なバリアントのみ許す。
1593
+
1594
+ 以前は ``Literal["llama.cpp","vllm","mlx"]`` だった。バリアント
1595
+ (``llama.cpp-cuda``) を通すため str に緩めたので、代わりに形式検証を
1596
+ ここで行う。既存の 3 値はそのまま通る (後方互換)。
1597
+ """
1598
+ from coderouter.launcher_devices import (
1599
+ KNOWN_BASE_BACKENDS,
1600
+ is_valid_backend_name,
1601
+ )
1602
+
1603
+ if not is_valid_backend_name(v):
1604
+ raise ValueError(
1605
+ f"backend {v!r} is not valid. Expected one of "
1606
+ f"{list(KNOWN_BASE_BACKENDS)} or '<base>-<variant>' "
1607
+ "(e.g. 'llama.cpp-cuda')."
1608
+ )
1609
+ return v
1550
1610
  model_path: str = Field(
1551
1611
  ...,
1552
1612
  description=(
@@ -1802,10 +1862,13 @@ class LauncherConfig(BaseModel):
1802
1862
  default_factory=dict,
1803
1863
  description=(
1804
1864
  "Per-backend binary path overrides. "
1805
- "Keys are backend names ('llama.cpp', 'vllm'). "
1806
- "When a key is absent, the default executable is used "
1807
- "('llama-server' / 'python') and resolved via PATH. "
1808
- "Useful when running a from-source build or a venv-specific binary."
1865
+ "Keys are backend names ('llama.cpp', 'vllm', 'mlx') or backend "
1866
+ "variants ('llama.cpp-cuda', 'llama.cpp-vulkan', ...) naming an "
1867
+ "additional build of the same backend — see "
1868
+ "LauncherBackendConfig. When a base key is absent, the default "
1869
+ "executable is used ('llama-server' / 'python') and resolved via "
1870
+ "PATH. Variants must set 'binary' explicitly and additionally "
1871
+ "appear in the Launcher's backend select."
1809
1872
  ),
1810
1873
  )
1811
1874
  option_profiles: dict[str, list[LauncherOptionProfile]] = Field(
@@ -1813,6 +1876,10 @@ class LauncherConfig(BaseModel):
1813
1876
  description=(
1814
1877
  "Named option presets per backend. "
1815
1878
  "Keys should be backend names: 'llama.cpp', 'vllm'. "
1879
+ "A backend-variant key ('llama.cpp-cuda') is also accepted: its "
1880
+ "presets are appended to the base backend's list, and a preset "
1881
+ "whose 'name' collides replaces the inherited one in place "
1882
+ "(see launcher_devices.resolve_option_profiles). "
1816
1883
  "Each key maps to an ordered list of named presets. "
1817
1884
  "A free-form 'extra args' field is always available in the UI "
1818
1885
  "for one-off overrides without touching this config."
@@ -1929,15 +1996,82 @@ class LauncherConfig(BaseModel):
1929
1996
  )
1930
1997
  return self
1931
1998
 
1999
+ @model_validator(mode="after")
2000
+ def _check_backend_names_and_variant_binaries(self) -> LauncherConfig:
2001
+ """``backends`` のキー形式と、バリアントの ``binary`` 必須を検証する。
2002
+
2003
+ 設計: docs/designs/launcher-multi-build.md §5.2。
2004
+
2005
+ 1. キーは既知基底名 (``llama.cpp`` / ``vllm`` / ``mlx``) そのもの、
2006
+ または ``<既知基底名>-<バリアント>`` (``llama.cpp-cuda`` 等) のみ。
2007
+ **破壊的変更**: これまで ``llamacpp:`` のような typo は黙って無視
2008
+ されていた (バックエンド一覧が固定 3 キーだったため)。今後は
2009
+ ロード時エラーになる。
2010
+ 2. **バリアントは ``binary`` 必須**。``llama.cpp-cuda: {}`` を許すと
2011
+ 既定名フォールバックで PATH の ``llama-server`` が使われ、CUDA
2012
+ ビルドを指定したつもりで素のビルドが静かに動く —— 本機能で最も
2013
+ 気づきにくい事故なのでロード時に殺す。基底名は従来どおり
2014
+ ``binary`` 省略可 (PATH 解決)。
2015
+ """
2016
+ from coderouter.launcher_devices import (
2017
+ KNOWN_BASE_BACKENDS,
2018
+ is_valid_backend_name,
2019
+ is_variant,
2020
+ )
2021
+
2022
+ for name, bc in self.backends.items():
2023
+ if not is_valid_backend_name(name):
2024
+ raise ValueError(
2025
+ f"launcher.backends: invalid backend key {name!r}. "
2026
+ f"Expected one of {list(KNOWN_BASE_BACKENDS)} or "
2027
+ "'<base>-<variant>' where variant matches "
2028
+ "[a-z0-9][a-z0-9._-]* (e.g. 'llama.cpp-cuda')."
2029
+ )
2030
+ if is_variant(name) and not bc.binary:
2031
+ raise ValueError(
2032
+ f"launcher.backends[{name!r}]: 'binary' is required for a "
2033
+ "backend variant. Without it the launcher would fall back "
2034
+ "to the base default on PATH and silently run a different "
2035
+ "build than the one you selected."
2036
+ )
2037
+ return self
2038
+
2039
+ @model_validator(mode="after")
2040
+ def _check_swap_backends_declared(self) -> LauncherConfig:
2041
+ """swap のモデルがバリアントを指すなら ``backends`` に実在すること。
2042
+
2043
+ 設計 §5.2-3。基底名 (``llama.cpp`` 等) は ``backends`` に書かなくても
2044
+ PATH 解決で動くので従来どおり不要。バリアントは実行ファイルパスが
2045
+ ``backends`` にしか無いので、宣言漏れをロード時に弾く。
2046
+ """
2047
+ if self.swap is None:
2048
+ return self
2049
+ from coderouter.launcher_devices import is_variant
2050
+
2051
+ for spec in self.swap.models:
2052
+ if is_variant(spec.backend) and spec.backend not in self.backends:
2053
+ raise ValueError(
2054
+ f"launcher.swap.models[{spec.name!r}]: backend "
2055
+ f"{spec.backend!r} is a variant but is not declared in "
2056
+ "launcher.backends (its binary path is unknown)."
2057
+ )
2058
+ return self
2059
+
1932
2060
  @model_validator(mode="after")
1933
2061
  def _check_swap_option_profiles_exist(self) -> LauncherConfig:
1934
- """§5.4 #2: a swap model's ``option_profile`` must be a real preset."""
2062
+ """§5.4 #2: a swap model's ``option_profile`` must be a real preset.
2063
+
2064
+ バリアント backend の場合は基底名のプロファイルも継承されるので
2065
+ (:func:`resolve_option_profiles`)、マージ後の一覧で判定する。
2066
+ """
1935
2067
  if self.swap is None:
1936
2068
  return self
2069
+ from coderouter.launcher_devices import resolve_option_profiles
2070
+
1937
2071
  for spec in self.swap.models:
1938
2072
  if spec.option_profile is None:
1939
2073
  continue
1940
- profiles = self.option_profiles.get(spec.backend, [])
2074
+ profiles = resolve_option_profiles(self.option_profiles, spec.backend)
1941
2075
  if not any(p.name == spec.option_profile for p in profiles):
1942
2076
  raise ValueError(
1943
2077
  f"launcher.swap.models[{spec.name!r}]: option_profile "