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
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
"""Factory for creating AuthManager instances from credential sources."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
|
+
|
|
9
|
+
from ccproxy.auth.exceptions import AuthenticationError
|
|
10
|
+
from ccproxy.auth.manager import AuthManager
|
|
11
|
+
from ccproxy.core.logging import TraceBoundLogger, get_plugin_logger
|
|
12
|
+
|
|
13
|
+
from .config import CredentialManager
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from ccproxy.services.auth_registry import AuthManagerRegistry
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
logger = get_plugin_logger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AuthManagerFactory:
|
|
24
|
+
"""Creates AuthManager instances from credential source configurations."""
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
auth_registry: AuthManagerRegistry | None = None,
|
|
29
|
+
*,
|
|
30
|
+
logger: TraceBoundLogger | None = None,
|
|
31
|
+
) -> None:
|
|
32
|
+
"""Initialize auth manager factory.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
auth_registry: Auth manager registry for resolving manager keys
|
|
36
|
+
logger: Optional logger for this factory
|
|
37
|
+
"""
|
|
38
|
+
self._auth_registry = auth_registry
|
|
39
|
+
self._logger = logger or get_plugin_logger(__name__)
|
|
40
|
+
|
|
41
|
+
async def create_from_source(
|
|
42
|
+
self,
|
|
43
|
+
source: CredentialManager,
|
|
44
|
+
provider: str,
|
|
45
|
+
) -> AuthManager:
|
|
46
|
+
"""Create AuthManager instance from credential source configuration.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
source: Manager credential configuration
|
|
50
|
+
provider: Provider name for this credential (unused, kept for compatibility)
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
AuthManager instance
|
|
54
|
+
|
|
55
|
+
Raises:
|
|
56
|
+
AuthenticationError: If manager creation fails
|
|
57
|
+
"""
|
|
58
|
+
return await self._create_provider_manager(source)
|
|
59
|
+
|
|
60
|
+
async def _create_provider_manager(
|
|
61
|
+
self,
|
|
62
|
+
source: CredentialManager,
|
|
63
|
+
) -> AuthManager:
|
|
64
|
+
"""Create provider-specific auth manager.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
source: Manager credential configuration
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
AuthManager instance
|
|
71
|
+
|
|
72
|
+
Raises:
|
|
73
|
+
AuthenticationError: If manager creation fails
|
|
74
|
+
"""
|
|
75
|
+
# Check if custom file path is specified (already expanded by validator)
|
|
76
|
+
custom_file = str(source.file.resolve()) if source.file else None
|
|
77
|
+
|
|
78
|
+
# Direct class specification approach
|
|
79
|
+
if source.manager_class:
|
|
80
|
+
return await self._create_manager_from_class_name(
|
|
81
|
+
source.manager_class,
|
|
82
|
+
source.storage_class,
|
|
83
|
+
custom_file,
|
|
84
|
+
source.resolved_label,
|
|
85
|
+
source.config,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Registry lookup approach
|
|
89
|
+
if source.manager_key:
|
|
90
|
+
return await self._create_manager_from_registry(
|
|
91
|
+
source.manager_key,
|
|
92
|
+
custom_file,
|
|
93
|
+
source.resolved_label,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
raise AuthenticationError(
|
|
97
|
+
"Neither manager_class nor manager_key specified in credential source"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
async def _create_manager_from_registry(
|
|
101
|
+
self,
|
|
102
|
+
manager_key: str,
|
|
103
|
+
custom_file: str | None,
|
|
104
|
+
label: str,
|
|
105
|
+
) -> AuthManager:
|
|
106
|
+
"""Create manager using registry lookup.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
manager_key: Registry key
|
|
110
|
+
custom_file: Optional custom file path
|
|
111
|
+
label: Label for logging
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
AuthManager instance
|
|
115
|
+
|
|
116
|
+
Raises:
|
|
117
|
+
AuthenticationError: If manager not found or creation fails
|
|
118
|
+
"""
|
|
119
|
+
if self._auth_registry is None:
|
|
120
|
+
raise AuthenticationError(
|
|
121
|
+
f"Auth registry not available for manager key: {manager_key}"
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
if custom_file:
|
|
125
|
+
# Create manager with custom storage
|
|
126
|
+
return await self._create_manager_with_custom_file(
|
|
127
|
+
manager_key,
|
|
128
|
+
custom_file,
|
|
129
|
+
label,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Standard registry lookup
|
|
133
|
+
self._logger.debug(
|
|
134
|
+
"creating_provider_manager_from_registry",
|
|
135
|
+
manager_key=manager_key,
|
|
136
|
+
label=label,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
manager = await self._auth_registry.get(manager_key)
|
|
140
|
+
if manager is None:
|
|
141
|
+
raise AuthenticationError(
|
|
142
|
+
f"Auth manager not found in registry: {manager_key}"
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
self._logger.info(
|
|
146
|
+
"provider_manager_created_from_registry",
|
|
147
|
+
manager_key=manager_key,
|
|
148
|
+
label=label,
|
|
149
|
+
manager_type=type(manager).__name__,
|
|
150
|
+
)
|
|
151
|
+
return manager # type: ignore[no-any-return]
|
|
152
|
+
|
|
153
|
+
async def _create_manager_from_class_name(
|
|
154
|
+
self,
|
|
155
|
+
manager_class_name: str,
|
|
156
|
+
storage_class_name: str | None,
|
|
157
|
+
custom_file: str | None,
|
|
158
|
+
label: str,
|
|
159
|
+
config: dict[str, Any] | None = None,
|
|
160
|
+
) -> AuthManager:
|
|
161
|
+
"""Create manager by dynamically importing class.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
manager_class_name: Fully qualified manager class name
|
|
165
|
+
storage_class_name: Fully qualified storage class name (required if custom_file specified)
|
|
166
|
+
custom_file: Optional custom file path
|
|
167
|
+
label: Label for logging
|
|
168
|
+
config: Additional configuration options for storage and manager
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
AuthManager instance
|
|
172
|
+
|
|
173
|
+
Raises:
|
|
174
|
+
AuthenticationError: If class cannot be imported or instantiated
|
|
175
|
+
"""
|
|
176
|
+
config = config or {}
|
|
177
|
+
|
|
178
|
+
self._logger.debug(
|
|
179
|
+
"creating_manager_from_class_name",
|
|
180
|
+
manager_class=manager_class_name,
|
|
181
|
+
storage_class=storage_class_name,
|
|
182
|
+
custom_file=custom_file,
|
|
183
|
+
label=label,
|
|
184
|
+
config_keys=list(config.keys()),
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# Import manager class
|
|
188
|
+
try:
|
|
189
|
+
manager_class = self._import_class(manager_class_name)
|
|
190
|
+
except Exception as exc:
|
|
191
|
+
raise AuthenticationError(
|
|
192
|
+
f"Failed to import manager class '{manager_class_name}': {exc}"
|
|
193
|
+
) from exc
|
|
194
|
+
|
|
195
|
+
# Create storage if custom file specified
|
|
196
|
+
storage = None
|
|
197
|
+
if custom_file:
|
|
198
|
+
if not storage_class_name:
|
|
199
|
+
raise AuthenticationError(
|
|
200
|
+
"storage_class is required when using custom file with manager_class"
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
try:
|
|
204
|
+
storage_class = self._import_class(storage_class_name)
|
|
205
|
+
# custom_file is already expanded and resolved by config validator
|
|
206
|
+
custom_path = Path(custom_file)
|
|
207
|
+
|
|
208
|
+
# Extract storage-specific config options
|
|
209
|
+
storage_kwargs: dict[str, Any] = {"storage_path": custom_path}
|
|
210
|
+
if "enable_backups" in config:
|
|
211
|
+
storage_kwargs["enable_backups"] = bool(config["enable_backups"])
|
|
212
|
+
|
|
213
|
+
storage = storage_class(**storage_kwargs)
|
|
214
|
+
except Exception as exc:
|
|
215
|
+
raise AuthenticationError(
|
|
216
|
+
f"Failed to create storage from '{storage_class_name}': {exc}"
|
|
217
|
+
) from exc
|
|
218
|
+
|
|
219
|
+
# Create manager instance with config options
|
|
220
|
+
try:
|
|
221
|
+
# Check if we have advanced config options that need direct __init__ call
|
|
222
|
+
has_advanced_config = (
|
|
223
|
+
"credentials_ttl" in config or "refresh_grace_seconds" in config
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
if has_advanced_config:
|
|
227
|
+
# Use direct __init__ to pass ttl/grace parameters
|
|
228
|
+
# These are supported by BaseTokenManager but not exposed in create() methods
|
|
229
|
+
init_kwargs: dict[str, Any] = {"storage": storage}
|
|
230
|
+
if "credentials_ttl" in config:
|
|
231
|
+
init_kwargs["credentials_ttl"] = float(config["credentials_ttl"])
|
|
232
|
+
if "refresh_grace_seconds" in config:
|
|
233
|
+
init_kwargs["refresh_grace_seconds"] = float(
|
|
234
|
+
config["refresh_grace_seconds"]
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
manager = manager_class(**init_kwargs)
|
|
238
|
+
elif hasattr(manager_class, "create"):
|
|
239
|
+
# Use async create() method for standard instantiation
|
|
240
|
+
manager = (
|
|
241
|
+
await manager_class.create(storage=storage)
|
|
242
|
+
if storage
|
|
243
|
+
else await manager_class.create()
|
|
244
|
+
)
|
|
245
|
+
else:
|
|
246
|
+
raise AuthenticationError(
|
|
247
|
+
f"Manager class {manager_class.__name__} does not have 'create' method"
|
|
248
|
+
)
|
|
249
|
+
except FileNotFoundError as exc:
|
|
250
|
+
# Clean warning for missing credential files
|
|
251
|
+
file_path = custom_file or "default location"
|
|
252
|
+
self._logger.warning(
|
|
253
|
+
"credential_file_not_found",
|
|
254
|
+
label=label,
|
|
255
|
+
file_path=file_path,
|
|
256
|
+
manager_class=manager_class_name,
|
|
257
|
+
)
|
|
258
|
+
raise AuthenticationError(f"Credential file not found: {file_path}")
|
|
259
|
+
except Exception as exc:
|
|
260
|
+
# Log the full error for debugging but raise a clean message
|
|
261
|
+
self._logger.error(
|
|
262
|
+
"manager_creation_failed",
|
|
263
|
+
label=label,
|
|
264
|
+
manager_class=manager_class_name,
|
|
265
|
+
error=str(exc),
|
|
266
|
+
error_type=type(exc).__name__,
|
|
267
|
+
)
|
|
268
|
+
raise AuthenticationError(
|
|
269
|
+
f"Failed to create manager from class '{manager_class_name}': {exc}"
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
self._logger.info(
|
|
273
|
+
"provider_manager_created_from_class",
|
|
274
|
+
manager_class=manager_class_name,
|
|
275
|
+
storage_class=storage_class_name,
|
|
276
|
+
custom_file=custom_file,
|
|
277
|
+
label=label,
|
|
278
|
+
manager_type=type(manager).__name__,
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
return manager # type: ignore[no-any-return]
|
|
282
|
+
|
|
283
|
+
def _import_class(self, class_path: str) -> type:
|
|
284
|
+
"""Dynamically import a class from a fully qualified path.
|
|
285
|
+
|
|
286
|
+
Args:
|
|
287
|
+
class_path: Fully qualified class path (e.g., 'module.submodule.ClassName')
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
Imported class
|
|
291
|
+
|
|
292
|
+
Raises:
|
|
293
|
+
ValueError: If class path is invalid
|
|
294
|
+
ImportError: If module cannot be imported
|
|
295
|
+
AttributeError: If class not found in module
|
|
296
|
+
"""
|
|
297
|
+
if "." not in class_path:
|
|
298
|
+
raise ValueError(
|
|
299
|
+
f"Invalid class path (must be fully qualified): {class_path}"
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
module_path, class_name = class_path.rsplit(".", 1)
|
|
303
|
+
|
|
304
|
+
try:
|
|
305
|
+
module = importlib.import_module(module_path)
|
|
306
|
+
cls = getattr(module, class_name)
|
|
307
|
+
|
|
308
|
+
if not isinstance(cls, type):
|
|
309
|
+
raise ValueError(f"'{class_path}' is not a class")
|
|
310
|
+
|
|
311
|
+
return cls
|
|
312
|
+
except ImportError as exc:
|
|
313
|
+
raise ImportError(f"Cannot import module '{module_path}': {exc}") from exc
|
|
314
|
+
except AttributeError as exc:
|
|
315
|
+
raise AttributeError(
|
|
316
|
+
f"Module '{module_path}' has no class '{class_name}'"
|
|
317
|
+
) from exc
|
|
318
|
+
|
|
319
|
+
async def _create_manager_with_custom_file(
|
|
320
|
+
self,
|
|
321
|
+
manager_key: str,
|
|
322
|
+
file_path: str,
|
|
323
|
+
label: str,
|
|
324
|
+
) -> AuthManager:
|
|
325
|
+
"""Create auth manager with custom file storage.
|
|
326
|
+
|
|
327
|
+
Args:
|
|
328
|
+
manager_key: Manager registry key
|
|
329
|
+
file_path: Custom file path for storage
|
|
330
|
+
label: Label for logging
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
AuthManager instance with custom storage
|
|
334
|
+
|
|
335
|
+
Raises:
|
|
336
|
+
AuthenticationError: If manager class not found or creation fails
|
|
337
|
+
"""
|
|
338
|
+
if self._auth_registry is None:
|
|
339
|
+
raise AuthenticationError("Auth registry not available")
|
|
340
|
+
|
|
341
|
+
# Get manager class from registry
|
|
342
|
+
manager_class = self._auth_registry.get_class(manager_key)
|
|
343
|
+
if manager_class is None:
|
|
344
|
+
raise AuthenticationError(
|
|
345
|
+
f"Manager class not found for key: {manager_key}. "
|
|
346
|
+
"Only managers registered via register_class support custom file paths."
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
self._logger.debug(
|
|
350
|
+
"creating_manager_with_custom_storage",
|
|
351
|
+
manager_key=manager_key,
|
|
352
|
+
file_path=file_path,
|
|
353
|
+
label=label,
|
|
354
|
+
manager_class=manager_class.__name__,
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
# Create custom storage based on manager type
|
|
358
|
+
# file_path is already expanded and resolved by config validator
|
|
359
|
+
custom_path = Path(file_path)
|
|
360
|
+
storage = await self._create_storage_for_manager(
|
|
361
|
+
manager_key, manager_class, custom_path
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
# Create manager with custom storage
|
|
365
|
+
if hasattr(manager_class, "create"):
|
|
366
|
+
manager = await manager_class.create(storage=storage)
|
|
367
|
+
else:
|
|
368
|
+
raise AuthenticationError(
|
|
369
|
+
f"Manager class {manager_class.__name__} does not support async creation"
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
self._logger.info(
|
|
373
|
+
"provider_manager_created_with_custom_storage",
|
|
374
|
+
manager_key=manager_key,
|
|
375
|
+
file_path=str(custom_path),
|
|
376
|
+
label=label,
|
|
377
|
+
manager_type=type(manager).__name__,
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
return manager # type: ignore[no-any-return]
|
|
381
|
+
|
|
382
|
+
async def _create_storage_for_manager(
|
|
383
|
+
self,
|
|
384
|
+
manager_key: str,
|
|
385
|
+
manager_class: type,
|
|
386
|
+
storage_path: Path,
|
|
387
|
+
) -> Any:
|
|
388
|
+
"""Create appropriate storage instance for the manager type.
|
|
389
|
+
|
|
390
|
+
Args:
|
|
391
|
+
manager_key: Manager registry key
|
|
392
|
+
manager_class: Manager class
|
|
393
|
+
storage_path: Path to storage file
|
|
394
|
+
|
|
395
|
+
Returns:
|
|
396
|
+
Storage instance
|
|
397
|
+
|
|
398
|
+
Raises:
|
|
399
|
+
AuthenticationError: If storage type cannot be determined
|
|
400
|
+
"""
|
|
401
|
+
# Map manager keys to their storage classes
|
|
402
|
+
# This could be made more dynamic by having managers expose their storage class
|
|
403
|
+
if manager_key == "codex":
|
|
404
|
+
from ccproxy.plugins.oauth_codex.storage import CodexTokenStorage
|
|
405
|
+
|
|
406
|
+
return CodexTokenStorage(storage_path=storage_path)
|
|
407
|
+
else:
|
|
408
|
+
raise AuthenticationError(
|
|
409
|
+
f"Custom file storage not yet supported for manager: {manager_key}. "
|
|
410
|
+
f"Supported managers: codex. "
|
|
411
|
+
f"Either use type='file' or add storage mapping for {manager_key}."
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
__all__ = ["AuthManagerFactory"]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Hook implementation that monitors provider responses for credential failures."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Iterable
|
|
6
|
+
|
|
7
|
+
from ccproxy.core.plugins.hooks import Hook
|
|
8
|
+
from ccproxy.core.plugins.hooks.base import HookContext
|
|
9
|
+
from ccproxy.core.plugins.hooks.events import HookEvent
|
|
10
|
+
|
|
11
|
+
from .manager import CredentialBalancerTokenManager
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CredentialBalancerHook(Hook):
|
|
15
|
+
"""Hook that routes HTTP lifecycle events to the balancer managers."""
|
|
16
|
+
|
|
17
|
+
name = "credential_balancer"
|
|
18
|
+
events = [HookEvent.HTTP_RESPONSE, HookEvent.HTTP_ERROR]
|
|
19
|
+
priority = 550
|
|
20
|
+
|
|
21
|
+
def __init__(self, managers: Iterable[CredentialBalancerTokenManager]):
|
|
22
|
+
self._managers: list[CredentialBalancerTokenManager] = list(managers)
|
|
23
|
+
|
|
24
|
+
def add_manager(self, manager: CredentialBalancerTokenManager) -> None:
|
|
25
|
+
if manager not in self._managers:
|
|
26
|
+
self._managers.append(manager)
|
|
27
|
+
|
|
28
|
+
def remove_manager(self, manager: CredentialBalancerTokenManager) -> None:
|
|
29
|
+
if manager in self._managers:
|
|
30
|
+
self._managers.remove(manager)
|
|
31
|
+
|
|
32
|
+
async def __call__(self, context: HookContext) -> None:
|
|
33
|
+
if not self._managers:
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
request_id = context.data.get("request_id")
|
|
37
|
+
is_provider = bool(
|
|
38
|
+
context.data.get("is_provider_response")
|
|
39
|
+
or context.data.get("is_provider_request")
|
|
40
|
+
)
|
|
41
|
+
if not request_id or not is_provider:
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
status_code = context.data.get("status_code")
|
|
45
|
+
for manager in list(self._managers):
|
|
46
|
+
handled = await manager.handle_response_event(request_id, status_code)
|
|
47
|
+
if handled:
|
|
48
|
+
break
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
__all__ = ["CredentialBalancerHook"]
|