ccproxy-api 0.1.7__py3-none-any.whl → 0.2.0a4__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.
Files changed (481) hide show
  1. ccproxy/api/__init__.py +1 -15
  2. ccproxy/api/app.py +434 -219
  3. ccproxy/api/bootstrap.py +30 -0
  4. ccproxy/api/decorators.py +85 -0
  5. ccproxy/api/dependencies.py +144 -168
  6. ccproxy/api/format_validation.py +54 -0
  7. ccproxy/api/middleware/cors.py +6 -3
  8. ccproxy/api/middleware/errors.py +388 -524
  9. ccproxy/api/middleware/hooks.py +563 -0
  10. ccproxy/api/middleware/normalize_headers.py +59 -0
  11. ccproxy/api/middleware/request_id.py +35 -16
  12. ccproxy/api/middleware/streaming_hooks.py +292 -0
  13. ccproxy/api/routes/__init__.py +5 -14
  14. ccproxy/api/routes/health.py +39 -672
  15. ccproxy/api/routes/plugins.py +277 -0
  16. ccproxy/auth/__init__.py +2 -19
  17. ccproxy/auth/bearer.py +25 -15
  18. ccproxy/auth/dependencies.py +123 -157
  19. ccproxy/auth/exceptions.py +0 -12
  20. ccproxy/auth/manager.py +35 -49
  21. ccproxy/auth/managers/__init__.py +10 -0
  22. ccproxy/auth/managers/base.py +523 -0
  23. ccproxy/auth/managers/base_enhanced.py +63 -0
  24. ccproxy/auth/managers/token_snapshot.py +77 -0
  25. ccproxy/auth/models/base.py +65 -0
  26. ccproxy/auth/models/credentials.py +40 -0
  27. ccproxy/auth/oauth/__init__.py +4 -18
  28. ccproxy/auth/oauth/base.py +533 -0
  29. ccproxy/auth/oauth/cli_errors.py +37 -0
  30. ccproxy/auth/oauth/flows.py +430 -0
  31. ccproxy/auth/oauth/protocol.py +366 -0
  32. ccproxy/auth/oauth/registry.py +408 -0
  33. ccproxy/auth/oauth/router.py +396 -0
  34. ccproxy/auth/oauth/routes.py +186 -113
  35. ccproxy/auth/oauth/session.py +151 -0
  36. ccproxy/auth/oauth/templates.py +342 -0
  37. ccproxy/auth/storage/__init__.py +2 -5
  38. ccproxy/auth/storage/base.py +279 -5
  39. ccproxy/auth/storage/generic.py +134 -0
  40. ccproxy/cli/__init__.py +1 -2
  41. ccproxy/cli/_settings_help.py +351 -0
  42. ccproxy/cli/commands/auth.py +1519 -793
  43. ccproxy/cli/commands/config/commands.py +209 -276
  44. ccproxy/cli/commands/plugins.py +669 -0
  45. ccproxy/cli/commands/serve.py +75 -810
  46. ccproxy/cli/commands/status.py +254 -0
  47. ccproxy/cli/decorators.py +83 -0
  48. ccproxy/cli/helpers.py +22 -60
  49. ccproxy/cli/main.py +359 -10
  50. ccproxy/cli/options/claude_options.py +0 -25
  51. ccproxy/config/__init__.py +7 -11
  52. ccproxy/config/core.py +227 -0
  53. ccproxy/config/env_generator.py +232 -0
  54. ccproxy/config/runtime.py +67 -0
  55. ccproxy/config/security.py +36 -3
  56. ccproxy/config/settings.py +382 -441
  57. ccproxy/config/toml_generator.py +299 -0
  58. ccproxy/config/utils.py +452 -0
  59. ccproxy/core/__init__.py +7 -271
  60. ccproxy/{_version.py → core/_version.py} +16 -3
  61. ccproxy/core/async_task_manager.py +516 -0
  62. ccproxy/core/async_utils.py +47 -14
  63. ccproxy/core/auth/__init__.py +6 -0
  64. ccproxy/core/constants.py +16 -50
  65. ccproxy/core/errors.py +53 -0
  66. ccproxy/core/id_utils.py +20 -0
  67. ccproxy/core/interfaces.py +16 -123
  68. ccproxy/core/logging.py +473 -18
  69. ccproxy/core/plugins/__init__.py +77 -0
  70. ccproxy/core/plugins/cli_discovery.py +211 -0
  71. ccproxy/core/plugins/declaration.py +455 -0
  72. ccproxy/core/plugins/discovery.py +604 -0
  73. ccproxy/core/plugins/factories.py +967 -0
  74. ccproxy/core/plugins/hooks/__init__.py +30 -0
  75. ccproxy/core/plugins/hooks/base.py +58 -0
  76. ccproxy/core/plugins/hooks/events.py +46 -0
  77. ccproxy/core/plugins/hooks/implementations/__init__.py +16 -0
  78. ccproxy/core/plugins/hooks/implementations/formatters/__init__.py +11 -0
  79. ccproxy/core/plugins/hooks/implementations/formatters/json.py +552 -0
  80. ccproxy/core/plugins/hooks/implementations/formatters/raw.py +370 -0
  81. ccproxy/core/plugins/hooks/implementations/http_tracer.py +431 -0
  82. ccproxy/core/plugins/hooks/layers.py +44 -0
  83. ccproxy/core/plugins/hooks/manager.py +186 -0
  84. ccproxy/core/plugins/hooks/registry.py +139 -0
  85. ccproxy/core/plugins/hooks/thread_manager.py +203 -0
  86. ccproxy/core/plugins/hooks/types.py +22 -0
  87. ccproxy/core/plugins/interfaces.py +416 -0
  88. ccproxy/core/plugins/loader.py +166 -0
  89. ccproxy/core/plugins/middleware.py +233 -0
  90. ccproxy/core/plugins/models.py +59 -0
  91. ccproxy/core/plugins/protocol.py +180 -0
  92. ccproxy/core/plugins/runtime.py +519 -0
  93. ccproxy/{observability/context.py → core/request_context.py} +137 -94
  94. ccproxy/core/status_report.py +211 -0
  95. ccproxy/core/transformers.py +13 -8
  96. ccproxy/data/claude_headers_fallback.json +540 -19
  97. ccproxy/data/codex_headers_fallback.json +114 -7
  98. ccproxy/http/__init__.py +30 -0
  99. ccproxy/http/base.py +95 -0
  100. ccproxy/http/client.py +323 -0
  101. ccproxy/http/hooks.py +642 -0
  102. ccproxy/http/pool.py +279 -0
  103. ccproxy/llms/formatters/__init__.py +7 -0
  104. ccproxy/llms/formatters/anthropic_to_openai/__init__.py +55 -0
  105. ccproxy/llms/formatters/anthropic_to_openai/errors.py +65 -0
  106. ccproxy/llms/formatters/anthropic_to_openai/requests.py +356 -0
  107. ccproxy/llms/formatters/anthropic_to_openai/responses.py +153 -0
  108. ccproxy/llms/formatters/anthropic_to_openai/streams.py +1546 -0
  109. ccproxy/llms/formatters/base.py +140 -0
  110. ccproxy/llms/formatters/base_model.py +33 -0
  111. ccproxy/llms/formatters/common/__init__.py +51 -0
  112. ccproxy/llms/formatters/common/identifiers.py +48 -0
  113. ccproxy/llms/formatters/common/streams.py +254 -0
  114. ccproxy/llms/formatters/common/thinking.py +74 -0
  115. ccproxy/llms/formatters/common/usage.py +135 -0
  116. ccproxy/llms/formatters/constants.py +55 -0
  117. ccproxy/llms/formatters/context.py +116 -0
  118. ccproxy/llms/formatters/mapping.py +33 -0
  119. ccproxy/llms/formatters/openai_to_anthropic/__init__.py +55 -0
  120. ccproxy/llms/formatters/openai_to_anthropic/_helpers.py +141 -0
  121. ccproxy/llms/formatters/openai_to_anthropic/errors.py +53 -0
  122. ccproxy/llms/formatters/openai_to_anthropic/requests.py +674 -0
  123. ccproxy/llms/formatters/openai_to_anthropic/responses.py +285 -0
  124. ccproxy/llms/formatters/openai_to_anthropic/streams.py +530 -0
  125. ccproxy/llms/formatters/openai_to_openai/__init__.py +53 -0
  126. ccproxy/llms/formatters/openai_to_openai/_helpers.py +325 -0
  127. ccproxy/llms/formatters/openai_to_openai/errors.py +6 -0
  128. ccproxy/llms/formatters/openai_to_openai/requests.py +388 -0
  129. ccproxy/llms/formatters/openai_to_openai/responses.py +594 -0
  130. ccproxy/llms/formatters/openai_to_openai/streams.py +1832 -0
  131. ccproxy/llms/formatters/utils.py +306 -0
  132. ccproxy/llms/models/__init__.py +9 -0
  133. ccproxy/llms/models/anthropic.py +619 -0
  134. ccproxy/llms/models/openai.py +844 -0
  135. ccproxy/llms/streaming/__init__.py +26 -0
  136. ccproxy/llms/streaming/accumulators.py +1074 -0
  137. ccproxy/llms/streaming/formatters.py +251 -0
  138. ccproxy/{adapters/openai/streaming.py → llms/streaming/processors.py} +193 -240
  139. ccproxy/models/__init__.py +8 -159
  140. ccproxy/models/detection.py +92 -193
  141. ccproxy/models/provider.py +75 -0
  142. ccproxy/plugins/access_log/README.md +32 -0
  143. ccproxy/plugins/access_log/__init__.py +20 -0
  144. ccproxy/plugins/access_log/config.py +33 -0
  145. ccproxy/plugins/access_log/formatter.py +126 -0
  146. ccproxy/plugins/access_log/hook.py +763 -0
  147. ccproxy/plugins/access_log/logger.py +254 -0
  148. ccproxy/plugins/access_log/plugin.py +137 -0
  149. ccproxy/plugins/access_log/writer.py +109 -0
  150. ccproxy/plugins/analytics/README.md +24 -0
  151. ccproxy/plugins/analytics/__init__.py +1 -0
  152. ccproxy/plugins/analytics/config.py +5 -0
  153. ccproxy/plugins/analytics/ingest.py +85 -0
  154. ccproxy/plugins/analytics/models.py +97 -0
  155. ccproxy/plugins/analytics/plugin.py +121 -0
  156. ccproxy/plugins/analytics/routes.py +163 -0
  157. ccproxy/plugins/analytics/service.py +284 -0
  158. ccproxy/plugins/claude_api/README.md +29 -0
  159. ccproxy/plugins/claude_api/__init__.py +10 -0
  160. ccproxy/plugins/claude_api/adapter.py +829 -0
  161. ccproxy/plugins/claude_api/config.py +52 -0
  162. ccproxy/plugins/claude_api/detection_service.py +461 -0
  163. ccproxy/plugins/claude_api/health.py +175 -0
  164. ccproxy/plugins/claude_api/hooks.py +284 -0
  165. ccproxy/plugins/claude_api/models.py +256 -0
  166. ccproxy/plugins/claude_api/plugin.py +298 -0
  167. ccproxy/plugins/claude_api/routes.py +118 -0
  168. ccproxy/plugins/claude_api/streaming_metrics.py +68 -0
  169. ccproxy/plugins/claude_api/tasks.py +84 -0
  170. ccproxy/plugins/claude_sdk/README.md +35 -0
  171. ccproxy/plugins/claude_sdk/__init__.py +80 -0
  172. ccproxy/plugins/claude_sdk/adapter.py +749 -0
  173. ccproxy/plugins/claude_sdk/auth.py +57 -0
  174. ccproxy/{claude_sdk → plugins/claude_sdk}/client.py +63 -39
  175. ccproxy/plugins/claude_sdk/config.py +210 -0
  176. ccproxy/{claude_sdk → plugins/claude_sdk}/converter.py +6 -6
  177. ccproxy/plugins/claude_sdk/detection_service.py +163 -0
  178. ccproxy/{services/claude_sdk_service.py → plugins/claude_sdk/handler.py} +123 -304
  179. ccproxy/plugins/claude_sdk/health.py +113 -0
  180. ccproxy/plugins/claude_sdk/hooks.py +115 -0
  181. ccproxy/{claude_sdk → plugins/claude_sdk}/manager.py +42 -32
  182. ccproxy/{claude_sdk → plugins/claude_sdk}/message_queue.py +8 -8
  183. ccproxy/{models/claude_sdk.py → plugins/claude_sdk/models.py} +64 -16
  184. ccproxy/plugins/claude_sdk/options.py +154 -0
  185. ccproxy/{claude_sdk → plugins/claude_sdk}/parser.py +23 -5
  186. ccproxy/plugins/claude_sdk/plugin.py +269 -0
  187. ccproxy/plugins/claude_sdk/routes.py +104 -0
  188. ccproxy/{claude_sdk → plugins/claude_sdk}/session_client.py +124 -12
  189. ccproxy/plugins/claude_sdk/session_pool.py +700 -0
  190. ccproxy/{claude_sdk → plugins/claude_sdk}/stream_handle.py +48 -43
  191. ccproxy/{claude_sdk → plugins/claude_sdk}/stream_worker.py +22 -18
  192. ccproxy/{claude_sdk → plugins/claude_sdk}/streaming.py +50 -16
  193. ccproxy/plugins/claude_sdk/tasks.py +97 -0
  194. ccproxy/plugins/claude_shared/README.md +18 -0
  195. ccproxy/plugins/claude_shared/__init__.py +12 -0
  196. ccproxy/plugins/claude_shared/model_defaults.py +171 -0
  197. ccproxy/plugins/codex/README.md +35 -0
  198. ccproxy/plugins/codex/__init__.py +6 -0
  199. ccproxy/plugins/codex/adapter.py +635 -0
  200. ccproxy/{config/codex.py → plugins/codex/config.py} +78 -12
  201. ccproxy/plugins/codex/detection_service.py +544 -0
  202. ccproxy/plugins/codex/health.py +162 -0
  203. ccproxy/plugins/codex/hooks.py +263 -0
  204. ccproxy/plugins/codex/model_defaults.py +39 -0
  205. ccproxy/plugins/codex/models.py +263 -0
  206. ccproxy/plugins/codex/plugin.py +275 -0
  207. ccproxy/plugins/codex/routes.py +129 -0
  208. ccproxy/plugins/codex/streaming_metrics.py +324 -0
  209. ccproxy/plugins/codex/tasks.py +106 -0
  210. ccproxy/plugins/codex/utils/__init__.py +1 -0
  211. ccproxy/plugins/codex/utils/sse_parser.py +106 -0
  212. ccproxy/plugins/command_replay/README.md +34 -0
  213. ccproxy/plugins/command_replay/__init__.py +17 -0
  214. ccproxy/plugins/command_replay/config.py +133 -0
  215. ccproxy/plugins/command_replay/formatter.py +432 -0
  216. ccproxy/plugins/command_replay/hook.py +294 -0
  217. ccproxy/plugins/command_replay/plugin.py +161 -0
  218. ccproxy/plugins/copilot/README.md +39 -0
  219. ccproxy/plugins/copilot/__init__.py +11 -0
  220. ccproxy/plugins/copilot/adapter.py +465 -0
  221. ccproxy/plugins/copilot/config.py +155 -0
  222. ccproxy/plugins/copilot/data/copilot_fallback.json +41 -0
  223. ccproxy/plugins/copilot/detection_service.py +255 -0
  224. ccproxy/plugins/copilot/manager.py +275 -0
  225. ccproxy/plugins/copilot/model_defaults.py +284 -0
  226. ccproxy/plugins/copilot/models.py +148 -0
  227. ccproxy/plugins/copilot/oauth/__init__.py +16 -0
  228. ccproxy/plugins/copilot/oauth/client.py +494 -0
  229. ccproxy/plugins/copilot/oauth/models.py +385 -0
  230. ccproxy/plugins/copilot/oauth/provider.py +602 -0
  231. ccproxy/plugins/copilot/oauth/storage.py +170 -0
  232. ccproxy/plugins/copilot/plugin.py +360 -0
  233. ccproxy/plugins/copilot/routes.py +294 -0
  234. ccproxy/plugins/credential_balancer/README.md +124 -0
  235. ccproxy/plugins/credential_balancer/__init__.py +6 -0
  236. ccproxy/plugins/credential_balancer/config.py +270 -0
  237. ccproxy/plugins/credential_balancer/factory.py +415 -0
  238. ccproxy/plugins/credential_balancer/hook.py +51 -0
  239. ccproxy/plugins/credential_balancer/manager.py +587 -0
  240. ccproxy/plugins/credential_balancer/plugin.py +146 -0
  241. ccproxy/plugins/dashboard/README.md +25 -0
  242. ccproxy/plugins/dashboard/__init__.py +1 -0
  243. ccproxy/plugins/dashboard/config.py +8 -0
  244. ccproxy/plugins/dashboard/plugin.py +71 -0
  245. ccproxy/plugins/dashboard/routes.py +67 -0
  246. ccproxy/plugins/docker/README.md +32 -0
  247. ccproxy/{docker → plugins/docker}/__init__.py +3 -0
  248. ccproxy/{docker → plugins/docker}/adapter.py +108 -10
  249. ccproxy/plugins/docker/config.py +82 -0
  250. ccproxy/{docker → plugins/docker}/docker_path.py +4 -3
  251. ccproxy/{docker → plugins/docker}/middleware.py +2 -2
  252. ccproxy/plugins/docker/plugin.py +198 -0
  253. ccproxy/{docker → plugins/docker}/stream_process.py +3 -3
  254. ccproxy/plugins/duckdb_storage/README.md +26 -0
  255. ccproxy/plugins/duckdb_storage/__init__.py +1 -0
  256. ccproxy/plugins/duckdb_storage/config.py +22 -0
  257. ccproxy/plugins/duckdb_storage/plugin.py +128 -0
  258. ccproxy/plugins/duckdb_storage/routes.py +51 -0
  259. ccproxy/plugins/duckdb_storage/storage.py +633 -0
  260. ccproxy/plugins/max_tokens/README.md +38 -0
  261. ccproxy/plugins/max_tokens/__init__.py +12 -0
  262. ccproxy/plugins/max_tokens/adapter.py +235 -0
  263. ccproxy/plugins/max_tokens/config.py +86 -0
  264. ccproxy/plugins/max_tokens/models.py +53 -0
  265. ccproxy/plugins/max_tokens/plugin.py +200 -0
  266. ccproxy/plugins/max_tokens/service.py +271 -0
  267. ccproxy/plugins/max_tokens/token_limits.json +54 -0
  268. ccproxy/plugins/metrics/README.md +35 -0
  269. ccproxy/plugins/metrics/__init__.py +10 -0
  270. ccproxy/{observability/metrics.py → plugins/metrics/collector.py} +20 -153
  271. ccproxy/plugins/metrics/config.py +85 -0
  272. ccproxy/plugins/metrics/grafana/dashboards/ccproxy-dashboard.json +1720 -0
  273. ccproxy/plugins/metrics/hook.py +403 -0
  274. ccproxy/plugins/metrics/plugin.py +268 -0
  275. ccproxy/{observability → plugins/metrics}/pushgateway.py +57 -59
  276. ccproxy/plugins/metrics/routes.py +107 -0
  277. ccproxy/plugins/metrics/tasks.py +117 -0
  278. ccproxy/plugins/oauth_claude/README.md +35 -0
  279. ccproxy/plugins/oauth_claude/__init__.py +14 -0
  280. ccproxy/plugins/oauth_claude/client.py +270 -0
  281. ccproxy/plugins/oauth_claude/config.py +84 -0
  282. ccproxy/plugins/oauth_claude/manager.py +482 -0
  283. ccproxy/plugins/oauth_claude/models.py +266 -0
  284. ccproxy/plugins/oauth_claude/plugin.py +149 -0
  285. ccproxy/plugins/oauth_claude/provider.py +571 -0
  286. ccproxy/plugins/oauth_claude/storage.py +212 -0
  287. ccproxy/plugins/oauth_codex/README.md +38 -0
  288. ccproxy/plugins/oauth_codex/__init__.py +14 -0
  289. ccproxy/plugins/oauth_codex/client.py +224 -0
  290. ccproxy/plugins/oauth_codex/config.py +95 -0
  291. ccproxy/plugins/oauth_codex/manager.py +256 -0
  292. ccproxy/plugins/oauth_codex/models.py +239 -0
  293. ccproxy/plugins/oauth_codex/plugin.py +146 -0
  294. ccproxy/plugins/oauth_codex/provider.py +574 -0
  295. ccproxy/plugins/oauth_codex/storage.py +92 -0
  296. ccproxy/plugins/permissions/README.md +28 -0
  297. ccproxy/plugins/permissions/__init__.py +22 -0
  298. ccproxy/plugins/permissions/config.py +28 -0
  299. ccproxy/{cli/commands/permission_handler.py → plugins/permissions/handlers/cli.py} +49 -25
  300. ccproxy/plugins/permissions/handlers/protocol.py +33 -0
  301. ccproxy/plugins/permissions/handlers/terminal.py +675 -0
  302. ccproxy/{api/routes → plugins/permissions}/mcp.py +34 -7
  303. ccproxy/{models/permissions.py → plugins/permissions/models.py} +65 -1
  304. ccproxy/plugins/permissions/plugin.py +153 -0
  305. ccproxy/{api/routes/permissions.py → plugins/permissions/routes.py} +20 -16
  306. ccproxy/{api/services/permission_service.py → plugins/permissions/service.py} +65 -11
  307. ccproxy/{api → plugins/permissions}/ui/permission_handler_protocol.py +1 -1
  308. ccproxy/{api → plugins/permissions}/ui/terminal_permission_handler.py +66 -10
  309. ccproxy/plugins/pricing/README.md +34 -0
  310. ccproxy/plugins/pricing/__init__.py +6 -0
  311. ccproxy/{pricing → plugins/pricing}/cache.py +7 -6
  312. ccproxy/{config/pricing.py → plugins/pricing/config.py} +32 -6
  313. ccproxy/plugins/pricing/exceptions.py +35 -0
  314. ccproxy/plugins/pricing/loader.py +440 -0
  315. ccproxy/{pricing → plugins/pricing}/models.py +13 -23
  316. ccproxy/plugins/pricing/plugin.py +169 -0
  317. ccproxy/plugins/pricing/service.py +191 -0
  318. ccproxy/plugins/pricing/tasks.py +300 -0
  319. ccproxy/{pricing → plugins/pricing}/updater.py +86 -72
  320. ccproxy/plugins/pricing/utils.py +99 -0
  321. ccproxy/plugins/request_tracer/README.md +40 -0
  322. ccproxy/plugins/request_tracer/__init__.py +7 -0
  323. ccproxy/plugins/request_tracer/config.py +120 -0
  324. ccproxy/plugins/request_tracer/hook.py +415 -0
  325. ccproxy/plugins/request_tracer/plugin.py +255 -0
  326. ccproxy/scheduler/__init__.py +2 -14
  327. ccproxy/scheduler/core.py +26 -41
  328. ccproxy/scheduler/manager.py +61 -105
  329. ccproxy/scheduler/registry.py +6 -32
  330. ccproxy/scheduler/tasks.py +268 -276
  331. ccproxy/services/__init__.py +0 -1
  332. ccproxy/services/adapters/__init__.py +11 -0
  333. ccproxy/services/adapters/base.py +123 -0
  334. ccproxy/services/adapters/chain_composer.py +88 -0
  335. ccproxy/services/adapters/chain_validation.py +44 -0
  336. ccproxy/services/adapters/chat_accumulator.py +200 -0
  337. ccproxy/services/adapters/delta_utils.py +142 -0
  338. ccproxy/services/adapters/format_adapter.py +136 -0
  339. ccproxy/services/adapters/format_context.py +11 -0
  340. ccproxy/services/adapters/format_registry.py +158 -0
  341. ccproxy/services/adapters/http_adapter.py +1045 -0
  342. ccproxy/services/adapters/mock_adapter.py +118 -0
  343. ccproxy/services/adapters/protocols.py +35 -0
  344. ccproxy/services/adapters/simple_converters.py +571 -0
  345. ccproxy/services/auth_registry.py +180 -0
  346. ccproxy/services/cache/__init__.py +6 -0
  347. ccproxy/services/cache/response_cache.py +261 -0
  348. ccproxy/services/cli_detection.py +437 -0
  349. ccproxy/services/config/__init__.py +6 -0
  350. ccproxy/services/config/proxy_configuration.py +111 -0
  351. ccproxy/services/container.py +256 -0
  352. ccproxy/services/factories.py +380 -0
  353. ccproxy/services/handler_config.py +76 -0
  354. ccproxy/services/interfaces.py +298 -0
  355. ccproxy/services/mocking/__init__.py +6 -0
  356. ccproxy/services/mocking/mock_handler.py +291 -0
  357. ccproxy/services/tracing/__init__.py +7 -0
  358. ccproxy/services/tracing/interfaces.py +61 -0
  359. ccproxy/services/tracing/null_tracer.py +57 -0
  360. ccproxy/streaming/__init__.py +23 -0
  361. ccproxy/streaming/buffer.py +1056 -0
  362. ccproxy/streaming/deferred.py +897 -0
  363. ccproxy/streaming/handler.py +117 -0
  364. ccproxy/streaming/interfaces.py +77 -0
  365. ccproxy/streaming/simple_adapter.py +39 -0
  366. ccproxy/streaming/sse.py +109 -0
  367. ccproxy/streaming/sse_parser.py +127 -0
  368. ccproxy/templates/__init__.py +6 -0
  369. ccproxy/templates/plugin_scaffold.py +695 -0
  370. ccproxy/testing/endpoints/__init__.py +33 -0
  371. ccproxy/testing/endpoints/cli.py +215 -0
  372. ccproxy/testing/endpoints/config.py +874 -0
  373. ccproxy/testing/endpoints/console.py +57 -0
  374. ccproxy/testing/endpoints/models.py +100 -0
  375. ccproxy/testing/endpoints/runner.py +1903 -0
  376. ccproxy/testing/endpoints/tools.py +308 -0
  377. ccproxy/testing/mock_responses.py +70 -1
  378. ccproxy/testing/response_handlers.py +20 -0
  379. ccproxy/utils/__init__.py +0 -6
  380. ccproxy/utils/binary_resolver.py +476 -0
  381. ccproxy/utils/caching.py +327 -0
  382. ccproxy/utils/cli_logging.py +101 -0
  383. ccproxy/utils/command_line.py +251 -0
  384. ccproxy/utils/headers.py +228 -0
  385. ccproxy/utils/model_mapper.py +120 -0
  386. ccproxy/utils/startup_helpers.py +68 -446
  387. ccproxy/utils/version_checker.py +273 -6
  388. ccproxy_api-0.2.0a4.dist-info/METADATA +212 -0
  389. ccproxy_api-0.2.0a4.dist-info/RECORD +417 -0
  390. {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0a4.dist-info}/WHEEL +1 -1
  391. ccproxy_api-0.2.0a4.dist-info/entry_points.txt +24 -0
  392. ccproxy/__init__.py +0 -4
  393. ccproxy/adapters/__init__.py +0 -11
  394. ccproxy/adapters/base.py +0 -80
  395. ccproxy/adapters/codex/__init__.py +0 -11
  396. ccproxy/adapters/openai/__init__.py +0 -42
  397. ccproxy/adapters/openai/adapter.py +0 -953
  398. ccproxy/adapters/openai/models.py +0 -412
  399. ccproxy/adapters/openai/response_adapter.py +0 -355
  400. ccproxy/adapters/openai/response_models.py +0 -178
  401. ccproxy/api/middleware/headers.py +0 -49
  402. ccproxy/api/middleware/logging.py +0 -180
  403. ccproxy/api/middleware/request_content_logging.py +0 -297
  404. ccproxy/api/middleware/server_header.py +0 -58
  405. ccproxy/api/responses.py +0 -89
  406. ccproxy/api/routes/claude.py +0 -371
  407. ccproxy/api/routes/codex.py +0 -1251
  408. ccproxy/api/routes/metrics.py +0 -1029
  409. ccproxy/api/routes/proxy.py +0 -211
  410. ccproxy/api/services/__init__.py +0 -6
  411. ccproxy/auth/conditional.py +0 -84
  412. ccproxy/auth/credentials_adapter.py +0 -93
  413. ccproxy/auth/models.py +0 -118
  414. ccproxy/auth/oauth/models.py +0 -48
  415. ccproxy/auth/openai/__init__.py +0 -13
  416. ccproxy/auth/openai/credentials.py +0 -166
  417. ccproxy/auth/openai/oauth_client.py +0 -334
  418. ccproxy/auth/openai/storage.py +0 -184
  419. ccproxy/auth/storage/json_file.py +0 -158
  420. ccproxy/auth/storage/keyring.py +0 -189
  421. ccproxy/claude_sdk/__init__.py +0 -18
  422. ccproxy/claude_sdk/options.py +0 -194
  423. ccproxy/claude_sdk/session_pool.py +0 -550
  424. ccproxy/cli/docker/__init__.py +0 -34
  425. ccproxy/cli/docker/adapter_factory.py +0 -157
  426. ccproxy/cli/docker/params.py +0 -274
  427. ccproxy/config/auth.py +0 -153
  428. ccproxy/config/claude.py +0 -348
  429. ccproxy/config/cors.py +0 -79
  430. ccproxy/config/discovery.py +0 -95
  431. ccproxy/config/docker_settings.py +0 -264
  432. ccproxy/config/observability.py +0 -158
  433. ccproxy/config/reverse_proxy.py +0 -31
  434. ccproxy/config/scheduler.py +0 -108
  435. ccproxy/config/server.py +0 -86
  436. ccproxy/config/validators.py +0 -231
  437. ccproxy/core/codex_transformers.py +0 -389
  438. ccproxy/core/http.py +0 -328
  439. ccproxy/core/http_transformers.py +0 -812
  440. ccproxy/core/proxy.py +0 -143
  441. ccproxy/core/validators.py +0 -288
  442. ccproxy/models/errors.py +0 -42
  443. ccproxy/models/messages.py +0 -269
  444. ccproxy/models/requests.py +0 -107
  445. ccproxy/models/responses.py +0 -270
  446. ccproxy/models/types.py +0 -102
  447. ccproxy/observability/__init__.py +0 -51
  448. ccproxy/observability/access_logger.py +0 -457
  449. ccproxy/observability/sse_events.py +0 -303
  450. ccproxy/observability/stats_printer.py +0 -753
  451. ccproxy/observability/storage/__init__.py +0 -1
  452. ccproxy/observability/storage/duckdb_simple.py +0 -677
  453. ccproxy/observability/storage/models.py +0 -70
  454. ccproxy/observability/streaming_response.py +0 -107
  455. ccproxy/pricing/__init__.py +0 -19
  456. ccproxy/pricing/loader.py +0 -251
  457. ccproxy/services/claude_detection_service.py +0 -243
  458. ccproxy/services/codex_detection_service.py +0 -252
  459. ccproxy/services/credentials/__init__.py +0 -55
  460. ccproxy/services/credentials/config.py +0 -105
  461. ccproxy/services/credentials/manager.py +0 -561
  462. ccproxy/services/credentials/oauth_client.py +0 -481
  463. ccproxy/services/proxy_service.py +0 -1827
  464. ccproxy/static/.keep +0 -0
  465. ccproxy/utils/cost_calculator.py +0 -210
  466. ccproxy/utils/disconnection_monitor.py +0 -83
  467. ccproxy/utils/model_mapping.py +0 -199
  468. ccproxy/utils/models_provider.py +0 -150
  469. ccproxy/utils/simple_request_logger.py +0 -284
  470. ccproxy/utils/streaming_metrics.py +0 -199
  471. ccproxy_api-0.1.7.dist-info/METADATA +0 -615
  472. ccproxy_api-0.1.7.dist-info/RECORD +0 -191
  473. ccproxy_api-0.1.7.dist-info/entry_points.txt +0 -4
  474. /ccproxy/{api/middleware/auth.py → auth/models/__init__.py} +0 -0
  475. /ccproxy/{claude_sdk → plugins/claude_sdk}/exceptions.py +0 -0
  476. /ccproxy/{docker → plugins/docker}/models.py +0 -0
  477. /ccproxy/{docker → plugins/docker}/protocol.py +0 -0
  478. /ccproxy/{docker → plugins/docker}/validators.py +0 -0
  479. /ccproxy/{auth/oauth/storage.py → plugins/permissions/handlers/__init__.py} +0 -0
  480. /ccproxy/{api → plugins/permissions}/ui/__init__.py +0 -0
  481. {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0a4.dist-info}/licenses/LICENSE +0 -0
