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/static/.keep
DELETED
|
File without changes
|
ccproxy/utils/cost_calculator.py
DELETED
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
"""Cost calculation utilities for token-based pricing.
|
|
2
|
-
|
|
3
|
-
This module provides shared cost calculation functionality that can be used
|
|
4
|
-
across different services to ensure consistent pricing calculations.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import structlog
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
logger = structlog.get_logger(__name__)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def calculate_token_cost(
|
|
14
|
-
tokens_input: int | None,
|
|
15
|
-
tokens_output: int | None,
|
|
16
|
-
model: str | None,
|
|
17
|
-
cache_read_tokens: int | None = None,
|
|
18
|
-
cache_write_tokens: int | None = None,
|
|
19
|
-
) -> float | None:
|
|
20
|
-
"""Calculate cost in USD for the given token usage including cache tokens.
|
|
21
|
-
|
|
22
|
-
This is a shared utility function that provides consistent cost calculation
|
|
23
|
-
across all services using the pricing data from the pricing system.
|
|
24
|
-
|
|
25
|
-
Args:
|
|
26
|
-
tokens_input: Number of input tokens
|
|
27
|
-
tokens_output: Number of output tokens
|
|
28
|
-
model: Model name for pricing lookup
|
|
29
|
-
cache_read_tokens: Number of cache read tokens
|
|
30
|
-
cache_write_tokens: Number of cache write tokens
|
|
31
|
-
|
|
32
|
-
Returns:
|
|
33
|
-
Cost in USD or None if calculation not possible
|
|
34
|
-
"""
|
|
35
|
-
if not model or (
|
|
36
|
-
not tokens_input
|
|
37
|
-
and not tokens_output
|
|
38
|
-
and not cache_read_tokens
|
|
39
|
-
and not cache_write_tokens
|
|
40
|
-
):
|
|
41
|
-
return None
|
|
42
|
-
|
|
43
|
-
try:
|
|
44
|
-
# Import pricing system components
|
|
45
|
-
from ccproxy.config.pricing import PricingSettings
|
|
46
|
-
from ccproxy.pricing.cache import PricingCache
|
|
47
|
-
from ccproxy.pricing.loader import PricingLoader
|
|
48
|
-
|
|
49
|
-
# Get canonical model name
|
|
50
|
-
canonical_model = PricingLoader.get_canonical_model_name(model)
|
|
51
|
-
|
|
52
|
-
# Create pricing components with dependency injection
|
|
53
|
-
settings = PricingSettings()
|
|
54
|
-
cache = PricingCache(settings)
|
|
55
|
-
cached_data = cache.load_cached_data()
|
|
56
|
-
|
|
57
|
-
# If cache is expired, try to use stale cache as fallback
|
|
58
|
-
if not cached_data:
|
|
59
|
-
try:
|
|
60
|
-
import json
|
|
61
|
-
|
|
62
|
-
if cache.cache_file.exists():
|
|
63
|
-
with cache.cache_file.open(encoding="utf-8") as f:
|
|
64
|
-
cached_data = json.load(f)
|
|
65
|
-
logger.debug(
|
|
66
|
-
"cost_calculation_using_stale_cache",
|
|
67
|
-
cache_age_hours=cache.get_cache_info().get("age_hours"),
|
|
68
|
-
)
|
|
69
|
-
except (OSError, json.JSONDecodeError):
|
|
70
|
-
pass
|
|
71
|
-
|
|
72
|
-
if not cached_data:
|
|
73
|
-
logger.debug("cost_calculation_skipped", reason="no_pricing_data")
|
|
74
|
-
return None
|
|
75
|
-
|
|
76
|
-
# Load pricing data
|
|
77
|
-
pricing_data = PricingLoader.load_pricing_from_data(cached_data, verbose=False)
|
|
78
|
-
if not pricing_data or canonical_model not in pricing_data:
|
|
79
|
-
logger.debug(
|
|
80
|
-
"cost_calculation_skipped",
|
|
81
|
-
model=canonical_model,
|
|
82
|
-
reason="model_not_found",
|
|
83
|
-
)
|
|
84
|
-
return None
|
|
85
|
-
|
|
86
|
-
model_pricing = pricing_data[canonical_model]
|
|
87
|
-
|
|
88
|
-
# Calculate cost (pricing is per 1M tokens)
|
|
89
|
-
input_cost = ((tokens_input or 0) / 1_000_000) * float(model_pricing.input)
|
|
90
|
-
output_cost = ((tokens_output or 0) / 1_000_000) * float(model_pricing.output)
|
|
91
|
-
cache_read_cost = ((cache_read_tokens or 0) / 1_000_000) * float(
|
|
92
|
-
model_pricing.cache_read
|
|
93
|
-
)
|
|
94
|
-
cache_write_cost = ((cache_write_tokens or 0) / 1_000_000) * float(
|
|
95
|
-
model_pricing.cache_write
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
total_cost = input_cost + output_cost + cache_read_cost + cache_write_cost
|
|
99
|
-
|
|
100
|
-
logger.debug(
|
|
101
|
-
"cost_calculated",
|
|
102
|
-
model=canonical_model,
|
|
103
|
-
tokens_input=tokens_input,
|
|
104
|
-
tokens_output=tokens_output,
|
|
105
|
-
cache_read_tokens=cache_read_tokens,
|
|
106
|
-
cache_write_tokens=cache_write_tokens,
|
|
107
|
-
input_cost=input_cost,
|
|
108
|
-
output_cost=output_cost,
|
|
109
|
-
cache_read_cost=cache_read_cost,
|
|
110
|
-
cache_write_cost=cache_write_cost,
|
|
111
|
-
cost_usd=total_cost,
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
return total_cost
|
|
115
|
-
|
|
116
|
-
except Exception as e:
|
|
117
|
-
logger.debug("cost_calculation_error", error=str(e), model=model)
|
|
118
|
-
return None
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
def calculate_cost_breakdown(
|
|
122
|
-
tokens_input: int | None,
|
|
123
|
-
tokens_output: int | None,
|
|
124
|
-
model: str | None,
|
|
125
|
-
cache_read_tokens: int | None = None,
|
|
126
|
-
cache_write_tokens: int | None = None,
|
|
127
|
-
) -> dict[str, float | str] | None:
|
|
128
|
-
"""Calculate detailed cost breakdown for the given token usage.
|
|
129
|
-
|
|
130
|
-
Args:
|
|
131
|
-
tokens_input: Number of input tokens
|
|
132
|
-
tokens_output: Number of output tokens
|
|
133
|
-
model: Model name for pricing lookup
|
|
134
|
-
cache_read_tokens: Number of cache read tokens
|
|
135
|
-
cache_write_tokens: Number of cache write tokens
|
|
136
|
-
|
|
137
|
-
Returns:
|
|
138
|
-
Dictionary with cost breakdown or None if calculation not possible
|
|
139
|
-
"""
|
|
140
|
-
if not model or (
|
|
141
|
-
not tokens_input
|
|
142
|
-
and not tokens_output
|
|
143
|
-
and not cache_read_tokens
|
|
144
|
-
and not cache_write_tokens
|
|
145
|
-
):
|
|
146
|
-
return None
|
|
147
|
-
|
|
148
|
-
try:
|
|
149
|
-
# Import pricing system components
|
|
150
|
-
from ccproxy.config.pricing import PricingSettings
|
|
151
|
-
from ccproxy.pricing.cache import PricingCache
|
|
152
|
-
from ccproxy.pricing.loader import PricingLoader
|
|
153
|
-
|
|
154
|
-
# Get canonical model name
|
|
155
|
-
canonical_model = PricingLoader.get_canonical_model_name(model)
|
|
156
|
-
|
|
157
|
-
# Create pricing components with dependency injection
|
|
158
|
-
settings = PricingSettings()
|
|
159
|
-
cache = PricingCache(settings)
|
|
160
|
-
cached_data = cache.load_cached_data()
|
|
161
|
-
|
|
162
|
-
# If cache is expired, try to use stale cache as fallback
|
|
163
|
-
if not cached_data:
|
|
164
|
-
try:
|
|
165
|
-
import json
|
|
166
|
-
|
|
167
|
-
if cache.cache_file.exists():
|
|
168
|
-
with cache.cache_file.open(encoding="utf-8") as f:
|
|
169
|
-
cached_data = json.load(f)
|
|
170
|
-
logger.debug(
|
|
171
|
-
"cost_breakdown_using_stale_cache",
|
|
172
|
-
cache_age_hours=cache.get_cache_info().get("age_hours"),
|
|
173
|
-
)
|
|
174
|
-
except (OSError, json.JSONDecodeError):
|
|
175
|
-
pass
|
|
176
|
-
|
|
177
|
-
if not cached_data:
|
|
178
|
-
return None
|
|
179
|
-
|
|
180
|
-
# Load pricing data
|
|
181
|
-
pricing_data = PricingLoader.load_pricing_from_data(cached_data, verbose=False)
|
|
182
|
-
if not pricing_data or canonical_model not in pricing_data:
|
|
183
|
-
return None
|
|
184
|
-
|
|
185
|
-
model_pricing = pricing_data[canonical_model]
|
|
186
|
-
|
|
187
|
-
# Calculate individual costs (pricing is per 1M tokens)
|
|
188
|
-
input_cost = ((tokens_input or 0) / 1_000_000) * float(model_pricing.input)
|
|
189
|
-
output_cost = ((tokens_output or 0) / 1_000_000) * float(model_pricing.output)
|
|
190
|
-
cache_read_cost = ((cache_read_tokens or 0) / 1_000_000) * float(
|
|
191
|
-
model_pricing.cache_read
|
|
192
|
-
)
|
|
193
|
-
cache_write_cost = ((cache_write_tokens or 0) / 1_000_000) * float(
|
|
194
|
-
model_pricing.cache_write
|
|
195
|
-
)
|
|
196
|
-
|
|
197
|
-
total_cost = input_cost + output_cost + cache_read_cost + cache_write_cost
|
|
198
|
-
|
|
199
|
-
return {
|
|
200
|
-
"input_cost": input_cost,
|
|
201
|
-
"output_cost": output_cost,
|
|
202
|
-
"cache_read_cost": cache_read_cost,
|
|
203
|
-
"cache_write_cost": cache_write_cost,
|
|
204
|
-
"total_cost": total_cost,
|
|
205
|
-
"model": canonical_model,
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
except Exception as e:
|
|
209
|
-
logger.debug("cost_breakdown_error", error=str(e), model=model)
|
|
210
|
-
return None
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
"""Utility functions for monitoring client disconnection and stuck streams during streaming responses."""
|
|
2
|
-
|
|
3
|
-
import asyncio
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
5
|
-
|
|
6
|
-
import structlog
|
|
7
|
-
from starlette.requests import Request
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if TYPE_CHECKING:
|
|
11
|
-
from ccproxy.services.claude_sdk_service import ClaudeSDKService
|
|
12
|
-
|
|
13
|
-
logger = structlog.get_logger(__name__)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
async def monitor_disconnection(
|
|
17
|
-
request: Request, session_id: str, claude_service: "ClaudeSDKService"
|
|
18
|
-
) -> None:
|
|
19
|
-
"""Monitor for client disconnection and interrupt session if detected.
|
|
20
|
-
|
|
21
|
-
Args:
|
|
22
|
-
request: The incoming HTTP request
|
|
23
|
-
session_id: The Claude SDK session ID to interrupt if disconnected
|
|
24
|
-
claude_service: The Claude SDK service instance
|
|
25
|
-
"""
|
|
26
|
-
try:
|
|
27
|
-
while True:
|
|
28
|
-
await asyncio.sleep(1.0) # Check every second
|
|
29
|
-
if await request.is_disconnected():
|
|
30
|
-
logger.info(
|
|
31
|
-
"client_disconnected_interrupting_session", session_id=session_id
|
|
32
|
-
)
|
|
33
|
-
try:
|
|
34
|
-
await claude_service.sdk_client.interrupt_session(session_id)
|
|
35
|
-
except Exception as e:
|
|
36
|
-
logger.error(
|
|
37
|
-
"failed_to_interrupt_session",
|
|
38
|
-
session_id=session_id,
|
|
39
|
-
error=str(e),
|
|
40
|
-
)
|
|
41
|
-
return
|
|
42
|
-
except asyncio.CancelledError:
|
|
43
|
-
# Task was cancelled, which is expected when streaming completes normally
|
|
44
|
-
logger.debug("disconnection_monitor_cancelled", session_id=session_id)
|
|
45
|
-
raise
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
async def monitor_stuck_stream(
|
|
49
|
-
session_id: str,
|
|
50
|
-
claude_service: "ClaudeSDKService",
|
|
51
|
-
first_chunk_event: asyncio.Event,
|
|
52
|
-
timeout: float = 10.0,
|
|
53
|
-
) -> None:
|
|
54
|
-
"""Monitor for stuck streams that don't produce a first chunk (SystemMessage).
|
|
55
|
-
|
|
56
|
-
Args:
|
|
57
|
-
session_id: The Claude SDK session ID to monitor
|
|
58
|
-
claude_service: The Claude SDK service instance
|
|
59
|
-
first_chunk_event: Event that will be set when first chunk is received
|
|
60
|
-
timeout: Seconds to wait for first chunk before considering stream stuck
|
|
61
|
-
"""
|
|
62
|
-
try:
|
|
63
|
-
# Wait for first chunk with timeout
|
|
64
|
-
await asyncio.wait_for(first_chunk_event.wait(), timeout=timeout)
|
|
65
|
-
logger.debug("stuck_stream_first_chunk_received", session_id=session_id)
|
|
66
|
-
except TimeoutError:
|
|
67
|
-
logger.error(
|
|
68
|
-
"streaming_system_message_timeout",
|
|
69
|
-
session_id=session_id,
|
|
70
|
-
timeout=timeout,
|
|
71
|
-
message=f"No SystemMessage received within {timeout}s, interrupting session",
|
|
72
|
-
)
|
|
73
|
-
try:
|
|
74
|
-
await claude_service.sdk_client.interrupt_session(session_id)
|
|
75
|
-
logger.info("stuck_session_interrupted_successfully", session_id=session_id)
|
|
76
|
-
except Exception as e:
|
|
77
|
-
logger.error(
|
|
78
|
-
"failed_to_interrupt_stuck_session", session_id=session_id, error=str(e)
|
|
79
|
-
)
|
|
80
|
-
except asyncio.CancelledError:
|
|
81
|
-
# Task was cancelled, which is expected when streaming completes normally
|
|
82
|
-
logger.debug("stuck_stream_monitor_cancelled", session_id=session_id)
|
|
83
|
-
raise
|
ccproxy/utils/model_mapping.py
DELETED
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
"""Unified model mapping utilities for OpenAI and Claude models.
|
|
2
|
-
|
|
3
|
-
This module provides a single source of truth for all model mappings,
|
|
4
|
-
consolidating OpenAI→Claude mappings and Claude alias resolution.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
# Combined mapping: OpenAI models → Claude models AND Claude aliases → canonical Claude models
|
|
11
|
-
MODEL_MAPPING: dict[str, str] = {
|
|
12
|
-
"gpt-5": "claude-sonnet-4-20250514",
|
|
13
|
-
# OpenAI GPT-4 models → Claude 3.5 Sonnet (most comparable)
|
|
14
|
-
"gpt-4": "claude-3-5-sonnet-20241022",
|
|
15
|
-
"gpt-4-turbo": "claude-3-5-sonnet-20241022",
|
|
16
|
-
"gpt-4-turbo-preview": "claude-3-5-sonnet-20241022",
|
|
17
|
-
"gpt-4-1106-preview": "claude-3-5-sonnet-20241022",
|
|
18
|
-
"gpt-4-0125-preview": "claude-3-5-sonnet-20241022",
|
|
19
|
-
"gpt-4-turbo-2024-04-09": "claude-3-5-sonnet-20241022",
|
|
20
|
-
# OpenAI GPT-4o models → Claude 3.7 Sonnet
|
|
21
|
-
"gpt-4o": "claude-3-7-sonnet-20250219",
|
|
22
|
-
"gpt-4o-2024-05-13": "claude-3-7-sonnet-20250219",
|
|
23
|
-
"gpt-4o-2024-08-06": "claude-3-7-sonnet-20250219",
|
|
24
|
-
"gpt-4o-2024-11-20": "claude-3-7-sonnet-20250219",
|
|
25
|
-
# OpenAI GPT-4o-mini models → Claude 3.5 Haiku
|
|
26
|
-
"gpt-4o-mini": "claude-3-5-haiku-latest",
|
|
27
|
-
"gpt-4o-mini-2024-07-18": "claude-3-5-haiku-latest",
|
|
28
|
-
# OpenAI o1 models → Claude models that support thinking
|
|
29
|
-
"o1": "claude-opus-4-20250514",
|
|
30
|
-
"o1-preview": "claude-opus-4-20250514",
|
|
31
|
-
"o1-mini": "claude-sonnet-4-20250514",
|
|
32
|
-
# OpenAI o3 models → Claude Opus 4
|
|
33
|
-
"o3-mini": "claude-opus-4-20250514",
|
|
34
|
-
# OpenAI GPT-3.5 models → Claude 3.5 Haiku (faster, cheaper)
|
|
35
|
-
"gpt-3.5-turbo": "claude-3-5-haiku-20241022",
|
|
36
|
-
"gpt-3.5-turbo-16k": "claude-3-5-haiku-20241022",
|
|
37
|
-
"gpt-3.5-turbo-1106": "claude-3-5-haiku-20241022",
|
|
38
|
-
"gpt-3.5-turbo-0125": "claude-3-5-haiku-20241022",
|
|
39
|
-
# OpenAI text models → Claude 3.5 Sonnet
|
|
40
|
-
"text-davinci-003": "claude-3-5-sonnet-20241022",
|
|
41
|
-
"text-davinci-002": "claude-3-5-sonnet-20241022",
|
|
42
|
-
# Claude model aliases → canonical Claude models
|
|
43
|
-
"claude-3-5-sonnet-latest": "claude-3-5-sonnet-20241022",
|
|
44
|
-
"claude-3-5-sonnet-20240620": "claude-3-5-sonnet-20240620",
|
|
45
|
-
"claude-3-5-sonnet-20241022": "claude-3-5-sonnet-20241022",
|
|
46
|
-
"claude-3-5-haiku-latest": "claude-3-5-haiku-20241022",
|
|
47
|
-
"claude-3-5-haiku-20241022": "claude-3-5-haiku-20241022",
|
|
48
|
-
"claude-3-opus": "claude-3-opus-20240229",
|
|
49
|
-
"claude-3-opus-20240229": "claude-3-opus-20240229",
|
|
50
|
-
"claude-3-sonnet": "claude-3-sonnet-20240229",
|
|
51
|
-
"claude-3-sonnet-20240229": "claude-3-sonnet-20240229",
|
|
52
|
-
"claude-3-haiku": "claude-3-haiku-20240307",
|
|
53
|
-
"claude-3-haiku-20240307": "claude-3-haiku-20240307",
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def map_model_to_claude(model_name: str) -> str:
|
|
58
|
-
"""Map any model name to its canonical Claude model name.
|
|
59
|
-
|
|
60
|
-
This function handles:
|
|
61
|
-
- OpenAI model names → Claude equivalents
|
|
62
|
-
- Claude aliases → canonical Claude names
|
|
63
|
-
- Pattern matching for versioned models
|
|
64
|
-
- Pass-through for unknown models
|
|
65
|
-
|
|
66
|
-
Args:
|
|
67
|
-
model_name: Model identifier (OpenAI, Claude, or alias)
|
|
68
|
-
|
|
69
|
-
Returns:
|
|
70
|
-
Canonical Claude model identifier
|
|
71
|
-
"""
|
|
72
|
-
# Direct mapping first (handles both OpenAI and Claude aliases)
|
|
73
|
-
claude_model = MODEL_MAPPING.get(model_name)
|
|
74
|
-
if claude_model:
|
|
75
|
-
return claude_model
|
|
76
|
-
|
|
77
|
-
# Pattern matching for versioned OpenAI models
|
|
78
|
-
if model_name.startswith("gpt-4o-mini"):
|
|
79
|
-
return "claude-3-5-haiku-latest"
|
|
80
|
-
elif model_name.startswith("gpt-4o") or model_name.startswith("gpt-4"):
|
|
81
|
-
return "claude-3-7-sonnet-20250219"
|
|
82
|
-
elif model_name.startswith("gpt-3.5"):
|
|
83
|
-
return "claude-3-5-haiku-latest"
|
|
84
|
-
elif (
|
|
85
|
-
model_name.startswith("o1")
|
|
86
|
-
or model_name.startswith("gpt-5")
|
|
87
|
-
or model_name.startswith("o3")
|
|
88
|
-
or model_name.startswith("gpt")
|
|
89
|
-
):
|
|
90
|
-
return "claude-sonnet-4-20250514"
|
|
91
|
-
|
|
92
|
-
# If it's already a Claude model, pass through unchanged
|
|
93
|
-
if model_name.startswith("claude-"):
|
|
94
|
-
return model_name
|
|
95
|
-
|
|
96
|
-
# For unknown models, pass through unchanged
|
|
97
|
-
return model_name
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def get_openai_to_claude_mapping() -> dict[str, str]:
|
|
101
|
-
"""Get mapping of OpenAI models to Claude models.
|
|
102
|
-
|
|
103
|
-
Returns:
|
|
104
|
-
Dictionary mapping OpenAI model names to Claude model names
|
|
105
|
-
"""
|
|
106
|
-
return {k: v for k, v in MODEL_MAPPING.items() if not k.startswith("claude-")}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
def get_claude_aliases_mapping() -> dict[str, str]:
|
|
110
|
-
"""Get mapping of Claude aliases to canonical Claude names.
|
|
111
|
-
|
|
112
|
-
Returns:
|
|
113
|
-
Dictionary mapping Claude aliases to canonical model names
|
|
114
|
-
"""
|
|
115
|
-
return {k: v for k, v in MODEL_MAPPING.items() if k.startswith("claude-")}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def get_supported_claude_models() -> list[str]:
|
|
119
|
-
"""Get list of supported canonical Claude models.
|
|
120
|
-
|
|
121
|
-
Returns:
|
|
122
|
-
Sorted list of unique canonical Claude model names
|
|
123
|
-
"""
|
|
124
|
-
return sorted(set(MODEL_MAPPING.values()))
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
def is_openai_model(model_name: str) -> bool:
|
|
128
|
-
"""Check if a model name is an OpenAI model.
|
|
129
|
-
|
|
130
|
-
Args:
|
|
131
|
-
model_name: Model identifier to check
|
|
132
|
-
|
|
133
|
-
Returns:
|
|
134
|
-
True if the model is an OpenAI model, False otherwise
|
|
135
|
-
"""
|
|
136
|
-
return (
|
|
137
|
-
model_name.startswith(("gpt-", "o1", "o3", "text-davinci"))
|
|
138
|
-
or model_name in get_openai_to_claude_mapping()
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
def is_claude_model(model_name: str) -> bool:
|
|
143
|
-
"""Check if a model name is a Claude model (canonical or alias).
|
|
144
|
-
|
|
145
|
-
Args:
|
|
146
|
-
model_name: Model identifier to check
|
|
147
|
-
|
|
148
|
-
Returns:
|
|
149
|
-
True if the model is a Claude model, False otherwise
|
|
150
|
-
"""
|
|
151
|
-
return (
|
|
152
|
-
model_name.startswith("claude-") or model_name in get_claude_aliases_mapping()
|
|
153
|
-
)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
# Backward compatibility exports
|
|
157
|
-
OPENAI_TO_CLAUDE_MODEL_MAPPING = get_openai_to_claude_mapping()
|
|
158
|
-
CLAUDE_MODEL_MAPPINGS = get_claude_aliases_mapping()
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
# Legacy function aliases
|
|
162
|
-
def map_openai_model_to_claude(openai_model: str) -> str:
|
|
163
|
-
"""Legacy alias for map_model_to_claude().
|
|
164
|
-
|
|
165
|
-
Args:
|
|
166
|
-
openai_model: OpenAI model identifier
|
|
167
|
-
|
|
168
|
-
Returns:
|
|
169
|
-
Claude model identifier
|
|
170
|
-
"""
|
|
171
|
-
return map_model_to_claude(openai_model)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
def get_canonical_model_name(model_name: str) -> str:
|
|
175
|
-
"""Legacy alias for map_model_to_claude().
|
|
176
|
-
|
|
177
|
-
Args:
|
|
178
|
-
model_name: Model name (possibly an alias)
|
|
179
|
-
|
|
180
|
-
Returns:
|
|
181
|
-
Canonical model name
|
|
182
|
-
"""
|
|
183
|
-
return map_model_to_claude(model_name)
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
__all__ = [
|
|
187
|
-
"MODEL_MAPPING",
|
|
188
|
-
"map_model_to_claude",
|
|
189
|
-
"get_openai_to_claude_mapping",
|
|
190
|
-
"get_claude_aliases_mapping",
|
|
191
|
-
"get_supported_claude_models",
|
|
192
|
-
"is_openai_model",
|
|
193
|
-
"is_claude_model",
|
|
194
|
-
# Backward compatibility
|
|
195
|
-
"OPENAI_TO_CLAUDE_MODEL_MAPPING",
|
|
196
|
-
"CLAUDE_MODEL_MAPPINGS",
|
|
197
|
-
"map_openai_model_to_claude",
|
|
198
|
-
"get_canonical_model_name",
|
|
199
|
-
]
|
ccproxy/utils/models_provider.py
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
"""Shared models provider for CCProxy API Server.
|
|
2
|
-
|
|
3
|
-
This module provides a centralized source for all available models,
|
|
4
|
-
combining Claude and OpenAI models in a consistent format.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from typing import Any
|
|
10
|
-
|
|
11
|
-
from ccproxy.utils.model_mapping import get_supported_claude_models
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def get_anthropic_models() -> list[dict[str, Any]]:
|
|
15
|
-
"""Get list of Anthropic models with metadata.
|
|
16
|
-
|
|
17
|
-
Returns:
|
|
18
|
-
List of Anthropic model entries with type, id, display_name, and created_at fields
|
|
19
|
-
"""
|
|
20
|
-
# Model display names mapping
|
|
21
|
-
display_names = {
|
|
22
|
-
"claude-opus-4-20250514": "Claude Opus 4",
|
|
23
|
-
"claude-sonnet-4-20250514": "Claude Sonnet 4",
|
|
24
|
-
"claude-3-7-sonnet-20250219": "Claude Sonnet 3.7",
|
|
25
|
-
"claude-3-5-sonnet-20241022": "Claude Sonnet 3.5 (New)",
|
|
26
|
-
"claude-3-5-haiku-20241022": "Claude Haiku 3.5",
|
|
27
|
-
"claude-3-5-haiku-latest": "Claude Haiku 3.5",
|
|
28
|
-
"claude-3-5-sonnet-20240620": "Claude Sonnet 3.5 (Old)",
|
|
29
|
-
"claude-3-haiku-20240307": "Claude Haiku 3",
|
|
30
|
-
"claude-3-opus-20240229": "Claude Opus 3",
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
# Model creation timestamps
|
|
34
|
-
timestamps = {
|
|
35
|
-
"claude-opus-4-20250514": 1747526400, # 2025-05-22
|
|
36
|
-
"claude-sonnet-4-20250514": 1747526400, # 2025-05-22
|
|
37
|
-
"claude-3-7-sonnet-20250219": 1740268800, # 2025-02-24
|
|
38
|
-
"claude-3-5-sonnet-20241022": 1729555200, # 2024-10-22
|
|
39
|
-
"claude-3-5-haiku-20241022": 1729555200, # 2024-10-22
|
|
40
|
-
"claude-3-5-haiku-latest": 1729555200, # 2024-10-22
|
|
41
|
-
"claude-3-5-sonnet-20240620": 1718841600, # 2024-06-20
|
|
42
|
-
"claude-3-haiku-20240307": 1709769600, # 2024-03-07
|
|
43
|
-
"claude-3-opus-20240229": 1709164800, # 2024-02-29
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
# Get supported Claude models from existing utility
|
|
47
|
-
supported_models = get_supported_claude_models()
|
|
48
|
-
|
|
49
|
-
# Create Anthropic-style model entries
|
|
50
|
-
models = []
|
|
51
|
-
for model_id in supported_models:
|
|
52
|
-
models.append(
|
|
53
|
-
{
|
|
54
|
-
"type": "model",
|
|
55
|
-
"id": model_id,
|
|
56
|
-
"display_name": display_names.get(model_id, model_id),
|
|
57
|
-
"created_at": timestamps.get(model_id, 1677610602), # Default timestamp
|
|
58
|
-
}
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
return models
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def get_openai_models() -> list[dict[str, Any]]:
|
|
65
|
-
"""Get list of recent OpenAI models with metadata.
|
|
66
|
-
|
|
67
|
-
Returns:
|
|
68
|
-
List of OpenAI model entries with id, object, created, and owned_by fields
|
|
69
|
-
"""
|
|
70
|
-
return [
|
|
71
|
-
{
|
|
72
|
-
"id": "gpt-4o",
|
|
73
|
-
"object": "model",
|
|
74
|
-
"created": 1715367049,
|
|
75
|
-
"owned_by": "openai",
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
"id": "gpt-4o-mini",
|
|
79
|
-
"object": "model",
|
|
80
|
-
"created": 1721172741,
|
|
81
|
-
"owned_by": "openai",
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
"id": "gpt-4-turbo",
|
|
85
|
-
"object": "model",
|
|
86
|
-
"created": 1712361441,
|
|
87
|
-
"owned_by": "openai",
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
"id": "gpt-4-turbo-preview",
|
|
91
|
-
"object": "model",
|
|
92
|
-
"created": 1706037777,
|
|
93
|
-
"owned_by": "openai",
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
"id": "o1",
|
|
97
|
-
"object": "model",
|
|
98
|
-
"created": 1734375816,
|
|
99
|
-
"owned_by": "openai",
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
"id": "o1-mini",
|
|
103
|
-
"object": "model",
|
|
104
|
-
"created": 1725649008,
|
|
105
|
-
"owned_by": "openai",
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
"id": "o1-preview",
|
|
109
|
-
"object": "model",
|
|
110
|
-
"created": 1725648897,
|
|
111
|
-
"owned_by": "openai",
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
"id": "o3",
|
|
115
|
-
"object": "model",
|
|
116
|
-
"created": 1744225308,
|
|
117
|
-
"owned_by": "openai",
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
"id": "o3-mini",
|
|
121
|
-
"object": "model",
|
|
122
|
-
"created": 1737146383,
|
|
123
|
-
"owned_by": "openai",
|
|
124
|
-
},
|
|
125
|
-
]
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def get_models_list() -> dict[str, Any]:
|
|
129
|
-
"""Get combined list of available Claude and OpenAI models.
|
|
130
|
-
|
|
131
|
-
Returns:
|
|
132
|
-
Dictionary with combined list of models in mixed format compatible with both
|
|
133
|
-
Anthropic and OpenAI API specifications
|
|
134
|
-
"""
|
|
135
|
-
anthropic_models = get_anthropic_models()
|
|
136
|
-
openai_models = get_openai_models()
|
|
137
|
-
|
|
138
|
-
# Return combined response in mixed format
|
|
139
|
-
return {
|
|
140
|
-
"data": anthropic_models + openai_models,
|
|
141
|
-
"has_more": False,
|
|
142
|
-
"object": "list",
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
__all__ = [
|
|
147
|
-
"get_anthropic_models",
|
|
148
|
-
"get_openai_models",
|
|
149
|
-
"get_models_list",
|
|
150
|
-
]
|