ccproxy-api 0.1.6__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 +439 -212
- ccproxy/api/bootstrap.py +30 -0
- ccproxy/api/decorators.py +85 -0
- ccproxy/api/dependencies.py +145 -176
- ccproxy/api/format_validation.py +54 -0
- ccproxy/api/middleware/cors.py +6 -3
- ccproxy/api/middleware/errors.py +402 -530
- 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 +558 -0
- ccproxy/data/codex_headers_fallback.json +121 -0
- 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 +63 -107
- ccproxy/scheduler/registry.py +6 -32
- ccproxy/scheduler/tasks.py +346 -314
- 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 +95 -342
- ccproxy/utils/version_checker.py +279 -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.6.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 -1231
- 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 -269
- ccproxy/services/codex_detection_service.py +0 -263
- 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.6.dist-info/METADATA +0 -615
- ccproxy_api-0.1.6.dist-info/RECORD +0 -189
- ccproxy_api-0.1.6.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.6.dist-info → ccproxy_api-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
SQLModel schema definitions for observability storage.
|
|
3
|
-
|
|
4
|
-
This module provides the centralized schema definitions for access logs and metrics
|
|
5
|
-
using SQLModel to ensure type safety and eliminate column name repetition.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from datetime import datetime
|
|
9
|
-
|
|
10
|
-
from sqlmodel import Field, SQLModel
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class AccessLog(SQLModel, table=True):
|
|
14
|
-
"""Access log model for storing request/response data."""
|
|
15
|
-
|
|
16
|
-
__tablename__ = "access_logs"
|
|
17
|
-
|
|
18
|
-
# Core request identification
|
|
19
|
-
request_id: str = Field(primary_key=True)
|
|
20
|
-
timestamp: datetime = Field(default_factory=datetime.now, index=True)
|
|
21
|
-
|
|
22
|
-
# Request details
|
|
23
|
-
method: str
|
|
24
|
-
endpoint: str
|
|
25
|
-
path: str
|
|
26
|
-
query: str = Field(default="")
|
|
27
|
-
client_ip: str
|
|
28
|
-
user_agent: str
|
|
29
|
-
|
|
30
|
-
# Service and model info
|
|
31
|
-
service_type: str
|
|
32
|
-
model: str
|
|
33
|
-
streaming: bool = Field(default=False)
|
|
34
|
-
|
|
35
|
-
# Response details
|
|
36
|
-
status_code: int
|
|
37
|
-
duration_ms: float
|
|
38
|
-
duration_seconds: float
|
|
39
|
-
|
|
40
|
-
# Token and cost tracking
|
|
41
|
-
tokens_input: int = Field(default=0)
|
|
42
|
-
tokens_output: int = Field(default=0)
|
|
43
|
-
cache_read_tokens: int = Field(default=0)
|
|
44
|
-
cache_write_tokens: int = Field(default=0)
|
|
45
|
-
cost_usd: float = Field(default=0.0)
|
|
46
|
-
cost_sdk_usd: float = Field(default=0.0)
|
|
47
|
-
num_turns: int = Field(default=0) # number of conversation turns
|
|
48
|
-
|
|
49
|
-
# Session context metadata
|
|
50
|
-
session_type: str = Field(default="") # "session_pool" or "direct"
|
|
51
|
-
session_status: str = Field(default="") # active, idle, connecting, etc.
|
|
52
|
-
session_age_seconds: float = Field(default=0.0) # how long session has been alive
|
|
53
|
-
session_message_count: int = Field(default=0) # number of messages in session
|
|
54
|
-
session_client_id: str = Field(default="") # unique session client identifier
|
|
55
|
-
session_pool_enabled: bool = Field(
|
|
56
|
-
default=False
|
|
57
|
-
) # whether session pooling is enabled
|
|
58
|
-
session_idle_seconds: float = Field(default=0.0) # how long since last activity
|
|
59
|
-
session_error_count: int = Field(default=0) # number of errors in this session
|
|
60
|
-
session_is_new: bool = Field(
|
|
61
|
-
default=True
|
|
62
|
-
) # whether this is a newly created session
|
|
63
|
-
|
|
64
|
-
class Config:
|
|
65
|
-
"""SQLModel configuration."""
|
|
66
|
-
|
|
67
|
-
# Enable automatic conversion from dict
|
|
68
|
-
from_attributes = True
|
|
69
|
-
# Use enum values
|
|
70
|
-
use_enum_values = True
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
"""FastAPI StreamingResponse with automatic access logging on completion.
|
|
2
|
-
|
|
3
|
-
This module provides a reusable StreamingResponseWithLogging class that wraps
|
|
4
|
-
any async generator and handles access logging when the stream completes,
|
|
5
|
-
eliminating code duplication between different streaming endpoints.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
|
|
10
|
-
from collections.abc import AsyncGenerator, AsyncIterator
|
|
11
|
-
from typing import TYPE_CHECKING, Any
|
|
12
|
-
|
|
13
|
-
import structlog
|
|
14
|
-
from fastapi.responses import StreamingResponse
|
|
15
|
-
|
|
16
|
-
from ccproxy.observability.access_logger import log_request_access
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if TYPE_CHECKING:
|
|
20
|
-
from ccproxy.observability.context import RequestContext
|
|
21
|
-
from ccproxy.observability.metrics import PrometheusMetrics
|
|
22
|
-
|
|
23
|
-
logger = structlog.get_logger(__name__)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class StreamingResponseWithLogging(StreamingResponse):
|
|
27
|
-
"""FastAPI StreamingResponse that triggers access logging on completion.
|
|
28
|
-
|
|
29
|
-
This class wraps a streaming response generator to automatically trigger
|
|
30
|
-
access logging when the stream completes (either successfully or with an error).
|
|
31
|
-
This eliminates the need for manual access logging in individual stream processors.
|
|
32
|
-
"""
|
|
33
|
-
|
|
34
|
-
def __init__(
|
|
35
|
-
self,
|
|
36
|
-
content: AsyncGenerator[bytes, None] | AsyncIterator[bytes],
|
|
37
|
-
request_context: RequestContext,
|
|
38
|
-
metrics: PrometheusMetrics | None = None,
|
|
39
|
-
status_code: int = 200,
|
|
40
|
-
**kwargs: Any,
|
|
41
|
-
) -> None:
|
|
42
|
-
"""Initialize streaming response with logging capability.
|
|
43
|
-
|
|
44
|
-
Args:
|
|
45
|
-
content: The async generator producing streaming content
|
|
46
|
-
request_context: The request context for access logging
|
|
47
|
-
metrics: Optional PrometheusMetrics instance for recording metrics
|
|
48
|
-
status_code: HTTP status code for the response
|
|
49
|
-
**kwargs: Additional arguments passed to StreamingResponse
|
|
50
|
-
"""
|
|
51
|
-
# Wrap the content generator to add logging
|
|
52
|
-
logged_content = self._wrap_with_logging(
|
|
53
|
-
content, request_context, metrics, status_code
|
|
54
|
-
)
|
|
55
|
-
super().__init__(logged_content, status_code=status_code, **kwargs)
|
|
56
|
-
|
|
57
|
-
async def _wrap_with_logging(
|
|
58
|
-
self,
|
|
59
|
-
content: AsyncGenerator[bytes, None] | AsyncIterator[bytes],
|
|
60
|
-
context: RequestContext,
|
|
61
|
-
metrics: PrometheusMetrics | None,
|
|
62
|
-
status_code: int,
|
|
63
|
-
) -> AsyncGenerator[bytes, None]:
|
|
64
|
-
"""Wrap content generator with access logging on completion.
|
|
65
|
-
|
|
66
|
-
Args:
|
|
67
|
-
content: The original content generator
|
|
68
|
-
context: Request context for logging
|
|
69
|
-
metrics: Optional metrics instance
|
|
70
|
-
status_code: HTTP status code
|
|
71
|
-
|
|
72
|
-
Yields:
|
|
73
|
-
bytes: Content chunks from the original generator
|
|
74
|
-
"""
|
|
75
|
-
try:
|
|
76
|
-
# Stream all content from the original generator
|
|
77
|
-
async for chunk in content:
|
|
78
|
-
yield chunk
|
|
79
|
-
except GeneratorExit:
|
|
80
|
-
# Client disconnected - log this and re-raise to propagate to underlying generators
|
|
81
|
-
logger.info(
|
|
82
|
-
"streaming_response_client_disconnected",
|
|
83
|
-
request_id=context.request_id,
|
|
84
|
-
message="Client disconnected from streaming response, propagating GeneratorExit",
|
|
85
|
-
)
|
|
86
|
-
# CRITICAL: Re-raise GeneratorExit to propagate disconnect to create_listener()
|
|
87
|
-
raise
|
|
88
|
-
finally:
|
|
89
|
-
# Log access when stream completes (success or error)
|
|
90
|
-
try:
|
|
91
|
-
# Add streaming completion event type to context
|
|
92
|
-
context.add_metadata(event_type="streaming_complete")
|
|
93
|
-
|
|
94
|
-
# Check if status_code was updated in context metadata (e.g., due to error)
|
|
95
|
-
final_status_code = context.metadata.get("status_code", status_code)
|
|
96
|
-
|
|
97
|
-
await log_request_access(
|
|
98
|
-
context=context,
|
|
99
|
-
status_code=final_status_code,
|
|
100
|
-
metrics=metrics,
|
|
101
|
-
)
|
|
102
|
-
except Exception as e:
|
|
103
|
-
logger.warning(
|
|
104
|
-
"streaming_access_log_failed",
|
|
105
|
-
error=str(e),
|
|
106
|
-
request_id=context.request_id,
|
|
107
|
-
)
|
ccproxy/pricing/__init__.py
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"""Dynamic pricing system for Claude models.
|
|
2
|
-
|
|
3
|
-
This module provides dynamic pricing capabilities by downloading and caching
|
|
4
|
-
pricing information from external sources like LiteLLM.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from .cache import PricingCache
|
|
8
|
-
from .loader import PricingLoader
|
|
9
|
-
from .models import ModelPricing, PricingData
|
|
10
|
-
from .updater import PricingUpdater
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
__all__ = [
|
|
14
|
-
"PricingCache",
|
|
15
|
-
"PricingLoader",
|
|
16
|
-
"PricingUpdater",
|
|
17
|
-
"ModelPricing",
|
|
18
|
-
"PricingData",
|
|
19
|
-
]
|
ccproxy/pricing/loader.py
DELETED
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
"""Pricing data loader and format converter for LiteLLM pricing data."""
|
|
2
|
-
|
|
3
|
-
from decimal import Decimal
|
|
4
|
-
from typing import Any
|
|
5
|
-
|
|
6
|
-
from pydantic import ValidationError
|
|
7
|
-
from structlog import get_logger
|
|
8
|
-
|
|
9
|
-
from ccproxy.utils.model_mapping import get_claude_aliases_mapping, map_model_to_claude
|
|
10
|
-
|
|
11
|
-
from .models import PricingData
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
logger = get_logger(__name__)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class PricingLoader:
|
|
18
|
-
"""Loads and converts pricing data from LiteLLM format to internal format."""
|
|
19
|
-
|
|
20
|
-
@staticmethod
|
|
21
|
-
def extract_claude_models(
|
|
22
|
-
litellm_data: dict[str, Any], verbose: bool = True
|
|
23
|
-
) -> dict[str, Any]:
|
|
24
|
-
"""Extract Claude model entries from LiteLLM data.
|
|
25
|
-
|
|
26
|
-
Args:
|
|
27
|
-
litellm_data: Raw LiteLLM pricing data
|
|
28
|
-
verbose: Whether to log individual model discoveries
|
|
29
|
-
|
|
30
|
-
Returns:
|
|
31
|
-
Dictionary with only Claude models
|
|
32
|
-
"""
|
|
33
|
-
claude_models = {}
|
|
34
|
-
|
|
35
|
-
for model_name, model_data in litellm_data.items():
|
|
36
|
-
# Check if this is a Claude model
|
|
37
|
-
if (
|
|
38
|
-
isinstance(model_data, dict)
|
|
39
|
-
and model_data.get("litellm_provider") == "anthropic"
|
|
40
|
-
and "claude" in model_name.lower()
|
|
41
|
-
):
|
|
42
|
-
claude_models[model_name] = model_data
|
|
43
|
-
if verbose:
|
|
44
|
-
logger.debug("claude_model_found", model_name=model_name)
|
|
45
|
-
|
|
46
|
-
if verbose:
|
|
47
|
-
logger.info(
|
|
48
|
-
"claude_models_extracted",
|
|
49
|
-
model_count=len(claude_models),
|
|
50
|
-
source="LiteLLM",
|
|
51
|
-
)
|
|
52
|
-
return claude_models
|
|
53
|
-
|
|
54
|
-
@staticmethod
|
|
55
|
-
def convert_to_internal_format(
|
|
56
|
-
claude_models: dict[str, Any], verbose: bool = True
|
|
57
|
-
) -> dict[str, dict[str, Decimal]]:
|
|
58
|
-
"""Convert LiteLLM pricing format to internal format.
|
|
59
|
-
|
|
60
|
-
LiteLLM format uses cost per token, we use cost per 1M tokens as Decimal.
|
|
61
|
-
|
|
62
|
-
Args:
|
|
63
|
-
claude_models: Claude models in LiteLLM format
|
|
64
|
-
verbose: Whether to log individual model conversions
|
|
65
|
-
|
|
66
|
-
Returns:
|
|
67
|
-
Dictionary in internal pricing format
|
|
68
|
-
"""
|
|
69
|
-
internal_format = {}
|
|
70
|
-
|
|
71
|
-
for model_name, model_data in claude_models.items():
|
|
72
|
-
try:
|
|
73
|
-
# Extract pricing fields
|
|
74
|
-
input_cost_per_token = model_data.get("input_cost_per_token")
|
|
75
|
-
output_cost_per_token = model_data.get("output_cost_per_token")
|
|
76
|
-
cache_creation_cost = model_data.get("cache_creation_input_token_cost")
|
|
77
|
-
cache_read_cost = model_data.get("cache_read_input_token_cost")
|
|
78
|
-
|
|
79
|
-
# Skip models without pricing info
|
|
80
|
-
if input_cost_per_token is None or output_cost_per_token is None:
|
|
81
|
-
if verbose:
|
|
82
|
-
logger.warning("model_pricing_missing", model_name=model_name)
|
|
83
|
-
continue
|
|
84
|
-
|
|
85
|
-
# Convert to per-1M-token pricing (multiply by 1,000,000)
|
|
86
|
-
pricing = {
|
|
87
|
-
"input": Decimal(str(input_cost_per_token * 1_000_000)),
|
|
88
|
-
"output": Decimal(str(output_cost_per_token * 1_000_000)),
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
# Add cache pricing if available
|
|
92
|
-
if cache_creation_cost is not None:
|
|
93
|
-
pricing["cache_write"] = Decimal(
|
|
94
|
-
str(cache_creation_cost * 1_000_000)
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
if cache_read_cost is not None:
|
|
98
|
-
pricing["cache_read"] = Decimal(str(cache_read_cost * 1_000_000))
|
|
99
|
-
|
|
100
|
-
# Map to canonical model name if needed
|
|
101
|
-
canonical_name = map_model_to_claude(model_name)
|
|
102
|
-
internal_format[canonical_name] = pricing
|
|
103
|
-
|
|
104
|
-
if verbose:
|
|
105
|
-
logger.debug(
|
|
106
|
-
"model_pricing_converted",
|
|
107
|
-
original_name=model_name,
|
|
108
|
-
canonical_name=canonical_name,
|
|
109
|
-
input_cost=str(pricing["input"]),
|
|
110
|
-
output_cost=str(pricing["output"]),
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
except (ValueError, TypeError) as e:
|
|
114
|
-
if verbose:
|
|
115
|
-
logger.error(
|
|
116
|
-
"pricing_conversion_failed", model_name=model_name, error=str(e)
|
|
117
|
-
)
|
|
118
|
-
continue
|
|
119
|
-
|
|
120
|
-
if verbose:
|
|
121
|
-
logger.info("models_converted", model_count=len(internal_format))
|
|
122
|
-
return internal_format
|
|
123
|
-
|
|
124
|
-
@staticmethod
|
|
125
|
-
def load_pricing_from_data(
|
|
126
|
-
litellm_data: dict[str, Any], verbose: bool = True
|
|
127
|
-
) -> PricingData | None:
|
|
128
|
-
"""Load and convert pricing data from LiteLLM format.
|
|
129
|
-
|
|
130
|
-
Args:
|
|
131
|
-
litellm_data: Raw LiteLLM pricing data
|
|
132
|
-
verbose: Whether to enable verbose logging
|
|
133
|
-
|
|
134
|
-
Returns:
|
|
135
|
-
Validated pricing data as PricingData model, or None if invalid
|
|
136
|
-
"""
|
|
137
|
-
try:
|
|
138
|
-
# Extract Claude models
|
|
139
|
-
claude_models = PricingLoader.extract_claude_models(
|
|
140
|
-
litellm_data, verbose=verbose
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
if not claude_models:
|
|
144
|
-
if verbose:
|
|
145
|
-
logger.warning("claude_models_not_found", source="LiteLLM")
|
|
146
|
-
return None
|
|
147
|
-
|
|
148
|
-
# Convert to internal format
|
|
149
|
-
internal_pricing = PricingLoader.convert_to_internal_format(
|
|
150
|
-
claude_models, verbose=verbose
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
if not internal_pricing:
|
|
154
|
-
if verbose:
|
|
155
|
-
logger.warning("pricing_data_invalid")
|
|
156
|
-
return None
|
|
157
|
-
|
|
158
|
-
# Validate and create PricingData model
|
|
159
|
-
pricing_data = PricingData.from_dict(internal_pricing)
|
|
160
|
-
|
|
161
|
-
if verbose:
|
|
162
|
-
logger.info("pricing_data_loaded", model_count=len(pricing_data))
|
|
163
|
-
|
|
164
|
-
return pricing_data
|
|
165
|
-
|
|
166
|
-
except ValidationError as e:
|
|
167
|
-
if verbose:
|
|
168
|
-
logger.error("pricing_validation_failed", error=str(e))
|
|
169
|
-
return None
|
|
170
|
-
except Exception as e:
|
|
171
|
-
if verbose:
|
|
172
|
-
logger.error("pricing_load_failed", source="LiteLLM", error=str(e))
|
|
173
|
-
return None
|
|
174
|
-
|
|
175
|
-
@staticmethod
|
|
176
|
-
def validate_pricing_data(
|
|
177
|
-
pricing_data: Any, verbose: bool = True
|
|
178
|
-
) -> PricingData | None:
|
|
179
|
-
"""Validate pricing data using Pydantic models.
|
|
180
|
-
|
|
181
|
-
Args:
|
|
182
|
-
pricing_data: Pricing data to validate (dict or PricingData)
|
|
183
|
-
verbose: Whether to enable verbose logging
|
|
184
|
-
|
|
185
|
-
Returns:
|
|
186
|
-
Valid PricingData model or None if validation fails
|
|
187
|
-
"""
|
|
188
|
-
try:
|
|
189
|
-
# If already a PricingData instance, return it
|
|
190
|
-
if isinstance(pricing_data, PricingData):
|
|
191
|
-
if verbose:
|
|
192
|
-
logger.debug(
|
|
193
|
-
"pricing_already_validated", model_count=len(pricing_data)
|
|
194
|
-
)
|
|
195
|
-
return pricing_data
|
|
196
|
-
|
|
197
|
-
# If it's a dict, try to create PricingData from it
|
|
198
|
-
if isinstance(pricing_data, dict):
|
|
199
|
-
if not pricing_data:
|
|
200
|
-
if verbose:
|
|
201
|
-
logger.warning("pricing_data_empty")
|
|
202
|
-
return None
|
|
203
|
-
|
|
204
|
-
# Try to create PricingData model
|
|
205
|
-
validated_data = PricingData.from_dict(pricing_data)
|
|
206
|
-
|
|
207
|
-
if verbose:
|
|
208
|
-
logger.debug(
|
|
209
|
-
"pricing_data_validated", model_count=len(validated_data)
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
return validated_data
|
|
213
|
-
|
|
214
|
-
# Invalid type
|
|
215
|
-
if verbose:
|
|
216
|
-
logger.error(
|
|
217
|
-
"pricing_data_invalid_type",
|
|
218
|
-
actual_type=type(pricing_data).__name__,
|
|
219
|
-
expected_types=["dict", "PricingData"],
|
|
220
|
-
)
|
|
221
|
-
return None
|
|
222
|
-
|
|
223
|
-
except ValidationError as e:
|
|
224
|
-
if verbose:
|
|
225
|
-
logger.error("pricing_validation_failed", error=str(e))
|
|
226
|
-
return None
|
|
227
|
-
except Exception as e:
|
|
228
|
-
if verbose:
|
|
229
|
-
logger.error("pricing_validation_unexpected_error", error=str(e))
|
|
230
|
-
return None
|
|
231
|
-
|
|
232
|
-
@staticmethod
|
|
233
|
-
def get_model_aliases() -> dict[str, str]:
|
|
234
|
-
"""Get mapping of model aliases to canonical names.
|
|
235
|
-
|
|
236
|
-
Returns:
|
|
237
|
-
Dictionary mapping aliases to canonical model names
|
|
238
|
-
"""
|
|
239
|
-
return get_claude_aliases_mapping()
|
|
240
|
-
|
|
241
|
-
@staticmethod
|
|
242
|
-
def get_canonical_model_name(model_name: str) -> str:
|
|
243
|
-
"""Get canonical model name for a given model name.
|
|
244
|
-
|
|
245
|
-
Args:
|
|
246
|
-
model_name: Model name (possibly an alias)
|
|
247
|
-
|
|
248
|
-
Returns:
|
|
249
|
-
Canonical model name
|
|
250
|
-
"""
|
|
251
|
-
return map_model_to_claude(model_name)
|