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
@@ -0,0 +1,198 @@
1
+ """Docker plugin with CLI extensions."""
2
+
3
+ from typing import Any
4
+
5
+ import ccproxy.core.logging
6
+ from ccproxy.core.plugins import (
7
+ BaseProviderPluginFactory,
8
+ PluginContext,
9
+ PluginManifest,
10
+ ProviderPluginRuntime,
11
+ )
12
+ from ccproxy.core.plugins.declaration import CliArgumentSpec
13
+
14
+ from .adapter import DockerAdapter
15
+ from .config import DockerConfig
16
+
17
+
18
+ logger = ccproxy.core.logging.get_plugin_logger(__name__)
19
+
20
+
21
+ class DockerRuntime(ProviderPluginRuntime):
22
+ """Runtime for Docker plugin."""
23
+
24
+ def __init__(self, manifest: PluginManifest):
25
+ """Initialize runtime."""
26
+ super().__init__(manifest)
27
+
28
+ async def _on_initialize(self) -> None:
29
+ """Initialize the Docker plugin."""
30
+ await super()._on_initialize()
31
+
32
+ if not self.context:
33
+ raise RuntimeError("Context not set")
34
+
35
+ # Get CLI arguments from context
36
+ settings = self.context.get("settings")
37
+ if settings:
38
+ cli_context = settings.get_cli_context()
39
+
40
+ # Process Docker CLI flags and update config
41
+ config = self.context.get("config")
42
+ if config and isinstance(config, DockerConfig):
43
+ self._apply_cli_overrides(cli_context, config)
44
+
45
+ config = self.context.get("config")
46
+ docker_image = (
47
+ config.docker_image if config and isinstance(config, DockerConfig) else None
48
+ )
49
+
50
+ logger.debug(
51
+ "plugin_initialized",
52
+ plugin="docker",
53
+ version="0.1.0",
54
+ status="initialized",
55
+ docker_image=docker_image,
56
+ )
57
+
58
+ def _apply_cli_overrides(
59
+ self, cli_context: dict[str, Any], config: DockerConfig
60
+ ) -> None:
61
+ """Apply CLI flag overrides to Docker config."""
62
+ # Apply CLI overrides to config
63
+ if cli_context.get("docker_image"):
64
+ config.docker_image = cli_context["docker_image"]
65
+
66
+ if cli_context.get("docker_home"):
67
+ config.docker_home_directory = cli_context["docker_home"]
68
+
69
+ if cli_context.get("docker_workspace"):
70
+ config.docker_workspace_directory = cli_context["docker_workspace"]
71
+
72
+ if cli_context.get("docker_env"):
73
+ config.docker_environment.extend(cli_context["docker_env"])
74
+
75
+ if cli_context.get("docker_volume"):
76
+ config.docker_volumes.extend(cli_context["docker_volume"])
77
+
78
+ if cli_context.get("user_mapping_enabled") is not None:
79
+ config.user_mapping_enabled = cli_context["user_mapping_enabled"]
80
+
81
+ if cli_context.get("user_uid"):
82
+ config.user_uid = cli_context["user_uid"]
83
+
84
+ if cli_context.get("user_gid"):
85
+ config.user_gid = cli_context["user_gid"]
86
+
87
+ logger.debug("docker_cli_overrides_applied", cli_overrides=cli_context)
88
+
89
+
90
+ class DockerFactory(BaseProviderPluginFactory):
91
+ """Factory for Docker plugin."""
92
+
93
+ # Plugin configuration via class attributes
94
+ plugin_name = "docker"
95
+ plugin_description = "Docker container management for CCProxy"
96
+ runtime_class = DockerRuntime
97
+ adapter_class = DockerAdapter
98
+ config_class = DockerConfig
99
+
100
+ # CLI extension declarations - all Docker-related CLI arguments
101
+ cli_arguments = [
102
+ CliArgumentSpec(
103
+ target_command="serve",
104
+ argument_name="docker",
105
+ argument_type=bool,
106
+ help_text="Run using Docker instead of local execution",
107
+ default=False,
108
+ typer_kwargs={
109
+ "is_flag": True,
110
+ "flag_value": True,
111
+ "option": ["--docker", "-d"],
112
+ },
113
+ ),
114
+ CliArgumentSpec(
115
+ target_command="serve",
116
+ argument_name="docker_image",
117
+ argument_type=str,
118
+ help_text="Docker image to use (overrides configuration)",
119
+ typer_kwargs={"rich_help_panel": "Docker Settings"},
120
+ ),
121
+ CliArgumentSpec(
122
+ target_command="serve",
123
+ argument_name="docker_env",
124
+ argument_type=list[str],
125
+ help_text="Environment variables to pass to Docker container",
126
+ typer_kwargs={
127
+ "rich_help_panel": "Docker Settings",
128
+ "option": ["--docker-env", "-e"],
129
+ },
130
+ ),
131
+ CliArgumentSpec(
132
+ target_command="serve",
133
+ argument_name="docker_volume",
134
+ argument_type=list[str],
135
+ help_text="Volume mounts for Docker container",
136
+ typer_kwargs={
137
+ "rich_help_panel": "Docker Settings",
138
+ "option": ["--docker-volume", "-v"],
139
+ },
140
+ ),
141
+ CliArgumentSpec(
142
+ target_command="serve",
143
+ argument_name="docker_arg",
144
+ argument_type=list[str],
145
+ help_text="Additional arguments to pass to docker run",
146
+ typer_kwargs={"rich_help_panel": "Docker Settings"},
147
+ ),
148
+ CliArgumentSpec(
149
+ target_command="serve",
150
+ argument_name="docker_home",
151
+ argument_type=str,
152
+ help_text="Override the home directory for Docker",
153
+ typer_kwargs={"rich_help_panel": "Docker Settings"},
154
+ ),
155
+ CliArgumentSpec(
156
+ target_command="serve",
157
+ argument_name="docker_workspace",
158
+ argument_type=str,
159
+ help_text="Override the workspace directory for Docker",
160
+ typer_kwargs={"rich_help_panel": "Docker Settings"},
161
+ ),
162
+ CliArgumentSpec(
163
+ target_command="serve",
164
+ argument_name="user_mapping_enabled",
165
+ argument_type=bool,
166
+ help_text="Enable user mapping for Docker",
167
+ typer_kwargs={
168
+ "rich_help_panel": "Docker Settings",
169
+ "option": ["--user-mapping/--no-user-mapping"],
170
+ },
171
+ ),
172
+ CliArgumentSpec(
173
+ target_command="serve",
174
+ argument_name="user_uid",
175
+ argument_type=int,
176
+ help_text="User UID for Docker user mapping",
177
+ typer_kwargs={"rich_help_panel": "Docker Settings"},
178
+ ),
179
+ CliArgumentSpec(
180
+ target_command="serve",
181
+ argument_name="user_gid",
182
+ argument_type=int,
183
+ help_text="User GID for Docker user mapping",
184
+ typer_kwargs={"rich_help_panel": "Docker Settings"},
185
+ ),
186
+ ]
187
+
188
+ async def create_adapter(self, context: PluginContext) -> DockerAdapter:
189
+ """Create Docker adapter instance."""
190
+ config = context.get("config")
191
+ if not isinstance(config, DockerConfig):
192
+ config = DockerConfig()
193
+
194
+ return DockerAdapter(config=config)
195
+
196
+
197
+ # Export factory instance
198
+ factory = DockerFactory()
@@ -6,7 +6,7 @@ for real-time output handling in CLI applications.
6
6
 
