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,113 @@
1
+ """Health check implementation for Claude SDK plugin."""
2
+
3
+ from typing import TYPE_CHECKING, Literal, cast
4
+
5
+ from ccproxy.core.plugins.protocol import HealthCheckResult
6
+
7
+
8
+ if TYPE_CHECKING:
9
+ from .config import ClaudeSDKSettings
10
+ from .detection_service import ClaudeSDKDetectionService
11
+
12
+
13
+ async def claude_sdk_health_check(
14
+ config: "ClaudeSDKSettings | None",
15
+ detection_service: "ClaudeSDKDetectionService | None",
16
+ *,
17
+ version: str,
18
+ ) -> HealthCheckResult:
19
+ """Perform health check for Claude SDK plugin.
20
+
21
+ Args:
22
+ config: Claude SDK plugin configuration
23
+ detection_service: Claude CLI detection service
24
+
25
+ Returns:
26
+ HealthCheckResult with plugin status
27
+ """
28
+ checks = []
29
+ status: str = "pass"
30
+
31
+ # Check if plugin is enabled
32
+ if not config or not config.enabled:
33
+ return HealthCheckResult(
34
+ status="fail",
35
+ componentId="plugin-claude_sdk",
36
+ output="Plugin is disabled",
37
+ version=version,
38
+ details={"enabled": False},
39
+ )
40
+
41
+ # Check Claude CLI detection
42
+ if detection_service:
43
+ cli_version = detection_service.get_version()
44
+ cli_path = detection_service.get_cli_path()
45
+ is_available = detection_service.is_claude_available()
46
+ cli_info = detection_service.get_cli_health_info()
47
+
48
+ if is_available and cli_path:
49
+ checks.append(f"CLI: {cli_version or 'detected'} at {cli_path}")
50
+ else:
51
+ checks.append("CLI: not found")
52
+ status = "warn" # CLI not found is a warning, not a failure
53
+ else:
54
+ checks.append("CLI: detection service not initialized")
55
+ status = "warn"
56
+
57
+ # Check configuration
58
+ if config:
59
+ checks.append(f"Models: {len(config.models_endpoint)} configured")
60
+ checks.append(
61
+ f"Session pool: {'enabled' if config.session_pool_enabled else 'disabled'}"
62
+ )
63
+ checks.append(
64
+ f"Streaming: {'enabled' if config.supports_streaming else 'disabled'}"
65
+ )
66
+ else:
67
+ checks.append("Config: not loaded")
68
+ status = "fail"
69
+
70
+ # Standardized details
71
+ from ccproxy.core.plugins.models import (
72
+ CLIHealth,
73
+ ConfigHealth,
74
+ ProviderHealthDetails,
75
+ )
76
+
77
+ cli_health = None
78
+ if detection_service:
79
+ path_list = detection_service.get_cli_path()
80
+ cli_status = cli_info.status.value if cli_info else "unknown"
81
+ cli_health = CLIHealth(
82
+ available=bool(detection_service.is_claude_available()),
83
+ status=cli_status,
84
+ version=detection_service.get_version(),
85
+ path=(path_list[0] if path_list else None),
86
+ )
87
+
88
+ details = ProviderHealthDetails(
89
+ provider="claude_sdk",
90
+ enabled=bool(config and config.enabled),
91
+ base_url=None,
92
+ cli=cli_health,
93
+ auth=None,
94
+ config=ConfigHealth(
95
+ model_count=len(config.models_endpoint)
96
+ if config and config.models_endpoint
97
+ else 0,
98
+ supports_openai_format=config.supports_streaming if config else None,
99
+ extra={
100
+ "session_pool_enabled": bool(config.session_pool_enabled)
101
+ if config
102
+ else None
103
+ },
104
+ ),
105
+ ).model_dump()
106
+
107
+ return HealthCheckResult(
108
+ status=cast(Literal["pass", "warn", "fail"], status),
109
+ componentId="plugin-claude_sdk",
110
+ output="; ".join(checks),
111
+ version=version,
112
+ details=details,
113
+ )
@@ -0,0 +1,115 @@
1
+ """Hook integration for Claude SDK plugin to emit streaming metrics."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import datetime
6
+ from typing import Any
7
+
8
+ from ccproxy.core.logging import get_plugin_logger
9
+ from ccproxy.core.plugins.hooks import Hook, HookContext, HookEvent, HookManager
10
+
11
+
12
+ logger = get_plugin_logger(__name__)
13
+
14
+
15
+ class ClaudeSDKStreamingHook(Hook):
16
+ """Hook for emitting Claude SDK streaming metrics.
17
+
18
+ This hook handles streaming completion events from claude_sdk and emits
19
+ PROVIDER_STREAM_END events with usage metrics for access logging.
20
+ """
21
+
22
+ name = "claude_sdk_streaming_metrics"
23
+ events = [] # We'll emit events directly, not listen to them
24
+ priority = 700 # HookLayer.METRICS
25
+
26
+ def __init__(self, hook_manager: HookManager | None = None) -> None:
27
+ """Initialize the Claude SDK streaming hook.
28
+
29
+ Args:
30
+ hook_manager: Hook manager for emitting events
31
+ """
32
+ self.hook_manager = hook_manager
33
+
34
+ async def emit_stream_end(
35
+ self,
36
+ request_id: str,
37
+ usage_metrics: dict[str, Any],
38
+ provider: str = "claude_sdk",
39
+ url: str = "claude-sdk://direct",
40
+ method: str = "POST",
41
+ total_chunks: int = 0,
42
+ total_bytes: int = 0,
43
+ ) -> None:
44
+ """Emit PROVIDER_STREAM_END event with usage metrics.
45
+
46
+ Args:
47
+ request_id: Request ID for correlation
48
+ usage_metrics: Dictionary containing token counts and costs
49
+ provider: Provider name (default: claude_sdk)
50
+ url: URL or endpoint identifier
51
+ method: HTTP method
52
+ total_chunks: Number of chunks streamed
53
+ total_bytes: Total bytes streamed
54
+ """
55
+ if not self.hook_manager:
56
+ logger.debug(
57
+ "no_hook_manager_for_stream_end",
58
+ request_id=request_id,
59
+ provider=provider,
60
+ )
61
+ return
62
+
63
+ try:
64
+ # Normalize usage metrics to standard format
65
+ normalized_metrics = {
66
+ "input_tokens": usage_metrics.get("tokens_input", 0),
67
+ "output_tokens": usage_metrics.get("tokens_output", 0),
68
+ "cache_read_input_tokens": usage_metrics.get("cache_read_tokens", 0),
69
+ "cache_creation_input_tokens": usage_metrics.get(
70
+ "cache_write_tokens", 0
71
+ ),
72
+ "cost_usd": usage_metrics.get("cost_usd", 0.0),
73
+ "model": usage_metrics.get("model", ""),
74
+ }
75
+
76
+ stream_end_context = HookContext(
77
+ event=HookEvent.PROVIDER_STREAM_END,
78
+ timestamp=datetime.now(),
79
+ provider=provider,
80
+ data={
81
+ "url": url,
82
+ "method": method,
83
+ "request_id": request_id,
84
+ "total_chunks": total_chunks,
85
+ "total_bytes": total_bytes,
86
+ "usage_metrics": normalized_metrics,
87
+ },
88
+ metadata={
89
+ "request_id": request_id,
90
+ },
91
+ )
92
+
93
+ await self.hook_manager.emit_with_context(stream_end_context)
94
+
95
+ logger.info(
96
+ "claude_sdk_stream_end_emitted",
97
+ request_id=request_id,
98
+ tokens_input=normalized_metrics["input_tokens"],
99
+ tokens_output=normalized_metrics["output_tokens"],
100
+ cost_usd=normalized_metrics["cost_usd"],
101
+ model=normalized_metrics["model"],
102
+ )
103
+
104
+ except Exception as e:
105
+ logger.error(
106
+ "claude_sdk_hook_emission_failed",
107
+ event="PROVIDER_STREAM_END",
108
+ error=str(e),
109
+ request_id=request_id,
110
+ exc_info=e,
111
+ )
112
+
113
+ async def __call__(self, context: HookContext) -> None:
114
+ """Handle hook events (not used for this hook as we emit directly)."""
115
+ pass
@@ -13,19 +13,20 @@ from collections.abc import Callable
13
13
  # Type alias for metrics factory function
14
14
  from typing import Any, TypeAlias
15
15
 
16
- import structlog
17
- from claude_code_sdk import ClaudeCodeOptions
16
+ from claude_agent_sdk import ClaudeAgentOptions
18
17
 
19
- from ccproxy.claude_sdk.session_client import SessionClient
20
- from ccproxy.claude_sdk.session_pool import SessionPool
21
- from ccproxy.config.settings import Settings
22
18
  from ccproxy.core.errors import ClaudeProxyError
19
+ from ccproxy.core.logging import get_plugin_logger
23
20
 
21
+ from .config import ClaudeSDKSettings, SessionPoolSettings
22
+ from .session_client import SessionClient
23
+ from .session_pool import SessionPool
24
24
 
25
- logger = structlog.get_logger(__name__)
26
25
 
26
+ # Type alias for metrics factory function
27
+ MetricsFactory: TypeAlias = Callable[[], Any]
27
28
 
28
- MetricsFactory: TypeAlias = Callable[[], Any | None]
29
+ logger = get_plugin_logger()
29
30
 
30
31
 
31
32
  class SessionManager:
@@ -33,21 +34,18 @@ class SessionManager:
33
34
 
34
35
  def __init__(
35
36
  self,
36
- settings: Settings,
37
+ config: ClaudeSDKSettings,
37
38
  metrics_factory: MetricsFactory | None = None,
38
39
  ) -> None:
39
40
  """Initialize SessionManager with optional settings and metrics factory.
