coding-proxy 0.2.3a5__tar.gz → 0.2.4a2__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 (150) hide show
  1. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/CHANGELOG.md +1 -1
  2. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/PKG-INFO +5 -1
  3. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/README.md +4 -0
  4. coding_proxy-0.2.4a2/assets/dashboard-v0.2.3.png +0 -0
  5. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/docs/zh-CN/README.md +4 -0
  6. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/pyproject.toml +1 -1
  7. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/config/config.default.yaml +6 -6
  8. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/logging/stats.py +1 -1
  9. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/model/pricing.py +2 -2
  10. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/routing/executor.py +96 -2
  11. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/routing/router.py +8 -2
  12. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/server/dashboard.py +392 -128
  13. coding_proxy-0.2.4a2/src/coding/proxy/server/request_normalizer.py +541 -0
  14. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/server/routes.py +17 -4
  15. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/vendors/anthropic.py +2 -53
  16. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_app_routes.py +10 -10
  17. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_currency.py +3 -3
  18. coding_proxy-0.2.4a2/tests/test_request_normalizer.py +983 -0
  19. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_router_executor.py +191 -0
  20. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_vendors.py +37 -76
  21. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/uv.lock +1 -1
  22. coding_proxy-0.2.3a5/src/coding/proxy/server/request_normalizer.py +0 -288
  23. coding_proxy-0.2.3a5/tests/test_request_normalizer.py +0 -368
  24. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/.github/workflows/ci.yml +0 -0
  25. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/.github/workflows/coverage.yml +0 -0
  26. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/.github/workflows/release.yml +0 -0
  27. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/.gitignore +0 -0
  28. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/AGENTS.md +0 -0
  29. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/CLAUDE.md +0 -0
  30. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/LICENSE +0 -0
  31. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/docs/ci-cd.md +0 -0
  32. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/docs/framework.md +0 -0
  33. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/docs/user-guide.md +0 -0
  34. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/__init__.py +0 -0
  35. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/__init__.py +0 -0
  36. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/__main__.py +0 -0
  37. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/auth/__init__.py +0 -0
  38. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/auth/providers/__init__.py +0 -0
  39. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/auth/providers/base.py +0 -0
  40. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/auth/providers/github.py +0 -0
  41. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/auth/providers/google.py +0 -0
  42. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/auth/runtime.py +0 -0
  43. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/auth/store.py +0 -0
  44. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/cli/__init__.py +0 -0
  45. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/cli/auth_commands.py +0 -0
  46. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/cli/banner.py +0 -0
  47. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/compat/__init__.py +0 -0
  48. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/compat/canonical.py +0 -0
  49. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/compat/session_store.py +0 -0
  50. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/config/__init__.py +0 -0
  51. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/config/auth_schema.py +0 -0
  52. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/config/loader.py +0 -0
  53. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/config/resiliency.py +0 -0
  54. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/config/routing.py +0 -0
  55. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/config/schema.py +0 -0
  56. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/config/server.py +0 -0
  57. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/config/vendors.py +0 -0
  58. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/convert/__init__.py +0 -0
  59. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/convert/anthropic_to_gemini.py +0 -0
  60. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/convert/anthropic_to_openai.py +0 -0
  61. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/convert/gemini_sse_adapter.py +0 -0
  62. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/convert/gemini_to_anthropic.py +0 -0
  63. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/convert/openai_to_anthropic.py +0 -0
  64. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/logging/__init__.py +0 -0
  65. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/logging/db.py +0 -0
  66. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/logging/formatters.py +0 -0
  67. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/model/__init__.py +0 -0
  68. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/model/auth.py +0 -0
  69. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/model/compat.py +0 -0
  70. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/model/constants.py +0 -0
  71. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/model/token.py +0 -0
  72. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/model/vendor.py +0 -0
  73. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/pricing.py +0 -0
  74. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/routing/__init__.py +0 -0
  75. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/routing/circuit_breaker.py +0 -0
  76. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/routing/error_classifier.py +0 -0
  77. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/routing/model_mapper.py +0 -0
  78. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/routing/quota_guard.py +0 -0
  79. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/routing/rate_limit.py +0 -0
  80. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/routing/retry.py +0 -0
  81. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/routing/session_manager.py +0 -0
  82. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/routing/tier.py +0 -0
  83. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/routing/usage_parser.py +0 -0
  84. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/routing/usage_recorder.py +0 -0
  85. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/server/__init__.py +0 -0
  86. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/server/app.py +0 -0
  87. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/server/factory.py +0 -0
  88. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/server/responses.py +0 -0
  89. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/streaming/__init__.py +0 -0
  90. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/streaming/anthropic_compat.py +0 -0
  91. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/vendors/__init__.py +0 -0
  92. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/vendors/alibaba.py +0 -0
  93. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/vendors/antigravity.py +0 -0
  94. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/vendors/base.py +0 -0
  95. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/vendors/copilot.py +0 -0
  96. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/vendors/copilot_models.py +0 -0
  97. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/vendors/copilot_token_manager.py +0 -0
  98. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/vendors/copilot_urls.py +0 -0
  99. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/vendors/doubao.py +0 -0
  100. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/vendors/kimi.py +0 -0
  101. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/vendors/minimax.py +0 -0
  102. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/vendors/mixins.py +0 -0
  103. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/vendors/native_anthropic.py +0 -0
  104. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/vendors/token_manager.py +0 -0
  105. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/vendors/xiaomi.py +0 -0
  106. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/src/coding/proxy/vendors/zhipu.py +0 -0
  107. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/__init__.py +0 -0
  108. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_antigravity.py +0 -0
  109. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_auto_login.py +0 -0
  110. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_banner.py +0 -0
  111. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_circuit_breaker.py +0 -0
  112. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_cli_usage.py +0 -0
  113. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_compat.py +0 -0
  114. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_config_init.py +0 -0
  115. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_config_loader.py +0 -0
  116. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_convert_request.py +0 -0
  117. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_convert_response.py +0 -0
  118. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_convert_sse.py +0 -0
  119. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_copilot.py +0 -0
  120. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_copilot_convert_request.py +0 -0
  121. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_copilot_convert_response.py +0 -0
  122. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_copilot_models.py +0 -0
  123. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_copilot_urls.py +0 -0
  124. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_error_classifier.py +0 -0
  125. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_logging_dual_write.py +0 -0
  126. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_mixins.py +0 -0
  127. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_model_auth.py +0 -0
  128. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_model_compat.py +0 -0
  129. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_model_constants.py +0 -0
  130. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_model_mapper.py +0 -0
  131. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_model_pricing.py +0 -0
  132. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_model_token.py +0 -0
  133. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_model_vendor.py +0 -0
  134. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_native_vendors.py +0 -0
  135. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_parse_usage.py +0 -0
  136. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_pricing.py +0 -0
  137. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_quota_guard.py +0 -0
  138. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_rate_limit.py +0 -0
  139. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_router_chain.py +0 -0
  140. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_runtime_reauth.py +0 -0
  141. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_schema.py +0 -0
  142. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_streaming_anthropic_compat.py +0 -0
  143. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_tier.py +0 -0
  144. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_tiers_config.py +0 -0
  145. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_time_range.py +0 -0
  146. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_token_logger.py +0 -0
  147. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_token_manager.py +0 -0
  148. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_types.py +0 -0
  149. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_vendor_streaming.py +0 -0
  150. {coding_proxy-0.2.3a5 → coding_proxy-0.2.4a2}/tests/test_zhipu.py +0 -0
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
- ## [v0.2.3](https://github.com/ThreeFish-AI/coding-proxy/releases/tag/v0.2.3a1) — 2026-04-15
7
+ ## [v0.2.3](https://github.com/ThreeFish-AI/coding-proxy/releases/tag/v0.2.3) — 2026-04-16
8
8
 
