ruby_llm 1.16.0 → 2.0.0.rc4

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 (480) hide show
  1. checksums.yaml +4 -4
  2. data/.rdoc_options +25 -0
  3. data/README.md +108 -44
  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/legacy_content_sql.rb +34 -0
  91. data/lib/generators/ruby_llm/upgrade/online_copy_migration/data.rb +341 -0
  92. data/lib/generators/ruby_llm/upgrade/online_copy_migration/journal.rb +171 -0
  93. data/lib/generators/ruby_llm/upgrade/online_copy_migration/verification.rb +197 -0
  94. data/lib/generators/ruby_llm/upgrade/online_copy_migration.rb +174 -0
  95. data/lib/generators/ruby_llm/upgrade/templates/backfill_v2_data.rb.tt +468 -0
  96. data/lib/generators/ruby_llm/upgrade/templates/cleanup_v2_upgrade.rb.tt +101 -0
  97. data/lib/generators/ruby_llm/upgrade/templates/finish_v2_upgrade.rb.tt +247 -0
  98. data/lib/generators/ruby_llm/upgrade/templates/prepare_v2_upgrade.rb.tt +662 -0
  99. data/lib/generators/ruby_llm/upgrade/templates/ruby_llm_upgrade.rb.tt +251 -0
  100. data/lib/generators/ruby_llm/upgrade/templates/upgrade_initializer.rb.tt +13 -0
  101. data/lib/generators/ruby_llm/upgrade/upgrade_generator.rb +188 -0
  102. data/lib/generators/ruby_llm/upgrade/upgrade_migration.rb +359 -0
  103. data/lib/ruby_llm/accounting/usage.rb +254 -0
  104. data/lib/ruby_llm/active_record/acts_as.rb +94 -111
  105. data/lib/ruby_llm/active_record/attachment_helpers.rb +186 -0
  106. data/lib/ruby_llm/active_record/batch.rb +97 -0
  107. data/lib/ruby_llm/active_record/chat_methods.rb +828 -305
  108. data/lib/ruby_llm/active_record/message_methods.rb +113 -136
  109. data/lib/ruby_llm/active_record/model.rb +135 -0
  110. data/lib/ruby_llm/active_record/payload_helpers.rb +1 -2
  111. data/lib/ruby_llm/active_record/tool_call.rb +33 -0
  112. data/lib/ruby_llm/active_record/usage.rb +61 -0
  113. data/lib/ruby_llm/agent.rb +1066 -151
  114. data/lib/ruby_llm/aliases.json +291 -101
  115. data/lib/ruby_llm/attachment.rb +192 -48
  116. data/lib/ruby_llm/batch.rb +432 -0
  117. data/lib/ruby_llm/cached_content.rb +112 -0
  118. data/lib/ruby_llm/chat/tool_concurrency.rb +111 -0
  119. data/lib/ruby_llm/chat.rb +1131 -198
  120. data/lib/ruby_llm/chunk.rb +10 -0
  121. data/lib/ruby_llm/citation.rb +105 -0
  122. data/lib/ruby_llm/configuration.rb +261 -24
  123. data/lib/ruby_llm/context.rb +128 -6
  124. data/lib/ruby_llm/cost.rb +217 -80
  125. data/lib/ruby_llm/downloaded_file.rb +33 -0
  126. data/lib/ruby_llm/embedding.rb +121 -17
  127. data/lib/ruby_llm/embedding_request.rb +53 -0
  128. data/lib/ruby_llm/error.rb +159 -23
  129. data/lib/ruby_llm/fallback.rb +133 -0
  130. data/lib/ruby_llm/files/mime_type.rb +97 -0
  131. data/lib/ruby_llm/image.rb +153 -32
  132. data/lib/ruby_llm/message.rb +242 -54
  133. data/lib/ruby_llm/model/modalities.rb +17 -4
  134. data/lib/ruby_llm/model/pricing.rb +24 -5
  135. data/lib/ruby_llm/model/pricing_category.rb +103 -14
  136. data/lib/ruby_llm/model/pricing_tier.rb +55 -15
  137. data/lib/ruby_llm/model.rb +244 -2
  138. data/lib/ruby_llm/models/aliases.rb +41 -0
  139. data/lib/ruby_llm/models/registry.rb +165 -0
  140. data/lib/ruby_llm/models/schema.rb +99 -0
  141. data/lib/ruby_llm/models.json +76038 -34173
  142. data/lib/ruby_llm/models.rb +477 -215
  143. data/lib/ruby_llm/moderation.rb +139 -26
  144. data/lib/ruby_llm/ocr.rb +112 -0
  145. data/lib/ruby_llm/prompt.rb +79 -0
  146. data/lib/ruby_llm/protocol/binary_streaming.rb +65 -0
  147. data/lib/ruby_llm/protocol/stream_accumulator.rb +214 -0
  148. data/lib/ruby_llm/protocol/streaming.rb +230 -0
  149. data/lib/ruby_llm/protocol.rb +662 -0
  150. data/lib/ruby_llm/protocols/anthropic/batches.rb +73 -0
  151. data/lib/ruby_llm/protocols/anthropic/chat.rb +543 -0
  152. data/lib/ruby_llm/protocols/anthropic/embeddings.rb +14 -0
  153. data/lib/ruby_llm/protocols/anthropic/files.rb +38 -0
  154. data/lib/ruby_llm/protocols/anthropic/media.rb +141 -0
  155. data/lib/ruby_llm/protocols/anthropic/models.rb +129 -0
  156. data/lib/ruby_llm/protocols/anthropic/streaming.rb +167 -0
  157. data/lib/ruby_llm/{providers → protocols}/anthropic/tools.rb +24 -41
  158. data/lib/ruby_llm/protocols/anthropic.rb +101 -0
  159. data/lib/ruby_llm/protocols/azure/files.rb +16 -0
  160. data/lib/ruby_llm/protocols/bedrock/async_videos.rb +110 -0
  161. data/lib/ruby_llm/protocols/bedrock/batches.rb +129 -0
  162. data/lib/ruby_llm/protocols/bedrock/files.rb +110 -0
  163. data/lib/ruby_llm/protocols/bedrock/guardrails.rb +122 -0
  164. data/lib/ruby_llm/protocols/bedrock/rerank.rb +95 -0
  165. data/lib/ruby_llm/protocols/chat_completions/batches.rb +32 -0
  166. data/lib/ruby_llm/protocols/chat_completions/chat.rb +484 -0
  167. data/lib/ruby_llm/protocols/chat_completions/embedding_batches.rb +35 -0
  168. data/lib/ruby_llm/protocols/chat_completions/embeddings.rb +60 -0
  169. data/lib/ruby_llm/protocols/chat_completions/images.rb +127 -0
  170. data/lib/ruby_llm/{providers/openai → protocols/chat_completions}/media.rb +30 -17
  171. data/lib/ruby_llm/protocols/chat_completions/models.rb +39 -0
  172. data/lib/ruby_llm/protocols/chat_completions/moderation.rb +52 -0
  173. data/lib/ruby_llm/protocols/chat_completions/rerank.rb +63 -0
  174. data/lib/ruby_llm/protocols/chat_completions/speech.rb +40 -0
  175. data/lib/ruby_llm/protocols/chat_completions/streaming.rb +69 -0
  176. data/lib/ruby_llm/{providers/openai → protocols/chat_completions}/tools.rb +15 -17
  177. data/lib/ruby_llm/protocols/chat_completions/transcription.rb +150 -0
  178. data/lib/ruby_llm/protocols/chat_completions.rb +21 -0
  179. data/lib/ruby_llm/protocols/cohere/batch_requests.rb +75 -0
  180. data/lib/ruby_llm/protocols/cohere/batches.rb +98 -0
  181. data/lib/ruby_llm/protocols/cohere/chat.rb +227 -0
  182. data/lib/ruby_llm/protocols/cohere/datasets.rb +102 -0
  183. data/lib/ruby_llm/protocols/cohere/embeddings.rb +68 -0
  184. data/lib/ruby_llm/protocols/cohere/media.rb +77 -0
  185. data/lib/ruby_llm/protocols/cohere/models.rb +92 -0
  186. data/lib/ruby_llm/protocols/cohere/ocr.rb +63 -0
  187. data/lib/ruby_llm/protocols/cohere/rerank.rb +59 -0
  188. data/lib/ruby_llm/protocols/cohere/streaming.rb +105 -0
  189. data/lib/ruby_llm/protocols/cohere/tokenization.rb +22 -0
  190. data/lib/ruby_llm/protocols/cohere/tools.rb +132 -0
  191. data/lib/ruby_llm/protocols/cohere/transcription.rb +41 -0
  192. data/lib/ruby_llm/protocols/cohere.rb +21 -0
  193. data/lib/ruby_llm/protocols/converse/batches.rb +55 -0
  194. data/lib/ruby_llm/protocols/converse/chat.rb +694 -0
  195. data/lib/ruby_llm/protocols/converse/media.rb +178 -0
  196. data/lib/ruby_llm/protocols/converse/streaming.rb +432 -0
  197. data/lib/ruby_llm/protocols/converse/thinking_stream.rb +56 -0
  198. data/lib/ruby_llm/protocols/converse.rb +54 -0
  199. data/lib/ruby_llm/protocols/deepgram/models.rb +74 -0
  200. data/lib/ruby_llm/protocols/deepgram/speech.rb +93 -0
  201. data/lib/ruby_llm/protocols/deepgram/streaming_transcription.rb +89 -0
  202. data/lib/ruby_llm/protocols/deepgram/transcription.rb +96 -0
  203. data/lib/ruby_llm/protocols/deepgram.rb +19 -0
  204. data/lib/ruby_llm/protocols/deepseek/files.rb +31 -0
  205. data/lib/ruby_llm/protocols/elevenlabs/assets.rb +36 -0
  206. data/lib/ruby_llm/protocols/elevenlabs/flows/images.rb +74 -0
  207. data/lib/ruby_llm/protocols/elevenlabs/flows/media.rb +42 -0
  208. data/lib/ruby_llm/protocols/elevenlabs/flows/videos.rb +93 -0
  209. data/lib/ruby_llm/protocols/elevenlabs/flows.rb +14 -0
  210. data/lib/ruby_llm/protocols/elevenlabs/models.rb +64 -0
  211. data/lib/ruby_llm/protocols/elevenlabs/speech.rb +67 -0
  212. data/lib/ruby_llm/protocols/elevenlabs/streaming_transcription.rb +127 -0
  213. data/lib/ruby_llm/protocols/elevenlabs/transcription.rb +61 -0
  214. data/lib/ruby_llm/protocols/elevenlabs.rb +15 -0
  215. data/lib/ruby_llm/protocols/files.rb +119 -0
  216. data/lib/ruby_llm/protocols/gemini/batches.rb +162 -0
  217. data/lib/ruby_llm/protocols/gemini/caches.rb +59 -0
  218. data/lib/ruby_llm/protocols/gemini/chat.rb +453 -0
  219. data/lib/ruby_llm/protocols/gemini/embedding_batches.rb +91 -0
  220. data/lib/ruby_llm/protocols/gemini/embeddings.rb +70 -0
  221. data/lib/ruby_llm/protocols/gemini/file_transcription.rb +32 -0
  222. data/lib/ruby_llm/protocols/gemini/files.rb +115 -0
  223. data/lib/ruby_llm/protocols/gemini/images.rb +183 -0
  224. data/lib/ruby_llm/protocols/gemini/live_transcription.rb +140 -0
  225. data/lib/ruby_llm/{providers → protocols}/gemini/media.rb +17 -11
  226. data/lib/ruby_llm/protocols/gemini/models.rb +71 -0
  227. data/lib/ruby_llm/protocols/gemini/speech.rb +56 -0
  228. data/lib/ruby_llm/protocols/gemini/streaming.rb +96 -0
  229. data/lib/ruby_llm/protocols/gemini/tools.rb +157 -0
  230. data/lib/ruby_llm/{providers → protocols}/gemini/transcription.rb +22 -22
  231. data/lib/ruby_llm/protocols/gemini/videos.rb +103 -0
  232. data/lib/ruby_llm/protocols/gemini.rb +35 -0
  233. data/lib/ruby_llm/protocols/gpustack/responses.rb +111 -0
  234. data/lib/ruby_llm/protocols/gpustack/tokenization.rb +22 -0
  235. data/lib/ruby_llm/protocols/gpustack/videos.rb +96 -0
  236. data/lib/ruby_llm/protocols/interactions/chat.rb +145 -0
  237. data/lib/ruby_llm/protocols/interactions/content.rb +90 -0
  238. data/lib/ruby_llm/protocols/interactions/streaming.rb +91 -0
  239. data/lib/ruby_llm/protocols/interactions/tools.rb +53 -0
  240. data/lib/ruby_llm/protocols/interactions/transcription.rb +58 -0
  241. data/lib/ruby_llm/protocols/interactions.rb +29 -0
  242. data/lib/ruby_llm/protocols/invoke_model/cohere_embeddings.rb +51 -0
  243. data/lib/ruby_llm/protocols/invoke_model/embedding_batches.rb +111 -0
  244. data/lib/ruby_llm/protocols/invoke_model/nova_embeddings.rb +50 -0
  245. data/lib/ruby_llm/protocols/invoke_model/stability_images.rb +103 -0
  246. data/lib/ruby_llm/protocols/invoke_model/titan_multimodal_embeddings.rb +33 -0
  247. data/lib/ruby_llm/protocols/invoke_model/titan_text_embeddings.rb +44 -0
  248. data/lib/ruby_llm/protocols/invoke_model.rb +57 -0
  249. data/lib/ruby_llm/protocols/mistral/content.rb +49 -0
  250. data/lib/ruby_llm/protocols/mistral/conversations/chat.rb +160 -0
  251. data/lib/ruby_llm/protocols/mistral/conversations/images.rb +43 -0
  252. data/lib/ruby_llm/protocols/mistral/conversations/streaming.rb +83 -0
  253. data/lib/ruby_llm/protocols/mistral/conversations.rb +30 -0
  254. data/lib/ruby_llm/protocols/mistral/files.rb +36 -0
  255. data/lib/ruby_llm/protocols/mistral/multi_completion.rb +160 -0
  256. data/lib/ruby_llm/protocols/openai/batches.rb +126 -0
  257. data/lib/ruby_llm/protocols/openai/files.rb +42 -0
  258. data/lib/ruby_llm/protocols/openrouter/batches.rb +147 -0
  259. data/lib/ruby_llm/protocols/openrouter/files.rb +24 -0
  260. data/lib/ruby_llm/protocols/openrouter/responses.rb +53 -0
  261. data/lib/ruby_llm/protocols/openrouter/transcription.rb +51 -0
  262. data/lib/ruby_llm/protocols/perplexity/files.rb +48 -0
  263. data/lib/ruby_llm/protocols/perplexity/router.rb +59 -0
  264. data/lib/ruby_llm/protocols/responses/approvals.rb +32 -0
  265. data/lib/ruby_llm/protocols/responses/batches.rb +32 -0
  266. data/lib/ruby_llm/protocols/responses/chat.rb +466 -0
  267. data/lib/ruby_llm/protocols/responses/compaction.rb +29 -0
  268. data/lib/ruby_llm/protocols/responses/media.rb +61 -0
  269. data/lib/ruby_llm/protocols/responses/streaming.rb +117 -0
  270. data/lib/ruby_llm/protocols/responses/token_counting.rb +26 -0
  271. data/lib/ruby_llm/protocols/responses/tools.rb +39 -0
  272. data/lib/ruby_llm/protocols/responses.rb +35 -0
  273. data/lib/ruby_llm/protocols/vertexai/batch_prediction.rb +155 -0
  274. data/lib/ruby_llm/protocols/vertexai/embedding_prediction/requests.rb +85 -0
  275. data/lib/ruby_llm/protocols/vertexai/embedding_prediction/results.rb +74 -0
  276. data/lib/ruby_llm/protocols/vertexai/embedding_prediction.rb +56 -0
  277. data/lib/ruby_llm/protocols/vertexai/files.rb +101 -0
  278. data/lib/ruby_llm/protocols/vertexai/ranking.rb +69 -0
  279. data/lib/ruby_llm/protocols/vertexai/research.rb +193 -0
  280. data/lib/ruby_llm/protocols/xai/files.rb +30 -0
  281. data/lib/ruby_llm/protocols/xai/streaming_transcription.rb +120 -0
  282. data/lib/ruby_llm/protocols/xai/tokenization.rb +23 -0
  283. data/lib/ruby_llm/provider.rb +560 -128
  284. data/lib/ruby_llm/providers/anthropic/capabilities.rb +5 -7
  285. data/lib/ruby_llm/providers/anthropic.rb +4 -6
  286. data/lib/ruby_llm/providers/azure/audio.rb +18 -0
  287. data/lib/ruby_llm/providers/azure/capabilities.rb +16 -0
  288. data/lib/ruby_llm/providers/azure/chat.rb +2 -9
  289. data/lib/ruby_llm/providers/azure/chat_completions/batches.rb +29 -0
  290. data/lib/ruby_llm/providers/azure/chat_completions.rb +80 -0
  291. data/lib/ruby_llm/providers/azure/cohere.rb +33 -0
  292. data/lib/ruby_llm/providers/azure/embeddings.rb +3 -2
  293. data/lib/ruby_llm/providers/azure/images.rb +22 -0
  294. data/lib/ruby_llm/providers/azure/media.rb +5 -14
  295. data/lib/ruby_llm/providers/azure/models.rb +35 -0
  296. data/lib/ruby_llm/providers/azure/responses.rb +26 -0
  297. data/lib/ruby_llm/providers/azure/videos.rb +64 -0
  298. data/lib/ruby_llm/providers/azure.rb +77 -78
  299. data/lib/ruby_llm/providers/bedrock/auth.rb +60 -41
  300. data/lib/ruby_llm/providers/bedrock/capabilities.rb +18 -0
  301. data/lib/ruby_llm/providers/bedrock/mantle/anthropic.rb +39 -0
  302. data/lib/ruby_llm/providers/bedrock/mantle/chat_completions.rb +23 -0
  303. data/lib/ruby_llm/providers/bedrock/mantle/responses.rb +24 -0
  304. data/lib/ruby_llm/providers/bedrock/mantle/voxtral.rb +96 -0
  305. data/lib/ruby_llm/providers/bedrock/mantle.rb +57 -0
  306. data/lib/ruby_llm/providers/bedrock/models.rb +193 -41
  307. data/lib/ruby_llm/providers/bedrock.rb +216 -45
  308. data/lib/ruby_llm/providers/cohere.rb +31 -0
  309. data/lib/ruby_llm/providers/deepgram.rb +37 -0
  310. data/lib/ruby_llm/providers/deepseek/capabilities.rb +4 -51
  311. data/lib/ruby_llm/providers/deepseek/chat.rb +49 -2
  312. data/lib/ruby_llm/providers/deepseek/responses.rb +67 -0
  313. data/lib/ruby_llm/providers/deepseek.rb +9 -2
  314. data/lib/ruby_llm/providers/elevenlabs.rb +35 -0
  315. data/lib/ruby_llm/providers/gemini/capabilities.rb +8 -107
  316. data/lib/ruby_llm/providers/gemini.rb +15 -8
  317. data/lib/ruby_llm/providers/gpustack/chat.rb +2 -16
  318. data/lib/ruby_llm/providers/gpustack/embeddings.rb +28 -0
  319. data/lib/ruby_llm/providers/gpustack/media.rb +17 -16
  320. data/lib/ruby_llm/providers/gpustack/models.rb +72 -62
  321. data/lib/ruby_llm/providers/gpustack/speech.rb +15 -0
  322. data/lib/ruby_llm/providers/gpustack/transcription.rb +29 -0
  323. data/lib/ruby_llm/providers/gpustack.rb +35 -11
  324. data/lib/ruby_llm/providers/mistral/capabilities.rb +7 -155
  325. data/lib/ruby_llm/providers/mistral/chat.rb +37 -61
  326. data/lib/ruby_llm/providers/mistral/chat_completions/batches.rb +120 -0
  327. data/lib/ruby_llm/providers/mistral/chat_completions.rb +21 -0
  328. data/lib/ruby_llm/providers/mistral/conversations.rb +12 -0
  329. data/lib/ruby_llm/providers/mistral/embeddings.rb +6 -4
  330. data/lib/ruby_llm/providers/mistral/media.rb +6 -18
  331. data/lib/ruby_llm/providers/mistral/models.rb +57 -23
  332. data/lib/ruby_llm/providers/mistral/ocr.rb +51 -0
  333. data/lib/ruby_llm/providers/mistral/speech.rb +51 -0
  334. data/lib/ruby_llm/providers/mistral/transcription.rb +62 -0
  335. data/lib/ruby_llm/providers/mistral.rb +16 -4
  336. data/lib/ruby_llm/providers/ollama/chat.rb +7 -13
  337. data/lib/ruby_llm/providers/ollama/media.rb +6 -15
  338. data/lib/ruby_llm/providers/ollama/models.rb +50 -9
  339. data/lib/ruby_llm/providers/ollama.rb +9 -8
  340. data/lib/ruby_llm/providers/ollama_cloud/models.rb +14 -0
  341. data/lib/ruby_llm/providers/ollama_cloud.rb +40 -0
  342. data/lib/ruby_llm/providers/openai/capabilities.rb +54 -259
  343. data/lib/ruby_llm/providers/openai/models.rb +23 -23
  344. data/lib/ruby_llm/providers/openai/responses.rb +13 -0
  345. data/lib/ruby_llm/providers/openai.rb +92 -11
  346. data/lib/ruby_llm/providers/openrouter/chat.rb +126 -108
  347. data/lib/ruby_llm/providers/openrouter/embeddings.rb +51 -0
  348. data/lib/ruby_llm/providers/openrouter/images.rb +44 -43
  349. data/lib/ruby_llm/providers/openrouter/media.rb +34 -0
  350. data/lib/ruby_llm/providers/openrouter/models.rb +50 -11
  351. data/lib/ruby_llm/providers/openrouter/speech.rb +32 -0
  352. data/lib/ruby_llm/providers/openrouter/streaming.rb +31 -38
  353. data/lib/ruby_llm/providers/openrouter/videos.rb +81 -0
  354. data/lib/ruby_llm/providers/openrouter.rb +78 -20
  355. data/lib/ruby_llm/providers/perplexity/chat.rb +2 -9
  356. data/lib/ruby_llm/providers/perplexity/embeddings.rb +32 -0
  357. data/lib/ruby_llm/providers/perplexity/media.rb +5 -21
  358. data/lib/ruby_llm/providers/perplexity/models.rb +80 -13
  359. data/lib/ruby_llm/providers/perplexity.rb +28 -20
  360. data/lib/ruby_llm/providers/vertexai/anthropic/batches.rb +52 -0
  361. data/lib/ruby_llm/providers/vertexai/anthropic.rb +34 -0
  362. data/lib/ruby_llm/providers/vertexai/capabilities.rb +19 -0
  363. data/lib/ruby_llm/providers/vertexai/chat_completions/batches.rb +54 -0
  364. data/lib/ruby_llm/providers/vertexai/chat_completions.rb +15 -0
  365. data/lib/ruby_llm/providers/vertexai/embed_content.rb +42 -0
  366. data/lib/ruby_llm/providers/vertexai/embeddings.rb +22 -7
  367. data/lib/ruby_llm/providers/vertexai/gemini/batches.rb +42 -0
  368. data/lib/ruby_llm/providers/vertexai/gemini.rb +69 -0
  369. data/lib/ruby_llm/providers/vertexai/live_transcription.rb +24 -0
  370. data/lib/ruby_llm/providers/vertexai/mistral.rb +28 -0
  371. data/lib/ruby_llm/providers/vertexai/models.rb +145 -43
  372. data/lib/ruby_llm/providers/vertexai/transcription.rb +49 -4
  373. data/lib/ruby_llm/providers/vertexai/videos.rb +61 -0
  374. data/lib/ruby_llm/providers/vertexai.rb +160 -17
  375. data/lib/ruby_llm/providers/xai/capabilities.rb +18 -0
  376. data/lib/ruby_llm/providers/xai/chat.rb +3 -2
  377. data/lib/ruby_llm/providers/xai/chat_completions/batches.rb +108 -0
  378. data/lib/ruby_llm/providers/xai/chat_completions.rb +19 -0
  379. data/lib/ruby_llm/providers/xai/images.rb +91 -0
  380. data/lib/ruby_llm/providers/xai/models.rb +32 -36
  381. data/lib/ruby_llm/providers/xai/reported_cost.rb +18 -0
  382. data/lib/ruby_llm/providers/xai/responses.rb +52 -0
  383. data/lib/ruby_llm/providers/xai/speech.rb +45 -0
  384. data/lib/ruby_llm/providers/xai/transcription.rb +48 -0
  385. data/lib/ruby_llm/providers/xai/videos.rb +87 -0
  386. data/lib/ruby_llm/providers/xai.rb +15 -5
  387. data/lib/ruby_llm/railtie.rb +7 -16
  388. data/lib/ruby_llm/rerank.rb +105 -0
  389. data/lib/ruby_llm/research_job.rb +241 -0
  390. data/lib/ruby_llm/search_results.rb +68 -0
  391. data/lib/ruby_llm/server_tool_call.rb +73 -0
  392. data/lib/ruby_llm/speech.rb +159 -0
  393. data/lib/ruby_llm/speech_chunk.rb +33 -0
  394. data/lib/ruby_llm/support/deprecator.rb +22 -0
  395. data/lib/ruby_llm/support/inspectable.rb +49 -0
  396. data/lib/ruby_llm/support/instrumentation.rb +41 -0
  397. data/lib/ruby_llm/support/utils.rb +147 -0
  398. data/lib/ruby_llm/thinking.rb +127 -20
  399. data/lib/ruby_llm/tokenization.rb +59 -0
  400. data/lib/ruby_llm/tokens.rb +103 -33
  401. data/lib/ruby_llm/tool.rb +266 -91
  402. data/lib/ruby_llm/tool_call.rb +36 -3
  403. data/lib/ruby_llm/tools/server_tools.rb +109 -0
  404. data/lib/ruby_llm/transcription/wav_audio.rb +62 -0
  405. data/lib/ruby_llm/transcription.rb +138 -13
  406. data/lib/ruby_llm/transcription_chunk.rb +68 -0
  407. data/lib/ruby_llm/transport/connection.rb +193 -0
  408. data/lib/ruby_llm/transport/error_middleware.rb +131 -0
  409. data/lib/ruby_llm/transport/usage_middleware.rb +28 -0
  410. data/lib/ruby_llm/transport/websocket_connection.rb +220 -0
  411. data/lib/ruby_llm/uploaded_file.rb +144 -0
  412. data/lib/ruby_llm/version.rb +2 -1
  413. data/lib/ruby_llm/video.rb +136 -0
  414. data/lib/ruby_llm/video_job.rb +150 -0
  415. data/lib/ruby_llm/workflow.rb +91 -0
  416. data/lib/ruby_llm.rb +380 -6
  417. data/lib/tasks/ruby_llm.rake +21 -16
  418. data/skills/rubyllm/SKILL.md +81 -0
  419. data/skills/rubyllm/agents/openai.yaml +4 -0
  420. metadata +344 -97
  421. data/lib/generators/ruby_llm/install/templates/add_references_to_chats_tool_calls_and_messages_migration.rb.tt +0 -9
  422. data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +0 -39
  423. data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +0 -21
  424. data/lib/generators/ruby_llm/install/templates/model_model.rb.tt +0 -3
  425. data/lib/generators/ruby_llm/install/templates/tool_call_model.rb.tt +0 -3
  426. data/lib/generators/ruby_llm/upgrade_to_v1_10/templates/add_v1_10_message_columns.rb.tt +0 -19
  427. data/lib/generators/ruby_llm/upgrade_to_v1_10/upgrade_to_v1_10_generator.rb +0 -50
  428. data/lib/generators/ruby_llm/upgrade_to_v1_14/templates/add_v1_14_tool_call_columns.rb.tt +0 -7
  429. data/lib/generators/ruby_llm/upgrade_to_v1_14/upgrade_to_v1_14_generator.rb +0 -49
  430. data/lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt +0 -145
  431. data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +0 -122
  432. data/lib/generators/ruby_llm/upgrade_to_v1_9/templates/add_v1_9_message_columns.rb.tt +0 -15
  433. data/lib/generators/ruby_llm/upgrade_to_v1_9/upgrade_to_v1_9_generator.rb +0 -49
  434. data/lib/ruby_llm/active_record/acts_as_legacy.rb +0 -597
  435. data/lib/ruby_llm/active_record/model_methods.rb +0 -82
  436. data/lib/ruby_llm/active_record/tool_call_methods.rb +0 -18
  437. data/lib/ruby_llm/aliases.rb +0 -41
  438. data/lib/ruby_llm/connection.rb +0 -159
  439. data/lib/ruby_llm/content.rb +0 -91
  440. data/lib/ruby_llm/deprecator.rb +0 -24
  441. data/lib/ruby_llm/error_middleware.rb +0 -81
  442. data/lib/ruby_llm/instrumentation.rb +0 -36
  443. data/lib/ruby_llm/mime_type.rb +0 -96
  444. data/lib/ruby_llm/model/info.rb +0 -164
  445. data/lib/ruby_llm/model_registry.rb +0 -39
  446. data/lib/ruby_llm/models_schema.json +0 -171
  447. data/lib/ruby_llm/providers/anthropic/chat.rb +0 -291
  448. data/lib/ruby_llm/providers/anthropic/content.rb +0 -44
  449. data/lib/ruby_llm/providers/anthropic/embeddings.rb +0 -20
  450. data/lib/ruby_llm/providers/anthropic/media.rb +0 -92
  451. data/lib/ruby_llm/providers/anthropic/models.rb +0 -59
  452. data/lib/ruby_llm/providers/anthropic/streaming.rb +0 -71
  453. data/lib/ruby_llm/providers/bedrock/chat.rb +0 -405
  454. data/lib/ruby_llm/providers/bedrock/media.rb +0 -108
  455. data/lib/ruby_llm/providers/bedrock/streaming.rb +0 -328
  456. data/lib/ruby_llm/providers/gemini/chat.rb +0 -542
  457. data/lib/ruby_llm/providers/gemini/embeddings.rb +0 -37
  458. data/lib/ruby_llm/providers/gemini/images.rb +0 -47
  459. data/lib/ruby_llm/providers/gemini/models.rb +0 -38
  460. data/lib/ruby_llm/providers/gemini/streaming.rb +0 -98
  461. data/lib/ruby_llm/providers/gemini/tools.rb +0 -234
  462. data/lib/ruby_llm/providers/gpustack/capabilities.rb +0 -20
  463. data/lib/ruby_llm/providers/ollama/capabilities.rb +0 -20
  464. data/lib/ruby_llm/providers/openai/chat.rb +0 -236
  465. data/lib/ruby_llm/providers/openai/embeddings.rb +0 -33
  466. data/lib/ruby_llm/providers/openai/images.rb +0 -90
  467. data/lib/ruby_llm/providers/openai/moderation.rb +0 -34
  468. data/lib/ruby_llm/providers/openai/streaming.rb +0 -55
  469. data/lib/ruby_llm/providers/openai/temperature.rb +0 -28
  470. data/lib/ruby_llm/providers/openai/transcription.rb +0 -71
  471. data/lib/ruby_llm/providers/perplexity/capabilities.rb +0 -72
  472. data/lib/ruby_llm/providers/vertexai/chat.rb +0 -14
  473. data/lib/ruby_llm/providers/vertexai/streaming.rb +0 -14
  474. data/lib/ruby_llm/stream_accumulator.rb +0 -218
  475. data/lib/ruby_llm/streaming.rb +0 -179
  476. data/lib/ruby_llm/tool_concurrency.rb +0 -105
  477. data/lib/ruby_llm/utils.rb +0 -130
  478. data/lib/tasks/models.rake +0 -593
  479. data/lib/tasks/release.rake +0 -94
  480. 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: c047801a2b7a5a642296f0ae84e81ab29b641353ffe8618b3ece44db0b0a3849
