ruby_llm 2.0.0 → 2.1.0

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 (284) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +65 -8
  3. data/lib/generators/ruby_llm/generator_helpers.rb +68 -6
  4. data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +1 -0
  5. data/lib/generators/ruby_llm/install/templates/create_ruby_llm_records_migration.rb.tt +36 -3
  6. data/lib/generators/ruby_llm/provider/scaffold.rb +32 -22
  7. data/lib/generators/ruby_llm/provider/templates/gem/ci.yml.erb +1 -5
  8. data/lib/generators/ruby_llm/provider/templates/gem/gemfile.erb +1 -1
  9. data/lib/generators/ruby_llm/provider/templates/gem/gemspec.erb +1 -1
  10. data/lib/generators/ruby_llm/provider/templates/gem/rubocop.yml.erb +1 -1
  11. data/lib/generators/ruby_llm/upgrade/templates/upgrade_ruby_llm_to_2_1.rb.tt +78 -0
  12. data/lib/generators/ruby_llm/upgrade/upgrade_generator.rb +6 -163
  13. data/lib/ruby_llm/accounting/usage.rb +86 -49
  14. data/lib/ruby_llm/active_record/acts_as.rb +3 -0
  15. data/lib/ruby_llm/active_record/attachment_helpers.rb +50 -25
  16. data/lib/ruby_llm/active_record/batch.rb +1 -1
  17. data/lib/ruby_llm/active_record/chat_methods.rb +136 -40
  18. data/lib/ruby_llm/active_record/mcp_credential.rb +41 -0
  19. data/lib/ruby_llm/active_record/message_methods.rb +32 -7
  20. data/lib/ruby_llm/active_record/model.rb +1 -1
  21. data/lib/ruby_llm/active_record/provider_file.rb +64 -0
  22. data/lib/ruby_llm/active_record/record.rb +19 -0
  23. data/lib/ruby_llm/active_record/tool_call.rb +1 -1
  24. data/lib/ruby_llm/active_record/usage.rb +53 -3
  25. data/lib/ruby_llm/agent.rb +198 -33
  26. data/lib/ruby_llm/aliases.json +66 -8
  27. data/lib/ruby_llm/attachment.rb +54 -10
  28. data/lib/ruby_llm/batch.rb +26 -3
  29. data/lib/ruby_llm/chat/tool_concurrency.rb +47 -23
  30. data/lib/ruby_llm/chat/tool_search.rb +26 -0
  31. data/lib/ruby_llm/chat.rb +488 -82
  32. data/lib/ruby_llm/choice.rb +38 -0
  33. data/lib/ruby_llm/configuration.rb +36 -11
  34. data/lib/ruby_llm/context.rb +45 -32
  35. data/lib/ruby_llm/cost.rb +42 -1
  36. data/lib/ruby_llm/embedding.rb +9 -2
  37. data/lib/ruby_llm/error.rb +34 -2
  38. data/lib/ruby_llm/evaluation/assertions.rb +40 -0
  39. data/lib/ruby_llm/evaluation/case.rb +52 -0
  40. data/lib/ruby_llm/evaluation/dataset.rb +65 -0
  41. data/lib/ruby_llm/evaluation/evaluator.rb +126 -0
  42. data/lib/ruby_llm/evaluation/evidence.rb +125 -0
  43. data/lib/ruby_llm/evaluation/minitest.rb +26 -0
  44. data/lib/ruby_llm/evaluation/report.rb +103 -0
  45. data/lib/ruby_llm/evaluation/result.rb +75 -0
  46. data/lib/ruby_llm/evaluation/reviewer.rb +20 -0
  47. data/lib/ruby_llm/evaluation/rspec.rb +23 -0
  48. data/lib/ruby_llm/evaluation/runner.rb +93 -0
  49. data/lib/ruby_llm/evaluation/trial.rb +101 -0
  50. data/lib/ruby_llm/evaluation.rb +289 -0
  51. data/lib/ruby_llm/image.rb +12 -4
  52. data/lib/ruby_llm/judge/builder.rb +51 -0
  53. data/lib/ruby_llm/judge/data.rb +43 -0
  54. data/lib/ruby_llm/judge/question.rb +101 -0
  55. data/lib/ruby_llm/judge.rb +179 -0
  56. data/lib/ruby_llm/judgment.rb +125 -0
  57. data/lib/ruby_llm/mcp/apps.rb +25 -0
  58. data/lib/ruby_llm/mcp/client.rb +213 -0
  59. data/lib/ruby_llm/mcp/collection.rb +56 -0
  60. data/lib/ruby_llm/mcp/content.rb +44 -0
  61. data/lib/ruby_llm/mcp/error.rb +27 -0
  62. data/lib/ruby_llm/mcp/http.rb +379 -0
  63. data/lib/ruby_llm/mcp/input_request.rb +118 -0
  64. data/lib/ruby_llm/mcp/input_required_error.rb +29 -0
  65. data/lib/ruby_llm/mcp/listener.rb +168 -0
  66. data/lib/ruby_llm/mcp/oauth/key.rb +78 -0
  67. data/lib/ruby_llm/mcp/oauth/memory_store.rb +28 -0
  68. data/lib/ruby_llm/mcp/oauth/proofs.rb +39 -0
  69. data/lib/ruby_llm/mcp/oauth.rb +653 -0
  70. data/lib/ruby_llm/mcp/param_headers.rb +83 -0
  71. data/lib/ruby_llm/mcp/prompt.rb +71 -0
  72. data/lib/ruby_llm/mcp/resource.rb +84 -0
  73. data/lib/ruby_llm/mcp/resource_template.rb +99 -0
  74. data/lib/ruby_llm/mcp/result.rb +76 -0
  75. data/lib/ruby_llm/mcp/session_expired.rb +10 -0
  76. data/lib/ruby_llm/mcp/stdio.rb +250 -0
  77. data/lib/ruby_llm/mcp/task.rb +151 -0
  78. data/lib/ruby_llm/mcp/tool.rb +130 -0
  79. data/lib/ruby_llm/mcp.rb +1101 -0
  80. data/lib/ruby_llm/message.rb +41 -6
  81. data/lib/ruby_llm/model.rb +2 -1
  82. data/lib/ruby_llm/models/aliases.rb +5 -2
  83. data/lib/ruby_llm/models/lookup.rb +80 -0
  84. data/lib/ruby_llm/models/registry.rb +2 -2
  85. data/lib/ruby_llm/models/schema.rb +1 -1
  86. data/lib/ruby_llm/models.json +23082 -10158
  87. data/lib/ruby_llm/models.rb +20 -69
  88. data/lib/ruby_llm/moderation.rb +7 -2
  89. data/lib/ruby_llm/ocr.rb +7 -2
  90. data/lib/ruby_llm/open_telemetry/attributes.rb +111 -0
  91. data/lib/ruby_llm/open_telemetry.rb +82 -0
  92. data/lib/ruby_llm/probability.rb +30 -0
  93. data/lib/ruby_llm/progress.rb +44 -0
  94. data/lib/ruby_llm/prompt/context.rb +55 -0
  95. data/lib/ruby_llm/prompt.rb +1 -1
  96. data/lib/ruby_llm/protocol/binary_streaming.rb +1 -1
  97. data/lib/ruby_llm/protocol/request_shapes/analysis.rb +190 -0
  98. data/lib/ruby_llm/protocol/request_shapes.rb +183 -0
  99. data/lib/ruby_llm/protocol/stored_uploads.rb +62 -0
  100. data/lib/ruby_llm/protocol/stream_accumulator.rb +9 -25
  101. data/lib/ruby_llm/protocol/streaming.rb +38 -31
  102. data/lib/ruby_llm/protocol.rb +192 -40
  103. data/lib/ruby_llm/protocols/anthropic/chat.rb +39 -32
  104. data/lib/ruby_llm/protocols/anthropic/media.rb +6 -2
  105. data/lib/ruby_llm/protocols/anthropic/models.rb +16 -1
  106. data/lib/ruby_llm/protocols/anthropic/request_shapes.rb +70 -0
  107. data/lib/ruby_llm/protocols/anthropic/streaming.rb +23 -9
  108. data/lib/ruby_llm/protocols/anthropic/tools.rb +43 -0
  109. data/lib/ruby_llm/protocols/anthropic.rb +20 -1
  110. data/lib/ruby_llm/protocols/bedrock/async_videos.rb +9 -17
  111. data/lib/ruby_llm/protocols/bedrock/guardrails.rb +8 -9
  112. data/lib/ruby_llm/protocols/bedrock/rerank.rb +1 -17
  113. data/lib/ruby_llm/protocols/chat_completions/chat.rb +5 -7
  114. data/lib/ruby_llm/protocols/chat_completions/media.rb +34 -8
  115. data/lib/ruby_llm/protocols/chat_completions/request_shapes.rb +121 -0
  116. data/lib/ruby_llm/protocols/chat_completions/streaming.rb +3 -0
  117. data/lib/ruby_llm/protocols/chat_completions.rb +1 -0
  118. data/lib/ruby_llm/protocols/cohere/batch_requests.rb +0 -5
  119. data/lib/ruby_llm/protocols/cohere/batches.rb +3 -7
  120. data/lib/ruby_llm/protocols/cohere/chat.rb +8 -0
  121. data/lib/ruby_llm/protocols/cohere/embeddings.rb +7 -4
  122. data/lib/ruby_llm/protocols/cohere/media.rb +6 -2
  123. data/lib/ruby_llm/protocols/cohere/ocr.rb +1 -1
  124. data/lib/ruby_llm/protocols/cohere/streaming.rb +1 -0
  125. data/lib/ruby_llm/protocols/cohere/tools.rb +5 -1
  126. data/lib/ruby_llm/protocols/cohere.rb +1 -0
  127. data/lib/ruby_llm/protocols/converse/chat.rb +22 -131
  128. data/lib/ruby_llm/protocols/converse/media.rb +16 -2
  129. data/lib/ruby_llm/protocols/converse/reasoning.rb +194 -0
  130. data/lib/ruby_llm/protocols/converse/request_shapes.rb +99 -0
  131. data/lib/ruby_llm/protocols/converse/streaming.rb +2 -2
  132. data/lib/ruby_llm/protocols/converse/thinking_stream.rb +2 -1
  133. data/lib/ruby_llm/protocols/converse.rb +3 -5
  134. data/lib/ruby_llm/protocols/deepseek/files.rb +2 -9
  135. data/lib/ruby_llm/protocols/elevenlabs/flows/images.rb +1 -7
  136. data/lib/ruby_llm/protocols/elevenlabs/flows/videos.rb +11 -17
  137. data/lib/ruby_llm/protocols/elevenlabs/streaming_transcription.rb +1 -4
  138. data/lib/ruby_llm/protocols/gemini/chat.rb +50 -21
  139. data/lib/ruby_llm/protocols/gemini/embeddings.rb +2 -2
  140. data/lib/ruby_llm/protocols/gemini/files.rb +1 -1
  141. data/lib/ruby_llm/protocols/gemini/images.rb +5 -1
  142. data/lib/ruby_llm/protocols/gemini/media.rb +15 -3
  143. data/lib/ruby_llm/protocols/gemini/request_shapes.rb +111 -0
  144. data/lib/ruby_llm/protocols/gemini/streaming.rb +2 -1
  145. data/lib/ruby_llm/protocols/gemini/tools.rb +17 -11
  146. data/lib/ruby_llm/protocols/gemini/transcription.rb +13 -0
  147. data/lib/ruby_llm/protocols/gemini/videos.rb +7 -2
  148. data/lib/ruby_llm/protocols/gemini.rb +1 -0
  149. data/lib/ruby_llm/protocols/gpustack/videos.rb +7 -9
  150. data/lib/ruby_llm/protocols/interactions/chat.rb +46 -18
  151. data/lib/ruby_llm/protocols/interactions/content.rb +9 -2
  152. data/lib/ruby_llm/protocols/interactions/request_shapes.rb +62 -0
  153. data/lib/ruby_llm/protocols/interactions/streaming.rb +15 -0
  154. data/lib/ruby_llm/protocols/interactions/tools.rb +28 -4
  155. data/lib/ruby_llm/protocols/interactions/transcription.rb +1 -11
  156. data/lib/ruby_llm/protocols/interactions.rb +1 -0
  157. data/lib/ruby_llm/protocols/invoke_model/cohere_embeddings.rb +2 -11
  158. data/lib/ruby_llm/protocols/invoke_model/embedding_batches.rb +2 -2
  159. data/lib/ruby_llm/protocols/invoke_model/stability_images.rb +2 -6
  160. data/lib/ruby_llm/protocols/invoke_model/titan_text_embeddings.rb +2 -8
  161. data/lib/ruby_llm/protocols/invoke_model.rb +1 -4
  162. data/lib/ruby_llm/protocols/mistral/content.rb +13 -0
  163. data/lib/ruby_llm/protocols/mistral/conversations/chat.rb +11 -1
  164. data/lib/ruby_llm/protocols/mistral/conversations/images.rb +5 -1
  165. data/lib/ruby_llm/protocols/mistral/conversations/request_shapes.rb +45 -0
  166. data/lib/ruby_llm/protocols/mistral/conversations.rb +1 -0
  167. data/lib/ruby_llm/protocols/mistral/multi_completion.rb +11 -1
  168. data/lib/ruby_llm/protocols/openai/decisions.rb +135 -0
  169. data/lib/ruby_llm/protocols/openai/files.rb +0 -7
  170. data/lib/ruby_llm/protocols/openrouter/batches.rb +13 -8
  171. data/lib/ruby_llm/protocols/openrouter/responses.rb +5 -1
  172. data/lib/ruby_llm/protocols/perplexity/agent.rb +116 -0
  173. data/lib/ruby_llm/protocols/perplexity/router.rb +2 -43
  174. data/lib/ruby_llm/protocols/responses/chat.rb +56 -22
  175. data/lib/ruby_llm/protocols/responses/media.rb +11 -7
  176. data/lib/ruby_llm/protocols/responses/request_shapes.rb +70 -0
  177. data/lib/ruby_llm/protocols/responses/streaming.rb +23 -7
  178. data/lib/ruby_llm/protocols/responses/token_counting.rb +2 -2
  179. data/lib/ruby_llm/protocols/responses/tools.rb +27 -0
  180. data/lib/ruby_llm/protocols/responses.rb +1 -0
  181. data/lib/ruby_llm/protocols/system_one/judgments.rb +42 -0
  182. data/lib/ruby_llm/protocols/system_one/models.rb +27 -0
  183. data/lib/ruby_llm/protocols/system_one/responses.rb +55 -0
  184. data/lib/ruby_llm/protocols/system_one.rb +27 -0
  185. data/lib/ruby_llm/protocols/vertexai/embedding_prediction/requests.rb +2 -2
  186. data/lib/ruby_llm/protocols/vertexai/files.rb +34 -6
  187. data/lib/ruby_llm/protocols/vertexai/ranking.rb +9 -30
  188. data/lib/ruby_llm/protocols/vertexai/research.rb +9 -41
  189. data/lib/ruby_llm/protocols/xai/streaming_transcription.rb +2 -5
  190. data/lib/ruby_llm/provider.rb +87 -15
  191. data/lib/ruby_llm/providers/anthropic.rb +23 -0
  192. data/lib/ruby_llm/providers/azure/chat.rb +4 -0
  193. data/lib/ruby_llm/providers/azure/images.rb +2 -2
  194. data/lib/ruby_llm/providers/azure/media.rb +8 -3
  195. data/lib/ruby_llm/providers/azure/videos.rb +7 -2
  196. data/lib/ruby_llm/providers/azure.rb +24 -4
  197. data/lib/ruby_llm/providers/bedrock/auth.rb +1 -1
  198. data/lib/ruby_llm/providers/bedrock/mantle/anthropic.rb +1 -1
  199. data/lib/ruby_llm/providers/bedrock/mantle/voxtral.rb +1 -3
  200. data/lib/ruby_llm/providers/bedrock/mantle.rb +5 -9
  201. data/lib/ruby_llm/providers/bedrock.rb +38 -6
  202. data/lib/ruby_llm/providers/deepseek/chat.rb +6 -1
  203. data/lib/ruby_llm/providers/deepseek/responses.rb +6 -0
  204. data/lib/ruby_llm/providers/deepseek.rb +4 -0
  205. data/lib/ruby_llm/providers/gemini.rb +4 -0
  206. data/lib/ruby_llm/providers/gpustack/media.rb +8 -5
  207. data/lib/ruby_llm/providers/hetzner/chat.rb +39 -0
  208. data/lib/ruby_llm/providers/hetzner.rb +41 -0
  209. data/lib/ruby_llm/providers/mistral/chat.rb +16 -12
  210. data/lib/ruby_llm/providers/mistral/media.rb +8 -4
  211. data/lib/ruby_llm/providers/mistral/ocr.rb +1 -5
  212. data/lib/ruby_llm/providers/ollama/judgments.rb +26 -0
  213. data/lib/ruby_llm/providers/ollama/media.rb +6 -2
  214. data/lib/ruby_llm/providers/ollama/models.rb +2 -0
  215. data/lib/ruby_llm/providers/ollama.rb +11 -0
  216. data/lib/ruby_llm/providers/openai/capabilities.rb +3 -1
  217. data/lib/ruby_llm/providers/openai.rb +14 -0
  218. data/lib/ruby_llm/providers/openrouter/chat.rb +20 -24
  219. data/lib/ruby_llm/providers/openrouter/embeddings.rb +2 -0
  220. data/lib/ruby_llm/providers/openrouter/media.rb +5 -1
  221. data/lib/ruby_llm/providers/openrouter/streaming.rb +4 -1
  222. data/lib/ruby_llm/providers/openrouter/videos.rb +10 -5
  223. data/lib/ruby_llm/providers/openrouter.rb +4 -0
  224. data/lib/ruby_llm/providers/perplexity/chat.rb +4 -0
  225. data/lib/ruby_llm/providers/perplexity/media.rb +5 -11
  226. data/lib/ruby_llm/providers/perplexity/models.rb +8 -4
  227. data/lib/ruby_llm/providers/perplexity.rb +11 -0
  228. data/lib/ruby_llm/providers/typesafe.rb +32 -0
  229. data/lib/ruby_llm/providers/vertexai/credentials.rb +63 -0
  230. data/lib/ruby_llm/providers/vertexai/gemini.rb +2 -2
  231. data/lib/ruby_llm/providers/vertexai/transcription.rb +1 -0
  232. data/lib/ruby_llm/providers/vertexai.rb +27 -28
  233. data/lib/ruby_llm/providers/xai/chat.rb +6 -0
  234. data/lib/ruby_llm/providers/xai/images.rb +9 -10
  235. data/lib/ruby_llm/providers/xai/responses.rb +10 -8
  236. data/lib/ruby_llm/providers/xai/videos.rb +11 -9
  237. data/lib/ruby_llm/providers/xai.rb +4 -0
  238. data/lib/ruby_llm/railtie.rb +11 -0
  239. data/lib/ruby_llm/request_shape/part.rb +117 -0
  240. data/lib/ruby_llm/request_shape/problem.rb +63 -0
  241. data/lib/ruby_llm/request_shape/tool_round.rb +64 -0
  242. data/lib/ruby_llm/request_shape/turn.rb +54 -0
  243. data/lib/ruby_llm/request_shape.rb +160 -0
  244. data/lib/ruby_llm/rerank.rb +9 -2
  245. data/lib/ruby_llm/research_job.rb +21 -5
  246. data/lib/ruby_llm/score.rb +42 -0
  247. data/lib/ruby_llm/search_results.rb +2 -2
  248. data/lib/ruby_llm/server_tool_call.rb +10 -2
  249. data/lib/ruby_llm/speech.rb +8 -1
  250. data/lib/ruby_llm/support/cancellation.rb +39 -0
  251. data/lib/ruby_llm/support/inspectable.rb +3 -2
  252. data/lib/ruby_llm/support/instrumentation.rb +38 -1
  253. data/lib/ruby_llm/support/process_cache.rb +60 -0
  254. data/lib/ruby_llm/support/progress_reporter.rb +33 -0
  255. data/lib/ruby_llm/thinking.rb +2 -1
  256. data/lib/ruby_llm/tokens.rb +57 -14
  257. data/lib/ruby_llm/tool.rb +50 -3
  258. data/lib/ruby_llm/tool_call.rb +17 -2
  259. data/lib/ruby_llm/transcription.rb +10 -3
  260. data/lib/ruby_llm/transport/connection.rb +127 -44
  261. data/lib/ruby_llm/transport/error_middleware.rb +90 -24
  262. data/lib/ruby_llm/transport/event_stream_parser.rb +121 -0
  263. data/lib/ruby_llm/transport/json_response.rb +33 -0
  264. data/lib/ruby_llm/transport/signing_middleware.rb +29 -0
  265. data/lib/ruby_llm/version.rb +1 -1
  266. data/lib/ruby_llm/video.rb +5 -3
  267. data/lib/ruby_llm/video_job.rb +48 -5
  268. data/lib/ruby_llm.rb +106 -5
  269. data/lib/tasks/ruby_llm/evaluations.rb +43 -0
  270. data/lib/tasks/ruby_llm.rake +6 -9
  271. data/skills/rubyllm/SKILL.md +33 -12
  272. metadata +95 -37
  273. data/lib/generators/ruby_llm/upgrade/legacy_content_sql.rb +0 -34
  274. data/lib/generators/ruby_llm/upgrade/online_copy_migration/data.rb +0 -341
  275. data/lib/generators/ruby_llm/upgrade/online_copy_migration/journal.rb +0 -171
  276. data/lib/generators/ruby_llm/upgrade/online_copy_migration/verification.rb +0 -197
  277. data/lib/generators/ruby_llm/upgrade/online_copy_migration.rb +0 -174
  278. data/lib/generators/ruby_llm/upgrade/templates/backfill_v2_data.rb.tt +0 -468
  279. data/lib/generators/ruby_llm/upgrade/templates/cleanup_v2_upgrade.rb.tt +0 -101
  280. data/lib/generators/ruby_llm/upgrade/templates/finish_v2_upgrade.rb.tt +0 -247
  281. data/lib/generators/ruby_llm/upgrade/templates/prepare_v2_upgrade.rb.tt +0 -662
  282. data/lib/generators/ruby_llm/upgrade/templates/ruby_llm_upgrade.rb.tt +0 -251
  283. data/lib/generators/ruby_llm/upgrade/templates/upgrade_initializer.rb.tt +0 -13
  284. data/lib/generators/ruby_llm/upgrade/upgrade_migration.rb +0 -359
