free-claude-code 6.2.64__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 (429) hide show
  1. free_claude_code/__init__.py +1 -0
  2. free_claude_code/api/__init__.py +1 -0
  3. free_claude_code/api/admin_cache.py +49 -0
  4. free_claude_code/api/admin_routes.py +501 -0
  5. free_claude_code/api/admin_security.py +67 -0
  6. free_claude_code/api/admin_static/admin.css +980 -0
  7. free_claude_code/api/admin_static/admin.js +1934 -0
  8. free_claude_code/api/admin_static/code_sessions.css +109 -0
  9. free_claude_code/api/admin_static/code_sessions.js +1693 -0
  10. free_claude_code/api/admin_static/form_controls.js +15 -0
  11. free_claude_code/api/admin_static/index.html +201 -0
  12. free_claude_code/api/admin_static/model_combobox.js +194 -0
  13. free_claude_code/api/admin_static/providers/LICENSE-llamacpp.txt +21 -0
  14. free_claude_code/api/admin_static/providers/LICENSE-lobe-icons.txt +21 -0
  15. free_claude_code/api/admin_static/providers/SOURCES.md +56 -0
  16. free_claude_code/api/admin_static/providers/agnesai.svg +1 -0
  17. free_claude_code/api/admin_static/providers/azure-color.svg +1 -0
  18. free_claude_code/api/admin_static/providers/bedrock-color.svg +1 -0
  19. free_claude_code/api/admin_static/providers/cerebras-color.svg +1 -0
  20. free_claude_code/api/admin_static/providers/chutes.png +0 -0
  21. free_claude_code/api/admin_static/providers/cline.svg +1 -0
  22. free_claude_code/api/admin_static/providers/cloudflare-color.svg +1 -0
  23. free_claude_code/api/admin_static/providers/cohere-color.svg +1 -0
  24. free_claude_code/api/admin_static/providers/deepinfra-color.svg +1 -0
  25. free_claude_code/api/admin_static/providers/deepseek-color.svg +1 -0
  26. free_claude_code/api/admin_static/providers/experiential.svg +9 -0
  27. free_claude_code/api/admin_static/providers/featherless-color.svg +1 -0
  28. free_claude_code/api/admin_static/providers/fireworks-color.svg +1 -0
  29. free_claude_code/api/admin_static/providers/gemini-color.svg +1 -0
  30. free_claude_code/api/admin_static/providers/githubcopilot.svg +1 -0
  31. free_claude_code/api/admin_static/providers/groq.svg +1 -0
  32. free_claude_code/api/admin_static/providers/huggingface-color.svg +1 -0
  33. free_claude_code/api/admin_static/providers/kilocode.svg +1 -0
  34. free_claude_code/api/admin_static/providers/kimi-color.svg +1 -0
  35. free_claude_code/api/admin_static/providers/lightning.png +0 -0
  36. free_claude_code/api/admin_static/providers/llamacpp.svg +77 -0
  37. free_claude_code/api/admin_static/providers/llm7.png +0 -0
  38. free_claude_code/api/admin_static/providers/lmstudio.svg +1 -0
  39. free_claude_code/api/admin_static/providers/minimax-color.svg +1 -0
  40. free_claude_code/api/admin_static/providers/mistral-color.svg +1 -0
  41. free_claude_code/api/admin_static/providers/nararoute.png +0 -0
  42. free_claude_code/api/admin_static/providers/nebius.svg +1 -0
  43. free_claude_code/api/admin_static/providers/novita-color.svg +1 -0
  44. free_claude_code/api/admin_static/providers/nvidia-color.svg +1 -0
  45. free_claude_code/api/admin_static/providers/ollama.svg +1 -0
  46. free_claude_code/api/admin_static/providers/openai.svg +1 -0
  47. free_claude_code/api/admin_static/providers/opencode.svg +1 -0
  48. free_claude_code/api/admin_static/providers/openrouter.svg +1 -0
  49. free_claude_code/api/admin_static/providers/poolside-color.svg +1 -0
  50. free_claude_code/api/admin_static/providers/qwen-color.svg +1 -0
  51. free_claude_code/api/admin_static/providers/sambanova-color.svg +1 -0
  52. free_claude_code/api/admin_static/providers/scaleway.svg +11 -0
  53. free_claude_code/api/admin_static/providers/siliconcloud-color.svg +1 -0
  54. free_claude_code/api/admin_static/providers/together-color.svg +1 -0
  55. free_claude_code/api/admin_static/providers/tokenrouter.png +0 -0
  56. free_claude_code/api/admin_static/providers/vercel.svg +1 -0
  57. free_claude_code/api/admin_static/providers/vertexai-color.svg +1 -0
  58. free_claude_code/api/admin_static/providers/wafer.png +0 -0
  59. free_claude_code/api/admin_static/providers/wandb.png +0 -0
  60. free_claude_code/api/admin_static/providers/xai.svg +1 -0
  61. free_claude_code/api/admin_static/providers/zai.svg +1 -0
  62. free_claude_code/api/admin_static/providers/zenmux.svg +1 -0
  63. free_claude_code/api/admin_static/session_layout.css +552 -0
  64. free_claude_code/api/admin_static/session_ui.js +202 -0
  65. free_claude_code/api/app.py +149 -0
  66. free_claude_code/api/code_sessions_routes.py +386 -0
  67. free_claude_code/api/command_utils.py +164 -0
  68. free_claude_code/api/dependencies.py +128 -0
  69. free_claude_code/api/detection.py +185 -0
  70. free_claude_code/api/handlers/__init__.py +7 -0
  71. free_claude_code/api/handlers/classifier_response.py +66 -0
  72. free_claude_code/api/handlers/messages.py +359 -0
  73. free_claude_code/api/handlers/responses.py +163 -0
  74. free_claude_code/api/handlers/token_count.py +87 -0
  75. free_claude_code/api/markdown.py +71 -0
  76. free_claude_code/api/model_catalog.py +282 -0
  77. free_claude_code/api/optimization_handlers.py +162 -0
  78. free_claude_code/api/ports.py +97 -0
  79. free_claude_code/api/request_errors.py +100 -0
  80. free_claude_code/api/request_ids.py +100 -0
  81. free_claude_code/api/request_lifetime.py +130 -0
  82. free_claude_code/api/response_streams.py +422 -0
  83. free_claude_code/api/routes.py +269 -0
  84. free_claude_code/api/validation_log.py +46 -0
  85. free_claude_code/application/__init__.py +1 -0
  86. free_claude_code/application/code_sessions/__init__.py +33 -0
  87. free_claude_code/application/code_sessions/models.py +227 -0
  88. free_claude_code/application/code_sessions/ports.py +197 -0
  89. free_claude_code/application/code_sessions/service.py +1123 -0
  90. free_claude_code/application/code_sessions/state.py +659 -0
  91. free_claude_code/application/connected_accounts.py +96 -0
  92. free_claude_code/application/errors.py +41 -0
  93. free_claude_code/application/execution.py +467 -0
  94. free_claude_code/application/model_catalog.py +95 -0
  95. free_claude_code/application/model_metadata.py +26 -0
  96. free_claude_code/application/ports.py +127 -0
  97. free_claude_code/application/readiness.py +43 -0
  98. free_claude_code/application/reasoning.py +134 -0
  99. free_claude_code/application/routing.py +291 -0
  100. free_claude_code/application/session_events.py +140 -0
  101. free_claude_code/application/web_tools/__init__.py +1 -0
  102. free_claude_code/application/web_tools/ports.py +35 -0
  103. free_claude_code/application/web_tools/request.py +348 -0
  104. free_claude_code/application/web_tools/service.py +481 -0
  105. free_claude_code/assets/app-icon.icns +0 -0
  106. free_claude_code/assets/app-icon.ico +0 -0
  107. free_claude_code/assets/app-icon.png +0 -0
  108. free_claude_code/assets/app-icon.svg +14 -0
  109. free_claude_code/cli/__init__.py +5 -0
  110. free_claude_code/cli/commands.py +379 -0
  111. free_claude_code/cli/desktop.py +156 -0
  112. free_claude_code/cli/desktop_assets.py +31 -0
  113. free_claude_code/cli/desktop_entrypoint.py +29 -0
  114. free_claude_code/cli/desktop_tray.py +67 -0
  115. free_claude_code/cli/entrypoints.py +25 -0
  116. free_claude_code/cli/launchers/__init__.py +1 -0
  117. free_claude_code/cli/launchers/aider.py +53 -0
  118. free_claude_code/cli/launchers/aider_config.py +80 -0
  119. free_claude_code/cli/launchers/catalog_http.py +134 -0
  120. free_claude_code/cli/launchers/claude.py +34 -0
  121. free_claude_code/cli/launchers/cline.py +60 -0
  122. free_claude_code/cli/launchers/cline_config.py +121 -0
  123. free_claude_code/cli/launchers/codex.py +47 -0
  124. free_claude_code/cli/launchers/common.py +103 -0
  125. free_claude_code/cli/launchers/dsh.py +69 -0
  126. free_claude_code/cli/launchers/dsh_config.py +124 -0
  127. free_claude_code/cli/launchers/grok.py +116 -0
  128. free_claude_code/cli/launchers/hermes.py +85 -0
  129. free_claude_code/cli/launchers/hermes_config.py +134 -0
  130. free_claude_code/cli/launchers/muse.py +68 -0
  131. free_claude_code/cli/launchers/opencode.py +83 -0
  132. free_claude_code/cli/launchers/opencode_config.py +107 -0
  133. free_claude_code/cli/launchers/pi.py +70 -0
  134. free_claude_code/cli/launchers/pi_extension.ts +190 -0
  135. free_claude_code/cli/launchers/runner.py +155 -0
  136. free_claude_code/cli/local_http.py +12 -0
  137. free_claude_code/cli/managed/__init__.py +6 -0
  138. free_claude_code/cli/managed/claude.py +225 -0
  139. free_claude_code/cli/managed/diagnostics.py +47 -0
  140. free_claude_code/cli/managed/manager.py +207 -0
  141. free_claude_code/cli/managed/session.py +293 -0
  142. free_claude_code/cli/process_registry.py +76 -0
  143. free_claude_code/cli/server_socket.py +71 -0
  144. free_claude_code/cli/uvicorn_server.py +41 -0
  145. free_claude_code/config/__init__.py +10 -0
  146. free_claude_code/config/admin/__init__.py +1 -0
  147. free_claude_code/config/admin/manifest.py +610 -0
  148. free_claude_code/config/admin/persistence.py +202 -0
  149. free_claude_code/config/admin/provider_manifest.py +443 -0
  150. free_claude_code/config/admin/specs.py +77 -0
  151. free_claude_code/config/admin/state.py +18 -0
  152. free_claude_code/config/admin/status.py +116 -0
  153. free_claude_code/config/admin/validation.py +40 -0
  154. free_claude_code/config/admin/values.py +115 -0
  155. free_claude_code/config/constants.py +9 -0
  156. free_claude_code/config/env_files.py +74 -0
  157. free_claude_code/config/env_migrations.py +457 -0
  158. free_claude_code/config/loader.py +196 -0
  159. free_claude_code/config/logging_config.py +197 -0
  160. free_claude_code/config/model_refs.py +118 -0
  161. free_claude_code/config/nim.py +121 -0
  162. free_claude_code/config/paths.py +104 -0
  163. free_claude_code/config/provider_catalog.py +746 -0
  164. free_claude_code/config/provider_proxies.py +44 -0
  165. free_claude_code/config/reasoning.py +24 -0
  166. free_claude_code/config/server_urls.py +55 -0
  167. free_claude_code/config/settings.py +812 -0
  168. free_claude_code/core/__init__.py +15 -0
  169. free_claude_code/core/anthropic/__init__.py +99 -0
  170. free_claude_code/core/anthropic/content.py +31 -0
  171. free_claude_code/core/anthropic/conversion.py +876 -0
  172. free_claude_code/core/anthropic/errors.py +97 -0
  173. free_claude_code/core/anthropic/image_sources.py +77 -0
  174. free_claude_code/core/anthropic/models.py +193 -0
  175. free_claude_code/core/anthropic/native.py +184 -0
  176. free_claude_code/core/anthropic/native_stream.py +306 -0
  177. free_claude_code/core/anthropic/request_serialization.py +57 -0
  178. free_claude_code/core/anthropic/request_snapshot.py +35 -0
  179. free_claude_code/core/anthropic/server_tool_sse.py +234 -0
  180. free_claude_code/core/anthropic/server_tool_types.py +9 -0
  181. free_claude_code/core/anthropic/sse_aggregation.py +144 -0
  182. free_claude_code/core/anthropic/stream_contracts.py +208 -0
  183. free_claude_code/core/anthropic/streaming/__init__.py +43 -0
  184. free_claude_code/core/anthropic/streaming/decoder.py +49 -0
  185. free_claude_code/core/anthropic/streaming/emitter.py +62 -0
  186. free_claude_code/core/anthropic/streaming/ledger.py +538 -0
  187. free_claude_code/core/anthropic/streaming/recovery.py +211 -0
  188. free_claude_code/core/anthropic/thinking.py +140 -0
  189. free_claude_code/core/anthropic/tokens.py +111 -0
  190. free_claude_code/core/anthropic/tool_results.py +63 -0
  191. free_claude_code/core/anthropic/tool_schema.py +94 -0
  192. free_claude_code/core/anthropic/tools.py +493 -0
  193. free_claude_code/core/anthropic/usage.py +38 -0
  194. free_claude_code/core/anthropic/utils.py +9 -0
  195. free_claude_code/core/async_iterators.py +26 -0
  196. free_claude_code/core/async_tasks.py +26 -0
  197. free_claude_code/core/data/README.md +12 -0
  198. free_claude_code/core/data/TIKTOKEN-LICENSE +21 -0
  199. free_claude_code/core/data/cl100k_base.tiktoken +100256 -0
  200. free_claude_code/core/diagnostics.py +319 -0
  201. free_claude_code/core/failures.py +50 -0
  202. free_claude_code/core/gateway_model_ids.py +73 -0
  203. free_claude_code/core/history_replay.py +561 -0
  204. free_claude_code/core/interprocess_lock.py +90 -0
  205. free_claude_code/core/json_types.py +7 -0
  206. free_claude_code/core/model_capabilities.py +10 -0
  207. free_claude_code/core/openai_chat.py +54 -0
  208. free_claude_code/core/openai_responses/__init__.py +90 -0
  209. free_claude_code/core/openai_responses/chat_request.py +747 -0
  210. free_claude_code/core/openai_responses/errors.py +78 -0
  211. free_claude_code/core/openai_responses/events.py +82 -0
  212. free_claude_code/core/openai_responses/ids.py +32 -0
  213. free_claude_code/core/openai_responses/items.py +35 -0
  214. free_claude_code/core/openai_responses/messages_request.py +624 -0
  215. free_claude_code/core/openai_responses/messages_stream.py +404 -0
  216. free_claude_code/core/openai_responses/models.py +33 -0
  217. free_claude_code/core/openai_responses/native.py +147 -0
  218. free_claude_code/core/openai_responses/provider_input.py +409 -0
  219. free_claude_code/core/openai_responses/provider_stream.py +282 -0
  220. free_claude_code/core/openai_responses/reasoning.py +73 -0
  221. free_claude_code/core/openai_responses/streaming/__init__.py +1 -0
  222. free_claude_code/core/openai_responses/streaming/blocks.py +36 -0
  223. free_claude_code/core/openai_responses/streaming/completion.py +169 -0
  224. free_claude_code/core/openai_responses/streaming/error_mapping.py +28 -0
  225. free_claude_code/core/openai_responses/streaming/event_builders.py +198 -0
  226. free_claude_code/core/openai_responses/streaming/ledger.py +96 -0
  227. free_claude_code/core/openai_responses/tokens.py +133 -0
  228. free_claude_code/core/openai_responses/tool_adaptation.py +828 -0
  229. free_claude_code/core/openai_responses/tool_search.py +204 -0
  230. free_claude_code/core/openai_responses/tools.py +169 -0
  231. free_claude_code/core/openai_tool_names.py +178 -0
  232. free_claude_code/core/rate_limit.py +72 -0
  233. free_claude_code/core/reasoning.py +136 -0
  234. free_claude_code/core/token_estimation.py +76 -0
  235. free_claude_code/core/tool_schema_patterns.py +168 -0
  236. free_claude_code/core/trace.py +197 -0
  237. free_claude_code/core/version.py +15 -0
  238. free_claude_code/core/web_tools.py +17 -0
  239. free_claude_code/core/windows_dpi.py +27 -0
  240. free_claude_code/harnesses/__init__.py +1 -0
  241. free_claude_code/harnesses/claude.py +42 -0
  242. free_claude_code/harnesses/claude_desktop_integration.py +418 -0
  243. free_claude_code/harnesses/claude_integration.py +167 -0
  244. free_claude_code/harnesses/codex.py +94 -0
  245. free_claude_code/harnesses/codex_integration.py +147 -0
  246. free_claude_code/harnesses/codex_model_catalog.py +109 -0
  247. free_claude_code/harnesses/config_file.py +33 -0
  248. free_claude_code/harnesses/environment.py +78 -0
  249. free_claude_code/harnesses/jetbrains_acp_integration.py +242 -0
  250. free_claude_code/harnesses/launch.py +21 -0
  251. free_claude_code/harnesses/resources.py +42 -0
  252. free_claude_code/messaging/__init__.py +16 -0
  253. free_claude_code/messaging/cli_event_constants.py +67 -0
  254. free_claude_code/messaging/command_context.py +99 -0
  255. free_claude_code/messaging/command_dispatcher.py +31 -0
  256. free_claude_code/messaging/commands.py +182 -0
  257. free_claude_code/messaging/event_parser.py +184 -0
  258. free_claude_code/messaging/limiter.py +342 -0
  259. free_claude_code/messaging/managed_protocols.py +35 -0
  260. free_claude_code/messaging/models.py +57 -0
  261. free_claude_code/messaging/node_event_pipeline.py +120 -0
  262. free_claude_code/messaging/node_runner.py +408 -0
  263. free_claude_code/messaging/platforms/__init__.py +20 -0
  264. free_claude_code/messaging/platforms/discord.py +319 -0
  265. free_claude_code/messaging/platforms/discord_inbound.py +112 -0
  266. free_claude_code/messaging/platforms/discord_io.py +167 -0
  267. free_claude_code/messaging/platforms/factory.py +109 -0
  268. free_claude_code/messaging/platforms/outbox.py +143 -0
  269. free_claude_code/messaging/platforms/ports.py +99 -0
  270. free_claude_code/messaging/platforms/telegram.py +300 -0
  271. free_claude_code/messaging/platforms/telegram_inbound.py +133 -0
  272. free_claude_code/messaging/platforms/telegram_io.py +287 -0
  273. free_claude_code/messaging/platforms/voice_flow.py +388 -0
  274. free_claude_code/messaging/rendering/__init__.py +3 -0
  275. free_claude_code/messaging/rendering/discord_markdown.py +318 -0
  276. free_claude_code/messaging/rendering/markdown_tables.py +47 -0
  277. free_claude_code/messaging/rendering/profiles.py +53 -0
  278. free_claude_code/messaging/rendering/telegram_markdown.py +331 -0
  279. free_claude_code/messaging/safe_diagnostics.py +15 -0
  280. free_claude_code/messaging/session/__init__.py +5 -0
  281. free_claude_code/messaging/session/managed_message_log.py +135 -0
  282. free_claude_code/messaging/session/persistence.py +161 -0
  283. free_claude_code/messaging/session/store.py +162 -0
  284. free_claude_code/messaging/transcript/__init__.py +6 -0
  285. free_claude_code/messaging/transcript/buffer.py +220 -0
  286. free_claude_code/messaging/transcript/context.py +18 -0
  287. free_claude_code/messaging/transcript/renderer.py +65 -0
  288. free_claude_code/messaging/transcript/segments.py +176 -0
  289. free_claude_code/messaging/transcript/subagents.py +108 -0
  290. free_claude_code/messaging/transcription.py +132 -0
  291. free_claude_code/messaging/trees/__init__.py +41 -0
  292. free_claude_code/messaging/trees/graph.py +250 -0
  293. free_claude_code/messaging/trees/identity.py +16 -0
  294. free_claude_code/messaging/trees/manager.py +587 -0
  295. free_claude_code/messaging/trees/node.py +56 -0
  296. free_claude_code/messaging/trees/processor.py +272 -0
  297. free_claude_code/messaging/trees/queue.py +47 -0
  298. free_claude_code/messaging/trees/repository.py +135 -0
  299. free_claude_code/messaging/trees/runtime.py +487 -0
  300. free_claude_code/messaging/trees/snapshot.py +221 -0
  301. free_claude_code/messaging/trees/transitions.py +174 -0
  302. free_claude_code/messaging/turn_intake.py +241 -0
  303. free_claude_code/messaging/ui_updates.py +99 -0
  304. free_claude_code/messaging/voice.py +376 -0
  305. free_claude_code/messaging/workflow.py +714 -0
  306. free_claude_code/providers/__init__.py +12 -0
  307. free_claude_code/providers/admission.py +942 -0
  308. free_claude_code/providers/anthropic_messages/__init__.py +1 -0
  309. free_claude_code/providers/anthropic_messages/request_policy.py +162 -0
  310. free_claude_code/providers/anthropic_messages/transport.py +499 -0
  311. free_claude_code/providers/base.py +73 -0
  312. free_claude_code/providers/cloudflare/__init__.py +8 -0
  313. free_claude_code/providers/cloudflare/client.py +166 -0
  314. free_claude_code/providers/credential_validation.py +314 -0
  315. free_claude_code/providers/deepseek/__init__.py +5 -0
  316. free_claude_code/providers/deepseek/client.py +117 -0
  317. free_claude_code/providers/deepseek/compat.py +241 -0
  318. free_claude_code/providers/endpoint.py +21 -0
  319. free_claude_code/providers/endpoint_types.py +21 -0
  320. free_claude_code/providers/failure_policy.py +574 -0
  321. free_claude_code/providers/gemini/__init__.py +5 -0
  322. free_claude_code/providers/gemini/client.py +42 -0
  323. free_claude_code/providers/github_copilot/__init__.py +1 -0
  324. free_claude_code/providers/github_copilot/auth.py +502 -0
  325. free_claude_code/providers/github_copilot/broker.py +248 -0
  326. free_claude_code/providers/github_copilot/lifecycle.py +18 -0
  327. free_claude_code/providers/github_copilot/login.py +112 -0
  328. free_claude_code/providers/github_copilot/native_cli.py +82 -0
  329. free_claude_code/providers/github_copilot/provider.py +367 -0
  330. free_claude_code/providers/github_copilot/request_policy.py +119 -0
  331. free_claude_code/providers/github_copilot/responses_events.py +90 -0
  332. free_claude_code/providers/github_copilot/sdk.py +496 -0
  333. free_claude_code/providers/github_copilot/types.py +67 -0
  334. free_claude_code/providers/google_openai/__init__.py +17 -0
  335. free_claude_code/providers/google_openai/provider.py +73 -0
  336. free_claude_code/providers/google_openai/reasoning.py +150 -0
  337. free_claude_code/providers/google_openai/thought_signatures.py +117 -0
  338. free_claude_code/providers/groq/__init__.py +5 -0
  339. free_claude_code/providers/groq/client.py +342 -0
  340. free_claude_code/providers/groq/tpm.py +114 -0
  341. free_claude_code/providers/history_replay.py +260 -0
  342. free_claude_code/providers/http.py +105 -0
  343. free_claude_code/providers/kilo/__init__.py +5 -0
  344. free_claude_code/providers/kilo/client.py +45 -0
  345. free_claude_code/providers/kilo/models.py +60 -0
  346. free_claude_code/providers/lmstudio/__init__.py +5 -0
  347. free_claude_code/providers/lmstudio/client.py +220 -0
  348. free_claude_code/providers/mistral/__init__.py +5 -0
  349. free_claude_code/providers/mistral/client.py +91 -0
  350. free_claude_code/providers/mistral/reasoning.py +410 -0
  351. free_claude_code/providers/model_listing.py +479 -0
  352. free_claude_code/providers/nvidia_nim/__init__.py +5 -0
  353. free_claude_code/providers/nvidia_nim/client.py +224 -0
  354. free_claude_code/providers/nvidia_nim/native_tool_stream.py +693 -0
  355. free_claude_code/providers/nvidia_nim/request_options.py +141 -0
  356. free_claude_code/providers/nvidia_nim/retry.py +85 -0
  357. free_claude_code/providers/nvidia_nim/tool_schema.py +255 -0
  358. free_claude_code/providers/nvidia_nim/voice.py +107 -0
  359. free_claude_code/providers/open_router/__init__.py +5 -0
  360. free_claude_code/providers/open_router/client.py +113 -0
  361. free_claude_code/providers/openai_api/__init__.py +5 -0
  362. free_claude_code/providers/openai_api/provider.py +150 -0
  363. free_claude_code/providers/openai_chat/__init__.py +72 -0
  364. free_claude_code/providers/openai_chat/base_url.py +7 -0
  365. free_claude_code/providers/openai_chat/behavior.py +133 -0
  366. free_claude_code/providers/openai_chat/client.py +40 -0
  367. free_claude_code/providers/openai_chat/extra_body.py +63 -0
  368. free_claude_code/providers/openai_chat/output_cap.py +161 -0
  369. free_claude_code/providers/openai_chat/profiles.py +778 -0
  370. free_claude_code/providers/openai_chat/provider.py +244 -0
  371. free_claude_code/providers/openai_chat/reasoning.py +169 -0
  372. free_claude_code/providers/openai_chat/reasoning_details.py +128 -0
  373. free_claude_code/providers/openai_chat/request_policy.py +140 -0
  374. free_claude_code/providers/openai_chat/stream_output.py +752 -0
  375. free_claude_code/providers/openai_chat/tool_calls.py +462 -0
  376. free_claude_code/providers/openai_chat/transport.py +1643 -0
  377. free_claude_code/providers/openai_chat/usage.py +117 -0
  378. free_claude_code/providers/openai_client.py +74 -0
  379. free_claude_code/providers/openai_codex/__init__.py +1 -0
  380. free_claude_code/providers/openai_codex/auth.py +626 -0
  381. free_claude_code/providers/openai_codex/endpoint.py +53 -0
  382. free_claude_code/providers/openai_codex/login.py +265 -0
  383. free_claude_code/providers/openai_codex/provider.py +259 -0
  384. free_claude_code/providers/openai_responses/__init__.py +5 -0
  385. free_claude_code/providers/openai_responses/presentation.py +115 -0
  386. free_claude_code/providers/openai_responses/transport.py +621 -0
  387. free_claude_code/providers/openai_stream.py +21 -0
  388. free_claude_code/providers/opencode/__init__.py +5 -0
  389. free_claude_code/providers/opencode/catalog.py +340 -0
  390. free_claude_code/providers/opencode/provider.py +359 -0
  391. free_claude_code/providers/reasoning_compatibility.py +199 -0
  392. free_claude_code/providers/request_recovery.py +122 -0
  393. free_claude_code/providers/runtime/__init__.py +10 -0
  394. free_claude_code/providers/runtime/config.py +91 -0
  395. free_claude_code/providers/runtime/discovery.py +63 -0
  396. free_claude_code/providers/runtime/factory.py +282 -0
  397. free_claude_code/providers/runtime/model_cache.py +93 -0
  398. free_claude_code/providers/runtime/runtime.py +108 -0
  399. free_claude_code/providers/stream_recovery.py +164 -0
  400. free_claude_code/providers/vertex/__init__.py +5 -0
  401. free_claude_code/providers/vertex/auth.py +105 -0
  402. free_claude_code/providers/vertex/client.py +140 -0
  403. free_claude_code/providers/vertex/endpoint.py +50 -0
  404. free_claude_code/providers/vertex/models.py +52 -0
  405. free_claude_code/runtime/__init__.py +1 -0
  406. free_claude_code/runtime/application.py +1121 -0
  407. free_claude_code/runtime/asgi.py +63 -0
  408. free_claude_code/runtime/bootstrap.py +153 -0
  409. free_claude_code/runtime/code_sessions_sqlite.py +785 -0
  410. free_claude_code/runtime/codex_app_server.py +775 -0
  411. free_claude_code/runtime/codex_catalog.py +62 -0
  412. free_claude_code/runtime/codex_protocol.py +662 -0
  413. free_claude_code/runtime/configuration.py +53 -0
  414. free_claude_code/runtime/folder_picker.py +134 -0
  415. free_claude_code/runtime/native_folder_dialog.py +123 -0
  416. free_claude_code/runtime/provider_manager.py +716 -0
  417. free_claude_code/runtime/retired_chat.py +47 -0
  418. free_claude_code/runtime/web_tools/__init__.py +1 -0
  419. free_claude_code/runtime/web_tools/client.py +238 -0
  420. free_claude_code/runtime/web_tools/constants.py +17 -0
  421. free_claude_code/runtime/web_tools/egress.py +89 -0
  422. free_claude_code/runtime/web_tools/parsers.py +78 -0
  423. free_claude_code-6.2.64.data/scripts/fcc-update +4 -0
  424. free_claude_code-6.2.64.data/scripts/fcc-update.cmd +5 -0
  425. free_claude_code-6.2.64.dist-info/METADATA +555 -0
  426. free_claude_code-6.2.64.dist-info/RECORD +429 -0
  427. free_claude_code-6.2.64.dist-info/WHEEL +4 -0
  428. free_claude_code-6.2.64.dist-info/entry_points.txt +15 -0
  429. free_claude_code-6.2.64.dist-info/licenses/LICENSE +239 -0
