ruby_llm 1.16.0 → 2.0.0.rc2

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 (475) hide show
  1. checksums.yaml +4 -4
  2. data/.rdoc_options +25 -0
  3. data/README.md +85 -32
  4. data/exe/ruby_llm +8 -0
  5. data/lib/generators/ruby_llm/agent/templates/agent.rb.tt +0 -1
  6. data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +3 -43
  7. data/lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt +11 -3
  8. data/lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt +8 -0
  9. data/lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt +4 -4
  10. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_chat.html.erb.tt +1 -1
  11. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_form.html.erb.tt +1 -1
  12. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/index.html.erb.tt +1 -1
  13. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/show.html.erb.tt +2 -2
  14. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_assistant.html.erb.tt +1 -1
  15. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_system.html.erb.tt +1 -1
  16. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_tool.html.erb.tt +1 -1
  17. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_tool_calls.html.erb.tt +6 -4
  18. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_user.html.erb.tt +1 -1
  19. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/tool_calls/_default.html.erb.tt +2 -2
  20. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/_model.html.erb.tt +5 -6
  21. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/index.html.erb.tt +2 -2
  22. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/show.html.erb.tt +5 -5
  23. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt +1 -1
  24. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt +1 -1
  25. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt +1 -1
  26. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt +2 -2
  27. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_assistant.html.erb.tt +1 -1
  28. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_system.html.erb.tt +1 -1
  29. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool.html.erb.tt +1 -1
  30. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool_calls.html.erb.tt +6 -4
  31. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_user.html.erb.tt +1 -1
  32. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt +4 -6
  33. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/tool_calls/_default.html.erb.tt +2 -2
  34. data/lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt +5 -6
  35. data/lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt +2 -2
  36. data/lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt +3 -3
  37. data/lib/generators/ruby_llm/generator_helpers.rb +106 -62
  38. data/lib/generators/ruby_llm/install/install_generator.rb +3 -11
  39. data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +2 -0
  40. data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +14 -8
  41. data/lib/generators/ruby_llm/install/templates/create_ruby_llm_records_migration.rb.tt +117 -0
  42. data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +0 -8
  43. data/lib/generators/ruby_llm/provider/cli.rb +175 -0
  44. data/lib/generators/ruby_llm/provider/scaffold.rb +323 -0
  45. data/lib/generators/ruby_llm/provider/templates/core/provider.rb.erb +37 -0
  46. data/lib/generators/ruby_llm/provider/templates/core/provider_spec.rb.erb +34 -0
  47. data/lib/generators/ruby_llm/provider/templates/gem/archspec.rb.erb +14 -0
  48. data/lib/generators/ruby_llm/provider/templates/gem/bin/console.erb +15 -0
  49. data/lib/generators/ruby_llm/provider/templates/gem/bin/setup.erb +5 -0
  50. data/lib/generators/ruby_llm/provider/templates/gem/chat_schema_spec.rb.erb +30 -0
  51. data/lib/generators/ruby_llm/provider/templates/gem/chat_spec.rb.erb +35 -0
  52. data/lib/generators/ruby_llm/provider/templates/gem/chat_streaming_spec.rb.erb +22 -0
  53. data/lib/generators/ruby_llm/provider/templates/gem/chat_tools_spec.rb.erb +30 -0
  54. data/lib/generators/ruby_llm/provider/templates/gem/ci.yml.erb +32 -0
  55. data/lib/generators/ruby_llm/provider/templates/gem/embedding_spec.rb.erb +49 -0
  56. data/lib/generators/ruby_llm/provider/templates/gem/env.erb +2 -0
  57. data/lib/generators/ruby_llm/provider/templates/gem/fixtures_gitkeep.erb +1 -0
  58. data/lib/generators/ruby_llm/provider/templates/gem/flayignore.erb +1 -0
  59. data/lib/generators/ruby_llm/provider/templates/gem/gemfile.erb +23 -0
  60. data/lib/generators/ruby_llm/provider/templates/gem/gemspec.erb +28 -0
  61. data/lib/generators/ruby_llm/provider/templates/gem/gitignore.erb +7 -0
  62. data/lib/generators/ruby_llm/provider/templates/gem/gitleaks.yml.erb +22 -0
  63. data/lib/generators/ruby_llm/provider/templates/gem/image_spec.rb.erb +23 -0
  64. data/lib/generators/ruby_llm/provider/templates/gem/license.erb +21 -0
  65. data/lib/generators/ruby_llm/provider/templates/gem/models.rb.erb +19 -0
  66. data/lib/generators/ruby_llm/provider/templates/gem/models_spec.rb.erb +17 -0
  67. data/lib/generators/ruby_llm/provider/templates/gem/moderation_spec.rb.erb +22 -0
  68. data/lib/generators/ruby_llm/provider/templates/gem/overcommit.yml.erb +31 -0
  69. data/lib/generators/ruby_llm/provider/templates/gem/provider.rb.erb +52 -0
  70. data/lib/generators/ruby_llm/provider/templates/gem/provider_spec.rb.erb +38 -0
  71. data/lib/generators/ruby_llm/provider/templates/gem/rakefile.erb +38 -0
  72. data/lib/generators/ruby_llm/provider/templates/gem/readme.md.erb +50 -0
  73. data/lib/generators/ruby_llm/provider/templates/gem/release.yml.erb +36 -0
  74. data/lib/generators/ruby_llm/provider/templates/gem/rerank_spec.rb.erb +23 -0
  75. data/lib/generators/ruby_llm/provider/templates/gem/rspec.erb +2 -0
  76. data/lib/generators/ruby_llm/provider/templates/gem/rubocop.yml.erb +29 -0
  77. data/lib/generators/ruby_llm/provider/templates/gem/rubyllm_configuration.rb.erb +14 -0
  78. data/lib/generators/ruby_llm/provider/templates/gem/spec_helper.rb.erb +26 -0
  79. data/lib/generators/ruby_llm/provider/templates/gem/speech_spec.rb.erb +25 -0
  80. data/lib/generators/ruby_llm/provider/templates/gem/vcr_configuration.rb.erb +16 -0
  81. data/lib/generators/ruby_llm/provider/templates/gem/video_spec.rb.erb +27 -0
  82. data/lib/generators/ruby_llm/schema/schema_generator.rb +5 -1
  83. data/lib/generators/ruby_llm/schema/templates/schema.rb.tt +1 -1
  84. data/lib/generators/ruby_llm/tool/templates/tailwind/tool_call.html.erb.tt +13 -0
  85. data/lib/generators/ruby_llm/tool/templates/tailwind/tool_result.html.erb.tt +21 -0
  86. data/lib/generators/ruby_llm/tool/templates/tool.rb.tt +3 -3
  87. data/lib/generators/ruby_llm/tool/templates/tool_call.html.erb.tt +7 -12
  88. data/lib/generators/ruby_llm/tool/templates/tool_result.html.erb.tt +5 -2
  89. data/lib/generators/ruby_llm/tool/tool_generator.rb +25 -59
  90. data/lib/generators/ruby_llm/upgrade/templates/backfill_v2_data.rb.tt +461 -0
  91. data/lib/generators/ruby_llm/upgrade/templates/cleanup_v2_upgrade.rb.tt +101 -0
  92. data/lib/generators/ruby_llm/upgrade/templates/finish_v2_upgrade.rb.tt +215 -0
  93. data/lib/generators/ruby_llm/upgrade/templates/prepare_v2_upgrade.rb.tt +660 -0
  94. data/lib/generators/ruby_llm/upgrade/templates/ruby_llm_upgrade.rb.tt +222 -0
  95. data/lib/generators/ruby_llm/upgrade/templates/upgrade_initializer.rb.tt +13 -0
  96. data/lib/generators/ruby_llm/upgrade/upgrade_generator.rb +167 -0
  97. data/lib/generators/ruby_llm/upgrade/upgrade_migration.rb +344 -0
  98. data/lib/ruby_llm/accounting/usage.rb +245 -0
  99. data/lib/ruby_llm/active_record/acts_as.rb +94 -111
  100. data/lib/ruby_llm/active_record/attachment_helpers.rb +186 -0
  101. data/lib/ruby_llm/active_record/batch.rb +97 -0
  102. data/lib/ruby_llm/active_record/chat_methods.rb +828 -305
  103. data/lib/ruby_llm/active_record/message_methods.rb +113 -136
  104. data/lib/ruby_llm/active_record/model.rb +135 -0
  105. data/lib/ruby_llm/active_record/payload_helpers.rb +1 -2
  106. data/lib/ruby_llm/active_record/tool_call.rb +33 -0
  107. data/lib/ruby_llm/active_record/usage.rb +61 -0
  108. data/lib/ruby_llm/agent.rb +1065 -151
  109. data/lib/ruby_llm/aliases.json +268 -100
  110. data/lib/ruby_llm/attachment.rb +187 -48
  111. data/lib/ruby_llm/batch.rb +432 -0
  112. data/lib/ruby_llm/cached_content.rb +112 -0
  113. data/lib/ruby_llm/chat/tool_concurrency.rb +111 -0
  114. data/lib/ruby_llm/chat.rb +1127 -198
  115. data/lib/ruby_llm/chunk.rb +10 -0
  116. data/lib/ruby_llm/citation.rb +105 -0
  117. data/lib/ruby_llm/configuration.rb +261 -24
  118. data/lib/ruby_llm/context.rb +128 -6
  119. data/lib/ruby_llm/cost.rb +217 -80
  120. data/lib/ruby_llm/downloaded_file.rb +33 -0
  121. data/lib/ruby_llm/embedding.rb +121 -17
  122. data/lib/ruby_llm/embedding_request.rb +53 -0
  123. data/lib/ruby_llm/error.rb +159 -23
  124. data/lib/ruby_llm/fallback.rb +133 -0
  125. data/lib/ruby_llm/files/mime_type.rb +97 -0
  126. data/lib/ruby_llm/image.rb +153 -32
  127. data/lib/ruby_llm/message.rb +233 -54
  128. data/lib/ruby_llm/model/modalities.rb +17 -4
  129. data/lib/ruby_llm/model/pricing.rb +24 -5
  130. data/lib/ruby_llm/model/pricing_category.rb +103 -14
  131. data/lib/ruby_llm/model/pricing_tier.rb +55 -15
  132. data/lib/ruby_llm/model.rb +244 -2
  133. data/lib/ruby_llm/models/aliases.rb +41 -0
  134. data/lib/ruby_llm/models/registry.rb +165 -0
  135. data/lib/ruby_llm/models/schema.rb +99 -0
  136. data/lib/ruby_llm/models.json +73261 -33733
  137. data/lib/ruby_llm/models.rb +477 -215
  138. data/lib/ruby_llm/moderation.rb +139 -26
  139. data/lib/ruby_llm/ocr.rb +112 -0
  140. data/lib/ruby_llm/prompt.rb +79 -0
  141. data/lib/ruby_llm/protocol/binary_streaming.rb +65 -0
  142. data/lib/ruby_llm/protocol/stream_accumulator.rb +214 -0
  143. data/lib/ruby_llm/protocol/streaming.rb +230 -0
  144. data/lib/ruby_llm/protocol.rb +662 -0
  145. data/lib/ruby_llm/protocols/anthropic/batches.rb +73 -0
  146. data/lib/ruby_llm/protocols/anthropic/chat.rb +548 -0
  147. data/lib/ruby_llm/protocols/anthropic/embeddings.rb +14 -0
  148. data/lib/ruby_llm/protocols/anthropic/files.rb +38 -0
  149. data/lib/ruby_llm/protocols/anthropic/media.rb +141 -0
  150. data/lib/ruby_llm/protocols/anthropic/models.rb +129 -0
  151. data/lib/ruby_llm/protocols/anthropic/streaming.rb +167 -0
  152. data/lib/ruby_llm/{providers → protocols}/anthropic/tools.rb +24 -41
  153. data/lib/ruby_llm/protocols/anthropic.rb +101 -0
  154. data/lib/ruby_llm/protocols/azure/files.rb +16 -0
  155. data/lib/ruby_llm/protocols/bedrock/async_videos.rb +109 -0
  156. data/lib/ruby_llm/protocols/bedrock/batches.rb +129 -0
  157. data/lib/ruby_llm/protocols/bedrock/files.rb +110 -0
  158. data/lib/ruby_llm/protocols/bedrock/guardrails.rb +122 -0
  159. data/lib/ruby_llm/protocols/bedrock/rerank.rb +95 -0
  160. data/lib/ruby_llm/protocols/chat_completions/batches.rb +32 -0
  161. data/lib/ruby_llm/protocols/chat_completions/chat.rb +493 -0
  162. data/lib/ruby_llm/protocols/chat_completions/embedding_batches.rb +35 -0
  163. data/lib/ruby_llm/protocols/chat_completions/embeddings.rb +60 -0
  164. data/lib/ruby_llm/protocols/chat_completions/images.rb +127 -0
  165. data/lib/ruby_llm/{providers/openai → protocols/chat_completions}/media.rb +30 -17
  166. data/lib/ruby_llm/protocols/chat_completions/models.rb +39 -0
  167. data/lib/ruby_llm/protocols/chat_completions/moderation.rb +52 -0
  168. data/lib/ruby_llm/protocols/chat_completions/rerank.rb +56 -0
  169. data/lib/ruby_llm/protocols/chat_completions/speech.rb +40 -0
  170. data/lib/ruby_llm/protocols/chat_completions/streaming.rb +69 -0
  171. data/lib/ruby_llm/{providers/openai → protocols/chat_completions}/tools.rb +15 -17
  172. data/lib/ruby_llm/protocols/chat_completions/transcription.rb +150 -0
  173. data/lib/ruby_llm/protocols/chat_completions.rb +21 -0
  174. data/lib/ruby_llm/protocols/cohere/batch_requests.rb +75 -0
  175. data/lib/ruby_llm/protocols/cohere/batches.rb +98 -0
  176. data/lib/ruby_llm/protocols/cohere/chat.rb +227 -0
  177. data/lib/ruby_llm/protocols/cohere/datasets.rb +102 -0
  178. data/lib/ruby_llm/protocols/cohere/embeddings.rb +68 -0
  179. data/lib/ruby_llm/protocols/cohere/media.rb +77 -0
  180. data/lib/ruby_llm/protocols/cohere/models.rb +92 -0
  181. data/lib/ruby_llm/protocols/cohere/ocr.rb +63 -0
  182. data/lib/ruby_llm/protocols/cohere/rerank.rb +52 -0
  183. data/lib/ruby_llm/protocols/cohere/streaming.rb +105 -0
  184. data/lib/ruby_llm/protocols/cohere/tokenization.rb +22 -0
  185. data/lib/ruby_llm/protocols/cohere/tools.rb +132 -0
  186. data/lib/ruby_llm/protocols/cohere/transcription.rb +41 -0
  187. data/lib/ruby_llm/protocols/cohere.rb +21 -0
  188. data/lib/ruby_llm/protocols/converse/batches.rb +55 -0
  189. data/lib/ruby_llm/protocols/converse/chat.rb +695 -0
  190. data/lib/ruby_llm/protocols/converse/media.rb +178 -0
  191. data/lib/ruby_llm/protocols/converse/streaming.rb +432 -0
  192. data/lib/ruby_llm/protocols/converse/thinking_stream.rb +56 -0
  193. data/lib/ruby_llm/protocols/converse.rb +54 -0
  194. data/lib/ruby_llm/protocols/deepgram/models.rb +74 -0
  195. data/lib/ruby_llm/protocols/deepgram/speech.rb +93 -0
  196. data/lib/ruby_llm/protocols/deepgram/streaming_transcription.rb +89 -0
  197. data/lib/ruby_llm/protocols/deepgram/transcription.rb +96 -0
  198. data/lib/ruby_llm/protocols/deepgram.rb +19 -0
  199. data/lib/ruby_llm/protocols/deepseek/files.rb +31 -0
  200. data/lib/ruby_llm/protocols/elevenlabs/assets.rb +36 -0
  201. data/lib/ruby_llm/protocols/elevenlabs/flows/images.rb +74 -0
  202. data/lib/ruby_llm/protocols/elevenlabs/flows/media.rb +42 -0
  203. data/lib/ruby_llm/protocols/elevenlabs/flows/videos.rb +93 -0
  204. data/lib/ruby_llm/protocols/elevenlabs/flows.rb +14 -0
  205. data/lib/ruby_llm/protocols/elevenlabs/models.rb +64 -0
  206. data/lib/ruby_llm/protocols/elevenlabs/speech.rb +67 -0
  207. data/lib/ruby_llm/protocols/elevenlabs/streaming_transcription.rb +127 -0
  208. data/lib/ruby_llm/protocols/elevenlabs/transcription.rb +61 -0
  209. data/lib/ruby_llm/protocols/elevenlabs.rb +15 -0
  210. data/lib/ruby_llm/protocols/files.rb +119 -0
  211. data/lib/ruby_llm/protocols/gemini/batches.rb +162 -0
  212. data/lib/ruby_llm/protocols/gemini/caches.rb +59 -0
  213. data/lib/ruby_llm/protocols/gemini/chat.rb +453 -0
  214. data/lib/ruby_llm/protocols/gemini/embedding_batches.rb +86 -0
  215. data/lib/ruby_llm/protocols/gemini/embeddings.rb +70 -0
  216. data/lib/ruby_llm/protocols/gemini/file_transcription.rb +32 -0
  217. data/lib/ruby_llm/protocols/gemini/files.rb +115 -0
  218. data/lib/ruby_llm/protocols/gemini/images.rb +183 -0
  219. data/lib/ruby_llm/protocols/gemini/live_transcription.rb +140 -0
  220. data/lib/ruby_llm/{providers → protocols}/gemini/media.rb +17 -11
  221. data/lib/ruby_llm/protocols/gemini/models.rb +71 -0
  222. data/lib/ruby_llm/protocols/gemini/speech.rb +56 -0
  223. data/lib/ruby_llm/protocols/gemini/streaming.rb +96 -0
  224. data/lib/ruby_llm/protocols/gemini/tools.rb +157 -0
  225. data/lib/ruby_llm/{providers → protocols}/gemini/transcription.rb +22 -22
  226. data/lib/ruby_llm/protocols/gemini/videos.rb +103 -0
  227. data/lib/ruby_llm/protocols/gemini.rb +35 -0
  228. data/lib/ruby_llm/protocols/gpustack/responses.rb +111 -0
  229. data/lib/ruby_llm/protocols/gpustack/tokenization.rb +22 -0
  230. data/lib/ruby_llm/protocols/gpustack/videos.rb +96 -0
  231. data/lib/ruby_llm/protocols/interactions/chat.rb +145 -0
  232. data/lib/ruby_llm/protocols/interactions/content.rb +90 -0
  233. data/lib/ruby_llm/protocols/interactions/streaming.rb +91 -0
  234. data/lib/ruby_llm/protocols/interactions/tools.rb +51 -0
  235. data/lib/ruby_llm/protocols/interactions/transcription.rb +58 -0
  236. data/lib/ruby_llm/protocols/interactions.rb +29 -0
  237. data/lib/ruby_llm/protocols/invoke_model/cohere_embeddings.rb +51 -0
  238. data/lib/ruby_llm/protocols/invoke_model/embedding_batches.rb +111 -0
  239. data/lib/ruby_llm/protocols/invoke_model/nova_embeddings.rb +50 -0
  240. data/lib/ruby_llm/protocols/invoke_model/stability_images.rb +103 -0
  241. data/lib/ruby_llm/protocols/invoke_model/titan_multimodal_embeddings.rb +33 -0
  242. data/lib/ruby_llm/protocols/invoke_model/titan_text_embeddings.rb +44 -0
  243. data/lib/ruby_llm/protocols/invoke_model.rb +57 -0
  244. data/lib/ruby_llm/protocols/mistral/content.rb +49 -0
  245. data/lib/ruby_llm/protocols/mistral/conversations/chat.rb +160 -0
  246. data/lib/ruby_llm/protocols/mistral/conversations/images.rb +43 -0
  247. data/lib/ruby_llm/protocols/mistral/conversations/streaming.rb +83 -0
  248. data/lib/ruby_llm/protocols/mistral/conversations.rb +30 -0
  249. data/lib/ruby_llm/protocols/mistral/files.rb +36 -0
  250. data/lib/ruby_llm/protocols/mistral/multi_completion.rb +160 -0
  251. data/lib/ruby_llm/protocols/openai/batches.rb +126 -0
  252. data/lib/ruby_llm/protocols/openai/files.rb +42 -0
  253. data/lib/ruby_llm/protocols/openrouter/batches.rb +147 -0
  254. data/lib/ruby_llm/protocols/openrouter/files.rb +24 -0
  255. data/lib/ruby_llm/protocols/openrouter/responses.rb +53 -0
  256. data/lib/ruby_llm/protocols/openrouter/transcription.rb +51 -0
  257. data/lib/ruby_llm/protocols/perplexity/files.rb +48 -0
  258. data/lib/ruby_llm/protocols/perplexity/router.rb +59 -0
  259. data/lib/ruby_llm/protocols/responses/approvals.rb +32 -0
  260. data/lib/ruby_llm/protocols/responses/batches.rb +32 -0
  261. data/lib/ruby_llm/protocols/responses/chat.rb +476 -0
  262. data/lib/ruby_llm/protocols/responses/compaction.rb +29 -0
  263. data/lib/ruby_llm/protocols/responses/media.rb +61 -0
  264. data/lib/ruby_llm/protocols/responses/streaming.rb +117 -0
  265. data/lib/ruby_llm/protocols/responses/token_counting.rb +26 -0
  266. data/lib/ruby_llm/protocols/responses/tools.rb +39 -0
  267. data/lib/ruby_llm/protocols/responses.rb +35 -0
  268. data/lib/ruby_llm/protocols/vertexai/batch_prediction.rb +155 -0
  269. data/lib/ruby_llm/protocols/vertexai/embedding_prediction/requests.rb +85 -0
  270. data/lib/ruby_llm/protocols/vertexai/embedding_prediction/results.rb +74 -0
  271. data/lib/ruby_llm/protocols/vertexai/embedding_prediction.rb +56 -0
  272. data/lib/ruby_llm/protocols/vertexai/files.rb +101 -0
  273. data/lib/ruby_llm/protocols/vertexai/ranking.rb +69 -0
  274. data/lib/ruby_llm/protocols/vertexai/research.rb +193 -0
  275. data/lib/ruby_llm/protocols/xai/files.rb +30 -0
  276. data/lib/ruby_llm/protocols/xai/streaming_transcription.rb +120 -0
  277. data/lib/ruby_llm/protocols/xai/tokenization.rb +23 -0
  278. data/lib/ruby_llm/provider.rb +560 -128
  279. data/lib/ruby_llm/providers/anthropic/capabilities.rb +5 -7
  280. data/lib/ruby_llm/providers/anthropic.rb +4 -6
  281. data/lib/ruby_llm/providers/azure/audio.rb +18 -0
  282. data/lib/ruby_llm/providers/azure/capabilities.rb +16 -0
  283. data/lib/ruby_llm/providers/azure/chat.rb +2 -9
  284. data/lib/ruby_llm/providers/azure/chat_completions/batches.rb +29 -0
  285. data/lib/ruby_llm/providers/azure/chat_completions.rb +80 -0
  286. data/lib/ruby_llm/providers/azure/cohere.rb +33 -0
  287. data/lib/ruby_llm/providers/azure/embeddings.rb +3 -2
  288. data/lib/ruby_llm/providers/azure/images.rb +22 -0
  289. data/lib/ruby_llm/providers/azure/media.rb +5 -14
  290. data/lib/ruby_llm/providers/azure/models.rb +35 -0
  291. data/lib/ruby_llm/providers/azure/responses.rb +26 -0
  292. data/lib/ruby_llm/providers/azure/videos.rb +64 -0
  293. data/lib/ruby_llm/providers/azure.rb +77 -78
  294. data/lib/ruby_llm/providers/bedrock/auth.rb +60 -41
  295. data/lib/ruby_llm/providers/bedrock/capabilities.rb +18 -0
  296. data/lib/ruby_llm/providers/bedrock/mantle/anthropic.rb +39 -0
  297. data/lib/ruby_llm/providers/bedrock/mantle/chat_completions.rb +23 -0
  298. data/lib/ruby_llm/providers/bedrock/mantle/responses.rb +24 -0
  299. data/lib/ruby_llm/providers/bedrock/mantle/voxtral.rb +96 -0
  300. data/lib/ruby_llm/providers/bedrock/mantle.rb +57 -0
  301. data/lib/ruby_llm/providers/bedrock/models.rb +193 -41
  302. data/lib/ruby_llm/providers/bedrock.rb +216 -45
  303. data/lib/ruby_llm/providers/cohere.rb +31 -0
  304. data/lib/ruby_llm/providers/deepgram.rb +37 -0
  305. data/lib/ruby_llm/providers/deepseek/capabilities.rb +4 -51
  306. data/lib/ruby_llm/providers/deepseek/chat.rb +49 -2
  307. data/lib/ruby_llm/providers/deepseek/responses.rb +68 -0
  308. data/lib/ruby_llm/providers/deepseek.rb +9 -2
  309. data/lib/ruby_llm/providers/elevenlabs.rb +35 -0
  310. data/lib/ruby_llm/providers/gemini/capabilities.rb +8 -107
  311. data/lib/ruby_llm/providers/gemini.rb +15 -8
  312. data/lib/ruby_llm/providers/gpustack/chat.rb +2 -16
  313. data/lib/ruby_llm/providers/gpustack/embeddings.rb +28 -0
  314. data/lib/ruby_llm/providers/gpustack/media.rb +17 -16
  315. data/lib/ruby_llm/providers/gpustack/models.rb +72 -62
  316. data/lib/ruby_llm/providers/gpustack/speech.rb +15 -0
  317. data/lib/ruby_llm/providers/gpustack/transcription.rb +29 -0
  318. data/lib/ruby_llm/providers/gpustack.rb +35 -11
  319. data/lib/ruby_llm/providers/mistral/capabilities.rb +7 -155
  320. data/lib/ruby_llm/providers/mistral/chat.rb +37 -61
  321. data/lib/ruby_llm/providers/mistral/chat_completions/batches.rb +120 -0
  322. data/lib/ruby_llm/providers/mistral/chat_completions.rb +21 -0
  323. data/lib/ruby_llm/providers/mistral/conversations.rb +12 -0
  324. data/lib/ruby_llm/providers/mistral/embeddings.rb +6 -4
  325. data/lib/ruby_llm/providers/mistral/media.rb +6 -18
  326. data/lib/ruby_llm/providers/mistral/models.rb +57 -23
  327. data/lib/ruby_llm/providers/mistral/ocr.rb +47 -0
  328. data/lib/ruby_llm/providers/mistral/speech.rb +51 -0
  329. data/lib/ruby_llm/providers/mistral/transcription.rb +62 -0
  330. data/lib/ruby_llm/providers/mistral.rb +16 -4
  331. data/lib/ruby_llm/providers/ollama/chat.rb +7 -13
  332. data/lib/ruby_llm/providers/ollama/media.rb +6 -15
  333. data/lib/ruby_llm/providers/ollama/models.rb +50 -9
  334. data/lib/ruby_llm/providers/ollama.rb +9 -8
  335. data/lib/ruby_llm/providers/ollama_cloud/models.rb +14 -0
  336. data/lib/ruby_llm/providers/ollama_cloud.rb +40 -0
  337. data/lib/ruby_llm/providers/openai/capabilities.rb +54 -259
  338. data/lib/ruby_llm/providers/openai/models.rb +23 -23
  339. data/lib/ruby_llm/providers/openai/responses.rb +13 -0
  340. data/lib/ruby_llm/providers/openai.rb +92 -11
  341. data/lib/ruby_llm/providers/openrouter/chat.rb +130 -108
  342. data/lib/ruby_llm/providers/openrouter/embeddings.rb +51 -0
  343. data/lib/ruby_llm/providers/openrouter/images.rb +44 -43
  344. data/lib/ruby_llm/providers/openrouter/media.rb +34 -0
  345. data/lib/ruby_llm/providers/openrouter/models.rb +50 -11
  346. data/lib/ruby_llm/providers/openrouter/speech.rb +32 -0
  347. data/lib/ruby_llm/providers/openrouter/streaming.rb +31 -38
  348. data/lib/ruby_llm/providers/openrouter/videos.rb +81 -0
  349. data/lib/ruby_llm/providers/openrouter.rb +78 -20
  350. data/lib/ruby_llm/providers/perplexity/chat.rb +2 -9
  351. data/lib/ruby_llm/providers/perplexity/embeddings.rb +32 -0
  352. data/lib/ruby_llm/providers/perplexity/media.rb +5 -21
  353. data/lib/ruby_llm/providers/perplexity/models.rb +80 -13
  354. data/lib/ruby_llm/providers/perplexity.rb +28 -20
  355. data/lib/ruby_llm/providers/vertexai/anthropic/batches.rb +52 -0
  356. data/lib/ruby_llm/providers/vertexai/anthropic.rb +34 -0
  357. data/lib/ruby_llm/providers/vertexai/capabilities.rb +19 -0
  358. data/lib/ruby_llm/providers/vertexai/chat_completions/batches.rb +54 -0
  359. data/lib/ruby_llm/providers/vertexai/chat_completions.rb +15 -0
  360. data/lib/ruby_llm/providers/vertexai/embed_content.rb +42 -0
  361. data/lib/ruby_llm/providers/vertexai/embeddings.rb +22 -7
  362. data/lib/ruby_llm/providers/vertexai/gemini/batches.rb +42 -0
  363. data/lib/ruby_llm/providers/vertexai/gemini.rb +69 -0
  364. data/lib/ruby_llm/providers/vertexai/live_transcription.rb +24 -0
  365. data/lib/ruby_llm/providers/vertexai/mistral.rb +28 -0
  366. data/lib/ruby_llm/providers/vertexai/models.rb +145 -43
  367. data/lib/ruby_llm/providers/vertexai/transcription.rb +49 -4
  368. data/lib/ruby_llm/providers/vertexai/videos.rb +61 -0
  369. data/lib/ruby_llm/providers/vertexai.rb +160 -17
  370. data/lib/ruby_llm/providers/xai/capabilities.rb +18 -0
  371. data/lib/ruby_llm/providers/xai/chat.rb +3 -2
  372. data/lib/ruby_llm/providers/xai/chat_completions/batches.rb +108 -0
  373. data/lib/ruby_llm/providers/xai/chat_completions.rb +19 -0
  374. data/lib/ruby_llm/providers/xai/images.rb +91 -0
  375. data/lib/ruby_llm/providers/xai/models.rb +32 -36
  376. data/lib/ruby_llm/providers/xai/reported_cost.rb +18 -0
  377. data/lib/ruby_llm/providers/xai/responses.rb +52 -0
  378. data/lib/ruby_llm/providers/xai/speech.rb +45 -0
  379. data/lib/ruby_llm/providers/xai/transcription.rb +48 -0
  380. data/lib/ruby_llm/providers/xai/videos.rb +87 -0
  381. data/lib/ruby_llm/providers/xai.rb +15 -5
  382. data/lib/ruby_llm/railtie.rb +7 -16
  383. data/lib/ruby_llm/rerank.rb +105 -0
  384. data/lib/ruby_llm/research_job.rb +241 -0
  385. data/lib/ruby_llm/search_results.rb +68 -0
  386. data/lib/ruby_llm/server_tool_call.rb +73 -0
  387. data/lib/ruby_llm/speech.rb +159 -0
  388. data/lib/ruby_llm/speech_chunk.rb +33 -0
  389. data/lib/ruby_llm/support/deprecator.rb +22 -0
  390. data/lib/ruby_llm/support/inspectable.rb +49 -0
  391. data/lib/ruby_llm/support/instrumentation.rb +41 -0
  392. data/lib/ruby_llm/support/utils.rb +147 -0
  393. data/lib/ruby_llm/thinking.rb +127 -20
  394. data/lib/ruby_llm/tokenization.rb +59 -0
  395. data/lib/ruby_llm/tokens.rb +103 -33
  396. data/lib/ruby_llm/tool.rb +266 -91
  397. data/lib/ruby_llm/tool_call.rb +36 -3
  398. data/lib/ruby_llm/tools/server_tools.rb +109 -0
  399. data/lib/ruby_llm/transcription/wav_audio.rb +62 -0
  400. data/lib/ruby_llm/transcription.rb +138 -13
  401. data/lib/ruby_llm/transcription_chunk.rb +68 -0
  402. data/lib/ruby_llm/transport/connection.rb +193 -0
  403. data/lib/ruby_llm/transport/error_middleware.rb +131 -0
  404. data/lib/ruby_llm/transport/usage_middleware.rb +28 -0
  405. data/lib/ruby_llm/transport/websocket_connection.rb +220 -0
  406. data/lib/ruby_llm/uploaded_file.rb +144 -0
  407. data/lib/ruby_llm/version.rb +2 -1
  408. data/lib/ruby_llm/video.rb +136 -0
  409. data/lib/ruby_llm/video_job.rb +150 -0
  410. data/lib/ruby_llm/workflow.rb +91 -0
  411. data/lib/ruby_llm.rb +380 -6
  412. data/lib/tasks/ruby_llm.rake +21 -16
  413. data/skills/rubyllm/SKILL.md +81 -0
  414. data/skills/rubyllm/agents/openai.yaml +4 -0
  415. metadata +339 -97
  416. data/lib/generators/ruby_llm/install/templates/add_references_to_chats_tool_calls_and_messages_migration.rb.tt +0 -9
  417. data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +0 -39
  418. data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +0 -21
  419. data/lib/generators/ruby_llm/install/templates/model_model.rb.tt +0 -3
  420. data/lib/generators/ruby_llm/install/templates/tool_call_model.rb.tt +0 -3
  421. data/lib/generators/ruby_llm/upgrade_to_v1_10/templates/add_v1_10_message_columns.rb.tt +0 -19
  422. data/lib/generators/ruby_llm/upgrade_to_v1_10/upgrade_to_v1_10_generator.rb +0 -50
  423. data/lib/generators/ruby_llm/upgrade_to_v1_14/templates/add_v1_14_tool_call_columns.rb.tt +0 -7
  424. data/lib/generators/ruby_llm/upgrade_to_v1_14/upgrade_to_v1_14_generator.rb +0 -49
  425. data/lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt +0 -145
  426. data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +0 -122
  427. data/lib/generators/ruby_llm/upgrade_to_v1_9/templates/add_v1_9_message_columns.rb.tt +0 -15
  428. data/lib/generators/ruby_llm/upgrade_to_v1_9/upgrade_to_v1_9_generator.rb +0 -49
  429. data/lib/ruby_llm/active_record/acts_as_legacy.rb +0 -597
  430. data/lib/ruby_llm/active_record/model_methods.rb +0 -82
  431. data/lib/ruby_llm/active_record/tool_call_methods.rb +0 -18
  432. data/lib/ruby_llm/aliases.rb +0 -41
  433. data/lib/ruby_llm/connection.rb +0 -159
  434. data/lib/ruby_llm/content.rb +0 -91
  435. data/lib/ruby_llm/deprecator.rb +0 -24
  436. data/lib/ruby_llm/error_middleware.rb +0 -81
  437. data/lib/ruby_llm/instrumentation.rb +0 -36
  438. data/lib/ruby_llm/mime_type.rb +0 -96
  439. data/lib/ruby_llm/model/info.rb +0 -164
  440. data/lib/ruby_llm/model_registry.rb +0 -39
  441. data/lib/ruby_llm/models_schema.json +0 -171
  442. data/lib/ruby_llm/providers/anthropic/chat.rb +0 -291
  443. data/lib/ruby_llm/providers/anthropic/content.rb +0 -44
  444. data/lib/ruby_llm/providers/anthropic/embeddings.rb +0 -20
  445. data/lib/ruby_llm/providers/anthropic/media.rb +0 -92
  446. data/lib/ruby_llm/providers/anthropic/models.rb +0 -59
  447. data/lib/ruby_llm/providers/anthropic/streaming.rb +0 -71
  448. data/lib/ruby_llm/providers/bedrock/chat.rb +0 -405
  449. data/lib/ruby_llm/providers/bedrock/media.rb +0 -108
  450. data/lib/ruby_llm/providers/bedrock/streaming.rb +0 -328
  451. data/lib/ruby_llm/providers/gemini/chat.rb +0 -542
  452. data/lib/ruby_llm/providers/gemini/embeddings.rb +0 -37
  453. data/lib/ruby_llm/providers/gemini/images.rb +0 -47
  454. data/lib/ruby_llm/providers/gemini/models.rb +0 -38
  455. data/lib/ruby_llm/providers/gemini/streaming.rb +0 -98
  456. data/lib/ruby_llm/providers/gemini/tools.rb +0 -234
  457. data/lib/ruby_llm/providers/gpustack/capabilities.rb +0 -20
  458. data/lib/ruby_llm/providers/ollama/capabilities.rb +0 -20
  459. data/lib/ruby_llm/providers/openai/chat.rb +0 -236
  460. data/lib/ruby_llm/providers/openai/embeddings.rb +0 -33
  461. data/lib/ruby_llm/providers/openai/images.rb +0 -90
  462. data/lib/ruby_llm/providers/openai/moderation.rb +0 -34
  463. data/lib/ruby_llm/providers/openai/streaming.rb +0 -55
  464. data/lib/ruby_llm/providers/openai/temperature.rb +0 -28
  465. data/lib/ruby_llm/providers/openai/transcription.rb +0 -71
  466. data/lib/ruby_llm/providers/perplexity/capabilities.rb +0 -72
  467. data/lib/ruby_llm/providers/vertexai/chat.rb +0 -14
  468. data/lib/ruby_llm/providers/vertexai/streaming.rb +0 -14
  469. data/lib/ruby_llm/stream_accumulator.rb +0 -218
  470. data/lib/ruby_llm/streaming.rb +0 -179
  471. data/lib/ruby_llm/tool_concurrency.rb +0 -105
  472. data/lib/ruby_llm/utils.rb +0 -130
  473. data/lib/tasks/models.rake +0 -593
  474. data/lib/tasks/release.rake +0 -94
  475. data/lib/tasks/vcr.rake +0 -124