@@ -21,8 +21,10 @@ module RubyLLM
21
21
  # that class instead.
22
22
  #
23
23
  # Configuration that depends on runtime state goes in blocks or lambdas.
24
- # They are evaluated when a chat is built, with +chat+ and any declared
25
- # ::inputs available as methods:
24
+ # Most blocks run after a chat is built, with +chat+ and any declared
25
+ # ::inputs available as methods. The +model+ block runs before a chat
26
+ # exists. A deferred +context+ block also runs before Chat construction in
27
+ # plain Ruby, but receives the Rails chat record while Rails configures it:
26
28
  #
27
29
  # class WorkAssistant < RubyLLM::Agent
28
30
  # inputs :workspace
@@ -44,6 +46,7 @@ module RubyLLM
44
46
  DUPED_INHERITED_CONFIG = {
45
47
  :@chat_kwargs => {},
46
48
  :@tools => [],
49
+ :@mcp => [],
47
50
  :@provider_tools => [],
48
51
  :@tool_options => {},
49
52
  :@caching => nil,
@@ -59,6 +62,8 @@ module RubyLLM
59
62
  # agent forwards to the matching Chat#with_* when it builds its chat.
60
63
  PASSTHROUGH_OPTIONS = %i[temperature max_output_tokens].freeze
61
64
  THINKING_OPTIONS = %i[effort budget display].freeze
65
+ UNRESOLVED_CONTEXT = Object.new.freeze
66
+ private_constant :UNRESOLVED_CONTEXT
62
67
 
63
68
  # The chat operations an agent instance runs through its ::rescue_from
64
69
  # handlers. Remaining delegated methods pass through untouched.
@@ -66,21 +71,22 @@ module RubyLLM
66
71
 
67
72
  # Chat methods that return the wrapped chat so calls can be chained there.
68
73
  CHAINABLE_CHAT_DELEGATES = %i[
69
- with_instructions with_tools with_provider_tools with_tool_options with_model
74
+ with_instructions with_tools with_mcp with_provider_tools with_tool_options with_model
70
75
  with_temperature with_max_output_tokens with_thinking with_citations
71
76
  with_end_user with_compaction with_caching with_context with_provider_options
72
77
  with_headers with_schema with_fallbacks
73
- before_request before_message after_message before_tool_call after_tool_result
78
+ before_request before_message after_message before_tool_call after_tool_result after_tool_progress
74
79
  before_fallback after_fallback
75
- cancel approve deny cache_until_here
80
+ convert_unsupported_attachments
81
+ cancel approve deny answer decline cache_until_here
76
82
  ].freeze
77
83
 
78
84
  # Chat values and operations whose return values pass through unchanged.
79
85
  PASSTHROUGH_CHAT_DELEGATES = %i[
80
- model provider messages tools provider_tools tool_options provider_options headers schema concurrency
81
- caching citations compaction context end_user fallbacks thinking temperature max_output_tokens
82
- each complete? cancelled? awaiting_approval? pending_approvals
83
- add_message add_completion tokens cost render
86
+ model provider messages tools deferred_tools mcp provider_tools tool_options provider_options headers schema
87
+ concurrency caching citations compaction context end_user fallbacks thinking temperature max_output_tokens
88
+ each complete? cancelled? waiting? awaiting_approval? pending_approvals awaiting_input? pending_inputs
89
+ awaiting_tasks? pending_tasks add_message add_completion tokens cost render
84
90
  ].freeze
85
91
 
86
92
  COPIED_INHERITED_CONFIG = (%i[
@@ -90,6 +96,8 @@ module RubyLLM
90
96
  @schema
91
97
  @context
92
98
  @chat_model
99
+ @tools_defer
100
+ @mcp_defer
93
101
  ] + PASSTHROUGH_OPTIONS.map { |option| :"@#{option}" }).freeze
94
102
  private_constant :DUPED_INHERITED_CONFIG, :COPIED_INHERITED_CONFIG,
95
103
  :PASSTHROUGH_OPTIONS, :THINKING_OPTIONS, :GUARDED_OPERATIONS,
@@ -130,12 +138,36 @@ module RubyLLM
130
138
  # tools SearchDocs, LookupAccount
131
139
  # tools { [TodoTool.new(chat: chat)] }
132
140
  #
133
- def tools(*tools, &block)
141
+ # Pass +defer:+ as Chat#with_tools takes it:
142
+ #
143
+ # tools SearchDocs, LookupAccount, defer: true
144
+ #
145
+ def tools(*tools, defer: nil, &block)
134
146
  return @tools || [] if tools.empty? && !block_given?
135
147
 
148
+ @tools_defer = defer
136
149
  @tools = block_given? ? block : tools.flatten
137
150
  end
138
151
 
152
+ # Connects MCP servers to chats this agent builds, applied via
153
+ # Chat#with_mcp. A block defers construction until the chat is built,
154
+ # with the declared ::inputs available. Called with no arguments,
155
+ # returns the declared servers.
156
+ #
157
+ # mcp Files
158
+ # mcp { [Linear.new(user: user), Files] }
159
+ #
160
+ # Pass +defer:+ as Chat#with_mcp takes it:
161
+ #
162
+ # mcp GitHub, defer: true
163
+ #
164
+ def mcp(*servers, defer: nil, &block)
165
+ return @mcp || [] if servers.empty? && !block_given?
166
+
167
+ @mcp_defer = defer
168
+ @mcp = block_given? ? block : servers.flatten
169
+ end
170
+
139
171
  # Sets how chats this agent builds use their tools, applied via
140
172
  # Chat#with_tool_options. Accepts +choice:+, +calls:+, and
141
173
  # +concurrency:+. A block defers evaluation until the chat is built.
@@ -363,12 +395,29 @@ module RubyLLM
363
395
  private :fallback_options
364
396
 
365
397
  # Sets a Context whose configuration chats this agent builds should
366
- # use, applied via Chat#with_context. Called with no argument, returns
367
- # the configured context.
368
- def context(value = nil)
369
- return @context if value.nil?
398
+ # use, applied via Chat#with_context. A block taking a configuration
399
+ # argument creates an isolated Context when the agent class is defined.
400
+ # A zero-argument block or Proc value defers evaluation until a chat is
401
+ # built or configured, with declared ::inputs available as methods. In
402
+ # plain Ruby, +chat+ is nil; in Rails mode, +chat+ is the record being
403
+ # configured. Called with no argument or block, returns the configured
404
+ # context or deferred Proc.
405
+ #
406
+ # context SharedContext
407
+ # context { |config| config.request_timeout = 180 }
408
+ # context -> { RubyLLM.context { |config| config.openai_api_key = api_key } }
409
+ def context(value = nil, &block)
410
+ raise ArgumentError, 'Pass a context or a block, not both' if value && block
411
+
412
+ validate_context_proc(value)
413
+
414
+ return @context if value.nil? && !block
370
415
 
371
- @context = value
416
+ @context = if block
417
+ block.arity.zero? ? block : RubyLLM.context(&block)
418
+ else
419
+ value
420
+ end
372
421
  end
373
422
 
374
423
  # Sets the ActiveRecord chat class this agent creates and finds,
@@ -453,6 +502,15 @@ module RubyLLM
453
502
  @chat_kwargs || {}
454
503
  end
455
504
 
505
+ # Resolves a deferred context before plain-Ruby chat construction;
506
+ # +chat+ is nil in the evaluation runtime.
507
+ def resolved_context(inputs:) # :nodoc:
508
+ value = context
509
+ return value unless value.is_a?(Proc)
510
+
511
+ evaluate(value, runtime_context(chat: nil, inputs:))
512
+ end
513
+
456
514
  def resolved_chat_kwargs(inputs: {}) # :nodoc:
457
515
  kwargs = chat_kwargs
458
516
  return kwargs unless kwargs[:model].is_a?(Proc)
@@ -460,8 +518,9 @@ module RubyLLM
460
518
  kwargs.merge(model: evaluate(kwargs[:model], runtime_context(chat: nil, inputs: inputs)))
461
519
  end
462
520
 
463
- def build_chat(inputs:, options:) # :nodoc:
464
- (context || RubyLLM).chat(**resolved_chat_kwargs(inputs:), **options)
521
+ def build_chat(inputs:, options:, resolved_context: UNRESOLVED_CONTEXT) # :nodoc:
522
+ context_value = resolved_context.equal?(UNRESOLVED_CONTEXT) ? self.resolved_context(inputs:) : resolved_context
523
+ (context_value || RubyLLM).chat(**resolved_chat_kwargs(inputs:), **options)
465
524
  end
466
525
 
467
526
  # Builds a Chat configured with this agent's declarations and returns
@@ -473,8 +532,9 @@ module RubyLLM
473
532
  #
474
533
  def chat(**kwargs)
475
534
  input_values, chat_options = partition_inputs(kwargs)
476
- chat = build_chat(inputs: input_values, options: chat_options)
477
- apply_configuration(chat, input_values:, persist_instructions: true)
535
+ context_value = resolved_context(inputs: input_values)
536
+ chat = build_chat(inputs: input_values, options: chat_options, resolved_context: context_value)
537
+ apply_configuration(chat, input_values:, persist_instructions: true, resolved_context: context_value)
478
538
  chat
479
539
  end
480
540
 
@@ -485,8 +545,8 @@ module RubyLLM
485
545
  # chat = WorkAssistant.create(user: current_user)
486
546
  #
487
547
  # Raises ArgumentError if ::chat_model is not configured.
488
- def create(**kwargs)
489
- with_rails_chat_record(:create, **kwargs)
548
+ def create(**)
549
+ with_rails_chat_record(:create, **)
490
550
  end
491
551
 
492
552
  # Like ::create, but calls the model's <tt>create!</tt>, raising if
@@ -494,8 +554,8 @@ module RubyLLM
494
554
  #
495
555
  # chat = WorkAssistant.create!(user: current_user)
496
556
  #
497
- def create!(**kwargs)
498
- with_rails_chat_record(:create!, **kwargs)
557
+ def create!(**)
558
+ with_rails_chat_record(:create!, **)
499
559
  end
500
560
 
501
561
  # Finds the ::chat_model record with +id+ and applies this agent's
@@ -530,8 +590,8 @@ module RubyLLM
530
590
  record = chat_or_id.is_a?(resolved_chat_model) ? chat_or_id : resolved_chat_model.find(chat_or_id)
531
591
  apply_assume_model_exists(record)
532
592
  apply_protocol(record)
533
- apply_context(record)
534
593
  runtime = runtime_context(chat: record, inputs: input_values)
594
+ apply_context(record, runtime)
535
595
  apply_instructions(
536
596
  record,
537
597
  runtime,
@@ -563,10 +623,15 @@ module RubyLLM
563
623
  [input_values, chat_options]
564
624
  end
565
625
 
566
- def apply_configuration(chat, input_values:, persist_instructions:) # :nodoc:
626
+ def apply_configuration( # :nodoc:
627
+ chat,
628
+ input_values:,
629
+ persist_instructions:,
630
+ resolved_context: UNRESOLVED_CONTEXT
631
+ )
567
632
  runtime = runtime_context(chat:, inputs: input_values)
568
633
  apply_chat_options(chat)
569
- apply_context(chat)
634
+ apply_context(chat, runtime, resolved_context:)
570
635
  apply_instructions(chat, runtime, inputs: input_values, persist: persist_instructions)
571
636
  apply_tools(chat, runtime)
572
637
  apply_passthrough_options(chat)
@@ -583,6 +648,13 @@ module RubyLLM
583
648
 
584
649
  private
585
650
 
651
+ def validate_context_proc(value)
652
+ return unless value.is_a?(Proc)
653
+ return if value.arity.zero?
654
+
655
+ raise ArgumentError, 'context Proc must accept zero arguments'
656
+ end
657
+
586
658
  def validate_thinking_options(enabled, options)
587
659
  raise ArgumentError, 'thinking accepts false or thinking options' unless [true, false].include?(enabled)
588
660
  raise ArgumentError, 'thinking false does not accept options' if !enabled && options.any?
@@ -634,8 +706,9 @@ module RubyLLM
634
706
  record
635
707
  end
636
708
 
637
- def apply_context(chat)
638
- chat.with_context(context) if context
709
+ def apply_context(chat, runtime, resolved_context: UNRESOLVED_CONTEXT)
710
+ value = resolved_context.equal?(UNRESOLVED_CONTEXT) ? evaluate(context, runtime) : resolved_context
711
+ chat.with_context(value) if value && !chat.context.equal?(value)
639
712
  end
640
713
 
641
714
  def apply_instructions(chat, runtime, inputs:, persist:, persistent_only: false)
@@ -662,7 +735,10 @@ module RubyLLM
662
735
 
663
736
  def apply_tools(chat, runtime)
664
737
  tools_to_apply = Array(evaluate(tools, runtime)).compact
665
- chat.with_tools(*tools_to_apply) if tools_to_apply.any?
738
+ chat.with_tools(*tools_to_apply, defer: @tools_defer) if tools_to_apply.any?
739
+
740
+ servers = Array(evaluate(mcp, runtime)).compact
741
+ chat.with_mcp(*servers, defer: @mcp_defer) if servers.any?
666
742
 
667
743
  options = evaluate(tool_options, runtime)
668
744
  chat.with_tool_options(**options) if options && !options.empty?
@@ -849,8 +925,25 @@ module RubyLLM
849
925
  def initialize(chat: nil, inputs: nil, persist_instructions: true, **kwargs)
850
926
  input_values, chat_options = self.class.partition_inputs(kwargs)
851
927
  input_values = input_values.merge(inputs || {})
852
- @chat = chat || self.class.build_chat(inputs: input_values, options: chat_options)
853
- self.class.apply_configuration(@chat, input_values:, persist_instructions:)
928
+
929
+ if chat
930
+ @chat = chat
931
+ self.class.apply_configuration(@chat, input_values:, persist_instructions:)
932
+ return
933
+ end
934
+
935
+ context_value = self.class.resolved_context(inputs: input_values)
936
+ @chat = self.class.build_chat(
937
+ inputs: input_values,
938
+ options: chat_options,
939
+ resolved_context: context_value
940
+ )
941
+ self.class.apply_configuration(
942
+ @chat,
943
+ input_values:,
944
+ persist_instructions:,
945
+ resolved_context: context_value
946
+ )
854
947
  end
855
948
 
856
949
  # The wrapped Chat, or the chat record in Rails mode.
@@ -868,10 +961,16 @@ module RubyLLM
868
961
 
869
962
  ##
870
963
  # :method: with_tools
871
- # :call-seq: with_tools(*tools)
964
+ # :call-seq: with_tools(*tools, defer: nil)
872
965
  #
873
966
  # Delegates to Chat#with_tools. See that method for arguments and return values.
874
967
 
968
+ ##
969
+ # :method: with_mcp
970
+ # :call-seq: with_mcp(*servers, defer: nil)
971
+ #
972
+ # Delegates to Chat#with_mcp. See that method for arguments and return values.
973
+
875
974
  ##
876
975
  # :method: with_provider_tools
877
976
  # :call-seq: with_provider_tools(*tools, **tools_with_options)
@@ -962,6 +1061,12 @@ module RubyLLM
962
1061
  #
963
1062
  # Delegates to Chat#with_fallbacks. See that method for arguments and return values.
964
1063
 
1064
+ ##
1065
+ # :method: convert_unsupported_attachments
1066
+ # :call-seq: convert_unsupported_attachments(&block)
1067
+ #
1068
+ # Delegates to Chat#convert_unsupported_attachments. See that method for arguments and return values.
1069
+
965
1070
  ##
966
1071
  # :method: before_request
967
1072
  # :call-seq: before_request(&block)
@@ -992,6 +1097,12 @@ module RubyLLM
992
1097
  #
993
1098
  # Delegates to Chat#after_tool_result. See that method for arguments and return values.
994
1099
 
1100
+ ##
1101
+ # :method: after_tool_progress
1102
+ # :call-seq: after_tool_progress(&block)
1103
+ #
1104
+ # Delegates to Chat#after_tool_progress. See that method for arguments and return values.
1105
+
995
1106
  ##
996
1107
  # :method: before_fallback
997
1108
  # :call-seq: before_fallback(&block)
@@ -1022,9 +1133,21 @@ module RubyLLM
1022
1133
  #
1023
1134
  # Delegates to Chat#deny. See that method for arguments and return values.
1024
1135
 
1136
+ ##
1137
+ # :method: answer
1138
+ # :call-seq: answer(request, **values)
1139
+ #
1140
+ # Delegates to Chat#answer. See that method for arguments and return values.
1141
+
1142
+ ##
1143
+ # :method: decline
1144
+ # :call-seq: decline(request)
1145
+ #
1146
+ # Delegates to Chat#decline. See that method for arguments and return values.
1147
+
1025
1148
  ##
1026
1149
  # :method: cache_until_here
1027
- # :call-seq: cache_until_here()
1150
+ # :call-seq: cache_until_here(ttl: nil)
1028
1151
  #
1029
1152
  # Delegates to Chat#cache_until_here. See that method for arguments and return values.
1030
1153
 
@@ -1052,6 +1175,18 @@ module RubyLLM
1052
1175
  #
1053
1176
  # Delegates to Chat#tools. See that method for arguments and return values.
1054
1177
 
1178
+ ##
1179
+ # :method: deferred_tools
1180
+ # :call-seq: deferred_tools
1181
+ #
1182
+ # Delegates to Chat#deferred_tools. See that method for arguments and return values.
1183
+
1184
+ ##
1185
+ # :method: mcp
1186
+ # :call-seq: mcp
1187
+ #
1188
+ # Delegates to Chat#mcp. See that method for arguments and return values.
1189
+
1055
1190
  ##
1056
1191
  # :method: provider_tools
1057
1192
  # :call-seq: provider_tools
@@ -1160,6 +1295,12 @@ module RubyLLM
1160
1295
  #
1161
1296
  # Delegates to Chat#cancelled?. See that method for arguments and return values.
1162
1297
 
1298
+ ##
1299
+ # :method: waiting?
1300
+ # :call-seq: waiting?()
1301
+ #
1302
+ # Delegates to Chat#waiting?. See that method for arguments and return values.
1303
+
1163
1304
  ##
1164
1305
  # :method: awaiting_approval?
1165
1306
  # :call-seq: awaiting_approval?()
@@ -1172,6 +1313,30 @@ module RubyLLM
1172
1313
  #
1173
1314
  # Delegates to Chat#pending_approvals. See that method for arguments and return values.
1174
1315
 
1316
+ ##
1317
+ # :method: awaiting_input?
1318
+ # :call-seq: awaiting_input?()
1319
+ #
1320
+ # Delegates to Chat#awaiting_input?. See that method for arguments and return values.
1321
+
1322
+ ##
1323
+ # :method: pending_inputs
1324
+ # :call-seq: pending_inputs()
1325
+ #
1326
+ # Delegates to Chat#pending_inputs. See that method for arguments and return values.
1327
+
1328
+ ##
1329
+ # :method: awaiting_tasks?
1330
+ # :call-seq: awaiting_tasks?()
1331
+ #
1332
+ # Delegates to Chat#awaiting_tasks?. See that method for arguments and return values.
1333
+
1334
+ ##
1335
+ # :method: pending_tasks
1336
+ # :call-seq: pending_tasks()
1337
+ #
1338
+ # Delegates to Chat#pending_tasks. See that method for arguments and return values.
1339
+
1175
1340
  ##
1176
1341
  # :method: add_message
1177
1342
  # :call-seq: add_message(message_or_attributes)
@@ -10,7 +10,8 @@
10
10
  "anthropic": "claude-fable-5-1",
11
11
  "openrouter": "anthropic/claude-fable-5.1",
12
12
  "bedrock": "anthropic.claude-fable-5-1",
13
- "vertexai": "claude-fable-5-1"
13
+ "vertexai": "claude-fable-5-1",
14
+ "azure": "claude-fable-5-1"
14
15
  },
15
16
  "claude-haiku-4-5": {
16
17
  "anthropic": "claude-haiku-4-5-20251001",
@@ -58,6 +59,13 @@
58
59
  "vertexai": "claude-opus-5",
59
60
  "azure": "claude-opus-5"
60
61
  },
62
+ "claude-opus-5-5": {
63
+ "anthropic": "claude-opus-5-5",
64
+ "openrouter": "anthropic/claude-opus-5.5",
65
+ "bedrock": "anthropic.claude-opus-5-5",
66
+ "vertexai": "claude-opus-5-5",
67
+ "azure": "claude-opus-5-5"
68
+ },
61
69
  "claude-sonnet-4-5": {
62
70
  "anthropic": "claude-sonnet-4-5-20250929",
63
71
  "openrouter": "anthropic/claude-sonnet-4.5",
@@ -79,6 +87,13 @@
79
87
  "vertexai": "claude-sonnet-5",
80
88
  "azure": "claude-sonnet-5"
81
89
  },
90
+ "claude-sonnet-5-5": {
91
+ "anthropic": "claude-sonnet-5-5",
92
+ "openrouter": "anthropic/claude-sonnet-5.5",
93
+ "bedrock": "anthropic.claude-sonnet-5-5",
94
+ "vertexai": "claude-sonnet-5-5",
95
+ "azure": "claude-sonnet-5-5"
96
+ },
82
97
  "deepseek-ocr": {
83
98
  "vertexai": "deepseek-ai/deepseek-ocr-maas"
84
99
  },
@@ -200,6 +215,10 @@
200
215
  "gemini": "gemini-3.5-live-translate-preview",
201
216
  "vertexai": "gemini-3.5-live-translate-preview"
202
217
  },
