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,162 @@
1
+ """Codex health check implementation."""
2
+
3
+ from typing import Any, Literal
4
+
5
+ from ccproxy.core.logging import get_plugin_logger
6
+ from ccproxy.core.plugins.protocol import HealthCheckResult
7
+
8
+ from .config import CodexSettings
9
+ from .detection_service import CodexDetectionService
10
+
11
+
12
+ logger = get_plugin_logger()
13
+
14
+
15
+ async def codex_health_check(
16
+ config: CodexSettings | None,
17
+ detection_service: CodexDetectionService | None = None,
18
+ auth_manager: Any | None = None,
19
+ *,
20
+ version: str,
21
+ ) -> HealthCheckResult:
22
+ """Perform health check for Codex plugin."""
23
+ try:
24
+ if not config:
25
+ return HealthCheckResult(
26
+ status="fail",
27
+ componentId="plugin-codex",
28
+ output="Codex plugin configuration not available",
29
+ version=version,
30
+ )
31
+
32
+ # Check basic configuration validity
33
+ if not config.base_url:
34
+ return HealthCheckResult(
35
+ status="fail",
36
+ componentId="plugin-codex",
37
+ output="Codex base URL not configured",
38
+ version=version,
39
+ )
40
+
41
+ # Check OAuth configuration
42
+ if not config.oauth.base_url or not config.oauth.client_id:
43
+ return HealthCheckResult(
44
+ status="warn",
45
+ componentId="plugin-codex",
46
+ output="Codex OAuth configuration incomplete",
47
+ version=version,
48
+ )
49
+
50
+ # Standardized details models
51
+ from ccproxy.core.plugins.models import (
52
+ AuthHealth,
53
+ CLIHealth,
54
+ ConfigHealth,
55
+ ProviderHealthDetails,
56
+ )
57
+
58
+ cli_info = (
59
+ detection_service.get_cli_health_info() if detection_service else None
60
+ )
61
+ status_val = (
62
+ cli_info.status.value
63
+ if (cli_info and hasattr(cli_info, "status"))
64
+ else "unknown"
65
+ )
66
+ available = bool(status_val == "available")
67
+ cli_health = (
68
+ CLIHealth(
69
+ available=available,
70
+ status=status_val,
71
+ version=(cli_info.version if cli_info else None),
72
+ path=(cli_info.binary_path if cli_info else None),
73
+ )
74
+ if cli_info
75
+ else None
76
+ )
77
+
78
+ # Get authentication status if auth manager is available
79
+ auth_details: dict[str, Any] = {}
80
+ if auth_manager:
81
+ try:
82
+ # Use the new helper method to get auth status
83
+ auth_details = await auth_manager.get_auth_status()
84
+ except Exception as e:
85
+ logger.debug(
86
+ "Failed to check auth status", error=str(e), category="auth"
87
+ )
88
+ auth_details = {
89
+ "authenticated": False,
90
+ "reason": str(e),
91
+ }
92
+
93
+ # Determine overall status
94
+ status: Literal["pass", "warn", "fail"]
95
+ provider_auth = (
96
+ AuthHealth(
97
+ configured=bool(auth_manager),
98
+ token_available=auth_details.get("authenticated"),
99
+ token_expired=(
100
+ not auth_details.get("authenticated")
101
+ and auth_details.get("reason") == "Token expired"
102
+ ),
103
+ account_id=auth_details.get("account_id"),
104
+ expires_at=auth_details.get("expires_at"),
105
+ error=(
106
+ None
107
+ if auth_details.get("authenticated")
108
+ else auth_details.get("reason")
109
+ ),
110
+ )
111
+ if auth_manager
112
+ else AuthHealth(configured=False)
113
+ )
114
+
115
+ if (cli_health and cli_health.available) and provider_auth.token_available:
116
+ output = f"Codex plugin is healthy (CLI v{cli_health.version} available, authenticated)"
117
+ status = "pass"
118
+ elif cli_health and cli_health.available:
119
+ output = f"Codex plugin is functional (CLI v{cli_health.version} available, auth missing)"
120
+ status = "warn"
121
+ elif provider_auth.token_available:
122
+ output = "Codex plugin is functional (authenticated, CLI not found)"
123
+ status = "warn"
124
+ else:
125
+ output = "Codex plugin is functional but CLI and auth missing"
126
+ status = "warn"
127
+
128
+ # Basic health check passes
129
+ return HealthCheckResult(
130
+ status=status,
131
+ componentId="plugin-codex",
132
+ output=output,
133
+ version=version,
134
+ details={
135
+ **ProviderHealthDetails(
136
+ provider="codex",
137
+ enabled=True,
138
+ base_url=config.base_url,
139
+ cli=cli_health,
140
+ auth=provider_auth,
141
+ config=ConfigHealth(
142
+ model_count=None,
143
+ supports_openai_format=None,
144
+ verbose_logging=config.verbose_logging,
145
+ extra={
146
+ "oauth_configured": bool(
147
+ config.oauth.base_url and config.oauth.client_id
148
+ )
149
+ },
150
+ ),
151
+ ).model_dump(),
152
+ },
153
+ )
154
+
155
+ except Exception as e:
156
+ logger.error("health_check_failed", error=str(e))
157
+ return HealthCheckResult(
158
+ status="fail",
159
+ componentId="plugin-codex",
160
+ output=f"Codex health check failed: {str(e)}",
161
+ version=version,
162
+ )
@@ -0,0 +1,263 @@
1
+ """Codex plugin hooks for streaming metrics extraction."""
2
+
3
+ import json
4
+ from typing import Any
5
+
6
+ from ccproxy.core.logging import get_plugin_logger
7
+ from ccproxy.core.plugins.hooks import Hook, HookContext, HookEvent
8
+ from ccproxy.streaming.sse_parser import SSEStreamParser
9
+
10
+ from .streaming_metrics import extract_usage_from_codex_chunk
11
+
12
+
13
+ logger = get_plugin_logger()
14
+
15
+
16
+ class CodexStreamingMetricsHook(Hook):
17
+ """Hook to extract and accumulate metrics from Codex streaming responses."""
18
+
19
+ name = "codex_streaming_metrics"
20
+ events = [HookEvent.PROVIDER_STREAM_CHUNK, HookEvent.PROVIDER_STREAM_END]
21
+ priority = 700 # HookLayer.OBSERVATION - Metrics collection layer
22
+
23
+ def __init__(
24
+ self, pricing_service: Any = None, plugin_registry: Any = None
25
+ ) -> None:
26
+ """Initialize with optional pricing service for cost calculation.
27
+
28
+ Args:
29
+ pricing_service: Direct pricing service instance (if available at init)
30
+ plugin_registry: Plugin registry to get pricing service lazily
31
+ """
32
+ self.pricing_service = pricing_service
33
+ self.plugin_registry = plugin_registry
34
+ # Store metrics per request_id
35
+ self._metrics_cache: dict[str, dict[str, Any]] = {}
36
+ # Incremental SSE parsers keyed by request
37
+ self._sse_parsers: dict[str, SSEStreamParser] = {}
38
+
39
+ def _get_pricing_service(self) -> Any:
40
+ """Get pricing service, trying lazy loading if not already available."""
41
+ if self.pricing_service:
42
+ return self.pricing_service
43
+
44
+ if self.plugin_registry:
45
+ try:
46
+ from ccproxy.plugins.pricing.service import PricingService
47
+
48
+ self.pricing_service = self.plugin_registry.get_service(
49
+ "pricing", PricingService
50
+ )
51
+ if self.pricing_service:
52
+ logger.debug(
53
+ "pricing_service_obtained_lazily",
54
+ plugin="codex",
55
+ )
56
+ except Exception as e:
57
+ logger.debug(
58
+ "lazy_pricing_service_failed",
59
+ plugin="codex",
60
+ error=str(e),
61
+ )
62
+
63
+ return self.pricing_service
64
+
65
+ async def __call__(self, context: HookContext) -> None:
66
+ """Extract metrics from streaming chunks and add to stream end events."""
67
+ # Only process codex provider events
68
+ if context.provider != "codex":
69
+ return
70
+
71
+ request_id = context.metadata.get("request_id")
72
+ if not request_id:
73
+ return
74
+
75
+ if context.event == HookEvent.PROVIDER_STREAM_CHUNK:
76
+ await self._process_chunk(context, request_id)
77
+ elif context.event == HookEvent.PROVIDER_STREAM_END:
78
+ await self._finalize_metrics(context, request_id)
79
+
80
+ async def _process_chunk(self, context: HookContext, request_id: str) -> None:
81
+ """Process a streaming chunk to extract metrics."""
82
+ chunk_data = context.data.get("chunk")
83
+ if not chunk_data:
84
+ return
85
+
86
+ # Initialize metrics cache for this request if needed
87
+ if request_id not in self._metrics_cache:
88
+ self._metrics_cache[request_id] = {
89
+ "tokens_input": None,
90
+ "tokens_output": None,
91
+ "cache_read_tokens": None,
92
+ "reasoning_tokens": None,
93
+ "cost_usd": None,
94
+ "model": None,
95
+ }
96
+
97
+ try:
98
+ # Parse SSE data if it's a string or bytes
99
+ if isinstance(chunk_data, str | bytes):
100
+ parser = self._sse_parsers.setdefault(request_id, SSEStreamParser())
101
+ for payload in parser.feed(chunk_data):
102
+ if isinstance(payload, dict):
103
+ self._extract_and_accumulate(payload, request_id)
104
+ for raw_event, error in parser.consume_errors():
105
+ logger.debug(
106
+ "chunk_metrics_sse_event_skipped",
107
+ plugin="codex",
108
+ request_id=request_id,
109
+ error=str(error),
110
+ event_preview=raw_event[:200],
111
+ )
112
+ elif isinstance(chunk_data, dict):
113
+ # Direct dict chunk
114
+ self._extract_and_accumulate(chunk_data, request_id)
115
+
116
+ except (json.JSONDecodeError, KeyError) as e:
117
+ logger.error(
118
+ "chunk_metrics_parse_failed",
119
+ plugin="codex",
120
+ error=str(e),
121
+ request_id=request_id,
122
+ )
123
+
124
+ def _extract_and_accumulate(
125
+ self, event_data: dict[str, Any], request_id: str
126
+ ) -> None:
127
+ """Extract metrics from parsed event data and accumulate."""
128
+ usage_data = extract_usage_from_codex_chunk(event_data)
129
+
130
+ if not usage_data:
131
+ return
132
+
133
+ cache = self._metrics_cache[request_id]
134
+ event_type = usage_data.get("event_type")
135
+
136
+ # Update metrics from usage data
137
+ if usage_data.get("input_tokens") is not None:
138
+ cache["tokens_input"] = usage_data.get("input_tokens")
139
+
140
+ if usage_data.get("output_tokens") is not None:
141
+ cache["tokens_output"] = usage_data.get("output_tokens")
142
+
143
+ if usage_data.get("cache_read_tokens") is not None:
144
+ cache["cache_read_tokens"] = usage_data.get("cache_read_tokens")
145
+
146
+ if usage_data.get("reasoning_tokens") is not None:
147
+ cache["reasoning_tokens"] = usage_data.get("reasoning_tokens")
148
+
149
+ # Extract model from the event
150
+ if not cache["model"] and usage_data.get("model"):
151
+ cache["model"] = usage_data.get("model")
152
+
153
+ # Calculate cost if we have all required data
154
+ pricing_service = self._get_pricing_service()
155
+ if (
156
+ pricing_service
157
+ and cache["model"]
158
+ and cache["tokens_input"] is not None
159
+ and cache["tokens_output"] is not None
160
+ ):
161
+ try:
162
+ from ccproxy.plugins.pricing.exceptions import (
163
+ ModelPricingNotFoundError,
164
+ PricingDataNotLoadedError,
165
+ PricingServiceDisabledError,
166
+ )
167
+
168
+ cost_decimal = pricing_service.calculate_cost_sync(
169
+ model_name=cache["model"],
170
+ input_tokens=cache["tokens_input"] or 0,
171
+ output_tokens=cache["tokens_output"] or 0,
172
+ cache_read_tokens=cache["cache_read_tokens"] or 0,
173
+ cache_write_tokens=0, # OpenAI/Codex doesn't have cache write
174
+ )
175
+ cache["cost_usd"] = float(cost_decimal)
176
+
177
+ logger.debug(
178
+ "hook_cost_calculated",
179
+ plugin="codex",
180
+ model=cache["model"],
181
+ cost_usd=cache["cost_usd"],
182
+ request_id=request_id,
183
+ )
184
+ except (
185
+ ModelPricingNotFoundError,
186
+ PricingDataNotLoadedError,
187
+ PricingServiceDisabledError,
188
+ ) as e:
189
+ logger.debug(
190
+ "hook_cost_calculation_skipped",
191
+ plugin="codex",
192
+ reason=str(e),
193
+ request_id=request_id,
194
+ )
195
+ except Exception as e:
196
+ logger.debug(
197
+ "hook_cost_calculation_failed",
198
+ plugin="codex",
199
+ error=str(e),
200
+ request_id=request_id,
201
+ )
202
+
203
+ logger.debug(
204
+ "hook_metrics_extracted",
205
+ plugin="codex",
206
+ event_type=event_type,
207
+ tokens_input=cache["tokens_input"],
208
+ tokens_output=cache["tokens_output"],
209
+ cache_read_tokens=cache.get("cache_read_tokens"),
210
+ reasoning_tokens=cache.get("reasoning_tokens"),
211
+ cost_usd=cache.get("cost_usd"),
212
+ request_id=request_id,
213
+ )
214
+
215
+ async def _finalize_metrics(self, context: HookContext, request_id: str) -> None:
216
+ """Add accumulated metrics to the PROVIDER_STREAM_END event."""
217
+ if request_id not in self._metrics_cache:
218
+ return
219
+
220
+ parser = self._sse_parsers.pop(request_id, None)
221
+ if parser:
222
+ for payload in parser.flush():
223
+ if isinstance(payload, dict):
224
+ self._extract_and_accumulate(payload, request_id)
225
+ for raw_event, error in parser.consume_errors():
226
+ logger.debug(
227
+ "chunk_metrics_sse_event_skipped",
228
+ plugin="codex",
229
+ request_id=request_id,
230
+ error=str(error),
231
+ event_preview=raw_event[:200],
232
+ )
233
+
234
+ metrics = self._metrics_cache.pop(request_id, {})
235
+
236
+ # Add metrics to the event's usage_metrics field
237
+ if not context.data.get("usage_metrics"):
238
+ context.data["usage_metrics"] = {}
239
+
240
+ # Update with our collected metrics (use standard naming)
241
+ if metrics["tokens_input"] is not None:
242
+ context.data["usage_metrics"]["input_tokens"] = metrics["tokens_input"]
243
+ if metrics["tokens_output"] is not None:
244
+ context.data["usage_metrics"]["output_tokens"] = metrics["tokens_output"]
245
+ if metrics["cache_read_tokens"] is not None:
246
+ context.data["usage_metrics"]["cache_read_input_tokens"] = metrics[
247
+ "cache_read_tokens"
248
+ ]
249
+ if metrics["reasoning_tokens"] is not None:
250
+ context.data["usage_metrics"]["reasoning_tokens"] = metrics[
251
+ "reasoning_tokens"
252
+ ]
253
+ if metrics["cost_usd"] is not None:
254
+ context.data["usage_metrics"]["cost_usd"] = metrics["cost_usd"]
255
+ if metrics["model"]:
256
+ context.data["model"] = metrics["model"]
257
+
258
+ logger.info(
259
+ "streaming_metrics_finalized",
260
+ plugin="codex",
261
+ request_id=request_id,
262
+ usage_metrics=context.data.get("usage_metrics", {}),
263
+ )
@@ -0,0 +1,39 @@
1
+ """Default model metadata and mapping rules for the Codex provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ccproxy.models.provider import ModelCard, ModelMappingRule
6
+
7
+
8
+ DEFAULT_CODEX_MODEL_CARDS: list[ModelCard] = [
9
+ ModelCard(
10
+ id="gpt-5",
11
+ created=1723075200,
12
+ owned_by="openai",
13
+ permission=[],
14
+ root="gpt-5",
15
+ parent=None,
16
+ ),
17
+ ModelCard(
18
+ id="gpt-5-codex",
19
+ created=1726444800,
20
+ owned_by="openai",
21
+ permission=[],
22
+ root="gpt-5-codex",
23
+ parent=None,
24
+ ),
25
+ ]
26
+
27
+
28
+ DEFAULT_CODEX_MODEL_MAPPINGS: list[ModelMappingRule] = [
29
+ ModelMappingRule(match="gpt-", target="gpt-5", kind="prefix"),
30
+ ModelMappingRule(match="o3-", target="gpt-5", kind="prefix"),
31
+ ModelMappingRule(match="o1-", target="gpt-5", kind="prefix"),
32
+ ModelMappingRule(match="claude-", target="gpt-5", kind="prefix"),
33
+ ]
34
+
35
+
36
+ __all__ = [
37
+ "DEFAULT_CODEX_MODEL_CARDS",
38
+ "DEFAULT_CODEX_MODEL_MAPPINGS",
39
+ ]