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
ccproxy/models/__init__.py
CHANGED
|
@@ -1,164 +1,13 @@
|
|
|
1
|
-
"""Pydantic models for Claude Proxy API Server.
|
|
1
|
+
"""Pydantic models for Claude Proxy API Server.
|
|
2
2
|
|
|
3
|
-
from .
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
ResultMessageBlock,
|
|
9
|
-
SDKContentBlock,
|
|
10
|
-
SDKMessageMode,
|
|
11
|
-
TextBlock,
|
|
12
|
-
ToolResultBlock,
|
|
13
|
-
ToolResultSDKBlock,
|
|
14
|
-
ToolUseBlock,
|
|
15
|
-
ToolUseSDKBlock,
|
|
16
|
-
UserMessage,
|
|
17
|
-
convert_sdk_result_message,
|
|
18
|
-
convert_sdk_system_message,
|
|
19
|
-
convert_sdk_text_block,
|
|
20
|
-
convert_sdk_tool_result_block,
|
|
21
|
-
convert_sdk_tool_use_block,
|
|
22
|
-
to_sdk_variant,
|
|
23
|
-
)
|
|
24
|
-
from .messages import (
|
|
25
|
-
MessageContentBlock,
|
|
26
|
-
MessageCreateParams,
|
|
27
|
-
MessageResponse,
|
|
28
|
-
MetadataParams,
|
|
29
|
-
SystemMessage,
|
|
30
|
-
ThinkingConfig,
|
|
31
|
-
ToolChoiceParams,
|
|
32
|
-
)
|
|
33
|
-
from .requests import (
|
|
34
|
-
ImageContent,
|
|
35
|
-
Message,
|
|
36
|
-
MessageContent,
|
|
37
|
-
TextContent,
|
|
38
|
-
ToolDefinition,
|
|
39
|
-
Usage,
|
|
40
|
-
)
|
|
41
|
-
from .responses import (
|
|
42
|
-
APIError,
|
|
43
|
-
AuthenticationError,
|
|
44
|
-
ChatCompletionResponse,
|
|
45
|
-
Choice,
|
|
46
|
-
ErrorResponse,
|
|
47
|
-
InternalServerError,
|
|
48
|
-
InvalidRequestError,
|
|
49
|
-
NotFoundError,
|
|
50
|
-
OverloadedError,
|
|
51
|
-
RateLimitError,
|
|
52
|
-
ResponseContent,
|
|
53
|
-
StreamingChatCompletionResponse,
|
|
54
|
-
StreamingChoice,
|
|
55
|
-
TextResponse,
|
|
56
|
-
ToolCall,
|
|
57
|
-
ToolUse,
|
|
58
|
-
)
|
|
59
|
-
from .types import (
|
|
60
|
-
ContentBlockType,
|
|
61
|
-
ErrorType,
|
|
62
|
-
ImageSourceType,
|
|
63
|
-
MessageRole,
|
|
64
|
-
ModalityType,
|
|
65
|
-
OpenAIFinishReason,
|
|
66
|
-
PermissionBehavior,
|
|
67
|
-
ResponseFormatType,
|
|
68
|
-
ServiceTier,
|
|
69
|
-
StopReason,
|
|
70
|
-
StreamEventType,
|
|
71
|
-
ToolChoiceType,
|
|
72
|
-
ToolType,
|
|
73
|
-
)
|
|
3
|
+
This package now re-exports Anthropic models from ccproxy.llms.models.anthropic
|
|
4
|
+
for backward compatibility, while keeping provider-agnostic models here.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .provider import ProviderConfig
|
|
74
8
|
|
|
75
9
|
|
|
76
10
|
__all__ = [
|
|
77
|
-
#
|
|
78
|
-
"
|
|
79
|
-
"ErrorType",
|
|
80
|
-
"ImageSourceType",
|
|
81
|
-
"MessageRole",
|
|
82
|
-
"ModalityType",
|
|
83
|
-
"OpenAIFinishReason",
|
|
84
|
-
"PermissionBehavior",
|
|
85
|
-
"ResponseFormatType",
|
|
86
|
-
"ServiceTier",
|
|
87
|
-
"StopReason",
|
|
88
|
-
"StreamEventType",
|
|
89
|
-
"ToolChoiceType",
|
|
90
|
-
"ToolType",
|
|
91
|
-
# Claude SDK models
|
|
92
|
-
"AssistantMessage",
|
|
93
|
-
"ContentBlock",
|
|
94
|
-
"ExtendedContentBlock",
|
|
95
|
-
"ResultMessage",
|
|
96
|
-
"ResultMessageBlock",
|
|
97
|
-
"SDKContentBlock",
|
|
98
|
-
"SDKMessageMode",
|
|
99
|
-
"TextBlock",
|
|
100
|
-
"ToolResultBlock",
|
|
101
|
-
"ToolResultSDKBlock",
|
|
102
|
-
"ToolUseBlock",
|
|
103
|
-
"ToolUseSDKBlock",
|
|
104
|
-
"UserMessage",
|
|
105
|
-
"convert_sdk_result_message",
|
|
106
|
-
"convert_sdk_system_message",
|
|
107
|
-
"convert_sdk_text_block",
|
|
108
|
-
"convert_sdk_tool_result_block",
|
|
109
|
-
"convert_sdk_tool_use_block",
|
|
110
|
-
"to_sdk_variant",
|
|
111
|
-
# Message models
|
|
112
|
-
"MessageContentBlock",
|
|
113
|
-
"MessageCreateParams",
|
|
114
|
-
"MessageResponse",
|
|
115
|
-
"MetadataParams",
|
|
116
|
-
"SystemMessage",
|
|
117
|
-
"ThinkingConfig",
|
|
118
|
-
"ToolChoiceParams",
|
|
119
|
-
# Request models
|
|
120
|
-
"ImageContent",
|
|
121
|
-
"Message",
|
|
122
|
-
"MessageContent",
|
|
123
|
-
"TextContent",
|
|
124
|
-
"ToolDefinition",
|
|
125
|
-
"Usage",
|
|
126
|
-
# Response models
|
|
127
|
-
"APIError",
|
|
128
|
-
"AuthenticationError",
|
|
129
|
-
"ChatCompletionResponse",
|
|
130
|
-
"Choice",
|
|
131
|
-
"ErrorResponse",
|
|
132
|
-
"InternalServerError",
|
|
133
|
-
"InvalidRequestError",
|
|
134
|
-
"NotFoundError",
|
|
135
|
-
"OverloadedError",
|
|
136
|
-
"RateLimitError",
|
|
137
|
-
"ResponseContent",
|
|
138
|
-
"StreamingChatCompletionResponse",
|
|
139
|
-
"StreamingChoice",
|
|
140
|
-
"TextResponse",
|
|
141
|
-
"ToolCall",
|
|
142
|
-
"ToolUse",
|
|
143
|
-
# OpenAI-compatible models
|
|
144
|
-
"OpenAIChatCompletionRequest",
|
|
145
|
-
"OpenAIChatCompletionResponse",
|
|
146
|
-
"OpenAIChoice",
|
|
147
|
-
"OpenAIErrorDetail",
|
|
148
|
-
"OpenAIErrorResponse",
|
|
149
|
-
"OpenAIFunction",
|
|
150
|
-
"OpenAILogprobs",
|
|
151
|
-
"OpenAIMessage",
|
|
152
|
-
"OpenAIMessageContent",
|
|
153
|
-
"OpenAIModelInfo",
|
|
154
|
-
"OpenAIModelsResponse",
|
|
155
|
-
"OpenAIResponseFormat",
|
|
156
|
-
"OpenAIResponseMessage",
|
|
157
|
-
"OpenAIStreamingChatCompletionResponse",
|
|
158
|
-
"OpenAIStreamingChoice",
|
|
159
|
-
"OpenAIStreamOptions",
|
|
160
|
-
"OpenAITool",
|
|
161
|
-
"OpenAIToolCall",
|
|
162
|
-
"OpenAIToolChoice",
|
|
163
|
-
"OpenAIUsage",
|
|
11
|
+
# Provider models
|
|
12
|
+
"ProviderConfig",
|
|
164
13
|
]
|
ccproxy/models/detection.py
CHANGED
|
@@ -1,208 +1,107 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Shared helper dataclasses for plugin detection caches."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from
|
|
6
|
-
from
|
|
7
|
-
|
|
8
|
-
from
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
alias="x-stainless-timeout", description="Request timeout", default="60"
|
|
45
|
-
)
|
|
46
|
-
x_stainless_package_version: str = Field(
|
|
47
|
-
alias="x-stainless-package-version",
|
|
48
|
-
description="Package version",
|
|
49
|
-
default="0.55.1",
|
|
50
|
-
)
|
|
51
|
-
x_stainless_os: str = Field(
|
|
52
|
-
alias="x-stainless-os", description="Operating system", default="Linux"
|
|
53
|
-
)
|
|
54
|
-
x_stainless_arch: str = Field(
|
|
55
|
-
alias="x-stainless-arch", description="Architecture", default="x64"
|
|
56
|
-
)
|
|
57
|
-
x_stainless_runtime: str = Field(
|
|
58
|
-
alias="x-stainless-runtime", description="Runtime", default="node"
|
|
59
|
-
)
|
|
60
|
-
x_stainless_runtime_version: str = Field(
|
|
61
|
-
alias="x-stainless-runtime-version",
|
|
62
|
-
description="Runtime version",
|
|
63
|
-
default="v24.3.0",
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
model_config = ConfigDict(extra="ignore", populate_by_name=True)
|
|
67
|
-
|
|
68
|
-
def to_headers_dict(self) -> dict[str, str]:
|
|
69
|
-
"""Convert to headers dictionary for HTTP forwarding with proper case."""
|
|
70
|
-
headers = {}
|
|
71
|
-
|
|
72
|
-
# Map field names to proper HTTP header names
|
|
73
|
-
header_mapping = {
|
|
74
|
-
"anthropic_beta": "anthropic-beta",
|
|
75
|
-
"anthropic_version": "anthropic-version",
|
|
76
|
-
"anthropic_dangerous_direct_browser_access": "anthropic-dangerous-direct-browser-access",
|
|
77
|
-
"x_app": "x-app",
|
|
78
|
-
"user_agent": "User-Agent",
|
|
79
|
-
"x_stainless_lang": "X-Stainless-Lang",
|
|
80
|
-
"x_stainless_retry_count": "X-Stainless-Retry-Count",
|
|
81
|
-
"x_stainless_timeout": "X-Stainless-Timeout",
|
|
82
|
-
"x_stainless_package_version": "X-Stainless-Package-Version",
|
|
83
|
-
"x_stainless_os": "X-Stainless-OS",
|
|
84
|
-
"x_stainless_arch": "X-Stainless-Arch",
|
|
85
|
-
"x_stainless_runtime": "X-Stainless-Runtime",
|
|
86
|
-
"x_stainless_runtime_version": "X-Stainless-Runtime-Version",
|
|
5
|
+
from collections.abc import ItemsView, Iterable
|
|
6
|
+
from copy import deepcopy
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(slots=True)
|
|
12
|
+
class DetectedHeaders:
|
|
13
|
+
"""Normalized, lowercase HTTP headers captured during CLI detection."""
|
|
14
|
+
|
|
15
|
+
values: dict[str, str] = field(default_factory=dict)
|
|
16
|
+
|
|
17
|
+
def __post_init__(self) -> None:
|
|
18
|
+
normalized: dict[str, str] = {}
|
|
19
|
+
for key, raw_value in (self.values or {}).items():
|
|
20
|
+
if key is None:
|
|
21
|
+
continue
|
|
22
|
+
normalized_key = str(key).lower()
|
|
23
|
+
normalized[normalized_key] = "" if raw_value is None else str(raw_value)
|
|
24
|
+
self.values = normalized
|
|
25
|
+
|
|
26
|
+
def as_dict(self) -> dict[str, str]:
|
|
27
|
+
"""Return a copy of the detected headers as a plain dict."""
|
|
28
|
+
|
|
29
|
+
return dict(self.values)
|
|
30
|
+
|
|
31
|
+
def filtered(
|
|
32
|
+
self,
|
|
33
|
+
ignores: Iterable[str] | None = None,
|
|
34
|
+
redacted: Iterable[str] | None = None,
|
|
35
|
+
) -> dict[str, str]:
|
|
36
|
+
"""Return headers filtered for safe forwarding."""
|
|
37
|
+
|
|
38
|
+
ignore_set = {item.lower() for item in ignores or ()}
|
|
39
|
+
redacted_set = {item.lower() for item in redacted or ()}
|
|
40
|
+
return {
|
|
41
|
+
key: value
|
|
42
|
+
for key, value in self.values.items()
|
|
43
|
+
if value and key not in ignore_set and key not in redacted_set
|
|
87
44
|
}
|
|
88
45
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
),
|
|
124
|
-
] = None # type: ignore # Pydantic handles this via default_factory
|
|
125
|
-
|
|
126
|
-
model_config = ConfigDict(extra="forbid")
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
class CodexHeaders(BaseModel):
|
|
130
|
-
"""Pydantic model for Codex CLI headers extraction with field aliases."""
|
|
131
|
-
|
|
132
|
-
session_id: str = Field(
|
|
133
|
-
alias="session_id",
|
|
134
|
-
description="Codex session identifier",
|
|
135
|
-
default="",
|
|
136
|
-
)
|
|
137
|
-
originator: str = Field(
|
|
138
|
-
description="Codex originator identifier",
|
|
139
|
-
default="codex_cli_rs",
|
|
140
|
-
)
|
|
141
|
-
openai_beta: str = Field(
|
|
142
|
-
alias="openai-beta",
|
|
143
|
-
description="OpenAI beta features",
|
|
144
|
-
default="responses=experimental",
|
|
145
|
-
)
|
|
146
|
-
version: str = Field(
|
|
147
|
-
description="Codex CLI version",
|
|
148
|
-
default="0.21.0",
|
|
149
|
-
)
|
|
150
|
-
chatgpt_account_id: str = Field(
|
|
151
|
-
alias="chatgpt-account-id",
|
|
152
|
-
description="ChatGPT account identifier",
|
|
153
|
-
default="",
|
|
154
|
-
)
|
|
155
|
-
|
|
156
|
-
model_config = ConfigDict(extra="ignore", populate_by_name=True)
|
|
157
|
-
|
|
158
|
-
def to_headers_dict(self) -> dict[str, str]:
|
|
159
|
-
"""Convert to headers dictionary for HTTP forwarding with proper case."""
|
|
160
|
-
headers = {}
|
|
161
|
-
|
|
162
|
-
# Map field names to proper HTTP header names
|
|
163
|
-
header_mapping = {
|
|
164
|
-
"session_id": "session_id",
|
|
165
|
-
"originator": "originator",
|
|
166
|
-
"openai_beta": "openai-beta",
|
|
167
|
-
"version": "version",
|
|
168
|
-
"chatgpt_account_id": "chatgpt-account-id",
|
|
169
|
-
}
|
|
46
|
+
def get(self, key: str, default: str | None = None) -> str | None:
|
|
47
|
+
"""Lookup a header by key (case-insensitive)."""
|
|
48
|
+
|
|
49
|
+
return self.values.get(key.lower(), default)
|
|
50
|
+
|
|
51
|
+
def items(self) -> ItemsView[str, str]:
|
|
52
|
+
"""Iterate over header key/value pairs."""
|
|
53
|
+
|
|
54
|
+
return self.values.items()
|
|
55
|
+
|
|
56
|
+
def __bool__(self) -> bool:
|
|
57
|
+
return bool(self.values)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass(slots=True)
|
|
61
|
+
class DetectedPrompts:
|
|
62
|
+
"""Structured prompt metadata extracted from CLI detection payloads."""
|
|
63
|
+
|
|
64
|
+
instructions: str | None = None
|
|
65
|
+
system: Any | None = None
|
|
66
|
+
raw: dict[str, Any] = field(default_factory=dict)
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
def from_body(cls, body: Any | None) -> DetectedPrompts:
|
|
70
|
+
"""Build a DetectedPrompts instance from a captured request body."""
|
|
71
|
+
|
|
72
|
+
if not isinstance(body, dict):
|
|
73
|
+
return cls(raw={} if body is None else {"__raw__": body})
|
|
74
|
+
|
|
75
|
+
body_copy = deepcopy(body)
|
|
76
|
+
|
|
77
|
+
instructions = body_copy.get("instructions")
|
|
78
|
+
if not isinstance(instructions, str) or not instructions.strip():
|
|
79
|
+
instructions = None
|
|
170
80
|
|
|
171
|
-
|
|
172
|
-
value = getattr(self, field_name, None)
|
|
173
|
-
if value is not None and value != "":
|
|
174
|
-
headers[header_name] = value
|
|
81
|
+
system_value = body_copy.get("system")
|
|
175
82
|
|
|
176
|
-
return
|
|
83
|
+
return cls(instructions=instructions, system=system_value, raw=body_copy)
|
|
177
84
|
|
|
85
|
+
def instructions_payload(self) -> dict[str, Any]:
|
|
86
|
+
"""Return a payload suitable for injecting Codex-style instructions."""
|
|
178
87
|
|
|
179
|
-
|
|
180
|
-
|
|
88
|
+
if self.instructions:
|
|
89
|
+
return {"instructions": self.instructions}
|
|
90
|
+
return {}
|
|
181
91
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
Field(
|
|
185
|
-
description="Complete instructions field as detected from Codex CLI, preserving exact text content"
|
|
186
|
-
),
|
|
187
|
-
]
|
|
92
|
+
def system_payload(self, mode: str = "full") -> dict[str, Any]:
|
|
93
|
+
"""Return anthropic-style system data respecting the requested mode."""
|
|
188
94
|
|
|
189
|
-
|
|
95
|
+
if self.system is None or mode == "none":
|
|
96
|
+
return {}
|
|
190
97
|
|
|
98
|
+
if mode == "minimal" and isinstance(self.system, list):
|
|
99
|
+
return {"system": self.system[:1]} if self.system else {}
|
|
191
100
|
|
|
192
|
-
|
|
193
|
-
"""Cached Codex CLI detection data with version tracking."""
|
|
101
|
+
return {"system": self.system}
|
|
194
102
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
instructions: Annotated[
|
|
198
|
-
CodexInstructionsData, Field(description="Extracted instructions")
|
|
199
|
-
]
|
|
200
|
-
cached_at: Annotated[
|
|
201
|
-
datetime,
|
|
202
|
-
Field(
|
|
203
|
-
description="Cache timestamp",
|
|
204
|
-
default_factory=lambda: datetime.now(UTC),
|
|
205
|
-
),
|
|
206
|
-
] = None # type: ignore # Pydantic handles this via default_factory
|
|
103
|
+
def has_system(self) -> bool:
|
|
104
|
+
return bool(self.system)
|
|
207
105
|
|
|
208
|
-
|
|
106
|
+
def has_instructions(self) -> bool:
|
|
107
|
+
return bool(self.instructions)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Provider configuration models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Literal
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ModelMappingRule(BaseModel):
|
|
11
|
+
"""Ordered mapping rule for translating client model identifiers."""
|
|
12
|
+
|
|
13
|
+
match: str = Field(..., description="Client-facing model identifier or pattern")
|
|
14
|
+
target: str = Field(..., description="Upstream model identifier to use")
|
|
15
|
+
kind: Literal["exact", "regex", "prefix", "suffix"] = Field(
|
|
16
|
+
default="exact",
|
|
17
|
+
description="Type of match to apply for this rule",
|
|
18
|
+
)
|
|
19
|
+
flags: list[Literal["IGNORECASE"]] = Field(
|
|
20
|
+
default_factory=list,
|
|
21
|
+
description="Optional regex flags to apply when kind=regex",
|
|
22
|
+
)
|
|
23
|
+
notes: str | None = Field(
|
|
24
|
+
default=None,
|
|
25
|
+
description="Optional human-readable description of this rule",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ModelCard(BaseModel):
|
|
30
|
+
"""Representation of a model entry returned by /models."""
|
|
31
|
+
|
|
32
|
+
id: str = Field(..., description="Unique model identifier exposed to clients")
|
|
33
|
+
object: str = Field(default="model", description="OpenAI-compatible object type")
|
|
34
|
+
created: int | None = Field(
|
|
35
|
+
default=None, description="Unix timestamp when the model became available"
|
|
36
|
+
)
|
|
37
|
+
owned_by: str | None = Field(
|
|
38
|
+
default=None, description="Provider or organization that owns the model"
|
|
39
|
+
)
|
|
40
|
+
permission: list[dict[str, Any]] | None = Field(
|
|
41
|
+
default=None,
|
|
42
|
+
description="Optional list of permission descriptors",
|
|
43
|
+
)
|
|
44
|
+
root: str | None = Field(
|
|
45
|
+
default=None, description="Root model identifier for fine-tuned variants"
|
|
46
|
+
)
|
|
47
|
+
parent: str | None = Field(
|
|
48
|
+
default=None, description="Parent model identifier, if any"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
model_config = ConfigDict(extra="allow")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ProviderConfig(BaseModel):
|
|
55
|
+
"""Configuration for a provider plugin."""
|
|
56
|
+
|
|
57
|
+
name: str = Field(..., description="Provider name")
|
|
58
|
+
base_url: str = Field(..., description="Base URL for the provider API")
|
|
59
|
+
supports_streaming: bool = Field(
|
|
60
|
+
default=False, description="Whether the provider supports streaming"
|
|
61
|
+
)
|
|
62
|
+
requires_auth: bool = Field(
|
|
63
|
+
default=True, description="Whether the provider requires authentication"
|
|
64
|
+
)
|
|
65
|
+
auth_type: str | None = Field(
|
|
66
|
+
default=None, description="Authentication type (bearer, api_key, etc.)"
|
|
67
|
+
)
|
|
68
|
+
model_mappings: list[ModelMappingRule] = Field(
|
|
69
|
+
default_factory=list,
|
|
70
|
+
description="Ordered list of client→upstream model mapping rules",
|
|
71
|
+
)
|
|
72
|
+
models_endpoint: list[ModelCard] = Field(
|
|
73
|
+
default_factory=list,
|
|
74
|
+
description="Model metadata returned by the provider's /models endpoint",
|
|
75
|
+
)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Access Log Plugin
|
|
2
|
+
|
|
3
|
+
Captures client and provider traffic and writes structured access logs.
|
|
4
|
+
|
|
5
|
+
## Highlights
|
|
6
|
+
- Hooks into request lifecycle events to log Common, Combined, or JSON formats
|
|
7
|
+
- Supports per-channel enablement and configurable log destinations
|
|
8
|
+
- Optionally forwards records to the analytics ingest service for persistence
|
|
9
|
+
|
|
10
|
+
## Configuration
|
|
11
|
+
- `AccessLogConfig` controls enable flags, formats, and file paths
|
|
12
|
+
- Generate defaults with `python3 scripts/generate_config_from_model.py \
|
|
13
|
+
--format toml --plugin access_log`
|
|
14
|
+
|
|
15
|
+
```toml
|
|
16
|
+
[plugins.access_log]
|
|
17
|
+
# enabled = true
|
|
18
|
+
# client_enabled = true
|
|
19
|
+
# client_format = "structured"
|
|
20
|
+
# client_log_file = "/tmp/ccproxy/access.log"
|
|
21
|
+
# provider_enabled = false
|
|
22
|
+
# provider_format = "structured"
|
|
23
|
+
# provider_log_file = "/tmp/ccproxy/provider_access.log"
|
|
24
|
+
# exclude_paths = ["/health", "/metrics", "/readyz", "/livez"]
|
|
25
|
+
# buffer_size = 100
|
|
26
|
+
# flush_interval = 1.0
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Related Components
|
|
30
|
+
- `plugin.py`: runtime lifecycle and hook registration
|
|
31
|
+
- `hook.py`: event handler that formats and writes log entries
|
|
32
|
+
- `formatter.py` / `writer.py`: formatting helpers and async file writer
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Access log plugin for CCProxy.
|
|
2
|
+
|
|
3
|
+
Provides structured access logging for both client and provider requests
|
|
4
|
+
using the hook system.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .config import AccessLogConfig
|
|
8
|
+
from .hook import AccessLogHook
|
|
9
|
+
from .plugin import AccessLogFactory, AccessLogRuntime
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"AccessLogConfig",
|
|
14
|
+
"AccessLogFactory",
|
|
15
|
+
"AccessLogHook",
|
|
16
|
+
"AccessLogRuntime",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
# Export the factory instance for plugin loading
|
|
20
|
+
factory = AccessLogFactory()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, ConfigDict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AccessLogConfig(BaseModel):
|
|
7
|
+
"""Configuration for access logging.
|
|
8
|
+
|
|
9
|
+
Supports logging at both client and provider levels with
|
|
10
|
+
different formats for each.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
# Global enable/disable
|
|
14
|
+
enabled: bool = True
|
|
15
|
+
|
|
16
|
+
# Client-level access logging
|
|
17
|
+
client_enabled: bool = True
|
|
18
|
+
client_format: Literal["combined", "common", "structured"] = "structured"
|
|
19
|
+
client_log_file: str = "/tmp/ccproxy/access.log"
|
|
20
|
+
|
|
21
|
+
# Provider-level access logging (optional)
|
|
22
|
+
provider_enabled: bool = False
|
|
23
|
+
provider_format: Literal["structured"] = "structured"
|
|
24
|
+
provider_log_file: str = "/tmp/ccproxy/provider_access.log"
|
|
25
|
+
|
|
26
|
+
# Path filters (only for client level)
|
|
27
|
+
exclude_paths: list[str] = ["/health", "/metrics", "/readyz", "/livez"]
|
|
28
|
+
|
|
29
|
+
# Performance options
|
|
30
|
+
buffer_size: int = 100 # Buffer this many log entries before writing
|
|
31
|
+
flush_interval: float = 1.0 # Flush buffer every N seconds
|
|
32
|
+
|
|
33
|
+
model_config = ConfigDict()
|