ccproxy-api 0.1.6__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 +439 -212
- ccproxy/api/bootstrap.py +30 -0
- ccproxy/api/decorators.py +85 -0
- ccproxy/api/dependencies.py +145 -176
- ccproxy/api/format_validation.py +54 -0
- ccproxy/api/middleware/cors.py +6 -3
- ccproxy/api/middleware/errors.py +402 -530
- 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 +558 -0
- ccproxy/data/codex_headers_fallback.json +121 -0
- 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 +63 -107
- ccproxy/scheduler/registry.py +6 -32
- ccproxy/scheduler/tasks.py +346 -314
- 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 +95 -342
- ccproxy/utils/version_checker.py +279 -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.6.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 -1231
- 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 -269
- ccproxy/services/codex_detection_service.py +0 -263
- 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.6.dist-info/METADATA +0 -615
- ccproxy_api-0.1.6.dist-info/RECORD +0 -189
- ccproxy_api-0.1.6.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.6.dist-info → ccproxy_api-0.2.0.dist-info}/licenses/LICENSE +0 -0
ccproxy/utils/startup_helpers.py
CHANGED
|
@@ -7,27 +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.observability import get_metrics
|
|
19
|
-
|
|
20
|
-
# Note: get_claude_cli_info is imported locally to avoid circular imports
|
|
21
|
-
from ccproxy.observability.storage.duckdb_simple import SimpleDuckDBStorage
|
|
22
15
|
from ccproxy.scheduler.errors import SchedulerError
|
|
23
16
|
from ccproxy.scheduler.manager import start_scheduler, stop_scheduler
|
|
24
|
-
from ccproxy.services.claude_detection_service import ClaudeDetectionService
|
|
25
|
-
from ccproxy.services.claude_sdk_service import ClaudeSDKService
|
|
26
|
-
from ccproxy.services.codex_detection_service import CodexDetectionService
|
|
27
|
-
from ccproxy.services.credentials.manager import CredentialsManager
|
|
28
17
|
|
|
29
18
|
|
|
30
|
-
#
|
|
19
|
+
# DuckDB storage initialization is handled by the duckdb_storage plugin.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# get_permission_service is imported locally to avoid circular imports
|
|
31
23
|
|
|
32
24
|
if TYPE_CHECKING:
|
|
33
25
|
from ccproxy.config.settings import Settings
|
|
@@ -35,173 +27,72 @@ if TYPE_CHECKING:
|
|
|
35
27
|
logger = structlog.get_logger(__name__)
|
|
36
28
|
|
|
37
29
|
|
|
38
|
-
async def
|
|
39
|
-
"""
|
|
30
|
+
async def check_version_updates_startup(app: FastAPI, settings: Settings) -> None:
|
|
31
|
+
"""Trigger version update check at startup.
|
|
32
|
+
|
|
33
|
+
Manually runs the version check task once during application startup,
|
|
34
|
+
before the scheduler starts managing periodic checks.
|
|
40
35
|
|
|
41
36
|
Args:
|
|
42
37
|
app: FastAPI application instance
|
|
43
38
|
settings: Application settings
|
|
44
39
|
"""
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if validation.valid and not validation.expired:
|
|
50
|
-
credentials = validation.credentials
|
|
51
|
-
oauth_token = credentials.claude_ai_oauth if credentials else None
|
|
52
|
-
|
|
53
|
-
if oauth_token and oauth_token.expires_at_datetime:
|
|
54
|
-
hours_until_expiry = int(
|
|
55
|
-
(
|
|
56
|
-
oauth_token.expires_at_datetime - datetime.now(UTC)
|
|
57
|
-
).total_seconds()
|
|
58
|
-
/ 3600
|
|
59
|
-
)
|
|
60
|
-
logger.debug(
|
|
61
|
-
"auth_token_valid",
|
|
62
|
-
expires_in_hours=hours_until_expiry,
|
|
63
|
-
subscription_type=oauth_token.subscription_type,
|
|
64
|
-
credentials_path=str(validation.path) if validation.path else None,
|
|
65
|
-
)
|
|
66
|
-
else:
|
|
67
|
-
logger.debug("auth_token_valid", credentials_path=str(validation.path))
|
|
68
|
-
elif validation.expired:
|
|
69
|
-
logger.warning(
|
|
70
|
-
"auth_token_expired",
|
|
71
|
-
message="Authentication token has expired. Please run 'ccproxy auth login' to refresh.",
|
|
72
|
-
credentials_path=str(validation.path) if validation.path else None,
|
|
73
|
-
)
|
|
74
|
-
else:
|
|
75
|
-
logger.warning(
|
|
76
|
-
"auth_token_invalid",
|
|
77
|
-
message="Authentication token is invalid. Please run 'ccproxy auth login'.",
|
|
78
|
-
credentials_path=str(validation.path) if validation.path else None,
|
|
79
|
-
)
|
|
80
|
-
except CredentialsNotFoundError:
|
|
81
|
-
logger.warning(
|
|
82
|
-
"auth_token_not_found",
|
|
83
|
-
message="No authentication credentials found. Please run 'ccproxy auth login' to authenticate.",
|
|
84
|
-
searched_paths=settings.auth.storage.storage_paths,
|
|
85
|
-
)
|
|
86
|
-
except Exception as e:
|
|
87
|
-
logger.error(
|
|
88
|
-
"auth_token_validation_error",
|
|
89
|
-
error=str(e),
|
|
90
|
-
message="Failed to validate authentication token. The server will continue without authentication.",
|
|
91
|
-
exc_info=True,
|
|
92
|
-
)
|
|
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
|
|
93
44
|
|
|
45
|
+
if not settings.scheduler.version_check_enabled:
|
|
46
|
+
logger.debug("version_check_startup_disabled")
|
|
47
|
+
return
|
|
94
48
|
|
|
95
|
-
async def check_claude_cli_startup(app: FastAPI, settings: Settings) -> None:
|
|
96
|
-
"""Check Claude CLI availability at startup.
|
|
97
|
-
|
|
98
|
-
Args:
|
|
99
|
-
app: FastAPI application instance
|
|
100
|
-
settings: Application settings
|
|
101
|
-
"""
|
|
102
49
|
try:
|
|
103
|
-
|
|
50
|
+
# Import locally to avoid circular imports and create task instance
|
|
51
|
+
from ccproxy.scheduler.tasks import VersionUpdateCheckTask
|
|
52
|
+
|
|
53
|
+
# Create a temporary task instance for startup check
|
|
54
|
+
version_task = VersionUpdateCheckTask(
|
|
55
|
+
name="version_check_startup",
|
|
56
|
+
interval_seconds=settings.scheduler.version_check_interval_hours * 3600,
|
|
57
|
+
enabled=True,
|
|
58
|
+
version_check_cache_ttl_hours=settings.scheduler.version_check_cache_ttl_hours,
|
|
59
|
+
skip_first_scheduled_run=False,
|
|
60
|
+
)
|
|
104
61
|
|
|
105
|
-
|
|
62
|
+
# Run the version check once and wait for it to complete
|
|
63
|
+
success = await version_task.run()
|
|
106
64
|
|
|
107
|
-
if
|
|
108
|
-
logger.
|
|
109
|
-
"claude_cli_available",
|
|
110
|
-
status=claude_info.status,
|
|
111
|
-
version=claude_info.version,
|
|
112
|
-
binary_path=claude_info.binary_path,
|
|
113
|
-
)
|
|
65
|
+
if success:
|
|
66
|
+
logger.debug("version_check_startup_completed")
|
|
114
67
|
else:
|
|
115
|
-
logger.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
message=f"Claude CLI status: {claude_info.status}",
|
|
121
|
-
)
|
|
122
|
-
except Exception as e:
|
|
123
|
-
logger.error(
|
|
124
|
-
"claude_cli_check_failed",
|
|
68
|
+
logger.debug("version_check_startup_failed")
|
|
69
|
+
|
|
70
|
+
except (ImportError, ModuleNotFoundError) as e:
|
|
71
|
+
logger.debug(
|
|
72
|
+
"version_check_startup_import_error",
|
|
125
73
|
error=str(e),
|
|
126
|
-
|
|
74
|
+
error_type=type(e).__name__,
|
|
127
75
|
)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
async def check_codex_cli_startup(app: FastAPI, settings: Settings) -> None:
|
|
131
|
-
"""Check Codex CLI availability at startup.
|
|
132
|
-
|
|
133
|
-
Args:
|
|
134
|
-
app: FastAPI application instance
|
|
135
|
-
settings: Application settings
|
|
136
|
-
"""
|
|
137
|
-
try:
|
|
138
|
-
from ccproxy.api.routes.health import get_codex_cli_info
|
|
139
|
-
|
|
140
|
-
codex_info = await get_codex_cli_info()
|
|
141
|
-
|
|
142
|
-
if codex_info.status == "available":
|
|
143
|
-
logger.info(
|
|
144
|
-
"codex_cli_available",
|
|
145
|
-
status=codex_info.status,
|
|
146
|
-
version=codex_info.version,
|
|
147
|
-
binary_path=codex_info.binary_path,
|
|
148
|
-
)
|
|
149
|
-
else:
|
|
150
|
-
logger.warning(
|
|
151
|
-
"codex_cli_unavailable",
|
|
152
|
-
status=codex_info.status,
|
|
153
|
-
error=codex_info.error,
|
|
154
|
-
binary_path=codex_info.binary_path,
|
|
155
|
-
message=f"Codex CLI status: {codex_info.status}",
|
|
156
|
-
)
|
|
157
76
|
except Exception as e:
|
|
158
|
-
logger.
|
|
159
|
-
"
|
|
77
|
+
logger.debug(
|
|
78
|
+
"version_check_startup_unexpected_error",
|
|
160
79
|
error=str(e),
|
|
161
|
-
|
|
80
|
+
error_type=type(e).__name__,
|
|
162
81
|
)
|
|
163
82
|
|
|
164
83
|
|
|
165
|
-
async def
|
|
166
|
-
"""
|
|
84
|
+
async def check_claude_cli_startup(app: FastAPI, settings: Settings) -> None:
|
|
85
|
+
"""Check Claude CLI availability at startup.
|
|
167
86
|
|
|
168
87
|
Args:
|
|
169
88
|
app: FastAPI application instance
|
|
170
89
|
settings: Application settings
|
|
171
90
|
"""
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
and settings.observability.log_storage_backend == "duckdb"
|
|
175
|
-
):
|
|
176
|
-
try:
|
|
177
|
-
storage = SimpleDuckDBStorage(
|
|
178
|
-
database_path=settings.observability.duckdb_path
|
|
179
|
-
)
|
|
180
|
-
await storage.initialize()
|
|
181
|
-
app.state.log_storage = storage
|
|
182
|
-
logger.debug(
|
|
183
|
-
"log_storage_initialized",
|
|
184
|
-
backend="duckdb",
|
|
185
|
-
path=str(settings.observability.duckdb_path),
|
|
186
|
-
collection_enabled=settings.observability.logs_collection_enabled,
|
|
187
|
-
)
|
|
188
|
-
except Exception as e:
|
|
189
|
-
logger.error("log_storage_initialization_failed", error=str(e))
|
|
190
|
-
# Continue without log storage (graceful degradation)
|
|
91
|
+
# Claude CLI check is now handled by the plugin
|
|
92
|
+
pass
|
|
191
93
|
|
|
192
94
|
|
|
193
|
-
|
|
194
|
-
"""Close log storage if initialized.
|
|
195
|
-
|
|
196
|
-
Args:
|
|
197
|
-
app: FastAPI application instance
|
|
198
|
-
"""
|
|
199
|
-
if hasattr(app.state, "log_storage") and app.state.log_storage:
|
|
200
|
-
try:
|
|
201
|
-
await app.state.log_storage.close()
|
|
202
|
-
logger.debug("log_storage_closed")
|
|
203
|
-
except Exception as e:
|
|
204
|
-
logger.error("log_storage_close_failed", error=str(e))
|
|
95
|
+
# DuckDB storage startup/shutdown handled by plugin
|
|
205
96
|
|
|
206
97
|
|
|
207
98
|
async def setup_scheduler_startup(app: FastAPI, settings: Settings) -> None:
|
|
@@ -212,9 +103,14 @@ async def setup_scheduler_startup(app: FastAPI, settings: Settings) -> None:
|
|
|
212
103
|
settings: Application settings
|
|
213
104
|
"""
|
|
214
105
|
try:
|
|
215
|
-
|
|
106
|
+
# Use DI container to resolve registry and dependencies
|
|
107
|
+
container = app.state.service_container
|
|
108
|
+
scheduler = await start_scheduler(settings, container)
|
|
216
109
|
app.state.scheduler = scheduler
|
|
217
|
-
|
|
110
|
+
if scheduler:
|
|
111
|
+
logger.debug("scheduler_initialized")
|
|
112
|
+
else:
|
|
113
|
+
logger.debug("scheduler_skipped_initialization")
|
|
218
114
|
|
|
219
115
|
# Add session pool stats task if session manager is available
|
|
220
116
|
if (
|
|
@@ -232,11 +128,19 @@ async def setup_scheduler_startup(app: FastAPI, settings: Settings) -> None:
|
|
|
232
128
|
pool_manager=app.state.session_manager,
|
|
233
129
|
)
|
|
234
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
|
+
)
|
|
235
138
|
except Exception as e:
|
|
236
139
|
logger.error(
|
|
237
|
-
"
|
|
140
|
+
"session_pool_stats_task_add_unexpected_error",
|
|
238
141
|
error=str(e),
|
|
239
142
|
error_type=type(e).__name__,
|
|
143
|
+
exc_info=e,
|
|
240
144
|
)
|
|
241
145
|
except SchedulerError as e:
|
|
242
146
|
logger.error("scheduler_initialization_failed", error=str(e))
|
|
@@ -267,204 +171,53 @@ async def setup_session_manager_shutdown(app: FastAPI) -> None:
|
|
|
267
171
|
try:
|
|
268
172
|
await app.state.session_manager.shutdown()
|
|
269
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
|
+
)
|
|
270
180
|
except Exception as e:
|
|
271
|
-
logger.error(
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
"""Initialize Claude detection service.
|
|
276
|
-
|
|
277
|
-
Args:
|
|
278
|
-
app: FastAPI application instance
|
|
279
|
-
settings: Application settings
|
|
280
|
-
"""
|
|
281
|
-
try:
|
|
282
|
-
logger.debug("initializing_claude_detection")
|
|
283
|
-
detection_service = ClaudeDetectionService(settings)
|
|
284
|
-
claude_data = await detection_service.initialize_detection()
|
|
285
|
-
app.state.claude_detection_data = claude_data
|
|
286
|
-
app.state.claude_detection_service = detection_service
|
|
287
|
-
logger.debug(
|
|
288
|
-
"claude_detection_completed",
|
|
289
|
-
version=claude_data.claude_version,
|
|
290
|
-
cached_at=claude_data.cached_at.isoformat(),
|
|
291
|
-
)
|
|
292
|
-
except Exception as e:
|
|
293
|
-
logger.error("claude_detection_startup_failed", error=str(e))
|
|
294
|
-
# Continue startup with fallback - detection service will provide fallback data
|
|
295
|
-
detection_service = ClaudeDetectionService(settings)
|
|
296
|
-
app.state.claude_detection_data = detection_service._get_fallback_data()
|
|
297
|
-
app.state.claude_detection_service = detection_service
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
async def initialize_codex_detection_startup(app: FastAPI, settings: Settings) -> None:
|
|
301
|
-
"""Initialize Codex detection service.
|
|
302
|
-
|
|
303
|
-
Args:
|
|
304
|
-
app: FastAPI application instance
|
|
305
|
-
settings: Application settings
|
|
306
|
-
"""
|
|
307
|
-
try:
|
|
308
|
-
logger.debug("initializing_codex_detection")
|
|
309
|
-
detection_service = CodexDetectionService(settings)
|
|
310
|
-
codex_data = await detection_service.initialize_detection()
|
|
311
|
-
app.state.codex_detection_data = codex_data
|
|
312
|
-
app.state.codex_detection_service = detection_service
|
|
313
|
-
logger.debug(
|
|
314
|
-
"codex_detection_completed",
|
|
315
|
-
version=codex_data.codex_version,
|
|
316
|
-
cached_at=codex_data.cached_at.isoformat(),
|
|
317
|
-
)
|
|
318
|
-
except Exception as e:
|
|
319
|
-
logger.error("codex_detection_startup_failed", error=str(e))
|
|
320
|
-
# Continue startup with fallback - detection service will provide fallback data
|
|
321
|
-
detection_service = CodexDetectionService(settings)
|
|
322
|
-
app.state.codex_detection_data = detection_service._get_fallback_data()
|
|
323
|
-
app.state.codex_detection_service = detection_service
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
async def initialize_claude_sdk_startup(app: FastAPI, settings: Settings) -> None:
|
|
327
|
-
"""Initialize ClaudeSDKService and store in app state.
|
|
328
|
-
|
|
329
|
-
Args:
|
|
330
|
-
app: FastAPI application instance
|
|
331
|
-
settings: Application settings
|
|
332
|
-
"""
|
|
333
|
-
try:
|
|
334
|
-
# Create auth manager with settings
|
|
335
|
-
auth_manager = CredentialsAuthManager()
|
|
336
|
-
|
|
337
|
-
# Get global metrics instance
|
|
338
|
-
metrics = get_metrics()
|
|
339
|
-
|
|
340
|
-
# Check if session pool should be enabled from settings configuration
|
|
341
|
-
use_session_pool = settings.claude.sdk_session_pool.enabled
|
|
342
|
-
|
|
343
|
-
# Initialize session manager if session pool is enabled
|
|
344
|
-
session_manager = None
|
|
345
|
-
if use_session_pool:
|
|
346
|
-
from ccproxy.claude_sdk.manager import SessionManager
|
|
347
|
-
|
|
348
|
-
# Create SessionManager with dependency injection
|
|
349
|
-
session_manager = SessionManager(
|
|
350
|
-
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,
|
|
351
185
|
)
|
|
352
186
|
|
|
353
|
-
# Start the session manager (initializes session pool if enabled)
|
|
354
|
-
await session_manager.start()
|
|
355
|
-
|
|
356
|
-
# Create ClaudeSDKService instance
|
|
357
|
-
claude_service = ClaudeSDKService(
|
|
358
|
-
auth_manager=auth_manager,
|
|
359
|
-
metrics=metrics,
|
|
360
|
-
settings=settings,
|
|
361
|
-
session_manager=session_manager,
|
|
362
|
-
)
|
|
363
187
|
|
|
364
|
-
|
|
365
|
-
app.state.claude_service = claude_service
|
|
366
|
-
app.state.session_manager = (
|
|
367
|
-
session_manager # Store session_manager for shutdown
|
|
368
|
-
)
|
|
369
|
-
logger.debug("claude_sdk_service_initialized")
|
|
370
|
-
except Exception as e:
|
|
371
|
-
logger.error("claude_sdk_service_initialization_failed", error=str(e))
|
|
372
|
-
# Continue startup even if ClaudeSDKService fails (graceful degradation)
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
async def initialize_permission_service_startup(
|
|
188
|
+
async def initialize_service_container_startup(
|
|
376
189
|
app: FastAPI, settings: Settings
|
|
377
190
|
) -> None:
|
|
378
|
-
"""Initialize
|
|
191
|
+
"""Initialize service container and proxy client.
|
|
379
192
|
|
|
380
193
|
Args:
|
|
381
194
|
app: FastAPI application instance
|
|
382
195
|
settings: Application settings
|
|
383
196
|
"""
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
permission_service = get_permission_service()
|
|
389
|
-
|
|
390
|
-
# Only connect terminal handler if not using external handler
|
|
391
|
-
if settings.server.use_terminal_permission_handler:
|
|
392
|
-
# terminal_handler = TerminalPermissionHandler()
|
|
393
|
-
|
|
394
|
-
# TODO: Terminal handler should subscribe to events from the service
|
|
395
|
-
# instead of trying to set a handler directly
|
|
396
|
-
# The service uses an event-based architecture, not direct handlers
|
|
397
|
-
|
|
398
|
-
# logger.info(
|
|
399
|
-
# "permission_handler_configured",
|
|
400
|
-
# handler_type="terminal",
|
|
401
|
-
# message="Connected terminal handler to permission service",
|
|
402
|
-
# )
|
|
403
|
-
# app.state.terminal_handler = terminal_handler
|
|
404
|
-
pass
|
|
405
|
-
else:
|
|
406
|
-
logger.debug(
|
|
407
|
-
"permission_handler_configured",
|
|
408
|
-
handler_type="external_sse",
|
|
409
|
-
message="Terminal permission handler disabled - use 'ccproxy permission-handler connect' to handle permissions",
|
|
410
|
-
)
|
|
411
|
-
logger.warning(
|
|
412
|
-
"permission_handler_required",
|
|
413
|
-
message="Start external handler with: ccproxy permission-handler connect",
|
|
414
|
-
)
|
|
197
|
+
try:
|
|
198
|
+
# Create HTTP client for proxy
|
|
199
|
+
from ccproxy.services.container import ServiceContainer
|
|
415
200
|
|
|
416
|
-
|
|
417
|
-
|
|
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
|
|
418
208
|
|
|
419
|
-
|
|
420
|
-
|
|
209
|
+
# Metrics are now handled by the metrics plugin
|
|
210
|
+
app.state.metrics = None
|
|
421
211
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
builtin_permissions_enabled=True,
|
|
427
|
-
)
|
|
428
|
-
except Exception as e:
|
|
429
|
-
logger.error("permission_service_initialization_failed", error=str(e))
|
|
430
|
-
# Continue without permission service (API will work but without prompts)
|
|
431
|
-
else:
|
|
432
|
-
logger.debug(
|
|
433
|
-
"permission_service_skipped",
|
|
434
|
-
builtin_permissions_enabled=False,
|
|
435
|
-
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
|
|
436
216
|
)
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
async def setup_permission_service_shutdown(app: FastAPI, settings: Settings) -> None:
|
|
440
|
-
"""Stop permission service (if it was initialized).
|
|
441
|
-
|
|
442
|
-
Args:
|
|
443
|
-
app: FastAPI application instance
|
|
444
|
-
settings: Application settings
|
|
445
|
-
"""
|
|
446
|
-
if (
|
|
447
|
-
hasattr(app.state, "permission_service")
|
|
448
|
-
and app.state.permission_service
|
|
449
|
-
and settings.claude.builtin_permissions
|
|
450
|
-
):
|
|
451
|
-
try:
|
|
452
|
-
await app.state.permission_service.stop()
|
|
453
|
-
logger.debug("permission_service_stopped")
|
|
454
|
-
except Exception as e:
|
|
455
|
-
logger.error("permission_service_stop_failed", error=str(e))
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
async def flush_streaming_batches_shutdown(app: FastAPI) -> None:
|
|
459
|
-
"""Flush any remaining streaming log batches.
|
|
460
|
-
|
|
461
|
-
Args:
|
|
462
|
-
app: FastAPI application instance
|
|
463
|
-
"""
|
|
464
|
-
try:
|
|
465
|
-
from ccproxy.utils.simple_request_logger import flush_all_streaming_batches
|
|
466
|
-
|
|
467
|
-
await flush_all_streaming_batches()
|
|
468
|
-
logger.debug("streaming_batches_flushed")
|
|
469
217
|
except Exception as e:
|
|
470
|
-
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)
|