@@ -0,0 +1,752 @@
1
+ """Chat-source output writers for Anthropic Messages and OpenAI Responses."""
2
+
3
+ import hashlib
4
+ import json
5
+ import time
6
+ import uuid
7
+ from abc import ABC, abstractmethod
8
+ from collections.abc import Callable, Iterator, Mapping
9
+ from dataclasses import dataclass, field
10
+
11
+ from loguru import logger
12
+
13
+ from free_claude_code.core.anthropic.streaming import (
14
+ AnthropicStreamLedger,
15
+ ToolSchema,
16
+ parse_complete_tool_input,
17
+ )
18
+ from free_claude_code.core.failures import ExecutionFailure
19
+ from free_claude_code.core.history_replay import (
20
+ ReplayOrigin,
21
+ ReplayRecord,
22
+ encode_replay,
23
+ )
24
+ from free_claude_code.core.json_types import JsonObject
25
+ from free_claude_code.core.openai_responses import (
26
+ ReasoningBlockState,
27
+ ResponseBlockCompleter,
28
+ ResponseEventBuilder,
29
+ ResponsesConversionError,
30
+ ResponsesOutputLedger,
31
+ ResponsesToolAdapter,
32
+ TextBlockState,
33
+ ToolBlockState,
34
+ new_call_id,
35
+ new_message_item_id,
36
+ new_reasoning_item_id,
37
+ new_response_id,
38
+ openai_error_from_failure,
39
+ reasoning_output_item,
40
+ replay_unsafe_function_call_error,
41
+ tool_item,
42
+ )
43
+ from free_claude_code.core.token_estimation import estimate_text_tokens
44
+ from free_claude_code.core.trace import trace_event
45
+
46
+
47
+ @dataclass(frozen=True, slots=True)
48
+ class ChatStreamUsage:
49
+ """Final Chat usage normalized for either client wire protocol."""
50
+
51
+ input_tokens: int
52
+ output_tokens: int
53
+ cached_tokens: int = 0
54
+ cache_write_tokens: int | None = None
55
+ reasoning_tokens: int = 0
56
+ anthropic_fields: Mapping[str, int] = field(default_factory=dict)
57
+
58
+
59
+ @dataclass(slots=True)
60
+ class ChatToolState:
61
+ """Chat parser state for one streamed tool call."""
62
+
63
+ tool_id: str = ""
64
+ name: str = ""
65
+ extra_content: JsonObject | None = None
66
+ started: bool = False
67
+ open: bool = False
68
+ task_arg_buffer: str = ""
69
+ task_args_emitted: bool = False
70
+ pre_start_args: str = ""
71
+ argument_parts: list[str] = field(default_factory=list)
72
+
73
+ @property
74
+ def content(self) -> str:
75
+ return "".join(self.argument_parts)
76
+
77
+
78
+ class ChatStreamOutput(ABC):
79
+ """Source-specific semantic output boundary for one Chat stream epoch."""
80
+
81
+ consumes_terminal_failure = False
82
+
83
+ def __init__(self, *, input_tokens: int) -> None:
84
+ self.input_tokens = input_tokens
85
+ self.replay_origin: ReplayOrigin | None = None
86
+ self.reasoning_replay_events: (
87
+ Callable[[ChatStreamOutput], Iterator[str]] | None
88
+ ) = None
89
+ self.tool_states: dict[int, ChatToolState] = {}
90
+ self._text_parts: list[str] = []
91
+ self._reasoning_parts: list[str] = []
92
+ self._text_started = False
93
+ self._reasoning_started = False
94
+ self._content_started = False
95
+ self._terminal = False
96
+
97
+ @property
98
+ def committed_output(self) -> bool:
99
+ return self._content_started
100
+
101
+ @property
102
+ def accumulated_text(self) -> str:
103
+ return "".join(self._text_parts)
104
+
105
+ @property
106
+ def accumulated_reasoning(self) -> str:
107
+ return "".join(self._reasoning_parts)
108
+
109
+ def start_events(self) -> list[str]:
110
+ return self._start_events()
111
+
112
+ def ensure_reasoning_block(self) -> list[str]:
113
+ events: list[str] = []
114
+ if self._text_started:
115
+ events.extend(self._stop_text_block())
116
+ self._text_started = False
117
+ if not self._reasoning_started:
118
+ events.extend(self._start_reasoning_block())
119
+ self._reasoning_started = True
120
+ self._content_started = True
121
+ return events
122
+
123
+ def emit_reasoning_delta(self, content: str) -> str:
124
+ self._reasoning_parts.append(content)
125
+ return self._emit_reasoning_delta(content)
126
+
127
+ def emit_reasoning_replay(self, native: JsonObject) -> list[str]:
128
+ data = json.dumps(native["reasoning_details"], separators=(",", ":"))
129
+ if self.replay_origin is not None:
130
+ data = encode_replay(ReplayRecord(self.replay_origin, native))
131
+ if self._reasoning_started:
132
+ return self._attach_reasoning_replay(data)
133
+ events = self._close_content_blocks()
134
+ events.extend(self._emit_opaque_reasoning(data))
135
+ if data:
136
+ self._content_started = True
137
+ return events
138
+
139
+ def flush_reasoning_replay(self) -> list[str]:
140
+ """Save the complete record when parsed output ends a reasoning block."""
141
+ if self.reasoning_replay_events is None:
142
+ return []
143
+ return list(self.reasoning_replay_events(self))
144
+
145
+ def ensure_text_block(self) -> list[str]:
146
+ events = self.flush_reasoning_replay()
147
+ if self._reasoning_started:
148
+ events.extend(self._stop_reasoning_block())
149
+ self._reasoning_started = False
150
+ if not self._text_started:
151
+ events.extend(self._start_text_block())
152
+ self._text_started = True
153
+ self._content_started = True
154
+ return events
155
+
156
+ def emit_text_delta(self, content: str) -> str:
157
+ self._text_parts.append(content)
158
+ return self._emit_text_delta(content)
159
+
160
+ def close_content_blocks(self) -> list[str]:
161
+ events = self.flush_reasoning_replay()
162
+ events.extend(self._close_content_blocks())
163
+ return events
164
+
165
+ def _close_content_blocks(self) -> list[str]:
166
+ events: list[str] = []
167
+ if self._reasoning_started:
168
+ events.extend(self._stop_reasoning_block())
169
+ self._reasoning_started = False
170
+ if self._text_started:
171
+ events.extend(self._stop_text_block())
172
+ self._text_started = False
173
+ return events
174
+
175
+ def ensure_tool_state(self, tool_index: int) -> ChatToolState:
176
+ return self.tool_states.setdefault(tool_index, ChatToolState())
177
+
178
+ def set_tool_extra_content(
179
+ self, tool_index: int, extra_content: JsonObject | None
180
+ ) -> None:
181
+ if extra_content:
182
+ self.ensure_tool_state(tool_index).extra_content = extra_content
183
+
184
+ def register_tool_name(self, tool_index: int, name: str) -> None:
185
+ state = self.ensure_tool_state(tool_index)
186
+ previous = state.name
187
+ if not previous or name.startswith(previous):
188
+ state.name = name
189
+ elif not previous.startswith(name):
190
+ state.name = previous + name
191
+
192
+ def start_tool_block(
193
+ self,
194
+ tool_index: int,
195
+ tool_id: str,
196
+ name: str,
197
+ *,
198
+ extra_content: JsonObject | None = None,
199
+ ) -> str:
200
+ state = self.ensure_tool_state(tool_index)
201
+ state.tool_id = tool_id
202
+ state.name = name
203
+ if extra_content:
204
+ state.extra_content = extra_content
205
+ state.started = True
206
+ state.open = True
207
+ self._content_started = True
208
+ return self._start_tool_block(tool_index, state)
209
+
210
+ def emit_tool_delta(self, tool_index: int, partial_json: str) -> str:
211
+ state = self.tool_states[tool_index]
212
+ state.argument_parts.append(partial_json)
213
+ return self._emit_tool_delta(tool_index, state, partial_json)
214
+
215
+ def stop_tool_block(self, tool_index: int) -> list[str]:
216
+ state = self.tool_states[tool_index]
217
+ if not state.open:
218
+ return []
219
+ state.open = False
220
+ return self._stop_tool_block(tool_index, state)
221
+
222
+ def close_all_blocks(self) -> list[str]:
223
+ events = self.close_content_blocks()
224
+ for tool_index, state in self.tool_states.items():
225
+ if state.open:
226
+ events.extend(self.stop_tool_block(tool_index))
227
+ return events
228
+
229
+ def close_unclosed_blocks(self) -> list[str]:
230
+ return self.close_all_blocks()
231
+
232
+ def has_emitted_tool_block(self) -> bool:
233
+ return any(state.started for state in self.tool_states.values())
234
+
235
+ def has_content_block(self) -> bool:
236
+ return self._content_started
237
+
238
+ def final_stop_reason(self, fallback: str) -> str:
239
+ return "tool_use" if self.has_emitted_tool_block() else fallback
240
+
241
+ def tool_block_for_tool_index(self, tool_index: int) -> ChatToolState | None:
242
+ state = self.tool_states.get(tool_index)
243
+ return state if state is not None and state.started else None
244
+
245
+ def started_tool_states(self) -> list[tuple[int, ChatToolState]]:
246
+ return [
247
+ (tool_index, state)
248
+ for tool_index, state in self.tool_states.items()
249
+ if state.started
250
+ ]
251
+
252
+ def can_salvage_tool_use(self, schemas: dict[str, ToolSchema]) -> bool:
253
+ states = [state for state in self.tool_states.values() if state.started]
254
+ return bool(states) and all(
255
+ state.tool_id
256
+ and state.name
257
+ and parse_complete_tool_input(state.content, state.name, schemas)
258
+ is not None
259
+ for state in states
260
+ )
261
+
262
+ def buffer_task_args(
263
+ self, tool_index: int, arguments: str
264
+ ) -> dict[str, object] | None:
265
+ state = self.tool_states.get(tool_index)
266
+ if state is None or state.task_args_emitted:
267
+ return None
268
+ state.task_arg_buffer += arguments
269
+ try:
270
+ parsed = json.loads(state.task_arg_buffer)
271
+ except json.JSONDecodeError:
272
+ return None
273
+ if not isinstance(parsed, dict):
274
+ return None
275
+ _normalize_task_args(parsed)
276
+ state.task_args_emitted = True
277
+ state.task_arg_buffer = ""
278
+ return parsed
279
+
280
+ def flush_task_arg_buffers(self) -> list[tuple[int, str]]:
281
+ results: list[tuple[int, str]] = []
282
+ for tool_index, state in self.tool_states.items():
283
+ if not state.task_arg_buffer or state.task_args_emitted:
284
+ continue
285
+ output = "{}"
286
+ try:
287
+ parsed = json.loads(state.task_arg_buffer)
288
+ if isinstance(parsed, dict):
289
+ _normalize_task_args(parsed)
290
+ output = json.dumps(parsed)
291
+ except (json.JSONDecodeError, TypeError, ValueError) as exc:
292
+ digest = hashlib.sha256(
293
+ state.task_arg_buffer.encode("utf-8", errors="replace")
294
+ ).hexdigest()[:16]
295
+ logger.warning(
296
+ "Task args invalid JSON (id={} len={} buffer_sha256_prefix={}): {}",
297
+ state.tool_id or "unknown",
298
+ len(state.task_arg_buffer),
299
+ digest,
300
+ exc,
301
+ )
302
+ state.task_args_emitted = True
303
+ state.task_arg_buffer = ""
304
+ results.append((tool_index, output))
305
+ return results
306
+
307
+ def estimate_output_tokens(self) -> int:
308
+ tool_tokens = sum(
309
+ estimate_text_tokens(state.name) + estimate_text_tokens(state.content) + 15
310
+ for state in self.tool_states.values()
311
+ if state.started
312
+ )
313
+ block_count = (
314
+ (1 if self.accumulated_reasoning else 0)
315
+ + (1 if self.accumulated_text else 0)
316
+ + sum(1 for state in self.tool_states.values() if state.started)
317
+ )
318
+ return (
319
+ estimate_text_tokens(self.accumulated_text)
320
+ + estimate_text_tokens(self.accumulated_reasoning)
321
+ + tool_tokens
322
+ + (block_count * 4)
323
+ )
324
+
325
+ def finish_success(self, *, stop_reason: str, usage: ChatStreamUsage) -> list[str]:
326
+ if self._terminal:
327
+ return []
328
+ events = self.close_all_blocks()
329
+ events.extend(self._finish_success(stop_reason=stop_reason, usage=usage))
330
+ self._terminal = True
331
+ return events
332
+
333
+ def finish_failure(self, failure: ExecutionFailure) -> list[str]:
334
+ if self._terminal:
335
+ return []
336
+ events = self.close_unclosed_blocks()
337
+ events.extend(self._finish_failure(failure))
338
+ self._terminal = True
339
+ return events
340
+
341
+ @abstractmethod
342
+ def _start_events(self) -> list[str]: ...
343
+
344
+ @abstractmethod
345
+ def _start_reasoning_block(self) -> list[str]: ...
346
+
347
+ @abstractmethod
348
+ def _emit_reasoning_delta(self, content: str) -> str: ...
349
+
350
+ @abstractmethod
351
+ def _emit_opaque_reasoning(self, data: str) -> list[str]: ...
352
+
353
+ @abstractmethod
354
+ def _attach_reasoning_replay(self, data: str) -> list[str]: ...
355
+
356
+ @abstractmethod
357
+ def _stop_reasoning_block(self) -> list[str]: ...
358
+
359
+ @abstractmethod
360
+ def _start_text_block(self) -> list[str]: ...
361
+
362
+ @abstractmethod
363
+ def _emit_text_delta(self, content: str) -> str: ...
364
+
365
+ @abstractmethod
366
+ def _stop_text_block(self) -> list[str]: ...
367
+
368
+ @abstractmethod
369
+ def _start_tool_block(self, tool_index: int, state: ChatToolState) -> str: ...
370
+
371
+ @abstractmethod
372
+ def _emit_tool_delta(
373
+ self, tool_index: int, state: ChatToolState, partial_json: str
374
+ ) -> str: ...
375
+
376
+ @abstractmethod
377
+ def _stop_tool_block(self, tool_index: int, state: ChatToolState) -> list[str]: ...
378
+
379
+ @abstractmethod
380
+ def _finish_success(
381
+ self, *, stop_reason: str, usage: ChatStreamUsage
382
+ ) -> list[str]: ...
383
+
384
+ @abstractmethod
385
+ def _finish_failure(self, failure: ExecutionFailure) -> list[str]: ...
386
+
387
+
388
+ class AnthropicChatStreamOutput(ChatStreamOutput):
389
+ """Anthropic SSE writer preserving the established Chat provider wire."""
390
+
391
+ def __init__(
392
+ self,
393
+ *,
394
+ message_id: str,
395
+ model: str,
396
+ input_tokens: int,
397
+ log_raw_events: bool = False,
398
+ ) -> None:
399
+ super().__init__(input_tokens=input_tokens)
400
+ self._ledger = AnthropicStreamLedger(
401
+ message_id,
402
+ model,
403
+ input_tokens,
404
+ log_raw_events=log_raw_events,
405
+ )
406
+
407
+ def close_unclosed_blocks(self) -> list[str]:
408
+ events = self.flush_reasoning_replay()
409
+ events.extend(self._ledger.close_unclosed_blocks())
410
+ self._text_started = False
411
+ self._reasoning_started = False
412
+ for state in self.tool_states.values():
413
+ state.open = False
414
+ return events
415
+
416
+ def _start_events(self) -> list[str]:
417
+ return [self._ledger.message_start()]
418
+
419
+ def _start_reasoning_block(self) -> list[str]:
420
+ return [self._ledger.start_thinking_block()]
421
+
422
+ def _emit_reasoning_delta(self, content: str) -> str:
423
+ return self._ledger.emit_thinking_delta(content)
424
+
425
+ def _emit_opaque_reasoning(self, data: str) -> list[str]:
426
+ index = self._ledger.blocks.allocate_index()
427
+ return [
428
+ self._ledger.content_block_start(index, "redacted_thinking", data=data),
429
+ self._ledger.content_block_stop(index),
430
+ ]
431
+
432
+ def _attach_reasoning_replay(self, data: str) -> list[str]:
433
+ return [
434
+ self._ledger.content_block_delta(
435
+ self._ledger.blocks.thinking_index, "signature_delta", data
436
+ )
437
+ ]
438
+
439
+ def _stop_reasoning_block(self) -> list[str]:
440
+ return [self._ledger.stop_thinking_block()]
441
+
442
+ def _start_text_block(self) -> list[str]:
443
+ return [self._ledger.start_text_block()]
444
+
445
+ def _emit_text_delta(self, content: str) -> str:
446
+ return self._ledger.emit_text_delta(content)
447
+
448
+ def _stop_text_block(self) -> list[str]:
449
+ return [self._ledger.stop_text_block()]
450
+
451
+ def _start_tool_block(self, tool_index: int, state: ChatToolState) -> str:
452
+ return self._ledger.start_tool_block(
453
+ tool_index,
454
+ state.tool_id,
455
+ state.name,
456
+ extra_content=state.extra_content,
457
+ )
458
+
459
+ def _emit_tool_delta(
460
+ self, tool_index: int, state: ChatToolState, partial_json: str
461
+ ) -> str:
462
+ return self._ledger.emit_tool_delta(tool_index, partial_json)
463
+
464
+ def _stop_tool_block(self, tool_index: int, state: ChatToolState) -> list[str]:
465
+ return [self._ledger.stop_tool_block(tool_index)]
466
+
467
+ def _finish_success(self, *, stop_reason: str, usage: ChatStreamUsage) -> list[str]:
468
+ return [
469
+ self._ledger.message_delta(
470
+ self.final_stop_reason(stop_reason),
471
+ usage.output_tokens,
472
+ input_tokens=usage.input_tokens,
473
+ usage_fields=usage.anthropic_fields,
474
+ ),
475
+ self._ledger.message_stop(),
476
+ ]
477
+
478
+ def _finish_failure(self, failure: ExecutionFailure) -> list[str]:
479
+ return []
480
+
481
+
482
+ class ResponsesChatStreamOutput(ChatStreamOutput):
483
+ """Direct Chat-to-Responses writer with one coherent Responses lifecycle."""
484
+
485
+ consumes_terminal_failure = True
486
+
487
+ def __init__(
488
+ self,
489
+ tool_adapter: ResponsesToolAdapter,
490
+ *,
491
+ input_tokens: int,
492
+ response_model: str | None = None,
493
+ ) -> None:
494
+ super().__init__(input_tokens=input_tokens)
495
+ self._request = tool_adapter.original
496
+ self._response_model = response_model or self._request.model
497
+ self._response_id = new_response_id()
498
+ self._created_at = int(time.time())
499
+ self._ledger = ResponsesOutputLedger()
500
+ tool_events = tool_adapter.event_adapter()
501
+ self._events = ResponseEventBuilder(tool_events.feed if tool_events else None)
502
+ self._completer = ResponseBlockCompleter(
503
+ self._ledger,
504
+ events=self._events,
505
+ on_invalid_function_call=self._fail_invalid_function_call,
506
+ prepare_tool_arguments=tool_adapter.prepare_arguments,
507
+ )
508
+ self._text_state: TextBlockState | None = None
509
+ self._reasoning_state: ReasoningBlockState | None = None
510
+ self._tool_output_states: dict[int, ToolBlockState] = {}
511
+ self._usage: dict[str, object] | None = None
512
+ self._provisional_error: dict[str, object] | None = None
513
+ self._started = False
514
+
515
+ def _response_payload(
516
+ self,
517
+ *,
518
+ status: str,
519
+ error: Mapping[str, object] | None = None,
520
+ incomplete_details: Mapping[str, str] | None = None,
521
+ ) -> dict[str, object]:
522
+ return {
523
+ "id": self._response_id,
524
+ "object": "response",
525
+ "created_at": self._created_at,
526
+ "status": status,
527
+ "model": self._response_model,
528
+ "output": self._ledger.output(),
529
+ "parallel_tool_calls": (
530
+ True
531
+ if self._request.parallel_tool_calls is None
532
+ else self._request.parallel_tool_calls
533
+ ),
534
+ "tool_choice": (
535
+ "auto"
536
+ if self._request.tool_choice is None
537
+ else self._request.tool_choice
538
+ ),
539
+ "temperature": self._request.temperature,
540
+ "top_p": self._request.top_p,
541
+ "max_output_tokens": self._request.max_output_tokens,
542
+ "usage": self._usage,
543
+ "error": dict(error) if error is not None else None,
544
+ "incomplete_details": (
545
+ dict(incomplete_details) if incomplete_details is not None else None
546
+ ),
547
+ }
548
+
549
+ def _start_events(self) -> list[str]:
550
+ if self._started:
551
+ return []
552
+ self._started = True
553
+ return [
554
+ self._events.response_created(self._response_payload(status="in_progress"))
555
+ ]
556
+
557
+ def _start_reasoning_block(self) -> list[str]:
558
+ output_index = self._ledger.reserve_output_slot()
559
+ state = ReasoningBlockState(
560
+ index=output_index,
561
+ output_index=output_index,
562
+ item_id=new_reasoning_item_id(),
563
+ )
564
+ self._reasoning_state = state
565
+ self._ledger.set_active_block(state)
566
+ return [
567
+ self._events.output_item_added(
568
+ output_index,
569
+ reasoning_output_item(state, status="in_progress"),
570
+ )
571
+ ]
572
+
573
+ def _emit_reasoning_delta(self, content: str) -> str:
574
+ state = self._reasoning_state
575
+ if state is None or not content:
576
+ return ""
577
+ state.text_parts.append(content)
578
+ return self._events.reasoning_text_delta(
579
+ state.item_id, state.output_index, content
580
+ )
581
+
582
+ def _emit_opaque_reasoning(self, data: str) -> list[str]:
583
+ output_index = self._ledger.reserve_output_slot()
584
+ state = ReasoningBlockState(
585
+ index=output_index,
586
+ output_index=output_index,
587
+ item_id=new_reasoning_item_id(),
588
+ encrypted_content=data,
589
+ )
590
+ self._ledger.set_active_block(state)
591
+ events = [
592
+ self._events.output_item_added(
593
+ output_index,
594
+ reasoning_output_item(state, status="in_progress"),
595
+ )
596
+ ]
597
+ self._ledger.pop_active_block(state.index)
598
+ events.extend(self._completer.complete_block(state))
599
+ return events
600
+
601
+ def _attach_reasoning_replay(self, data: str) -> list[str]:
602
+ if self._reasoning_state is not None:
603
+ self._reasoning_state.encrypted_content = data
604
+ return []
605
+
606
+ def _stop_reasoning_block(self) -> list[str]:
607
+ state = self._reasoning_state
608
+ self._reasoning_state = None
609
+ if state is None:
610
+ return []
611
+ self._ledger.pop_active_block(state.index)
612
+ return self._completer.complete_block(state)
613
+
614
+ def _start_text_block(self) -> list[str]:
615
+ output_index = self._ledger.reserve_output_slot()
616
+ state = TextBlockState(
617
+ index=output_index,
618
+ output_index=output_index,
619
+ item_id=new_message_item_id(),
620
+ )
621
+ self._text_state = state
622
+ self._ledger.set_active_block(state)
623
+ item = {
624
+ "id": state.item_id,
625
+ "type": "message",
626
+ "status": "in_progress",
627
+ "role": "assistant",
628
+ "content": [],
629
+ }
630
+ return [
631
+ self._events.output_item_added(output_index, item),
632
+ self._events.content_part_added(state.item_id, output_index),
633
+ ]
634
+
635
+ def _emit_text_delta(self, content: str) -> str:
636
+ state = self._text_state
637
+ if state is None or not content:
638
+ return ""
639
+ state.text_parts.append(content)
640
+ return self._events.output_text_delta(
641
+ state.item_id, state.output_index, content
642
+ )
643
+
644
+ def _stop_text_block(self) -> list[str]:
645
+ state = self._text_state
646
+ self._text_state = None
647
+ if state is None:
648
+ return []
649
+ self._ledger.pop_active_block(state.index)
650
+ return self._completer.complete_block(state)
651
+
652
+ def _start_tool_block(self, tool_index: int, state: ChatToolState) -> str:
653
+ output_index = self._ledger.reserve_output_slot()
654
+ output_state = ToolBlockState(
655
+ index=output_index,
656
+ output_index=output_index,
657
+ item_id=f"fc_{uuid.uuid4().hex[:24]}",
658
+ call_id=state.tool_id or new_call_id(),
659
+ kind="function",
660
+ name=state.name,
661
+ )
662
+ self._tool_output_states[tool_index] = output_state
663
+ self._ledger.set_active_block(output_state)
664
+ return self._events.output_item_added(
665
+ output_index,
666
+ tool_item(output_state, status="in_progress"),
667
+ )
668
+
669
+ def _emit_tool_delta(
670
+ self, tool_index: int, state: ChatToolState, partial_json: str
671
+ ) -> str:
672
+ output_state = self._tool_output_states.get(tool_index)
673
+ if output_state is not None:
674
+ output_state.argument_parts.append(partial_json)
675
+ return ""
676
+
677
+ def _stop_tool_block(self, tool_index: int, state: ChatToolState) -> list[str]:
678
+ output_state = self._tool_output_states.get(tool_index)
679
+ if output_state is None:
680
+ return []
681
+ self._ledger.pop_active_block(output_state.index)
682
+ return self._completer.complete_block(output_state)
683
+
684
+ def _finish_success(self, *, stop_reason: str, usage: ChatStreamUsage) -> list[str]:
685
+ self._usage = _responses_usage(usage)
686
+ if self._provisional_error is not None:
687
+ response = self._response_payload(
688
+ status="failed",
689
+ error=self._provisional_error,
690
+ )
691
+ return [self._events.response_failed(response)]
692
+ if stop_reason in {"length", "max_tokens"}:
693
+ response = self._response_payload(
694
+ status="incomplete",
695
+ incomplete_details={"reason": "max_output_tokens"},
696
+ )
697
+ return [self._events.response_incomplete(response)]
698
+ response = self._response_payload(status="completed")
699
+ return [self._events.response_completed(response)]
700
+
701
+ def _finish_failure(self, failure: ExecutionFailure) -> list[str]:
702
+ response = self._response_payload(
703
+ status="failed",
704
+ error=openai_error_from_failure(failure),
705
+ )
706
+ return [self._events.response_failed(response)]
707
+
708
+ def _fail_invalid_function_call(
709
+ self, state: ToolBlockState, exc: ResponsesConversionError
710
+ ) -> list[str]:
711
+ trace_event(
712
+ stage="responses",
713
+ event="responses.output.function_call_invalid_arguments",
714
+ source="openai_responses",
715
+ call_id=state.call_id,
716
+ tool_name=state.name,
717
+ error_type=type(exc).__name__,
718
+ )
719
+ if self._provisional_error is None:
720
+ self._provisional_error = replay_unsafe_function_call_error()
721
+ return []
722
+
723
+
724
+ def _responses_usage(usage: ChatStreamUsage) -> dict[str, object]:
725
+ cached_tokens = usage.cached_tokens
726
+ if (
727
+ not isinstance(cached_tokens, int)
728
+ or isinstance(cached_tokens, bool)
729
+ or not 0 <= cached_tokens <= usage.input_tokens
730
+ ):
731
+ cached_tokens = 0
732
+ reasoning_tokens = max(0, min(usage.reasoning_tokens, usage.output_tokens))
733
+ input_token_details = {"cached_tokens": cached_tokens}
734
+ cache_write_tokens = usage.cache_write_tokens
735
+ if (
736
+ isinstance(cache_write_tokens, int)
737
+ and not isinstance(cache_write_tokens, bool)
738
+ and 0 <= cache_write_tokens <= usage.input_tokens - cached_tokens
739
+ ):
740
+ input_token_details["cache_write_tokens"] = cache_write_tokens
741
+ return {
742
+ "input_tokens": usage.input_tokens,
743
+ "input_tokens_details": input_token_details,
744
+ "output_tokens": usage.output_tokens,
745
+ "output_tokens_details": {"reasoning_tokens": reasoning_tokens},
746
+ "total_tokens": usage.input_tokens + usage.output_tokens,
747
+ }
748
+
749
+
750
+ def _normalize_task_args(arguments: dict[str, object]) -> None:
751
+ if arguments.get("run_in_background") is not False:
752
+ arguments["run_in_background"] = False