218
+ "gemini-3.5-transcribe": {
219
+ "gemini": "gemini-3.5-transcribe",
220
+ "openrouter": "google/gemini-3.5-transcribe"
221
+ },
203
222
  "gemini-3.6-flash": {
204
223
  "gemini": "gemini-3.6-flash",
205
224
  "openrouter": "google/gemini-3.6-flash",
@@ -215,6 +234,18 @@
215
234
  "openrouter": "google/gemini-3.8-flash",
216
235
  "vertexai": "gemini-3.8-flash"
217
236
  },
237
+ "gemini-3.8-flash-lite-tts": {
238
+ "gemini": "gemini-3.8-flash-lite-tts",
239
+ "openrouter": "google/gemini-3.8-flash-lite-tts"
240
+ },
241
+ "gemini-3.8-flash-tts": {
242
+ "gemini": "gemini-3.8-flash-tts",
243
+ "openrouter": "google/gemini-3.8-flash-tts"
244
+ },
245
+ "gemini-3.8-live": {
246
+ "gemini": "gemini-3.8-live",
247
+ "vertexai": "gemini-3.8-live"
248
+ },
218
249
  "gemini-embedding-001": {
219
250
  "gemini": "gemini-embedding-001",
220
251
  "openrouter": "google/gemini-embedding-001",
@@ -237,6 +268,11 @@
237
268
  "gemini": "gemini-flash-lite-latest",
238
269
  "vertexai": "gemini-flash-lite-latest"
239
270
  },
