ruby_llm 1.15.0 → 2.0.0.rc1

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 (470) hide show
  1. checksums.yaml +4 -4
  2. data/.rdoc_options +25 -0
  3. data/README.md +87 -33
  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 -6
  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 +25 -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 +100 -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 -136
  100. data/lib/ruby_llm/active_record/attachment_helpers.rb +180 -0
  101. data/lib/ruby_llm/active_record/batch.rb +97 -0
  102. data/lib/ruby_llm/active_record/chat_methods.rb +823 -305
  103. data/lib/ruby_llm/active_record/message_methods.rb +119 -75
  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 -150
  109. data/lib/ruby_llm/aliases.json +338 -167
  110. data/lib/ruby_llm/attachment.rb +217 -61
  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 +1208 -150
  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 +274 -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 +141 -7
  122. data/lib/ruby_llm/embedding_request.rb +53 -0
  123. data/lib/ruby_llm/error.rb +161 -89
  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 +155 -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 +43 -14
  130. data/lib/ruby_llm/model/pricing_category.rb +103 -14
  131. data/lib/ruby_llm/model/pricing_tier.rb +66 -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 +70380 -33380
  137. data/lib/ruby_llm/models.rb +528 -201
  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 +540 -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 +166 -0
  152. data/lib/ruby_llm/{providers → protocols}/anthropic/tools.rb +43 -34
  153. data/lib/ruby_llm/protocols/anthropic.rb +100 -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/protocols/chat_completions/media.rb +121 -0
  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 +17 -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 +685 -0
  190. data/lib/ruby_llm/protocols/converse/media.rb +178 -0
  191. data/lib/ruby_llm/protocols/converse/streaming.rb +424 -0
  192. data/lib/ruby_llm/protocols/converse.rb +54 -0
  193. data/lib/ruby_llm/protocols/deepgram/models.rb +74 -0
  194. data/lib/ruby_llm/protocols/deepgram/speech.rb +93 -0
  195. data/lib/ruby_llm/protocols/deepgram/streaming_transcription.rb +89 -0
  196. data/lib/ruby_llm/protocols/deepgram/transcription.rb +96 -0
  197. data/lib/ruby_llm/protocols/deepgram.rb +19 -0
  198. data/lib/ruby_llm/protocols/deepseek/files.rb +31 -0
  199. data/lib/ruby_llm/protocols/elevenlabs/assets.rb +36 -0
  200. data/lib/ruby_llm/protocols/elevenlabs/flows/images.rb +74 -0
  201. data/lib/ruby_llm/protocols/elevenlabs/flows/media.rb +42 -0
  202. data/lib/ruby_llm/protocols/elevenlabs/flows/videos.rb +93 -0
  203. data/lib/ruby_llm/protocols/elevenlabs/flows.rb +14 -0
  204. data/lib/ruby_llm/protocols/elevenlabs/models.rb +64 -0
  205. data/lib/ruby_llm/protocols/elevenlabs/speech.rb +67 -0
  206. data/lib/ruby_llm/protocols/elevenlabs/streaming_transcription.rb +127 -0
  207. data/lib/ruby_llm/protocols/elevenlabs/transcription.rb +61 -0
  208. data/lib/ruby_llm/protocols/elevenlabs.rb +15 -0
  209. data/lib/ruby_llm/protocols/files.rb +119 -0
  210. data/lib/ruby_llm/protocols/gemini/batches.rb +162 -0
  211. data/lib/ruby_llm/protocols/gemini/caches.rb +59 -0
  212. data/lib/ruby_llm/protocols/gemini/chat.rb +453 -0
  213. data/lib/ruby_llm/protocols/gemini/embedding_batches.rb +86 -0
  214. data/lib/ruby_llm/protocols/gemini/embeddings.rb +70 -0
  215. data/lib/ruby_llm/protocols/gemini/file_transcription.rb +32 -0
  216. data/lib/ruby_llm/protocols/gemini/files.rb +115 -0
  217. data/lib/ruby_llm/protocols/gemini/images.rb +183 -0
  218. data/lib/ruby_llm/protocols/gemini/live_transcription.rb +140 -0
  219. data/lib/ruby_llm/{providers → protocols}/gemini/media.rb +33 -20
  220. data/lib/ruby_llm/protocols/gemini/models.rb +71 -0
  221. data/lib/ruby_llm/protocols/gemini/speech.rb +56 -0
  222. data/lib/ruby_llm/protocols/gemini/streaming.rb +96 -0
  223. data/lib/ruby_llm/protocols/gemini/tools.rb +157 -0
  224. data/lib/ruby_llm/{providers → protocols}/gemini/transcription.rb +22 -22
  225. data/lib/ruby_llm/protocols/gemini/videos.rb +103 -0
  226. data/lib/ruby_llm/protocols/gemini.rb +35 -0
  227. data/lib/ruby_llm/protocols/gpustack/responses.rb +111 -0
  228. data/lib/ruby_llm/protocols/gpustack/tokenization.rb +22 -0
  229. data/lib/ruby_llm/protocols/gpustack/videos.rb +96 -0
  230. data/lib/ruby_llm/protocols/interactions/chat.rb +145 -0
  231. data/lib/ruby_llm/protocols/interactions/content.rb +90 -0
  232. data/lib/ruby_llm/protocols/interactions/streaming.rb +91 -0
  233. data/lib/ruby_llm/protocols/interactions/tools.rb +51 -0
  234. data/lib/ruby_llm/protocols/interactions/transcription.rb +58 -0
  235. data/lib/ruby_llm/protocols/interactions.rb +29 -0
  236. data/lib/ruby_llm/protocols/invoke_model/cohere_embeddings.rb +51 -0
  237. data/lib/ruby_llm/protocols/invoke_model/embedding_batches.rb +111 -0
  238. data/lib/ruby_llm/protocols/invoke_model/nova_embeddings.rb +50 -0
  239. data/lib/ruby_llm/protocols/invoke_model/stability_images.rb +103 -0
  240. data/lib/ruby_llm/protocols/invoke_model/titan_multimodal_embeddings.rb +33 -0
  241. data/lib/ruby_llm/protocols/invoke_model/titan_text_embeddings.rb +44 -0
  242. data/lib/ruby_llm/protocols/invoke_model.rb +57 -0
  243. data/lib/ruby_llm/protocols/mistral/content.rb +49 -0
  244. data/lib/ruby_llm/protocols/mistral/conversations/chat.rb +160 -0
  245. data/lib/ruby_llm/protocols/mistral/conversations/images.rb +43 -0
  246. data/lib/ruby_llm/protocols/mistral/conversations/streaming.rb +83 -0
  247. data/lib/ruby_llm/protocols/mistral/conversations.rb +30 -0
  248. data/lib/ruby_llm/protocols/mistral/files.rb +36 -0
  249. data/lib/ruby_llm/protocols/mistral/multi_completion.rb +160 -0
  250. data/lib/ruby_llm/protocols/openai/batches.rb +126 -0
  251. data/lib/ruby_llm/protocols/openai/files.rb +42 -0
  252. data/lib/ruby_llm/protocols/openrouter/batches.rb +147 -0
  253. data/lib/ruby_llm/protocols/openrouter/files.rb +24 -0
  254. data/lib/ruby_llm/protocols/openrouter/responses.rb +53 -0
  255. data/lib/ruby_llm/protocols/openrouter/transcription.rb +51 -0
  256. data/lib/ruby_llm/protocols/perplexity/files.rb +48 -0
  257. data/lib/ruby_llm/protocols/perplexity/router.rb +59 -0
  258. data/lib/ruby_llm/protocols/responses/approvals.rb +32 -0
  259. data/lib/ruby_llm/protocols/responses/batches.rb +32 -0
  260. data/lib/ruby_llm/protocols/responses/chat.rb +476 -0
  261. data/lib/ruby_llm/protocols/responses/compaction.rb +29 -0
  262. data/lib/ruby_llm/protocols/responses/media.rb +61 -0
  263. data/lib/ruby_llm/protocols/responses/streaming.rb +117 -0
  264. data/lib/ruby_llm/protocols/responses/token_counting.rb +26 -0
  265. data/lib/ruby_llm/protocols/responses/tools.rb +39 -0
  266. data/lib/ruby_llm/protocols/responses.rb +35 -0
  267. data/lib/ruby_llm/protocols/vertexai/batch_prediction.rb +155 -0
  268. data/lib/ruby_llm/protocols/vertexai/embedding_prediction/requests.rb +85 -0
  269. data/lib/ruby_llm/protocols/vertexai/embedding_prediction/results.rb +74 -0
  270. data/lib/ruby_llm/protocols/vertexai/embedding_prediction.rb +56 -0
  271. data/lib/ruby_llm/protocols/vertexai/files.rb +101 -0
  272. data/lib/ruby_llm/protocols/vertexai/ranking.rb +69 -0
  273. data/lib/ruby_llm/protocols/vertexai/research.rb +193 -0
  274. data/lib/ruby_llm/protocols/xai/files.rb +30 -0
  275. data/lib/ruby_llm/protocols/xai/streaming_transcription.rb +120 -0
  276. data/lib/ruby_llm/protocols/xai/tokenization.rb +23 -0
  277. data/lib/ruby_llm/provider.rb +565 -124
  278. data/lib/ruby_llm/providers/anthropic/capabilities.rb +5 -7
  279. data/lib/ruby_llm/providers/anthropic.rb +4 -6
  280. data/lib/ruby_llm/providers/azure/audio.rb +18 -0
  281. data/lib/ruby_llm/providers/azure/capabilities.rb +16 -0
  282. data/lib/ruby_llm/providers/azure/chat.rb +2 -9
  283. data/lib/ruby_llm/providers/azure/chat_completions/batches.rb +29 -0
  284. data/lib/ruby_llm/providers/azure/chat_completions.rb +80 -0
  285. data/lib/ruby_llm/providers/azure/cohere.rb +33 -0
  286. data/lib/ruby_llm/providers/azure/embeddings.rb +3 -2
  287. data/lib/ruby_llm/providers/azure/images.rb +22 -0
  288. data/lib/ruby_llm/providers/azure/media.rb +6 -15
  289. data/lib/ruby_llm/providers/azure/models.rb +35 -0
  290. data/lib/ruby_llm/providers/azure/responses.rb +26 -0
  291. data/lib/ruby_llm/providers/azure/videos.rb +64 -0
  292. data/lib/ruby_llm/providers/azure.rb +77 -78
  293. data/lib/ruby_llm/providers/bedrock/auth.rb +61 -41
  294. data/lib/ruby_llm/providers/bedrock/capabilities.rb +18 -0
  295. data/lib/ruby_llm/providers/bedrock/mantle/anthropic.rb +39 -0
  296. data/lib/ruby_llm/providers/bedrock/mantle/chat_completions.rb +23 -0
  297. data/lib/ruby_llm/providers/bedrock/mantle/responses.rb +24 -0
  298. data/lib/ruby_llm/providers/bedrock/mantle/voxtral.rb +96 -0
  299. data/lib/ruby_llm/providers/bedrock/mantle.rb +57 -0
  300. data/lib/ruby_llm/providers/bedrock/models.rb +194 -42
  301. data/lib/ruby_llm/providers/bedrock.rb +217 -46
  302. data/lib/ruby_llm/providers/cohere.rb +31 -0
  303. data/lib/ruby_llm/providers/deepgram.rb +37 -0
  304. data/lib/ruby_llm/providers/deepseek/capabilities.rb +4 -8
  305. data/lib/ruby_llm/providers/deepseek/chat.rb +56 -0
  306. data/lib/ruby_llm/providers/deepseek/responses.rb +68 -0
  307. data/lib/ruby_llm/providers/deepseek.rb +9 -2
  308. data/lib/ruby_llm/providers/elevenlabs.rb +35 -0
  309. data/lib/ruby_llm/providers/gemini/capabilities.rb +8 -107
  310. data/lib/ruby_llm/providers/gemini.rb +15 -8
  311. data/lib/ruby_llm/providers/gpustack/chat.rb +2 -9
  312. data/lib/ruby_llm/providers/gpustack/embeddings.rb +28 -0
  313. data/lib/ruby_llm/providers/gpustack/media.rb +17 -16
  314. data/lib/ruby_llm/providers/gpustack/models.rb +72 -60
  315. data/lib/ruby_llm/providers/gpustack/speech.rb +15 -0
  316. data/lib/ruby_llm/providers/gpustack/transcription.rb +29 -0
  317. data/lib/ruby_llm/providers/gpustack.rb +35 -11
  318. data/lib/ruby_llm/providers/mistral/capabilities.rb +7 -155
  319. data/lib/ruby_llm/providers/mistral/chat.rb +37 -61
  320. data/lib/ruby_llm/providers/mistral/chat_completions/batches.rb +120 -0
  321. data/lib/ruby_llm/providers/mistral/chat_completions.rb +21 -0
  322. data/lib/ruby_llm/providers/mistral/conversations.rb +12 -0
  323. data/lib/ruby_llm/providers/mistral/embeddings.rb +6 -4
  324. data/lib/ruby_llm/providers/mistral/media.rb +43 -0
  325. data/lib/ruby_llm/providers/mistral/models.rb +57 -21
  326. data/lib/ruby_llm/providers/mistral/ocr.rb +47 -0
  327. data/lib/ruby_llm/providers/mistral/speech.rb +51 -0
  328. data/lib/ruby_llm/providers/mistral/transcription.rb +62 -0
  329. data/lib/ruby_llm/providers/mistral.rb +18 -6
  330. data/lib/ruby_llm/providers/ollama/chat.rb +9 -8
  331. data/lib/ruby_llm/providers/ollama/media.rb +6 -15
  332. data/lib/ruby_llm/providers/ollama/models.rb +50 -9
  333. data/lib/ruby_llm/providers/ollama.rb +9 -8
  334. data/lib/ruby_llm/providers/ollama_cloud/models.rb +14 -0
  335. data/lib/ruby_llm/providers/ollama_cloud.rb +40 -0
  336. data/lib/ruby_llm/providers/openai/capabilities.rb +54 -259
  337. data/lib/ruby_llm/providers/openai/models.rb +23 -23
  338. data/lib/ruby_llm/providers/openai/responses.rb +13 -0
  339. data/lib/ruby_llm/providers/openai.rb +92 -11
  340. data/lib/ruby_llm/providers/openrouter/chat.rb +130 -104
  341. data/lib/ruby_llm/providers/openrouter/embeddings.rb +51 -0
  342. data/lib/ruby_llm/providers/openrouter/images.rb +44 -43
  343. data/lib/ruby_llm/providers/openrouter/media.rb +34 -0
  344. data/lib/ruby_llm/providers/openrouter/models.rb +50 -11
  345. data/lib/ruby_llm/providers/openrouter/speech.rb +32 -0
  346. data/lib/ruby_llm/providers/openrouter/streaming.rb +31 -38
  347. data/lib/ruby_llm/providers/openrouter/videos.rb +81 -0
  348. data/lib/ruby_llm/providers/openrouter.rb +78 -20
  349. data/lib/ruby_llm/providers/perplexity/chat.rb +4 -0
  350. data/lib/ruby_llm/providers/perplexity/embeddings.rb +32 -0
  351. data/lib/ruby_llm/providers/perplexity/media.rb +46 -0
  352. data/lib/ruby_llm/providers/perplexity/models.rb +80 -13
  353. data/lib/ruby_llm/providers/perplexity.rb +29 -21
  354. data/lib/ruby_llm/providers/vertexai/anthropic/batches.rb +52 -0
  355. data/lib/ruby_llm/providers/vertexai/anthropic.rb +34 -0
  356. data/lib/ruby_llm/providers/vertexai/capabilities.rb +19 -0
  357. data/lib/ruby_llm/providers/vertexai/chat_completions/batches.rb +54 -0
  358. data/lib/ruby_llm/providers/vertexai/chat_completions.rb +15 -0
  359. data/lib/ruby_llm/providers/vertexai/embed_content.rb +42 -0
  360. data/lib/ruby_llm/providers/vertexai/embeddings.rb +22 -7
  361. data/lib/ruby_llm/providers/vertexai/gemini/batches.rb +42 -0
  362. data/lib/ruby_llm/providers/vertexai/gemini.rb +69 -0
  363. data/lib/ruby_llm/providers/vertexai/live_transcription.rb +24 -0
  364. data/lib/ruby_llm/providers/vertexai/mistral.rb +28 -0
  365. data/lib/ruby_llm/providers/vertexai/models.rb +145 -43
  366. data/lib/ruby_llm/providers/vertexai/transcription.rb +49 -4
  367. data/lib/ruby_llm/providers/vertexai/videos.rb +61 -0
  368. data/lib/ruby_llm/providers/vertexai.rb +164 -17
  369. data/lib/ruby_llm/providers/xai/capabilities.rb +18 -0
  370. data/lib/ruby_llm/providers/xai/chat.rb +10 -0
  371. data/lib/ruby_llm/providers/xai/chat_completions/batches.rb +108 -0
  372. data/lib/ruby_llm/providers/xai/chat_completions.rb +19 -0
  373. data/lib/ruby_llm/providers/xai/images.rb +91 -0
  374. data/lib/ruby_llm/providers/xai/models.rb +32 -48
  375. data/lib/ruby_llm/providers/xai/reported_cost.rb +18 -0
  376. data/lib/ruby_llm/providers/xai/responses.rb +52 -0
  377. data/lib/ruby_llm/providers/xai/speech.rb +45 -0
  378. data/lib/ruby_llm/providers/xai/transcription.rb +48 -0
  379. data/lib/ruby_llm/providers/xai/videos.rb +87 -0
  380. data/lib/ruby_llm/providers/xai.rb +17 -7
  381. data/lib/ruby_llm/railtie.rb +11 -16
  382. data/lib/ruby_llm/rerank.rb +105 -0
  383. data/lib/ruby_llm/research_job.rb +241 -0
  384. data/lib/ruby_llm/search_results.rb +68 -0
  385. data/lib/ruby_llm/server_tool_call.rb +73 -0
  386. data/lib/ruby_llm/speech.rb +159 -0
  387. data/lib/ruby_llm/speech_chunk.rb +33 -0
  388. data/lib/ruby_llm/support/deprecator.rb +22 -0
  389. data/lib/ruby_llm/support/inspectable.rb +49 -0
  390. data/lib/ruby_llm/support/instrumentation.rb +41 -0
  391. data/lib/ruby_llm/support/utils.rb +147 -0
  392. data/lib/ruby_llm/thinking.rb +127 -20
  393. data/lib/ruby_llm/tokenization.rb +59 -0
  394. data/lib/ruby_llm/tokens.rb +103 -33
  395. data/lib/ruby_llm/tool.rb +266 -91
  396. data/lib/ruby_llm/tool_call.rb +36 -3
  397. data/lib/ruby_llm/tools/server_tools.rb +109 -0
  398. data/lib/ruby_llm/transcription/wav_audio.rb +62 -0
  399. data/lib/ruby_llm/transcription.rb +139 -13
  400. data/lib/ruby_llm/transcription_chunk.rb +68 -0
  401. data/lib/ruby_llm/transport/connection.rb +193 -0
  402. data/lib/ruby_llm/transport/error_middleware.rb +131 -0
  403. data/lib/ruby_llm/transport/usage_middleware.rb +28 -0
  404. data/lib/ruby_llm/transport/websocket_connection.rb +220 -0
  405. data/lib/ruby_llm/uploaded_file.rb +144 -0
  406. data/lib/ruby_llm/version.rb +2 -1
  407. data/lib/ruby_llm/video.rb +136 -0
  408. data/lib/ruby_llm/video_job.rb +150 -0
  409. data/lib/ruby_llm/workflow.rb +91 -0
  410. data/lib/ruby_llm.rb +385 -4
  411. data/lib/tasks/ruby_llm.rake +21 -16
  412. data/skills/rubyllm/SKILL.md +81 -0
  413. data/skills/rubyllm/agents/openai.yaml +4 -0
  414. metadata +340 -92
  415. data/lib/generators/ruby_llm/install/templates/add_references_to_chats_tool_calls_and_messages_migration.rb.tt +0 -9
  416. data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +0 -39
  417. data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +0 -21
  418. data/lib/generators/ruby_llm/install/templates/model_model.rb.tt +0 -3
  419. data/lib/generators/ruby_llm/install/templates/tool_call_model.rb.tt +0 -3
  420. data/lib/generators/ruby_llm/upgrade_to_v1_10/templates/add_v1_10_message_columns.rb.tt +0 -19
  421. data/lib/generators/ruby_llm/upgrade_to_v1_10/upgrade_to_v1_10_generator.rb +0 -50
  422. data/lib/generators/ruby_llm/upgrade_to_v1_14/templates/add_v1_14_tool_call_columns.rb.tt +0 -7
  423. data/lib/generators/ruby_llm/upgrade_to_v1_14/upgrade_to_v1_14_generator.rb +0 -49
  424. data/lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt +0 -145
  425. data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +0 -122
  426. data/lib/generators/ruby_llm/upgrade_to_v1_9/templates/add_v1_9_message_columns.rb.tt +0 -15
  427. data/lib/generators/ruby_llm/upgrade_to_v1_9/upgrade_to_v1_9_generator.rb +0 -49
  428. data/lib/ruby_llm/active_record/acts_as_legacy.rb +0 -530
  429. data/lib/ruby_llm/active_record/model_methods.rb +0 -82
  430. data/lib/ruby_llm/active_record/tool_call_methods.rb +0 -18
  431. data/lib/ruby_llm/aliases.rb +0 -38
  432. data/lib/ruby_llm/connection.rb +0 -130
  433. data/lib/ruby_llm/content.rb +0 -77
  434. data/lib/ruby_llm/mime_type.rb +0 -71
  435. data/lib/ruby_llm/model/info.rb +0 -130
  436. data/lib/ruby_llm/models_schema.json +0 -171
  437. data/lib/ruby_llm/providers/anthropic/chat.rb +0 -257
  438. data/lib/ruby_llm/providers/anthropic/content.rb +0 -44
  439. data/lib/ruby_llm/providers/anthropic/embeddings.rb +0 -20
  440. data/lib/ruby_llm/providers/anthropic/media.rb +0 -92
  441. data/lib/ruby_llm/providers/anthropic/models.rb +0 -57
  442. data/lib/ruby_llm/providers/anthropic/streaming.rb +0 -69
  443. data/lib/ruby_llm/providers/bedrock/chat.rb +0 -403
  444. data/lib/ruby_llm/providers/bedrock/media.rb +0 -90
  445. data/lib/ruby_llm/providers/bedrock/streaming.rb +0 -322
  446. data/lib/ruby_llm/providers/gemini/chat.rb +0 -543
  447. data/lib/ruby_llm/providers/gemini/embeddings.rb +0 -37
  448. data/lib/ruby_llm/providers/gemini/images.rb +0 -47
  449. data/lib/ruby_llm/providers/gemini/models.rb +0 -38
  450. data/lib/ruby_llm/providers/gemini/streaming.rb +0 -96
  451. data/lib/ruby_llm/providers/gemini/tools.rb +0 -232
  452. data/lib/ruby_llm/providers/gpustack/capabilities.rb +0 -20
  453. data/lib/ruby_llm/providers/ollama/capabilities.rb +0 -20
  454. data/lib/ruby_llm/providers/openai/chat.rb +0 -221
  455. data/lib/ruby_llm/providers/openai/embeddings.rb +0 -33
  456. data/lib/ruby_llm/providers/openai/images.rb +0 -90
  457. data/lib/ruby_llm/providers/openai/media.rb +0 -84
  458. data/lib/ruby_llm/providers/openai/moderation.rb +0 -34
  459. data/lib/ruby_llm/providers/openai/streaming.rb +0 -53
  460. data/lib/ruby_llm/providers/openai/temperature.rb +0 -28
  461. data/lib/ruby_llm/providers/openai/transcription.rb +0 -70
  462. data/lib/ruby_llm/providers/perplexity/capabilities.rb +0 -72
  463. data/lib/ruby_llm/providers/vertexai/chat.rb +0 -14
  464. data/lib/ruby_llm/providers/vertexai/streaming.rb +0 -14
  465. data/lib/ruby_llm/stream_accumulator.rb +0 -203
  466. data/lib/ruby_llm/streaming.rb +0 -175
  467. data/lib/ruby_llm/utils.rb +0 -91
  468. data/lib/tasks/models.rake +0 -565
  469. data/lib/tasks/release.rake +0 -67
  470. data/lib/tasks/vcr.rake +0 -124
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3cfdd21451b6e3daac0463dfdb7655892967e5a4da6b00846f353809a9abe18a
4
- data.tar.gz: e1505ddd57326601298bb8975c20049a195eb87babd62b08ac0e6340c9e653f6
3
+ metadata.gz: 3e6a6455ecb1c6bf276d863d9e1f181f1a1d3ea87092800a01522c1fb9be35fc
4
+ data.tar.gz: 39554eb45edfde45e43e0a9eb9afe2a5e07cf6601b9b8076e19065493e62e0fd
5
5
  SHA512:
6
- metadata.gz: 7222e575d4f1e5cf620fdc37f4c255c7ededeee945d7b77fac4129c2947d6f78f7d5641b45ed39470180e5bf2dfd48b36e0a6c16579e4e4835564c48b6907920
7
- data.tar.gz: ce3fb0f0e8b3665fafe56717cf8fcb10733008821633b024013d0f6e5d0bc90ef47f25b0109aa0e80ee3df4002e2f9b6a20cd98595031aa57235588cb4677052
6
+ metadata.gz: c92879f31f983745665f2a08db2e9b01a293d40ebd2a0e68ee899e1af4b77f7d4c6767c700bcbdecc85088a43a0fae58a11b617c4409e1b04b73d44a7f59b4da
7
+ data.tar.gz: 853e3cdde61afafbfdff5ff84cdc3c9878002f4da1b81e45e1b105b0caf759587647a03cb05d85d806a5e3c91c48ec426d24e356615914802860cb1cdec9c6cb
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,9 +5,11 @@
5
5
  <img src="/docs/assets/images/logotype.svg" alt="RubyLLM" height="120" width="250">
6
6
  </picture>
7
7
 
8
- <strong>One *beautiful* Ruby API for GPT, Claude, Gemini, and more.</strong>
8
+ <strong>Build AI features the Ruby way</strong>
9
9
 