9
9
  - feat(dashboard): 新增实时 Web Dashboard 页面,聚合展示流量与用量统计;
10
10
  - docs(user-guide): 补充 POST /v1/messages 完整 API 参考文档;
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coding-proxy
3
- Version: 0.2.3a5
3
+ Version: 0.2.4a2
4
4
  Summary: A High-Availability, Transparent, and Smart Multi-Vendor Proxy for Claude Code. Support Claude Plans, GitHub Copilot, Google Antigravity, ZAI/GLM, MiniMax, Qwen, Xiaomi, Kimi, Doubao...
5
5
  Project-URL: Source Code, https://github.com/ThreeFish-AI/coding-proxy
6
6
  Project-URL: User Guide, https://github.com/ThreeFish-AI/coding-proxy/blob/master/docs/user-guide.md
@@ -56,6 +56,10 @@ When you're deeply immersed in your coding "zone" with **Claude Code** (or any A
56
56
 
57
57
  ## 🌟 Core Features
58
58
 
59
+ <div align="center">
60
+ <img src="assets/dashboard-v0.2.3.png">
61
+ </div>
62
+
59
63
  - **⛓️ N-tier Chained Failover**: Autonomous descending sequence, supporting Claude's official plans, as well as Coding Plans from GitHub Copilot, Z AI, MiniMax, Alibaba Qwen, Xiaomi, Kimi, Doubao, etc.
