ruby_llm 1.13.2 → 1.16.0

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 (171) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +16 -13
  3. data/lib/generators/ruby_llm/agent/agent_generator.rb +36 -0
  4. data/lib/generators/ruby_llm/agent/templates/agent.rb.tt +6 -0
  5. data/lib/generators/ruby_llm/agent/templates/instructions.txt.erb.tt +0 -0
  6. data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +110 -41
  7. data/lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt +14 -15
  8. data/lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt +8 -11
  9. data/lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt +2 -2
  10. data/lib/generators/ruby_llm/chat_ui/templates/helpers/messages_helper.rb.tt +25 -0
  11. data/lib/generators/ruby_llm/chat_ui/templates/jobs/chat_response_job.rb.tt +1 -1
  12. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_chat.html.erb.tt +16 -0
  13. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_form.html.erb.tt +31 -0
  14. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/index.html.erb.tt +31 -0
  15. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/new.html.erb.tt +9 -0
  16. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/show.html.erb.tt +27 -0
  17. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_assistant.html.erb.tt +14 -0
  18. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_content.html.erb.tt +1 -0
  19. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_error.html.erb.tt +13 -0
  20. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_form.html.erb.tt +23 -0
  21. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_system.html.erb.tt +10 -0
  22. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_tool.html.erb.tt +2 -0
  23. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_tool_calls.html.erb.tt +4 -0
  24. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_user.html.erb.tt +14 -0
  25. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/tool_calls/_default.html.erb.tt +13 -0
  26. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/tool_results/_default.html.erb.tt +21 -0
  27. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/_model.html.erb.tt +17 -0
  28. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/index.html.erb.tt +40 -0
  29. data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/show.html.erb.tt +27 -0
  30. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt +2 -2
  31. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt +2 -2
  32. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt +19 -7
  33. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/new.html.erb.tt +1 -1
  34. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt +5 -3
  35. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_assistant.html.erb.tt +9 -0
  36. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_content.html.erb.tt +1 -1
  37. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_error.html.erb.tt +8 -0
  38. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_form.html.erb.tt +1 -1
  39. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_system.html.erb.tt +6 -0
  40. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool.html.erb.tt +2 -0
  41. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool_calls.html.erb.tt +4 -7
  42. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_user.html.erb.tt +9 -0
  43. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt +5 -7
  44. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/tool_calls/_default.html.erb.tt +8 -0
  45. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/tool_results/_default.html.erb.tt +16 -0
  46. data/lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt +11 -12
  47. data/lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt +27 -17
  48. data/lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt +3 -4
  49. data/lib/generators/ruby_llm/generator_helpers.rb +41 -17
  50. data/lib/generators/ruby_llm/install/install_generator.rb +21 -18
  51. data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +3 -4
  52. data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +1 -1
  53. data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +3 -3
  54. data/lib/generators/ruby_llm/schema/schema_generator.rb +26 -0
  55. data/lib/generators/ruby_llm/schema/templates/schema.rb.tt +2 -0
  56. data/lib/generators/ruby_llm/tool/templates/tool.rb.tt +9 -0
  57. data/lib/generators/ruby_llm/tool/templates/tool_call.html.erb.tt +13 -0
  58. data/lib/generators/ruby_llm/tool/templates/tool_result.html.erb.tt +13 -0
  59. data/lib/generators/ruby_llm/tool/tool_generator.rb +96 -0
  60. data/lib/generators/ruby_llm/upgrade_to_v1_10/upgrade_to_v1_10_generator.rb +1 -1
  61. data/lib/generators/ruby_llm/upgrade_to_v1_14/templates/add_v1_14_tool_call_columns.rb.tt +7 -0
  62. data/lib/generators/ruby_llm/upgrade_to_v1_14/upgrade_to_v1_14_generator.rb +49 -0
  63. data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +5 -7
  64. data/lib/generators/ruby_llm/upgrade_to_v1_9/upgrade_to_v1_9_generator.rb +1 -1
  65. data/lib/ruby_llm/active_record/acts_as.rb +6 -26
  66. data/lib/ruby_llm/active_record/acts_as_legacy.rb +125 -29
  67. data/lib/ruby_llm/active_record/chat_methods.rb +50 -26
  68. data/lib/ruby_llm/active_record/message_methods.rb +104 -4
  69. data/lib/ruby_llm/active_record/model_methods.rb +8 -10
  70. data/lib/ruby_llm/active_record/payload_helpers.rb +29 -0
  71. data/lib/ruby_llm/active_record/tool_call_methods.rb +18 -0
  72. data/lib/ruby_llm/agent.rb +15 -2
  73. data/lib/ruby_llm/aliases.json +141 -89
  74. data/lib/ruby_llm/aliases.rb +3 -0
  75. data/lib/ruby_llm/attachment.rb +44 -40
  76. data/lib/ruby_llm/chat.rb +229 -59
  77. data/lib/ruby_llm/configuration.rb +69 -75
  78. data/lib/ruby_llm/connection.rb +37 -10
  79. data/lib/ruby_llm/content.rb +15 -1
  80. data/lib/ruby_llm/cost.rb +224 -0
  81. data/lib/ruby_llm/deprecator.rb +24 -0
  82. data/lib/ruby_llm/embedding.rb +31 -1
  83. data/lib/ruby_llm/error.rb +16 -75
  84. data/lib/ruby_llm/error_middleware.rb +81 -0
  85. data/lib/ruby_llm/image.rb +39 -4
  86. data/lib/ruby_llm/instrumentation.rb +36 -0
  87. data/lib/ruby_llm/message.rb +20 -0
  88. data/lib/ruby_llm/mime_type.rb +25 -0
  89. data/lib/ruby_llm/model/info.rb +67 -14
  90. data/lib/ruby_llm/model/pricing.rb +19 -9
  91. data/lib/ruby_llm/model/pricing_category.rb +13 -2
  92. data/lib/ruby_llm/model/pricing_tier.rb +20 -9
  93. data/lib/ruby_llm/model_registry.rb +39 -0
  94. data/lib/ruby_llm/models.json +21796 -20601
  95. data/lib/ruby_llm/models.rb +107 -34
  96. data/lib/ruby_llm/models_schema.json +3 -0
  97. data/lib/ruby_llm/provider.rb +25 -4
  98. data/lib/ruby_llm/providers/anthropic/capabilities.rb +1 -133
  99. data/lib/ruby_llm/providers/anthropic/chat.rb +49 -15
  100. data/lib/ruby_llm/providers/anthropic/models.rb +4 -8
  101. data/lib/ruby_llm/providers/anthropic/streaming.rb +2 -0
  102. data/lib/ruby_llm/providers/anthropic/tools.rb +32 -3
  103. data/lib/ruby_llm/providers/anthropic.rb +4 -0
  104. data/lib/ruby_llm/providers/azure/media.rb +1 -1
  105. data/lib/ruby_llm/providers/azure.rb +4 -0
  106. data/lib/ruby_llm/providers/bedrock/auth.rb +1 -0
  107. data/lib/ruby_llm/providers/bedrock/chat.rb +26 -13
  108. data/lib/ruby_llm/providers/bedrock/media.rb +21 -3
  109. data/lib/ruby_llm/providers/bedrock/models.rb +1 -1
  110. data/lib/ruby_llm/providers/bedrock/streaming.rb +10 -1
  111. data/lib/ruby_llm/providers/bedrock.rb +5 -1
  112. data/lib/ruby_llm/providers/deepseek/capabilities.rb +28 -103
  113. data/lib/ruby_llm/providers/deepseek/chat.rb +9 -0
  114. data/lib/ruby_llm/providers/deepseek.rb +4 -0
  115. data/lib/ruby_llm/providers/gemini/capabilities.rb +45 -215
  116. data/lib/ruby_llm/providers/gemini/chat.rb +10 -4
  117. data/lib/ruby_llm/providers/gemini/images.rb +2 -2
  118. data/lib/ruby_llm/providers/gemini/media.rb +16 -9
  119. data/lib/ruby_llm/providers/gemini/models.rb +2 -4
  120. data/lib/ruby_llm/providers/gemini/streaming.rb +6 -1
  121. data/lib/ruby_llm/providers/gemini/tools.rb +5 -1
  122. data/lib/ruby_llm/providers/gemini.rb +4 -0
  123. data/lib/ruby_llm/providers/gpustack/chat.rb +8 -1
  124. data/lib/ruby_llm/providers/gpustack/models.rb +2 -0
  125. data/lib/ruby_llm/providers/gpustack.rb +4 -0
  126. data/lib/ruby_llm/providers/mistral/capabilities.rb +7 -2
  127. data/lib/ruby_llm/providers/mistral/chat.rb +56 -5
  128. data/lib/ruby_llm/providers/mistral/media.rb +55 -0
  129. data/lib/ruby_llm/providers/mistral/models.rb +2 -0
  130. data/lib/ruby_llm/providers/mistral.rb +5 -1
  131. data/lib/ruby_llm/providers/ollama/chat.rb +8 -1
  132. data/lib/ruby_llm/providers/ollama.rb +4 -0
  133. data/lib/ruby_llm/providers/openai/capabilities.rb +157 -195
  134. data/lib/ruby_llm/providers/openai/chat.rb +61 -7
  135. data/lib/ruby_llm/providers/openai/images.rb +58 -6
  136. data/lib/ruby_llm/providers/openai/media.rb +40 -16
  137. data/lib/ruby_llm/providers/openai/models.rb +2 -4
  138. data/lib/ruby_llm/providers/openai/streaming.rb +7 -6
  139. data/lib/ruby_llm/providers/openai/tools.rb +2 -0
  140. data/lib/ruby_llm/providers/openai/transcription.rb +1 -0
  141. data/lib/ruby_llm/providers/openai.rb +10 -0
  142. data/lib/ruby_llm/providers/openrouter/chat.rb +36 -8
  143. data/lib/ruby_llm/providers/openrouter/images.rb +3 -3
  144. data/lib/ruby_llm/providers/openrouter/models.rb +1 -1
  145. data/lib/ruby_llm/providers/openrouter/streaming.rb +5 -6
  146. data/lib/ruby_llm/providers/openrouter.rb +4 -0
  147. data/lib/ruby_llm/providers/perplexity/capabilities.rb +34 -99
  148. data/lib/ruby_llm/providers/perplexity/chat.rb +11 -0
  149. data/lib/ruby_llm/providers/perplexity/media.rb +62 -0
  150. data/lib/ruby_llm/providers/perplexity/models.rb +12 -14
  151. data/lib/ruby_llm/providers/perplexity.rb +5 -1
  152. data/lib/ruby_llm/providers/vertexai.rb +8 -0
  153. data/lib/ruby_llm/providers/xai/chat.rb +9 -0
  154. data/lib/ruby_llm/providers/xai/models.rb +15 -27
  155. data/lib/ruby_llm/providers/xai.rb +5 -1
  156. data/lib/ruby_llm/railtie.rb +11 -1
  157. data/lib/ruby_llm/stream_accumulator.rb +45 -30
  158. data/lib/ruby_llm/streaming.rb +4 -0
  159. data/lib/ruby_llm/tokens.rb +8 -0
  160. data/lib/ruby_llm/tool.rb +24 -7
  161. data/lib/ruby_llm/tool_concurrency.rb +105 -0
  162. data/lib/ruby_llm/transcription.rb +2 -1
  163. data/lib/ruby_llm/utils.rb +39 -0
  164. data/lib/ruby_llm/version.rb +1 -1
  165. data/lib/ruby_llm.rb +11 -6
  166. data/lib/tasks/models.rake +45 -16
  167. data/lib/tasks/release.rake +51 -24
  168. data/lib/tasks/ruby_llm.rake +6 -5
  169. data/lib/tasks/vcr.rake +1 -1
  170. metadata +81 -21
  171. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_message.html.erb.tt +0 -13
