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
|
@@ -8,15 +8,16 @@ import uuid
|
|
|
8
8
|
from collections.abc import AsyncIterator
|
|
9
9
|
from typing import Any
|
|
10
10
|
|
|
11
|
-
import
|
|
11
|
+
from ccproxy.core.async_task_manager import create_managed_task
|
|
12
|
+
from ccproxy.core.logging import get_plugin_logger
|
|
12
13
|
|
|
13
|
-
from
|
|
14
|
-
from
|
|
15
|
-
from
|
|
16
|
-
from
|
|
14
|
+
from .config import SessionPoolSettings
|
|
15
|
+
from .message_queue import QueueListener
|
|
16
|
+
from .session_client import SessionClient
|
|
17
|
+
from .stream_worker import StreamWorker, WorkerStatus
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
logger =
|
|
20
|
+
logger = get_plugin_logger()
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class StreamHandle:
|
|
@@ -73,19 +74,39 @@ class StreamHandle:
|
|
|
73
74
|
async def create_listener(self) -> AsyncIterator[Any]:
|
|
74
75
|
"""Create a new listener for this stream.
|
|
75
76
|
|
|
76
|
-
This method
|
|
77
|
-
|
|
77
|
+
This method creates the worker if needed, pre-registers the listener,
|
|
78
|
+
then starts the worker. This ordering prevents race conditions where
|
|
79
|
+
fast STDIO tools could return results before the listener was ready.
|
|
78
80
|
|
|
79
81
|
Yields:
|
|
80
82
|
Messages from the stream
|
|
81
83
|
"""
|
|
82
|
-
#
|
|
83
|
-
|
|
84
|
+
# Create worker if needed (but don't start yet)
|
|
85
|
+
async with self._worker_lock:
|
|
86
|
+
if self._worker is None:
|
|
87
|
+
worker_id = f"{self.handle_id}-worker"
|
|
88
|
+
self._worker = StreamWorker(
|
|
89
|
+
worker_id=worker_id,
|
|
90
|
+
message_iterator=self._message_iterator,
|
|
91
|
+
session_id=self.session_id,
|
|
92
|
+
request_id=self.request_id,
|
|
93
|
+
session_client=self._session_client,
|
|
94
|
+
stream_handle=self,
|
|
95
|
+
)
|
|
96
|
+
logger.debug(
|
|
97
|
+
"stream_handle_worker_created",
|
|
98
|
+
handle_id=self.handle_id,
|
|
99
|
+
worker_id=worker_id,
|
|
100
|
+
session_id=self.session_id,
|
|
101
|
+
category="streaming",
|
|
102
|
+
)
|
|
84
103
|
|
|
85
104
|
if not self._worker:
|
|
86
|
-
raise RuntimeError("Failed to
|
|
105
|
+
raise RuntimeError("Failed to create stream worker")
|
|
87
106
|
|
|
88
|
-
#
|
|
107
|
+
# Pre-register listener BEFORE starting worker
|
|
108
|
+
# This fixes the race condition where fast STDIO tools could
|
|
109
|
+
# return results before the listener was ready
|
|
89
110
|
queue = self._worker.get_message_queue()
|
|
90
111
|
listener = await queue.create_listener()
|
|
91
112
|
self._listeners[listener.listener_id] = listener
|
|
@@ -93,12 +114,16 @@ class StreamHandle:
|
|
|
93
114
|
if self._first_listener_at is None:
|
|
94
115
|
self._first_listener_at = time.time()
|
|
95
116
|
|
|
117
|
+
# NOW start the worker (after listener is registered)
|
|
118
|
+
await self._worker.start()
|
|
119
|
+
|
|
96
120
|
logger.debug(
|
|
97
121
|
"stream_handle_listener_created",
|
|
98
122
|
handle_id=self.handle_id,
|
|
99
123
|
listener_id=listener.listener_id,
|
|
100
124
|
total_listeners=len(self._listeners),
|
|
101
125
|
worker_status=self._worker.status.value,
|
|
126
|
+
category="streaming",
|
|
102
127
|
)
|
|
103
128
|
|
|
104
129
|
try:
|
|
@@ -133,31 +158,6 @@ class StreamHandle:
|
|
|
133
158
|
# Check if we should trigger cleanup
|
|
134
159
|
await self._check_cleanup()
|
|
135
160
|
|
|
136
|
-
async def _ensure_worker_started(self) -> None:
|
|
137
|
-
"""Ensure the worker is started, creating it if needed."""
|
|
138
|
-
async with self._worker_lock:
|
|
139
|
-
if self._worker is None:
|
|
140
|
-
# Create worker
|
|
141
|
-
worker_id = f"{self.handle_id}-worker"
|
|
142
|
-
self._worker = StreamWorker(
|
|
143
|
-
worker_id=worker_id,
|
|
144
|
-
message_iterator=self._message_iterator,
|
|
145
|
-
session_id=self.session_id,
|
|
146
|
-
request_id=self.request_id,
|
|
147
|
-
session_client=self._session_client,
|
|
148
|
-
stream_handle=self, # Pass self for message tracking
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
# Start worker
|
|
152
|
-
await self._worker.start()
|
|
153
|
-
|
|
154
|
-
logger.debug(
|
|
155
|
-
"stream_handle_worker_created",
|
|
156
|
-
handle_id=self.handle_id,
|
|
157
|
-
worker_id=worker_id,
|
|
158
|
-
session_id=self.session_id,
|
|
159
|
-
)
|
|
160
|
-
|
|
161
161
|
async def _remove_listener(self, listener_id: str) -> None:
|
|
162
162
|
"""Remove a listener and clean it up.
|
|
163
163
|
|
|
@@ -177,6 +177,7 @@ class StreamHandle:
|
|
|
177
177
|
handle_id=self.handle_id,
|
|
178
178
|
listener_id=listener_id,
|
|
179
179
|
remaining_listeners=len(self._listeners),
|
|
180
|
+
category="streaming",
|
|
180
181
|
)
|
|
181
182
|
|
|
182
183
|
async def _check_cleanup(self) -> None:
|
|
@@ -220,7 +221,7 @@ class StreamHandle:
|
|
|
220
221
|
)
|
|
221
222
|
# Still stop the worker to ensure cleanup
|
|
222
223
|
if self._worker:
|
|
223
|
-
logger.
|
|
224
|
+
logger.trace(
|
|
224
225
|
"stream_handle_stopping_worker_direct",
|
|
225
226
|
handle_id=self.handle_id,
|
|
226
227
|
message="Stopping worker directly since SDK interrupt not needed",
|
|
@@ -245,7 +246,11 @@ class StreamHandle:
|
|
|
245
246
|
# Schedule interrupt using a background task with timeout control
|
|
246
247
|
try:
|
|
247
248
|
# Create a background task with proper timeout and error handling
|
|
248
|
-
|
|
249
|
+
await create_managed_task(
|
|
250
|
+
self._safe_interrupt_with_timeout(),
|
|
251
|
+
name=f"stream_interrupt_{self.handle_id}",
|
|
252
|
+
creator="StreamHandle",
|
|
253
|
+
)
|
|
249
254
|
logger.debug(
|
|
250
255
|
"stream_handle_interrupt_scheduled",
|
|
251
256
|
handle_id=self.handle_id,
|
|
@@ -295,7 +300,7 @@ class StreamHandle:
|
|
|
295
300
|
|
|
296
301
|
# Stop our worker after SDK interrupt to ensure it's not blocking the session
|
|
297
302
|
if self._worker:
|
|
298
|
-
logger.
|
|
303
|
+
logger.trace(
|
|
299
304
|
"stream_handle_stopping_worker_after_interrupt",
|
|
300
305
|
handle_id=self.handle_id,
|
|
301
306
|
message="Stopping worker to free up session for reuse",
|
|
@@ -319,7 +324,7 @@ class StreamHandle:
|
|
|
319
324
|
|
|
320
325
|
# Fallback: Stop our worker manually if SDK interrupt timed out
|
|
321
326
|
if self._worker:
|
|
322
|
-
logger.
|
|
327
|
+
logger.trace(
|
|
323
328
|
"stream_handle_fallback_worker_stop",
|
|
324
329
|
handle_id=self.handle_id,
|
|
325
330
|
message="SDK interrupt timed out, stopping worker as fallback",
|
|
@@ -345,7 +350,7 @@ class StreamHandle:
|
|
|
345
350
|
|
|
346
351
|
# Fallback: Stop our worker manually if SDK interrupt failed
|
|
347
352
|
if self._worker:
|
|
348
|
-
logger.
|
|
353
|
+
logger.trace(
|
|
349
354
|
"stream_handle_fallback_worker_stop_after_error",
|
|
350
355
|
handle_id=self.handle_id,
|
|
351
356
|
message="SDK interrupt failed, stopping worker as fallback",
|
|
@@ -389,7 +394,7 @@ class StreamHandle:
|
|
|
389
394
|
listener.close()
|
|
390
395
|
self._listeners.clear()
|
|
391
396
|
|
|
392
|
-
logger.
|
|
397
|
+
logger.trace(
|
|
393
398
|
"stream_handle_interrupted",
|
|
394
399
|
handle_id=self.handle_id,
|
|
395
400
|
)
|
|
@@ -8,18 +8,19 @@ from collections.abc import AsyncIterator
|
|
|
8
8
|
from enum import Enum
|
|
9
9
|
from typing import TYPE_CHECKING, Any
|
|
10
10
|
|
|
11
|
-
import
|
|
11
|
+
from ccproxy.core.async_task_manager import create_managed_task
|
|
12
|
+
from ccproxy.core.logging import get_plugin_logger
|
|
12
13
|
|
|
13
|
-
from
|
|
14
|
-
from
|
|
15
|
-
from
|
|
14
|
+
from . import models as sdk_models
|
|
15
|
+
from .exceptions import StreamTimeoutError
|
|
16
|
+
from .message_queue import MessageQueue
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
if TYPE_CHECKING:
|
|
19
|
-
from
|
|
20
|
-
from
|
|
20
|
+
from .session_client import SessionClient
|
|
21
|
+
from .stream_handle import StreamHandle
|
|
21
22
|
|
|
22
|
-
logger =
|
|
23
|
+
logger = get_plugin_logger()
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
class WorkerStatus(str, Enum):
|
|
@@ -35,7 +36,7 @@ class WorkerStatus(str, Enum):
|
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
class StreamWorker:
|
|
38
|
-
"""Worker that consumes
|
|
39
|
+
"""Worker that consumes messa`es from Claude SDK and distributes via queue."""
|
|
39
40
|
|
|
40
41
|
def __init__(
|
|
41
42
|
self,
|
|
@@ -90,7 +91,11 @@ class StreamWorker:
|
|
|
90
91
|
self._started_at = time.time()
|
|
91
92
|
|
|
92
93
|
# Create worker task
|
|
93
|
-
self._worker_task =
|
|
94
|
+
self._worker_task = await create_managed_task(
|
|
95
|
+
self._run_worker(),
|
|
96
|
+
name=f"stream_worker_{self.worker_id}",
|
|
97
|
+
creator="StreamWorker",
|
|
98
|
+
)
|
|
94
99
|
|
|
95
100
|
logger.debug(
|
|
96
101
|
"stream_worker_started",
|
|
@@ -189,13 +194,13 @@ class StreamWorker:
|
|
|
189
194
|
self._total_messages += 1
|
|
190
195
|
self._last_message_time = time.time()
|
|
191
196
|
|
|
192
|
-
#
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
delivered_count = await self._message_queue.broadcast(message)
|
|
196
|
-
self._messages_delivered += delivered_count
|
|
197
|
+
# Always broadcast - the queue handles no-listeners case atomically
|
|
198
|
+
# Previous bug: Separate has_listeners() check was racy with fast STDIO tools
|
|
199
|
+
delivered_count = await self._message_queue.broadcast(message)
|
|
197
200
|
|
|
198
|
-
|
|
201
|
+
if delivered_count > 0:
|
|
202
|
+
self._messages_delivered += delivered_count
|
|
203
|
+
logger.trace(
|
|
199
204
|
"stream_worker_message_delivered",
|
|
200
205
|
worker_id=self.worker_id,
|
|
201
206
|
message_type=type(message).__name__,
|
|
@@ -203,10 +208,9 @@ class StreamWorker:
|
|
|
203
208
|
total_messages=self._total_messages,
|
|
204
209
|
)
|
|
205
210
|
else:
|
|
206
|
-
# No listeners -
|
|
211
|
+
# No listeners at broadcast time - message discarded
|
|
207
212
|
self._messages_discarded += 1
|
|
208
|
-
|
|
209
|
-
logger.debug(
|
|
213
|
+
logger.trace(
|
|
210
214
|
"stream_worker_message_discarded",
|
|
211
215
|
worker_id=self.worker_id,
|
|
212
216
|
message_type=type(message).__name__,
|
|
@@ -4,16 +4,17 @@ from collections.abc import AsyncIterator
|
|
|
4
4
|
from typing import Any
|
|
5
5
|
from uuid import uuid4
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
from ccproxy.core.logging import get_plugin_logger
|
|
8
|
+
from ccproxy.core.request_context import RequestContext
|
|
8
9
|
|
|
9
|
-
from ccproxy.
|
|
10
|
-
from
|
|
11
|
-
from
|
|
12
|
-
from
|
|
13
|
-
from
|
|
10
|
+
# from ccproxy.observability.metrics import # Metrics moved to plugin PrometheusMetrics
|
|
11
|
+
from . import models as sdk_models
|
|
12
|
+
from .config import SDKMessageMode
|
|
13
|
+
from .converter import MessageConverter
|
|
14
|
+
from .hooks import ClaudeSDKStreamingHook
|
|
14
15
|
|
|
15
16
|
|
|
16
|
-
logger =
|
|
17
|
+
logger = get_plugin_logger()
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
class ClaudeStreamProcessor:
|
|
@@ -22,16 +23,19 @@ class ClaudeStreamProcessor:
|
|
|
22
23
|
def __init__(
|
|
23
24
|
self,
|
|
24
25
|
message_converter: MessageConverter,
|
|
25
|
-
metrics:
|
|
26
|
+
metrics: Any | None = None, # Metrics now handled by metrics plugin
|
|
27
|
+
streaming_hook: ClaudeSDKStreamingHook | None = None,
|
|
26
28
|
) -> None:
|
|
27
29
|
"""Initialize the stream processor.
|
|
28
30
|
|
|
29
31
|
Args:
|
|
30
32
|
message_converter: Converter for message formats.
|
|
31
|
-
metrics:
|
|
33
|
+
metrics: Optional metrics handler.
|
|
34
|
+
streaming_hook: Hook for emitting streaming events.
|
|
32
35
|
"""
|
|
33
36
|
self.message_converter = message_converter
|
|
34
37
|
self.metrics = metrics
|
|
38
|
+
self.streaming_hook = streaming_hook
|
|
35
39
|
|
|
36
40
|
async def process_stream(
|
|
37
41
|
self,
|
|
@@ -72,7 +76,7 @@ class ClaudeStreamProcessor:
|
|
|
72
76
|
yield chunk
|
|
73
77
|
|
|
74
78
|
async for message in sdk_stream:
|
|
75
|
-
logger.
|
|
79
|
+
logger.trace(
|
|
76
80
|
"sdk_message_received",
|
|
77
81
|
message_type=type(message).__name__,
|
|
78
82
|
request_id=request_id,
|
|
@@ -82,7 +86,7 @@ class ClaudeStreamProcessor:
|
|
|
82
86
|
)
|
|
83
87
|
|
|
84
88
|
if isinstance(message, sdk_models.SystemMessage):
|
|
85
|
-
logger.
|
|
89
|
+
logger.trace(
|
|
86
90
|
"sdk_system_message_processing",
|
|
87
91
|
mode=sdk_message_mode.value,
|
|
88
92
|
subtype=message.subtype,
|
|
@@ -109,7 +113,7 @@ class ClaudeStreamProcessor:
|
|
|
109
113
|
)
|
|
110
114
|
for block in message.content:
|
|
111
115
|
if isinstance(block, sdk_models.TextBlock):
|
|
112
|
-
logger.
|
|
116
|
+
logger.trace(
|
|
113
117
|
"sdk_text_block_processing",
|
|
114
118
|
text_length=len(block.text),
|
|
115
119
|
text_preview=block.text[:50],
|
|
@@ -139,7 +143,7 @@ class ClaudeStreamProcessor:
|
|
|
139
143
|
mode=sdk_message_mode.value,
|
|
140
144
|
request_id=request_id,
|
|
141
145
|
)
|
|
142
|
-
logger.
|
|
146
|
+
logger.debug(
|
|
143
147
|
"sdk_tool_use_block",
|
|
144
148
|
tool_id=block.id,
|
|
145
149
|
tool_name=block.name,
|
|
@@ -176,7 +180,7 @@ class ClaudeStreamProcessor:
|
|
|
176
180
|
mode=sdk_message_mode.value,
|
|
177
181
|
request_id=request_id,
|
|
178
182
|
)
|
|
179
|
-
logger.
|
|
183
|
+
logger.debug(
|
|
180
184
|
"sdk_tool_result_block",
|
|
181
185
|
tool_use_id=block.tool_use_id,
|
|
182
186
|
is_error=block.is_error,
|
|
@@ -287,6 +291,32 @@ class ClaudeStreamProcessor:
|
|
|
287
291
|
num_turns=message.num_turns,
|
|
288
292
|
)
|
|
289
293
|
|
|
294
|
+
# Emit PROVIDER_STREAM_END hook with usage metrics
|
|
295
|
+
if self.streaming_hook and message.usage:
|
|
296
|
+
usage_metrics = {
|
|
297
|
+
"tokens_input": message.usage_model.input_tokens,
|
|
298
|
+
"tokens_output": message.usage_model.output_tokens,
|
|
299
|
+
"cache_read_tokens": message.usage_model.cache_read_input_tokens,
|
|
300
|
+
"cache_write_tokens": message.usage_model.cache_creation_input_tokens,
|
|
301
|
+
"cost_usd": message.total_cost_usd,
|
|
302
|
+
"model": getattr(
|
|
303
|
+
message, "model", "claude-3-5-sonnet-20241022"
|
|
304
|
+
),
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
# Emit the hook asynchronously
|
|
308
|
+
import asyncio
|
|
309
|
+
|
|
310
|
+
asyncio.create_task(
|
|
311
|
+
self.streaming_hook.emit_stream_end(
|
|
312
|
+
request_id=str(request_id or ""),
|
|
313
|
+
usage_metrics=usage_metrics,
|
|
314
|
+
provider="claude_sdk",
|
|
315
|
+
url="claude-sdk://direct",
|
|
316
|
+
method="POST",
|
|
317
|
+
)
|
|
318
|
+
)
|
|
319
|
+
|
|
290
320
|
end_chunks = self.message_converter.create_streaming_end_chunks(
|
|
291
321
|
stop_reason=message.stop_reason
|
|
292
322
|
)
|
|
@@ -300,7 +330,7 @@ class ClaudeStreamProcessor:
|
|
|
300
330
|
yield end_chunks[1][1] # message_stop
|
|
301
331
|
break # End of stream
|
|
302
332
|
else:
|
|
303
|
-
logger.warning(
|
|
333
|
+
logger.warning(
|
|
304
334
|
"sdk_unknown_message_type",
|
|
305
335
|
message_type=type(message).__name__,
|
|
306
336
|
message_content=str(message)[:200],
|
|
@@ -325,4 +355,8 @@ class ClaudeStreamProcessor:
|
|
|
325
355
|
# NOTE: Access logging is now handled by StreamingResponseWithLogging
|
|
326
356
|
# No need for manual access logging here anymore
|
|
327
357
|
|
|
328
|
-
logger.
|
|
358
|
+
logger.info(
|
|
359
|
+
"streaming_complete",
|
|
360
|
+
plugin="claude_sdk",
|
|
361
|
+
request_id=request_id,
|
|
362
|
+
)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Scheduled tasks for Claude SDK plugin."""
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any
|
|
4
|
+
|
|
5
|
+
from ccproxy.core.logging import get_plugin_logger
|
|
6
|
+
from ccproxy.scheduler.tasks import BaseScheduledTask
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from .detection_service import ClaudeSDKDetectionService
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
logger = get_plugin_logger()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ClaudeSDKDetectionRefreshTask(BaseScheduledTask):
|
|
17
|
+
"""Task to periodically refresh Claude CLI detection."""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
name: str,
|
|
22
|
+
interval_seconds: float,
|
|
23
|
+
detection_service: "ClaudeSDKDetectionService",
|
|
24
|
+
enabled: bool = True,
|
|
25
|
+
skip_initial_run: bool = True,
|
|
26
|
+
**kwargs: Any,
|
|
27
|
+
):
|
|
28
|
+
"""Initialize the detection refresh task.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
name: Task name
|
|
32
|
+
interval_seconds: How often to run the task
|
|
33
|
+
detection_service: Claude CLI detection service
|
|
34
|
+
enabled: Whether the task is enabled
|
|
35
|
+
skip_initial_run: Whether to skip the first run
|
|
36
|
+
**kwargs: Additional task arguments
|
|
37
|
+
"""
|
|
38
|
+
super().__init__(
|
|
39
|
+
name=name,
|
|
40
|
+
interval_seconds=interval_seconds,
|
|
41
|
+
enabled=enabled,
|
|
42
|
+
**kwargs,
|
|
43
|
+
)
|
|
44
|
+
self.detection_service = detection_service
|
|
45
|
+
self.skip_initial_run = skip_initial_run
|
|
46
|
+
self._first_run = True
|
|
47
|
+
|
|
48
|
+
async def run(self) -> bool:
|
|
49
|
+
"""Execute the Claude CLI detection refresh.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
True if successful, False otherwise
|
|
53
|
+
"""
|
|
54
|
+
if self._first_run and self.skip_initial_run:
|
|
55
|
+
self._first_run = False
|
|
56
|
+
logger.debug(
|
|
57
|
+
"claude_sdk_detection_refresh_skipped_initial",
|
|
58
|
+
task_name=self.name,
|
|
59
|
+
)
|
|
60
|
+
return True
|
|
61
|
+
|
|
62
|
+
self._first_run = False
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
logger.debug(
|
|
66
|
+
"claude_sdk_detection_refresh_starting",
|
|
67
|
+
task_name=self.name,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Refresh Claude CLI detection
|
|
71
|
+
detection_data = await self.detection_service.initialize_detection()
|
|
72
|
+
|
|
73
|
+
logger.debug(
|
|
74
|
+
"claude_sdk_detection_refresh_completed",
|
|
75
|
+
task_name=self.name,
|
|
76
|
+
version=detection_data.claude_version or "unknown",
|
|
77
|
+
cli_command=detection_data.cli_command,
|
|
78
|
+
is_available=detection_data.is_available,
|
|
79
|
+
)
|
|
80
|
+
return True
|
|
81
|
+
|
|
82
|
+
except Exception as e:
|
|
83
|
+
logger.error(
|
|
84
|
+
"claude_sdk_detection_refresh_failed",
|
|
85
|
+
task_name=self.name,
|
|
86
|
+
error=str(e),
|
|
87
|
+
exc_info=e,
|
|
88
|
+
)
|
|
89
|
+
return False
|
|
90
|
+
|
|
91
|
+
async def setup(self) -> None:
|
|
92
|
+
"""Setup before task execution starts."""
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
async def cleanup(self) -> None:
|
|
96
|
+
"""Cleanup after task execution stops."""
|
|
97
|
+
pass
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Claude Shared Utilities
|
|
2
|
+
|
|
3
|
+
Shared metadata consumed by Claude provider plugins.
|
|
4
|
+
|
|
5
|
+
## Highlights
|
|
6
|
+
- Defines default `ModelCard` entries for common Claude model releases
|
|
7
|
+
- Declares mapping rules that translate OpenAI-style aliases to Claude IDs
|
|
8
|
+
|
|
9
|
+
## Configuration
|
|
10
|
+
- No standalone plugin configuration; these helpers are imported by
|
|
11
|
+
`claude_api` and `claude_sdk` settings modules.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
- Imported during Claude plugin configuration to seed model catalogs
|
|
15
|
+
- Extend by editing `DEFAULT_CLAUDE_MODEL_CARDS` or mapping rules
|
|
16
|
+
|
|
17
|
+
## Related Components
|
|
18
|
+
- `model_defaults.py`: source of cards and alias mapping helpers
|