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
ccproxy/config/utils.py
ADDED
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
"""Configuration utilities - constants, validators, discovery, and scheduler."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import subprocess
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
from urllib.parse import urlparse
|
|
8
|
+
|
|
9
|
+
from pydantic import Field
|
|
10
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
11
|
+
|
|
12
|
+
from ccproxy.core.system import get_xdg_cache_home, get_xdg_config_home
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# === Configuration Constants ===
|
|
16
|
+
|
|
17
|
+
# Plugin System Constants
|
|
18
|
+
PLUGIN_HEALTH_CHECK_TIMEOUT = 10.0 # seconds
|
|
19
|
+
PLUGIN_SUMMARY_CACHE_TTL = 300.0 # 5 minutes
|
|
20
|
+
PLUGIN_SUMMARY_CACHE_SIZE = 32 # entries
|
|
21
|
+
|
|
22
|
+
# Task Scheduler Constants
|
|
23
|
+
DEFAULT_TASK_INTERVAL = 3600 # 1 hour in seconds
|
|
24
|
+
|
|
25
|
+
# URL Constants
|
|
26
|
+
CLAUDE_API_BASE_URL = "https://api.anthropic.com"
|
|
27
|
+
CODEX_API_BASE_URL = "https://chatgpt.com/backend-api"
|
|
28
|
+
|
|
29
|
+
# API Endpoints
|
|
30
|
+
CLAUDE_MESSAGES_ENDPOINT = "/v1/messages"
|
|
31
|
+
CODEX_RESPONSES_ENDPOINT = "/codex/responses"
|
|
32
|
+
|
|
33
|
+
# Format Conversion Patterns
|
|
34
|
+
OPENAI_CHAT_COMPLETIONS_PATH = "/v1/chat/completions"
|
|
35
|
+
OPENAI_COMPLETIONS_PATH = "/chat/completions"
|
|
36
|
+
ANTHROPIC_MESSAGES_PATH = "/v1/messages"
|
|
37
|
+
|
|
38
|
+
# HTTP Client Configuration
|
|
39
|
+
HTTP_CLIENT_TIMEOUT = 120.0 # 2 minutes default timeout
|
|
40
|
+
HTTP_STREAMING_TIMEOUT = 300.0 # 5 minutes for streaming requests
|
|
41
|
+
HTTP_CLIENT_POOL_SIZE = 20 # Max connections per pool
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# === Configuration Validators ===
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ConfigValidationError(Exception):
|
|
48
|
+
"""Configuration validation error."""
|
|
49
|
+
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def validate_host(host: str) -> str:
|
|
54
|
+
"""Validate host address.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
host: Host address to validate
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
The validated host address
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
ConfigValidationError: If host is invalid
|
|
64
|
+
"""
|
|
65
|
+
if not host:
|
|
66
|
+
raise ConfigValidationError("Host cannot be empty")
|
|
67
|
+
|
|
68
|
+
# Allow localhost, IP addresses, and domain names
|
|
69
|
+
if host in ["localhost", "0.0.0.0", "127.0.0.1"]:
|
|
70
|
+
return host
|
|
71
|
+
|
|
72
|
+
# Basic IP address validation
|
|
73
|
+
if re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", host):
|
|
74
|
+
parts = host.split(".")
|
|
75
|
+
if all(0 <= int(part) <= 255 for part in parts):
|
|
76
|
+
return host
|
|
77
|
+
raise ConfigValidationError(f"Invalid IP address: {host}")
|
|
78
|
+
|
|
79
|
+
# Basic domain name validation
|
|
80
|
+
if re.match(r"^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", host):
|
|
81
|
+
return host
|
|
82
|
+
|
|
83
|
+
return host # Allow other formats for flexibility
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def validate_port(port: int | str) -> int:
|
|
87
|
+
"""Validate port number.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
port: Port number to validate
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
The validated port number
|
|
94
|
+
|
|
95
|
+
Raises:
|
|
96
|
+
ConfigValidationError: If port is invalid
|
|
97
|
+
"""
|
|
98
|
+
if isinstance(port, str):
|
|
99
|
+
try:
|
|
100
|
+
port = int(port)
|
|
101
|
+
except ValueError as e:
|
|
102
|
+
raise ConfigValidationError(f"Port must be a valid integer: {port}") from e
|
|
103
|
+
|
|
104
|
+
if not isinstance(port, int):
|
|
105
|
+
raise ConfigValidationError(f"Port must be an integer: {port}")
|
|
106
|
+
|
|
107
|
+
if port < 1 or port > 65535:
|
|
108
|
+
raise ConfigValidationError(f"Port must be between 1 and 65535: {port}")
|
|
109
|
+
|
|
110
|
+
return port
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def validate_url(url: str) -> str:
|
|
114
|
+
"""Validate URL format.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
url: URL to validate
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
The validated URL
|
|
121
|
+
|
|
122
|
+
Raises:
|
|
123
|
+
ConfigValidationError: If URL is invalid
|
|
124
|
+
"""
|
|
125
|
+
if not url:
|
|
126
|
+
raise ConfigValidationError("URL cannot be empty")
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
result = urlparse(url)
|
|
130
|
+
if not result.scheme or not result.netloc:
|
|
131
|
+
raise ConfigValidationError(f"Invalid URL format: {url}")
|
|
132
|
+
except Exception as e:
|
|
133
|
+
raise ConfigValidationError(f"Invalid URL: {url}") from e
|
|
134
|
+
|
|
135
|
+
return url
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def validate_path(path: str | Path) -> Path:
|
|
139
|
+
"""Validate file path.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
path: Path to validate
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
The validated Path object
|
|
146
|
+
|
|
147
|
+
Raises:
|
|
148
|
+
ConfigValidationError: If path is invalid
|
|
149
|
+
"""
|
|
150
|
+
if isinstance(path, str):
|
|
151
|
+
path = Path(path)
|
|
152
|
+
|
|
153
|
+
if not isinstance(path, Path):
|
|
154
|
+
raise ConfigValidationError(f"Path must be a string or Path object: {path}")
|
|
155
|
+
|
|
156
|
+
return path
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def validate_log_level(level: str) -> str:
|
|
160
|
+
"""Validate log level.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
level: Log level to validate
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
The validated log level
|
|
167
|
+
|
|
168
|
+
Raises:
|
|
169
|
+
ConfigValidationError: If log level is invalid
|
|
170
|
+
"""
|
|
171
|
+
valid_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
|
172
|
+
level = level.upper()
|
|
173
|
+
|
|
174
|
+
if level not in valid_levels:
|
|
175
|
+
raise ConfigValidationError(
|
|
176
|
+
f"Invalid log level: {level}. Must be one of: {valid_levels}"
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
return level
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def validate_cors_origins(origins: list[str]) -> list[str]:
|
|
183
|
+
"""Validate CORS origins.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
origins: List of origin URLs to validate
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
The validated list of origins
|
|
190
|
+
|
|
191
|
+
Raises:
|
|
192
|
+
ConfigValidationError: If any origin is invalid
|
|
193
|
+
"""
|
|
194
|
+
if not isinstance(origins, list):
|
|
195
|
+
raise ConfigValidationError("CORS origins must be a list")
|
|
196
|
+
|
|
197
|
+
validated_origins = []
|
|
198
|
+
for origin in origins:
|
|
199
|
+
if origin == "*":
|
|
200
|
+
validated_origins.append(origin)
|
|
201
|
+
else:
|
|
202
|
+
validated_origins.append(validate_url(origin))
|
|
203
|
+
|
|
204
|
+
return validated_origins
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def validate_timeout(timeout: int | float) -> int | float:
|
|
208
|
+
"""Validate timeout value.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
timeout: Timeout value to validate
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
The validated timeout value
|
|
215
|
+
|
|
216
|
+
Raises:
|
|
217
|
+
ConfigValidationError: If timeout is invalid
|
|
218
|
+
"""
|
|
219
|
+
if not isinstance(timeout, int | float):
|
|
220
|
+
raise ConfigValidationError(f"Timeout must be a number: {timeout}")
|
|
221
|
+
|
|
222
|
+
if timeout <= 0:
|
|
223
|
+
raise ConfigValidationError(f"Timeout must be positive: {timeout}")
|
|
224
|
+
|
|
225
|
+
return timeout
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def validate_config_dict(config: dict[str, Any]) -> dict[str, Any]:
|
|
229
|
+
"""Validate configuration dictionary.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
config: Configuration dictionary to validate
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
The validated configuration dictionary
|
|
236
|
+
|
|
237
|
+
Raises:
|
|
238
|
+
ConfigValidationError: If configuration is invalid
|
|
239
|
+
"""
|
|
240
|
+
if not isinstance(config, dict):
|
|
241
|
+
raise ConfigValidationError("Configuration must be a dictionary")
|
|
242
|
+
|
|
243
|
+
validated_config: dict[str, Any] = {}
|
|
244
|
+
|
|
245
|
+
# Validate specific fields if present
|
|
246
|
+
if "host" in config:
|
|
247
|
+
validated_config["host"] = validate_host(config["host"])
|
|
248
|
+
|
|
249
|
+
if "port" in config:
|
|
250
|
+
validated_config["port"] = validate_port(config["port"])
|
|
251
|
+
|
|
252
|
+
if "target_url" in config:
|
|
253
|
+
validated_config["target_url"] = validate_url(config["target_url"])
|
|
254
|
+
|
|
255
|
+
if "log_level" in config:
|
|
256
|
+
validated_config["log_level"] = validate_log_level(config["log_level"])
|
|
257
|
+
|
|
258
|
+
if "cors_origins" in config:
|
|
259
|
+
validated_config["cors_origins"] = validate_cors_origins(config["cors_origins"])
|
|
260
|
+
|
|
261
|
+
if "timeout" in config:
|
|
262
|
+
validated_config["timeout"] = validate_timeout(config["timeout"])
|
|
263
|
+
|
|
264
|
+
# Copy other fields without validation
|
|
265
|
+
for key, value in config.items():
|
|
266
|
+
if key not in validated_config:
|
|
267
|
+
validated_config[key] = value
|
|
268
|
+
|
|
269
|
+
return validated_config
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
# === Configuration Discovery ===
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def find_toml_config_file() -> Path | None:
|
|
276
|
+
"""Find the TOML configuration file for ccproxy.
|
|
277
|
+
|
|
278
|
+
Searches in the following order:
|
|
279
|
+
1. .ccproxy.toml in current directory
|
|
280
|
+
2. ccproxy.toml in git repository root (if in a git repo)
|
|
281
|
+
3. config.toml in XDG_CONFIG_HOME/ccproxy/
|
|
282
|
+
"""
|
|
283
|
+
# Check current directory first
|
|
284
|
+
candidates = [
|
|
285
|
+
Path(".ccproxy.toml").resolve(),
|
|
286
|
+
Path("ccproxy.toml").resolve(),
|
|
287
|
+
]
|
|
288
|
+
|
|
289
|
+
# Check git repo root
|
|
290
|
+
git_root = find_git_root()
|
|
291
|
+
if git_root:
|
|
292
|
+
candidates.extend(
|
|
293
|
+
[
|
|
294
|
+
git_root / ".ccproxy.toml",
|
|
295
|
+
git_root / "ccproxy.toml",
|
|
296
|
+
]
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
# Check XDG config directory
|
|
300
|
+
config_dir = get_ccproxy_config_dir()
|
|
301
|
+
candidates.append(config_dir / "config.toml")
|
|
302
|
+
|
|
303
|
+
# Return first existing file
|
|
304
|
+
for candidate in candidates:
|
|
305
|
+
if candidate.exists() and candidate.is_file():
|
|
306
|
+
return candidate
|
|
307
|
+
|
|
308
|
+
return None
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def find_git_root(path: Path | None = None) -> Path | None:
|
|
312
|
+
"""Find the root directory of a git repository."""
|
|
313
|
+
if path is None:
|
|
314
|
+
path = Path.cwd()
|
|
315
|
+
|
|
316
|
+
try:
|
|
317
|
+
result = subprocess.run(
|
|
318
|
+
["git", "rev-parse", "--show-toplevel"],
|
|
319
|
+
cwd=path,
|
|
320
|
+
capture_output=True,
|
|
321
|
+
text=True,
|
|
322
|
+
check=True,
|
|
323
|
+
)
|
|
324
|
+
return Path(result.stdout.strip())
|
|
325
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
326
|
+
return None
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def get_ccproxy_config_dir() -> Path:
|
|
330
|
+
"""Get the ccproxy configuration directory.
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
Path to the ccproxy configuration directory within XDG_CONFIG_HOME.
|
|
334
|
+
"""
|
|
335
|
+
return get_xdg_config_home() / "ccproxy"
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def get_claude_cli_config_dir() -> Path:
|
|
339
|
+
"""Get the Claude CLI configuration directory.
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
Path to the Claude CLI configuration directory within XDG_CONFIG_HOME.
|
|
343
|
+
"""
|
|
344
|
+
return get_xdg_config_home() / "claude"
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def get_claude_docker_home_dir() -> Path:
|
|
348
|
+
"""Get the Claude Docker home directory.
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
Path to the Claude Docker home directory within XDG_DATA_HOME.
|
|
352
|
+
"""
|
|
353
|
+
return get_ccproxy_config_dir() / "home"
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def get_ccproxy_cache_dir() -> Path:
|
|
357
|
+
"""Get the ccproxy cache directory.
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
Path to the ccproxy cache directory within XDG_CACHE_HOME.
|
|
361
|
+
"""
|
|
362
|
+
return get_xdg_cache_home() / "ccproxy"
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
# === Scheduler Configuration ===
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
class SchedulerSettings(BaseSettings):
|
|
369
|
+
"""
|
|
370
|
+
Configuration settings for the unified scheduler system.
|
|
371
|
+
|
|
372
|
+
Controls global scheduler behavior and individual task configurations.
|
|
373
|
+
Settings can be configured via environment variables with SCHEDULER__ prefix.
|
|
374
|
+
"""
|
|
375
|
+
|
|
376
|
+
# Global scheduler settings
|
|
377
|
+
enabled: bool = Field(
|
|
378
|
+
default=True,
|
|
379
|
+
description="Whether the scheduler system is enabled",
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
max_concurrent_tasks: int = Field(
|
|
383
|
+
default=10,
|
|
384
|
+
ge=1,
|
|
385
|
+
le=100,
|
|
386
|
+
description="Maximum number of tasks that can run concurrently",
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
graceful_shutdown_timeout: float = Field(
|
|
390
|
+
default=30.0,
|
|
391
|
+
ge=1.0,
|
|
392
|
+
le=300.0,
|
|
393
|
+
description="Timeout in seconds for graceful task shutdown",
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
# Pricing updater task settings
|
|
397
|
+
pricing_update_enabled: bool = Field(
|
|
398
|
+
default=True,
|
|
399
|
+
description="Whether pricing cache update task is enabled. Enabled by default for privacy - downloads from GitHub when enabled",
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
pricing_update_interval_hours: int = Field(
|
|
403
|
+
default=24,
|
|
404
|
+
ge=1,
|
|
405
|
+
le=168, # Max 1 week
|
|
406
|
+
description="Interval in hours between pricing cache updates",
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
pricing_force_refresh_on_startup: bool = Field(
|
|
410
|
+
default=False,
|
|
411
|
+
description="Whether to force pricing refresh immediately on startup",
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
# Pushgateway settings are handled by the metrics plugin
|
|
415
|
+
# The metrics plugin now manages its own pushgateway configuration
|
|
416
|
+
|
|
417
|
+
stats_printing_enabled: bool = Field(
|
|
418
|
+
default=False,
|
|
419
|
+
description="Whether stats printing task is enabled",
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
stats_printing_interval_seconds: float = Field(
|
|
423
|
+
default=300.0,
|
|
424
|
+
ge=1.0,
|
|
425
|
+
le=3600.0, # Max 1 hour
|
|
426
|
+
description="Interval in seconds between stats printing",
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
# Version checking task settings
|
|
430
|
+
version_check_enabled: bool = Field(
|
|
431
|
+
default=True,
|
|
432
|
+
description="Whether version update checking is enabled. Enabled by default for privacy - checks GitHub API when enabled",
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
version_check_interval_hours: int = Field(
|
|
436
|
+
default=6,
|
|
437
|
+
ge=1,
|
|
438
|
+
le=168, # Max 1 week
|
|
439
|
+
description="Interval in hours between version checks",
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
version_check_cache_ttl_hours: float = Field(
|
|
443
|
+
default=6,
|
|
444
|
+
ge=0.1,
|
|
445
|
+
le=24.0,
|
|
446
|
+
description="Maximum age in hours since last check version check",
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
model_config = SettingsConfigDict(
|
|
450
|
+
env_prefix="SCHEDULER__",
|
|
451
|
+
case_sensitive=False,
|
|
452
|
+
)
|