@@ -8,17 +8,10 @@
8
8
  "anthropic": "claude-3-5-haiku-latest"
9
9
  },
10
10
  "claude-3-5-sonnet": {
11
- "anthropic": "claude-3-5-sonnet-20241022",
12
- "openrouter": "anthropic/claude-3.5-sonnet",
13
- "bedrock": "anthropic.claude-3-5-sonnet-20240620-v1:0:200k"
11
+ "anthropic": "claude-3-5-sonnet-20241022"
14
12
  },
15
13
  "claude-3-7-sonnet": {
16
- "anthropic": "claude-3-7-sonnet-20250219",
17
- "openrouter": "anthropic/claude-3.7-sonnet",
18
- "bedrock": "anthropic.claude-3-7-sonnet-20250219-v1:0"
19
- },
20
- "claude-3-7-sonnet-latest": {
21
- "anthropic": "claude-3-7-sonnet-latest"
14
+ "anthropic": "claude-3-7-sonnet-20250219"
22
15
  },
23
16
  "claude-3-haiku": {
24
17
  "anthropic": "claude-3-haiku-20240307",
@@ -26,8 +19,7 @@
26
19
  "bedrock": "anthropic.claude-3-haiku-20240307-v1:0:200k"
27
20
  },
28
21
  "claude-3-opus": {
29
- "anthropic": "claude-3-opus-20240229",
30
- "bedrock": "anthropic.claude-3-opus-20240229-v1:0"
22
+ "anthropic": "claude-3-opus-20240229"
31
23
  },
