coderouter-cli 2.3.0a4__tar.gz → 2.5.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 (194) hide show
  1. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/.gitignore +5 -0
  2. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/CHANGELOG.md +124 -0
  3. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/PKG-INFO +36 -4
  4. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/README.en.md +35 -3
  5. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/README.md +35 -3
  6. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/cli.py +31 -0
  7. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/config/schemas.py +157 -0
  8. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/guards/__init__.py +2 -0
  9. coderouter_cli-2.5.0/coderouter/guards/_fingerprint.py +125 -0
  10. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/guards/drift_detection.py +55 -0
  11. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/ingress/app.py +11 -0
  12. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/ingress/dashboard_routes.py +1 -0
  13. coderouter_cli-2.5.0/coderouter/ingress/launcher_routes.py +1176 -0
  14. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/routing/fallback.py +33 -3
  15. coderouter_cli-2.5.0/coderouter/state/__init__.py +19 -0
  16. coderouter_cli-2.5.0/coderouter/state/suggest_rules.py +413 -0
  17. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/architecture.md +82 -2
  18. coderouter_cli-2.5.0/docs/launcher-gui.md +200 -0
  19. coderouter_cli-2.5.0/docs/launcher-quickstart.md +187 -0
  20. coderouter_cli-2.5.0/docs/launcher.md +288 -0
  21. coderouter_cli-2.5.0/docs/openrouter-roster/CHANGES.md +50 -0
  22. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/openrouter-roster/latest.json +11 -41
  23. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/quickstart.en.md +22 -3
  24. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/quickstart.md +23 -3
  25. coderouter_cli-2.5.0/docs/verify-ollama-0.23.1.md +243 -0
  26. coderouter_cli-2.5.0/examples/providers.llama-cpp-vllm.yaml +151 -0
  27. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/pyproject.toml +1 -1
  28. coderouter_cli-2.5.0/scripts/verify-providers.yaml +161 -0
  29. coderouter_cli-2.5.0/scripts/verify_ollama_0_23.py +679 -0
  30. coderouter_cli-2.3.0a4/coderouter/state/__init__.py +0 -15
  31. coderouter_cli-2.3.0a4/docs/openrouter-roster/CHANGES.md +0 -24
  32. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/LICENSE +0 -0
  33. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/__init__.py +0 -0
  34. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/__main__.py +0 -0
  35. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/adapters/__init__.py +0 -0
  36. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/adapters/anthropic_native.py +0 -0
  37. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/adapters/base.py +0 -0
  38. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/adapters/openai_compat.py +0 -0
  39. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/adapters/registry.py +0 -0
  40. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/cli_stats.py +0 -0
  41. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/config/__init__.py +0 -0
  42. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/config/capability_registry.py +0 -0
  43. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/config/env_file.py +0 -0
  44. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/config/loader.py +0 -0
  45. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/cost.py +0 -0
  46. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/data/__init__.py +0 -0
  47. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/data/model-capabilities.yaml +0 -0
  48. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/doctor.py +0 -0
  49. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/doctor_apply.py +0 -0
  50. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/env_security.py +0 -0
  51. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/errors.py +0 -0
  52. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/guards/backend_health.py +0 -0
  53. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/guards/context_budget.py +0 -0
  54. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/guards/continuous_probe.py +0 -0
  55. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/guards/drift_actions.py +0 -0
  56. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/guards/memory_pressure.py +0 -0
  57. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/guards/self_healing.py +0 -0
  58. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/guards/tool_loop.py +0 -0
  59. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/ingress/__init__.py +0 -0
  60. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/ingress/anthropic_routes.py +0 -0
  61. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/ingress/metrics_routes.py +0 -0
  62. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/ingress/openai_routes.py +0 -0
  63. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/logging.py +0 -0
  64. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/metrics/__init__.py +0 -0
  65. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/metrics/collector.py +0 -0
  66. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/metrics/prometheus.py +0 -0
  67. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/output_filters.py +0 -0
  68. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/plugins/__init__.py +0 -0
  69. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/plugins/base.py +0 -0
  70. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/plugins/loader.py +0 -0
  71. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/plugins/registry.py +0 -0
  72. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/routing/__init__.py +0 -0
  73. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/routing/adaptive.py +0 -0
  74. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/routing/auto_router.py +0 -0
  75. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/routing/budget.py +0 -0
  76. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/routing/capability.py +0 -0
  77. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/state/audit_log.py +0 -0
  78. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/state/replay.py +0 -0
  79. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/state/request_log.py +0 -0
  80. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/state/store.py +0 -0
  81. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/token_estimation.py +0 -0
  82. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/translation/__init__.py +0 -0
  83. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/translation/anthropic.py +0 -0
  84. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/translation/convert.py +0 -0
  85. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/coderouter/translation/tool_repair.py +0 -0
  86. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/assets/dashboard-demo.png +0 -0
  87. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/context-budget.md +0 -0
  88. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/continuous-probing.md +0 -0
  89. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/designs/v1.5-dashboard-mockup.html +0 -0
  90. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/designs/v1.6-auto-router-verification.md +0 -0
  91. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/designs/v1.6-auto-router.md +0 -0
  92. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/drift-detection.md +0 -0
  93. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/free-tier-guide.en.md +0 -0
  94. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/free-tier-guide.md +0 -0
  95. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/gguf_dl.md +0 -0
  96. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/hf-ollama-models.md +0 -0
  97. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/llamacpp-direct.en.md +0 -0
  98. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/llamacpp-direct.md +0 -0
  99. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/lmstudio-direct.en.md +0 -0
  100. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/lmstudio-direct.md +0 -0
  101. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/openrouter-roster/README.md +0 -0
  102. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/partial-stitch.md +0 -0
  103. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/retrospectives/v0.4.md +0 -0
  104. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/retrospectives/v0.5-verify.md +0 -0
  105. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/retrospectives/v0.5.md +0 -0
  106. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/retrospectives/v0.6.md +0 -0
  107. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/retrospectives/v0.7.md +0 -0
  108. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/retrospectives/v1.0-verify.md +0 -0
  109. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/retrospectives/v1.0.md +0 -0
  110. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/security.en.md +0 -0
  111. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/security.md +0 -0
  112. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/troubleshooting.en.md +0 -0
  113. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/troubleshooting.md +0 -0
  114. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/usage-guide.en.md +0 -0
  115. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/usage-guide.md +0 -0
  116. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/when-do-i-need-coderouter.en.md +0 -0
  117. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/docs/when-do-i-need-coderouter.md +0 -0
  118. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/examples/.env.example +0 -0
  119. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/examples/providers.auto-custom.yaml +0 -0
  120. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/examples/providers.auto.yaml +0 -0
  121. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/examples/providers.note-2026.yaml +0 -0
  122. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/examples/providers.nvidia-nim.yaml +0 -0
  123. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/examples/providers.raspberrypi.yaml +0 -0
  124. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/examples/providers.v2-context-budget.yaml +0 -0
  125. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/examples/providers.yaml +0 -0
  126. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/scripts/demo_traffic.sh +0 -0
  127. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/scripts/openrouter_roster_diff.py +0 -0
  128. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/scripts/smoke_v2_2.sh +0 -0
  129. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/scripts/verify_v0_5.sh +0 -0
  130. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/scripts/verify_v1_0.sh +0 -0
  131. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/__init__.py +0 -0
  132. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/conftest.py +0 -0
  133. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_adapter_anthropic.py +0 -0
  134. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_audit_log.py +0 -0
  135. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_auto_router.py +0 -0
  136. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_backend_health.py +0 -0
  137. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_budget.py +0 -0
  138. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_capability.py +0 -0
  139. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_capability_degraded_payload.py +0 -0
  140. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_capability_registry.py +0 -0
  141. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_capability_registry_cache_control.py +0 -0
  142. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_claude_code_suitability.py +0 -0
  143. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_cli.py +0 -0
  144. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_cli_stats.py +0 -0
  145. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_config.py +0 -0
  146. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_context_budget.py +0 -0
  147. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_continuous_probe.py +0 -0
  148. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_dashboard_endpoint.py +0 -0
  149. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_doctor.py +0 -0
  150. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_doctor_apply.py +0 -0
  151. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_doctor_cache_probe.py +0 -0
  152. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_drift_actions.py +0 -0
  153. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_drift_detection.py +0 -0
  154. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_drift_detection_integration.py +0 -0
  155. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_env_file.py +0 -0
  156. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_env_security.py +0 -0
  157. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_errors.py +0 -0
  158. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_examples_yaml.py +0 -0
  159. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_fallback.py +0 -0
  160. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_fallback_anthropic.py +0 -0
  161. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_fallback_cache_control.py +0 -0
  162. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_fallback_cache_observed.py +0 -0
  163. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_fallback_misconfig_warn.py +0 -0
  164. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_fallback_paid_gate.py +0 -0
  165. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_fallback_thinking.py +0 -0
  166. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_guards_tool_loop.py +0 -0
  167. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_ingress_anthropic.py +0 -0
  168. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_ingress_profile.py +0 -0
  169. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_memory_pressure.py +0 -0
  170. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_metrics_cache.py +0 -0
  171. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_metrics_collector.py +0 -0
  172. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_metrics_cost.py +0 -0
  173. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_metrics_endpoint.py +0 -0
  174. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_metrics_jsonl.py +0 -0
  175. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_metrics_prometheus.py +0 -0
  176. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_metrics_prometheus_cache.py +0 -0
  177. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_openai_compat.py +0 -0
  178. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_openrouter_roster_diff.py +0 -0
  179. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_output_filters.py +0 -0
  180. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_output_filters_adapters.py +0 -0
  181. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_partial_stitch.py +0 -0
  182. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_plugins_integration.py +0 -0
  183. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_plugins_loader.py +0 -0
  184. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_plugins_registry.py +0 -0
  185. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_reasoning_strip.py +0 -0
  186. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_request_log.py +0 -0
  187. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_routing_adaptive.py +0 -0
  188. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_self_healing.py +0 -0
  189. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_setup_sh.py +0 -0
  190. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_state_store.py +0 -0
  191. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_token_estimation.py +0 -0
  192. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_tool_repair.py +0 -0
  193. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_translation_anthropic.py +0 -0
  194. {coderouter_cli-2.3.0a4 → coderouter_cli-2.5.0}/tests/test_translation_reverse.py +0 -0
