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
@@ -1,16 +1,18 @@
1
- """Prometheus Pushgateway integration for batch metrics."""
1
+ """Prometheus Pushgateway integration for the metrics plugin."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
5
  import time
6
6
  from typing import Any
7
7
 
8
- from structlog import get_logger
8
+ import httpx
9
9
 
10
- from ccproxy.config.observability import ObservabilitySettings
10
+ from ccproxy.core.logging import get_plugin_logger
11
11
 
12
+ from .config import MetricsConfig
12
13
 
13
- logger = get_logger(__name__)
14
+
15
+ logger = get_plugin_logger(__name__)
14
16
 
15
17
 
16
18
  # Import prometheus_client with graceful degradation (matching existing metrics.py pattern)
@@ -92,22 +94,30 @@ class PushgatewayClient:
92
94
  Also supports VictoriaMetrics remote write protocol for compatibility.
93
95
  """
94
96
 
95
- def __init__(self, settings: ObservabilitySettings) -> None:
97
+ def __init__(self, config: MetricsConfig) -> None:
96
98
  """Initialize Pushgateway client.
97
99
 
98
100
  Args:
99
- settings: Observability configuration settings
101
+ config: Metrics plugin configuration
100
102
  """
101
- self.settings = settings
103
+ self.config = config
102
104
  # Pushgateway is enabled if URL is configured and prometheus_client is available
103
- self._enabled = PROMETHEUS_AVAILABLE and bool(settings.pushgateway_url)
105
+ self._enabled = (
106
+ PROMETHEUS_AVAILABLE
107
+ and bool(config.pushgateway_url)
108
+ and config.pushgateway_enabled
109
+ )
104
110
  self._circuit_breaker = CircuitBreaker(
105
111
  failure_threshold=5,
106
112
  recovery_timeout=60.0,
107
113
  )
108
114
 
109
115
  # Only log if pushgateway URL is configured but prometheus is not available
