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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ba1ac6556eb9321cc5c4840eb10c4f2253a35991298d3a4f9cf6dd85035ba069
4
- data.tar.gz: f87c2f1e7c1898c31ae732db0bae3a96f8b9c9082d8f9abd9a728b2b9d295426
3
+ metadata.gz: ef3c927159add14551e31ce03609ce42e150f9f161af9b68602f9c0e080fc31d
4
+ data.tar.gz: b894b99f6d6e541c643d76810b9542364f9d0beef4d00efd7b87b80d36c071af
5
5
  SHA512:
6
- metadata.gz: f75855b2dc79679febdf4edb67130f5ee60d7445bd1a34d7f29c69f3cd363711b881ffe2b51eac8fa4f2a74b297e364e53263e12674333055c8daf702d528a3a
7
- data.tar.gz: aceac99dd86c4e4db028411d1d58cd2602351f3ba0b3cb7fc2334d979459a97294f2617e10770d851b57f70aa26b49f9995dd239d1f52cff43814d1293419a75
6
+ metadata.gz: 7dacd51baf534c7b226e1db52f09421b72d9939ec17af6cfd4b18db8bd7d9fbe2a22b488a8571d64f2194d46ed82ba045a726a309465b448f3a31a077a1f98c1
7
+ data.tar.gz: 90f866305245b759ae821cd8932d518c8648f5a3afce588028b820ced1f8aa7b05ac5de7c55a6b0f89e5aac80052d348fbc3fec8cf3b9efc83dec5effa32b260
data/.rdoc_options ADDED
@@ -0,0 +1,25 @@
1
+ ---
2
+ title: RubyLLM
3
+ exclude:
4
+ - lib/ruby_llm/providers/
5
+ - lib/ruby_llm/protocols/
6
+ - lib/ruby_llm/protocol/
7
+ - lib/ruby_llm/transport/
8
+ - lib/ruby_llm/support/
9
+ - lib/ruby_llm/accounting/
10
+ - lib/ruby_llm/files/
11
+ - lib/ruby_llm/tools/
12
+ - lib/ruby_llm/models/
13
+ - lib/ruby_llm/chat/tool_concurrency\.rb$
14
+ - lib/ruby_llm/transcription/wav_audio\.rb$
15
+ - lib/tasks/
16
+ - lib/generators/
17
+ - \.json$
18
+ footer_content:
19
+ Documentation:
20
+ Guides: https://rubyllm.com/next/
21
+ Upgrading: https://rubyllm.com/next/upgrading/
22
+ Resources:
23
+ GitHub: https://github.com/crmne/ruby_llm
24
+ Issues: https://github.com/crmne/ruby_llm/issues
25
+ Releases: https://github.com/crmne/ruby_llm/releases
data/README.md CHANGED
@@ -5,8 +5,9 @@
5
5
  <img src="/docs/assets/images/logotype.svg" alt="RubyLLM" height="120" width="250">
6
6
  </picture>
7
7
 
8
- <strong>A single, beautiful Ruby framework for all major AI providers. Easily build chatbots, AI agents, RAG applications, content generators, and every AI workflow you can think of.
9
- </strong>
8
+ <strong>Build AI features the Ruby way</strong>
9
+
10
+ <p>The Ruby-native AI framework. Build with chats, tools, agents, images, audio, and video through one consistent API, in plain Ruby or Rails.</p>
10
11
 
