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,277 @@
1
+ """Plugin management API endpoints."""
2
+
3
+ from typing import Any
4
+
5
+ from fastapi import APIRouter, HTTPException, Request
6
+ from pydantic import BaseModel
7
+ from starlette import status
8
+
9
+ import ccproxy.core.logging
10
+ from ccproxy.auth.dependencies import ConditionalAuthDep
11
+
12
+
13
+ logger = ccproxy.core.logging.get_logger(__name__)
14
+
15
+
16
+ router = APIRouter(prefix="/plugins", tags=["plugins"])
17
+
18
+
19
+ class PluginInfo(BaseModel):
20
+ """Plugin information model."""
21
+
22
+ name: str
23
+ type: str # "builtin" or "plugin"
24
+ status: str # "active", "inactive", "error"
25
+ version: str | None = None
26
+
27
+
28
+ class PluginListResponse(BaseModel):
29
+ """Response model for plugin list."""
30
+
31
+ plugins: list[PluginInfo]
32
+ total: int
33
+
34
+
35
+ class PluginStatusEntry(BaseModel):
36
+ name: str
37
+ version: str | None = None
38
+ type: str # "provider" or "system"
39
+ provides: list[str] = []
40
+ requires: list[str] = []
41
+ optional_requires: list[str] = []
42
+ initialized: bool
43
+
44
+
45
+ class PluginStatusResponse(BaseModel):
46
+ initialization_order: list[str]
47
+ services: dict[str, str] # service_name -> provider plugin
48
+ plugins: list[PluginStatusEntry]
49
+
50
+
51
+ class PluginHealthResponse(BaseModel):
52
+ """Response model for plugin health check."""
53
+
54
+ plugin: str
55
+ status: str # "healthy", "unhealthy", "unknown"
56
+ adapter_loaded: bool
57
+ details: dict[str, Any] | None = None
58
+
59
+
60
+ # Only core plugin management endpoints are exposed:
61
+ # - GET /plugins: list loaded plugins
62
+ # - GET /plugins/{plugin_name}/health: check plugin health if provided by runtime
63
+ # - GET /plugins/status: summarize manifests and initialization state
64
+ #
65
+ # Dynamic reload/discover/unregister are not supported in v2 and have been removed.
66
+
67
+
68
+ # Plugin registry is accessed directly from app state
69
+
70
+
71
+ @router.get("", response_model=PluginListResponse)
72
+ async def list_plugins(
73
+ request: Request,
74
+ auth: ConditionalAuthDep = None,
75
+ ) -> PluginListResponse:
76
+ """List all loaded plugins and built-in providers.
77
+
78
+ Returns:
79
+ List of all available plugins and providers
80
+ """
81
+ plugins: list[PluginInfo] = []
82
+
83
+ # Access v2 plugin registry from app state
84
+ if hasattr(request.app.state, "plugin_registry"):
85
+ from ccproxy.core.plugins import PluginRegistry
86
+
87
+ registry: PluginRegistry = request.app.state.plugin_registry
88
+
89
+ for name in registry.list_plugins():
90
+ factory = registry.get_factory(name)
91
+ if factory:
92
+ from ccproxy.core.plugins import factory_type_name
93
+
94
+ manifest = factory.get_manifest()
95
+ plugin_type = factory_type_name(factory)
96
+
97
+ plugins.append(
98
+ PluginInfo(
99
+ name=name,
100
+ type=plugin_type,
101
+ status="active",
102
+ version=manifest.version,
103
+ )
104
+ )
105
+
106
+ return PluginListResponse(plugins=plugins, total=len(plugins))
107
+
108
+
109
+ @router.get("/{plugin_name}/health", response_model=PluginHealthResponse)
110
+ async def plugin_health(
111
+ plugin_name: str,
112
+ request: Request,
113
+ auth: ConditionalAuthDep = None,
114
+ ) -> PluginHealthResponse:
115
+ """Check the health status of a specific plugin.
116
+
117
+ Args:
118
+ plugin_name: Name of the plugin to check
119
+
120
+ Returns:
121
+ Health status of the plugin
122
+
123
+ Raises:
124
+ HTTPException: If plugin not found
125
+ """
126
+ # Access v2 plugin registry from app state
127
+ if not hasattr(request.app.state, "plugin_registry"):
128
+ raise HTTPException(status_code=503, detail="Plugin registry not initialized")
129
+
130
+ from ccproxy.core.plugins import PluginRegistry
131
+
132
+ registry: PluginRegistry = request.app.state.plugin_registry
133
+
134
+ # Check if plugin exists
135
+ if plugin_name not in registry.list_plugins():
136
+ raise HTTPException(
137
+ status_code=status.HTTP_404_NOT_FOUND,
138
+ detail=f"Plugin '{plugin_name}' not found",
139
+ )
140
+
141
+ # Get the plugin runtime instance
142
+ runtime = registry.get_runtime(plugin_name)
143
+ if runtime and hasattr(runtime, "health_check"):
144
+ try:
145
+ health_result = await runtime.health_check()
146
+ # Convert HealthCheckResult to PluginHealthResponse
147
+ # Handle both dict and object response
148
+ if isinstance(health_result, dict):
149
+ status_value = health_result.get("status", "unknown")
150
+ output_value = health_result.get("output")
151
+ version_value = health_result.get("version")
152
+ details_value = health_result.get("details")
153
+ else:
154
+ # Access attributes for non-dict responses
155
+ status_value = getattr(health_result, "status", "unknown")
156
+ output_value = getattr(health_result, "output", None)
157
+ version_value = getattr(health_result, "version", None)
158
+ details_value = getattr(health_result, "details", None)
159
+
160
+ return PluginHealthResponse(
161
+ plugin=plugin_name,
162
+ status="healthy"
163
+ if status_value == "pass"
164
+ else "unhealthy"
165
+ if status_value == "fail"
166
+ else "unknown",
167
+ adapter_loaded=True,
168
+ details={
169
+ "type": "plugin",
170
+ "active": True,
171
+ "health_check": {
172
+ "status": status_value,
173
+ "output": output_value,
174
+ "version": version_value,
175
+ "details": details_value,
176
+ },
177
+ },
178
+ )
179
+ except (OSError, PermissionError) as e:
180
+ logger.error(
181
+ "plugin_health_check_io_failed",
182
+ plugin=plugin_name,
183
+ error=str(e),
184
+ exc_info=e,
185
+ )
186
+ return PluginHealthResponse(
187
+ plugin=plugin_name,
188
+ status="unhealthy",
189
+ adapter_loaded=True,
190
+ details={"type": "plugin", "active": True, "io_error": str(e)},
191
+ )
192
+ except Exception as e:
193
+ logger.error(
194
+ "plugin_health_check_failed",
195
+ plugin=plugin_name,
196
+ error=str(e),
197
+ exc_info=e,
198
+ )
199
+ return PluginHealthResponse(
200
+ plugin=plugin_name,
201
+ status="unhealthy",
202
+ adapter_loaded=True,
203
+ details={"type": "plugin", "active": True, "error": str(e)},
204
+ )
205
+ else:
206
+ # Plugin doesn't have health check, use basic status
207
+ return PluginHealthResponse(
208
+ plugin=plugin_name,
209
+ status="healthy",
210
+ adapter_loaded=True,
211
+ details={"type": "plugin", "active": True},
212
+ )
213
+
214
+ # Endpoints are loaded at startup only
215
+
216
+
217
+ @router.get("/status", response_model=PluginStatusResponse)
218
+ async def plugins_status(
219
+ request: Request, auth: ConditionalAuthDep = None
220
+ ) -> PluginStatusResponse:
221
+ """Get plugin system status, including manifests and init order.
222
+
223
+ Returns:
224
+ Initialization order, registered services, and per-plugin manifest summary
225
+ """
226
+ if not hasattr(request.app.state, "plugin_registry"):
227
+ raise HTTPException(status_code=503, detail="Plugin registry not initialized")
228
+
229
+ from ccproxy.core.plugins import PluginRegistry
230
+
231
+ registry: PluginRegistry = request.app.state.plugin_registry
232
+
233
+ # Get manifests and runtime status
234
+ entries: list[PluginStatusEntry] = []
235
+ for name in registry.list_plugins():
236
+ factory = registry.get_factory(name)
237
+ if not factory:
238
+ continue
239
+ manifest = factory.get_manifest()
240
+ runtime = registry.get_runtime(name)
241
+
242
+ # Determine plugin type via factory helper
243
+ from ccproxy.core.plugins import factory_type_name
244
+
245
+ plugin_type = factory_type_name(factory)
246
+
247
+ entries.append(
248
+ PluginStatusEntry(
249
+ name=name,
250
+ version=manifest.version,
251
+ type=plugin_type,
252
+ provides=list(manifest.provides),
253
+ requires=list(manifest.requires),
254
+ optional_requires=list(manifest.optional_requires),
255
+ initialized=runtime is not None
256
+ and getattr(runtime, "initialized", False),
257
+ )
258
+ )
259
+
260
+ # Extract init order and services map
261
+ init_order = list(getattr(registry, "initialization_order", []) or [])
262
+ services_map = dict(getattr(registry, "_service_providers", {}) or {})
263
+
264
+ return PluginStatusResponse(
265
+ initialization_order=init_order,
266
+ services=services_map,
267
+ plugins=entries,
268
+ )
269
+
270
+
271
+ @router.delete("/{plugin_name}")
272
+ async def unregister_plugin() -> dict[str, str]:
273
+ """Plugin unregistration is not supported in v2; endpoint removed."""
274
+ raise HTTPException(
275
+ status_code=status.HTTP_501_NOT_IMPLEMENTED,
276
+ detail="Plugin unregistration is not supported; restart with desired config.",
277
+ )
ccproxy/auth/__init__.py CHANGED
@@ -1,15 +1,12 @@
1
1
  """Authentication module for centralized auth handling."""