@@ -32,6 +32,11 @@ env/
32
32
  ENV/
33
33
  .python-version-local
34
34
 
35
+ # ============================================================
36
+ # _OUTPUTS
37
+ # ============================================================
38
+ _OUTPUTS/
39
+
35
40
  # uv
36
41
  # Note: uv.lock SHOULD be committed (per plan.md §5.4)
37
42
  # Do NOT add uv.lock here.
@@ -6,6 +6,130 @@ versioning follows [SemVer](https://semver.org/).
6
6
 
7
7
  ---
8
8
 
9
+ ## [v2.5.0] — 2026-05-22 (Launcher — llama.cpp / vllm GUI)
10
+
11
+ Browser-based process manager for local inference backends, integrated
12
+ into the existing CodeRouter web UI at `/launcher`.
13
+
14
+ ### Added
15
+
16
+ - **`coderouter/ingress/launcher_routes.py`**: New route module providing
17
+ the Launcher UI and its backing API.
18
+
19
+ - `GET /launcher` — Single-page HTML UI (Tailwind CDN + inline JS,
20
+ same dark-theme aesthetic as `/dashboard`).
21
+ - `GET /api/launcher/models` — Recursively scans `launcher.model_dirs`
22
+ and returns discovered model files with name, path, size (GB), and
23
+ extension.
24
+ - `GET /api/launcher/option-profiles` — Returns named option presets
25
+ from `providers.yaml` keyed by backend (`llama.cpp`, `vllm`).
26
+ - `GET /api/launcher/processes` — Lists all managed processes.
27
+ - `POST /api/launcher/start` — Starts a backend process. Accepts name,
28
+ backend, model_path, port, options dict, and extra_args free-text.
29
+ - `POST /api/launcher/stop/{id}` — SIGTERM → SIGKILL (5 s timeout).
30
+ - `DELETE /api/launcher/processes/{id}` — Removes a stopped process.
31
+ - `GET /api/launcher/logs/{id}` — Returns last N lines from the
32
+ process's 200-line stdout/stderr ring buffer.
33
+
34
+ - **`LauncherOptionProfile` / `LauncherConfig`** (`config/schemas.py`):
35
+ New Pydantic models for the `launcher:` block in `providers.yaml`.
36
+ Adding new CLI flags requires only a YAML edit — no code change.
37
+
38
+ - **`launcher_profiles.yaml.example`**: Template with 7 llama.cpp
39
+ presets and 7 vllm presets. For GitHub distribution and community
40
+ profile contributions.
41
+
42
+ - **`/dashboard` header**: Added a "Launcher" navigation link.
43
+
44
+ ### Design notes
45
+
46
+ - **YAML-driven**: option profiles live entirely in `providers.yaml`.
47
+ No code changes needed to add new backend flags.
48
+ - **Multi-process**: each launched process gets a UUID-based ID and is
49
+ tracked independently. llama.cpp and vllm can run side by side.
50
+ - **Zero new dependencies**: uses `asyncio.create_subprocess_exec`
51
+ (stdlib only). The 5-dep invariant is maintained.
52
+ - **In-memory registry**: does not persist across CodeRouter restarts
53
+ (intentional — avoids zombie GPU allocations on restart).
54
+
55
+ ---
56
+
57
+ ## [v2.4.0] — 2026-05-15 (Goal-session awareness — P1-4/5/6)
58
+
59
+ Stable release following v2.3.0a4. Promotes the Plugin SDK to stable,
60
+ adds three goal-session features, and ships a rule-suggestion CLI.
61
+
62
+ ### Added
63
+
64
+ - **`coderouter/guards/_fingerprint.py`** (P1-4): Response fingerprinting
65
+ helper. `fingerprint_response(text)` returns a 12-hex SHA-256 digest
66
+ of the top-N content words (stop-word-filtered, order-independent).
67
+ Used by the new `goal_progress_stall` drift signal to detect when a
68
+ model repeats itself without making progress.
69
+
70
+ - **Signal 6 — `goal_progress_stall`** (`drift_detection.py`, P1-4):
71
+ Sixth drift signal added to `detect_drift()`. Fires (mild) when the
72
+ fraction of fingerprinted responses that repeat an already-seen
73
+ fingerprint exceeds `repetition_rate_threshold` (default 0.4).
74
+ Requires `response_fingerprint` to be populated on observations; when
75
+ absent the signal is silently skipped (backward-compatible).
76
+
77
+ - **`DriftThresholds.repetition_rate_threshold`** (P1-4): New field on
78
+ `DriftThresholds`, present on all three presets. `THRESHOLDS_GOAL`
79
+ preset added (`min_window_fill=4`, `repetition_rate_threshold=0.2`,
80
+ tighter across the board) and exposed via `SENSITIVITY_PRESETS["goal"]`.
81
+
82
+ - **`FallbackChain.goal_mode: bool = False`** (`config/schemas.py`, P1-5):
83
+ Profile-level flag. When `True`, the drift detector ignores
84
+ `drift_detection_sensitivity` and uses `THRESHOLDS_GOAL` instead
85
+ (stricter thresholds + `min_window_fill=4`). Designed for `/goal`
86
+ agent sessions where forward-progress stalls are more actionable.
87
+
88
+ - **`coderouter/state/suggest_rules.py`** (P1-6): Statistical rule
89
+ suggestion engine. `suggest_rules(WindowSummary) → list[RuleSuggestion]`
90
+ analyses the request journal and emits copy-paste YAML snippets.
91
+ Five rules: `provider_reorder` (cost rank), `enable_prompt_cache`
92
+ (high-token / low-hit providers), `enable_drift_detection` (reminder),
93
+ `low_sensitivity_small_window` (sparse-traffic guard), `goal_profile`
94
+ (output-divergence → `goal_mode: true`). Pure statistics — no LLM.
95
+
96
+ - **`coderouter replay --suggest-rules`** (`cli.py`, P1-6): New flag on
97
+ the existing `replay` subcommand. Reads the full request journal,
98
+ runs `suggest_rules`, and prints a formatted terminal report with
99
+ confidence badges and YAML snippets.
100
+
101
+ ### Changed
102
+
103
+ - **`ResponseObservation.response_fingerprint: str | None = None`**
104
+ (`drift_detection.py`): New optional field (slots-safe, defaults to
105
+ `None`). Fully backward-compatible — existing callers that don't
106
+ populate it get the same five-signal behaviour as before.
107
+
108
+ - **`FallbackEngine._observe_drift_signal`** (`fallback.py`): Accepts
109
+ new `response_fingerprint` kwarg. Non-streaming and streaming success
110
+ paths now compute and pass a fingerprint for the `goal_progress_stall`
111
+ signal. `goal_mode` check applies `THRESHOLDS_GOAL` when the profile
112
+ flag is set.
113
+
114
+ ### Files touched
115
+
116
+ ```
117
+ A coderouter/guards/_fingerprint.py
118
+ M coderouter/guards/__init__.py — module registry comment
119
+ M coderouter/guards/drift_detection.py — Signal 6, THRESHOLDS_GOAL, new fields
120
+ M coderouter/config/schemas.py — FallbackChain.goal_mode
121
+ M coderouter/routing/fallback.py — fingerprint wiring, goal_mode dispatch
122
+ A coderouter/state/suggest_rules.py
123
+ M coderouter/state/__init__.py — module registry comment
124
+ M coderouter/cli.py — replay --suggest-rules
125
+ A docs/articles/v1-saga/note-14-v0-4-goal-mode.md
126
+ M docs/articles/v1-saga/INDEX.md
127
+ M docs/inside/future.md
128
+ M CHANGELOG.md, pyproject.toml — 2.3.0a4 → 2.4.0
129
+ ```
130
+
131
+ ---
132
+
9
133
  ## [v2.3.0a4] — 2026-05-08 (Plugin SDK — ruff cleanup)
10
134
 
11
135
  Patch over `v2.3.0a3`. CI's `ruff check .` job surfaced six lint
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coderouter-cli
3
- Version: 2.3.0a4
3
+ Version: 2.5.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
@@ -47,7 +47,7 @@ Description-Content-Type: text/markdown
47
47
 
48
48
  <p align="center">
49
49
  <a href="https://github.com/zephel01/CodeRouter/actions/workflows/ci.yml"><img src="https://github.com/zephel01/CodeRouter/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI"></a>
50
- <a href=""><img src="https://img.shields.io/badge/version-2.2.0-blue" alt="version"></a>
50
+ <a href="https://pypi.org/project/coderouter-cli/"><img src="https://img.shields.io/pypi/v/coderouter-cli?include_prereleases&color=blue&label=pypi" alt="pypi"></a>
51
51
  <a href=""><img src="https://img.shields.io/badge/python-3.12%2B-blue" alt="python"></a>
52
52
  <a href=""><img src="https://img.shields.io/badge/deps-5-brightgreen" alt="deps"></a>
53
53
  <a href=""><img src="https://img.shields.io/badge/license-MIT-yellow" alt="license"></a>
@@ -141,7 +141,7 @@ ANTHROPIC_BASE_URL=http://localhost:8088 ANTHROPIC_AUTH_TOKEN=dummy claude
141
141
  | ガード | 何から守るか |
142
142
  |---|---|
143
143
  | **Context Budget** | メッセージが溜まりすぎて context window 溢れ → 自動 trim |
144
- | **Drift Detection** | モデルの応答品質が徐々に劣化 → 別 provider に切替 or KV cache flush |
144
+ | **Drift Detection** | モデルの応答品質が徐々に劣化 → 別 provider に切替 or KV cache flush (6 シグナル、`goal_mode` で目標達成停滞も検知) |
145
145
  | **Self-healing** | backend が落ちた → 自動除外 + restart + 回復 probe で自動復帰 |
146
146
  | **Tool Loop Guard** | 同じツールを無限に呼び続ける → 検知して停止 |
147
147
  | **Memory Pressure** | GPU メモリ不足を検知 → 軽量モデルに切替 |
@@ -154,9 +154,40 @@ ANTHROPIC_BASE_URL=http://localhost:8088 ANTHROPIC_AUTH_TOKEN=dummy claude
154
154
  | **`coderouter doctor`** | プロバイダの問題を 6 プローブで即診断 + 修正パッチ出力 |
155
155
  | **`/dashboard`** | ブラウザで今何が起きてるかリアルタイム確認 |
156
156
  | **`coderouter audit`** | guard 発火履歴を検索 |
157
- | **`coderouter replay`** | provider 切替の効果を統計比較 (A/B 分析) |
157
+ | **`coderouter replay`** | provider 切替の効果を統計比較 (A/B 分析) / `--suggest-rules` でルール最適化提案 |
158
158
  | **Continuous Probe** | idle 時も定期的に backend を監視 |
159
159
 
160
+ ### Launcher — llama.cpp / vllm 起動 UI
161
+
162
+ `http://localhost:8088/launcher` で開けるブラウザ UI。llama.cpp や vllm を GUI で起動・管理できます。
163
+
164
+ | 機能 | 詳細 |
165
+ |---|---|
166
+ | **モデルスキャン** | `model_dirs` に指定したフォルダを再帰スキャンして `.gguf` / `.safetensors` をリスト化 |
167
+ | **オプションプロファイル** | `providers.yaml` に名前付きプリセットを定義 → ドロップダウンで選択するだけ |
168
+ | **複数プロセス管理** | llama.cpp と vllm を同時に起動し、ポートごとに独立管理 |
169
+ | **ログビューア** | 各プロセスの stdout/stderr をブラウザ内でリアルタイム確認 |
170
+
171
+ ```yaml
172
+ # providers.yaml に追記するだけで有効になる
173
+ launcher:
174
+ model_dirs:
175
+ - ~/models
176
+ option_profiles:
177
+ llama.cpp:
178
+ - name: "GPU フル活用"
179
+ args:
180
+ "-ngl": 99
181
+ "--ctx-size": 4096
182
+ vllm:
183
+ - name: "標準"
184
+ args:
185
+ "--dtype": "auto"
186
+ "--max-model-len": 4096
187
+ ```
188
+
189
+ 詳細 → [Launcher ガイド](./docs/launcher.md)
190
+
160
191
  ---
161
192
 
162
193
  ## 設定例 (最小)
@@ -193,6 +224,7 @@ providers:
193
224
  | すぐ動かす | [Quickstart](./docs/quickstart.md) |
194
225
  | 使いこなす | [利用ガイド](./docs/usage-guide.md) |
195
226
  | 無料で回す | [無料枠ガイド](./docs/free-tier-guide.md) |
227
+ | llama.cpp / vllm を GUI で起動 | [Launcher ガイド](./docs/launcher.md) |
196
228
  | 詰まった | [トラブルシューティング](./docs/troubleshooting.md) |
197
229
  | 設計を知りたい | [アーキテクチャ詳細](./docs/architecture.md) |
198
230
  | 全リリース履歴 | [CHANGELOG](./CHANGELOG.md) |
@@ -6,7 +6,7 @@
6
6
 
7
7
  <p align="center">
8
8
  <a href="https://github.com/zephel01/CodeRouter/actions/workflows/ci.yml"><img src="https://github.com/zephel01/CodeRouter/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI"></a>
9
- <a href=""><img src="https://img.shields.io/badge/version-2.2.0-blue" alt="version"></a>
9
+ <a href="https://pypi.org/project/coderouter-cli/"><img src="https://img.shields.io/pypi/v/coderouter-cli?include_prereleases&color=blue&label=pypi" alt="pypi"></a>
10
10
  <a href=""><img src="https://img.shields.io/badge/python-3.12%2B-blue" alt="python"></a>
11
11
  <a href=""><img src="https://img.shields.io/badge/deps-5-brightgreen" alt="deps"></a>
12
12
  <a href=""><img src="https://img.shields.io/badge/license-MIT-yellow" alt="license"></a>
@@ -100,7 +100,7 @@ Full decision matrix → [Do I need CodeRouter?](./docs/when-do-i-need-coderoute
100
100
  | Guard | What it protects against |
101
101
  |---|---|
102
102
  | **Context Budget** | Messages piling up → context window overflow. Auto-trim at 90% |
103
- | **Drift Detection** | Model quality degrading over time → switch provider or flush KV cache |
103
+ | **Drift Detection** | Model quality degrading over time → switch provider or flush KV cache (6 signals incl. `goal_progress_stall`; `goal_mode` for tighter thresholds) |
104
104
  | **Self-healing** | Backend crashes → auto-exclude + restart + recovery probe → auto-restore |
105
105
  | **Tool Loop Guard** | Agent calling the same tool forever → detect and break |
106
106
  | **Memory Pressure** | GPU running out of VRAM → switch to lighter model |
@@ -113,9 +113,40 @@ Full decision matrix → [Do I need CodeRouter?](./docs/when-do-i-need-coderoute
113
113
  | **`coderouter doctor`** | 6-probe diagnosis of provider issues + copy-paste YAML patches |
114
114
  | **`/dashboard`** | Real-time browser view of what's happening |
115
115
  | **`coderouter audit`** | Search guard activation history |
116
- | **`coderouter replay`** | Compare providers statistically (A/B analysis) |
116
+ | **`coderouter replay`** | Compare providers statistically (A/B analysis) / `--suggest-rules` for automated rule suggestions |
117
117
  | **Continuous Probe** | Background health monitoring even during idle |
118
118
 
119
+ ### Launcher — llama.cpp / vllm GUI
120
+
121
+ Browser UI at `http://localhost:8088/launcher` for starting and managing local inference backends.
122
+
123
+ | Feature | Detail |
124
+ |---|---|
125
+ | **Model scanner** | Recursively scans `model_dirs` for `.gguf` / `.safetensors` and lists them |
126
+ | **Option profiles** | Name your flag presets in `providers.yaml` — select from a dropdown, no CLI needed |
127
+ | **Multi-process** | Run llama.cpp and vllm side by side on different ports |
128
+ | **Log viewer** | stdout/stderr of each process shown live in the browser |
129
+
130
+ ```yaml
131
+ # Add to providers.yaml — no code changes needed
132
+ launcher:
133
+ model_dirs:
134
+ - ~/models
135
+ option_profiles:
136
+ llama.cpp:
137
+ - name: "Full GPU"
138
+ args:
139
+ "-ngl": 99
140
+ "--ctx-size": 4096
141
+ vllm:
142
+ - name: "Standard"
143
+ args:
144
+ "--dtype": "auto"
145
+ "--max-model-len": 4096
146
+ ```
147
+
148
+ Details → [Launcher guide](./docs/launcher.md)
149
+
119
150
  ---
120
151
 
121
152
  ## Minimal Config
@@ -152,6 +183,7 @@ More detail → [Usage guide](./docs/usage-guide.en.md) · [Architecture](./docs
152
183
  | Get running fast | [Quickstart](./docs/quickstart.en.md) |
153
184
  | Use it well | [Usage guide](./docs/usage-guide.en.md) |
154
185
  | Run for free | [Free-tier guide](./docs/free-tier-guide.en.md) |
186
+ | Launch llama.cpp / vllm via GUI | [Launcher guide](./docs/launcher.md) |
155
187
  | Stuck? | [Troubleshooting](./docs/troubleshooting.en.md) |
156
188
  | Understand the design | [Architecture](./docs/architecture.md) |
157
189
  | Full release history | [CHANGELOG](./CHANGELOG.md) |
@@ -6,7 +6,7 @@
6
6
 
7
7
  <p align="center">
8
8
  <a href="https://github.com/zephel01/CodeRouter/actions/workflows/ci.yml"><img src="https://github.com/zephel01/CodeRouter/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI"></a>
9
- <a href=""><img src="https://img.shields.io/badge/version-2.2.0-blue" alt="version"></a>
9
+ <a href="https://pypi.org/project/coderouter-cli/"><img src="https://img.shields.io/pypi/v/coderouter-cli?include_prereleases&color=blue&label=pypi" alt="pypi"></a>
10
10
  <a href=""><img src="https://img.shields.io/badge/python-3.12%2B-blue" alt="python"></a>
11
11
  <a href=""><img src="https://img.shields.io/badge/deps-5-brightgreen" alt="deps"></a>
12
12
  <a href=""><img src="https://img.shields.io/badge/license-MIT-yellow" alt="license"></a>
@@ -100,7 +100,7 @@ ANTHROPIC_BASE_URL=http://localhost:8088 ANTHROPIC_AUTH_TOKEN=dummy claude
100
100
  | ガード | 何から守るか |
101
101
  |---|---|
102
102
  | **Context Budget** | メッセージが溜まりすぎて context window 溢れ → 自動 trim |
103
- | **Drift Detection** | モデルの応答品質が徐々に劣化 → 別 provider に切替 or KV cache flush |
103
+ | **Drift Detection** | モデルの応答品質が徐々に劣化 → 別 provider に切替 or KV cache flush (6 シグナル、`goal_mode` で目標達成停滞も検知) |
104
104
  | **Self-healing** | backend が落ちた → 自動除外 + restart + 回復 probe で自動復帰 |
105
105
  | **Tool Loop Guard** | 同じツールを無限に呼び続ける → 検知して停止 |
106
106
  | **Memory Pressure** | GPU メモリ不足を検知 → 軽量モデルに切替 |
@@ -113,9 +113,40 @@ ANTHROPIC_BASE_URL=http://localhost:8088 ANTHROPIC_AUTH_TOKEN=dummy claude
113
113
  | **`coderouter doctor`** | プロバイダの問題を 6 プローブで即診断 + 修正パッチ出力 |
114
114
  | **`/dashboard`** | ブラウザで今何が起きてるかリアルタイム確認 |
115
115
  | **`coderouter audit`** | guard 発火履歴を検索 |
116
- | **`coderouter replay`** | provider 切替の効果を統計比較 (A/B 分析) |
116
+ | **`coderouter replay`** | provider 切替の効果を統計比較 (A/B 分析) / `--suggest-rules` でルール最適化提案 |
117
117
  | **Continuous Probe** | idle 時も定期的に backend を監視 |
118
118
 
119
+ ### Launcher — llama.cpp / vllm 起動 UI
120
+
121
+ `http://localhost:8088/launcher` で開けるブラウザ UI。llama.cpp や vllm を GUI で起動・管理できます。
122
+
123
+ | 機能 | 詳細 |
124
+ |---|---|
125
+ | **モデルスキャン** | `model_dirs` に指定したフォルダを再帰スキャンして `.gguf` / `.safetensors` をリスト化 |
126
+ | **オプションプロファイル** | `providers.yaml` に名前付きプリセットを定義 → ドロップダウンで選択するだけ |
127
+ | **複数プロセス管理** | llama.cpp と vllm を同時に起動し、ポートごとに独立管理 |
128
+ | **ログビューア** | 各プロセスの stdout/stderr をブラウザ内でリアルタイム確認 |
129
+
130
+ ```yaml
131
+ # providers.yaml に追記するだけで有効になる
132
+ launcher:
133
+ model_dirs:
134
+ - ~/models
135
+ option_profiles:
136
+ llama.cpp:
137
+ - name: "GPU フル活用"
138
+ args:
139
+ "-ngl": 99
140
+ "--ctx-size": 4096
141
+ vllm:
142
+ - name: "標準"
143
+ args:
144
+ "--dtype": "auto"
145
+ "--max-model-len": 4096
146
+ ```
147
+
148
+ 詳細 → [Launcher ガイド](./docs/launcher.md)
149
+
119
150
  ---
120
151
 
121
152
  ## 設定例 (最小)
@@ -152,6 +183,7 @@ providers:
152
183
  | すぐ動かす | [Quickstart](./docs/quickstart.md) |
153
184
  | 使いこなす | [利用ガイド](./docs/usage-guide.md) |
154
185
  | 無料で回す | [無料枠ガイド](./docs/free-tier-guide.md) |
186
+ | llama.cpp / vllm を GUI で起動 | [Launcher ガイド](./docs/launcher.md) |
155
187
  | 詰まった | [トラブルシューティング](./docs/troubleshooting.md) |
156
188
  | 設計を知りたい | [アーキテクチャ詳細](./docs/architecture.md) |
157
189
  | 全リリース履歴 | [CHANGELOG](./CHANGELOG.md) |
@@ -293,6 +293,18 @@ def _build_parser() -> argparse.ArgumentParser:
293
293
  metavar="N",
294
294
  help="Use only the last N entries (applied after --since and --provider filters).",
295
295
  )
296
+ # P1-6: --suggest-rules — statistical analysis → routing rule proposals.
297
+ replay.add_argument(
298
+ "--suggest-rules",
299
+ action="store_true",
300
+ help=(
301
+ "P1-6: analyse the request journal and print actionable routing "
302
+ "rule suggestions as copy-paste YAML snippets. Suggestions cover "
303
+ "provider reordering by cost, prompt_cache enablement, drift "
304
+ "detection configuration, and goal profile creation. "
305
+ "Can be combined with --since / --limit to scope the analysis window."
306
+ ),
307
+ )
296
308
 
297
309
  return parser
298
310
 
@@ -684,6 +696,25 @@ def _run_replay(args: argparse.Namespace) -> int:
684
696
  print("replay: no matching entries found.")
685
697
  return 0
686
698
 
699
+ if getattr(args, "suggest_rules", False):
700
+ # P1-6: statistical rule suggestion mode.
701
+ # Always compute a full window summary (ignores --compare / --provider).
702
+ from coderouter.state.replay import summarize_window as _sw
703
+ from coderouter.state.suggest_rules import format_suggestions, suggest_rules
704
+
705
+ # Re-read without provider filter so we see all providers.
706
+ all_entries = read_request_log(log_path, since=args.since)
707
+ if args.limit is not None and args.limit > 0:
708
+ all_entries = all_entries[-args.limit:]
709
+ full_summary = _sw(all_entries)
710
+ suggestions = suggest_rules(full_summary)
711
+ print(f"Request journal: {len(all_entries)} entries analysed")
712
+ print(f" Window: {full_summary.first_ts} → {full_summary.last_ts}")
713
+ print(f" Providers: {', '.join(sorted(full_summary.providers))}")
714
+ print()
715
+ print(format_suggestions(suggestions))
716
+ return 0
717
+
687
718
  if args.compare:
688
719
  provider_a, provider_b = args.compare
689
720
  comparison = compare_providers(entries, provider_a, provider_b)
@@ -658,6 +658,28 @@ class FallbackChain(BaseModel):
658
658
  ),
659
659
  )
660
660
 
661
+ # --- P1-5: goal_mode — tighter drift thresholds for /goal sessions -------
662
+ #
663
+ # When True, the drift detector automatically switches to the
664
+ # ``THRESHOLDS_GOAL`` preset regardless of ``drift_detection_sensitivity``,
665
+ # and lowers ``min_window_fill`` to 4 so stall detection fires faster.
666
+ #
667
+ # Intended for profiles routed by the ``/goal`` meta-command where
668
+ # the agent is expected to make steady forward progress. Repetition and
669
+ # length collapse are much more meaningful signals in that context than
670
+ # in a general-purpose chat session.
671
+ goal_mode: bool = Field(
672
+ default=False,
673
+ description=(
674
+ "P1-5: when True, automatically applies the ``goal`` drift "
675
+ "threshold preset (stricter thresholds, lower ``min_window_fill`` "
676
+ "of 4) for this profile. Overrides ``drift_detection_sensitivity`` "
677
+ "when drift_detection_action is not ``off``. Designed for "
678
+ "agent/goal sessions where forward-progress stalls are more "
679
+ "actionable than in ad-hoc chat."
680
+ ),
681
+ )
682
+
661
683
  # --- v2.0-H (L6): Mid-stream partial stitching --------------------------
662
684
  # * ``off`` — discard partial content on mid-stream failure (legacy).
663
685
  # * ``surface`` — return partial content as a truncated-but-valid response.
@@ -852,6 +874,130 @@ class AutoRouterConfig(BaseModel):
852
874
  )
