ccproxy-api 0.1.7__py3-none-any.whl → 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (481) hide show
  1. ccproxy/api/__init__.py +1 -15
  2. ccproxy/api/app.py +434 -219
  3. ccproxy/api/bootstrap.py +30 -0
  4. ccproxy/api/decorators.py +85 -0
  5. ccproxy/api/dependencies.py +144 -168
  6. ccproxy/api/format_validation.py +54 -0
  7. ccproxy/api/middleware/cors.py +6 -3
  8. ccproxy/api/middleware/errors.py +388 -524
  9. ccproxy/api/middleware/hooks.py +563 -0
  10. ccproxy/api/middleware/normalize_headers.py +59 -0
  11. ccproxy/api/middleware/request_id.py +35 -16
  12. ccproxy/api/middleware/streaming_hooks.py +292 -0
  13. ccproxy/api/routes/__init__.py +5 -14
  14. ccproxy/api/routes/health.py +39 -672
  15. ccproxy/api/routes/plugins.py +277 -0
  16. ccproxy/auth/__init__.py +2 -19
  17. ccproxy/auth/bearer.py +25 -15
  18. ccproxy/auth/dependencies.py +123 -157
  19. ccproxy/auth/exceptions.py +0 -12
  20. ccproxy/auth/manager.py +35 -49
  21. ccproxy/auth/managers/__init__.py +10 -0
  22. ccproxy/auth/managers/base.py +523 -0
  23. ccproxy/auth/managers/base_enhanced.py +63 -0
  24. ccproxy/auth/managers/token_snapshot.py +77 -0
  25. ccproxy/auth/models/base.py +65 -0
  26. ccproxy/auth/models/credentials.py +40 -0
  27. ccproxy/auth/oauth/__init__.py +4 -18
  28. ccproxy/auth/oauth/base.py +533 -0
  29. ccproxy/auth/oauth/cli_errors.py +37 -0
  30. ccproxy/auth/oauth/flows.py +430 -0
  31. ccproxy/auth/oauth/protocol.py +366 -0
  32. ccproxy/auth/oauth/registry.py +408 -0
  33. ccproxy/auth/oauth/router.py +396 -0
  34. ccproxy/auth/oauth/routes.py +186 -113
  35. ccproxy/auth/oauth/session.py +151 -0
  36. ccproxy/auth/oauth/templates.py +342 -0
  37. ccproxy/auth/storage/__init__.py +2 -5
  38. ccproxy/auth/storage/base.py +279 -5
  39. ccproxy/auth/storage/generic.py +134 -0
  40. ccproxy/cli/__init__.py +1 -2
  41. ccproxy/cli/_settings_help.py +351 -0
  42. ccproxy/cli/commands/auth.py +1519 -793
  43. ccproxy/cli/commands/config/commands.py +209 -276
  44. ccproxy/cli/commands/plugins.py +669 -0
  45. ccproxy/cli/commands/serve.py +75 -810
  46. ccproxy/cli/commands/status.py +254 -0
  47. ccproxy/cli/decorators.py +83 -0
  48. ccproxy/cli/helpers.py +22 -60
  49. ccproxy/cli/main.py +359 -10
  50. ccproxy/cli/options/claude_options.py +0 -25
  51. ccproxy/config/__init__.py +7 -11
  52. ccproxy/config/core.py +227 -0
  53. ccproxy/config/env_generator.py +232 -0
  54. ccproxy/config/runtime.py +67 -0
  55. ccproxy/config/security.py +36 -3
  56. ccproxy/config/settings.py +382 -441
  57. ccproxy/config/toml_generator.py +299 -0
  58. ccproxy/config/utils.py +452 -0
  59. ccproxy/core/__init__.py +7 -271
  60. ccproxy/{_version.py → core/_version.py} +16 -3
  61. ccproxy/core/async_task_manager.py +516 -0
  62. ccproxy/core/async_utils.py +47 -14
  63. ccproxy/core/auth/__init__.py +6 -0
  64. ccproxy/core/constants.py +16 -50
  65. ccproxy/core/errors.py +53 -0
  66. ccproxy/core/id_utils.py +20 -0
  67. ccproxy/core/interfaces.py +16 -123
  68. ccproxy/core/logging.py +473 -18
  69. ccproxy/core/plugins/__init__.py +77 -0
  70. ccproxy/core/plugins/cli_discovery.py +211 -0
  71. ccproxy/core/plugins/declaration.py +455 -0
  72. ccproxy/core/plugins/discovery.py +604 -0
  73. ccproxy/core/plugins/factories.py +967 -0
  74. ccproxy/core/plugins/hooks/__init__.py +30 -0
  75. ccproxy/core/plugins/hooks/base.py +58 -0
  76. ccproxy/core/plugins/hooks/events.py +46 -0
  77. ccproxy/core/plugins/hooks/implementations/__init__.py +16 -0
  78. ccproxy/core/plugins/hooks/implementations/formatters/__init__.py +11 -0
  79. ccproxy/core/plugins/hooks/implementations/formatters/json.py +552 -0
  80. ccproxy/core/plugins/hooks/implementations/formatters/raw.py +370 -0
  81. ccproxy/core/plugins/hooks/implementations/http_tracer.py +431 -0
  82. ccproxy/core/plugins/hooks/layers.py +44 -0
  83. ccproxy/core/plugins/hooks/manager.py +186 -0
  84. ccproxy/core/plugins/hooks/registry.py +139 -0
  85. ccproxy/core/plugins/hooks/thread_manager.py +203 -0
  86. ccproxy/core/plugins/hooks/types.py +22 -0
  87. ccproxy/core/plugins/interfaces.py +416 -0
  88. ccproxy/core/plugins/loader.py +166 -0
  89. ccproxy/core/plugins/middleware.py +233 -0
  90. ccproxy/core/plugins/models.py +59 -0
  91. ccproxy/core/plugins/protocol.py +180 -0
  92. ccproxy/core/plugins/runtime.py +519 -0
  93. ccproxy/{observability/context.py → core/request_context.py} +137 -94
  94. ccproxy/core/status_report.py +211 -0
  95. ccproxy/core/transformers.py +13 -8
  96. ccproxy/data/claude_headers_fallback.json +540 -19
  97. ccproxy/data/codex_headers_fallback.json +114 -7
  98. ccproxy/http/__init__.py +30 -0
  99. ccproxy/http/base.py +95 -0
  100. ccproxy/http/client.py +323 -0
  101. ccproxy/http/hooks.py +642 -0
  102. ccproxy/http/pool.py +279 -0
  103. ccproxy/llms/formatters/__init__.py +7 -0
  104. ccproxy/llms/formatters/anthropic_to_openai/__init__.py +55 -0
  105. ccproxy/llms/formatters/anthropic_to_openai/errors.py +65 -0
  106. ccproxy/llms/formatters/anthropic_to_openai/requests.py +356 -0
  107. ccproxy/llms/formatters/anthropic_to_openai/responses.py +153 -0
  108. ccproxy/llms/formatters/anthropic_to_openai/streams.py +1546 -0
  109. ccproxy/llms/formatters/base.py +140 -0
  110. ccproxy/llms/formatters/base_model.py +33 -0
  111. ccproxy/llms/formatters/common/__init__.py +51 -0
  112. ccproxy/llms/formatters/common/identifiers.py +48 -0
  113. ccproxy/llms/formatters/common/streams.py +254 -0
  114. ccproxy/llms/formatters/common/thinking.py +74 -0
  115. ccproxy/llms/formatters/common/usage.py +135 -0
  116. ccproxy/llms/formatters/constants.py +55 -0
  117. ccproxy/llms/formatters/context.py +116 -0
  118. ccproxy/llms/formatters/mapping.py +33 -0
  119. ccproxy/llms/formatters/openai_to_anthropic/__init__.py +55 -0
  120. ccproxy/llms/formatters/openai_to_anthropic/_helpers.py +141 -0
  121. ccproxy/llms/formatters/openai_to_anthropic/errors.py +53 -0
  122. ccproxy/llms/formatters/openai_to_anthropic/requests.py +674 -0
  123. ccproxy/llms/formatters/openai_to_anthropic/responses.py +285 -0
  124. ccproxy/llms/formatters/openai_to_anthropic/streams.py +530 -0
  125. ccproxy/llms/formatters/openai_to_openai/__init__.py +53 -0
  126. ccproxy/llms/formatters/openai_to_openai/_helpers.py +325 -0
  127. ccproxy/llms/formatters/openai_to_openai/errors.py +6 -0
  128. ccproxy/llms/formatters/openai_to_openai/requests.py +388 -0
  129. ccproxy/llms/formatters/openai_to_openai/responses.py +594 -0
  130. ccproxy/llms/formatters/openai_to_openai/streams.py +1832 -0
  131. ccproxy/llms/formatters/utils.py +306 -0
  132. ccproxy/llms/models/__init__.py +9 -0
  133. ccproxy/llms/models/anthropic.py +619 -0
  134. ccproxy/llms/models/openai.py +844 -0
  135. ccproxy/llms/streaming/__init__.py +26 -0
  136. ccproxy/llms/streaming/accumulators.py +1074 -0
  137. ccproxy/llms/streaming/formatters.py +251 -0
  138. ccproxy/{adapters/openai/streaming.py → llms/streaming/processors.py} +193 -240
  139. ccproxy/models/__init__.py +8 -159
  140. ccproxy/models/detection.py +92 -193
  141. ccproxy/models/provider.py +75 -0
  142. ccproxy/plugins/access_log/README.md +32 -0
  143. ccproxy/plugins/access_log/__init__.py +20 -0
  144. ccproxy/plugins/access_log/config.py +33 -0
  145. ccproxy/plugins/access_log/formatter.py +126 -0
  146. ccproxy/plugins/access_log/hook.py +763 -0
  147. ccproxy/plugins/access_log/logger.py +254 -0
  148. ccproxy/plugins/access_log/plugin.py +137 -0
  149. ccproxy/plugins/access_log/writer.py +109 -0
  150. ccproxy/plugins/analytics/README.md +24 -0
  151. ccproxy/plugins/analytics/__init__.py +1 -0
  152. ccproxy/plugins/analytics/config.py +5 -0
  153. ccproxy/plugins/analytics/ingest.py +85 -0
  154. ccproxy/plugins/analytics/models.py +97 -0
  155. ccproxy/plugins/analytics/plugin.py +121 -0
  156. ccproxy/plugins/analytics/routes.py +163 -0
  157. ccproxy/plugins/analytics/service.py +284 -0
  158. ccproxy/plugins/claude_api/README.md +29 -0
  159. ccproxy/plugins/claude_api/__init__.py +10 -0
  160. ccproxy/plugins/claude_api/adapter.py +829 -0
  161. ccproxy/plugins/claude_api/config.py +52 -0
  162. ccproxy/plugins/claude_api/detection_service.py +461 -0
  163. ccproxy/plugins/claude_api/health.py +175 -0
  164. ccproxy/plugins/claude_api/hooks.py +284 -0
  165. ccproxy/plugins/claude_api/models.py +256 -0
  166. ccproxy/plugins/claude_api/plugin.py +298 -0
  167. ccproxy/plugins/claude_api/routes.py +118 -0
  168. ccproxy/plugins/claude_api/streaming_metrics.py +68 -0
  169. ccproxy/plugins/claude_api/tasks.py +84 -0
  170. ccproxy/plugins/claude_sdk/README.md +35 -0
  171. ccproxy/plugins/claude_sdk/__init__.py +80 -0
  172. ccproxy/plugins/claude_sdk/adapter.py +749 -0
  173. ccproxy/plugins/claude_sdk/auth.py +57 -0
  174. ccproxy/{claude_sdk → plugins/claude_sdk}/client.py +63 -39
  175. ccproxy/plugins/claude_sdk/config.py +210 -0
  176. ccproxy/{claude_sdk → plugins/claude_sdk}/converter.py +6 -6
  177. ccproxy/plugins/claude_sdk/detection_service.py +163 -0
  178. ccproxy/{services/claude_sdk_service.py → plugins/claude_sdk/handler.py} +123 -304
  179. ccproxy/plugins/claude_sdk/health.py +113 -0
  180. ccproxy/plugins/claude_sdk/hooks.py +115 -0
  181. ccproxy/{claude_sdk → plugins/claude_sdk}/manager.py +42 -32
  182. ccproxy/{claude_sdk → plugins/claude_sdk}/message_queue.py +8 -8
  183. ccproxy/{models/claude_sdk.py → plugins/claude_sdk/models.py} +64 -16
  184. ccproxy/plugins/claude_sdk/options.py +154 -0
  185. ccproxy/{claude_sdk → plugins/claude_sdk}/parser.py +23 -5
  186. ccproxy/plugins/claude_sdk/plugin.py +269 -0
  187. ccproxy/plugins/claude_sdk/routes.py +104 -0
  188. ccproxy/{claude_sdk → plugins/claude_sdk}/session_client.py +124 -12
  189. ccproxy/plugins/claude_sdk/session_pool.py +700 -0
  190. ccproxy/{claude_sdk → plugins/claude_sdk}/stream_handle.py +48 -43
  191. ccproxy/{claude_sdk → plugins/claude_sdk}/stream_worker.py +22 -18
  192. ccproxy/{claude_sdk → plugins/claude_sdk}/streaming.py +50 -16
  193. ccproxy/plugins/claude_sdk/tasks.py +97 -0
  194. ccproxy/plugins/claude_shared/README.md +18 -0
  195. ccproxy/plugins/claude_shared/__init__.py +12 -0
  196. ccproxy/plugins/claude_shared/model_defaults.py +171 -0
  197. ccproxy/plugins/codex/README.md +35 -0
  198. ccproxy/plugins/codex/__init__.py +6 -0
  199. ccproxy/plugins/codex/adapter.py +635 -0
  200. ccproxy/{config/codex.py → plugins/codex/config.py} +78 -12
  201. ccproxy/plugins/codex/detection_service.py +544 -0
  202. ccproxy/plugins/codex/health.py +162 -0
  203. ccproxy/plugins/codex/hooks.py +263 -0
  204. ccproxy/plugins/codex/model_defaults.py +39 -0
  205. ccproxy/plugins/codex/models.py +263 -0
  206. ccproxy/plugins/codex/plugin.py +275 -0
  207. ccproxy/plugins/codex/routes.py +129 -0
  208. ccproxy/plugins/codex/streaming_metrics.py +324 -0
  209. ccproxy/plugins/codex/tasks.py +106 -0
  210. ccproxy/plugins/codex/utils/__init__.py +1 -0
  211. ccproxy/plugins/codex/utils/sse_parser.py +106 -0
  212. ccproxy/plugins/command_replay/README.md +34 -0
  213. ccproxy/plugins/command_replay/__init__.py +17 -0
  214. ccproxy/plugins/command_replay/config.py +133 -0
  215. ccproxy/plugins/command_replay/formatter.py +432 -0
  216. ccproxy/plugins/command_replay/hook.py +294 -0
  217. ccproxy/plugins/command_replay/plugin.py +161 -0
  218. ccproxy/plugins/copilot/README.md +39 -0
  219. ccproxy/plugins/copilot/__init__.py +11 -0
  220. ccproxy/plugins/copilot/adapter.py +465 -0
  221. ccproxy/plugins/copilot/config.py +155 -0
  222. ccproxy/plugins/copilot/data/copilot_fallback.json +41 -0
  223. ccproxy/plugins/copilot/detection_service.py +255 -0
  224. ccproxy/plugins/copilot/manager.py +275 -0
  225. ccproxy/plugins/copilot/model_defaults.py +284 -0
  226. ccproxy/plugins/copilot/models.py +148 -0
  227. ccproxy/plugins/copilot/oauth/__init__.py +16 -0
  228. ccproxy/plugins/copilot/oauth/client.py +494 -0
  229. ccproxy/plugins/copilot/oauth/models.py +385 -0
  230. ccproxy/plugins/copilot/oauth/provider.py +602 -0
  231. ccproxy/plugins/copilot/oauth/storage.py +170 -0
  232. ccproxy/plugins/copilot/plugin.py +360 -0
  233. ccproxy/plugins/copilot/routes.py +294 -0
  234. ccproxy/plugins/credential_balancer/README.md +124 -0
  235. ccproxy/plugins/credential_balancer/__init__.py +6 -0
  236. ccproxy/plugins/credential_balancer/config.py +270 -0
  237. ccproxy/plugins/credential_balancer/factory.py +415 -0
  238. ccproxy/plugins/credential_balancer/hook.py +51 -0
  239. ccproxy/plugins/credential_balancer/manager.py +587 -0
  240. ccproxy/plugins/credential_balancer/plugin.py +146 -0
  241. ccproxy/plugins/dashboard/README.md +25 -0
  242. ccproxy/plugins/dashboard/__init__.py +1 -0
  243. ccproxy/plugins/dashboard/config.py +8 -0
  244. ccproxy/plugins/dashboard/plugin.py +71 -0
  245. ccproxy/plugins/dashboard/routes.py +67 -0
  246. ccproxy/plugins/docker/README.md +32 -0
  247. ccproxy/{docker → plugins/docker}/__init__.py +3 -0
  248. ccproxy/{docker → plugins/docker}/adapter.py +108 -10
  249. ccproxy/plugins/docker/config.py +82 -0
  250. ccproxy/{docker → plugins/docker}/docker_path.py +4 -3
  251. ccproxy/{docker → plugins/docker}/middleware.py +2 -2
  252. ccproxy/plugins/docker/plugin.py +198 -0
  253. ccproxy/{docker → plugins/docker}/stream_process.py +3 -3
  254. ccproxy/plugins/duckdb_storage/README.md +26 -0
  255. ccproxy/plugins/duckdb_storage/__init__.py +1 -0
  256. ccproxy/plugins/duckdb_storage/config.py +22 -0
  257. ccproxy/plugins/duckdb_storage/plugin.py +128 -0
  258. ccproxy/plugins/duckdb_storage/routes.py +51 -0
  259. ccproxy/plugins/duckdb_storage/storage.py +633 -0
  260. ccproxy/plugins/max_tokens/README.md +38 -0
  261. ccproxy/plugins/max_tokens/__init__.py +12 -0
  262. ccproxy/plugins/max_tokens/adapter.py +235 -0
  263. ccproxy/plugins/max_tokens/config.py +86 -0
  264. ccproxy/plugins/max_tokens/models.py +53 -0
  265. ccproxy/plugins/max_tokens/plugin.py +200 -0
  266. ccproxy/plugins/max_tokens/service.py +271 -0
  267. ccproxy/plugins/max_tokens/token_limits.json +54 -0
  268. ccproxy/plugins/metrics/README.md +35 -0
  269. ccproxy/plugins/metrics/__init__.py +10 -0
  270. ccproxy/{observability/metrics.py → plugins/metrics/collector.py} +20 -153
  271. ccproxy/plugins/metrics/config.py +85 -0
  272. ccproxy/plugins/metrics/grafana/dashboards/ccproxy-dashboard.json +1720 -0
  273. ccproxy/plugins/metrics/hook.py +403 -0
  274. ccproxy/plugins/metrics/plugin.py +268 -0
  275. ccproxy/{observability → plugins/metrics}/pushgateway.py +57 -59
  276. ccproxy/plugins/metrics/routes.py +107 -0
  277. ccproxy/plugins/metrics/tasks.py +117 -0
  278. ccproxy/plugins/oauth_claude/README.md +35 -0
  279. ccproxy/plugins/oauth_claude/__init__.py +14 -0
  280. ccproxy/plugins/oauth_claude/client.py +270 -0
  281. ccproxy/plugins/oauth_claude/config.py +84 -0
  282. ccproxy/plugins/oauth_claude/manager.py +482 -0
  283. ccproxy/plugins/oauth_claude/models.py +266 -0
  284. ccproxy/plugins/oauth_claude/plugin.py +149 -0
  285. ccproxy/plugins/oauth_claude/provider.py +571 -0
  286. ccproxy/plugins/oauth_claude/storage.py +212 -0
  287. ccproxy/plugins/oauth_codex/README.md +38 -0
  288. ccproxy/plugins/oauth_codex/__init__.py +14 -0
  289. ccproxy/plugins/oauth_codex/client.py +224 -0
  290. ccproxy/plugins/oauth_codex/config.py +95 -0
  291. ccproxy/plugins/oauth_codex/manager.py +256 -0
  292. ccproxy/plugins/oauth_codex/models.py +239 -0
  293. ccproxy/plugins/oauth_codex/plugin.py +146 -0
  294. ccproxy/plugins/oauth_codex/provider.py +574 -0
  295. ccproxy/plugins/oauth_codex/storage.py +92 -0
  296. ccproxy/plugins/permissions/README.md +28 -0
  297. ccproxy/plugins/permissions/__init__.py +22 -0
  298. ccproxy/plugins/permissions/config.py +28 -0
  299. ccproxy/{cli/commands/permission_handler.py → plugins/permissions/handlers/cli.py} +49 -25
  300. ccproxy/plugins/permissions/handlers/protocol.py +33 -0
  301. ccproxy/plugins/permissions/handlers/terminal.py +675 -0
  302. ccproxy/{api/routes → plugins/permissions}/mcp.py +34 -7
  303. ccproxy/{models/permissions.py → plugins/permissions/models.py} +65 -1
  304. ccproxy/plugins/permissions/plugin.py +153 -0
  305. ccproxy/{api/routes/permissions.py → plugins/permissions/routes.py} +20 -16
  306. ccproxy/{api/services/permission_service.py → plugins/permissions/service.py} +65 -11
  307. ccproxy/{api → plugins/permissions}/ui/permission_handler_protocol.py +1 -1
  308. ccproxy/{api → plugins/permissions}/ui/terminal_permission_handler.py +66 -10
  309. ccproxy/plugins/pricing/README.md +34 -0
  310. ccproxy/plugins/pricing/__init__.py +6 -0
  311. ccproxy/{pricing → plugins/pricing}/cache.py +7 -6
  312. ccproxy/{config/pricing.py → plugins/pricing/config.py} +32 -6
  313. ccproxy/plugins/pricing/exceptions.py +35 -0
  314. ccproxy/plugins/pricing/loader.py +440 -0
  315. ccproxy/{pricing → plugins/pricing}/models.py +13 -23
  316. ccproxy/plugins/pricing/plugin.py +169 -0
  317. ccproxy/plugins/pricing/service.py +191 -0
  318. ccproxy/plugins/pricing/tasks.py +300 -0
  319. ccproxy/{pricing → plugins/pricing}/updater.py +86 -72
  320. ccproxy/plugins/pricing/utils.py +99 -0
  321. ccproxy/plugins/request_tracer/README.md +40 -0
  322. ccproxy/plugins/request_tracer/__init__.py +7 -0
  323. ccproxy/plugins/request_tracer/config.py +120 -0
  324. ccproxy/plugins/request_tracer/hook.py +415 -0
  325. ccproxy/plugins/request_tracer/plugin.py +255 -0
  326. ccproxy/scheduler/__init__.py +2 -14
  327. ccproxy/scheduler/core.py +26 -41
  328. ccproxy/scheduler/manager.py +61 -105
  329. ccproxy/scheduler/registry.py +6 -32
  330. ccproxy/scheduler/tasks.py +268 -276
  331. ccproxy/services/__init__.py +0 -1
  332. ccproxy/services/adapters/__init__.py +11 -0
  333. ccproxy/services/adapters/base.py +123 -0
  334. ccproxy/services/adapters/chain_composer.py +88 -0
  335. ccproxy/services/adapters/chain_validation.py +44 -0
  336. ccproxy/services/adapters/chat_accumulator.py +200 -0
  337. ccproxy/services/adapters/delta_utils.py +142 -0
  338. ccproxy/services/adapters/format_adapter.py +136 -0
  339. ccproxy/services/adapters/format_context.py +11 -0
  340. ccproxy/services/adapters/format_registry.py +158 -0
  341. ccproxy/services/adapters/http_adapter.py +1045 -0
  342. ccproxy/services/adapters/mock_adapter.py +118 -0
  343. ccproxy/services/adapters/protocols.py +35 -0
  344. ccproxy/services/adapters/simple_converters.py +571 -0
  345. ccproxy/services/auth_registry.py +180 -0
  346. ccproxy/services/cache/__init__.py +6 -0
  347. ccproxy/services/cache/response_cache.py +261 -0
  348. ccproxy/services/cli_detection.py +437 -0
  349. ccproxy/services/config/__init__.py +6 -0
  350. ccproxy/services/config/proxy_configuration.py +111 -0
  351. ccproxy/services/container.py +256 -0
  352. ccproxy/services/factories.py +380 -0
  353. ccproxy/services/handler_config.py +76 -0
  354. ccproxy/services/interfaces.py +298 -0
  355. ccproxy/services/mocking/__init__.py +6 -0
  356. ccproxy/services/mocking/mock_handler.py +291 -0
  357. ccproxy/services/tracing/__init__.py +7 -0
  358. ccproxy/services/tracing/interfaces.py +61 -0
  359. ccproxy/services/tracing/null_tracer.py +57 -0
  360. ccproxy/streaming/__init__.py +23 -0
  361. ccproxy/streaming/buffer.py +1056 -0
  362. ccproxy/streaming/deferred.py +897 -0
  363. ccproxy/streaming/handler.py +117 -0
  364. ccproxy/streaming/interfaces.py +77 -0
  365. ccproxy/streaming/simple_adapter.py +39 -0
  366. ccproxy/streaming/sse.py +109 -0
  367. ccproxy/streaming/sse_parser.py +127 -0
  368. ccproxy/templates/__init__.py +6 -0
  369. ccproxy/templates/plugin_scaffold.py +695 -0
  370. ccproxy/testing/endpoints/__init__.py +33 -0
  371. ccproxy/testing/endpoints/cli.py +215 -0
  372. ccproxy/testing/endpoints/config.py +874 -0
  373. ccproxy/testing/endpoints/console.py +57 -0
  374. ccproxy/testing/endpoints/models.py +100 -0
  375. ccproxy/testing/endpoints/runner.py +1903 -0
  376. ccproxy/testing/endpoints/tools.py +308 -0
  377. ccproxy/testing/mock_responses.py +70 -1
  378. ccproxy/testing/response_handlers.py +20 -0
  379. ccproxy/utils/__init__.py +0 -6
  380. ccproxy/utils/binary_resolver.py +476 -0
  381. ccproxy/utils/caching.py +327 -0
  382. ccproxy/utils/cli_logging.py +101 -0
  383. ccproxy/utils/command_line.py +251 -0
  384. ccproxy/utils/headers.py +228 -0
  385. ccproxy/utils/model_mapper.py +120 -0
  386. ccproxy/utils/startup_helpers.py +68 -446
  387. ccproxy/utils/version_checker.py +273 -6
  388. ccproxy_api-0.2.0.dist-info/METADATA +212 -0
  389. ccproxy_api-0.2.0.dist-info/RECORD +417 -0
  390. {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0.dist-info}/WHEEL +1 -1
  391. ccproxy_api-0.2.0.dist-info/entry_points.txt +24 -0
  392. ccproxy/__init__.py +0 -4
  393. ccproxy/adapters/__init__.py +0 -11
  394. ccproxy/adapters/base.py +0 -80
  395. ccproxy/adapters/codex/__init__.py +0 -11
  396. ccproxy/adapters/openai/__init__.py +0 -42
  397. ccproxy/adapters/openai/adapter.py +0 -953
  398. ccproxy/adapters/openai/models.py +0 -412
  399. ccproxy/adapters/openai/response_adapter.py +0 -355
  400. ccproxy/adapters/openai/response_models.py +0 -178
  401. ccproxy/api/middleware/headers.py +0 -49
  402. ccproxy/api/middleware/logging.py +0 -180
  403. ccproxy/api/middleware/request_content_logging.py +0 -297
  404. ccproxy/api/middleware/server_header.py +0 -58
  405. ccproxy/api/responses.py +0 -89
  406. ccproxy/api/routes/claude.py +0 -371
  407. ccproxy/api/routes/codex.py +0 -1251
  408. ccproxy/api/routes/metrics.py +0 -1029
  409. ccproxy/api/routes/proxy.py +0 -211
  410. ccproxy/api/services/__init__.py +0 -6
  411. ccproxy/auth/conditional.py +0 -84
  412. ccproxy/auth/credentials_adapter.py +0 -93
  413. ccproxy/auth/models.py +0 -118
  414. ccproxy/auth/oauth/models.py +0 -48
  415. ccproxy/auth/openai/__init__.py +0 -13
  416. ccproxy/auth/openai/credentials.py +0 -166
  417. ccproxy/auth/openai/oauth_client.py +0 -334
  418. ccproxy/auth/openai/storage.py +0 -184
  419. ccproxy/auth/storage/json_file.py +0 -158
  420. ccproxy/auth/storage/keyring.py +0 -189
  421. ccproxy/claude_sdk/__init__.py +0 -18
  422. ccproxy/claude_sdk/options.py +0 -194
  423. ccproxy/claude_sdk/session_pool.py +0 -550
  424. ccproxy/cli/docker/__init__.py +0 -34
  425. ccproxy/cli/docker/adapter_factory.py +0 -157
  426. ccproxy/cli/docker/params.py +0 -274
  427. ccproxy/config/auth.py +0 -153
  428. ccproxy/config/claude.py +0 -348
  429. ccproxy/config/cors.py +0 -79
  430. ccproxy/config/discovery.py +0 -95
  431. ccproxy/config/docker_settings.py +0 -264
  432. ccproxy/config/observability.py +0 -158
  433. ccproxy/config/reverse_proxy.py +0 -31
  434. ccproxy/config/scheduler.py +0 -108
  435. ccproxy/config/server.py +0 -86
  436. ccproxy/config/validators.py +0 -231
  437. ccproxy/core/codex_transformers.py +0 -389
  438. ccproxy/core/http.py +0 -328
  439. ccproxy/core/http_transformers.py +0 -812
  440. ccproxy/core/proxy.py +0 -143
  441. ccproxy/core/validators.py +0 -288
  442. ccproxy/models/errors.py +0 -42
  443. ccproxy/models/messages.py +0 -269
  444. ccproxy/models/requests.py +0 -107
  445. ccproxy/models/responses.py +0 -270
  446. ccproxy/models/types.py +0 -102
  447. ccproxy/observability/__init__.py +0 -51
  448. ccproxy/observability/access_logger.py +0 -457
  449. ccproxy/observability/sse_events.py +0 -303
  450. ccproxy/observability/stats_printer.py +0 -753
  451. ccproxy/observability/storage/__init__.py +0 -1
  452. ccproxy/observability/storage/duckdb_simple.py +0 -677
  453. ccproxy/observability/storage/models.py +0 -70
  454. ccproxy/observability/streaming_response.py +0 -107
  455. ccproxy/pricing/__init__.py +0 -19
  456. ccproxy/pricing/loader.py +0 -251
  457. ccproxy/services/claude_detection_service.py +0 -243
  458. ccproxy/services/codex_detection_service.py +0 -252
  459. ccproxy/services/credentials/__init__.py +0 -55
  460. ccproxy/services/credentials/config.py +0 -105
  461. ccproxy/services/credentials/manager.py +0 -561
  462. ccproxy/services/credentials/oauth_client.py +0 -481
  463. ccproxy/services/proxy_service.py +0 -1827
  464. ccproxy/static/.keep +0 -0
  465. ccproxy/utils/cost_calculator.py +0 -210
  466. ccproxy/utils/disconnection_monitor.py +0 -83
  467. ccproxy/utils/model_mapping.py +0 -199
  468. ccproxy/utils/models_provider.py +0 -150
  469. ccproxy/utils/simple_request_logger.py +0 -284
  470. ccproxy/utils/streaming_metrics.py +0 -199
  471. ccproxy_api-0.1.7.dist-info/METADATA +0 -615
  472. ccproxy_api-0.1.7.dist-info/RECORD +0 -191
  473. ccproxy_api-0.1.7.dist-info/entry_points.txt +0 -4
  474. /ccproxy/{api/middleware/auth.py → auth/models/__init__.py} +0 -0
  475. /ccproxy/{claude_sdk → plugins/claude_sdk}/exceptions.py +0 -0
  476. /ccproxy/{docker → plugins/docker}/models.py +0 -0
  477. /ccproxy/{docker → plugins/docker}/protocol.py +0 -0
  478. /ccproxy/{docker → plugins/docker}/validators.py +0 -0
  479. /ccproxy/{auth/oauth/storage.py → plugins/permissions/handlers/__init__.py} +0 -0
  480. /ccproxy/{api → plugins/permissions}/ui/__init__.py +0 -0
  481. {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0.dist-info}/licenses/LICENSE +0 -0