32
24
  "claude-3-sonnet": {
33
25
  "anthropic": "claude-3-sonnet-20240229",
@@ -41,8 +33,7 @@
41
33
  },
42
34
  "claude-opus-4": {
43
35
  "anthropic": "claude-opus-4-20250514",
44
- "openrouter": "anthropic/claude-opus-4",
45
- "bedrock": "anthropic.claude-opus-4-1-20250805-v1:0"
36
+ "openrouter": "anthropic/claude-opus-4"
46
37
  },
47
38
  "claude-opus-4-0": {
48
39
  "anthropic": "claude-opus-4-0"
@@ -62,12 +53,24 @@
62
53
  "claude-opus-4-6": {
63
54
  "anthropic": "claude-opus-4-6",
64
55
  "openrouter": "anthropic/claude-opus-4.6",
65
- "bedrock": "anthropic.claude-opus-4-6-v1"
56
+ "bedrock": "anthropic.claude-opus-4-6-v1",
57
+ "azure": "claude-opus-4-6"
58
+ },
59
+ "claude-opus-4-7": {
60
+ "anthropic": "claude-opus-4-7",
61
+ "openrouter": "anthropic/claude-opus-4.7",
62
+ "bedrock": "anthropic.claude-opus-4-7",
63
+ "azure": "claude-opus-4-7"
64
+ },
65
+ "claude-opus-4-8": {
66
+ "anthropic": "claude-opus-4-8",
67
+ "openrouter": "anthropic/claude-opus-4.8",
68
+ "bedrock": "anthropic.claude-opus-4-8"
66
69
  },
67
70
  "claude-sonnet-4": {
68
71
  "anthropic": "claude-sonnet-4-20250514",
69
72
  "openrouter": "anthropic/claude-sonnet-4",
70
- "bedrock": "anthropic.claude-sonnet-4-20250514-v1:0"
73
+ "bedrock": "us.anthropic.claude-sonnet-4-20250514-v1:0"
71
74
  },
72
75
  "claude-sonnet-4-0": {
73
76
  "anthropic": "claude-sonnet-4-0"
@@ -81,23 +84,20 @@
81
84
  "claude-sonnet-4-6": {
82
85
  "anthropic": "claude-sonnet-4-6",
83
86
  "openrouter": "anthropic/claude-sonnet-4.6",
84
- "bedrock": "anthropic.claude-sonnet-4-6"
87
+ "bedrock": "anthropic.claude-sonnet-4-6",
88
+ "azure": "claude-sonnet-4-6"
85
89
  },
86
90
  "deepseek-chat": {
87
91
  "deepseek": "deepseek-chat",
88
92
  "openrouter": "deepseek/deepseek-chat"
89
93
  },
90
- "gemini-1.5-flash": {
91
- "gemini": "gemini-1.5-flash",
92
- "vertexai": "gemini-1.5-flash"
94
+ "deepseek-v4-flash": {
95
+ "deepseek": "deepseek-v4-flash",
96
+ "openrouter": "deepseek/deepseek-v4-flash"
93
97
  },
94
- "gemini-1.5-flash-8b": {
95
- "gemini": "gemini-1.5-flash-8b",
96
- "vertexai": "gemini-1.5-flash-8b"
97
- },
98
- "gemini-1.5-pro": {
99
- "gemini": "gemini-1.5-pro",
100
- "vertexai": "gemini-1.5-pro"
98
+ "deepseek-v4-pro": {
99
+ "deepseek": "deepseek-v4-pro",
100
+ "openrouter": "deepseek/deepseek-v4-pro"
101
101
  },
102
102
  "gemini-2.0-flash": {
103
103
  "gemini": "gemini-2.0-flash",
@@ -105,7 +105,6 @@
105
105
  },
106
106
  "gemini-2.0-flash-001": {
107
107
  "gemini": "gemini-2.0-flash-001",
108
- "openrouter": "google/gemini-2.0-flash-001",
109
108
  "vertexai": "gemini-2.0-flash-001"
110
109
  },
111
110
  "gemini-2.0-flash-lite": {
@@ -114,7 +113,6 @@
114
113
  },
115
114
  "gemini-2.0-flash-lite-001": {
116
115
  "gemini": "gemini-2.0-flash-lite-001",
117
- "openrouter": "google/gemini-2.0-flash-lite-001",
118
116
  "vertexai": "gemini-2.0-flash-lite-001"
119
117
  },
120
118
  "gemini-2.5-flash": {
@@ -131,65 +129,46 @@
131
129
  "openrouter": "google/gemini-2.5-flash-lite",
132
130
  "vertexai": "gemini-2.5-flash-lite"
133
131
  },
134
- "gemini-2.5-flash-lite-preview-06-17": {
135
- "gemini": "gemini-2.5-flash-lite-preview-06-17",
136
- "vertexai": "gemini-2.5-flash-lite-preview-06-17"
137
- },
138
- "gemini-2.5-flash-lite-preview-09-2025": {
139
- "gemini": "gemini-2.5-flash-lite-preview-09-2025",
140
- "openrouter": "google/gemini-2.5-flash-lite-preview-09-2025",
141
- "vertexai": "gemini-2.5-flash-lite-preview-09-2025"
142
- },
143
- "gemini-2.5-flash-preview-04-17": {
144
- "gemini": "gemini-2.5-flash-preview-04-17",
145
- "vertexai": "gemini-2.5-flash-preview-04-17"
146
- },
147
- "gemini-2.5-flash-preview-05-20": {
148
- "gemini": "gemini-2.5-flash-preview-05-20",
149
- "vertexai": "gemini-2.5-flash-preview-05-20"
150
- },
151
- "gemini-2.5-flash-preview-09-2025": {
152
- "gemini": "gemini-2.5-flash-preview-09-2025",
153
- "openrouter": "google/gemini-2.5-flash-preview-09-2025",
154
- "vertexai": "gemini-2.5-flash-preview-09-2025"
155
- },
156
132
  "gemini-2.5-pro": {
157
133
  "gemini": "gemini-2.5-pro",
158
134
  "openrouter": "google/gemini-2.5-pro",
159
135
  "vertexai": "gemini-2.5-pro"
160
136
  },
161
- "gemini-2.5-pro-preview-05-06": {
162
- "gemini": "gemini-2.5-pro-preview-05-06",
163
- "openrouter": "google/gemini-2.5-pro-preview-05-06",
164
- "vertexai": "gemini-2.5-pro-preview-05-06"
165
- },
166
- "gemini-2.5-pro-preview-06-05": {
167
- "gemini": "gemini-2.5-pro-preview-06-05",
168
- "openrouter": "google/gemini-2.5-pro-preview-06-05",
169
- "vertexai": "gemini-2.5-pro-preview-06-05"
170
- },
171
137
  "gemini-3-flash-preview": {
172
138
  "gemini": "gemini-3-flash-preview",
173
139
  "openrouter": "google/gemini-3-flash-preview",
174
140
  "vertexai": "gemini-3-flash-preview"
175
141
  },
142
+ "gemini-3-pro-image": {
143
+ "gemini": "gemini-3-pro-image",
144
+ "vertexai": "gemini-3-pro-image"
145
+ },
176
146
  "gemini-3-pro-image-preview": {
177
147
  "gemini": "gemini-3-pro-image-preview",
178
148
  "openrouter": "google/gemini-3-pro-image-preview"
179
149
  },
180
150
  "gemini-3-pro-preview": {
181
151
  "gemini": "gemini-3-pro-preview",
182
- "openrouter": "google/gemini-3-pro-preview",
183
152
  "vertexai": "gemini-3-pro-preview"
184
153
  },
154
+ "gemini-3.1-flash-image": {
155
+ "gemini": "gemini-3.1-flash-image",
156
+ "vertexai": "gemini-3.1-flash-image"
157
+ },
185
158
  "gemini-3.1-flash-image-preview": {
186
159
  "gemini": "gemini-3.1-flash-image-preview",
187
160
  "openrouter": "google/gemini-3.1-flash-image-preview",
188
161
  "vertexai": "gemini-3.1-flash-image-preview"
189
162
  },
163
+ "gemini-3.1-flash-lite": {
164
+ "gemini": "gemini-3.1-flash-lite",
165
+ "openrouter": "google/gemini-3.1-flash-lite",
166
+ "vertexai": "gemini-3.1-flash-lite"
167
+ },
190
168
  "gemini-3.1-flash-lite-preview": {
191
169
  "gemini": "gemini-3.1-flash-lite-preview",
192
- "openrouter": "google/gemini-3.1-flash-lite-preview"
170
+ "openrouter": "google/gemini-3.1-flash-lite-preview",
171
+ "vertexai": "gemini-3.1-flash-lite-preview"
193
172
  },
194
173
  "gemini-3.1-pro-preview": {
195
174
  "gemini": "gemini-3.1-pro-preview",
@@ -201,10 +180,19 @@
201
180
  "openrouter": "google/gemini-3.1-pro-preview-customtools",
202
181
  "vertexai": "gemini-3.1-pro-preview-customtools"
203
182
  },
183
+ "gemini-3.5-flash": {
184
+ "gemini": "gemini-3.5-flash",
185
+ "openrouter": "google/gemini-3.5-flash",
186
+ "vertexai": "gemini-3.5-flash"
187
+ },
204
188
  "gemini-embedding-001": {
205
189
  "gemini": "gemini-embedding-001",
206
190
  "vertexai": "gemini-embedding-001"
207
191
  },
192
+ "gemini-embedding-2": {
193
+ "gemini": "gemini-embedding-2",
194
+ "vertexai": "gemini-embedding-2"
195
+ },
208
196
  "gemini-flash": {
209
197
  "gemini": "gemini-flash-latest",
210
198
  "vertexai": "gemini-flash-latest"
@@ -213,21 +201,13 @@
213
201
  "gemini": "gemini-flash-lite-latest",
214
202
  "vertexai": "gemini-flash-lite-latest"
215
203
  },
216
- "gemma-3-12b-it": {
217
- "gemini": "gemma-3-12b-it",
218
- "openrouter": "google/gemma-3-12b-it"
219
- },
220
- "gemma-3-27b-it": {
221
- "gemini": "gemma-3-27b-it",
222
- "openrouter": "google/gemma-3-27b-it"
223
- },
224
- "gemma-3-4b-it": {
225
- "gemini": "gemma-3-4b-it",
226
- "openrouter": "google/gemma-3-4b-it"
204
+ "gemma-4-26b-a4b-it": {
205
+ "gemini": "gemma-4-26b-a4b-it",
206
+ "openrouter": "google/gemma-4-26b-a4b-it"
227
207
  },
228
- "gemma-3n-e4b-it": {
229
- "gemini": "gemma-3n-e4b-it",
230
- "openrouter": "google/gemma-3n-e4b-it"
208
+ "gemma-4-31b-it": {
209
+ "gemini": "gemma-4-31b-it",
210
+ "openrouter": "google/gemma-4-31b-it"
231
211
  },
232
212
  "gpt-3.5-turbo": {
233
213
  "openai": "gpt-3.5-turbo",
@@ -246,18 +226,10 @@
246
226
  "openrouter": "openai/gpt-4",
247
227
  "azure": "gpt-4"
248
228
  },
249
- "gpt-4-1106-preview": {
250
- "openai": "gpt-4-1106-preview",
251
- "openrouter": "openai/gpt-4-1106-preview"
252
- },
253
229
  "gpt-4-turbo": {
254
230
  "openai": "gpt-4-turbo",
255
231
  "openrouter": "openai/gpt-4-turbo"
256
232
  },
257
- "gpt-4-turbo-preview": {
258
- "openai": "gpt-4-turbo-preview",
259
- "openrouter": "openai/gpt-4-turbo-preview"
260
- },
261
233
  "gpt-4.1": {
262
234
  "openai": "gpt-4.1",
263
235
  "openrouter": "openai/gpt-4.1",
@@ -293,10 +265,6 @@
293
265
  "openrouter": "openai/gpt-4o-2024-11-20",
294
266
  "azure": "gpt-4o-2024-11-20"
295
267
  },
296
- "gpt-4o-audio-preview": {
297
- "openai": "gpt-4o-audio-preview",
298
- "openrouter": "openai/gpt-4o-audio-preview"
299
- },
300
268
  "gpt-4o-mini": {
301
269
  "openai": "gpt-4o-mini",
302
270
  "openrouter": "openai/gpt-4o-mini",
@@ -337,7 +305,8 @@
337
305
  },
338
306
  "gpt-5.1": {
339
307
  "openai": "gpt-5.1",
340
- "openrouter": "openai/gpt-5.1"
308
+ "openrouter": "openai/gpt-5.1",
309
+ "azure": "gpt-5.1"
341
310
  },
342
311
  "gpt-5.1-codex": {
343
312
  "openai": "gpt-5.1-codex",
@@ -367,6 +336,30 @@
367
336
  "openai": "gpt-5.3-codex",
368
337
  "openrouter": "openai/gpt-5.3-codex"
369
338
  },
339
+ "gpt-5.4": {
340
+ "openai": "gpt-5.4",
341
+ "openrouter": "openai/gpt-5.4"
342
+ },
343
+ "gpt-5.4-mini": {
344
+ "openai": "gpt-5.4-mini",
345
+ "openrouter": "openai/gpt-5.4-mini"
346
+ },
347
+ "gpt-5.4-nano": {
348
+ "openai": "gpt-5.4-nano",
349
+ "openrouter": "openai/gpt-5.4-nano"
350
+ },
351
+ "gpt-5.4-pro": {
352
+ "openai": "gpt-5.4-pro",
353
+ "openrouter": "openai/gpt-5.4-pro"
354
+ },
355
+ "gpt-5.5": {
356
+ "openai": "gpt-5.5",
357
+ "openrouter": "openai/gpt-5.5"
358
+ },
359
+ "gpt-5.5-pro": {
360
+ "openai": "gpt-5.5-pro",
361
+ "openrouter": "openai/gpt-5.5-pro"
362
+ },
370
363
  "gpt-audio": {
371
364
  "openai": "gpt-audio",
372
365
  "openrouter": "openai/gpt-audio"
@@ -375,6 +368,65 @@
375
368
  "openai": "gpt-audio-mini",
376
369
  "openrouter": "openai/gpt-audio-mini"
377
370
  },
371
+ "grok-3": {
372
+ "xai": "grok-4.3"
373
+ },
374
+ "grok-3-latest": {
375
+ "xai": "grok-4.3"
376
+ },
377
+ "grok-3-mini": {
378
+ "xai": "grok-4.3"
379
+ },
380
+ "grok-3-mini-latest": {
381
+ "xai": "grok-4.3"
382
+ },
383
+ "grok-4": {
384
+ "xai": "grok-4.3"
385
+ },
386
+ "grok-4-1-fast": {
387
+ "xai": "grok-4.3"
388
+ },
389
+ "grok-4-1-fast-non-reasoning": {
390
+ "xai": "grok-4.3"
391
+ },
392
+ "grok-4-1-fast-non-reasoning-latest": {
393
+ "xai": "grok-4.3"
394
+ },
395
+ "grok-4-1-fast-reasoning": {
396
+ "xai": "grok-4.3"
397
+ },
398
+ "grok-4-1-fast-reasoning-latest": {
399
+ "xai": "grok-4.3"
400
+ },
401
+ "grok-4-fast": {
402
+ "xai": "grok-4.3"
403
+ },
404
+ "grok-4-fast-non-reasoning": {
405
+ "xai": "grok-4.3"
406
+ },
407
+ "grok-4-fast-non-reasoning-latest": {
408
+ "xai": "grok-4.3"
409
+ },
410
+ "grok-4-fast-reasoning": {
411
+ "xai": "grok-4.3"
412
+ },
413
+ "grok-4-fast-reasoning-latest": {
414
+ "xai": "grok-4.3"
415
+ },
416
+ "grok-4-latest": {
417
+ "xai": "grok-4.3"
418
+ },
419
+ "grok-latest": {
420
+ "xai": "grok-4.3"
421
+ },
422
+ "lyria-3-clip-preview": {
423
+ "gemini": "lyria-3-clip-preview",
424
+ "openrouter": "google/lyria-3-clip-preview"
425
+ },
426
+ "lyria-3-pro-preview": {
427
+ "gemini": "lyria-3-pro-preview",
428
+ "openrouter": "google/lyria-3-pro-preview"
429
+ },
378
430
  "o1": {
379
431
  "openai": "o1",
380
432
  "openrouter": "openai/o1"
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
4
+
3
5
  module RubyLLM
4
6
  # Manages model aliases for provider-specific versions
5
7
  class Aliases
@@ -30,6 +32,7 @@ module RubyLLM
30
32
  end
31
33
  end
32
34
 
35
+ # Replaces the cached alias map from aliases.json.
33
36
  def reload!
34
37
  @aliases = load_aliases
35
38
  end
@@ -1,10 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'base64'
4
+ require 'pathname'
5
+ require 'uri'
6
+
3
7
  module RubyLLM
4
8
  # A class representing a file attachment.
5
9
  class Attachment
6
10
  attr_reader :source, :filename, :mime_type
7
11
 
12
+ DOCUMENT_EXTENSIONS = %w[
13
+ doc docx dot key numbers odp ods odt pages pot pps ppt pptx rtf xls xlsx
14
+ ].freeze
15
+ ACTIVE_STORAGE_CLASS_NAMES = %w[
16
+ ActiveStorage::Blob
17
+ ActiveStorage::Attachment
18
+ ActiveStorage::Attached::One
19
+ ActiveStorage::Attached::Many
20
+ ].freeze
21
+
8
22
  def initialize(source, filename: nil)
9
23
  @source = source
10
24
  @source = source_type_cast
@@ -26,11 +40,7 @@ module RubyLLM
26
40
  end
27
41
 
28
42
  def active_storage?
29
- return false unless defined?(ActiveStorage)
30
-
31
- @source.is_a?(ActiveStorage::Blob) ||
32
- @source.is_a?(ActiveStorage::Attached::One) ||
33
- @source.is_a?(ActiveStorage::Attached::Many)
43
+ ACTIVE_STORAGE_CLASS_NAMES.any? { |class_name| source_is_a?(class_name) }
34
44
  end
35
45
 
36
46
  def content
@@ -79,6 +89,7 @@ module RubyLLM
79
89
  return :audio if audio?
80
90
  return :pdf if pdf?
81
91
  return :text if text?
92
+ return :document if document?
82
93
 
83
94
  :unknown
84
95
  end
@@ -110,6 +121,17 @@ module RubyLLM
110
121
  RubyLLM::MimeType.pdf? mime_type
111
122
  end
112
123
 
124
+ def document?
125
+ return false if pdf? || text?
126
+
127
+ RubyLLM::MimeType.document?(mime_type) || DOCUMENT_EXTENSIONS.include?(extension)
128
+ end
129
+
130
+ def extension
131
+ extension = File.extname(filename.to_s).delete_prefix('.').downcase
132
+ extension.empty? ? nil : extension
133
+ end
134
+
113
135
  def text?
114
136
  RubyLLM::MimeType.text? mime_type
115
137
  end
@@ -143,18 +165,7 @@ module RubyLLM
143
165
  end
144
166
 
145
167
  def load_content_from_active_storage
146
- return unless defined?(ActiveStorage)
147
-
148
- @content = case @source
149
- when ActiveStorage::Blob
150
- @source.download
151
- when ActiveStorage::Attached::One
152
- @source.blob&.download
153
- when ActiveStorage::Attached::Many
154
- # For multiple attachments, just take the first one
155
- # This maintains the single-attachment interface
156
- @source.blobs.first&.download
157
- end
168
+ @content = active_storage_blob&.download
158
169
  end
159
170
 
160
171
  def source_type_cast
@@ -189,32 +200,25 @@ module RubyLLM
189
200
  end
190
201
  end
191
202
 
192
- def extract_filename_from_active_storage # rubocop:disable Metrics/PerceivedComplexity
193
- return 'attachment' unless defined?(ActiveStorage)
194
-
195
- case @source
196
- when ActiveStorage::Blob
197
- @source.filename.to_s
198
- when ActiveStorage::Attached::One
199
- @source.blob&.filename&.to_s || 'attachment'
200
- when ActiveStorage::Attached::Many
201
- @source.blobs.first&.filename&.to_s || 'attachment'
202
- else
203
- 'attachment'
204
- end
203
+ def extract_filename_from_active_storage
204
+ active_storage_blob&.filename&.to_s || 'attachment'
205
205
  end
206
206
 
207
207
  def active_storage_content_type
208
- return unless defined?(ActiveStorage)
209
-
210
- case @source
211
- when ActiveStorage::Blob
212
- @source.content_type
213
- when ActiveStorage::Attached::One
214
- @source.blob&.content_type
215
- when ActiveStorage::Attached::Many
216
- @source.blobs.first&.content_type
217
- end
208
+ active_storage_blob&.content_type
209
+ end
210
+
211
+ def active_storage_blob
212
+ return @source if source_is_a?('ActiveStorage::Blob')
213
+ return @source.blob if source_is_a?('ActiveStorage::Attachment')
214
+ return @source.blob if source_is_a?('ActiveStorage::Attached::One')
215
+
216
+ @source.blobs.first if source_is_a?('ActiveStorage::Attached::Many')
217
+ end
218
+
219
+ def source_is_a?(class_name)
220
+ klass = RubyLLM::Utils.safe_constantize(class_name)
221
+ klass ? @source.is_a?(klass) : false
218
222
  end
219
223
  end
220
224
  end