my-claude-code 7.13.1__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 (567) hide show
  1. my_claude_code/__init__.py +1 -0
  2. my_claude_code/api/__init__.py +1 -0
  3. my_claude_code/api/admin_cache.py +49 -0
  4. my_claude_code/api/admin_claude_config_routes.py +212 -0
  5. my_claude_code/api/admin_custom_routes.py +610 -0
  6. my_claude_code/api/admin_desktop_app_routes.py +472 -0
  7. my_claude_code/api/admin_export_routes.py +455 -0
  8. my_claude_code/api/admin_harness_routes.py +621 -0
  9. my_claude_code/api/admin_routes.py +3517 -0
  10. my_claude_code/api/admin_server_routes.py +183 -0
  11. my_claude_code/api/admin_static/admin.css +7774 -0
  12. my_claude_code/api/admin_static/admin.js +18659 -0
  13. my_claude_code/api/admin_static/img/admin-analytics-cost.png +0 -0
  14. my_claude_code/api/admin_static/img/admin-analytics-harness.png +0 -0
  15. my_claude_code/api/admin_static/img/admin-analytics.png +0 -0
  16. my_claude_code/api/admin_static/img/admin-catalogue-refresh.png +0 -0
  17. my_claude_code/api/admin_static/img/admin-coding-agent-card.png +0 -0
  18. my_claude_code/api/admin_static/img/admin-coding-agents.png +0 -0
  19. my_claude_code/api/admin_static/img/admin-credential-health.png +0 -0
  20. my_claude_code/api/admin_static/img/admin-custom-provider.png +0 -0
  21. my_claude_code/api/admin_static/img/admin-desktop-apps.png +0 -0
  22. my_claude_code/api/admin_static/img/admin-get-started.png +0 -0
  23. my_claude_code/api/admin_static/img/admin-key-performance.png +0 -0
  24. my_claude_code/api/admin_static/img/admin-limits-calculator.png +0 -0
  25. my_claude_code/api/admin_static/img/admin-limits.png +0 -0
  26. my_claude_code/api/admin_static/img/admin-messaging.png +0 -0
  27. my_claude_code/api/admin_static/img/admin-model-config.png +0 -0
  28. my_claude_code/api/admin_static/img/admin-models-billed-est.png +0 -0
  29. my_claude_code/api/admin_static/img/admin-models-bulk.png +0 -0
  30. my_claude_code/api/admin_static/img/admin-models-learned.png +0 -0
  31. my_claude_code/api/admin_static/img/admin-models.png +0 -0
  32. my_claude_code/api/admin_static/img/admin-page.png +0 -0
  33. my_claude_code/api/admin_static/img/admin-providers.png +0 -0
  34. my_claude_code/api/admin_static/img/admin-request-contradiction.png +0 -0
  35. my_claude_code/api/admin_static/img/admin-request-detail.png +0 -0
  36. my_claude_code/api/admin_static/img/admin-requests.png +0 -0
  37. my_claude_code/api/admin_static/img/admin-version.png +0 -0
  38. my_claude_code/api/admin_static/img/admin-vision-adapter-mode.png +0 -0
  39. my_claude_code/api/admin_static/img/admin-websearch-analytics.png +0 -0
  40. my_claude_code/api/admin_static/img/admin-websearch.png +0 -0
  41. my_claude_code/api/admin_static/img/app-icon-96.png +0 -0
  42. my_claude_code/api/admin_static/img/cc-model-picker.png +0 -0
  43. my_claude_code/api/admin_static/img/claude-desktop-developer-menu.png +0 -0
  44. my_claude_code/api/admin_static/img/claude-desktop-gateway-config.png +0 -0
  45. my_claude_code/api/admin_static/img/codex-model-picker.png +0 -0
  46. my_claude_code/api/admin_static/index.html +4842 -0
  47. my_claude_code/api/admin_websearch_routes.py +121 -0
  48. my_claude_code/api/app.py +171 -0
  49. my_claude_code/api/dependencies.py +140 -0
  50. my_claude_code/api/detection.py +121 -0
  51. my_claude_code/api/docs_content.py +290 -0
  52. my_claude_code/api/docs_render.py +278 -0
  53. my_claude_code/api/gemini_model_catalog.py +71 -0
  54. my_claude_code/api/gemini_routes.py +245 -0
  55. my_claude_code/api/handlers/__init__.py +15 -0
  56. my_claude_code/api/handlers/chat_completions.py +335 -0
  57. my_claude_code/api/handlers/gemini.py +350 -0
  58. my_claude_code/api/handlers/messages.py +688 -0
  59. my_claude_code/api/handlers/responses.py +239 -0
  60. my_claude_code/api/handlers/token_count.py +85 -0
  61. my_claude_code/api/model_admin.py +1349 -0
  62. my_claude_code/api/model_catalog.py +264 -0
  63. my_claude_code/api/models_page_cache.py +166 -0
  64. my_claude_code/api/optimization_handlers.py +223 -0
  65. my_claude_code/api/ports.py +62 -0
  66. my_claude_code/api/request_capture.py +1397 -0
  67. my_claude_code/api/request_errors.py +112 -0
  68. my_claude_code/api/request_ids.py +100 -0
  69. my_claude_code/api/request_pricing.py +209 -0
  70. my_claude_code/api/response_streams.py +470 -0
  71. my_claude_code/api/routes.py +310 -0
  72. my_claude_code/api/validation_log.py +46 -0
  73. my_claude_code/api/web_tools/__init__.py +51 -0
  74. my_claude_code/api/web_tools/constants.py +17 -0
  75. my_claude_code/api/web_tools/egress.py +105 -0
  76. my_claude_code/api/web_tools/outbound.py +692 -0
  77. my_claude_code/api/web_tools/parsers.py +102 -0
  78. my_claude_code/api/web_tools/request.py +132 -0
  79. my_claude_code/api/web_tools/search_providers.py +56 -0
  80. my_claude_code/api/web_tools/streaming.py +315 -0
  81. my_claude_code/api/wire_surfaces.py +66 -0
  82. my_claude_code/application/__init__.py +1 -0
  83. my_claude_code/application/catalogue_model.py +512 -0
  84. my_claude_code/application/catalogues/__init__.py +166 -0
  85. my_claude_code/application/catalogues/aider.py +249 -0
  86. my_claude_code/application/catalogues/base.py +265 -0
  87. my_claude_code/application/catalogues/claude_desktop.py +80 -0
  88. my_claude_code/application/catalogues/cline.py +211 -0
  89. my_claude_code/application/catalogues/codex.py +294 -0
  90. my_claude_code/application/catalogues/commandcode.py +303 -0
  91. my_claude_code/application/catalogues/crush.py +394 -0
  92. my_claude_code/application/catalogues/droid.py +184 -0
  93. my_claude_code/application/catalogues/gemini_cli.py +237 -0
  94. my_claude_code/application/catalogues/goose.py +254 -0
  95. my_claude_code/application/catalogues/kilo.py +69 -0
  96. my_claude_code/application/catalogues/kimi.py +245 -0
  97. my_claude_code/application/catalogues/opencode.py +365 -0
  98. my_claude_code/application/catalogues/pi.py +131 -0
  99. my_claude_code/application/catalogues/qwen.py +262 -0
  100. my_claude_code/application/catalogues/vscode.py +147 -0
  101. my_claude_code/application/cost.py +323 -0
  102. my_claude_code/application/deadline_hints.py +98 -0
  103. my_claude_code/application/derived_payloads.py +224 -0
  104. my_claude_code/application/desktop_documents.py +379 -0
  105. my_claude_code/application/errors.py +80 -0
  106. my_claude_code/application/execution.py +2238 -0
  107. my_claude_code/application/model_metadata.py +565 -0
  108. my_claude_code/application/output_tokens.py +335 -0
  109. my_claude_code/application/ports.py +165 -0
  110. my_claude_code/application/reasoning.py +112 -0
  111. my_claude_code/application/reasoning_budget.py +229 -0
  112. my_claude_code/application/reasoning_gating.py +719 -0
  113. my_claude_code/application/release_updates.py +1485 -0
  114. my_claude_code/application/route_health.py +441 -0
  115. my_claude_code/application/routing.py +1282 -0
  116. my_claude_code/application/tier_chains.py +175 -0
  117. my_claude_code/application/vision_describe.py +529 -0
  118. my_claude_code/assets/app-icon.icns +0 -0
  119. my_claude_code/assets/app-icon.ico +0 -0
  120. my_claude_code/assets/app-icon.png +0 -0
  121. my_claude_code/assets/tray-icon.png +0 -0
  122. my_claude_code/cli/__init__.py +5 -0
  123. my_claude_code/cli/apps_command.py +274 -0
  124. my_claude_code/cli/claude_env.py +118 -0
  125. my_claude_code/cli/commands.py +827 -0
  126. my_claude_code/cli/desktop.py +1239 -0
  127. my_claude_code/cli/desktop_assets.py +153 -0
  128. my_claude_code/cli/desktop_entrypoint.py +232 -0
  129. my_claude_code/cli/desktop_status.py +360 -0
  130. my_claude_code/cli/desktop_tray.py +249 -0
  131. my_claude_code/cli/desktop_window.py +635 -0
  132. my_claude_code/cli/entrypoints.py +217 -0
  133. my_claude_code/cli/first_start.py +205 -0
  134. my_claude_code/cli/harnesses/__init__.py +6 -0
  135. my_claude_code/cli/harnesses/catalogue_client.py +243 -0
  136. my_claude_code/cli/harnesses/catalogue_documents.py +110 -0
  137. my_claude_code/cli/harnesses/registry.py +53 -0
  138. my_claude_code/cli/installer_support.py +467 -0
  139. my_claude_code/cli/launchers/__init__.py +1 -0
  140. my_claude_code/cli/launchers/aider.py +268 -0
  141. my_claude_code/cli/launchers/claude.py +126 -0
  142. my_claude_code/cli/launchers/cline.py +270 -0
  143. my_claude_code/cli/launchers/codex.py +235 -0
  144. my_claude_code/cli/launchers/commandcode.py +243 -0
  145. my_claude_code/cli/launchers/common.py +183 -0
  146. my_claude_code/cli/launchers/crush.py +231 -0
  147. my_claude_code/cli/launchers/droid.py +226 -0
  148. my_claude_code/cli/launchers/gemini.py +322 -0
  149. my_claude_code/cli/launchers/goose.py +269 -0
  150. my_claude_code/cli/launchers/kimi.py +214 -0
  151. my_claude_code/cli/launchers/opencode.py +203 -0
  152. my_claude_code/cli/launchers/pi.py +158 -0
  153. my_claude_code/cli/launchers/pi_extension.ts +216 -0
  154. my_claude_code/cli/launchers/qwen.py +266 -0
  155. my_claude_code/cli/legacy_stubs.py +85 -0
  156. my_claude_code/cli/managed/__init__.py +6 -0
  157. my_claude_code/cli/managed/claude.py +215 -0
  158. my_claude_code/cli/managed/diagnostics.py +47 -0
  159. my_claude_code/cli/managed/manager.py +207 -0
  160. my_claude_code/cli/managed/session.py +293 -0
  161. my_claude_code/cli/migrate_config_dir.py +479 -0
  162. my_claude_code/cli/port_diagnostics.py +142 -0
  163. my_claude_code/cli/port_takeover.py +308 -0
  164. my_claude_code/cli/process_registry.py +85 -0
  165. my_claude_code/cli/rtk_commands.py +117 -0
  166. my_claude_code/cli/tool_paths.py +58 -0
  167. my_claude_code/config/__init__.py +5 -0
  168. my_claude_code/config/admin/__init__.py +1 -0
  169. my_claude_code/config/admin/manifest.py +2485 -0
  170. my_claude_code/config/admin/persistence.py +425 -0
  171. my_claude_code/config/admin/provider_manifest.py +749 -0
  172. my_claude_code/config/admin/route_refs.py +119 -0
  173. my_claude_code/config/admin/sources.py +91 -0
  174. my_claude_code/config/admin/spec.py +71 -0
  175. my_claude_code/config/admin/status.py +129 -0
  176. my_claude_code/config/admin/validation.py +81 -0
  177. my_claude_code/config/admin/values.py +256 -0
  178. my_claude_code/config/admin/websearch_manifest.py +280 -0
  179. my_claude_code/config/atomic_json.py +69 -0
  180. my_claude_code/config/claude_code_catalog.py +242 -0
  181. my_claude_code/config/claude_config_editor.py +443 -0
  182. my_claude_code/config/claude_discovery.py +225 -0
  183. my_claude_code/config/claude_settings.py +426 -0
  184. my_claude_code/config/constants.py +669 -0
  185. my_claude_code/config/credentials.py +21 -0
  186. my_claude_code/config/data/__init__.py +1 -0
  187. my_claude_code/config/data/claude_code_config_catalog.json +6105 -0
  188. my_claude_code/config/desktop.py +792 -0
  189. my_claude_code/config/desktop_apply.py +1810 -0
  190. my_claude_code/config/desktop_apps.py +1873 -0
  191. my_claude_code/config/desktop_shell.py +1032 -0
  192. my_claude_code/config/document_codecs.py +923 -0
  193. my_claude_code/config/env.example +1584 -0
  194. my_claude_code/config/env_files.py +192 -0
  195. my_claude_code/config/env_migrations.py +234 -0
  196. my_claude_code/config/env_template.py +76 -0
  197. my_claude_code/config/harness_attribution.py +42 -0
  198. my_claude_code/config/harness_base_url.py +104 -0
  199. my_claude_code/config/harness_cline.py +159 -0
  200. my_claude_code/config/harness_config_merge.py +326 -0
  201. my_claude_code/config/harness_tiers.py +386 -0
  202. my_claude_code/config/harness_toml.py +227 -0
  203. my_claude_code/config/harnesses.py +1702 -0
  204. my_claude_code/config/legacy_env_rewrite.py +158 -0
  205. my_claude_code/config/limits.py +318 -0
  206. my_claude_code/config/logging_config.py +372 -0
  207. my_claude_code/config/model_overrides.py +383 -0
  208. my_claude_code/config/model_refs.py +120 -0
  209. my_claude_code/config/nim.py +119 -0
  210. my_claude_code/config/onboarding.py +458 -0
  211. my_claude_code/config/paths.py +724 -0
  212. my_claude_code/config/provider_catalog.py +912 -0
  213. my_claude_code/config/provider_registry.py +539 -0
  214. my_claude_code/config/proxy_auth.py +73 -0
  215. my_claude_code/config/reasoning.py +25 -0
  216. my_claude_code/config/reasoning_enum.py +146 -0
  217. my_claude_code/config/restore_record.py +420 -0
  218. my_claude_code/config/rtk.py +563 -0
  219. my_claude_code/config/server_urls.py +32 -0
  220. my_claude_code/config/settings.py +2260 -0
  221. my_claude_code/config/update_progress.py +492 -0
  222. my_claude_code/config/websearch_catalog.py +925 -0
  223. my_claude_code/core/__init__.py +15 -0
  224. my_claude_code/core/anthropic/__init__.py +128 -0
  225. my_claude_code/core/anthropic/content.py +31 -0
  226. my_claude_code/core/anthropic/conversion.py +968 -0
  227. my_claude_code/core/anthropic/errors.py +93 -0
  228. my_claude_code/core/anthropic/image_downscale.py +185 -0
  229. my_claude_code/core/anthropic/image_tokens.py +349 -0
  230. my_claude_code/core/anthropic/models.py +183 -0
  231. my_claude_code/core/anthropic/openai_tool_names.py +124 -0
  232. my_claude_code/core/anthropic/request_modalities.py +130 -0
  233. my_claude_code/core/anthropic/request_serialization.py +57 -0
  234. my_claude_code/core/anthropic/request_snapshot.py +35 -0
  235. my_claude_code/core/anthropic/server_tool_sse.py +12 -0
  236. my_claude_code/core/anthropic/sse_aggregation.py +128 -0
  237. my_claude_code/core/anthropic/stream_contracts.py +343 -0
  238. my_claude_code/core/anthropic/streaming/__init__.py +39 -0
  239. my_claude_code/core/anthropic/streaming/emitter.py +62 -0
  240. my_claude_code/core/anthropic/streaming/ledger.py +550 -0
  241. my_claude_code/core/anthropic/streaming/recovery.py +212 -0
  242. my_claude_code/core/anthropic/streaming/splice.py +542 -0
  243. my_claude_code/core/anthropic/streaming/truncation.py +297 -0
  244. my_claude_code/core/anthropic/thinking.py +140 -0
  245. my_claude_code/core/anthropic/tokens.py +169 -0
  246. my_claude_code/core/anthropic/tool_result_media.py +524 -0
  247. my_claude_code/core/anthropic/tool_result_trimming.py +487 -0
  248. my_claude_code/core/anthropic/tool_schema.py +94 -0
  249. my_claude_code/core/anthropic/tools.py +462 -0
  250. my_claude_code/core/anthropic/upstream_errors.py +55 -0
  251. my_claude_code/core/anthropic/utils.py +9 -0
  252. my_claude_code/core/async_iterators.py +26 -0
  253. my_claude_code/core/catalogue_refs.py +84 -0
  254. my_claude_code/core/client_fingerprint.py +415 -0
  255. my_claude_code/core/credential_attribution.py +73 -0
  256. my_claude_code/core/credential_rotation.py +582 -0
  257. my_claude_code/core/derived_cache.py +172 -0
  258. my_claude_code/core/diagnostics.py +330 -0
  259. my_claude_code/core/export.py +1334 -0
  260. my_claude_code/core/failures.py +147 -0
  261. my_claude_code/core/gateway_model_ids.py +51 -0
  262. my_claude_code/core/gemini_api/__init__.py +59 -0
  263. my_claude_code/core/gemini_api/adapter.py +69 -0
  264. my_claude_code/core/gemini_api/assembler.py +258 -0
  265. my_claude_code/core/gemini_api/catalog.py +58 -0
  266. my_claude_code/core/gemini_api/errors.py +131 -0
  267. my_claude_code/core/gemini_api/events.py +51 -0
  268. my_claude_code/core/gemini_api/input.py +558 -0
  269. my_claude_code/core/gemini_api/models.py +186 -0
  270. my_claude_code/core/gemini_api/paths.py +77 -0
  271. my_claude_code/core/gemini_api/response.py +74 -0
  272. my_claude_code/core/gemini_api/stream.py +97 -0
  273. my_claude_code/core/gemini_api/tools.py +203 -0
  274. my_claude_code/core/gemini_api/usage.py +92 -0
  275. my_claude_code/core/identity.py +161 -0
  276. my_claude_code/core/image_geometry.py +200 -0
  277. my_claude_code/core/interprocess_lock.py +90 -0
  278. my_claude_code/core/mcc_processes.py +687 -0
  279. my_claude_code/core/model_ids.py +224 -0
  280. my_claude_code/core/model_visibility.py +215 -0
  281. my_claude_code/core/openai_chat_completions/__init__.py +25 -0
  282. my_claude_code/core/openai_chat_completions/adapter.py +72 -0
  283. my_claude_code/core/openai_chat_completions/assembler.py +250 -0
  284. my_claude_code/core/openai_chat_completions/completion.py +99 -0
  285. my_claude_code/core/openai_chat_completions/errors.py +34 -0
  286. my_claude_code/core/openai_chat_completions/events.py +46 -0
  287. my_claude_code/core/openai_chat_completions/ids.py +12 -0
  288. my_claude_code/core/openai_chat_completions/input.py +403 -0
  289. my_claude_code/core/openai_chat_completions/models.py +69 -0
  290. my_claude_code/core/openai_chat_completions/stream.py +97 -0
  291. my_claude_code/core/openai_chat_completions/tools.py +139 -0
  292. my_claude_code/core/openai_chat_completions/usage.py +88 -0
  293. my_claude_code/core/openai_common/__init__.py +29 -0
  294. my_claude_code/core/openai_common/anthropic_sse.py +75 -0
  295. my_claude_code/core/openai_common/errors.py +74 -0
  296. my_claude_code/core/openai_common/usage.py +26 -0
  297. my_claude_code/core/openai_responses/__init__.py +17 -0
  298. my_claude_code/core/openai_responses/adapter.py +39 -0
  299. my_claude_code/core/openai_responses/anthropic_sse.py +15 -0
  300. my_claude_code/core/openai_responses/errors.py +28 -0
  301. my_claude_code/core/openai_responses/events.py +17 -0
  302. my_claude_code/core/openai_responses/ids.py +19 -0
  303. my_claude_code/core/openai_responses/input.py +388 -0
  304. my_claude_code/core/openai_responses/items.py +35 -0
  305. my_claude_code/core/openai_responses/models.py +26 -0
  306. my_claude_code/core/openai_responses/reasoning.py +54 -0
  307. my_claude_code/core/openai_responses/stream.py +92 -0
  308. my_claude_code/core/openai_responses/streaming/__init__.py +5 -0
  309. my_claude_code/core/openai_responses/streaming/assembler.py +356 -0
  310. my_claude_code/core/openai_responses/streaming/blocks.py +36 -0
  311. my_claude_code/core/openai_responses/streaming/completion.py +154 -0
  312. my_claude_code/core/openai_responses/streaming/error_mapping.py +28 -0
  313. my_claude_code/core/openai_responses/streaming/event_builders.py +182 -0
  314. my_claude_code/core/openai_responses/streaming/ledger.py +105 -0
  315. my_claude_code/core/openai_responses/tools.py +375 -0
  316. my_claude_code/core/openai_responses/usage.py +11 -0
  317. my_claude_code/core/optimization_discovery.py +314 -0
  318. my_claude_code/core/process_handoff.py +24 -0
  319. my_claude_code/core/rate_limit.py +321 -0
  320. my_claude_code/core/reasoning.py +490 -0
  321. my_claude_code/core/reported_cost.py +199 -0
  322. my_claude_code/core/request_headers.py +153 -0
  323. my_claude_code/core/request_images.py +164 -0
  324. my_claude_code/core/request_log.py +7447 -0
  325. my_claude_code/core/server_inventory.py +431 -0
  326. my_claude_code/core/startup_state.py +229 -0
  327. my_claude_code/core/stop_deadline.py +228 -0
  328. my_claude_code/core/tier_refs.py +394 -0
  329. my_claude_code/core/token_encoder.py +36 -0
  330. my_claude_code/core/trace.py +197 -0
  331. my_claude_code/core/upstream_ladder.py +668 -0
  332. my_claude_code/core/version.py +39 -0
  333. my_claude_code/core/waiting_clock.py +71 -0
  334. my_claude_code/core/websearch/__init__.py +8 -0
  335. my_claude_code/core/websearch/models.py +30 -0
  336. my_claude_code/core/wire_capture.py +855 -0
  337. my_claude_code/docs_bundle/ANTHROPIC-SUBSCRIPTION.md +378 -0
  338. my_claude_code/docs_bundle/ARCHITECTURE.md +2661 -0
  339. my_claude_code/docs_bundle/CLAUDE-CODE-CONFIG.md +982 -0
  340. my_claude_code/docs_bundle/CLIENTS.md +143 -0
  341. my_claude_code/docs_bundle/CONTRIBUTING.md +64 -0
  342. my_claude_code/docs_bundle/DOCS-README.md +21 -0
  343. my_claude_code/docs_bundle/MESSAGING.md +90 -0
  344. my_claude_code/docs_bundle/OAUTH-PROVIDERS.md +98 -0
  345. my_claude_code/docs_bundle/README.md +378 -0
  346. my_claude_code/docs_bundle/ROUTING-REFERENCE.md +310 -0
  347. my_claude_code/docs_bundle/USAGE.md +4484 -0
  348. my_claude_code/docs_bundle/WEB-SEARCH.md +209 -0
  349. my_claude_code/installers/install.ps1 +4675 -0
  350. my_claude_code/installers/install.sh +2644 -0
  351. my_claude_code/messaging/__init__.py +16 -0
  352. my_claude_code/messaging/cli_event_constants.py +67 -0
  353. my_claude_code/messaging/command_context.py +99 -0
  354. my_claude_code/messaging/command_dispatcher.py +31 -0
  355. my_claude_code/messaging/commands.py +182 -0
  356. my_claude_code/messaging/event_parser.py +184 -0
  357. my_claude_code/messaging/limiter.py +342 -0
  358. my_claude_code/messaging/managed_protocols.py +35 -0
  359. my_claude_code/messaging/models.py +57 -0
  360. my_claude_code/messaging/node_event_pipeline.py +120 -0
  361. my_claude_code/messaging/node_runner.py +408 -0
  362. my_claude_code/messaging/platforms/__init__.py +20 -0
  363. my_claude_code/messaging/platforms/discord.py +319 -0
  364. my_claude_code/messaging/platforms/discord_inbound.py +112 -0
  365. my_claude_code/messaging/platforms/discord_io.py +167 -0
  366. my_claude_code/messaging/platforms/factory.py +109 -0
  367. my_claude_code/messaging/platforms/outbox.py +143 -0
  368. my_claude_code/messaging/platforms/ports.py +99 -0
  369. my_claude_code/messaging/platforms/telegram.py +300 -0
  370. my_claude_code/messaging/platforms/telegram_inbound.py +133 -0
  371. my_claude_code/messaging/platforms/telegram_io.py +287 -0
  372. my_claude_code/messaging/platforms/voice_flow.py +388 -0
  373. my_claude_code/messaging/rendering/__init__.py +3 -0
  374. my_claude_code/messaging/rendering/discord_markdown.py +318 -0
  375. my_claude_code/messaging/rendering/markdown_tables.py +47 -0
  376. my_claude_code/messaging/rendering/profiles.py +53 -0
  377. my_claude_code/messaging/rendering/telegram_markdown.py +327 -0
  378. my_claude_code/messaging/safe_diagnostics.py +15 -0
  379. my_claude_code/messaging/session/__init__.py +5 -0
  380. my_claude_code/messaging/session/managed_message_log.py +135 -0
  381. my_claude_code/messaging/session/persistence.py +161 -0
  382. my_claude_code/messaging/session/store.py +162 -0
  383. my_claude_code/messaging/transcript/__init__.py +6 -0
  384. my_claude_code/messaging/transcript/buffer.py +220 -0
  385. my_claude_code/messaging/transcript/context.py +18 -0
  386. my_claude_code/messaging/transcript/renderer.py +65 -0
  387. my_claude_code/messaging/transcript/segments.py +176 -0
  388. my_claude_code/messaging/transcript/subagents.py +108 -0
  389. my_claude_code/messaging/transcription.py +132 -0
  390. my_claude_code/messaging/trees/__init__.py +41 -0
  391. my_claude_code/messaging/trees/graph.py +250 -0
  392. my_claude_code/messaging/trees/identity.py +16 -0
  393. my_claude_code/messaging/trees/manager.py +587 -0
  394. my_claude_code/messaging/trees/node.py +56 -0
  395. my_claude_code/messaging/trees/processor.py +272 -0
  396. my_claude_code/messaging/trees/queue.py +47 -0
  397. my_claude_code/messaging/trees/repository.py +135 -0
  398. my_claude_code/messaging/trees/runtime.py +487 -0
  399. my_claude_code/messaging/trees/snapshot.py +221 -0
  400. my_claude_code/messaging/trees/transitions.py +174 -0
  401. my_claude_code/messaging/turn_intake.py +241 -0
  402. my_claude_code/messaging/ui_updates.py +99 -0
  403. my_claude_code/messaging/voice.py +376 -0
  404. my_claude_code/messaging/workflow.py +714 -0
  405. my_claude_code/providers/__init__.py +12 -0
  406. my_claude_code/providers/anthropic/__init__.py +10 -0
  407. my_claude_code/providers/anthropic/models.py +55 -0
  408. my_claude_code/providers/anthropic/provider.py +160 -0
  409. my_claude_code/providers/anthropic_messages/__init__.py +21 -0
  410. my_claude_code/providers/anthropic_messages/auth.py +55 -0
  411. my_claude_code/providers/anthropic_messages/probe.py +19 -0
  412. my_claude_code/providers/anthropic_messages/provider.py +527 -0
  413. my_claude_code/providers/anthropic_messages/request.py +175 -0
  414. my_claude_code/providers/anthropic_messages/streaming.py +130 -0
  415. my_claude_code/providers/anthropic_oauth/__init__.py +74 -0
  416. my_claude_code/providers/anthropic_oauth/auth.py +266 -0
  417. my_claude_code/providers/anthropic_oauth/betas.py +48 -0
  418. my_claude_code/providers/anthropic_oauth/cli.py +242 -0
  419. my_claude_code/providers/anthropic_oauth/constants.py +270 -0
  420. my_claude_code/providers/anthropic_oauth/credentials.py +729 -0
  421. my_claude_code/providers/anthropic_oauth/entrypoint.py +109 -0
  422. my_claude_code/providers/anthropic_oauth/loopback.py +311 -0
  423. my_claude_code/providers/anthropic_oauth/oauth_login.py +222 -0
  424. my_claude_code/providers/anthropic_oauth/provider.py +341 -0
  425. my_claude_code/providers/anthropic_oauth/rate_limit_headers.py +137 -0
  426. my_claude_code/providers/base.py +206 -0
  427. my_claude_code/providers/chatgpt_oauth/__init__.py +6 -0
  428. my_claude_code/providers/chatgpt_oauth/browser_login.py +546 -0
  429. my_claude_code/providers/chatgpt_oauth/codex_catalogue.py +551 -0
  430. my_claude_code/providers/chatgpt_oauth/conversion.py +67 -0
  431. my_claude_code/providers/chatgpt_oauth/credentials.py +600 -0
  432. my_claude_code/providers/chatgpt_oauth/oauth_login.py +316 -0
  433. my_claude_code/providers/chatgpt_oauth/provider.py +827 -0
  434. my_claude_code/providers/chatgpt_oauth/response_headers.py +128 -0
  435. my_claude_code/providers/chatgpt_oauth/streaming.py +25 -0
  436. my_claude_code/providers/cloudflare/__init__.py +8 -0
  437. my_claude_code/providers/cloudflare/client.py +150 -0
  438. my_claude_code/providers/commandcode/__init__.py +10 -0
  439. my_claude_code/providers/commandcode/client.py +175 -0
  440. my_claude_code/providers/commandcode/models.py +67 -0
  441. my_claude_code/providers/credential_rotation.py +546 -0
  442. my_claude_code/providers/deepseek/__init__.py +5 -0
  443. my_claude_code/providers/deepseek/client.py +129 -0
  444. my_claude_code/providers/deepseek/compat.py +376 -0
  445. my_claude_code/providers/deepseek/tool_choice.py +72 -0
  446. my_claude_code/providers/failure_policy.py +842 -0
  447. my_claude_code/providers/gemini/__init__.py +5 -0
  448. my_claude_code/providers/gemini/client.py +91 -0
  449. my_claude_code/providers/gemini/quirks.py +161 -0
  450. my_claude_code/providers/github_models/__init__.py +5 -0
  451. my_claude_code/providers/github_models/client.py +142 -0
  452. my_claude_code/providers/google_openai/__init__.py +15 -0
  453. my_claude_code/providers/google_openai/provider.py +80 -0
  454. my_claude_code/providers/google_openai/reasoning.py +123 -0
  455. my_claude_code/providers/google_openai/thought_signatures.py +117 -0
  456. my_claude_code/providers/http.py +51 -0
  457. my_claude_code/providers/kilo/__init__.py +5 -0
  458. my_claude_code/providers/kilo/client.py +27 -0
  459. my_claude_code/providers/lmstudio/__init__.py +5 -0
  460. my_claude_code/providers/lmstudio/client.py +132 -0
  461. my_claude_code/providers/mistral/__init__.py +5 -0
  462. my_claude_code/providers/mistral/client.py +110 -0
  463. my_claude_code/providers/mistral/reasoning.py +404 -0
  464. my_claude_code/providers/model_listing.py +589 -0
  465. my_claude_code/providers/nous_portal/__init__.py +5 -0
  466. my_claude_code/providers/nous_portal/client.py +65 -0
  467. my_claude_code/providers/nvidia_nim/__init__.py +5 -0
  468. my_claude_code/providers/nvidia_nim/client.py +240 -0
  469. my_claude_code/providers/nvidia_nim/request_options.py +151 -0
  470. my_claude_code/providers/nvidia_nim/retry.py +112 -0
  471. my_claude_code/providers/nvidia_nim/tool_schema.py +255 -0
  472. my_claude_code/providers/nvidia_nim/voice.py +113 -0
  473. my_claude_code/providers/open_router/__init__.py +5 -0
  474. my_claude_code/providers/open_router/client.py +22 -0
  475. my_claude_code/providers/openai_chat/__init__.py +113 -0
  476. my_claude_code/providers/openai_chat/base_url.py +7 -0
  477. my_claude_code/providers/openai_chat/chunks.py +357 -0
  478. my_claude_code/providers/openai_chat/client_identity.py +262 -0
  479. my_claude_code/providers/openai_chat/complaint.py +26 -0
  480. my_claude_code/providers/openai_chat/extra_body.py +60 -0
  481. my_claude_code/providers/openai_chat/identity_enforcement.py +169 -0
  482. my_claude_code/providers/openai_chat/learned_dialect.py +164 -0
  483. my_claude_code/providers/openai_chat/messages_transport.py +201 -0
  484. my_claude_code/providers/openai_chat/opencode_identity.py +342 -0
  485. my_claude_code/providers/openai_chat/output_cap.py +17 -0
  486. my_claude_code/providers/openai_chat/profiles.py +954 -0
  487. my_claude_code/providers/openai_chat/provider.py +1644 -0
  488. my_claude_code/providers/openai_chat/reasoning.py +337 -0
  489. my_claude_code/providers/openai_chat/reasoning_details.py +174 -0
  490. my_claude_code/providers/openai_chat/reasoning_reject.py +16 -0
  491. my_claude_code/providers/openai_chat/request_policy.py +231 -0
  492. my_claude_code/providers/openai_chat/response_surface.py +292 -0
  493. my_claude_code/providers/openai_chat/responses_transport.py +329 -0
  494. my_claude_code/providers/openai_chat/tool_calls.py +295 -0
  495. my_claude_code/providers/openai_chat/usage.py +114 -0
  496. my_claude_code/providers/openai_responses/__init__.py +32 -0
  497. my_claude_code/providers/openai_responses/conversion.py +382 -0
  498. my_claude_code/providers/openai_responses/streaming.py +426 -0
  499. my_claude_code/providers/openrouter_gateway.py +218 -0
  500. my_claude_code/providers/rate_limit.py +400 -0
  501. my_claude_code/providers/recovery/__init__.py +141 -0
  502. my_claude_code/providers/recovery/complaint.py +190 -0
  503. my_claude_code/providers/recovery/facts.py +386 -0
  504. my_claude_code/providers/recovery/ladder.py +264 -0
  505. my_claude_code/providers/recovery/memory.py +161 -0
  506. my_claude_code/providers/recovery/output_cap.py +118 -0
  507. my_claude_code/providers/recovery/reasoning_reject.py +146 -0
  508. my_claude_code/providers/recovery/store.py +569 -0
  509. my_claude_code/providers/recovery/surface.py +107 -0
  510. my_claude_code/providers/runtime/__init__.py +11 -0
  511. my_claude_code/providers/runtime/capability_probes.py +359 -0
  512. my_claude_code/providers/runtime/config.py +178 -0
  513. my_claude_code/providers/runtime/discovery.py +313 -0
  514. my_claude_code/providers/runtime/factory.py +355 -0
  515. my_claude_code/providers/runtime/identity_probe.py +229 -0
  516. my_claude_code/providers/runtime/litellm_prices.py +411 -0
  517. my_claude_code/providers/runtime/model_cache.py +237 -0
  518. my_claude_code/providers/runtime/models_dev.py +2206 -0
  519. my_claude_code/providers/runtime/reasoning_probe.py +155 -0
  520. my_claude_code/providers/runtime/rotating.py +420 -0
  521. my_claude_code/providers/runtime/runtime.py +48 -0
  522. my_claude_code/providers/runtime/served_models.py +137 -0
  523. my_claude_code/providers/runtime/validation.py +132 -0
  524. my_claude_code/providers/stream_recovery.py +364 -0
  525. my_claude_code/providers/vertex/__init__.py +5 -0
  526. my_claude_code/providers/vertex/auth.py +105 -0
  527. my_claude_code/providers/vertex/client.py +134 -0
  528. my_claude_code/providers/vertex/endpoint.py +50 -0
  529. my_claude_code/providers/vertex/models.py +52 -0
  530. my_claude_code/runtime/__init__.py +1 -0
  531. my_claude_code/runtime/application.py +1050 -0
  532. my_claude_code/runtime/asgi.py +525 -0
  533. my_claude_code/runtime/bootstrap.py +72 -0
  534. my_claude_code/runtime/catalogue_store.py +203 -0
  535. my_claude_code/runtime/discovery_timer.py +239 -0
  536. my_claude_code/runtime/harness_catalogues.py +345 -0
  537. my_claude_code/runtime/provider_manager.py +982 -0
  538. my_claude_code/runtime/warmup.py +149 -0
  539. my_claude_code/websearch/__init__.py +52 -0
  540. my_claude_code/websearch/adapters/__init__.py +37 -0
  541. my_claude_code/websearch/adapters/brave.py +160 -0
  542. my_claude_code/websearch/adapters/ddgs.py +88 -0
  543. my_claude_code/websearch/adapters/exa.py +133 -0
  544. my_claude_code/websearch/adapters/firecrawl.py +138 -0
  545. my_claude_code/websearch/adapters/http.py +154 -0
  546. my_claude_code/websearch/adapters/jina.py +91 -0
  547. my_claude_code/websearch/adapters/linkup.py +109 -0
  548. my_claude_code/websearch/adapters/ollama.py +79 -0
  549. my_claude_code/websearch/adapters/parallel.py +111 -0
  550. my_claude_code/websearch/adapters/perplexity.py +99 -0
  551. my_claude_code/websearch/adapters/searchapi.py +119 -0
  552. my_claude_code/websearch/adapters/searxng.py +97 -0
  553. my_claude_code/websearch/adapters/serpapi.py +119 -0
  554. my_claude_code/websearch/adapters/serper.py +124 -0
  555. my_claude_code/websearch/adapters/tavily.py +113 -0
  556. my_claude_code/websearch/analytics.py +1407 -0
  557. my_claude_code/websearch/base.py +157 -0
  558. my_claude_code/websearch/errors.py +79 -0
  559. my_claude_code/websearch/options.py +69 -0
  560. my_claude_code/websearch/registry.py +534 -0
  561. my_claude_code/websearch/rotation.py +253 -0
  562. my_claude_code-7.13.1.dist-info/METADATA +423 -0
  563. my_claude_code-7.13.1.dist-info/RECORD +567 -0
  564. my_claude_code-7.13.1.dist-info/WHEEL +4 -0
  565. my_claude_code-7.13.1.dist-info/entry_points.txt +44 -0
  566. my_claude_code-7.13.1.dist-info/licenses/COMMERCIAL-LICENSE.md +55 -0
  567. my_claude_code-7.13.1.dist-info/licenses/LICENSE +748 -0