271
+ "gemini-nano-banana-2.1": {
272
+ "gemini": "gemini-nano-banana-2.1",
273
+ "openrouter": "google/gemini-nano-banana-2.1",
274
+ "vertexai": "gemini-nano-banana-2.1"
275
+ },
240
276
  "gemini-omni-flash-preview": {
241
277
  "gemini": "gemini-omni-flash-preview",
242
278
  "vertexai": "gemini-omni-flash-preview"
@@ -272,7 +308,8 @@
272
308
  },
273
309
  "gpt-3.5-turbo-instruct": {
274
310
  "openai": "gpt-3.5-turbo-instruct",
275
- "openrouter": "openai/gpt-3.5-turbo-instruct"
311
+ "openrouter": "openai/gpt-3.5-turbo-instruct",
312
+ "azure": "gpt-3.5-turbo-instruct"
276
313
  },
277
314
  "gpt-4": {
278
315
  "openai": "gpt-4",
@@ -280,7 +317,8 @@
280
317
  },
281
318
  "gpt-4-turbo": {
282
319
  "openai": "gpt-4-turbo",
283
- "openrouter": "openai/gpt-4-turbo"
320
+ "openrouter": "openai/gpt-4-turbo",
321
+ "azure": "gpt-4-turbo"
284
322
  },
