coderouter-cli 2.3.0a3__tar.gz → 2.3.0a4__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 (182) hide show
  1. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/CHANGELOG.md +45 -0
  2. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/PKG-INFO +1 -1
  3. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/plugins/__init__.py +5 -8
  4. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/routing/fallback.py +19 -1
  5. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/pyproject.toml +1 -1
  6. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_plugins_integration.py +13 -118
  7. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_plugins_loader.py +3 -7
  8. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_plugins_registry.py +1 -0
  9. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/.gitignore +0 -0
  10. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/LICENSE +0 -0
  11. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/README.en.md +0 -0
  12. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/README.md +0 -0
  13. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/__init__.py +0 -0
  14. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/__main__.py +0 -0
  15. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/adapters/__init__.py +0 -0
  16. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/adapters/anthropic_native.py +0 -0
  17. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/adapters/base.py +0 -0
  18. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/adapters/openai_compat.py +0 -0
  19. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/adapters/registry.py +0 -0
  20. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/cli.py +0 -0
  21. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/cli_stats.py +0 -0
  22. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/config/__init__.py +0 -0
  23. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/config/capability_registry.py +0 -0
  24. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/config/env_file.py +0 -0
  25. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/config/loader.py +0 -0
  26. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/config/schemas.py +0 -0
  27. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/cost.py +0 -0
  28. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/data/__init__.py +0 -0
  29. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/data/model-capabilities.yaml +0 -0
  30. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/doctor.py +0 -0
  31. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/doctor_apply.py +0 -0
  32. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/env_security.py +0 -0
  33. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/errors.py +0 -0
  34. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/guards/__init__.py +0 -0
  35. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/guards/backend_health.py +0 -0
  36. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/guards/context_budget.py +0 -0
  37. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/guards/continuous_probe.py +0 -0
  38. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/guards/drift_actions.py +0 -0
  39. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/guards/drift_detection.py +0 -0
  40. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/guards/memory_pressure.py +0 -0
  41. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/guards/self_healing.py +0 -0
  42. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/guards/tool_loop.py +0 -0
  43. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/ingress/__init__.py +0 -0
  44. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/ingress/anthropic_routes.py +0 -0
  45. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/ingress/app.py +0 -0
  46. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/ingress/dashboard_routes.py +0 -0
  47. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/ingress/metrics_routes.py +0 -0
  48. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/ingress/openai_routes.py +0 -0
  49. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/logging.py +0 -0
  50. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/metrics/__init__.py +0 -0
  51. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/metrics/collector.py +0 -0
  52. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/metrics/prometheus.py +0 -0
  53. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/output_filters.py +0 -0
  54. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/plugins/base.py +0 -0
  55. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/plugins/loader.py +0 -0
  56. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/plugins/registry.py +0 -0
  57. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/routing/__init__.py +0 -0
  58. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/routing/adaptive.py +0 -0
  59. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/routing/auto_router.py +0 -0
  60. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/routing/budget.py +0 -0
  61. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/routing/capability.py +0 -0
  62. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/state/__init__.py +0 -0
  63. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/state/audit_log.py +0 -0
  64. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/state/replay.py +0 -0
  65. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/state/request_log.py +0 -0
  66. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/state/store.py +0 -0
  67. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/token_estimation.py +0 -0
  68. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/translation/__init__.py +0 -0
  69. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/translation/anthropic.py +0 -0
  70. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/translation/convert.py +0 -0
  71. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/coderouter/translation/tool_repair.py +0 -0
  72. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/architecture.md +0 -0
  73. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/assets/dashboard-demo.png +0 -0
  74. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/context-budget.md +0 -0
  75. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/continuous-probing.md +0 -0
  76. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/designs/v1.5-dashboard-mockup.html +0 -0
  77. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/designs/v1.6-auto-router-verification.md +0 -0
  78. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/designs/v1.6-auto-router.md +0 -0
  79. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/drift-detection.md +0 -0
  80. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/free-tier-guide.en.md +0 -0
  81. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/free-tier-guide.md +0 -0
  82. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/gguf_dl.md +0 -0
  83. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/hf-ollama-models.md +0 -0
  84. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/llamacpp-direct.en.md +0 -0
  85. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/llamacpp-direct.md +0 -0
  86. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/lmstudio-direct.en.md +0 -0
  87. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/lmstudio-direct.md +0 -0
  88. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/openrouter-roster/CHANGES.md +0 -0
  89. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/openrouter-roster/README.md +0 -0
  90. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/openrouter-roster/latest.json +0 -0
  91. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/partial-stitch.md +0 -0
  92. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/quickstart.en.md +0 -0
  93. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/quickstart.md +0 -0
  94. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/retrospectives/v0.4.md +0 -0
  95. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/retrospectives/v0.5-verify.md +0 -0
  96. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/retrospectives/v0.5.md +0 -0
  97. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/retrospectives/v0.6.md +0 -0
  98. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/retrospectives/v0.7.md +0 -0
  99. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/retrospectives/v1.0-verify.md +0 -0
  100. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/retrospectives/v1.0.md +0 -0
  101. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/security.en.md +0 -0
  102. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/security.md +0 -0
  103. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/troubleshooting.en.md +0 -0
  104. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/troubleshooting.md +0 -0
  105. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/usage-guide.en.md +0 -0
  106. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/usage-guide.md +0 -0
  107. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/when-do-i-need-coderouter.en.md +0 -0
  108. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/docs/when-do-i-need-coderouter.md +0 -0
  109. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/examples/.env.example +0 -0
  110. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/examples/providers.auto-custom.yaml +0 -0
  111. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/examples/providers.auto.yaml +0 -0
  112. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/examples/providers.note-2026.yaml +0 -0
  113. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/examples/providers.nvidia-nim.yaml +0 -0
  114. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/examples/providers.raspberrypi.yaml +0 -0
  115. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/examples/providers.v2-context-budget.yaml +0 -0
  116. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/examples/providers.yaml +0 -0
  117. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/scripts/demo_traffic.sh +0 -0
  118. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/scripts/openrouter_roster_diff.py +0 -0
  119. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/scripts/smoke_v2_2.sh +0 -0
  120. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/scripts/verify_v0_5.sh +0 -0
  121. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/scripts/verify_v1_0.sh +0 -0
  122. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/__init__.py +0 -0
  123. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/conftest.py +0 -0
  124. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_adapter_anthropic.py +0 -0
  125. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_audit_log.py +0 -0
  126. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_auto_router.py +0 -0
  127. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_backend_health.py +0 -0
  128. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_budget.py +0 -0
  129. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_capability.py +0 -0
  130. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_capability_degraded_payload.py +0 -0
  131. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_capability_registry.py +0 -0
  132. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_capability_registry_cache_control.py +0 -0
  133. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_claude_code_suitability.py +0 -0
  134. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_cli.py +0 -0
  135. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_cli_stats.py +0 -0
  136. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_config.py +0 -0
  137. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_context_budget.py +0 -0
  138. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_continuous_probe.py +0 -0
  139. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_dashboard_endpoint.py +0 -0
  140. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_doctor.py +0 -0
  141. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_doctor_apply.py +0 -0
  142. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_doctor_cache_probe.py +0 -0
  143. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_drift_actions.py +0 -0
  144. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_drift_detection.py +0 -0
  145. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_drift_detection_integration.py +0 -0
  146. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_env_file.py +0 -0
  147. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_env_security.py +0 -0
  148. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_errors.py +0 -0
  149. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_examples_yaml.py +0 -0
  150. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_fallback.py +0 -0
  151. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_fallback_anthropic.py +0 -0
  152. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_fallback_cache_control.py +0 -0
  153. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_fallback_cache_observed.py +0 -0
  154. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_fallback_misconfig_warn.py +0 -0
  155. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_fallback_paid_gate.py +0 -0
  156. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_fallback_thinking.py +0 -0
  157. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_guards_tool_loop.py +0 -0
  158. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_ingress_anthropic.py +0 -0
  159. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_ingress_profile.py +0 -0
  160. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_memory_pressure.py +0 -0
  161. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_metrics_cache.py +0 -0
  162. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_metrics_collector.py +0 -0
  163. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_metrics_cost.py +0 -0
  164. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_metrics_endpoint.py +0 -0
  165. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_metrics_jsonl.py +0 -0
  166. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_metrics_prometheus.py +0 -0
  167. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_metrics_prometheus_cache.py +0 -0
  168. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_openai_compat.py +0 -0
  169. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_openrouter_roster_diff.py +0 -0
  170. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_output_filters.py +0 -0
  171. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_output_filters_adapters.py +0 -0
  172. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_partial_stitch.py +0 -0
  173. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_reasoning_strip.py +0 -0
  174. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_request_log.py +0 -0
  175. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_routing_adaptive.py +0 -0
  176. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_self_healing.py +0 -0
  177. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_setup_sh.py +0 -0
  178. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_state_store.py +0 -0
  179. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_token_estimation.py +0 -0
  180. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_tool_repair.py +0 -0
  181. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_translation_anthropic.py +0 -0
  182. {coderouter_cli-2.3.0a3 → coderouter_cli-2.3.0a4}/tests/test_translation_reverse.py +0 -0