@@ -1,6 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyLLM
4
+ # A Chunk is one streamed fragment of an assistant response. Chunks are
5
+ # yielded to the block passed to Chat#ask or Chat#complete when streaming.
6
+ # A Chunk is a Message, so it responds to the same readers, but it holds
7
+ # only the data received in that part of the stream, such as a piece of
8
+ # content, partial tool calls, or the finish reason on the final chunk.
9
+ #
10
+ # chat.ask "Write a haiku about Ruby" do |chunk|
11
+ # print chunk.content
12
+ # end
13
+ #
4
14
  class Chunk < Message
5
15
  end
6
16
  end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyLLM
4
+ # A Citation links a span of generated text to the source material that
5
+ # supports it. Providers return citations in different shapes. RubyLLM
6
+ # normalizes all of them into Citation objects on Message#citations.
7
+ # Fields a provider does not report are +nil+.
8
+ #
9
+ # chat = RubyLLM.chat(model: 'claude-sonnet-5').with_citations
10
+ # response = chat.ask "Who created Ruby?", with: "facts.txt"
11
+ #
12
+ # response.citations.each do |citation|
13
+ # citation.title # => "facts.txt"
14
+ # citation.cited_text # => the quoted passage from facts.txt
15
+ # citation.text # => the span of the answer it supports
16
+ # end
17
+ #
18
+ class Citation
19
+ include Support::Inspectable
20
+
21
+ # The URL of the cited source, when citing the web, or +nil+.
22
+ attr_reader :url
23
+
24
+ # The title or filename of the cited source, or +nil+.
25
+ attr_reader :title
26
+
27
+ # The quoted snippet from the source material, or +nil+.
28
+ attr_reader :cited_text
29
+
30
+ # The span of the response content this citation supports, or +nil+.
31
+ attr_reader :text
32
+
33
+ # The character offset where the cited span starts in the response
34
+ # content, or +nil+.
35
+ #
36
+ # response.content[citation.start_index...citation.end_index] == citation.text # => true
37
+ #
38
+ attr_reader :start_index
39
+
40
+ # The character offset where the cited span ends in the response
41
+ # content, or +nil+.
42
+ attr_reader :end_index
43
+
44
+ # The provider's identifier for the cited source, or +nil+.
45
+ attr_reader :source_id
46
+
47
+ # The 0-indexed position of the source document or search result,
48
+ # or +nil+.
49
+ attr_reader :source_index
50
+
51
+ # The first page of a PDF citation (1-indexed, inclusive), or +nil+.
52
+ attr_reader :start_page
53
+
54
+ # The last page of a PDF citation (1-indexed, inclusive), or +nil+.
55
+ attr_reader :end_page
56
+
57
+ def initialize(options = {}) # :nodoc:
58
+ @url = options[:url]
59
+ @title = options[:title]
60
+ @cited_text = options[:cited_text]
61
+ @text = options[:text]
62
+ @start_index = options[:start_index]
63
+ @end_index = options[:end_index]
64
+ @source_id = options[:source_id]
65
+ @source_index = options[:source_index]
66
+ @start_page = options[:start_page]
67
+ @end_page = options[:end_page]
68
+ end
69
+
70
+ def self.from_h(data) # :nodoc:
71
+ new(Support::Utils.deep_symbolize_keys(data))
72
+ end
73
+
74
+ def inspect_attributes # :nodoc:
75
+ { url: url, title: title, start_index: start_index, end_index: end_index }
76
+ end
77
+
78
+ # Returns the citation as a Hash with symbol keys, omitting +nil+ fields.
79
+ def to_h
80
+ {
81
+ url: url,
82
+ title: title,
83
+ cited_text: cited_text,
84
+ text: text,
85
+ start_index: start_index,
86
+ end_index: end_index,
87
+ source_id: source_id,
88
+ source_index: source_index,
89
+ start_page: start_page,
90
+ end_page: end_page
91
+ }.compact
92
+ end
93
+
94
+ # Returns +true+ if +other+ is a Citation with the same attributes,
95
+ # +false+ otherwise.
96
+ def ==(other)
97
+ other.is_a?(Citation) && to_h == other.to_h
98
+ end
99
+ alias eql? == # :nodoc:
100
+
101
+ def hash # :nodoc:
102
+ to_h.hash
103
+ end
104
+ end
105
+ end
@@ -3,11 +3,29 @@
3
3
  require 'logger'
