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/http/client.py
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
"""Centralized HTTP client configuration and abstractions for CCProxy.
|
|
2
|
+
|
|
3
|
+
This module provides:
|
|
4
|
+
- HTTP client factory with optimized configuration for proxy use cases
|
|
5
|
+
- Generic HTTP client abstractions for pure forwarding without business logic
|
|
6
|
+
- Lifecycle managed by the ServiceContainer
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
from collections.abc import AsyncGenerator
|
|
11
|
+
from contextlib import asynccontextmanager
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import TYPE_CHECKING, Any
|
|
14
|
+
|
|
15
|
+
import httpx
|
|
16
|
+
import structlog
|
|
17
|
+
|
|
18
|
+
from ccproxy.config.settings import Settings
|
|
19
|
+
from ccproxy.http.hooks import HookableHTTPClient
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
logger = structlog.get_logger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
import httpx
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class HTTPError(Exception):
|
|
30
|
+
"""Base exception for HTTP client errors."""
|
|
31
|
+
|
|
32
|
+
def __init__(self, message: str, status_code: int | None = None) -> None:
|
|
33
|
+
"""Initialize HTTP error.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
message: Error message
|
|
37
|
+
status_code: HTTP status code (optional)
|
|
38
|
+
"""
|
|
39
|
+
super().__init__(message)
|
|
40
|
+
self.status_code = status_code
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class HTTPTimeoutError(HTTPError):
|
|
44
|
+
"""Exception raised when HTTP request times out."""
|
|
45
|
+
|
|
46
|
+
def __init__(self, message: str = "Request timed out") -> None:
|
|
47
|
+
"""Initialize timeout error.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
message: Error message
|
|
51
|
+
"""
|
|
52
|
+
super().__init__(message, status_code=408)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class HTTPConnectionError(HTTPError):
|
|
56
|
+
"""Exception raised when HTTP connection fails."""
|
|
57
|
+
|
|
58
|
+
def __init__(self, message: str = "Connection failed") -> None:
|
|
59
|
+
"""Initialize connection error.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
message: Error message
|
|
63
|
+
"""
|
|
64
|
+
super().__init__(message, status_code=503)
|
|
65
|
+
|
|
66
|
+
# Note: legacy HTTPXClient and BaseProxyClient removed in favor of using
|
|
67
|
+
# HTTPClientFactory + httpx.AsyncClient directly.
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class HTTPClientFactory:
|
|
71
|
+
"""Factory for creating optimized HTTP clients.
|
|
72
|
+
|
|
73
|
+
Provides centralized configuration for HTTP clients with:
|
|
74
|
+
- Consistent timeout/retry configuration
|
|
75
|
+
- Unified connection limits
|
|
76
|
+
- HTTP/2 multiplexing for non-streaming endpoints
|
|
77
|
+
- Centralized observability hooks (via HookableHTTPClient)
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
@staticmethod
|
|
81
|
+
def create_client(
|
|
82
|
+
*,
|
|
83
|
+
settings: Settings | None = None,
|
|
84
|
+
timeout_connect: float = 5.0,
|
|
85
|
+
timeout_read: float = 240.0, # Long timeout for streaming
|
|
86
|
+
max_keepalive_connections: int = 100, # For non-streaming endpoints
|
|
87
|
+
max_connections: int = 1000, # High limit for concurrent streams
|
|
88
|
+
http2: bool = True, # Enable multiplexing (requires httpx[http2])
|
|
89
|
+
verify: bool | str = True,
|
|
90
|
+
hook_manager: Any | None = None,
|
|
91
|
+
**kwargs: Any,
|
|
92
|
+
) -> httpx.AsyncClient:
|
|
93
|
+
"""Create an optimized HTTP client with recommended configuration.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
settings: Optional settings object for additional configuration
|
|
97
|
+
timeout_connect: Connection timeout in seconds
|
|
98
|
+
timeout_read: Read timeout in seconds (long for streaming)
|
|
99
|
+
max_keepalive_connections: Max keep-alive connections for reuse
|
|
100
|
+
max_connections: Max total concurrent connections
|
|
101
|
+
http2: Enable HTTP/2 multiplexing
|
|
102
|
+
verify: SSL verification (True/False or path to CA bundle)
|
|
103
|
+
hook_manager: Optional HookManager for request/response interception
|
|
104
|
+
**kwargs: Additional httpx.AsyncClient arguments
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Configured httpx.AsyncClient instance
|
|
108
|
+
"""
|
|
109
|
+
# Get proxy configuration from environment
|
|
110
|
+
proxy = get_proxy_url()
|
|
111
|
+
|
|
112
|
+
# Get SSL context configuration
|
|
113
|
+
if isinstance(verify, bool) and verify:
|
|
114
|
+
verify = get_ssl_context()
|
|
115
|
+
|
|
116
|
+
# Create timeout configuration
|
|
117
|
+
timeout = httpx.Timeout(
|
|
118
|
+
connect=timeout_connect,
|
|
119
|
+
read=timeout_read,
|
|
120
|
+
write=30.0, # Write timeout
|
|
121
|
+
pool=30.0, # Pool timeout
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Create connection limits
|
|
125
|
+
limits = httpx.Limits(
|
|
126
|
+
max_keepalive_connections=max_keepalive_connections,
|
|
127
|
+
max_connections=max_connections,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Create transport
|
|
131
|
+
transport = httpx.AsyncHTTPTransport(
|
|
132
|
+
limits=limits,
|
|
133
|
+
http2=http2,
|
|
134
|
+
verify=verify,
|
|
135
|
+
proxy=proxy,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Note: Transport wrapping for logging is now handled by the raw_http_logger plugin
|
|
139
|
+
|
|
140
|
+
# Handle compression settings
|
|
141
|
+
default_headers = {}
|
|
142
|
+
if settings and hasattr(settings, "http"):
|
|
143
|
+
http_settings = settings.http
|
|
144
|
+
if not http_settings.compression_enabled:
|
|
145
|
+
# Disable compression by setting identity encoding
|
|
146
|
+
# "identity" means no compression
|
|
147
|
+
default_headers["accept-encoding"] = "identity"
|
|
148
|
+
elif http_settings.accept_encoding:
|
|
149
|
+
# Use custom Accept-Encoding value
|
|
150
|
+
default_headers["accept-encoding"] = http_settings.accept_encoding
|
|
151
|
+
# else: let httpx use its default compression handling
|
|
152
|
+
else:
|
|
153
|
+
logger.warning(
|
|
154
|
+
"http_settings_not_found", settings_present=settings is not None
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Merge headers with any provided in kwargs
|
|
158
|
+
if "headers" in kwargs:
|
|
159
|
+
default_headers.update(kwargs["headers"])
|
|
160
|
+
kwargs["headers"] = default_headers
|
|
161
|
+
elif default_headers:
|
|
162
|
+
kwargs["headers"] = default_headers
|
|
163
|
+
|
|
164
|
+
# Merge with any additional kwargs
|
|
165
|
+
client_config = {
|
|
166
|
+
"timeout": timeout,
|
|
167
|
+
"transport": transport,
|
|
168
|
+
**kwargs,
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
# Determine effective compression status
|
|
172
|
+
compression_status = "httpx default"
|
|
173
|
+
if "accept-encoding" in default_headers:
|
|
174
|
+
if default_headers["accept-encoding"] == "identity":
|
|
175
|
+
compression_status = "disabled"
|
|
176
|
+
else:
|
|
177
|
+
compression_status = default_headers["accept-encoding"]
|
|
178
|
+
|
|
179
|
+
logger.debug(
|
|
180
|
+
"http_client_created",
|
|
181
|
+
timeout_connect=timeout_connect,
|
|
182
|
+
timeout_read=timeout_read,
|
|
183
|
+
max_keepalive_connections=max_keepalive_connections,
|
|
184
|
+
max_connections=max_connections,
|
|
185
|
+
http2=http2,
|
|
186
|
+
has_proxy=proxy is not None,
|
|
187
|
+
has_hooks=hook_manager is not None,
|
|
188
|
+
compression_enabled=settings.http.compression_enabled
|
|
189
|
+
if settings and hasattr(settings, "http")
|
|
190
|
+
else True,
|
|
191
|
+
accept_encoding=compression_status,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
# Create client with or without hook support
|
|
195
|
+
if hook_manager:
|
|
196
|
+
return HookableHTTPClient(hook_manager=hook_manager, **client_config)
|
|
197
|
+
else:
|
|
198
|
+
return httpx.AsyncClient(**client_config)
|
|
199
|
+
|
|
200
|
+
@staticmethod
|
|
201
|
+
def create_shared_client(settings: Settings | None = None) -> httpx.AsyncClient:
|
|
202
|
+
"""Create an optimized HTTP client.
|
|
203
|
+
|
|
204
|
+
Prefer managing lifecycle via ServiceContainer + HTTPPoolManager.
|
|
205
|
+
Kept for compatibility with existing factory call sites.
|
|
206
|
+
"""
|
|
207
|
+
return HTTPClientFactory.create_client(settings=settings)
|
|
208
|
+
|
|
209
|
+
@staticmethod
|
|
210
|
+
def create_short_lived_client(
|
|
211
|
+
timeout: float = 15.0,
|
|
212
|
+
**kwargs: Any,
|
|
213
|
+
) -> httpx.AsyncClient:
|
|
214
|
+
"""Create a client for short-lived operations like version checks.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
timeout: Short timeout for quick operations
|
|
218
|
+
**kwargs: Additional client configuration
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
Configured httpx.AsyncClient instance for short operations
|
|
222
|
+
"""
|
|
223
|
+
return HTTPClientFactory.create_client(
|
|
224
|
+
timeout_connect=5.0,
|
|
225
|
+
timeout_read=timeout,
|
|
226
|
+
max_keepalive_connections=10,
|
|
227
|
+
max_connections=50,
|
|
228
|
+
**kwargs,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
@staticmethod
|
|
232
|
+
@asynccontextmanager
|
|
233
|
+
async def managed_client(
|
|
234
|
+
settings: Settings | None = None, **kwargs: Any
|
|
235
|
+
) -> AsyncGenerator[httpx.AsyncClient, None]:
|
|
236
|
+
"""Create a managed HTTP client with automatic cleanup.
|
|
237
|
+
|
|
238
|
+
This context manager ensures proper cleanup of HTTP clients
|
|
239
|
+
in error cases and provides a clean resource management pattern.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
settings: Optional settings for configuration
|
|
243
|
+
**kwargs: Additional client configuration
|
|
244
|
+
|
|
245
|
+
Yields:
|
|
246
|
+
Configured httpx.AsyncClient instance
|
|
247
|
+
|
|
248
|
+
Example:
|
|
249
|
+
async with HTTPClientFactory.managed_client() as client:
|
|
250
|
+
response = await client.get("https://api.example.com")
|
|
251
|
+
"""
|
|
252
|
+
client = HTTPClientFactory.create_client(settings=settings, **kwargs)
|
|
253
|
+
try:
|
|
254
|
+
logger.debug("managed_http_client_created")
|
|
255
|
+
yield client
|
|
256
|
+
finally:
|
|
257
|
+
try:
|
|
258
|
+
await client.aclose()
|
|
259
|
+
logger.debug("managed_http_client_closed")
|
|
260
|
+
except Exception as e:
|
|
261
|
+
logger.warning(
|
|
262
|
+
"managed_http_client_close_failed",
|
|
263
|
+
error=str(e),
|
|
264
|
+
exc_info=e,
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def get_proxy_url() -> str | None:
|
|
269
|
+
"""Get proxy URL from environment variables.
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
str or None: Proxy URL if any proxy is set
|
|
273
|
+
"""
|
|
274
|
+
# Check for standard proxy environment variables
|
|
275
|
+
# For HTTPS requests, prioritize HTTPS_PROXY
|
|
276
|
+
https_proxy = os.environ.get("HTTPS_PROXY") or os.environ.get("https_proxy")
|
|
277
|
+
all_proxy = os.environ.get("ALL_PROXY")
|
|
278
|
+
http_proxy = os.environ.get("HTTP_PROXY") or os.environ.get("http_proxy")
|
|
279
|
+
|
|
280
|
+
proxy_url = https_proxy or all_proxy or http_proxy
|
|
281
|
+
|
|
282
|
+
if proxy_url:
|
|
283
|
+
logger.debug(
|
|
284
|
+
"proxy_configured",
|
|
285
|
+
proxy_url=proxy_url,
|
|
286
|
+
operation="get_proxy_url",
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
return proxy_url
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def get_ssl_context() -> str | bool:
|
|
293
|
+
"""Get SSL context configuration from environment variables.
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
SSL verification configuration:
|
|
297
|
+
- Path to CA bundle file
|
|
298
|
+
- True for default verification
|
|
299
|
+
- False to disable verification (insecure)
|
|
300
|
+
"""
|
|
301
|
+
# Check for custom CA bundle
|
|
302
|
+
ca_bundle = os.environ.get("REQUESTS_CA_BUNDLE") or os.environ.get("SSL_CERT_FILE")
|
|
303
|
+
|
|
304
|
+
# Check if SSL verification should be disabled (NOT RECOMMENDED)
|
|
305
|
+
ssl_verify = os.environ.get("SSL_VERIFY", "true").lower()
|
|
306
|
+
|
|
307
|
+
if ca_bundle and Path(ca_bundle).exists():
|
|
308
|
+
logger.debug(
|
|
309
|
+
"ssl_ca_bundle_configured",
|
|
310
|
+
ca_bundle_path=ca_bundle,
|
|
311
|
+
operation="get_ssl_context",
|
|
312
|
+
)
|
|
313
|
+
return ca_bundle
|
|
314
|
+
elif ssl_verify in ("false", "0", "no"):
|
|
315
|
+
logger.warning(
|
|
316
|
+
"ssl_verification_disabled",
|
|
317
|
+
ssl_verify_value=ssl_verify,
|
|
318
|
+
operation="get_ssl_context",
|
|
319
|
+
security_warning=True,
|
|
320
|
+
)
|
|
321
|
+
return False
|
|
322
|
+
else:
|
|
323
|
+
return True
|