40
41
 
41
42
  Args:
42
- settings: Optional settings containing session pool configuration
43
+ config: Plugin-specific configuration for Claude SDK
43
44
  metrics_factory: Optional callable that returns a metrics instance.
44
45
  If None, no metrics will be used.
45
46
  """
46
- import structlog
47
-
48
- logger = structlog.get_logger(__name__)
49
47
 
50
- self._settings = settings
48
+ self.config = config
51
49
  self._session_pool: SessionPool | None = None
52
50
  self._lock = asyncio.Lock()
53
51
  self._metrics_factory = metrics_factory
@@ -56,14 +54,36 @@ class SessionManager:
56
54
  session_pool_enabled = self._should_enable_session_pool()
57
55
  logger.debug(
58
56
  "session_manager_init",
59
- has_settings=bool(settings),
57
+ has_config=bool(config),
60
58
  has_metrics_factory=bool(metrics_factory),
61
59
  session_pool_enabled=session_pool_enabled,
62
60
  )
63
61
 
64
62
  if session_pool_enabled:
65
- self._session_pool = SessionPool(settings.claude.sdk_session_pool)
66
- logger.info(
63
+ # Convert core settings to plugin settings
64
+ core_pool_settings = self.config.sdk_session_pool
65
+ if core_pool_settings is None:
66
+ logger.debug("session_pool_disabled", reason="no_settings")
67
+ return
68
+
69
+ plugin_pool_settings = SessionPoolSettings(
70
+ enabled=core_pool_settings.enabled,
71
+ session_ttl=core_pool_settings.session_ttl,
72
+ max_sessions=core_pool_settings.max_sessions,
73
+ cleanup_interval=getattr(core_pool_settings, "cleanup_interval", 300),
74
+ idle_threshold=getattr(core_pool_settings, "idle_threshold", 300),
75
+ connection_recovery=getattr(
76
+ core_pool_settings, "connection_recovery", True
77
+ ),
78
+ stream_first_chunk_timeout=getattr(
79
+ core_pool_settings, "stream_first_chunk_timeout", 8
80
+ ),
81
+ stream_ongoing_timeout=getattr(
82
+ core_pool_settings, "stream_ongoing_timeout", 60
83
+ ),
84
+ )
85
+ self._session_pool = SessionPool(plugin_pool_settings)
86
+ logger.debug(
67
87
  "session_manager_session_pool_initialized",
68
88
  session_ttl=self._session_pool.config.session_ttl,
69
89
  max_sessions=self._session_pool.config.max_sessions,
@@ -77,21 +97,12 @@ class SessionManager:
77
97
 
78
98
  def _should_enable_session_pool(self) -> bool:
79
99
  """Check if session pool should be enabled."""
80
- import structlog
81
-
82
- logger = structlog.get_logger(__name__)
83
100
 
84
- if not self._settings:
85
- logger.debug("session_pool_check", decision="no_settings", enabled=False)
101
+ if not self.config:
102
+ logger.debug("session_pool_check", decision="no_config", enabled=False)
86
103
  return False
87
104
 
88
- if not hasattr(self._settings, "claude"):
89
- logger.debug(
90
- "session_pool_check", decision="no_claude_settings", enabled=False
91
- )
92
- return False
93
-
94
- session_pool_settings = getattr(self._settings.claude, "sdk_session_pool", None)
105
+ session_pool_settings = getattr(self.config, "sdk_session_pool", None)
95
106
  if not session_pool_settings:
96
107
  logger.debug(
97
108
  "session_pool_check", decision="no_session_pool_settings", enabled=False
@@ -121,11 +132,10 @@ class SessionManager:
121
132
  async def get_session_client(
122
133
  self,
123
134
  session_id: str,
124
- options: ClaudeCodeOptions,
135
+ options: ClaudeAgentOptions,
125
136
  ) -> SessionClient:
126
137
  """Get session-aware client."""
127
138
 
128
- logger = structlog.get_logger(__name__)
129
139
  logger.debug(
130
140
  "session_manager_get_session_client",
131
141
  session_id=session_id,
@@ -161,7 +171,7 @@ class SessionManager:
161
171
  )
162
172
  return False
163
173
 
164
- logger.info(
174
+ logger.debug(
165
175
  "session_manager_interrupt_session",
166
176
  session_id=session_id,
167
177
  )
@@ -178,7 +188,7 @@ class SessionManager:
178
188
  logger.warning("session_manager_interrupt_all_no_pool")
179
189
  return 0
180
190
 
181
- logger.info("session_manager_interrupt_all_sessions")
191
+ logger.debug("session_manager_interrupt_all_sessions")
182
192
  return await self._session_pool.interrupt_all_sessions()
183
193
 
184
194
  async def get_session_pool_stats(self) -> dict[str, Any]:
@@ -11,10 +11,10 @@ from dataclasses import dataclass, field
11
11
  from enum import Enum
12
12
  from typing import Any, TypeVar
13
13
 
14
- import structlog
14
+ from ccproxy.core.logging import get_plugin_logger
15
15
 
16
16
 
17
- logger = structlog.get_logger(__name__)
17
+ logger = get_plugin_logger()
18
18
 
19
19
  T = TypeVar("T")
20
20
 
@@ -150,7 +150,7 @@ class MessageQueue:
150
150
  listener = QueueListener(listener_id)
151
151
  self._listeners[listener.listener_id] = listener
152
152
 
153
- logger.debug(
153
+ logger.trace(
154
154
  "message_queue_listener_added",
155
155
  listener_id=listener.listener_id,
156
156
  active_listeners=len(self._listeners),
@@ -169,7 +169,7 @@ class MessageQueue:
169
169
  listener = self._listeners.pop(listener_id)
170
170
  listener.close()
171
171
 
172
- logger.debug(
172
+ logger.trace(
173
173
  "message_queue_listener_removed",
174
174
  listener_id=listener_id,
175
175
  active_listeners=len(self._listeners),
@@ -242,7 +242,7 @@ class MessageQueue:
242
242
  if delivered_count == 0:
243
243
  self._total_messages_discarded += 1
244
244
 
245
- logger.debug(
245
+ logger.trace(
246
246
  "message_queue_broadcast",
247
247
  listeners_count=len(self._listeners),
248
248
  delivered_count=delivered_count,
@@ -265,7 +265,7 @@ class MessageQueue:
265
265
  with contextlib.suppress(asyncio.QueueFull):
266
266
  listener._queue.put_nowait(queue_msg)
267
267
 
268
- logger.debug(
268
+ logger.trace(
269
269
  "message_queue_broadcast_error",
270
270
  error_type=type(error).__name__,
271
271
  listeners_count=len(self._listeners),
@@ -281,7 +281,7 @@ class MessageQueue:
281
281
  with contextlib.suppress(asyncio.QueueFull):
282
282
  listener._queue.put_nowait(queue_msg)
283
283
 
284
- logger.debug(
284
+ logger.trace(
285
285
  "message_queue_broadcast_complete",
286
286
  listeners_count=len(self._listeners),
287
287
  )
@@ -296,7 +296,7 @@ class MessageQueue:
296
296
  with contextlib.suppress(asyncio.QueueFull):
297
297
  listener._queue.put_nowait(queue_msg)
298
298
 
299
- logger.debug(
299
+ logger.trace(
300
300
  "message_queue_broadcast_shutdown",
301
301
  listeners_count=len(self._listeners),
302
302
  message="Shutdown signal sent to all listeners due to interrupt",
@@ -4,7 +4,7 @@ This module provides Pydantic models that mirror the Claude SDK types from the
4
4
  official claude-code-sdk-python repository. These models enable strong typing
5
5
  throughout the proxy system and provide runtime validation.
6
6
 
7
- Based on: https://github.com/anthropics/claude-code-sdk-python/blob/main/src/claude_code_sdk/types.py
7
+ Based on: https://github.com/anthropics/claude-code-sdk-python/blob/main/src/claude_agent_sdk/types.py
8
8
  """