4
4
 
5
5
  module RubyLLM
6
- # Global configuration for RubyLLM
6
+ # A Configuration holds every RubyLLM setting: provider credentials,
7
+ # default models, timeouts, retries, logging, and the model registry.
8
+ # The global instance is yielded by RubyLLM.configure and available as
9
+ # RubyLLM.config.
10
+ #
11
+ # RubyLLM.configure do |config|
12
+ # config.openai_api_key = ENV['OPENAI_API_KEY']
13
+ # config.anthropic_api_key = ENV['ANTHROPIC_API_KEY']
14
+ # end
15
+ #
16
+ # RubyLLM.context yields an isolated copy for per-request or per-tenant
17
+ # overrides.
18
+ #
19
+ # Provider credentials such as +openai_api_key+ are declared by each
20
+ # provider. See https://rubyllm.com/next/configuration-providers/ for
21
+ # provider credentials and endpoint settings.
22
+ #
23
+ # Assigning an empty or whitespace-only string to any option stores
24
+ # +nil+, so unset environment variables behave as if the option was
25
+ # never set.
7
26
  class Configuration
8
27
  class << self
9
- # Declare a single configuration option.
10
- def option(key, default = nil)
28
+ def option(key, default = nil) # :nodoc:
11
29
  key = key.to_sym
