ccproxy-api 0.1.7__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ccproxy/api/__init__.py +1 -15
- ccproxy/api/app.py +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.0.dist-info/METADATA +212 -0
- ccproxy_api-0.2.0.dist-info/RECORD +417 -0
- {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0.dist-info}/WHEEL +1 -1
- ccproxy_api-0.2.0.dist-info/entry_points.txt +24 -0
- ccproxy/__init__.py +0 -4
- ccproxy/adapters/__init__.py +0 -11
- ccproxy/adapters/base.py +0 -80
- ccproxy/adapters/codex/__init__.py +0 -11
- ccproxy/adapters/openai/__init__.py +0 -42
- ccproxy/adapters/openai/adapter.py +0 -953
- ccproxy/adapters/openai/models.py +0 -412
- ccproxy/adapters/openai/response_adapter.py +0 -355
- ccproxy/adapters/openai/response_models.py +0 -178
- ccproxy/api/middleware/headers.py +0 -49
- ccproxy/api/middleware/logging.py +0 -180
- ccproxy/api/middleware/request_content_logging.py +0 -297
- ccproxy/api/middleware/server_header.py +0 -58
- ccproxy/api/responses.py +0 -89
- ccproxy/api/routes/claude.py +0 -371
- ccproxy/api/routes/codex.py +0 -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.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
"""Abstract interfaces for the plugin system.
|
|
2
|
+
|
|
3
|
+
This module contains all abstract base classes and protocols to avoid
|
|
4
|
+
circular dependencies between factory and runtime modules.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Protocol, TypeVar, cast
|
|
9
|
+
|
|
10
|
+
import structlog
|
|
11
|
+
from fastapi import FastAPI
|
|
12
|
+
|
|
13
|
+
from ccproxy.auth import TokenStorage
|
|
14
|
+
from ccproxy.auth.managers import BaseTokenManager, TokenSnapshot
|
|
15
|
+
from ccproxy.auth.oauth import OAuthProviderProtocol
|
|
16
|
+
from ccproxy.models.detection import DetectedHeaders, DetectedPrompts
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from ccproxy.services.container import ServiceContainer
|
|
21
|
+
from ccproxy.services.interfaces import StreamingMetrics
|
|
22
|
+
|
|
23
|
+
from .declaration import PluginContext, PluginManifest
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Type variable for service type checking
|
|
27
|
+
T = TypeVar("T")
|
|
28
|
+
|
|
29
|
+
logger = structlog.get_logger(__name__)
|
|
30
|
+
|
|
31
|
+
# --- Adapter protocol helpers -------------------------------------------------
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class DetectionServiceProtocol(Protocol):
|
|
35
|
+
"""Common capabilities shared by detection services."""
|
|
36
|
+
|
|
37
|
+
def get_detected_headers(self) -> DetectedHeaders: ...
|
|
38
|
+
|
|
39
|
+
def get_detected_prompts(self) -> DetectedPrompts: ...
|
|
40
|
+
|
|
41
|
+
def get_cached_data(self) -> object: ...
|
|
42
|
+
|
|
43
|
+
def get_system_prompt(self, mode: str | None = None) -> dict[str, object]: ...
|
|
44
|
+
|
|
45
|
+
def get_ignored_headers(self) -> list[str]: ...
|
|
46
|
+
|
|
47
|
+
def get_redacted_headers(self) -> list[str]: ...
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class TokenManagerProtocol(Protocol):
|
|
51
|
+
"""Minimal async token manager contract used by adapters."""
|
|
52
|
+
|
|
53
|
+
async def get_access_token(self) -> str | None: ...
|
|
54
|
+
|
|
55
|
+
async def get_access_token_with_refresh(self) -> str | None: ...
|
|
56
|
+
|
|
57
|
+
async def load_credentials(self) -> object: ...
|
|
58
|
+
|
|
59
|
+
async def get_token_snapshot(self) -> TokenSnapshot | None: ...
|
|
60
|
+
|
|
61
|
+
def should_refresh(
|
|
62
|
+
self, credentials: object, grace_seconds: float | None = None
|
|
63
|
+
) -> bool: ...
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class ProfiledTokenManagerProtocol(TokenManagerProtocol, Protocol):
|
|
67
|
+
"""Token manager that can return a lightweight profile snapshot."""
|
|
68
|
+
|
|
69
|
+
async def get_profile_quick(self) -> object | None: ...
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class PluginFactory(ABC):
|
|
73
|
+
"""Abstract factory for creating plugin runtime instances.
|
|
74
|
+
|
|
75
|
+
Each plugin must provide a factory that knows how to create
|
|
76
|
+
its runtime instance from its manifest.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
@abstractmethod
|
|
80
|
+
def get_manifest(self) -> PluginManifest:
|
|
81
|
+
"""Get the plugin manifest with static declarations.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Plugin manifest
|
|
85
|
+
"""
|
|
86
|
+
...
|
|
87
|
+
|
|
88
|
+
@abstractmethod
|
|
89
|
+
def create_runtime(self) -> Any:
|
|
90
|
+
"""Create a runtime instance for this plugin.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Plugin runtime instance
|
|
94
|
+
"""
|
|
95
|
+
...
|
|
96
|
+
|
|
97
|
+
@abstractmethod
|
|
98
|
+
def create_context(self, core_services: "ServiceContainer") -> PluginContext:
|
|
99
|
+
"""Create the context for plugin initialization.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
core_services: Core services container
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Plugin context with required services
|
|
106
|
+
"""
|
|
107
|
+
...
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class BasePluginFactory(PluginFactory):
|
|
111
|
+
"""Base implementation of plugin factory.
|
|
112
|
+
|
|
113
|
+
This class provides common functionality for creating plugin
|
|
114
|
+
runtime instances from manifests.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
def __init__(self, manifest: PluginManifest, runtime_class: type[Any]):
|
|
118
|
+
"""Initialize factory with manifest and runtime class.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
manifest: Plugin manifest
|
|
122
|
+
runtime_class: Runtime class to instantiate
|
|
123
|
+
"""
|
|
124
|
+
self.manifest = manifest
|
|
125
|
+
self.runtime_class = runtime_class
|
|
126
|
+
|
|
127
|
+
def get_manifest(self) -> PluginManifest:
|
|
128
|
+
"""Get the plugin manifest."""
|
|
129
|
+
return self.manifest
|
|
130
|
+
|
|
131
|
+
def create_runtime(self) -> Any:
|
|
132
|
+
"""Create a runtime instance."""
|
|
133
|
+
return self.runtime_class(self.manifest)
|
|
134
|
+
|
|
135
|
+
def create_context(self, service_container: "ServiceContainer") -> PluginContext:
|
|
136
|
+
"""Create base context for plugin initialization.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
service_container: Service container with all available services
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Plugin context with base services
|
|
143
|
+
"""
|
|
144
|
+
context = PluginContext()
|
|
145
|
+
|
|
146
|
+
# Set core services
|
|
147
|
+
context.settings = service_container.settings
|
|
148
|
+
context.http_pool_manager = service_container.get_pool_manager()
|
|
149
|
+
context.logger = structlog.get_logger().bind(plugin=self.manifest.name)
|
|
150
|
+
|
|
151
|
+
# Add explicit dependency injection services
|
|
152
|
+
context.request_tracer = service_container.get_request_tracer()
|
|
153
|
+
context.streaming_handler = cast(
|
|
154
|
+
"StreamingMetrics", service_container.get_streaming_handler()
|
|
155
|
+
)
|
|
156
|
+
context.metrics = None # Will be set by plugins if needed
|
|
157
|
+
|
|
158
|
+
# Add CLI detection service
|
|
159
|
+
context.cli_detection_service = service_container.get_cli_detection_service()
|
|
160
|
+
|
|
161
|
+
# Add scheduler - not available in ServiceContainer, get from app state
|
|
162
|
+
context.scheduler = None # Will be set from app.state if needed
|
|
163
|
+
|
|
164
|
+
# Add plugin registry - not directly in ServiceContainer, get from app state
|
|
165
|
+
context.plugin_registry = None # Will be set from app.state
|
|
166
|
+
|
|
167
|
+
# Add OAuth registry for auth providers
|
|
168
|
+
context.oauth_registry = service_container.get_oauth_registry()
|
|
169
|
+
|
|
170
|
+
# Add hook registry and manager
|
|
171
|
+
context.hook_registry = service_container.get_hook_registry()
|
|
172
|
+
|
|
173
|
+
# Provide runtime helpers when available in the container
|
|
174
|
+
try:
|
|
175
|
+
from ccproxy.core.plugins.hooks.manager import HookManager
|
|
176
|
+
|
|
177
|
+
context.hook_manager = service_container.get_service(HookManager)
|
|
178
|
+
except (ValueError, ImportError):
|
|
179
|
+
context.hook_manager = None
|
|
180
|
+
|
|
181
|
+
try:
|
|
182
|
+
context.app = service_container.get_service(FastAPI)
|
|
183
|
+
except ValueError:
|
|
184
|
+
context.app = None
|
|
185
|
+
|
|
186
|
+
# Add service container directly
|
|
187
|
+
context.service_container = service_container
|
|
188
|
+
|
|
189
|
+
# Add plugin-specific config if available
|
|
190
|
+
# ServiceContainer doesn't have get_plugin_config, so we'll get it from settings directly
|
|
191
|
+
if self.manifest.config_class:
|
|
192
|
+
plugin_config = service_container.settings.plugins.get(self.manifest.name)
|
|
193
|
+
|
|
194
|
+
try:
|
|
195
|
+
if plugin_config is None:
|
|
196
|
+
# No explicit config provided; instantiate defaults
|
|
197
|
+
context.config = self.manifest.config_class()
|
|
198
|
+
else:
|
|
199
|
+
# Validate (even if empty dict) to honor model defaults
|
|
200
|
+
validated_config = self.manifest.config_class.model_validate(
|
|
201
|
+
plugin_config
|
|
202
|
+
)
|
|
203
|
+
context.config = validated_config
|
|
204
|
+
except Exception as exc: # pragma: no cover - defensive safety
|
|
205
|
+
logger.warning(
|
|
206
|
+
"plugin_config_initialization_failed",
|
|
207
|
+
plugin=self.manifest.name,
|
|
208
|
+
error=str(exc),
|
|
209
|
+
)
|
|
210
|
+
raise
|
|
211
|
+
|
|
212
|
+
# Add format registry
|
|
213
|
+
context.format_registry = service_container.get_format_registry()
|
|
214
|
+
|
|
215
|
+
return context
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class ProviderPluginFactory(BasePluginFactory):
|
|
219
|
+
"""Factory for provider plugins.
|
|
220
|
+
|
|
221
|
+
Provider plugins require additional components like adapters
|
|
222
|
+
and detection services that must be created during initialization.
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
def __init__(self, manifest: PluginManifest):
|
|
226
|
+
"""Initialize provider plugin factory.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
manifest: Plugin manifest
|
|
230
|
+
"""
|
|
231
|
+
# Local import to avoid circular dependency at module load time
|
|
232
|
+
from .runtime import ProviderPluginRuntime
|
|
233
|
+
|
|
234
|
+
super().__init__(manifest, ProviderPluginRuntime)
|
|
235
|
+
|
|
236
|
+
# Validate this is a provider plugin
|
|
237
|
+
if not manifest.is_provider:
|
|
238
|
+
raise ValueError(
|
|
239
|
+
f"Plugin {manifest.name} is not marked as provider but using ProviderPluginFactory"
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
def create_context(self, service_container: "ServiceContainer") -> PluginContext:
|
|
243
|
+
"""Create context with provider-specific components.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
core_services: Core services container
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
Plugin context with provider components
|
|
250
|
+
"""
|
|
251
|
+
# Start with base context
|
|
252
|
+
context = super().create_context(service_container)
|
|
253
|
+
|
|
254
|
+
# Provider plugins need to create their own adapter and detection service
|
|
255
|
+
# This is typically done in the specific plugin factory implementation
|
|
256
|
+
# Here we just ensure the structure is correct
|
|
257
|
+
|
|
258
|
+
return context
|
|
259
|
+
|
|
260
|
+
@abstractmethod
|
|
261
|
+
async def create_adapter(self, context: PluginContext) -> Any:
|
|
262
|
+
"""Create the adapter for this provider.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
context: Plugin context
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
Provider adapter instance
|
|
269
|
+
"""
|
|
270
|
+
...
|
|
271
|
+
|
|
272
|
+
@abstractmethod
|
|
273
|
+
def create_detection_service(
|
|
274
|
+
self, context: PluginContext
|
|
275
|
+
) -> DetectionServiceProtocol | None:
|
|
276
|
+
"""Create the detection service for this provider.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
context: Plugin context
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
Detection service instance or None
|
|
283
|
+
"""
|
|
284
|
+
...
|
|
285
|
+
|
|
286
|
+
@abstractmethod
|
|
287
|
+
async def create_credentials_manager(
|
|
288
|
+
self, context: PluginContext
|
|
289
|
+
) -> BaseTokenManager[Any] | None:
|
|
290
|
+
"""Create the credentials manager for this provider.
|
|
291
|
+
|
|
292
|
+
Args:
|
|
293
|
+
context: Plugin context
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
Credentials manager instance or None
|
|
297
|
+
"""
|
|
298
|
+
...
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
class SystemPluginFactory(BasePluginFactory):
|
|
302
|
+
"""Factory for system plugins."""
|
|
303
|
+
|
|
304
|
+
def __init__(self, manifest: PluginManifest):
|
|
305
|
+
"""Initialize system plugin factory.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
manifest: Plugin manifest
|
|
309
|
+
"""
|
|
310
|
+
# Local import to avoid circular dependency at module load time
|
|
311
|
+
from .runtime import SystemPluginRuntime
|
|
312
|
+
|
|
313
|
+
super().__init__(manifest, SystemPluginRuntime)
|
|
314
|
+
|
|
315
|
+
# Validate this is a system plugin
|
|
316
|
+
if manifest.is_provider:
|
|
317
|
+
raise ValueError(
|
|
318
|
+
f"Plugin {manifest.name} is marked as provider but using SystemPluginFactory"
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
class AuthProviderPluginFactory(BasePluginFactory):
|
|
323
|
+
"""Factory for authentication provider plugins.
|
|
324
|
+
|
|
325
|
+
Auth provider plugins provide OAuth authentication flows and token management
|
|
326
|
+
without directly proxying requests to API providers.
|
|
327
|
+
"""
|
|
328
|
+
|
|
329
|
+
auth_manager_class: type[Any] | None = None
|
|
330
|
+
|
|
331
|
+
def __init__(self, manifest: PluginManifest):
|
|
332
|
+
"""Initialize auth provider plugin factory.
|
|
333
|
+
|
|
334
|
+
Args:
|
|
335
|
+
manifest: Plugin manifest
|
|
336
|
+
"""
|
|
337
|
+
# Local import to avoid circular dependency at module load time
|
|
338
|
+
from .runtime import AuthProviderPluginRuntime
|
|
339
|
+
|
|
340
|
+
super().__init__(manifest, AuthProviderPluginRuntime)
|
|
341
|
+
|
|
342
|
+
# Validate this is marked as a provider plugin (auth providers are a type of provider)
|
|
343
|
+
if not manifest.is_provider:
|
|
344
|
+
raise ValueError(
|
|
345
|
+
f"Plugin {manifest.name} must be marked as provider for AuthProviderPluginFactory"
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
def create_context(self, service_container: "ServiceContainer") -> PluginContext:
|
|
349
|
+
"""Create context with auth provider-specific components.
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
core_services: Core services container
|
|
353
|
+
|
|
354
|
+
Returns:
|
|
355
|
+
Plugin context with auth provider components
|
|
356
|
+
"""
|
|
357
|
+
# Start with base context
|
|
358
|
+
context = super().create_context(service_container)
|
|
359
|
+
|
|
360
|
+
# Auth provider plugins need to create their auth components
|
|
361
|
+
# This is typically done in the specific plugin factory implementation
|
|
362
|
+
|
|
363
|
+
return context
|
|
364
|
+
|
|
365
|
+
def get_auth_manager_registry_name(self) -> str:
|
|
366
|
+
"""Return registry key used for this auth manager."""
|
|
367
|
+
|
|
368
|
+
name = getattr(self, "auth_manager_name", None)
|
|
369
|
+
return name or self.manifest.name
|
|
370
|
+
|
|
371
|
+
@abstractmethod
|
|
372
|
+
def create_auth_provider(
|
|
373
|
+
self, context: PluginContext | None = None
|
|
374
|
+
) -> OAuthProviderProtocol:
|
|
375
|
+
"""Create the OAuth provider for this auth plugin.
|
|
376
|
+
|
|
377
|
+
Args:
|
|
378
|
+
context: Optional plugin context for initialization
|
|
379
|
+
|
|
380
|
+
Returns:
|
|
381
|
+
OAuth provider instance implementing OAuthProviderProtocol
|
|
382
|
+
"""
|
|
383
|
+
...
|
|
384
|
+
|
|
385
|
+
def create_token_manager(self) -> BaseTokenManager[Any] | None:
|
|
386
|
+
"""Create the token manager for this auth plugin.
|
|
387
|
+
|
|
388
|
+
Returns:
|
|
389
|
+
Token manager instance or None if not needed
|
|
390
|
+
"""
|
|
391
|
+
return None
|
|
392
|
+
|
|
393
|
+
def create_storage(self) -> TokenStorage[Any] | None:
|
|
394
|
+
"""Create the storage implementation for this auth plugin.
|
|
395
|
+
|
|
396
|
+
Returns:
|
|
397
|
+
Storage instance or None if using default
|
|
398
|
+
"""
|
|
399
|
+
return None
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def factory_type_name(factory: PluginFactory) -> str:
|
|
403
|
+
"""Return a stable type name for a plugin factory.
|
|
404
|
+
|
|
405
|
+
Returns one of: "auth_provider", "provider", "system", or "plugin" (fallback).
|
|
406
|
+
"""
|
|
407
|
+
try:
|
|
408
|
+
if isinstance(factory, AuthProviderPluginFactory):
|
|
409
|
+
return "auth_provider"
|
|
410
|
+
if isinstance(factory, ProviderPluginFactory):
|
|
411
|
+
return "provider"
|
|
412
|
+
if isinstance(factory, SystemPluginFactory):
|
|
413
|
+
return "system"
|
|
414
|
+
except Exception:
|
|
415
|
+
pass
|
|
416
|
+
return "plugin"
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""Centralized plugin loader.
|
|
2
|
+
|
|
3
|
+
Provides a single entry to discover factories, build a `PluginRegistry`, and
|
|
4
|
+
prepare `MiddlewareManager` based on settings. This isolates loader usage to
|
|
5
|
+
one place and reinforces import boundaries (core should not import concrete
|
|
6
|
+
plugin modules directly).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
import structlog
|
|
14
|
+
|
|
15
|
+
from ccproxy.config.settings import Settings
|
|
16
|
+
from ccproxy.core.plugins.discovery import discover_and_load_plugins
|
|
17
|
+
from ccproxy.core.plugins.factories import PluginRegistry
|
|
18
|
+
from ccproxy.core.plugins.interfaces import (
|
|
19
|
+
AuthProviderPluginFactory,
|
|
20
|
+
PluginFactory,
|
|
21
|
+
)
|
|
22
|
+
from ccproxy.core.plugins.middleware import MiddlewareManager
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
logger = structlog.get_logger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def load_plugin_system(settings: Settings) -> tuple[PluginRegistry, MiddlewareManager]:
|
|
29
|
+
"""Discover plugins and build a registry + middleware manager.
|
|
30
|
+
|
|
31
|
+
This function is the single entry point to set up the plugin layer for
|
|
32
|
+
the application factory. It avoids scattering discovery/registry logic.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
settings: Application settings (with plugin config)
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Tuple of (PluginRegistry, MiddlewareManager)
|
|
39
|
+
"""
|
|
40
|
+
# Discover factories (filesystem + entry points) with existing helper
|
|
41
|
+
factories: dict[str, PluginFactory] = discover_and_load_plugins(settings)
|
|
42
|
+
|
|
43
|
+
# Create registry and register all factories
|
|
44
|
+
registry = PluginRegistry()
|
|
45
|
+
for _name, factory in factories.items():
|
|
46
|
+
registry.register_factory(factory)
|
|
47
|
+
|
|
48
|
+
# Prepare middleware manager; plugins will populate via manifests during
|
|
49
|
+
# app creation (manifest population stage) and at runtime as needed
|
|
50
|
+
middleware_manager = MiddlewareManager()
|
|
51
|
+
|
|
52
|
+
logger.debug(
|
|
53
|
+
"plugin_system_loaded",
|
|
54
|
+
factory_count=len(factories),
|
|
55
|
+
plugins=list(factories.keys()),
|
|
56
|
+
category="plugin",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
return registry, middleware_manager
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def load_cli_plugins(
|
|
63
|
+
settings: Any,
|
|
64
|
+
auth_provider: str | None = None,
|
|
65
|
+
allow_plugins: list[str] | None = None,
|
|
66
|
+
) -> PluginRegistry:
|
|
67
|
+
"""Load filtered plugins for CLI operations.
|
|
68
|
+
|
|
69
|
+
This function creates a lightweight plugin registry for CLI commands that:
|
|
70
|
+
- Includes only CLI-safe plugins (marked with cli_safe = True)
|
|
71
|
+
- Optionally includes a specific auth provider plugin if requested
|
|
72
|
+
- Excludes heavy provider plugins that cause DuckDB locks, task manager errors, etc.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
settings: Application settings
|
|
76
|
+
auth_provider: Name of auth provider to include (e.g., "codex", "claude-api")
|
|
77
|
+
allow_plugins: Additional plugins to explicitly allow (beyond cli_safe ones)
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Filtered PluginRegistry containing only CLI-appropriate plugins
|
|
81
|
+
"""
|
|
82
|
+
# Discover all available factories
|
|
83
|
+
all_factories: dict[str, PluginFactory] = discover_and_load_plugins(settings)
|
|
84
|
+
|
|
85
|
+
# Start with CLI-safe plugins
|
|
86
|
+
cli_factories: dict[str, PluginFactory] = {}
|
|
87
|
+
|
|
88
|
+
for name, factory in all_factories.items():
|
|
89
|
+
# Include plugins explicitly marked as CLI-safe
|
|
90
|
+
if getattr(factory, "cli_safe", False):
|
|
91
|
+
cli_factories[name] = factory
|
|
92
|
+
|
|
93
|
+
# Add specific auth provider if requested
|
|
94
|
+
if auth_provider:
|
|
95
|
+
auth_plugin_name = _resolve_auth_provider_plugin_name(auth_provider)
|
|
96
|
+
if auth_plugin_name and auth_plugin_name in all_factories:
|
|
97
|
+
cli_factories[auth_plugin_name] = all_factories[auth_plugin_name]
|
|
98
|
+
else:
|
|
99
|
+
logger.warning(
|
|
100
|
+
"auth_provider_not_found",
|
|
101
|
+
provider=auth_provider,
|
|
102
|
+
resolved_name=auth_plugin_name,
|
|
103
|
+
available_auth_providers=[
|
|
104
|
+
name
|
|
105
|
+
for name, factory in all_factories.items()
|
|
106
|
+
if isinstance(factory, AuthProviderPluginFactory)
|
|
107
|
+
],
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Add explicitly allowed plugins
|
|
111
|
+
if allow_plugins:
|
|
112
|
+
for plugin_name in allow_plugins:
|
|
113
|
+
if plugin_name in all_factories and plugin_name not in cli_factories:
|
|
114
|
+
cli_factories[plugin_name] = all_factories[plugin_name]
|
|
115
|
+
|
|
116
|
+
# Create filtered registry
|
|
117
|
+
registry = PluginRegistry()
|
|
118
|
+
for _name, factory in cli_factories.items():
|
|
119
|
+
registry.register_factory(factory)
|
|
120
|
+
|
|
121
|
+
logger.debug(
|
|
122
|
+
"cli_plugin_system_loaded",
|
|
123
|
+
total_available=len(all_factories),
|
|
124
|
+
cli_safe_count=len(
|
|
125
|
+
[f for f in all_factories.values() if getattr(f, "cli_safe", False)]
|
|
126
|
+
),
|
|
127
|
+
loaded_count=len(cli_factories),
|
|
128
|
+
loaded_plugins=list(cli_factories.keys()),
|
|
129
|
+
auth_provider=auth_provider,
|
|
130
|
+
allow_plugins=allow_plugins or [],
|
|
131
|
+
category="plugin",
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
return registry
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _resolve_auth_provider_plugin_name(provider: str) -> str | None:
|
|
138
|
+
"""Map CLI provider name to auth plugin name.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
provider: CLI provider name (e.g., "codex", "claude-api")
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
Plugin name (e.g., "oauth_codex", "oauth_claude") or None
|
|
145
|
+
"""
|
|
146
|
+
provider_key = provider.strip().lower().replace("_", "-")
|
|
147
|
+
|
|
148
|
+
mapping: dict[str, str] = {
|
|
149
|
+
"codex": "oauth_codex",
|
|
150
|
+
"openai": "oauth_codex",
|
|
151
|
+
"openai-api": "oauth_codex",
|
|
152
|
+
"claude": "oauth_claude",
|
|
153
|
+
"claude-api": "oauth_claude",
|
|
154
|
+
"claude_api": "oauth_claude",
|
|
155
|
+
"copilot": "copilot",
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
resolved = mapping.get(provider_key)
|
|
159
|
+
if resolved:
|
|
160
|
+
return resolved
|
|
161
|
+
# Fallback: build dynamically as oauth_<provider>
|
|
162
|
+
fallback = "oauth_" + provider_key.replace("-", "_")
|
|
163
|
+
return fallback
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
__all__ = ["load_plugin_system", "load_cli_plugins"]
|