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
ccproxy/utils/startup_helpers.py
CHANGED
|
@@ -7,28 +7,19 @@ the KISS principle and avoiding overengineering.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
-
from datetime import UTC, datetime
|
|
11
10
|
from typing import TYPE_CHECKING
|
|
12
11
|
|
|
13
12
|
import structlog
|
|
14
13
|
from fastapi import FastAPI
|
|
15
14
|
|
|
16
|
-
from ccproxy.auth.credentials_adapter import CredentialsAuthManager
|
|
17
|
-
from ccproxy.auth.exceptions import CredentialsNotFoundError
|
|
18
|
-
from ccproxy.auth.openai.credentials import OpenAITokenManager
|
|
19
|
-
from ccproxy.observability import get_metrics
|
|
20
|
-
|
|
21
|
-
# Note: get_claude_cli_info is imported locally to avoid circular imports
|
|
22
|
-
from ccproxy.observability.storage.duckdb_simple import SimpleDuckDBStorage
|
|
23
15
|
from ccproxy.scheduler.errors import SchedulerError
|
|
24
16
|
from ccproxy.scheduler.manager import start_scheduler, stop_scheduler
|
|
25
|
-
from ccproxy.services.claude_detection_service import ClaudeDetectionService
|
|
26
|
-
from ccproxy.services.claude_sdk_service import ClaudeSDKService
|
|
27
|
-
from ccproxy.services.codex_detection_service import CodexDetectionService
|
|
28
|
-
from ccproxy.services.credentials.manager import CredentialsManager
|
|
29
17
|
|
|
30
18
|
|
|
31
|
-
#
|
|
19
|
+
# DuckDB storage initialization is handled by the duckdb_storage plugin.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# get_permission_service is imported locally to avoid circular imports
|
|
32
23
|
|
|
33
24
|
if TYPE_CHECKING:
|
|
34
25
|
from ccproxy.config.settings import Settings
|
|
@@ -36,126 +27,6 @@ if TYPE_CHECKING:
|
|
|
36
27
|
logger = structlog.get_logger(__name__)
|
|
37
28
|
|
|
38
29
|
|
|
39
|
-
async def validate_claude_authentication_startup(
|
|
40
|
-
app: FastAPI, settings: Settings
|
|
41
|
-
) -> None:
|
|
42
|
-
"""Validate Claude authentication credentials at startup.
|
|
43
|
-
|
|
44
|
-
Args:
|
|
45
|
-
app: FastAPI application instance
|
|
46
|
-
settings: Application settings
|
|
47
|
-
"""
|
|
48
|
-
try:
|
|
49
|
-
credentials_manager = CredentialsManager()
|
|
50
|
-
validation = await credentials_manager.validate()
|
|
51
|
-
|
|
52
|
-
if validation.valid and not validation.expired:
|
|
53
|
-
credentials = validation.credentials
|
|
54
|
-
oauth_token = credentials.claude_ai_oauth if credentials else None
|
|
55
|
-
|
|
56
|
-
if oauth_token and oauth_token.expires_at_datetime:
|
|
57
|
-
hours_until_expiry = int(
|
|
58
|
-
(
|
|
59
|
-
oauth_token.expires_at_datetime - datetime.now(UTC)
|
|
60
|
-
).total_seconds()
|
|
61
|
-
/ 3600
|
|
62
|
-
)
|
|
63
|
-
logger.debug(
|
|
64
|
-
"claude_token_valid",
|
|
65
|
-
expires_in_hours=hours_until_expiry,
|
|
66
|
-
subscription_type=oauth_token.subscription_type,
|
|
67
|
-
credentials_path=str(validation.path) if validation.path else None,
|
|
68
|
-
)
|
|
69
|
-
else:
|
|
70
|
-
logger.debug(
|
|
71
|
-
"claude_token_valid", credentials_path=str(validation.path)
|
|
72
|
-
)
|
|
73
|
-
elif validation.expired:
|
|
74
|
-
logger.warning(
|
|
75
|
-
"claude_token_expired",
|
|
76
|
-
message="Claude authentication token has expired. Please run 'ccproxy auth login' to refresh.",
|
|
77
|
-
credentials_path=str(validation.path) if validation.path else None,
|
|
78
|
-
)
|
|
79
|
-
else:
|
|
80
|
-
logger.warning(
|
|
81
|
-
"claude_token_invalid",
|
|
82
|
-
message="Claude authentication token is invalid. Please run 'ccproxy auth login'.",
|
|
83
|
-
credentials_path=str(validation.path) if validation.path else None,
|
|
84
|
-
)
|
|
85
|
-
except CredentialsNotFoundError:
|
|
86
|
-
logger.warning(
|
|
87
|
-
"claude_token_not_found",
|
|
88
|
-
message="No Claude authentication credentials found. Please run 'ccproxy auth login' to authenticate.",
|
|
89
|
-
searched_paths=settings.auth.storage.storage_paths,
|
|
90
|
-
)
|
|
91
|
-
except Exception as e:
|
|
92
|
-
logger.error(
|
|
93
|
-
"claude_token_validation_error",
|
|
94
|
-
error=str(e),
|
|
95
|
-
message="Failed to validate Claude authentication token. The server will continue without Claude authentication.",
|
|
96
|
-
exc_info=True,
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
async def validate_codex_authentication_startup(
|
|
101
|
-
app: FastAPI, settings: Settings
|
|
102
|
-
) -> None:
|
|
103
|
-
"""Validate Codex (OpenAI) authentication credentials at startup.
|
|
104
|
-
|
|
105
|
-
Args:
|
|
106
|
-
app: FastAPI application instance
|
|
107
|
-
settings: Application settings
|
|
108
|
-
"""
|
|
109
|
-
# Skip codex authentication validation if codex is disabled
|
|
110
|
-
if not settings.codex.enabled:
|
|
111
|
-
logger.debug("codex_token_validation_skipped", reason="codex_disabled")
|
|
112
|
-
return
|
|
113
|
-
|
|
114
|
-
try:
|
|
115
|
-
token_manager = OpenAITokenManager()
|
|
116
|
-
credentials = await token_manager.load_credentials()
|
|
117
|
-
|
|
118
|
-
if not credentials:
|
|
119
|
-
logger.warning(
|
|
120
|
-
"codex_token_not_found",
|
|
121
|
-
message="No Codex authentication credentials found. Please run 'ccproxy auth login-openai' to authenticate.",
|
|
122
|
-
location=token_manager.get_storage_location(),
|
|
123
|
-
)
|
|
124
|
-
return
|
|
125
|
-
|
|
126
|
-
if not credentials.active:
|
|
127
|
-
logger.warning(
|
|
128
|
-
"codex_token_inactive",
|
|
129
|
-
message="Codex authentication credentials are inactive. Please run 'ccproxy auth login-openai' to refresh.",
|
|
130
|
-
location=token_manager.get_storage_location(),
|
|
131
|
-
)
|
|
132
|
-
return
|
|
133
|
-
|
|
134
|
-
if credentials.is_expired():
|
|
135
|
-
logger.warning(
|
|
136
|
-
"codex_token_expired",
|
|
137
|
-
message="Codex authentication token has expired. Please run 'ccproxy auth login-openai' to refresh.",
|
|
138
|
-
location=token_manager.get_storage_location(),
|
|
139
|
-
expires_at=credentials.expires_at.isoformat(),
|
|
140
|
-
)
|
|
141
|
-
else:
|
|
142
|
-
hours_until_expiry = int(credentials.expires_in_seconds() / 3600)
|
|
143
|
-
logger.debug(
|
|
144
|
-
"codex_token_valid",
|
|
145
|
-
expires_in_hours=hours_until_expiry,
|
|
146
|
-
account_id=credentials.account_id,
|
|
147
|
-
location=token_manager.get_storage_location(),
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
except Exception as e:
|
|
151
|
-
logger.error(
|
|
152
|
-
"codex_token_validation_error",
|
|
153
|
-
error=str(e),
|
|
154
|
-
message="Failed to validate Codex authentication token. The server will continue without Codex authentication.",
|
|
155
|
-
exc_info=True,
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
|
|
159
30
|
async def check_version_updates_startup(app: FastAPI, settings: Settings) -> None:
|
|
160
31
|
"""Trigger version update check at startup.
|
|
161
32
|
|
|
@@ -167,6 +38,10 @@ async def check_version_updates_startup(app: FastAPI, settings: Settings) -> Non
|
|
|
167
38
|
settings: Application settings
|
|
168
39
|
"""
|
|
169
40
|
# Skip version check if disabled by settings
|
|
41
|
+
if not settings.scheduler.enabled:
|
|
42
|
+
logger.debug("version_check_startup_skipped_scheduler_disabled")
|
|
43
|
+
return
|
|
44
|
+
|
|
170
45
|
if not settings.scheduler.version_check_enabled:
|
|
171
46
|
logger.debug("version_check_startup_disabled")
|
|
172
47
|
return
|
|
@@ -192,124 +67,32 @@ async def check_version_updates_startup(app: FastAPI, settings: Settings) -> Non
|
|
|
192
67
|
else:
|
|
193
68
|
logger.debug("version_check_startup_failed")
|
|
194
69
|
|
|
195
|
-
except
|
|
70
|
+
except (ImportError, ModuleNotFoundError) as e:
|
|
196
71
|
logger.debug(
|
|
197
|
-
"
|
|
72
|
+
"version_check_startup_import_error",
|
|
198
73
|
error=str(e),
|
|
199
74
|
error_type=type(e).__name__,
|
|
200
75
|
)
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
async def check_claude_cli_startup(app: FastAPI, settings: Settings) -> None:
|
|
204
|
-
"""Check Claude CLI availability at startup.
|
|
205
|
-
|
|
206
|
-
Args:
|
|
207
|
-
app: FastAPI application instance
|
|
208
|
-
settings: Application settings
|
|
209
|
-
"""
|
|
210
|
-
try:
|
|
211
|
-
from ccproxy.api.routes.health import get_claude_cli_info
|
|
212
|
-
|
|
213
|
-
claude_info = await get_claude_cli_info()
|
|
214
|
-
|
|
215
|
-
if claude_info.status == "available":
|
|
216
|
-
logger.info(
|
|
217
|
-
"claude_cli_available",
|
|
218
|
-
status=claude_info.status,
|
|
219
|
-
version=claude_info.version,
|
|
220
|
-
binary_path=claude_info.binary_path,
|
|
221
|
-
)
|
|
222
|
-
else:
|
|
223
|
-
logger.warning(
|
|
224
|
-
"claude_cli_unavailable",
|
|
225
|
-
status=claude_info.status,
|
|
226
|
-
error=claude_info.error,
|
|
227
|
-
binary_path=claude_info.binary_path,
|
|
228
|
-
message=f"Claude CLI status: {claude_info.status}",
|
|
229
|
-
)
|
|
230
76
|
except Exception as e:
|
|
231
|
-
logger.
|
|
232
|
-
"
|
|
77
|
+
logger.debug(
|
|
78
|
+
"version_check_startup_unexpected_error",
|
|
233
79
|
error=str(e),
|
|
234
|
-
|
|
80
|
+
error_type=type(e).__name__,
|
|
235
81
|
)
|
|
236
82
|
|
|
237
83
|
|
|
238
|
-
async def
|
|
239
|
-
"""Check
|
|
84
|
+
async def check_claude_cli_startup(app: FastAPI, settings: Settings) -> None:
|
|
85
|
+
"""Check Claude CLI availability at startup.
|
|
240
86
|
|
|
241
87
|
Args:
|
|
242
88
|
app: FastAPI application instance
|
|
243
89
|
settings: Application settings
|
|
244
90
|
"""
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
codex_info = await get_codex_cli_info()
|
|
249
|
-
|
|
250
|
-
if codex_info.status == "available":
|
|
251
|
-
logger.info(
|
|
252
|
-
"codex_cli_available",
|
|
253
|
-
status=codex_info.status,
|
|
254
|
-
version=codex_info.version,
|
|
255
|
-
binary_path=codex_info.binary_path,
|
|
256
|
-
)
|
|
257
|
-
else:
|
|
258
|
-
logger.warning(
|
|
259
|
-
"codex_cli_unavailable",
|
|
260
|
-
status=codex_info.status,
|
|
261
|
-
error=codex_info.error,
|
|
262
|
-
binary_path=codex_info.binary_path,
|
|
263
|
-
message=f"Codex CLI status: {codex_info.status}",
|
|
264
|
-
)
|
|
265
|
-
except Exception as e:
|
|
266
|
-
logger.error(
|
|
267
|
-
"codex_cli_check_failed",
|
|
268
|
-
error=str(e),
|
|
269
|
-
message="Failed to check Codex CLI status during startup",
|
|
270
|
-
)
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
async def initialize_log_storage_startup(app: FastAPI, settings: Settings) -> None:
|
|
274
|
-
"""Initialize log storage if needed and backend is DuckDB.
|
|
91
|
+
# Claude CLI check is now handled by the plugin
|
|
92
|
+
pass
|
|
275
93
|
|
|
276
|
-
Args:
|
|
277
|
-
app: FastAPI application instance
|
|
278
|
-
settings: Application settings
|
|
279
|
-
"""
|
|
280
|
-
if (
|
|
281
|
-
settings.observability.needs_storage_backend
|
|
282
|
-
and settings.observability.log_storage_backend == "duckdb"
|
|
283
|
-
):
|
|
284
|
-
try:
|
|
285
|
-
storage = SimpleDuckDBStorage(
|
|
286
|
-
database_path=settings.observability.duckdb_path
|
|
287
|
-
)
|
|
288
|
-
await storage.initialize()
|
|
289
|
-
app.state.log_storage = storage
|
|
290
|
-
logger.debug(
|
|
291
|
-
"log_storage_initialized",
|
|
292
|
-
backend="duckdb",
|
|
293
|
-
path=str(settings.observability.duckdb_path),
|
|
294
|
-
collection_enabled=settings.observability.logs_collection_enabled,
|
|
295
|
-
)
|
|
296
|
-
except Exception as e:
|
|
297
|
-
logger.error("log_storage_initialization_failed", error=str(e))
|
|
298
|
-
# Continue without log storage (graceful degradation)
|
|
299
94
|
|
|
300
|
-
|
|
301
|
-
async def initialize_log_storage_shutdown(app: FastAPI) -> None:
|
|
302
|
-
"""Close log storage if initialized.
|
|
303
|
-
|
|
304
|
-
Args:
|
|
305
|
-
app: FastAPI application instance
|
|
306
|
-
"""
|
|
307
|
-
if hasattr(app.state, "log_storage") and app.state.log_storage:
|
|
308
|
-
try:
|
|
309
|
-
await app.state.log_storage.close()
|
|
310
|
-
logger.debug("log_storage_closed")
|
|
311
|
-
except Exception as e:
|
|
312
|
-
logger.error("log_storage_close_failed", error=str(e))
|
|
95
|
+
# DuckDB storage startup/shutdown handled by plugin
|
|
313
96
|
|
|
314
97
|
|
|
315
98
|
async def setup_scheduler_startup(app: FastAPI, settings: Settings) -> None:
|
|
@@ -320,9 +103,14 @@ async def setup_scheduler_startup(app: FastAPI, settings: Settings) -> None:
|
|
|
320
103
|
settings: Application settings
|
|
321
104
|
"""
|
|
322
105
|
try:
|
|
323
|
-
|
|
106
|
+
# Use DI container to resolve registry and dependencies
|
|
107
|
+
container = app.state.service_container
|
|
108
|
+
scheduler = await start_scheduler(settings, container)
|
|
324
109
|
app.state.scheduler = scheduler
|
|
325
|
-
|
|
110
|
+
if scheduler:
|
|
111
|
+
logger.debug("scheduler_initialized")
|
|
112
|
+
else:
|
|
113
|
+
logger.debug("scheduler_skipped_initialization")
|
|
326
114
|
|
|
327
115
|
# Add session pool stats task if session manager is available
|
|
328
116
|
if (
|
|
@@ -340,11 +128,19 @@ async def setup_scheduler_startup(app: FastAPI, settings: Settings) -> None:
|
|
|
340
128
|
pool_manager=app.state.session_manager,
|
|
341
129
|
)
|
|
342
130
|
logger.debug("session_pool_stats_task_added", interval_seconds=60)
|
|
131
|
+
except (ImportError, ModuleNotFoundError) as e:
|
|
132
|
+
logger.error(
|
|
133
|
+
"session_pool_stats_task_add_import_error",
|
|
134
|
+
error=str(e),
|
|
135
|
+
error_type=type(e).__name__,
|
|
136
|
+
exc_info=e,
|
|
137
|
+
)
|
|
343
138
|
except Exception as e:
|
|
344
139
|
logger.error(
|
|
345
|
-
"
|
|
140
|
+
"session_pool_stats_task_add_unexpected_error",
|
|
346
141
|
error=str(e),
|
|
347
142
|
error_type=type(e).__name__,
|
|
143
|
+
exc_info=e,
|
|
348
144
|
)
|
|
349
145
|
except SchedulerError as e:
|
|
350
146
|
logger.error("scheduler_initialization_failed", error=str(e))
|
|
@@ -375,227 +171,53 @@ async def setup_session_manager_shutdown(app: FastAPI) -> None:
|
|
|
375
171
|
try:
|
|
376
172
|
await app.state.session_manager.shutdown()
|
|
377
173
|
logger.debug("claude_sdk_session_manager_shutdown")
|
|
174
|
+
except (ImportError, ModuleNotFoundError) as e:
|
|
175
|
+
logger.error(
|
|
176
|
+
"claude_sdk_session_manager_shutdown_import_error",
|
|
177
|
+
error=str(e),
|
|
178
|
+
exc_info=e,
|
|
179
|
+
)
|
|
378
180
|
except Exception as e:
|
|
379
|
-
logger.error(
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
"""Initialize Claude detection service.
|
|
384
|
-
|
|
385
|
-
Args:
|
|
386
|
-
app: FastAPI application instance
|
|
387
|
-
settings: Application settings
|
|
388
|
-
"""
|
|
389
|
-
try:
|
|
390
|
-
logger.debug("initializing_claude_detection")
|
|
391
|
-
detection_service = ClaudeDetectionService(settings)
|
|
392
|
-
claude_data = await detection_service.initialize_detection()
|
|
393
|
-
app.state.claude_detection_data = claude_data
|
|
394
|
-
app.state.claude_detection_service = detection_service
|
|
395
|
-
logger.debug(
|
|
396
|
-
"claude_detection_completed",
|
|
397
|
-
version=claude_data.claude_version,
|
|
398
|
-
cached_at=claude_data.cached_at.isoformat(),
|
|
399
|
-
)
|
|
400
|
-
except Exception as e:
|
|
401
|
-
logger.error("claude_detection_startup_failed", error=str(e))
|
|
402
|
-
# Continue startup with fallback - detection service will provide fallback data
|
|
403
|
-
detection_service = ClaudeDetectionService(settings)
|
|
404
|
-
app.state.claude_detection_data = detection_service._get_fallback_data()
|
|
405
|
-
app.state.claude_detection_service = detection_service
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
async def initialize_codex_detection_startup(app: FastAPI, settings: Settings) -> None:
|
|
409
|
-
"""Initialize Codex detection service.
|
|
410
|
-
|
|
411
|
-
Args:
|
|
412
|
-
app: FastAPI application instance
|
|
413
|
-
settings: Application settings
|
|
414
|
-
"""
|
|
415
|
-
# Skip codex detection if codex is disabled
|
|
416
|
-
if not settings.codex.enabled:
|
|
417
|
-
logger.debug("codex_detection_skipped", reason="codex_disabled")
|
|
418
|
-
detection_service = CodexDetectionService(settings)
|
|
419
|
-
app.state.codex_detection_data = detection_service._get_fallback_data()
|
|
420
|
-
app.state.codex_detection_service = detection_service
|
|
421
|
-
return
|
|
422
|
-
|
|
423
|
-
# Check if Codex CLI is available before attempting header detection
|
|
424
|
-
from ccproxy.api.routes.health import get_codex_cli_info
|
|
425
|
-
|
|
426
|
-
codex_info = await get_codex_cli_info()
|
|
427
|
-
if codex_info.status != "available":
|
|
428
|
-
logger.debug(
|
|
429
|
-
"codex_detection_skipped",
|
|
430
|
-
reason="codex_cli_not_available",
|
|
431
|
-
status=codex_info.status,
|
|
432
|
-
)
|
|
433
|
-
detection_service = CodexDetectionService(settings)
|
|
434
|
-
app.state.codex_detection_data = detection_service._get_fallback_data()
|
|
435
|
-
app.state.codex_detection_service = detection_service
|
|
436
|
-
return
|
|
437
|
-
|
|
438
|
-
try:
|
|
439
|
-
logger.debug("initializing_codex_detection")
|
|
440
|
-
detection_service = CodexDetectionService(settings)
|
|
441
|
-
codex_data = await detection_service.initialize_detection()
|
|
442
|
-
app.state.codex_detection_data = codex_data
|
|
443
|
-
app.state.codex_detection_service = detection_service
|
|
444
|
-
logger.debug(
|
|
445
|
-
"codex_detection_completed",
|
|
446
|
-
version=codex_data.codex_version,
|
|
447
|
-
cached_at=codex_data.cached_at.isoformat(),
|
|
448
|
-
)
|
|
449
|
-
except Exception as e:
|
|
450
|
-
logger.error("codex_detection_startup_failed", error=str(e))
|
|
451
|
-
# Continue startup with fallback - detection service will provide fallback data
|
|
452
|
-
detection_service = CodexDetectionService(settings)
|
|
453
|
-
app.state.codex_detection_data = detection_service._get_fallback_data()
|
|
454
|
-
app.state.codex_detection_service = detection_service
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
async def initialize_claude_sdk_startup(app: FastAPI, settings: Settings) -> None:
|
|
458
|
-
"""Initialize ClaudeSDKService and store in app state.
|
|
459
|
-
|
|
460
|
-
Args:
|
|
461
|
-
app: FastAPI application instance
|
|
462
|
-
settings: Application settings
|
|
463
|
-
"""
|
|
464
|
-
try:
|
|
465
|
-
# Create auth manager with settings
|
|
466
|
-
auth_manager = CredentialsAuthManager()
|
|
467
|
-
|
|
468
|
-
# Get global metrics instance
|
|
469
|
-
metrics = get_metrics()
|
|
470
|
-
|
|
471
|
-
# Check if session pool should be enabled from settings configuration
|
|
472
|
-
use_session_pool = settings.claude.sdk_session_pool.enabled
|
|
473
|
-
|
|
474
|
-
# Initialize session manager if session pool is enabled
|
|
475
|
-
session_manager = None
|
|
476
|
-
if use_session_pool:
|
|
477
|
-
from ccproxy.claude_sdk.manager import SessionManager
|
|
478
|
-
|
|
479
|
-
# Create SessionManager with dependency injection
|
|
480
|
-
session_manager = SessionManager(
|
|
481
|
-
settings=settings, metrics_factory=lambda: metrics
|
|
181
|
+
logger.error(
|
|
182
|
+
"claude_sdk_session_manager_shutdown_unexpected_error",
|
|
183
|
+
error=str(e),
|
|
184
|
+
exc_info=e,
|
|
482
185
|
)
|
|
483
186
|
|
|
484
|
-
# Start the session manager (initializes session pool if enabled)
|
|
485
|
-
await session_manager.start()
|
|
486
|
-
|
|
487
|
-
# Create ClaudeSDKService instance
|
|
488
|
-
claude_service = ClaudeSDKService(
|
|
489
|
-
auth_manager=auth_manager,
|
|
490
|
-
metrics=metrics,
|
|
491
|
-
settings=settings,
|
|
492
|
-
session_manager=session_manager,
|
|
493
|
-
)
|
|
494
|
-
|
|
495
|
-
# Store in app state for reuse in dependencies
|
|
496
|
-
app.state.claude_service = claude_service
|
|
497
|
-
app.state.session_manager = (
|
|
498
|
-
session_manager # Store session_manager for shutdown
|
|
499
|
-
)
|
|
500
|
-
logger.debug("claude_sdk_service_initialized")
|
|
501
|
-
except Exception as e:
|
|
502
|
-
logger.error("claude_sdk_service_initialization_failed", error=str(e))
|
|
503
|
-
# Continue startup even if ClaudeSDKService fails (graceful degradation)
|
|
504
|
-
|
|
505
187
|
|
|
506
|
-
async def
|
|
188
|
+
async def initialize_service_container_startup(
|
|
507
189
|
app: FastAPI, settings: Settings
|
|
508
190
|
) -> None:
|
|
509
|
-
"""Initialize
|
|
191
|
+
"""Initialize service container and proxy client.
|
|
510
192
|
|
|
511
193
|
Args:
|
|
512
194
|
app: FastAPI application instance
|
|
513
195
|
settings: Application settings
|
|
514
196
|
"""
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
permission_service = get_permission_service()
|
|
520
|
-
|
|
521
|
-
# Only connect terminal handler if not using external handler
|
|
522
|
-
if settings.server.use_terminal_permission_handler:
|
|
523
|
-
# terminal_handler = TerminalPermissionHandler()
|
|
524
|
-
|
|
525
|
-
# TODO: Terminal handler should subscribe to events from the service
|
|
526
|
-
# instead of trying to set a handler directly
|
|
527
|
-
# The service uses an event-based architecture, not direct handlers
|
|
528
|
-
|
|
529
|
-
# logger.info(
|
|
530
|
-
# "permission_handler_configured",
|
|
531
|
-
# handler_type="terminal",
|
|
532
|
-
# message="Connected terminal handler to permission service",
|
|
533
|
-
# )
|
|
534
|
-
# app.state.terminal_handler = terminal_handler
|
|
535
|
-
pass
|
|
536
|
-
else:
|
|
537
|
-
logger.debug(
|
|
538
|
-
"permission_handler_configured",
|
|
539
|
-
handler_type="external_sse",
|
|
540
|
-
message="Terminal permission handler disabled - use 'ccproxy permission-handler connect' to handle permissions",
|
|
541
|
-
)
|
|
542
|
-
logger.warning(
|
|
543
|
-
"permission_handler_required",
|
|
544
|
-
message="Start external handler with: ccproxy permission-handler connect",
|
|
545
|
-
)
|
|
197
|
+
try:
|
|
198
|
+
# Create HTTP client for proxy
|
|
199
|
+
from ccproxy.services.container import ServiceContainer
|
|
546
200
|
|
|
547
|
-
|
|
548
|
-
|
|
201
|
+
# Reuse ServiceContainer from app state or create new one
|
|
202
|
+
if hasattr(app.state, "service_container"):
|
|
203
|
+
container = app.state.service_container
|
|
204
|
+
else:
|
|
205
|
+
logger.debug("creating_new_service_container")
|
|
206
|
+
container = ServiceContainer(settings)
|
|
207
|
+
app.state.service_container = container
|
|
549
208
|
|
|
550
|
-
|
|
551
|
-
|
|
209
|
+
# Metrics are now handled by the metrics plugin
|
|
210
|
+
app.state.metrics = None
|
|
552
211
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
builtin_permissions_enabled=True,
|
|
558
|
-
)
|
|
559
|
-
except Exception as e:
|
|
560
|
-
logger.error("permission_service_initialization_failed", error=str(e))
|
|
561
|
-
# Continue without permission service (API will work but without prompts)
|
|
562
|
-
else:
|
|
563
|
-
logger.debug(
|
|
564
|
-
"permission_service_skipped",
|
|
565
|
-
builtin_permissions_enabled=False,
|
|
566
|
-
message="Built-in permission handling disabled - users can configure custom MCP servers and permission tools",
|
|
212
|
+
logger.debug("service_container_initialized")
|
|
213
|
+
except (ImportError, ModuleNotFoundError) as e:
|
|
214
|
+
logger.error(
|
|
215
|
+
"service_container_initialization_import_error", error=str(e), exc_info=e
|
|
567
216
|
)
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
async def setup_permission_service_shutdown(app: FastAPI, settings: Settings) -> None:
|
|
571
|
-
"""Stop permission service (if it was initialized).
|
|
572
|
-
|
|
573
|
-
Args:
|
|
574
|
-
app: FastAPI application instance
|
|
575
|
-
settings: Application settings
|
|
576
|
-
"""
|
|
577
|
-
if (
|
|
578
|
-
hasattr(app.state, "permission_service")
|
|
579
|
-
and app.state.permission_service
|
|
580
|
-
and settings.claude.builtin_permissions
|
|
581
|
-
):
|
|
582
|
-
try:
|
|
583
|
-
await app.state.permission_service.stop()
|
|
584
|
-
logger.debug("permission_service_stopped")
|
|
585
|
-
except Exception as e:
|
|
586
|
-
logger.error("permission_service_stop_failed", error=str(e))
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
async def flush_streaming_batches_shutdown(app: FastAPI) -> None:
|
|
590
|
-
"""Flush any remaining streaming log batches.
|
|
591
|
-
|
|
592
|
-
Args:
|
|
593
|
-
app: FastAPI application instance
|
|
594
|
-
"""
|
|
595
|
-
try:
|
|
596
|
-
from ccproxy.utils.simple_request_logger import flush_all_streaming_batches
|
|
597
|
-
|
|
598
|
-
await flush_all_streaming_batches()
|
|
599
|
-
logger.debug("streaming_batches_flushed")
|
|
600
217
|
except Exception as e:
|
|
601
|
-
logger.error(
|
|
218
|
+
logger.error(
|
|
219
|
+
"service_container_initialization_unexpected_error",
|
|
220
|
+
error=str(e),
|
|
221
|
+
exc_info=e,
|
|
222
|
+
)
|
|
223
|
+
# Continue startup even if service container fails (graceful degradation)
|