9
9
 
10
10
  from __future__ import annotations
@@ -12,12 +12,12 @@ from __future__ import annotations
12
12
  from typing import Annotated, Any, Literal, TypeVar, cast
13
13
 
14
14
  # Import Claude SDK types for isinstance checks
15
- from claude_code_sdk import TextBlock as SDKTextBlock
16
- from claude_code_sdk import ToolResultBlock as SDKToolResultBlock
17
- from claude_code_sdk import ToolUseBlock as SDKToolUseBlock
15
+ from claude_agent_sdk import TextBlock as SDKTextBlock
16
+ from claude_agent_sdk import ToolResultBlock as SDKToolResultBlock
17
+ from claude_agent_sdk import ToolUseBlock as SDKToolUseBlock
18
18
  from pydantic import BaseModel, ConfigDict, Field, field_validator
19
19
 
20
- from ccproxy.models.requests import Usage
20
+ from ccproxy.llms.models import anthropic as anthropic_models
21
21
 
22
22
 
23
23
  # Type variables for generic functions
@@ -69,7 +69,7 @@ class ToolUseBlock(BaseModel):
69
69
  "id": self.id,
70
70
  "name": self.name,
71
71
  "input": self.input,
72
- "source": "claude_code_sdk",
72
+ "source": "claude_agent_sdk",
73
73
  }