ccproxy/core/proxy.py DELETED
@@ -1,143 +0,0 @@
1
- """Core proxy abstractions for handling HTTP and WebSocket connections."""
2
-
3
- from abc import ABC, abstractmethod
4
- from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
5
-
6
- from ccproxy.core.types import ProxyRequest, ProxyResponse
7
-
8
-
9
- if TYPE_CHECKING:
10
- from ccproxy.core.http import HTTPClient
11
-
12
-
13
- class BaseProxy(ABC):
14
- """Abstract base class for all proxy implementations."""
15
-
16
- @abstractmethod
17
- async def forward(self, request: ProxyRequest) -> ProxyResponse:
18
- """Forward a request and return the response.
19
-
20
- Args:
21
- request: The proxy request to forward
22
-
23
- Returns:
24
- The proxy response
25
-
26
- Raises:
27
- ProxyError: If the request cannot be forwarded
28
- """
29
- pass
30
-
31
- @abstractmethod
32
- async def close(self) -> None:
33
- """Close any resources held by the proxy."""
34
- pass
35
-
36
-
37
- class HTTPProxy(BaseProxy):
38
- """HTTP proxy implementation using HTTPClient abstractions."""
39
-
40
- def __init__(self, http_client: "HTTPClient") -> None:
41
- """Initialize with an HTTP client.
42
-
43
- Args:
44
- http_client: The HTTP client to use for requests
45
- """
46
- self.http_client = http_client
47
-
48
- async def forward(self, request: ProxyRequest) -> ProxyResponse:
49
- """Forward an HTTP request using the HTTP client.
50
-
51
- Args:
52
- request: The proxy request to forward
53
-
54
- Returns:
55
- The proxy response
56
-
57
- Raises:
58
- ProxyError: If the request cannot be forwarded
59
- """
60
- from ccproxy.core.errors import ProxyError
61
- from ccproxy.core.http import HTTPError
62
-
63
- try:
64
- # Convert ProxyRequest to HTTP client format
65
- body_bytes = None
66
- if request.body is not None:
67
- if isinstance(request.body, bytes):
68
- body_bytes = request.body
69
- elif isinstance(request.body, str):
70
- body_bytes = request.body.encode("utf-8")
71
- elif isinstance(request.body, dict):
72
- import json
73
-
74
- body_bytes = json.dumps(request.body).encode("utf-8")
75
-
76
- # Make the HTTP request
77
- status_code, headers, response_body = await self.http_client.request(
78
- method=request.method.value,
79
- url=request.url,
80
- headers=request.headers,
81
- body=body_bytes,
82
- timeout=request.timeout,
83
- )
84
-
85
- # Convert response body to appropriate format
86
- body: str | bytes | dict[str, Any] | None = response_body
87
- if response_body:
88
- # Try to decode as JSON if content-type suggests it
89
- content_type = headers.get("content-type", "").lower()
90
- if "application/json" in content_type:
91
- try:
92
- import json
93
-
94
- body = json.loads(response_body.decode("utf-8"))
95
- except (json.JSONDecodeError, UnicodeDecodeError):
96
- # Keep as bytes if JSON parsing fails
97
- body = response_body
98
- elif "text/" in content_type:
99
- try:
100
- body = response_body.decode("utf-8")
101
- except UnicodeDecodeError:
102
- # Keep as bytes if text decoding fails
103
- body = response_body
104
-
105
- return ProxyResponse(
106
- status_code=status_code,
107
- headers=headers,
108
- body=body,
109
- )
110
-
111
- except HTTPError as e:
112
- raise ProxyError(f"HTTP request failed: {e}") from e
113
- except Exception as e:
114
- raise ProxyError(f"Unexpected error during HTTP request: {e}") from e
115
-
116
- async def close(self) -> None:
117
- """Close HTTP proxy resources."""
118
- await self.http_client.close()
119
-
120
-
121
- class WebSocketProxy(BaseProxy):
122
- """WebSocket proxy implementation placeholder."""
123
-
124
- async def forward(self, request: ProxyRequest) -> ProxyResponse:
125
- """Forward a WebSocket request."""
126
- raise NotImplementedError("WebSocketProxy.forward not yet implemented")
127
-
128
- async def close(self) -> None:
129
- """Close WebSocket proxy resources."""
130
- pass
131
-
132
-
133
- @runtime_checkable
134
- class ProxyProtocol(Protocol):
135
- """Protocol defining the proxy interface."""
136
-
137
- async def forward(self, request: ProxyRequest) -> ProxyResponse:
138
- """Forward a request and return the response."""
139
- ...
140
-
141
- async def close(self) -> None:
142
- """Close any resources held by the proxy."""
143
- ...
@@ -1,288 +0,0 @@
1
- """Generic validation utilities for the CCProxy API."""
2
-
3
- import re
4
- from pathlib import Path
5
- from typing import Any
6
- from urllib.parse import urlparse
7
-
8
- from ccproxy.core.constants import EMAIL_PATTERN, URL_PATTERN, UUID_PATTERN
9
-
10
-
11
- class ValidationError(Exception):
12
- """Base class for validation errors."""
13
-
14
- pass
15
-
16
-
17
- def validate_email(email: str) -> str:
18
- """Validate email format.
19
-
20
- Args:
21
- email: Email address to validate
22
-
23
- Returns:
24
- The validated email address
25
-
26
- Raises:
27
- ValidationError: If email format is invalid
28
- """
29
- if not isinstance(email, str):
30
- raise ValidationError("Email must be a string")
31
-
32
- if not re.match(EMAIL_PATTERN, email):
33
- raise ValidationError(f"Invalid email format: {email}")
34
-
35
- return email.strip().lower()
36
-
37
-
38
- def validate_url(url: str) -> str:
39
- """Validate URL format.
40
-
41
- Args:
42
- url: URL to validate
43
-
44
- Returns:
45
- The validated URL
46
-
47
- Raises:
48
- ValidationError: If URL format is invalid
49
- """
50
- if not isinstance(url, str):
51
- raise ValidationError("URL must be a string")
52
-
53
- if not re.match(URL_PATTERN, url):
54
- raise ValidationError(f"Invalid URL format: {url}")
55
-
56
- try:
57
- parsed = urlparse(url)
58
- if not parsed.scheme or not parsed.netloc:
59
- raise ValidationError(f"Invalid URL format: {url}")
60
- except Exception as e:
61
- raise ValidationError(f"Invalid URL format: {url}") from e
62
-
63
- return url.strip()
64
-
65
-
66
- def validate_uuid(uuid_str: str) -> str:
67
- """Validate UUID format.
68
-
69
- Args:
70
- uuid_str: UUID string to validate
71
-
72
- Returns:
73
- The validated UUID string
74
-
75
- Raises:
76
- ValidationError: If UUID format is invalid
77
- """
78
- if not isinstance(uuid_str, str):
79
- raise ValidationError("UUID must be a string")
80
-
81
- if not re.match(UUID_PATTERN, uuid_str.lower()):
82
- raise ValidationError(f"Invalid UUID format: {uuid_str}")
83
-
84
- return uuid_str.strip().lower()
85
-
86
-
87
- def validate_path(path: str | Path, must_exist: bool = True) -> Path:
88
- """Validate file system path.
89
-
90
- Args:
91
- path: Path to validate
92
- must_exist: Whether the path must exist
93
-
94
- Returns:
95
- The validated Path object
96
-
97
- Raises:
98
- ValidationError: If path is invalid
99
- """
100
- if isinstance(path, str):
101
- path = Path(path)
102
- elif not isinstance(path, Path):
103
- raise ValidationError("Path must be a string or Path object")
104
-
105
- if must_exist and not path.exists():
106
- raise ValidationError(f"Path does not exist: {path}")
107
-
108
- return path.resolve()
109
-
110
-
111
- def validate_port(port: int | str) -> int:
112
- """Validate port number.
113
-
114
- Args:
115
- port: Port number to validate
116
-
117
- Returns:
118
- The validated port number
119
-
120
- Raises:
121
- ValidationError: If port is invalid
122
- """
123
- if isinstance(port, str):
124
- try:
125
- port = int(port)
126
- except ValueError as e:
127
- raise ValidationError(f"Port must be a valid integer: {port}") from e
128
-
129
- if not isinstance(port, int):
130
- raise ValidationError(f"Port must be an integer: {port}")
131
-
132
- if port < 1 or port > 65535:
133
- raise ValidationError(f"Port must be between 1 and 65535: {port}")
134
-
135
- return port
136
-
137
-
138
- def validate_timeout(timeout: float | int | str) -> float:
139
- """Validate timeout value.
140
-
141
- Args:
142
- timeout: Timeout value to validate
143
-
144
- Returns:
145
- The validated timeout value
146
-
147
- Raises:
148
- ValidationError: If timeout is invalid
149
- """
150
- if isinstance(timeout, str):
151
- try:
152
- timeout = float(timeout)
153
- except ValueError as e:
154
- raise ValidationError(f"Timeout must be a valid number: {timeout}") from e
155
-
156
- if not isinstance(timeout, int | float):
157
- raise ValidationError(f"Timeout must be a number: {timeout}")
158
-
159
- if timeout < 0:
160
- raise ValidationError(f"Timeout must be non-negative: {timeout}")
161
-
162
- return float(timeout)
163
-
164
-
165
- def validate_non_empty_string(value: str, name: str = "value") -> str:
166
- """Validate that a string is not empty.
167
-
168
- Args:
169
- value: String value to validate
170
- name: Name of the field for error messages
171
-
172
- Returns:
173
- The validated string
174
-
175
- Raises:
176
- ValidationError: If string is empty or not a string
177
- """
178
- if not isinstance(value, str):
179
- raise ValidationError(f"{name} must be a string")
180
-
181
- if not value.strip():
182
- raise ValidationError(f"{name} cannot be empty")
183
-
184
- return value.strip()
185
-
186
-
187
- def validate_dict(value: Any, required_keys: list[str] | None = None) -> dict[str, Any]:
188
- """Validate dictionary and required keys.
189
-
190
- Args:
191
- value: Value to validate as dictionary
192
- required_keys: List of required keys
193
-
194
- Returns:
195
- The validated dictionary
196
-
197
- Raises:
198
- ValidationError: If not a dictionary or missing required keys
199
- """
200
- if not isinstance(value, dict):
201
- raise ValidationError("Value must be a dictionary")
202
-
203
- if required_keys:
204
- missing_keys = [key for key in required_keys if key not in value]
205
- if missing_keys:
206
- raise ValidationError(f"Missing required keys: {missing_keys}")
207
-
208
- return value
209
-
210
-
211
- def validate_list(
212
- value: Any, min_length: int = 0, max_length: int | None = None
213
- ) -> list[Any]:
214
- """Validate list and length constraints.
215
-
216
- Args:
217
- value: Value to validate as list
218
- min_length: Minimum list length
219
- max_length: Maximum list length
220
-
221
- Returns:
222
- The validated list
223
-
224
- Raises:
225
- ValidationError: If not a list or length constraints are violated
226
- """
227
- if not isinstance(value, list):
228
- raise ValidationError("Value must be a list")
229
-
230
- if len(value) < min_length:
231
- raise ValidationError(f"List must have at least {min_length} items")
232
-
233
- if max_length is not None and len(value) > max_length:
234
- raise ValidationError(f"List cannot have more than {max_length} items")
235
-
236
- return value
237
-
238
-
239
- def validate_choice(value: Any, choices: list[Any], name: str = "value") -> Any:
240
- """Validate that value is one of the allowed choices.
241
-
242
- Args:
243
- value: Value to validate
244
- choices: List of allowed choices
245
- name: Name of the field for error messages
246
-
247
- Returns:
248
- The validated value
249
-
250
- Raises:
251
- ValidationError: If value is not in choices
252
- """
253
- if value not in choices:
254
- raise ValidationError(f"{name} must be one of {choices}, got: {value}")
255
-
256
- return value
257
-
258
-
259
- def validate_range(
260
- value: float | int,
261
- min_value: float | int | None = None,
262
- max_value: float | int | None = None,
263
- name: str = "value",
264
- ) -> float | int:
265
- """Validate that a numeric value is within a specified range.
266
-
267
- Args:
268
- value: Numeric value to validate
269
- min_value: Minimum allowed value
270
- max_value: Maximum allowed value
271
- name: Name of the field for error messages
272
-
273
- Returns:
274
- The validated value
275
-
276
- Raises:
277
- ValidationError: If value is outside the allowed range
278
- """
279
- if not isinstance(value, int | float):
280
- raise ValidationError(f"{name} must be a number")
281
-
282
- if min_value is not None and value < min_value:
283
- raise ValidationError(f"{name} must be at least {min_value}")
284
-
285
- if max_value is not None and value > max_value:
286
- raise ValidationError(f"{name} must be at most {max_value}")
287
-
288
- return value
ccproxy/models/errors.py DELETED
@@ -1,42 +0,0 @@
1
- """Error response models for Anthropic API compatibility."""
2
-
3
- from typing import Annotated, Any, Literal
4
-
5
- from pydantic import BaseModel, Field
6
-
7
-
8
- class ErrorDetail(BaseModel):
9
- """Error detail information."""
10
-
11
- type: Annotated[str, Field(description="Error type identifier")]
12
- message: Annotated[str, Field(description="Human-readable error message")]
13
-
14
-
15
- class AnthropicError(BaseModel):
16
- """Anthropic API error response format."""
17
-
18
- type: Annotated[Literal["error"], Field(description="Error type")] = "error"
19
- error: Annotated[ErrorDetail, Field(description="Error details")]
20
-
21
-
22
- # Note: Specific error model classes were removed as they were unused.
23
- # Error responses are now forwarded directly from the upstream Claude API
24
- # to preserve the exact error format and headers.
25
-
26
-
27
- def create_error_response(
28
- error_type: str, message: str, status_code: int = 500
29
- ) -> tuple[dict[str, Any], int]:
30
- """
31
- Create a standardized error response.
32
-
33
- Args:
34
- error_type: Type of error (e.g., "invalid_request_error")
35
- message: Human-readable error message
36
- status_code: HTTP status code
37
-
38
- Returns:
39
- Tuple of (error_dict, status_code)
40
- """
41
- error_response = AnthropicError(error=ErrorDetail(type=error_type, message=message))
42
- return error_response.model_dump(), status_code