coding-proxy 0.4.1a5__tar.gz → 0.4.1a7__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.4.1a5 → coding_proxy-0.4.1a7}/PKG-INFO +1 -1
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/pyproject.toml +1 -1
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/logging/db.py +38 -1
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/routing/executor.py +101 -4
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/routing/session_manager.py +9 -4
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/routing/usage_recorder.py +5 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/server/dashboard.py +17 -11
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_router_executor.py +6 -5
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/uv.lock +1 -1
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/.github/workflows/ci.yml +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/.github/workflows/coverage.yml +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/.github/workflows/release.yml +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/.gitignore +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/.pre-commit-config.yaml +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/AGENTS.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/CHANGELOG.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/CLAUDE.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/LICENSE +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/README.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/assets/dashboard-v0.4.0.png +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/assets/session-v0.4.0.png +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/docs/agents/browser-validation.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/docs/agents/issue.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/docs/agents/knowledge-map.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/docs/agents/reference-specifications.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/docs/arch/config-reference.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/docs/arch/convert.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/docs/arch/design-patterns.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/docs/arch/routing.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/docs/arch/testing.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/docs/arch/vendors.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/docs/framework.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/docs/guide/api-reference.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/docs/guide/cli-reference.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/docs/guide/dashboard.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/docs/guide/monitoring.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/docs/guide/quickstart.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/docs/guide/vendors.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/docs/ops/ci-cd.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/docs/user-guide.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/docs/zh-CN/README.md +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/__init__.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/__init__.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/__main__.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/auth/__init__.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/auth/providers/__init__.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/auth/providers/base.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/auth/providers/github.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/auth/providers/google.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/auth/runtime.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/auth/store.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/cli/__init__.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/cli/auth_commands.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/cli/banner.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/compat/__init__.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/compat/canonical.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/compat/session_store.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/config/__init__.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/config/auth_schema.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/config/config.default.yaml +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/config/loader.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/config/resiliency.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/config/routing.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/config/schema.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/config/server.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/config/session_policy.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/config/vendors.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/convert/__init__.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/convert/anthropic_to_gemini.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/convert/anthropic_to_openai.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/convert/gemini_sse_adapter.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/convert/gemini_to_anthropic.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/convert/openai_to_anthropic.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/convert/vendor_channels.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/logging/__init__.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/logging/formatters.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/logging/stats.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/model/__init__.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/model/auth.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/model/compat.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/model/constants.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/model/pricing.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/model/token.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/model/vendor.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/native_api/__init__.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/native_api/config.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/native_api/extractors/__init__.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/native_api/extractors/anthropic.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/native_api/extractors/gemini.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/native_api/extractors/openai.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/native_api/handler.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/native_api/operation.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/native_api/routes.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/native_api/usage_registry.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/pricing.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/routing/__init__.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/routing/circuit_breaker.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/routing/error_classifier.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/routing/model_mapper.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/routing/quota_guard.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/routing/rate_limit.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/routing/retry.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/routing/router.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/routing/session_policy.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/routing/tier.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/routing/usage_parser.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/server/__init__.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/server/app.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/server/factory.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/server/responses.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/server/routes.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/streaming/__init__.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/streaming/anthropic_compat.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/vendors/__init__.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/vendors/alibaba.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/vendors/anthropic.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/vendors/antigravity.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/vendors/base.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/vendors/copilot.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/vendors/copilot_models.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/vendors/copilot_token_manager.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/vendors/copilot_urls.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/vendors/doubao.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/vendors/kimi.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/vendors/minimax.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/vendors/mixins.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/vendors/native_anthropic.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/vendors/token_manager.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/vendors/xiaomi.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/vendors/zhipu.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/__init__.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/e2e/__init__.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/e2e/conftest.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/e2e/test_e2e_http.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/e2e/test_e2e_token.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/e2e/test_e2e_vendor.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_antigravity.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_app_routes.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_auto_login.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_banner.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_circuit_breaker.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_cli_usage.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_compat.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_config_init.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_config_loader.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_convert_request.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_convert_response.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_convert_sse.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_copilot.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_copilot_convert_request.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_copilot_convert_response.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_copilot_models.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_copilot_urls.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_currency.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_error_classifier.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_logging_dual_write.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_mixins.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_model_auth.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_model_compat.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_model_constants.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_model_mapper.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_model_pricing.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_model_token.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_model_vendor.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_native_api_base_url_override.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_native_api_extractors.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_native_api_handler.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_native_api_operation.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_native_api_routes.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_native_vendors.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_parse_usage.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_parse_usage_gemini.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_pricing.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_quota_guard.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_rate_limit.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_router_chain.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_runtime_reauth.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_schema.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_session_aware.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_streaming_anthropic_compat.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_tier.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_tiers_config.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_time_range.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_token_logger.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_token_logger_native_columns.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_token_manager.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_types.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_vendor_channels.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_vendor_streaming.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_vendors.py +0 -0
- {coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/tests/test_zhipu.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: coding-proxy
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.1a7
|
|
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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "coding-proxy"
|
|
3
|
-
version = "0.4.
|
|
3
|
+
version = "0.4.1a7"
|
|
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"
|
|
@@ -190,6 +190,14 @@ CREATE TABLE IF NOT EXISTS usage_evidence (
|
|
|
190
190
|
);
|
|
191
191
|
"""
|
|
192
192
|
|
|
193
|
+
_CREATE_SESSION_META = """
|
|
194
|
+
CREATE TABLE IF NOT EXISTS session_meta (
|
|
195
|
+
session_key TEXT PRIMARY KEY,
|
|
196
|
+
title TEXT NOT NULL DEFAULT '',
|
|
197
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
198
|
+
);
|
|
199
|
+
"""
|
|
200
|
+
|
|
193
201
|
_CREATE_INDEXES = """
|
|
194
202
|
CREATE INDEX IF NOT EXISTS idx_usage_ts ON usage_log(ts);
|
|
195
203
|
CREATE INDEX IF NOT EXISTS idx_usage_vendor ON usage_log(vendor);
|
|
@@ -245,6 +253,7 @@ class TokenLogger:
|
|
|
245
253
|
self._db.row_factory = aiosqlite.Row
|
|
246
254
|
await self._db.execute("PRAGMA journal_mode=WAL")
|
|
247
255
|
await self._db.executescript(_CREATE_TABLES)
|
|
256
|
+
await self._db.executescript(_CREATE_SESSION_META)
|
|
248
257
|
# 迁移必须在建索引之前执行,确保 vendor 列已存在
|
|
249
258
|
await self._migrate_rename_backend_to_vendor()
|
|
250
259
|
await self._migrate_add_failover_from()
|
|
@@ -316,6 +325,28 @@ class TokenLogger:
|
|
|
316
325
|
"Migration: renamed 'backend' column to 'vendor' in %s", table
|
|
317
326
|
)
|
|
318
327
|
|
|
328
|
+
async def set_session_title(self, session_key: str, title: str) -> None:
|
|
329
|
+
"""为新 session 设置标题(幂等,仅首次写入)."""
|
|
330
|
+
if not self._db or not title or not session_key:
|
|
331
|
+
return
|
|
332
|
+
await self._db.execute(
|
|
333
|
+
"INSERT OR IGNORE INTO session_meta (session_key, title) VALUES (?, ?)",
|
|
334
|
+
(session_key, title),
|
|
335
|
+
)
|
|
336
|
+
await self._db.commit()
|
|
337
|
+
|
|
338
|
+
async def get_session_titles(self, session_keys: list[str]) -> dict[str, str]:
|
|
339
|
+
"""批量查询 session 标题."""
|
|
340
|
+
if not self._db or not session_keys:
|
|
341
|
+
return {}
|
|
342
|
+
placeholders = ",".join("?" for _ in session_keys)
|
|
343
|
+
cursor = await self._db.execute(
|
|
344
|
+
f"SELECT session_key, title FROM session_meta WHERE session_key IN ({placeholders})",
|
|
345
|
+
session_keys,
|
|
346
|
+
)
|
|
347
|
+
rows = await cursor.fetchall()
|
|
348
|
+
return {row["session_key"]: row["title"] for row in rows}
|
|
349
|
+
|
|
319
350
|
async def log(
|
|
320
351
|
self,
|
|
321
352
|
vendor: str,
|
|
@@ -621,7 +652,13 @@ class TokenLogger:
|
|
|
621
652
|
(cutoff_iso, limit),
|
|
622
653
|
)
|
|
623
654
|
rows = await cursor.fetchall()
|
|
624
|
-
|
|
655
|
+
sessions = [dict(row) for row in rows]
|
|
656
|
+
if sessions:
|
|
657
|
+
keys = [s["session_key"] for s in sessions]
|
|
658
|
+
titles = await self.get_session_titles(keys)
|
|
659
|
+
for s in sessions:
|
|
660
|
+
s["title"] = titles.get(s["session_key"], "")
|
|
661
|
+
return sessions
|
|
625
662
|
|
|
626
663
|
async def query_session_profile(self, session_key: str) -> dict | None:
|
|
627
664
|
"""查询单个会话的完整聚合数据."""
|
|
@@ -43,10 +43,81 @@ from .usage_recorder import UsageRecorder
|
|
|
43
43
|
# 向后兼容别名
|
|
44
44
|
BackendResponse = VendorResponse
|
|
45
45
|
NoCompatibleBackendError = NoCompatibleVendorError
|
|
46
|
-
from ..compat.canonical import
|
|
46
|
+
from ..compat.canonical import (
|
|
47
|
+
CanonicalPartType,
|
|
48
|
+
CompatibilityStatus,
|
|
49
|
+
build_canonical_request,
|
|
50
|
+
)
|
|
51
|
+
from ..model.compat import CanonicalRequest
|
|
47
52
|
|
|
48
53
|
logger = logging.getLogger(__name__)
|
|
49
54
|
|
|
55
|
+
_SESSION_TITLE_MAX_LEN = 30
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _extract_session_title(request: CanonicalRequest) -> str:
|
|
59
|
+
"""从规范化请求中提取首个用户消息文本作为 session 标题."""
|
|
60
|
+
for part in request.messages:
|
|
61
|
+
if (
|
|
62
|
+
part.role == "user"
|
|
63
|
+
and part.type == CanonicalPartType.TEXT
|
|
64
|
+
and part.text.strip()
|
|
65
|
+
):
|
|
66
|
+
return part.text.strip()[:_SESSION_TITLE_MAX_LEN]
|
|
67
|
+
return ""
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _build_semantic_rejection_diagnostic(body: dict[str, Any]) -> str:
|
|
71
|
+
"""构建语义拒绝的请求体诊断上下文.
|
|
72
|
+
|
|
73
|
+
在 semantic rejection 日志中附加请求体的可疑参数快照,
|
|
74
|
+
用于定位供应商参数校验失败的具体祸根参数。
|
|
75
|
+
"""
|
|
76
|
+
parts: list[str] = []
|
|
77
|
+
# 顶层不兼容参数
|
|
78
|
+
for key in ("thinking", "extended_thinking", "reasoning_effort"):
|
|
79
|
+
if key in body:
|
|
80
|
+
val = body[key]
|
|
81
|
+
parts.append(f"{key}={val!r:.80}")
|
|
82
|
+
# 会话历史中的 thinking blocks
|
|
83
|
+
thinking_count = 0
|
|
84
|
+
for msg in body.get("messages", []):
|
|
85
|
+
content = msg.get("content")
|
|
86
|
+
if not isinstance(content, list):
|
|
87
|
+
continue
|
|
88
|
+
for block in content:
|
|
89
|
+
if isinstance(block, dict) and block.get("type") in (
|
|
90
|
+
"thinking",
|
|
91
|
+
"redacted_thinking",
|
|
92
|
+
):
|
|
93
|
+
thinking_count += 1
|
|
94
|
+
if thinking_count:
|
|
95
|
+
parts.append(f"thinking_blocks_in_history={thinking_count}")
|
|
96
|
+
# cache_control 存在检测
|
|
97
|
+
has_cc = False
|
|
98
|
+
for section in (
|
|
99
|
+
body.get("system", []) if isinstance(body.get("system"), list) else [],
|
|
100
|
+
*(
|
|
101
|
+
m.get("content", [])
|
|
102
|
+
for m in body.get("messages", [])
|
|
103
|
+
if isinstance(m.get("content"), list)
|
|
104
|
+
),
|
|
105
|
+
body.get("tools", []),
|
|
106
|
+
):
|
|
107
|
+
if isinstance(section, list):
|
|
108
|
+
for item in section:
|
|
109
|
+
if isinstance(item, dict) and "cache_control" in item:
|
|
110
|
+
has_cc = True
|
|
111
|
+
break
|
|
112
|
+
if has_cc:
|
|
113
|
+
break
|
|
114
|
+
if has_cc:
|
|
115
|
+
parts.append("cache_control_fields=present")
|
|
116
|
+
# 模型 + 消息数
|
|
117
|
+
parts.append(f"model={body.get('model', 'N/A')}")
|
|
118
|
+
parts.append(f"messages={len(body.get('messages', []))}")
|
|
119
|
+
return f" [{', '.join(parts)}]" if parts else ""
|
|
120
|
+
|
|
50
121
|
|
|
51
122
|
def _log_http_error_detail(
|
|
52
123
|
tier_name: str,
|
|
@@ -341,10 +412,16 @@ class _RouteExecutor:
|
|
|
341
412
|
failed_tier_name: str | None = None
|
|
342
413
|
request_caps = build_request_capabilities(body)
|
|
343
414
|
canonical_request = build_canonical_request(body, headers)
|
|
344
|
-
session_record = await self._session_mgr.get_or_create_record(
|
|
415
|
+
session_record, is_new_session = await self._session_mgr.get_or_create_record(
|
|
345
416
|
canonical_request.session_key,
|
|
346
417
|
canonical_request.trace_id,
|
|
347
418
|
)
|
|
419
|
+
if is_new_session:
|
|
420
|
+
title = _extract_session_title(canonical_request)
|
|
421
|
+
if title:
|
|
422
|
+
await self._recorder.set_session_title(
|
|
423
|
+
canonical_request.session_key, title
|
|
424
|
+
)
|
|
348
425
|
incompatible_reasons: list[str] = []
|
|
349
426
|
effective_tiers = self._resolve_effective_tiers(canonical_request.session_key)
|
|
350
427
|
last_idx = len(effective_tiers) - 1
|
|
@@ -512,10 +589,16 @@ class _RouteExecutor:
|
|
|
512
589
|
failed_tier_name: str | None = None
|
|
513
590
|
request_caps = build_request_capabilities(body)
|
|
514
591
|
canonical_request = build_canonical_request(body, headers)
|
|
515
|
-
session_record = await self._session_mgr.get_or_create_record(
|
|
592
|
+
session_record, is_new_session = await self._session_mgr.get_or_create_record(
|
|
516
593
|
canonical_request.session_key,
|
|
517
594
|
canonical_request.trace_id,
|
|
518
595
|
)
|
|
596
|
+
if is_new_session:
|
|
597
|
+
title = _extract_session_title(canonical_request)
|
|
598
|
+
if title:
|
|
599
|
+
await self._recorder.set_session_title(
|
|
600
|
+
canonical_request.session_key, title
|
|
601
|
+
)
|
|
519
602
|
incompatible_reasons: list[str] = []
|
|
520
603
|
effective_tiers = self._resolve_effective_tiers(canonical_request.session_key)
|
|
521
604
|
last_idx = len(effective_tiers) - 1
|
|
@@ -601,12 +684,14 @@ class _RouteExecutor:
|
|
|
601
684
|
)
|
|
602
685
|
|
|
603
686
|
if not is_last and is_semantic:
|
|
687
|
+
diagnostic = _build_semantic_rejection_diagnostic(body)
|
|
604
688
|
logger.warning(
|
|
605
|
-
"Tier %s semantic rejection (type=%s, msg=%s), "
|
|
689
|
+
"Tier %s semantic rejection (type=%s, msg=%s)%s, "
|
|
606
690
|
"trying next tier without recording failure",
|
|
607
691
|
tier.name,
|
|
608
692
|
resp.error_type or resp.status_code,
|
|
609
693
|
(resp.error_message or "N/A")[:200],
|
|
694
|
+
diagnostic,
|
|
610
695
|
)
|
|
611
696
|
failed_tier_name = tier.name
|
|
612
697
|
continue
|
|
@@ -838,6 +923,18 @@ class _RouteExecutor:
|
|
|
838
923
|
)
|
|
839
924
|
|
|
840
925
|
if semantic_rejection and not is_last:
|
|
926
|
+
if request_body is not None:
|
|
927
|
+
diagnostic = _build_semantic_rejection_diagnostic(request_body)
|
|
928
|
+
logger.warning(
|
|
929
|
+
"Tier %s stream semantic rejection (type=%s, msg=%s)%s, "
|
|
930
|
+
"trying next tier without recording failure",
|
|
931
|
+
tier.name,
|
|
932
|
+
error.get("type") if isinstance(error, dict) else None,
|
|
933
|
+
(error.get("message") if isinstance(error, dict) else "N/A")[
|
|
934
|
+
:200
|
|
935
|
+
],
|
|
936
|
+
diagnostic,
|
|
937
|
+
)
|
|
841
938
|
return True, tier.name, exc
|
|
842
939
|
|
|
843
940
|
rl_info = parse_rate_limit_headers(
|
|
@@ -19,13 +19,18 @@ class RouteSessionManager:
|
|
|
19
19
|
|
|
20
20
|
async def get_or_create_record(
|
|
21
21
|
self, session_key: str, trace_id: str
|
|
22
|
-
) -> CompatSessionRecord | None:
|
|
22
|
+
) -> tuple[CompatSessionRecord | None, bool]:
|
|
23
|
+
"""获取或创建兼容性会话记录.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
(record, is_new) — is_new 为 True 表示本次创建的新会话。
|
|
27
|
+
"""
|
|
23
28
|
if self._store is None:
|
|
24
|
-
return None
|
|
29
|
+
return None, False
|
|
25
30
|
record = await self._store.get(session_key)
|
|
26
31
|
if record is not None:
|
|
27
|
-
return record
|
|
28
|
-
return CompatSessionRecord(session_key=session_key, trace_id=trace_id)
|
|
32
|
+
return record, False
|
|
33
|
+
return CompatSessionRecord(session_key=session_key, trace_id=trace_id), True
|
|
29
34
|
|
|
30
35
|
def apply_compat_context(
|
|
31
36
|
self,
|
|
@@ -28,6 +28,11 @@ class UsageRecorder:
|
|
|
28
28
|
def set_pricing_table(self, table: PricingTable) -> None:
|
|
29
29
|
self._pricing_table = table
|
|
30
30
|
|
|
31
|
+
async def set_session_title(self, session_key: str, title: str) -> None:
|
|
32
|
+
"""为新 session 设置标题(委托给 TokenLogger)."""
|
|
33
|
+
if self._token_logger:
|
|
34
|
+
await self._token_logger.set_session_title(session_key, title)
|
|
35
|
+
|
|
31
36
|
# ── 用量信息构建 ──────────────────────────────────────
|
|
32
37
|
|
|
33
38
|
@staticmethod
|
|
@@ -411,6 +411,7 @@ _DASHBOARD_HTML = """<!DOCTYPE html>
|
|
|
411
411
|
.session-table td.cell-tags { white-space: normal; overflow: visible; text-overflow: clip; line-height: 1.8; vertical-align: middle; }
|
|
412
412
|
.session-table tr:hover td { background: var(--bg-card-hover); }
|
|
413
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-table .session-title { font-size: 12px; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 0; }
|
|
414
415
|
.session-id { display: flex; align-items: center; gap: 4px; }
|
|
415
416
|
.session-id-text { overflow: hidden; text-overflow: ellipsis; }
|
|
416
417
|
.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; }
|
|
@@ -676,20 +677,22 @@ _DASHBOARD_HTML = """<!DOCTYPE html>
|
|
|
676
677
|
<div class="session-table-wrap" id="sessions-table-wrap">
|
|
677
678
|
<table class="session-table">
|
|
678
679
|
<colgroup>
|
|
679
|
-
<col style="width:
|
|
680
|
-
<col style="width:
|
|
680
|
+
<col style="width:10%">
|
|
681
|
+
<col style="width:15%">
|
|
681
682
|
<col style="width:6%">
|
|
683
|
+
<col style="width:5%">
|
|
684
|
+
<col style="width:5%">
|
|
685
|
+
<col style="width:15%">
|
|
686
|
+
<col style="width:10%">
|
|
682
687
|
<col style="width:6%">
|
|
683
|
-
<col style="width:
|
|
684
|
-
<col style="width:
|
|
685
|
-
<col style="width:
|
|
686
|
-
<col style="width:9%">
|
|
687
|
-
<col style="width:12%">
|
|
688
|
-
<col style="width:12%">
|
|
688
|
+
<col style="width:8%">
|
|
689
|
+
<col style="width:10%">
|
|
690
|
+
<col style="width:10%">
|
|
689
691
|
</colgroup>
|
|
690
692
|
<thead>
|
|
691
693
|
<tr>
|
|
692
694
|
<th>Session ID</th>
|
|
695
|
+
<th>Title</th>
|
|
693
696
|
<th>Last Active</th>
|
|
694
697
|
<th>Requests</th>
|
|
695
698
|
<th>Tokens</th>
|
|
@@ -702,7 +705,7 @@ _DASHBOARD_HTML = """<!DOCTYPE html>
|
|
|
702
705
|
</tr>
|
|
703
706
|
</thead>
|
|
704
707
|
<tbody id="sessions-tbody">
|
|
705
|
-
<tr><td colspan="
|
|
708
|
+
<tr><td colspan="11" class="empty">Loading...</td></tr>
|
|
706
709
|
</tbody>
|
|
707
710
|
</table>
|
|
708
711
|
<div class="session-pagination" id="session-pagination">
|
|
@@ -1573,7 +1576,7 @@ function renderSessionPage() {
|
|
|
1573
1576
|
var tbody = document.getElementById('sessions-tbody');
|
|
1574
1577
|
|
|
1575
1578
|
if (!total) {
|
|
1576
|
-
tbody.innerHTML = '<tr><td colspan="
|
|
1579
|
+
tbody.innerHTML = '<tr><td colspan="11" class="empty"><div class="empty-icon">📭</div>No session data</td></tr>';
|
|
1577
1580
|
} else {
|
|
1578
1581
|
tbody.innerHTML = page.map(function(s) {
|
|
1579
1582
|
var parsed = parseSessionKey(s.session_key);
|
|
@@ -1582,6 +1585,7 @@ function renderSessionPage() {
|
|
|
1582
1585
|
var modelsFull = (s.models || '').split(',').map(function(c){return c.trim();});
|
|
1583
1586
|
var vendorsFull = (s.vendors || '').split(',').map(function(v){return formatVendorLabel(v.trim());});
|
|
1584
1587
|
var sr = s.success_rate != null ? Math.round(s.success_rate) : null;
|
|
1588
|
+
var sessionTitle = s.title || '';
|
|
1585
1589
|
return '<tr data-row onclick="toggleRow(this)">' +
|
|
1586
1590
|
'<td class="session-key" onclick="event.stopPropagation()">' +
|
|
1587
1591
|
'<div class="session-id" data-key="' + escapeHtml(s.session_key) + '" title="' + escapeHtml(s.session_key) + '">' +
|
|
@@ -1592,6 +1596,7 @@ function renderSessionPage() {
|
|
|
1592
1596
|
'dev:' + escapeHtml(shortId(parsed.device_id, 8)) + ' · acct:' + escapeHtml(shortId(parsed.account_uuid, 8)) +
|
|
1593
1597
|
'</div>' +
|
|
1594
1598
|
'</td>' +
|
|
1599
|
+
'<td class="session-title" title="' + escapeHtml(sessionTitle) + '">' + (sessionTitle ? escapeHtml(sessionTitle) : '–') + '</td>' +
|
|
1595
1600
|
'<td>' + relativeTime(s.last_active_ts) + '</td>' +
|
|
1596
1601
|
'<td style="font-family:JetBrains Mono,monospace">' + fmtNum(s.total_requests) + '</td>' +
|
|
1597
1602
|
'<td style="font-family:JetBrains Mono,monospace">' + fmtTokens(s.total_tokens) + '</td>' +
|
|
@@ -1602,9 +1607,10 @@ function renderSessionPage() {
|
|
|
1602
1607
|
'<td onclick="event.stopPropagation()">' + selectHtml + '</td>' +
|
|
1603
1608
|
'<td>' + formatCategories(s.client_categories) + '</td>' +
|
|
1604
1609
|
'</tr>' +
|
|
1605
|
-
'<tr class="row-detail"><td colspan="
|
|
1610
|
+
'<tr class="row-detail"><td colspan="11"><div class="detail-card">' +
|
|
1606
1611
|
'<div class="detail-identity-row">' +
|
|
1607
1612
|
'<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>' +
|
|
1613
|
+
'<div class="detail-item"><div class="detail-label">Title</div><div class="detail-value">' + (sessionTitle ? escapeHtml(sessionTitle) : '–') + '</div></div>' +
|
|
1608
1614
|
'<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
1615
|
'<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
1616
|
'</div>' +
|
|
@@ -222,7 +222,7 @@ class TestTryGateTier:
|
|
|
222
222
|
headers = {}
|
|
223
223
|
caps = RequestCapabilities()
|
|
224
224
|
req = build_canonical_request(body, headers)
|
|
225
|
-
session_record = await exec_inst._session_mgr.get_or_create_record(
|
|
225
|
+
session_record, _is_new = await exec_inst._session_mgr.get_or_create_record(
|
|
226
226
|
req.session_key, req.trace_id
|
|
227
227
|
)
|
|
228
228
|
reasons: list[str] = []
|
|
@@ -246,7 +246,7 @@ class TestTryGateTier:
|
|
|
246
246
|
body = {"model": "test"}
|
|
247
247
|
headers = {}
|
|
248
248
|
req = build_canonical_request(body, headers)
|
|
249
|
-
session_record = await exec_inst._session_mgr.get_or_create_record(
|
|
249
|
+
session_record, _is_new = await exec_inst._session_mgr.get_or_create_record(
|
|
250
250
|
req.session_key, req.trace_id
|
|
251
251
|
)
|
|
252
252
|
reasons: list[str] = []
|
|
@@ -275,7 +275,7 @@ class TestTryGateTier:
|
|
|
275
275
|
body = {"model": "test", "thinking": {"type": "enabled"}}
|
|
276
276
|
headers = {}
|
|
277
277
|
req = build_canonical_request(body, headers)
|
|
278
|
-
session_record = await exec_inst._session_mgr.get_or_create_record(
|
|
278
|
+
session_record, _is_new = await exec_inst._session_mgr.get_or_create_record(
|
|
279
279
|
req.session_key, req.trace_id
|
|
280
280
|
)
|
|
281
281
|
reasons: list[str] = []
|
|
@@ -651,9 +651,10 @@ class TestRouteSessionManagerIntegration:
|
|
|
651
651
|
@pytest.mark.asyncio
|
|
652
652
|
async def test_get_or_create_without_store(self):
|
|
653
653
|
mgr = RouteSessionManager(compat_session_store=None)
|
|
654
|
-
record = await mgr.get_or_create_record("sk_test", "trace_1")
|
|
655
|
-
# 无 store 时返回 None
|
|
654
|
+
record, is_new = await mgr.get_or_create_record("sk_test", "trace_1")
|
|
655
|
+
# 无 store 时返回 (None, False)
|
|
656
656
|
assert record is None
|
|
657
|
+
assert is_new is False
|
|
657
658
|
|
|
658
659
|
@pytest.mark.asyncio
|
|
659
660
|
async def test_persist_session_without_store_is_noop(self):
|
|
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
|
{coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/convert/anthropic_to_gemini.py
RENAMED
|
File without changes
|
{coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/convert/anthropic_to_openai.py
RENAMED
|
File without changes
|
{coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/convert/gemini_sse_adapter.py
RENAMED
|
File without changes
|
{coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/convert/gemini_to_anthropic.py
RENAMED
|
File without changes
|
{coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/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
|
{coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/native_api/extractors/__init__.py
RENAMED
|
File without changes
|
{coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/native_api/extractors/anthropic.py
RENAMED
|
File without changes
|
{coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/native_api/extractors/gemini.py
RENAMED
|
File without changes
|
{coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/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
|
{coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/src/coding/proxy/streaming/anthropic_compat.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
|
{coding_proxy-0.4.1a5 → coding_proxy-0.4.1a7}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|