ccproxy-api 0.1.6__py3-none-any.whl → 0.2.0__py3-none-any.whl
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.
- ccproxy/api/__init__.py +1 -15
- ccproxy/api/app.py +439 -212
- ccproxy/api/bootstrap.py +30 -0
- ccproxy/api/decorators.py +85 -0
- ccproxy/api/dependencies.py +145 -176
- ccproxy/api/format_validation.py +54 -0
- ccproxy/api/middleware/cors.py +6 -3
- ccproxy/api/middleware/errors.py +402 -530
- ccproxy/api/middleware/hooks.py +563 -0
- ccproxy/api/middleware/normalize_headers.py +59 -0
- ccproxy/api/middleware/request_id.py +35 -16
- ccproxy/api/middleware/streaming_hooks.py +292 -0
- ccproxy/api/routes/__init__.py +5 -14
- ccproxy/api/routes/health.py +39 -672
- ccproxy/api/routes/plugins.py +277 -0
- ccproxy/auth/__init__.py +2 -19
- ccproxy/auth/bearer.py +25 -15
- ccproxy/auth/dependencies.py +123 -157
- ccproxy/auth/exceptions.py +0 -12
- ccproxy/auth/manager.py +35 -49
- ccproxy/auth/managers/__init__.py +10 -0
- ccproxy/auth/managers/base.py +523 -0
- ccproxy/auth/managers/base_enhanced.py +63 -0
- ccproxy/auth/managers/token_snapshot.py +77 -0
- ccproxy/auth/models/base.py +65 -0
- ccproxy/auth/models/credentials.py +40 -0
- ccproxy/auth/oauth/__init__.py +4 -18
- ccproxy/auth/oauth/base.py +533 -0
- ccproxy/auth/oauth/cli_errors.py +37 -0
- ccproxy/auth/oauth/flows.py +430 -0
- ccproxy/auth/oauth/protocol.py +366 -0
- ccproxy/auth/oauth/registry.py +408 -0
- ccproxy/auth/oauth/router.py +396 -0
- ccproxy/auth/oauth/routes.py +186 -113
- ccproxy/auth/oauth/session.py +151 -0
- ccproxy/auth/oauth/templates.py +342 -0
- ccproxy/auth/storage/__init__.py +2 -5
- ccproxy/auth/storage/base.py +279 -5
- ccproxy/auth/storage/generic.py +134 -0
- ccproxy/cli/__init__.py +1 -2
- ccproxy/cli/_settings_help.py +351 -0
- ccproxy/cli/commands/auth.py +1519 -793
- ccproxy/cli/commands/config/commands.py +209 -276
- ccproxy/cli/commands/plugins.py +669 -0
- ccproxy/cli/commands/serve.py +75 -810
- ccproxy/cli/commands/status.py +254 -0
- ccproxy/cli/decorators.py +83 -0
- ccproxy/cli/helpers.py +22 -60
- ccproxy/cli/main.py +359 -10
- ccproxy/cli/options/claude_options.py +0 -25
- ccproxy/config/__init__.py +7 -11
- ccproxy/config/core.py +227 -0
- ccproxy/config/env_generator.py +232 -0
- ccproxy/config/runtime.py +67 -0
- ccproxy/config/security.py +36 -3
- ccproxy/config/settings.py +382 -441
- ccproxy/config/toml_generator.py +299 -0
- ccproxy/config/utils.py +452 -0
- ccproxy/core/__init__.py +7 -271
- ccproxy/{_version.py → core/_version.py} +16 -3
- ccproxy/core/async_task_manager.py +516 -0
- ccproxy/core/async_utils.py +47 -14
- ccproxy/core/auth/__init__.py +6 -0
- ccproxy/core/constants.py +16 -50
- ccproxy/core/errors.py +53 -0
- ccproxy/core/id_utils.py +20 -0
- ccproxy/core/interfaces.py +16 -123
- ccproxy/core/logging.py +473 -18
- ccproxy/core/plugins/__init__.py +77 -0
- ccproxy/core/plugins/cli_discovery.py +211 -0
- ccproxy/core/plugins/declaration.py +455 -0
- ccproxy/core/plugins/discovery.py +604 -0
- ccproxy/core/plugins/factories.py +967 -0
- ccproxy/core/plugins/hooks/__init__.py +30 -0
- ccproxy/core/plugins/hooks/base.py +58 -0
- ccproxy/core/plugins/hooks/events.py +46 -0
- ccproxy/core/plugins/hooks/implementations/__init__.py +16 -0
- ccproxy/core/plugins/hooks/implementations/formatters/__init__.py +11 -0
- ccproxy/core/plugins/hooks/implementations/formatters/json.py +552 -0
- ccproxy/core/plugins/hooks/implementations/formatters/raw.py +370 -0
- ccproxy/core/plugins/hooks/implementations/http_tracer.py +431 -0
- ccproxy/core/plugins/hooks/layers.py +44 -0
- ccproxy/core/plugins/hooks/manager.py +186 -0
- ccproxy/core/plugins/hooks/registry.py +139 -0
- ccproxy/core/plugins/hooks/thread_manager.py +203 -0
- ccproxy/core/plugins/hooks/types.py +22 -0
- ccproxy/core/plugins/interfaces.py +416 -0
- ccproxy/core/plugins/loader.py +166 -0
- ccproxy/core/plugins/middleware.py +233 -0
- ccproxy/core/plugins/models.py +59 -0
- ccproxy/core/plugins/protocol.py +180 -0
- ccproxy/core/plugins/runtime.py +519 -0
- ccproxy/{observability/context.py → core/request_context.py} +137 -94
- ccproxy/core/status_report.py +211 -0
- ccproxy/core/transformers.py +13 -8
- ccproxy/data/claude_headers_fallback.json +558 -0
- ccproxy/data/codex_headers_fallback.json +121 -0
- ccproxy/http/__init__.py +30 -0
- ccproxy/http/base.py +95 -0
- ccproxy/http/client.py +323 -0
- ccproxy/http/hooks.py +642 -0
- ccproxy/http/pool.py +279 -0
- ccproxy/llms/formatters/__init__.py +7 -0
- ccproxy/llms/formatters/anthropic_to_openai/__init__.py +55 -0
- ccproxy/llms/formatters/anthropic_to_openai/errors.py +65 -0
- ccproxy/llms/formatters/anthropic_to_openai/requests.py +356 -0
- ccproxy/llms/formatters/anthropic_to_openai/responses.py +153 -0
- ccproxy/llms/formatters/anthropic_to_openai/streams.py +1546 -0
- ccproxy/llms/formatters/base.py +140 -0
- ccproxy/llms/formatters/base_model.py +33 -0
- ccproxy/llms/formatters/common/__init__.py +51 -0
- ccproxy/llms/formatters/common/identifiers.py +48 -0
- ccproxy/llms/formatters/common/streams.py +254 -0
- ccproxy/llms/formatters/common/thinking.py +74 -0
- ccproxy/llms/formatters/common/usage.py +135 -0
- ccproxy/llms/formatters/constants.py +55 -0
- ccproxy/llms/formatters/context.py +116 -0
- ccproxy/llms/formatters/mapping.py +33 -0
- ccproxy/llms/formatters/openai_to_anthropic/__init__.py +55 -0
- ccproxy/llms/formatters/openai_to_anthropic/_helpers.py +141 -0
- ccproxy/llms/formatters/openai_to_anthropic/errors.py +53 -0
- ccproxy/llms/formatters/openai_to_anthropic/requests.py +674 -0
- ccproxy/llms/formatters/openai_to_anthropic/responses.py +285 -0
- ccproxy/llms/formatters/openai_to_anthropic/streams.py +530 -0
- ccproxy/llms/formatters/openai_to_openai/__init__.py +53 -0
- ccproxy/llms/formatters/openai_to_openai/_helpers.py +325 -0
- ccproxy/llms/formatters/openai_to_openai/errors.py +6 -0
- ccproxy/llms/formatters/openai_to_openai/requests.py +388 -0
- ccproxy/llms/formatters/openai_to_openai/responses.py +594 -0
- ccproxy/llms/formatters/openai_to_openai/streams.py +1832 -0
- ccproxy/llms/formatters/utils.py +306 -0
- ccproxy/llms/models/__init__.py +9 -0
- ccproxy/llms/models/anthropic.py +619 -0
- ccproxy/llms/models/openai.py +844 -0
- ccproxy/llms/streaming/__init__.py +26 -0
- ccproxy/llms/streaming/accumulators.py +1074 -0
- ccproxy/llms/streaming/formatters.py +251 -0
- ccproxy/{adapters/openai/streaming.py → llms/streaming/processors.py} +193 -240
- ccproxy/models/__init__.py +8 -159
- ccproxy/models/detection.py +92 -193
- ccproxy/models/provider.py +75 -0
- ccproxy/plugins/access_log/README.md +32 -0
- ccproxy/plugins/access_log/__init__.py +20 -0
- ccproxy/plugins/access_log/config.py +33 -0
- ccproxy/plugins/access_log/formatter.py +126 -0
- ccproxy/plugins/access_log/hook.py +763 -0
- ccproxy/plugins/access_log/logger.py +254 -0
- ccproxy/plugins/access_log/plugin.py +137 -0
- ccproxy/plugins/access_log/writer.py +109 -0
- ccproxy/plugins/analytics/README.md +24 -0
- ccproxy/plugins/analytics/__init__.py +1 -0
- ccproxy/plugins/analytics/config.py +5 -0
- ccproxy/plugins/analytics/ingest.py +85 -0
- ccproxy/plugins/analytics/models.py +97 -0
- ccproxy/plugins/analytics/plugin.py +121 -0
- ccproxy/plugins/analytics/routes.py +163 -0
- ccproxy/plugins/analytics/service.py +284 -0
- ccproxy/plugins/claude_api/README.md +29 -0
- ccproxy/plugins/claude_api/__init__.py +10 -0
- ccproxy/plugins/claude_api/adapter.py +829 -0
- ccproxy/plugins/claude_api/config.py +52 -0
- ccproxy/plugins/claude_api/detection_service.py +461 -0
- ccproxy/plugins/claude_api/health.py +175 -0
- ccproxy/plugins/claude_api/hooks.py +284 -0
- ccproxy/plugins/claude_api/models.py +256 -0
- ccproxy/plugins/claude_api/plugin.py +298 -0
- ccproxy/plugins/claude_api/routes.py +118 -0
- ccproxy/plugins/claude_api/streaming_metrics.py +68 -0
- ccproxy/plugins/claude_api/tasks.py +84 -0
- ccproxy/plugins/claude_sdk/README.md +35 -0
- ccproxy/plugins/claude_sdk/__init__.py +80 -0
- ccproxy/plugins/claude_sdk/adapter.py +749 -0
- ccproxy/plugins/claude_sdk/auth.py +57 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/client.py +63 -39
- ccproxy/plugins/claude_sdk/config.py +210 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/converter.py +6 -6
- ccproxy/plugins/claude_sdk/detection_service.py +163 -0
- ccproxy/{services/claude_sdk_service.py → plugins/claude_sdk/handler.py} +123 -304
- ccproxy/plugins/claude_sdk/health.py +113 -0
- ccproxy/plugins/claude_sdk/hooks.py +115 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/manager.py +42 -32
- ccproxy/{claude_sdk → plugins/claude_sdk}/message_queue.py +8 -8
- ccproxy/{models/claude_sdk.py → plugins/claude_sdk/models.py} +64 -16
- ccproxy/plugins/claude_sdk/options.py +154 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/parser.py +23 -5
- ccproxy/plugins/claude_sdk/plugin.py +269 -0
- ccproxy/plugins/claude_sdk/routes.py +104 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/session_client.py +124 -12
- ccproxy/plugins/claude_sdk/session_pool.py +700 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/stream_handle.py +48 -43
- ccproxy/{claude_sdk → plugins/claude_sdk}/stream_worker.py +22 -18
- ccproxy/{claude_sdk → plugins/claude_sdk}/streaming.py +50 -16
- ccproxy/plugins/claude_sdk/tasks.py +97 -0
- ccproxy/plugins/claude_shared/README.md +18 -0
- ccproxy/plugins/claude_shared/__init__.py +12 -0
- ccproxy/plugins/claude_shared/model_defaults.py +171 -0
- ccproxy/plugins/codex/README.md +35 -0
- ccproxy/plugins/codex/__init__.py +6 -0
- ccproxy/plugins/codex/adapter.py +635 -0
- ccproxy/{config/codex.py → plugins/codex/config.py} +78 -12
- ccproxy/plugins/codex/detection_service.py +544 -0
- ccproxy/plugins/codex/health.py +162 -0
- ccproxy/plugins/codex/hooks.py +263 -0
- ccproxy/plugins/codex/model_defaults.py +39 -0
- ccproxy/plugins/codex/models.py +263 -0
- ccproxy/plugins/codex/plugin.py +275 -0
- ccproxy/plugins/codex/routes.py +129 -0
- ccproxy/plugins/codex/streaming_metrics.py +324 -0
- ccproxy/plugins/codex/tasks.py +106 -0
- ccproxy/plugins/codex/utils/__init__.py +1 -0
- ccproxy/plugins/codex/utils/sse_parser.py +106 -0
- ccproxy/plugins/command_replay/README.md +34 -0
- ccproxy/plugins/command_replay/__init__.py +17 -0
- ccproxy/plugins/command_replay/config.py +133 -0
- ccproxy/plugins/command_replay/formatter.py +432 -0
- ccproxy/plugins/command_replay/hook.py +294 -0
- ccproxy/plugins/command_replay/plugin.py +161 -0
- ccproxy/plugins/copilot/README.md +39 -0
- ccproxy/plugins/copilot/__init__.py +11 -0
- ccproxy/plugins/copilot/adapter.py +465 -0
- ccproxy/plugins/copilot/config.py +155 -0
- ccproxy/plugins/copilot/data/copilot_fallback.json +41 -0
- ccproxy/plugins/copilot/detection_service.py +255 -0
- ccproxy/plugins/copilot/manager.py +275 -0
- ccproxy/plugins/copilot/model_defaults.py +284 -0
- ccproxy/plugins/copilot/models.py +148 -0
- ccproxy/plugins/copilot/oauth/__init__.py +16 -0
- ccproxy/plugins/copilot/oauth/client.py +494 -0
- ccproxy/plugins/copilot/oauth/models.py +385 -0
- ccproxy/plugins/copilot/oauth/provider.py +602 -0
- ccproxy/plugins/copilot/oauth/storage.py +170 -0
- ccproxy/plugins/copilot/plugin.py +360 -0
- ccproxy/plugins/copilot/routes.py +294 -0
- ccproxy/plugins/credential_balancer/README.md +124 -0
- ccproxy/plugins/credential_balancer/__init__.py +6 -0
- ccproxy/plugins/credential_balancer/config.py +270 -0
- ccproxy/plugins/credential_balancer/factory.py +415 -0
- ccproxy/plugins/credential_balancer/hook.py +51 -0
- ccproxy/plugins/credential_balancer/manager.py +587 -0
- ccproxy/plugins/credential_balancer/plugin.py +146 -0
- ccproxy/plugins/dashboard/README.md +25 -0
- ccproxy/plugins/dashboard/__init__.py +1 -0
- ccproxy/plugins/dashboard/config.py +8 -0
- ccproxy/plugins/dashboard/plugin.py +71 -0
- ccproxy/plugins/dashboard/routes.py +67 -0
- ccproxy/plugins/docker/README.md +32 -0
- ccproxy/{docker → plugins/docker}/__init__.py +3 -0
- ccproxy/{docker → plugins/docker}/adapter.py +108 -10
- ccproxy/plugins/docker/config.py +82 -0
- ccproxy/{docker → plugins/docker}/docker_path.py +4 -3
- ccproxy/{docker → plugins/docker}/middleware.py +2 -2
- ccproxy/plugins/docker/plugin.py +198 -0
- ccproxy/{docker → plugins/docker}/stream_process.py +3 -3
- ccproxy/plugins/duckdb_storage/README.md +26 -0
- ccproxy/plugins/duckdb_storage/__init__.py +1 -0
- ccproxy/plugins/duckdb_storage/config.py +22 -0
- ccproxy/plugins/duckdb_storage/plugin.py +128 -0
- ccproxy/plugins/duckdb_storage/routes.py +51 -0
- ccproxy/plugins/duckdb_storage/storage.py +633 -0
- ccproxy/plugins/max_tokens/README.md +38 -0
- ccproxy/plugins/max_tokens/__init__.py +12 -0
- ccproxy/plugins/max_tokens/adapter.py +235 -0
- ccproxy/plugins/max_tokens/config.py +86 -0
- ccproxy/plugins/max_tokens/models.py +53 -0
- ccproxy/plugins/max_tokens/plugin.py +200 -0
- ccproxy/plugins/max_tokens/service.py +271 -0
- ccproxy/plugins/max_tokens/token_limits.json +54 -0
- ccproxy/plugins/metrics/README.md +35 -0
- ccproxy/plugins/metrics/__init__.py +10 -0
- ccproxy/{observability/metrics.py → plugins/metrics/collector.py} +20 -153
- ccproxy/plugins/metrics/config.py +85 -0
- ccproxy/plugins/metrics/grafana/dashboards/ccproxy-dashboard.json +1720 -0
- ccproxy/plugins/metrics/hook.py +403 -0
- ccproxy/plugins/metrics/plugin.py +268 -0
- ccproxy/{observability → plugins/metrics}/pushgateway.py +57 -59
- ccproxy/plugins/metrics/routes.py +107 -0
- ccproxy/plugins/metrics/tasks.py +117 -0
- ccproxy/plugins/oauth_claude/README.md +35 -0
- ccproxy/plugins/oauth_claude/__init__.py +14 -0
- ccproxy/plugins/oauth_claude/client.py +270 -0
- ccproxy/plugins/oauth_claude/config.py +84 -0
- ccproxy/plugins/oauth_claude/manager.py +482 -0
- ccproxy/plugins/oauth_claude/models.py +266 -0
- ccproxy/plugins/oauth_claude/plugin.py +149 -0
- ccproxy/plugins/oauth_claude/provider.py +571 -0
- ccproxy/plugins/oauth_claude/storage.py +212 -0
- ccproxy/plugins/oauth_codex/README.md +38 -0
- ccproxy/plugins/oauth_codex/__init__.py +14 -0
- ccproxy/plugins/oauth_codex/client.py +224 -0
- ccproxy/plugins/oauth_codex/config.py +95 -0
- ccproxy/plugins/oauth_codex/manager.py +256 -0
- ccproxy/plugins/oauth_codex/models.py +239 -0
- ccproxy/plugins/oauth_codex/plugin.py +146 -0
- ccproxy/plugins/oauth_codex/provider.py +574 -0
- ccproxy/plugins/oauth_codex/storage.py +92 -0
- ccproxy/plugins/permissions/README.md +28 -0
- ccproxy/plugins/permissions/__init__.py +22 -0
- ccproxy/plugins/permissions/config.py +28 -0
- ccproxy/{cli/commands/permission_handler.py → plugins/permissions/handlers/cli.py} +49 -25
- ccproxy/plugins/permissions/handlers/protocol.py +33 -0
- ccproxy/plugins/permissions/handlers/terminal.py +675 -0
- ccproxy/{api/routes → plugins/permissions}/mcp.py +34 -7
- ccproxy/{models/permissions.py → plugins/permissions/models.py} +65 -1
- ccproxy/plugins/permissions/plugin.py +153 -0
- ccproxy/{api/routes/permissions.py → plugins/permissions/routes.py} +20 -16
- ccproxy/{api/services/permission_service.py → plugins/permissions/service.py} +65 -11
- ccproxy/{api → plugins/permissions}/ui/permission_handler_protocol.py +1 -1
- ccproxy/{api → plugins/permissions}/ui/terminal_permission_handler.py +66 -10
- ccproxy/plugins/pricing/README.md +34 -0
- ccproxy/plugins/pricing/__init__.py +6 -0
- ccproxy/{pricing → plugins/pricing}/cache.py +7 -6
- ccproxy/{config/pricing.py → plugins/pricing/config.py} +32 -6
- ccproxy/plugins/pricing/exceptions.py +35 -0
- ccproxy/plugins/pricing/loader.py +440 -0
- ccproxy/{pricing → plugins/pricing}/models.py +13 -23
- ccproxy/plugins/pricing/plugin.py +169 -0
- ccproxy/plugins/pricing/service.py +191 -0
- ccproxy/plugins/pricing/tasks.py +300 -0
- ccproxy/{pricing → plugins/pricing}/updater.py +86 -72
- ccproxy/plugins/pricing/utils.py +99 -0
- ccproxy/plugins/request_tracer/README.md +40 -0
- ccproxy/plugins/request_tracer/__init__.py +7 -0
- ccproxy/plugins/request_tracer/config.py +120 -0
- ccproxy/plugins/request_tracer/hook.py +415 -0
- ccproxy/plugins/request_tracer/plugin.py +255 -0
- ccproxy/scheduler/__init__.py +2 -14
- ccproxy/scheduler/core.py +26 -41
- ccproxy/scheduler/manager.py +63 -107
- ccproxy/scheduler/registry.py +6 -32
- ccproxy/scheduler/tasks.py +346 -314
- ccproxy/services/__init__.py +0 -1
- ccproxy/services/adapters/__init__.py +11 -0
- ccproxy/services/adapters/base.py +123 -0
- ccproxy/services/adapters/chain_composer.py +88 -0
- ccproxy/services/adapters/chain_validation.py +44 -0
- ccproxy/services/adapters/chat_accumulator.py +200 -0
- ccproxy/services/adapters/delta_utils.py +142 -0
- ccproxy/services/adapters/format_adapter.py +136 -0
- ccproxy/services/adapters/format_context.py +11 -0
- ccproxy/services/adapters/format_registry.py +158 -0
- ccproxy/services/adapters/http_adapter.py +1045 -0
- ccproxy/services/adapters/mock_adapter.py +118 -0
- ccproxy/services/adapters/protocols.py +35 -0
- ccproxy/services/adapters/simple_converters.py +571 -0
- ccproxy/services/auth_registry.py +180 -0
- ccproxy/services/cache/__init__.py +6 -0
- ccproxy/services/cache/response_cache.py +261 -0
- ccproxy/services/cli_detection.py +437 -0
- ccproxy/services/config/__init__.py +6 -0
- ccproxy/services/config/proxy_configuration.py +111 -0
- ccproxy/services/container.py +256 -0
- ccproxy/services/factories.py +380 -0
- ccproxy/services/handler_config.py +76 -0
- ccproxy/services/interfaces.py +298 -0
- ccproxy/services/mocking/__init__.py +6 -0
- ccproxy/services/mocking/mock_handler.py +291 -0
- ccproxy/services/tracing/__init__.py +7 -0
- ccproxy/services/tracing/interfaces.py +61 -0
- ccproxy/services/tracing/null_tracer.py +57 -0
- ccproxy/streaming/__init__.py +23 -0
- ccproxy/streaming/buffer.py +1056 -0
- ccproxy/streaming/deferred.py +897 -0
- ccproxy/streaming/handler.py +117 -0
- ccproxy/streaming/interfaces.py +77 -0
- ccproxy/streaming/simple_adapter.py +39 -0
- ccproxy/streaming/sse.py +109 -0
- ccproxy/streaming/sse_parser.py +127 -0
- ccproxy/templates/__init__.py +6 -0
- ccproxy/templates/plugin_scaffold.py +695 -0
- ccproxy/testing/endpoints/__init__.py +33 -0
- ccproxy/testing/endpoints/cli.py +215 -0
- ccproxy/testing/endpoints/config.py +874 -0
- ccproxy/testing/endpoints/console.py +57 -0
- ccproxy/testing/endpoints/models.py +100 -0
- ccproxy/testing/endpoints/runner.py +1903 -0
- ccproxy/testing/endpoints/tools.py +308 -0
- ccproxy/testing/mock_responses.py +70 -1
- ccproxy/testing/response_handlers.py +20 -0
- ccproxy/utils/__init__.py +0 -6
- ccproxy/utils/binary_resolver.py +476 -0
- ccproxy/utils/caching.py +327 -0
- ccproxy/utils/cli_logging.py +101 -0
- ccproxy/utils/command_line.py +251 -0
- ccproxy/utils/headers.py +228 -0
- ccproxy/utils/model_mapper.py +120 -0
- ccproxy/utils/startup_helpers.py +95 -342
- ccproxy/utils/version_checker.py +279 -6
- ccproxy_api-0.2.0.dist-info/METADATA +212 -0
- ccproxy_api-0.2.0.dist-info/RECORD +417 -0
- {ccproxy_api-0.1.6.dist-info → ccproxy_api-0.2.0.dist-info}/WHEEL +1 -1
- ccproxy_api-0.2.0.dist-info/entry_points.txt +24 -0
- ccproxy/__init__.py +0 -4
- ccproxy/adapters/__init__.py +0 -11
- ccproxy/adapters/base.py +0 -80
- ccproxy/adapters/codex/__init__.py +0 -11
- ccproxy/adapters/openai/__init__.py +0 -42
- ccproxy/adapters/openai/adapter.py +0 -953
- ccproxy/adapters/openai/models.py +0 -412
- ccproxy/adapters/openai/response_adapter.py +0 -355
- ccproxy/adapters/openai/response_models.py +0 -178
- ccproxy/api/middleware/headers.py +0 -49
- ccproxy/api/middleware/logging.py +0 -180
- ccproxy/api/middleware/request_content_logging.py +0 -297
- ccproxy/api/middleware/server_header.py +0 -58
- ccproxy/api/responses.py +0 -89
- ccproxy/api/routes/claude.py +0 -371
- ccproxy/api/routes/codex.py +0 -1231
- ccproxy/api/routes/metrics.py +0 -1029
- ccproxy/api/routes/proxy.py +0 -211
- ccproxy/api/services/__init__.py +0 -6
- ccproxy/auth/conditional.py +0 -84
- ccproxy/auth/credentials_adapter.py +0 -93
- ccproxy/auth/models.py +0 -118
- ccproxy/auth/oauth/models.py +0 -48
- ccproxy/auth/openai/__init__.py +0 -13
- ccproxy/auth/openai/credentials.py +0 -166
- ccproxy/auth/openai/oauth_client.py +0 -334
- ccproxy/auth/openai/storage.py +0 -184
- ccproxy/auth/storage/json_file.py +0 -158
- ccproxy/auth/storage/keyring.py +0 -189
- ccproxy/claude_sdk/__init__.py +0 -18
- ccproxy/claude_sdk/options.py +0 -194
- ccproxy/claude_sdk/session_pool.py +0 -550
- ccproxy/cli/docker/__init__.py +0 -34
- ccproxy/cli/docker/adapter_factory.py +0 -157
- ccproxy/cli/docker/params.py +0 -274
- ccproxy/config/auth.py +0 -153
- ccproxy/config/claude.py +0 -348
- ccproxy/config/cors.py +0 -79
- ccproxy/config/discovery.py +0 -95
- ccproxy/config/docker_settings.py +0 -264
- ccproxy/config/observability.py +0 -158
- ccproxy/config/reverse_proxy.py +0 -31
- ccproxy/config/scheduler.py +0 -108
- ccproxy/config/server.py +0 -86
- ccproxy/config/validators.py +0 -231
- ccproxy/core/codex_transformers.py +0 -389
- ccproxy/core/http.py +0 -328
- ccproxy/core/http_transformers.py +0 -812
- ccproxy/core/proxy.py +0 -143
- ccproxy/core/validators.py +0 -288
- ccproxy/models/errors.py +0 -42
- ccproxy/models/messages.py +0 -269
- ccproxy/models/requests.py +0 -107
- ccproxy/models/responses.py +0 -270
- ccproxy/models/types.py +0 -102
- ccproxy/observability/__init__.py +0 -51
- ccproxy/observability/access_logger.py +0 -457
- ccproxy/observability/sse_events.py +0 -303
- ccproxy/observability/stats_printer.py +0 -753
- ccproxy/observability/storage/__init__.py +0 -1
- ccproxy/observability/storage/duckdb_simple.py +0 -677
- ccproxy/observability/storage/models.py +0 -70
- ccproxy/observability/streaming_response.py +0 -107
- ccproxy/pricing/__init__.py +0 -19
- ccproxy/pricing/loader.py +0 -251
- ccproxy/services/claude_detection_service.py +0 -269
- ccproxy/services/codex_detection_service.py +0 -263
- ccproxy/services/credentials/__init__.py +0 -55
- ccproxy/services/credentials/config.py +0 -105
- ccproxy/services/credentials/manager.py +0 -561
- ccproxy/services/credentials/oauth_client.py +0 -481
- ccproxy/services/proxy_service.py +0 -1827
- ccproxy/static/.keep +0 -0
- ccproxy/utils/cost_calculator.py +0 -210
- ccproxy/utils/disconnection_monitor.py +0 -83
- ccproxy/utils/model_mapping.py +0 -199
- ccproxy/utils/models_provider.py +0 -150
- ccproxy/utils/simple_request_logger.py +0 -284
- ccproxy/utils/streaming_metrics.py +0 -199
- ccproxy_api-0.1.6.dist-info/METADATA +0 -615
- ccproxy_api-0.1.6.dist-info/RECORD +0 -189
- ccproxy_api-0.1.6.dist-info/entry_points.txt +0 -4
- /ccproxy/{api/middleware/auth.py → auth/models/__init__.py} +0 -0
- /ccproxy/{claude_sdk → plugins/claude_sdk}/exceptions.py +0 -0
- /ccproxy/{docker → plugins/docker}/models.py +0 -0
- /ccproxy/{docker → plugins/docker}/protocol.py +0 -0
- /ccproxy/{docker → plugins/docker}/validators.py +0 -0
- /ccproxy/{auth/oauth/storage.py → plugins/permissions/handlers/__init__.py} +0 -0
- /ccproxy/{api → plugins/permissions}/ui/__init__.py +0 -0
- {ccproxy_api-0.1.6.dist-info → ccproxy_api-0.2.0.dist-info}/licenses/LICENSE +0 -0
ccproxy/http/pool.py
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"""HTTP Connection Pool Manager for CCProxy.
|
|
2
|
+
|
|
3
|
+
This module provides centralized management of HTTP connection pools,
|
|
4
|
+
ensuring efficient resource usage and preventing duplicate client creation.
|
|
5
|
+
Implements Phase 2.3 of the refactoring plan.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
from typing import Any
|
|
10
|
+
from urllib.parse import urlparse
|
|
11
|
+
|
|
12
|
+
import httpx
|
|
13
|
+
import structlog
|
|
14
|
+
|
|
15
|
+
from ccproxy.config.settings import Settings
|
|
16
|
+
from ccproxy.config.utils import HTTP_STREAMING_TIMEOUT
|
|
17
|
+
from ccproxy.http.client import HTTPClientFactory
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
logger = structlog.get_logger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class HTTPPoolManager:
|
|
24
|
+
"""Manages HTTP connection pools for different base URLs.
|
|
25
|
+
|
|
26
|
+
This manager ensures that:
|
|
27
|
+
- Each unique base URL gets its own optimized connection pool
|
|
28
|
+
- Connection pools are reused across all components
|
|
29
|
+
- Resources are properly cleaned up on shutdown
|
|
30
|
+
- Configuration is consistent across all clients
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self, settings: Settings | None = None, hook_manager: Any | None = None
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Initialize the HTTP pool manager.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
settings: Optional application settings for configuration
|
|
40
|
+
hook_manager: Optional hook manager for request/response tracing
|
|
41
|
+
"""
|
|
42
|
+
self.settings = settings
|
|
43
|
+
self.hook_manager = hook_manager
|
|
44
|
+
self._pools: dict[str, httpx.AsyncClient] = {}
|
|
45
|
+
self._shared_client: httpx.AsyncClient | None = None
|
|
46
|
+
self._lock = asyncio.Lock()
|
|
47
|
+
|
|
48
|
+
logger.trace("http_pool_manager_initialized", category="lifecycle")
|
|
49
|
+
|
|
50
|
+
async def get_client(
|
|
51
|
+
self,
|
|
52
|
+
base_url: str | None = None,
|
|
53
|
+
*,
|
|
54
|
+
timeout: float | None = None,
|
|
55
|
+
headers: dict[str, str] | None = None,
|
|
56
|
+
**kwargs: Any,
|
|
57
|
+
) -> httpx.AsyncClient:
|
|
58
|
+
"""Get or create an HTTP client for the specified base URL.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
base_url: Optional base URL for the client. If None, returns the default client
|
|
62
|
+
timeout: Optional custom timeout for this client
|
|
63
|
+
headers: Optional default headers for this client
|
|
64
|
+
**kwargs: Additional configuration for the client
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
Configured httpx.AsyncClient instance
|
|
68
|
+
"""
|
|
69
|
+
# If no base URL, return the shared general-purpose client
|
|
70
|
+
if not base_url:
|
|
71
|
+
return await self.get_shared_client()
|
|
72
|
+
|
|
73
|
+
# Normalize the base URL to use as a key
|
|
74
|
+
pool_key = self._normalize_base_url(base_url)
|
|
75
|
+
|
|
76
|
+
async with self._lock:
|
|
77
|
+
# Check if we already have a client for this base URL
|
|
78
|
+
if pool_key in self._pools:
|
|
79
|
+
logger.trace(
|
|
80
|
+
"reusing_existing_pool",
|
|
81
|
+
base_url=base_url,
|
|
82
|
+
pool_key=pool_key,
|
|
83
|
+
category="lifecycle",
|
|
84
|
+
)
|
|
85
|
+
return self._pools[pool_key]
|
|
86
|
+
|
|
87
|
+
# Create a new client for this base URL
|
|
88
|
+
logger.trace(
|
|
89
|
+
"creating_new_pool",
|
|
90
|
+
base_url=base_url,
|
|
91
|
+
pool_key=pool_key,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Build client configuration
|
|
95
|
+
client_config: dict[str, Any] = {
|
|
96
|
+
"base_url": base_url,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if headers:
|
|
100
|
+
client_config["headers"] = headers
|
|
101
|
+
|
|
102
|
+
if timeout is not None:
|
|
103
|
+
client_config["timeout_read"] = timeout
|
|
104
|
+
|
|
105
|
+
# Merge with any additional kwargs
|
|
106
|
+
client_config.update(kwargs)
|
|
107
|
+
|
|
108
|
+
# Create the client using the factory with HTTP/2 enabled for better multiplexing
|
|
109
|
+
client = HTTPClientFactory.create_client(
|
|
110
|
+
settings=self.settings,
|
|
111
|
+
hook_manager=self.hook_manager,
|
|
112
|
+
http2=False, # Enable HTTP/2 for connection multiplexing
|
|
113
|
+
**client_config,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Store in the pool
|
|
117
|
+
self._pools[pool_key] = client
|
|
118
|
+
|
|
119
|
+
return client
|
|
120
|
+
|
|
121
|
+
async def get_shared_client(self) -> httpx.AsyncClient:
|
|
122
|
+
"""Get the default general-purpose HTTP client.
|
|
123
|
+
|
|
124
|
+
This client is used for requests without a specific base URL and is managed
|
|
125
|
+
by this pool manager for reuse during the app lifetime.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
The default httpx.AsyncClient instance
|
|
129
|
+
"""
|
|
130
|
+
async with self._lock:
|
|
131
|
+
if self._shared_client is None:
|
|
132
|
+
logger.trace("default_client_created")
|
|
133
|
+
self._shared_client = HTTPClientFactory.create_client(
|
|
134
|
+
settings=self.settings,
|
|
135
|
+
hook_manager=self.hook_manager,
|
|
136
|
+
http2=False, # Enable HTTP/1 for default client
|
|
137
|
+
)
|
|
138
|
+
return self._shared_client
|
|
139
|
+
|
|
140
|
+
async def get_streaming_client(
|
|
141
|
+
self,
|
|
142
|
+
base_url: str | None = None,
|
|
143
|
+
*,
|
|
144
|
+
headers: dict[str, str] | None = None,
|
|
145
|
+
**kwargs: Any,
|
|
146
|
+
) -> httpx.AsyncClient:
|
|
147
|
+
"""Get or create a client optimized for streaming.
|
|
148
|
+
|
|
149
|
+
Uses a longer read timeout appropriate for SSE/streaming endpoints.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
base_url: Optional base URL for the client
|
|
153
|
+
headers: Optional default headers
|
|
154
|
+
**kwargs: Additional client kwargs merged into configuration
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
Configured httpx.AsyncClient instance
|
|
158
|
+
"""
|
|
159
|
+
return await self.get_client(
|
|
160
|
+
base_url=base_url,
|
|
161
|
+
timeout=HTTP_STREAMING_TIMEOUT,
|
|
162
|
+
headers=headers,
|
|
163
|
+
**kwargs,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
def get_shared_client_sync(self) -> httpx.AsyncClient:
|
|
167
|
+
"""Get or create the default client synchronously.
|
|
168
|
+
|
|
169
|
+
This is used during initialization when we're not in an async context.
|
|
170
|
+
Note: This doesn't use locking, so it should only be called during
|
|
171
|
+
single-threaded initialization.
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
The default httpx.AsyncClient instance
|
|
175
|
+
"""
|
|
176
|
+
if self._shared_client is None:
|
|
177
|
+
logger.trace("default_client_created_sync")
|
|
178
|
+
self._shared_client = HTTPClientFactory.create_client(
|
|
179
|
+
settings=self.settings,
|
|
180
|
+
hook_manager=self.hook_manager,
|
|
181
|
+
http2=False, # Disable HTTP/2 to ensure logging transport works
|
|
182
|
+
)
|
|
183
|
+
return self._shared_client
|
|
184
|
+
|
|
185
|
+
def get_pool_client(self, base_url: str) -> httpx.AsyncClient | None:
|
|
186
|
+
"""Get an existing client for a base URL without creating one.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
base_url: The base URL to look up
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
Existing client or None if not found
|
|
193
|
+
"""
|
|
194
|
+
pool_key = self._normalize_base_url(base_url)
|
|
195
|
+
return self._pools.get(pool_key)
|
|
196
|
+
|
|
197
|
+
def _normalize_base_url(self, base_url: str) -> str:
|
|
198
|
+
"""Normalize a base URL to use as a pool key.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
base_url: The base URL to normalize
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
Normalized URL suitable for use as a dictionary key
|
|
205
|
+
"""
|
|
206
|
+
parsed = urlparse(base_url)
|
|
207
|
+
# Use scheme + netloc as the key (ignore path/query/fragment)
|
|
208
|
+
# This ensures all requests to the same host share a pool
|
|
209
|
+
return f"{parsed.scheme}://{parsed.netloc}"
|
|
210
|
+
|
|
211
|
+
async def close_pool(self, base_url: str) -> None:
|
|
212
|
+
"""Close and remove a specific connection pool.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
base_url: The base URL of the pool to close
|
|
216
|
+
"""
|
|
217
|
+
pool_key = self._normalize_base_url(base_url)
|
|
218
|
+
|
|
219
|
+
async with self._lock:
|
|
220
|
+
if pool_key in self._pools:
|
|
221
|
+
client = self._pools.pop(pool_key)
|
|
222
|
+
await client.aclose()
|
|
223
|
+
logger.trace(
|
|
224
|
+
"pool_closed",
|
|
225
|
+
base_url=base_url,
|
|
226
|
+
pool_key=pool_key,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
async def close_all(self) -> None:
|
|
230
|
+
"""Close all connection pools and clean up resources.
|
|
231
|
+
|
|
232
|
+
This should be called during application shutdown.
|
|
233
|
+
"""
|
|
234
|
+
async with self._lock:
|
|
235
|
+
# Close all URL-specific pools
|
|
236
|
+
for pool_key, client in self._pools.items():
|
|
237
|
+
try:
|
|
238
|
+
await client.aclose()
|
|
239
|
+
logger.trace("pool_closed", pool_key=pool_key)
|
|
240
|
+
except Exception as e:
|
|
241
|
+
logger.error(
|
|
242
|
+
"pool_close_error",
|
|
243
|
+
pool_key=pool_key,
|
|
244
|
+
error=str(e),
|
|
245
|
+
exc_info=e,
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
self._pools.clear()
|
|
249
|
+
|
|
250
|
+
# Close the default client
|
|
251
|
+
if self._shared_client:
|
|
252
|
+
try:
|
|
253
|
+
await self._shared_client.aclose()
|
|
254
|
+
logger.trace("default_client_closed")
|
|
255
|
+
except Exception as e:
|
|
256
|
+
logger.error(
|
|
257
|
+
"default_client_close_error",
|
|
258
|
+
error=str(e),
|
|
259
|
+
exc_info=e,
|
|
260
|
+
)
|
|
261
|
+
self._shared_client = None
|
|
262
|
+
|
|
263
|
+
logger.trace("all_pools_closed")
|
|
264
|
+
|
|
265
|
+
def get_pool_stats(self) -> dict[str, Any]:
|
|
266
|
+
"""Get statistics about the current connection pools.
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
Dictionary with pool statistics
|
|
270
|
+
"""
|
|
271
|
+
return {
|
|
272
|
+
"total_pools": len(self._pools),
|
|
273
|
+
"pool_keys": list(self._pools.keys()),
|
|
274
|
+
"has_default_client": self._shared_client is not None,
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
# Global helper functions were removed to avoid mixed patterns.
|
|
279
|
+
# Use the DI container to access an `HTTPPoolManager` instance.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Facade module exposing Anthropic→OpenAI formatter entry points."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from types import ModuleType
|
|
5
|
+
|
|
6
|
+
from . import streams as _streams
|
|
7
|
+
from .errors import convert__anthropic_to_openai__error
|
|
8
|
+
from .requests import (
|
|
9
|
+
convert__anthropic_message_to_openai_chat__request,
|
|
10
|
+
convert__anthropic_message_to_openai_responses__request,
|
|
11
|
+
)
|
|
12
|
+
from .responses import (
|
|
13
|
+
convert__anthropic_message_to_openai_chat__response,
|
|
14
|
+
convert__anthropic_message_to_openai_responses__response,
|
|
15
|
+
convert__anthropic_usage_to_openai_completion__usage,
|
|
16
|
+
convert__anthropic_usage_to_openai_responses__usage,
|
|
17
|
+
)
|
|
18
|
+
from .streams import (
|
|
19
|
+
AnthropicToOpenAIChatStreamAdapter,
|
|
20
|
+
AnthropicToOpenAIResponsesStreamAdapter,
|
|
21
|
+
convert__anthropic_message_to_openai_chat__stream,
|
|
22
|
+
convert__anthropic_message_to_openai_responses__stream,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"convert__anthropic_to_openai__error",
|
|
28
|
+
"convert__anthropic_message_to_openai_chat__request",
|
|
29
|
+
"convert__anthropic_message_to_openai_responses__request",
|
|
30
|
+
"convert__anthropic_message_to_openai_chat__response",
|
|
31
|
+
"convert__anthropic_message_to_openai_responses__response",
|
|
32
|
+
"convert__anthropic_usage_to_openai_completion__usage",
|
|
33
|
+
"convert__anthropic_usage_to_openai_responses__usage",
|
|
34
|
+
"AnthropicToOpenAIChatStreamAdapter",
|
|
35
|
+
"AnthropicToOpenAIResponsesStreamAdapter",
|
|
36
|
+
"convert__anthropic_message_to_openai_chat__stream",
|
|
37
|
+
"convert__anthropic_message_to_openai_responses__stream",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class _AnthropicToOpenAIModule(ModuleType):
|
|
42
|
+
_propagated_names = {
|
|
43
|
+
"AnthropicToOpenAIChatStreamAdapter",
|
|
44
|
+
"AnthropicToOpenAIResponsesStreamAdapter",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
def __setattr__(self, name: str, value: object) -> None:
|
|
48
|
+
super().__setattr__(name, value)
|
|
49
|
+
if name in self._propagated_names and hasattr(_streams, name):
|
|
50
|
+
setattr(_streams, name, value)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
_module = sys.modules[__name__]
|
|
54
|
+
if not isinstance(_module, _AnthropicToOpenAIModule):
|
|
55
|
+
_module.__class__ = _AnthropicToOpenAIModule
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Anthropic→OpenAI error conversion entry point."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
from ccproxy.llms.formatters.constants import ANTHROPIC_TO_OPENAI_ERROR_TYPE
|
|
8
|
+
from ccproxy.llms.models.openai import ErrorDetail
|
|
9
|
+
from ccproxy.llms.models.openai import ErrorResponse as OpenAIErrorResponse
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def convert__anthropic_to_openai__error(error: BaseModel) -> BaseModel:
|
|
13
|
+
"""Convert an Anthropic error payload to the OpenAI envelope."""
|
|
14
|
+
from ccproxy.llms.models.anthropic import ErrorResponse as AnthropicErrorResponse
|
|
15
|
+
|
|
16
|
+
if isinstance(error, AnthropicErrorResponse):
|
|
17
|
+
anthropic_error = error.error
|
|
18
|
+
error_message = anthropic_error.message
|
|
19
|
+
anthropic_error_type = "api_error"
|
|
20
|
+
if hasattr(anthropic_error, "type"):
|
|
21
|
+
anthropic_error_type = anthropic_error.type
|
|
22
|
+
|
|
23
|
+
openai_error_type = ANTHROPIC_TO_OPENAI_ERROR_TYPE.get(
|
|
24
|
+
anthropic_error_type, "api_error"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
return OpenAIErrorResponse(
|
|
28
|
+
error=ErrorDetail(
|
|
29
|
+
message=error_message,
|
|
30
|
+
type=openai_error_type,
|
|
31
|
+
code=None,
|
|
32
|
+
param=None,
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if hasattr(error, "error") and hasattr(error.error, "message"):
|
|
37
|
+
error_message = error.error.message
|
|
38
|
+
return OpenAIErrorResponse(
|
|
39
|
+
error=ErrorDetail(
|
|
40
|
+
message=error_message,
|
|
41
|
+
type="api_error",
|
|
42
|
+
code=None,
|
|
43
|
+
param=None,
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
error_message = "Unknown error occurred"
|
|
48
|
+
if hasattr(error, "message"):
|
|
49
|
+
error_message = error.message
|
|
50
|
+
elif hasattr(error, "model_dump"):
|
|
51
|
+
error_dict = error.model_dump()
|
|
52
|
+
if isinstance(error_dict, dict):
|
|
53
|
+
error_message = error_dict.get("message", str(error_dict))
|
|
54
|
+
|
|
55
|
+
return OpenAIErrorResponse(
|
|
56
|
+
error=ErrorDetail(
|
|
57
|
+
message=error_message,
|
|
58
|
+
type="api_error",
|
|
59
|
+
code=None,
|
|
60
|
+
param=None,
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
__all__ = ["convert__anthropic_to_openai__error"]
|