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
@@ -0,0 +1,233 @@
1
+ """Middleware management and ordering for the plugin system.
2
+
3
+ This module provides utilities for managing middleware registration
4
+ and ensuring proper ordering across core and plugin middleware.
5
+ """
6
+
7
+ from dataclasses import dataclass
8
+ from typing import TYPE_CHECKING, Any
9
+
10
+ from fastapi import FastAPI
11
+
12
+ from ccproxy.core.logging import TraceBoundLogger, get_logger
13
+
14
+ from .declaration import MiddlewareLayer, MiddlewareSpec
15
+
16
+
17
+ if TYPE_CHECKING:
18
+ from starlette.middleware.base import BaseHTTPMiddleware
19
+ else:
20
+ from starlette.middleware.base import BaseHTTPMiddleware
21
+
22
+
23
+ logger: TraceBoundLogger = get_logger()
24
+
25
+
26
+ @dataclass
27
+ class CoreMiddlewareSpec(MiddlewareSpec):
28
+ """Specification for core application middleware.
29
+
30
+ Extends MiddlewareSpec with a source field to distinguish
31
+ between core and plugin middleware.
32
+ """
33
+
34
+ source: str = "core" # "core" or plugin name
35
+
36
+
37
+ class MiddlewareManager:
38
+ """Manages middleware registration and ordering."""
39
+
40
+ def __init__(self) -> None:
41
+ """Initialize middleware manager."""
42
+ self.middleware_specs: list[CoreMiddlewareSpec] = []
43
+
44
+ def add_core_middleware(
45
+ self,
46
+ middleware_class: type[BaseHTTPMiddleware],
47
+ priority: int = MiddlewareLayer.APPLICATION,
48
+ **kwargs: Any,
49
+ ) -> None:
50
+ """Add core application middleware.
51
+
52
+ Args:
53
+ middleware_class: Middleware class
54
+ priority: Priority for ordering
55
+ **kwargs: Additional middleware arguments
56
+ """
57
+ spec = CoreMiddlewareSpec(
58
+ middleware_class=middleware_class,
59
+ priority=priority,
60
+ kwargs=kwargs,
61
+ source="core",
62
+ )
63
+ self.middleware_specs.append(spec)
64
+
65
+ def add_plugin_middleware(
66
+ self, plugin_name: str, specs: list[MiddlewareSpec]
67
+ ) -> None:
68
+ """Add middleware from a plugin.
69
+
70
+ Args:
71
+ plugin_name: Name of the plugin
72
+ specs: List of middleware specifications
73
+ """
74
+ for spec in specs:
75
+ core_spec = CoreMiddlewareSpec(
76
+ middleware_class=spec.middleware_class,
77
+ priority=spec.priority,
78
+ kwargs=spec.kwargs,
79
+ source=plugin_name,
80
+ )
81
+ self.middleware_specs.append(core_spec)
82
+ logger.trace(
83
+ "plugin_middleware_added",
84
+ plugin=plugin_name,
85
+ middleware=spec.middleware_class.__name__,
86
+ priority=spec.priority,
87
+ category="middleware",
88
+ )
89
+
90
+ def get_ordered_middleware(self) -> list[CoreMiddlewareSpec]:
91
+ """Get all middleware sorted by priority.
92
+
93
+ Returns:
94
+ List of middleware specs sorted by priority (lower first)
95
+ """
96
+ # Sort by priority (lower values first)
97
+ # Secondary sort by source (core before plugins) for same priority
98
+ return sorted(
99
+ self.middleware_specs,
100
+ key=lambda x: (x.priority, x.source != "core", x.source),
101
+ )
102
+
103
+ def apply_to_app(self, app: FastAPI) -> None:
104
+ """Apply all middleware to the FastAPI app in correct order.
105
+
106
+ Note: Middleware in FastAPI/Starlette is applied in reverse order
107
+ (last added runs first), so we add them in reverse priority order.
108
+
109
+ Args:
110
+ app: FastAPI application
111
+ """
112
+ ordered = self.get_ordered_middleware()
113
+ applied_middleware = []
114
+ failed_middleware = []
115
+
116
+ # Apply in reverse order (highest priority last so it runs first)
117
+ for spec in reversed(ordered):
118
+ try:
119
+ app.add_middleware(spec.middleware_class, **spec.kwargs) # type: ignore[arg-type]
120
+ applied_middleware.append(
121
+ {
122
+ "name": spec.middleware_class.__name__,
123
+ "priority": spec.priority,
124
+ "source": spec.source,
125
+ }
126
+ )
127
+ except Exception as e:
128
+ failed_middleware.append(
129
+ {
130
+ "name": spec.middleware_class.__name__,
131
+ "source": spec.source,
132
+ "error": str(e),
133
+ }
134
+ )
135
+ logger.error(
136
+ "middleware_application_failed",
137
+ middleware=spec.middleware_class.__name__,
138
+ source=spec.source,
139
+ error=str(e),
140
+ exc_info=e,
141
+ category="middleware",
142
+ )
143
+
144
+ # Log aggregated success
145
+ if applied_middleware:
146
+ logger.info(
147
+ "middleware_stack_configured",
148
+ applied=len(applied_middleware),
149
+ failed=len(failed_middleware),
150
+ middleware=[m["name"] for m in applied_middleware],
151
+ category="middleware",
152
+ )
153
+
154
+ def get_middleware_summary(self) -> dict[str, Any]:
155
+ """Get a summary of registered middleware.
156
+
157
+ Returns:
158
+ Dictionary with middleware statistics and order
159
+ """
160
+ ordered = self.get_ordered_middleware()
161
+
162
+ summary = {
163
+ "total": len(ordered),
164
+ "core": len([m for m in ordered if m.source == "core"]),
165
+ "plugins": len([m for m in ordered if m.source != "core"]),
166
+ "order": [
167
+ {
168
+ "name": spec.middleware_class.__name__,
169
+ "priority": spec.priority,
170
+ "layer": self._get_layer_name(spec.priority),
171
+ "source": spec.source,
172
+ }
173
+ for spec in ordered
174
+ ],
175
+ }
176
+
177
+ return summary
178
+
179
+ def _get_layer_name(self, priority: int) -> str:
180
+ """Get the layer name for a priority value.
181
+
182
+ Args:
183
+ priority: Priority value
184
+
185
+ Returns:
186
+ Layer name
187
+ """
188
+ # Find the closest layer
189
+ for layer in MiddlewareLayer:
190
+ if priority < layer:
191
+ return f"before_{layer.name.lower()}"
192
+ elif priority == layer:
193
+ return layer.name.lower()
194
+
195
+ # If higher than all layers
196
+ return "after_application"
197
+
198
+
199
+ def setup_default_middleware(manager: MiddlewareManager) -> None:
200
+ """Setup default core middleware.
201
+
202
+ Args:
203
+ manager: Middleware manager
204
+ """
205
+ from ccproxy.api.middleware.hooks import HooksMiddleware
206
+ from ccproxy.api.middleware.normalize_headers import NormalizeHeadersMiddleware
207
+ from ccproxy.api.middleware.request_id import RequestIDMiddleware
208
+
209
+ # Request ID should be first (lowest priority) to set context for all others
210
+ manager.add_core_middleware(
211
+ RequestIDMiddleware,
212
+ priority=MiddlewareLayer.SECURITY - 50, # Before security layer
213
+ )
214
+
215
+ # Hooks middleware should be early to capture all requests
216
+ manager.add_core_middleware(
217
+ HooksMiddleware,
218
+ priority=MiddlewareLayer.SECURITY
219
+ - 40, # After request ID, before other middleware
220
+ )
221
+
222
+ # # Access logging in observability layer
223
+ # manager.add_core_middleware(
224
+ # AccessLogMiddleware, priority=MiddlewareLayer.OBSERVABILITY
225
+ # )
226
+ #
227
+ # Normalize headers: strip unsafe and ensure server header
228
+ manager.add_core_middleware(
229
+ NormalizeHeadersMiddleware, # type: ignore[arg-type]
230
+ priority=MiddlewareLayer.ROUTING, # after routing layer
231
+ )
232
+
233
+ logger.debug("default_middleware_configured", category="middleware")
@@ -0,0 +1,59 @@
1
+ """Common provider plugin health detail models.
2
+
3
+ These models standardize the `details` payload returned by provider plugins
4
+ in their health checks, enabling consistent inspection across plugins.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any
10
+
11
+ from pydantic import BaseModel, Field
12
+
13
+
14
+ class CLIHealth(BaseModel):
15
+ """Standardized CLI health information for a provider plugin."""
16
+
17
+ available: bool = Field(description="Whether the CLI is available")
18
+ status: str = Field(description="CLI status string from plugin detector")
19
+ version: str | None = Field(default=None, description="Detected CLI version")
20
+ path: str | None = Field(default=None, description="Resolved CLI binary path")
21
+
22
+
23
+ class AuthHealth(BaseModel):
24
+ """Standardized authentication health information."""
25
+
26
+ configured: bool = Field(description="Whether auth is configured for this plugin")
27
+ token_available: bool | None = Field(
28
+ default=None, description="Valid, non-expired token is available"
29
+ )
30
+ token_expired: bool | None = Field(default=None, description="Token is expired")
31
+ account_id: str | None = Field(default=None, description="Associated account id")
32
+ expires_at: str | None = Field(default=None, description="Token expiry ISO time")
33
+ error: str | None = Field(default=None, description="Auth error or reason text")
34
+
35
+
36
+ class ConfigHealth(BaseModel):
37
+ """Standardized configuration summary for a provider plugin."""
38
+
39
+ model_count: int | None = Field(default=None, description="Configured model count")
40
+ supports_openai_format: bool | None = Field(
41
+ default=None, description="Whether OpenAI-compatible format is supported"
42
+ )
43
+ verbose_logging: bool | None = Field(
44
+ default=None, description="Whether plugin verbose logging is enabled"
45
+ )
46
+ extra: dict[str, Any] | None = Field(
47
+ default=None, description="Additional provider-specific configuration"
48
+ )
49
+
50
+
51
+ class ProviderHealthDetails(BaseModel):
52
+ """Top-level standardized provider health details payload."""
53
+
54
+ provider: str = Field(description="Provider plugin name")
55
+ enabled: bool = Field(description="Whether this plugin is enabled")
56
+ base_url: str | None = Field(default=None, description="Provider base URL")
57
+ cli: CLIHealth | None = Field(default=None, description="CLI health")
58
+ auth: AuthHealth | None = Field(default=None, description="Auth health")
59
+ config: ConfigHealth | None = Field(default=None, description="Config summary")
@@ -0,0 +1,180 @@
1
+ """Plugin protocol for provider plugins."""
2
+
3
+ from typing import TYPE_CHECKING, Any, Literal, Protocol, runtime_checkable
4
+
5
+ from fastapi import APIRouter
6
+ from pydantic import BaseModel
7
+ from typing_extensions import TypedDict
8
+
9
+ from ccproxy.core.plugins.hooks.base import Hook
10
+ from ccproxy.models.provider import ProviderConfig
11
+ from ccproxy.services.adapters.base import BaseAdapter
12
+
13
+
14
+ if TYPE_CHECKING:
15
+ from ccproxy.scheduler.tasks import BaseScheduledTask
16
+ from ccproxy.services.container import ServiceContainer
17
+
18
+
19
+ @runtime_checkable
20
+ class OAuthClientProtocol(Protocol):
21
+ """Protocol for OAuth client implementations."""
22
+
23
+ async def authenticate(self, open_browser: bool = True) -> Any:
24
+ """Perform OAuth authentication flow.
25
+
26
+ Args:
27
+ open_browser: Whether to automatically open browser
28
+
29
+ Returns:
30
+ Provider-specific credentials object
31
+ """
32
+ ...
33
+
34
+ async def refresh_access_token(self, refresh_token: str) -> Any:
35
+ """Refresh access token using refresh token.
36
+
37
+ Args:
38
+ refresh_token: Refresh token
39
+
40
+ Returns:
41
+ New token response
42
+ """
43
+ ...
44
+
45
+
46
+ class AuthCommandDefinition(TypedDict, total=False):
47
+ """Definition for provider-specific auth command extensions."""
48
+
49
+ command_name: str # Required: Command name (e.g., 'validate', 'profile')
50
+ description: str # Required: Command description
51
+ handler: Any # Required: Async command handler function
52
+ options: dict[str, Any] # Optional: Additional command options
53
+
54
+
55
+ class HealthCheckResult(BaseModel):
56
+ """Standardized health check result following IETF format."""
57
+
58
+ status: Literal["pass", "warn", "fail"]
59
+ componentId: str # noqa: N815
60
+ componentType: str = "provider_plugin" # noqa: N815
61
+ output: str | None = None
62
+ version: str | None = None
63
+ details: dict[str, Any] | None = None
64
+
65
+
66
+ class ScheduledTaskDefinition(TypedDict, total=False):
67
+ """Definition for a scheduled task from a plugin."""
68
+
69
+ task_name: str # Required: Unique name for the task instance
70
+ task_type: str # Required: Type identifier for task registry
71
+ task_class: type["BaseScheduledTask"] # Required: Task class
72
+ interval_seconds: float # Required: Interval between executions
73
+ enabled: bool # Optional: Whether task is enabled (default: True)
74
+ # Additional kwargs can be passed for task initialization
75
+
76
+
77
+ @runtime_checkable
78
+ class BasePlugin(Protocol):
79
+ """Base protocol for all plugins."""
80
+
81
+ @property
82
+ def name(self) -> str:
83
+ """Plugin name."""
84
+ ...
85
+
86
+ @property
87
+ def version(self) -> str:
88
+ """Plugin version."""
89
+ ...
90
+
91
+ @property
92
+ def dependencies(self) -> list[str]:
93
+ """List of plugin names this plugin depends on."""
94
+ ...
95
+
96
+ @property
97
+ def router_prefix(self) -> str:
98
+ """Unique route prefix for this plugin."""
99
+ ...
100
+
101
+ async def initialize(self, services: "ServiceContainer") -> None:
102
+ """Initialize plugin with shared services. Called once on startup."""
103
+ ...
104
+
105
+ async def shutdown(self) -> None:
106
+ """Perform graceful shutdown. Called once on app shutdown."""
107
+ ...
108
+
109
+ async def validate(self) -> bool:
110
+ """Validate plugin is ready."""
111
+ ...
112
+
113
+ def get_routes(self) -> APIRouter | dict[str, APIRouter] | None:
114
+ """Get plugin-specific routes (optional)."""
115
+ ...
116
+
117
+ async def health_check(self) -> HealthCheckResult:
118
+ """Perform health check following IETF format."""
119
+ ...
120
+
121
+ def get_scheduled_tasks(self) -> list[ScheduledTaskDefinition] | None:
122
+ """Get scheduled task definitions for this plugin (optional).
123
+
124
+ Returns:
125
+ List of task definitions or None if no scheduled tasks needed
126
+ """
127
+ ...
128
+
129
+ def get_config_class(self) -> type[BaseModel] | None:
130
+ """Get the Pydantic configuration model for this plugin.
131
+
132
+ Returns:
133
+ Pydantic BaseModel class for plugin configuration or None if no configuration needed
134
+ """
135
+ ...
136
+
137
+ def get_hooks(self) -> list[Hook] | None:
138
+ """Get hooks provided by this plugin (optional).
139
+
140
+ Returns:
141
+ List of hook instances or None if no hooks
142
+ """
143
+ ...
144
+
145
+
146
+ @runtime_checkable
147
+ class SystemPlugin(BasePlugin, Protocol):
148
+ """Protocol for system plugins (non-provider plugins).
149
+
150
+ System plugins inherit all methods from BasePlugin and don't add
151
+ any additional requirements. They don't proxy to external providers
152
+ and therefore don't need adapters or provider configurations.
153
+ """
154
+
155
+ # SystemPlugin has no additional methods beyond BasePlugin
156
+ pass
157
+
158
+
159
+ @runtime_checkable
160
+ class ProviderPlugin(BasePlugin, Protocol):
161
+ """Enhanced protocol for provider plugins.
162
+
163
+ Provider plugins proxy requests to external API providers and therefore
164
+ need additional methods for creating adapters and configurations.
165
+ """
166
+
167
+ def create_adapter(self) -> BaseAdapter:
168
+ """Create adapter instance for handling provider requests."""
169
+ ...
170
+
171
+ def create_config(self) -> ProviderConfig:
172
+ """Create provider configuration from settings."""
173
+ ...
174
+
175
+ async def get_oauth_client(self) -> OAuthClientProtocol | None:
176
+ """Get OAuth client for this plugin if it supports OAuth authentication.
177
+
178
+ Returns:
179
+ OAuth client instance or None if plugin doesn't support OAuth
180
+ """