12
30
  return if options.include?(key)
13
31
 
@@ -22,10 +40,12 @@ module RubyLLM
22
40
  defaults[key] = default
23
41
  end
24
42
 
25
- def register_provider_options(options)
43
+ def register_provider_options(options) # :nodoc:
26
44
  Array(options).each { |key| option(key, nil) }
27
45
  end
28
46
 
47
+ # Returns the names of all declared options as an array of symbols,
48
+ # including options registered by providers.
29
49
  def options
30
50
  option_keys.dup
31
51
  end
@@ -40,55 +60,272 @@ module RubyLLM
40
60
  # System-level options are declared here.
41
61
  # Provider-specific options are declared in each provider class via
42
62
  # `self.configuration_options` and registered through Provider.register.
43
- option :default_model, 'gpt-5.4'
63
+
64
+ ##
65
+ # :attr_accessor: default_model
66
+ #
67
+ # The model id used by RubyLLM.chat when no model is given.
68
+ # Default: <tt>'gpt-5.6'</tt>.
69
+ option :default_model, 'gpt-5.6'
70
+
71
+ ##
72
+ # :attr_accessor: default_embedding_model
73
+ #
74
+ # The model id used by RubyLLM.embed when no model is given.
75
+ # Default: <tt>'text-embedding-3-small'</tt>.
44
76
  option :default_embedding_model, 'text-embedding-3-small'
