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.
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.0.dist-info/METADATA +212 -0
  389. ccproxy_api-0.2.0.dist-info/RECORD +417 -0
  390. {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0.dist-info}/WHEEL +1 -1
  391. ccproxy_api-0.2.0.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.0.dist-info}/licenses/LICENSE +0 -0
ccproxy/scheduler/core.py CHANGED
@@ -11,7 +11,7 @@ from .errors import (
11
11
  TaskNotFoundError,
12
12
  TaskRegistrationError,
13
13
  )
14
- from .registry import TaskRegistry, get_task_registry
14
+ from .registry import TaskRegistry
15
15
  from .tasks import BaseScheduledTask
16
16
 
17
17
 
@@ -31,9 +31,9 @@ class Scheduler:
31
31
 
32
32
  def __init__(
33
33
  self,
34
+ task_registry: TaskRegistry,
34
35
  max_concurrent_tasks: int = 10,
35
36
  graceful_shutdown_timeout: float = 30.0,
36
- task_registry: TaskRegistry | None = None,
37
37
  ):
38
38
  """
39
39
  Initialize the scheduler.
@@ -41,11 +41,11 @@ class Scheduler:
41
41
  Args:
42
42
  max_concurrent_tasks: Maximum number of tasks to run concurrently
43
43
  graceful_shutdown_timeout: Timeout for graceful shutdown in seconds
44
- task_registry: Task registry instance (uses global if None)
44
+ task_registry: Task registry instance (required)
45
45
  """
46
46
  self.max_concurrent_tasks = max_concurrent_tasks
47
47
  self.graceful_shutdown_timeout = graceful_shutdown_timeout
48
- self.task_registry = task_registry or get_task_registry()
48
+ self.task_registry = task_registry
49
49
 
50
50
  self._running = False
51
51
  self._tasks: dict[str, BaseScheduledTask] = {}
@@ -63,7 +63,7 @@ class Scheduler:
63
63
  logger.debug(
64
64
  "scheduler_starting",
65
65
  max_concurrent_tasks=self.max_concurrent_tasks,
66
- registered_tasks=self.task_registry.list_tasks(),
66
+ registered_tasks=self.task_registry.list(),
67
67
  )
68
68
 
69
69
  try:
@@ -81,6 +81,7 @@ class Scheduler:
81
81
  "scheduler_start_failed",
82
82
  error=str(e),
83
83
  error_type=type(e).__name__,
84
+ exc_info=e,
84
85
  )
85
86
  raise SchedulerError(f"Failed to start scheduler: {e}") from e
86
87
 
@@ -90,7 +91,7 @@ class Scheduler:
90
91
  return
91
92
 
92
93
  self._running = False
93
- logger.info("scheduler_stopping", active_tasks=len(self._tasks))
94
+ logger.debug("scheduler_stopping", active_tasks=len(self._tasks))
94
95
 
95
96
  # Stop all tasks
96
97
  stop_tasks = []
@@ -106,7 +107,7 @@ class Scheduler:
106
107
  asyncio.gather(*stop_tasks, return_exceptions=True),
107
108
  timeout=self.graceful_shutdown_timeout,
108
109
  )
109
- logger.info("scheduler_stopped_gracefully")
110
+ logger.debug("scheduler_stopped_gracefully")
110
111
  except TimeoutError:
111
112
  logger.warning(
112
113
  "scheduler_shutdown_timeout",
@@ -123,6 +124,7 @@ class Scheduler:
123
124
  "scheduler_shutdown_error",
124
125
  error=str(e),
125
126
  error_type=type(e).__name__,
127
+ exc_info=e,
126
128
  )
127
129
  raise SchedulerShutdownError(
128
130
  f"Error during scheduler shutdown: {e}"
@@ -152,7 +154,7 @@ class Scheduler:
152
154
  if task_name in self._tasks:
153
155
  raise SchedulerError(f"Task '{task_name}' already exists")
154
156
 
155
- if not self.task_registry.is_registered(task_type):
157
+ if not self.task_registry.has(task_type):
156
158
  raise TaskRegistrationError(f"Task type '{task_type}' is not registered")
157
159
 
158
160
  try:
@@ -160,6 +162,18 @@ class Scheduler:
160
162
  task_class = self.task_registry.get(task_type)
161
163
  task_instance = task_class(name=task_name, **task_kwargs)
162
164
 
165
+ interval_value = task_kwargs.get("interval_seconds")
166
+ if interval_value is not None:
167
+ try:
168
+ task_instance.interval_seconds = max(1.0, float(interval_value))
169
+ except (TypeError, ValueError):
170
+ logger.warning(
171
+ "task_interval_invalid",
172
+ task_name=task_name,
173
+ task_type=task_type,
174
+ interval_value=interval_value,
175
+ )
176
+
163
177
  # Add to our tasks dict
164
178
  self._tasks[task_name] = task_instance
165
179
 
@@ -191,6 +205,7 @@ class Scheduler:
191
205
  task_type=task_type,
192
206
  error=str(e),
193
207
  error_type=type(e).__name__,
208
+ exc_info=e,
194
209
  )
