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,308 @@
1
+ """Function-tool helpers used by the endpoint test harness."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import inspect
6
+ import math
7
+ from typing import Any
8
+
9
+ import structlog
10
+
11
+
12
+ logger = structlog.get_logger(__name__)
13
+
14
+
15
+ def get_weather(location: str, unit: str = "celsius") -> dict[str, Any]:
16
+ """Get current weather for a location."""
17
+ logger.info("weather_request", location=location, unit=unit)
18
+ result = {
19
+ "location": location,
20
+ "temperature": 22 if unit == "celsius" else 72,
21
+ "unit": unit,
22
+ "condition": "sunny",
23
+ "humidity": 65,
24
+ "wind_speed": 10,
25
+ }
26
+ logger.info("weather_result", result=result)
27
+ return result
28
+
29
+
30
+ def calculate_distance(
31
+ lat1: float, lon1: float, lat2: float, lon2: float
32
+ ) -> dict[str, Any]:
33
+ """Calculate distance between two geographic coordinates."""
34
+ logger.info(
35
+ "distance_calculation_start", lat1=lat1, lon1=lon1, lat2=lat2, lon2=lon2
36
+ )
37
+
38
+ lat1_r = math.radians(lat1)
39
+ lon1_r = math.radians(lon1)
40
+ lat2_r = math.radians(lat2)
41
+ lon2_r = math.radians(lon2)
42
+
43
+ dlat = lat2_r - lat1_r
44
+ dlon = lon2_r - lon1_r
45
+ a = (
46
+ math.sin(dlat / 2) ** 2
47
+ + math.cos(lat1_r) * math.cos(lat2_r) * math.sin(dlon / 2) ** 2
48
+ )
49
+ c = 2 * math.asin(math.sqrt(a))
50
+ distance_km = 6371 * c
51
+
52
+ result = {
53
+ "distance_km": round(distance_km, 2),
54
+ "distance_miles": round(distance_km * 0.621371, 2),
55
+ "coordinates": {
56
+ "start": {"lat": lat1, "lon": lon1},
57
+ "end": {"lat": lat2, "lon": lon2},
58
+ },
59
+ }
60
+ logger.info("distance_calculation_result", result=result)
61
+ return result
62
+
63
+
64
+ def calculate(expression: str) -> dict[str, Any]:
65
+ """Perform basic arithmetic calculations."""
66
+ logger.info("calculation_request", expression=expression)
67
+ try:
68
+ safe_expression = expression.replace("^", "**")
69
+ result = eval(safe_expression)
70
+ response = {"expression": expression, "result": result, "success": True}
71
+ except Exception as exc: # noqa: BLE001 - surface evaluation errors verbatim
72
+ response = {"expression": expression, "error": str(exc), "success": False}
73
+
74
+ logger.info("calculation_result", response=response)
75
+ return response
76
+
77
+
78
+ def generate_json_schema_for_function(func: Any) -> dict[str, Any]:
79
+ """Generate JSON schema for a function."""
80
+ sig = inspect.signature(func)
81
+ schema: dict[str, Any] = {"type": "object", "properties": {}, "required": []}
82
+
83
+ for param_name, param in sig.parameters.items():
84
+ prop_schema = {"type": "string"}
85
+
86
+ if param.annotation is str:
87
+ prop_schema = {"type": "string"}
88
+ elif param.annotation is float:
89
+ prop_schema = {"type": "number", "format": "float"}
90
+ elif param.annotation is int:
91
+ prop_schema = {"type": "integer"}
92
+
93
+ if func.__doc__:
94
+ lines = func.__doc__.strip().split("\n")
95
+ for line in lines:
96
+ if param_name in line and ":" in line:
97
+ desc = line.split(":", 1)[1].strip()
98
+ prop_schema["description"] = desc
99
+ break
100
+
101
+ schema["properties"][param_name] = prop_schema
102
+
103
+ if param.default == inspect.Parameter.empty:
104
+ required_list = schema["required"]
105
+ if isinstance(required_list, list):
106
+ required_list.append(param_name)
107
+
108
+ return schema
109
+
110
+
111
+ def handle_tool_call(tool_name: str, tool_input: dict[str, Any]) -> dict[str, Any]:
112
+ """Handle tool calls by routing to appropriate functions."""
113
+ logger.info("tool_call_start", tool_name=tool_name, tool_input=tool_input)
114
+
115
+ if tool_name == "get_weather":
116
+ result = get_weather(**tool_input)
117
+ elif tool_name == "calculate_distance":
118
+ numeric_args = {
119
+ key: float(value) if isinstance(value, str) else value
120
+ for key, value in tool_input.items()
121
+ }
122
+ result = calculate_distance(**numeric_args)
123
+ elif tool_name == "calculate":
124
+ result = calculate(**tool_input)
125
+ else:
126
+ result = {"error": f"Unknown tool: {tool_name}"}
127
+ logger.error("unknown_tool_requested", tool_name=tool_name)
128
+
129
+ logger.info("tool_call_result", result=result)
130
+ return result
131
+
132
+
133
+ def create_openai_tools() -> list[dict[str, Any]]:
134
+ """Create OpenAI-compatible tool definitions with JSON schemas."""
135
+ weather_schema = generate_json_schema_for_function(get_weather)
136
+ distance_schema = generate_json_schema_for_function(calculate_distance)
137
+ calc_schema = generate_json_schema_for_function(calculate)
138
+
139
+ return [
140
+ {
141
+ "type": "function",
142
+ "function": {
143
+ "name": "get_weather",
144
+ "description": "Get current weather information for a specific location",
145
+ "parameters": weather_schema,
146
+ },
147
+ },
148
+ {
149
+ "type": "function",
150
+ "function": {
151
+ "name": "calculate_distance",
152
+ "description": "Calculate the distance between two geographic coordinates",
153
+ "parameters": distance_schema,
154
+ },
155
+ },
156
+ {
157
+ "type": "function",
158
+ "function": {
159
+ "name": "calculate",
160
+ "description": "Perform basic arithmetic calculations",
161
+ "parameters": calc_schema,
162
+ },
163
+ },
164
+ ]
165
+
166
+
167
+ def create_anthropic_tools() -> list[dict[str, Any]]:
168
+ """Create Anthropic-compatible tool definitions with JSON schemas."""
169
+ weather_schema = generate_json_schema_for_function(get_weather)
170
+ distance_schema = generate_json_schema_for_function(calculate_distance)
171
+ calc_schema = generate_json_schema_for_function(calculate)
172
+
173
+ return [
174
+ {
175
+ "type": "custom",
176
+ "name": "get_weather",
177
+ "description": "Get current weather information for a specific location",
178
+ "input_schema": weather_schema,
179
+ },
180
+ {
181
+ "type": "custom",
182
+ "name": "calculate_distance",
183
+ "description": "Calculate the distance between two geographic coordinates",
184
+ "input_schema": distance_schema,
185
+ },
186
+ {
187
+ "type": "custom",
188
+ "name": "calculate",
189
+ "description": "Perform basic arithmetic calculations",
190
+ "input_schema": calc_schema,
191
+ },
192
+ ]
193
+
194
+
195
+ def create_codex_tools() -> list[dict[str, Any]]:
196
+ """Create Codex-compatible tool definitions."""
197
+ weather_schema = generate_json_schema_for_function(get_weather)
198
+ distance_schema = generate_json_schema_for_function(calculate_distance)
199
+ calc_schema = generate_json_schema_for_function(calculate)
200
+
201
+ return [
202
+ {
203
+ "type": "function",
204
+ "name": "get_weather",
205
+ "description": "Get current weather information for a specific location",
206
+ "parameters": weather_schema,
207
+ },
208
+ {
209
+ "type": "function",
210
+ "name": "calculate_distance",
211
+ "description": "Calculate the distance between two geographic coordinates",
212
+ "parameters": distance_schema,
213
+ },
214
+ {
215
+ "type": "function",
216
+ "name": "calculate",
217
+ "description": "Perform basic arithmetic calculations",
218
+ "parameters": calc_schema,
219
+ },
220
+ ]
221
+
222
+
223
+ _WEATHER_SCHEMA = generate_json_schema_for_function(get_weather)
224
+ _DISTANCE_SCHEMA = generate_json_schema_for_function(calculate_distance)
225
+ _CALC_SCHEMA = generate_json_schema_for_function(calculate)
226
+
227
+
228
+ OPENAI_TOOLS = [
229
+ {
230
+ "type": "function",
231
+ "function": {
232
+ "name": "get_weather",
233
+ "description": "Get current weather information for a specific location",
234
+ "parameters": _WEATHER_SCHEMA,
235
+ },
236
+ },
237
+ {
238
+ "type": "function",
239
+ "function": {
240
+ "name": "calculate_distance",
241
+ "description": "Calculate the distance between two geographic coordinates",
242
+ "parameters": _DISTANCE_SCHEMA,
243
+ },
244
+ },
245
+ {
246
+ "type": "function",
247
+ "function": {
248
+ "name": "calculate",
249
+ "description": "Perform basic arithmetic calculations",
250
+ "parameters": _CALC_SCHEMA,
251
+ },
252
+ },
253
+ ]
254
+
255
+
256
+ ANTHROPIC_TOOLS = [
257
+ {
258
+ "type": "custom",
259
+ "name": "get_weather",
260
+ "description": "Get current weather information for a specific location",
261
+ "input_schema": _WEATHER_SCHEMA,
262
+ },
263
+ {
264
+ "type": "custom",
265
+ "name": "calculate_distance",
266
+ "description": "Calculate the distance between two geographic coordinates",
267
+ "input_schema": _DISTANCE_SCHEMA,
268
+ },
269
+ {
270
+ "type": "custom",
271
+ "name": "calculate",
272
+ "description": "Perform basic arithmetic calculations",
273
+ "input_schema": _CALC_SCHEMA,
274
+ },
275
+ ]
276
+
277
+
278
+ CODEX_TOOLS = [
279
+ {
280
+ "type": "function",
281
+ "name": "get_weather",
282
+ "description": "Get current weather information for a specific location",
283
+ "parameters": _WEATHER_SCHEMA,
284
+ },
285
+ {
286
+ "type": "function",
287
+ "name": "calculate_distance",
288
+ "description": "Calculate the distance between two geographic coordinates",
289
+ "parameters": _DISTANCE_SCHEMA,
290
+ },
291
+ {
292
+ "type": "function",
293
+ "name": "calculate",
294
+ "description": "Perform basic arithmetic calculations",
295
+ "parameters": _CALC_SCHEMA,
296
+ },
297
+ ]
298
+
299
+
300
+ __all__ = [
301
+ "handle_tool_call",
302
+ "create_openai_tools",
303
+ "create_anthropic_tools",
304
+ "create_codex_tools",
305
+ "OPENAI_TOOLS",
306
+ "ANTHROPIC_TOOLS",
307
+ "CODEX_TOOLS",
308
+ ]
@@ -180,7 +180,7 @@ class RealisticMockResponseGenerator:
180
180
  # Convert to OpenAI format
181
181
  openai_chunks = []
182
182
  for chunk in anthropic_chunks:
183
- # Use simplified conversion logic
183
+ # Use basic conversion logic
184
184
  if chunk.get("type") == "message_start":
185
185
  openai_chunks.append(
186
186
  {
@@ -227,6 +227,75 @@ class RealisticMockResponseGenerator:
227
227
 
228
228
  return openai_chunks
229
229
 
230
+ def generate_short_response(self, model: str | None = None) -> dict[str, Any]:
231
+ """Generate a short mock response."""
232
+ content, input_tokens, output_tokens = self.generate_response_content(
233
+ "short", model or "claude-3-sonnet"
234
+ )
235
+ return {
236
+ "id": f"msg_{random.randint(1000, 9999)}",
237
+ "type": "message",
238
+ "role": "assistant",
239
+ "content": [{"type": "text", "text": content}],
240
+ "model": model or "claude-3-sonnet",
241
+ "stop_reason": "end_turn",
242
+ "usage": {"input_tokens": input_tokens, "output_tokens": output_tokens},
243
+ }
244
+
245
+ def generate_medium_response(self, model: str | None = None) -> dict[str, Any]:
246
+ """Generate a medium mock response."""
247
+ content, input_tokens, output_tokens = self.generate_response_content(
248
+ "medium", model or "claude-3-sonnet"
249
+ )
250
+ return {
251
+ "id": f"msg_{random.randint(1000, 9999)}",
252
+ "type": "message",
253
+ "role": "assistant",
254
+ "content": [{"type": "text", "text": content}],
255
+ "model": model or "claude-3-sonnet",
256
+ "stop_reason": "end_turn",
257
+ "usage": {"input_tokens": input_tokens, "output_tokens": output_tokens},
258
+ }
259
+
260
+ def generate_long_response(self, model: str | None = None) -> dict[str, Any]:
261
+ """Generate a long mock response."""
262
+ content, input_tokens, output_tokens = self.generate_response_content(
263
+ "long", model or "claude-3-sonnet"
264
+ )
265
+ return {
266
+ "id": f"msg_{random.randint(1000, 9999)}",
267
+ "type": "message",
268
+ "role": "assistant",
269
+ "content": [{"type": "text", "text": content}],
270
+ "model": model or "claude-3-sonnet",
271
+ "stop_reason": "end_turn",
272
+ "usage": {"input_tokens": input_tokens, "output_tokens": output_tokens},
273
+ }
274
+
275
+ def generate_tool_use_response(self, model: str | None = None) -> dict[str, Any]:
276
+ """Generate a tool use mock response."""
277
+ content, input_tokens, output_tokens = self.generate_response_content(
278
+ "tool_use", model or "claude-3-sonnet"
279
+ )
280
+ random.randint(1, 1000)
281
+ return {
282
+ "id": f"msg_{random.randint(1000, 9999)}",
283
+ "type": "message",
284
+ "role": "assistant",
285
+ "content": [
286
+ {"type": "text", "text": content},
287
+ {
288
+ "type": "tool_use",
289
+ "id": f"toolu_{random.randint(1000, 9999)}",
290
+ "name": "calculator",
291
+ "input": {"expression": "23 * 45"},
292
+ },
293
+ ],
294
+ "model": model or "claude-3-sonnet",
295
+ "stop_reason": "tool_use",
296
+ "usage": {"input_tokens": input_tokens, "output_tokens": output_tokens},
297
+ }
298
+
230
299
  def calculate_realistic_cost(
231
300
  self,
232
301
  input_tokens: int,
@@ -57,6 +57,20 @@ class ResponseHandler:
57
57
  "format": scenario.api_format,
58
58
  }
59
59
 
60
+ except json.JSONDecodeError as e:
61
+ return {
62
+ "status_code": response.status_code,
63
+ "headers": dict(response.headers),
64
+ "error": f"Failed to parse {scenario.api_format} JSON response: {str(e)}",
65
+ "raw_text": response.text[:500] if hasattr(response, "text") else "",
66
+ }
67
+ except (OSError, PermissionError) as e:
68
+ return {
69
+ "status_code": response.status_code,
70
+ "headers": dict(response.headers),
71
+ "error": f"IO/Permission error parsing {scenario.api_format} response: {str(e)}",
72
+ "raw_text": response.text[:500] if hasattr(response, "text") else "",
73
+ }
60
74
  except Exception as e:
61
75
  return {
62
76
  "status_code": response.status_code,
@@ -109,6 +123,12 @@ class ResponseHandler:
109
123
  "format": scenario.api_format,
110
124
  }
111
125
 
126
+ except (OSError, PermissionError) as e:
127
+ return {
128
+ "status_code": response.status_code,
129
+ "headers": dict(response.headers),
130
+ "error": f"IO/Permission error processing {scenario.api_format} stream: {str(e)}",
131
+ }
112
132
  except Exception as e:
113
133
  return {
114
134
  "status_code": response.status_code,
ccproxy/utils/__init__.py CHANGED
@@ -1,14 +1,8 @@
1
1
  """Utility modules for shared functionality across the application."""
2
2
 
3
- from .cost_calculator import calculate_cost_breakdown, calculate_token_cost
4
- from .disconnection_monitor import monitor_disconnection, monitor_stuck_stream
5
3
  from .id_generator import generate_client_id
6
4
 
7
5
 
8
6
  __all__ = [
9
- "calculate_token_cost",
10
- "calculate_cost_breakdown",
11
- "monitor_disconnection",
12
- "monitor_stuck_stream",
13
7
  "generate_client_id",
14
8
  ]