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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (481) hide show
  1. ccproxy/api/__init__.py +1 -15
  2. ccproxy/api/app.py +434 -219
  3. ccproxy/api/bootstrap.py +30 -0
  4. ccproxy/api/decorators.py +85 -0
  5. ccproxy/api/dependencies.py +144 -168
  6. ccproxy/api/format_validation.py +54 -0
  7. ccproxy/api/middleware/cors.py +6 -3
  8. ccproxy/api/middleware/errors.py +388 -524
  9. ccproxy/api/middleware/hooks.py +563 -0
  10. ccproxy/api/middleware/normalize_headers.py +59 -0
  11. ccproxy/api/middleware/request_id.py +35 -16
  12. ccproxy/api/middleware/streaming_hooks.py +292 -0
  13. ccproxy/api/routes/__init__.py +5 -14
  14. ccproxy/api/routes/health.py +39 -672
  15. ccproxy/api/routes/plugins.py +277 -0
  16. ccproxy/auth/__init__.py +2 -19
  17. ccproxy/auth/bearer.py +25 -15
  18. ccproxy/auth/dependencies.py +123 -157
  19. ccproxy/auth/exceptions.py +0 -12
  20. ccproxy/auth/manager.py +35 -49
  21. ccproxy/auth/managers/__init__.py +10 -0
  22. ccproxy/auth/managers/base.py +523 -0
  23. ccproxy/auth/managers/base_enhanced.py +63 -0
  24. ccproxy/auth/managers/token_snapshot.py +77 -0
  25. ccproxy/auth/models/base.py +65 -0
  26. ccproxy/auth/models/credentials.py +40 -0
  27. ccproxy/auth/oauth/__init__.py +4 -18
  28. ccproxy/auth/oauth/base.py +533 -0
  29. ccproxy/auth/oauth/cli_errors.py +37 -0
  30. ccproxy/auth/oauth/flows.py +430 -0
  31. ccproxy/auth/oauth/protocol.py +366 -0
  32. ccproxy/auth/oauth/registry.py +408 -0
  33. ccproxy/auth/oauth/router.py +396 -0
  34. ccproxy/auth/oauth/routes.py +186 -113
  35. ccproxy/auth/oauth/session.py +151 -0
  36. ccproxy/auth/oauth/templates.py +342 -0
  37. ccproxy/auth/storage/__init__.py +2 -5
  38. ccproxy/auth/storage/base.py +279 -5
  39. ccproxy/auth/storage/generic.py +134 -0
  40. ccproxy/cli/__init__.py +1 -2
  41. ccproxy/cli/_settings_help.py +351 -0
  42. ccproxy/cli/commands/auth.py +1519 -793
  43. ccproxy/cli/commands/config/commands.py +209 -276
  44. ccproxy/cli/commands/plugins.py +669 -0
  45. ccproxy/cli/commands/serve.py +75 -810
  46. ccproxy/cli/commands/status.py +254 -0
  47. ccproxy/cli/decorators.py +83 -0
  48. ccproxy/cli/helpers.py +22 -60
  49. ccproxy/cli/main.py +359 -10
  50. ccproxy/cli/options/claude_options.py +0 -25
  51. ccproxy/config/__init__.py +7 -11
  52. ccproxy/config/core.py +227 -0
  53. ccproxy/config/env_generator.py +232 -0
  54. ccproxy/config/runtime.py +67 -0
  55. ccproxy/config/security.py +36 -3
  56. ccproxy/config/settings.py +382 -441
  57. ccproxy/config/toml_generator.py +299 -0
  58. ccproxy/config/utils.py +452 -0
  59. ccproxy/core/__init__.py +7 -271
  60. ccproxy/{_version.py → core/_version.py} +16 -3
  61. ccproxy/core/async_task_manager.py +516 -0
  62. ccproxy/core/async_utils.py +47 -14
  63. ccproxy/core/auth/__init__.py +6 -0
  64. ccproxy/core/constants.py +16 -50
  65. ccproxy/core/errors.py +53 -0
  66. ccproxy/core/id_utils.py +20 -0
  67. ccproxy/core/interfaces.py +16 -123
  68. ccproxy/core/logging.py +473 -18
  69. ccproxy/core/plugins/__init__.py +77 -0
  70. ccproxy/core/plugins/cli_discovery.py +211 -0
  71. ccproxy/core/plugins/declaration.py +455 -0
  72. ccproxy/core/plugins/discovery.py +604 -0
  73. ccproxy/core/plugins/factories.py +967 -0
  74. ccproxy/core/plugins/hooks/__init__.py +30 -0
  75. ccproxy/core/plugins/hooks/base.py +58 -0
  76. ccproxy/core/plugins/hooks/events.py +46 -0
  77. ccproxy/core/plugins/hooks/implementations/__init__.py +16 -0
  78. ccproxy/core/plugins/hooks/implementations/formatters/__init__.py +11 -0
  79. ccproxy/core/plugins/hooks/implementations/formatters/json.py +552 -0
  80. ccproxy/core/plugins/hooks/implementations/formatters/raw.py +370 -0
  81. ccproxy/core/plugins/hooks/implementations/http_tracer.py +431 -0
  82. ccproxy/core/plugins/hooks/layers.py +44 -0
  83. ccproxy/core/plugins/hooks/manager.py +186 -0
  84. ccproxy/core/plugins/hooks/registry.py +139 -0
  85. ccproxy/core/plugins/hooks/thread_manager.py +203 -0
  86. ccproxy/core/plugins/hooks/types.py +22 -0
  87. ccproxy/core/plugins/interfaces.py +416 -0
  88. ccproxy/core/plugins/loader.py +166 -0
  89. ccproxy/core/plugins/middleware.py +233 -0
  90. ccproxy/core/plugins/models.py +59 -0
  91. ccproxy/core/plugins/protocol.py +180 -0
  92. ccproxy/core/plugins/runtime.py +519 -0
  93. ccproxy/{observability/context.py → core/request_context.py} +137 -94
  94. ccproxy/core/status_report.py +211 -0
  95. ccproxy/core/transformers.py +13 -8
  96. ccproxy/data/claude_headers_fallback.json +540 -19
  97. ccproxy/data/codex_headers_fallback.json +114 -7
  98. ccproxy/http/__init__.py +30 -0
  99. ccproxy/http/base.py +95 -0
  100. ccproxy/http/client.py +323 -0
  101. ccproxy/http/hooks.py +642 -0
  102. ccproxy/http/pool.py +279 -0
  103. ccproxy/llms/formatters/__init__.py +7 -0
  104. ccproxy/llms/formatters/anthropic_to_openai/__init__.py +55 -0
  105. ccproxy/llms/formatters/anthropic_to_openai/errors.py +65 -0
  106. ccproxy/llms/formatters/anthropic_to_openai/requests.py +356 -0
  107. ccproxy/llms/formatters/anthropic_to_openai/responses.py +153 -0
  108. ccproxy/llms/formatters/anthropic_to_openai/streams.py +1546 -0
  109. ccproxy/llms/formatters/base.py +140 -0
  110. ccproxy/llms/formatters/base_model.py +33 -0
  111. ccproxy/llms/formatters/common/__init__.py +51 -0
  112. ccproxy/llms/formatters/common/identifiers.py +48 -0
  113. ccproxy/llms/formatters/common/streams.py +254 -0
  114. ccproxy/llms/formatters/common/thinking.py +74 -0
  115. ccproxy/llms/formatters/common/usage.py +135 -0
  116. ccproxy/llms/formatters/constants.py +55 -0
  117. ccproxy/llms/formatters/context.py +116 -0
  118. ccproxy/llms/formatters/mapping.py +33 -0
  119. ccproxy/llms/formatters/openai_to_anthropic/__init__.py +55 -0
  120. ccproxy/llms/formatters/openai_to_anthropic/_helpers.py +141 -0
  121. ccproxy/llms/formatters/openai_to_anthropic/errors.py +53 -0
  122. ccproxy/llms/formatters/openai_to_anthropic/requests.py +674 -0
  123. ccproxy/llms/formatters/openai_to_anthropic/responses.py +285 -0
  124. ccproxy/llms/formatters/openai_to_anthropic/streams.py +530 -0
  125. ccproxy/llms/formatters/openai_to_openai/__init__.py +53 -0
  126. ccproxy/llms/formatters/openai_to_openai/_helpers.py +325 -0
  127. ccproxy/llms/formatters/openai_to_openai/errors.py +6 -0
  128. ccproxy/llms/formatters/openai_to_openai/requests.py +388 -0
  129. ccproxy/llms/formatters/openai_to_openai/responses.py +594 -0
  130. ccproxy/llms/formatters/openai_to_openai/streams.py +1832 -0
  131. ccproxy/llms/formatters/utils.py +306 -0
  132. ccproxy/llms/models/__init__.py +9 -0
  133. ccproxy/llms/models/anthropic.py +619 -0
  134. ccproxy/llms/models/openai.py +844 -0
  135. ccproxy/llms/streaming/__init__.py +26 -0
  136. ccproxy/llms/streaming/accumulators.py +1074 -0
  137. ccproxy/llms/streaming/formatters.py +251 -0
  138. ccproxy/{adapters/openai/streaming.py → llms/streaming/processors.py} +193 -240
  139. ccproxy/models/__init__.py +8 -159
  140. ccproxy/models/detection.py +92 -193
  141. ccproxy/models/provider.py +75 -0
  142. ccproxy/plugins/access_log/README.md +32 -0
  143. ccproxy/plugins/access_log/__init__.py +20 -0
  144. ccproxy/plugins/access_log/config.py +33 -0
  145. ccproxy/plugins/access_log/formatter.py +126 -0
  146. ccproxy/plugins/access_log/hook.py +763 -0
  147. ccproxy/plugins/access_log/logger.py +254 -0
  148. ccproxy/plugins/access_log/plugin.py +137 -0
  149. ccproxy/plugins/access_log/writer.py +109 -0
  150. ccproxy/plugins/analytics/README.md +24 -0
  151. ccproxy/plugins/analytics/__init__.py +1 -0
  152. ccproxy/plugins/analytics/config.py +5 -0
  153. ccproxy/plugins/analytics/ingest.py +85 -0
  154. ccproxy/plugins/analytics/models.py +97 -0
  155. ccproxy/plugins/analytics/plugin.py +121 -0
  156. ccproxy/plugins/analytics/routes.py +163 -0
  157. ccproxy/plugins/analytics/service.py +284 -0
  158. ccproxy/plugins/claude_api/README.md +29 -0
  159. ccproxy/plugins/claude_api/__init__.py +10 -0
  160. ccproxy/plugins/claude_api/adapter.py +829 -0
  161. ccproxy/plugins/claude_api/config.py +52 -0
  162. ccproxy/plugins/claude_api/detection_service.py +461 -0
  163. ccproxy/plugins/claude_api/health.py +175 -0
  164. ccproxy/plugins/claude_api/hooks.py +284 -0
  165. ccproxy/plugins/claude_api/models.py +256 -0
  166. ccproxy/plugins/claude_api/plugin.py +298 -0
  167. ccproxy/plugins/claude_api/routes.py +118 -0
  168. ccproxy/plugins/claude_api/streaming_metrics.py +68 -0
  169. ccproxy/plugins/claude_api/tasks.py +84 -0
  170. ccproxy/plugins/claude_sdk/README.md +35 -0
  171. ccproxy/plugins/claude_sdk/__init__.py +80 -0
  172. ccproxy/plugins/claude_sdk/adapter.py +749 -0
  173. ccproxy/plugins/claude_sdk/auth.py +57 -0
  174. ccproxy/{claude_sdk → plugins/claude_sdk}/client.py +63 -39
  175. ccproxy/plugins/claude_sdk/config.py +210 -0
  176. ccproxy/{claude_sdk → plugins/claude_sdk}/converter.py +6 -6
  177. ccproxy/plugins/claude_sdk/detection_service.py +163 -0
  178. ccproxy/{services/claude_sdk_service.py → plugins/claude_sdk/handler.py} +123 -304
  179. ccproxy/plugins/claude_sdk/health.py +113 -0
  180. ccproxy/plugins/claude_sdk/hooks.py +115 -0
  181. ccproxy/{claude_sdk → plugins/claude_sdk}/manager.py +42 -32
  182. ccproxy/{claude_sdk → plugins/claude_sdk}/message_queue.py +8 -8
  183. ccproxy/{models/claude_sdk.py → plugins/claude_sdk/models.py} +64 -16
  184. ccproxy/plugins/claude_sdk/options.py +154 -0
  185. ccproxy/{claude_sdk → plugins/claude_sdk}/parser.py +23 -5
  186. ccproxy/plugins/claude_sdk/plugin.py +269 -0
  187. ccproxy/plugins/claude_sdk/routes.py +104 -0
  188. ccproxy/{claude_sdk → plugins/claude_sdk}/session_client.py +124 -12
  189. ccproxy/plugins/claude_sdk/session_pool.py +700 -0
  190. ccproxy/{claude_sdk → plugins/claude_sdk}/stream_handle.py +48 -43
  191. ccproxy/{claude_sdk → plugins/claude_sdk}/stream_worker.py +22 -18
  192. ccproxy/{claude_sdk → plugins/claude_sdk}/streaming.py +50 -16
  193. ccproxy/plugins/claude_sdk/tasks.py +97 -0
  194. ccproxy/plugins/claude_shared/README.md +18 -0
  195. ccproxy/plugins/claude_shared/__init__.py +12 -0
  196. ccproxy/plugins/claude_shared/model_defaults.py +171 -0
  197. ccproxy/plugins/codex/README.md +35 -0
  198. ccproxy/plugins/codex/__init__.py +6 -0
  199. ccproxy/plugins/codex/adapter.py +635 -0
  200. ccproxy/{config/codex.py → plugins/codex/config.py} +78 -12
  201. ccproxy/plugins/codex/detection_service.py +544 -0
  202. ccproxy/plugins/codex/health.py +162 -0
  203. ccproxy/plugins/codex/hooks.py +263 -0
  204. ccproxy/plugins/codex/model_defaults.py +39 -0
  205. ccproxy/plugins/codex/models.py +263 -0
  206. ccproxy/plugins/codex/plugin.py +275 -0
  207. ccproxy/plugins/codex/routes.py +129 -0
  208. ccproxy/plugins/codex/streaming_metrics.py +324 -0
  209. ccproxy/plugins/codex/tasks.py +106 -0
  210. ccproxy/plugins/codex/utils/__init__.py +1 -0
  211. ccproxy/plugins/codex/utils/sse_parser.py +106 -0
  212. ccproxy/plugins/command_replay/README.md +34 -0
  213. ccproxy/plugins/command_replay/__init__.py +17 -0
  214. ccproxy/plugins/command_replay/config.py +133 -0
  215. ccproxy/plugins/command_replay/formatter.py +432 -0
  216. ccproxy/plugins/command_replay/hook.py +294 -0
  217. ccproxy/plugins/command_replay/plugin.py +161 -0
  218. ccproxy/plugins/copilot/README.md +39 -0
  219. ccproxy/plugins/copilot/__init__.py +11 -0
  220. ccproxy/plugins/copilot/adapter.py +465 -0
  221. ccproxy/plugins/copilot/config.py +155 -0
  222. ccproxy/plugins/copilot/data/copilot_fallback.json +41 -0
  223. ccproxy/plugins/copilot/detection_service.py +255 -0
  224. ccproxy/plugins/copilot/manager.py +275 -0
  225. ccproxy/plugins/copilot/model_defaults.py +284 -0
  226. ccproxy/plugins/copilot/models.py +148 -0
  227. ccproxy/plugins/copilot/oauth/__init__.py +16 -0
  228. ccproxy/plugins/copilot/oauth/client.py +494 -0
  229. ccproxy/plugins/copilot/oauth/models.py +385 -0
  230. ccproxy/plugins/copilot/oauth/provider.py +602 -0
  231. ccproxy/plugins/copilot/oauth/storage.py +170 -0
  232. ccproxy/plugins/copilot/plugin.py +360 -0
  233. ccproxy/plugins/copilot/routes.py +294 -0
  234. ccproxy/plugins/credential_balancer/README.md +124 -0
  235. ccproxy/plugins/credential_balancer/__init__.py +6 -0
  236. ccproxy/plugins/credential_balancer/config.py +270 -0
  237. ccproxy/plugins/credential_balancer/factory.py +415 -0
  238. ccproxy/plugins/credential_balancer/hook.py +51 -0
  239. ccproxy/plugins/credential_balancer/manager.py +587 -0
  240. ccproxy/plugins/credential_balancer/plugin.py +146 -0
  241. ccproxy/plugins/dashboard/README.md +25 -0
  242. ccproxy/plugins/dashboard/__init__.py +1 -0
  243. ccproxy/plugins/dashboard/config.py +8 -0
  244. ccproxy/plugins/dashboard/plugin.py +71 -0
  245. ccproxy/plugins/dashboard/routes.py +67 -0
  246. ccproxy/plugins/docker/README.md +32 -0
  247. ccproxy/{docker → plugins/docker}/__init__.py +3 -0
  248. ccproxy/{docker → plugins/docker}/adapter.py +108 -10
  249. ccproxy/plugins/docker/config.py +82 -0
  250. ccproxy/{docker → plugins/docker}/docker_path.py +4 -3
  251. ccproxy/{docker → plugins/docker}/middleware.py +2 -2
  252. ccproxy/plugins/docker/plugin.py +198 -0
  253. ccproxy/{docker → plugins/docker}/stream_process.py +3 -3
  254. ccproxy/plugins/duckdb_storage/README.md +26 -0
  255. ccproxy/plugins/duckdb_storage/__init__.py +1 -0
  256. ccproxy/plugins/duckdb_storage/config.py +22 -0
  257. ccproxy/plugins/duckdb_storage/plugin.py +128 -0
  258. ccproxy/plugins/duckdb_storage/routes.py +51 -0
  259. ccproxy/plugins/duckdb_storage/storage.py +633 -0
  260. ccproxy/plugins/max_tokens/README.md +38 -0
  261. ccproxy/plugins/max_tokens/__init__.py +12 -0
  262. ccproxy/plugins/max_tokens/adapter.py +235 -0
  263. ccproxy/plugins/max_tokens/config.py +86 -0
  264. ccproxy/plugins/max_tokens/models.py +53 -0
  265. ccproxy/plugins/max_tokens/plugin.py +200 -0
  266. ccproxy/plugins/max_tokens/service.py +271 -0
  267. ccproxy/plugins/max_tokens/token_limits.json +54 -0
  268. ccproxy/plugins/metrics/README.md +35 -0
  269. ccproxy/plugins/metrics/__init__.py +10 -0
  270. ccproxy/{observability/metrics.py → plugins/metrics/collector.py} +20 -153
  271. ccproxy/plugins/metrics/config.py +85 -0
  272. ccproxy/plugins/metrics/grafana/dashboards/ccproxy-dashboard.json +1720 -0
  273. ccproxy/plugins/metrics/hook.py +403 -0
  274. ccproxy/plugins/metrics/plugin.py +268 -0
  275. ccproxy/{observability → plugins/metrics}/pushgateway.py +57 -59
  276. ccproxy/plugins/metrics/routes.py +107 -0
  277. ccproxy/plugins/metrics/tasks.py +117 -0
  278. ccproxy/plugins/oauth_claude/README.md +35 -0
  279. ccproxy/plugins/oauth_claude/__init__.py +14 -0
  280. ccproxy/plugins/oauth_claude/client.py +270 -0
  281. ccproxy/plugins/oauth_claude/config.py +84 -0
  282. ccproxy/plugins/oauth_claude/manager.py +482 -0
  283. ccproxy/plugins/oauth_claude/models.py +266 -0
  284. ccproxy/plugins/oauth_claude/plugin.py +149 -0
  285. ccproxy/plugins/oauth_claude/provider.py +571 -0
  286. ccproxy/plugins/oauth_claude/storage.py +212 -0
  287. ccproxy/plugins/oauth_codex/README.md +38 -0
  288. ccproxy/plugins/oauth_codex/__init__.py +14 -0
  289. ccproxy/plugins/oauth_codex/client.py +224 -0
  290. ccproxy/plugins/oauth_codex/config.py +95 -0
  291. ccproxy/plugins/oauth_codex/manager.py +256 -0
  292. ccproxy/plugins/oauth_codex/models.py +239 -0
  293. ccproxy/plugins/oauth_codex/plugin.py +146 -0
  294. ccproxy/plugins/oauth_codex/provider.py +574 -0
  295. ccproxy/plugins/oauth_codex/storage.py +92 -0
  296. ccproxy/plugins/permissions/README.md +28 -0
  297. ccproxy/plugins/permissions/__init__.py +22 -0
  298. ccproxy/plugins/permissions/config.py +28 -0
  299. ccproxy/{cli/commands/permission_handler.py → plugins/permissions/handlers/cli.py} +49 -25
  300. ccproxy/plugins/permissions/handlers/protocol.py +33 -0
  301. ccproxy/plugins/permissions/handlers/terminal.py +675 -0
  302. ccproxy/{api/routes → plugins/permissions}/mcp.py +34 -7
  303. ccproxy/{models/permissions.py → plugins/permissions/models.py} +65 -1
  304. ccproxy/plugins/permissions/plugin.py +153 -0
  305. ccproxy/{api/routes/permissions.py → plugins/permissions/routes.py} +20 -16
  306. ccproxy/{api/services/permission_service.py → plugins/permissions/service.py} +65 -11
  307. ccproxy/{api → plugins/permissions}/ui/permission_handler_protocol.py +1 -1
  308. ccproxy/{api → plugins/permissions}/ui/terminal_permission_handler.py +66 -10
  309. ccproxy/plugins/pricing/README.md +34 -0
  310. ccproxy/plugins/pricing/__init__.py +6 -0
  311. ccproxy/{pricing → plugins/pricing}/cache.py +7 -6
  312. ccproxy/{config/pricing.py → plugins/pricing/config.py} +32 -6
  313. ccproxy/plugins/pricing/exceptions.py +35 -0
  314. ccproxy/plugins/pricing/loader.py +440 -0
  315. ccproxy/{pricing → plugins/pricing}/models.py +13 -23
  316. ccproxy/plugins/pricing/plugin.py +169 -0
  317. ccproxy/plugins/pricing/service.py +191 -0
  318. ccproxy/plugins/pricing/tasks.py +300 -0
  319. ccproxy/{pricing → plugins/pricing}/updater.py +86 -72
  320. ccproxy/plugins/pricing/utils.py +99 -0
  321. ccproxy/plugins/request_tracer/README.md +40 -0
  322. ccproxy/plugins/request_tracer/__init__.py +7 -0
  323. ccproxy/plugins/request_tracer/config.py +120 -0
  324. ccproxy/plugins/request_tracer/hook.py +415 -0
  325. ccproxy/plugins/request_tracer/plugin.py +255 -0
  326. ccproxy/scheduler/__init__.py +2 -14
  327. ccproxy/scheduler/core.py +26 -41
  328. ccproxy/scheduler/manager.py +61 -105
  329. ccproxy/scheduler/registry.py +6 -32
  330. ccproxy/scheduler/tasks.py +268 -276
  331. ccproxy/services/__init__.py +0 -1
  332. ccproxy/services/adapters/__init__.py +11 -0
  333. ccproxy/services/adapters/base.py +123 -0
  334. ccproxy/services/adapters/chain_composer.py +88 -0
  335. ccproxy/services/adapters/chain_validation.py +44 -0
  336. ccproxy/services/adapters/chat_accumulator.py +200 -0
  337. ccproxy/services/adapters/delta_utils.py +142 -0
  338. ccproxy/services/adapters/format_adapter.py +136 -0
  339. ccproxy/services/adapters/format_context.py +11 -0
  340. ccproxy/services/adapters/format_registry.py +158 -0
  341. ccproxy/services/adapters/http_adapter.py +1045 -0
  342. ccproxy/services/adapters/mock_adapter.py +118 -0
  343. ccproxy/services/adapters/protocols.py +35 -0
  344. ccproxy/services/adapters/simple_converters.py +571 -0
  345. ccproxy/services/auth_registry.py +180 -0
  346. ccproxy/services/cache/__init__.py +6 -0
  347. ccproxy/services/cache/response_cache.py +261 -0
  348. ccproxy/services/cli_detection.py +437 -0
  349. ccproxy/services/config/__init__.py +6 -0
  350. ccproxy/services/config/proxy_configuration.py +111 -0
  351. ccproxy/services/container.py +256 -0
  352. ccproxy/services/factories.py +380 -0
  353. ccproxy/services/handler_config.py +76 -0
  354. ccproxy/services/interfaces.py +298 -0
  355. ccproxy/services/mocking/__init__.py +6 -0
  356. ccproxy/services/mocking/mock_handler.py +291 -0
  357. ccproxy/services/tracing/__init__.py +7 -0
  358. ccproxy/services/tracing/interfaces.py +61 -0
  359. ccproxy/services/tracing/null_tracer.py +57 -0
  360. ccproxy/streaming/__init__.py +23 -0
  361. ccproxy/streaming/buffer.py +1056 -0
  362. ccproxy/streaming/deferred.py +897 -0
  363. ccproxy/streaming/handler.py +117 -0
  364. ccproxy/streaming/interfaces.py +77 -0
  365. ccproxy/streaming/simple_adapter.py +39 -0
  366. ccproxy/streaming/sse.py +109 -0
  367. ccproxy/streaming/sse_parser.py +127 -0
  368. ccproxy/templates/__init__.py +6 -0
  369. ccproxy/templates/plugin_scaffold.py +695 -0
  370. ccproxy/testing/endpoints/__init__.py +33 -0
  371. ccproxy/testing/endpoints/cli.py +215 -0
  372. ccproxy/testing/endpoints/config.py +874 -0
  373. ccproxy/testing/endpoints/console.py +57 -0
  374. ccproxy/testing/endpoints/models.py +100 -0
  375. ccproxy/testing/endpoints/runner.py +1903 -0
  376. ccproxy/testing/endpoints/tools.py +308 -0
  377. ccproxy/testing/mock_responses.py +70 -1
  378. ccproxy/testing/response_handlers.py +20 -0
  379. ccproxy/utils/__init__.py +0 -6
  380. ccproxy/utils/binary_resolver.py +476 -0
  381. ccproxy/utils/caching.py +327 -0
  382. ccproxy/utils/cli_logging.py +101 -0
  383. ccproxy/utils/command_line.py +251 -0
  384. ccproxy/utils/headers.py +228 -0
  385. ccproxy/utils/model_mapper.py +120 -0
  386. ccproxy/utils/startup_helpers.py +68 -446
  387. ccproxy/utils/version_checker.py +273 -6
  388. ccproxy_api-0.2.0.dist-info/METADATA +212 -0
  389. ccproxy_api-0.2.0.dist-info/RECORD +417 -0
  390. {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0.dist-info}/WHEEL +1 -1
  391. ccproxy_api-0.2.0.dist-info/entry_points.txt +24 -0
  392. ccproxy/__init__.py +0 -4
  393. ccproxy/adapters/__init__.py +0 -11
  394. ccproxy/adapters/base.py +0 -80
  395. ccproxy/adapters/codex/__init__.py +0 -11
  396. ccproxy/adapters/openai/__init__.py +0 -42
  397. ccproxy/adapters/openai/adapter.py +0 -953
  398. ccproxy/adapters/openai/models.py +0 -412
  399. ccproxy/adapters/openai/response_adapter.py +0 -355
  400. ccproxy/adapters/openai/response_models.py +0 -178
  401. ccproxy/api/middleware/headers.py +0 -49
  402. ccproxy/api/middleware/logging.py +0 -180
  403. ccproxy/api/middleware/request_content_logging.py +0 -297
  404. ccproxy/api/middleware/server_header.py +0 -58
  405. ccproxy/api/responses.py +0 -89
  406. ccproxy/api/routes/claude.py +0 -371
  407. ccproxy/api/routes/codex.py +0 -1251
  408. ccproxy/api/routes/metrics.py +0 -1029
  409. ccproxy/api/routes/proxy.py +0 -211
  410. ccproxy/api/services/__init__.py +0 -6
  411. ccproxy/auth/conditional.py +0 -84
  412. ccproxy/auth/credentials_adapter.py +0 -93
  413. ccproxy/auth/models.py +0 -118
  414. ccproxy/auth/oauth/models.py +0 -48
  415. ccproxy/auth/openai/__init__.py +0 -13
  416. ccproxy/auth/openai/credentials.py +0 -166
  417. ccproxy/auth/openai/oauth_client.py +0 -334
  418. ccproxy/auth/openai/storage.py +0 -184
  419. ccproxy/auth/storage/json_file.py +0 -158
  420. ccproxy/auth/storage/keyring.py +0 -189
  421. ccproxy/claude_sdk/__init__.py +0 -18
  422. ccproxy/claude_sdk/options.py +0 -194
  423. ccproxy/claude_sdk/session_pool.py +0 -550
  424. ccproxy/cli/docker/__init__.py +0 -34
  425. ccproxy/cli/docker/adapter_factory.py +0 -157
  426. ccproxy/cli/docker/params.py +0 -274
  427. ccproxy/config/auth.py +0 -153
  428. ccproxy/config/claude.py +0 -348
  429. ccproxy/config/cors.py +0 -79
  430. ccproxy/config/discovery.py +0 -95
  431. ccproxy/config/docker_settings.py +0 -264
  432. ccproxy/config/observability.py +0 -158
  433. ccproxy/config/reverse_proxy.py +0 -31
  434. ccproxy/config/scheduler.py +0 -108
  435. ccproxy/config/server.py +0 -86
  436. ccproxy/config/validators.py +0 -231
  437. ccproxy/core/codex_transformers.py +0 -389
  438. ccproxy/core/http.py +0 -328
  439. ccproxy/core/http_transformers.py +0 -812
  440. ccproxy/core/proxy.py +0 -143
  441. ccproxy/core/validators.py +0 -288
  442. ccproxy/models/errors.py +0 -42
  443. ccproxy/models/messages.py +0 -269
  444. ccproxy/models/requests.py +0 -107
  445. ccproxy/models/responses.py +0 -270
  446. ccproxy/models/types.py +0 -102
  447. ccproxy/observability/__init__.py +0 -51
  448. ccproxy/observability/access_logger.py +0 -457
  449. ccproxy/observability/sse_events.py +0 -303
  450. ccproxy/observability/stats_printer.py +0 -753
  451. ccproxy/observability/storage/__init__.py +0 -1
  452. ccproxy/observability/storage/duckdb_simple.py +0 -677
  453. ccproxy/observability/storage/models.py +0 -70
  454. ccproxy/observability/streaming_response.py +0 -107
  455. ccproxy/pricing/__init__.py +0 -19
  456. ccproxy/pricing/loader.py +0 -251
  457. ccproxy/services/claude_detection_service.py +0 -243
  458. ccproxy/services/codex_detection_service.py +0 -252
  459. ccproxy/services/credentials/__init__.py +0 -55
  460. ccproxy/services/credentials/config.py +0 -105
  461. ccproxy/services/credentials/manager.py +0 -561
  462. ccproxy/services/credentials/oauth_client.py +0 -481
  463. ccproxy/services/proxy_service.py +0 -1827
  464. ccproxy/static/.keep +0 -0
  465. ccproxy/utils/cost_calculator.py +0 -210
  466. ccproxy/utils/disconnection_monitor.py +0 -83
  467. ccproxy/utils/model_mapping.py +0 -199
  468. ccproxy/utils/models_provider.py +0 -150
  469. ccproxy/utils/simple_request_logger.py +0 -284
  470. ccproxy/utils/streaming_metrics.py +0 -199
  471. ccproxy_api-0.1.7.dist-info/METADATA +0 -615
  472. ccproxy_api-0.1.7.dist-info/RECORD +0 -191
  473. ccproxy_api-0.1.7.dist-info/entry_points.txt +0 -4
  474. /ccproxy/{api/middleware/auth.py → auth/models/__init__.py} +0 -0
  475. /ccproxy/{claude_sdk → plugins/claude_sdk}/exceptions.py +0 -0
  476. /ccproxy/{docker → plugins/docker}/models.py +0 -0
  477. /ccproxy/{docker → plugins/docker}/protocol.py +0 -0
  478. /ccproxy/{docker → plugins/docker}/validators.py +0 -0
  479. /ccproxy/{auth/oauth/storage.py → plugins/permissions/handlers/__init__.py} +0 -0
  480. /ccproxy/{api → plugins/permissions}/ui/__init__.py +0 -0
  481. {ccproxy_api-0.1.7.dist-info → ccproxy_api-0.2.0.dist-info}/licenses/LICENSE +0 -0
