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,306 @@
1
+ """Native Messages event validation and identity-preserving relay."""
2
+
3
+ import json
4
+ from collections.abc import Mapping
5
+ from dataclasses import dataclass, field, replace
6
+ from typing import cast
7
+
8
+ from free_claude_code.core.history_replay import (
9
+ ReplayOrigin,
10
+ ReplayRecord,
11
+ encode_replay,
12
+ )
13
+ from free_claude_code.core.json_types import JsonObject, JsonValue
14
+
15
+ from .native import NativeMessagesError
16
+
17
+ _STOP_REASONS = {
18
+ "end_turn",
19
+ "stop_sequence",
20
+ "tool_use",
21
+ "max_tokens",
22
+ "refusal",
23
+ "pause_turn",
24
+ "model_context_window_exceeded",
25
+ }
26
+
27
+
28
+ @dataclass(slots=True)
29
+ class _Block:
30
+ body: JsonObject
31
+ parts: list[str] = field(default_factory=list)
32
+ fragments: dict[str, list[str]] = field(default_factory=dict)
33
+
34
+
35
+ class NativeMessagesStreamState:
36
+ """Validate one upstream lifecycle without rewriting protocol identities."""
37
+
38
+ def __init__(self) -> None:
39
+ self.started = False
40
+ self.completed = False
41
+ self.stop_reason: str | None = None
42
+ self._blocks: dict[int, _Block] = {}
43
+ self._seen: set[int] = set()
44
+ self._tool_ids: set[str] = set()
45
+
46
+ def accept(
47
+ self, event_type: str, payload: Mapping[str, JsonValue]
48
+ ) -> JsonObject | None:
49
+ if self.completed:
50
+ raise NativeMessagesError("Messages event arrived after message_stop.")
51
+ try:
52
+ json.dumps(dict(payload), ensure_ascii=False, allow_nan=False).encode(
53
+ "utf-8"
54
+ )
55
+ except (ValueError, UnicodeError, RecursionError) as exc:
56
+ raise NativeMessagesError(
57
+ "Native Messages event is not representable JSON."
58
+ ) from exc
59
+ if payload.get("type") != event_type:
60
+ raise NativeMessagesError("Messages event type disagrees with its payload.")
61
+ if event_type == "ping":
62
+ return None
63
+ if event_type == "message_start":
64
+ if self.started:
65
+ raise NativeMessagesError("Duplicate message_start.")
66
+ message = payload.get("message")
67
+ if (
68
+ not isinstance(message, Mapping)
69
+ or not isinstance(message.get("id"), str)
70
+ or not message["id"]
71
+ or message.get("role") != "assistant"
72
+ or message.get("content", []) != []
73
+ ):
74
+ raise NativeMessagesError("Invalid native message_start.")
75
+ self.started = True
76
+ return None
77
+ if not self.started:
78
+ raise NativeMessagesError("Messages event arrived before message_start.")
79
+ if event_type == "message_delta":
80
+ delta = payload.get("delta")
81
+ if not isinstance(delta, Mapping):
82
+ raise NativeMessagesError("Native message_delta requires an object.")
83
+ reason = delta.get("stop_reason")
84
+ if reason is not None:
85
+ if self._blocks:
86
+ raise NativeMessagesError(
87
+ "Stop reason arrived with open content blocks."
88
+ )
89
+ if not isinstance(reason, str) or reason not in _STOP_REASONS:
90
+ raise NativeMessagesError(
91
+ "Unsupported native Messages stop reason."
92
+ )
93
+ if self.stop_reason is not None:
94
+ raise NativeMessagesError("Duplicate native Messages stop reason.")
95
+ self.stop_reason = reason
96
+ return None
97
+ if event_type == "message_stop":
98
+ if self._blocks or self.stop_reason is None:
99
+ raise NativeMessagesError(
100
+ "message_stop arrived before content or stop reason completed."
101
+ )
102
+ self.completed = True
103
+ return None
104
+ if event_type not in {
105
+ "content_block_start",
106
+ "content_block_delta",
107
+ "content_block_stop",
108
+ }:
109
+ raise NativeMessagesError(
110
+ f"Unsupported native Messages event: {event_type!r}."
111
+ )
112
+ if self.stop_reason is not None:
113
+ raise NativeMessagesError("Content arrived after the message stop reason.")
114
+ index = payload.get("index")
115
+ if not isinstance(index, int) or isinstance(index, bool) or index < 0:
116
+ raise NativeMessagesError(
117
+ "Messages block index must be a nonnegative integer."
118
+ )
119
+ if event_type == "content_block_start":
120
+ if index in self._seen:
121
+ raise NativeMessagesError("Duplicate Messages content block index.")
122
+ body = payload.get("content_block")
123
+ if not isinstance(body, Mapping) or not isinstance(body.get("type"), str):
124
+ raise NativeMessagesError("Invalid native content block.")
125
+ kind = body["type"]
126
+ if kind == "text" and not isinstance(body.get("text"), str):
127
+ raise NativeMessagesError("Native text block requires text.")
128
+ if kind == "thinking" and not isinstance(body.get("thinking"), str):
129
+ raise NativeMessagesError(
130
+ "Native thinking block requires thinking text."
131
+ )
132
+ if (
133
+ kind == "thinking"
134
+ and "signature" in body
135
+ and not isinstance(body["signature"], str)
136
+ ):
137
+ raise NativeMessagesError("Native thinking signature must be a string.")
138
+ if kind == "redacted_thinking" and (
139
+ not isinstance(body.get("data"), str) or not body["data"]
140
+ ):
141
+ raise NativeMessagesError("Redacted native thinking requires its data.")
142
+ if kind == "tool_use":
143
+ tool_id = body.get("id")
144
+ if (
145
+ not isinstance(tool_id, str)
146
+ or not tool_id
147
+ or tool_id in self._tool_ids
148
+ or not isinstance(body.get("name"), str)
149
+ or not body["name"]
150
+ or not isinstance(body.get("input"), Mapping)
151
+ ):
152
+ raise NativeMessagesError(
153
+ "Invalid or duplicate native tool identity/input."
154
+ )
155
+ self._tool_ids.add(tool_id)
156
+ self._blocks[index] = _Block(dict(body))
157
+ self._seen.add(index)
158
+ return None
159
+ block = self._blocks.get(index)
160
+ if block is None:
161
+ raise NativeMessagesError(
162
+ "Messages content event refers to a closed or unknown block."
163
+ )
164
+ if event_type == "content_block_delta":
165
+ delta = payload.get("delta")
166
+ if not isinstance(delta, Mapping):
167
+ raise NativeMessagesError("Native content delta must be an object.")
168
+ kind = delta.get("type")
169
+ target = (
170
+ {
171
+ "text_delta": ("text", "text"),
172
+ "thinking_delta": ("thinking", "thinking"),
173
+ "signature_delta": ("thinking", "signature"),
174
+ "input_json_delta": ("tool_use", "partial_json"),
175
+ }.get(kind)
176
+ if isinstance(kind, str)
177
+ else None
178
+ )
179
+ if target is None:
180
+ if kind == "citations_delta" and block.body["type"] == "text":
181
+ citation = delta.get("citation")
182
+ citations = block.body.get("citations")
183
+ if citations is None:
184
+ citations = []
185
+ if not isinstance(citation, Mapping) or not isinstance(
186
+ citations, list
187
+ ):
188
+ raise NativeMessagesError("Invalid native citation delta.")
189
+ block.body["citations"] = [*citations, dict(citation)]
190
+ return None
191
+ raise NativeMessagesError("Unsupported native content delta.")
192
+ block_type, key = target
193
+ value = delta.get(key)
194
+ if block.body["type"] != block_type or not isinstance(value, str):
195
+ raise NativeMessagesError(
196
+ "Native content delta does not match its block."
197
+ )
198
+ if key == "partial_json":
199
+ if block.body.get("input"):
200
+ raise NativeMessagesError(
201
+ "Tool input mixes eager content and JSON deltas."
202
+ )
203
+ block.parts.append(value)
204
+ else:
205
+ existing = block.body.get(key, "")
206
+ if not isinstance(existing, str):
207
+ raise NativeMessagesError("Native content field must be a string.")
208
+ block.fragments.setdefault(key, []).append(value)
209
+ return None
210
+ for key, parts in block.fragments.items():
211
+ initial = block.body.get(key, "")
212
+ if not isinstance(initial, str):
213
+ raise AssertionError("Validated native content fields must be strings.")
214
+ block.body[key] = initial + "".join(parts)
215
+ if block.body["type"] == "thinking" and not block.body.get("signature"):
216
+ raise NativeMessagesError(
217
+ "Completed native thinking requires its signature."
218
+ )
219
+ if block.body["type"] == "tool_use" and block.parts:
220
+ try:
221
+ value = cast(JsonValue, json.loads("".join(block.parts)))
222
+ if not isinstance(value, Mapping):
223
+ raise NativeMessagesError(
224
+ "Native tool arguments must decode to an object."
225
+ )
226
+ json.dumps(value, ensure_ascii=False, allow_nan=False).encode("utf-8")
227
+ except (ValueError, UnicodeError, RecursionError) as exc:
228
+ raise NativeMessagesError(
229
+ "Native tool arguments are incomplete or invalid JSON."
230
+ ) from exc
231
+ block.body["input"] = dict(value)
232
+ self._blocks.pop(index)
233
+ return block.body
234
+
235
+
236
+ class NativeMessagesRelay:
237
+ """Preserve upstream IDs, indexes, fields and event order for Messages clients."""
238
+
239
+ def __init__(
240
+ self, *, public_model: str, replay_origin: ReplayOrigin | None = None
241
+ ) -> None:
242
+ self._public_model = public_model
243
+ self._replay_origin = replay_origin
244
+ self._state = NativeMessagesStreamState()
245
+
246
+ @property
247
+ def completed(self) -> bool:
248
+ return self._state.completed
249
+
250
+ @property
251
+ def stop_reason(self) -> str | None:
252
+ return self._state.stop_reason
253
+
254
+ def feed(self, event_type: str, payload: Mapping[str, JsonValue]) -> str:
255
+ completed = self._state.accept(event_type, payload)
256
+ body = dict(payload)
257
+ if event_type == "message_start":
258
+ message = body.get("message")
259
+ if not isinstance(message, dict):
260
+ raise AssertionError("Validated message_start must contain a message.")
261
+ body["message"] = {**message, "model": self._public_model}
262
+ if (
263
+ self._replay_origin is not None
264
+ and isinstance(message.get("model"), str)
265
+ and message["model"]
266
+ ):
267
+ self._replay_origin = replace(
268
+ self._replay_origin, model=message["model"]
269
+ )
270
+ prefix = ""
271
+ if self._replay_origin is not None:
272
+ block = body.get("content_block")
273
+ if event_type == "content_block_start" and isinstance(block, dict):
274
+ if block.get("type") == "thinking":
275
+ body["content_block"] = {**block, "signature": ""}
276
+ elif block.get("type") == "redacted_thinking":
277
+ body["content_block"] = {
278
+ **block,
279
+ "data": encode_replay(ReplayRecord(self._replay_origin, block)),
280
+ }
281
+ delta = body.get("delta")
282
+ if (
283
+ event_type == "content_block_delta"
284
+ and isinstance(delta, dict)
285
+ and delta.get("type") == "signature_delta"
286
+ ):
287
+ return ""
288
+ if (
289
+ completed is not None
290
+ and completed.get("type") == "thinking"
291
+ and completed.get("signature")
292
+ ):
293
+ signature = encode_replay(ReplayRecord(self._replay_origin, completed))
294
+ prefix = _event(
295
+ "content_block_delta",
296
+ {
297
+ "type": "content_block_delta",
298
+ "index": body["index"],
299
+ "delta": {"type": "signature_delta", "signature": signature},
300
+ },
301
+ )
302
+ return prefix + _event(event_type, body)
303
+
304
+
305
+ def _event(kind: str, payload: JsonObject) -> str:
306
+ return f"event: {kind}\ndata: {json.dumps(payload, ensure_ascii=False)}\n\n"
@@ -0,0 +1,57 @@
1
+ """Shared Anthropic request serialization helpers."""
2
+
3
+ import json
4
+ from typing import Any
5
+
6
+ from .models import MessagesRequest
7
+
8
+ _MESSAGES_REQUEST_FIELDS = (
9
+ "model",
10
+ "messages",
11
+ "system",
12
+ "max_tokens",
13
+ "stop_sequences",
14
+ "stream",
15
+ "temperature",
16
+ "top_p",
17
+ "top_k",
18
+ "metadata",
19
+ "tools",
20
+ "tool_choice",
21
+ "thinking",
22
+ "context_management",
23
+ "output_config",
24
+ "mcp_servers",
25
+ "extra_body",
26
+ )
27
+
28
+
29
+ def dump_messages_request(request: MessagesRequest) -> dict[str, Any]:
30
+ """Return JSON-ready public Messages fields without FCC routing state."""
31
+ raw = request.model_dump(exclude_none=True)
32
+ return {
33
+ field: raw[field]
34
+ for field in _MESSAGES_REQUEST_FIELDS
35
+ if field in raw and raw[field] is not None
36
+ }
37
+
38
+
39
+ def serialize_tool_result_content(content: Any) -> str:
40
+ """Serialize Anthropic ``tool_result.content`` into provider-safe text."""
41
+ if content is None:
42
+ return ""
43
+ if isinstance(content, str):
44
+ return content
45
+ if isinstance(content, dict):
46
+ return json.dumps(content, ensure_ascii=False)
47
+ if isinstance(content, list):
48
+ parts: list[str] = []
49
+ for item in content:
50
+ if isinstance(item, dict) and item.get("type") == "text":
51
+ parts.append(str(item.get("text", "")))
52
+ elif isinstance(item, dict):
53
+ parts.append(json.dumps(item, ensure_ascii=False))
54
+ else:
55
+ parts.append(str(item))
56
+ return "\n".join(parts)
57
+ return str(content)
@@ -0,0 +1,35 @@
1
+ """Trace-safe snapshots of Anthropic protocol requests."""
2
+
3
+ from typing import Any
4
+
5
+ from free_claude_code.core.trace import sanitize_trace_value
6
+
7
+ from .models import MessagesRequest, TokenCountRequest
8
+
9
+
10
+ def anthropic_request_snapshot(
11
+ request: MessagesRequest | TokenCountRequest,
12
+ ) -> dict[str, Any]:
13
+ """Return the traceable public fields of an Anthropic request."""
14
+ data = request.model_dump(mode="python")
15
+ snapshot = {
16
+ key: data[key]
17
+ for key in (
18
+ "model",
19
+ "messages",
20
+ "system",
21
+ "tools",
22
+ "tool_choice",
23
+ "max_tokens",
24
+ "thinking",
25
+ "temperature",
26
+ "top_p",
27
+ "top_k",
28
+ "stop_sequences",
29
+ "metadata",
30
+ "stream",
31
+ )
32
+ if key in data and data[key] is not None
33
+ }
34
+ sanitized = sanitize_trace_value(snapshot)
35
+ return sanitized if isinstance(sanitized, dict) else {}
@@ -0,0 +1,234 @@
1
+ """Anthropic representation of completed local web-tool operations."""
2
+
3
+ from collections.abc import Iterator, Mapping, Sequence
4
+ from dataclasses import dataclass
5
+
6
+ from free_claude_code.core.json_types import JsonObject
7
+ from free_claude_code.core.web_tools import WebFetchResult, WebSearchResult
8
+
9
+ from .server_tool_types import (
10
+ SERVER_TOOL_USE,
11
+ WEB_FETCH_TOOL_ERROR,
12
+ WEB_FETCH_TOOL_RESULT,
13
+ WEB_SEARCH_TOOL_RESULT,
14
+ WEB_SEARCH_TOOL_RESULT_ERROR,
15
+ )
16
+ from .streaming import format_sse_event
17
+
18
+
19
+ @dataclass(frozen=True, slots=True)
20
+ class ServerToolResponseContext:
21
+ message_id: str
22
+ tool_id: str
23
+ model: str
24
+ tool_name: str
25
+ tool_input: Mapping[str, str]
26
+ input_tokens: int
27
+ provider_usage: Mapping[str, object] | None = None
28
+
29
+
30
+ def server_tool_start_frames(context: ServerToolResponseContext) -> Iterator[str]:
31
+ yield from server_tool_message_start_frames(context)
32
+ yield from server_tool_use_frames(context, index=0)
33
+
34
+
35
+ def server_tool_message_start_frames(
36
+ context: ServerToolResponseContext,
37
+ ) -> Iterator[str]:
38
+ yield format_sse_event(
39
+ "message_start",
40
+ {
41
+ "type": "message_start",
42
+ "message": {
43
+ "id": context.message_id,
44
+ "type": "message",
45
+ "role": "assistant",
46
+ "content": [],
47
+ "model": context.model,
48
+ "stop_reason": None,
49
+ "stop_sequence": None,
50
+ "usage": _message_start_usage(
51
+ context.input_tokens, context.provider_usage
52
+ ),
53
+ },
54
+ },
55
+ )
56
+
57
+
58
+ def server_tool_use_frames(
59
+ context: ServerToolResponseContext, *, index: int
60
+ ) -> Iterator[str]:
61
+ yield format_sse_event(
62
+ "content_block_start",
63
+ {
64
+ "type": "content_block_start",
65
+ "index": index,
66
+ "content_block": {
67
+ "type": SERVER_TOOL_USE,
68
+ "id": context.tool_id,
69
+ "name": context.tool_name,
70
+ "input": context.tool_input,
71
+ },
72
+ },
73
+ )
74
+ yield format_sse_event(
75
+ "content_block_stop", {"type": "content_block_stop", "index": index}
76
+ )
77
+
78
+
79
+ def web_search_result_block(
80
+ context: ServerToolResponseContext, results: Sequence[WebSearchResult]
81
+ ) -> JsonObject:
82
+ return {
83
+ "type": WEB_SEARCH_TOOL_RESULT,
84
+ "tool_use_id": context.tool_id,
85
+ "content": [
86
+ {"type": "web_search_result", "title": result.title, "url": result.url}
87
+ for result in results
88
+ ],
89
+ }
90
+
91
+
92
+ def web_fetch_result_block(
93
+ context: ServerToolResponseContext,
94
+ result: WebFetchResult,
95
+ *,
96
+ retrieved_at: str,
97
+ ) -> JsonObject:
98
+ return {
99
+ "type": WEB_FETCH_TOOL_RESULT,
100
+ "tool_use_id": context.tool_id,
101
+ "content": {
102
+ "type": "web_fetch_result",
103
+ "url": result.url,
104
+ "content": {
105
+ "type": "document",
106
+ "source": {
107
+ "type": "text",
108
+ "media_type": result.media_type,
109
+ "data": result.data,
110
+ },
111
+ "title": result.title,
112
+ "citations": {"enabled": True},
113
+ },
114
+ "retrieved_at": retrieved_at,
115
+ },
116
+ }
117
+
118
+
119
+ def web_tool_error_block(
120
+ context: ServerToolResponseContext, *, error_code: str = "unavailable"
121
+ ) -> JsonObject:
122
+ search = context.tool_name == "web_search"
123
+ return {
124
+ "type": WEB_SEARCH_TOOL_RESULT if search else WEB_FETCH_TOOL_RESULT,
125
+ "tool_use_id": context.tool_id,
126
+ "content": {
127
+ "type": WEB_SEARCH_TOOL_RESULT_ERROR if search else WEB_FETCH_TOOL_ERROR,
128
+ "error_code": error_code,
129
+ },
130
+ }
131
+
132
+
133
+ def server_tool_completion_frames(
134
+ context: ServerToolResponseContext,
135
+ result_block: JsonObject,
136
+ *,
137
+ summary: str,
138
+ ) -> Iterator[str]:
139
+ yield from server_tool_result_frames(result_block, index=1)
140
+ yield from server_tool_finish_frames(context, summary=summary, index=2)
141
+
142
+
143
+ def server_tool_result_frames(result_block: JsonObject, *, index: int) -> Iterator[str]:
144
+ yield format_sse_event(
145
+ "content_block_start",
146
+ {"type": "content_block_start", "index": index, "content_block": result_block},
147
+ )
148
+ yield format_sse_event(
149
+ "content_block_stop", {"type": "content_block_stop", "index": index}
150
+ )
151
+
152
+
153
+ def server_tool_finish_frames(
154
+ context: ServerToolResponseContext,
155
+ *,
156
+ summary: str,
157
+ index: int,
158
+ request_count: int = 1,
159
+ ) -> Iterator[str]:
160
+ yield format_sse_event(
161
+ "content_block_start",
162
+ {
163
+ "type": "content_block_start",
164
+ "index": index,
165
+ "content_block": {"type": "text", "text": ""},
166
+ },
167
+ )
168
+ yield format_sse_event(
169
+ "content_block_delta",
170
+ {
171
+ "type": "content_block_delta",
172
+ "index": index,
173
+ "delta": {"type": "text_delta", "text": summary},
174
+ },
175
+ )
176
+ yield format_sse_event(
177
+ "content_block_stop", {"type": "content_block_stop", "index": index}
178
+ )
179
+ yield format_sse_event(
180
+ "message_delta",
181
+ {
182
+ "type": "message_delta",
183
+ "delta": {"stop_reason": "end_turn", "stop_sequence": None},
184
+ "usage": _completion_usage(
185
+ context.input_tokens,
186
+ summary,
187
+ context.provider_usage,
188
+ usage_key=f"{context.tool_name}_requests",
189
+ request_count=request_count,
190
+ ),
191
+ },
192
+ )
193
+ yield format_sse_event("message_stop", {"type": "message_stop"})
194
+
195
+
196
+ def _message_start_usage(
197
+ input_tokens: int,
198
+ provider_usage: Mapping[str, object] | None,
199
+ ) -> dict[str, object]:
200
+ if provider_usage is None:
201
+ return {"input_tokens": input_tokens, "output_tokens": 1}
202
+ usage = _integer_usage(provider_usage)
203
+ usage.setdefault("input_tokens", input_tokens)
204
+ usage["output_tokens"] = 1
205
+ return usage
206
+
207
+
208
+ def _completion_usage(
209
+ input_tokens: int,
210
+ summary: str,
211
+ provider_usage: Mapping[str, object] | None,
212
+ *,
213
+ usage_key: str,
214
+ request_count: int,
215
+ ) -> dict[str, object]:
216
+ if provider_usage is None:
217
+ usage: dict[str, object] = {
218
+ "input_tokens": input_tokens,
219
+ "output_tokens": max(1, len(summary) // 4),
220
+ }
221
+ else:
222
+ usage = _integer_usage(provider_usage)
223
+ usage.setdefault("input_tokens", input_tokens)
224
+ usage.setdefault("output_tokens", max(1, len(summary) // 4))
225
+ usage["server_tool_use"] = {usage_key: request_count}
226
+ return usage
227
+
228
+
229
+ def _integer_usage(usage: Mapping[str, object]) -> dict[str, object]:
230
+ return {
231
+ key: value
232
+ for key, value in usage.items()
233
+ if isinstance(value, int) and not isinstance(value, bool)
234
+ }
@@ -0,0 +1,9 @@
1
+ """Anthropic server-tool block types shared by decoders and formatters."""
2
+
3
+ from typing import Final
4
+
5
+ SERVER_TOOL_USE: Final = "server_tool_use"
6
+ WEB_SEARCH_TOOL_RESULT: Final = "web_search_tool_result"
7
+ WEB_FETCH_TOOL_RESULT: Final = "web_fetch_tool_result"
8
+ WEB_SEARCH_TOOL_RESULT_ERROR: Final = "web_search_tool_result_error"
9
+ WEB_FETCH_TOOL_ERROR: Final = "web_fetch_tool_error"