77
+
78
+ ##
79
+ # :attr_accessor: default_moderation_model
80
+ #
81
+ # The model id used by RubyLLM.moderate when no model is given.
82
+ # Default: <tt>'omni-moderation-latest'</tt>.
45
83
  option :default_moderation_model, 'omni-moderation-latest'
46
- option :default_image_model, 'gpt-image-1.5'
47
- option :default_transcription_model, 'whisper-1'
48
84
 
49
- option :model_registry_file, -> { File.expand_path('models.json', __dir__) }
50
- option :model_registry_class, 'Model'
85
+ ##
86
+ # :attr_accessor: default_image_model
87
+ #
88
+ # The model id used by RubyLLM.paint when no model is given.
89
+ # Default: <tt>'gpt-image-2'</tt>.
90
+ option :default_image_model, 'gpt-image-2'
91
+
92
+ ##
93
+ # :attr_accessor: default_speech_model
94
+ #
95
+ # The model id used by RubyLLM.speak when no model is given.
96
+ # Default: <tt>'gpt-4o-mini-tts-2025-12-15'</tt>.
97
+ option :default_speech_model, 'gpt-4o-mini-tts-2025-12-15'
98
+
99
+ ##
100
+ # :attr_accessor: default_transcription_model
101
+ #
102
+ # The model id used by RubyLLM.transcribe when no model is given.
103
+ # Default: <tt>'gpt-transcribe'</tt>.
104
+ option :default_transcription_model, 'gpt-transcribe'
105
+
106
+ ##
107
+ # :attr_accessor: default_ocr_model
108
+ #
109
+ # The model id used by RubyLLM.ocr when no model is given.
110
+ # Default: <tt>'mistral-ocr-latest'</tt>.
111
+ option :default_ocr_model, 'mistral-ocr-latest'
112
+
113
+ ##
114
+ # :attr_accessor: default_video_model
115
+ #
116
+ # The model id used by RubyLLM.animate when no model is given.
117
+ # Default: <tt>'grok-imagine-video-1.5'</tt>.
118
+ option :default_video_model, 'grok-imagine-video-1.5'
119
+
120
+ ##
121
+ # :attr_accessor: video_generation_timeout
122
+ #
123
+ # Seconds RubyLLM.animate waits for a video job to finish before
124
+ # raising an error. Default: 600.
125
+ option :video_generation_timeout, 600
126
+
127
+ ##
128
+ # :attr_accessor: video_generation_poll_interval
129
+ #
130
+ # Seconds between status polls while RubyLLM.animate waits for a
131
+ # video job. Default: 5.
132
+ option :video_generation_poll_interval, 5
51
133
 