110
- if settings.pushgateway_url and not PROMETHEUS_AVAILABLE:
116
+ if (
117
+ config.pushgateway_url
118
+ and config.pushgateway_enabled
119
+ and not PROMETHEUS_AVAILABLE
120
+ ):
111
121
  logger.warning(
112
122
  "prometheus_client not available. Pushgateway will be disabled. "
113
123
  "Install with: pip install prometheus-client"
@@ -124,7 +134,7 @@ class PushgatewayClient:
124
134
  True if push succeeded, False otherwise
125
135
  """
126
136
 
127
- if not self._enabled or not self.settings.pushgateway_url:
137
+ if not self._enabled or not self.config.pushgateway_url:
128
138
  return False
129
139
 
130
140
  # Check circuit breaker before attempting operation
@@ -138,7 +148,7 @@ class PushgatewayClient:
138
148
 
139
149
  try:
140
150
  # Check if URL looks like VictoriaMetrics remote write endpoint
141
- if "/api/v1/write" in self.settings.pushgateway_url:
151
+ if "/api/v1/write" in self.config.pushgateway_url:
142
152
  success = self._push_remote_write(registry)
143
153
  else:
144
154
  success = self._push_standard(registry, method)
@@ -154,11 +164,12 @@ class PushgatewayClient:
154
164
  self._circuit_breaker.record_failure()
155
165
  logger.error(
156
166
  "pushgateway_push_failed",
157
- url=self.settings.pushgateway_url,
158
- job=self.settings.pushgateway_job,
167
+ url=self.config.pushgateway_url,
168
+ job=self.config.pushgateway_job,
159
169
  method=method,
160
170
  error=str(e),
161
171
  error_type=type(e).__name__,
172
+ exc_info=e,
162
173
  )
163
174
  return False
164
175
 
@@ -169,27 +180,27 @@ class PushgatewayClient:
169
180
  registry: Prometheus metrics registry
170
181
  method: Push method - "push" (replace), "pushadd" (add), or "delete"
171
182
  """
172
- if not self.settings.pushgateway_url:
183
+ if not self.config.pushgateway_url:
173
184
  return False
174
185
 
175
186
  try:
176
187
  # Use the appropriate prometheus_client function based on method
177
188
  if method == "push":
178
189
  push_to_gateway(
179
- gateway=self.settings.pushgateway_url,
180
- job=self.settings.pushgateway_job,
190
+ gateway=self.config.pushgateway_url,
191
+ job=self.config.pushgateway_job,
181
192
  registry=registry,
182
193
  )
183
194
  elif method == "pushadd":
184
195
  pushadd_to_gateway(
185
- gateway=self.settings.pushgateway_url,
186
- job=self.settings.pushgateway_job,
196
+ gateway=self.config.pushgateway_url,
197
+ job=self.config.pushgateway_job,
187
198
  registry=registry,
188
199
  )
189
200
  elif method == "delete":
190
201
  delete_from_gateway(
191
- gateway=self.settings.pushgateway_url,
192
- job=self.settings.pushgateway_job,
202
+ gateway=self.config.pushgateway_url,
203
+ job=self.config.pushgateway_job,
193
204
  )
194
205
  else:
195
206
  logger.error("pushgateway_invalid_method", method=method)
@@ -197,8 +208,8 @@ class PushgatewayClient:
197
208
 
198
209
  logger.debug(
199
210
  "pushgateway_push_success",
200
- url=self.settings.pushgateway_url,
201
- job=self.settings.pushgateway_job,
211
+ url=self.config.pushgateway_url,
212
+ job=self.config.pushgateway_job,
202
213
  protocol="standard",
203
214
  method=method,
204
215
  )
@@ -207,11 +218,12 @@ class PushgatewayClient:
207
218
  except Exception as e:
208
219
  logger.error(
209
220
  "pushgateway_standard_push_failed",
210
- url=self.settings.pushgateway_url,
211
- job=self.settings.pushgateway_job,
221
+ url=self.config.pushgateway_url,
222
+ job=self.config.pushgateway_job,
212
223
  method=method,
213
224
  error=str(e),
214
225
  error_type=type(e).__name__,
226
+ exc_info=e,
215
227
  )
216
228
  return False
217
229
 
@@ -222,10 +234,9 @@ class PushgatewayClient:
222
234
  via the /api/v1/import/prometheus endpoint, which is simpler than
223
235
  the full remote write protocol that requires protobuf encoding.
224
236
  """
225
- import httpx
226
237
  from prometheus_client.exposition import generate_latest
227
238
 
228
- if not self.settings.pushgateway_url:
239
+ if not self.config.pushgateway_url:
229
240
  return False
230
241
 
231
242
  # Generate metrics in Prometheus exposition format
@@ -233,13 +244,13 @@ class PushgatewayClient:
233
244
 
234
245
  # Convert /api/v1/write URL to /api/v1/import/prometheus for VictoriaMetrics
235
246
  # This endpoint accepts Prometheus exposition format directly
236
- if "/api/v1/write" in self.settings.pushgateway_url:
237
- import_url = self.settings.pushgateway_url.replace(
247
+ if "/api/v1/write" in self.config.pushgateway_url:
248
+ import_url = self.config.pushgateway_url.replace(
238
249
  "/api/v1/write", "/api/v1/import/prometheus"
239
250
  )
240
251
  else:
241
252
  # Fallback - assume it's already the correct import URL
242
- import_url = self.settings.pushgateway_url
253
+ import_url = self.config.pushgateway_url
243
254
 
244
255
  try:
245
256
  # VictoriaMetrics import endpoint accepts text/plain exposition format
@@ -257,7 +268,7 @@ class PushgatewayClient:
257
268
  logger.debug(
258
269
  "pushgateway_import_success",
259
270
  url=import_url,
260
- job=self.settings.pushgateway_job,
271
+ job=self.config.pushgateway_job,
261
272
  protocol="victoriametrics_import",
262
273
  status=response.status_code,
263
274
  )
@@ -276,6 +287,16 @@ class PushgatewayClient:
276
287
  url=import_url,
277
288
  error=str(e),
278
289
  error_type=type(e).__name__,
290
+ exc_info=e,
291
+ )
292
+ return False
293
+ except Exception as e:
294
+ logger.error(
295
+ "pushgateway_import_unexpected_error",
296
+ url=import_url,
297
+ error=str(e),
298
+ error_type=type(e).__name__,
299
+ exc_info=e,
279
300
  )
280
301
  return False
281
302
 
@@ -297,7 +318,7 @@ class PushgatewayClient:
297
318
  True if delete succeeded, False otherwise
298
319
  """
299
320
 
300
- if not self._enabled or not self.settings.pushgateway_url:
321
+ if not self._enabled or not self.config.pushgateway_url:
301
322
  return False
302
323
 
303
324
  # Check circuit breaker before attempting operation
@@ -311,7 +332,7 @@ class PushgatewayClient:
311
332
 
312
333
  try:
313
334
  # Only standard pushgateway supports delete operation
314
- if "/api/v1/write" in self.settings.pushgateway_url:
335
+ if "/api/v1/write" in self.config.pushgateway_url:
315
336
  logger.warning("pushgateway_delete_not_supported_for_remote_write")
316
337
  return False
317
338
  else:
@@ -328,37 +349,14 @@ class PushgatewayClient:
328
349
  self._circuit_breaker.record_failure()
329
350
  logger.error(
330
351
  "pushgateway_delete_failed",
331
- url=self.settings.pushgateway_url,
332
- job=self.settings.pushgateway_job,
352
+ url=self.config.pushgateway_url,
353
+ job=self.config.pushgateway_job,
333
354
  error=str(e),
334
355
  error_type=type(e).__name__,
356
+ exc_info=e,
335
357
  )
336
358
  return False
337
359
 
338
360
  def is_enabled(self) -> bool:
339
361
  """Check if Pushgateway client is enabled and configured."""
340
- return self._enabled and bool(self.settings.pushgateway_url)
341
-
342
-
343
- # Global pushgateway client instance
344
- _global_pushgateway_client: PushgatewayClient | None = None
345
-
346
-
347
- def get_pushgateway_client() -> PushgatewayClient:
348
- """Get or create global pushgateway client instance."""
349
- global _global_pushgateway_client
350
-
351
- if _global_pushgateway_client is None:
352
- # Import here to avoid circular imports
353
- from ccproxy.config.settings import get_settings
354
-
355
- settings = get_settings()
356
- _global_pushgateway_client = PushgatewayClient(settings.observability)
357
-
358
- return _global_pushgateway_client
359
-
360
-
361
- def reset_pushgateway_client() -> None:
362
- """Reset global pushgateway client instance (mainly for testing)."""
363
- global _global_pushgateway_client
364
- _global_pushgateway_client = None
362
+ return self._enabled and bool(self.config.pushgateway_url)
@@ -0,0 +1,107 @@
1
+ """Metrics endpoints for the metrics plugin."""
2
+
3
+ from typing import Any
4
+
5
+ from fastapi import APIRouter, HTTPException, Response
6
+
7
+ from ccproxy.core.logging import get_plugin_logger
8
+
9
+ from .collector import PrometheusMetrics
10
+
11
+
12
+ logger = get_plugin_logger(__name__)
13
+
14
+
15
+ def create_metrics_router(collector: PrometheusMetrics | None) -> APIRouter:
16
+ """Create metrics router with the given collector.
17
+
18
+ Args:
19
+ collector: Prometheus metrics collector instance
20
+
21
+ Returns:
22
+ FastAPI router with metrics endpoints
23
+ """
24
+ router = APIRouter(tags=["metrics"])
25
+
26
+ @router.get("/metrics")
27
+ async def get_prometheus_metrics() -> Response:
28
+ """Export metrics in Prometheus format.
29
+
30
+ This endpoint exposes operational metrics collected by the metrics plugin
31
+ for Prometheus scraping.
32
+
33
+ Returns:
34
+ Prometheus-formatted metrics text
35
+ """
36
+ if not collector or not collector.is_enabled():
37
+ raise HTTPException(
38
+ status_code=503,
39
+ detail="Metrics collection not enabled. Ensure prometheus-client is installed.",
40
+ )
41
+
42
+ try:
43
+ # Check if prometheus_client is available
44
+ try:
45
+ from prometheus_client import CONTENT_TYPE_LATEST, generate_latest
46
+ except ImportError as err:
47
+ raise HTTPException(
48
+ status_code=503,
49
+ detail="Prometheus client not available. Install with: pip install prometheus-client",
50
+ ) from err
51
+
52
+ # Generate prometheus format using the registry
53
+ from prometheus_client import REGISTRY
54
+
55
+ # Use the collector's registry or fall back to global
56
+ registry = (
57
+ collector.registry if collector.registry is not None else REGISTRY
58
+ )
59
+ prometheus_data = generate_latest(registry)
60
+
61
+ # Return the metrics data with proper content type
62
+ return Response(
63
+ content=prometheus_data,
64
+ media_type=CONTENT_TYPE_LATEST,
65
+ headers={
66
+ "Cache-Control": "no-cache, no-store, must-revalidate",
67
+ "Pragma": "no-cache",
68
+ "Expires": "0",
69
+ },
70
+ )
71
+
72
+ except HTTPException:
73
+ raise
74
+ except ImportError as e:
75
+ logger.error(
76
+ "prometheus_import_error",
77
+ error=str(e),
78
+ exc_info=e,
79
+ )
80
+ raise HTTPException(
81
+ status_code=503, detail=f"Prometheus dependencies missing: {str(e)}"
82
+ ) from e
83
+ except Exception as e:
84
+ logger.error(
85
+ "metrics_generation_error",
86
+ error=str(e),
87
+ exc_info=e,
88
+ )
89
+ raise HTTPException(
90
+ status_code=500,
91
+ detail=f"Failed to generate Prometheus metrics: {str(e)}",
92
+ ) from e
93
+
94
+ @router.get("/metrics/health")
95
+ async def metrics_health() -> dict[str, Any]:
96
+ """Get metrics system health status.
97
+
98
+ Returns:
99
+ Health status of the metrics collection system
100
+ """
101
+ return {
102
+ "status": "healthy" if collector and collector.is_enabled() else "disabled",
103
+ "prometheus_enabled": collector.is_enabled() if collector else False,
104
+ "namespace": collector.namespace if collector else None,
105
+ }
106
+
107
+ return router
@@ -0,0 +1,117 @@
1
+ """Scheduled tasks for the metrics plugin."""
2
+
3
+ from typing import Any
4
+
5
+ from ccproxy.core.logging import get_plugin_logger
6
+ from ccproxy.scheduler.tasks import BaseScheduledTask
7
+
8
+ from .pushgateway import PushgatewayClient
9
+
10
+
11
+ logger = get_plugin_logger(__name__)
12
+
13
+
14
+ class PushgatewayTask(BaseScheduledTask):
15
+ """Task for pushing metrics to Pushgateway periodically."""
16
+
17
+ def __init__(
18
+ self,
19
+ name: str,
20
+ interval_seconds: float,
21
+ enabled: bool = True,
22
+ max_backoff_seconds: float = 300.0,
23
+ metrics_config: Any | None = None,
24
+ metrics_hook: Any | None = None,
25
+ ):
26
+ """
27
+ Initialize pushgateway task.
28
+
29
+ Args:
30
+ name: Task name
31
+ interval_seconds: Interval between pushgateway operations
32
+ enabled: Whether task is enabled
33
+ max_backoff_seconds: Maximum backoff delay for failures
34
+ metrics_config: Metrics plugin configuration
35
+ metrics_hook: Metrics hook instance for getting collector
36
+ """
37
+ super().__init__(
38
+ name=name,
39
+ interval_seconds=interval_seconds,
40
+ enabled=enabled,
41
+ max_backoff_seconds=max_backoff_seconds,
42
+ )
43
+ self._metrics_config = metrics_config
44
+ self._metrics_hook = metrics_hook
45
+ self._pushgateway_client: PushgatewayClient | None = None
46
+
47
+ async def setup(self) -> None:
48
+ """Initialize pushgateway client for operations."""
49
+ try:
50
+ if self._metrics_config and self._metrics_hook:
51
+ self._pushgateway_client = PushgatewayClient(self._metrics_config)
52
+ logger.debug(
53
+ "pushgateway_task_setup_complete",
54
+ task_name=self.name,
55
+ url=self._metrics_config.pushgateway_url,
56
+ job=self._metrics_config.pushgateway_job,
57
+ )
58
+ else:
59
+ logger.warning(
60
+ "pushgateway_task_setup_missing_config",
61
+ task_name=self.name,
62
+ has_config=self._metrics_config is not None,
63
+ has_hook=self._metrics_hook is not None,
64
+ )
65
+ except Exception as e:
66
+ logger.error(
67
+ "pushgateway_task_setup_failed",
68
+ task_name=self.name,
69
+ error=str(e),
70
+ error_type=type(e).__name__,
71
+ exc_info=e,
72
+ )
73
+ raise
74
+
75
+ async def run(self) -> bool:
76
+ """Execute pushgateway metrics push."""
77
+ try:
78
+ if not self._pushgateway_client or not self._metrics_hook:
79
+ logger.warning(
80
+ "pushgateway_no_client_or_hook",
81
+ task_name=self.name,
82
+ has_client=self._pushgateway_client is not None,
83
+ has_hook=self._metrics_hook is not None,
84
+ )
85
+ return False
86
+
87
+ if not self._pushgateway_client.is_enabled():
88
+ logger.debug("pushgateway_disabled", task_name=self.name)
89
+ return True # Not an error, just disabled
90
+
91
+ # Get the metrics collector and push metrics
92
+ collector = self._metrics_hook.get_collector()
93
+ if not collector:
94
+ logger.warning("pushgateway_no_collector", task_name=self.name)
95
+ return False
96
+
97
+ # Push metrics using the client
98
+ success = self._pushgateway_client.push_metrics(
99
+ collector.get_registry(), method="push"
100
+ )
101
+
102
+ if success:
103
+ logger.debug("pushgateway_push_success", task_name=self.name)
104
+ else:
105
+ logger.warning("pushgateway_push_failed", task_name=self.name)
106
+
107
+ return success
108
+
109
+ except Exception as e:
110
+ logger.error(
111
+ "pushgateway_task_error",
112
+ task_name=self.name,
113
+ error=str(e),
114
+ error_type=type(e).__name__,
115
+ exc_info=e,
116
+ )
117
+ return False
@@ -0,0 +1,35 @@
1
+ # OAuth Claude Plugin
2
+
3
+ Standalone OAuth provider for managing Claude API access tokens.
4
+
5
+ ## Highlights
6
+ - Implements `ClaudeOAuthProvider` for authorization-code and refresh flows
7
+ - Integrates with the auth runtime to expose a reusable auth manager
8
+ - Shares detection utilities to diagnose CLI and token availability
9
+
10
+ ## Configuration
11
+ - `ClaudeOAuthConfig` sets client credentials, scopes, and storage paths
12
+ - Works alongside `claude_api` or credential balancer powered providers
13
+ - Generate defaults with `python3 scripts/generate_config_from_model.py \
14
+ --format toml --plugin oauth_claude --config-class ClaudeOAuthConfig`
15
+
16
+ ```toml
17
+ [plugins.oauth_claude]
18
+ # enabled = true
19
+ # base_url = "https://console.anthropic.com"
20
+ # token_url = "https://console.anthropic.com/v1/oauth/token"
21
+ # authorize_url = "https://claude.ai/oauth/authorize"
22
+ # profile_url = "https://api.anthropic.com/api/oauth/profile"
23
+ # client_id = "9d1c250a-e61b-44d9-88ed-5944d1962f5e"
24
+ # redirect_uri = "http://localhost:35593/callback"
25
+ # scopes = ["org:create_api_key", "user:profile", "user:inference"]
26
+ # request_timeout = 30
27
+ # callback_timeout = 300
28
+ # callback_port = 35593
29
+ # use_pkce = true
30
+ ```
31
+
32
+ ## Related Components
33
+ - `provider.py`: OAuth implementation and token storage helpers
34
+ - `plugin.py`: runtime wiring for auth-only plugin
35
+ - `config.py`: settings model for Claude OAuth parameters
@@ -0,0 +1,14 @@
1
+ """OAuth Claude plugin for standalone Claude OAuth authentication."""
2
+
3
+ from .client import ClaudeOAuthClient
4
+ from .config import ClaudeOAuthConfig
5
+ from .provider import ClaudeOAuthProvider
6
+ from .storage import ClaudeOAuthStorage
7
+
8
+
9
+ __all__ = [
10
+ "ClaudeOAuthClient",
11
+ "ClaudeOAuthConfig",
12
+ "ClaudeOAuthProvider",
13
+ "ClaudeOAuthStorage",
14
+ ]