195
210
  raise SchedulerError(f"Failed to add task '{task_name}': {e}") from e
196
211
 
@@ -222,6 +237,7 @@ class Scheduler:
222
237
  task_name=task_name,
223
238
  error=str(e),
224
239
  error_type=type(e).__name__,
240
+ exc_info=e,
225
241
  )
226
242
  raise SchedulerError(f"Failed to remove task '{task_name}': {e}") from e
227
243
 
@@ -287,7 +303,7 @@ class Scheduler:
287
303
  "graceful_shutdown_timeout": self.graceful_shutdown_timeout,
288
304
  "task_names": list(self._tasks.keys()),
289
305
  "running_task_names": running_tasks,
290
- "registered_task_types": self.task_registry.list_tasks(),
306
+ "registered_task_types": self.task_registry.list(),
291
307
  }
292
308
 
293
309
  @property
@@ -301,35 +317,4 @@ class Scheduler:
301
317
  return len(self._tasks)
302
318
 
303
319
 
304
- # Global scheduler instance
305
- _global_scheduler: Scheduler | None = None
306
-
307
-
308
- async def get_scheduler() -> Scheduler:
309
- """
310
- Get or create the global scheduler instance.
311
-
312
- Returns:
313
- Global Scheduler instance
314
- """
315
- global _global_scheduler
316
-
317
- if _global_scheduler is None:
318
- _global_scheduler = Scheduler()
319
-
320
- return _global_scheduler
321
-
322
-
323
- async def start_scheduler() -> None:
324
- """Start the global scheduler."""
325
- scheduler = await get_scheduler()
326
- await scheduler.start()
327
-
328
-
329
- async def stop_scheduler() -> None:
330
- """Stop the global scheduler."""
331
- global _global_scheduler
332
-
333
- if _global_scheduler:
334
- await _global_scheduler.stop()
335
- _global_scheduler = None
320
+ # Global scheduler helpers omitted.
@@ -3,16 +3,12 @@
3
3
  import structlog
4
4
 
5
5
  from ccproxy.config.settings import Settings
6
+ from ccproxy.services.container import ServiceContainer
6
7
 
7
8
  from .core import Scheduler
8
- from .registry import register_task
9
- from .tasks import (
10
- PoolStatsTask,
11
- PricingCacheUpdateTask,
12
- PushgatewayTask,
13
- StatsPrintingTask,
14
- VersionUpdateCheckTask,
15
- )
9
+ from .errors import SchedulerError, TaskRegistrationError
10
+ from .registry import TaskRegistry
11
+ from .tasks import PoolStatsTask, VersionUpdateCheckTask
16
12
 
17
13
 
18
14
  logger = structlog.get_logger(__name__)
@@ -29,11 +25,11 @@ async def setup_scheduler_tasks(scheduler: Scheduler, settings: Settings) -> Non
29
25
  scheduler_config = settings.scheduler
30
26
 
31
27
  if not scheduler_config.enabled:
32
- logger.info("scheduler_disabled")
28
+ logger.debug("scheduler_disabled")
33
29
  return
34
30
 
35
31
  # Log network features status
36
- logger.info(
32
+ logger.debug(
37
33
  "network_features_status",
38
34
  pricing_updates_enabled=scheduler_config.pricing_update_enabled,
39
35
  version_check_enabled=scheduler_config.version_check_enabled,
@@ -45,71 +41,21 @@ async def setup_scheduler_tasks(scheduler: Scheduler, settings: Settings) -> Non
45
41
  ),
46
42
  )
47
43
 
48
- # Add pushgateway task if enabled
49
- if scheduler_config.pushgateway_enabled:
50
- try:
51
- await scheduler.add_task(
52
- task_name="pushgateway",
53
- task_type="pushgateway",
54
- interval_seconds=scheduler_config.pushgateway_interval_seconds,
55
- enabled=True,
56
- max_backoff_seconds=scheduler_config.pushgateway_max_backoff_seconds,
57
- )
58
- logger.info(
59
- "pushgateway_task_added",
60
- interval_seconds=scheduler_config.pushgateway_interval_seconds,
61
- )
62
- except Exception as e:
63
- logger.error(
64
- "pushgateway_task_add_failed",
65
- error=str(e),
66
- error_type=type(e).__name__,
67
- )
68
-
69
- # Add stats printing task if enabled
70
- if scheduler_config.stats_printing_enabled:
71
- try:
72
- await scheduler.add_task(
73
- task_name="stats_printing",
74
- task_type="stats_printing",
75
- interval_seconds=scheduler_config.stats_printing_interval_seconds,
76
- enabled=True,
77
- )
78
- logger.info(
79
- "stats_printing_task_added",
80
- interval_seconds=scheduler_config.stats_printing_interval_seconds,
81
- )
82
- except Exception as e:
83
- logger.error(
84
- "stats_printing_task_add_failed",
85
- error=str(e),
86
- error_type=type(e).__name__,
87
- )
44
+ if (
45
+ hasattr(scheduler_config, "stats_printing_enabled")
46
+ and scheduler_config.stats_printing_enabled
47
+ ):
48
+ logger.debug(
49
+ "stats_printing_task_skipped",
50
+ message="Stats printing is handled by plugin",
51
+ )
88
52
 
