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
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
"""JSON file storage implementation for token storage."""
|
|
2
|
-
|
|
3
|
-
import contextlib
|
|
4
|
-
import json
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
|
|
7
|
-
from structlog import get_logger
|
|
8
|
-
|
|
9
|
-
from ccproxy.auth.exceptions import (
|
|
10
|
-
CredentialsInvalidError,
|
|
11
|
-
CredentialsStorageError,
|
|
12
|
-
)
|
|
13
|
-
from ccproxy.auth.models import ClaudeCredentials
|
|
14
|
-
from ccproxy.auth.storage.base import TokenStorage
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
logger = get_logger(__name__)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class JsonFileTokenStorage(TokenStorage):
|
|
21
|
-
"""JSON file storage implementation for Claude credentials with keyring fallback."""
|
|
22
|
-
|
|
23
|
-
def __init__(self, file_path: Path):
|
|
24
|
-
"""Initialize JSON file storage.
|
|
25
|
-
|
|
26
|
-
Args:
|
|
27
|
-
file_path: Path to the JSON credentials file
|
|
28
|
-
"""
|
|
29
|
-
self.file_path = file_path
|
|
30
|
-
|
|
31
|
-
async def load(self) -> ClaudeCredentials | None:
|
|
32
|
-
"""Load credentials from JSON file .
|
|
33
|
-
|
|
34
|
-
Returns:
|
|
35
|
-
Parsed credentials if found and valid, None otherwise
|
|
36
|
-
|
|
37
|
-
Raises:
|
|
38
|
-
CredentialsInvalidError: If the JSON file is invalid
|
|
39
|
-
CredentialsStorageError: If there's an error reading the file
|
|
40
|
-
"""
|
|
41
|
-
if not await self.exists():
|
|
42
|
-
logger.debug("credentials_file_not_found", path=str(self.file_path))
|
|
43
|
-
return None
|
|
44
|
-
|
|
45
|
-
try:
|
|
46
|
-
logger.debug(
|
|
47
|
-
"credentials_load_start", source="file", path=str(self.file_path)
|
|
48
|
-
)
|
|
49
|
-
with self.file_path.open() as f:
|
|
50
|
-
data = json.load(f)
|
|
51
|
-
|
|
52
|
-
credentials = ClaudeCredentials.model_validate(data)
|
|
53
|
-
logger.debug("credentials_load_completed", source="file")
|
|
54
|
-
|
|
55
|
-
return credentials
|
|
56
|
-
|
|
57
|
-
except json.JSONDecodeError as e:
|
|
58
|
-
raise CredentialsInvalidError(
|
|
59
|
-
f"Failed to parse credentials file {self.file_path}: {e}"
|
|
60
|
-
) from e
|
|
61
|
-
except Exception as e:
|
|
62
|
-
raise CredentialsStorageError(
|
|
63
|
-
f"Error loading credentials from {self.file_path}: {e}"
|
|
64
|
-
) from e
|
|
65
|
-
|
|
66
|
-
async def save(self, credentials: ClaudeCredentials) -> bool:
|
|
67
|
-
"""Save credentials to both keyring and JSON file.
|
|
68
|
-
|
|
69
|
-
Args:
|
|
70
|
-
credentials: Credentials to save
|
|
71
|
-
|
|
72
|
-
Returns:
|
|
73
|
-
True if saved successfully, False otherwise
|
|
74
|
-
|
|
75
|
-
Raises:
|
|
76
|
-
CredentialsStorageError: If there's an error writing the file
|
|
77
|
-
"""
|
|
78
|
-
try:
|
|
79
|
-
# Convert to dict with proper aliases
|
|
80
|
-
data = credentials.model_dump(by_alias=True, mode="json")
|
|
81
|
-
|
|
82
|
-
# Always save to file as well
|
|
83
|
-
# Ensure parent directory exists
|
|
84
|
-
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
85
|
-
|
|
86
|
-
# Use atomic write: write to temp file then rename
|
|
87
|
-
temp_path = self.file_path.with_suffix(".tmp")
|
|
88
|
-
|
|
89
|
-
try:
|
|
90
|
-
with temp_path.open("w") as f:
|
|
91
|
-
json.dump(data, f, indent=2)
|
|
92
|
-
|
|
93
|
-
# Set appropriate file permissions (read/write for owner only)
|
|
94
|
-
temp_path.chmod(0o600)
|
|
95
|
-
|
|
96
|
-
# Atomically replace the original file
|
|
97
|
-
Path.replace(temp_path, self.file_path)
|
|
98
|
-
|
|
99
|
-
logger.debug(
|
|
100
|
-
"credentials_save_completed",
|
|
101
|
-
source="file",
|
|
102
|
-
path=str(self.file_path),
|
|
103
|
-
)
|
|
104
|
-
return True
|
|
105
|
-
except Exception as e:
|
|
106
|
-
raise
|
|
107
|
-
finally:
|
|
108
|
-
# Clean up temp file if it exists
|
|
109
|
-
if temp_path.exists():
|
|
110
|
-
with contextlib.suppress(Exception):
|
|
111
|
-
temp_path.unlink()
|
|
112
|
-
|
|
113
|
-
except Exception as e:
|
|
114
|
-
raise CredentialsStorageError(f"Error saving credentials: {e}") from e
|
|
115
|
-
|
|
116
|
-
async def exists(self) -> bool:
|
|
117
|
-
"""Check if credentials file exists.
|
|
118
|
-
|
|
119
|
-
Returns:
|
|
120
|
-
True if file exists, False otherwise
|
|
121
|
-
"""
|
|
122
|
-
return self.file_path.exists() and self.file_path.is_file()
|
|
123
|
-
|
|
124
|
-
async def delete(self) -> bool:
|
|
125
|
-
"""Delete credentials from both keyring and file.
|
|
126
|
-
|
|
127
|
-
Returns:
|
|
128
|
-
True if deleted successfully, False otherwise
|
|
129
|
-
|
|
130
|
-
Raises:
|
|
131
|
-
CredentialsStorageError: If there's an error deleting the file
|
|
132
|
-
"""
|
|
133
|
-
deleted = False
|
|
134
|
-
|
|
135
|
-
# Delete from file
|
|
136
|
-
try:
|
|
137
|
-
if await self.exists():
|
|
138
|
-
self.file_path.unlink()
|
|
139
|
-
logger.debug(
|
|
140
|
-
"credentials_delete_completed",
|
|
141
|
-
source="file",
|
|
142
|
-
path=str(self.file_path),
|
|
143
|
-
)
|
|
144
|
-
deleted = True
|
|
145
|
-
except Exception as e:
|
|
146
|
-
if not deleted: # Only raise if we failed to delete from both
|
|
147
|
-
raise CredentialsStorageError(f"Error deleting credentials: {e}") from e
|
|
148
|
-
logger.debug("credentials_delete_partial", source="file", error=str(e))
|
|
149
|
-
|
|
150
|
-
return deleted
|
|
151
|
-
|
|
152
|
-
def get_location(self) -> str:
|
|
153
|
-
"""Get the storage location description.
|
|
154
|
-
|
|
155
|
-
Returns:
|
|
156
|
-
Path to the JSON file with keyring info if available
|
|
157
|
-
"""
|
|
158
|
-
return str(self.file_path)
|
ccproxy/auth/storage/keyring.py
DELETED
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
"""OS keyring storage implementation for token storage."""
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
|
|
5
|
-
from structlog import get_logger
|
|
6
|
-
|
|
7
|
-
from ccproxy.auth.exceptions import (
|
|
8
|
-
CredentialsStorageError,
|
|
9
|
-
)
|
|
10
|
-
from ccproxy.auth.models import ClaudeCredentials
|
|
11
|
-
from ccproxy.auth.storage.base import TokenStorage
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
logger = get_logger(__name__)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class KeyringTokenStorage(TokenStorage):
|
|
18
|
-
"""OS keyring storage implementation for Claude credentials."""
|
|
19
|
-
|
|
20
|
-
def __init__(
|
|
21
|
-
self, service_name: str = "claude-code-proxy", username: str = "default"
|
|
22
|
-
):
|
|
23
|
-
"""Initialize keyring storage.
|
|
24
|
-
|
|
25
|
-
Args:
|
|
26
|
-
service_name: Name of the service in the keyring
|
|
27
|
-
username: Username to associate with the stored credentials
|
|
28
|
-
"""
|
|
29
|
-
self.service_name = service_name
|
|
30
|
-
self.username = username
|
|
31
|
-
|
|
32
|
-
async def load(self) -> ClaudeCredentials | None:
|
|
33
|
-
"""Load credentials from the OS keyring.
|
|
34
|
-
|
|
35
|
-
Returns:
|
|
36
|
-
Parsed credentials if found and valid, None otherwise
|
|
37
|
-
|
|
38
|
-
Raises:
|
|
39
|
-
CredentialsStorageError: If the stored data is invalid
|
|
40
|
-
CredentialsStorageError: If there's an error reading from keyring
|
|
41
|
-
"""
|
|
42
|
-
try:
|
|
43
|
-
import keyring
|
|
44
|
-
except ImportError as e:
|
|
45
|
-
raise CredentialsStorageError(
|
|
46
|
-
"keyring package is required for keyring storage. "
|
|
47
|
-
"Install it with: pip install keyring"
|
|
48
|
-
) from e
|
|
49
|
-
|
|
50
|
-
try:
|
|
51
|
-
logger.debug(
|
|
52
|
-
"credentials_load_start",
|
|
53
|
-
source="keyring",
|
|
54
|
-
service_name=self.service_name,
|
|
55
|
-
)
|
|
56
|
-
password = keyring.get_password(self.service_name, self.username)
|
|
57
|
-
|
|
58
|
-
if password is None:
|
|
59
|
-
logger.debug(
|
|
60
|
-
"credentials_not_found",
|
|
61
|
-
source="keyring",
|
|
62
|
-
service_name=self.service_name,
|
|
63
|
-
)
|
|
64
|
-
return None
|
|
65
|
-
|
|
66
|
-
# Parse the stored JSON
|
|
67
|
-
data = json.loads(password)
|
|
68
|
-
credentials = ClaudeCredentials.model_validate(data)
|
|
69
|
-
|
|
70
|
-
self._log_credential_details(credentials)
|
|
71
|
-
return credentials
|
|
72
|
-
|
|
73
|
-
except json.JSONDecodeError as e:
|
|
74
|
-
raise CredentialsStorageError(
|
|
75
|
-
f"Failed to parse credentials from keyring: {e}"
|
|
76
|
-
) from e
|
|
77
|
-
except Exception as e:
|
|
78
|
-
raise CredentialsStorageError(
|
|
79
|
-
f"Error loading credentials from keyring: {e}"
|
|
80
|
-
) from e
|
|
81
|
-
|
|
82
|
-
def _log_credential_details(self, credentials: ClaudeCredentials) -> None:
|
|
83
|
-
"""Log credential details safely."""
|
|
84
|
-
oauth_token = credentials.claude_ai_oauth
|
|
85
|
-
logger.debug(
|
|
86
|
-
"credentials_load_completed",
|
|
87
|
-
source="keyring",
|
|
88
|
-
subscription_type=oauth_token.subscription_type,
|
|
89
|
-
expires_at=str(oauth_token.expires_at_datetime),
|
|
90
|
-
is_expired=oauth_token.is_expired,
|
|
91
|
-
scopes=oauth_token.scopes,
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
async def save(self, credentials: ClaudeCredentials) -> bool:
|
|
95
|
-
"""Save credentials to the OS keyring.
|
|
96
|
-
|
|
97
|
-
Args:
|
|
98
|
-
credentials: Credentials to save
|
|
99
|
-
|
|
100
|
-
Returns:
|
|
101
|
-
True if saved successfully, False otherwise
|
|
102
|
-
|
|
103
|
-
Raises:
|
|
104
|
-
CredentialsStorageError: If there's an error writing to keyring
|
|
105
|
-
"""
|
|
106
|
-
try:
|
|
107
|
-
import keyring
|
|
108
|
-
except ImportError as e:
|
|
109
|
-
raise CredentialsStorageError(
|
|
110
|
-
"keyring package is required for keyring storage. "
|
|
111
|
-
"Install it with: pip install keyring"
|
|
112
|
-
) from e
|
|
113
|
-
|
|
114
|
-
try:
|
|
115
|
-
# Convert to JSON string
|
|
116
|
-
data = credentials.model_dump(by_alias=True)
|
|
117
|
-
json_data = json.dumps(data)
|
|
118
|
-
|
|
119
|
-
# Store in keyring
|
|
120
|
-
keyring.set_password(self.service_name, self.username, json_data)
|
|
121
|
-
|
|
122
|
-
logger.debug(
|
|
123
|
-
"credentials_save_completed",
|
|
124
|
-
source="keyring",
|
|
125
|
-
service_name=self.service_name,
|
|
126
|
-
)
|
|
127
|
-
return True
|
|
128
|
-
|
|
129
|
-
except Exception as e:
|
|
130
|
-
raise CredentialsStorageError(
|
|
131
|
-
f"Error saving credentials to keyring: {e}"
|
|
132
|
-
) from e
|
|
133
|
-
|
|
134
|
-
async def exists(self) -> bool:
|
|
135
|
-
"""Check if credentials exist in the keyring.
|
|
136
|
-
|
|
137
|
-
Returns:
|
|
138
|
-
True if credentials exist, False otherwise
|
|
139
|
-
"""
|
|
140
|
-
try:
|
|
141
|
-
import keyring
|
|
142
|
-
except ImportError:
|
|
143
|
-
return False
|
|
144
|
-
|
|
145
|
-
try:
|
|
146
|
-
password = keyring.get_password(self.service_name, self.username)
|
|
147
|
-
return password is not None
|
|
148
|
-
except Exception:
|
|
149
|
-
return False
|
|
150
|
-
|
|
151
|
-
async def delete(self) -> bool:
|
|
152
|
-
"""Delete credentials from the keyring.
|
|
153
|
-
|
|
154
|
-
Returns:
|
|
155
|
-
True if deleted successfully, False otherwise
|
|
156
|
-
|
|
157
|
-
Raises:
|
|
158
|
-
CredentialsStorageError: If there's an error deleting from keyring
|
|
159
|
-
"""
|
|
160
|
-
try:
|
|
161
|
-
import keyring
|
|
162
|
-
except ImportError as e:
|
|
163
|
-
raise CredentialsStorageError(
|
|
164
|
-
"keyring package is required for keyring storage. "
|
|
165
|
-
"Install it with: pip install keyring"
|
|
166
|
-
) from e
|
|
167
|
-
|
|
168
|
-
try:
|
|
169
|
-
if await self.exists():
|
|
170
|
-
keyring.delete_password(self.service_name, self.username)
|
|
171
|
-
logger.debug(
|
|
172
|
-
"credentials_delete_completed",
|
|
173
|
-
source="keyring",
|
|
174
|
-
service_name=self.service_name,
|
|
175
|
-
)
|
|
176
|
-
return True
|
|
177
|
-
return False
|
|
178
|
-
except Exception as e:
|
|
179
|
-
raise CredentialsStorageError(
|
|
180
|
-
f"Error deleting credentials from keyring: {e}"
|
|
181
|
-
) from e
|
|
182
|
-
|
|
183
|
-
def get_location(self) -> str:
|
|
184
|
-
"""Get the storage location description.
|
|
185
|
-
|
|
186
|
-
Returns:
|
|
187
|
-
Description of the keyring storage location
|
|
188
|
-
"""
|
|
189
|
-
return f"OS keyring (service: {self.service_name}, user: {self.username})"
|
ccproxy/claude_sdk/__init__.py
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"""Claude SDK integration module."""
|
|
2
|
-
|
|
3
|
-
from .client import ClaudeSDKClient
|
|
4
|
-
from .converter import MessageConverter
|
|
5
|
-
from .exceptions import ClaudeSDKError, StreamTimeoutError
|
|
6
|
-
from .options import OptionsHandler
|
|
7
|
-
from .parser import parse_formatted_sdk_content
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
__all__ = [
|
|
11
|
-
# Session Context will be imported here once created
|
|
12
|
-
"ClaudeSDKClient",
|
|
13
|
-
"ClaudeSDKError",
|
|
14
|
-
"StreamTimeoutError",
|
|
15
|
-
"MessageConverter",
|
|
16
|
-
"OptionsHandler",
|
|
17
|
-
"parse_formatted_sdk_content",
|
|
18
|
-
]
|
ccproxy/claude_sdk/options.py
DELETED
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
"""Options handling for Claude SDK interactions."""
|
|
2
|
-
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
import structlog
|
|
6
|
-
|
|
7
|
-
from ccproxy.config.settings import Settings
|
|
8
|
-
from ccproxy.core.async_utils import patched_typing
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
with patched_typing():
|
|
12
|
-
from claude_code_sdk import ClaudeCodeOptions
|
|
13
|
-
|
|
14
|
-
logger = structlog.get_logger(__name__)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class OptionsHandler:
|
|
18
|
-
"""
|
|
19
|
-
Handles creation and management of Claude SDK options.
|
|
20
|
-
"""
|
|
21
|
-
|
|
22
|
-
def __init__(self, settings: Settings | None = None) -> None:
|
|
23
|
-
"""
|
|
24
|
-
Initialize options handler.
|
|
25
|
-
|
|
26
|
-
Args:
|
|
27
|
-
settings: Application settings containing default Claude options
|
|
28
|
-
"""
|
|
29
|
-
self.settings = settings
|
|
30
|
-
|
|
31
|
-
def create_options(
|
|
32
|
-
self,
|
|
33
|
-
model: str,
|
|
34
|
-
temperature: float | None = None,
|
|
35
|
-
max_tokens: int | None = None,
|
|
36
|
-
system_message: str | None = None,
|
|
37
|
-
**additional_options: Any,
|
|
38
|
-
) -> ClaudeCodeOptions:
|
|
39
|
-
"""
|
|
40
|
-
Create Claude SDK options from API parameters.
|
|
41
|
-
|
|
42
|
-
Args:
|
|
43
|
-
model: The model name
|
|
44
|
-
temperature: Temperature for response generation
|
|
45
|
-
max_tokens: Maximum tokens in response
|
|
46
|
-
system_message: System message to include
|
|
47
|
-
**additional_options: Additional options to set on the ClaudeCodeOptions instance
|
|
48
|
-
|
|
49
|
-
Returns:
|
|
50
|
-
Configured ClaudeCodeOptions instance
|
|
51
|
-
"""
|
|
52
|
-
# Start with configured defaults if available, otherwise create fresh instance
|
|
53
|
-
if self.settings and self.settings.claude.code_options:
|
|
54
|
-
# Use the configured options as base - this preserves all default settings
|
|
55
|
-
# including complex objects like mcp_servers and permission_prompt_tool_name
|
|
56
|
-
configured_opts = self.settings.claude.code_options
|
|
57
|
-
|
|
58
|
-
# Create a new instance with the same configuration
|
|
59
|
-
# We need to extract the configuration values properly with type safety
|
|
60
|
-
|
|
61
|
-
# Extract configuration values with proper types
|
|
62
|
-
mcp_servers = (
|
|
63
|
-
configured_opts.mcp_servers.copy()
|
|
64
|
-
if isinstance(configured_opts.mcp_servers, dict)
|
|
65
|
-
else {}
|
|
66
|
-
)
|
|
67
|
-
permission_prompt_tool_name = configured_opts.permission_prompt_tool_name
|
|
68
|
-
max_thinking_tokens = getattr(configured_opts, "max_thinking_tokens", None)
|
|
69
|
-
allowed_tools = getattr(configured_opts, "allowed_tools", None)
|
|
70
|
-
disallowed_tools = getattr(configured_opts, "disallowed_tools", None)
|
|
71
|
-
cwd = getattr(configured_opts, "cwd", None)
|
|
72
|
-
append_system_prompt = getattr(
|
|
73
|
-
configured_opts, "append_system_prompt", None
|
|
74
|
-
)
|
|
75
|
-
max_turns = getattr(configured_opts, "max_turns", None)
|
|
76
|
-
continue_conversation = getattr(
|
|
77
|
-
configured_opts, "continue_conversation", None
|
|
78
|
-
)
|
|
79
|
-
permission_mode = getattr(configured_opts, "permission_mode", None)
|
|
80
|
-
|
|
81
|
-
# Build ClaudeCodeOptions with proper type handling
|
|
82
|
-
# Start with a basic instance and set attributes individually for type safety
|
|
83
|
-
options = ClaudeCodeOptions(
|
|
84
|
-
mcp_servers=mcp_servers,
|
|
85
|
-
permission_prompt_tool_name=permission_prompt_tool_name,
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
# Set additional attributes if they exist and are not None
|
|
89
|
-
if max_thinking_tokens is not None:
|
|
90
|
-
options.max_thinking_tokens = int(max_thinking_tokens)
|
|
91
|
-
if allowed_tools is not None:
|
|
92
|
-
options.allowed_tools = list(allowed_tools)
|
|
93
|
-
if disallowed_tools is not None:
|
|
94
|
-
options.disallowed_tools = list(disallowed_tools)
|
|
95
|
-
if cwd is not None:
|
|
96
|
-
options.cwd = cwd
|
|
97
|
-
if append_system_prompt is not None:
|
|
98
|
-
options.append_system_prompt = append_system_prompt
|
|
99
|
-
if max_turns is not None:
|
|
100
|
-
options.max_turns = max_turns
|
|
101
|
-
if continue_conversation is not None:
|
|
102
|
-
options.continue_conversation = bool(continue_conversation)
|
|
103
|
-
if permission_mode is not None:
|
|
104
|
-
options.permission_mode = permission_mode
|
|
105
|
-
else:
|
|
106
|
-
options = ClaudeCodeOptions()
|
|
107
|
-
|
|
108
|
-
# Override the model (API parameter takes precedence)
|
|
109
|
-
options.model = model
|
|
110
|
-
|
|
111
|
-
# Apply system message if provided (this is supported by ClaudeCodeOptions)
|
|
112
|
-
if system_message is not None:
|
|
113
|
-
options.system_prompt = system_message
|
|
114
|
-
|
|
115
|
-
# If session_id is provided via additional_options, enable continue_conversation
|
|
116
|
-
# This ensures conversation continuity when using session IDs
|
|
117
|
-
if additional_options.get("session_id"):
|
|
118
|
-
options.continue_conversation = True
|
|
119
|
-
|
|
120
|
-
# Note: temperature and max_tokens are API-level parameters, not ClaudeCodeOptions parameters
|
|
121
|
-
# These are handled at the API request level, not in the options object
|
|
122
|
-
|
|
123
|
-
# Handle additional options as needed
|
|
124
|
-
for key, value in additional_options.items():
|
|
125
|
-
if hasattr(options, key):
|
|
126
|
-
setattr(options, key, value)
|
|
127
|
-
|
|
128
|
-
return options
|
|
129
|
-
|
|
130
|
-
@staticmethod
|
|
131
|
-
def extract_system_message(messages: list[dict[str, Any]]) -> str | None:
|
|
132
|
-
"""
|
|
133
|
-
Extract system message from Anthropic messages format.
|
|
134
|
-
|
|
135
|
-
Args:
|
|
136
|
-
messages: List of messages in Anthropic format
|
|
137
|
-
|
|
138
|
-
Returns:
|
|
139
|
-
System message content if found, None otherwise
|
|
140
|
-
"""
|
|
141
|
-
for message in messages:
|
|
142
|
-
if message.get("role") == "system":
|
|
143
|
-
content = message.get("content", "")
|
|
144
|
-
if isinstance(content, list):
|
|
145
|
-
# Handle content blocks
|
|
146
|
-
text_parts = []
|
|
147
|
-
for block in content:
|
|
148
|
-
if block.get("type") == "text":
|
|
149
|
-
text_parts.append(block.get("text", ""))
|
|
150
|
-
return " ".join(text_parts)
|
|
151
|
-
return str(content)
|
|
152
|
-
return None
|
|
153
|
-
|
|
154
|
-
@staticmethod
|
|
155
|
-
def get_supported_models() -> list[str]:
|
|
156
|
-
"""
|
|
157
|
-
Get list of supported Claude models.
|
|
158
|
-
|
|
159
|
-
Returns:
|
|
160
|
-
List of supported model names
|
|
161
|
-
"""
|
|
162
|
-
# Import here to avoid circular imports
|
|
163
|
-
from ccproxy.utils.model_mapping import get_supported_claude_models
|
|
164
|
-
|
|
165
|
-
# Get supported Claude models
|
|
166
|
-
claude_models = get_supported_claude_models()
|
|
167
|
-
return claude_models
|
|
168
|
-
|
|
169
|
-
@staticmethod
|
|
170
|
-
def validate_model(model: str) -> bool:
|
|
171
|
-
"""
|
|
172
|
-
Validate if a model is supported.
|
|
173
|
-
|
|
174
|
-
Args:
|
|
175
|
-
model: The model name to validate
|
|
176
|
-
|
|
177
|
-
Returns:
|
|
178
|
-
True if supported, False otherwise
|
|
179
|
-
"""
|
|
180
|
-
return model in OptionsHandler.get_supported_models()
|
|
181
|
-
|
|
182
|
-
@staticmethod
|
|
183
|
-
def get_default_options() -> dict[str, Any]:
|
|
184
|
-
"""
|
|
185
|
-
Get default options for API parameters.
|
|
186
|
-
|
|
187
|
-
Returns:
|
|
188
|
-
Dictionary of default API parameter values
|
|
189
|
-
"""
|
|
190
|
-
return {
|
|
191
|
-
"model": "claude-3-5-sonnet-20241022",
|
|
192
|
-
"temperature": 0.7,
|
|
193
|
-
"max_tokens": 4000,
|
|
194
|
-
}
|