ccproxy/static/.keep DELETED
File without changes
@@ -1,210 +0,0 @@
1
- """Cost calculation utilities for token-based pricing.
2
-
3
- This module provides shared cost calculation functionality that can be used
4
- across different services to ensure consistent pricing calculations.
5
- """
6
-
7
- import structlog
8
-
9
-
10
- logger = structlog.get_logger(__name__)
11
-
12
-
13
- def calculate_token_cost(
14
- tokens_input: int | None,
15
- tokens_output: int | None,
16
- model: str | None,
17
- cache_read_tokens: int | None = None,
18
- cache_write_tokens: int | None = None,
19
- ) -> float | None:
20
- """Calculate cost in USD for the given token usage including cache tokens.
21
-
22
- This is a shared utility function that provides consistent cost calculation
23
- across all services using the pricing data from the pricing system.
24
-
25
- Args:
26
- tokens_input: Number of input tokens
27
- tokens_output: Number of output tokens
28
- model: Model name for pricing lookup
29
- cache_read_tokens: Number of cache read tokens
30
- cache_write_tokens: Number of cache write tokens
31
-
32
- Returns:
33
- Cost in USD or None if calculation not possible
34
- """
35
- if not model or (
36
- not tokens_input
37
- and not tokens_output
38
- and not cache_read_tokens
39
- and not cache_write_tokens
40
- ):
41
- return None
42
-
43
- try:
44
- # Import pricing system components
45
- from ccproxy.config.pricing import PricingSettings
46
- from ccproxy.pricing.cache import PricingCache
47
- from ccproxy.pricing.loader import PricingLoader
48
-
49
- # Get canonical model name
50
- canonical_model = PricingLoader.get_canonical_model_name(model)
51
-
52
- # Create pricing components with dependency injection
53
- settings = PricingSettings()
54
- cache = PricingCache(settings)
55
- cached_data = cache.load_cached_data()
56
-
57
- # If cache is expired, try to use stale cache as fallback
58
- if not cached_data:
59
- try:
60
- import json
61
-
62
- if cache.cache_file.exists():
63
- with cache.cache_file.open(encoding="utf-8") as f:
64
- cached_data = json.load(f)
65
- logger.debug(
66
- "cost_calculation_using_stale_cache",
67
- cache_age_hours=cache.get_cache_info().get("age_hours"),
68
- )
69
- except (OSError, json.JSONDecodeError):
70
- pass
71
-
72
- if not cached_data:
73
- logger.debug("cost_calculation_skipped", reason="no_pricing_data")
74
- return None
75
-
76
- # Load pricing data
77
- pricing_data = PricingLoader.load_pricing_from_data(cached_data, verbose=False)
78
- if not pricing_data or canonical_model not in pricing_data:
79
- logger.debug(
80
- "cost_calculation_skipped",
81
- model=canonical_model,
82
- reason="model_not_found",
83
- )
84
- return None
85
-
86
- model_pricing = pricing_data[canonical_model]
87
-
88
- # Calculate cost (pricing is per 1M tokens)
89
- input_cost = ((tokens_input or 0) / 1_000_000) * float(model_pricing.input)
90
- output_cost = ((tokens_output or 0) / 1_000_000) * float(model_pricing.output)
91
- cache_read_cost = ((cache_read_tokens or 0) / 1_000_000) * float(
92
- model_pricing.cache_read
93
- )
94
- cache_write_cost = ((cache_write_tokens or 0) / 1_000_000) * float(
95
- model_pricing.cache_write
96
- )
97
-
98
- total_cost = input_cost + output_cost + cache_read_cost + cache_write_cost
99
-
100
- logger.debug(
101
- "cost_calculated",
102
- model=canonical_model,
103
- tokens_input=tokens_input,
104
- tokens_output=tokens_output,
105
- cache_read_tokens=cache_read_tokens,
106
- cache_write_tokens=cache_write_tokens,
107
- input_cost=input_cost,
108
- output_cost=output_cost,
109
- cache_read_cost=cache_read_cost,
110
- cache_write_cost=cache_write_cost,
111
- cost_usd=total_cost,
112
- )
113
-
114
- return total_cost
115
-
116
- except Exception as e:
117
- logger.debug("cost_calculation_error", error=str(e), model=model)
118
- return None
119
-
120
-
121
- def calculate_cost_breakdown(
122
- tokens_input: int | None,
123
- tokens_output: int | None,
124
- model: str | None,
125
- cache_read_tokens: int | None = None,
126
- cache_write_tokens: int | None = None,
127
- ) -> dict[str, float | str] | None:
128
- """Calculate detailed cost breakdown for the given token usage.
129
-
130
- Args:
131
- tokens_input: Number of input tokens
132
- tokens_output: Number of output tokens
133
- model: Model name for pricing lookup
134
- cache_read_tokens: Number of cache read tokens
135
- cache_write_tokens: Number of cache write tokens
136
-
137
- Returns:
138
- Dictionary with cost breakdown or None if calculation not possible
139
- """
140
- if not model or (
141
- not tokens_input
142
- and not tokens_output
143
- and not cache_read_tokens
144
- and not cache_write_tokens
145
- ):
146
- return None
147
-
148
- try:
149
- # Import pricing system components
150
- from ccproxy.config.pricing import PricingSettings
151
- from ccproxy.pricing.cache import PricingCache
152
- from ccproxy.pricing.loader import PricingLoader
153
-
154
- # Get canonical model name
155
- canonical_model = PricingLoader.get_canonical_model_name(model)
156
-
157
- # Create pricing components with dependency injection
158
- settings = PricingSettings()
159
- cache = PricingCache(settings)
160
- cached_data = cache.load_cached_data()
161
-
162
- # If cache is expired, try to use stale cache as fallback
163
- if not cached_data:
164
- try:
165
- import json
166
-
167
- if cache.cache_file.exists():
168
- with cache.cache_file.open(encoding="utf-8") as f:
169
- cached_data = json.load(f)
170
- logger.debug(
171
- "cost_breakdown_using_stale_cache",
172
- cache_age_hours=cache.get_cache_info().get("age_hours"),
173
- )
174
- except (OSError, json.JSONDecodeError):
175
- pass
176
-
177
- if not cached_data:
178
- return None
179
-
180
- # Load pricing data
181
- pricing_data = PricingLoader.load_pricing_from_data(cached_data, verbose=False)
182
- if not pricing_data or canonical_model not in pricing_data:
183
- return None
184
-
185
- model_pricing = pricing_data[canonical_model]
186
-
187
- # Calculate individual costs (pricing is per 1M tokens)
188
- input_cost = ((tokens_input or 0) / 1_000_000) * float(model_pricing.input)
189
- output_cost = ((tokens_output or 0) / 1_000_000) * float(model_pricing.output)
190
- cache_read_cost = ((cache_read_tokens or 0) / 1_000_000) * float(
191
- model_pricing.cache_read
192
- )
193
- cache_write_cost = ((cache_write_tokens or 0) / 1_000_000) * float(
194
- model_pricing.cache_write
195
- )
196
-
197
- total_cost = input_cost + output_cost + cache_read_cost + cache_write_cost
198
-
199
- return {
200
- "input_cost": input_cost,
201
- "output_cost": output_cost,
202
- "cache_read_cost": cache_read_cost,
203
- "cache_write_cost": cache_write_cost,
204
- "total_cost": total_cost,
205
- "model": canonical_model,
206
- }
207
-
208
- except Exception as e:
209
- logger.debug("cost_breakdown_error", error=str(e), model=model)
210
- return None
@@ -1,83 +0,0 @@
1
- """Utility functions for monitoring client disconnection and stuck streams during streaming responses."""
2
-
3
- import asyncio
4
- from typing import TYPE_CHECKING
5
-
6
- import structlog
7
- from starlette.requests import Request
8
-
9
-
10
- if TYPE_CHECKING:
11
- from ccproxy.services.claude_sdk_service import ClaudeSDKService
12
-
13
- logger = structlog.get_logger(__name__)
14
-
15
-
16
- async def monitor_disconnection(
17
- request: Request, session_id: str, claude_service: "ClaudeSDKService"
18
- ) -> None:
19
- """Monitor for client disconnection and interrupt session if detected.
20
-
21
- Args:
22
- request: The incoming HTTP request
23
- session_id: The Claude SDK session ID to interrupt if disconnected
24
- claude_service: The Claude SDK service instance
25
- """
26
- try:
27
- while True:
28
- await asyncio.sleep(1.0) # Check every second
29
- if await request.is_disconnected():
30
- logger.info(
31
- "client_disconnected_interrupting_session", session_id=session_id
32
- )
33
- try:
34
- await claude_service.sdk_client.interrupt_session(session_id)
35
- except Exception as e:
36
- logger.error(
37
- "failed_to_interrupt_session",
38
- session_id=session_id,
39
- error=str(e),
40
- )
41
- return
42
- except asyncio.CancelledError:
43
- # Task was cancelled, which is expected when streaming completes normally
44
- logger.debug("disconnection_monitor_cancelled", session_id=session_id)
45
- raise
46
-
47
-
48
- async def monitor_stuck_stream(
49
- session_id: str,
50
- claude_service: "ClaudeSDKService",
51
- first_chunk_event: asyncio.Event,
52
- timeout: float = 10.0,
53
- ) -> None:
54
- """Monitor for stuck streams that don't produce a first chunk (SystemMessage).
55
-
56
- Args:
57
- session_id: The Claude SDK session ID to monitor
58
- claude_service: The Claude SDK service instance
59
- first_chunk_event: Event that will be set when first chunk is received
60
- timeout: Seconds to wait for first chunk before considering stream stuck
61
- """
62
- try:
63
- # Wait for first chunk with timeout
64
- await asyncio.wait_for(first_chunk_event.wait(), timeout=timeout)
65
- logger.debug("stuck_stream_first_chunk_received", session_id=session_id)
66
- except TimeoutError:
67
- logger.error(
68
- "streaming_system_message_timeout",
69
- session_id=session_id,
70
- timeout=timeout,
71
- message=f"No SystemMessage received within {timeout}s, interrupting session",
72
- )
73
- try:
74
- await claude_service.sdk_client.interrupt_session(session_id)
75
- logger.info("stuck_session_interrupted_successfully", session_id=session_id)
76
- except Exception as e:
77
- logger.error(
78
- "failed_to_interrupt_stuck_session", session_id=session_id, error=str(e)
79
- )
80
- except asyncio.CancelledError:
81
- # Task was cancelled, which is expected when streaming completes normally
82
- logger.debug("stuck_stream_monitor_cancelled", session_id=session_id)
83
- raise
@@ -1,199 +0,0 @@
1
- """Unified model mapping utilities for OpenAI and Claude models.
2
-
3
- This module provides a single source of truth for all model mappings,
4
- consolidating OpenAI→Claude mappings and Claude alias resolution.
5
- """
6
-
7
- from __future__ import annotations
8
-
9
-
10
- # Combined mapping: OpenAI models → Claude models AND Claude aliases → canonical Claude models
11
- MODEL_MAPPING: dict[str, str] = {
12
- "gpt-5": "claude-sonnet-4-20250514",
13
- # OpenAI GPT-4 models → Claude 3.5 Sonnet (most comparable)
14
- "gpt-4": "claude-3-5-sonnet-20241022",
15
- "gpt-4-turbo": "claude-3-5-sonnet-20241022",
16
- "gpt-4-turbo-preview": "claude-3-5-sonnet-20241022",
17
- "gpt-4-1106-preview": "claude-3-5-sonnet-20241022",
18
- "gpt-4-0125-preview": "claude-3-5-sonnet-20241022",
19
- "gpt-4-turbo-2024-04-09": "claude-3-5-sonnet-20241022",
20
- # OpenAI GPT-4o models → Claude 3.7 Sonnet
21
- "gpt-4o": "claude-3-7-sonnet-20250219",
22
- "gpt-4o-2024-05-13": "claude-3-7-sonnet-20250219",
23
- "gpt-4o-2024-08-06": "claude-3-7-sonnet-20250219",
24
- "gpt-4o-2024-11-20": "claude-3-7-sonnet-20250219",
25
- # OpenAI GPT-4o-mini models → Claude 3.5 Haiku
26
- "gpt-4o-mini": "claude-3-5-haiku-latest",
27
- "gpt-4o-mini-2024-07-18": "claude-3-5-haiku-latest",
28
- # OpenAI o1 models → Claude models that support thinking
29
- "o1": "claude-opus-4-20250514",
30
- "o1-preview": "claude-opus-4-20250514",
31
- "o1-mini": "claude-sonnet-4-20250514",
32
- # OpenAI o3 models → Claude Opus 4
33
- "o3-mini": "claude-opus-4-20250514",
34
- # OpenAI GPT-3.5 models → Claude 3.5 Haiku (faster, cheaper)
35
- "gpt-3.5-turbo": "claude-3-5-haiku-20241022",
36
- "gpt-3.5-turbo-16k": "claude-3-5-haiku-20241022",
37
- "gpt-3.5-turbo-1106": "claude-3-5-haiku-20241022",
38
- "gpt-3.5-turbo-0125": "claude-3-5-haiku-20241022",
39
- # OpenAI text models → Claude 3.5 Sonnet
40
- "text-davinci-003": "claude-3-5-sonnet-20241022",
41
- "text-davinci-002": "claude-3-5-sonnet-20241022",
42
- # Claude model aliases → canonical Claude models
43
- "claude-3-5-sonnet-latest": "claude-3-5-sonnet-20241022",
44
- "claude-3-5-sonnet-20240620": "claude-3-5-sonnet-20240620",
45
- "claude-3-5-sonnet-20241022": "claude-3-5-sonnet-20241022",
46
- "claude-3-5-haiku-latest": "claude-3-5-haiku-20241022",
47
- "claude-3-5-haiku-20241022": "claude-3-5-haiku-20241022",
48
- "claude-3-opus": "claude-3-opus-20240229",
49
- "claude-3-opus-20240229": "claude-3-opus-20240229",
50
- "claude-3-sonnet": "claude-3-sonnet-20240229",
51
- "claude-3-sonnet-20240229": "claude-3-sonnet-20240229",
52
- "claude-3-haiku": "claude-3-haiku-20240307",
53
- "claude-3-haiku-20240307": "claude-3-haiku-20240307",
54
- }
55
-
56
-
57
- def map_model_to_claude(model_name: str) -> str:
58
- """Map any model name to its canonical Claude model name.
59
-
60
- This function handles:
61
- - OpenAI model names → Claude equivalents
62
- - Claude aliases → canonical Claude names
63
- - Pattern matching for versioned models
64
- - Pass-through for unknown models
65
-
66
- Args:
67
- model_name: Model identifier (OpenAI, Claude, or alias)
68
-
69
- Returns:
70
- Canonical Claude model identifier
71
- """
72
- # Direct mapping first (handles both OpenAI and Claude aliases)
73
- claude_model = MODEL_MAPPING.get(model_name)
74
- if claude_model:
75
- return claude_model
76
-
77
- # Pattern matching for versioned OpenAI models
78
- if model_name.startswith("gpt-4o-mini"):
79
- return "claude-3-5-haiku-latest"
80
- elif model_name.startswith("gpt-4o") or model_name.startswith("gpt-4"):
81
- return "claude-3-7-sonnet-20250219"
82
- elif model_name.startswith("gpt-3.5"):
83
- return "claude-3-5-haiku-latest"
84
- elif (
85
- model_name.startswith("o1")
86
- or model_name.startswith("gpt-5")
87
- or model_name.startswith("o3")
88
- or model_name.startswith("gpt")
89
- ):
90
- return "claude-sonnet-4-20250514"
91
-
92
- # If it's already a Claude model, pass through unchanged
93
- if model_name.startswith("claude-"):
94
- return model_name
95
-
96
- # For unknown models, pass through unchanged
97
- return model_name
98
-
99
-
100
- def get_openai_to_claude_mapping() -> dict[str, str]:
101
- """Get mapping of OpenAI models to Claude models.
102
-
103
- Returns:
104
- Dictionary mapping OpenAI model names to Claude model names
105
- """
106
- return {k: v for k, v in MODEL_MAPPING.items() if not k.startswith("claude-")}
107
-
108
-
109
- def get_claude_aliases_mapping() -> dict[str, str]:
110
- """Get mapping of Claude aliases to canonical Claude names.
111
-
112
- Returns:
113
- Dictionary mapping Claude aliases to canonical model names
114
- """
115
- return {k: v for k, v in MODEL_MAPPING.items() if k.startswith("claude-")}
116
-
117
-
118
- def get_supported_claude_models() -> list[str]:
119
- """Get list of supported canonical Claude models.
120
-
121
- Returns:
122
- Sorted list of unique canonical Claude model names
123
- """
124
- return sorted(set(MODEL_MAPPING.values()))
125
-
126
-
127
- def is_openai_model(model_name: str) -> bool:
128
- """Check if a model name is an OpenAI model.
129
-
130
- Args:
131
- model_name: Model identifier to check
132
-
133
- Returns:
134
- True if the model is an OpenAI model, False otherwise
135
- """
136
- return (
137
- model_name.startswith(("gpt-", "o1", "o3", "text-davinci"))
138
- or model_name in get_openai_to_claude_mapping()
139
- )
140
-
141
-
142
- def is_claude_model(model_name: str) -> bool:
143
- """Check if a model name is a Claude model (canonical or alias).
144
-
145
- Args:
146
- model_name: Model identifier to check
147
-
148
- Returns:
149
- True if the model is a Claude model, False otherwise
150
- """
151
- return (
152
- model_name.startswith("claude-") or model_name in get_claude_aliases_mapping()
153
- )
154
-
155
-
156
- # Backward compatibility exports
157
- OPENAI_TO_CLAUDE_MODEL_MAPPING = get_openai_to_claude_mapping()
158
- CLAUDE_MODEL_MAPPINGS = get_claude_aliases_mapping()
159
-
160
-
161
- # Legacy function aliases
162
- def map_openai_model_to_claude(openai_model: str) -> str:
163
- """Legacy alias for map_model_to_claude().
164
-
165
- Args:
166
- openai_model: OpenAI model identifier
167
-
168
- Returns:
169
- Claude model identifier
170
- """
171
- return map_model_to_claude(openai_model)
172
-
173
-
174
- def get_canonical_model_name(model_name: str) -> str:
175
- """Legacy alias for map_model_to_claude().
176
-
177
- Args:
178
- model_name: Model name (possibly an alias)
179
-
180
- Returns:
181
- Canonical model name
182
- """
183
- return map_model_to_claude(model_name)
184
-
185
-
186
- __all__ = [
187
- "MODEL_MAPPING",
188
- "map_model_to_claude",
189
- "get_openai_to_claude_mapping",
190
- "get_claude_aliases_mapping",
191
- "get_supported_claude_models",
192
- "is_openai_model",
193
- "is_claude_model",
194
- # Backward compatibility
195
- "OPENAI_TO_CLAUDE_MODEL_MAPPING",
196
- "CLAUDE_MODEL_MAPPINGS",
197
- "map_openai_model_to_claude",
198
- "get_canonical_model_name",
199
- ]
@@ -1,150 +0,0 @@
1
- """Shared models provider for CCProxy API Server.
2
-
3
- This module provides a centralized source for all available models,
4
- combining Claude and OpenAI models in a consistent format.
5
- """
6
-
7
- from __future__ import annotations
8
-
9
- from typing import Any
10
-
11
- from ccproxy.utils.model_mapping import get_supported_claude_models
12
-
13
-
14
- def get_anthropic_models() -> list[dict[str, Any]]:
15
- """Get list of Anthropic models with metadata.
16
-
17
- Returns:
18
- List of Anthropic model entries with type, id, display_name, and created_at fields
19
- """
20
- # Model display names mapping
21
- display_names = {
22
- "claude-opus-4-20250514": "Claude Opus 4",
23
- "claude-sonnet-4-20250514": "Claude Sonnet 4",
24
- "claude-3-7-sonnet-20250219": "Claude Sonnet 3.7",
25
- "claude-3-5-sonnet-20241022": "Claude Sonnet 3.5 (New)",
26
- "claude-3-5-haiku-20241022": "Claude Haiku 3.5",
27
- "claude-3-5-haiku-latest": "Claude Haiku 3.5",
28
- "claude-3-5-sonnet-20240620": "Claude Sonnet 3.5 (Old)",
29
- "claude-3-haiku-20240307": "Claude Haiku 3",
30
- "claude-3-opus-20240229": "Claude Opus 3",
31
- }
32
-
33
- # Model creation timestamps
34
- timestamps = {
35
- "claude-opus-4-20250514": 1747526400, # 2025-05-22
36
- "claude-sonnet-4-20250514": 1747526400, # 2025-05-22
37
- "claude-3-7-sonnet-20250219": 1740268800, # 2025-02-24
38
- "claude-3-5-sonnet-20241022": 1729555200, # 2024-10-22
39
- "claude-3-5-haiku-20241022": 1729555200, # 2024-10-22
40
- "claude-3-5-haiku-latest": 1729555200, # 2024-10-22
41
- "claude-3-5-sonnet-20240620": 1718841600, # 2024-06-20
42
- "claude-3-haiku-20240307": 1709769600, # 2024-03-07
43
- "claude-3-opus-20240229": 1709164800, # 2024-02-29
44
- }
45
-
46
- # Get supported Claude models from existing utility
47
- supported_models = get_supported_claude_models()
48
-
49
- # Create Anthropic-style model entries
50
- models = []
51
- for model_id in supported_models:
52
- models.append(
53
- {
54
- "type": "model",
55
- "id": model_id,
56
- "display_name": display_names.get(model_id, model_id),
57
- "created_at": timestamps.get(model_id, 1677610602), # Default timestamp
58
- }
59
- )
60
-
61
- return models
62
-
63
-
64
- def get_openai_models() -> list[dict[str, Any]]:
65
- """Get list of recent OpenAI models with metadata.
66
-
67
- Returns:
68
- List of OpenAI model entries with id, object, created, and owned_by fields
69
- """
70
- return [
71
- {
72
- "id": "gpt-4o",
73
- "object": "model",
74
- "created": 1715367049,
75
- "owned_by": "openai",
76
- },
77
- {
78
- "id": "gpt-4o-mini",
79
- "object": "model",
80
- "created": 1721172741,
81
- "owned_by": "openai",
82
- },
83
- {
84
- "id": "gpt-4-turbo",
85
- "object": "model",
86
- "created": 1712361441,
87
- "owned_by": "openai",
88
- },
89
- {
90
- "id": "gpt-4-turbo-preview",
91
- "object": "model",
92
- "created": 1706037777,
93
- "owned_by": "openai",
94
- },
95
- {
96
- "id": "o1",
97
- "object": "model",
98
- "created": 1734375816,
99
- "owned_by": "openai",
100
- },
101
- {
102
- "id": "o1-mini",
103
- "object": "model",
104
- "created": 1725649008,
105
- "owned_by": "openai",
106
- },
107
- {
108
- "id": "o1-preview",
109
- "object": "model",
110
- "created": 1725648897,
111
- "owned_by": "openai",
112
- },
113
- {
114
- "id": "o3",
115
- "object": "model",
116
- "created": 1744225308,
117
- "owned_by": "openai",
118
- },
119
- {
120
- "id": "o3-mini",
121
- "object": "model",
122
- "created": 1737146383,
123
- "owned_by": "openai",
124
- },
125
- ]
126
-
127
-
128
- def get_models_list() -> dict[str, Any]:
129
- """Get combined list of available Claude and OpenAI models.
130
-
131
- Returns:
132
- Dictionary with combined list of models in mixed format compatible with both
133
- Anthropic and OpenAI API specifications
134
- """
135
- anthropic_models = get_anthropic_models()
136
- openai_models = get_openai_models()
137
-
138
- # Return combined response in mixed format
139
- return {
140
- "data": anthropic_models + openai_models,
141
- "has_more": False,
142
- "object": "list",
143
- }
144
-
145
-
146
- __all__ = [
147
- "get_anthropic_models",
148
- "get_openai_models",
149
- "get_models_list",
150
- ]