74
74
 
75
75
 
@@ -96,7 +96,7 @@ class ToolResultBlock(BaseModel):
96
96
  "tool_use_id": self.tool_use_id,
97
97
  "content": self.content,
98
98
  "is_error": self.is_error,
99
- "source": "claude_code_sdk",
99
+ "source": "claude_agent_sdk",
100
100
  }
101
101
 
102
102
 
@@ -259,7 +259,6 @@ class ResultMessage(BaseModel):
259
259
  )
260
260
  result: str | None = Field(None, description="Result string if available")
261
261
 
262
- # Add computed properties for backward compatibility
263
262
  @property
264
263
  def stop_reason(self) -> str:
265
264
  """Get stop reason from result or default to end_turn."""
@@ -268,11 +267,11 @@ class ResultMessage(BaseModel):
268
267
  return "end_turn"
269
268
 
270
269
  @property
271
- def usage_model(self) -> Usage:
272
- """Get usage information as a Usage model for backward compatibility."""
270
+ def usage_model(self) -> anthropic_models.Usage:
271
+ """Get usage information as a Usage model."""
273
272
  if self.usage is None:
274
- return Usage()
275
- return Usage.model_validate(self.usage)
273
+ return anthropic_models.Usage(input_tokens=0, output_tokens=0)
274
+ return anthropic_models.Usage.model_validate(self.usage)
276
275
 