89
- # Add pricing cache update task if enabled
90
53
  if scheduler_config.pricing_update_enabled:
91
- try:
92
- # Convert hours to seconds
93
- interval_seconds = scheduler_config.pricing_update_interval_hours * 3600
94
-
95
- await scheduler.add_task(
96
- task_name="pricing_cache_update",
97
- task_type="pricing_cache_update",
98
- interval_seconds=interval_seconds,
99
- enabled=True,
100
- force_refresh_on_startup=scheduler_config.pricing_force_refresh_on_startup,
101
- )
102
- logger.debug(
103
- "pricing_update_task_added",
104
- interval_hours=scheduler_config.pricing_update_interval_hours,
105
- force_refresh_on_startup=scheduler_config.pricing_force_refresh_on_startup,
106
- )
107
- except Exception as e:
108
- logger.error(
109
- "pricing_update_task_add_failed",
110
- error=str(e),
111
- error_type=type(e).__name__,
112
- )
54
+ logger.debug(
55
+ "pricing_update_task_handled_by_plugin",
56
+ message="Pricing updates managed by plugin",
57
+ interval_hours=scheduler_config.pricing_update_interval_hours,
58
+ )
113
59
 
114
60
  # Add version update check task if enabled
115
61
  if scheduler_config.version_check_enabled:
@@ -129,43 +75,36 @@ async def setup_scheduler_tasks(scheduler: Scheduler, settings: Settings) -> Non
129
75
  interval_hours=scheduler_config.version_check_interval_hours,
130
76
  version_check_cache_ttl_hours=scheduler_config.version_check_cache_ttl_hours,
131
77
  )
78
+ except TaskRegistrationError as e:
79
+ logger.error(
80
+ "version_check_task_registration_failed",
81
+ error=str(e),
82
+ error_type=type(e).__name__,
83
+ exc_info=e,
84
+ )
132
85
  except Exception as e:
133
86
  logger.error(
134
87
  "version_check_task_add_failed",
135
88
  error=str(e),
136
89
  error_type=type(e).__name__,
90
+ exc_info=e,
137
91
  )
138
92
 
139
93
 
140
- def _register_default_tasks(settings: Settings) -> None:
94
+ def _register_default_tasks(registry: TaskRegistry, settings: Settings) -> None:
141
95
  """Register default task types in the global registry based on configuration."""
142
- from .registry import get_task_registry
143
-
144
- registry = get_task_registry()
145
- scheduler_config = settings.scheduler
146
-
147
- # Only register pushgateway task if enabled
148
- if scheduler_config.pushgateway_enabled and not registry.is_registered(
149
- "pushgateway"
150
- ):
151
- register_task("pushgateway", PushgatewayTask)
152
-
153
- # Only register stats printing task if enabled
154
- if scheduler_config.stats_printing_enabled and not registry.is_registered(
155
- "stats_printing"
156
- ):
157
- register_task("stats_printing", StatsPrintingTask)
96
+ # Registry is provided by DI
158
97
 
159
98
  # Always register core tasks (not metrics-related)
160
- if not registry.is_registered("pricing_cache_update"):
161
- register_task("pricing_cache_update", PricingCacheUpdateTask)
162
- if not registry.is_registered("version_update_check"):
163
- register_task("version_update_check", VersionUpdateCheckTask)
164
- if not registry.is_registered("pool_stats"):
165
- register_task("pool_stats", PoolStatsTask)
99
+ if not registry.has("version_update_check"):
100
+ registry.register("version_update_check", VersionUpdateCheckTask)
101
+ if not registry.has("pool_stats"):
102
+ registry.register("pool_stats", PoolStatsTask)
166
103
 
167
104
 