11
12
  Battle tested at [<picture><source media="(prefers-color-scheme: dark)" srcset="https://chatwithwork.com/logotype-dark.svg"><img src="https://chatwithwork.com/logotype.svg" alt="Chat with Work" height="30" align="absmiddle"></picture>](https://chatwithwork.com) - *Fully private work AI*
12
13
 
@@ -23,7 +24,7 @@ Battle tested at [<picture><source media="(prefers-color-scheme: dark)" srcset="
23
24
 
24
25
  ---
25
26
 
26
- Build chatbots, AI agents, RAG applications. Works with OpenAI, xAI, Anthropic, Google, AWS, local models, and any OpenAI-compatible API.
27
+ Work with OpenAI, xAI, Anthropic, Google, AWS, local models, and more. Seventeen providers are built in, and you can connect an OpenAI-compatible endpoint directly.
27
28
 
28
29
  ## Build a working Ruby AI chat in two minutes
29
30
 
@@ -31,12 +32,14 @@ https://github.com/user-attachments/assets/65422091-9338-47da-a303-92b918bd1345
31
32
 
32
33
  ## Why RubyLLM?
33
34
 
34
- Every AI provider ships their own bloated client. Different APIs. Different response formats. Different conventions. It's exhausting.
35
+ Use the same Ruby methods across providers. Add files to a conversation, give an agent tools, generate media, or build a search feature with embeddings and reranking. Read response text, generated files, and usage through Ruby objects.
35
36
 
36
- RubyLLM gives you one beautiful framework for all of them. Same interface whether you're using GPT, Claude, or your local Ollama. Just three dependencies: Faraday, Zeitwerk, and Marcel. That's it.
37
+ In Rails, the API works on your own Chat and Message records, with Active Storage attachments, Hotwire streaming, and background jobs. RubyLLM maintains the supporting model registry, tool calls, usage ledger, and batches. A handful of small dependencies keeps it easy to bring into an existing application.
37
38
 
38
39
  ## Show me the code
39
40
 
41
+ These examples use **2.0.0.rc2**. Follow [Getting Started](https://rubyllm.com/next/getting-started/) to install it and configure the providers you want to try.
42
+
40
43
  ```ruby
41
44
  # Just ask questions
42
45
  chat = RubyLLM.chat
@@ -44,7 +47,8 @@ chat.ask "What's the best way to learn Ruby?"
44
47
  ```
45
48
 
46
49
  ```ruby
47
- # Analyze any file type
50
+ # Ask about files with a model that supports their input types
51
+ chat = RubyLLM.chat(model: "gemini-3.7-flash")
48
52
  chat.ask "What's in this image?", with: "ruby_conf.jpg"
49
53
  chat.ask "What's happening in this video?", with: "video.mp4"
50
54
  chat.ask "Describe this meeting", with: "meeting.wav"
@@ -66,28 +70,56 @@ end
66
70
 
67
71
  ```ruby
68
72
  # Generate images
69
- RubyLLM.paint "a sunset over mountains in watercolor style"
73
+ image = RubyLLM.paint "a sunset over mountains in watercolor style"
74
+ image.save "sunset.png"
75
+ ```
76
+
77
+ ```ruby
78
+ # Generate videos
79
+ video = RubyLLM.animate "a paper boat sailing down a rainy gutter"
80
+ video.save "paper_boat.mp4"
70
81
  ```
71
82
 
72
83
  ```ruby
73
84
  # Create embeddings
74
- RubyLLM.embed "Ruby is elegant and expressive"
85
+ embedding = RubyLLM.embed "Ruby is elegant and expressive"
86
+ embedding.vectors
87
+ ```
88
+
89
+ ```ruby
90
+ # Rank search results
91
+ documents = ["Reset your password in Settings.", "Invoices arrive by email."]
92
+ ranked = RubyLLM.rerank("How do I reset my password?", documents, model: "rerank-v3.5")
93
+ ranked.results.first.document
75
94
  ```
76
95
 
77
96
  ```ruby
78
97
  # Transcribe audio to text
79
- RubyLLM.transcribe "meeting.wav"
98
+ transcript = RubyLLM.transcribe "meeting.wav"
99
+ puts transcript.text
100
+ ```
101
+
102
+ ```ruby
103
+ # Turn text into speech
104
+ speech = RubyLLM.speak "Hello, welcome to RubyLLM!"
105
+ speech.save "welcome.mp3"
80
106
  ```
81
107
 
82
108
  ```ruby
83
- # Moderate content for safety
84
- RubyLLM.moderate "Check if this text is safe"
109
+ # Extract document text as markdown
110
+ document = RubyLLM.ocr "contract.pdf"
111
+ puts document.markdown
112
+ ```
113
+
114
+ ```ruby
115
+ # Check whether a moderation model flags content
116
+ RubyLLM.moderate("Some user-generated content").flagged?
85
117
  ```
86
118
 
87
119
  ```ruby
88
120
  # Let AI use your code
89
121
  class Weather < RubyLLM::Tool
90
- desc "Get current weather"
122
+ description "Get current weather"
91
123
 
92
124
  def execute(latitude:, longitude:)
93
125
  url = "https://api.open-meteo.com/v1/forecast?latitude=#{latitude}&longitude=#{longitude}&current=temperature_2m,wind_speed_10m"
@@ -95,13 +127,13 @@ class Weather < RubyLLM::Tool
95
127
  end
96
128
  end
97
129
 
98
- chat.with_tool(Weather).ask "What's the weather in Berlin?"
130
+ chat.with_tools(Weather).ask "What's the weather in Berlin?"
99
131
  ```
100
132
 
101
133
  ```ruby
102
134
  # Define an agent with instructions + tools
103
135
  class WeatherAssistant < RubyLLM::Agent
104
- model "gpt-5-nano"
136
+ model "gpt-5.6-luna"
105
137
  instructions "Be concise and always use tools for weather."
106
138
  tools Weather
107
139
  end
@@ -111,7 +143,7 @@ WeatherAssistant.new.ask "What's the weather in Berlin?"
111
143
 
112
144
  ```ruby
113
145
  # Get structured output
114
- class ProductSchema < RubyLLM::Schema
146
+ class ProductSchema < Schematist::Schema
115
147
  string :name
116
148
  number :price
117
149
  array :features do
@@ -120,50 +152,71 @@ class ProductSchema < RubyLLM::Schema
120
152
  end
121
153
 
122
154
  response = chat.with_schema(ProductSchema).ask "Analyze this product", with: "product.txt"
155
+ response.parsed
123
156
  ```
124
157
 
125
158
  ## Features
126
159
 
127
160
  * **Chat:** Conversational AI with `RubyLLM.chat`
128
161
  * **Vision:** Analyze images and videos
129
- * **Audio:** Transcribe and understand speech with `RubyLLM.transcribe`
130
- * **Documents:** Extract from PDFs, CSVs, JSON, any file type
162
+ * **Audio:** Transcribe speech with `RubyLLM.transcribe` and generate it with `RubyLLM.speak`
163
+ * **Documents:** Ask questions about PDFs, text files, and other supported formats
164
+ * **OCR:** Turn documents into markdown with `RubyLLM.ocr`
131
165
  * **Image generation:** Create images with `RubyLLM.paint`
166
+ * **Video generation:** Create videos with `RubyLLM.animate`
132
167
  * **Embeddings:** Generate embeddings with `RubyLLM.embed`
133
- * **Moderation:** Content safety with `RubyLLM.moderate`
168
+ * **Reranking:** Order retrieval candidates by relevance with `RubyLLM.rerank`
169
+ * **Moderation:** Content flags, categories, and scores with `RubyLLM.moderate`
134
170
  * **Tools:** Let AI call your Ruby methods
171
+ * **Tool approval:** Park a run until a human approves with `requires_approval`
172
+ * **The agentic loop:** Drive it yourself with `ask_later`, `step`, and `complete?`
173
+ * **Server tools:** Web search, code execution, and MCP connectors with `with_server_tools`
135
174
  * **Agents:** Reusable assistants with `RubyLLM::Agent`
136
- * **Structured output:** JSON schemas that just work
175
+ * **Prompt templates:** ERB prompts in `app/prompts`, rendered with `RubyLLM.render_prompt`
176
+ * **Workflows:** Correlate multi-agent runs in your telemetry with `RubyLLM.workflow`
177
+ * **Structured output:** Define a Ruby schema and read the result with `response.parsed`
137
178
  * **Streaming:** Real-time responses with blocks
138
- * **Rails:** ActiveRecord integration with `acts_as_chat`
179
+ * **Rails:** Active Record persistence, Active Storage attachments, Hotwire streaming, and generators
180
+ * **Files:** Upload once and reuse across chats with `RubyLLM.upload`
181
+ * **Prompt caching:** Turn on the provider's cache with `with_caching` and `cache_until_here`
182
+ * **Fallbacks and cancellation:** Retry on backup models with `with_fallbacks`, stop a run with `cancel`
183
+ * **Cost tracking:** A per-attempt usage ledger behind `chat.tokens` and `chat.cost`
139
184
  * **Async:** Fiber-based concurrency
140
- * **Model registry:** 800+ models with capability detection and pricing
185
+ * **Model registry:** Browse capabilities, limits, and pricing across providers
141
186
  * **Extended thinking:** Control, view, and persist model deliberation
142
- * **Providers:** OpenAI, xAI, Anthropic, Gemini, VertexAI, Bedrock, DeepSeek, Mistral, Ollama, OpenRouter, Perplexity, GPUStack, and any OpenAI-compatible API
187
+ * **Citations:** Normalized source citations from documents, search, and grounding
188
+ * **Batches:** Provider-side batch processing with provider-specific discounts via `RubyLLM.batch`
189
+ * **Compaction:** Let providers condense long conversations with `with_compaction`
190
+ * **Token counting:** Count a request before you send it with `count_tokens`
191
+ * **Providers:** OpenAI, Azure, xAI, Anthropic, Gemini, VertexAI, Bedrock, Cohere, DeepSeek, Mistral, Ollama, Ollama Cloud, OpenRouter, Perplexity, GPUStack, ElevenLabs, Deepgram, and any OpenAI-compatible API
143
192
 
144
193
  ## Installation
145
194
 
146
- Add to your Gemfile:
147
- ```ruby
148
- gem 'ruby_llm'
195
+ Install the 2.0 release candidate:
196
+
197
+ ```bash
198
+ bundle add ruby_llm --version 2.0.0.rc2
149
199
  ```
150
- Then `bundle install`.
151
200
 
152
- Configure your API keys:
201
+ Configure a provider in your script, or in `config/initializers/ruby_llm.rb` in Rails:
202
+
153
203
  ```ruby
154
- # config/initializers/ruby_llm.rb
204
+ require 'ruby_llm'
205
+
155
206
  RubyLLM.configure do |config|
156
- config.openai_api_key = ENV['OPENAI_API_KEY']
207
+ config.openai_api_key = ENV.fetch('OPENAI_API_KEY')
157
208
  end
158
209
  ```
159
210
 
211
+ Configure the other providers used by the examples as needed: Gemini for files, xAI for video, Mistral for OCR, and Cohere for reranking. [Getting Started](https://rubyllm.com/next/getting-started/) shows each setup beside its example. If your app uses 1.16, follow the [upgrade guide](https://rubyllm.com/next/upgrading/) before deploying 2.0.
212
+
160
213
  ## Rails
161
214
 
162
215
  ```bash
163
216
  # Install Rails Integration
164
217
  bin/rails generate ruby_llm:install
165
218
  bin/rails db:migrate
166
- bin/rails ruby_llm:load_models # v1.13+
219
+ bin/rails ruby_llm:load_models
167
220
 
168
221
  # Add Chat UI (optional)
169
222
  bin/rails generate ruby_llm:chat_ui
@@ -174,7 +227,7 @@ class Chat < ApplicationRecord
174
227
  acts_as_chat
175
228
  end
176
229
 
177
- chat = Chat.create! model: "claude-sonnet-4"
230
+ chat = Chat.create! model: "gpt-5.6-luna"
178
231
  chat.ask "What's in this file?", with: "report.pdf"
179
232
  ```
180
233
 
@@ -182,7 +235,7 @@ Visit `http://localhost:3000/chats` for a ready-to-use chat interface!
182
235
 
183
236
  ## Documentation
184
237
 
185
- [rubyllm.com](https://rubyllm.com)
238
+ [Guides](https://rubyllm.com/next/getting-started/) · [API reference](https://rubyllm.com/next/api/) · [Models](https://rubyllm.com/available-models/) · [Upgrading](https://rubyllm.com/next/upgrading/)
186
239
 
187
240
  ## Contributing
188
241
 
data/exe/ruby_llm ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
5
+
6
+ require 'generators/ruby_llm/provider/cli'
7
+
8
+ exit RubyLLM::Generators::Provider::CLI.run(ARGV)
@@ -2,5 +2,4 @@ class <%= agent_class_name %> < RubyLLM::Agent
2
2
  # Change `Chat` to your app's chat model for Rails persistence.
3
3
  # Remove this line to skip persistence and use plain RubyLLM chats.
4
4
  chat_model Chat
5
- instructions
6
5
  end
@@ -28,8 +28,6 @@ module RubyLLM
28
28
  args = []
29
29
  args << "chat:#{chat_model_name}" if chat_model_name != 'Chat'
30
30
  args << "message:#{message_model_name}" if message_model_name != 'Message'
31
- args << "model:#{model_model_name}" if model_model_name != 'Model'
32
- args << "tool_call:#{tool_call_model_name}" if tool_call_model_name != 'ToolCall'
33
31
  arg_string = args.any? ? " #{args.join(' ')}" : ''
34
32
 
35
33
  raise Thor::Error, <<~ERROR
@@ -37,9 +35,6 @@ module RubyLLM
37
35
 
38
36
  Please run the install generator first:
39
37
  bin/rails generate ruby_llm:install#{arg_string}
40
-
41
- Or if upgrading from <= 1.6.x, run the upgrade generator:
42
- bin/rails generate ruby_llm:upgrade_to_v1_7#{arg_string}
43
38
  ERROR
44
39
  end
45
40
 
@@ -114,6 +109,7 @@ module RubyLLM
114
109
  return if application_controller.include?('def available_chat_models')
115
110
 
116
111
  inject_into_file path, <<-RUBY, before: /^end\s*\z/
112
+
117
113
  private
118
114
 
119
115
  def available_chat_models
@@ -133,7 +129,7 @@ module RubyLLM
133
129
 
134
130
  routes_content = <<~ROUTES.strip
135
131
  namespace :#{namespace} do
136
- resources :#{model_resource}, only: [ :index, :show ] do
132
+ resources :#{model_resource}, only: [ :index, :show ], constraints: { id: /[^\\/]+/ } do
137
133
  collection do
138
134
  post :refresh
139
135
  end
@@ -146,7 +142,7 @@ module RubyLLM
146
142
  route routes_content
147
143
  else
148
144
  model_routes = <<~ROUTES.strip
149
- resources :#{model_table_name}, only: [ :index, :show ] do
145
+ resources :#{model_resource_name}, only: [ :index, :show ], constraints: { id: /[^\\/]+/ } do
150
146
  collection do
151
147
  post :refresh
152
148
  end
@@ -208,26 +204,6 @@ module RubyLLM
208
204
 
209
205
  private
210
206
 
211
- def ui_variant
212
- @ui_variant ||= case options[:ui]
213
- when 'tailwind'
214
- :tailwind
215
- when 'auto'
216
- tailwind_available? ? :tailwind : :scaffold
217
- else
218
- :scaffold
219
- end
220
- end
221
-
222
- def ui_template(template_path)
223
- return template_path unless ui_variant == :tailwind
224
-
225
- # Keep Tailwind templates as a separate set so we can mirror Rails/Tailwind
226
- # scaffold conventions without complicating scaffold templates.
227
- tailwind_template = "tailwind/#{template_path}"
228
- File.exist?(File.join(self.class.source_root, "#{tailwind_template}.tt")) ? tailwind_template : template_path
229
- end
230
-
231
207
  def message_helper_module_name
232
208
  if message_model_name.include?('::')
233
209
  "#{message_model_name.deconstantize}::#{message_model_name.demodulize.pluralize}Helper"
@@ -235,22 +211,6 @@ module RubyLLM
235
211
  "#{message_model_name.pluralize}Helper"
236
212
  end
237
213
  end
238
-
239
- def tailwind_available?
240
- Rails.root.join('app/assets/tailwind/application.css').exist? ||
241
- Rails.root.join('config/tailwind.config.js').exist? ||
242
- gem_in_bundle?('tailwindcss-rails') ||
243
- gem_in_bundle?('cssbundling-rails')
244
- end
245
-
246
- def gem_in_bundle?(gem_name)
247
- gemfile_path = Rails.root.join('Gemfile')
248
- lockfile_path = Rails.root.join('Gemfile.lock')
249
-
250
- [gemfile_path, lockfile_path].any? do |path|
251
- path.exist? && path.read.include?(gem_name)
252
- end
253
- end
254
214
  end
255
215
  end
256
216
  end
@@ -7,22 +7,30 @@ class <%= chat_controller_class_name %> < ApplicationController
7
7
 
8
8
  def new
9
9
  @<%= chat_variable_name %> = <%= chat_model_name %>.new
10
- @selected_model = params[:model]
10
+ @selected_model = params[:provider] ? [ params[:provider], params[:model] ].join(":") : params[:model]
11
11
  @chat_models = available_chat_models
12
12
  end
13
13
 
14
14
  def create
15
15
  prompt = params.dig(:<%= chat_variable_name %>, :prompt)
16
16
  if prompt.present?
17
- @<%= chat_variable_name %> = <%= chat_model_name %>.create!(model: params.dig(:<%= chat_variable_name %>, :model).presence)
17
+ provider, model = params.dig(:<%= chat_variable_name %>, :model).to_s.split(":", 2)
18
+ @<%= chat_variable_name %> = <%= chat_model_name %>.create!(model: model.presence, provider: provider.presence)
18
19
  <%= chat_job_class_name %>.perform_later(@<%= chat_variable_name %>.id, prompt)
19
20
 
20
21
  redirect_to @<%= chat_variable_name %>, notice: "<%= chat_model_name.humanize %> was successfully created."
22
+ else
23
+ @<%= chat_variable_name %> = <%= chat_model_name %>.new
24
+ @<%= chat_variable_name %>.errors.add(:prompt, "can't be blank")
25
+ @selected_model = params.dig(:<%= chat_variable_name %>, :model)
26
+ @chat_models = available_chat_models
27
+
28
+ render :new, status: <%= unprocessable_content_status.inspect %>
21
29
  end
22
30
  end
23
31
 
24
32
  def show
25
- @<%= message_variable_name %> = @<%= chat_variable_name %>.<%= message_table_name %>.build
33
+ @<%= message_variable_name %> = <%= message_model_name %>.new
26
34
  end
27
35
 
28
36
  def destroy
@@ -10,6 +10,14 @@ class <%= message_controller_class_name %> < ApplicationController
10
10
  format.turbo_stream
11
11
  format.html { redirect_to @<%= chat_variable_name %> }
12
12
  end
13
+ else
14
+ @<%= message_variable_name %> = @<%= chat_variable_name %>.<%= message_table_name %>.build
15
+ @<%= message_variable_name %>.errors.add(:content, "can't be blank")
16
+
17
+ respond_to do |format|
18
+ format.turbo_stream { render :create, status: <%= unprocessable_content_status.inspect %> }
19
+ format.html { render "<%= chat_model_name.underscore.pluralize %>/show", status: <%= unprocessable_content_status.inspect %> }
20
+ end
13
21
  end
14
22
  end
15
23
 
@@ -1,14 +1,14 @@
1
1
  class <%= model_controller_class_name %> < ApplicationController
2
2
  def index
3
- @<%= model_table_name %> = available_chat_models
3
+ @<%= model_resource_name %> = available_chat_models
4
4
  end
5
5
 
6
6
  def show
7
- @<%= model_variable_name %> = <%= model_model_name %>.find(params[:id])
7
+ @<%= model_variable_name %> = RubyLLM.models.find(params[:id], params[:provider])
8
8
  end
9
9
 
10
10
  def refresh
11
- <%= model_model_name %>.refresh!
12
- redirect_to <%= model_table_name %>_path, notice: "<%= model_model_name.pluralize %> refreshed successfully"
11
+ RubyLLM.models.refresh
12
+ redirect_to <%= model_resource_name %>_path, notice: "<%= model_model_name.pluralize %> refreshed successfully"
13
13
  end
14
14
  end
@@ -1,7 +1,7 @@
1
1
  <div id="<%%= dom_id <%= chat_model_name.demodulize.underscore %> %>" class="w-full sm:w-auto my-5 space-y-3">
2
2
  <div>
3
3
  <strong class="block font-medium mb-1">Model:</strong>
4
- <%%= <%= chat_model_name.demodulize.underscore %>.<%= model_table_name.singularize %>&.label || default_model_display_name %>
4
+ <%%= <%= chat_model_name.demodulize.underscore %>.model&.label || default_model_display_name %>
5
5
  </div>
6
6
 
7
7
  <div>
@@ -14,7 +14,7 @@
14
14
  <div class="my-5">
15
15
  <%%= form.label :model, "Select AI model:" %>
16
16
  <%%= form.select :model,
17
- options_for_select(@chat_models.map { |model| [model.label, model.id] }.unshift([default_model_display_name, nil]), @selected_model),
17
+ options_for_select(@chat_models.map { |model| [model.label, [model.provider, model.id].join(":")] }.unshift([default_model_display_name, nil]), @selected_model),
18
18
  {},
19
19
  class: ["block shadow-sm rounded-md border px-3 py-2 mt-2 w-full", {"border-gray-400 focus:outline-blue-600": <%= chat_variable_name %>.errors[:model].none?, "border-red-400 focus:outline-red-600": <%= chat_variable_name %>.errors[:model].any?}] %>
20
20
  </div>
@@ -8,7 +8,7 @@
8
8
  <div class="flex justify-between items-center">
9
9
  <h1 class="font-bold text-4xl"><%= chat_model_name.pluralize %></h1>
10
10
  <div class="flex items-center gap-2">
11
- <%%= link_to "<%= model_model_name.pluralize %>", <%= model_table_name %>_path, class: "rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 text-gray-900 block font-medium" %>
11
+ <%%= link_to "<%= model_model_name.pluralize %>", <%= model_resource_name %>_path, class: "rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 text-gray-900 block font-medium" %>
12
12
  <%%= link_to "New <%= chat_table_name.singularize.humanize.downcase %>", new_<%= chat_variable_name %>_path, class: "rounded-md px-3.5 py-2.5 bg-blue-600 hover:bg-blue-500 text-white block font-medium" %>
13
13
  </div>
14
14
  </div>
@@ -12,11 +12,11 @@
12
12
 
13
13
  <div class="my-5">
14
14
  <strong class="block font-medium mb-1">Model:</strong>
15
- <%%= @<%= chat_variable_name %>.<%= model_table_name.singularize %>&.label || default_model_display_name %>
15
+ <%%= @<%= chat_variable_name %>.model&.label || default_model_display_name %>
16
16
  </div>
17
17
 
18
18
  <div id="<%= message_table_name %>" class="min-w-full divide-y divide-gray-200 space-y-5 my-5">
19
- <%% @<%= chat_variable_name %>.<%= message_table_name %>.where.not(id: nil).each do |<%= message_variable_name %>| %>
19
+ <%% @<%= chat_variable_name %>.<%= message_table_name %>.each do |<%= message_variable_name %>| %>
20
20
  <%%= render <%= message_variable_name %> %>
21
21
  <%% end %>
22
22
  </div>
@@ -1,4 +1,4 @@
1
- <%% assistant ||= local_assigns[:message] %>
1
+ <%% assistant ||= local_assigns[:<%= message_model_name.demodulize.underscore %>] %>
2
2
  <div id="<%= message_variable_name %>_<%%= assistant.id %>" class="w-full sm:w-auto my-5 space-y-3 rounded-md px-3 py-2 bg-green-50">
3
3
  <div>
4
4
  <span class="inline-block rounded px-2 py-0.5 text-xs font-medium bg-green-100 text-green-700">
@@ -1,4 +1,4 @@
1
- <%% system ||= local_assigns[:message] %>
1
+ <%% system ||= local_assigns[:<%= message_model_name.demodulize.underscore %>] %>
2
2
  <div id="<%= message_variable_name %>_<%%= system.id %>" class="w-full sm:w-auto my-5 space-y-3 rounded-md px-3 py-2 bg-gray-50 border border-gray-200">
3
3
  <div>
4
4
  <span class="inline-block rounded px-2 py-0.5 text-xs font-medium bg-gray-200 text-gray-700">
@@ -1,2 +1,2 @@
1
- <%% tool ||= local_assigns[:message] %>
1
+ <%% tool ||= local_assigns[:<%= message_model_name.demodulize.underscore %>] %>
2
2
  <%%= render tool_result_partial(tool), tool: tool %>
@@ -1,4 +1,6 @@
1
- <%% tool_calls ||= local_assigns[:message] %>
2
- <%% tool_calls.<%= tool_call_variable_name.pluralize %>.each do |tool_call| %>
3
- <%%= render tool_call_partial(tool_call), tool_calls: tool_calls, tool_call: tool_call %>
4
- <%% end %>
1
+ <%% message = local_assigns[:<%= message_model_name.demodulize.underscore %>] || tool_calls %>
2
+ <div id="<%= message_variable_name %>_<%%= message.id %>">
3
+ <%% message.<%= tool_call_variable_name.pluralize %>.each_value do |tool_call| %>
4
+ <%%= render tool_call_partial(tool_call), message: message, tool_call: tool_call %>
5
+ <%% end %>
6
+ </div>
@@ -1,4 +1,4 @@
1
- <%% user ||= local_assigns[:message] %>
1
+ <%% user ||= local_assigns[:<%= message_model_name.demodulize.underscore %>] %>
2
2
  <div id="<%= message_variable_name %>_<%%= user.id %>" class="w-full sm:w-auto my-5 space-y-3 rounded-md px-3 py-2 bg-blue-50">
3
3
  <div>
4
4
  <span class="inline-block rounded px-2 py-0.5 text-xs font-medium bg-blue-100 text-blue-700">
@@ -1,4 +1,4 @@
1
- <div id="<%= message_variable_name %>_<%%= tool_calls.id %>" class="w-full sm:w-auto my-5 space-y-3 rounded-md px-3 py-2 bg-gray-50 border border-gray-200">
1
+ <div id="<%= message_variable_name %>_tool_call_<%%= tool_call.id %>" class="w-full sm:w-auto my-5 space-y-3 rounded-md px-3 py-2 bg-gray-50 border border-gray-200">
2
2
  <div>
3
3
  <span class="inline-block rounded px-2 py-0.5 text-xs font-medium bg-gray-200 text-gray-700">
4
4
  Tool Call
@@ -8,6 +8,6 @@
8
8
  <pre class="whitespace-pre-wrap text-gray-700 text-sm overflow-x-auto"><%%= tool_call.name %>(<%%= tool_call.arguments.map { |k, v| "#{k}: #{v.inspect}" }.join(", ") %>)</pre>
9
9
 
10
10
  <div>
11
- <span class="text-sm text-gray-600"><%%= tool_calls.created_at&.strftime("%I:%M %p") %></span>
11
+ <span class="text-sm text-gray-600"><%%= message.created_at&.strftime("%I:%M %p") %></span>
12
12
  </div>
13
13
  </div>
@@ -1,17 +1,16 @@
1
- <%% model_id = <%= model_model_name.demodulize.underscore %>.respond_to?(:model_id) ? <%= model_model_name.demodulize.underscore %>.model_id : <%= model_model_name.demodulize.underscore %>.id %>
2
- <%% row_id = [<%= model_model_name.demodulize.underscore %>.provider, model_id].join("_").parameterize(separator: "_") %>
1
+ <%% row_id = [<%= model_model_name.demodulize.underscore %>.provider, <%= model_model_name.demodulize.underscore %>.id].join("_").parameterize(separator: "_") %>
3
2
  <tr id="model_<%%= row_id %>">
4
3
  <td class="px-3 py-2 text-sm text-gray-700"><%%= <%= model_model_name.demodulize.underscore %>.provider_class&.name || <%= model_model_name.demodulize.underscore %>.provider %></td>
5
- <td class="px-3 py-2 text-sm text-gray-900"><%%= <%= model_model_name.demodulize.underscore %>.respond_to?(:display_name) ? <%= model_model_name.demodulize.underscore %>.display_name : <%= model_model_name.demodulize.underscore %>.name %></td>
4
+ <td class="px-3 py-2 text-sm text-gray-900"><%%= <%= model_model_name.demodulize.underscore %>.name %></td>
6
5
  <td class="px-3 py-2 text-sm text-gray-700"><%%= number_with_delimiter(<%= model_model_name.demodulize.underscore %>.context_window) if <%= model_model_name.demodulize.underscore %>.context_window %></td>
7
6
  <td class="px-3 py-2 text-sm text-gray-700">
8
- <%% input = <%= model_model_name.demodulize.underscore %>.input_price_per_million %>
9
- <%% output = <%= model_model_name.demodulize.underscore %>.output_price_per_million %>
7
+ <%% input = <%= model_model_name.demodulize.underscore %>.price(:input) %>
8
+ <%% output = <%= model_model_name.demodulize.underscore %>.price(:output) %>
10
9
  <%% if input && output %>
11
10
  $<%%= "%.2f" % input %> / $<%%= "%.2f" % output %>
12
11
  <%% end %>
13
12
  </td>
14
13
  <td class="px-3 py-2 text-sm">
15
- <%%= link_to "Start <%= chat_table_name.singularize.humanize.downcase %>", new_<%= chat_variable_name %>_path(model: model_id), class: "text-blue-600 hover:text-blue-500" %>
14
+ <%%= link_to "Start <%= chat_table_name.singularize.humanize.downcase %>", new_<%= chat_variable_name %>_path(model: [<%= model_model_name.demodulize.underscore %>.provider, <%= model_model_name.demodulize.underscore %>.id].join(":")), class: "text-blue-600 hover:text-blue-500" %>
16
15
  </td>
17
16
  </tr>
@@ -9,11 +9,11 @@
9
9
  <h1 class="font-bold text-4xl"><%= model_model_name.pluralize %></h1>
10
10
  <div class="flex items-center gap-2">
11
11
  <%%= link_to "<%= chat_model_name.pluralize %>", <%= chat_table_name %>_path, class: "rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 text-gray-900 block font-medium" %>
12
- <%%= button_to "Refresh <%= model_model_name.pluralize %>", refresh_<%= model_table_name %>_path, method: :post, class: "rounded-md px-3.5 py-2.5 bg-blue-600 hover:bg-blue-500 text-white block font-medium cursor-pointer" %>
12
+ <%%= button_to "Refresh <%= model_model_name.pluralize %>", refresh_<%= model_resource_name %>_path, method: :post, class: "rounded-md px-3.5 py-2.5 bg-blue-600 hover:bg-blue-500 text-white block font-medium cursor-pointer" %>
13
13
  </div>
14
14
  </div>
15
15
 
16
- <div id="<%= model_table_name %>" class="mt-5 overflow-x-auto">
16
+ <div id="<%= model_resource_name %>" class="mt-5 overflow-x-auto">
17
17
  <%% if @<%= model_variable_name.pluralize %>.any? %>
18
18
  <table class="min-w-full divide-y divide-gray-300 border border-gray-300">
19
19
  <thead class="bg-gray-50">
@@ -1,16 +1,16 @@
1
- <%% content_for :title, "Showing <%= model_table_name.singularize.humanize.downcase %>" %>
1
+ <%% content_for :title, "Showing <%= model_resource_name.singularize.humanize.downcase %>" %>
2
2
 
3
3
  <div class="md:w-2/3 w-full">
4
4
  <%% if notice.present? %>
5
5
  <p class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-md inline-block" id="notice"><%%= notice %></p>
6
6
  <%% end %>
7
7
 
8
- <h1 class="font-bold text-4xl">Showing <%= model_table_name.singularize.humanize.downcase %></h1>
8
+ <h1 class="font-bold text-4xl">Showing <%= model_resource_name.singularize.humanize.downcase %></h1>
9
9
 
10
10
  <div class="my-5 space-y-3">
11
11
  <p><strong class="block font-medium mb-1">Provider:</strong> <%%= @<%= model_variable_name %>.provider_class&.name || @<%= model_variable_name %>.provider %></p>
12
12
  <p><strong class="block font-medium mb-1">Model:</strong> <%%= @<%= model_variable_name %>.name %></p>
13
- <p><strong class="block font-medium mb-1">ID:</strong> <%%= @<%= model_variable_name %>.model_id %></p>
13
+ <p><strong class="block font-medium mb-1">ID:</strong> <%%= @<%= model_variable_name %>.id %></p>
14
14
  <p><strong class="block font-medium mb-1">Context Window:</strong> <%%= number_with_delimiter(@<%= model_variable_name %>.context_window) if @<%= model_variable_name %>.context_window %></p>
15
15
  <p><strong class="block font-medium mb-1">Max Output Tokens:</strong> <%%= number_with_delimiter(@<%= model_variable_name %>.max_output_tokens) if @<%= model_variable_name %>.max_output_tokens %></p>
16
16
  </div>
@@ -22,6 +22,6 @@
22
22
  </div>
23
23
  <%% end %>
24
24
 
25
- <%%= link_to "Start <%= chat_table_name.singularize.humanize.downcase %> with this model", new_<%= chat_variable_name %>_path(model: @<%= model_variable_name %>.model_id), class: "w-full sm:w-auto text-center rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 inline-block font-medium" %>
26
- <%%= link_to "All <%= model_table_name.humanize.downcase %>", <%= model_table_name %>_path, class: "w-full sm:w-auto text-center mt-2 sm:mt-0 sm:ml-2 rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 inline-block font-medium" %>
25
+ <%%= link_to "Start <%= chat_table_name.singularize.humanize.downcase %> with this model", new_<%= chat_variable_name %>_path(model: [@<%= model_variable_name %>.provider, @<%= model_variable_name %>.id].join(":")), class: "w-full sm:w-auto text-center rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 inline-block font-medium" %>
26
+ <%%= link_to "All <%= model_resource_name.humanize.downcase %>", <%= model_resource_name %>_path, class: "w-full sm:w-auto text-center mt-2 sm:mt-0 sm:ml-2 rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 inline-block font-medium" %>
27
27
  </div>