@@ -6,6 +6,51 @@ versioning follows [SemVer](https://semver.org/).
6
6
 
7
7
  ---
8
8
 
9
+ ## [v2.3.0a4] — 2026-05-08 (Plugin SDK — ruff cleanup)
10
+
11
+ Patch over `v2.3.0a3`. CI's `ruff check .` job surfaced six lint
12
+ findings in the new Plugin SDK code. None affect runtime behavior.
13
+
14
+ ### Fixed
15
+
16
+ - **RUF022**: `__all__` in `coderouter/plugins/__init__.py` is now
17
+ isort-sorted alphabetically.
18
+ - **RUF006**: `_fanout_observers` was using `asyncio.create_task`
19
+ without holding a strong reference. Asyncio's task tracker only
20
+ keeps a weakref, so a fanout-in-flight task could be GC'd before
21
+ the observer ran. Fixed by storing tasks in a per-engine
22
+ ``_observer_tasks: set[asyncio.Task[None]]`` and removing each
23
+ via ``task.add_done_callback(set.discard)`` on completion. The
24
+ attribute is lazy-initialized in `_fanout_observers` itself so
25
+ engines built via ``__new__`` (which bypass ``__init__``) still
26
+ work.
27
+ - **I001 + F841**: `tests/test_plugins_integration.py` had unused
28
+ imports (`AnthropicResponse`, `AnthropicUsage`) and an unused
29
+ local (`captured_chat`) left over from a build-engine helper
30
+ whose code path never ran. Removed the helper entirely; the
31
+ remaining tests exercise the engine's hook surface
32
+ (``_apply_input_filters`` / ``_fanout_observers`` /
33
+ ``_safe_observe``) directly, which is what they always actually
34
+ did.
35
+ - **I001**: `tests/test_plugins_loader.py` import block reordered
36
+ alphabetically by module name.
37
+
38
+ ### Files touched
39
+
40
+ ```
41
+ M coderouter/plugins/__init__.py — __all__ alphabetical
42
+ M coderouter/routing/fallback.py — task strong-ref set
43
+ M tests/test_plugins_integration.py — drop dead helper
44
+ M tests/test_plugins_loader.py — import order
45
+ M tests/test_plugins_registry.py — formatting nit (blank line)
46
+ M CHANGELOG.md, pyproject.toml — 2.3.0a3 → 2.3.0a4
47
+ ```
48
+
49
+ After this patch, ``ruff check .`` passes against every tracked
50
+ Python file in the repo.
51
+
52
+ ---
53
+
9
54
  ## [v2.3.0a3] — 2026-05-08 (Plugin SDK — LogRecord.module collision fix)
10
55
 
11
56
  Patch over `v2.3.0a2`. The wheel-install-and-test job in CI surfaced
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coderouter-cli
3
- Version: 2.3.0a3
3
+ Version: 2.3.0a4
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
@@ -43,17 +43,14 @@ from coderouter.plugins.loader import (
43
43
  from coderouter.plugins.registry import PluginRegistry
44
44
 
45
45
  __all__ = [
46
- # Active hooks
47
- "InputFilter",
48
- "Observer",
49
- # Future hooks (Protocol-only, no engine integration yet)
46
+ "PLUGIN_GROUPS_FUTURE",
47
+ "PLUGIN_GROUPS_V2_3",
48
+ "Adapter",
50
49
  "Frontend",
51
50
  "Guard",
51
+ "InputFilter",
52
+ "Observer",
52
53
  "OutputFilter",
53
- "Adapter",
54
- # Discovery + container
55
54
  "PluginRegistry",
56
55
  "discover_and_load",
57
- "PLUGIN_GROUPS_V2_3",
58
- "PLUGIN_GROUPS_FUTURE",
59
56
  ]
@@ -838,6 +838,12 @@ class FallbackEngine:
838
838
  # so tests that build the engine via ``FallbackEngine.__new__``
839
839
  # see an empty registry instead of AttributeError.
840
840
  self._plugin_registry: PluginRegistry = plugins or PluginRegistry.empty()
841
+ # v2.3.0: holds strong refs to in-flight Observer fanout tasks
842
+ # so the asyncio event loop's weak-ref bookkeeping doesn't GC
843
+ # them mid-flight (RUF006). Tasks remove themselves on done
844
+ # via ``add_done_callback(_observer_tasks.discard)`` in
845
+ # :meth:`_fanout_observers`.
846
+ self._observer_tasks: set[asyncio.Task[None]] = set()
841
847
  # Cache adapters so we don't re-instantiate per request
842
848
  self._adapters: dict[str, BaseAdapter] = {
843
849
  p.name: build_adapter(p) for p in config.providers
@@ -1881,10 +1887,22 @@ class FallbackEngine:
1881
1887
  observers = self.plugins.observers
1882
1888
  if not observers:
1883
1889
  return
1890
+ # Lazy-init the task set for engines built via ``__new__`` —
1891
+ # mirrors the lazy ``plugins`` property pattern so legacy
1892
+ # tests that bypass __init__ don't crash here.
1893
+ if not hasattr(self, "_observer_tasks"):
1894
+ self._observer_tasks = set()
1884
1895
  for obs in observers:
1885
- asyncio.create_task(
1896
+ task = asyncio.create_task(
1886
1897
  self._safe_observe(obs, event_type, payload)
1887
1898
  )
1899
+ # Strong-ref keeps the task alive past the loop iteration;
1900
+ # ``discard`` cleans up after the task completes (success
1901
+ # or exception). Avoids the RUF006 footgun where
1902
+ # asyncio.create_task's weakref-only bookkeeping can let
1903
+ # the loop GC a fanout-in-progress task.
1904
+ self._observer_tasks.add(task)
1905
+ task.add_done_callback(self._observer_tasks.discard)
1888
1906
 
1889
1907
  async def _safe_observe(
1890
1908
  self,
@@ -11,7 +11,7 @@
11
11
  # in plan.md §11.B; once granted, this name will become an alias and
12
12
  # `coderouter` will become the canonical distribution name.
13
13
  name = "coderouter-cli"
14
- version = "2.3.0a3"
14
+ version = "2.3.0a4"
15
15
  description = "Local-first, free-first, fallback-built-in LLM router. Claude Code / OpenAI compatible."
16
16
  readme = "README.md"
17
17
  requires-python = ">=3.12"
@@ -1,17 +1,14 @@
1
1
  """Integration tests: FallbackEngine + InputFilter / Observer hooks (v2.3.0).
2
2
 
3
- Exercises the actual hot path:
4
-
5
- - engine.generate_anthropic invokes registered InputFilters before
6
- chain dispatch
7
- - a successful response triggers observer fanout with the
8
- ``request_completed`` event
9
- - a failing filter is logged + skipped, the chain still runs
10
- - the no-plugin path is bit-identical to v2.2.0 (zero hook calls)
11
-
12
- Adapter calls are mocked at the dispatch level so we don't reach
13
- HTTPx — keeps the tests fast and offline.
3
+ These exercise the engine-side helpers directly
4
+ (``_apply_input_filters`` / ``_fanout_observers`` /
5
+ ``_safe_observe``). We don't actually invoke ``generate_anthropic`` /
6
+ ``stream_anthropic`` here because doing so would mean stubbing the
7
+ entire HTTP / chain dispatch path; the helpers themselves carry the
8
+ plugin contract, and the broader engine paths are covered by
9
+ ``test_fallback*.py``.
14
10
  """
11
+
15
12
  from __future__ import annotations
16
13
 
17
14
  import asyncio
@@ -22,13 +19,7 @@ import pytest
22
19
  from coderouter.config.schemas import CodeRouterConfig
23
20
  from coderouter.plugins.registry import PluginRegistry
24
21
  from coderouter.routing.fallback import FallbackEngine
25
- from coderouter.translation.anthropic import (
26
- AnthropicMessage,
27
- AnthropicRequest,
28
- AnthropicResponse,
29
- AnthropicUsage,
30
- )
31
-
22
+ from coderouter.translation.anthropic import AnthropicMessage, AnthropicRequest
32
23
 
33
24
  # ---------------------------------------------------------------------
34
25
  # Synthetic plugin classes
@@ -78,101 +69,10 @@ class _RecordingObserver:
78
69
 
79
70
 
80
71
  # ---------------------------------------------------------------------
81
- # Fixtures + helpers
72
+ # Helpers
82
73
  # ---------------------------------------------------------------------
83
74
 
84
75
 
85
- @pytest.fixture
86
- def fake_response() -> AnthropicResponse:
87
- return AnthropicResponse(
88
- id="msg_test",
89
- type="message",
90
- role="assistant",
91
- content=[{"type": "text", "text": "ok"}],
92
- model="x",
93
- stop_reason="end_turn",
94
- usage=AnthropicUsage(input_tokens=5, output_tokens=2),
95
- )
96
-
97
-
98
- def _build_engine(
99
- basic_config: CodeRouterConfig,
100
- *,
101
- plugins: PluginRegistry | None = None,
102
- fake_response: AnthropicResponse | None = None,
103
- ) -> tuple[FallbackEngine, list[AnthropicRequest]]:
104
- """Build an engine and stub its first adapter to capture the
105
- request and return ``fake_response``.
106
-
107
- Returns ``(engine, captured_requests)`` — captured_requests is the
108
- list of requests the (mocked) adapter saw, useful for asserting
109
- that the InputFilter's mutation reached the dispatch layer.
110
- """
111
- engine = FallbackEngine(basic_config, plugins=plugins)
112
- captured: list[AnthropicRequest] = []
113
-
114
- # Stub: only used when test hands us a fake_response to return.
115
- if fake_response is not None:
116
- async def stub_generate_anthropic(
117
- req: AnthropicRequest, *, overrides: Any = None
118
- ) -> AnthropicResponse:
119
- captured.append(req)
120
- return fake_response
121
-
122
- # Wrap the first adapter into a "native Anthropic" shape.
123
- first_name = next(iter(engine._adapters))
124
- first_adapter = engine._adapters[first_name]
125
- # Convert the stub into AnthropicAdapter-shaped surface by
126
- # patching the method directly. The engine's isinstance check
127
- # for AnthropicAdapter still matters; instead of fighting it,
128
- # we use a simpler approach: replace the chain resolution to
129
- # always pick our stub adapter, AND patch generate_anthropic.
130
- from coderouter.adapters.anthropic_native import AnthropicAdapter
131
-
132
- if not isinstance(first_adapter, AnthropicAdapter):
133
- # Not an Anthropic-native adapter — patch generate() and
134
- # rely on the openai_compat code path which calls
135
- # to_chat_request → adapter.generate() → to_anthropic_response.
136
- async def stub_generate(
137
- chat_req: Any, *, overrides: Any = None
138
- ) -> Any:
139
- # Build a minimal ChatResponse using the existing
140
- # AnthropicResponse text. We import lazily to avoid a
141
- # heavy module import at test collection time.
142
- from coderouter.translation.convert import to_chat_request # noqa: F401
143
- from coderouter.translation import ( # noqa: F401
144
- ChatResponse,
145
- ChatResponseMessage,
146
- )
147
-
148
- # Capture the inbound chat request so we can also
149
- # introspect what InputFilters mutated.
150
- captured_chat = chat_req
151
- # Build a chat-shaped response.
152
- return ChatResponse( # type: ignore[call-arg]
153
- id="cmpl_test",
154
- object="chat.completion",
155
- created=0,
156
- model=chat_req.model,
157
- choices=[
158
- {
159
- "index": 0,
160
- "message": {"role": "assistant", "content": "ok"},
161
- "finish_reason": "stop",
162
- }
163
- ],
164
- usage={"prompt_tokens": 5, "completion_tokens": 2, "total_tokens": 7},
165
- )
166
-
167
- first_adapter.generate = stub_generate # type: ignore[method-assign]
168
-
169
- # Always patch generate_anthropic regardless — if the adapter
170
- # IS Anthropic-native this is the path; otherwise it's harmless.
171
- first_adapter.generate_anthropic = stub_generate_anthropic # type: ignore[method-assign,attr-defined]
172
-
173
- return engine, captured
174
-
175
-
176
76
  def _make_request(text: str = "hi") -> AnthropicRequest:
177
77
  return AnthropicRequest(
178
78
  model="x",
@@ -188,15 +88,13 @@ def _make_request(text: str = "hi") -> AnthropicRequest:
188
88
 
189
89
  def test_no_plugins_means_no_hook_calls(basic_config: CodeRouterConfig) -> None:
190
90
  """With an empty registry, hook helpers are short-circuit no-ops."""
191
- engine, _ = _build_engine(basic_config)
91
+ engine = FallbackEngine(basic_config)
192
92
 
193
- # Direct private-method calls confirm zero work happens with no plugins.
93
+ # InputFilter chain returns request unchanged.
194
94
  out = asyncio.run(engine._apply_input_filters(_make_request()))
195
95
  assert out.messages[0].content == "hi"
196
96
 
197
- # _fanout_observers with empty registry must NOT spawn tasks.
198
- # Synchronous probe: just confirm it returns without error and
199
- # doesn't iterate (no tasks pending afterwards).
97
+ # Observer fanout with empty registry must NOT spawn tasks.
200
98
  engine._fanout_observers("anything", request=None, response=None)
201
99
 
202
100
 
@@ -254,7 +152,6 @@ def test_safe_observe_swallows_exceptions(
254
152
  obs = BadObserver()
255
153
 
256
154
  with caplog.at_level("WARNING"):
257
- # Direct call — the fanout wrapper does the same try/except.
258
155
  asyncio.run(engine._safe_observe(obs, "request_completed", {}))
259
156
 
260
157
  assert any(rec.msg == "observer-failed" for rec in caplog.records)
@@ -279,8 +176,6 @@ def test_observer_fanout_creates_tasks_when_observers_present(
279
176
  engine._fanout_observers("custom_event", payload_key="value")
280
177
  # Yield control so the spawned task gets a chance to run.
281
178
  await asyncio.sleep(0)
282
- # Give the loop one more tick to ensure the fire-and-forget
283
- # coroutine actually completes before we inspect.
284
179
  await asyncio.sleep(0)
285
180
 
286
181
  asyncio.run(run())
@@ -11,6 +11,7 @@ while exercising the loader's real path: enabled-allowlist filtering,
11
11
  ``__init__`` dispatch with config dict, and degraded-continue on
12
12
  construction failure.
13
13
  """
14
+
14
15
  from __future__ import annotations
15
16
 
16
17
  import importlib.metadata as md
@@ -28,7 +29,6 @@ from coderouter.config.schemas import (
28
29
  from coderouter.plugins import loader as loader_mod
29
30
  from coderouter.plugins.loader import discover_and_load
30
31
 
31
-
32
32
  # ---------------------------------------------------------------------
33
33
  # Synthetic plugin classes — module-level so EntryPoint.load() can
34
34
  # resolve "tests.test_plugins_loader:..." strings via importlib.
@@ -187,9 +187,7 @@ def test_failing_init_does_not_abort_loader(
187
187
  )
188
188
  monkeypatch.setattr(loader_mod.md, "entry_points", fake_eps)
189
189
 
190
- cfg = _make_config(
191
- plugins_cfg=PluginsConfig(enabled=["bad-init", "good-filter"])
192
- )
190
+ cfg = _make_config(plugins_cfg=PluginsConfig(enabled=["bad-init", "good-filter"]))
193
191
  with caplog.at_level("ERROR"):
194
192
  reg = discover_and_load(cfg)
195
193
 
@@ -256,6 +254,4 @@ def test_future_group_logs_warning_but_still_loads(
256
254
  reg = discover_and_load(cfg)
257
255
 
258
256
  assert reg.count("frontend") == 1
259
- assert any(
260
- rec.msg == "plugin-group-not-yet-active" for rec in caplog.records
261
- )
257
+ assert any(rec.msg == "plugin-group-not-yet-active" for rec in caplog.records)
@@ -1,4 +1,5 @@
1
1
  """Unit tests for ``coderouter.plugins.registry`` (v2.3.0)."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from coderouter.plugins.registry import PluginRegistry