ccproxy-api 0.1.7__py3-none-any.whl → 0.2.0a4__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 +434 -219
- ccproxy/api/bootstrap.py +30 -0
- ccproxy/api/decorators.py +85 -0
- ccproxy/api/dependencies.py +144 -168
- ccproxy/api/format_validation.py +54 -0
- ccproxy/api/middleware/cors.py +6 -3
- ccproxy/api/middleware/errors.py +388 -524
- 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 +540 -19
- ccproxy/data/codex_headers_fallback.json +114 -7
- 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 +61 -105
- ccproxy/scheduler/registry.py +6 -32
- ccproxy/scheduler/tasks.py +268 -276
- 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 +68 -446
- ccproxy/utils/version_checker.py +273 -6
- ccproxy_api-0.2.0a4.dist-info/METADATA +212 -0
- ccproxy_api-0.2.0a4.dist-info/RECORD +417 -0
- {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0a4.dist-info}/WHEEL +1 -1
- ccproxy_api-0.2.0a4.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 -1251
- 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 -243
- ccproxy/services/codex_detection_service.py +0 -252
- 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.7.dist-info/METADATA +0 -615
- ccproxy_api-0.1.7.dist-info/RECORD +0 -191
- ccproxy_api-0.1.7.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.7.dist-info → ccproxy_api-0.2.0a4.dist-info}/licenses/LICENSE +0 -0
ccproxy/core/proxy.py
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
"""Core proxy abstractions for handling HTTP and WebSocket connections."""
|
|
2
|
-
|
|
3
|
-
from abc import ABC, abstractmethod
|
|
4
|
-
from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
|
|
5
|
-
|
|
6
|
-
from ccproxy.core.types import ProxyRequest, ProxyResponse
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
if TYPE_CHECKING:
|
|
10
|
-
from ccproxy.core.http import HTTPClient
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class BaseProxy(ABC):
|
|
14
|
-
"""Abstract base class for all proxy implementations."""
|
|
15
|
-
|
|
16
|
-
@abstractmethod
|
|
17
|
-
async def forward(self, request: ProxyRequest) -> ProxyResponse:
|
|
18
|
-
"""Forward a request and return the response.
|
|
19
|
-
|
|
20
|
-
Args:
|
|
21
|
-
request: The proxy request to forward
|
|
22
|
-
|
|
23
|
-
Returns:
|
|
24
|
-
The proxy response
|
|
25
|
-
|
|
26
|
-
Raises:
|
|
27
|
-
ProxyError: If the request cannot be forwarded
|
|
28
|
-
"""
|
|
29
|
-
pass
|
|
30
|
-
|
|
31
|
-
@abstractmethod
|
|
32
|
-
async def close(self) -> None:
|
|
33
|
-
"""Close any resources held by the proxy."""
|
|
34
|
-
pass
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class HTTPProxy(BaseProxy):
|
|
38
|
-
"""HTTP proxy implementation using HTTPClient abstractions."""
|
|
39
|
-
|
|
40
|
-
def __init__(self, http_client: "HTTPClient") -> None:
|
|
41
|
-
"""Initialize with an HTTP client.
|
|
42
|
-
|
|
43
|
-
Args:
|
|
44
|
-
http_client: The HTTP client to use for requests
|
|
45
|
-
"""
|
|
46
|
-
self.http_client = http_client
|
|
47
|
-
|
|
48
|
-
async def forward(self, request: ProxyRequest) -> ProxyResponse:
|
|
49
|
-
"""Forward an HTTP request using the HTTP client.
|
|
50
|
-
|
|
51
|
-
Args:
|
|
52
|
-
request: The proxy request to forward
|
|
53
|
-
|
|
54
|
-
Returns:
|
|
55
|
-
The proxy response
|
|
56
|
-
|
|
57
|
-
Raises:
|
|
58
|
-
ProxyError: If the request cannot be forwarded
|
|
59
|
-
"""
|
|
60
|
-
from ccproxy.core.errors import ProxyError
|
|
61
|
-
from ccproxy.core.http import HTTPError
|
|
62
|
-
|
|
63
|
-
try:
|
|
64
|
-
# Convert ProxyRequest to HTTP client format
|
|
65
|
-
body_bytes = None
|
|
66
|
-
if request.body is not None:
|
|
67
|
-
if isinstance(request.body, bytes):
|
|
68
|
-
body_bytes = request.body
|
|
69
|
-
elif isinstance(request.body, str):
|
|
70
|
-
body_bytes = request.body.encode("utf-8")
|
|
71
|
-
elif isinstance(request.body, dict):
|
|
72
|
-
import json
|
|
73
|
-
|
|
74
|
-
body_bytes = json.dumps(request.body).encode("utf-8")
|
|
75
|
-
|
|
76
|
-
# Make the HTTP request
|
|
77
|
-
status_code, headers, response_body = await self.http_client.request(
|
|
78
|
-
method=request.method.value,
|
|
79
|
-
url=request.url,
|
|
80
|
-
headers=request.headers,
|
|
81
|
-
body=body_bytes,
|
|
82
|
-
timeout=request.timeout,
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
# Convert response body to appropriate format
|
|
86
|
-
body: str | bytes | dict[str, Any] | None = response_body
|
|
87
|
-
if response_body:
|
|
88
|
-
# Try to decode as JSON if content-type suggests it
|
|
89
|
-
content_type = headers.get("content-type", "").lower()
|
|
90
|
-
if "application/json" in content_type:
|
|
91
|
-
try:
|
|
92
|
-
import json
|
|
93
|
-
|
|
94
|
-
body = json.loads(response_body.decode("utf-8"))
|
|
95
|
-
except (json.JSONDecodeError, UnicodeDecodeError):
|
|
96
|
-
# Keep as bytes if JSON parsing fails
|
|
97
|
-
body = response_body
|
|
98
|
-
elif "text/" in content_type:
|
|
99
|
-
try:
|
|
100
|
-
body = response_body.decode("utf-8")
|
|
101
|
-
except UnicodeDecodeError:
|
|
102
|
-
# Keep as bytes if text decoding fails
|
|
103
|
-
body = response_body
|
|
104
|
-
|
|
105
|
-
return ProxyResponse(
|
|
106
|
-
status_code=status_code,
|
|
107
|
-
headers=headers,
|
|
108
|
-
body=body,
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
except HTTPError as e:
|
|
112
|
-
raise ProxyError(f"HTTP request failed: {e}") from e
|
|
113
|
-
except Exception as e:
|
|
114
|
-
raise ProxyError(f"Unexpected error during HTTP request: {e}") from e
|
|
115
|
-
|
|
116
|
-
async def close(self) -> None:
|
|
117
|
-
"""Close HTTP proxy resources."""
|
|
118
|
-
await self.http_client.close()
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
class WebSocketProxy(BaseProxy):
|
|
122
|
-
"""WebSocket proxy implementation placeholder."""
|
|
123
|
-
|
|
124
|
-
async def forward(self, request: ProxyRequest) -> ProxyResponse:
|
|
125
|
-
"""Forward a WebSocket request."""
|
|
126
|
-
raise NotImplementedError("WebSocketProxy.forward not yet implemented")
|
|
127
|
-
|
|
128
|
-
async def close(self) -> None:
|
|
129
|
-
"""Close WebSocket proxy resources."""
|
|
130
|
-
pass
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
@runtime_checkable
|
|
134
|
-
class ProxyProtocol(Protocol):
|
|
135
|
-
"""Protocol defining the proxy interface."""
|
|
136
|
-
|
|
137
|
-
async def forward(self, request: ProxyRequest) -> ProxyResponse:
|
|
138
|
-
"""Forward a request and return the response."""
|
|
139
|
-
...
|
|
140
|
-
|
|
141
|
-
async def close(self) -> None:
|
|
142
|
-
"""Close any resources held by the proxy."""
|
|
143
|
-
...
|
ccproxy/core/validators.py
DELETED
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
"""Generic validation utilities for the CCProxy API."""
|
|
2
|
-
|
|
3
|
-
import re
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
from typing import Any
|
|
6
|
-
from urllib.parse import urlparse
|
|
7
|
-
|
|
8
|
-
from ccproxy.core.constants import EMAIL_PATTERN, URL_PATTERN, UUID_PATTERN
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class ValidationError(Exception):
|
|
12
|
-
"""Base class for validation errors."""
|
|
13
|
-
|
|
14
|
-
pass
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def validate_email(email: str) -> str:
|
|
18
|
-
"""Validate email format.
|
|
19
|
-
|
|
20
|
-
Args:
|
|
21
|
-
email: Email address to validate
|
|
22
|
-
|
|
23
|
-
Returns:
|
|
24
|
-
The validated email address
|
|
25
|
-
|
|
26
|
-
Raises:
|
|
27
|
-
ValidationError: If email format is invalid
|
|
28
|
-
"""
|
|
29
|
-
if not isinstance(email, str):
|
|
30
|
-
raise ValidationError("Email must be a string")
|
|
31
|
-
|
|
32
|
-
if not re.match(EMAIL_PATTERN, email):
|
|
33
|
-
raise ValidationError(f"Invalid email format: {email}")
|
|
34
|
-
|
|
35
|
-
return email.strip().lower()
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def validate_url(url: str) -> str:
|
|
39
|
-
"""Validate URL format.
|
|
40
|
-
|
|
41
|
-
Args:
|
|
42
|
-
url: URL to validate
|
|
43
|
-
|
|
44
|
-
Returns:
|
|
45
|
-
The validated URL
|
|
46
|
-
|
|
47
|
-
Raises:
|
|
48
|
-
ValidationError: If URL format is invalid
|
|
49
|
-
"""
|
|
50
|
-
if not isinstance(url, str):
|
|
51
|
-
raise ValidationError("URL must be a string")
|
|
52
|
-
|
|
53
|
-
if not re.match(URL_PATTERN, url):
|
|
54
|
-
raise ValidationError(f"Invalid URL format: {url}")
|
|
55
|
-
|
|
56
|
-
try:
|
|
57
|
-
parsed = urlparse(url)
|
|
58
|
-
if not parsed.scheme or not parsed.netloc:
|
|
59
|
-
raise ValidationError(f"Invalid URL format: {url}")
|
|
60
|
-
except Exception as e:
|
|
61
|
-
raise ValidationError(f"Invalid URL format: {url}") from e
|
|
62
|
-
|
|
63
|
-
return url.strip()
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def validate_uuid(uuid_str: str) -> str:
|
|
67
|
-
"""Validate UUID format.
|
|
68
|
-
|
|
69
|
-
Args:
|
|
70
|
-
uuid_str: UUID string to validate
|
|
71
|
-
|
|
72
|
-
Returns:
|
|
73
|
-
The validated UUID string
|
|
74
|
-
|
|
75
|
-
Raises:
|
|
76
|
-
ValidationError: If UUID format is invalid
|
|
77
|
-
"""
|
|
78
|
-
if not isinstance(uuid_str, str):
|
|
79
|
-
raise ValidationError("UUID must be a string")
|
|
80
|
-
|
|
81
|
-
if not re.match(UUID_PATTERN, uuid_str.lower()):
|
|
82
|
-
raise ValidationError(f"Invalid UUID format: {uuid_str}")
|
|
83
|
-
|
|
84
|
-
return uuid_str.strip().lower()
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def validate_path(path: str | Path, must_exist: bool = True) -> Path:
|
|
88
|
-
"""Validate file system path.
|
|
89
|
-
|
|
90
|
-
Args:
|
|
91
|
-
path: Path to validate
|
|
92
|
-
must_exist: Whether the path must exist
|
|
93
|
-
|
|
94
|
-
Returns:
|
|
95
|
-
The validated Path object
|
|
96
|
-
|
|
97
|
-
Raises:
|
|
98
|
-
ValidationError: If path is invalid
|
|
99
|
-
"""
|
|
100
|
-
if isinstance(path, str):
|
|
101
|
-
path = Path(path)
|
|
102
|
-
elif not isinstance(path, Path):
|
|
103
|
-
raise ValidationError("Path must be a string or Path object")
|
|
104
|
-
|
|
105
|
-
if must_exist and not path.exists():
|
|
106
|
-
raise ValidationError(f"Path does not exist: {path}")
|
|
107
|
-
|
|
108
|
-
return path.resolve()
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def validate_port(port: int | str) -> int:
|
|
112
|
-
"""Validate port number.
|
|
113
|
-
|
|
114
|
-
Args:
|
|
115
|
-
port: Port number to validate
|
|
116
|
-
|
|
117
|
-
Returns:
|
|
118
|
-
The validated port number
|
|
119
|
-
|
|
120
|
-
Raises:
|
|
121
|
-
ValidationError: If port is invalid
|
|
122
|
-
"""
|
|
123
|
-
if isinstance(port, str):
|
|
124
|
-
try:
|
|
125
|
-
port = int(port)
|
|
126
|
-
except ValueError as e:
|
|
127
|
-
raise ValidationError(f"Port must be a valid integer: {port}") from e
|
|
128
|
-
|
|
129
|
-
if not isinstance(port, int):
|
|
130
|
-
raise ValidationError(f"Port must be an integer: {port}")
|
|
131
|
-
|
|
132
|
-
if port < 1 or port > 65535:
|
|
133
|
-
raise ValidationError(f"Port must be between 1 and 65535: {port}")
|
|
134
|
-
|
|
135
|
-
return port
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
def validate_timeout(timeout: float | int | str) -> float:
|
|
139
|
-
"""Validate timeout value.
|
|
140
|
-
|
|
141
|
-
Args:
|
|
142
|
-
timeout: Timeout value to validate
|
|
143
|
-
|
|
144
|
-
Returns:
|
|
145
|
-
The validated timeout value
|
|
146
|
-
|
|
147
|
-
Raises:
|
|
148
|
-
ValidationError: If timeout is invalid
|
|
149
|
-
"""
|
|
150
|
-
if isinstance(timeout, str):
|
|
151
|
-
try:
|
|
152
|
-
timeout = float(timeout)
|
|
153
|
-
except ValueError as e:
|
|
154
|
-
raise ValidationError(f"Timeout must be a valid number: {timeout}") from e
|
|
155
|
-
|
|
156
|
-
if not isinstance(timeout, int | float):
|
|
157
|
-
raise ValidationError(f"Timeout must be a number: {timeout}")
|
|
158
|
-
|
|
159
|
-
if timeout < 0:
|
|
160
|
-
raise ValidationError(f"Timeout must be non-negative: {timeout}")
|
|
161
|
-
|
|
162
|
-
return float(timeout)
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
def validate_non_empty_string(value: str, name: str = "value") -> str:
|
|
166
|
-
"""Validate that a string is not empty.
|
|
167
|
-
|
|
168
|
-
Args:
|
|
169
|
-
value: String value to validate
|
|
170
|
-
name: Name of the field for error messages
|
|
171
|
-
|
|
172
|
-
Returns:
|
|
173
|
-
The validated string
|
|
174
|
-
|
|
175
|
-
Raises:
|
|
176
|
-
ValidationError: If string is empty or not a string
|
|
177
|
-
"""
|
|
178
|
-
if not isinstance(value, str):
|
|
179
|
-
raise ValidationError(f"{name} must be a string")
|
|
180
|
-
|
|
181
|
-
if not value.strip():
|
|
182
|
-
raise ValidationError(f"{name} cannot be empty")
|
|
183
|
-
|
|
184
|
-
return value.strip()
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
def validate_dict(value: Any, required_keys: list[str] | None = None) -> dict[str, Any]:
|
|
188
|
-
"""Validate dictionary and required keys.
|
|
189
|
-
|
|
190
|
-
Args:
|
|
191
|
-
value: Value to validate as dictionary
|
|
192
|
-
required_keys: List of required keys
|
|
193
|
-
|
|
194
|
-
Returns:
|
|
195
|
-
The validated dictionary
|
|
196
|
-
|
|
197
|
-
Raises:
|
|
198
|
-
ValidationError: If not a dictionary or missing required keys
|
|
199
|
-
"""
|
|
200
|
-
if not isinstance(value, dict):
|
|
201
|
-
raise ValidationError("Value must be a dictionary")
|
|
202
|
-
|
|
203
|
-
if required_keys:
|
|
204
|
-
missing_keys = [key for key in required_keys if key not in value]
|
|
205
|
-
if missing_keys:
|
|
206
|
-
raise ValidationError(f"Missing required keys: {missing_keys}")
|
|
207
|
-
|
|
208
|
-
return value
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
def validate_list(
|
|
212
|
-
value: Any, min_length: int = 0, max_length: int | None = None
|
|
213
|
-
) -> list[Any]:
|
|
214
|
-
"""Validate list and length constraints.
|
|
215
|
-
|
|
216
|
-
Args:
|
|
217
|
-
value: Value to validate as list
|
|
218
|
-
min_length: Minimum list length
|
|
219
|
-
max_length: Maximum list length
|
|
220
|
-
|
|
221
|
-
Returns:
|
|
222
|
-
The validated list
|
|
223
|
-
|
|
224
|
-
Raises:
|
|
225
|
-
ValidationError: If not a list or length constraints are violated
|
|
226
|
-
"""
|
|
227
|
-
if not isinstance(value, list):
|
|
228
|
-
raise ValidationError("Value must be a list")
|
|
229
|
-
|
|
230
|
-
if len(value) < min_length:
|
|
231
|
-
raise ValidationError(f"List must have at least {min_length} items")
|
|
232
|
-
|
|
233
|
-
if max_length is not None and len(value) > max_length:
|
|
234
|
-
raise ValidationError(f"List cannot have more than {max_length} items")
|
|
235
|
-
|
|
236
|
-
return value
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
def validate_choice(value: Any, choices: list[Any], name: str = "value") -> Any:
|
|
240
|
-
"""Validate that value is one of the allowed choices.
|
|
241
|
-
|
|
242
|
-
Args:
|
|
243
|
-
value: Value to validate
|
|
244
|
-
choices: List of allowed choices
|
|
245
|
-
name: Name of the field for error messages
|
|
246
|
-
|
|
247
|
-
Returns:
|
|
248
|
-
The validated value
|
|
249
|
-
|
|
250
|
-
Raises:
|
|
251
|
-
ValidationError: If value is not in choices
|
|
252
|
-
"""
|
|
253
|
-
if value not in choices:
|
|
254
|
-
raise ValidationError(f"{name} must be one of {choices}, got: {value}")
|
|
255
|
-
|
|
256
|
-
return value
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
def validate_range(
|
|
260
|
-
value: float | int,
|
|
261
|
-
min_value: float | int | None = None,
|
|
262
|
-
max_value: float | int | None = None,
|
|
263
|
-
name: str = "value",
|
|
264
|
-
) -> float | int:
|
|
265
|
-
"""Validate that a numeric value is within a specified range.
|
|
266
|
-
|
|
267
|
-
Args:
|
|
268
|
-
value: Numeric value to validate
|
|
269
|
-
min_value: Minimum allowed value
|
|
270
|
-
max_value: Maximum allowed value
|
|
271
|
-
name: Name of the field for error messages
|
|
272
|
-
|
|
273
|
-
Returns:
|
|
274
|
-
The validated value
|
|
275
|
-
|
|
276
|
-
Raises:
|
|
277
|
-
ValidationError: If value is outside the allowed range
|
|
278
|
-
"""
|
|
279
|
-
if not isinstance(value, int | float):
|
|
280
|
-
raise ValidationError(f"{name} must be a number")
|
|
281
|
-
|
|
282
|
-
if min_value is not None and value < min_value:
|
|
283
|
-
raise ValidationError(f"{name} must be at least {min_value}")
|
|
284
|
-
|
|
285
|
-
if max_value is not None and value > max_value:
|
|
286
|
-
raise ValidationError(f"{name} must be at most {max_value}")
|
|
287
|
-
|
|
288
|
-
return value
|
ccproxy/models/errors.py
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
"""Error response models for Anthropic API compatibility."""
|
|
2
|
-
|
|
3
|
-
from typing import Annotated, Any, Literal
|
|
4
|
-
|
|
5
|
-
from pydantic import BaseModel, Field
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class ErrorDetail(BaseModel):
|
|
9
|
-
"""Error detail information."""
|
|
10
|
-
|
|
11
|
-
type: Annotated[str, Field(description="Error type identifier")]
|
|
12
|
-
message: Annotated[str, Field(description="Human-readable error message")]
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class AnthropicError(BaseModel):
|
|
16
|
-
"""Anthropic API error response format."""
|
|
17
|
-
|
|
18
|
-
type: Annotated[Literal["error"], Field(description="Error type")] = "error"
|
|
19
|
-
error: Annotated[ErrorDetail, Field(description="Error details")]
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
# Note: Specific error model classes were removed as they were unused.
|
|
23
|
-
# Error responses are now forwarded directly from the upstream Claude API
|
|
24
|
-
# to preserve the exact error format and headers.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def create_error_response(
|
|
28
|
-
error_type: str, message: str, status_code: int = 500
|
|
29
|
-
) -> tuple[dict[str, Any], int]:
|
|
30
|
-
"""
|
|
31
|
-
Create a standardized error response.
|
|
32
|
-
|
|
33
|
-
Args:
|
|
34
|
-
error_type: Type of error (e.g., "invalid_request_error")
|
|
35
|
-
message: Human-readable error message
|
|
36
|
-
status_code: HTTP status code
|
|
37
|
-
|
|
38
|
-
Returns:
|
|
39
|
-
Tuple of (error_dict, status_code)
|
|
40
|
-
"""
|
|
41
|
-
error_response = AnthropicError(error=ErrorDetail(type=error_type, message=message))
|
|
42
|
-
return error_response.model_dump(), status_code
|