ccproxy-api 0.1.7__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ccproxy/api/__init__.py +1 -15
- ccproxy/api/app.py +434 -219
- ccproxy/api/bootstrap.py +30 -0
- ccproxy/api/decorators.py +85 -0
- ccproxy/api/dependencies.py +144 -168
- ccproxy/api/format_validation.py +54 -0
- ccproxy/api/middleware/cors.py +6 -3
- ccproxy/api/middleware/errors.py +388 -524
- ccproxy/api/middleware/hooks.py +563 -0
- ccproxy/api/middleware/normalize_headers.py +59 -0
- ccproxy/api/middleware/request_id.py +35 -16
- ccproxy/api/middleware/streaming_hooks.py +292 -0
- ccproxy/api/routes/__init__.py +5 -14
- ccproxy/api/routes/health.py +39 -672
- ccproxy/api/routes/plugins.py +277 -0
- ccproxy/auth/__init__.py +2 -19
- ccproxy/auth/bearer.py +25 -15
- ccproxy/auth/dependencies.py +123 -157
- ccproxy/auth/exceptions.py +0 -12
- ccproxy/auth/manager.py +35 -49
- ccproxy/auth/managers/__init__.py +10 -0
- ccproxy/auth/managers/base.py +523 -0
- ccproxy/auth/managers/base_enhanced.py +63 -0
- ccproxy/auth/managers/token_snapshot.py +77 -0
- ccproxy/auth/models/base.py +65 -0
- ccproxy/auth/models/credentials.py +40 -0
- ccproxy/auth/oauth/__init__.py +4 -18
- ccproxy/auth/oauth/base.py +533 -0
- ccproxy/auth/oauth/cli_errors.py +37 -0
- ccproxy/auth/oauth/flows.py +430 -0
- ccproxy/auth/oauth/protocol.py +366 -0
- ccproxy/auth/oauth/registry.py +408 -0
- ccproxy/auth/oauth/router.py +396 -0
- ccproxy/auth/oauth/routes.py +186 -113
- ccproxy/auth/oauth/session.py +151 -0
- ccproxy/auth/oauth/templates.py +342 -0
- ccproxy/auth/storage/__init__.py +2 -5
- ccproxy/auth/storage/base.py +279 -5
- ccproxy/auth/storage/generic.py +134 -0
- ccproxy/cli/__init__.py +1 -2
- ccproxy/cli/_settings_help.py +351 -0
- ccproxy/cli/commands/auth.py +1519 -793
- ccproxy/cli/commands/config/commands.py +209 -276
- ccproxy/cli/commands/plugins.py +669 -0
- ccproxy/cli/commands/serve.py +75 -810
- ccproxy/cli/commands/status.py +254 -0
- ccproxy/cli/decorators.py +83 -0
- ccproxy/cli/helpers.py +22 -60
- ccproxy/cli/main.py +359 -10
- ccproxy/cli/options/claude_options.py +0 -25
- ccproxy/config/__init__.py +7 -11
- ccproxy/config/core.py +227 -0
- ccproxy/config/env_generator.py +232 -0
- ccproxy/config/runtime.py +67 -0
- ccproxy/config/security.py +36 -3
- ccproxy/config/settings.py +382 -441
- ccproxy/config/toml_generator.py +299 -0
- ccproxy/config/utils.py +452 -0
- ccproxy/core/__init__.py +7 -271
- ccproxy/{_version.py → core/_version.py} +16 -3
- ccproxy/core/async_task_manager.py +516 -0
- ccproxy/core/async_utils.py +47 -14
- ccproxy/core/auth/__init__.py +6 -0
- ccproxy/core/constants.py +16 -50
- ccproxy/core/errors.py +53 -0
- ccproxy/core/id_utils.py +20 -0
- ccproxy/core/interfaces.py +16 -123
- ccproxy/core/logging.py +473 -18
- ccproxy/core/plugins/__init__.py +77 -0
- ccproxy/core/plugins/cli_discovery.py +211 -0
- ccproxy/core/plugins/declaration.py +455 -0
- ccproxy/core/plugins/discovery.py +604 -0
- ccproxy/core/plugins/factories.py +967 -0
- ccproxy/core/plugins/hooks/__init__.py +30 -0
- ccproxy/core/plugins/hooks/base.py +58 -0
- ccproxy/core/plugins/hooks/events.py +46 -0
- ccproxy/core/plugins/hooks/implementations/__init__.py +16 -0
- ccproxy/core/plugins/hooks/implementations/formatters/__init__.py +11 -0
- ccproxy/core/plugins/hooks/implementations/formatters/json.py +552 -0
- ccproxy/core/plugins/hooks/implementations/formatters/raw.py +370 -0
- ccproxy/core/plugins/hooks/implementations/http_tracer.py +431 -0
- ccproxy/core/plugins/hooks/layers.py +44 -0
- ccproxy/core/plugins/hooks/manager.py +186 -0
- ccproxy/core/plugins/hooks/registry.py +139 -0
- ccproxy/core/plugins/hooks/thread_manager.py +203 -0
- ccproxy/core/plugins/hooks/types.py +22 -0
- ccproxy/core/plugins/interfaces.py +416 -0
- ccproxy/core/plugins/loader.py +166 -0
- ccproxy/core/plugins/middleware.py +233 -0
- ccproxy/core/plugins/models.py +59 -0
- ccproxy/core/plugins/protocol.py +180 -0
- ccproxy/core/plugins/runtime.py +519 -0
- ccproxy/{observability/context.py → core/request_context.py} +137 -94
- ccproxy/core/status_report.py +211 -0
- ccproxy/core/transformers.py +13 -8
- ccproxy/data/claude_headers_fallback.json +540 -19
- ccproxy/data/codex_headers_fallback.json +114 -7
- ccproxy/http/__init__.py +30 -0
- ccproxy/http/base.py +95 -0
- ccproxy/http/client.py +323 -0
- ccproxy/http/hooks.py +642 -0
- ccproxy/http/pool.py +279 -0
- ccproxy/llms/formatters/__init__.py +7 -0
- ccproxy/llms/formatters/anthropic_to_openai/__init__.py +55 -0
- ccproxy/llms/formatters/anthropic_to_openai/errors.py +65 -0
- ccproxy/llms/formatters/anthropic_to_openai/requests.py +356 -0
- ccproxy/llms/formatters/anthropic_to_openai/responses.py +153 -0
- ccproxy/llms/formatters/anthropic_to_openai/streams.py +1546 -0
- ccproxy/llms/formatters/base.py +140 -0
- ccproxy/llms/formatters/base_model.py +33 -0
- ccproxy/llms/formatters/common/__init__.py +51 -0
- ccproxy/llms/formatters/common/identifiers.py +48 -0
- ccproxy/llms/formatters/common/streams.py +254 -0
- ccproxy/llms/formatters/common/thinking.py +74 -0
- ccproxy/llms/formatters/common/usage.py +135 -0
- ccproxy/llms/formatters/constants.py +55 -0
- ccproxy/llms/formatters/context.py +116 -0
- ccproxy/llms/formatters/mapping.py +33 -0
- ccproxy/llms/formatters/openai_to_anthropic/__init__.py +55 -0
- ccproxy/llms/formatters/openai_to_anthropic/_helpers.py +141 -0
- ccproxy/llms/formatters/openai_to_anthropic/errors.py +53 -0
- ccproxy/llms/formatters/openai_to_anthropic/requests.py +674 -0
- ccproxy/llms/formatters/openai_to_anthropic/responses.py +285 -0
- ccproxy/llms/formatters/openai_to_anthropic/streams.py +530 -0
- ccproxy/llms/formatters/openai_to_openai/__init__.py +53 -0
- ccproxy/llms/formatters/openai_to_openai/_helpers.py +325 -0
- ccproxy/llms/formatters/openai_to_openai/errors.py +6 -0
- ccproxy/llms/formatters/openai_to_openai/requests.py +388 -0
- ccproxy/llms/formatters/openai_to_openai/responses.py +594 -0
- ccproxy/llms/formatters/openai_to_openai/streams.py +1832 -0
- ccproxy/llms/formatters/utils.py +306 -0
- ccproxy/llms/models/__init__.py +9 -0
- ccproxy/llms/models/anthropic.py +619 -0
- ccproxy/llms/models/openai.py +844 -0
- ccproxy/llms/streaming/__init__.py +26 -0
- ccproxy/llms/streaming/accumulators.py +1074 -0
- ccproxy/llms/streaming/formatters.py +251 -0
- ccproxy/{adapters/openai/streaming.py → llms/streaming/processors.py} +193 -240
- ccproxy/models/__init__.py +8 -159
- ccproxy/models/detection.py +92 -193
- ccproxy/models/provider.py +75 -0
- ccproxy/plugins/access_log/README.md +32 -0
- ccproxy/plugins/access_log/__init__.py +20 -0
- ccproxy/plugins/access_log/config.py +33 -0
- ccproxy/plugins/access_log/formatter.py +126 -0
- ccproxy/plugins/access_log/hook.py +763 -0
- ccproxy/plugins/access_log/logger.py +254 -0
- ccproxy/plugins/access_log/plugin.py +137 -0
- ccproxy/plugins/access_log/writer.py +109 -0
- ccproxy/plugins/analytics/README.md +24 -0
- ccproxy/plugins/analytics/__init__.py +1 -0
- ccproxy/plugins/analytics/config.py +5 -0
- ccproxy/plugins/analytics/ingest.py +85 -0
- ccproxy/plugins/analytics/models.py +97 -0
- ccproxy/plugins/analytics/plugin.py +121 -0
- ccproxy/plugins/analytics/routes.py +163 -0
- ccproxy/plugins/analytics/service.py +284 -0
- ccproxy/plugins/claude_api/README.md +29 -0
- ccproxy/plugins/claude_api/__init__.py +10 -0
- ccproxy/plugins/claude_api/adapter.py +829 -0
- ccproxy/plugins/claude_api/config.py +52 -0
- ccproxy/plugins/claude_api/detection_service.py +461 -0
- ccproxy/plugins/claude_api/health.py +175 -0
- ccproxy/plugins/claude_api/hooks.py +284 -0
- ccproxy/plugins/claude_api/models.py +256 -0
- ccproxy/plugins/claude_api/plugin.py +298 -0
- ccproxy/plugins/claude_api/routes.py +118 -0
- ccproxy/plugins/claude_api/streaming_metrics.py +68 -0
- ccproxy/plugins/claude_api/tasks.py +84 -0
- ccproxy/plugins/claude_sdk/README.md +35 -0
- ccproxy/plugins/claude_sdk/__init__.py +80 -0
- ccproxy/plugins/claude_sdk/adapter.py +749 -0
- ccproxy/plugins/claude_sdk/auth.py +57 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/client.py +63 -39
- ccproxy/plugins/claude_sdk/config.py +210 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/converter.py +6 -6
- ccproxy/plugins/claude_sdk/detection_service.py +163 -0
- ccproxy/{services/claude_sdk_service.py → plugins/claude_sdk/handler.py} +123 -304
- ccproxy/plugins/claude_sdk/health.py +113 -0
- ccproxy/plugins/claude_sdk/hooks.py +115 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/manager.py +42 -32
- ccproxy/{claude_sdk → plugins/claude_sdk}/message_queue.py +8 -8
- ccproxy/{models/claude_sdk.py → plugins/claude_sdk/models.py} +64 -16
- ccproxy/plugins/claude_sdk/options.py +154 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/parser.py +23 -5
- ccproxy/plugins/claude_sdk/plugin.py +269 -0
- ccproxy/plugins/claude_sdk/routes.py +104 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/session_client.py +124 -12
- ccproxy/plugins/claude_sdk/session_pool.py +700 -0
- ccproxy/{claude_sdk → plugins/claude_sdk}/stream_handle.py +48 -43
- ccproxy/{claude_sdk → plugins/claude_sdk}/stream_worker.py +22 -18
- ccproxy/{claude_sdk → plugins/claude_sdk}/streaming.py +50 -16
- ccproxy/plugins/claude_sdk/tasks.py +97 -0
- ccproxy/plugins/claude_shared/README.md +18 -0
- ccproxy/plugins/claude_shared/__init__.py +12 -0
- ccproxy/plugins/claude_shared/model_defaults.py +171 -0
- ccproxy/plugins/codex/README.md +35 -0
- ccproxy/plugins/codex/__init__.py +6 -0
- ccproxy/plugins/codex/adapter.py +635 -0
- ccproxy/{config/codex.py → plugins/codex/config.py} +78 -12
- ccproxy/plugins/codex/detection_service.py +544 -0
- ccproxy/plugins/codex/health.py +162 -0
- ccproxy/plugins/codex/hooks.py +263 -0
- ccproxy/plugins/codex/model_defaults.py +39 -0
- ccproxy/plugins/codex/models.py +263 -0
- ccproxy/plugins/codex/plugin.py +275 -0
- ccproxy/plugins/codex/routes.py +129 -0
- ccproxy/plugins/codex/streaming_metrics.py +324 -0
- ccproxy/plugins/codex/tasks.py +106 -0
- ccproxy/plugins/codex/utils/__init__.py +1 -0
- ccproxy/plugins/codex/utils/sse_parser.py +106 -0
- ccproxy/plugins/command_replay/README.md +34 -0
- ccproxy/plugins/command_replay/__init__.py +17 -0
- ccproxy/plugins/command_replay/config.py +133 -0
- ccproxy/plugins/command_replay/formatter.py +432 -0
- ccproxy/plugins/command_replay/hook.py +294 -0
- ccproxy/plugins/command_replay/plugin.py +161 -0
- ccproxy/plugins/copilot/README.md +39 -0
- ccproxy/plugins/copilot/__init__.py +11 -0
- ccproxy/plugins/copilot/adapter.py +465 -0
- ccproxy/plugins/copilot/config.py +155 -0
- ccproxy/plugins/copilot/data/copilot_fallback.json +41 -0
- ccproxy/plugins/copilot/detection_service.py +255 -0
- ccproxy/plugins/copilot/manager.py +275 -0
- ccproxy/plugins/copilot/model_defaults.py +284 -0
- ccproxy/plugins/copilot/models.py +148 -0
- ccproxy/plugins/copilot/oauth/__init__.py +16 -0
- ccproxy/plugins/copilot/oauth/client.py +494 -0
- ccproxy/plugins/copilot/oauth/models.py +385 -0
- ccproxy/plugins/copilot/oauth/provider.py +602 -0
- ccproxy/plugins/copilot/oauth/storage.py +170 -0
- ccproxy/plugins/copilot/plugin.py +360 -0
- ccproxy/plugins/copilot/routes.py +294 -0
- ccproxy/plugins/credential_balancer/README.md +124 -0
- ccproxy/plugins/credential_balancer/__init__.py +6 -0
- ccproxy/plugins/credential_balancer/config.py +270 -0
- ccproxy/plugins/credential_balancer/factory.py +415 -0
- ccproxy/plugins/credential_balancer/hook.py +51 -0
- ccproxy/plugins/credential_balancer/manager.py +587 -0
- ccproxy/plugins/credential_balancer/plugin.py +146 -0
- ccproxy/plugins/dashboard/README.md +25 -0
- ccproxy/plugins/dashboard/__init__.py +1 -0
- ccproxy/plugins/dashboard/config.py +8 -0
- ccproxy/plugins/dashboard/plugin.py +71 -0
- ccproxy/plugins/dashboard/routes.py +67 -0
- ccproxy/plugins/docker/README.md +32 -0
- ccproxy/{docker → plugins/docker}/__init__.py +3 -0
- ccproxy/{docker → plugins/docker}/adapter.py +108 -10
- ccproxy/plugins/docker/config.py +82 -0
- ccproxy/{docker → plugins/docker}/docker_path.py +4 -3
- ccproxy/{docker → plugins/docker}/middleware.py +2 -2
- ccproxy/plugins/docker/plugin.py +198 -0
- ccproxy/{docker → plugins/docker}/stream_process.py +3 -3
- ccproxy/plugins/duckdb_storage/README.md +26 -0
- ccproxy/plugins/duckdb_storage/__init__.py +1 -0
- ccproxy/plugins/duckdb_storage/config.py +22 -0
- ccproxy/plugins/duckdb_storage/plugin.py +128 -0
- ccproxy/plugins/duckdb_storage/routes.py +51 -0
- ccproxy/plugins/duckdb_storage/storage.py +633 -0
- ccproxy/plugins/max_tokens/README.md +38 -0
- ccproxy/plugins/max_tokens/__init__.py +12 -0
- ccproxy/plugins/max_tokens/adapter.py +235 -0
- ccproxy/plugins/max_tokens/config.py +86 -0
- ccproxy/plugins/max_tokens/models.py +53 -0
- ccproxy/plugins/max_tokens/plugin.py +200 -0
- ccproxy/plugins/max_tokens/service.py +271 -0
- ccproxy/plugins/max_tokens/token_limits.json +54 -0
- ccproxy/plugins/metrics/README.md +35 -0
- ccproxy/plugins/metrics/__init__.py +10 -0
- ccproxy/{observability/metrics.py → plugins/metrics/collector.py} +20 -153
- ccproxy/plugins/metrics/config.py +85 -0
- ccproxy/plugins/metrics/grafana/dashboards/ccproxy-dashboard.json +1720 -0
- ccproxy/plugins/metrics/hook.py +403 -0
- ccproxy/plugins/metrics/plugin.py +268 -0
- ccproxy/{observability → plugins/metrics}/pushgateway.py +57 -59
- ccproxy/plugins/metrics/routes.py +107 -0
- ccproxy/plugins/metrics/tasks.py +117 -0
- ccproxy/plugins/oauth_claude/README.md +35 -0
- ccproxy/plugins/oauth_claude/__init__.py +14 -0
- ccproxy/plugins/oauth_claude/client.py +270 -0
- ccproxy/plugins/oauth_claude/config.py +84 -0
- ccproxy/plugins/oauth_claude/manager.py +482 -0
- ccproxy/plugins/oauth_claude/models.py +266 -0
- ccproxy/plugins/oauth_claude/plugin.py +149 -0
- ccproxy/plugins/oauth_claude/provider.py +571 -0
- ccproxy/plugins/oauth_claude/storage.py +212 -0
- ccproxy/plugins/oauth_codex/README.md +38 -0
- ccproxy/plugins/oauth_codex/__init__.py +14 -0
- ccproxy/plugins/oauth_codex/client.py +224 -0
- ccproxy/plugins/oauth_codex/config.py +95 -0
- ccproxy/plugins/oauth_codex/manager.py +256 -0
- ccproxy/plugins/oauth_codex/models.py +239 -0
- ccproxy/plugins/oauth_codex/plugin.py +146 -0
- ccproxy/plugins/oauth_codex/provider.py +574 -0
- ccproxy/plugins/oauth_codex/storage.py +92 -0
- ccproxy/plugins/permissions/README.md +28 -0
- ccproxy/plugins/permissions/__init__.py +22 -0
- ccproxy/plugins/permissions/config.py +28 -0
- ccproxy/{cli/commands/permission_handler.py → plugins/permissions/handlers/cli.py} +49 -25
- ccproxy/plugins/permissions/handlers/protocol.py +33 -0
- ccproxy/plugins/permissions/handlers/terminal.py +675 -0
- ccproxy/{api/routes → plugins/permissions}/mcp.py +34 -7
- ccproxy/{models/permissions.py → plugins/permissions/models.py} +65 -1
- ccproxy/plugins/permissions/plugin.py +153 -0
- ccproxy/{api/routes/permissions.py → plugins/permissions/routes.py} +20 -16
- ccproxy/{api/services/permission_service.py → plugins/permissions/service.py} +65 -11
- ccproxy/{api → plugins/permissions}/ui/permission_handler_protocol.py +1 -1
- ccproxy/{api → plugins/permissions}/ui/terminal_permission_handler.py +66 -10
- ccproxy/plugins/pricing/README.md +34 -0
- ccproxy/plugins/pricing/__init__.py +6 -0
- ccproxy/{pricing → plugins/pricing}/cache.py +7 -6
- ccproxy/{config/pricing.py → plugins/pricing/config.py} +32 -6
- ccproxy/plugins/pricing/exceptions.py +35 -0
- ccproxy/plugins/pricing/loader.py +440 -0
- ccproxy/{pricing → plugins/pricing}/models.py +13 -23
- ccproxy/plugins/pricing/plugin.py +169 -0
- ccproxy/plugins/pricing/service.py +191 -0
- ccproxy/plugins/pricing/tasks.py +300 -0
- ccproxy/{pricing → plugins/pricing}/updater.py +86 -72
- ccproxy/plugins/pricing/utils.py +99 -0
- ccproxy/plugins/request_tracer/README.md +40 -0
- ccproxy/plugins/request_tracer/__init__.py +7 -0
- ccproxy/plugins/request_tracer/config.py +120 -0
- ccproxy/plugins/request_tracer/hook.py +415 -0
- ccproxy/plugins/request_tracer/plugin.py +255 -0
- ccproxy/scheduler/__init__.py +2 -14
- ccproxy/scheduler/core.py +26 -41
- ccproxy/scheduler/manager.py +61 -105
- ccproxy/scheduler/registry.py +6 -32
- ccproxy/scheduler/tasks.py +268 -276
- ccproxy/services/__init__.py +0 -1
- ccproxy/services/adapters/__init__.py +11 -0
- ccproxy/services/adapters/base.py +123 -0
- ccproxy/services/adapters/chain_composer.py +88 -0
- ccproxy/services/adapters/chain_validation.py +44 -0
- ccproxy/services/adapters/chat_accumulator.py +200 -0
- ccproxy/services/adapters/delta_utils.py +142 -0
- ccproxy/services/adapters/format_adapter.py +136 -0
- ccproxy/services/adapters/format_context.py +11 -0
- ccproxy/services/adapters/format_registry.py +158 -0
- ccproxy/services/adapters/http_adapter.py +1045 -0
- ccproxy/services/adapters/mock_adapter.py +118 -0
- ccproxy/services/adapters/protocols.py +35 -0
- ccproxy/services/adapters/simple_converters.py +571 -0
- ccproxy/services/auth_registry.py +180 -0
- ccproxy/services/cache/__init__.py +6 -0
- ccproxy/services/cache/response_cache.py +261 -0
- ccproxy/services/cli_detection.py +437 -0
- ccproxy/services/config/__init__.py +6 -0
- ccproxy/services/config/proxy_configuration.py +111 -0
- ccproxy/services/container.py +256 -0
- ccproxy/services/factories.py +380 -0
- ccproxy/services/handler_config.py +76 -0
- ccproxy/services/interfaces.py +298 -0
- ccproxy/services/mocking/__init__.py +6 -0
- ccproxy/services/mocking/mock_handler.py +291 -0
- ccproxy/services/tracing/__init__.py +7 -0
- ccproxy/services/tracing/interfaces.py +61 -0
- ccproxy/services/tracing/null_tracer.py +57 -0
- ccproxy/streaming/__init__.py +23 -0
- ccproxy/streaming/buffer.py +1056 -0
- ccproxy/streaming/deferred.py +897 -0
- ccproxy/streaming/handler.py +117 -0
- ccproxy/streaming/interfaces.py +77 -0
- ccproxy/streaming/simple_adapter.py +39 -0
- ccproxy/streaming/sse.py +109 -0
- ccproxy/streaming/sse_parser.py +127 -0
- ccproxy/templates/__init__.py +6 -0
- ccproxy/templates/plugin_scaffold.py +695 -0
- ccproxy/testing/endpoints/__init__.py +33 -0
- ccproxy/testing/endpoints/cli.py +215 -0
- ccproxy/testing/endpoints/config.py +874 -0
- ccproxy/testing/endpoints/console.py +57 -0
- ccproxy/testing/endpoints/models.py +100 -0
- ccproxy/testing/endpoints/runner.py +1903 -0
- ccproxy/testing/endpoints/tools.py +308 -0
- ccproxy/testing/mock_responses.py +70 -1
- ccproxy/testing/response_handlers.py +20 -0
- ccproxy/utils/__init__.py +0 -6
- ccproxy/utils/binary_resolver.py +476 -0
- ccproxy/utils/caching.py +327 -0
- ccproxy/utils/cli_logging.py +101 -0
- ccproxy/utils/command_line.py +251 -0
- ccproxy/utils/headers.py +228 -0
- ccproxy/utils/model_mapper.py +120 -0
- ccproxy/utils/startup_helpers.py +68 -446
- ccproxy/utils/version_checker.py +273 -6
- ccproxy_api-0.2.0.dist-info/METADATA +212 -0
- ccproxy_api-0.2.0.dist-info/RECORD +417 -0
- {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0.dist-info}/WHEEL +1 -1
- ccproxy_api-0.2.0.dist-info/entry_points.txt +24 -0
- ccproxy/__init__.py +0 -4
- ccproxy/adapters/__init__.py +0 -11
- ccproxy/adapters/base.py +0 -80
- ccproxy/adapters/codex/__init__.py +0 -11
- ccproxy/adapters/openai/__init__.py +0 -42
- ccproxy/adapters/openai/adapter.py +0 -953
- ccproxy/adapters/openai/models.py +0 -412
- ccproxy/adapters/openai/response_adapter.py +0 -355
- ccproxy/adapters/openai/response_models.py +0 -178
- ccproxy/api/middleware/headers.py +0 -49
- ccproxy/api/middleware/logging.py +0 -180
- ccproxy/api/middleware/request_content_logging.py +0 -297
- ccproxy/api/middleware/server_header.py +0 -58
- ccproxy/api/responses.py +0 -89
- ccproxy/api/routes/claude.py +0 -371
- ccproxy/api/routes/codex.py +0 -1251
- ccproxy/api/routes/metrics.py +0 -1029
- ccproxy/api/routes/proxy.py +0 -211
- ccproxy/api/services/__init__.py +0 -6
- ccproxy/auth/conditional.py +0 -84
- ccproxy/auth/credentials_adapter.py +0 -93
- ccproxy/auth/models.py +0 -118
- ccproxy/auth/oauth/models.py +0 -48
- ccproxy/auth/openai/__init__.py +0 -13
- ccproxy/auth/openai/credentials.py +0 -166
- ccproxy/auth/openai/oauth_client.py +0 -334
- ccproxy/auth/openai/storage.py +0 -184
- ccproxy/auth/storage/json_file.py +0 -158
- ccproxy/auth/storage/keyring.py +0 -189
- ccproxy/claude_sdk/__init__.py +0 -18
- ccproxy/claude_sdk/options.py +0 -194
- ccproxy/claude_sdk/session_pool.py +0 -550
- ccproxy/cli/docker/__init__.py +0 -34
- ccproxy/cli/docker/adapter_factory.py +0 -157
- ccproxy/cli/docker/params.py +0 -274
- ccproxy/config/auth.py +0 -153
- ccproxy/config/claude.py +0 -348
- ccproxy/config/cors.py +0 -79
- ccproxy/config/discovery.py +0 -95
- ccproxy/config/docker_settings.py +0 -264
- ccproxy/config/observability.py +0 -158
- ccproxy/config/reverse_proxy.py +0 -31
- ccproxy/config/scheduler.py +0 -108
- ccproxy/config/server.py +0 -86
- ccproxy/config/validators.py +0 -231
- ccproxy/core/codex_transformers.py +0 -389
- ccproxy/core/http.py +0 -328
- ccproxy/core/http_transformers.py +0 -812
- ccproxy/core/proxy.py +0 -143
- ccproxy/core/validators.py +0 -288
- ccproxy/models/errors.py +0 -42
- ccproxy/models/messages.py +0 -269
- ccproxy/models/requests.py +0 -107
- ccproxy/models/responses.py +0 -270
- ccproxy/models/types.py +0 -102
- ccproxy/observability/__init__.py +0 -51
- ccproxy/observability/access_logger.py +0 -457
- ccproxy/observability/sse_events.py +0 -303
- ccproxy/observability/stats_printer.py +0 -753
- ccproxy/observability/storage/__init__.py +0 -1
- ccproxy/observability/storage/duckdb_simple.py +0 -677
- ccproxy/observability/storage/models.py +0 -70
- ccproxy/observability/streaming_response.py +0 -107
- ccproxy/pricing/__init__.py +0 -19
- ccproxy/pricing/loader.py +0 -251
- ccproxy/services/claude_detection_service.py +0 -243
- ccproxy/services/codex_detection_service.py +0 -252
- ccproxy/services/credentials/__init__.py +0 -55
- ccproxy/services/credentials/config.py +0 -105
- ccproxy/services/credentials/manager.py +0 -561
- ccproxy/services/credentials/oauth_client.py +0 -481
- ccproxy/services/proxy_service.py +0 -1827
- ccproxy/static/.keep +0 -0
- ccproxy/utils/cost_calculator.py +0 -210
- ccproxy/utils/disconnection_monitor.py +0 -83
- ccproxy/utils/model_mapping.py +0 -199
- ccproxy/utils/models_provider.py +0 -150
- ccproxy/utils/simple_request_logger.py +0 -284
- ccproxy/utils/streaming_metrics.py +0 -199
- ccproxy_api-0.1.7.dist-info/METADATA +0 -615
- ccproxy_api-0.1.7.dist-info/RECORD +0 -191
- ccproxy_api-0.1.7.dist-info/entry_points.txt +0 -4
- /ccproxy/{api/middleware/auth.py → auth/models/__init__.py} +0 -0
- /ccproxy/{claude_sdk → plugins/claude_sdk}/exceptions.py +0 -0
- /ccproxy/{docker → plugins/docker}/models.py +0 -0
- /ccproxy/{docker → plugins/docker}/protocol.py +0 -0
- /ccproxy/{docker → plugins/docker}/validators.py +0 -0
- /ccproxy/{auth/oauth/storage.py → plugins/permissions/handlers/__init__.py} +0 -0
- /ccproxy/{api → plugins/permissions}/ui/__init__.py +0 -0
- {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,303 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Server-Sent Events (SSE) event manager for real-time dashboard updates.
|
|
3
|
-
|
|
4
|
-
This module provides centralized SSE connection management and event broadcasting
|
|
5
|
-
for real-time dashboard notifications when requests start, complete, or error.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
|
|
10
|
-
import asyncio
|
|
11
|
-
import json
|
|
12
|
-
import time
|
|
13
|
-
import uuid
|
|
14
|
-
from collections.abc import AsyncGenerator
|
|
15
|
-
from typing import Any
|
|
16
|
-
|
|
17
|
-
import structlog
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
logger = structlog.get_logger(__name__)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class SSEEventManager:
|
|
24
|
-
"""
|
|
25
|
-
Centralized SSE connection management and event broadcasting.
|
|
26
|
-
|
|
27
|
-
Manages multiple SSE connections and broadcasts events to all connected clients.
|
|
28
|
-
Uses bounded queues to prevent memory issues with slow clients.
|
|
29
|
-
"""
|
|
30
|
-
|
|
31
|
-
def __init__(self, max_queue_size: int = 100) -> None:
|
|
32
|
-
"""
|
|
33
|
-
Initialize SSE event manager.
|
|
34
|
-
|
|
35
|
-
Args:
|
|
36
|
-
max_queue_size: Maximum events to queue per connection before dropping
|
|
37
|
-
"""
|
|
38
|
-
self._connections: dict[str, asyncio.Queue[dict[str, Any]]] = {}
|
|
39
|
-
self._lock = asyncio.Lock()
|
|
40
|
-
self._max_queue_size = max_queue_size
|
|
41
|
-
|
|
42
|
-
async def add_connection(
|
|
43
|
-
self, connection_id: str | None = None, request_id: str | None = None
|
|
44
|
-
) -> AsyncGenerator[str, None]:
|
|
45
|
-
"""
|
|
46
|
-
Add SSE connection and yield events as JSON strings.
|
|
47
|
-
|
|
48
|
-
Args:
|
|
49
|
-
connection_id: Unique connection identifier (generated if not provided)
|
|
50
|
-
request_id: Request identifier for tracking
|
|
51
|
-
|
|
52
|
-
Yields:
|
|
53
|
-
JSON-formatted event strings for SSE
|
|
54
|
-
"""
|
|
55
|
-
if connection_id is None:
|
|
56
|
-
connection_id = str(uuid.uuid4())
|
|
57
|
-
|
|
58
|
-
# Create bounded queue for this connection
|
|
59
|
-
queue: asyncio.Queue[dict[str, Any]] = asyncio.Queue(
|
|
60
|
-
maxsize=self._max_queue_size
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
async with self._lock:
|
|
64
|
-
self._connections[connection_id] = queue
|
|
65
|
-
|
|
66
|
-
logger.debug(
|
|
67
|
-
"sse_connection_added", connection_id=connection_id, request_id=request_id
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
try:
|
|
71
|
-
# Send initial connection event
|
|
72
|
-
connection_event = {
|
|
73
|
-
"type": "connection",
|
|
74
|
-
"message": "Connected to metrics stream",
|
|
75
|
-
"connection_id": connection_id,
|
|
76
|
-
"timestamp": time.time(),
|
|
77
|
-
}
|
|
78
|
-
yield self._format_sse_event(connection_event)
|
|
79
|
-
|
|
80
|
-
while True:
|
|
81
|
-
# Wait for next event
|
|
82
|
-
event = await queue.get()
|
|
83
|
-
|
|
84
|
-
# Check for special disconnect event
|
|
85
|
-
if event.get("type") == "_disconnect":
|
|
86
|
-
break
|
|
87
|
-
|
|
88
|
-
# Yield formatted event
|
|
89
|
-
yield self._format_sse_event(event)
|
|
90
|
-
|
|
91
|
-
except asyncio.CancelledError:
|
|
92
|
-
logger.debug("sse_connection_cancelled", connection_id=connection_id)
|
|
93
|
-
raise
|
|
94
|
-
except GeneratorExit:
|
|
95
|
-
logger.debug("sse_connection_generator_exit", connection_id=connection_id)
|
|
96
|
-
raise
|
|
97
|
-
finally:
|
|
98
|
-
# Clean up connection
|
|
99
|
-
await self._cleanup_connection(connection_id)
|
|
100
|
-
|
|
101
|
-
# Send disconnect event only if not in shutdown
|
|
102
|
-
try:
|
|
103
|
-
disconnect_event = {
|
|
104
|
-
"type": "disconnect",
|
|
105
|
-
"message": "Stream disconnected",
|
|
106
|
-
"connection_id": connection_id,
|
|
107
|
-
"timestamp": time.time(),
|
|
108
|
-
}
|
|
109
|
-
yield self._format_sse_event(disconnect_event)
|
|
110
|
-
except (GeneratorExit, asyncio.CancelledError):
|
|
111
|
-
# Ignore errors during cleanup
|
|
112
|
-
pass
|
|
113
|
-
|
|
114
|
-
async def emit_event(self, event_type: str, data: dict[str, Any]) -> None:
|
|
115
|
-
"""
|
|
116
|
-
Broadcast event to all connected clients.
|
|
117
|
-
|
|
118
|
-
Args:
|
|
119
|
-
event_type: Type of event (request_start, request_complete, request_error)
|
|
120
|
-
data: Event data dictionary
|
|
121
|
-
"""
|
|
122
|
-
if not self._connections:
|
|
123
|
-
return # No connected clients
|
|
124
|
-
|
|
125
|
-
event = {
|
|
126
|
-
"type": event_type,
|
|
127
|
-
"data": data,
|
|
128
|
-
"timestamp": time.time(),
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
async with self._lock:
|
|
132
|
-
# Get copy of connections to avoid modification during iteration
|
|
133
|
-
connections = dict(self._connections)
|
|
134
|
-
|
|
135
|
-
# Broadcast to all connections
|
|
136
|
-
failed_connections = []
|
|
137
|
-
|
|
138
|
-
for connection_id, queue in connections.items():
|
|
139
|
-
try:
|
|
140
|
-
# Try to put event in queue without blocking
|
|
141
|
-
queue.put_nowait(event)
|
|
142
|
-
except asyncio.QueueFull:
|
|
143
|
-
# Queue is full, handle overflow
|
|
144
|
-
try:
|
|
145
|
-
# Try to drop oldest event and add overflow indicator
|
|
146
|
-
queue.get_nowait() # Remove oldest
|
|
147
|
-
overflow_event = {
|
|
148
|
-
"type": "overflow",
|
|
149
|
-
"message": "Event queue full, some events dropped",
|
|
150
|
-
"timestamp": time.time(),
|
|
151
|
-
}
|
|
152
|
-
try:
|
|
153
|
-
queue.put_nowait(overflow_event)
|
|
154
|
-
queue.put_nowait(event)
|
|
155
|
-
except asyncio.QueueFull:
|
|
156
|
-
# Still full after dropping, connection is problematic
|
|
157
|
-
failed_connections.append(connection_id)
|
|
158
|
-
continue
|
|
159
|
-
|
|
160
|
-
logger.warning(
|
|
161
|
-
"sse_queue_overflow",
|
|
162
|
-
connection_id=connection_id,
|
|
163
|
-
max_queue_size=self._max_queue_size,
|
|
164
|
-
)
|
|
165
|
-
except asyncio.QueueEmpty:
|
|
166
|
-
# Queue became empty, try again
|
|
167
|
-
try:
|
|
168
|
-
queue.put_nowait(event)
|
|
169
|
-
except asyncio.QueueFull:
|
|
170
|
-
# Still full, connection is problematic
|
|
171
|
-
failed_connections.append(connection_id)
|
|
172
|
-
except Exception as e:
|
|
173
|
-
logger.error(
|
|
174
|
-
"sse_overflow_error",
|
|
175
|
-
connection_id=connection_id,
|
|
176
|
-
error=str(e),
|
|
177
|
-
)
|
|
178
|
-
failed_connections.append(connection_id)
|
|
179
|
-
except Exception as e:
|
|
180
|
-
logger.error(
|
|
181
|
-
"sse_broadcast_error",
|
|
182
|
-
connection_id=connection_id,
|
|
183
|
-
error=str(e),
|
|
184
|
-
)
|
|
185
|
-
failed_connections.append(connection_id)
|
|
186
|
-
|
|
187
|
-
# Clean up failed connections
|
|
188
|
-
for connection_id in failed_connections:
|
|
189
|
-
await self._cleanup_connection(connection_id)
|
|
190
|
-
|
|
191
|
-
if failed_connections:
|
|
192
|
-
logger.debug(
|
|
193
|
-
"sse_connections_cleaned",
|
|
194
|
-
failed_count=len(failed_connections),
|
|
195
|
-
active_count=len(self._connections),
|
|
196
|
-
)
|
|
197
|
-
|
|
198
|
-
async def disconnect_all(self) -> None:
|
|
199
|
-
"""Disconnect all active connections gracefully."""
|
|
200
|
-
async with self._lock:
|
|
201
|
-
connections = dict(self._connections)
|
|
202
|
-
|
|
203
|
-
for connection_id, queue in connections.items():
|
|
204
|
-
try:
|
|
205
|
-
# Send disconnect signal
|
|
206
|
-
disconnect_signal = {"type": "_disconnect"}
|
|
207
|
-
queue.put_nowait(disconnect_signal)
|
|
208
|
-
except asyncio.QueueFull:
|
|
209
|
-
# Queue is full, force cleanup
|
|
210
|
-
await self._cleanup_connection(connection_id)
|
|
211
|
-
except Exception as e:
|
|
212
|
-
logger.error(
|
|
213
|
-
"sse_disconnect_error",
|
|
214
|
-
connection_id=connection_id,
|
|
215
|
-
error=str(e),
|
|
216
|
-
)
|
|
217
|
-
|
|
218
|
-
logger.debug("sse_all_connections_disconnected")
|
|
219
|
-
|
|
220
|
-
async def _cleanup_connection(self, connection_id: str) -> None:
|
|
221
|
-
"""Remove connection from active connections."""
|
|
222
|
-
async with self._lock:
|
|
223
|
-
if connection_id in self._connections:
|
|
224
|
-
del self._connections[connection_id]
|
|
225
|
-
logger.debug("sse_connection_removed", connection_id=connection_id)
|
|
226
|
-
|
|
227
|
-
def _format_sse_event(self, event: dict[str, Any]) -> str:
|
|
228
|
-
"""Format event as SSE data string."""
|
|
229
|
-
try:
|
|
230
|
-
json_data = json.dumps(event, default=self._json_serializer)
|
|
231
|
-
return f"data: {json_data}\n\n"
|
|
232
|
-
except (TypeError, ValueError) as e:
|
|
233
|
-
logger.error("sse_format_error", error=str(e), event_type=event.get("type"))
|
|
234
|
-
# Return error event instead
|
|
235
|
-
error_event = {
|
|
236
|
-
"type": "error",
|
|
237
|
-
"message": "Failed to format event",
|
|
238
|
-
"timestamp": time.time(),
|
|
239
|
-
}
|
|
240
|
-
json_data = json.dumps(error_event, default=self._json_serializer)
|
|
241
|
-
return f"data: {json_data}\n\n"
|
|
242
|
-
|
|
243
|
-
def _json_serializer(self, obj: Any) -> Any:
|
|
244
|
-
"""Custom JSON serializer for datetime and other objects."""
|
|
245
|
-
from datetime import datetime
|
|
246
|
-
|
|
247
|
-
if isinstance(obj, datetime):
|
|
248
|
-
return obj.isoformat()
|
|
249
|
-
raise TypeError(f"Object of type {type(obj)} is not JSON serializable")
|
|
250
|
-
|
|
251
|
-
async def get_connection_count(self) -> int:
|
|
252
|
-
"""Get number of active connections."""
|
|
253
|
-
async with self._lock:
|
|
254
|
-
return len(self._connections)
|
|
255
|
-
|
|
256
|
-
async def get_connection_info(self) -> dict[str, Any]:
|
|
257
|
-
"""Get connection status information."""
|
|
258
|
-
async with self._lock:
|
|
259
|
-
return {
|
|
260
|
-
"active_connections": len(self._connections),
|
|
261
|
-
"max_queue_size": self._max_queue_size,
|
|
262
|
-
"connection_ids": list(self._connections.keys()),
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
# Global SSE event manager instance
|
|
267
|
-
_global_sse_manager: SSEEventManager | None = None
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
def get_sse_manager() -> SSEEventManager:
|
|
271
|
-
"""Get or create global SSE event manager."""
|
|
272
|
-
global _global_sse_manager
|
|
273
|
-
|
|
274
|
-
if _global_sse_manager is None:
|
|
275
|
-
_global_sse_manager = SSEEventManager()
|
|
276
|
-
|
|
277
|
-
return _global_sse_manager
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
async def emit_sse_event(event_type: str, data: dict[str, Any]) -> None:
|
|
281
|
-
"""
|
|
282
|
-
Convenience function to emit SSE event using global manager.
|
|
283
|
-
|
|
284
|
-
Args:
|
|
285
|
-
event_type: Type of event (request_start, request_complete, request_error)
|
|
286
|
-
data: Event data dictionary
|
|
287
|
-
"""
|
|
288
|
-
try:
|
|
289
|
-
manager = get_sse_manager()
|
|
290
|
-
await manager.emit_event(event_type, data)
|
|
291
|
-
except Exception as e:
|
|
292
|
-
# Log error but don't fail the request
|
|
293
|
-
logger.debug("sse_emit_failed", event_type=event_type, error=str(e))
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
async def cleanup_sse_manager() -> None:
|
|
297
|
-
"""Clean up global SSE manager and disconnect all clients."""
|
|
298
|
-
global _global_sse_manager
|
|
299
|
-
|
|
300
|
-
if _global_sse_manager is not None:
|
|
301
|
-
await _global_sse_manager.disconnect_all()
|
|
302
|
-
_global_sse_manager = None
|
|
303
|
-
logger.debug("sse_manager_cleaned_up")
|