@@ -1,264 +0,0 @@
1
- """Docker settings configuration for CCProxy API."""
2
-
3
- import os
4
-
5
- from pydantic import BaseModel, Field, field_validator, model_validator
6
-
7
- from ccproxy import __version__
8
- from ccproxy.core.async_utils import format_version, get_claude_docker_home_dir
9
-
10
-
11
- # Docker validation functions moved here to avoid utils dependency
12
-
13
-
14
- def validate_host_path(path: str) -> str:
15
- """Validate host path for Docker volume mounting."""
16
- import os
17
- from pathlib import Path
18
-
19
- if not path:
20
- raise ValueError("Path cannot be empty")
21
-
22
- # Expand environment variables and user home directory
23
- expanded_path = os.path.expandvars(str(Path(path).expanduser()))
24
-
25
- # Convert to absolute path and normalize
26
- abs_path = Path(expanded_path).resolve()
27
- return str(abs_path)
28
-
29
-
30
- def validate_volumes_list(volumes: list[str]) -> list[str]:
31
- """Validate Docker volumes list format."""
32
- validated = []
33
-
34
- for volume in volumes:
35
- if not volume:
36
- continue
37
-
38
- # Use validate_volume_format for comprehensive validation
39
- validated_volume = validate_volume_format(volume)
40
- validated.append(validated_volume)
41
-
42
- return validated
43
-
44
-
45
- def validate_volume_format(volume: str) -> str:
46
- """Validate individual Docker volume format.
47
-
48
- Args:
49
- volume: Volume mount string in format 'host:container[:options]'
50
-
51
- Returns:
52
- Validated volume string with normalized host path
53
-
54
- Raises:
55
- ValueError: If volume format is invalid or host path doesn't exist
56
- """
57
- import os
58
- from pathlib import Path
59
-
60
- if not volume:
61
- raise ValueError("Volume cannot be empty")
62
-
63
- # Expected format: "host_path:container_path" or "host_path:container_path:options"
64
- parts = volume.split(":")
65
- if len(parts) < 2:
66
- raise ValueError(
67
- f"Invalid volume format: {volume}. Expected 'host:container' or 'host:container:options'"
68
- )
69
-
70
- host_path = parts[0]
71
- container_path = parts[1]
72
- options = ":".join(parts[2:]) if len(parts) > 2 else ""
73
-
74
- if not host_path or not container_path:
75
- raise ValueError(
76
- f"Invalid volume format: {volume}. Expected 'host:container' or 'host:container:options'"
77
- )
78
-
79
- # Expand environment variables and user home directory
80
- expanded_host_path = os.path.expandvars(str(Path(host_path).expanduser()))
81
-
82
- # Convert to absolute path
83
- abs_host_path = Path(expanded_host_path).resolve()
84
-
85
- # Check if the path exists
86
- if not abs_host_path.exists():
87
- raise ValueError(f"Host path does not exist: {expanded_host_path}")
88
-
89
- # Validate container path (should be absolute)
90
- if not container_path.startswith("/"):
91
- raise ValueError(f"Container path must be absolute: {container_path}")
92
-
93
- # Reconstruct the volume string with normalized host path
94
- result = f"{abs_host_path}:{container_path}"
95
- if options:
96
- result += f":{options}"
97
-
98
- return result
99
-
100
-
101
- def validate_environment_variable(env_var: str) -> tuple[str, str]:
102
- """Validate environment variable format.
103
-
104
- Args:
105
- env_var: Environment variable string in format 'KEY=VALUE'
106
-
107
- Returns:
108
- Tuple of (key, value)
109
-
110
- Raises:
111
- ValueError: If environment variable format is invalid
112
- """
113
- if not env_var:
114
- raise ValueError("Environment variable cannot be empty")
115
-
116
- if "=" not in env_var:
117
- raise ValueError(
118
- f"Invalid environment variable format: {env_var}. Expected KEY=VALUE format"
119
- )
120
-
121
- # Split on first equals sign only (value may contain equals)
122
- key, value = env_var.split("=", 1)
123
-
124
- if not key:
125
- raise ValueError(
126
- f"Invalid environment variable format: {env_var}. Expected KEY=VALUE format"
127
- )
128
-
129
- return key, value
130
-
131
-
132
- def validate_docker_volumes(volumes: list[str]) -> list[str]:
133
- """Validate Docker volumes list format.
134
-
135
- Args:
136
- volumes: List of volume mount strings
137
-
138
- Returns:
139
- List of validated volume strings with normalized host paths
140
-
141
- Raises:
142
- ValueError: If any volume format is invalid
143
- """
144
- validated = []
145
-
146
- for volume in volumes:
147
- if not volume:
148
- continue
149
-
150
- validated_volume = validate_volume_format(volume)
151
- validated.append(validated_volume)
152
-
153
- return validated
154
-
155
-
156
- class DockerSettings(BaseModel):
157
- """Docker configuration settings for running Claude commands in containers."""
158
-
159
- docker_image: str = Field(
160
- default=f"ghcr.io/caddyglow/ccproxy-api:{format_version(__version__, level='docker')}",
161
- description="Docker image to use for Claude commands",
162
- )
163
-
164
- docker_volumes: list[str] = Field(
165
- default_factory=list,
166
- description="List of volume mounts in 'host:container[:options]' format",
167
- )
168
-
169
- docker_environment: dict[str, str] = Field(
170
- default_factory=dict,
171
- description="Environment variables to pass to Docker container",
172
- )
173
-
174
- docker_additional_args: list[str] = Field(
175
- default_factory=list,
176
- description="Additional arguments to pass to docker run command",
177
- )
178
-
179
- docker_home_directory: str | None = Field(
180
- default=None,
181
- description="Local host directory to mount as the home directory in container",
182
- )
183
-
184
- docker_workspace_directory: str | None = Field(
185
- default=None,
186
- description="Local host directory to mount as the workspace directory in container",
187
- )
188
-
189
- user_mapping_enabled: bool = Field(
190
- default=True,
191
- description="Enable/disable UID/GID mapping for container user",
192
- )
193
-
194
- user_uid: int | None = Field(
195
- default=None,
196
- description="User ID to run container as (auto-detect current user if None)",
197
- ge=0,
198
- )
199
-
200
- user_gid: int | None = Field(
201
- default=None,
202
- description="Group ID to run container as (auto-detect current user if None)",
203
- ge=0,
204
- )
205
-
206
- @field_validator("docker_volumes")
207
- @classmethod
208
- def validate_docker_volumes(cls, v: list[str]) -> list[str]:
209
- """Validate Docker volume mount format."""
210
- return validate_volumes_list(v)
211
-
212
- @field_validator("docker_home_directory")
213
- @classmethod
214
- def validate_docker_home_directory(cls, v: str | None) -> str | None:
215
- """Validate and normalize Docker home directory (host path)."""
216
- if v is None:
217
- return None
218
- return validate_host_path(v)
219
-
220
- @field_validator("docker_workspace_directory")
221
- @classmethod
222
- def validate_docker_workspace_directory(cls, v: str | None) -> str | None:
223
- """Validate and normalize Docker workspace directory (host path)."""
224
- if v is None:
225
- return None
226
- return validate_host_path(v)
227
-
228
- @model_validator(mode="after")
229
- def setup_docker_configuration(self) -> "DockerSettings":
230
- """Set up Docker volumes and user mapping configuration."""
231
- # Set up Docker volumes based on home and workspace directories
232
- if (
233
- not self.docker_volumes
234
- and not self.docker_home_directory
235
- and not self.docker_workspace_directory
236
- ):
237
- # Use XDG config directory for Claude CLI data
238
- claude_config_dir = get_claude_docker_home_dir()
239
- home_host_path = str(claude_config_dir)
240
- workspace_host_path = os.path.expandvars("$PWD")
241
-
242
- self.docker_volumes = [
243
- f"{home_host_path}:/data/home",
244
- f"{workspace_host_path}:/data/workspace",
245
- ]
246
-
247
- # Update environment variables to point to container paths
248
- if "CLAUDE_HOME" not in self.docker_environment:
249
- self.docker_environment["CLAUDE_HOME"] = "/data/home"
250
- if "CLAUDE_WORKSPACE" not in self.docker_environment:
251
- self.docker_environment["CLAUDE_WORKSPACE"] = "/data/workspace"
252
-
253
- # Set up user mapping with auto-detection if enabled but not configured
254
- if self.user_mapping_enabled and os.name == "posix":
255
- # Auto-detect current user UID/GID if not explicitly set
256
- if self.user_uid is None:
257
- self.user_uid = os.getuid()
258
- if self.user_gid is None:
259
- self.user_gid = os.getgid()
260
- elif self.user_mapping_enabled and os.name != "posix":
261
- # Disable user mapping on non-Unix systems (Windows)
262
- self.user_mapping_enabled = False
263
-
264
- return self
@@ -1,158 +0,0 @@
1
- """Observability configuration settings."""
2
-
3
- from __future__ import annotations
4
-
5
- import os
6
- from pathlib import Path
7
- from typing import Literal
8
-
9
- from pydantic import BaseModel, Field, field_validator, model_validator
10
-
11
-
12
- class ObservabilitySettings(BaseModel):
13
- """Observability configuration settings."""
14
-
15
- # Endpoint Controls
16
- metrics_endpoint_enabled: bool = Field(
17
- default=False,
18
- description="Enable Prometheus /metrics endpoint",
19
- )
20
-
21
- logs_endpoints_enabled: bool = Field(
22
- default=False,
23
- description="Enable logs query/analytics/streaming endpoints (/logs/*)",
24
- )
25
-
26
- dashboard_enabled: bool = Field(
27
- default=False,
28
- description="Enable metrics dashboard endpoint (/dashboard)",
29
- )
30
-
31
- # Data Collection & Storage
32
- logs_collection_enabled: bool = Field(
33
- default=False,
34
- description="Enable collection of request/response logs to storage backend",
35
- )
36
-
37
- log_storage_backend: Literal["duckdb", "none"] = Field(
38
- default="duckdb",
39
- description="Storage backend for logs ('duckdb' or 'none')",
40
- )
41
-
42
- # Storage Configuration
43
- duckdb_path: str = Field(
44
- default_factory=lambda: str(
45
- Path(os.environ.get("XDG_DATA_HOME", Path.home() / ".local" / "share"))
46
- / "ccproxy"
47
- / "metrics.duckdb"
48
- ),
49
- description="Path to DuckDB database file",
50
- )
51
-
52
- # Pushgateway Configuration
53
- pushgateway_url: str | None = Field(
54
- default=None,
55
- description="Pushgateway URL (e.g., http://pushgateway:9091)",
56
- )
57
-
58
- pushgateway_job: str = Field(
59
- default="ccproxy",
60
- description="Job name for Pushgateway metrics",
61
- )
62
-
63
- # Stats printing configuration
64
- stats_printing_format: str = Field(
65
- default="console",
66
- description="Format for stats output: 'console', 'rich', 'log', 'json'",
67
- )
68
-
69
- # Enhanced logging integration
70
- logging_pipeline_enabled: bool = Field(
71
- default=True,
72
- description="Enable structlog pipeline integration for observability",
73
- )
74
-
75
- logging_format: str = Field(
76
- default="auto",
77
- description="Logging format for observability: 'rich', 'json', 'auto' (auto-detects based on environment)",
78
- )
79
-
80
- @model_validator(mode="after")
81
- def check_feature_dependencies(self) -> ObservabilitySettings:
82
- """Validate feature dependencies to prevent invalid configurations."""
83
- # Dashboard requires logs endpoints (functional dependency)
84
- if self.dashboard_enabled and not self.logs_endpoints_enabled:
85
- raise ValueError(
86
- "Cannot enable 'dashboard_enabled' without 'logs_endpoints_enabled'. "
87
- "Dashboard needs logs API to function."
88
- )
89
-
90
- # Logs endpoints require storage to query from
91
- if self.logs_endpoints_enabled and self.log_storage_backend == "none":
92
- raise ValueError(
93
- "Cannot enable 'logs_endpoints_enabled' when 'log_storage_backend' is 'none'. "
94
- "Logs endpoints need storage backend to query data."
95
- )
96
-
97
- # Log collection requires storage to write to
98
- if self.logs_collection_enabled and self.log_storage_backend == "none":
99
- raise ValueError(
100
- "Cannot enable 'logs_collection_enabled' when 'log_storage_backend' is 'none'. "
101
- "Collection needs storage backend to persist data."
102
- )
103
-
104
- return self
105
-
106
- @field_validator("stats_printing_format")
107
- @classmethod
108
- def validate_stats_printing_format(cls, v: str) -> str:
109
- """Validate and normalize stats printing format."""
110
- lower_v = v.lower()
111
- valid_formats = ["console", "rich", "log", "json"]
112
- if lower_v not in valid_formats:
113
- raise ValueError(
114
- f"Invalid stats printing format: {v}. Must be one of {valid_formats}"
115
- )
116
- return lower_v
117
-
118
- @field_validator("logging_format")
119
- @classmethod
120
- def validate_logging_format(cls, v: str) -> str:
121
- """Validate and normalize logging format."""
122
- lower_v = v.lower()
123
- valid_formats = ["auto", "rich", "json", "plain"]
124
- if lower_v not in valid_formats:
125
- raise ValueError(
126
- f"Invalid logging format: {v}. Must be one of {valid_formats}"
127
- )
128
- return lower_v
129
-
130
- @property
131
- def needs_storage_backend(self) -> bool:
132
- """Check if any feature requires storage backend initialization."""
133
- return self.logs_endpoints_enabled or self.logs_collection_enabled
134
-
135
- @property
136
- def any_endpoint_enabled(self) -> bool:
137
- """Check if any observability endpoint is enabled."""
138
- return (
139
- self.metrics_endpoint_enabled
140
- or self.logs_endpoints_enabled
141
- or self.dashboard_enabled
142
- )
143
-
144
- # Backward compatibility properties
145
- @property
146
- def metrics_enabled(self) -> bool:
147
- """Backward compatibility: True if any metrics feature is enabled."""
148
- return self.any_endpoint_enabled
149
-
150
- @property
151
- def duckdb_enabled(self) -> bool:
152
- """Backward compatibility: True if DuckDB storage backend is selected."""
153
- return self.log_storage_backend == "duckdb"
154
-
155
- @property
156
- def enabled(self) -> bool:
157
- """Check if observability is enabled (backward compatibility property)."""
158
- return self.any_endpoint_enabled or self.logging_pipeline_enabled
@@ -1,31 +0,0 @@
1
- """Reverse proxy configuration settings."""
2
-
3
- from typing import Literal
4
-
5
- from pydantic import BaseModel, Field
6
-
7
-
8
- class ReverseProxySettings(BaseModel):
9
- """Reverse proxy configuration settings."""
10
-
11
- target_url: str = Field(
12
- default="https://api.anthropic.com",
13
- description="Target URL for reverse proxy requests",
14
- )
15
-
16
- timeout: float = Field(
17
- default=120.0,
18
- description="Timeout for reverse proxy requests in seconds",
19
- ge=1.0,
20
- le=600.0,
21
- )
22
-
23
- default_mode: Literal["claude_code", "full", "minimal"] = Field(
24
- default="claude_code",
25
- description="Default transformation mode for root path reverse proxy, over claude code or auth injection with full",
26
- )
27
-
28
- claude_code_prefix: str = Field(
29
- default="/cc",
30
- description="URL prefix for Claude Code SDK endpoints",
31
- )
@@ -1,108 +0,0 @@
1
- """Scheduler configuration settings."""
2
-
3
- from pydantic import Field
4
- from pydantic_settings import BaseSettings, SettingsConfigDict
5
-
6
-
7
- class SchedulerSettings(BaseSettings):
8
- """
9
- Configuration settings for the unified scheduler system.
10
-
11
- Controls global scheduler behavior and individual task configurations.
12
- Settings can be configured via environment variables with SCHEDULER__ prefix.
13
- """
14
-
15
- # Global scheduler settings
16
- enabled: bool = Field(
17
- default=True,
18
- description="Whether the scheduler system is enabled",
19
- )
20
-
21
- max_concurrent_tasks: int = Field(
22
- default=10,
23
- ge=1,
24
- le=100,
25
- description="Maximum number of tasks that can run concurrently",
26
- )
27
-
28
- graceful_shutdown_timeout: float = Field(
29
- default=30.0,
30
- ge=1.0,
31
- le=300.0,
32
- description="Timeout in seconds for graceful task shutdown",
33
- )
34
-
35
- # Pricing updater task settings
36
- pricing_update_enabled: bool = Field(
37
- default=True,
38
- description="Whether pricing cache update task is enabled. Enabled by default for privacy - downloads from GitHub when enabled",
39
- )
40
-
41
- pricing_update_interval_hours: int = Field(
42
- default=24,
43
- ge=1,
44
- le=168, # Max 1 week
45
- description="Interval in hours between pricing cache updates",
46
- )
47
-
48
- pricing_force_refresh_on_startup: bool = Field(
49
- default=False,
50
- description="Whether to force pricing refresh immediately on startup",
51
- )
52
-
53
- # Observability tasks (migrated from ObservabilitySettings)
54
- pushgateway_enabled: bool = Field(
55
- default=False,
56
- description="Whether pushgateway metrics pushing task is enabled",
57
- )
58
-
59
- pushgateway_interval_seconds: float = Field(
60
- default=60.0,
61
- ge=1.0,
62
- le=3600.0, # Max 1 hour
63
- description="Interval in seconds between pushgateway metric pushes",
64
- )
65
-
66
- pushgateway_max_backoff_seconds: float = Field(
67
- default=300.0,
68
- ge=1.0,
69
- le=1800.0, # Max 30 minutes
70
- description="Maximum backoff delay for failed pushgateway operations",
71
- )
72
-
73
- stats_printing_enabled: bool = Field(
74
- default=False,
75
- description="Whether stats printing task is enabled",
76
- )
77
-
78
- stats_printing_interval_seconds: float = Field(
79
- default=300.0,
80
- ge=1.0,
81
- le=3600.0, # Max 1 hour
82
- description="Interval in seconds between stats printing",
83
- )
84
-
85
- # Version checking task settings
86
- version_check_enabled: bool = Field(
87
- default=True,
88
- description="Whether version update checking is enabled. Enabled by default for privacy - checks GitHub API when enabled",
89
- )
90
-
91
- version_check_interval_hours: int = Field(
92
- default=6,
93
- ge=1,
94
- le=168, # Max 1 week
95
- description="Interval in hours between version checks",
96
- )
97
-
98
- version_check_cache_ttl_hours: float = Field(
99
- default=6,
100
- ge=0.1,
101
- le=24.0,
102
- description="Maximum age in hours since last check version check",
103
- )
104
-
105
- model_config = SettingsConfigDict(
106
- env_prefix="SCHEDULER__",
107
- case_sensitive=False,
108
- )
ccproxy/config/server.py DELETED
@@ -1,86 +0,0 @@
1
- """Server configuration settings."""
2
-
3
- from pydantic import BaseModel, Field, field_validator
4
-
5
-
6
- class ServerSettings(BaseModel):
7
- """Server-specific configuration settings."""
8
-
9
- host: str = Field(
10
- default="127.0.0.1",
11
- description="Server host address",
12
- )
13
-
14
- port: int = Field(
15
- default=8000,
16
- description="Server port number",
17
- ge=1,
18
- le=65535,
19
- )
20
-
21
- workers: int = Field(
22
- default=1,
23
- description="Number of worker processes",
24
- ge=1,
25
- le=32,
26
- )
27
-
28
- reload: bool = Field(
29
- default=False,
30
- description="Enable auto-reload for development",
31
- )
32
-
33
- log_level: str = Field(
34
- default="INFO",
35
- description="Logging level",
36
- )
37
-
38
- log_format: str = Field(
39
- default="auto",
40
- description="Logging output format: 'rich' for development, 'json' for production, 'auto' for automatic selection",
41
- )
42
-
43
- log_show_path: bool = Field(
44
- default=False,
45
- description="Whether to show module path in logs (automatically enabled for DEBUG level)",
46
- )
47
-
48
- log_show_time: bool = Field(
49
- default=True,
50
- description="Whether to show timestamps in logs",
51
- )
52
-
53
- log_console_width: int | None = Field(
54
- default=None,
55
- description="Optional console width override for Rich output",
56
- )
57
-
58
- log_file: str | None = Field(
59
- default=None,
60
- description="Path to JSON log file. If specified, logs will be written to this file in JSON format",
61
- )
62
-
63
- use_terminal_permission_handler: bool = Field(
64
- default=False,
65
- description="Enable terminal UI for permission prompts. Set to False to use external handler via SSE (not implemented)",
66
- )
67
-
68
- @field_validator("log_level")
69
- @classmethod
70
- def validate_log_level(cls, v: str) -> str:
71
- """Validate and normalize log level."""
72
- upper_v = v.upper()
73
- valid_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
74
- if upper_v not in valid_levels:
75
- raise ValueError(f"Invalid log level: {v}. Must be one of {valid_levels}")
76
- return upper_v
77
-
78
- @field_validator("log_format")
79
- @classmethod
80
- def validate_log_format(cls, v: str) -> str:
81
- """Validate and normalize log format."""
82
- lower_v = v.lower()
83
- valid_formats = ["auto", "rich", "json", "plain"]
84
- if lower_v not in valid_formats:
85
- raise ValueError(f"Invalid log format: {v}. Must be one of {valid_formats}")
86
- return lower_v