52
- option :use_new_acts_as, false
53
- option :model_registry_source, nil
134
+ ##
135
+ # :attr_accessor: model_registry_file
136
+ #
137
+ # Path of the writable JSON cache holding the model registry. Defaults
138
+ # to the operating system's user cache directory. The copy bundled with
139
+ # the gem is used until this file exists.
140
+ option :model_registry_file, -> { Models::Registry.cache_path }
54
141
 
142
+ ##
143
+ # :attr_accessor: model_registry_store
144
+ #
145
+ # Store object the model registry reads from and persists to. Rails
146
+ # apps using the acts_as helpers set this to the database store
147
+ # automatically. A store must respond to +read+, returning an array of
148
+ # Model entries, and may respond to +write(registry)+ to let
149
+ # Models#refresh persist. Default: +nil+ (use +model_registry_file+).
150
+ option :model_registry_store, nil
151
+
152
+ # Optional persistence adapter installed by the Rails integration.
153
+ option :batch_store, nil # :nodoc:
154
+
155
+ # Keeps custom 1.x initializers bootable long enough to run the 2.0
156
+ # upgrade generator. The registry is always RubyLLM-owned in 2.0.
157
+ def model_registry_class=(_value) # :nodoc:
158
+ RubyLLM.deprecator.warn(
159
+ 'config.model_registry_class is ignored in RubyLLM 2.0; remove it from your initializer'
160
+ )
161
+ end
162
+
163
+ def use_new_acts_as=(_value) # :nodoc:
164
+ RubyLLM.deprecator.warn(
165
+ 'config.use_new_acts_as is ignored in RubyLLM 2.0; remove it from your initializer'
166
+ )
167
+ end
168
+
169
+ ##
170
+ # :attr_accessor: request_timeout
171
+ #
172
+ # Seconds to wait for a response before timing out. Default: 300.
55
173
  option :request_timeout, 300