168
- async def start_scheduler(settings: Settings) -> Scheduler | None:
105
+ async def start_scheduler(
106
+ settings: Settings, container: ServiceContainer
107
+ ) -> Scheduler | None:
169
108
  """
170
109
  Start the scheduler with configured tasks.
171
110
 
@@ -180,13 +119,15 @@ async def start_scheduler(settings: Settings) -> Scheduler | None:
180
119
  logger.info("scheduler_disabled")
181
120
  return None
182
121
 
183
- # Register task types (only when actually starting scheduler)
184
- _register_default_tasks(settings)
122
+ # Resolve registry from DI and register task types
123
+ registry = container.get_task_registry()
124
+ _register_default_tasks(registry, settings)
185
125
 
186
126
  # Create scheduler with settings
187
127
  scheduler = Scheduler(
188
128
  max_concurrent_tasks=settings.scheduler.max_concurrent_tasks,
189
129
  graceful_shutdown_timeout=settings.scheduler.graceful_shutdown_timeout,
130
+ task_registry=registry,
190
131
  )
191
132
 
192
133
  # Start the scheduler
@@ -195,26 +136,33 @@ async def start_scheduler(settings: Settings) -> Scheduler | None:
195
136
  # Setup tasks based on configuration
196
137
  await setup_scheduler_tasks(scheduler, settings)
197
138
 
198
- logger.info(
139
+ task_names = scheduler.list_tasks()
140
+ logger.debug(
199
141
  "scheduler_started",
200
142
  max_concurrent_tasks=settings.scheduler.max_concurrent_tasks,
201
143
  active_tasks=scheduler.task_count,
202
144
  running_tasks=len(
203
- [
204
- name
205
- for name in scheduler.list_tasks()
206
- if scheduler.get_task(name).is_running
207
- ]
145
+ [name for name in task_names if scheduler.get_task(name).is_running]
208
146
  ),
147
+ names=task_names,
209
148
  )
210
149
 
211
150
  return scheduler
212
151
 
152
+ except SchedulerError as e:
153
+ logger.error(
154
+ "scheduler_start_scheduler_error",
155
+ error=str(e),
156
+ error_type=type(e).__name__,
157
+ exc_info=e,
158
+ )
159
+ return None
213
160
  except Exception as e:
214
161
  logger.error(
215
162
  "scheduler_start_failed",
216
163
  error=str(e),
217
164
  error_type=type(e).__name__,
165
+ exc_info=e,
218
166
  )
219
167
  return None
220
168
 
@@ -231,9 +179,17 @@ async def stop_scheduler(scheduler: Scheduler | None) -> None:
231
179
 
232
180
  try:
233
181
  await scheduler.stop()
182
+ except SchedulerError as e:
183
+ logger.error(
184
+ "scheduler_stop_scheduler_error",
185
+ error=str(e),
186
+ error_type=type(e).__name__,
187
+ exc_info=e,
188
+ )
234
189
  except Exception as e:
235
190
  logger.error(
236
191
  "scheduler_stop_failed",
237
192
  error=str(e),
238
193
  error_type=type(e).__name__,
194
+ exc_info=e,
239
195
  )
@@ -1,5 +1,7 @@
1
1
  """Task registry for dynamic task registration and discovery."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from typing import Any
4
6
 
5
7
  import structlog
@@ -79,7 +81,7 @@ class TaskRegistry:
79
81
 
80
82
  return self._tasks[name]
81
83
 
82
- def list_tasks(self) -> list[str]:
84
+ def list(self) -> list[str]:
83
85
  """
84
86
  Get list of all registered task names.
85
87
 
@@ -88,7 +90,7 @@ class TaskRegistry:
88
90
  """
89
91
  return list(self._tasks.keys())
90
92
 
91
- def is_registered(self, name: str) -> bool:
93
+ def has(self, name: str) -> bool:
92
94
  """
93
95
  Check if a task is registered.
94
96
 
@@ -105,7 +107,7 @@ class TaskRegistry:
105
107
  self._tasks.clear()
106
108
  logger.debug("task_registry_cleared")
107
109
 
108
- def get_registry_info(self) -> dict[str, Any]:
110
+ def info(self) -> dict[str, Any]:
109
111
  """
110
112
  Get information about the current registry state.
111
113
 
@@ -119,32 +121,4 @@ class TaskRegistry:
119
121
  }
120
122
 
121
123
 
122
- # Global task registry instance
123
- _global_registry: TaskRegistry | None = None
124
-
125
-
126
- def get_task_registry() -> TaskRegistry:
127
- """
128
- Get the global task registry instance.
129
-
130
- Returns:
131
- Global TaskRegistry instance
132
- """
133
- global _global_registry
134
-
135
- if _global_registry is None:
136
- _global_registry = TaskRegistry()
137
-
138
- return _global_registry
139
-
140
-
141
- def register_task(name: str, task_class: type[BaseScheduledTask]) -> None:
142
- """
143
- Register a task in the global registry.
144
-
145
- Args:
146
- name: Unique name for the task
147
- task_class: Task class that inherits from BaseScheduledTask
148
- """
149
- registry = get_task_registry()
150
- registry.register(name, task_class)
124
+ # Module-level accessors intentionally omitted.