ruby_llm 1.16.0 → 2.0.0.rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (475) hide show
  1. checksums.yaml +4 -4
  2. data/.rdoc_options +25 -0
  3. data/README.md +85 -32
  4. data/exe/ruby_llm +8 -0
  5. data/lib/generators/ruby_llm/agent/templates/agent.rb.tt +0 -1
  6. data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +3 -43
  7. data/lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt +11 -3
  8. data/lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt +8 -0
  9. data/lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt +4 -4
  10. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_chat.html.erb.tt +1 -1
  11. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_form.html.erb.tt +1 -1
  12. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/index.html.erb.tt +1 -1
  13. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/show.html.erb.tt +2 -2
  14. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_assistant.html.erb.tt +1 -1
  15. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_system.html.erb.tt +1 -1
  16. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_tool.html.erb.tt +1 -1
  17. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_tool_calls.html.erb.tt +6 -4
  18. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_user.html.erb.tt +1 -1
  19. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/tool_calls/_default.html.erb.tt +2 -2
  20. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/_model.html.erb.tt +5 -6
  21. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/index.html.erb.tt +2 -2
  22. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/show.html.erb.tt +5 -5
  23. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt +1 -1
  24. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt +1 -1
  25. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt +1 -1
  26. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt +2 -2
  27. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_assistant.html.erb.tt +1 -1
  28. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_system.html.erb.tt +1 -1
  29. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool.html.erb.tt +1 -1
  30. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool_calls.html.erb.tt +6 -4
  31. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_user.html.erb.tt +1 -1
  32. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt +4 -6
  33. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/tool_calls/_default.html.erb.tt +2 -2
  34. data/lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt +5 -6
  35. data/lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt +2 -2
  36. data/lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt +3 -3
  37. data/lib/generators/ruby_llm/generator_helpers.rb +106 -62
  38. data/lib/generators/ruby_llm/install/install_generator.rb +3 -11
  39. data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +2 -0
  40. data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +14 -8
  41. data/lib/generators/ruby_llm/install/templates/create_ruby_llm_records_migration.rb.tt +117 -0
  42. data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +0 -8
  43. data/lib/generators/ruby_llm/provider/cli.rb +175 -0
  44. data/lib/generators/ruby_llm/provider/scaffold.rb +323 -0
  45. data/lib/generators/ruby_llm/provider/templates/core/provider.rb.erb +37 -0
  46. data/lib/generators/ruby_llm/provider/templates/core/provider_spec.rb.erb +34 -0
  47. data/lib/generators/ruby_llm/provider/templates/gem/archspec.rb.erb +14 -0
  48. data/lib/generators/ruby_llm/provider/templates/gem/bin/console.erb +15 -0
  49. data/lib/generators/ruby_llm/provider/templates/gem/bin/setup.erb +5 -0
  50. data/lib/generators/ruby_llm/provider/templates/gem/chat_schema_spec.rb.erb +30 -0
  51. data/lib/generators/ruby_llm/provider/templates/gem/chat_spec.rb.erb +35 -0
  52. data/lib/generators/ruby_llm/provider/templates/gem/chat_streaming_spec.rb.erb +22 -0
  53. data/lib/generators/ruby_llm/provider/templates/gem/chat_tools_spec.rb.erb +30 -0
  54. data/lib/generators/ruby_llm/provider/templates/gem/ci.yml.erb +32 -0
  55. data/lib/generators/ruby_llm/provider/templates/gem/embedding_spec.rb.erb +49 -0
  56. data/lib/generators/ruby_llm/provider/templates/gem/env.erb +2 -0
  57. data/lib/generators/ruby_llm/provider/templates/gem/fixtures_gitkeep.erb +1 -0
  58. data/lib/generators/ruby_llm/provider/templates/gem/flayignore.erb +1 -0
  59. data/lib/generators/ruby_llm/provider/templates/gem/gemfile.erb +23 -0
  60. data/lib/generators/ruby_llm/provider/templates/gem/gemspec.erb +28 -0
  61. data/lib/generators/ruby_llm/provider/templates/gem/gitignore.erb +7 -0
  62. data/lib/generators/ruby_llm/provider/templates/gem/gitleaks.yml.erb +22 -0
  63. data/lib/generators/ruby_llm/provider/templates/gem/image_spec.rb.erb +23 -0
  64. data/lib/generators/ruby_llm/provider/templates/gem/license.erb +21 -0
  65. data/lib/generators/ruby_llm/provider/templates/gem/models.rb.erb +19 -0
  66. data/lib/generators/ruby_llm/provider/templates/gem/models_spec.rb.erb +17 -0
  67. data/lib/generators/ruby_llm/provider/templates/gem/moderation_spec.rb.erb +22 -0
  68. data/lib/generators/ruby_llm/provider/templates/gem/overcommit.yml.erb +31 -0
  69. data/lib/generators/ruby_llm/provider/templates/gem/provider.rb.erb +52 -0
  70. data/lib/generators/ruby_llm/provider/templates/gem/provider_spec.rb.erb +38 -0
  71. data/lib/generators/ruby_llm/provider/templates/gem/rakefile.erb +38 -0
  72. data/lib/generators/ruby_llm/provider/templates/gem/readme.md.erb +50 -0
  73. data/lib/generators/ruby_llm/provider/templates/gem/release.yml.erb +36 -0
  74. data/lib/generators/ruby_llm/provider/templates/gem/rerank_spec.rb.erb +23 -0
  75. data/lib/generators/ruby_llm/provider/templates/gem/rspec.erb +2 -0
  76. data/lib/generators/ruby_llm/provider/templates/gem/rubocop.yml.erb +29 -0
  77. data/lib/generators/ruby_llm/provider/templates/gem/rubyllm_configuration.rb.erb +14 -0
  78. data/lib/generators/ruby_llm/provider/templates/gem/spec_helper.rb.erb +26 -0
  79. data/lib/generators/ruby_llm/provider/templates/gem/speech_spec.rb.erb +25 -0
  80. data/lib/generators/ruby_llm/provider/templates/gem/vcr_configuration.rb.erb +16 -0
  81. data/lib/generators/ruby_llm/provider/templates/gem/video_spec.rb.erb +27 -0
  82. data/lib/generators/ruby_llm/schema/schema_generator.rb +5 -1
  83. data/lib/generators/ruby_llm/schema/templates/schema.rb.tt +1 -1
  84. data/lib/generators/ruby_llm/tool/templates/tailwind/tool_call.html.erb.tt +13 -0
  85. data/lib/generators/ruby_llm/tool/templates/tailwind/tool_result.html.erb.tt +21 -0
  86. data/lib/generators/ruby_llm/tool/templates/tool.rb.tt +3 -3
  87. data/lib/generators/ruby_llm/tool/templates/tool_call.html.erb.tt +7 -12
  88. data/lib/generators/ruby_llm/tool/templates/tool_result.html.erb.tt +5 -2
  89. data/lib/generators/ruby_llm/tool/tool_generator.rb +25 -59
  90. data/lib/generators/ruby_llm/upgrade/templates/backfill_v2_data.rb.tt +461 -0
  91. data/lib/generators/ruby_llm/upgrade/templates/cleanup_v2_upgrade.rb.tt +101 -0
  92. data/lib/generators/ruby_llm/upgrade/templates/finish_v2_upgrade.rb.tt +215 -0
  93. data/lib/generators/ruby_llm/upgrade/templates/prepare_v2_upgrade.rb.tt +660 -0
  94. data/lib/generators/ruby_llm/upgrade/templates/ruby_llm_upgrade.rb.tt +222 -0
  95. data/lib/generators/ruby_llm/upgrade/templates/upgrade_initializer.rb.tt +13 -0
  96. data/lib/generators/ruby_llm/upgrade/upgrade_generator.rb +167 -0
  97. data/lib/generators/ruby_llm/upgrade/upgrade_migration.rb +344 -0
  98. data/lib/ruby_llm/accounting/usage.rb +245 -0
  99. data/lib/ruby_llm/active_record/acts_as.rb +94 -111
  100. data/lib/ruby_llm/active_record/attachment_helpers.rb +186 -0
  101. data/lib/ruby_llm/active_record/batch.rb +97 -0
  102. data/lib/ruby_llm/active_record/chat_methods.rb +828 -305
  103. data/lib/ruby_llm/active_record/message_methods.rb +113 -136
  104. data/lib/ruby_llm/active_record/model.rb +135 -0
  105. data/lib/ruby_llm/active_record/payload_helpers.rb +1 -2
  106. data/lib/ruby_llm/active_record/tool_call.rb +33 -0
  107. data/lib/ruby_llm/active_record/usage.rb +61 -0
  108. data/lib/ruby_llm/agent.rb +1065 -151
  109. data/lib/ruby_llm/aliases.json +268 -100
  110. data/lib/ruby_llm/attachment.rb +187 -48
  111. data/lib/ruby_llm/batch.rb +432 -0
  112. data/lib/ruby_llm/cached_content.rb +112 -0
  113. data/lib/ruby_llm/chat/tool_concurrency.rb +111 -0
  114. data/lib/ruby_llm/chat.rb +1127 -198
  115. data/lib/ruby_llm/chunk.rb +10 -0
  116. data/lib/ruby_llm/citation.rb +105 -0
  117. data/lib/ruby_llm/configuration.rb +261 -24
  118. data/lib/ruby_llm/context.rb +128 -6
  119. data/lib/ruby_llm/cost.rb +217 -80
  120. data/lib/ruby_llm/downloaded_file.rb +33 -0
  121. data/lib/ruby_llm/embedding.rb +121 -17
  122. data/lib/ruby_llm/embedding_request.rb +53 -0
  123. data/lib/ruby_llm/error.rb +159 -23
  124. data/lib/ruby_llm/fallback.rb +133 -0
  125. data/lib/ruby_llm/files/mime_type.rb +97 -0
  126. data/lib/ruby_llm/image.rb +153 -32
  127. data/lib/ruby_llm/message.rb +233 -54
  128. data/lib/ruby_llm/model/modalities.rb +17 -4
  129. data/lib/ruby_llm/model/pricing.rb +24 -5
  130. data/lib/ruby_llm/model/pricing_category.rb +103 -14
  131. data/lib/ruby_llm/model/pricing_tier.rb +55 -15
  132. data/lib/ruby_llm/model.rb +244 -2
  133. data/lib/ruby_llm/models/aliases.rb +41 -0
  134. data/lib/ruby_llm/models/registry.rb +165 -0
  135. data/lib/ruby_llm/models/schema.rb +99 -0
  136. data/lib/ruby_llm/models.json +73261 -33733
  137. data/lib/ruby_llm/models.rb +477 -215
  138. data/lib/ruby_llm/moderation.rb +139 -26
  139. data/lib/ruby_llm/ocr.rb +112 -0
  140. data/lib/ruby_llm/prompt.rb +79 -0
  141. data/lib/ruby_llm/protocol/binary_streaming.rb +65 -0
  142. data/lib/ruby_llm/protocol/stream_accumulator.rb +214 -0
  143. data/lib/ruby_llm/protocol/streaming.rb +230 -0
  144. data/lib/ruby_llm/protocol.rb +662 -0
  145. data/lib/ruby_llm/protocols/anthropic/batches.rb +73 -0
  146. data/lib/ruby_llm/protocols/anthropic/chat.rb +548 -0
  147. data/lib/ruby_llm/protocols/anthropic/embeddings.rb +14 -0
  148. data/lib/ruby_llm/protocols/anthropic/files.rb +38 -0
  149. data/lib/ruby_llm/protocols/anthropic/media.rb +141 -0
  150. data/lib/ruby_llm/protocols/anthropic/models.rb +129 -0
  151. data/lib/ruby_llm/protocols/anthropic/streaming.rb +167 -0
  152. data/lib/ruby_llm/{providers → protocols}/anthropic/tools.rb +24 -41
  153. data/lib/ruby_llm/protocols/anthropic.rb +101 -0
  154. data/lib/ruby_llm/protocols/azure/files.rb +16 -0
  155. data/lib/ruby_llm/protocols/bedrock/async_videos.rb +109 -0
  156. data/lib/ruby_llm/protocols/bedrock/batches.rb +129 -0
  157. data/lib/ruby_llm/protocols/bedrock/files.rb +110 -0
  158. data/lib/ruby_llm/protocols/bedrock/guardrails.rb +122 -0
  159. data/lib/ruby_llm/protocols/bedrock/rerank.rb +95 -0
  160. data/lib/ruby_llm/protocols/chat_completions/batches.rb +32 -0
  161. data/lib/ruby_llm/protocols/chat_completions/chat.rb +493 -0
  162. data/lib/ruby_llm/protocols/chat_completions/embedding_batches.rb +35 -0
  163. data/lib/ruby_llm/protocols/chat_completions/embeddings.rb +60 -0
  164. data/lib/ruby_llm/protocols/chat_completions/images.rb +127 -0
  165. data/lib/ruby_llm/{providers/openai → protocols/chat_completions}/media.rb +30 -17
  166. data/lib/ruby_llm/protocols/chat_completions/models.rb +39 -0
  167. data/lib/ruby_llm/protocols/chat_completions/moderation.rb +52 -0
  168. data/lib/ruby_llm/protocols/chat_completions/rerank.rb +56 -0
  169. data/lib/ruby_llm/protocols/chat_completions/speech.rb +40 -0
  170. data/lib/ruby_llm/protocols/chat_completions/streaming.rb +69 -0
  171. data/lib/ruby_llm/{providers/openai → protocols/chat_completions}/tools.rb +15 -17
  172. data/lib/ruby_llm/protocols/chat_completions/transcription.rb +150 -0
  173. data/lib/ruby_llm/protocols/chat_completions.rb +21 -0
  174. data/lib/ruby_llm/protocols/cohere/batch_requests.rb +75 -0
  175. data/lib/ruby_llm/protocols/cohere/batches.rb +98 -0
  176. data/lib/ruby_llm/protocols/cohere/chat.rb +227 -0
  177. data/lib/ruby_llm/protocols/cohere/datasets.rb +102 -0
  178. data/lib/ruby_llm/protocols/cohere/embeddings.rb +68 -0
  179. data/lib/ruby_llm/protocols/cohere/media.rb +77 -0
  180. data/lib/ruby_llm/protocols/cohere/models.rb +92 -0
  181. data/lib/ruby_llm/protocols/cohere/ocr.rb +63 -0
  182. data/lib/ruby_llm/protocols/cohere/rerank.rb +52 -0
  183. data/lib/ruby_llm/protocols/cohere/streaming.rb +105 -0
  184. data/lib/ruby_llm/protocols/cohere/tokenization.rb +22 -0
  185. data/lib/ruby_llm/protocols/cohere/tools.rb +132 -0
  186. data/lib/ruby_llm/protocols/cohere/transcription.rb +41 -0
  187. data/lib/ruby_llm/protocols/cohere.rb +21 -0
  188. data/lib/ruby_llm/protocols/converse/batches.rb +55 -0
  189. data/lib/ruby_llm/protocols/converse/chat.rb +695 -0
  190. data/lib/ruby_llm/protocols/converse/media.rb +178 -0
  191. data/lib/ruby_llm/protocols/converse/streaming.rb +432 -0
  192. data/lib/ruby_llm/protocols/converse/thinking_stream.rb +56 -0
  193. data/lib/ruby_llm/protocols/converse.rb +54 -0
  194. data/lib/ruby_llm/protocols/deepgram/models.rb +74 -0
  195. data/lib/ruby_llm/protocols/deepgram/speech.rb +93 -0
  196. data/lib/ruby_llm/protocols/deepgram/streaming_transcription.rb +89 -0
  197. data/lib/ruby_llm/protocols/deepgram/transcription.rb +96 -0
  198. data/lib/ruby_llm/protocols/deepgram.rb +19 -0
  199. data/lib/ruby_llm/protocols/deepseek/files.rb +31 -0
  200. data/lib/ruby_llm/protocols/elevenlabs/assets.rb +36 -0
  201. data/lib/ruby_llm/protocols/elevenlabs/flows/images.rb +74 -0
  202. data/lib/ruby_llm/protocols/elevenlabs/flows/media.rb +42 -0
  203. data/lib/ruby_llm/protocols/elevenlabs/flows/videos.rb +93 -0
  204. data/lib/ruby_llm/protocols/elevenlabs/flows.rb +14 -0
  205. data/lib/ruby_llm/protocols/elevenlabs/models.rb +64 -0
  206. data/lib/ruby_llm/protocols/elevenlabs/speech.rb +67 -0
  207. data/lib/ruby_llm/protocols/elevenlabs/streaming_transcription.rb +127 -0
  208. data/lib/ruby_llm/protocols/elevenlabs/transcription.rb +61 -0
  209. data/lib/ruby_llm/protocols/elevenlabs.rb +15 -0
  210. data/lib/ruby_llm/protocols/files.rb +119 -0
  211. data/lib/ruby_llm/protocols/gemini/batches.rb +162 -0
  212. data/lib/ruby_llm/protocols/gemini/caches.rb +59 -0
  213. data/lib/ruby_llm/protocols/gemini/chat.rb +453 -0
  214. data/lib/ruby_llm/protocols/gemini/embedding_batches.rb +86 -0
  215. data/lib/ruby_llm/protocols/gemini/embeddings.rb +70 -0
  216. data/lib/ruby_llm/protocols/gemini/file_transcription.rb +32 -0
  217. data/lib/ruby_llm/protocols/gemini/files.rb +115 -0
  218. data/lib/ruby_llm/protocols/gemini/images.rb +183 -0
  219. data/lib/ruby_llm/protocols/gemini/live_transcription.rb +140 -0
  220. data/lib/ruby_llm/{providers → protocols}/gemini/media.rb +17 -11
  221. data/lib/ruby_llm/protocols/gemini/models.rb +71 -0
  222. data/lib/ruby_llm/protocols/gemini/speech.rb +56 -0
  223. data/lib/ruby_llm/protocols/gemini/streaming.rb +96 -0
  224. data/lib/ruby_llm/protocols/gemini/tools.rb +157 -0
  225. data/lib/ruby_llm/{providers → protocols}/gemini/transcription.rb +22 -22
  226. data/lib/ruby_llm/protocols/gemini/videos.rb +103 -0
  227. data/lib/ruby_llm/protocols/gemini.rb +35 -0
  228. data/lib/ruby_llm/protocols/gpustack/responses.rb +111 -0
  229. data/lib/ruby_llm/protocols/gpustack/tokenization.rb +22 -0
  230. data/lib/ruby_llm/protocols/gpustack/videos.rb +96 -0
  231. data/lib/ruby_llm/protocols/interactions/chat.rb +145 -0
  232. data/lib/ruby_llm/protocols/interactions/content.rb +90 -0
  233. data/lib/ruby_llm/protocols/interactions/streaming.rb +91 -0
  234. data/lib/ruby_llm/protocols/interactions/tools.rb +51 -0
  235. data/lib/ruby_llm/protocols/interactions/transcription.rb +58 -0
  236. data/lib/ruby_llm/protocols/interactions.rb +29 -0
  237. data/lib/ruby_llm/protocols/invoke_model/cohere_embeddings.rb +51 -0
  238. data/lib/ruby_llm/protocols/invoke_model/embedding_batches.rb +111 -0
  239. data/lib/ruby_llm/protocols/invoke_model/nova_embeddings.rb +50 -0
  240. data/lib/ruby_llm/protocols/invoke_model/stability_images.rb +103 -0
  241. data/lib/ruby_llm/protocols/invoke_model/titan_multimodal_embeddings.rb +33 -0
  242. data/lib/ruby_llm/protocols/invoke_model/titan_text_embeddings.rb +44 -0
  243. data/lib/ruby_llm/protocols/invoke_model.rb +57 -0
  244. data/lib/ruby_llm/protocols/mistral/content.rb +49 -0
  245. data/lib/ruby_llm/protocols/mistral/conversations/chat.rb +160 -0
  246. data/lib/ruby_llm/protocols/mistral/conversations/images.rb +43 -0
  247. data/lib/ruby_llm/protocols/mistral/conversations/streaming.rb +83 -0
  248. data/lib/ruby_llm/protocols/mistral/conversations.rb +30 -0
  249. data/lib/ruby_llm/protocols/mistral/files.rb +36 -0
  250. data/lib/ruby_llm/protocols/mistral/multi_completion.rb +160 -0
  251. data/lib/ruby_llm/protocols/openai/batches.rb +126 -0
  252. data/lib/ruby_llm/protocols/openai/files.rb +42 -0
  253. data/lib/ruby_llm/protocols/openrouter/batches.rb +147 -0
  254. data/lib/ruby_llm/protocols/openrouter/files.rb +24 -0
  255. data/lib/ruby_llm/protocols/openrouter/responses.rb +53 -0
  256. data/lib/ruby_llm/protocols/openrouter/transcription.rb +51 -0
  257. data/lib/ruby_llm/protocols/perplexity/files.rb +48 -0
  258. data/lib/ruby_llm/protocols/perplexity/router.rb +59 -0
  259. data/lib/ruby_llm/protocols/responses/approvals.rb +32 -0
  260. data/lib/ruby_llm/protocols/responses/batches.rb +32 -0
  261. data/lib/ruby_llm/protocols/responses/chat.rb +476 -0
  262. data/lib/ruby_llm/protocols/responses/compaction.rb +29 -0
  263. data/lib/ruby_llm/protocols/responses/media.rb +61 -0
  264. data/lib/ruby_llm/protocols/responses/streaming.rb +117 -0
  265. data/lib/ruby_llm/protocols/responses/token_counting.rb +26 -0
  266. data/lib/ruby_llm/protocols/responses/tools.rb +39 -0
  267. data/lib/ruby_llm/protocols/responses.rb +35 -0
  268. data/lib/ruby_llm/protocols/vertexai/batch_prediction.rb +155 -0
  269. data/lib/ruby_llm/protocols/vertexai/embedding_prediction/requests.rb +85 -0
  270. data/lib/ruby_llm/protocols/vertexai/embedding_prediction/results.rb +74 -0
  271. data/lib/ruby_llm/protocols/vertexai/embedding_prediction.rb +56 -0
  272. data/lib/ruby_llm/protocols/vertexai/files.rb +101 -0
  273. data/lib/ruby_llm/protocols/vertexai/ranking.rb +69 -0
  274. data/lib/ruby_llm/protocols/vertexai/research.rb +193 -0
  275. data/lib/ruby_llm/protocols/xai/files.rb +30 -0
  276. data/lib/ruby_llm/protocols/xai/streaming_transcription.rb +120 -0
  277. data/lib/ruby_llm/protocols/xai/tokenization.rb +23 -0
  278. data/lib/ruby_llm/provider.rb +560 -128
  279. data/lib/ruby_llm/providers/anthropic/capabilities.rb +5 -7
  280. data/lib/ruby_llm/providers/anthropic.rb +4 -6
  281. data/lib/ruby_llm/providers/azure/audio.rb +18 -0
  282. data/lib/ruby_llm/providers/azure/capabilities.rb +16 -0
  283. data/lib/ruby_llm/providers/azure/chat.rb +2 -9
  284. data/lib/ruby_llm/providers/azure/chat_completions/batches.rb +29 -0
  285. data/lib/ruby_llm/providers/azure/chat_completions.rb +80 -0
  286. data/lib/ruby_llm/providers/azure/cohere.rb +33 -0
  287. data/lib/ruby_llm/providers/azure/embeddings.rb +3 -2
  288. data/lib/ruby_llm/providers/azure/images.rb +22 -0
  289. data/lib/ruby_llm/providers/azure/media.rb +5 -14
  290. data/lib/ruby_llm/providers/azure/models.rb +35 -0
  291. data/lib/ruby_llm/providers/azure/responses.rb +26 -0
  292. data/lib/ruby_llm/providers/azure/videos.rb +64 -0
  293. data/lib/ruby_llm/providers/azure.rb +77 -78
  294. data/lib/ruby_llm/providers/bedrock/auth.rb +60 -41
  295. data/lib/ruby_llm/providers/bedrock/capabilities.rb +18 -0
  296. data/lib/ruby_llm/providers/bedrock/mantle/anthropic.rb +39 -0
  297. data/lib/ruby_llm/providers/bedrock/mantle/chat_completions.rb +23 -0
  298. data/lib/ruby_llm/providers/bedrock/mantle/responses.rb +24 -0
  299. data/lib/ruby_llm/providers/bedrock/mantle/voxtral.rb +96 -0
  300. data/lib/ruby_llm/providers/bedrock/mantle.rb +57 -0
  301. data/lib/ruby_llm/providers/bedrock/models.rb +193 -41
  302. data/lib/ruby_llm/providers/bedrock.rb +216 -45
  303. data/lib/ruby_llm/providers/cohere.rb +31 -0
  304. data/lib/ruby_llm/providers/deepgram.rb +37 -0
  305. data/lib/ruby_llm/providers/deepseek/capabilities.rb +4 -51
  306. data/lib/ruby_llm/providers/deepseek/chat.rb +49 -2
  307. data/lib/ruby_llm/providers/deepseek/responses.rb +68 -0
  308. data/lib/ruby_llm/providers/deepseek.rb +9 -2
  309. data/lib/ruby_llm/providers/elevenlabs.rb +35 -0
  310. data/lib/ruby_llm/providers/gemini/capabilities.rb +8 -107
  311. data/lib/ruby_llm/providers/gemini.rb +15 -8
  312. data/lib/ruby_llm/providers/gpustack/chat.rb +2 -16
  313. data/lib/ruby_llm/providers/gpustack/embeddings.rb +28 -0
  314. data/lib/ruby_llm/providers/gpustack/media.rb +17 -16
  315. data/lib/ruby_llm/providers/gpustack/models.rb +72 -62
  316. data/lib/ruby_llm/providers/gpustack/speech.rb +15 -0
  317. data/lib/ruby_llm/providers/gpustack/transcription.rb +29 -0
  318. data/lib/ruby_llm/providers/gpustack.rb +35 -11
  319. data/lib/ruby_llm/providers/mistral/capabilities.rb +7 -155
  320. data/lib/ruby_llm/providers/mistral/chat.rb +37 -61
  321. data/lib/ruby_llm/providers/mistral/chat_completions/batches.rb +120 -0
  322. data/lib/ruby_llm/providers/mistral/chat_completions.rb +21 -0
  323. data/lib/ruby_llm/providers/mistral/conversations.rb +12 -0
  324. data/lib/ruby_llm/providers/mistral/embeddings.rb +6 -4
  325. data/lib/ruby_llm/providers/mistral/media.rb +6 -18
  326. data/lib/ruby_llm/providers/mistral/models.rb +57 -23
  327. data/lib/ruby_llm/providers/mistral/ocr.rb +47 -0
  328. data/lib/ruby_llm/providers/mistral/speech.rb +51 -0
  329. data/lib/ruby_llm/providers/mistral/transcription.rb +62 -0
  330. data/lib/ruby_llm/providers/mistral.rb +16 -4
  331. data/lib/ruby_llm/providers/ollama/chat.rb +7 -13
  332. data/lib/ruby_llm/providers/ollama/media.rb +6 -15
  333. data/lib/ruby_llm/providers/ollama/models.rb +50 -9
  334. data/lib/ruby_llm/providers/ollama.rb +9 -8
  335. data/lib/ruby_llm/providers/ollama_cloud/models.rb +14 -0
  336. data/lib/ruby_llm/providers/ollama_cloud.rb +40 -0
  337. data/lib/ruby_llm/providers/openai/capabilities.rb +54 -259
  338. data/lib/ruby_llm/providers/openai/models.rb +23 -23
  339. data/lib/ruby_llm/providers/openai/responses.rb +13 -0
  340. data/lib/ruby_llm/providers/openai.rb +92 -11
  341. data/lib/ruby_llm/providers/openrouter/chat.rb +130 -108
  342. data/lib/ruby_llm/providers/openrouter/embeddings.rb +51 -0
  343. data/lib/ruby_llm/providers/openrouter/images.rb +44 -43
  344. data/lib/ruby_llm/providers/openrouter/media.rb +34 -0
  345. data/lib/ruby_llm/providers/openrouter/models.rb +50 -11
  346. data/lib/ruby_llm/providers/openrouter/speech.rb +32 -0
  347. data/lib/ruby_llm/providers/openrouter/streaming.rb +31 -38
  348. data/lib/ruby_llm/providers/openrouter/videos.rb +81 -0
  349. data/lib/ruby_llm/providers/openrouter.rb +78 -20
  350. data/lib/ruby_llm/providers/perplexity/chat.rb +2 -9
  351. data/lib/ruby_llm/providers/perplexity/embeddings.rb +32 -0
  352. data/lib/ruby_llm/providers/perplexity/media.rb +5 -21
  353. data/lib/ruby_llm/providers/perplexity/models.rb +80 -13
  354. data/lib/ruby_llm/providers/perplexity.rb +28 -20
  355. data/lib/ruby_llm/providers/vertexai/anthropic/batches.rb +52 -0
  356. data/lib/ruby_llm/providers/vertexai/anthropic.rb +34 -0
  357. data/lib/ruby_llm/providers/vertexai/capabilities.rb +19 -0
  358. data/lib/ruby_llm/providers/vertexai/chat_completions/batches.rb +54 -0
  359. data/lib/ruby_llm/providers/vertexai/chat_completions.rb +15 -0
  360. data/lib/ruby_llm/providers/vertexai/embed_content.rb +42 -0
  361. data/lib/ruby_llm/providers/vertexai/embeddings.rb +22 -7
  362. data/lib/ruby_llm/providers/vertexai/gemini/batches.rb +42 -0
  363. data/lib/ruby_llm/providers/vertexai/gemini.rb +69 -0
  364. data/lib/ruby_llm/providers/vertexai/live_transcription.rb +24 -0
  365. data/lib/ruby_llm/providers/vertexai/mistral.rb +28 -0
  366. data/lib/ruby_llm/providers/vertexai/models.rb +145 -43
  367. data/lib/ruby_llm/providers/vertexai/transcription.rb +49 -4
  368. data/lib/ruby_llm/providers/vertexai/videos.rb +61 -0
  369. data/lib/ruby_llm/providers/vertexai.rb +160 -17
  370. data/lib/ruby_llm/providers/xai/capabilities.rb +18 -0
  371. data/lib/ruby_llm/providers/xai/chat.rb +3 -2
  372. data/lib/ruby_llm/providers/xai/chat_completions/batches.rb +108 -0
  373. data/lib/ruby_llm/providers/xai/chat_completions.rb +19 -0
  374. data/lib/ruby_llm/providers/xai/images.rb +91 -0
  375. data/lib/ruby_llm/providers/xai/models.rb +32 -36
  376. data/lib/ruby_llm/providers/xai/reported_cost.rb +18 -0
  377. data/lib/ruby_llm/providers/xai/responses.rb +52 -0
  378. data/lib/ruby_llm/providers/xai/speech.rb +45 -0
  379. data/lib/ruby_llm/providers/xai/transcription.rb +48 -0
  380. data/lib/ruby_llm/providers/xai/videos.rb +87 -0
  381. data/lib/ruby_llm/providers/xai.rb +15 -5
  382. data/lib/ruby_llm/railtie.rb +7 -16
  383. data/lib/ruby_llm/rerank.rb +105 -0
  384. data/lib/ruby_llm/research_job.rb +241 -0
  385. data/lib/ruby_llm/search_results.rb +68 -0
  386. data/lib/ruby_llm/server_tool_call.rb +73 -0
  387. data/lib/ruby_llm/speech.rb +159 -0
  388. data/lib/ruby_llm/speech_chunk.rb +33 -0
  389. data/lib/ruby_llm/support/deprecator.rb +22 -0
  390. data/lib/ruby_llm/support/inspectable.rb +49 -0
  391. data/lib/ruby_llm/support/instrumentation.rb +41 -0
  392. data/lib/ruby_llm/support/utils.rb +147 -0
  393. data/lib/ruby_llm/thinking.rb +127 -20
  394. data/lib/ruby_llm/tokenization.rb +59 -0
  395. data/lib/ruby_llm/tokens.rb +103 -33
  396. data/lib/ruby_llm/tool.rb +266 -91
  397. data/lib/ruby_llm/tool_call.rb +36 -3
  398. data/lib/ruby_llm/tools/server_tools.rb +109 -0
  399. data/lib/ruby_llm/transcription/wav_audio.rb +62 -0
  400. data/lib/ruby_llm/transcription.rb +138 -13
  401. data/lib/ruby_llm/transcription_chunk.rb +68 -0
  402. data/lib/ruby_llm/transport/connection.rb +193 -0
  403. data/lib/ruby_llm/transport/error_middleware.rb +131 -0
  404. data/lib/ruby_llm/transport/usage_middleware.rb +28 -0
  405. data/lib/ruby_llm/transport/websocket_connection.rb +220 -0
  406. data/lib/ruby_llm/uploaded_file.rb +144 -0
  407. data/lib/ruby_llm/version.rb +2 -1
  408. data/lib/ruby_llm/video.rb +136 -0
  409. data/lib/ruby_llm/video_job.rb +150 -0
  410. data/lib/ruby_llm/workflow.rb +91 -0
  411. data/lib/ruby_llm.rb +380 -6
  412. data/lib/tasks/ruby_llm.rake +21 -16
  413. data/skills/rubyllm/SKILL.md +81 -0
  414. data/skills/rubyllm/agents/openai.yaml +4 -0
  415. metadata +339 -97
  416. data/lib/generators/ruby_llm/install/templates/add_references_to_chats_tool_calls_and_messages_migration.rb.tt +0 -9
  417. data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +0 -39
  418. data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +0 -21
  419. data/lib/generators/ruby_llm/install/templates/model_model.rb.tt +0 -3
  420. data/lib/generators/ruby_llm/install/templates/tool_call_model.rb.tt +0 -3
  421. data/lib/generators/ruby_llm/upgrade_to_v1_10/templates/add_v1_10_message_columns.rb.tt +0 -19
  422. data/lib/generators/ruby_llm/upgrade_to_v1_10/upgrade_to_v1_10_generator.rb +0 -50
  423. data/lib/generators/ruby_llm/upgrade_to_v1_14/templates/add_v1_14_tool_call_columns.rb.tt +0 -7
  424. data/lib/generators/ruby_llm/upgrade_to_v1_14/upgrade_to_v1_14_generator.rb +0 -49
  425. data/lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt +0 -145
  426. data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +0 -122
  427. data/lib/generators/ruby_llm/upgrade_to_v1_9/templates/add_v1_9_message_columns.rb.tt +0 -15
  428. data/lib/generators/ruby_llm/upgrade_to_v1_9/upgrade_to_v1_9_generator.rb +0 -49
  429. data/lib/ruby_llm/active_record/acts_as_legacy.rb +0 -597
  430. data/lib/ruby_llm/active_record/model_methods.rb +0 -82
  431. data/lib/ruby_llm/active_record/tool_call_methods.rb +0 -18
  432. data/lib/ruby_llm/aliases.rb +0 -41
  433. data/lib/ruby_llm/connection.rb +0 -159
  434. data/lib/ruby_llm/content.rb +0 -91
  435. data/lib/ruby_llm/deprecator.rb +0 -24
  436. data/lib/ruby_llm/error_middleware.rb +0 -81
  437. data/lib/ruby_llm/instrumentation.rb +0 -36
  438. data/lib/ruby_llm/mime_type.rb +0 -96
  439. data/lib/ruby_llm/model/info.rb +0 -164
  440. data/lib/ruby_llm/model_registry.rb +0 -39
  441. data/lib/ruby_llm/models_schema.json +0 -171
  442. data/lib/ruby_llm/providers/anthropic/chat.rb +0 -291
  443. data/lib/ruby_llm/providers/anthropic/content.rb +0 -44
  444. data/lib/ruby_llm/providers/anthropic/embeddings.rb +0 -20
  445. data/lib/ruby_llm/providers/anthropic/media.rb +0 -92
  446. data/lib/ruby_llm/providers/anthropic/models.rb +0 -59
  447. data/lib/ruby_llm/providers/anthropic/streaming.rb +0 -71
  448. data/lib/ruby_llm/providers/bedrock/chat.rb +0 -405
  449. data/lib/ruby_llm/providers/bedrock/media.rb +0 -108
  450. data/lib/ruby_llm/providers/bedrock/streaming.rb +0 -328
  451. data/lib/ruby_llm/providers/gemini/chat.rb +0 -542
  452. data/lib/ruby_llm/providers/gemini/embeddings.rb +0 -37
  453. data/lib/ruby_llm/providers/gemini/images.rb +0 -47
  454. data/lib/ruby_llm/providers/gemini/models.rb +0 -38
  455. data/lib/ruby_llm/providers/gemini/streaming.rb +0 -98
  456. data/lib/ruby_llm/providers/gemini/tools.rb +0 -234
  457. data/lib/ruby_llm/providers/gpustack/capabilities.rb +0 -20
  458. data/lib/ruby_llm/providers/ollama/capabilities.rb +0 -20
  459. data/lib/ruby_llm/providers/openai/chat.rb +0 -236
  460. data/lib/ruby_llm/providers/openai/embeddings.rb +0 -33
  461. data/lib/ruby_llm/providers/openai/images.rb +0 -90
  462. data/lib/ruby_llm/providers/openai/moderation.rb +0 -34
  463. data/lib/ruby_llm/providers/openai/streaming.rb +0 -55
  464. data/lib/ruby_llm/providers/openai/temperature.rb +0 -28
  465. data/lib/ruby_llm/providers/openai/transcription.rb +0 -71
  466. data/lib/ruby_llm/providers/perplexity/capabilities.rb +0 -72
  467. data/lib/ruby_llm/providers/vertexai/chat.rb +0 -14
  468. data/lib/ruby_llm/providers/vertexai/streaming.rb +0 -14
  469. data/lib/ruby_llm/stream_accumulator.rb +0 -218
  470. data/lib/ruby_llm/streaming.rb +0 -179
  471. data/lib/ruby_llm/tool_concurrency.rb +0 -105
  472. data/lib/ruby_llm/utils.rb +0 -130
  473. data/lib/tasks/models.rake +0 -593
  474. data/lib/tasks/release.rake +0 -94
  475. data/lib/tasks/vcr.rake +0 -124
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
-
5
- module RubyLLM
6
- # Manages model aliases for provider-specific versions
7
- class Aliases
8
- class << self
9
- def resolve(model_id, provider = nil)
10
- return model_id unless aliases[model_id]
11
-
12
- if provider
13
- aliases[model_id][provider.to_s] || model_id
14
- else
15
- aliases[model_id].values.first || model_id
16
- end
17
- end
18
-
19
- def aliases
20
- @aliases ||= load_aliases
21
- end
22
-
23
- def aliases_file
24
- File.expand_path('aliases.json', __dir__)
25
- end
26
-
27
- def load_aliases
28
- if File.exist?(aliases_file)
29
- JSON.parse(File.read(aliases_file))
30
- else
31
- {}
32
- end
33
- end
34
-
35
- # Replaces the cached alias map from aliases.json.
36
- def reload!
37
- @aliases = load_aliases
38
- end
39
- end
40
- end
41
- end
@@ -1,159 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'faraday'
4
- require 'faraday/multipart'
5
- require 'faraday/retry'
6
- require 'ruby_llm/error_middleware'
7
- require 'timeout'
8
-
9
- module RubyLLM
10
- # Connection class for managing API connections to various providers.
11
- class Connection
12
- attr_reader :provider, :connection, :config
13
-
14
- def self.basic(&)
15
- Faraday.new do |f|
16
- f.response :logger,
17
- RubyLLM.logger,
18
- bodies: false,
19
- errors: true,
20
- headers: false,
21
- log_level: :debug
22
- f.response :raise_error
23
- yield f if block_given?
24
- end
25
- end
26
-
27
- def initialize(provider, config)
28
- @provider = provider
29
- @config = config
30
-
31
- ensure_configured!
32
- @connection ||= Faraday.new(provider.api_base) do |faraday|
33
- setup_timeout(faraday)
34
- setup_logging(faraday)
35
- setup_retry(faraday)
36
- setup_middleware(faraday)
37
- setup_http_proxy(faraday)
38
- end
39
- end
40
-
41
- def post(url, payload, &)
42
- instrument_request(:post, url) do
43
- @connection.post url, payload do |req|
44
- req.headers.merge! provider_headers
45
- yield req if block_given?
46
- end
47
- end
48
- end
49
-
50
- def get(url, &)
51
- instrument_request(:get, url) do
52
- @connection.get url do |req|
53
- req.headers.merge! provider_headers
54
- yield req if block_given?
55
- end
56
- end
57
- end
58
-
59
- def instance_variables
60
- super - %i[@config @connection]
61
- end
62
-
63
- private
64
-
65
- def provider_headers
66
- @provider.respond_to?(:headers) ? @provider.headers : {}
67
- end
68
-
69
- def instrument_request(method, url)
70
- payload = {
71
- provider: @provider.respond_to?(:slug) ? @provider.slug : @provider.class.name,
72
- method: method,
73
- url: url
74
- }
75
-
76
- RubyLLM.instrument('request.ruby_llm', payload, config: @config) do
77
- response = yield
78
- payload[:status] = response.status if response.respond_to?(:status)
79
- response
80
- end
81
- end
82
-
83
- def setup_timeout(faraday)
84
- faraday.options.timeout = @config.request_timeout
85
- end
86
-
87
- def setup_logging(faraday)
88
- faraday.response :logger,
89
- RubyLLM.logger,
90
- bodies: RubyLLM.logger.debug?,
91
- errors: true,
92
- headers: false,
93
- log_level: :debug do |logger|
94
- logger.filter(logging_regexp('[A-Za-z0-9+/=]{100,}'), '[BASE64 DATA]')
95
- logger.filter(logging_regexp('[-\\d.e,\\s]{100,}'), '[EMBEDDINGS ARRAY]')
96
- end
97
- end
98
-
99
- def logging_regexp(pattern)
100
- return Regexp.new(pattern) if @config.log_regexp_timeout.nil? || !Regexp.respond_to?(:timeout)
101
-
102
- Regexp.new(pattern, timeout: @config.log_regexp_timeout)
103
- end
104
-
105
- def setup_retry(faraday)
106
- faraday.request :retry, {
107
- max: @config.max_retries,
108
- interval: @config.retry_interval,
109
- interval_randomness: @config.retry_interval_randomness,
110
- backoff_factor: @config.retry_backoff_factor,
111
- methods: Faraday::Retry::Middleware::IDEMPOTENT_METHODS + [:post],
112
- exceptions: retry_exceptions
113
- }
114
- end
115
-
116
- def setup_middleware(faraday)
117
- faraday.request :multipart
118
- faraday.request :json
119
- faraday.response :json
120
- adapter = @config.respond_to?(:faraday_adapter) ? @config.faraday_adapter : :net_http
121
- faraday.adapter(adapter || :net_http)
122
- faraday.use :llm_errors, provider: @provider
123
- end
124
-
125
- def setup_http_proxy(faraday)
126
- return unless @config.http_proxy
127
-
128
- faraday.proxy = @config.http_proxy
129
- end
130
-
131
- def retry_exceptions
132
- [
133
- Errno::ETIMEDOUT,
134
- Timeout::Error,
135
- Faraday::TimeoutError,
136
- Faraday::ConnectionFailed,
137
- Faraday::RetriableResponse,
138
- RubyLLM::RateLimitError,
139
- RubyLLM::ServerError,
140
- RubyLLM::ServiceUnavailableError,
141
- RubyLLM::OverloadedError
142
- ]
143
- end
144
-
145
- def ensure_configured!
146
- return if @provider.configured?
147
-
148
- missing = @provider.configuration_requirements.reject { |req| @config.send(req) }
149
- config_block = <<~RUBY
150
- RubyLLM.configure do |config|
151
- #{missing.map { |key| "config.#{key} = ENV['#{key.to_s.upcase}']" }.join("\n ")}
152
- end
153
- RUBY
154
-
155
- raise ConfigurationError,
156
- "#{@provider.name} provider is not configured. Add this to your initialization:\n\n#{config_block}"
157
- end
158
- end
159
- end
@@ -1,91 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- # Represents the content sent to or received from an LLM.
5
- class Content
6
- attr_reader :text, :attachments
7
-
8
- def initialize(text = nil, attachments = nil)
9
- @text = text
10
- @attachments = []
11
-
12
- process_attachments(attachments)
13
- raise ArgumentError, 'Text and attachments cannot be both nil' if @text.nil? && @attachments.empty?
14
- end
15
-
16
- def add_attachment(source, filename: nil)
17
- @attachments << build_attachment(source, filename:)
18
- self
19
- end
20
-
21
- def format
22
- if @text && @attachments.empty?
23
- @text
24
- else
25
- self
26
- end
27
- end
28
-
29
- def empty?
30
- attachments.empty? && (@text.nil? || (@text.respond_to?(:empty?) && @text.empty?))
31
- end
32
-
33
- # For Rails serialization
34
- def to_h
35
- { text: @text, attachments: @attachments.map(&:to_h) }
36
- end
37
-
38
- private
39
-
40
- def build_attachment(source, filename: nil)
41
- if source.is_a?(Attachment)
42
- return source unless filename
43
-
44
- return Attachment.new(source.source, filename:)
45
- end
46
-
47
- Attachment.new(source, filename:)
48
- end
49
-
50
- def process_attachments_array_or_string(attachments)
51
- Utils.to_safe_array(attachments).each do |file|
52
- next if blank_attachment_entry?(file)
53
-
54
- add_attachment(file)
55
- end
56
- end
57
-
58
- def blank_attachment_entry?(file)
59
- file.nil? || (file.is_a?(String) && file.strip.empty?)
60
- end
61
-
62
- def process_attachments(attachments)
63
- if attachments.is_a?(Hash)
64
- attachments.each_value { |attachment| process_attachments_array_or_string(attachment) }
65
- else
66
- process_attachments_array_or_string attachments
67
- end
68
- end
69
- end
70
-
71
- class Content
72
- # Represents provider-specific payloads that should bypass RubyLLM formatting.
73
- class Raw
74
- attr_reader :value
75
-
76
- def initialize(value)
77
- raise ArgumentError, 'Raw content payload cannot be nil' if value.nil?
78
-
79
- @value = value
80
- end
81
-
82
- def format
83
- @value
84
- end
85
-
86
- def to_h
87
- @value
88
- end
89
- end
90
- end
91
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- # Owns RubyLLM deprecation warnings so applications and tests can decide how
5
- # aggressively to handle compatibility paths.
6
- class Deprecator
7
- def warn(message)
8
- case RubyLLM.config.deprecation_behavior
9
- when :silence
10
- nil
11
- when :raise
12
- raise DeprecationError, message
13
- else
14
- RubyLLM.logger.warn(message)
15
- end
16
- end
17
-
18
- def deprecate(name, replacement:, removal:)
19
- warn("#{name} is deprecated and will be removed in RubyLLM #{removal}. Use #{replacement} instead.")
20
- end
21
- end
22
-
23
- class DeprecationError < StandardError; end
24
- end
@@ -1,81 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'faraday'
4
- require 'ruby_llm/error'
5
-
6
- module RubyLLM
7
- # Faraday middleware that maps provider-specific API errors to RubyLLM errors.
8
- class ErrorMiddleware < Faraday::Middleware
9
- def initialize(app, options = {})
10
- super(app)
11
- @provider = options[:provider]
12
- end
13
-
14
- def call(env)
15
- @app.call(env).on_complete do |response|
16
- self.class.parse_error(provider: @provider, response: response)
17
- end
18
- end
19
-
20
- class << self
21
- CONTEXT_LENGTH_PATTERNS = [
22
- /context length/i,
23
- /context window/i,
24
- /maximum context/i,
25
- /request too large/i,
26
- /too many tokens/i,
27
- /token count exceeds/i,
28
- /input[_\s-]?token/i,
29
- /input or output tokens? must be reduced/i,
30
- /reduce the length of messages/i,
31
- /prompt is too long/i
32
- ].freeze
33
-
34
- def parse_error(provider:, response:) # rubocop:disable Metrics/PerceivedComplexity
35
- message = provider&.parse_error(response)
36
-
37
- case response.status
38
- when 200..399
39
- message
40
- when 400
41
- if context_length_exceeded?(message)
42
- raise ContextLengthExceededError.new(response, message || 'Context length exceeded')
43
- end
44
-
45
- raise BadRequestError.new(response, message || 'Invalid request - please check your input')
46
- when 401
47
- raise UnauthorizedError.new(response, message || 'Invalid API key - check your credentials')
48
- when 402
49
- raise PaymentRequiredError.new(response, message || 'Payment required - please top up your account')
50
- when 403
51
- raise ForbiddenError.new(response,
52
- message || 'Forbidden - you do not have permission to access this resource')
53
- when 429
54
- if context_length_exceeded?(message)
55
- raise ContextLengthExceededError.new(response, message || 'Context length exceeded')
56
- end
57
-
58
- raise RateLimitError.new(response, message || 'Rate limit exceeded - please wait a moment')
59
- when 500
60
- raise ServerError.new(response, message || 'API server error - please try again')
61
- when 502..504
62
- raise ServiceUnavailableError.new(response, message || 'API server unavailable - please try again later')
63
- when 529
64
- raise OverloadedError.new(response, message || 'Service overloaded - please try again later')
65
- else
66
- raise Error.new(response, message || 'An unknown error occurred')
67
- end
68
- end
69
-
70
- private
71
-
72
- def context_length_exceeded?(message)
73
- return false if message.to_s.empty?
74
-
75
- CONTEXT_LENGTH_PATTERNS.any? { |pattern| message.match?(pattern) }
76
- end
77
- end
78
- end
79
- end
80
-
81
- Faraday::Middleware.register_middleware(llm_errors: RubyLLM::ErrorMiddleware)
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- # Emits structured RubyLLM events without requiring a specific observability
5
- # backend. Rails apps can use ActiveSupport::Notifications as the instrumenter.
6
- module Instrumentation
7
- module_function
8
-
9
- def instrument(name, payload = nil, config: nil, **attributes)
10
- payload = build_payload(payload, attributes)
11
- instrumenter = instrumenter_for(config)
12
- return yield(payload) if block_given? && !instrumenter
13
-
14
- unless instrumenter.respond_to?(:instrument)
15
- return yield(payload) if block_given?
16
-
17
- return
18
- end
19
-
20
- if block_given?
21
- instrumenter.instrument(name, payload) { yield(payload) }
22
- else
23
- instrumenter.instrument(name, payload)
24
- end
25
- end
26
-
27
- def build_payload(payload, attributes)
28
- payload ||= {}
29
- attributes.empty? ? payload : payload.merge(attributes)
30
- end
31
-
32
- def instrumenter_for(config)
33
- (config || RubyLLM.config).instrumenter
34
- end
35
- end
36
- end
@@ -1,96 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'marcel'
4
-
5
- module RubyLLM
6
- # MimeTypes module provides methods to handle MIME types using Marcel gem
7
- module MimeType
8
- module_function
9
-
10
- def for(...)
11
- Marcel::MimeType.for(...)
12
- end
13
-
14
- def image?(type)
15
- type.start_with?('image/')
16
- end
17
-
18
- def video?(type)
19
- type.start_with?('video/')
20
- end
21
-
22
- def audio?(type)
23
- type.start_with?('audio/')
24
- end
25
-
26
- def pdf?(type)
27
- type == 'application/pdf'
28
- end
29
-
30
- def document?(type)
31
- return false if pdf?(type) || text?(type)
32
-
33
- DOCUMENT_MIME_TYPES.include?(type) ||
34
- DOCUMENT_MIME_PREFIXES.any? { |prefix| type.start_with?(prefix) }
35
- end
36
-
37
- def text?(type)
38
- type.start_with?('text/') ||
39
- TEXT_SUFFIXES.any? { |suffix| type.end_with?(suffix) } ||
40
- NON_TEXT_PREFIX_TEXT_MIME_TYPES.include?(type)
41
- end
42
-
43
- # MIME types that have a text/ prefix but need to be handled differently
44
- TEXT_SUFFIXES = ['+json', '+xml', '+html', '+yaml', '+csv', '+plain', '+javascript', '+svg'].freeze
45
-
46
- # MIME types that don't have a text/ prefix but should be treated as text
47
- NON_TEXT_PREFIX_TEXT_MIME_TYPES = [
48
- 'application/json', # Base type, even if specific ones end with +json
49
- 'application/xml', # Base type, even if specific ones end with +xml
50
- 'application/javascript',
51
- 'application/ecmascript',
52
- 'application/rtf',
53
- 'application/sql',
54
- 'application/x-sh',
55
- 'application/x-csh',
56
- 'application/x-httpd-php',
57
- 'application/sdp',
58
- 'application/sparql-query',
59
- 'application/graphql',
60
- 'application/yang', # Data modeling language, often serialized as XML/JSON but the type itself is distinct
61
- 'application/mbox', # Mailbox format
62
- 'application/x-tex',
63
- 'application/x-latex',
64
- 'application/x-perl',
65
- 'application/x-python',
66
- 'application/x-tcl',
67
- 'application/pgp-signature', # Often ASCII armored
68
- 'application/pgp-keys', # Often ASCII armored
69
- 'application/vnd.coffeescript',
70
- 'application/vnd.dart',
71
- 'application/vnd.oai.openapi', # Base for OpenAPI, often with +json or +yaml suffix
72
- 'application/vnd.zul', # ZK User Interface Language (can be XML-like)
73
- 'application/x-yaml', # Common non-standard for YAML
74
- 'application/yaml', # Standard for YAML
75
- 'application/toml' # TOML configuration files
76
- ].freeze
77
-
78
- DOCUMENT_MIME_TYPES = [
79
- 'application/msword',
80
- 'application/rtf',
81
- 'application/vnd.apple.keynote',
82
- 'application/vnd.apple.numbers',
83
- 'application/vnd.apple.pages',
84
- 'application/vnd.google-apps.document',
85
- 'application/vnd.google-apps.presentation',
86
- 'application/vnd.google-apps.spreadsheet',
87
- 'application/vnd.ms-excel',
88
- 'application/vnd.ms-powerpoint'
89
- ].freeze
90
-
91
- DOCUMENT_MIME_PREFIXES = [
92
- 'application/vnd.openxmlformats-officedocument.',
93
- 'application/vnd.oasis.opendocument.'
94
- ].freeze
95
- end
96
- end
@@ -1,164 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module Model
5
- # Information about an AI model's capabilities, pricing, and metadata.
6
- class Info
7
- attr_reader :id, :name, :provider, :family, :created_at, :context_window, :max_output_tokens, :knowledge_cutoff,
8
- :modalities, :capabilities, :pricing, :metadata, :reasoning_options
9
-
10
- # Create a default model with assumed capabilities
11
- def self.default(model_id, provider)
12
- new(
13
- id: model_id,
14
- name: model_id.tr('-', ' ').capitalize,
15
- provider: provider,
16
- capabilities: %w[function_calling streaming vision structured_output],
17
- modalities: { input: %w[text image], output: %w[text] },
18
- metadata: { warning: 'Assuming model exists, capabilities may not be accurate' }
19
- )
20
- end
21
-
22
- def initialize(data)
23
- @id = data[:id]
24
- @name = data[:name]
25
- @provider = data[:provider]
26
- @family = data[:family]
27
- @created_at = Utils.to_time(data[:created_at])&.utc
28
- @context_window = data[:context_window]
29
- @max_output_tokens = data[:max_output_tokens]
30
- @knowledge_cutoff = Utils.to_date(data[:knowledge_cutoff])
31
- @modalities = Modalities.new(data[:modalities] || {})
32
- @capabilities = data[:capabilities] || []
33
- @pricing = Pricing.new(data[:pricing] || {})
34
- @metadata = data[:metadata]&.dup || {}
35
- @reasoning_options = normalize_reasoning_options(reasoning_options_from(data))
36
- store_reasoning_options_metadata
37
- end
38
-
39
- def supports?(capability)
40
- capabilities.include?(capability.to_s)
41
- end
42
-
43
- %w[function_calling structured_output batch reasoning citations streaming].each do |cap|
44
- define_method "#{cap}?" do
45
- supports?(cap)
46
- end
47
- end
48
-
49
- def display_name
50
- name
51
- end
52
-
53
- def label
54
- provider_name = provider_class&.name || provider
55
- "#{provider_name} - #{display_name}"
56
- end
57
-
58
- def max_tokens
59
- max_output_tokens
60
- end
61
-
62
- def supports_vision?
63
- modalities.input.include?('image')
64
- end
65
-
66
- def reasoning_option(type)
67
- reasoning_options.find { |option| option[:type] == type.to_s }
68
- end
69
-
70
- def reasoning_option_values(type)
71
- Array(reasoning_option(type)&.fetch(:values, nil))
72
- end
73
-
74
- def supports_video?
75
- modalities.input.include?('video')
76
- end
77
-
78
- def supports_functions?
79
- function_calling?
80
- end
81
-
82
- def input_price_per_million
83
- pricing.text_tokens.input
84
- end
85
-
86
- def output_price_per_million
87
- pricing.text_tokens.output
88
- end
89
-
90
- def cache_read_input_price_per_million
91
- pricing.text_tokens.cache_read_input
92
- end
93
-
94
- def cache_write_input_price_per_million
95
- pricing.text_tokens.cache_write_input
96
- end
97
-
98
- alias cached_input_price_per_million cache_read_input_price_per_million
99
- alias cache_creation_input_price_per_million cache_write_input_price_per_million
100
-
101
- def cost_for(tokens)
102
- tokens = tokens.tokens if tokens.respond_to?(:tokens)
103
-
104
- Cost.new(tokens:, model: self)
105
- end
106
-
107
- def provider_class
108
- RubyLLM::Provider.resolve provider
109
- end
110
-
111
- def type
112
- output = modalities.output
113
- return 'embedding' if output.include?('embeddings')
114
- return 'moderation' if output.include?('moderation')
115
- return 'image' if output.include?('image')
116
- return 'audio' if output.include?('audio')
117
- return 'video' if output.include?('video')
118
-
119
- 'chat'
120
- end
121
-
122
- def to_h
123
- {
124
- id: id,
125
- name: name,
126
- provider: provider,
127
- family: family,
128
- created_at: created_at,
129
- context_window: context_window,
130
- max_output_tokens: max_output_tokens,
131
- knowledge_cutoff: knowledge_cutoff,
132
- modalities: modalities.to_h,
133
- capabilities: capabilities,
134
- pricing: pricing.to_h,
135
- metadata: metadata
136
- }
137
- end
138
-
139
- private
140
-
141
- def reasoning_options_from(data)
142
- data[:reasoning_options] || metadata[:reasoning_options] || metadata['reasoning_options']
143
- end
144
-
145
- def store_reasoning_options_metadata
146
- return unless reasoning_options.any?
147
-
148
- metadata.delete('reasoning_options')
149
- metadata[:reasoning_options] = reasoning_options
150
- end
151
-
152
- def normalize_reasoning_options(options)
153
- Array(options).filter_map do |option|
154
- next unless option.is_a?(Hash)
155
-
156
- normalized = option.to_h.transform_keys(&:to_sym)
157
- normalized[:type] = normalized[:type].to_s if normalized[:type]
158
- normalized[:values] = Array(normalized[:values]).map(&:to_s) if normalized.key?(:values)
159
- normalized
160
- end
161
- end
162
- end
163
- end
164
- end