285
323
  "gpt-4.1": {
286
324
  "openai": "gpt-4.1",
@@ -354,7 +392,8 @@
354
392
  },
355
393
  "gpt-5-pro": {
356
394
  "openai": "gpt-5-pro",
357
- "openrouter": "openai/gpt-5-pro"
395
+ "openrouter": "openai/gpt-5-pro",
396
+ "azure": "gpt-5-pro"
358
397
  },
359
398
  "gpt-5.1": {
360
399
  "openai": "gpt-5.1",
@@ -363,7 +402,8 @@
363
402
  },
364
403
  "gpt-5.1-codex": {
365
404
  "openai": "gpt-5.1-codex",
366
- "openrouter": "openai/gpt-5.1-codex"
405
+ "openrouter": "openai/gpt-5.1-codex",
406
+ "azure": "gpt-5.1-codex"
367
407
  },
368
408
  "gpt-5.1-codex-max": {
369
409
  "openai": "gpt-5.1-codex-max",
@@ -372,7 +412,8 @@
372
412
  },
373
413
  "gpt-5.1-codex-mini": {
374
414
  "openai": "gpt-5.1-codex-mini",
375
- "openrouter": "openai/gpt-5.1-codex-mini"
415
+ "openrouter": "openai/gpt-5.1-codex-mini",
416
+ "azure": "gpt-5.1-codex-mini"
376
417
  },