60
64
  - **🛡️ Smart Resilience & Quota Guardians**: Every single vendor node comes fully armed with an independent **Circuit Breaker** and **Quota Guard** to proactively dodge avalanches without breaking a sweat.
61
65
  - **👻 Phantom-like Transparency**: **100% transparent** to the client! No code tweaks required. Overwrite `ANTHROPIC_BASE_URL` with a single line, and you're good to go.
@@ -29,6 +29,10 @@ When you're deeply immersed in your coding "zone" with **Claude Code** (or any A
29
29
 
30
30
  ## 🌟 Core Features
31
31
 
32
+ <div align="center">
33
+ <img src="assets/dashboard-v0.2.3.png">
34
+ </div>
35
+
32
36
  - **⛓️ N-tier Chained Failover**: Autonomous descending sequence, supporting Claude's official plans, as well as Coding Plans from GitHub Copilot, Z AI, MiniMax, Alibaba Qwen, Xiaomi, Kimi, Doubao, etc.
33
37
  - **🛡️ Smart Resilience & Quota Guardians**: Every single vendor node comes fully armed with an independent **Circuit Breaker** and **Quota Guard** to proactively dodge avalanches without breaking a sweat.
34
38
  - **👻 Phantom-like Transparency**: **100% transparent** to the client! No code tweaks required. Overwrite `ANTHROPIC_BASE_URL` with a single line, and you're good to go.
@@ -29,6 +29,10 @@
29
29
 
30
30
  ## 🌟 核心特性 (Core Features)
31
31
 
32
+ <div align="center">
33
+ <img src="../../assets/dashboard-v0.2.3.png">
34
+ </div>
35
+
32
36
  - **⛓️ N-tier 链式故障转移 (Failover)**:自主降序序列,支持 Claude 官方 Plans,以及 GitHub Copilot、智谱、MiniMax、阿里千问、小米、Kimi、豆包等的 Coding Plan。
33
37
  - **🛡️ 智能弹性与容灾守卫**:每个供应商节点独立配备 **熔断器 (Circuit Breaker)** 与 **配额守卫 (Quota Guard)**,防雪崩、主动避险。
34
38
  - **👻 透明无感代理机制**:对客户端 **100% 透明**!无需修改任何代码,仅需一行配置覆盖 `ANTHROPIC_BASE_URL` 即可接入。
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "coding-proxy"
3
- version = "0.2.3a5"
3
+ version = "0.2.4a2"
4
4
  description = "A High-Availability, Transparent, and Smart Multi-Vendor Proxy for Claude Code. Support Claude Plans, GitHub Copilot, Google Antigravity, ZAI/GLM, MiniMax, Qwen, Xiaomi, Kimi, Doubao..."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -51,14 +51,14 @@ vendors:
51
51
  recovery_timeout_seconds: 300
52
52
  success_threshold: 2
53
53
  quota_guard:
54
- enabled: true
55
- token_budget: 45000000 # 5 小时 token 预算(根据订阅计划调整)
54
+ enabled: false
55
+ token_budget: 60000000 # 5 小时 token 预算(根据订阅计划调整)
56
56
  window_hours: 5.0
57
57
  threshold_percent: 99.0
58
58
  probe_interval_seconds: 300
59
59
  weekly_quota_guard:
60
- enabled: true
61
- token_budget: 500000000 # 一周 token 预算(根据订阅计划调整)
60
+ enabled: false
61
+ token_budget: 800000000 # 一周 token 预算(根据订阅计划调整)
62
62
  window_hours: 168.0 # 7 天滑动窗口
63
63
  threshold_percent: 99.0
64
64
  probe_interval_seconds: 1800 # 每 30 分钟探测一次
@@ -84,7 +84,7 @@ vendors:
84
84
 