277
276
  model_config = ConfigDict(extra="allow")
278
277
 
@@ -282,7 +281,7 @@ class SDKMessageMode(SystemMessage):
282
281
  """Custom content block for system messages with source attribution."""
283
282
 
284
283
  type: Literal["system_message"] = "system_message"
285
- source: str = "claude_code_sdk"
284
+ source: str = "claude_agent_sdk"
286
285
 
287
286
  model_config = ConfigDict(extra="allow")
288
287
 
@@ -294,7 +293,7 @@ class ToolUseSDKBlock(BaseModel):
294
293
  id: str = Field(..., description="Unique identifier for the tool use")
295
294
  name: str = Field(..., description="Name of the tool being used")
296
295
  input: dict[str, Any] = Field(..., description="Input parameters for the tool")
297
- source: str = "claude_code_sdk"
296
+ source: str = "claude_agent_sdk"
298
297
 
299
298
 
300
299
  class ToolResultSDKBlock(BaseModel):
@@ -310,14 +309,14 @@ class ToolResultSDKBlock(BaseModel):
310
309
  is_error: bool | None = Field(
311
310
  None, description="Whether this result represents an error"
312
311
  )
313
- source: str = "claude_code_sdk"
312
+ source: str = "claude_agent_sdk"
314
313
 