853
875
 
854
876
 
877
+ class LauncherBackendConfig(BaseModel):
878
+ """Per-backend binary path configuration for the Launcher.
879
+
880
+ When ``binary`` is unset, the Launcher falls back to the default
881
+ executable name (``llama-server`` for llama.cpp, ``python`` for vllm)
882
+ and relies on ``$PATH`` resolution — which works when the tool is
883
+ globally installed. Set ``binary`` when:
884
+
885
+ - llama.cpp was built from source (e.g. ``~/llama.cpp/build/bin/llama-server``)
886
+ - vllm lives in a virtualenv (e.g. ``~/.venv/bin/python``)
887
+ - Multiple builds coexist and you want to pin a specific one
888
+
889
+ Tilde (``~``) and environment variables are expanded at launch time.
890
+
891
+ Example::
892
+
893
+ backends:
894
+ llama.cpp:
895
+ binary: ~/llama.cpp/build/bin/llama-server
896
+ vllm:
897
+ binary: ~/.venv/bin/python
898
+ """
899
+
900
+ model_config = ConfigDict(extra="forbid")
901
+
902
+ binary: str | None = Field(
903
+ default=None,
904
+ description=(
905
+ "Absolute or ``~``-relative path to the backend executable. "
906
+ "llama.cpp default: ``llama-server`` (PATH). "
907
+ "vllm default: ``python`` (PATH). "
908
+ "Expanded at launch time."
909
+ ),
910
+ )
911
+
912
+
913
+ class LauncherOptionProfile(BaseModel):
914
+ """One named option preset for a launcher backend (e.g. llama.cpp / vllm).
915
+
916
+ ``args`` maps CLI flag strings to their values. A bool value of
917
+ ``True`` means "include the flag without a value" (e.g. ``--no-mmap``);
918
+ ``False`` means "omit the flag entirely". All other value types are
919
+ converted to strings and appended as ``--flag value`` pairs.
920
+
921
+ Example::
922
+
923
+ name: "GPU速度重視"
924
+ args:
925
+ "-ngl": 99
926
+ "--ctx-size": 4096
927
+ "--no-mmap": false
928
+ """
929
+
930
+ model_config = ConfigDict(extra="forbid")
931
+
932
+ name: str = Field(..., description="Display name shown in the Launcher UI dropdown.")
933
+ args: dict[str, str | int | float | bool] = Field(
934
+ default_factory=dict,
935
+ description=(
936
+ "CLI flag → value mapping. "
937
+ "bool True = flag only (no value). "
938
+ "bool False = omit flag. "
939
+ "All other types are stringified and passed as '--flag value'."
940
+ ),
941
+ )
942
+
943
+
944
+ class LauncherConfig(BaseModel):
945
+ """The ``launcher:`` block in providers.yaml.
946
+
947
+ Controls the Launcher UI available at ``/launcher``.
948
+
949
+ Example::
950
+
951
+ launcher:
952
+ model_dirs:
953
+ - ~/models
954
+ - /data/gguf
955
+ option_profiles:
956
+ llama.cpp:
957
+ - name: "GPU速度重視"
958
+ args:
959
+ "-ngl": 99
960
+ "--ctx-size": 4096
961
+ vllm:
962
+ - name: "標準"
963
+ args:
964
+ "--dtype": "auto"
965
+ "--max-model-len": 4096
966
+ """
967
+
968
+ model_config = ConfigDict(extra="forbid")
969
+
970
+ model_dirs: list[str] = Field(
971
+ default_factory=list,
972
+ description=(
973
+ "Directories to scan for model files "
974
+ "(.gguf, .safetensors, .bin, .pt, .ggml). "
975
+ "Paths are expanded (~ and env vars) at scan time, not at load. "
976
+ "Non-existent paths are silently skipped."
977
+ ),
978
+ )
979
+ backends: dict[str, LauncherBackendConfig] = Field(
980
+ default_factory=dict,
981
+ description=(
982
+ "Per-backend binary path overrides. "
983
+ "Keys are backend names ('llama.cpp', 'vllm'). "
984
+ "When a key is absent, the default executable is used "
985
+ "('llama-server' / 'python') and resolved via PATH. "
986
+ "Useful when running a from-source build or a venv-specific binary."
987
+ ),
988
+ )
989
+ option_profiles: dict[str, list[LauncherOptionProfile]] = Field(
990
+ default_factory=dict,
991
+ description=(
992
+ "Named option presets per backend. "
993
+ "Keys should be backend names: 'llama.cpp', 'vllm'. "
994
+ "Each key maps to an ordered list of named presets. "
995
+ "A free-form 'extra args' field is always available in the UI "
996
+ "for one-off overrides without touching this config."
997
+ ),
998
+ )
999
+
1000
+
855
1001
  class PluginsConfig(BaseModel):