4
+ data.tar.gz: 05e4624f14a8e2045408dd21007758612f0644522bae4e250902fd60d66955e1
5
5
  SHA512:
6
- metadata.gz: f75855b2dc79679febdf4edb67130f5ee60d7445bd1a34d7f29c69f3cd363711b881ffe2b51eac8fa4f2a74b297e364e53263e12674333055c8daf702d528a3a
7
- data.tar.gz: aceac99dd86c4e4db028411d1d58cd2602351f3ba0b3cb7fc2334d979459a97294f2617e10770d851b57f70aa26b49f9995dd239d1f52cff43814d1293419a75
6
+ metadata.gz: 6e73e5f6818e70c607c3cabfc6e0dfb19f9271491375b71279e0a205f2ecff8fe0051c691c51230644d32f7ad8f377b8f8bddfcae919e33ff64015ccd9a942e7
7
+ data.tar.gz: fd4285734764621dc2cab77579b8c713aaff907930c1b6849472c470839b2a75e6975832baf4e43adeedd1d3326b92af816af4a63242bdfa2585fab2b72fdc58
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/
21
+ Upgrading: https://rubyllm.com/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,11 @@
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>RubyLLM is the Ruby-native AI framework. Work with models, tools, and agents through one consistent API, in plain Ruby or Rails.</p>
11
+
12
+ [Website](https://rubyllm.com/) · [Getting Started](https://rubyllm.com/getting-started/) · [What's New in 2.0](https://rubyllm.com/whats-new-in-2-0/)
10
13
 
11
14
  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
15
 
@@ -23,28 +26,24 @@ Battle tested at [<picture><source media="(prefers-color-scheme: dark)" srcset="
23
26
 
24
27
  ---
25
28
 
26
- Build chatbots, AI agents, RAG applications. Works with OpenAI, xAI, Anthropic, Google, AWS, local models, and any OpenAI-compatible API.
27
-
28
- ## Build a working Ruby AI chat in two minutes
29
-
30
- https://github.com/user-attachments/assets/65422091-9338-47da-a303-92b918bd1345
29
+ ## 17 providers. One Ruby API.
31
30
 
32
- ## Why RubyLLM?
31
+ Build with the models you want. Move between hosted and local providers without rewriting your application, or connect an OpenAI-compatible endpoint.
33
32
 
34
- Every AI provider ships their own bloated client. Different APIs. Different response formats. Different conventions. It's exhausting.
33
+ [Browse models and pricing](https://rubyllm.com/available-models/) · [Connect a provider](https://rubyllm.com/configuration-providers/)
35
34
 
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.
35
+ ## Start with one line. Add files, tools, and agents
37
36
 
38
- ## Show me the code
37
+ These examples use **2.0.0.rc4 (prerelease)**. Follow [Getting Started](https://rubyllm.com/getting-started/) to install it and configure the providers you want to try. For 1.x, use the [stable-version docs](https://rubyllm.com/v1/).
39
38
 
40
39
  ```ruby
41
- # Just ask questions
42
- chat = RubyLLM.chat
43
- chat.ask "What's the best way to learn Ruby?"
40
+ # Just ask
41
+ RubyLLM.chat.ask "What's the best way to learn Ruby?"
44
42
  ```
45
43
 
46
44
  ```ruby
47
- # Analyze any file type
45
+ # Ask about files with a model that supports their input types
46
+ chat = RubyLLM.chat(model: "gemini-3.7-flash")
48
47
  chat.ask "What's in this image?", with: "ruby_conf.jpg"
49
48
  chat.ask "What's happening in this video?", with: "video.mp4"
50
49
  chat.ask "Describe this meeting", with: "meeting.wav"
@@ -66,28 +65,56 @@ end
66
65
 
67
66
  ```ruby
68
67
  # Generate images
69
- RubyLLM.paint "a sunset over mountains in watercolor style"
68
+ image = RubyLLM.paint "a sunset over mountains in watercolor style"
69
+ image.save "sunset.png"
70
+ ```
71
+
72
+ ```ruby
73
+ # Generate videos
74
+ video = RubyLLM.animate "a paper boat sailing down a rainy gutter"
75
+ video.save "paper_boat.mp4"
70
76
  ```
71
77
 
72
78
  ```ruby
73
79
  # Create embeddings
74
- RubyLLM.embed "Ruby is elegant and expressive"
80
+ embedding = RubyLLM.embed "Ruby is elegant and expressive"
81
+ embedding.vectors
82
+ ```
83
+
84
+ ```ruby
85
+ # Rank search results
86
+ documents = ["Reset your password in Settings.", "Invoices arrive by email."]
87
+ ranked = RubyLLM.rerank("How do I reset my password?", documents, model: "rerank-v3.5")
88
+ ranked.results.first.document
75
89
  ```
76
90
 
77
91
  ```ruby
78
92
  # Transcribe audio to text
79
- RubyLLM.transcribe "meeting.wav"
93
+ transcript = RubyLLM.transcribe "meeting.wav"
94
+ puts transcript.text
95
+ ```
96
+
97
+ ```ruby
98
+ # Turn text into speech
99
+ speech = RubyLLM.speak "Hello, welcome to RubyLLM!"
100
+ speech.save "welcome.mp3"
101
+ ```
102
+
103
+ ```ruby
104
+ # Extract document text as markdown
105
+ document = RubyLLM.ocr "contract.pdf"
106
+ puts document.markdown
80
107
  ```
81
108
 
82
109
  ```ruby
83
- # Moderate content for safety
84
- RubyLLM.moderate "Check if this text is safe"
110
+ # Check whether a moderation model flags content
111
+ RubyLLM.moderate("Some user-generated content").flagged?
85
112
  ```
86
113
 
87
114
  ```ruby
88
115
  # Let AI use your code
89
116
  class Weather < RubyLLM::Tool
90
- desc "Get current weather"
117
+ description "Get current weather"
91
118
 
92
119
  def execute(latitude:, longitude:)
93
120
  url = "https://api.open-meteo.com/v1/forecast?latitude=#{latitude}&longitude=#{longitude}&current=temperature_2m,wind_speed_10m"
@@ -95,13 +122,13 @@ class Weather < RubyLLM::Tool
95
122
  end
96
123
  end
97
124
 
98
- chat.with_tool(Weather).ask "What's the weather in Berlin?"
125
+ chat.with_tools(Weather).ask "What's the weather in Berlin?"
99
126
  ```
100
127
 
101
128
  ```ruby
102
129
  # Define an agent with instructions + tools
103
130
  class WeatherAssistant < RubyLLM::Agent
104
- model "gpt-5-nano"
131
+ model "gpt-5.6-luna"
105
132
  instructions "Be concise and always use tools for weather."
106
133
  tools Weather
107
134
  end
@@ -111,7 +138,7 @@ WeatherAssistant.new.ask "What's the weather in Berlin?"
111
138
 
112
139
  ```ruby
113
140
  # Get structured output
114
- class ProductSchema < RubyLLM::Schema
141
+ class ProductSchema < Schematist::Schema
115
142
  string :name
116
143
  number :price
117
144
  array :features do
@@ -120,50 +147,77 @@ class ProductSchema < RubyLLM::Schema
120
147
  end
121
148
 
122
149
  response = chat.with_schema(ProductSchema).ask "Analyze this product", with: "product.txt"
150
+ response.parsed
123
151
  ```
124
152
 
125
- ## Features
153
+ ## A complete AI framework for Ruby
154
+
155
+ Agents, workflows, RAG, images, audio, and video. Built in, with usage tracking and Rails integration to bring them into your app.
126
156
 
127
157
  * **Chat:** Conversational AI with `RubyLLM.chat`
128
158
  * **Vision:** Analyze images and videos
129
- * **Audio:** Transcribe and understand speech with `RubyLLM.transcribe`
130
- * **Documents:** Extract from PDFs, CSVs, JSON, any file type
159
+ * **Audio:** Transcribe speech with `RubyLLM.transcribe` and generate it with `RubyLLM.speak`
160
+ * **Documents:** Ask questions about PDFs, text files, and other supported formats
161
+ * **OCR:** Turn documents into markdown with `RubyLLM.ocr`
131
162
  * **Image generation:** Create images with `RubyLLM.paint`
163
+ * **Video generation:** Create videos with `RubyLLM.animate`
132
164
  * **Embeddings:** Generate embeddings with `RubyLLM.embed`
133
- * **Moderation:** Content safety with `RubyLLM.moderate`
165
+ * **Reranking:** Order retrieval candidates by relevance with `RubyLLM.rerank`
166
+ * **Moderation:** Content flags, categories, and scores with `RubyLLM.moderate`
134
167
  * **Tools:** Let AI call your Ruby methods
168
+ * **Tool approval:** Park a run until a human approves with `requires_approval`
169
+ * **The agentic loop:** Drive it yourself with `ask_later`, `step`, and `complete?`
170
+ * **Server tools:** Web search, code execution, and MCP connectors with `with_server_tools`
135
171
  * **Agents:** Reusable assistants with `RubyLLM::Agent`
136
- * **Structured output:** JSON schemas that just work
172
+ * **Prompt templates:** ERB prompts in `app/prompts`, rendered with `RubyLLM.render_prompt`
173
+ * **Workflows:** Correlate multi-agent runs in your telemetry with `RubyLLM.workflow`
174
+ * **Structured output:** Define a Ruby schema and read the result with `response.parsed`
137
175
  * **Streaming:** Real-time responses with blocks
138
- * **Rails:** ActiveRecord integration with `acts_as_chat`
176
+ * **Rails:** Active Record persistence, Active Storage attachments, Hotwire streaming, and generators
177
+ * **Files:** Upload once and reuse across chats with `RubyLLM.upload`
178
+ * **Prompt caching:** Turn on the provider's cache with `with_caching` and `cache_until_here`
179
+ * **Fallbacks and cancellation:** Retry on backup models with `with_fallbacks`, stop a run with `cancel`
180
+ * **Cost tracking:** A per-attempt usage ledger behind `chat.tokens` and `chat.cost`
139
181
  * **Async:** Fiber-based concurrency
140
- * **Model registry:** 800+ models with capability detection and pricing
182
+ * **Model registry:** Browse capabilities, limits, and pricing across providers
141
183
  * **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
184
+ * **Citations:** Normalized source citations from documents, search, and grounding
185
+ * **Batches:** Provider-side batch processing with provider-specific discounts via `RubyLLM.batch`
186
+ * **Compaction:** Let providers condense long conversations with `with_compaction`
187
+ * **Token counting:** Count a request before you send it with `count_tokens`
188
+ * **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
189
 
144
190
  ## Installation
145
191
 
146
- Add to your Gemfile:
147
- ```ruby
148
- gem 'ruby_llm'
192
+ Install the 2.0 release candidate:
193
+
194
+ ```bash
195
+ bundle add ruby_llm --version 2.0.0.rc4
149
196
  ```
150
- Then `bundle install`.
151
197
 
152
- Configure your API keys:
198
+ Configure a provider in your script, or in `config/initializers/ruby_llm.rb` in Rails:
199
+
153
200
  ```ruby
154
- # config/initializers/ruby_llm.rb
201
+ require 'ruby_llm'
202
+
155
203
  RubyLLM.configure do |config|
156
- config.openai_api_key = ENV['OPENAI_API_KEY']
204
+ config.openai_api_key = ENV.fetch('OPENAI_API_KEY')
157
205
  end
158
206
  ```
159
207
 
160
- ## Rails
208
+ 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/getting-started/) shows each setup beside its example. If your app uses 1.16, follow the [upgrade guide](https://rubyllm.com/upgrading/) before deploying 2.0.
209
+
210
+ ## Feels at home in Rails
211
+
212
+ Save conversations with Active Record and stream replies with Hotwire. The generators give you a working chat UI. Watch the two-minute demo.
213
+
214
+ https://github.com/user-attachments/assets/65422091-9338-47da-a303-92b918bd1345
161
215
 
162
216
  ```bash
163
217
  # Install Rails Integration
164
218
  bin/rails generate ruby_llm:install
165
219
  bin/rails db:migrate
166
- bin/rails ruby_llm:load_models # v1.13+
220
+ bin/rails ruby_llm:load_models
167
221
 
168
222
  # Add Chat UI (optional)
169
223
  bin/rails generate ruby_llm:chat_ui
@@ -174,15 +228,25 @@ class Chat < ApplicationRecord
174
228
  acts_as_chat
175
229
  end
176
230
 
177
- chat = Chat.create! model: "claude-sonnet-4"
231
+ chat = Chat.create! model: "gpt-5.6-luna"
178
232
  chat.ask "What's in this file?", with: "report.pdf"
179
233
  ```
180
234
 
181
- Visit `http://localhost:3000/chats` for a ready-to-use chat interface!
235
+ Start your Rails server and visit `http://localhost:3000/chats` to try the chat interface. See the [Rails guide](https://rubyllm.com/rails/) for persistence, streaming, and background jobs.
236
+
237
+ ## AI coding assistants
238
+
239
+ Give your coding assistant the RubyLLM API and documentation that match your application. From your application directory, install the skill packaged with your gem:
240
+
241
+ ```bash
242
+ npx skills add "$(bundle show ruby_llm)" --skill rubyllm
243
+ ```
244
+
245
+ Choose your coding assistant and installation scope when prompted. See [AI Coding Assistants](https://rubyllm.com/ai-coding-assistants/) for setup and updates.
182
246
 
183
247
  ## Documentation
184
248
 
185
- [rubyllm.com](https://rubyllm.com)
249
+ [Guides](https://rubyllm.com/getting-started/) · [API reference](https://rubyllm.com/api/) · [Models](https://rubyllm.com/available-models/) · [Upgrading](https://rubyllm.com/upgrading/) · [1.x docs](https://rubyllm.com/v1/)
186
250
 
187
251
  ## Contributing
188
252
 
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">