2
2
 
3
3
  from ccproxy.auth.bearer import BearerTokenAuthManager
4
- from ccproxy.auth.credentials_adapter import CredentialsAuthManager
5
4
  from ccproxy.auth.dependencies import (
6
5
  AccessTokenDep,
7
6
  AuthManagerDep,
8
7
  RequiredAuthDep,
9
8
  get_access_token,
10
9
  get_auth_manager,
11
- get_bearer_auth_manager,
12
- get_credentials_auth_manager,
13
10
  require_auth,
14
11
  )
15
12
  from ccproxy.auth.exceptions import (
@@ -22,32 +19,22 @@ from ccproxy.auth.exceptions import (
22
19
  CredentialsStorageError,
23
20
  InsufficientPermissionsError,
24
21
  InvalidTokenError,
25
- OAuthCallbackError,
26
22
  OAuthError,
27
- OAuthLoginError,
28
23
  OAuthTokenRefreshError,
29
24
  )
30
- from ccproxy.auth.manager import AuthManager, BaseAuthManager
25
+ from ccproxy.auth.manager import AuthManager
31
26
  from ccproxy.auth.storage import (
32
- JsonFileTokenStorage,
33
- KeyringTokenStorage,
34
27
  TokenStorage,
35
28
  )
36
- from ccproxy.services.credentials.manager import CredentialsManager
37
29
 
38
30
 
39
31
  __all__ = [
40
- # Manager interfaces
32
+ # Manager interface
41
33
  "AuthManager",
42
- "BaseAuthManager",
43
34
  # Implementations
44
35
  "BearerTokenAuthManager",
45
- "CredentialsAuthManager",
46
- "CredentialsManager",
47
36
  # Storage interfaces and implementations
48
37
  "TokenStorage",
49
- "JsonFileTokenStorage",
50
- "KeyringTokenStorage",
51
38
  # Exceptions
52
39
  "AuthenticationError",
53
40
  "AuthenticationRequiredError",
@@ -58,14 +45,10 @@ __all__ = [
58
45
  "CredentialsStorageError",
59
46
  "InvalidTokenError",
60
47
  "InsufficientPermissionsError",
61
- "OAuthCallbackError",
62
48
  "OAuthError",
63
- "OAuthLoginError",
64
49
  "OAuthTokenRefreshError",
65
50
  # Dependencies
66
51
  "get_auth_manager",
67
- "get_bearer_auth_manager",
68
- "get_credentials_auth_manager",
69
52
  "require_auth",
70
53
  "get_access_token",
71
54
  # Type aliases
ccproxy/auth/bearer.py CHANGED
@@ -3,11 +3,11 @@
3
3
  from typing import Any
4
4
 
5
5
  from ccproxy.auth.exceptions import AuthenticationError
6
- from ccproxy.auth.manager import BaseAuthManager
7
- from ccproxy.auth.models import ClaudeCredentials, UserProfile
6
+ from ccproxy.auth.models.credentials import BaseCredentials
7
+ from ccproxy.auth.oauth.protocol import StandardProfileFields
8
8
 
9
9
 
10
- class BearerTokenAuthManager(BaseAuthManager):
10
+ class BearerTokenAuthManager:
11
11
  """Authentication manager for static bearer tokens."""
12
12
 
13
13
  def __init__(self, token: str) -> None:
@@ -33,12 +33,8 @@ class BearerTokenAuthManager(BaseAuthManager):
33
33
  raise AuthenticationError("No bearer token available")
34
34
  return self.token
35
35
 
36
- async def get_credentials(self) -> ClaudeCredentials:
37
- """Get credentials (not supported for bearer tokens).
38
-
39
- Raises:
40
- AuthenticationError: Bearer tokens don't support full credentials
41
- """
36
+ async def get_credentials(self) -> BaseCredentials:
37
+ """Bearer tokens do not expose structured credentials."""
42
38
  raise AuthenticationError(
43
39
  "Bearer token authentication doesn't support full credentials"
44
40
  )
@@ -51,12 +47,8 @@ class BearerTokenAuthManager(BaseAuthManager):
51
47
  """
52
48
  return bool(self.token)
53
49
 
54
- async def get_user_profile(self) -> UserProfile | None:
55
- """Get user profile (not supported for bearer tokens).
56
-
57
- Returns:
58
- None - bearer tokens don't support user profiles
59
- """
50
+ async def get_user_profile(self) -> StandardProfileFields | None:
51
+ """Return ``None`` because bearer tokens have no profile context."""
60
52
  return None
61
53
 
62
54
  async def __aenter__(self) -> "BearerTokenAuthManager":
@@ -66,3 +58,21 @@ class BearerTokenAuthManager(BaseAuthManager):
66
58
  async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
67
59
  """Async context manager exit."""
68
60
  pass
61
+
62
+ # ==================== Provider-Generic Methods ====================
63
+
64
+ async def validate_credentials(self) -> bool:
65
+ """Validate that credentials are available and valid.
66
+
67
+ Returns:
68
+ True if credentials are valid, False otherwise
69
+ """
70
+ return bool(self.token)
71
+
72
+ def get_provider_name(self) -> str:
73
+ """Get the provider name for logging.
74
+
75
+ Returns:
76
+ Provider name string
77
+ """
78
+ return "bearer-token"