ccproxy-api 0.1.7__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ccproxy/api/__init__.py +1 -15
- ccproxy/api/app.py +434 -219
- ccproxy/api/bootstrap.py +30 -0
- ccproxy/api/decorators.py +85 -0
- ccproxy/api/dependencies.py +144 -168
- ccproxy/api/format_validation.py +54 -0
- ccproxy/api/middleware/cors.py +6 -3
- ccproxy/api/middleware/errors.py +388 -524
- ccproxy/api/middleware/hooks.py +563 -0
- ccproxy/api/middleware/normalize_headers.py +59 -0
- ccproxy/api/middleware/request_id.py +35 -16
- ccproxy/api/middleware/streaming_hooks.py +292 -0
- ccproxy/api/routes/__init__.py +5 -14
- ccproxy/api/routes/health.py +39 -672
- ccproxy/api/routes/plugins.py +277 -0
- ccproxy/auth/__init__.py +2 -19
- ccproxy/auth/bearer.py +25 -15
- ccproxy/auth/dependencies.py +123 -157
- ccproxy/auth/exceptions.py +0 -12
- ccproxy/auth/manager.py +35 -49
- ccproxy/auth/managers/__init__.py +10 -0
- ccproxy/auth/managers/base.py +523 -0
- ccproxy/auth/managers/base_enhanced.py +63 -0
- ccproxy/auth/managers/token_snapshot.py +77 -0
- ccproxy/auth/models/base.py +65 -0
- ccproxy/auth/models/credentials.py +40 -0
- ccproxy/auth/oauth/__init__.py +4 -18
- ccproxy/auth/oauth/base.py +533 -0
- ccproxy/auth/oauth/cli_errors.py +37 -0
- ccproxy/auth/oauth/flows.py +430 -0
- ccproxy/auth/oauth/protocol.py +366 -0
- ccproxy/auth/oauth/registry.py +408 -0
- ccproxy/auth/oauth/router.py +396 -0
- ccproxy/auth/oauth/routes.py +186 -113
- ccproxy/auth/oauth/session.py +151 -0
- ccproxy/auth/oauth/templates.py +342 -0
- ccproxy/auth/storage/__init__.py +2 -5
- ccproxy/auth/storage/base.py +279 -5
- ccproxy/auth/storage/generic.py +134 -0
- ccproxy/cli/__init__.py +1 -2
- ccproxy/cli/_settings_help.py +351 -0
- ccproxy/cli/commands/auth.py +1519 -793
- ccproxy/cli/commands/config/commands.py +209 -276
- ccproxy/cli/commands/plugins.py +669 -0
- ccproxy/cli/commands/serve.py +75 -810
- ccproxy/cli/commands/status.py +254 -0
- ccproxy/cli/decorators.py +83 -0
- ccproxy/cli/helpers.py +22 -60
- ccproxy/cli/main.py +359 -10
- ccproxy/cli/options/claude_options.py +0 -25
- ccproxy/config/__init__.py +7 -11
- ccproxy/config/core.py +227 -0
- ccproxy/config/env_generator.py +232 -0
- ccproxy/config/runtime.py +67 -0
- ccproxy/config/security.py +36 -3
- ccproxy/config/settings.py +382 -441
- ccproxy/config/toml_generator.py +299 -0
- ccproxy/config/utils.py +452 -0
- ccproxy/core/__init__.py +7 -271
- ccproxy/{_version.py → core/_version.py} +16 -3
- ccproxy/core/async_task_manager.py +516 -0
- ccproxy/core/async_utils.py +47 -14
- ccproxy/core/auth/__init__.py +6 -0
- ccproxy/core/constants.py +16 -50
- ccproxy/core/errors.py +53 -0
- ccproxy/core/id_utils.py +20 -0
- ccproxy/core/interfaces.py +16 -123
- ccproxy/core/logging.py +473 -18
- ccproxy/core/plugins/__init__.py +77 -0
- ccproxy/core/plugins/cli_discovery.py +211 -0
- ccproxy/core/plugins/declaration.py +455 -0
- ccproxy/core/plugins/discovery.py +604 -0
- ccproxy/core/plugins/factories.py +967 -0
- ccproxy/core/plugins/hooks/__init__.py +30 -0
- ccproxy/core/plugins/hooks/base.py +58 -0
- ccproxy/core/plugins/hooks/events.py +46 -0
- ccproxy/core/plugins/hooks/implementations/__init__.py +16 -0
- ccproxy/core/plugins/hooks/implementations/formatters/__init__.py +11 -0
- ccproxy/core/plugins/hooks/implementations/formatters/json.py +552 -0
- ccproxy/core/plugins/hooks/implementations/formatters/raw.py +370 -0
- ccproxy/core/plugins/hooks/implementations/http_tracer.py +431 -0
- ccproxy/core/plugins/hooks/layers.py +44 -0
- ccproxy/core/plugins/hooks/manager.py +186 -0
- ccproxy/core/plugins/hooks/registry.py +139 -0
- ccproxy/core/plugins/hooks/thread_manager.py +203 -0
- ccproxy/core/plugins/hooks/types.py +22 -0
- ccproxy/core/plugins/interfaces.py +416 -0
- ccproxy/core/plugins/loader.py +166 -0
- ccproxy/core/plugins/middleware.py +233 -0
- ccproxy/core/plugins/models.py +59 -0
- ccproxy/core/plugins/protocol.py +180 -0
- ccproxy/core/plugins/runtime.py +519 -0
- ccproxy/{observability/context.py → core/request_context.py} +137 -94
- ccproxy/core/status_report.py +211 -0
- ccproxy/core/transformers.py +13 -8
- ccproxy/data/claude_headers_fallback.json +540 -19
- ccproxy/data/codex_headers_fallback.json +114 -7
- ccproxy/http/__init__.py +30 -0
- ccproxy/http/base.py +95 -0
- ccproxy/http/client.py +323 -0
- ccproxy/http/hooks.py +642 -0
- ccproxy/http/pool.py +279 -0
- ccproxy/llms/formatters/__init__.py +7 -0
- ccproxy/llms/formatters/anthropic_to_openai/__init__.py +55 -0
- ccproxy/llms/formatters/anthropic_to_openai/errors.py +65 -0
- ccproxy/llms/formatters/anthropic_to_openai/requests.py +356 -0
- ccproxy/llms/formatters/anthropic_to_openai/responses.py +153 -0
- ccproxy/llms/formatters/anthropic_to_openai/streams.py +1546 -0
- ccproxy/llms/formatters/base.py +140 -0
- ccproxy/llms/formatters/base_model.py +33 -0
- ccproxy/llms/formatters/common/__init__.py +51 -0
- ccproxy/llms/formatters/common/identifiers.py +48 -0
- ccproxy/llms/formatters/common/streams.py +254 -0
- ccproxy/llms/formatters/common/thinking.py +74 -0
- ccproxy/llms/formatters/common/usage.py +135 -0
- ccproxy/llms/formatters/constants.py +55 -0
- ccproxy/llms/formatters/context.py +116 -0
- ccproxy/llms/formatters/mapping.py +33 -0
- ccproxy/llms/formatters/openai_to_anthropic/__init__.py +55 -0
- ccproxy/llms/formatters/openai_to_anthropic/_helpers.py +141 -0
- ccproxy/llms/formatters/openai_to_anthropic/errors.py +53 -0
- ccproxy/llms/formatters/openai_to_anthropic/requests.py +674 -0
- ccproxy/llms/formatters/openai_to_anthropic/responses.py +285 -0
- ccproxy/llms/formatters/openai_to_anthropic/streams.py +530 -0
- ccproxy/llms/formatters/openai_to_openai/__init__.py +53 -0
- ccproxy/llms/formatters/openai_to_openai/_helpers.py +325 -0
- ccproxy/llms/formatters/openai_to_openai/errors.py +6 -0
- ccproxy/llms/formatters/openai_to_openai/requests.py +388 -0
- ccproxy/llms/formatters/openai_to_openai/responses.py +594 -0
- ccproxy/llms/formatters/openai_to_openai/streams.py +1832 -0
- ccproxy/llms/formatters/utils.py +306 -0
- ccproxy/llms/models/__init__.py +9 -0
- ccproxy/llms/models/anthropic.py +619 -0
- ccproxy/llms/models/openai.py +844 -0
- ccproxy/llms/streaming/__init__.py +26 -0
- ccproxy/llms/streaming/accumulators.py +1074 -0
- ccproxy/llms/streaming/formatters.py +251 -0
- ccproxy/{adapters/openai/streaming.py → llms/streaming/processors.py} +193 -240
- ccproxy/models/__init__.py +8 -159
- ccproxy/models/detection.py +92 -193
- ccproxy/models/provider.py +75 -0
- ccproxy/plugins/access_log/README.md +32 -0
- ccproxy/plugins/access_log/__init__.py +20 -0
- ccproxy/plugins/access_log/config.py +33 -0
- ccproxy/plugins/access_log/formatter.py +126 -0
- ccproxy/plugins/access_log/hook.py +763 -0
- ccproxy/plugins/access_log/logger.py +254 -0
- ccproxy/plugins/access_log/plugin.py +137 -0
- ccproxy/plugins/access_log/writer.py +109 -0
- ccproxy/plugins/analytics/README.md +24 -0
- ccproxy/plugins/analytics/__init__.py +1 -0
- ccproxy/plugins/analytics/config.py +5 -0
- ccproxy/plugins/analytics/ingest.py +85 -0
- ccproxy/plugins/analytics/models.py +97 -0
- ccproxy/plugins/analytics/plugin.py +121 -0
- ccproxy/plugins/analytics/routes.py +163 -0
- ccproxy/plugins/analytics/service.py +284 -0
- ccproxy/plugins/claude_api/README.md +29 -0
- ccproxy/plugins/claude_api/__init__.py +10 -0
- ccproxy/plugins/claude_api/adapter.py +829 -0
- ccproxy/plugins/claude_api/config.py +52 -0
- ccproxy/plugins/claude_api/detection_service.py +461 -0
- ccproxy/plugins/claude_api/health.py +175 -0
- ccproxy/plugins/claude_api/hooks.py +284 -0
- ccproxy/plugins/claude_api/models.py +256 -0
- ccproxy/plugins/claude_api/plugin.py +298 -0
- ccproxy/plugins/claude_api/routes.py +118 -0
- ccproxy/plugins/claude_api/streaming_metrics.py +68 -0
- ccproxy/plugins/claude_api/tasks.py +84 -0
- ccproxy/plugins/claude_sdk/README.md +35 -0
- ccproxy/plugins/claude_sdk/__init__.py +80 -0
- ccproxy/plugins/claude_sdk/adapter.py +749 -0
- ccproxy/plugins/claude_sdk/auth.py +57 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/client.py +63 -39
- ccproxy/plugins/claude_sdk/config.py +210 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/converter.py +6 -6
- ccproxy/plugins/claude_sdk/detection_service.py +163 -0
- ccproxy/{services/claude_sdk_service.py → plugins/claude_sdk/handler.py} +123 -304
- ccproxy/plugins/claude_sdk/health.py +113 -0
- ccproxy/plugins/claude_sdk/hooks.py +115 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/manager.py +42 -32
- ccproxy/{claude_sdk → plugins/claude_sdk}/message_queue.py +8 -8
- ccproxy/{models/claude_sdk.py → plugins/claude_sdk/models.py} +64 -16
- ccproxy/plugins/claude_sdk/options.py +154 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/parser.py +23 -5
- ccproxy/plugins/claude_sdk/plugin.py +269 -0
- ccproxy/plugins/claude_sdk/routes.py +104 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/session_client.py +124 -12
- ccproxy/plugins/claude_sdk/session_pool.py +700 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/stream_handle.py +48 -43
- ccproxy/{claude_sdk → plugins/claude_sdk}/stream_worker.py +22 -18
- ccproxy/{claude_sdk → plugins/claude_sdk}/streaming.py +50 -16
- ccproxy/plugins/claude_sdk/tasks.py +97 -0
- ccproxy/plugins/claude_shared/README.md +18 -0
- ccproxy/plugins/claude_shared/__init__.py +12 -0
- ccproxy/plugins/claude_shared/model_defaults.py +171 -0
- ccproxy/plugins/codex/README.md +35 -0
- ccproxy/plugins/codex/__init__.py +6 -0
- ccproxy/plugins/codex/adapter.py +635 -0
- ccproxy/{config/codex.py → plugins/codex/config.py} +78 -12
- ccproxy/plugins/codex/detection_service.py +544 -0
- ccproxy/plugins/codex/health.py +162 -0
- ccproxy/plugins/codex/hooks.py +263 -0
- ccproxy/plugins/codex/model_defaults.py +39 -0
- ccproxy/plugins/codex/models.py +263 -0
- ccproxy/plugins/codex/plugin.py +275 -0
- ccproxy/plugins/codex/routes.py +129 -0
- ccproxy/plugins/codex/streaming_metrics.py +324 -0
- ccproxy/plugins/codex/tasks.py +106 -0
- ccproxy/plugins/codex/utils/__init__.py +1 -0
- ccproxy/plugins/codex/utils/sse_parser.py +106 -0
- ccproxy/plugins/command_replay/README.md +34 -0
- ccproxy/plugins/command_replay/__init__.py +17 -0
- ccproxy/plugins/command_replay/config.py +133 -0
- ccproxy/plugins/command_replay/formatter.py +432 -0
- ccproxy/plugins/command_replay/hook.py +294 -0
- ccproxy/plugins/command_replay/plugin.py +161 -0
- ccproxy/plugins/copilot/README.md +39 -0
- ccproxy/plugins/copilot/__init__.py +11 -0
- ccproxy/plugins/copilot/adapter.py +465 -0
- ccproxy/plugins/copilot/config.py +155 -0
- ccproxy/plugins/copilot/data/copilot_fallback.json +41 -0
- ccproxy/plugins/copilot/detection_service.py +255 -0
- ccproxy/plugins/copilot/manager.py +275 -0
- ccproxy/plugins/copilot/model_defaults.py +284 -0
- ccproxy/plugins/copilot/models.py +148 -0
- ccproxy/plugins/copilot/oauth/__init__.py +16 -0
- ccproxy/plugins/copilot/oauth/client.py +494 -0
- ccproxy/plugins/copilot/oauth/models.py +385 -0
- ccproxy/plugins/copilot/oauth/provider.py +602 -0
- ccproxy/plugins/copilot/oauth/storage.py +170 -0
- ccproxy/plugins/copilot/plugin.py +360 -0
- ccproxy/plugins/copilot/routes.py +294 -0
- ccproxy/plugins/credential_balancer/README.md +124 -0
- ccproxy/plugins/credential_balancer/__init__.py +6 -0
- ccproxy/plugins/credential_balancer/config.py +270 -0
- ccproxy/plugins/credential_balancer/factory.py +415 -0
- ccproxy/plugins/credential_balancer/hook.py +51 -0
- ccproxy/plugins/credential_balancer/manager.py +587 -0
- ccproxy/plugins/credential_balancer/plugin.py +146 -0
- ccproxy/plugins/dashboard/README.md +25 -0
- ccproxy/plugins/dashboard/__init__.py +1 -0
- ccproxy/plugins/dashboard/config.py +8 -0
- ccproxy/plugins/dashboard/plugin.py +71 -0
- ccproxy/plugins/dashboard/routes.py +67 -0
- ccproxy/plugins/docker/README.md +32 -0
- ccproxy/{docker → plugins/docker}/__init__.py +3 -0
- ccproxy/{docker → plugins/docker}/adapter.py +108 -10
- ccproxy/plugins/docker/config.py +82 -0
- ccproxy/{docker → plugins/docker}/docker_path.py +4 -3
- ccproxy/{docker → plugins/docker}/middleware.py +2 -2
- ccproxy/plugins/docker/plugin.py +198 -0
- ccproxy/{docker → plugins/docker}/stream_process.py +3 -3
- ccproxy/plugins/duckdb_storage/README.md +26 -0
- ccproxy/plugins/duckdb_storage/__init__.py +1 -0
- ccproxy/plugins/duckdb_storage/config.py +22 -0
- ccproxy/plugins/duckdb_storage/plugin.py +128 -0
- ccproxy/plugins/duckdb_storage/routes.py +51 -0
- ccproxy/plugins/duckdb_storage/storage.py +633 -0
- ccproxy/plugins/max_tokens/README.md +38 -0
- ccproxy/plugins/max_tokens/__init__.py +12 -0
- ccproxy/plugins/max_tokens/adapter.py +235 -0
- ccproxy/plugins/max_tokens/config.py +86 -0
- ccproxy/plugins/max_tokens/models.py +53 -0
- ccproxy/plugins/max_tokens/plugin.py +200 -0
- ccproxy/plugins/max_tokens/service.py +271 -0
- ccproxy/plugins/max_tokens/token_limits.json +54 -0
- ccproxy/plugins/metrics/README.md +35 -0
- ccproxy/plugins/metrics/__init__.py +10 -0
- ccproxy/{observability/metrics.py → plugins/metrics/collector.py} +20 -153
- ccproxy/plugins/metrics/config.py +85 -0
- ccproxy/plugins/metrics/grafana/dashboards/ccproxy-dashboard.json +1720 -0
- ccproxy/plugins/metrics/hook.py +403 -0
- ccproxy/plugins/metrics/plugin.py +268 -0
- ccproxy/{observability → plugins/metrics}/pushgateway.py +57 -59
- ccproxy/plugins/metrics/routes.py +107 -0
- ccproxy/plugins/metrics/tasks.py +117 -0
- ccproxy/plugins/oauth_claude/README.md +35 -0
- ccproxy/plugins/oauth_claude/__init__.py +14 -0
- ccproxy/plugins/oauth_claude/client.py +270 -0
- ccproxy/plugins/oauth_claude/config.py +84 -0
- ccproxy/plugins/oauth_claude/manager.py +482 -0
- ccproxy/plugins/oauth_claude/models.py +266 -0
- ccproxy/plugins/oauth_claude/plugin.py +149 -0
- ccproxy/plugins/oauth_claude/provider.py +571 -0
- ccproxy/plugins/oauth_claude/storage.py +212 -0
- ccproxy/plugins/oauth_codex/README.md +38 -0
- ccproxy/plugins/oauth_codex/__init__.py +14 -0
- ccproxy/plugins/oauth_codex/client.py +224 -0
- ccproxy/plugins/oauth_codex/config.py +95 -0
- ccproxy/plugins/oauth_codex/manager.py +256 -0
- ccproxy/plugins/oauth_codex/models.py +239 -0
- ccproxy/plugins/oauth_codex/plugin.py +146 -0
- ccproxy/plugins/oauth_codex/provider.py +574 -0
- ccproxy/plugins/oauth_codex/storage.py +92 -0
- ccproxy/plugins/permissions/README.md +28 -0
- ccproxy/plugins/permissions/__init__.py +22 -0
- ccproxy/plugins/permissions/config.py +28 -0
- ccproxy/{cli/commands/permission_handler.py → plugins/permissions/handlers/cli.py} +49 -25
- ccproxy/plugins/permissions/handlers/protocol.py +33 -0
- ccproxy/plugins/permissions/handlers/terminal.py +675 -0
- ccproxy/{api/routes → plugins/permissions}/mcp.py +34 -7
- ccproxy/{models/permissions.py → plugins/permissions/models.py} +65 -1
- ccproxy/plugins/permissions/plugin.py +153 -0
- ccproxy/{api/routes/permissions.py → plugins/permissions/routes.py} +20 -16
- ccproxy/{api/services/permission_service.py → plugins/permissions/service.py} +65 -11
- ccproxy/{api → plugins/permissions}/ui/permission_handler_protocol.py +1 -1
- ccproxy/{api → plugins/permissions}/ui/terminal_permission_handler.py +66 -10
- ccproxy/plugins/pricing/README.md +34 -0
- ccproxy/plugins/pricing/__init__.py +6 -0
- ccproxy/{pricing → plugins/pricing}/cache.py +7 -6
- ccproxy/{config/pricing.py → plugins/pricing/config.py} +32 -6
- ccproxy/plugins/pricing/exceptions.py +35 -0
- ccproxy/plugins/pricing/loader.py +440 -0
- ccproxy/{pricing → plugins/pricing}/models.py +13 -23
- ccproxy/plugins/pricing/plugin.py +169 -0
- ccproxy/plugins/pricing/service.py +191 -0
- ccproxy/plugins/pricing/tasks.py +300 -0
- ccproxy/{pricing → plugins/pricing}/updater.py +86 -72
- ccproxy/plugins/pricing/utils.py +99 -0
- ccproxy/plugins/request_tracer/README.md +40 -0
- ccproxy/plugins/request_tracer/__init__.py +7 -0
- ccproxy/plugins/request_tracer/config.py +120 -0
- ccproxy/plugins/request_tracer/hook.py +415 -0
- ccproxy/plugins/request_tracer/plugin.py +255 -0
- ccproxy/scheduler/__init__.py +2 -14
- ccproxy/scheduler/core.py +26 -41
- ccproxy/scheduler/manager.py +61 -105
- ccproxy/scheduler/registry.py +6 -32
- ccproxy/scheduler/tasks.py +268 -276
- ccproxy/services/__init__.py +0 -1
- ccproxy/services/adapters/__init__.py +11 -0
- ccproxy/services/adapters/base.py +123 -0
- ccproxy/services/adapters/chain_composer.py +88 -0
- ccproxy/services/adapters/chain_validation.py +44 -0
- ccproxy/services/adapters/chat_accumulator.py +200 -0
- ccproxy/services/adapters/delta_utils.py +142 -0
- ccproxy/services/adapters/format_adapter.py +136 -0
- ccproxy/services/adapters/format_context.py +11 -0
- ccproxy/services/adapters/format_registry.py +158 -0
- ccproxy/services/adapters/http_adapter.py +1045 -0
- ccproxy/services/adapters/mock_adapter.py +118 -0
- ccproxy/services/adapters/protocols.py +35 -0
- ccproxy/services/adapters/simple_converters.py +571 -0
- ccproxy/services/auth_registry.py +180 -0
- ccproxy/services/cache/__init__.py +6 -0
- ccproxy/services/cache/response_cache.py +261 -0
- ccproxy/services/cli_detection.py +437 -0
- ccproxy/services/config/__init__.py +6 -0
- ccproxy/services/config/proxy_configuration.py +111 -0
- ccproxy/services/container.py +256 -0
- ccproxy/services/factories.py +380 -0
- ccproxy/services/handler_config.py +76 -0
- ccproxy/services/interfaces.py +298 -0
- ccproxy/services/mocking/__init__.py +6 -0
- ccproxy/services/mocking/mock_handler.py +291 -0
- ccproxy/services/tracing/__init__.py +7 -0
- ccproxy/services/tracing/interfaces.py +61 -0
- ccproxy/services/tracing/null_tracer.py +57 -0
- ccproxy/streaming/__init__.py +23 -0
- ccproxy/streaming/buffer.py +1056 -0
- ccproxy/streaming/deferred.py +897 -0
- ccproxy/streaming/handler.py +117 -0
- ccproxy/streaming/interfaces.py +77 -0
- ccproxy/streaming/simple_adapter.py +39 -0
- ccproxy/streaming/sse.py +109 -0
- ccproxy/streaming/sse_parser.py +127 -0
- ccproxy/templates/__init__.py +6 -0
- ccproxy/templates/plugin_scaffold.py +695 -0
- ccproxy/testing/endpoints/__init__.py +33 -0
- ccproxy/testing/endpoints/cli.py +215 -0
- ccproxy/testing/endpoints/config.py +874 -0
- ccproxy/testing/endpoints/console.py +57 -0
- ccproxy/testing/endpoints/models.py +100 -0
- ccproxy/testing/endpoints/runner.py +1903 -0
- ccproxy/testing/endpoints/tools.py +308 -0
- ccproxy/testing/mock_responses.py +70 -1
- ccproxy/testing/response_handlers.py +20 -0
- ccproxy/utils/__init__.py +0 -6
- ccproxy/utils/binary_resolver.py +476 -0
- ccproxy/utils/caching.py +327 -0
- ccproxy/utils/cli_logging.py +101 -0
- ccproxy/utils/command_line.py +251 -0
- ccproxy/utils/headers.py +228 -0
- ccproxy/utils/model_mapper.py +120 -0
- ccproxy/utils/startup_helpers.py +68 -446
- ccproxy/utils/version_checker.py +273 -6
- ccproxy_api-0.2.0.dist-info/METADATA +212 -0
- ccproxy_api-0.2.0.dist-info/RECORD +417 -0
- {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0.dist-info}/WHEEL +1 -1
- ccproxy_api-0.2.0.dist-info/entry_points.txt +24 -0
- ccproxy/__init__.py +0 -4
- ccproxy/adapters/__init__.py +0 -11
- ccproxy/adapters/base.py +0 -80
- ccproxy/adapters/codex/__init__.py +0 -11
- ccproxy/adapters/openai/__init__.py +0 -42
- ccproxy/adapters/openai/adapter.py +0 -953
- ccproxy/adapters/openai/models.py +0 -412
- ccproxy/adapters/openai/response_adapter.py +0 -355
- ccproxy/adapters/openai/response_models.py +0 -178
- ccproxy/api/middleware/headers.py +0 -49
- ccproxy/api/middleware/logging.py +0 -180
- ccproxy/api/middleware/request_content_logging.py +0 -297
- ccproxy/api/middleware/server_header.py +0 -58
- ccproxy/api/responses.py +0 -89
- ccproxy/api/routes/claude.py +0 -371
- ccproxy/api/routes/codex.py +0 -1251
- ccproxy/api/routes/metrics.py +0 -1029
- ccproxy/api/routes/proxy.py +0 -211
- ccproxy/api/services/__init__.py +0 -6
- ccproxy/auth/conditional.py +0 -84
- ccproxy/auth/credentials_adapter.py +0 -93
- ccproxy/auth/models.py +0 -118
- ccproxy/auth/oauth/models.py +0 -48
- ccproxy/auth/openai/__init__.py +0 -13
- ccproxy/auth/openai/credentials.py +0 -166
- ccproxy/auth/openai/oauth_client.py +0 -334
- ccproxy/auth/openai/storage.py +0 -184
- ccproxy/auth/storage/json_file.py +0 -158
- ccproxy/auth/storage/keyring.py +0 -189
- ccproxy/claude_sdk/__init__.py +0 -18
- ccproxy/claude_sdk/options.py +0 -194
- ccproxy/claude_sdk/session_pool.py +0 -550
- ccproxy/cli/docker/__init__.py +0 -34
- ccproxy/cli/docker/adapter_factory.py +0 -157
- ccproxy/cli/docker/params.py +0 -274
- ccproxy/config/auth.py +0 -153
- ccproxy/config/claude.py +0 -348
- ccproxy/config/cors.py +0 -79
- ccproxy/config/discovery.py +0 -95
- ccproxy/config/docker_settings.py +0 -264
- ccproxy/config/observability.py +0 -158
- ccproxy/config/reverse_proxy.py +0 -31
- ccproxy/config/scheduler.py +0 -108
- ccproxy/config/server.py +0 -86
- ccproxy/config/validators.py +0 -231
- ccproxy/core/codex_transformers.py +0 -389
- ccproxy/core/http.py +0 -328
- ccproxy/core/http_transformers.py +0 -812
- ccproxy/core/proxy.py +0 -143
- ccproxy/core/validators.py +0 -288
- ccproxy/models/errors.py +0 -42
- ccproxy/models/messages.py +0 -269
- ccproxy/models/requests.py +0 -107
- ccproxy/models/responses.py +0 -270
- ccproxy/models/types.py +0 -102
- ccproxy/observability/__init__.py +0 -51
- ccproxy/observability/access_logger.py +0 -457
- ccproxy/observability/sse_events.py +0 -303
- ccproxy/observability/stats_printer.py +0 -753
- ccproxy/observability/storage/__init__.py +0 -1
- ccproxy/observability/storage/duckdb_simple.py +0 -677
- ccproxy/observability/storage/models.py +0 -70
- ccproxy/observability/streaming_response.py +0 -107
- ccproxy/pricing/__init__.py +0 -19
- ccproxy/pricing/loader.py +0 -251
- ccproxy/services/claude_detection_service.py +0 -243
- ccproxy/services/codex_detection_service.py +0 -252
- ccproxy/services/credentials/__init__.py +0 -55
- ccproxy/services/credentials/config.py +0 -105
- ccproxy/services/credentials/manager.py +0 -561
- ccproxy/services/credentials/oauth_client.py +0 -481
- ccproxy/services/proxy_service.py +0 -1827
- ccproxy/static/.keep +0 -0
- ccproxy/utils/cost_calculator.py +0 -210
- ccproxy/utils/disconnection_monitor.py +0 -83
- ccproxy/utils/model_mapping.py +0 -199
- ccproxy/utils/models_provider.py +0 -150
- ccproxy/utils/simple_request_logger.py +0 -284
- ccproxy/utils/streaming_metrics.py +0 -199
- ccproxy_api-0.1.7.dist-info/METADATA +0 -615
- ccproxy_api-0.1.7.dist-info/RECORD +0 -191
- ccproxy_api-0.1.7.dist-info/entry_points.txt +0 -4
- /ccproxy/{api/middleware/auth.py → auth/models/__init__.py} +0 -0
- /ccproxy/{claude_sdk → plugins/claude_sdk}/exceptions.py +0 -0
- /ccproxy/{docker → plugins/docker}/models.py +0 -0
- /ccproxy/{docker → plugins/docker}/protocol.py +0 -0
- /ccproxy/{docker → plugins/docker}/validators.py +0 -0
- /ccproxy/{auth/oauth/storage.py → plugins/permissions/handlers/__init__.py} +0 -0
- /ccproxy/{api → plugins/permissions}/ui/__init__.py +0 -0
- {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""Health check implementation for Claude SDK plugin."""
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Literal, cast
|
|
4
|
+
|
|
5
|
+
from ccproxy.core.plugins.protocol import HealthCheckResult
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from .config import ClaudeSDKSettings
|
|
10
|
+
from .detection_service import ClaudeSDKDetectionService
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
async def claude_sdk_health_check(
|
|
14
|
+
config: "ClaudeSDKSettings | None",
|
|
15
|
+
detection_service: "ClaudeSDKDetectionService | None",
|
|
16
|
+
*,
|
|
17
|
+
version: str,
|
|
18
|
+
) -> HealthCheckResult:
|
|
19
|
+
"""Perform health check for Claude SDK plugin.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
config: Claude SDK plugin configuration
|
|
23
|
+
detection_service: Claude CLI detection service
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
HealthCheckResult with plugin status
|
|
27
|
+
"""
|
|
28
|
+
checks = []
|
|
29
|
+
status: str = "pass"
|
|
30
|
+
|
|
31
|
+
# Check if plugin is enabled
|
|
32
|
+
if not config or not config.enabled:
|
|
33
|
+
return HealthCheckResult(
|
|
34
|
+
status="fail",
|
|
35
|
+
componentId="plugin-claude_sdk",
|
|
36
|
+
output="Plugin is disabled",
|
|
37
|
+
version=version,
|
|
38
|
+
details={"enabled": False},
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# Check Claude CLI detection
|
|
42
|
+
if detection_service:
|
|
43
|
+
cli_version = detection_service.get_version()
|
|
44
|
+
cli_path = detection_service.get_cli_path()
|
|
45
|
+
is_available = detection_service.is_claude_available()
|
|
46
|
+
cli_info = detection_service.get_cli_health_info()
|
|
47
|
+
|
|
48
|
+
if is_available and cli_path:
|
|
49
|
+
checks.append(f"CLI: {cli_version or 'detected'} at {cli_path}")
|
|
50
|
+
else:
|
|
51
|
+
checks.append("CLI: not found")
|
|
52
|
+
status = "warn" # CLI not found is a warning, not a failure
|
|
53
|
+
else:
|
|
54
|
+
checks.append("CLI: detection service not initialized")
|
|
55
|
+
status = "warn"
|
|
56
|
+
|
|
57
|
+
# Check configuration
|
|
58
|
+
if config:
|
|
59
|
+
checks.append(f"Models: {len(config.models_endpoint)} configured")
|
|
60
|
+
checks.append(
|
|
61
|
+
f"Session pool: {'enabled' if config.session_pool_enabled else 'disabled'}"
|
|
62
|
+
)
|
|
63
|
+
checks.append(
|
|
64
|
+
f"Streaming: {'enabled' if config.supports_streaming else 'disabled'}"
|
|
65
|
+
)
|
|
66
|
+
else:
|
|
67
|
+
checks.append("Config: not loaded")
|
|
68
|
+
status = "fail"
|
|
69
|
+
|
|
70
|
+
# Standardized details
|
|
71
|
+
from ccproxy.core.plugins.models import (
|
|
72
|
+
CLIHealth,
|
|
73
|
+
ConfigHealth,
|
|
74
|
+
ProviderHealthDetails,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
cli_health = None
|
|
78
|
+
if detection_service:
|
|
79
|
+
path_list = detection_service.get_cli_path()
|
|
80
|
+
cli_status = cli_info.status.value if cli_info else "unknown"
|
|
81
|
+
cli_health = CLIHealth(
|
|
82
|
+
available=bool(detection_service.is_claude_available()),
|
|
83
|
+
status=cli_status,
|
|
84
|
+
version=detection_service.get_version(),
|
|
85
|
+
path=(path_list[0] if path_list else None),
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
details = ProviderHealthDetails(
|
|
89
|
+
provider="claude_sdk",
|
|
90
|
+
enabled=bool(config and config.enabled),
|
|
91
|
+
base_url=None,
|
|
92
|
+
cli=cli_health,
|
|
93
|
+
auth=None,
|
|
94
|
+
config=ConfigHealth(
|
|
95
|
+
model_count=len(config.models_endpoint)
|
|
96
|
+
if config and config.models_endpoint
|
|
97
|
+
else 0,
|
|
98
|
+
supports_openai_format=config.supports_streaming if config else None,
|
|
99
|
+
extra={
|
|
100
|
+
"session_pool_enabled": bool(config.session_pool_enabled)
|
|
101
|
+
if config
|
|
102
|
+
else None
|
|
103
|
+
},
|
|
104
|
+
),
|
|
105
|
+
).model_dump()
|
|
106
|
+
|
|
107
|
+
return HealthCheckResult(
|
|
108
|
+
status=cast(Literal["pass", "warn", "fail"], status),
|
|
109
|
+
componentId="plugin-claude_sdk",
|
|
110
|
+
output="; ".join(checks),
|
|
111
|
+
version=version,
|
|
112
|
+
details=details,
|
|
113
|
+
)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""Hook integration for Claude SDK plugin to emit streaming metrics."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from ccproxy.core.logging import get_plugin_logger
|
|
9
|
+
from ccproxy.core.plugins.hooks import Hook, HookContext, HookEvent, HookManager
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
logger = get_plugin_logger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ClaudeSDKStreamingHook(Hook):
|
|
16
|
+
"""Hook for emitting Claude SDK streaming metrics.
|
|
17
|
+
|
|
18
|
+
This hook handles streaming completion events from claude_sdk and emits
|
|
19
|
+
PROVIDER_STREAM_END events with usage metrics for access logging.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
name = "claude_sdk_streaming_metrics"
|
|
23
|
+
events = [] # We'll emit events directly, not listen to them
|
|
24
|
+
priority = 700 # HookLayer.METRICS
|
|
25
|
+
|
|
26
|
+
def __init__(self, hook_manager: HookManager | None = None) -> None:
|
|
27
|
+
"""Initialize the Claude SDK streaming hook.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
hook_manager: Hook manager for emitting events
|
|
31
|
+
"""
|
|
32
|
+
self.hook_manager = hook_manager
|
|
33
|
+
|
|
34
|
+
async def emit_stream_end(
|
|
35
|
+
self,
|
|
36
|
+
request_id: str,
|
|
37
|
+
usage_metrics: dict[str, Any],
|
|
38
|
+
provider: str = "claude_sdk",
|
|
39
|
+
url: str = "claude-sdk://direct",
|
|
40
|
+
method: str = "POST",
|
|
41
|
+
total_chunks: int = 0,
|
|
42
|
+
total_bytes: int = 0,
|
|
43
|
+
) -> None:
|
|
44
|
+
"""Emit PROVIDER_STREAM_END event with usage metrics.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
request_id: Request ID for correlation
|
|
48
|
+
usage_metrics: Dictionary containing token counts and costs
|
|
49
|
+
provider: Provider name (default: claude_sdk)
|
|
50
|
+
url: URL or endpoint identifier
|
|
51
|
+
method: HTTP method
|
|
52
|
+
total_chunks: Number of chunks streamed
|
|
53
|
+
total_bytes: Total bytes streamed
|
|
54
|
+
"""
|
|
55
|
+
if not self.hook_manager:
|
|
56
|
+
logger.debug(
|
|
57
|
+
"no_hook_manager_for_stream_end",
|
|
58
|
+
request_id=request_id,
|
|
59
|
+
provider=provider,
|
|
60
|
+
)
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
# Normalize usage metrics to standard format
|
|
65
|
+
normalized_metrics = {
|
|
66
|
+
"input_tokens": usage_metrics.get("tokens_input", 0),
|
|
67
|
+
"output_tokens": usage_metrics.get("tokens_output", 0),
|
|
68
|
+
"cache_read_input_tokens": usage_metrics.get("cache_read_tokens", 0),
|
|
69
|
+
"cache_creation_input_tokens": usage_metrics.get(
|
|
70
|
+
"cache_write_tokens", 0
|
|
71
|
+
),
|
|
72
|
+
"cost_usd": usage_metrics.get("cost_usd", 0.0),
|
|
73
|
+
"model": usage_metrics.get("model", ""),
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
stream_end_context = HookContext(
|
|
77
|
+
event=HookEvent.PROVIDER_STREAM_END,
|
|
78
|
+
timestamp=datetime.now(),
|
|
79
|
+
provider=provider,
|
|
80
|
+
data={
|
|
81
|
+
"url": url,
|
|
82
|
+
"method": method,
|
|
83
|
+
"request_id": request_id,
|
|
84
|
+
"total_chunks": total_chunks,
|
|
85
|
+
"total_bytes": total_bytes,
|
|
86
|
+
"usage_metrics": normalized_metrics,
|
|
87
|
+
},
|
|
88
|
+
metadata={
|
|
89
|
+
"request_id": request_id,
|
|
90
|
+
},
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
await self.hook_manager.emit_with_context(stream_end_context)
|
|
94
|
+
|
|
95
|
+
logger.info(
|
|
96
|
+
"claude_sdk_stream_end_emitted",
|
|
97
|
+
request_id=request_id,
|
|
98
|
+
tokens_input=normalized_metrics["input_tokens"],
|
|
99
|
+
tokens_output=normalized_metrics["output_tokens"],
|
|
100
|
+
cost_usd=normalized_metrics["cost_usd"],
|
|
101
|
+
model=normalized_metrics["model"],
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
except Exception as e:
|
|
105
|
+
logger.error(
|
|
106
|
+
"claude_sdk_hook_emission_failed",
|
|
107
|
+
event="PROVIDER_STREAM_END",
|
|
108
|
+
error=str(e),
|
|
109
|
+
request_id=request_id,
|
|
110
|
+
exc_info=e,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
async def __call__(self, context: HookContext) -> None:
|
|
114
|
+
"""Handle hook events (not used for this hook as we emit directly)."""
|
|
115
|
+
pass
|
|
@@ -13,19 +13,20 @@ from collections.abc import Callable
|
|
|
13
13
|
# Type alias for metrics factory function
|
|
14
14
|
from typing import Any, TypeAlias
|
|
15
15
|
|
|
16
|
-
import
|
|
17
|
-
from claude_code_sdk import ClaudeCodeOptions
|
|
16
|
+
from claude_agent_sdk import ClaudeAgentOptions
|
|
18
17
|
|
|
19
|
-
from ccproxy.claude_sdk.session_client import SessionClient
|
|
20
|
-
from ccproxy.claude_sdk.session_pool import SessionPool
|
|
21
|
-
from ccproxy.config.settings import Settings
|
|
22
18
|
from ccproxy.core.errors import ClaudeProxyError
|
|
19
|
+
from ccproxy.core.logging import get_plugin_logger
|
|
23
20
|
|
|
21
|
+
from .config import ClaudeSDKSettings, SessionPoolSettings
|
|
22
|
+
from .session_client import SessionClient
|
|
23
|
+
from .session_pool import SessionPool
|
|
24
24
|
|
|
25
|
-
logger = structlog.get_logger(__name__)
|
|
26
25
|
|
|
26
|
+
# Type alias for metrics factory function
|
|
27
|
+
MetricsFactory: TypeAlias = Callable[[], Any]
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
logger = get_plugin_logger()
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
class SessionManager:
|
|
@@ -33,21 +34,18 @@ class SessionManager:
|
|
|
33
34
|
|
|
34
35
|
def __init__(
|
|
35
36
|
self,
|
|
36
|
-
|
|
37
|
+
config: ClaudeSDKSettings,
|
|
37
38
|
metrics_factory: MetricsFactory | None = None,
|
|
38
39
|
) -> None:
|
|
39
40
|
"""Initialize SessionManager with optional settings and metrics factory.
|
|
40
41
|
|
|
41
42
|
Args:
|
|
42
|
-
|
|
43
|
+
config: Plugin-specific configuration for Claude SDK
|
|
43
44
|
metrics_factory: Optional callable that returns a metrics instance.
|
|
44
45
|
If None, no metrics will be used.
|
|
45
46
|
"""
|
|
46
|
-
import structlog
|
|
47
|
-
|
|
48
|
-
logger = structlog.get_logger(__name__)
|
|
49
47
|
|
|
50
|
-
self.
|
|
48
|
+
self.config = config
|
|
51
49
|
self._session_pool: SessionPool | None = None
|
|
52
50
|
self._lock = asyncio.Lock()
|
|
53
51
|
self._metrics_factory = metrics_factory
|
|
@@ -56,14 +54,36 @@ class SessionManager:
|
|
|
56
54
|
session_pool_enabled = self._should_enable_session_pool()
|
|
57
55
|
logger.debug(
|
|
58
56
|
"session_manager_init",
|
|
59
|
-
|
|
57
|
+
has_config=bool(config),
|
|
60
58
|
has_metrics_factory=bool(metrics_factory),
|
|
61
59
|
session_pool_enabled=session_pool_enabled,
|
|
62
60
|
)
|
|
63
61
|
|
|
64
62
|
if session_pool_enabled:
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
# Convert core settings to plugin settings
|
|
64
|
+
core_pool_settings = self.config.sdk_session_pool
|
|
65
|
+
if core_pool_settings is None:
|
|
66
|
+
logger.debug("session_pool_disabled", reason="no_settings")
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
plugin_pool_settings = SessionPoolSettings(
|
|
70
|
+
enabled=core_pool_settings.enabled,
|
|
71
|
+
session_ttl=core_pool_settings.session_ttl,
|
|
72
|
+
max_sessions=core_pool_settings.max_sessions,
|
|
73
|
+
cleanup_interval=getattr(core_pool_settings, "cleanup_interval", 300),
|
|
74
|
+
idle_threshold=getattr(core_pool_settings, "idle_threshold", 300),
|
|
75
|
+
connection_recovery=getattr(
|
|
76
|
+
core_pool_settings, "connection_recovery", True
|
|
77
|
+
),
|
|
78
|
+
stream_first_chunk_timeout=getattr(
|
|
79
|
+
core_pool_settings, "stream_first_chunk_timeout", 8
|
|
80
|
+
),
|
|
81
|
+
stream_ongoing_timeout=getattr(
|
|
82
|
+
core_pool_settings, "stream_ongoing_timeout", 60
|
|
83
|
+
),
|
|
84
|
+
)
|
|
85
|
+
self._session_pool = SessionPool(plugin_pool_settings)
|
|
86
|
+
logger.debug(
|
|
67
87
|
"session_manager_session_pool_initialized",
|
|
68
88
|
session_ttl=self._session_pool.config.session_ttl,
|
|
69
89
|
max_sessions=self._session_pool.config.max_sessions,
|
|
@@ -77,21 +97,12 @@ class SessionManager:
|
|
|
77
97
|
|
|
78
98
|
def _should_enable_session_pool(self) -> bool:
|
|
79
99
|
"""Check if session pool should be enabled."""
|
|
80
|
-
import structlog
|
|
81
|
-
|
|
82
|
-
logger = structlog.get_logger(__name__)
|
|
83
100
|
|
|
84
|
-
if not self.
|
|
85
|
-
logger.debug("session_pool_check", decision="
|
|
101
|
+
if not self.config:
|
|
102
|
+
logger.debug("session_pool_check", decision="no_config", enabled=False)
|
|
86
103
|
return False
|
|
87
104
|
|
|
88
|
-
|
|
89
|
-
logger.debug(
|
|
90
|
-
"session_pool_check", decision="no_claude_settings", enabled=False
|
|
91
|
-
)
|
|
92
|
-
return False
|
|
93
|
-
|
|
94
|
-
session_pool_settings = getattr(self._settings.claude, "sdk_session_pool", None)
|
|
105
|
+
session_pool_settings = getattr(self.config, "sdk_session_pool", None)
|
|
95
106
|
if not session_pool_settings:
|
|
96
107
|
logger.debug(
|
|
97
108
|
"session_pool_check", decision="no_session_pool_settings", enabled=False
|
|
@@ -121,11 +132,10 @@ class SessionManager:
|
|
|
121
132
|
async def get_session_client(
|
|
122
133
|
self,
|
|
123
134
|
session_id: str,
|
|
124
|
-
options:
|
|
135
|
+
options: ClaudeAgentOptions,
|
|
125
136
|
) -> SessionClient:
|
|
126
137
|
"""Get session-aware client."""
|
|
127
138
|
|
|
128
|
-
logger = structlog.get_logger(__name__)
|
|
129
139
|
logger.debug(
|
|
130
140
|
"session_manager_get_session_client",
|
|
131
141
|
session_id=session_id,
|
|
@@ -161,7 +171,7 @@ class SessionManager:
|
|
|
161
171
|
)
|
|
162
172
|
return False
|
|
163
173
|
|
|
164
|
-
logger.
|
|
174
|
+
logger.debug(
|
|
165
175
|
"session_manager_interrupt_session",
|
|
166
176
|
session_id=session_id,
|
|
167
177
|
)
|
|
@@ -178,7 +188,7 @@ class SessionManager:
|
|
|
178
188
|
logger.warning("session_manager_interrupt_all_no_pool")
|
|
179
189
|
return 0
|
|
180
190
|
|
|
181
|
-
logger.
|
|
191
|
+
logger.debug("session_manager_interrupt_all_sessions")
|
|
182
192
|
return await self._session_pool.interrupt_all_sessions()
|
|
183
193
|
|
|
184
194
|
async def get_session_pool_stats(self) -> dict[str, Any]:
|
|
@@ -11,10 +11,10 @@ from dataclasses import dataclass, field
|
|
|
11
11
|
from enum import Enum
|
|
12
12
|
from typing import Any, TypeVar
|
|
13
13
|
|
|
14
|
-
import
|
|
14
|
+
from ccproxy.core.logging import get_plugin_logger
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
logger =
|
|
17
|
+
logger = get_plugin_logger()
|
|
18
18
|
|
|
19
19
|
T = TypeVar("T")
|
|
20
20
|
|
|
@@ -150,7 +150,7 @@ class MessageQueue:
|
|
|
150
150
|
listener = QueueListener(listener_id)
|
|
151
151
|
self._listeners[listener.listener_id] = listener
|
|
152
152
|
|
|
153
|
-
logger.
|
|
153
|
+
logger.trace(
|
|
154
154
|
"message_queue_listener_added",
|
|
155
155
|
listener_id=listener.listener_id,
|
|
156
156
|
active_listeners=len(self._listeners),
|
|
@@ -169,7 +169,7 @@ class MessageQueue:
|
|
|
169
169
|
listener = self._listeners.pop(listener_id)
|
|
170
170
|
listener.close()
|
|
171
171
|
|
|
172
|
-
logger.
|
|
172
|
+
logger.trace(
|
|
173
173
|
"message_queue_listener_removed",
|
|
174
174
|
listener_id=listener_id,
|
|
175
175
|
active_listeners=len(self._listeners),
|
|
@@ -242,7 +242,7 @@ class MessageQueue:
|
|
|
242
242
|
if delivered_count == 0:
|
|
243
243
|
self._total_messages_discarded += 1
|
|
244
244
|
|
|
245
|
-
logger.
|
|
245
|
+
logger.trace(
|
|
246
246
|
"message_queue_broadcast",
|
|
247
247
|
listeners_count=len(self._listeners),
|
|
248
248
|
delivered_count=delivered_count,
|
|
@@ -265,7 +265,7 @@ class MessageQueue:
|
|
|
265
265
|
with contextlib.suppress(asyncio.QueueFull):
|
|
266
266
|
listener._queue.put_nowait(queue_msg)
|
|
267
267
|
|
|
268
|
-
logger.
|
|
268
|
+
logger.trace(
|
|
269
269
|
"message_queue_broadcast_error",
|
|
270
270
|
error_type=type(error).__name__,
|
|
271
271
|
listeners_count=len(self._listeners),
|
|
@@ -281,7 +281,7 @@ class MessageQueue:
|
|
|
281
281
|
with contextlib.suppress(asyncio.QueueFull):
|
|
282
282
|
listener._queue.put_nowait(queue_msg)
|
|
283
283
|
|
|
284
|
-
logger.
|
|
284
|
+
logger.trace(
|
|
285
285
|
"message_queue_broadcast_complete",
|
|
286
286
|
listeners_count=len(self._listeners),
|
|
287
287
|
)
|
|
@@ -296,7 +296,7 @@ class MessageQueue:
|
|
|
296
296
|
with contextlib.suppress(asyncio.QueueFull):
|
|
297
297
|
listener._queue.put_nowait(queue_msg)
|
|
298
298
|
|
|
299
|
-
logger.
|
|
299
|
+
logger.trace(
|
|
300
300
|
"message_queue_broadcast_shutdown",
|
|
301
301
|
listeners_count=len(self._listeners),
|
|
302
302
|
message="Shutdown signal sent to all listeners due to interrupt",
|
|
@@ -4,7 +4,7 @@ This module provides Pydantic models that mirror the Claude SDK types from the
|
|
|
4
4
|
official claude-code-sdk-python repository. These models enable strong typing
|
|
5
5
|
throughout the proxy system and provide runtime validation.
|
|
6
6
|
|
|
7
|
-
Based on: https://github.com/anthropics/claude-code-sdk-python/blob/main/src/
|
|
7
|
+
Based on: https://github.com/anthropics/claude-code-sdk-python/blob/main/src/claude_agent_sdk/types.py
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
@@ -12,12 +12,12 @@ from __future__ import annotations
|
|
|
12
12
|
from typing import Annotated, Any, Literal, TypeVar, cast
|
|
13
13
|
|
|
14
14
|
# Import Claude SDK types for isinstance checks
|
|
15
|
-
from
|
|
16
|
-
from
|
|
17
|
-
from
|
|
15
|
+
from claude_agent_sdk import TextBlock as SDKTextBlock
|
|
16
|
+
from claude_agent_sdk import ToolResultBlock as SDKToolResultBlock
|
|
17
|
+
from claude_agent_sdk import ToolUseBlock as SDKToolUseBlock
|
|
18
18
|
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
19
19
|
|
|
20
|
-
from ccproxy.models
|
|
20
|
+
from ccproxy.llms.models import anthropic as anthropic_models
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
# Type variables for generic functions
|
|
@@ -69,7 +69,7 @@ class ToolUseBlock(BaseModel):
|
|
|
69
69
|
"id": self.id,
|
|
70
70
|
"name": self.name,
|
|
71
71
|
"input": self.input,
|
|
72
|
-
"source": "
|
|
72
|
+
"source": "claude_agent_sdk",
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
|
|
@@ -96,7 +96,7 @@ class ToolResultBlock(BaseModel):
|
|
|
96
96
|
"tool_use_id": self.tool_use_id,
|
|
97
97
|
"content": self.content,
|
|
98
98
|
"is_error": self.is_error,
|
|
99
|
-
"source": "
|
|
99
|
+
"source": "claude_agent_sdk",
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
|
|
@@ -259,7 +259,6 @@ class ResultMessage(BaseModel):
|
|
|
259
259
|
)
|
|
260
260
|
result: str | None = Field(None, description="Result string if available")
|
|
261
261
|
|
|
262
|
-
# Add computed properties for backward compatibility
|
|
263
262
|
@property
|
|
264
263
|
def stop_reason(self) -> str:
|
|
265
264
|
"""Get stop reason from result or default to end_turn."""
|
|
@@ -268,11 +267,11 @@ class ResultMessage(BaseModel):
|
|
|
268
267
|
return "end_turn"
|
|
269
268
|
|
|
270
269
|
@property
|
|
271
|
-
def usage_model(self) -> Usage:
|
|
272
|
-
"""Get usage information as a Usage model
|
|
270
|
+
def usage_model(self) -> anthropic_models.Usage:
|
|
271
|
+
"""Get usage information as a Usage model."""
|
|
273
272
|
if self.usage is None:
|
|
274
|
-
return Usage()
|
|
275
|
-
return Usage.model_validate(self.usage)
|
|
273
|
+
return anthropic_models.Usage(input_tokens=0, output_tokens=0)
|
|
274
|
+
return anthropic_models.Usage.model_validate(self.usage)
|
|
276
275
|
|
|
277
276
|
model_config = ConfigDict(extra="allow")
|
|
278
277
|
|
|
@@ -282,7 +281,7 @@ class SDKMessageMode(SystemMessage):
|
|
|
282
281
|
"""Custom content block for system messages with source attribution."""
|
|
283
282
|
|
|
284
283
|
type: Literal["system_message"] = "system_message"
|
|
285
|
-
source: str = "
|
|
284
|
+
source: str = "claude_agent_sdk"
|
|
286
285
|
|
|
287
286
|
model_config = ConfigDict(extra="allow")
|
|
288
287
|
|
|
@@ -294,7 +293,7 @@ class ToolUseSDKBlock(BaseModel):
|
|
|
294
293
|
id: str = Field(..., description="Unique identifier for the tool use")
|
|
295
294
|
name: str = Field(..., description="Name of the tool being used")
|
|
296
295
|
input: dict[str, Any] = Field(..., description="Input parameters for the tool")
|
|
297
|
-
source: str = "
|
|
296
|
+
source: str = "claude_agent_sdk"
|
|
298
297
|
|
|
299
298
|
|
|
300
299
|
class ToolResultSDKBlock(BaseModel):
|
|
@@ -310,14 +309,14 @@ class ToolResultSDKBlock(BaseModel):
|
|
|
310
309
|
is_error: bool | None = Field(
|
|
311
310
|
None, description="Whether this result represents an error"
|
|
312
311
|
)
|
|
313
|
-
source: str = "
|
|
312
|
+
source: str = "claude_agent_sdk"
|
|
314
313
|
|
|
315
314
|
|
|
316
315
|
class ResultMessageBlock(ResultMessage):
|
|
317
316
|
"""Custom content block for result messages with session data."""
|
|
318
317
|
|
|
319
318
|
type: Literal["result_message"] = "result_message"
|
|
320
|
-
source: str = "
|
|
319
|
+
source: str = "claude_agent_sdk"
|
|
321
320
|
|
|
322
321
|
|
|
323
322
|
# Union type for all custom content blocks
|
|
@@ -337,6 +336,53 @@ SDKContentBlock = Annotated[
|
|
|
337
336
|
# Extended content block type that includes both SDK and custom blocks
|
|
338
337
|
ExtendedContentBlock = SDKContentBlock
|
|
339
338
|
|
|
339
|
+
# Union definition moved after imports
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
# Plugin-specific content block union that includes core and SDK-specific types
|
|
343
|
+
# Note: We only include SDK-specific types to avoid discriminator conflicts
|
|
344
|
+
# with core types that have the same discriminator values
|
|
345
|
+
CCProxyContentBlock = Annotated[
|
|
346
|
+
anthropic_models.RequestContentBlock
|
|
347
|
+
| SDKMessageMode
|
|
348
|
+
| ToolUseSDKBlock
|
|
349
|
+
| ToolResultSDKBlock
|
|
350
|
+
| ResultMessageBlock,
|
|
351
|
+
Field(discriminator="type"),
|
|
352
|
+
]
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
# Plugin-specific MessageResponse that uses the extended content block types
|
|
356
|
+
class MessageResponse(BaseModel):
|
|
357
|
+
"""Plugin-specific response model that supports both core and SDK content blocks."""
|
|
358
|
+
|
|
359
|
+
id: Annotated[str, Field(description="Unique identifier for the message")]
|
|
360
|
+
type: Annotated[Literal["message"], Field(description="Response type")] = "message"
|
|
361
|
+
role: Annotated[Literal["assistant"], Field(description="Message role")] = (
|
|
362
|
+
"assistant"
|
|
363
|
+
)
|
|
364
|
+
content: Annotated[
|
|
365
|
+
list[CCProxyContentBlock],
|
|
366
|
+
Field(description="Array of content blocks in the response"),
|
|
367
|
+
]
|
|
368
|
+
model: Annotated[str, Field(description="The model used for the response")]
|
|
369
|
+
stop_reason: Annotated[
|
|
370
|
+
str | None, Field(description="Reason why the model stopped generating")
|
|
371
|
+
] = None
|
|
372
|
+
stop_sequence: Annotated[
|
|
373
|
+
str | None,
|
|
374
|
+
Field(description="The stop sequence that triggered stopping (if applicable)"),
|
|
375
|
+
] = None
|
|
376
|
+
usage: Annotated[
|
|
377
|
+
anthropic_models.Usage, Field(description="Token usage information")
|
|
378
|
+
]
|
|
379
|
+
container: Annotated[
|
|
380
|
+
dict[str, Any] | None,
|
|
381
|
+
Field(description="Information about container used in the request"),
|
|
382
|
+
] = None
|
|
383
|
+
|
|
384
|
+
model_config = ConfigDict(extra="forbid", validate_assignment=True)
|
|
385
|
+
|
|
340
386
|
|
|
341
387
|
# SDK Query Message Types
|
|
342
388
|
class SDKMessageContent(BaseModel):
|
|
@@ -468,6 +514,8 @@ __all__ = [
|
|
|
468
514
|
"ResultMessageBlock",
|
|
469
515
|
"SDKContentBlock",
|
|
470
516
|
"ExtendedContentBlock",
|
|
517
|
+
"CCProxyContentBlock",
|
|
518
|
+
"MessageResponse",
|
|
471
519
|
# Conversion functions
|
|
472
520
|
"convert_sdk_text_block",
|
|
473
521
|
"convert_sdk_tool_use_block",
|