315
314
 
316
315
  class ResultMessageBlock(ResultMessage):
317
316
  """Custom content block for result messages with session data."""
318
317
 
319
318
  type: Literal["result_message"] = "result_message"
320
- source: str = "claude_code_sdk"
319
+ source: str = "claude_agent_sdk"
321
320
 
322
321
 
323
322
  # Union type for all custom content blocks
@@ -337,6 +336,53 @@ SDKContentBlock = Annotated[
337
336
  # Extended content block type that includes both SDK and custom blocks
338
337
  ExtendedContentBlock = SDKContentBlock
339
338
 
339
+ # Union definition moved after imports
340
+
341
+
342
+ # Plugin-specific content block union that includes core and SDK-specific types
343
+ # Note: We only include SDK-specific types to avoid discriminator conflicts
344
+ # with core types that have the same discriminator values
345
+ CCProxyContentBlock = Annotated[
346
+ anthropic_models.RequestContentBlock
347
+ | SDKMessageMode
348
+ | ToolUseSDKBlock
349
+ | ToolResultSDKBlock
350
+ | ResultMessageBlock,
351
+ Field(discriminator="type"),
352
+ ]
353
+
354
+
355
+ # Plugin-specific MessageResponse that uses the extended content block types
356
+ class MessageResponse(BaseModel):
357
+ """Plugin-specific response model that supports both core and SDK content blocks."""
358
+
359
+ id: Annotated[str, Field(description="Unique identifier for the message")]
360
+ type: Annotated[Literal["message"], Field(description="Response type")] = "message"
361
+ role: Annotated[Literal["assistant"], Field(description="Message role")] = (
362
+ "assistant"
363
+ )
364
+ content: Annotated[
365
+ list[CCProxyContentBlock],
366
+ Field(description="Array of content blocks in the response"),
367
+ ]
368
+ model: Annotated[str, Field(description="The model used for the response")]
369
+ stop_reason: Annotated[
370
+ str | None, Field(description="Reason why the model stopped generating")
371
+ ] = None
372
+ stop_sequence: Annotated[
373
+ str | None,
374
+ Field(description="The stop sequence that triggered stopping (if applicable)"),
375
+ ] = None
376
+ usage: Annotated[
377
+ anthropic_models.Usage, Field(description="Token usage information")
378
+ ]
379
+ container: Annotated[
380
+ dict[str, Any] | None,
381
+ Field(description="Information about container used in the request"),
382
+ ] = None
383
+
384
+ model_config = ConfigDict(extra="forbid", validate_assignment=True)
385
+
340
386
 
341
387
  # SDK Query Message Types
342
388
  class SDKMessageContent(BaseModel):
@@ -468,6 +514,8 @@ __all__ = [
468
514
  "ResultMessageBlock",
469
515
  "SDKContentBlock",
470
516
  "ExtendedContentBlock",
517
+ "CCProxyContentBlock",
518
+ "MessageResponse",
471
519
  # Conversion functions
472
520
  "convert_sdk_text_block",
473
521
  "convert_sdk_tool_use_block",