coding-proxy 0.3.1a10__tar.gz → 0.4.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.
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/.gitignore +3 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/CHANGELOG.md +19 -8
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/PKG-INFO +1 -1
- coding_proxy-0.4.0/assets/session-v0.4.0.png +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/pyproject.toml +1 -1
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/server/dashboard.py +164 -61
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/uv.lock +1 -1
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/.github/workflows/ci.yml +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/.github/workflows/coverage.yml +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/.github/workflows/release.yml +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/.pre-commit-config.yaml +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/AGENTS.md +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/CLAUDE.md +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/LICENSE +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/README.md +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/assets/dashboard-v0.2.4.png +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/docs/arch/config-reference.md +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/docs/arch/convert.md +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/docs/arch/design-patterns.md +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/docs/arch/routing.md +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/docs/arch/testing.md +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/docs/arch/vendors.md +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/docs/ci-cd.md +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/docs/framework.md +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/docs/guide/api-reference.md +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/docs/guide/cli-reference.md +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/docs/guide/dashboard.md +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/docs/guide/monitoring.md +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/docs/guide/quickstart.md +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/docs/guide/vendors.md +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/docs/issue.md +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/docs/user-guide.md +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/docs/zh-CN/README.md +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/__init__.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/__init__.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/__main__.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/auth/__init__.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/auth/providers/__init__.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/auth/providers/base.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/auth/providers/github.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/auth/providers/google.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/auth/runtime.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/auth/store.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/cli/__init__.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/cli/auth_commands.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/cli/banner.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/compat/__init__.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/compat/canonical.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/compat/session_store.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/config/__init__.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/config/auth_schema.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/config/config.default.yaml +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/config/loader.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/config/resiliency.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/config/routing.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/config/schema.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/config/server.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/config/session_policy.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/config/vendors.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/convert/__init__.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/convert/anthropic_to_gemini.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/convert/anthropic_to_openai.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/convert/gemini_sse_adapter.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/convert/gemini_to_anthropic.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/convert/openai_to_anthropic.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/convert/vendor_channels.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/logging/__init__.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/logging/db.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/logging/formatters.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/logging/stats.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/model/__init__.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/model/auth.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/model/compat.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/model/constants.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/model/pricing.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/model/token.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/model/vendor.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/native_api/__init__.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/native_api/config.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/native_api/extractors/__init__.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/native_api/extractors/anthropic.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/native_api/extractors/gemini.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/native_api/extractors/openai.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/native_api/handler.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/native_api/operation.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/native_api/routes.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/native_api/usage_registry.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/pricing.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/routing/__init__.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/routing/circuit_breaker.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/routing/error_classifier.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/routing/executor.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/routing/model_mapper.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/routing/quota_guard.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/routing/rate_limit.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/routing/retry.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/routing/router.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/routing/session_manager.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/routing/session_policy.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/routing/tier.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/routing/usage_parser.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/routing/usage_recorder.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/server/__init__.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/server/app.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/server/factory.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/server/responses.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/server/routes.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/streaming/__init__.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/streaming/anthropic_compat.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/vendors/__init__.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/vendors/alibaba.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/vendors/anthropic.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/vendors/antigravity.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/vendors/base.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/vendors/copilot.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/vendors/copilot_models.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/vendors/copilot_token_manager.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/vendors/copilot_urls.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/vendors/doubao.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/vendors/kimi.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/vendors/minimax.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/vendors/mixins.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/vendors/native_anthropic.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/vendors/token_manager.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/vendors/xiaomi.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/vendors/zhipu.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/__init__.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_antigravity.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_app_routes.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_auto_login.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_banner.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_circuit_breaker.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_cli_usage.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_compat.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_config_init.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_config_loader.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_convert_request.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_convert_response.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_convert_sse.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_copilot.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_copilot_convert_request.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_copilot_convert_response.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_copilot_models.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_copilot_urls.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_currency.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_error_classifier.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_logging_dual_write.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_mixins.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_model_auth.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_model_compat.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_model_constants.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_model_mapper.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_model_pricing.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_model_token.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_model_vendor.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_native_api_base_url_override.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_native_api_extractors.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_native_api_handler.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_native_api_operation.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_native_api_routes.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_native_vendors.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_parse_usage.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_parse_usage_gemini.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_pricing.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_quota_guard.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_rate_limit.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_router_chain.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_router_executor.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_runtime_reauth.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_schema.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_session_aware.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_streaming_anthropic_compat.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_tier.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_tiers_config.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_time_range.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_token_logger.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_token_logger_native_columns.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_token_manager.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_types.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_vendor_channels.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_vendor_streaming.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_vendors.py +0 -0
- {coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/tests/test_zhipu.py +0 -0
|
@@ -4,16 +4,27 @@
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- fix(vendor-channels): 新增 zhipu 同 vendor 自清理通道,修复 GLM-5 自循环 400 + tool_results 偶发降级;
|
|
9
|
-
- fix(vendor-channels): 修复 `_rewrite_srvtoolu_ids` 块顺序敏感性导致 inline tool_result 漏改名,进而 enforce 阶段 dict key 与 tool_use_ids 错位、anthropic 报 `tool_use ids without tool_result blocks immediately after` 的 cascade failover 问题(改为两遍扫描:先收集 id_map,再统一改写所有 tool_result.tool_use_id 引用);
|
|
10
|
-
- fix(vendor-channels): `enforce_anthropic_tool_pairing` 增加全局 sanity check pass,主循环边角错位让 dangling tool_use 漏过校验时兜底合成 is_error 占位并打 `pairing_sanity_repaired` 标签,避免 anthropic 二次报错;
|
|
7
|
+
## [v0.4.0](https://github.com/ThreeFish-AI/coding-proxy/releases/tag/v0.4.0) — 2026-05-01
|
|
11
8
|
|
|
12
|
-
|
|
9
|
+
> [!IMPORTANT]
|
|
10
|
+
>
|
|
11
|
+
> **🚀 Session 级专属路由策略!**
|
|
12
|
+
>
|
|
13
|
+
> 给每个 Session 指定专属的 vendor,动态调节不同 vendors 间的 LLM 流量。
|
|
14
|
+
|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
### ✨ 核心亮点
|
|
18
|
+
|
|
19
|
+
- feat(session-policy): 新增 Session 级专属路由策略 (#219)
|
|
20
|
+
- feat(dashboard): 新增会话活动面板 (#222)
|
|
21
|
+
|
|
22
|
+
### 🔧 更多特性
|
|
13
23
|
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
- fix(
|
|
24
|
+
- refactor(logging): 移除已被 ModelCall 汇总行覆盖的冗余 DEBUG 日志 (#203)
|
|
25
|
+
- style(dashboard): 加宽图表 tooltip 令模型名称与用量值单行显示 (#211)
|
|
26
|
+
- fix(usage-parser): 补充 OpenAI/Gemini SSE 流式分支的 model_served 提取 (#214)
|
|
27
|
+
- fix(usage-parser): 兼容 SSE chunk 中 usage 字段为 null 的极端格式 (#212)
|
|
17
28
|
|
|
18
29
|
## [v0.3.0](https://github.com/ThreeFish-AI/coding-proxy/releases/tag/v0.3.0) — 2026-04-20
|
|
19
30
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: coding-proxy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
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
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "coding-proxy"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
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"
|
|
@@ -159,7 +159,7 @@ _DASHBOARD_HTML = """<!DOCTYPE html>
|
|
|
159
159
|
.kpi-grid {
|
|
160
160
|
display: grid;
|
|
161
161
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
162
|
-
gap:
|
|
162
|
+
gap: 5px;
|
|
163
163
|
margin-bottom: 24px;
|
|
164
164
|
}
|
|
165
165
|
.kpi-card {
|
|
@@ -310,6 +310,7 @@ _DASHBOARD_HTML = """<!DOCTYPE html>
|
|
|
310
310
|
}
|
|
311
311
|
.vendor-name { font-weight: 600; font-size: 14px; }
|
|
312
312
|
.vendor-badges { display: flex; gap: 5px; flex-wrap: wrap; align-items: center; }
|
|
313
|
+
.quota-group { display: flex; align-items: center; gap: 6px; }
|
|
313
314
|
.status-badge {
|
|
314
315
|
font-size: 11px; padding: 2px 7px;
|
|
315
316
|
border-radius: 10px;
|
|
@@ -319,7 +320,7 @@ _DASHBOARD_HTML = """<!DOCTYPE html>
|
|
|
319
320
|
.sb-warn { background: rgba(210,153,34,.12); color: var(--accent-yellow); border: 1px solid rgba(210,153,34,.2); }
|
|
320
321
|
.sb-err { background: rgba(248,81,73,.12); color: var(--accent-red); border: 1px solid rgba(248,81,73,.2); }
|
|
321
322
|
.sb-info { background: rgba(88,166,255,.12); color: var(--accent-blue); border: 1px solid rgba(88,166,255,.2); }
|
|
322
|
-
.quota-bar-wrap { flex: 1;
|
|
323
|
+
.quota-bar-wrap { flex: 1; min-width: 40px; max-width: 100px; }
|
|
323
324
|
.quota-bar-bg {
|
|
324
325
|
height: 4px; border-radius: 2px;
|
|
325
326
|
background: rgba(255,255,255,.06);
|
|
@@ -407,9 +408,14 @@ _DASHBOARD_HTML = """<!DOCTYPE html>
|
|
|
407
408
|
border-bottom: 1px solid var(--border);
|
|
408
409
|
}
|
|
409
410
|
.session-table td { padding: 8px 12px; border-bottom: 1px solid var(--border-subtle); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
411
|
+
.session-table td.cell-tags { white-space: normal; overflow: visible; text-overflow: clip; line-height: 1.8; vertical-align: middle; }
|
|
410
412
|
.session-table tr:hover td { background: var(--bg-card-hover); }
|
|
411
|
-
.session-table .session-key { font-family: 'JetBrains Mono', monospace; font-size: 12px; color: var(--accent-blue); cursor: default; white-space:
|
|
412
|
-
.session-id {
|
|
413
|
+
.session-table .session-key { font-family: 'JetBrains Mono', monospace; font-size: 12px; color: var(--accent-blue); cursor: default; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
414
|
+
.session-id { display: flex; align-items: center; gap: 4px; }
|
|
415
|
+
.session-id-text { overflow: hidden; text-overflow: ellipsis; }
|
|
416
|
+
.copy-btn { background: none; border: none; color: var(--text-tertiary); cursor: pointer; padding: 2px; border-radius: 4px; font-size: 12px; line-height: 1; opacity: .5; flex-shrink: 0; }
|
|
417
|
+
.copy-btn:hover { opacity: 1; color: var(--accent-blue); background: rgba(88,166,255,.1); }
|
|
418
|
+
.copy-btn.copied { color: var(--accent-green); opacity: 1; }
|
|
413
419
|
.session-meta { font-size: 10px; color: var(--text-tertiary); line-height: 1.2; margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
414
420
|
.session-tag {
|
|
415
421
|
display: inline-block; font-size: 11px; padding: 2px 7px;
|
|
@@ -417,8 +423,43 @@ _DASHBOARD_HTML = """<!DOCTYPE html>
|
|
|
417
423
|
background: rgba(88,166,255,.08); border: 1px solid rgba(88,166,255,.15);
|
|
418
424
|
color: var(--text-secondary);
|
|
419
425
|
}
|
|
420
|
-
.
|
|
421
|
-
|
|
426
|
+
.session-tag-cc {
|
|
427
|
+
background: rgba(63,185,80,.08); border-color: rgba(63,185,80,.15);
|
|
428
|
+
}
|
|
429
|
+
.session-table td.cell-success { overflow: visible; text-overflow: clip; }
|
|
430
|
+
/* ── 展开行 ── */
|
|
431
|
+
.session-table tr.row-detail { display: none; }
|
|
432
|
+
.session-table tr.row-detail.open { display: table-row; }
|
|
433
|
+
.session-table tr.row-detail td { padding: 0; }
|
|
434
|
+
.detail-card {
|
|
435
|
+
padding: 16px 24px; margin: 6px 0;
|
|
436
|
+
background: linear-gradient(135deg, rgba(30,37,54,.95), rgba(22,28,40,.95));
|
|
437
|
+
border: 1px solid rgba(88,166,255,.15); border-radius: 12px;
|
|
438
|
+
font-size: 13px;
|
|
439
|
+
white-space: normal; overflow: hidden;
|
|
440
|
+
box-shadow: 0 4px 16px rgba(0,0,0,.3);
|
|
441
|
+
}
|
|
442
|
+
.detail-card .detail-item { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
443
|
+
.detail-card .detail-label { font-size: 11px; color: var(--text-tertiary); text-transform: uppercase; letter-spacing: .3px; }
|
|
444
|
+
.detail-card .detail-value { color: var(--text-primary); line-height: 1.4; word-break: break-all; overflow-wrap: break-word; }
|
|
445
|
+
.detail-identity-row {
|
|
446
|
+
display: flex; gap: 16px;
|
|
447
|
+
padding-bottom: 10px; margin-bottom: 10px;
|
|
448
|
+
border-bottom: 1px solid var(--border);
|
|
449
|
+
}
|
|
450
|
+
.detail-identity-row .detail-item { flex: 3 1 0; }
|
|
451
|
+
.detail-identity-row .detail-item:first-child { flex: 2 1 0; }
|
|
452
|
+
.detail-identity-row .detail-value { font-family: 'JetBrains Mono', monospace; font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; word-break: normal; }
|
|
453
|
+
.detail-metrics-grid {
|
|
454
|
+
display: grid;
|
|
455
|
+
grid-template-columns: repeat(8, 1fr);
|
|
456
|
+
gap: 10px 16px;
|
|
457
|
+
}
|
|
458
|
+
.detail-inline-pair { display: flex; gap: 16px; }
|
|
459
|
+
.detail-inline-pair > div { flex: 1; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
460
|
+
.session-table tbody tr[data-row]:not(.row-detail) { cursor: pointer; }
|
|
461
|
+
.success-bar { width: 56px; height: 4px; border-radius: 2px; background: rgba(255,255,255,.12); display: inline-block; vertical-align: middle; margin-left: 6px; }
|
|
462
|
+
.success-bar-fill { height: 100%; border-radius: 2px; display: block; }
|
|
422
463
|
/* ── Vendor Bind 选择器 ── */
|
|
423
464
|
.bind-select {
|
|
424
465
|
padding: 3px 6px; border-radius: 6px;
|
|
@@ -490,28 +531,28 @@ _DASHBOARD_HTML = """<!DOCTYPE html>
|
|
|
490
531
|
/* ── Tabs ─────────────────────────────────────────────────── */
|
|
491
532
|
.tabs {
|
|
492
533
|
display: flex;
|
|
493
|
-
gap:
|
|
494
|
-
|
|
495
|
-
border-bottom: 1px solid var(--border);
|
|
496
|
-
padding: 0 2px;
|
|
534
|
+
gap: 2px;
|
|
535
|
+
padding: 0;
|
|
497
536
|
}
|
|
498
537
|
.tab-btn {
|
|
499
538
|
appearance: none;
|
|
500
539
|
background: transparent;
|
|
501
|
-
border:
|
|
502
|
-
border-bottom: 2px solid transparent;
|
|
540
|
+
border: 1px solid transparent;
|
|
503
541
|
color: var(--text-secondary);
|
|
504
542
|
cursor: pointer;
|
|
505
543
|
font-family: inherit;
|
|
506
|
-
font-size:
|
|
544
|
+
font-size: 13px;
|
|
507
545
|
font-weight: 500;
|
|
508
|
-
padding:
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
border-radius: 6px 6px 0 0;
|
|
546
|
+
padding: 4px 12px;
|
|
547
|
+
transition: color .15s ease, background .15s ease, border-color .15s ease;
|
|
548
|
+
border-radius: var(--radius-sm);
|
|
512
549
|
}
|
|
513
550
|
.tab-btn:hover { color: var(--text-primary); background: var(--bg-card-hover); }
|
|
514
|
-
.tab-btn.active {
|
|
551
|
+
.tab-btn.active {
|
|
552
|
+
color: var(--text-primary);
|
|
553
|
+
background: rgba(88,166,255,.1);
|
|
554
|
+
border-color: rgba(88,166,255,.2);
|
|
555
|
+
}
|
|
515
556
|
.tab-btn:focus-visible { outline: 2px solid var(--accent-blue); outline-offset: 2px; }
|
|
516
557
|
.tab-pane { display: none; }
|
|
517
558
|
.tab-pane.active { display: block; }
|
|
@@ -525,18 +566,16 @@ _DASHBOARD_HTML = """<!DOCTYPE html>
|
|
|
525
566
|
<span class="badge" id="version-badge">v-.-.-</span>
|
|
526
567
|
</div>
|
|
527
568
|
<div class="header-right">
|
|
569
|
+
<nav class="tabs" role="tablist" aria-label="Dashboard sections">
|
|
570
|
+
<button type="button" class="tab-btn active" id="tab-btn-overview" role="tab" aria-controls="tab-pane-overview" aria-selected="true" data-tab="overview" onclick="switchTab('overview')">Overview</button>
|
|
571
|
+
<button type="button" class="tab-btn" id="tab-btn-sessions" role="tab" aria-controls="tab-pane-sessions" aria-selected="false" data-tab="sessions" onclick="switchTab('sessions')">Sessions</button>
|
|
572
|
+
</nav>
|
|
528
573
|
<span class="refresh-time" id="refresh-time">正在加载…</span>
|
|
529
574
|
<button class="btn-refresh" onclick="refresh()">⟳ 刷新</button>
|
|
530
575
|
</div>
|
|
531
576
|
</header>
|
|
532
577
|
|
|
533
578
|
<main>
|
|
534
|
-
<!-- 页签导航 -->
|
|
535
|
-
<nav class="tabs" role="tablist" aria-label="Dashboard sections">
|
|
536
|
-
<button type="button" class="tab-btn active" id="tab-btn-overview" role="tab" aria-controls="tab-pane-overview" aria-selected="true" data-tab="overview" onclick="switchTab('overview')">Overview</button>
|
|
537
|
-
<button type="button" class="tab-btn" id="tab-btn-sessions" role="tab" aria-controls="tab-pane-sessions" aria-selected="false" data-tab="sessions" onclick="switchTab('sessions')">Recent Active Sessions</button>
|
|
538
|
-
</nav>
|
|
539
|
-
|
|
540
579
|
<!-- Overview 页签 -->
|
|
541
580
|
<section class="tab-pane active" id="tab-pane-overview" role="tabpanel" aria-labelledby="tab-btn-overview" data-tab="overview">
|
|
542
581
|
<!-- 时间区间选择器 -->
|
|
@@ -630,27 +669,23 @@ _DASHBOARD_HTML = """<!DOCTYPE html>
|
|
|
630
669
|
</div>
|
|
631
670
|
</section>
|
|
632
671
|
|
|
633
|
-
<!--
|
|
672
|
+
<!-- Sessions 页签 -->
|
|
634
673
|
<section class="tab-pane" id="tab-pane-sessions" role="tabpanel" aria-labelledby="tab-btn-sessions" data-tab="sessions">
|
|
635
|
-
<!--
|
|
674
|
+
<!-- Sessions -->
|
|
636
675
|
<div class="card sessions-card">
|
|
637
|
-
<div class="card-title">
|
|
638
|
-
<span>Recent Active Sessions</span>
|
|
639
|
-
<span style="font-size:12px;color:var(--text-tertiary)" id="sessions-subtitle">Last 24h</span>
|
|
640
|
-
</div>
|
|
641
676
|
<div class="session-table-wrap" id="sessions-table-wrap">
|
|
642
677
|
<table class="session-table">
|
|
643
678
|
<colgroup>
|
|
644
|
-
<col style="width:
|
|
645
|
-
<col style="width:8%">
|
|
679
|
+
<col style="width:12%">
|
|
646
680
|
<col style="width:7%">
|
|
681
|
+
<col style="width:6%">
|
|
682
|
+
<col style="width:6%">
|
|
683
|
+
<col style="width:17%">
|
|
684
|
+
<col style="width:12%">
|
|
647
685
|
<col style="width:7%">
|
|
648
|
-
<col style="width:
|
|
686
|
+
<col style="width:9%">
|
|
687
|
+
<col style="width:12%">
|
|
649
688
|
<col style="width:12%">
|
|
650
|
-
<col style="width:8%">
|
|
651
|
-
<col style="width:8%">
|
|
652
|
-
<col style="width:8%">
|
|
653
|
-
<col style="width:6%">
|
|
654
689
|
</colgroup>
|
|
655
690
|
<thead>
|
|
656
691
|
<tr>
|
|
@@ -721,7 +756,32 @@ function fmtTokens(n) {
|
|
|
721
756
|
return String(n);
|
|
722
757
|
}
|
|
723
758
|
function fmtNum(n) { return n == null ? '–' : n.toLocaleString(); }
|
|
759
|
+
function copyFromParent(btn) {
|
|
760
|
+
var text = btn.parentElement.getAttribute('data-key') || btn.parentElement.getAttribute('title') || '';
|
|
761
|
+
navigator.clipboard.writeText(text).then(function() {
|
|
762
|
+
btn.classList.add('copied');
|
|
763
|
+
btn.textContent = '✓';
|
|
764
|
+
setTimeout(function() { btn.classList.remove('copied'); btn.textContent = '⧉'; }, 1500);
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
function toggleRow(tr) {
|
|
768
|
+
var detail = tr.nextElementSibling;
|
|
769
|
+
if (!detail || !detail.classList.contains('row-detail')) return;
|
|
770
|
+
var wasOpen = detail.classList.contains('open');
|
|
771
|
+
// close all open rows first
|
|
772
|
+
document.querySelectorAll('.session-table tr.row-detail.open').forEach(function(r) { r.classList.remove('open'); });
|
|
773
|
+
if (!wasOpen) detail.classList.add('open');
|
|
774
|
+
}
|
|
724
775
|
function isValidLabel(s) { return typeof s === 'string' && s !== 'undefined' && s !== 'null' && s.trim() !== ''; }
|
|
776
|
+
function fmtDuration(ms) {
|
|
777
|
+
if (ms == null) return '–';
|
|
778
|
+
var s = ms / 1000;
|
|
779
|
+
if (s < 1) return Math.round(ms) + 'ms';
|
|
780
|
+
if (s < 60) return s.toFixed(1).replace(/\\.0$/, '') + 's';
|
|
781
|
+
var m = Math.floor(s / 60);
|
|
782
|
+
var sec = Math.round(s % 60);
|
|
783
|
+
return sec > 0 ? m + 'min ' + sec + 's' : m + 'min';
|
|
784
|
+
}
|
|
725
785
|
function now() {
|
|
726
786
|
return new Date().toLocaleTimeString('zh-CN', {hour:'2-digit',minute:'2-digit',second:'2-digit'});
|
|
727
787
|
}
|
|
@@ -731,12 +791,15 @@ function now() {
|
|
|
731
791
|
// _API_VENDORS 需与后端 native_api/handler.py::_VENDOR_LABEL 对齐,
|
|
732
792
|
// 新增无 -native 后缀的 native vendor 时同步更新本集合。
|
|
733
793
|
const _API_VENDORS = new Set(['anthropic-native', 'openai', 'gemini']);
|
|
794
|
+
function isApiVendor(v) { return _API_VENDORS.has(v); }
|
|
795
|
+
function vendorShortName(v) {
|
|
796
|
+
if (!isValidLabel(v)) return v;
|
|
797
|
+
if (isApiVendor(v)) return v.endsWith('-native') ? v.slice(0, -'-native'.length) : v;
|
|
798
|
+
return v;
|
|
799
|
+
}
|
|
734
800
|
function formatVendorLabel(v) {
|
|
735
801
|
if (!isValidLabel(v)) return v;
|
|
736
|
-
if (
|
|
737
|
-
const name = v.endsWith('-native') ? v.slice(0, -'-native'.length) : v;
|
|
738
|
-
return 'api | ' + name;
|
|
739
|
-
}
|
|
802
|
+
if (isApiVendor(v)) return 'api | ' + vendorShortName(v);
|
|
740
803
|
return 'cc | ' + v;
|
|
741
804
|
}
|
|
742
805
|
|
|
@@ -1027,10 +1090,11 @@ function renderQuotaBar(qg) {
|
|
|
1027
1090
|
if (!qg || qg.usage_percent == null) return '';
|
|
1028
1091
|
const pct = Math.round(qg.usage_percent);
|
|
1029
1092
|
const label = quotaWindowLabel(qg.window_hours);
|
|
1030
|
-
return `<
|
|
1093
|
+
return `<div class="quota-group">` +
|
|
1094
|
+
`<span class="status-badge ${quotaClass(pct)}">${label} ${pct}%</span>` +
|
|
1031
1095
|
`<div class="quota-bar-wrap"><div class="quota-bar-bg">` +
|
|
1032
1096
|
`<div class="quota-bar-fill" style="width:${Math.min(pct,100)}%;background:${quotaBarColor(pct)}"></div>` +
|
|
1033
|
-
`</div></div>`;
|
|
1097
|
+
`</div></div></div>`;
|
|
1034
1098
|
}
|
|
1035
1099
|
|
|
1036
1100
|
function updateVendorStatus(status) {
|
|
@@ -1440,7 +1504,10 @@ function formatSessionTags(str, max) {
|
|
|
1440
1504
|
var html = list.slice(0, max).map(function(c) {
|
|
1441
1505
|
return '<span class="session-tag">' + escapeHtml(c.trim()) + '</span>';
|
|
1442
1506
|
}).join('');
|
|
1443
|
-
if (list.length > max)
|
|
1507
|
+
if (list.length > max) {
|
|
1508
|
+
var fullList = list.map(function(c) { return c.trim(); }).join(', ');
|
|
1509
|
+
html += '<span class="session-tag" title="' + escapeHtml(fullList) + '">+' + (list.length - max) + '</span>';
|
|
1510
|
+
}
|
|
1444
1511
|
return html;
|
|
1445
1512
|
}
|
|
1446
1513
|
function formatCategories(cats) {
|
|
@@ -1453,9 +1520,20 @@ function formatCategories(cats) {
|
|
|
1453
1520
|
}
|
|
1454
1521
|
function formatVendorTags(vendors) {
|
|
1455
1522
|
if (!vendors) return '–';
|
|
1456
|
-
|
|
1457
|
-
|
|
1523
|
+
var list = vendors.split(',');
|
|
1524
|
+
var max = 4;
|
|
1525
|
+
var html = list.slice(0, max).map(function(v) {
|
|
1526
|
+
var vt = v.trim();
|
|
1527
|
+
var name = vendorShortName(vt);
|
|
1528
|
+
var fullLabel = formatVendorLabel(vt);
|
|
1529
|
+
var cls = isApiVendor(vt) ? 'session-tag' : 'session-tag session-tag-cc';
|
|
1530
|
+
return '<span class="' + cls + '" title="' + escapeHtml(fullLabel) + '">' + escapeHtml(name) + '</span>';
|
|
1458
1531
|
}).join('');
|
|
1532
|
+
if (list.length > max) {
|
|
1533
|
+
var fullList = list.map(function(v) { return formatVendorLabel(v.trim()); }).join(', ');
|
|
1534
|
+
html += '<span class="session-tag" title="' + escapeHtml(fullList) + '">+' + (list.length - max) + '</span>';
|
|
1535
|
+
}
|
|
1536
|
+
return html;
|
|
1459
1537
|
}
|
|
1460
1538
|
// ── Sessions Pagination State ──
|
|
1461
1539
|
var allSessions = [];
|
|
@@ -1479,8 +1557,6 @@ async function updateSessions() {
|
|
|
1479
1557
|
sessionBindMap = {};
|
|
1480
1558
|
(bindData.bindings || []).forEach(function(b) { sessionBindMap[b.session_key] = b.vendors; });
|
|
1481
1559
|
sessionAvailableVendors = (statusData.tiers || []).map(function(t) { return t.name; });
|
|
1482
|
-
var subtitle = document.getElementById('sessions-subtitle');
|
|
1483
|
-
if (subtitle) subtitle.textContent = 'Last ' + data.hours + 'h';
|
|
1484
1560
|
sessionPage = 0;
|
|
1485
1561
|
renderSessionPage();
|
|
1486
1562
|
} catch (e) {
|
|
@@ -1503,9 +1579,15 @@ function renderSessionPage() {
|
|
|
1503
1579
|
var parsed = parseSessionKey(s.session_key);
|
|
1504
1580
|
var boundVendors = sessionBindMap[s.session_key];
|
|
1505
1581
|
var selectHtml = buildBindSelect(s.session_key, boundVendors, sessionAvailableVendors);
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1582
|
+
var modelsFull = (s.models || '').split(',').map(function(c){return c.trim();});
|
|
1583
|
+
var vendorsFull = (s.vendors || '').split(',').map(function(v){return formatVendorLabel(v.trim());});
|
|
1584
|
+
var sr = s.success_rate != null ? Math.round(s.success_rate) : null;
|
|
1585
|
+
return '<tr data-row onclick="toggleRow(this)">' +
|
|
1586
|
+
'<td class="session-key" onclick="event.stopPropagation()">' +
|
|
1587
|
+
'<div class="session-id" data-key="' + escapeHtml(s.session_key) + '" title="' + escapeHtml(s.session_key) + '">' +
|
|
1588
|
+
'<span class="session-id-text">' + escapeHtml(parsed.session_id || s.session_key) + '</span>' +
|
|
1589
|
+
'<button class="copy-btn" onclick="copyFromParent(this)" title="Copy Session ID">⧉</button>' +
|
|
1590
|
+
'</div>' +
|
|
1509
1591
|
'<div class="session-meta" title="device: ' + escapeHtml(parsed.device_id) + ' | account: ' + escapeHtml(parsed.account_uuid) + '">' +
|
|
1510
1592
|
'dev:' + escapeHtml(shortId(parsed.device_id, 8)) + ' · acct:' + escapeHtml(shortId(parsed.account_uuid, 8)) +
|
|
1511
1593
|
'</div>' +
|
|
@@ -1513,13 +1595,32 @@ function renderSessionPage() {
|
|
|
1513
1595
|
'<td>' + relativeTime(s.last_active_ts) + '</td>' +
|
|
1514
1596
|
'<td style="font-family:JetBrains Mono,monospace">' + fmtNum(s.total_requests) + '</td>' +
|
|
1515
1597
|
'<td style="font-family:JetBrains Mono,monospace">' + fmtTokens(s.total_tokens) + '</td>' +
|
|
1516
|
-
'<td>' + formatSessionTags(s.models,
|
|
1517
|
-
'<td>' + formatVendorTags(s.vendors) + '</td>' +
|
|
1518
|
-
'<td style="font-family:JetBrains Mono,monospace">' + (s.avg_duration_ms
|
|
1519
|
-
'<td>' + successBarHtml(s.success_rate) + '</td>' +
|
|
1520
|
-
'<td>' + selectHtml + '</td>' +
|
|
1598
|
+
'<td title="' + escapeHtml(modelsFull.join(', ')) + '">' + formatSessionTags(s.models, 3) + '</td>' +
|
|
1599
|
+
'<td title="' + escapeHtml(vendorsFull.join(', ')) + '">' + formatVendorTags(s.vendors) + '</td>' +
|
|
1600
|
+
'<td style="font-family:JetBrains Mono,monospace">' + fmtDuration(s.avg_duration_ms) + '</td>' +
|
|
1601
|
+
'<td class="cell-success">' + successBarHtml(s.success_rate) + '</td>' +
|
|
1602
|
+
'<td onclick="event.stopPropagation()">' + selectHtml + '</td>' +
|
|
1521
1603
|
'<td>' + formatCategories(s.client_categories) + '</td>' +
|
|
1522
|
-
'</tr>'
|
|
1604
|
+
'</tr>' +
|
|
1605
|
+
'<tr class="row-detail"><td colspan="10"><div class="detail-card">' +
|
|
1606
|
+
'<div class="detail-identity-row">' +
|
|
1607
|
+
'<div class="detail-item"><div class="detail-label">Session ID</div><div class="detail-value" title="' + escapeHtml(s.session_key) + '">' + escapeHtml(parsed.session_id || s.session_key) + '</div></div>' +
|
|
1608
|
+
'<div class="detail-item"><div class="detail-label">Device</div><div class="detail-value" title="' + escapeHtml(parsed.device_id || '') + '">' + (parsed.device_id ? escapeHtml(parsed.device_id) : '–') + '</div></div>' +
|
|
1609
|
+
'<div class="detail-item"><div class="detail-label">Account</div><div class="detail-value" title="' + escapeHtml(parsed.account_uuid || '') + '">' + (parsed.account_uuid ? escapeHtml(parsed.account_uuid) : '–') + '</div></div>' +
|
|
1610
|
+
'</div>' +
|
|
1611
|
+
'<div class="detail-metrics-grid">' +
|
|
1612
|
+
'<div class="detail-item"><div class="detail-label">Last Active</div><div class="detail-value">' + relativeTime(s.last_active_ts) + '</div></div>' +
|
|
1613
|
+
'<div class="detail-item"><div class="detail-label">Requests</div><div class="detail-value">' + fmtNum(s.total_requests) + '</div></div>' +
|
|
1614
|
+
'<div class="detail-item"><div class="detail-label">Tokens</div><div class="detail-value">' + fmtTokens(s.total_tokens) + '</div></div>' +
|
|
1615
|
+
'<div class="detail-item"><div class="detail-label">Models</div><div class="detail-value">' + (modelsFull.length ? modelsFull.map(function(m){return '<span class="session-tag">' + escapeHtml(m) + '</span>';}).join(' ') : '–') + '</div></div>' +
|
|
1616
|
+
'<div class="detail-item"><div class="detail-label">Vendors</div><div class="detail-value">' + (vendorsFull.length ? vendorsFull.map(function(v){return '<span class="session-tag">' + escapeHtml(v) + '</span>';}).join(' ') : '–') + '</div></div>' +
|
|
1617
|
+
'<div class="detail-item"><div class="detail-label">Avg Latency</div><div class="detail-value">' + fmtDuration(s.avg_duration_ms) + '</div></div>' +
|
|
1618
|
+
'<div class="detail-item" style="grid-column:span 2"><div class="detail-inline-pair">' +
|
|
1619
|
+
'<div><div class="detail-label">Success Rate</div><div class="detail-value">' + (sr != null ? sr + '%' : '–') + '</div></div>' +
|
|
1620
|
+
'<div><div class="detail-label">Client</div><div class="detail-value">' + escapeHtml(s.client_categories || '–') + '</div></div>' +
|
|
1621
|
+
'</div></div>' +
|
|
1622
|
+
'</div>' +
|
|
1623
|
+
'</div></td></tr>';
|
|
1523
1624
|
}).join('');
|
|
1524
1625
|
}
|
|
1525
1626
|
|
|
@@ -1590,7 +1691,7 @@ sessionsTbody.addEventListener('change', function(e) {
|
|
|
1590
1691
|
let refreshing = false;
|
|
1591
1692
|
let currentTab = 'overview';
|
|
1592
1693
|
const tabLoaded = { overview: false, sessions: false };
|
|
1593
|
-
const TAB_LABELS = { overview: 'Overview', sessions: '
|
|
1694
|
+
const TAB_LABELS = { overview: 'Overview', sessions: 'Sessions' };
|
|
1594
1695
|
|
|
1595
1696
|
async function refreshOverview() {
|
|
1596
1697
|
const days = currentDays > 0 ? currentDays : 7;
|
|
@@ -1681,9 +1782,7 @@ function switchTab(name) {
|
|
|
1681
1782
|
currentTab = name;
|
|
1682
1783
|
applyTabState(name);
|
|
1683
1784
|
syncTabUrl(name);
|
|
1684
|
-
|
|
1685
|
-
refresh();
|
|
1686
|
-
}
|
|
1785
|
+
refresh();
|
|
1687
1786
|
}
|
|
1688
1787
|
|
|
1689
1788
|
// ── 初始化 ────────────────────────────────────────────────
|
|
@@ -1696,6 +1795,10 @@ function switchTab(name) {
|
|
|
1696
1795
|
currentTab = initial;
|
|
1697
1796
|
applyTabState(initial);
|
|
1698
1797
|
syncTabUrl(initial);
|
|
1798
|
+
// Load version immediately regardless of active tab
|
|
1799
|
+
fetchJSON('/api/dashboard/summary?days=7').then(function(s) {
|
|
1800
|
+
if (s && s.version) document.getElementById('version-badge').textContent = 'v' + s.version;
|
|
1801
|
+
}).catch(function(){});
|
|
1699
1802
|
refresh(); // 仅加载初始页签的数据
|
|
1700
1803
|
setInterval(refresh, 600000); // 每 10 分钟刷新当前页签
|
|
1701
1804
|
})();
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/convert/anthropic_to_gemini.py
RENAMED
|
File without changes
|
{coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/convert/anthropic_to_openai.py
RENAMED
|
File without changes
|
|
File without changes
|
{coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/convert/gemini_to_anthropic.py
RENAMED
|
File without changes
|
{coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/convert/openai_to_anthropic.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/native_api/extractors/__init__.py
RENAMED
|
File without changes
|
{coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/native_api/extractors/anthropic.py
RENAMED
|
File without changes
|
{coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/native_api/extractors/gemini.py
RENAMED
|
File without changes
|
{coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/native_api/extractors/openai.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{coding_proxy-0.3.1a10 → coding_proxy-0.4.0}/src/coding/proxy/vendors/copilot_token_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|