174
+
175
+ ##
176
+ # :attr_accessor: max_retries
177
+ #
178
+ # Number of times to retry failed requests. Default: 3.
56
179
  option :max_retries, 3
180
+
181
+ ##
182
+ # :attr_accessor: retry_interval
183
+ #
184
+ # Initial delay in seconds before the first retry. Default: 0.1.
57
185
  option :retry_interval, 0.1
186
+
187
+ ##
188
+ # :attr_accessor: retry_backoff_factor
189
+ #
190
+ # Multiplier applied to the retry delay after each attempt.
191
+ # Default: 2.
58
192
  option :retry_backoff_factor, 2
193
+
194
+ ##
195
+ # :attr_accessor: retry_interval_randomness
196
+ #
197
+ # Random jitter factor applied to retry delays. Default: 0.5.
59
198
  option :retry_interval_randomness, 0.5
199
+
200
+ ##
201
+ # :attr_accessor: retry_max_interval
202
+ #
203
+ # Longest delay in seconds to wait between retries, including delays
204
+ # requested by the provider through rate-limit headers. When a
205
+ # provider asks for a longer wait, the request fails immediately
206
+ # instead of sleeping. Default: 30.
207
+ option :retry_max_interval, 30
208
+
209
+ ##
210
+ # :attr_accessor: http_proxy
211
+ #
212
+ # Proxy URL for all requests, such as
213
+ # <tt>'http://proxy.example.com:8080'</tt>. HTTP, authenticated, and
214
+ # SOCKS5 proxies are supported. Default: +nil+.
60
215
  option :http_proxy, nil
216
+
217
+ ##
218
+ # :attr_accessor: tool_concurrency
219
+ #
220
+ # How chats run multiple tool calls from one response: +false+ for
221
+ # sequential execution, +true+ or +:threads+ for threads, +:fibers+
222
+ # for fibers via the async gem. Default: +false+.
61
223
  option :tool_concurrency, false
62
224
 
225
+ ##
226
+ # :attr_accessor: auto_upload_large_files
227
+ #
228
+ # Whether oversized local attachments are uploaded to provider file
229
+ # storage automatically. Default: +true+.
230
+ option :auto_upload_large_files, true
231
+
232
+ ##
233
+ # :attr_accessor: logger
234
+ #
235
+ # Logger receiving RubyLLM output. When set, it overrides #log_file
236
+ # and #log_level. Default: +nil+.
63
237
  option :logger, nil
238
+
239
+ ##
240
+ # :attr_accessor: instrumenter
241
+ #
242
+ # Object receiving instrumentation events. It must respond to
243
+ # <tt>instrument(name, payload)</tt> and accept an optional block,
244
+ # like ActiveSupport::Notifications, which Rails apps use
245
+ # automatically. Default: +nil+.
64
246
  option :instrumenter, nil
247
+
248
+ ##
249
+ # :attr_accessor: deprecation_behavior
250
+ #
251
+ # How deprecation warnings are handled: +:warn+, +:silence+, or
252
+ # +:raise+. Default: +:warn+.
65
253
  option :deprecation_behavior, :warn
254
+
255
+ ##
256
+ # :attr_accessor: faraday_adapter
257
+ #
258
+ # Faraday adapter used for HTTP requests. Default: +:net_http+.
66
259
  option :faraday_adapter, :net_http
67
- option :log_file, -> { $stdout }
68
- option :log_level, -> { ENV['RUBYLLM_DEBUG'] ? Logger::DEBUG : Logger::INFO }
69
- option :log_stream_debug, -> { ENV['RUBYLLM_STREAM_DEBUG'] == 'true' }
260
+
261
+ ##
262
+ # :attr_accessor: log_file
263
+ #
264
+ # Destination for the built-in logger, a path or an IO.
265
+ # Defaults to the +RUBYLLM_LOG_FILE+ environment variable when set,
266
+ # <tt>$stdout</tt> otherwise.
267
+ option :log_file, -> { ENV.fetch('RUBYLLM_LOG_FILE', nil) || $stdout }
268
+
269
+ ##
270
+ # :attr_accessor: log_level
271
+ #
272
+ # Severity of the built-in logger. Defaults to <tt>Logger::DEBUG</tt>
273
+ # when the +RUBYLLM_DEBUG+ environment variable says yes
274
+ # (<tt>true</tt>, <tt>1</tt>, <tt>yes</tt>, or <tt>on</tt>),
275
+ # <tt>Logger::INFO</tt> otherwise.
276
+ option :log_level, -> { env_says_yes?('RUBYLLM_DEBUG') ? Logger::DEBUG : Logger::INFO }
277
+
278
+ ##
279
+ # :attr_accessor: log_stream_debug
280
+ #
281
+ # Whether raw streaming chunks are logged. Defaults to +true+ when the
282
+ # +RUBYLLM_STREAM_DEBUG+ environment variable says yes, +false+
283
+ # otherwise.
284
+ option :log_stream_debug, -> { env_says_yes?('RUBYLLM_STREAM_DEBUG') }
285
+
286
+ ##
287
+ # :attr_accessor: log_regexp_timeout
288
+ #
289
+ # Timeout in seconds for the regular expressions that scrub logged
290
+ # payloads. Defaults to the global <tt>Regexp.timeout</tt>, or 1.0
291
+ # when none is set. Requires Ruby 3.2 or later; on older Rubies
292
+ # setting a value logs a warning.
70
293
  option :log_regexp_timeout, -> { Regexp.respond_to?(:timeout) ? (Regexp.timeout || 1.0) : nil }
71
294
 
72
- def initialize
295
+ def initialize # :nodoc:
73
296
  self.class.send(:defaults).each do |key, default|
74
297
  value = default.respond_to?(:call) ? instance_exec(&default) : default
75
298
  public_send("#{key}=", value)
76
299
  end
77
300
  end
78
301
 
79
- def instance_variables
80
- super.reject { |ivar| ivar.to_s.match?(/_id|_key|_secret|_token$/) }
302
+ def instance_variables # :nodoc:
303
+ super.reject { |ivar| ivar.to_s.match?(/(_id|_key|_secret|_token|_credential_provider)$/) }
304
+ end
305
+
306
+ # Object#inspect reads the instance variable table directly, so
307
+ # credentials only stay out of console and log output when #inspect is
308
+ # built from the redacted #instance_variables.
309
+ def inspect # :nodoc:
310
+ attributes = instance_variables.map { |ivar| "#{ivar}=#{instance_variable_get(ivar).inspect}" }
311
+ "#<#{self.class.name} #{attributes.join(', ')}>"
81
312
  end