856
1002
  """The ``plugins:`` block in providers.yaml (v2.3.0).
857
1003
 
@@ -1060,6 +1206,17 @@ class CodeRouterConfig(BaseModel):
1060
1206
  "plugins (zero-cost, backward-compatible default)."
1061
1207
  ),
1062
1208
  )
1209
+ launcher: LauncherConfig | None = Field(
1210
+ default=None,
1211
+ description=(
1212
+ "Launcher configuration for the /launcher UI. "
1213
+ "Defines model_dirs to scan and option_profiles per backend "
1214
+ "('llama.cpp', 'vllm'). "
1215
+ "Unset (None) = Launcher UI shows empty model list and no profiles. "
1216
+ "The Launcher UI itself is always available at /launcher "
1217
+ "regardless of this setting."
1218
+ ),
1219
+ )
1063
1220
 
1064
1221
  @model_validator(mode="after")
1065
1222
  def _check_default_profile_exists(self) -> CodeRouterConfig:
@@ -12,6 +12,8 @@ to hit:
12
12
  * :mod:`coderouter.guards.self_healing` — v2.0-J auto-exclude +
13
13
  restart + recovery probe
14
14
  * :mod:`coderouter.guards.continuous_probe` — v2.0-I background probing
15
+ * :mod:`coderouter.guards._fingerprint` — P1-4 response fingerprinting
16
+ for goal_progress_stall signal
15
17
 
16
18
  Each guard is a pure-functional / single-class module that the engine
17
19
  consults at the appropriate dispatch point. Guards never block the