377
418
  "gpt-5.2": {
378
419
  "openai": "gpt-5.2",
@@ -442,6 +483,21 @@
442
483
  "openrouter": "openai/gpt-6-astra",
443
484
  "azure": "gpt-6-astra"
444
485
  },
486
+ "gpt-6-luna": {
487
+ "openai": "gpt-6-luna",
488
+ "openrouter": "openai/gpt-6-luna",
489
+ "azure": "gpt-6-luna"
490
+ },
491
+ "gpt-6-sol": {
492
+ "openai": "gpt-6-sol",
493
+ "openrouter": "openai/gpt-6-sol",
494
+ "azure": "gpt-6-sol"
495
+ },
496
+ "gpt-6.1-sol": {
497
+ "openai": "gpt-6.1-sol",
498
+ "openrouter": "openai/gpt-6.1-sol",
499
+ "azure": "gpt-6.1-sol"
500
+ },
445
501
  "gpt-audio": {
446
502
  "openai": "gpt-audio",
447
503
  "openrouter": "openai/gpt-audio",
@@ -469,11 +525,13 @@
469
525
  },
470
526
  "gpt-image-2.5-flare": {
471
527
  "openai": "gpt-image-2.5-flare",
472
- "openrouter": "openai/gpt-image-2.5-flare"
528
+ "openrouter": "openai/gpt-image-2.5-flare",
529
+ "azure": "gpt-image-2.5-flare"
473
530
  },
474
531
  "gpt-image-2.5-sunburst": {
475
532
  "openai": "gpt-image-2.5-sunburst",
476
- "openrouter": "openai/gpt-image-2.5-sunburst"
533
+ "openrouter": "openai/gpt-image-2.5-sunburst",
534
+ "azure": "gpt-image-2.5-sunburst"
477
535
  },
478
536
  "gpt-oss-120b": {
479
537
  "vertexai": "openai/gpt-oss-120b-maas",