@@ -0,0 +1,376 @@
1
+ """DeepSeek Anthropic-to-OpenAI chat request policy."""
2
+
3
+ from collections.abc import Mapping
4
+ from typing import Any
5
+
6
+ from loguru import logger
7
+
8
+ from my_claude_code.application.errors import InvalidRequestError
9
+ from my_claude_code.config.settings import configured_default_max_output_tokens
10
+ from my_claude_code.core.anthropic import (
11
+ ReasoningReplayMode,
12
+ dump_messages_request,
13
+ serialize_tool_result_content,
14
+ )
15
+ from my_claude_code.core.anthropic.models import MessagesRequest
16
+ from my_claude_code.core.reasoning import (
17
+ ReasoningControl,
18
+ ReasoningPolicy,
19
+ )
20
+ from my_claude_code.providers.openai_chat import (
21
+ OpenAIChatRequestPolicy,
22
+ build_openai_chat_request_body,
23
+ )
24
+
25
+ DEEPSEEK_REQUEST_POLICY = OpenAIChatRequestPolicy(
26
+ provider_name="DEEPSEEK",
27
+ reasoning_replay=ReasoningReplayMode.REASONING_CONTENT,
28
+ include_extra_body=True,
29
+ )
30
+
31
+ # Server-tool blocks DeepSeek's native surface cannot represent at all. Images
32
+ # and documents are deliberately *not* here any more: since 6.49.0 the router
33
+ # replaces a visual block with a placeholder for any model published as not
34
+ # accepting images -- which DeepSeek's chat models are -- so by the time a body
35
+ # is built there is nothing to strip, and the general rule covers every other
36
+ # provider too. A DeepSeek model that does publish image support now gets the
37
+ # picture through the ordinary hoist rather than a provider-specific refusal.
38
+ _UNSUPPORTED_MESSAGE_BLOCK_TYPES = frozenset(
39
+ {
40
+ "server_tool_use",
41
+ "web_search_tool_result",
42
+ "web_fetch_tool_result",
43
+ }
44
+ )
45
+
46
+
47
+ def build_deepseek_request_body(
48
+ request_data: MessagesRequest,
49
+ *,
50
+ reasoning: ReasoningPolicy,
51
+ provider_id: str = "",
52
+ ) -> dict:
53
+ """Build a DeepSeek Chat Completions body from an Anthropic request."""
54
+ logger.debug(
55
+ "DEEPSEEK_REQUEST: chat build model={} msgs={}",
56
+ request_data.model,
57
+ len(request_data.messages),
58
+ )
59
+
60
+ data = dump_messages_request(request_data)
61
+ _validate_deepseek_request_dict(data)
62
+
63
+ has_tool_history = _has_tool_history(data)
64
+ has_replayable_tool_thinking = _all_tool_calls_have_replayable_thinking(data)
65
+ unsafe_tool_followup = has_tool_history and not has_replayable_tool_thinking
66
+ effective_reasoning = reasoning
67
+ if reasoning.control is not ReasoningControl.OFF:
68
+ if unsafe_tool_followup:
69
+ logger.debug(
70
+ "DEEPSEEK_REQUEST: disabling thinking for tool follow-up without "
71
+ "replayable thinking model={} msgs={} tools={}",
72
+ data.get("model"),
73
+ len(data.get("messages", [])),
74
+ len(data.get("tools", [])),
75
+ )
76
+ _remove_deepseek_thinking_hints(data)
77
+ effective_reasoning = ReasoningPolicy.off()
78
+ elif has_tool_history:
79
+ logger.debug(
80
+ "DEEPSEEK_REQUEST: keeping thinking for tool follow-up with "
81
+ "replayable thinking model={} msgs={} tools={}",
82
+ data.get("model"),
83
+ len(data.get("messages", [])),
84
+ len(data.get("tools", [])),
85
+ )
86
+ elif data.get("tools") or data.get("tool_choice"):
87
+ logger.debug(
88
+ "DEEPSEEK_REQUEST: keeping thinking for initial tool request "
89
+ "model={} msgs={} tools={}",
90
+ data.get("model"),
91
+ len(data.get("messages", [])),
92
+ len(data.get("tools", [])),
93
+ )
94
+
95
+ if "messages" in data:
96
+ data["messages"] = _normalize_tool_result_content(
97
+ sanitize_deepseek_messages_for_openai(data["messages"])
98
+ )
99
+
100
+ sanitized_request = MessagesRequest.model_validate(data)
101
+ body = build_openai_chat_request_body(
102
+ sanitized_request,
103
+ reasoning=effective_reasoning,
104
+ policy=DEEPSEEK_REQUEST_POLICY,
105
+ postprocessors=(_apply_deepseek_chat_extras,),
106
+ provider_id=provider_id,
107
+ )
108
+ if body.get("max_tokens") is None:
109
+ configured = configured_default_max_output_tokens()
110
+ if configured is None:
111
+ body.pop("max_tokens", None)
112
+ else:
113
+ body["max_tokens"] = configured
114
+
115
+ logger.debug(
116
+ "DEEPSEEK_REQUEST: build done model={} msgs={} tools={}",
117
+ body.get("model"),
118
+ len(body.get("messages", [])),
119
+ len(body.get("tools", [])),
120
+ )
121
+ return body
122
+
123
+
124
+ def sanitize_deepseek_messages_for_openai(messages: Any) -> Any:
125
+ """Keep only DeepSeek-required reasoning history on assistant tool calls."""
126
+ if not isinstance(messages, list):
127
+ return messages
128
+
129
+ sanitized: list[Any] = []
130
+ for message in messages:
131
+ if not isinstance(message, dict):
132
+ sanitized.append(message)
133
+ continue
134
+ if message.get("role") != "assistant":
135
+ sanitized.append(message)
136
+ continue
137
+ replay_tool_reasoning = _assistant_has_tool_use(message)
138
+ new_msg = dict(message)
139
+ if not replay_tool_reasoning:
140
+ new_msg.pop("reasoning_content", None)
141
+ content = message.get("content")
142
+ if not isinstance(content, list):
143
+ sanitized.append(new_msg)
144
+ continue
145
+
146
+ filtered = [
147
+ block
148
+ for block in content
149
+ if not (
150
+ isinstance(block, dict)
151
+ and (
152
+ block.get("type") == "redacted_thinking"
153
+ or (block.get("type") == "thinking" and not replay_tool_reasoning)
154
+ )
155
+ )
156
+ ]
157
+ new_msg["content"] = filtered or ""
158
+ sanitized.append(new_msg)
159
+ return sanitized
160
+
161
+
162
+ def _is_server_listed_tool(tool: Mapping[str, Any]) -> bool:
163
+ name = (tool.get("name") or "").strip()
164
+ if name in ("web_search", "web_fetch"):
165
+ return True
166
+ typ = tool.get("type")
167
+ if isinstance(typ, str):
168
+ return typ.startswith("web_search") or typ.startswith("web_fetch")
169
+ return False
170
+
171
+
172
+ def _walk_block_list_for_unsupported(blocks: Any, *, where: str) -> None:
173
+ if not isinstance(blocks, list):
174
+ return
175
+ for block in blocks:
176
+ if not isinstance(block, dict):
177
+ continue
178
+ btype = block.get("type")
179
+ if btype in _UNSUPPORTED_MESSAGE_BLOCK_TYPES:
180
+ raise InvalidRequestError(
181
+ f"DeepSeek native does not support {btype!r} blocks ({where})."
182
+ )
183
+ if btype == "tool_result" and "content" in block:
184
+ _walk_block_list_for_unsupported(
185
+ block["content"], where=f"{where} (tool_result content)"
186
+ )
187
+
188
+
189
+ def _validate_deepseek_request_dict(data: dict[str, Any]) -> None:
190
+ mcp = data.get("mcp_servers")
191
+ if mcp:
192
+ raise InvalidRequestError("DeepSeek does not support mcp_servers on requests.")
193
+
194
+ for tool in data.get("tools") or ():
195
+ if not isinstance(tool, dict):
196
+ continue
197
+ if _is_server_listed_tool(tool):
198
+ raise InvalidRequestError(
199
+ "DeepSeek does not support listed Anthropic server tools "
200
+ "(web_search / web_fetch). Remove them or use a different provider."
201
+ )
202
+
203
+ for i, message in enumerate(data.get("messages") or ()):
204
+ if not isinstance(message, dict):
205
+ continue
206
+ content = message.get("content")
207
+ if isinstance(content, list):
208
+ _walk_block_list_for_unsupported(content, where=f"messages[{i}].content")
209
+
210
+ system = data.get("system")
211
+ if isinstance(system, list):
212
+ _walk_block_list_for_unsupported(system, where="system")
213
+
214
+
215
+ def _has_tool_history_blocks(message: Mapping[str, Any]) -> bool:
216
+ role = message.get("role")
217
+ content = message.get("content")
218
+ if not isinstance(content, list):
219
+ return False
220
+
221
+ for block in content:
222
+ if not isinstance(block, dict):
223
+ continue
224
+ btype = block.get("type")
225
+ if role == "assistant" and btype == "tool_use":
226
+ return True
227
+ if role == "user" and btype == "tool_result":
228
+ return True
229
+ return False
230
+
231
+
232
+ def _has_replayable_thinking_before_tool_use(message: Mapping[str, Any]) -> bool:
233
+ if message.get("role") != "assistant":
234
+ return False
235
+ content = message.get("content")
236
+ if not isinstance(content, list):
237
+ return False
238
+
239
+ has_thinking = isinstance(message.get("reasoning_content"), str)
240
+ for block in content:
241
+ if not isinstance(block, dict):
242
+ continue
243
+ btype = block.get("type")
244
+ if btype == "thinking" and isinstance(block.get("thinking"), str):
245
+ has_thinking = True
246
+ continue
247
+ if btype == "tool_use":
248
+ return has_thinking
249
+ return False
250
+
251
+
252
+ def _assistant_has_tool_use(message: Mapping[str, Any]) -> bool:
253
+ if message.get("role") != "assistant":
254
+ return False
255
+ content = message.get("content")
256
+ return isinstance(content, list) and any(
257
+ isinstance(block, dict) and block.get("type") == "tool_use" for block in content
258
+ )
259
+
260
+
261
+ def _has_tool_history(data: dict[str, Any]) -> bool:
262
+ for message in data.get("messages") or ():
263
+ if isinstance(message, Mapping) and _has_tool_history_blocks(message):
264
+ return True
265
+ return False
266
+
267
+
268
+ def _all_tool_calls_have_replayable_thinking(data: dict[str, Any]) -> bool:
269
+ found_tool_call = False
270
+ for message in data.get("messages") or ():
271
+ if not isinstance(message, Mapping) or not _assistant_has_tool_use(message):
272
+ continue
273
+ found_tool_call = True
274
+ if not _has_replayable_thinking_before_tool_use(message):
275
+ return False
276
+ return found_tool_call
277
+
278
+
279
+ def _remove_deepseek_thinking_hints(data: dict[str, Any]) -> None:
280
+ output_config = data.get("output_config")
281
+ if isinstance(output_config, dict) and "effort" in output_config:
282
+ cleaned_output_config = dict(output_config)
283
+ cleaned_output_config.pop("effort", None)
284
+ if cleaned_output_config:
285
+ data["output_config"] = cleaned_output_config
286
+ else:
287
+ data.pop("output_config", None)
288
+
289
+ context_management = data.get("context_management")
290
+ if not isinstance(context_management, dict):
291
+ return
292
+ edits = context_management.get("edits")
293
+ if not isinstance(edits, list):
294
+ return
295
+ filtered_edits = [
296
+ edit
297
+ for edit in edits
298
+ if not (
299
+ isinstance(edit, dict)
300
+ and isinstance(edit.get("type"), str)
301
+ and edit["type"].startswith("clear_thinking_")
302
+ )
303
+ ]
304
+ if len(filtered_edits) == len(edits):
305
+ return
306
+ cleaned_context_management = dict(context_management)
307
+ if filtered_edits:
308
+ cleaned_context_management["edits"] = filtered_edits
309
+ data["context_management"] = cleaned_context_management
310
+ else:
311
+ cleaned_context_management.pop("edits", None)
312
+ if cleaned_context_management:
313
+ data["context_management"] = cleaned_context_management
314
+ else:
315
+ data.pop("context_management", None)
316
+
317
+
318
+ def _normalize_tool_result_content(messages: Any) -> Any:
319
+ if not isinstance(messages, list):
320
+ return messages
321
+
322
+ normalized: list[Any] = []
323
+ for message in messages:
324
+ if not isinstance(message, dict):
325
+ normalized.append(message)
326
+ continue
327
+
328
+ content = message.get("content")
329
+ if not isinstance(content, list):
330
+ normalized.append(message)
331
+ continue
332
+
333
+ new_content: list[Any] = []
334
+ for block in content:
335
+ if not isinstance(block, dict):
336
+ new_content.append(block)
337
+ continue
338
+
339
+ if block.get("type") == "tool_result":
340
+ normalized_block = dict(block)
341
+ normalized_block["content"] = serialize_tool_result_content(
342
+ block.get("content")
343
+ )
344
+ new_content.append(normalized_block)
345
+ else:
346
+ new_content.append(block)
347
+
348
+ new_msg = dict(message)
349
+ new_msg["content"] = new_content
350
+ normalized.append(new_msg)
351
+
352
+ return normalized
353
+
354
+
355
+ def _apply_deepseek_chat_extras(
356
+ body: dict[str, Any], _request_data: MessagesRequest, policy: ReasoningPolicy
357
+ ) -> None:
358
+ extra_body = body.setdefault("extra_body", {})
359
+ if not isinstance(extra_body, dict):
360
+ return
361
+ if policy.control is ReasoningControl.OFF:
362
+ extra_body["thinking"] = {"type": "disabled"}
363
+ return
364
+ # 2026-08-29: send the effort the caller actually asked for. This used to
365
+ # collapse the whole ladder onto two rungs -- "max" for XHIGH/MAX and "high"
366
+ # for everything else -- so a request for MINIMAL bought HIGH's latency and
367
+ # tokens, and MEDIUM and HIGH were indistinguishable. DeepSeek names its own
368
+ # enum when it rejects an invalid one: "reasoning_effort: unknown variant
369
+ # `bogus_value`, expected one of `none`, `minimal`, `low`, `medium`, `high`,
370
+ # `xhigh`, `max`". That is FCC's :class:`ReasoningEffort` ladder exactly,
371
+ # value for value, so the translation is the identity and no per-model
372
+ # branching is needed to pick it.
373
+ if policy.effort is not None:
374
+ body["reasoning_effort"] = policy.effort.value
375
+ elif policy.requests_reasoning:
376
+ extra_body["thinking"] = {"type": "enabled"}
@@ -0,0 +1,72 @@
1
+ """DeepSeek forced tool_choice rejection recovery.
2
+
3
+ DeepSeek's documented Chat Completions API accepts a forced named
4
+ ``tool_choice`` (``{"type": "function", "function": {"name": ...}}``), but
5
+ some DeepSeek reasoning models reject it at request time with an HTTP 400
6
+ even though the shape is spec-compliant. This module detects that specific
7
+ rejection reactively (from the upstream error, never from the model name --
8
+ DeepSeek's model ids have already shifted once and are not a stable signal)
9
+ and produces a retry body that keeps the "must call some tool" half of the
10
+ caller's intent by downgrading to ``"required"``.
11
+ """
12
+
13
+ from copy import deepcopy
14
+ from typing import Any
15
+
16
+ from my_claude_code.providers.recovery import is_bad_request, upstream_complaint
17
+
18
+ _REJECTION_HINTS = (
19
+ "tool_choice",
20
+ "tool choice",
21
+ )
22
+ _UNSUPPORTED_WORDS = (
23
+ "not support",
24
+ "unsupported",
25
+ "does not support",
26
+ "invalid",
27
+ "not allowed",
28
+ "not permitted",
29
+ )
30
+
31
+
32
+ def is_deepseek_tool_choice_rejection(error: Exception) -> bool:
33
+ """Return whether an upstream error rejects a forced ``tool_choice``.
34
+
35
+ Matches narrowly: the error must be a request-validation rejection AND the
36
+ provider's own words must mention ``tool_choice`` (or "tool choice")
37
+ together with an unsupported/rejection word (e.g. "does not support",
38
+ "invalid", "not allowed"). Requiring both keeps this from firing on
39
+ unrelated 400s such as context-length-exceeded or bad-schema errors, which
40
+ never mention tool_choice at all.
41
+
42
+ Echo-safe since 6.33.0: the words come from the fleet-wide matcher, which
43
+ prunes the keys under which a validation error echoes the submitted request
44
+ back. Reading that echo made every forced-tool request its own evidence --
45
+ the body it sent contains ``tool_choice`` by definition, and one unrelated
46
+ "invalid" anywhere in the same 400 was enough to complete the pair.
47
+ """
48
+ if not is_bad_request(error):
49
+ return False
50
+ complaint = upstream_complaint(error)
51
+ mentions_tool_choice = any(hint in complaint for hint in _REJECTION_HINTS)
52
+ mentions_rejection = any(word in complaint for word in _UNSUPPORTED_WORDS)
53
+ return mentions_tool_choice and mentions_rejection
54
+
55
+
56
+ def clone_body_with_required_tool_choice(body: dict[str, Any]) -> dict[str, Any] | None:
57
+ """Return a body clone with a forced named ``tool_choice`` downgraded.
58
+
59
+ Downgrades OpenAI-shaped ``{"type": "function", "function": {"name": ...}}``
60
+ to the string ``"required"`` -- the caller demanded a tool call, and
61
+ ``"required"`` preserves that half even when the *which tool* half
62
+ cannot be honoured. Returns ``None`` if there is nothing to downgrade
63
+ (i.e. ``tool_choice`` was not a forced named choice), so the caller can
64
+ tell "no retry available" apart from "retry with an unchanged body".
65
+ """
66
+ tool_choice = body.get("tool_choice")
67
+ if not isinstance(tool_choice, dict) or tool_choice.get("type") != "function":
68
+ return None
69
+
70
+ cloned = deepcopy(body)
71
+ cloned["tool_choice"] = "required"
72
+ return cloned