82
313
 
83
- def log_regexp_timeout=(value)
84
- if value.nil?
85
- @log_regexp_timeout = nil
86
- elsif Regexp.respond_to?(:timeout)
87
- @log_regexp_timeout = value
88
- else
314
+ remove_method :log_regexp_timeout=
315
+ def log_regexp_timeout=(value) # :nodoc:
316
+ if value && !Regexp.respond_to?(:timeout)
89
317
  RubyLLM.logger.warn("log_regexp_timeout is not supported on Ruby #{RUBY_VERSION}")
90
- @log_regexp_timeout = value
91
318
  end
319
+ @log_regexp_timeout = value
320
+ end
321
+
322
+ private
323
+
324
+ # Whether the +name+ environment variable is set to something that means
325
+ # yes, so that <tt>RUBYLLM_DEBUG=false</tt> reads as off rather than as
326
+ # merely set.
327
+ def env_says_yes?(name)
328
+ %w[true 1 yes on].include?(ENV[name].to_s.strip.downcase)
92
329
  end
93
330
  end
94
331
  end
@@ -1,29 +1,151 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyLLM
4
- # Holds per-call configs
4
+ # A Context is an isolated configuration scope. It offers the same entry
5
+ # points as the top-level RubyLLM module but reads from its own
6
+ # Configuration copy instead of the global one, which suits multi-tenant
7
+ # applications and per-request overrides.
8
+ #
9
+ # Contexts are created with RubyLLM.context:
10
+ #
11
+ # ctx = RubyLLM.context do |config|
12
+ # config.openai_api_key = ENV.fetch('TENANT_OPENAI_API_KEY')
13
+ # config.request_timeout = 180
14
+ # end
15
+ #
16
+ # ctx.chat.ask "Explain Ruby blocks."
17
+ # ctx.paint("A paper boat in the rain").save("boat.png")
18
+ # ctx.transcribe("meeting.wav").text
19
+ #
20
+ # The global configuration is left untouched.
21
+ #
22
+ # Batch submission uses each chat's own configuration (a chat carries
23
+ # the config it was built with), so there is no context entry point for
24
+ # it. To look up an existing batch, where no chats carry the config,
25
+ # pass a context to Batch.find:
26
+ #
27
+ # RubyLLM::Batch.find(id, provider: :anthropic, context: ctx)
28
+ #
5
29
  class Context
6
- attr_reader :config
30
+ attr_reader :config # :nodoc:
7
31
 
8
- def initialize(config)
32
+ def initialize(config) # :nodoc:
9
33
  @config = config
10
- @connections = {}
11
34
  end
12
35
 
36
+ # Creates a new Chat that uses this context's configuration.
37
+ # Accepts the same arguments as RubyLLM.chat.
13
38
  def chat(*args, **kwargs, &)
14
39
  Chat.new(*args, **kwargs, context: self, &)
15
40
  end
16
41
 
42
+ # Counts tokens using this context's configuration.
43
+ # Accepts the same arguments as RubyLLM.count_tokens.
44
+ def count_tokens(text, model: nil, provider: nil)
45
+ chat(model:, provider:).count_tokens(text)
46
+ end
47
+
48
+ # Tokenizes plain text using this context's configuration.
49
+ # Accepts the same arguments as RubyLLM.tokenize.
50
+ def tokenize(*args, **kwargs)
51
+ Tokenization.tokenize(*args, **kwargs, context: self)
52
+ end
53
+
54
+ # Runs a named workflow using this context's instrumenter. Accepts the same
55
+ # arguments as RubyLLM.workflow.
56
+ def workflow(name, id: nil, metadata: nil, &)
57
+ Workflow.new(name, id:, metadata:, config: config).run(&)
58
+ end
59
+
60
+ # Generates embeddings using this context's configuration.
61
+ # Accepts the same arguments as RubyLLM.embed.
17
62
  def embed(*args, **kwargs, &)
18
63
  Embedding.embed(*args, **kwargs, context: self, &)
19
64
  end
20
65
 
66
+ # Stages an embedding request using this context's configuration.
67
+ # Accepts the same arguments as RubyLLM.embed_later.
68
+ def embed_later(text, model: nil, provider: nil, dimensions: nil)
69
+ EmbeddingRequest.new(text, model:, provider:, dimensions:, context: self)
70
+ end
71
+
72
+ # Generates an image using this context's configuration.
73
+ # Accepts the same arguments as RubyLLM.paint.
21
74
  def paint(*args, **kwargs, &)
22
75
  Image.paint(*args, **kwargs, context: self, &)
23
76
  end
24
77
 
25
- def connection_for(provider_instance)
26
- provider_instance.connection
78
+ # Generates a video using this context's configuration, blocking
79
+ # until it is ready. Accepts the same arguments as RubyLLM.animate.
80
+ def animate(*args, **kwargs, &)
81
+ Video.animate(*args, **kwargs, context: self, &)
82
+ end
83
+
84
+ # Submits a video generation job using this context's configuration.
85
+ # Accepts the same arguments as RubyLLM.animate_later.
86
+ def animate_later(*args, **kwargs, &)
87
+ VideoJob.animate_later(*args, **kwargs, context: self, &)
88
+ end
89
+
90
+ # Runs content moderation using this context's configuration.
91
+ # Accepts the same arguments as RubyLLM.moderate.
92
+ def moderate(*args, **kwargs, &)
93
+ Moderation.moderate(*args, **kwargs, context: self, &)
94
+ end
95
+
96
+ # Runs hosted research using this context's configuration.
97
+ # Accepts the same arguments as RubyLLM.research.
98
+ def research(*args, **kwargs)
99
+ ResearchJob.research(*args, **kwargs, context: self)
100
+ end
101
+
102
+ # Submits hosted research using this context's configuration.
103
+ # Accepts the same arguments as RubyLLM.research_later.
104
+ def research_later(*args, **kwargs)
105
+ ResearchJob.research_later(*args, **kwargs, context: self)
106
+ end
107
+
108
+ # Generates speech audio using this context's configuration. Given a
109
+ # block, yields SpeechChunk objects and returns the complete Speech.
110
+ # Accepts the same arguments as RubyLLM.speak.
111
+ def speak(*args, **kwargs, &)
112
+ Speech.speak(*args, **kwargs, context: self, &)
113
+ end
114
+
115
+ # Transcribes audio using this context's configuration.
116
+ # Accepts the same arguments as RubyLLM.transcribe.
117
+ def transcribe(*args, **kwargs, &)
118
+ Transcription.transcribe(*args, **kwargs, context: self, &)
119
+ end
120
+
121
+ # Extracts document text using this context's configuration.
122
+ # Accepts the same arguments as RubyLLM.ocr.
123
+ def ocr(*args, **kwargs, &)
124
+ OCR.ocr(*args, **kwargs, context: self, &)
125
+ end
126
+
127
+ # Ranks documents using this context's configuration.
128
+ # Accepts the same arguments as RubyLLM.rerank.
129
+ def rerank(*args, **kwargs, &)
130
+ Rerank.rerank(*args, **kwargs, context: self, &)
131
+ end
132
+
133
+ # Uploads a file to a provider using this context's configuration.
134
+ # Accepts the same arguments as RubyLLM.upload.
135
+ def upload(*args, **kwargs, &)
136
+ UploadedFile.upload(*args, **kwargs, context: self, &)
137
+ end
138
+
139
+ # Downloads a provider-hosted file using this context's configuration.
140
+ # Accepts the same arguments as RubyLLM.download.
141
+ def download(*args, **kwargs, &)
142
+ UploadedFile.download(*args, **kwargs, context: self, &)
143
+ end
144
+
145
+ # Creates a provider-side prompt cache using this context's
146
+ # configuration. Accepts the same arguments as RubyLLM.cache.
147
+ def cache(*args, **kwargs, &)
148
+ CachedContent.create(*args, **kwargs, context: self, &)
27
149
  end
28
150
  end
29
151
  end