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
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
"""Docker settings configuration for CCProxy API."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
|
|
5
|
-
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
6
|
-
|
|
7
|
-
from ccproxy import __version__
|
|
8
|
-
from ccproxy.core.async_utils import format_version, get_claude_docker_home_dir
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
# Docker validation functions moved here to avoid utils dependency
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def validate_host_path(path: str) -> str:
|
|
15
|
-
"""Validate host path for Docker volume mounting."""
|
|
16
|
-
import os
|
|
17
|
-
from pathlib import Path
|
|
18
|
-
|
|
19
|
-
if not path:
|
|
20
|
-
raise ValueError("Path cannot be empty")
|
|
21
|
-
|
|
22
|
-
# Expand environment variables and user home directory
|
|
23
|
-
expanded_path = os.path.expandvars(str(Path(path).expanduser()))
|
|
24
|
-
|
|
25
|
-
# Convert to absolute path and normalize
|
|
26
|
-
abs_path = Path(expanded_path).resolve()
|
|
27
|
-
return str(abs_path)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def validate_volumes_list(volumes: list[str]) -> list[str]:
|
|
31
|
-
"""Validate Docker volumes list format."""
|
|
32
|
-
validated = []
|
|
33
|
-
|
|
34
|
-
for volume in volumes:
|
|
35
|
-
if not volume:
|
|
36
|
-
continue
|
|
37
|
-
|
|
38
|
-
# Use validate_volume_format for comprehensive validation
|
|
39
|
-
validated_volume = validate_volume_format(volume)
|
|
40
|
-
validated.append(validated_volume)
|
|
41
|
-
|
|
42
|
-
return validated
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def validate_volume_format(volume: str) -> str:
|
|
46
|
-
"""Validate individual Docker volume format.
|
|
47
|
-
|
|
48
|
-
Args:
|
|
49
|
-
volume: Volume mount string in format 'host:container[:options]'
|
|
50
|
-
|
|
51
|
-
Returns:
|
|
52
|
-
Validated volume string with normalized host path
|
|
53
|
-
|
|
54
|
-
Raises:
|
|
55
|
-
ValueError: If volume format is invalid or host path doesn't exist
|
|
56
|
-
"""
|
|
57
|
-
import os
|
|
58
|
-
from pathlib import Path
|
|
59
|
-
|
|
60
|
-
if not volume:
|
|
61
|
-
raise ValueError("Volume cannot be empty")
|
|
62
|
-
|
|
63
|
-
# Expected format: "host_path:container_path" or "host_path:container_path:options"
|
|
64
|
-
parts = volume.split(":")
|
|
65
|
-
if len(parts) < 2:
|
|
66
|
-
raise ValueError(
|
|
67
|
-
f"Invalid volume format: {volume}. Expected 'host:container' or 'host:container:options'"
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
host_path = parts[0]
|
|
71
|
-
container_path = parts[1]
|
|
72
|
-
options = ":".join(parts[2:]) if len(parts) > 2 else ""
|
|
73
|
-
|
|
74
|
-
if not host_path or not container_path:
|
|
75
|
-
raise ValueError(
|
|
76
|
-
f"Invalid volume format: {volume}. Expected 'host:container' or 'host:container:options'"
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
# Expand environment variables and user home directory
|
|
80
|
-
expanded_host_path = os.path.expandvars(str(Path(host_path).expanduser()))
|
|
81
|
-
|
|
82
|
-
# Convert to absolute path
|
|
83
|
-
abs_host_path = Path(expanded_host_path).resolve()
|
|
84
|
-
|
|
85
|
-
# Check if the path exists
|
|
86
|
-
if not abs_host_path.exists():
|
|
87
|
-
raise ValueError(f"Host path does not exist: {expanded_host_path}")
|
|
88
|
-
|
|
89
|
-
# Validate container path (should be absolute)
|
|
90
|
-
if not container_path.startswith("/"):
|
|
91
|
-
raise ValueError(f"Container path must be absolute: {container_path}")
|
|
92
|
-
|
|
93
|
-
# Reconstruct the volume string with normalized host path
|
|
94
|
-
result = f"{abs_host_path}:{container_path}"
|
|
95
|
-
if options:
|
|
96
|
-
result += f":{options}"
|
|
97
|
-
|
|
98
|
-
return result
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
def validate_environment_variable(env_var: str) -> tuple[str, str]:
|
|
102
|
-
"""Validate environment variable format.
|
|
103
|
-
|
|
104
|
-
Args:
|
|
105
|
-
env_var: Environment variable string in format 'KEY=VALUE'
|
|
106
|
-
|
|
107
|
-
Returns:
|
|
108
|
-
Tuple of (key, value)
|
|
109
|
-
|
|
110
|
-
Raises:
|
|
111
|
-
ValueError: If environment variable format is invalid
|
|
112
|
-
"""
|
|
113
|
-
if not env_var:
|
|
114
|
-
raise ValueError("Environment variable cannot be empty")
|
|
115
|
-
|
|
116
|
-
if "=" not in env_var:
|
|
117
|
-
raise ValueError(
|
|
118
|
-
f"Invalid environment variable format: {env_var}. Expected KEY=VALUE format"
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
# Split on first equals sign only (value may contain equals)
|
|
122
|
-
key, value = env_var.split("=", 1)
|
|
123
|
-
|
|
124
|
-
if not key:
|
|
125
|
-
raise ValueError(
|
|
126
|
-
f"Invalid environment variable format: {env_var}. Expected KEY=VALUE format"
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
return key, value
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
def validate_docker_volumes(volumes: list[str]) -> list[str]:
|
|
133
|
-
"""Validate Docker volumes list format.
|
|
134
|
-
|
|
135
|
-
Args:
|
|
136
|
-
volumes: List of volume mount strings
|
|
137
|
-
|
|
138
|
-
Returns:
|
|
139
|
-
List of validated volume strings with normalized host paths
|
|
140
|
-
|
|
141
|
-
Raises:
|
|
142
|
-
ValueError: If any volume format is invalid
|
|
143
|
-
"""
|
|
144
|
-
validated = []
|
|
145
|
-
|
|
146
|
-
for volume in volumes:
|
|
147
|
-
if not volume:
|
|
148
|
-
continue
|
|
149
|
-
|
|
150
|
-
validated_volume = validate_volume_format(volume)
|
|
151
|
-
validated.append(validated_volume)
|
|
152
|
-
|
|
153
|
-
return validated
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
class DockerSettings(BaseModel):
|
|
157
|
-
"""Docker configuration settings for running Claude commands in containers."""
|
|
158
|
-
|
|
159
|
-
docker_image: str = Field(
|
|
160
|
-
default=f"ghcr.io/caddyglow/ccproxy-api:{format_version(__version__, level='docker')}",
|
|
161
|
-
description="Docker image to use for Claude commands",
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
docker_volumes: list[str] = Field(
|
|
165
|
-
default_factory=list,
|
|
166
|
-
description="List of volume mounts in 'host:container[:options]' format",
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
docker_environment: dict[str, str] = Field(
|
|
170
|
-
default_factory=dict,
|
|
171
|
-
description="Environment variables to pass to Docker container",
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
docker_additional_args: list[str] = Field(
|
|
175
|
-
default_factory=list,
|
|
176
|
-
description="Additional arguments to pass to docker run command",
|
|
177
|
-
)
|
|
178
|
-
|
|
179
|
-
docker_home_directory: str | None = Field(
|
|
180
|
-
default=None,
|
|
181
|
-
description="Local host directory to mount as the home directory in container",
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
docker_workspace_directory: str | None = Field(
|
|
185
|
-
default=None,
|
|
186
|
-
description="Local host directory to mount as the workspace directory in container",
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
user_mapping_enabled: bool = Field(
|
|
190
|
-
default=True,
|
|
191
|
-
description="Enable/disable UID/GID mapping for container user",
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
user_uid: int | None = Field(
|
|
195
|
-
default=None,
|
|
196
|
-
description="User ID to run container as (auto-detect current user if None)",
|
|
197
|
-
ge=0,
|
|
198
|
-
)
|
|
199
|
-
|
|
200
|
-
user_gid: int | None = Field(
|
|
201
|
-
default=None,
|
|
202
|
-
description="Group ID to run container as (auto-detect current user if None)",
|
|
203
|
-
ge=0,
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
@field_validator("docker_volumes")
|
|
207
|
-
@classmethod
|
|
208
|
-
def validate_docker_volumes(cls, v: list[str]) -> list[str]:
|
|
209
|
-
"""Validate Docker volume mount format."""
|
|
210
|
-
return validate_volumes_list(v)
|
|
211
|
-
|
|
212
|
-
@field_validator("docker_home_directory")
|
|
213
|
-
@classmethod
|
|
214
|
-
def validate_docker_home_directory(cls, v: str | None) -> str | None:
|
|
215
|
-
"""Validate and normalize Docker home directory (host path)."""
|
|
216
|
-
if v is None:
|
|
217
|
-
return None
|
|
218
|
-
return validate_host_path(v)
|
|
219
|
-
|
|
220
|
-
@field_validator("docker_workspace_directory")
|
|
221
|
-
@classmethod
|
|
222
|
-
def validate_docker_workspace_directory(cls, v: str | None) -> str | None:
|
|
223
|
-
"""Validate and normalize Docker workspace directory (host path)."""
|
|
224
|
-
if v is None:
|
|
225
|
-
return None
|
|
226
|
-
return validate_host_path(v)
|
|
227
|
-
|
|
228
|
-
@model_validator(mode="after")
|
|
229
|
-
def setup_docker_configuration(self) -> "DockerSettings":
|
|
230
|
-
"""Set up Docker volumes and user mapping configuration."""
|
|
231
|
-
# Set up Docker volumes based on home and workspace directories
|
|
232
|
-
if (
|
|
233
|
-
not self.docker_volumes
|
|
234
|
-
and not self.docker_home_directory
|
|
235
|
-
and not self.docker_workspace_directory
|
|
236
|
-
):
|
|
237
|
-
# Use XDG config directory for Claude CLI data
|
|
238
|
-
claude_config_dir = get_claude_docker_home_dir()
|
|
239
|
-
home_host_path = str(claude_config_dir)
|
|
240
|
-
workspace_host_path = os.path.expandvars("$PWD")
|
|
241
|
-
|
|
242
|
-
self.docker_volumes = [
|
|
243
|
-
f"{home_host_path}:/data/home",
|
|
244
|
-
f"{workspace_host_path}:/data/workspace",
|
|
245
|
-
]
|
|
246
|
-
|
|
247
|
-
# Update environment variables to point to container paths
|
|
248
|
-
if "CLAUDE_HOME" not in self.docker_environment:
|
|
249
|
-
self.docker_environment["CLAUDE_HOME"] = "/data/home"
|
|
250
|
-
if "CLAUDE_WORKSPACE" not in self.docker_environment:
|
|
251
|
-
self.docker_environment["CLAUDE_WORKSPACE"] = "/data/workspace"
|
|
252
|
-
|
|
253
|
-
# Set up user mapping with auto-detection if enabled but not configured
|
|
254
|
-
if self.user_mapping_enabled and os.name == "posix":
|
|
255
|
-
# Auto-detect current user UID/GID if not explicitly set
|
|
256
|
-
if self.user_uid is None:
|
|
257
|
-
self.user_uid = os.getuid()
|
|
258
|
-
if self.user_gid is None:
|
|
259
|
-
self.user_gid = os.getgid()
|
|
260
|
-
elif self.user_mapping_enabled and os.name != "posix":
|
|
261
|
-
# Disable user mapping on non-Unix systems (Windows)
|
|
262
|
-
self.user_mapping_enabled = False
|
|
263
|
-
|
|
264
|
-
return self
|
ccproxy/config/observability.py
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
"""Observability configuration settings."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import os
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from typing import Literal
|
|
8
|
-
|
|
9
|
-
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class ObservabilitySettings(BaseModel):
|
|
13
|
-
"""Observability configuration settings."""
|
|
14
|
-
|
|
15
|
-
# Endpoint Controls
|
|
16
|
-
metrics_endpoint_enabled: bool = Field(
|
|
17
|
-
default=False,
|
|
18
|
-
description="Enable Prometheus /metrics endpoint",
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
logs_endpoints_enabled: bool = Field(
|
|
22
|
-
default=False,
|
|
23
|
-
description="Enable logs query/analytics/streaming endpoints (/logs/*)",
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
dashboard_enabled: bool = Field(
|
|
27
|
-
default=False,
|
|
28
|
-
description="Enable metrics dashboard endpoint (/dashboard)",
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
# Data Collection & Storage
|
|
32
|
-
logs_collection_enabled: bool = Field(
|
|
33
|
-
default=False,
|
|
34
|
-
description="Enable collection of request/response logs to storage backend",
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
log_storage_backend: Literal["duckdb", "none"] = Field(
|
|
38
|
-
default="duckdb",
|
|
39
|
-
description="Storage backend for logs ('duckdb' or 'none')",
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
# Storage Configuration
|
|
43
|
-
duckdb_path: str = Field(
|
|
44
|
-
default_factory=lambda: str(
|
|
45
|
-
Path(os.environ.get("XDG_DATA_HOME", Path.home() / ".local" / "share"))
|
|
46
|
-
/ "ccproxy"
|
|
47
|
-
/ "metrics.duckdb"
|
|
48
|
-
),
|
|
49
|
-
description="Path to DuckDB database file",
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
# Pushgateway Configuration
|
|
53
|
-
pushgateway_url: str | None = Field(
|
|
54
|
-
default=None,
|
|
55
|
-
description="Pushgateway URL (e.g., http://pushgateway:9091)",
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
pushgateway_job: str = Field(
|
|
59
|
-
default="ccproxy",
|
|
60
|
-
description="Job name for Pushgateway metrics",
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
# Stats printing configuration
|
|
64
|
-
stats_printing_format: str = Field(
|
|
65
|
-
default="console",
|
|
66
|
-
description="Format for stats output: 'console', 'rich', 'log', 'json'",
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
# Enhanced logging integration
|
|
70
|
-
logging_pipeline_enabled: bool = Field(
|
|
71
|
-
default=True,
|
|
72
|
-
description="Enable structlog pipeline integration for observability",
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
logging_format: str = Field(
|
|
76
|
-
default="auto",
|
|
77
|
-
description="Logging format for observability: 'rich', 'json', 'auto' (auto-detects based on environment)",
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
@model_validator(mode="after")
|
|
81
|
-
def check_feature_dependencies(self) -> ObservabilitySettings:
|
|
82
|
-
"""Validate feature dependencies to prevent invalid configurations."""
|
|
83
|
-
# Dashboard requires logs endpoints (functional dependency)
|
|
84
|
-
if self.dashboard_enabled and not self.logs_endpoints_enabled:
|
|
85
|
-
raise ValueError(
|
|
86
|
-
"Cannot enable 'dashboard_enabled' without 'logs_endpoints_enabled'. "
|
|
87
|
-
"Dashboard needs logs API to function."
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
# Logs endpoints require storage to query from
|
|
91
|
-
if self.logs_endpoints_enabled and self.log_storage_backend == "none":
|
|
92
|
-
raise ValueError(
|
|
93
|
-
"Cannot enable 'logs_endpoints_enabled' when 'log_storage_backend' is 'none'. "
|
|
94
|
-
"Logs endpoints need storage backend to query data."
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
# Log collection requires storage to write to
|
|
98
|
-
if self.logs_collection_enabled and self.log_storage_backend == "none":
|
|
99
|
-
raise ValueError(
|
|
100
|
-
"Cannot enable 'logs_collection_enabled' when 'log_storage_backend' is 'none'. "
|
|
101
|
-
"Collection needs storage backend to persist data."
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
return self
|
|
105
|
-
|
|
106
|
-
@field_validator("stats_printing_format")
|
|
107
|
-
@classmethod
|
|
108
|
-
def validate_stats_printing_format(cls, v: str) -> str:
|
|
109
|
-
"""Validate and normalize stats printing format."""
|
|
110
|
-
lower_v = v.lower()
|
|
111
|
-
valid_formats = ["console", "rich", "log", "json"]
|
|
112
|
-
if lower_v not in valid_formats:
|
|
113
|
-
raise ValueError(
|
|
114
|
-
f"Invalid stats printing format: {v}. Must be one of {valid_formats}"
|
|
115
|
-
)
|
|
116
|
-
return lower_v
|
|
117
|
-
|
|
118
|
-
@field_validator("logging_format")
|
|
119
|
-
@classmethod
|
|
120
|
-
def validate_logging_format(cls, v: str) -> str:
|
|
121
|
-
"""Validate and normalize logging format."""
|
|
122
|
-
lower_v = v.lower()
|
|
123
|
-
valid_formats = ["auto", "rich", "json", "plain"]
|
|
124
|
-
if lower_v not in valid_formats:
|
|
125
|
-
raise ValueError(
|
|
126
|
-
f"Invalid logging format: {v}. Must be one of {valid_formats}"
|
|
127
|
-
)
|
|
128
|
-
return lower_v
|
|
129
|
-
|
|
130
|
-
@property
|
|
131
|
-
def needs_storage_backend(self) -> bool:
|
|
132
|
-
"""Check if any feature requires storage backend initialization."""
|
|
133
|
-
return self.logs_endpoints_enabled or self.logs_collection_enabled
|
|
134
|
-
|
|
135
|
-
@property
|
|
136
|
-
def any_endpoint_enabled(self) -> bool:
|
|
137
|
-
"""Check if any observability endpoint is enabled."""
|
|
138
|
-
return (
|
|
139
|
-
self.metrics_endpoint_enabled
|
|
140
|
-
or self.logs_endpoints_enabled
|
|
141
|
-
or self.dashboard_enabled
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
# Backward compatibility properties
|
|
145
|
-
@property
|
|
146
|
-
def metrics_enabled(self) -> bool:
|
|
147
|
-
"""Backward compatibility: True if any metrics feature is enabled."""
|
|
148
|
-
return self.any_endpoint_enabled
|
|
149
|
-
|
|
150
|
-
@property
|
|
151
|
-
def duckdb_enabled(self) -> bool:
|
|
152
|
-
"""Backward compatibility: True if DuckDB storage backend is selected."""
|
|
153
|
-
return self.log_storage_backend == "duckdb"
|
|
154
|
-
|
|
155
|
-
@property
|
|
156
|
-
def enabled(self) -> bool:
|
|
157
|
-
"""Check if observability is enabled (backward compatibility property)."""
|
|
158
|
-
return self.any_endpoint_enabled or self.logging_pipeline_enabled
|
ccproxy/config/reverse_proxy.py
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
"""Reverse proxy configuration settings."""
|
|
2
|
-
|
|
3
|
-
from typing import Literal
|
|
4
|
-
|
|
5
|
-
from pydantic import BaseModel, Field
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class ReverseProxySettings(BaseModel):
|
|
9
|
-
"""Reverse proxy configuration settings."""
|
|
10
|
-
|
|
11
|
-
target_url: str = Field(
|
|
12
|
-
default="https://api.anthropic.com",
|
|
13
|
-
description="Target URL for reverse proxy requests",
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
timeout: float = Field(
|
|
17
|
-
default=120.0,
|
|
18
|
-
description="Timeout for reverse proxy requests in seconds",
|
|
19
|
-
ge=1.0,
|
|
20
|
-
le=600.0,
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
default_mode: Literal["claude_code", "full", "minimal"] = Field(
|
|
24
|
-
default="claude_code",
|
|
25
|
-
description="Default transformation mode for root path reverse proxy, over claude code or auth injection with full",
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
claude_code_prefix: str = Field(
|
|
29
|
-
default="/cc",
|
|
30
|
-
description="URL prefix for Claude Code SDK endpoints",
|
|
31
|
-
)
|
ccproxy/config/scheduler.py
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
"""Scheduler configuration settings."""
|
|
2
|
-
|
|
3
|
-
from pydantic import Field
|
|
4
|
-
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class SchedulerSettings(BaseSettings):
|
|
8
|
-
"""
|
|
9
|
-
Configuration settings for the unified scheduler system.
|
|
10
|
-
|
|
11
|
-
Controls global scheduler behavior and individual task configurations.
|
|
12
|
-
Settings can be configured via environment variables with SCHEDULER__ prefix.
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
# Global scheduler settings
|
|
16
|
-
enabled: bool = Field(
|
|
17
|
-
default=True,
|
|
18
|
-
description="Whether the scheduler system is enabled",
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
max_concurrent_tasks: int = Field(
|
|
22
|
-
default=10,
|
|
23
|
-
ge=1,
|
|
24
|
-
le=100,
|
|
25
|
-
description="Maximum number of tasks that can run concurrently",
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
graceful_shutdown_timeout: float = Field(
|
|
29
|
-
default=30.0,
|
|
30
|
-
ge=1.0,
|
|
31
|
-
le=300.0,
|
|
32
|
-
description="Timeout in seconds for graceful task shutdown",
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
# Pricing updater task settings
|
|
36
|
-
pricing_update_enabled: bool = Field(
|
|
37
|
-
default=True,
|
|
38
|
-
description="Whether pricing cache update task is enabled. Enabled by default for privacy - downloads from GitHub when enabled",
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
pricing_update_interval_hours: int = Field(
|
|
42
|
-
default=24,
|
|
43
|
-
ge=1,
|
|
44
|
-
le=168, # Max 1 week
|
|
45
|
-
description="Interval in hours between pricing cache updates",
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
pricing_force_refresh_on_startup: bool = Field(
|
|
49
|
-
default=False,
|
|
50
|
-
description="Whether to force pricing refresh immediately on startup",
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
# Observability tasks (migrated from ObservabilitySettings)
|
|
54
|
-
pushgateway_enabled: bool = Field(
|
|
55
|
-
default=False,
|
|
56
|
-
description="Whether pushgateway metrics pushing task is enabled",
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
pushgateway_interval_seconds: float = Field(
|
|
60
|
-
default=60.0,
|
|
61
|
-
ge=1.0,
|
|
62
|
-
le=3600.0, # Max 1 hour
|
|
63
|
-
description="Interval in seconds between pushgateway metric pushes",
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
pushgateway_max_backoff_seconds: float = Field(
|
|
67
|
-
default=300.0,
|
|
68
|
-
ge=1.0,
|
|
69
|
-
le=1800.0, # Max 30 minutes
|
|
70
|
-
description="Maximum backoff delay for failed pushgateway operations",
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
stats_printing_enabled: bool = Field(
|
|
74
|
-
default=False,
|
|
75
|
-
description="Whether stats printing task is enabled",
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
stats_printing_interval_seconds: float = Field(
|
|
79
|
-
default=300.0,
|
|
80
|
-
ge=1.0,
|
|
81
|
-
le=3600.0, # Max 1 hour
|
|
82
|
-
description="Interval in seconds between stats printing",
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
# Version checking task settings
|
|
86
|
-
version_check_enabled: bool = Field(
|
|
87
|
-
default=True,
|
|
88
|
-
description="Whether version update checking is enabled. Enabled by default for privacy - checks GitHub API when enabled",
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
version_check_interval_hours: int = Field(
|
|
92
|
-
default=6,
|
|
93
|
-
ge=1,
|
|
94
|
-
le=168, # Max 1 week
|
|
95
|
-
description="Interval in hours between version checks",
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
version_check_cache_ttl_hours: float = Field(
|
|
99
|
-
default=6,
|
|
100
|
-
ge=0.1,
|
|
101
|
-
le=24.0,
|
|
102
|
-
description="Maximum age in hours since last check version check",
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
model_config = SettingsConfigDict(
|
|
106
|
-
env_prefix="SCHEDULER__",
|
|
107
|
-
case_sensitive=False,
|
|
108
|
-
)
|
ccproxy/config/server.py
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
"""Server configuration settings."""
|
|
2
|
-
|
|
3
|
-
from pydantic import BaseModel, Field, field_validator
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class ServerSettings(BaseModel):
|
|
7
|
-
"""Server-specific configuration settings."""
|
|
8
|
-
|
|
9
|
-
host: str = Field(
|
|
10
|
-
default="127.0.0.1",
|
|
11
|
-
description="Server host address",
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
port: int = Field(
|
|
15
|
-
default=8000,
|
|
16
|
-
description="Server port number",
|
|
17
|
-
ge=1,
|
|
18
|
-
le=65535,
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
workers: int = Field(
|
|
22
|
-
default=1,
|
|
23
|
-
description="Number of worker processes",
|
|
24
|
-
ge=1,
|
|
25
|
-
le=32,
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
reload: bool = Field(
|
|
29
|
-
default=False,
|
|
30
|
-
description="Enable auto-reload for development",
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
log_level: str = Field(
|
|
34
|
-
default="INFO",
|
|
35
|
-
description="Logging level",
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
log_format: str = Field(
|
|
39
|
-
default="auto",
|
|
40
|
-
description="Logging output format: 'rich' for development, 'json' for production, 'auto' for automatic selection",
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
log_show_path: bool = Field(
|
|
44
|
-
default=False,
|
|
45
|
-
description="Whether to show module path in logs (automatically enabled for DEBUG level)",
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
log_show_time: bool = Field(
|
|
49
|
-
default=True,
|
|
50
|
-
description="Whether to show timestamps in logs",
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
log_console_width: int | None = Field(
|
|
54
|
-
default=None,
|
|
55
|
-
description="Optional console width override for Rich output",
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
log_file: str | None = Field(
|
|
59
|
-
default=None,
|
|
60
|
-
description="Path to JSON log file. If specified, logs will be written to this file in JSON format",
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
use_terminal_permission_handler: bool = Field(
|
|
64
|
-
default=False,
|
|
65
|
-
description="Enable terminal UI for permission prompts. Set to False to use external handler via SSE (not implemented)",
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
@field_validator("log_level")
|
|
69
|
-
@classmethod
|
|
70
|
-
def validate_log_level(cls, v: str) -> str:
|
|
71
|
-
"""Validate and normalize log level."""
|
|
72
|
-
upper_v = v.upper()
|
|
73
|
-
valid_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
|
74
|
-
if upper_v not in valid_levels:
|
|
75
|
-
raise ValueError(f"Invalid log level: {v}. Must be one of {valid_levels}")
|
|
76
|
-
return upper_v
|
|
77
|
-
|
|
78
|
-
@field_validator("log_format")
|
|
79
|
-
@classmethod
|
|
80
|
-
def validate_log_format(cls, v: str) -> str:
|
|
81
|
-
"""Validate and normalize log format."""
|
|
82
|
-
lower_v = v.lower()
|
|
83
|
-
valid_formats = ["auto", "rich", "json", "plain"]
|
|
84
|
-
if lower_v not in valid_formats:
|
|
85
|
-
raise ValueError(f"Invalid log format: {v}. Must be one of {valid_formats}")
|
|
86
|
-
return lower_v
|