7
7
  Example:
8
8
  ```python
9
- from ccproxy.docker.stream_process import run_command, DefaultOutputMiddleware
9
+ from ccproxy.plugins.docker.stream_process import run_command, DefaultOutputMiddleware
10
10
 
11
11
  # Create custom middleware to add timestamps
12
12
  from datetime import datetime
@@ -162,8 +162,8 @@ def create_chained_middleware(
162
162
 
163
163
  Example:
164
164
  ```python
165
- from ccproxy.docker.stream_process import create_chained_middleware
166
- from ccproxy.docker.adapter import LoggerOutputMiddleware
165
+ from ccproxy.plugins.docker.stream_process import create_chained_middleware
166
+ from ccproxy.plugins.docker.adapter import LoggerOutputMiddleware
167
167
 
168
168
  # Create individual middleware components
169
169
  logger_middleware = LoggerOutputMiddleware(logger)
@@ -0,0 +1,26 @@
1
+ # DuckDB Storage Plugin
2
+
3
+ Provides DuckDB-backed storage for analytics and request logging data.
4
+
5
+ ## Highlights
6
+ - Initializes a DuckDB database and exposes it via the plugin registry
7
+ - Creates directories automatically and reuses the configured database path
8
+ - Optionally runs VACUUM/OPTIMIZE on shutdown for compactness
9
+
10
+ ## Configuration
11
+ - `DuckDBStorageConfig` toggles enablement, database path, and optimizations
12
+ - Other plugins reference the exposed `log_storage` service by name
13
+ - Generate defaults with `python3 scripts/generate_config_from_model.py \
14
+ --format toml --plugin duckdb_storage --config-class DuckDBStorageConfig`
15
+
16
+ ```toml
17
+ [plugins.duckdb_storage]
18
+ # enabled = true
19
+ # database_path = "~/.local/share/ccproxy/metrics.duckdb"
20
+ # optimize_on_shutdown = false
21
+ ```
22
+
23
+ ## Related Components
24
+ - `plugin.py`: runtime lifecycle and service registration
25
+ - `storage.py`: `SimpleDuckDBStorage` helper for connections
26
+ - `routes.py`: FastAPI router under `/duckdb` for simple diagnostics
@@ -0,0 +1 @@
1
+ """DuckDB storage plugin package."""
@@ -0,0 +1,22 @@
1
+ from pydantic import BaseModel, Field
2
+
3
+
4
+ class DuckDBStorageConfig(BaseModel):
5
+ """Config for the DuckDB storage plugin.
6
+
7
+ Notes:
8
+ - By default this plugin mirrors core Observability settings and path.
9
+ - You can override the database path if needed via plugin config.
10
+ """
11
+
12
+ enabled: bool = Field(
13
+ default=True,
14
+ description="Enable DuckDB storage plugin",
15
+ )
16
+ database_path: str | None = Field(
17
+ default=None, description="Optional override for DuckDB database path"
18
+ )
19
+ optimize_on_shutdown: bool = Field(
20
+ default=False,
21
+ description="Run PRAGMA optimize on shutdown (file-backed DB only)",
22
+ )
@@ -0,0 +1,128 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ from typing import Any
5
+
6
+ from ccproxy.core.logging import get_plugin_logger
7
+ from ccproxy.core.plugins import (
8
+ PluginManifest,
9
+ RouteSpec,
10
+ SystemPluginFactory,
11
+ SystemPluginRuntime,
12
+ )
13
+
14
+ from .config import DuckDBStorageConfig
15
+ from .storage import SimpleDuckDBStorage
16
+
17
+
18
+ logger = get_plugin_logger()
19
+
20
+
21
+ def _default_db_path() -> str:
22
+ # Mirrors previous default: XDG_DATA_HOME/ccproxy/metrics.duckdb
23
+ import os
24
+
25
+ return str(
26
+ Path(os.environ.get("XDG_DATA_HOME", Path.home() / ".local" / "share"))
27
+ / "ccproxy"
28
+ / "metrics.duckdb"
29
+ )
30
+
31
+
32
+ class DuckDBStorageRuntime(SystemPluginRuntime):
33
+ """Runtime for DuckDB storage plugin."""
34
+
35
+ def __init__(self, manifest: PluginManifest):
36
+ super().__init__(manifest)
37
+ self.config: DuckDBStorageConfig | None = None
38
+ self.storage: SimpleDuckDBStorage | None = None
39
+
40
+ async def _on_initialize(self) -> None:
41
+ if not self.context:
42
+ raise RuntimeError("Context not set")
43
+
44
+ # Resolve config
45
+ cfg = self.context.get("config")
46
+ if not isinstance(cfg, DuckDBStorageConfig):
47
+ logger.warning("plugin_no_config_using_defaults")
48
+ cfg = DuckDBStorageConfig()
49
+ self.config = cfg
50
+
51
+ # Determine if storage should be enabled: respect plugin flag and any
52
+ # app-wide observability needs (logs endpoints/collection) if present.
53
+ # Enable only if plugin config enables it
54
+ enabled = bool(cfg.enabled)
55
+ if not enabled:
56
+ logger.debug("duckdb_plugin_disabled", category="plugin")
57
+ return
58
+
59
+ # Resolve DB path
60
+ db_path = cfg.database_path or _default_db_path()
61
+ Path(db_path).parent.mkdir(parents=True, exist_ok=True)
62
+
63
+ # Initialize storage
64
+ self.storage = SimpleDuckDBStorage(database_path=db_path)
65
+ await self.storage.initialize()
66
+
67
+ # Expose storage via plugin registry and app.state
68
+ registry = self.context.get("plugin_registry")
69
+ if registry:
70
+ registry.register_service("log_storage", self.storage, self.manifest.name)
71
+ logger.debug(
72
+ "duckdb_storage_service_registered", path=db_path, category="plugin"
73
+ )
74
+
75
+ logger.info("duckdb_storage_initialized", path=db_path, category="plugin")
76
+
77
+ async def _on_shutdown(self) -> None:
78
+ if self.storage:
79
+ # Optional optimize on shutdown
80
+ if self.config and self.config.optimize_on_shutdown:
81
+ try:
82
+ self.storage.optimize()
83
+ except Exception as e: # pragma: no cover - best-effort
84
+ logger.warning("duckdb_optimize_on_shutdown_failed", error=str(e))
85
+ try:
86
+ await self.storage.close()
87
+ except Exception as e:
88
+ logger.warning("duckdb_storage_close_error", error=str(e))
89
+ self.storage = None
90
+
91
+ async def _get_health_details(self) -> dict[str, Any]:
92
+ has_service = False
93
+ if self.context:
94
+ reg = self.context.get("plugin_registry")
95
+ if reg is not None:
96
+ try:
97
+ has_service = reg.has_service("log_storage")
98
+ except Exception:
99
+ has_service = False
100
+ return {
101
+ "type": "system",
102
+ "initialized": self.initialized,
103
+ "enabled": bool(self.storage),
104
+ "has_service": has_service,
105
+ }
106
+
107
+
108
+ class DuckDBStorageFactory(SystemPluginFactory):
109
+ def __init__(self) -> None:
110
+ from .routes import router as duckdb_router
111
+
112
+ manifest = PluginManifest(
113
+ name="duckdb_storage",
114
+ version="0.1.0",
115
+ description="Provides DuckDB-backed request log storage",
116
+ is_provider=False,
117
+ provides=["log_storage"],
118
+ config_class=DuckDBStorageConfig,
119
+ routes=[RouteSpec(router=duckdb_router, prefix="/duckdb", tags=["duckdb"])],
120
+ )
121
+ super().__init__(manifest)
122
+
123
+ def create_runtime(self) -> DuckDBStorageRuntime:
124
+ return DuckDBStorageRuntime(self.manifest)
125
+
126
+
127
+ # Export the factory instance for entry points
128
+ factory = DuckDBStorageFactory()
@@ -0,0 +1,51 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, cast
4
+
5
+ from fastapi import APIRouter, HTTPException, Request
6
+
7
+
8
+ router = APIRouter()
9
+
10
+
11
+ def _get_storage(request: Request) -> Any:
12
+ storage = getattr(request.app.state, "log_storage", None)
13
+ if not storage:
14
+ # Backward-compat alias
15
+ storage = getattr(request.app.state, "duckdb_storage", None)
16
+ return storage
17
+
18
+
19
+ @router.get("/health")
20
+ async def health(request: Request) -> dict[str, Any]:
21
+ storage = _get_storage(request)
22
+ if not storage:
23
+ raise HTTPException(status_code=503, detail="Storage not initialized")
24
+ return cast(dict[str, Any], await storage.health_check())
25
+
26
+
27
+ @router.get("/status")
28
+ async def status(request: Request) -> dict[str, Any]:
29
+ storage = _get_storage(request)
30
+ if not storage:
31
+ raise HTTPException(status_code=503, detail="Storage not initialized")
32
+
33
+ health = cast(dict[str, Any], await storage.health_check())
34
+
35
+ # Include basic plugin/service context when available
36
+ plugin_info: dict[str, Any] = {
37
+ "plugin": "duckdb_storage",
38
+ "service_registered": False,
39
+ }
40
+
41
+ try:
42
+ if hasattr(request.app.state, "plugin_registry"):
43
+ registry = request.app.state.plugin_registry
44
+ plugin_info["service_registered"] = registry.has_service("log_storage")
45
+ except Exception:
46
+ pass
47
+
48
+ return {
49
+ "health": health,
50
+ **plugin_info,
51
+ }