85
85
  # Vendor 2: Google Antigravity Plans(中间层,默认禁用)
86
86
  - vendor: antigravity
87
- enabled: true # 启用需配置 OAuth 凭据
87
+ enabled: false # 启用需配置 OAuth 凭据
88
88
  client_id: "${GOOG_CLIENT_ID}" # Google OAuth2 Client ID
89
89
  client_secret: "${GOOG_CLIENT_SECRET}" # Google OAuth2 Client Secret
90
90
  refresh_token: "${GOOG_REFRESH_TOKEN}" # Google OAuth2 Refresh Token
@@ -96,7 +96,7 @@ vendors:
96
96
  recovery_timeout_seconds: 300
97
97
  success_threshold: 2
98
98
  quota_guard:
99
- enabled: true # 启用后按 Premium Requests 配额管理
99
+ enabled: false # 启用后按 Premium Requests 配额管理
100
100
  token_budget: 0
101
101
  window_hours: 24.0
102
102
  threshold_percent: 95.0
@@ -198,7 +198,7 @@ async def show_usage(
198
198
 
199
199
  if cost_totals:
200
200
  total_cost_str = " + ".join(
201
- f"{cur.symbol}{amt:.4f}" for cur, amt in cost_totals.items()
201
+ f"{cur.symbol}{amt:.2f}" for cur, amt in cost_totals.items()
202
202
  )
203
203
  else:
204
204
  total_cost_str = "-"
@@ -50,8 +50,8 @@ class CostValue:
50
50
  amount: float
51
51
  currency: Currency = Currency.default()
52
52
 
53
- def format(self, precision: int = 4) -> str:
54
- """格式化为 ``$0.1234`` 或 ``¥0.1234``."""
53
+ def format(self, precision: int = 2) -> str:
54
+ """格式化为 ``$0.12`` 或 ``¥0.12``."""
55
55
  return f"{self.currency.symbol}{self.amount:.{precision}f}"
56
56
 
57
57
  @property
@@ -6,6 +6,7 @@
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
+ import copy
9
10
  import logging
10
11
  import time
11
12
  from collections.abc import AsyncIterator
@@ -222,10 +223,94 @@ class _RouteExecutor:
222
223
 
223
224
  # ── 公开执行入口 ──────────────────────────────────────
224
225
 
226
+ def _prepare_body_for_tier(
227
+ self,
228
+ body: dict[str, Any],
229
+ tier: VendorTier,
230
+ normalization: Any = None,
231
+ session_record: Any = None,
232
+ ) -> dict[str, Any]:
233
+ """为指定 tier 准备请求体,必要时应用 Anthropic 专属修复(Phase 2).
234
+
235
+ 仅当 tier 为 Anthropic 时才执行以下处理:
236
+ 1. tool_result 重定位 + 孤儿修复(需 normalization.has_anthropic_fixes)
237
+ 2. 条件化 thinking block 剥离(仅跨供应商场景)
238
+
239
+ 确保 Zhipu 等其他 vendor 不受影响。
240
+ """
241
+ if tier.name != "anthropic":
242
+ return body
243
+
244
+ needs_tool_fixes = (
245
+ normalization is not None and normalization.has_anthropic_fixes
246
+ )
247
+ needs_thinking_strip = self._needs_thinking_strip(normalization, session_record)
248
+
249
+ if not needs_tool_fixes and not needs_thinking_strip:
250
+ return body
251
+
252
+ from ..server.request_normalizer import (
253
+ apply_anthropic_specific_fixes,
254
+ strip_thinking_blocks,
255
+ )
256
+
257
+ body_for_vendor = copy.deepcopy(body)
258
+
259
+ if needs_tool_fixes:
260
+ fixes = apply_anthropic_specific_fixes(
261
+ body_for_vendor.get("messages", []),
262
+ normalization.misplaced_tool_results,
263
+ normalization.misplaced_log_info,
264
+ )
265
+ if fixes:
266
+ logger.debug(
267
+ "Applied Anthropic-specific fixes for tier %s: %s",
268
+ tier.name,
269
+ ", ".join(fixes),
270
+ )
271
+
272
+ if needs_thinking_strip:
273
+ stripped = strip_thinking_blocks(body_for_vendor)
274
+ if stripped:
275
+ logger.debug(
276
+ "Stripped %d thinking block(s) for cross-vendor compatibility",
277
+ stripped,
278
+ )
279
+
280
+ return body_for_vendor
281
+
282
+ @staticmethod
283
+ def _needs_thinking_strip(normalization: Any, session_record: Any) -> bool:
284
+ """判断是否需要剥离 thinking blocks(仅跨供应商场景).
285
+
286
+ 信号优先级:
287
+ 1. 请求规范化信号 — 当前请求体中检测到跨供应商产物
288
+ 2. 会话历史信号 — provider_state 中存在非 Anthropic 供应商记录
289
+
290
+ 安全默认:当无法确定会话来源时(session_record 为 None),
291
+ 回退到始终剥离,确保与 compat_session_store 未配置时的向后兼容。
292
+ """
293
+ # Signal 1: normalization 检测到跨供应商产物
294
+ if normalization is not None and normalization.has_cross_vendor_signals:
295
+ return True
296
+ # Signal 2: 无会话追踪能力 → 无法判断是否跨供应商 → 安全回退到剥离
297
+ if session_record is None:
298
+ return True
299
+ # Signal 3: 会话历史中有非 Anthropic 供应商
300
+ if session_record.provider_state:
301
+ non_anthropic = {
302
+ v for v in session_record.provider_state if v != "anthropic"
303
+ }
304
+ if non_anthropic:
305
+ return True
306
+ # 纯 Anthropic 会话,无跨供应商信号 → 保留 thinking blocks
307
+ return False
308
+
225
309
  async def execute_stream(
226
310
  self,
227
311
  body: dict[str, Any],
228
312
  headers: dict[str, str],
313
+ normalization: Any = None,
229
314
  ) -> AsyncIterator[tuple[bytes, str]]:
230
315
  """路由流式请求,按优先级尝试各层级."""
231
316
  last_idx = len(self._tiers) - 1
@@ -257,7 +342,12 @@ class _RouteExecutor:
257
342
  usage: dict[str, Any] = {}
258
343
 
259
344
  try:
260
- async for chunk in tier.vendor.send_message_stream(body, headers):
345
+ body_for_tier = self._prepare_body_for_tier(
346
+ body, tier, normalization, session_record=session_record
347
+ )
348
+ async for chunk in tier.vendor.send_message_stream(
349
+ body_for_tier, headers
350
+ ):
261
351
  parse_usage_from_chunk(
262
352
  chunk,
263
353
  usage,
@@ -389,6 +479,7 @@ class _RouteExecutor:
389
479
  self,
390
480
  body: dict[str, Any],
391
481
  headers: dict[str, str],
482
+ normalization: Any = None,
392
483
  ) -> VendorResponse:
393
484
  """路由非流式请求,按优先级尝试各层级."""
394
485
  last_idx = len(self._tiers) - 1
@@ -417,7 +508,10 @@ class _RouteExecutor:
417
508
  continue
418
509
 
419
510
  try:
420
- resp = await tier.vendor.send_message(body, headers)
511
+ body_for_tier = self._prepare_body_for_tier(
512
+ body, tier, normalization, session_record=session_record
513
+ )
514
+ resp = await tier.vendor.send_message(body_for_tier, headers)
421
515
 
422
516
  if resp.status_code < 400:
423
517
  duration = int((time.monotonic() - start) * 1000)
@@ -134,18 +134,24 @@ class RequestRouter:
134
134
  self,
135
135
  body: dict[str, Any],
136
136
  headers: dict[str, str],
137
+ normalization: Any = None,
137
138
  ) -> AsyncIterator[tuple[bytes, str]]:
138
139
  """路由流式请求,按优先级尝试各层级."""
139
- async for chunk, vendor_name in self._executor.execute_stream(body, headers):
140
+ async for chunk, vendor_name in self._executor.execute_stream(
141
+ body, headers, normalization=normalization
142
+ ):
140
143
  yield chunk, vendor_name
141
144
 
142
145
  async def route_message(
143
146
  self,
144
147
  body: dict[str, Any],
145
148
  headers: dict[str, str],
149
+ normalization: Any = None,
146
150
  ) -> Any:
147
151
  """路由非流式请求,按优先级尝试各层级."""
148
- return await self._executor.execute_message(body, headers)
152
+ return await self._executor.execute_message(
153
+ body, headers, normalization=normalization
154
+ )
149
155
 
150
156
  # ── 生命周期 ───────────────────────────────────────────
151
157