ccproxy-api 0.1.7__py3-none-any.whl → 0.2.0a4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ccproxy/api/__init__.py +1 -15
- ccproxy/api/app.py +434 -219
- ccproxy/api/bootstrap.py +30 -0
- ccproxy/api/decorators.py +85 -0
- ccproxy/api/dependencies.py +144 -168
- ccproxy/api/format_validation.py +54 -0
- ccproxy/api/middleware/cors.py +6 -3
- ccproxy/api/middleware/errors.py +388 -524
- ccproxy/api/middleware/hooks.py +563 -0
- ccproxy/api/middleware/normalize_headers.py +59 -0
- ccproxy/api/middleware/request_id.py +35 -16
- ccproxy/api/middleware/streaming_hooks.py +292 -0
- ccproxy/api/routes/__init__.py +5 -14
- ccproxy/api/routes/health.py +39 -672
- ccproxy/api/routes/plugins.py +277 -0
- ccproxy/auth/__init__.py +2 -19
- ccproxy/auth/bearer.py +25 -15
- ccproxy/auth/dependencies.py +123 -157
- ccproxy/auth/exceptions.py +0 -12
- ccproxy/auth/manager.py +35 -49
- ccproxy/auth/managers/__init__.py +10 -0
- ccproxy/auth/managers/base.py +523 -0
- ccproxy/auth/managers/base_enhanced.py +63 -0
- ccproxy/auth/managers/token_snapshot.py +77 -0
- ccproxy/auth/models/base.py +65 -0
- ccproxy/auth/models/credentials.py +40 -0
- ccproxy/auth/oauth/__init__.py +4 -18
- ccproxy/auth/oauth/base.py +533 -0
- ccproxy/auth/oauth/cli_errors.py +37 -0
- ccproxy/auth/oauth/flows.py +430 -0
- ccproxy/auth/oauth/protocol.py +366 -0
- ccproxy/auth/oauth/registry.py +408 -0
- ccproxy/auth/oauth/router.py +396 -0
- ccproxy/auth/oauth/routes.py +186 -113
- ccproxy/auth/oauth/session.py +151 -0
- ccproxy/auth/oauth/templates.py +342 -0
- ccproxy/auth/storage/__init__.py +2 -5
- ccproxy/auth/storage/base.py +279 -5
- ccproxy/auth/storage/generic.py +134 -0
- ccproxy/cli/__init__.py +1 -2
- ccproxy/cli/_settings_help.py +351 -0
- ccproxy/cli/commands/auth.py +1519 -793
- ccproxy/cli/commands/config/commands.py +209 -276
- ccproxy/cli/commands/plugins.py +669 -0
- ccproxy/cli/commands/serve.py +75 -810
- ccproxy/cli/commands/status.py +254 -0
- ccproxy/cli/decorators.py +83 -0
- ccproxy/cli/helpers.py +22 -60
- ccproxy/cli/main.py +359 -10
- ccproxy/cli/options/claude_options.py +0 -25
- ccproxy/config/__init__.py +7 -11
- ccproxy/config/core.py +227 -0
- ccproxy/config/env_generator.py +232 -0
- ccproxy/config/runtime.py +67 -0
- ccproxy/config/security.py +36 -3
- ccproxy/config/settings.py +382 -441
- ccproxy/config/toml_generator.py +299 -0
- ccproxy/config/utils.py +452 -0
- ccproxy/core/__init__.py +7 -271
- ccproxy/{_version.py → core/_version.py} +16 -3
- ccproxy/core/async_task_manager.py +516 -0
- ccproxy/core/async_utils.py +47 -14
- ccproxy/core/auth/__init__.py +6 -0
- ccproxy/core/constants.py +16 -50
- ccproxy/core/errors.py +53 -0
- ccproxy/core/id_utils.py +20 -0
- ccproxy/core/interfaces.py +16 -123
- ccproxy/core/logging.py +473 -18
- ccproxy/core/plugins/__init__.py +77 -0
- ccproxy/core/plugins/cli_discovery.py +211 -0
- ccproxy/core/plugins/declaration.py +455 -0
- ccproxy/core/plugins/discovery.py +604 -0
- ccproxy/core/plugins/factories.py +967 -0
- ccproxy/core/plugins/hooks/__init__.py +30 -0
- ccproxy/core/plugins/hooks/base.py +58 -0
- ccproxy/core/plugins/hooks/events.py +46 -0
- ccproxy/core/plugins/hooks/implementations/__init__.py +16 -0
- ccproxy/core/plugins/hooks/implementations/formatters/__init__.py +11 -0
- ccproxy/core/plugins/hooks/implementations/formatters/json.py +552 -0
- ccproxy/core/plugins/hooks/implementations/formatters/raw.py +370 -0
- ccproxy/core/plugins/hooks/implementations/http_tracer.py +431 -0
- ccproxy/core/plugins/hooks/layers.py +44 -0
- ccproxy/core/plugins/hooks/manager.py +186 -0
- ccproxy/core/plugins/hooks/registry.py +139 -0
- ccproxy/core/plugins/hooks/thread_manager.py +203 -0
- ccproxy/core/plugins/hooks/types.py +22 -0
- ccproxy/core/plugins/interfaces.py +416 -0
- ccproxy/core/plugins/loader.py +166 -0
- ccproxy/core/plugins/middleware.py +233 -0
- ccproxy/core/plugins/models.py +59 -0
- ccproxy/core/plugins/protocol.py +180 -0
- ccproxy/core/plugins/runtime.py +519 -0
- ccproxy/{observability/context.py → core/request_context.py} +137 -94
- ccproxy/core/status_report.py +211 -0
- ccproxy/core/transformers.py +13 -8
- ccproxy/data/claude_headers_fallback.json +540 -19
- ccproxy/data/codex_headers_fallback.json +114 -7
- ccproxy/http/__init__.py +30 -0
- ccproxy/http/base.py +95 -0
- ccproxy/http/client.py +323 -0
- ccproxy/http/hooks.py +642 -0
- ccproxy/http/pool.py +279 -0
- ccproxy/llms/formatters/__init__.py +7 -0
- ccproxy/llms/formatters/anthropic_to_openai/__init__.py +55 -0
- ccproxy/llms/formatters/anthropic_to_openai/errors.py +65 -0
- ccproxy/llms/formatters/anthropic_to_openai/requests.py +356 -0
- ccproxy/llms/formatters/anthropic_to_openai/responses.py +153 -0
- ccproxy/llms/formatters/anthropic_to_openai/streams.py +1546 -0
- ccproxy/llms/formatters/base.py +140 -0
- ccproxy/llms/formatters/base_model.py +33 -0
- ccproxy/llms/formatters/common/__init__.py +51 -0
- ccproxy/llms/formatters/common/identifiers.py +48 -0
- ccproxy/llms/formatters/common/streams.py +254 -0
- ccproxy/llms/formatters/common/thinking.py +74 -0
- ccproxy/llms/formatters/common/usage.py +135 -0
- ccproxy/llms/formatters/constants.py +55 -0
- ccproxy/llms/formatters/context.py +116 -0
- ccproxy/llms/formatters/mapping.py +33 -0
- ccproxy/llms/formatters/openai_to_anthropic/__init__.py +55 -0
- ccproxy/llms/formatters/openai_to_anthropic/_helpers.py +141 -0
- ccproxy/llms/formatters/openai_to_anthropic/errors.py +53 -0
- ccproxy/llms/formatters/openai_to_anthropic/requests.py +674 -0
- ccproxy/llms/formatters/openai_to_anthropic/responses.py +285 -0
- ccproxy/llms/formatters/openai_to_anthropic/streams.py +530 -0
- ccproxy/llms/formatters/openai_to_openai/__init__.py +53 -0
- ccproxy/llms/formatters/openai_to_openai/_helpers.py +325 -0
- ccproxy/llms/formatters/openai_to_openai/errors.py +6 -0
- ccproxy/llms/formatters/openai_to_openai/requests.py +388 -0
- ccproxy/llms/formatters/openai_to_openai/responses.py +594 -0
- ccproxy/llms/formatters/openai_to_openai/streams.py +1832 -0
- ccproxy/llms/formatters/utils.py +306 -0
- ccproxy/llms/models/__init__.py +9 -0
- ccproxy/llms/models/anthropic.py +619 -0
- ccproxy/llms/models/openai.py +844 -0
- ccproxy/llms/streaming/__init__.py +26 -0
- ccproxy/llms/streaming/accumulators.py +1074 -0
- ccproxy/llms/streaming/formatters.py +251 -0
- ccproxy/{adapters/openai/streaming.py → llms/streaming/processors.py} +193 -240
- ccproxy/models/__init__.py +8 -159
- ccproxy/models/detection.py +92 -193
- ccproxy/models/provider.py +75 -0
- ccproxy/plugins/access_log/README.md +32 -0
- ccproxy/plugins/access_log/__init__.py +20 -0
- ccproxy/plugins/access_log/config.py +33 -0
- ccproxy/plugins/access_log/formatter.py +126 -0
- ccproxy/plugins/access_log/hook.py +763 -0
- ccproxy/plugins/access_log/logger.py +254 -0
- ccproxy/plugins/access_log/plugin.py +137 -0
- ccproxy/plugins/access_log/writer.py +109 -0
- ccproxy/plugins/analytics/README.md +24 -0
- ccproxy/plugins/analytics/__init__.py +1 -0
- ccproxy/plugins/analytics/config.py +5 -0
- ccproxy/plugins/analytics/ingest.py +85 -0
- ccproxy/plugins/analytics/models.py +97 -0
- ccproxy/plugins/analytics/plugin.py +121 -0
- ccproxy/plugins/analytics/routes.py +163 -0
- ccproxy/plugins/analytics/service.py +284 -0
- ccproxy/plugins/claude_api/README.md +29 -0
- ccproxy/plugins/claude_api/__init__.py +10 -0
- ccproxy/plugins/claude_api/adapter.py +829 -0
- ccproxy/plugins/claude_api/config.py +52 -0
- ccproxy/plugins/claude_api/detection_service.py +461 -0
- ccproxy/plugins/claude_api/health.py +175 -0
- ccproxy/plugins/claude_api/hooks.py +284 -0
- ccproxy/plugins/claude_api/models.py +256 -0
- ccproxy/plugins/claude_api/plugin.py +298 -0
- ccproxy/plugins/claude_api/routes.py +118 -0
- ccproxy/plugins/claude_api/streaming_metrics.py +68 -0
- ccproxy/plugins/claude_api/tasks.py +84 -0
- ccproxy/plugins/claude_sdk/README.md +35 -0
- ccproxy/plugins/claude_sdk/__init__.py +80 -0
- ccproxy/plugins/claude_sdk/adapter.py +749 -0
- ccproxy/plugins/claude_sdk/auth.py +57 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/client.py +63 -39
- ccproxy/plugins/claude_sdk/config.py +210 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/converter.py +6 -6
- ccproxy/plugins/claude_sdk/detection_service.py +163 -0
- ccproxy/{services/claude_sdk_service.py → plugins/claude_sdk/handler.py} +123 -304
- ccproxy/plugins/claude_sdk/health.py +113 -0
- ccproxy/plugins/claude_sdk/hooks.py +115 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/manager.py +42 -32
- ccproxy/{claude_sdk → plugins/claude_sdk}/message_queue.py +8 -8
- ccproxy/{models/claude_sdk.py → plugins/claude_sdk/models.py} +64 -16
- ccproxy/plugins/claude_sdk/options.py +154 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/parser.py +23 -5
- ccproxy/plugins/claude_sdk/plugin.py +269 -0
- ccproxy/plugins/claude_sdk/routes.py +104 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/session_client.py +124 -12
- ccproxy/plugins/claude_sdk/session_pool.py +700 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/stream_handle.py +48 -43
- ccproxy/{claude_sdk → plugins/claude_sdk}/stream_worker.py +22 -18
- ccproxy/{claude_sdk → plugins/claude_sdk}/streaming.py +50 -16
- ccproxy/plugins/claude_sdk/tasks.py +97 -0
- ccproxy/plugins/claude_shared/README.md +18 -0
- ccproxy/plugins/claude_shared/__init__.py +12 -0
- ccproxy/plugins/claude_shared/model_defaults.py +171 -0
- ccproxy/plugins/codex/README.md +35 -0
- ccproxy/plugins/codex/__init__.py +6 -0
- ccproxy/plugins/codex/adapter.py +635 -0
- ccproxy/{config/codex.py → plugins/codex/config.py} +78 -12
- ccproxy/plugins/codex/detection_service.py +544 -0
- ccproxy/plugins/codex/health.py +162 -0
- ccproxy/plugins/codex/hooks.py +263 -0
- ccproxy/plugins/codex/model_defaults.py +39 -0
- ccproxy/plugins/codex/models.py +263 -0
- ccproxy/plugins/codex/plugin.py +275 -0
- ccproxy/plugins/codex/routes.py +129 -0
- ccproxy/plugins/codex/streaming_metrics.py +324 -0
- ccproxy/plugins/codex/tasks.py +106 -0
- ccproxy/plugins/codex/utils/__init__.py +1 -0
- ccproxy/plugins/codex/utils/sse_parser.py +106 -0
- ccproxy/plugins/command_replay/README.md +34 -0
- ccproxy/plugins/command_replay/__init__.py +17 -0
- ccproxy/plugins/command_replay/config.py +133 -0
- ccproxy/plugins/command_replay/formatter.py +432 -0
- ccproxy/plugins/command_replay/hook.py +294 -0
- ccproxy/plugins/command_replay/plugin.py +161 -0
- ccproxy/plugins/copilot/README.md +39 -0
- ccproxy/plugins/copilot/__init__.py +11 -0
- ccproxy/plugins/copilot/adapter.py +465 -0
- ccproxy/plugins/copilot/config.py +155 -0
- ccproxy/plugins/copilot/data/copilot_fallback.json +41 -0
- ccproxy/plugins/copilot/detection_service.py +255 -0
- ccproxy/plugins/copilot/manager.py +275 -0
- ccproxy/plugins/copilot/model_defaults.py +284 -0
- ccproxy/plugins/copilot/models.py +148 -0
- ccproxy/plugins/copilot/oauth/__init__.py +16 -0
- ccproxy/plugins/copilot/oauth/client.py +494 -0
- ccproxy/plugins/copilot/oauth/models.py +385 -0
- ccproxy/plugins/copilot/oauth/provider.py +602 -0
- ccproxy/plugins/copilot/oauth/storage.py +170 -0
- ccproxy/plugins/copilot/plugin.py +360 -0
- ccproxy/plugins/copilot/routes.py +294 -0
- ccproxy/plugins/credential_balancer/README.md +124 -0
- ccproxy/plugins/credential_balancer/__init__.py +6 -0
- ccproxy/plugins/credential_balancer/config.py +270 -0
- ccproxy/plugins/credential_balancer/factory.py +415 -0
- ccproxy/plugins/credential_balancer/hook.py +51 -0
- ccproxy/plugins/credential_balancer/manager.py +587 -0
- ccproxy/plugins/credential_balancer/plugin.py +146 -0
- ccproxy/plugins/dashboard/README.md +25 -0
- ccproxy/plugins/dashboard/__init__.py +1 -0
- ccproxy/plugins/dashboard/config.py +8 -0
- ccproxy/plugins/dashboard/plugin.py +71 -0
- ccproxy/plugins/dashboard/routes.py +67 -0
- ccproxy/plugins/docker/README.md +32 -0
- ccproxy/{docker → plugins/docker}/__init__.py +3 -0
- ccproxy/{docker → plugins/docker}/adapter.py +108 -10
- ccproxy/plugins/docker/config.py +82 -0
- ccproxy/{docker → plugins/docker}/docker_path.py +4 -3
- ccproxy/{docker → plugins/docker}/middleware.py +2 -2
- ccproxy/plugins/docker/plugin.py +198 -0
- ccproxy/{docker → plugins/docker}/stream_process.py +3 -3
- ccproxy/plugins/duckdb_storage/README.md +26 -0
- ccproxy/plugins/duckdb_storage/__init__.py +1 -0
- ccproxy/plugins/duckdb_storage/config.py +22 -0
- ccproxy/plugins/duckdb_storage/plugin.py +128 -0
- ccproxy/plugins/duckdb_storage/routes.py +51 -0
- ccproxy/plugins/duckdb_storage/storage.py +633 -0
- ccproxy/plugins/max_tokens/README.md +38 -0
- ccproxy/plugins/max_tokens/__init__.py +12 -0
- ccproxy/plugins/max_tokens/adapter.py +235 -0
- ccproxy/plugins/max_tokens/config.py +86 -0
- ccproxy/plugins/max_tokens/models.py +53 -0
- ccproxy/plugins/max_tokens/plugin.py +200 -0
- ccproxy/plugins/max_tokens/service.py +271 -0
- ccproxy/plugins/max_tokens/token_limits.json +54 -0
- ccproxy/plugins/metrics/README.md +35 -0
- ccproxy/plugins/metrics/__init__.py +10 -0
- ccproxy/{observability/metrics.py → plugins/metrics/collector.py} +20 -153
- ccproxy/plugins/metrics/config.py +85 -0
- ccproxy/plugins/metrics/grafana/dashboards/ccproxy-dashboard.json +1720 -0
- ccproxy/plugins/metrics/hook.py +403 -0
- ccproxy/plugins/metrics/plugin.py +268 -0
- ccproxy/{observability → plugins/metrics}/pushgateway.py +57 -59
- ccproxy/plugins/metrics/routes.py +107 -0
- ccproxy/plugins/metrics/tasks.py +117 -0
- ccproxy/plugins/oauth_claude/README.md +35 -0
- ccproxy/plugins/oauth_claude/__init__.py +14 -0
- ccproxy/plugins/oauth_claude/client.py +270 -0
- ccproxy/plugins/oauth_claude/config.py +84 -0
- ccproxy/plugins/oauth_claude/manager.py +482 -0
- ccproxy/plugins/oauth_claude/models.py +266 -0
- ccproxy/plugins/oauth_claude/plugin.py +149 -0
- ccproxy/plugins/oauth_claude/provider.py +571 -0
- ccproxy/plugins/oauth_claude/storage.py +212 -0
- ccproxy/plugins/oauth_codex/README.md +38 -0
- ccproxy/plugins/oauth_codex/__init__.py +14 -0
- ccproxy/plugins/oauth_codex/client.py +224 -0
- ccproxy/plugins/oauth_codex/config.py +95 -0
- ccproxy/plugins/oauth_codex/manager.py +256 -0
- ccproxy/plugins/oauth_codex/models.py +239 -0
- ccproxy/plugins/oauth_codex/plugin.py +146 -0
- ccproxy/plugins/oauth_codex/provider.py +574 -0
- ccproxy/plugins/oauth_codex/storage.py +92 -0
- ccproxy/plugins/permissions/README.md +28 -0
- ccproxy/plugins/permissions/__init__.py +22 -0
- ccproxy/plugins/permissions/config.py +28 -0
- ccproxy/{cli/commands/permission_handler.py → plugins/permissions/handlers/cli.py} +49 -25
- ccproxy/plugins/permissions/handlers/protocol.py +33 -0
- ccproxy/plugins/permissions/handlers/terminal.py +675 -0
- ccproxy/{api/routes → plugins/permissions}/mcp.py +34 -7
- ccproxy/{models/permissions.py → plugins/permissions/models.py} +65 -1
- ccproxy/plugins/permissions/plugin.py +153 -0
- ccproxy/{api/routes/permissions.py → plugins/permissions/routes.py} +20 -16
- ccproxy/{api/services/permission_service.py → plugins/permissions/service.py} +65 -11
- ccproxy/{api → plugins/permissions}/ui/permission_handler_protocol.py +1 -1
- ccproxy/{api → plugins/permissions}/ui/terminal_permission_handler.py +66 -10
- ccproxy/plugins/pricing/README.md +34 -0
- ccproxy/plugins/pricing/__init__.py +6 -0
- ccproxy/{pricing → plugins/pricing}/cache.py +7 -6
- ccproxy/{config/pricing.py → plugins/pricing/config.py} +32 -6
- ccproxy/plugins/pricing/exceptions.py +35 -0
- ccproxy/plugins/pricing/loader.py +440 -0
- ccproxy/{pricing → plugins/pricing}/models.py +13 -23
- ccproxy/plugins/pricing/plugin.py +169 -0
- ccproxy/plugins/pricing/service.py +191 -0
- ccproxy/plugins/pricing/tasks.py +300 -0
- ccproxy/{pricing → plugins/pricing}/updater.py +86 -72
- ccproxy/plugins/pricing/utils.py +99 -0
- ccproxy/plugins/request_tracer/README.md +40 -0
- ccproxy/plugins/request_tracer/__init__.py +7 -0
- ccproxy/plugins/request_tracer/config.py +120 -0
- ccproxy/plugins/request_tracer/hook.py +415 -0
- ccproxy/plugins/request_tracer/plugin.py +255 -0
- ccproxy/scheduler/__init__.py +2 -14
- ccproxy/scheduler/core.py +26 -41
- ccproxy/scheduler/manager.py +61 -105
- ccproxy/scheduler/registry.py +6 -32
- ccproxy/scheduler/tasks.py +268 -276
- ccproxy/services/__init__.py +0 -1
- ccproxy/services/adapters/__init__.py +11 -0
- ccproxy/services/adapters/base.py +123 -0
- ccproxy/services/adapters/chain_composer.py +88 -0
- ccproxy/services/adapters/chain_validation.py +44 -0
- ccproxy/services/adapters/chat_accumulator.py +200 -0
- ccproxy/services/adapters/delta_utils.py +142 -0
- ccproxy/services/adapters/format_adapter.py +136 -0
- ccproxy/services/adapters/format_context.py +11 -0
- ccproxy/services/adapters/format_registry.py +158 -0
- ccproxy/services/adapters/http_adapter.py +1045 -0
- ccproxy/services/adapters/mock_adapter.py +118 -0
- ccproxy/services/adapters/protocols.py +35 -0
- ccproxy/services/adapters/simple_converters.py +571 -0
- ccproxy/services/auth_registry.py +180 -0
- ccproxy/services/cache/__init__.py +6 -0
- ccproxy/services/cache/response_cache.py +261 -0
- ccproxy/services/cli_detection.py +437 -0
- ccproxy/services/config/__init__.py +6 -0
- ccproxy/services/config/proxy_configuration.py +111 -0
- ccproxy/services/container.py +256 -0
- ccproxy/services/factories.py +380 -0
- ccproxy/services/handler_config.py +76 -0
- ccproxy/services/interfaces.py +298 -0
- ccproxy/services/mocking/__init__.py +6 -0
- ccproxy/services/mocking/mock_handler.py +291 -0
- ccproxy/services/tracing/__init__.py +7 -0
- ccproxy/services/tracing/interfaces.py +61 -0
- ccproxy/services/tracing/null_tracer.py +57 -0
- ccproxy/streaming/__init__.py +23 -0
- ccproxy/streaming/buffer.py +1056 -0
- ccproxy/streaming/deferred.py +897 -0
- ccproxy/streaming/handler.py +117 -0
- ccproxy/streaming/interfaces.py +77 -0
- ccproxy/streaming/simple_adapter.py +39 -0
- ccproxy/streaming/sse.py +109 -0
- ccproxy/streaming/sse_parser.py +127 -0
- ccproxy/templates/__init__.py +6 -0
- ccproxy/templates/plugin_scaffold.py +695 -0
- ccproxy/testing/endpoints/__init__.py +33 -0
- ccproxy/testing/endpoints/cli.py +215 -0
- ccproxy/testing/endpoints/config.py +874 -0
- ccproxy/testing/endpoints/console.py +57 -0
- ccproxy/testing/endpoints/models.py +100 -0
- ccproxy/testing/endpoints/runner.py +1903 -0
- ccproxy/testing/endpoints/tools.py +308 -0
- ccproxy/testing/mock_responses.py +70 -1
- ccproxy/testing/response_handlers.py +20 -0
- ccproxy/utils/__init__.py +0 -6
- ccproxy/utils/binary_resolver.py +476 -0
- ccproxy/utils/caching.py +327 -0
- ccproxy/utils/cli_logging.py +101 -0
- ccproxy/utils/command_line.py +251 -0
- ccproxy/utils/headers.py +228 -0
- ccproxy/utils/model_mapper.py +120 -0
- ccproxy/utils/startup_helpers.py +68 -446
- ccproxy/utils/version_checker.py +273 -6
- ccproxy_api-0.2.0a4.dist-info/METADATA +212 -0
- ccproxy_api-0.2.0a4.dist-info/RECORD +417 -0
- {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0a4.dist-info}/WHEEL +1 -1
- ccproxy_api-0.2.0a4.dist-info/entry_points.txt +24 -0
- ccproxy/__init__.py +0 -4
- ccproxy/adapters/__init__.py +0 -11
- ccproxy/adapters/base.py +0 -80
- ccproxy/adapters/codex/__init__.py +0 -11
- ccproxy/adapters/openai/__init__.py +0 -42
- ccproxy/adapters/openai/adapter.py +0 -953
- ccproxy/adapters/openai/models.py +0 -412
- ccproxy/adapters/openai/response_adapter.py +0 -355
- ccproxy/adapters/openai/response_models.py +0 -178
- ccproxy/api/middleware/headers.py +0 -49
- ccproxy/api/middleware/logging.py +0 -180
- ccproxy/api/middleware/request_content_logging.py +0 -297
- ccproxy/api/middleware/server_header.py +0 -58
- ccproxy/api/responses.py +0 -89
- ccproxy/api/routes/claude.py +0 -371
- ccproxy/api/routes/codex.py +0 -1251
- ccproxy/api/routes/metrics.py +0 -1029
- ccproxy/api/routes/proxy.py +0 -211
- ccproxy/api/services/__init__.py +0 -6
- ccproxy/auth/conditional.py +0 -84
- ccproxy/auth/credentials_adapter.py +0 -93
- ccproxy/auth/models.py +0 -118
- ccproxy/auth/oauth/models.py +0 -48
- ccproxy/auth/openai/__init__.py +0 -13
- ccproxy/auth/openai/credentials.py +0 -166
- ccproxy/auth/openai/oauth_client.py +0 -334
- ccproxy/auth/openai/storage.py +0 -184
- ccproxy/auth/storage/json_file.py +0 -158
- ccproxy/auth/storage/keyring.py +0 -189
- ccproxy/claude_sdk/__init__.py +0 -18
- ccproxy/claude_sdk/options.py +0 -194
- ccproxy/claude_sdk/session_pool.py +0 -550
- ccproxy/cli/docker/__init__.py +0 -34
- ccproxy/cli/docker/adapter_factory.py +0 -157
- ccproxy/cli/docker/params.py +0 -274
- ccproxy/config/auth.py +0 -153
- ccproxy/config/claude.py +0 -348
- ccproxy/config/cors.py +0 -79
- ccproxy/config/discovery.py +0 -95
- ccproxy/config/docker_settings.py +0 -264
- ccproxy/config/observability.py +0 -158
- ccproxy/config/reverse_proxy.py +0 -31
- ccproxy/config/scheduler.py +0 -108
- ccproxy/config/server.py +0 -86
- ccproxy/config/validators.py +0 -231
- ccproxy/core/codex_transformers.py +0 -389
- ccproxy/core/http.py +0 -328
- ccproxy/core/http_transformers.py +0 -812
- ccproxy/core/proxy.py +0 -143
- ccproxy/core/validators.py +0 -288
- ccproxy/models/errors.py +0 -42
- ccproxy/models/messages.py +0 -269
- ccproxy/models/requests.py +0 -107
- ccproxy/models/responses.py +0 -270
- ccproxy/models/types.py +0 -102
- ccproxy/observability/__init__.py +0 -51
- ccproxy/observability/access_logger.py +0 -457
- ccproxy/observability/sse_events.py +0 -303
- ccproxy/observability/stats_printer.py +0 -753
- ccproxy/observability/storage/__init__.py +0 -1
- ccproxy/observability/storage/duckdb_simple.py +0 -677
- ccproxy/observability/storage/models.py +0 -70
- ccproxy/observability/streaming_response.py +0 -107
- ccproxy/pricing/__init__.py +0 -19
- ccproxy/pricing/loader.py +0 -251
- ccproxy/services/claude_detection_service.py +0 -243
- ccproxy/services/codex_detection_service.py +0 -252
- ccproxy/services/credentials/__init__.py +0 -55
- ccproxy/services/credentials/config.py +0 -105
- ccproxy/services/credentials/manager.py +0 -561
- ccproxy/services/credentials/oauth_client.py +0 -481
- ccproxy/services/proxy_service.py +0 -1827
- ccproxy/static/.keep +0 -0
- ccproxy/utils/cost_calculator.py +0 -210
- ccproxy/utils/disconnection_monitor.py +0 -83
- ccproxy/utils/model_mapping.py +0 -199
- ccproxy/utils/models_provider.py +0 -150
- ccproxy/utils/simple_request_logger.py +0 -284
- ccproxy/utils/streaming_metrics.py +0 -199
- ccproxy_api-0.1.7.dist-info/METADATA +0 -615
- ccproxy_api-0.1.7.dist-info/RECORD +0 -191
- ccproxy_api-0.1.7.dist-info/entry_points.txt +0 -4
- /ccproxy/{api/middleware/auth.py → auth/models/__init__.py} +0 -0
- /ccproxy/{claude_sdk → plugins/claude_sdk}/exceptions.py +0 -0
- /ccproxy/{docker → plugins/docker}/models.py +0 -0
- /ccproxy/{docker → plugins/docker}/protocol.py +0 -0
- /ccproxy/{docker → plugins/docker}/validators.py +0 -0
- /ccproxy/{auth/oauth/storage.py → plugins/permissions/handlers/__init__.py} +0 -0
- /ccproxy/{api → plugins/permissions}/ui/__init__.py +0 -0
- {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0a4.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""No-op auth manager for Claude SDK plugin."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from pydantic import SecretStr
|
|
6
|
+
|
|
7
|
+
from ccproxy.auth.oauth.protocol import StandardProfileFields
|
|
8
|
+
from ccproxy.plugins.oauth_claude.models import ClaudeCredentials, ClaudeOAuthToken
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class NoOpAuthManager:
|
|
12
|
+
"""No-operation auth manager for Claude SDK.
|
|
13
|
+
|
|
14
|
+
The SDK handles authentication internally through the CLI,
|
|
15
|
+
so we don't need to manage auth headers.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
async def get_access_token(self) -> str:
|
|
19
|
+
"""Return empty token since SDK handles auth internally."""
|
|
20
|
+
return ""
|
|
21
|
+
|
|
22
|
+
async def get_credentials(self) -> ClaudeCredentials:
|
|
23
|
+
"""Return dummy credentials since SDK handles auth internally."""
|
|
24
|
+
# Create minimal credentials object with OAuthToken
|
|
25
|
+
|
|
26
|
+
oauth_token = ClaudeOAuthToken(
|
|
27
|
+
accessToken=SecretStr("sdk-managed"),
|
|
28
|
+
refreshToken=SecretStr("sdk-managed"),
|
|
29
|
+
expiresAt=None,
|
|
30
|
+
scopes=[],
|
|
31
|
+
subscriptionType="sdk",
|
|
32
|
+
)
|
|
33
|
+
return ClaudeCredentials(claudeAiOauth=oauth_token)
|
|
34
|
+
|
|
35
|
+
async def is_authenticated(self) -> bool:
|
|
36
|
+
"""Always return True since SDK handles auth internally."""
|
|
37
|
+
return True
|
|
38
|
+
|
|
39
|
+
async def get_user_profile(self) -> StandardProfileFields | None:
|
|
40
|
+
"""Return ``None`` because the SDK does not surface profile metadata."""
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
async def __aenter__(self) -> "NoOpAuthManager":
|
|
44
|
+
"""Async context manager entry."""
|
|
45
|
+
return self
|
|
46
|
+
|
|
47
|
+
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
48
|
+
"""No cleanup needed."""
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
async def validate_credentials(self) -> bool:
|
|
52
|
+
"""Always return True since SDK handles auth internally."""
|
|
53
|
+
return True
|
|
54
|
+
|
|
55
|
+
def get_provider_name(self) -> str:
|
|
56
|
+
"""Get the provider name for logging."""
|
|
57
|
+
return "claude-sdk"
|
|
@@ -5,46 +5,47 @@ import contextlib
|
|
|
5
5
|
from collections.abc import AsyncIterator
|
|
6
6
|
from typing import Any, TypeVar, cast
|
|
7
7
|
|
|
8
|
-
import structlog
|
|
9
8
|
from pydantic import BaseModel
|
|
10
9
|
|
|
11
|
-
from ccproxy.claude_sdk.exceptions import ClaudeSDKError, StreamTimeoutError
|
|
12
|
-
from ccproxy.claude_sdk.manager import SessionManager
|
|
13
|
-
from ccproxy.claude_sdk.stream_handle import StreamHandle
|
|
14
|
-
from ccproxy.config.settings import Settings
|
|
15
10
|
from ccproxy.core.async_utils import patched_typing
|
|
16
11
|
from ccproxy.core.errors import ClaudeProxyError, ServiceUnavailableError
|
|
17
|
-
from ccproxy.
|
|
18
|
-
from ccproxy.
|
|
19
|
-
|
|
12
|
+
from ccproxy.core.logging import get_plugin_logger
|
|
13
|
+
from ccproxy.core.request_context import timed_operation
|
|
14
|
+
|
|
15
|
+
from . import models as sdk_models
|
|
16
|
+
from .config import ClaudeSDKSettings, SessionPoolSettings
|
|
17
|
+
from .exceptions import ClaudeSDKError, StreamTimeoutError
|
|
18
|
+
from .manager import SessionManager
|
|
19
|
+
from .models import SDKMessage
|
|
20
|
+
from .stream_handle import StreamHandle
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
with patched_typing():
|
|
23
|
-
from
|
|
24
|
+
from claude_agent_sdk import (
|
|
24
25
|
AssistantMessage as SDKAssistantMessage,
|
|
25
26
|
)
|
|
26
|
-
from
|
|
27
|
-
|
|
27
|
+
from claude_agent_sdk import (
|
|
28
|
+
ClaudeAgentOptions,
|
|
28
29
|
CLIConnectionError,
|
|
29
30
|
CLIJSONDecodeError,
|
|
30
31
|
CLINotFoundError,
|
|
31
32
|
ProcessError,
|
|
32
33
|
)
|
|
33
|
-
from
|
|
34
|
+
from claude_agent_sdk import (
|
|
34
35
|
ClaudeSDKClient as ImportedClaudeSDKClient,
|
|
35
36
|
)
|
|
36
|
-
from
|
|
37
|
+
from claude_agent_sdk import (
|
|
37
38
|
ResultMessage as SDKResultMessage,
|
|
38
39
|
)
|
|
39
|
-
from
|
|
40
|
+
from claude_agent_sdk import (
|
|
40
41
|
SystemMessage as SDKSystemMessage,
|
|
41
42
|
)
|
|
42
|
-
from
|
|
43
|
+
from claude_agent_sdk import (
|
|
43
44
|
UserMessage as SDKUserMessage,
|
|
44
45
|
)
|
|
45
46
|
|
|
46
47
|
|
|
47
|
-
logger =
|
|
48
|
+
logger = get_plugin_logger()
|
|
48
49
|
|
|
49
50
|
T = TypeVar("T", bound=BaseModel)
|
|
50
51
|
|
|
@@ -69,17 +70,17 @@ class ClaudeSDKClient:
|
|
|
69
70
|
|
|
70
71
|
def __init__(
|
|
71
72
|
self,
|
|
72
|
-
|
|
73
|
+
config: ClaudeSDKSettings,
|
|
73
74
|
session_manager: SessionManager | None = None,
|
|
74
75
|
) -> None:
|
|
75
76
|
"""Initialize the Claude SDK client.
|
|
76
77
|
|
|
77
78
|
Args:
|
|
78
|
-
|
|
79
|
+
config: Plugin-specific configuration for Claude SDK
|
|
79
80
|
session_manager: Optional SessionManager instance for dependency injection
|
|
80
81
|
"""
|
|
81
82
|
self._last_api_call_time_ms: float = 0.0
|
|
82
|
-
self.
|
|
83
|
+
self.config = config
|
|
83
84
|
self._session_manager = session_manager
|
|
84
85
|
|
|
85
86
|
@contextlib.asynccontextmanager
|
|
@@ -121,6 +122,7 @@ class ClaudeSDKClient:
|
|
|
121
122
|
error_type=type(e).__name__,
|
|
122
123
|
operation=operation,
|
|
123
124
|
request_id=request_id,
|
|
125
|
+
exc_info=e,
|
|
124
126
|
)
|
|
125
127
|
raise ClaudeProxyError(
|
|
126
128
|
message=f"Unexpected error: {str(e)}",
|
|
@@ -230,14 +232,10 @@ class ClaudeSDKClient:
|
|
|
230
232
|
return False
|
|
231
233
|
|
|
232
234
|
# Check settings using safe attribute chaining
|
|
233
|
-
if not self.
|
|
234
|
-
return False
|
|
235
|
-
|
|
236
|
-
claude_settings = getattr(self._settings, "claude", None)
|
|
237
|
-
if not claude_settings:
|
|
235
|
+
if not self.config:
|
|
238
236
|
return False
|
|
239
237
|
|
|
240
|
-
pool_settings = getattr(
|
|
238
|
+
pool_settings = getattr(self.config, "sdk_session_pool", None)
|
|
241
239
|
if not pool_settings:
|
|
242
240
|
return False
|
|
243
241
|
|
|
@@ -246,7 +244,7 @@ class ClaudeSDKClient:
|
|
|
246
244
|
async def query_completion(
|
|
247
245
|
self,
|
|
248
246
|
message: SDKMessage,
|
|
249
|
-
options:
|
|
247
|
+
options: ClaudeAgentOptions,
|
|
250
248
|
request_id: str | None = None,
|
|
251
249
|
session_id: str | None = None,
|
|
252
250
|
) -> StreamHandle:
|
|
@@ -278,27 +276,46 @@ class ClaudeSDKClient:
|
|
|
278
276
|
async def _create_direct_stream_handle(
|
|
279
277
|
self,
|
|
280
278
|
message: SDKMessage,
|
|
281
|
-
options:
|
|
279
|
+
options: ClaudeAgentOptions,
|
|
282
280
|
request_id: str | None = None,
|
|
283
281
|
session_id: str | None = None,
|
|
284
282
|
) -> StreamHandle:
|
|
285
283
|
"""Create stream handle for direct query (no session pool)."""
|
|
286
284
|
message_iterator = self._query(message, options, request_id, session_id)
|
|
287
285
|
|
|
286
|
+
# Convert core settings to plugin settings if available
|
|
287
|
+
plugin_session_config = None
|
|
288
|
+
if self.config and self.config.sdk_session_pool:
|
|
289
|
+
core_pool_settings = self.config.sdk_session_pool
|
|
290
|
+
plugin_session_config = SessionPoolSettings(
|
|
291
|
+
enabled=core_pool_settings.enabled,
|
|
292
|
+
session_ttl=core_pool_settings.session_ttl,
|
|
293
|
+
max_sessions=core_pool_settings.max_sessions,
|
|
294
|
+
cleanup_interval=getattr(core_pool_settings, "cleanup_interval", 300),
|
|
295
|
+
idle_threshold=getattr(core_pool_settings, "idle_threshold", 300),
|
|
296
|
+
connection_recovery=getattr(
|
|
297
|
+
core_pool_settings, "connection_recovery", True
|
|
298
|
+
),
|
|
299
|
+
stream_first_chunk_timeout=getattr(
|
|
300
|
+
core_pool_settings, "stream_first_chunk_timeout", 8
|
|
301
|
+
),
|
|
302
|
+
stream_ongoing_timeout=getattr(
|
|
303
|
+
core_pool_settings, "stream_ongoing_timeout", 60
|
|
304
|
+
),
|
|
305
|
+
)
|
|
306
|
+
|
|
288
307
|
return StreamHandle(
|
|
289
308
|
message_iterator=message_iterator,
|
|
290
309
|
session_id=session_id,
|
|
291
310
|
request_id=request_id,
|
|
292
311
|
session_client=None,
|
|
293
|
-
session_config=
|
|
294
|
-
if self._settings
|
|
295
|
-
else None, # StreamHandle will use defaults
|
|
312
|
+
session_config=plugin_session_config, # StreamHandle will use defaults if None
|
|
296
313
|
)
|
|
297
314
|
|
|
298
315
|
async def _create_session_pool_stream_handle(
|
|
299
316
|
self,
|
|
300
317
|
message: SDKMessage,
|
|
301
|
-
options:
|
|
318
|
+
options: ClaudeAgentOptions,
|
|
302
319
|
request_id: str | None = None,
|
|
303
320
|
session_id: str | None = None,
|
|
304
321
|
) -> StreamHandle:
|
|
@@ -343,7 +360,7 @@ class ClaudeSDKClient:
|
|
|
343
360
|
async def _query(
|
|
344
361
|
self,
|
|
345
362
|
message: SDKMessage,
|
|
346
|
-
options:
|
|
363
|
+
options: ClaudeAgentOptions,
|
|
347
364
|
request_id: str | None = None,
|
|
348
365
|
session_id: str | None = None,
|
|
349
366
|
) -> AsyncIterator[
|
|
@@ -380,12 +397,13 @@ class ClaudeSDKClient:
|
|
|
380
397
|
"claude_sdk_disconnect_failed",
|
|
381
398
|
error=str(e),
|
|
382
399
|
request_id=request_id,
|
|
400
|
+
exc_info=e,
|
|
383
401
|
)
|
|
384
402
|
|
|
385
403
|
async def _query_with_session_pool(
|
|
386
404
|
self,
|
|
387
405
|
message: SDKMessage,
|
|
388
|
-
options:
|
|
406
|
+
options: ClaudeAgentOptions,
|
|
389
407
|
request_id: str | None = None,
|
|
390
408
|
session_id: str | None = None,
|
|
391
409
|
) -> AsyncIterator[
|
|
@@ -487,10 +505,10 @@ class ClaudeSDKClient:
|
|
|
487
505
|
error=str(e),
|
|
488
506
|
error_type=type(e).__name__,
|
|
489
507
|
session_id=session_id,
|
|
490
|
-
exc_info=
|
|
508
|
+
exc_info=e,
|
|
491
509
|
)
|
|
492
510
|
# Fall back to direct query
|
|
493
|
-
logger.
|
|
511
|
+
logger.debug(
|
|
494
512
|
"claude_sdk_fallback_to_direct_query", session_id=session_id
|
|
495
513
|
)
|
|
496
514
|
async for msg in self._query(message, options, request_id, session_id):
|
|
@@ -520,7 +538,9 @@ class ClaudeSDKClient:
|
|
|
520
538
|
"""
|
|
521
539
|
try:
|
|
522
540
|
# Wait for the first chunk with timeout - don't care about message type
|
|
523
|
-
logger.debug(
|
|
541
|
+
logger.debug(
|
|
542
|
+
"waiting_for_first_chunk", timeout=timeout_seconds, category="streaming"
|
|
543
|
+
)
|
|
524
544
|
first_message = await asyncio.wait_for(
|
|
525
545
|
anext(message_iterator), timeout=timeout_seconds
|
|
526
546
|
)
|
|
@@ -556,6 +576,7 @@ class ClaudeSDKClient:
|
|
|
556
576
|
"failed_to_interrupt_stuck_session",
|
|
557
577
|
session_id=session_id,
|
|
558
578
|
error=str(e),
|
|
579
|
+
exc_info=e,
|
|
559
580
|
)
|
|
560
581
|
|
|
561
582
|
# Raise a custom exception with error details
|
|
@@ -627,6 +648,7 @@ class ClaudeSDKClient:
|
|
|
627
648
|
error=str(e),
|
|
628
649
|
request_id=request_id,
|
|
629
650
|
session_id=session_id,
|
|
651
|
+
exc_info=e,
|
|
630
652
|
)
|
|
631
653
|
break
|
|
632
654
|
else:
|
|
@@ -659,7 +681,7 @@ class ClaudeSDKClient:
|
|
|
659
681
|
|
|
660
682
|
async def drain_stream() -> None:
|
|
661
683
|
try:
|
|
662
|
-
logger.
|
|
684
|
+
logger.trace(
|
|
663
685
|
"claude_sdk_starting_stream_drain",
|
|
664
686
|
session_id=session_id,
|
|
665
687
|
request_id=request_id,
|
|
@@ -675,7 +697,7 @@ class ClaudeSDKClient:
|
|
|
675
697
|
):
|
|
676
698
|
message_count += 1
|
|
677
699
|
|
|
678
|
-
logger.
|
|
700
|
+
logger.trace(
|
|
679
701
|
"claude_sdk_stream_drained",
|
|
680
702
|
session_id=session_id,
|
|
681
703
|
request_id=request_id,
|
|
@@ -688,6 +710,7 @@ class ClaudeSDKClient:
|
|
|
688
710
|
request_id=request_id,
|
|
689
711
|
error=str(e),
|
|
690
712
|
error_type=type(e).__name__,
|
|
713
|
+
exc_info=e,
|
|
691
714
|
)
|
|
692
715
|
finally:
|
|
693
716
|
if session_client:
|
|
@@ -749,6 +772,7 @@ class ClaudeSDKClient:
|
|
|
749
772
|
component="claude_sdk",
|
|
750
773
|
error=str(e),
|
|
751
774
|
error_type=type(e).__name__,
|
|
775
|
+
exc_info=e,
|
|
752
776
|
)
|
|
753
777
|
return False
|
|
754
778
|
|
|
@@ -763,7 +787,7 @@ class ClaudeSDKClient:
|
|
|
763
787
|
"""
|
|
764
788
|
logger.debug("sdk_client_interrupt_session_started", session_id=session_id)
|
|
765
789
|
if self._session_manager:
|
|
766
|
-
logger.
|
|
790
|
+
logger.debug(
|
|
767
791
|
"client_interrupt_session_requested",
|
|
768
792
|
session_id=session_id,
|
|
769
793
|
has_session_manager=True,
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""Configuration for Claude SDK plugin."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from claude_agent_sdk import ClaudeAgentOptions
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
8
|
+
|
|
9
|
+
from ccproxy.models.provider import ModelCard, ModelMappingRule, ProviderConfig
|
|
10
|
+
from ccproxy.plugins.claude_shared.model_defaults import (
|
|
11
|
+
DEFAULT_CLAUDE_MODEL_CARDS,
|
|
12
|
+
DEFAULT_CLAUDE_MODEL_MAPPINGS,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _create_default_claude_code_options(
|
|
17
|
+
builtin_permissions: bool = True,
|
|
18
|
+
continue_conversation: bool = False,
|
|
19
|
+
) -> ClaudeAgentOptions:
|
|
20
|
+
"""Create ClaudeAgentOptions with default values.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
builtin_permissions: Whether to include built-in permission handling defaults
|
|
24
|
+
"""
|
|
25
|
+
if builtin_permissions:
|
|
26
|
+
return ClaudeAgentOptions(
|
|
27
|
+
continue_conversation=continue_conversation,
|
|
28
|
+
mcp_servers={
|
|
29
|
+
"confirmation": {"type": "sse", "url": "http://127.0.0.1:8000/mcp"}
|
|
30
|
+
},
|
|
31
|
+
permission_prompt_tool_name="mcp__confirmation__check_permission",
|
|
32
|
+
)
|
|
33
|
+
else:
|
|
34
|
+
return ClaudeAgentOptions(
|
|
35
|
+
mcp_servers={},
|
|
36
|
+
permission_prompt_tool_name=None,
|
|
37
|
+
continue_conversation=continue_conversation,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class SDKMessageMode(str, Enum):
|
|
42
|
+
"""Modes for handling SDK messages from Claude SDK.
|
|
43
|
+
|
|
44
|
+
- forward: Forward SDK content blocks directly with original types and metadata
|
|
45
|
+
- ignore: Skip SDK messages and blocks completely
|
|
46
|
+
- formatted: Format as XML tags with JSON data in text deltas
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
FORWARD = "forward"
|
|
50
|
+
IGNORE = "ignore"
|
|
51
|
+
FORMATTED = "formatted"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class SystemPromptInjectionMode(str, Enum):
|
|
55
|
+
"""Modes for system prompt injection.
|
|
56
|
+
|
|
57
|
+
- minimal: Only inject Claude Code identification prompt
|
|
58
|
+
- full: Inject all detected system messages from Claude CLI
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
MINIMAL = "minimal"
|
|
62
|
+
FULL = "full"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class SessionPoolSettings(BaseModel):
|
|
66
|
+
"""Session pool configuration settings."""
|
|
67
|
+
|
|
68
|
+
enabled: bool = Field(
|
|
69
|
+
default=True, description="Enable session-aware persistent pooling"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
session_ttl: int = Field(
|
|
73
|
+
default=3600,
|
|
74
|
+
ge=60,
|
|
75
|
+
le=86400,
|
|
76
|
+
description="Session time-to-live in seconds (1 minute to 24 hours)",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
max_sessions: int = Field(
|
|
80
|
+
default=1000,
|
|
81
|
+
ge=1,
|
|
82
|
+
le=10000,
|
|
83
|
+
description="Maximum number of concurrent sessions",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
cleanup_interval: int = Field(
|
|
87
|
+
default=300,
|
|
88
|
+
ge=30,
|
|
89
|
+
le=3600,
|
|
90
|
+
description="Session cleanup interval in seconds (30 seconds to 1 hour)",
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
idle_threshold: int = Field(
|
|
94
|
+
default=600,
|
|
95
|
+
ge=60,
|
|
96
|
+
le=7200,
|
|
97
|
+
description="Session idle threshold in seconds (1 minute to 2 hours)",
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
connection_recovery: bool = Field(
|
|
101
|
+
default=True,
|
|
102
|
+
description="Enable automatic connection recovery for unhealthy sessions",
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
stream_first_chunk_timeout: int = Field(
|
|
106
|
+
default=3,
|
|
107
|
+
ge=1,
|
|
108
|
+
le=30,
|
|
109
|
+
description="Stream first chunk timeout in seconds (1-30 seconds)",
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
stream_ongoing_timeout: int = Field(
|
|
113
|
+
default=60,
|
|
114
|
+
ge=10,
|
|
115
|
+
le=600,
|
|
116
|
+
description="Stream ongoing timeout in seconds after first chunk (10 seconds to 10 minutes)",
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
stream_interrupt_timeout: int = Field(
|
|
120
|
+
default=10,
|
|
121
|
+
ge=2,
|
|
122
|
+
le=60,
|
|
123
|
+
description="Stream interrupt timeout in seconds for SDK and worker operations (2-60 seconds)",
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
@model_validator(mode="after")
|
|
127
|
+
def validate_timeout_hierarchy(self) -> "SessionPoolSettings":
|
|
128
|
+
"""Ensure stream timeouts are less than session TTL."""
|
|
129
|
+
if self.stream_ongoing_timeout >= self.session_ttl:
|
|
130
|
+
raise ValueError(
|
|
131
|
+
f"stream_ongoing_timeout ({self.stream_ongoing_timeout}s) must be less than session_ttl ({self.session_ttl}s)"
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
if self.stream_first_chunk_timeout >= self.stream_ongoing_timeout:
|
|
135
|
+
raise ValueError(
|
|
136
|
+
f"stream_first_chunk_timeout ({self.stream_first_chunk_timeout}s) must be less than stream_ongoing_timeout ({self.stream_ongoing_timeout}s)"
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
return self
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class ClaudeSDKSettings(ProviderConfig):
|
|
143
|
+
"""Claude SDK specific configuration."""
|
|
144
|
+
|
|
145
|
+
# Base required fields for ProviderConfig
|
|
146
|
+
name: str = "claude_sdk"
|
|
147
|
+
base_url: str = "claude-sdk://local" # Special URL for SDK
|
|
148
|
+
supports_streaming: bool = True
|
|
149
|
+
requires_auth: bool = False # SDK handles auth internally
|
|
150
|
+
auth_type: str | None = None
|
|
151
|
+
model_mappings: list[ModelMappingRule] = Field(
|
|
152
|
+
default_factory=lambda: [
|
|
153
|
+
rule.model_copy(deep=True) for rule in DEFAULT_CLAUDE_MODEL_MAPPINGS
|
|
154
|
+
]
|
|
155
|
+
)
|
|
156
|
+
models_endpoint: list[ModelCard] = Field(
|
|
157
|
+
default_factory=lambda: [
|
|
158
|
+
card.model_copy(deep=True) for card in DEFAULT_CLAUDE_MODEL_CARDS
|
|
159
|
+
]
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# Plugin lifecycle settings
|
|
163
|
+
enabled: bool = True
|
|
164
|
+
priority: int = 0
|
|
165
|
+
|
|
166
|
+
# Claude SDK specific settings
|
|
167
|
+
cli_path: str | None = None
|
|
168
|
+
builtin_permissions: bool = True
|
|
169
|
+
session_pool_enabled: bool = False
|
|
170
|
+
session_pool_size: int = 5
|
|
171
|
+
session_timeout_seconds: int = 300
|
|
172
|
+
|
|
173
|
+
# SDK behavior settings
|
|
174
|
+
include_system_messages_in_stream: bool = True
|
|
175
|
+
pretty_format: bool = True
|
|
176
|
+
sdk_message_mode: SDKMessageMode = SDKMessageMode.FORMATTED
|
|
177
|
+
|
|
178
|
+
# Performance settings
|
|
179
|
+
max_tokens_default: int = 4096
|
|
180
|
+
temperature_default: float = 0.7
|
|
181
|
+
|
|
182
|
+
# Additional fields from ClaudeSettings to prevent validation errors
|
|
183
|
+
# Use Any to avoid Pydantic schema generation on external TypedDicts (Py<3.12)
|
|
184
|
+
code_options: Any | None = None
|
|
185
|
+
system_prompt_injection_mode: SystemPromptInjectionMode = (
|
|
186
|
+
SystemPromptInjectionMode.MINIMAL
|
|
187
|
+
)
|
|
188
|
+
sdk_session_pool: SessionPoolSettings | None = None
|
|
189
|
+
|
|
190
|
+
# Default session configuration
|
|
191
|
+
default_session_id: str | None = Field(
|
|
192
|
+
default=None,
|
|
193
|
+
description="Default session ID to use when none is provided. "
|
|
194
|
+
"Useful for single-user setups or development environments.",
|
|
195
|
+
)
|
|
196
|
+
auto_generate_default_session: bool = Field(
|
|
197
|
+
default=False,
|
|
198
|
+
description="Automatically generate a random default session ID at startup. "
|
|
199
|
+
"Overrides default_session_id if enabled. Useful for single-user "
|
|
200
|
+
"setups where you want session persistence during runtime.",
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
@model_validator(mode="after")
|
|
204
|
+
def ensure_session_pool_settings(self) -> "ClaudeSDKSettings":
|
|
205
|
+
"""Ensure sdk_session_pool is initialized."""
|
|
206
|
+
if self.sdk_session_pool is None:
|
|
207
|
+
self.sdk_session_pool = SessionPoolSettings()
|
|
208
|
+
return self
|
|
209
|
+
|
|
210
|
+
model_config = ConfigDict(extra="allow")
|
|
@@ -5,15 +5,15 @@ import json
|
|
|
5
5
|
from collections.abc import Callable
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
-
import structlog
|
|
9
|
-
|
|
10
|
-
from ccproxy.config.claude import SDKMessageMode
|
|
11
8
|
from ccproxy.core.async_utils import patched_typing
|
|
12
|
-
from ccproxy.
|
|
13
|
-
|
|
9
|
+
from ccproxy.core.logging import get_plugin_logger
|
|
10
|
+
|
|
11
|
+
from . import models as sdk_models
|
|
12
|
+
from .config import SDKMessageMode
|
|
13
|
+
from .models import MessageResponse
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
logger =
|
|
16
|
+
logger = get_plugin_logger()
|
|
17
17
|
|
|
18
18
|
with patched_typing():
|
|
19
19
|
pass
|