10
- 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) *Your AI coworker*
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>
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*
11
13
 
12
14
  [![Gem Version](https://badge.fury.io/rb/ruby_llm.svg)](https://badge.fury.io/rb/ruby_llm)
13
15
  [![Ruby Style Guide](https://img.shields.io/badge/code_style-rubocop-brightgreen.svg)](https://github.com/rubocop/rubocop)
@@ -22,20 +24,22 @@ Battle tested at [<picture><source media="(prefers-color-scheme: dark)" srcset="
22
24
 
23
25
  ---
24
26
 
25
- 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.
26
28
 
27
- ## From zero to AI chat app in under two minutes
29
+ ## Build a working Ruby AI chat in two minutes
28
30
 
29
31
  https://github.com/user-attachments/assets/65422091-9338-47da-a303-92b918bd1345
30
32
 
31
33
  ## Why RubyLLM?
32
34
 
33
- 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.
34
36
 
35
- RubyLLM gives you one beautiful API 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.
36
38
 
37
39
  ## Show me the code
38
40
 
41
+ These examples use **2.0.0.rc1**. Follow [Getting Started](https://rubyllm.com/next/getting-started/) to install it and configure the providers you want to try.
42
+
39
43
  ```ruby
40
44
  # Just ask questions
41
45
  chat = RubyLLM.chat
@@ -43,7 +47,8 @@ chat.ask "What's the best way to learn Ruby?"
43
47
  ```
44
48
 
45
49
  ```ruby
46
- # 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")
47
52
  chat.ask "What's in this image?", with: "ruby_conf.jpg"
48
53
  chat.ask "What's happening in this video?", with: "video.mp4"
49
54
  chat.ask "Describe this meeting", with: "meeting.wav"
@@ -65,28 +70,56 @@ end
65
70
 
66
71
  ```ruby
67
72
  # Generate images
68
- 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"
69
81
  ```
70
82
 
71
83
  ```ruby
72
84
  # Create embeddings
73
- 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
74
94
  ```
75
95
 
76
96
  ```ruby
77
97
  # Transcribe audio to text
78
- 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"
79
106
  ```
80
107
 
81
108
  ```ruby
82
- # Moderate content for safety
83
- 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?
84
117
  ```
85
118
 
86
119
  ```ruby
87
120
  # Let AI use your code
88
121
  class Weather < RubyLLM::Tool
89
- desc "Get current weather"
122
+ description "Get current weather"
90
123
 
91
124
  def execute(latitude:, longitude:)
92
125
  url = "https://api.open-meteo.com/v1/forecast?latitude=#{latitude}&longitude=#{longitude}&current=temperature_2m,wind_speed_10m"
@@ -94,13 +127,13 @@ class Weather < RubyLLM::Tool
94
127
  end
95
128
  end
96
129
 
97
- chat.with_tool(Weather).ask "What's the weather in Berlin?"
130
+ chat.with_tools(Weather).ask "What's the weather in Berlin?"
98
131
  ```
99
132
 
100
133
  ```ruby
101
134
  # Define an agent with instructions + tools
102
135
  class WeatherAssistant < RubyLLM::Agent
103
- model "gpt-5-nano"
136
+ model "gpt-5.6-luna"
104
137
  instructions "Be concise and always use tools for weather."
105
138
  tools Weather
106
139
  end
@@ -110,7 +143,7 @@ WeatherAssistant.new.ask "What's the weather in Berlin?"
110
143
 
111
144
  ```ruby
112
145
  # Get structured output
113
- class ProductSchema < RubyLLM::Schema
146
+ class ProductSchema < Schematist::Schema
114
147
  string :name
115
148
  number :price
116
149
  array :features do
@@ -119,50 +152,71 @@ class ProductSchema < RubyLLM::Schema
119
152
  end
120
153
 
121
154
  response = chat.with_schema(ProductSchema).ask "Analyze this product", with: "product.txt"
155
+ response.parsed
122
156
  ```
123
157
 
124
158
  ## Features
125
159
 
126
160
  * **Chat:** Conversational AI with `RubyLLM.chat`
127
161
  * **Vision:** Analyze images and videos
128
- * **Audio:** Transcribe and understand speech with `RubyLLM.transcribe`
129
- * **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`
130
165
  * **Image generation:** Create images with `RubyLLM.paint`
166
+ * **Video generation:** Create videos with `RubyLLM.animate`
131
167
  * **Embeddings:** Generate embeddings with `RubyLLM.embed`
132
- * **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`
133
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`
134
174
  * **Agents:** Reusable assistants with `RubyLLM::Agent`
135
- * **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`
136
178
  * **Streaming:** Real-time responses with blocks
137
- * **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`
138
184
  * **Async:** Fiber-based concurrency
139
- * **Model registry:** 800+ models with capability detection and pricing
185
+ * **Model registry:** Browse capabilities, limits, and pricing across providers
140
186
  * **Extended thinking:** Control, view, and persist model deliberation
141
- * **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
142
192
 
143
193
  ## Installation
144
194
 
145
- Add to your Gemfile:
146
- ```ruby
147
- gem 'ruby_llm'
195
+ Install the 2.0 release candidate:
196
+
197
+ ```bash
198
+ bundle add ruby_llm --version 2.0.0.rc1
148
199
  ```
149
- Then `bundle install`.
150
200
 
151
- Configure your API keys:
201
+ Configure a provider in your script, or in `config/initializers/ruby_llm.rb` in Rails:
202
+
152
203
  ```ruby
153
- # config/initializers/ruby_llm.rb
204
+ require 'ruby_llm'
205
+
154
206
  RubyLLM.configure do |config|
155
- config.openai_api_key = ENV['OPENAI_API_KEY']
207
+ config.openai_api_key = ENV.fetch('OPENAI_API_KEY')
156
208
  end
157
209
  ```
158
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
+
159
213
  ## Rails
160
214
 
161
215
  ```bash
162
216
  # Install Rails Integration
163
217
  bin/rails generate ruby_llm:install
164
218
  bin/rails db:migrate
165
- bin/rails ruby_llm:load_models # v1.13+
219
+ bin/rails ruby_llm:load_models
166
220
 
167
221
  # Add Chat UI (optional)
168
222
  bin/rails generate ruby_llm:chat_ui
@@ -173,7 +227,7 @@ class Chat < ApplicationRecord
173
227
  acts_as_chat
174
228
  end
175
229
 
176
- chat = Chat.create! model: "claude-sonnet-4"
230
+ chat = Chat.create! model: "gpt-5.6-luna"
177
231
  chat.ask "What's in this file?", with: "report.pdf"
178
232
  ```
179
233
 
@@ -181,7 +235,7 @@ Visit `http://localhost:3000/chats` for a ready-to-use chat interface!
181
235
 
182
236
  ## Documentation
183
237
 
184
- [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/)
185
239
 
186
240
  ## Contributing
187
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">