ruby_llm 1.11.0 → 1.12.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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +12 -0
  3. data/lib/ruby_llm/active_record/acts_as_legacy.rb +41 -7
  4. data/lib/ruby_llm/active_record/chat_methods.rb +41 -7
  5. data/lib/ruby_llm/agent.rb +323 -0
  6. data/lib/ruby_llm/aliases.json +47 -29
  7. data/lib/ruby_llm/chat.rb +27 -3
  8. data/lib/ruby_llm/configuration.rb +3 -0
  9. data/lib/ruby_llm/models.json +19090 -5190
  10. data/lib/ruby_llm/models.rb +35 -6
  11. data/lib/ruby_llm/provider.rb +8 -0
  12. data/lib/ruby_llm/providers/azure/chat.rb +29 -0
  13. data/lib/ruby_llm/providers/azure/embeddings.rb +24 -0
  14. data/lib/ruby_llm/providers/azure/media.rb +45 -0
  15. data/lib/ruby_llm/providers/azure/models.rb +14 -0
  16. data/lib/ruby_llm/providers/azure.rb +56 -0
  17. data/lib/ruby_llm/providers/bedrock/auth.rb +122 -0
  18. data/lib/ruby_llm/providers/bedrock/chat.rb +296 -64
  19. data/lib/ruby_llm/providers/bedrock/media.rb +62 -33
  20. data/lib/ruby_llm/providers/bedrock/models.rb +88 -65
  21. data/lib/ruby_llm/providers/bedrock/streaming.rb +305 -8
  22. data/lib/ruby_llm/providers/bedrock.rb +61 -52
  23. data/lib/ruby_llm/version.rb +1 -1
  24. data/lib/ruby_llm.rb +4 -0
  25. data/lib/tasks/models.rake +10 -5
  26. data/lib/tasks/vcr.rake +32 -0
  27. metadata +13 -13
  28. data/lib/ruby_llm/providers/bedrock/capabilities.rb +0 -167
  29. data/lib/ruby_llm/providers/bedrock/signing.rb +0 -831
  30. data/lib/ruby_llm/providers/bedrock/streaming/base.rb +0 -51
  31. data/lib/ruby_llm/providers/bedrock/streaming/content_extraction.rb +0 -128
  32. data/lib/ruby_llm/providers/bedrock/streaming/message_processing.rb +0 -67
  33. data/lib/ruby_llm/providers/bedrock/streaming/payload_processing.rb +0 -85
  34. data/lib/ruby_llm/providers/bedrock/streaming/prelude_handling.rb +0 -78
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c6cabf287dc7cd62616a9061131291f404c391be59f1468ccdb3b7f7cb9d2cb5
4
- data.tar.gz: bf1123d01a7ddadcbfde46a2ffb6e378928bcbcc0b1cdb0b927b7905f0534c12
3
+ metadata.gz: e8ff2f4da0c39e4909925217affa2b76908207e2c936a6dc05b56cffb2781863
4
+ data.tar.gz: 0eebb76434b049d4332a247e7581bb721d4dd86b0496be8655d15a4adcb42f65
5
5
  SHA512:
6
- metadata.gz: 12c1f267367279716c8e5e7fe6ef44327e4921b4e24516313f99954775a4c51d7e874d42184f466f8eb71ebe4f814b14a93b63b043c46096e7be4d804a15fc76
7
- data.tar.gz: dd2d797e49e3c1741108b63f191ccc3252d1fec1868851802ceb33b65afec4609498cbdb8ee516800e48f04220fdd4179b43abb645177e8ed14ea25c1036916d
6
+ metadata.gz: 95a3eb5a1c6a50c69dd8166044d99d77848fc8dcdd5d31b748e8303a3e8c5756cc86a8ce0220dd3c643bc0b32080f6c2125c79f5e437bb654725a119b4b4b601
7
+ data.tar.gz: 5efbd317193ca7f5e28df0a2d9bcc7ffd8a1740f40d29e7b872bed8a2ee910db376dc2dc890723b0c253718404993e55deab87356d414afdfd741d9392bd5e0f
data/README.md CHANGED
@@ -95,6 +95,17 @@ end
95
95
  chat.with_tool(Weather).ask "What's the weather in Berlin?"
96
96
  ```
97
97
 
98
+ ```ruby
99
+ # Define an agent with instructions + tools
100
+ class WeatherAssistant < RubyLLM::Agent
101
+ model "gpt-4.1-nano"
102
+ instructions "Be concise and always use tools for weather."
103
+ tools Weather
104
+ end
105
+
106
+ WeatherAssistant.new.ask "What's the weather in Berlin?"
107
+ ```
108
+
98
109
  ```ruby
99
110
  # Get structured output
100
111
  class ProductSchema < RubyLLM::Schema
@@ -118,6 +129,7 @@ response = chat.with_schema(ProductSchema).ask "Analyze this product", with: "pr
118
129
  * **Embeddings:** Generate embeddings with `RubyLLM.embed`
119
130
  * **Moderation:** Content safety with `RubyLLM.moderate`
120
131
  * **Tools:** Let AI call your Ruby methods
132
+ * **Agents:** Reusable assistants with `RubyLLM::Agent`
121
133
  * **Structured output:** JSON schemas that just work
122
134
  * **Streaming:** Real-time responses with blocks
123
135
  * **Rails:** ActiveRecord integration with `acts_as_chat`
@@ -95,19 +95,19 @@ module RubyLLM
95
95
  end
96
96
  @chat.reset_messages!
97
97
 
98
- messages.each do |msg|
98
+ ordered_messages = order_messages_for_llm(messages.to_a)
99
+ ordered_messages.each do |msg|
99
100
  @chat.add_message(msg.to_llm)
100
101
  end
101
102
 
102
103
  setup_persistence_callbacks
103
104
  end
104
105
 
105
- def with_instructions(instructions, replace: false)
106
- transaction do
107
- messages.where(role: :system).destroy_all if replace
108
- messages.create!(role: :system, content: instructions)
109
- end
110
- to_llm.with_instructions(instructions)
106
+ def with_instructions(instructions, append: false, replace: nil)
107
+ append = append_instructions?(append:, replace:)
108
+ persist_system_instruction(instructions, append:)
109
+
110
+ to_llm.with_instructions(instructions, append:, replace:)
111
111
  self
112
112
  end
113
113
 
@@ -233,6 +233,40 @@ module RubyLLM
233
233
  end
234
234
  end
235
235
 
236
+ def replace_persisted_system_instructions(instructions)
237
+ system_messages = messages.where(role: :system).order(:id).to_a
238
+
239
+ if system_messages.empty?
240
+ messages.create!(role: :system, content: instructions)
241
+ return
242
+ end
243
+
244
+ primary_message = system_messages.shift
245
+ primary_message.update!(content: instructions) if primary_message.content != instructions
246
+ system_messages.each(&:destroy!)
247
+ end
248
+
249
+ def append_instructions?(append:, replace:)
250
+ return append if replace.nil?
251
+
252
+ append || (replace == false)
253
+ end
254
+
255
+ def persist_system_instruction(instructions, append:)
256
+ transaction do
257
+ if append
258
+ messages.create!(role: :system, content: instructions)
259
+ else
260
+ replace_persisted_system_instructions(instructions)
261
+ end
262
+ end
263
+ end
264
+
265
+ def order_messages_for_llm(messages)
266
+ system_messages, non_system_messages = messages.partition { |msg| msg.role.to_s == 'system' }
267
+ system_messages + non_system_messages
268
+ end
269
+
236
270
  def setup_persistence_callbacks
237
271
  return @chat if @chat.instance_variable_get(:@_persistence_callbacks_setup)
238
272
 
@@ -83,19 +83,19 @@ module RubyLLM
83
83
  )
84
84
  @chat.reset_messages!
85
85
 
86
- messages_association.each do |msg|
86
+ ordered_messages = order_messages_for_llm(messages_association.to_a)
87
+ ordered_messages.each do |msg|
87
88
  @chat.add_message(msg.to_llm)
88
89
  end
89
90
 
90
91
  setup_persistence_callbacks
91
92
  end
92
93
 
93
- def with_instructions(instructions, replace: false)
94
- transaction do
95
- messages_association.where(role: :system).destroy_all if replace
96
- messages_association.create!(role: :system, content: instructions)
97
- end
98
- to_llm.with_instructions(instructions)
94
+ def with_instructions(instructions, append: false, replace: nil)
95
+ append = append_instructions?(append:, replace:)
96
+ persist_system_instruction(instructions, append:)
97
+
98
+ to_llm.with_instructions(instructions, append:, replace:)
99
99
  self
100
100
  end
101
101
 
@@ -244,6 +244,40 @@ module RubyLLM
244
244
  @chat
245
245
  end
246
246
 
247
+ def replace_persisted_system_instructions(instructions)
248
+ system_messages = messages_association.where(role: :system).order(:id).to_a
249
+
250
+ if system_messages.empty?
251
+ messages_association.create!(role: :system, content: instructions)
252
+ return
253
+ end
254
+
255
+ primary_message = system_messages.shift
256
+ primary_message.update!(content: instructions) if primary_message.content != instructions
257
+ system_messages.each(&:destroy!)
258
+ end
259
+
260
+ def append_instructions?(append:, replace:)
261
+ return append if replace.nil?
262
+
263
+ append || (replace == false)
264
+ end
265
+
266
+ def persist_system_instruction(instructions, append:)
267
+ transaction do
268
+ if append
269
+ messages_association.create!(role: :system, content: instructions)
270
+ else
271
+ replace_persisted_system_instructions(instructions)
272
+ end
273
+ end
274
+ end
275
+
276
+ def order_messages_for_llm(messages)
277
+ system_messages, non_system_messages = messages.partition { |msg| msg.role.to_s == 'system' }
278
+ system_messages + non_system_messages
279
+ end
280
+
247
281
  def persist_new_message
248
282
  @message = messages_association.create!(role: :assistant, content: '')
249
283
  end
@@ -0,0 +1,323 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erb'
4
+ require 'pathname'
5
+
6
+ module RubyLLM
7
+ # Base class for simple, class-configured agents.
8
+ class Agent
9
+ include Enumerable
10
+
11
+ class << self
12
+ def inherited(subclass)
13
+ super
14
+ subclass.instance_variable_set(:@chat_kwargs, (@chat_kwargs || {}).dup)
15
+ subclass.instance_variable_set(:@tools, (@tools || []).dup)
16
+ subclass.instance_variable_set(:@instructions, @instructions)
17
+ subclass.instance_variable_set(:@temperature, @temperature)
18
+ subclass.instance_variable_set(:@thinking, @thinking)
19
+ subclass.instance_variable_set(:@params, (@params || {}).dup)
20
+ subclass.instance_variable_set(:@headers, (@headers || {}).dup)
21
+ subclass.instance_variable_set(:@schema, @schema)
22
+ subclass.instance_variable_set(:@context, @context)
23
+ subclass.instance_variable_set(:@chat_model, @chat_model)
24
+ subclass.instance_variable_set(:@input_names, (@input_names || []).dup)
25
+ end
26
+
27
+ def model(model_id = nil, **options)
28
+ options[:model] = model_id unless model_id.nil?
29
+ @chat_kwargs = options
30
+ end
31
+
32
+ def tools(*tools, &block)
33
+ return @tools || [] if tools.empty? && !block_given?
34
+
35
+ @tools = block_given? ? block : tools.flatten
36
+ end
37
+
38
+ def instructions(text = nil, **prompt_locals, &block)
39
+ if text.nil? && prompt_locals.empty? && !block_given?
40
+ @instructions ||= { prompt: 'instructions', locals: {} }
41
+ return @instructions
42
+ end
43
+
44
+ @instructions = block || text || { prompt: 'instructions', locals: prompt_locals }
45
+ end
46
+
47
+ def temperature(value = nil)
48
+ return @temperature if value.nil?
49
+
50
+ @temperature = value
51
+ end
52
+
53
+ def thinking(effort: nil, budget: nil)
54
+ return @thinking if effort.nil? && budget.nil?
55
+
56
+ @thinking = { effort: effort, budget: budget }
57
+ end
58
+
59
+ def params(**params, &block)
60
+ return @params || {} if params.empty? && !block_given?
61
+
62
+ @params = block_given? ? block : params
63
+ end
64
+
65
+ def headers(**headers, &block)
66
+ return @headers || {} if headers.empty? && !block_given?
67
+
68
+ @headers = block_given? ? block : headers
69
+ end
70
+
71
+ def schema(value = nil, &block)
72
+ return @schema if value.nil? && !block_given?
73
+
74
+ @schema = block_given? ? block : value
75
+ end
76
+
77
+ def context(value = nil)
78
+ return @context if value.nil?
79
+
80
+ @context = value
81
+ end
82
+
83
+ def chat_model(value = nil)
84
+ return @chat_model if value.nil?
85
+
86
+ @chat_model = value
87
+ remove_instance_variable(:@resolved_chat_model) if instance_variable_defined?(:@resolved_chat_model)
88
+ end
89
+
90
+ def inputs(*names)
91
+ return @input_names || [] if names.empty?
92
+
93
+ @input_names = names.flatten.map(&:to_sym)
94
+ end
95
+
96
+ def chat_kwargs
97
+ @chat_kwargs || {}
98
+ end
99
+
100
+ def chat(**kwargs)
101
+ input_values, chat_options = partition_inputs(kwargs)
102
+ chat = RubyLLM.chat(**chat_kwargs, **chat_options)
103
+ apply_configuration(chat, input_values:, persist_instructions: true)
104
+ chat
105
+ end
106
+
107
+ def create(**kwargs)
108
+ with_rails_chat_record(:create, **kwargs)
109
+ end
110
+
111
+ def create!(**kwargs)
112
+ with_rails_chat_record(:create!, **kwargs)
113
+ end
114
+
115
+ def find(id, **kwargs)
116
+ raise ArgumentError, 'chat_model must be configured to use find' unless resolved_chat_model
117
+
118
+ input_values, = partition_inputs(kwargs)
119
+ record = resolved_chat_model.find(id)
120
+ apply_configuration(record, input_values:, persist_instructions: false)
121
+ record
122
+ end
123
+
124
+ def sync_instructions!(chat_or_id, **kwargs)
125
+ raise ArgumentError, 'chat_model must be configured to use sync_instructions!' unless resolved_chat_model
126
+
127
+ input_values, = partition_inputs(kwargs)
128
+ record = chat_or_id.is_a?(resolved_chat_model) ? chat_or_id : resolved_chat_model.find(chat_or_id)
129
+ runtime = runtime_context(chat: record, inputs: input_values)
130
+ instructions_value = resolved_instructions_value(record, runtime, inputs: input_values)
131
+ return record if instructions_value.nil?
132
+
133
+ record.with_instructions(instructions_value)
134
+ record
135
+ end
136
+
137
+ def render_prompt(name, chat:, inputs:, locals:)
138
+ path = prompt_path_for(name)
139
+ return nil unless File.exist?(path)
140
+
141
+ resolved_locals = resolve_prompt_locals(locals, runtime: runtime_context(chat:, inputs:), chat:, inputs:)
142
+ ERB.new(File.read(path)).result_with_hash(resolved_locals)
143
+ end
144
+
145
+ private
146
+
147
+ def with_rails_chat_record(method_name, **kwargs)
148
+ raise ArgumentError, 'chat_model must be configured to use create/create!' unless resolved_chat_model
149
+
150
+ input_values, chat_options = partition_inputs(kwargs)
151
+ record = resolved_chat_model.public_send(method_name, **chat_kwargs, **chat_options)
152
+ apply_configuration(record, input_values:, persist_instructions: true) if record
153
+ record
154
+ end
155
+
156
+ def apply_configuration(chat_object, input_values:, persist_instructions:)
157
+ runtime = runtime_context(chat: chat_object, inputs: input_values)
158
+ llm_chat = llm_chat_for(chat_object)
159
+
160
+ apply_context(llm_chat)
161
+ apply_instructions(chat_object, runtime, inputs: input_values, persist: persist_instructions)
162
+ apply_tools(llm_chat, runtime)
163
+ apply_temperature(llm_chat)
164
+ apply_thinking(llm_chat)
165
+ apply_params(llm_chat, runtime)
166
+ apply_headers(llm_chat, runtime)
167
+ apply_schema(llm_chat, runtime)
168
+ end
169
+
170
+ def apply_context(llm_chat)
171
+ llm_chat.with_context(context) if context
172
+ end
173
+
174
+ def apply_instructions(chat_object, runtime, inputs:, persist:)
175
+ value = resolved_instructions_value(chat_object, runtime, inputs:)
176
+ return if value.nil?
177
+
178
+ instruction_target(chat_object, persist:).with_instructions(value)
179
+ end
180
+
181
+ def apply_tools(llm_chat, runtime)
182
+ tools_to_apply = Array(evaluate(tools, runtime))
183
+ llm_chat.with_tools(*tools_to_apply) unless tools_to_apply.empty?
184
+ end
185
+
186
+ def apply_temperature(llm_chat)
187
+ llm_chat.with_temperature(temperature) unless temperature.nil?
188
+ end
189
+
190
+ def apply_thinking(llm_chat)
191
+ llm_chat.with_thinking(**thinking) if thinking
192
+ end
193
+
194
+ def apply_params(llm_chat, runtime)
195
+ value = evaluate(params, runtime)
196
+ llm_chat.with_params(**value) if value && !value.empty?
197
+ end
198
+
199
+ def apply_headers(llm_chat, runtime)
200
+ value = evaluate(headers, runtime)
201
+ llm_chat.with_headers(**value) if value && !value.empty?
202
+ end
203
+
204
+ def apply_schema(llm_chat, runtime)
205
+ value = evaluate(schema, runtime)
206
+ llm_chat.with_schema(value) if value
207
+ end
208
+
209
+ def llm_chat_for(chat_object)
210
+ chat_object.respond_to?(:to_llm) ? chat_object.to_llm : chat_object
211
+ end
212
+
213
+ def evaluate(value, runtime)
214
+ value.is_a?(Proc) ? runtime.instance_exec(&value) : value
215
+ end
216
+
217
+ def resolved_instructions_value(chat_object, runtime, inputs:)
218
+ value = evaluate(instructions, runtime)
219
+ return value unless prompt_instruction?(value)
220
+
221
+ runtime.prompt(
222
+ value[:prompt],
223
+ **resolve_prompt_locals(value[:locals] || {}, runtime:, chat: chat_object, inputs:)
224
+ )
225
+ end
226
+
227
+ def prompt_instruction?(value)
228
+ value.is_a?(Hash) && value[:prompt]
229
+ end
230
+
231
+ def instruction_target(chat_object, persist:)
232
+ if persist || !chat_object.respond_to?(:to_llm)
233
+ chat_object
234
+ else
235
+ chat_object.to_llm
236
+ end
237
+ end
238
+
239
+ def resolve_prompt_locals(locals, runtime:, chat:, inputs:)
240
+ base = { chat: chat }.merge(inputs)
241
+ evaluated = locals.each_with_object({}) do |(key, value), acc|
242
+ acc[key.to_sym] = value.is_a?(Proc) ? runtime.instance_exec(&value) : value
243
+ end
244
+ base.merge(evaluated)
245
+ end
246
+
247
+ def partition_inputs(kwargs)
248
+ input_values = {}
249
+ chat_options = {}
250
+
251
+ kwargs.each do |key, value|
252
+ symbolized_key = key.to_sym
253
+ if inputs.include?(symbolized_key)
254
+ input_values[symbolized_key] = value
255
+ else
256
+ chat_options[symbolized_key] = value
257
+ end
258
+ end
259
+
260
+ [input_values, chat_options]
261
+ end
262
+
263
+ def runtime_context(chat:, inputs:)
264
+ agent_class = self
265
+ Object.new.tap do |runtime|
266
+ runtime.define_singleton_method(:chat) { chat }
267
+ runtime.define_singleton_method(:prompt) do |name, **locals|
268
+ agent_class.render_prompt(name, chat:, inputs:, locals:)
269
+ end
270
+
271
+ inputs.each do |name, value|
272
+ runtime.define_singleton_method(name) { value }
273
+ end
274
+ end
275
+ end
276
+
277
+ def prompt_path_for(name)
278
+ filename = name.to_s
279
+ filename += '.txt.erb' unless filename.end_with?('.txt.erb')
280
+ prompt_root.join(prompt_agent_path, filename)
281
+ end
282
+
283
+ def prompt_agent_path
284
+ class_name = name || 'agent'
285
+ class_name.gsub('::', '/')
286
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
287
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
288
+ .tr('-', '_')
289
+ .downcase
290
+ end
291
+
292
+ def prompt_root
293
+ if defined?(Rails) && Rails.respond_to?(:root) && Rails.root
294
+ Rails.root.join('app/prompts')
295
+ else
296
+ Pathname.new(Dir.pwd).join('app/prompts')
297
+ end
298
+ end
299
+
300
+ def resolved_chat_model
301
+ return @resolved_chat_model if defined?(@resolved_chat_model)
302
+
303
+ @resolved_chat_model = case @chat_model
304
+ when String then Object.const_get(@chat_model)
305
+ else @chat_model
306
+ end
307
+ end
308
+ end
309
+
310
+ def initialize(chat: nil, inputs: nil, persist_instructions: true, **kwargs)
311
+ input_values, chat_options = self.class.send(:partition_inputs, kwargs)
312
+ @chat = chat || RubyLLM.chat(**self.class.chat_kwargs, **chat_options)
313
+ self.class.send(:apply_configuration, @chat, input_values: input_values.merge(inputs || {}),
314
+ persist_instructions:)
315
+ end
316
+
317
+ attr_reader :chat
318
+
319
+ delegate :ask, :say, :complete, :add_message, :messages,
320
+ :on_new_message, :on_end_message, :on_tool_call, :on_tool_result, :each,
321
+ to: :chat
322
+ end
323
+ end
@@ -40,7 +40,8 @@
40
40
  "claude-haiku-4-5": {
41
41
  "anthropic": "claude-haiku-4-5-20251001",
42
42
  "openrouter": "anthropic/claude-haiku-4.5",
43
- "bedrock": "anthropic.claude-haiku-4-5-20251001-v1:0"
43
+ "bedrock": "anthropic.claude-haiku-4-5-20251001-v1:0",
44
+ "azure": "claude-haiku-4-5-20251001"
44
45
  },
45
46
  "claude-opus-4": {
46
47
  "anthropic": "claude-opus-4-20250514",
@@ -53,12 +54,19 @@
53
54
  "claude-opus-4-1": {
54
55
  "anthropic": "claude-opus-4-1-20250805",
55
56
  "openrouter": "anthropic/claude-opus-4.1",
56
- "bedrock": "anthropic.claude-opus-4-1-20250805-v1:0"
57
+ "bedrock": "anthropic.claude-opus-4-1-20250805-v1:0",
58
+ "azure": "claude-opus-4-1-20250805"
57
59
  },
58
60
  "claude-opus-4-5": {
59
61
  "anthropic": "claude-opus-4-5-20251101",
60
62
  "openrouter": "anthropic/claude-opus-4.5",
61
- "bedrock": "anthropic.claude-opus-4-5-20251101-v1:0"
63
+ "bedrock": "anthropic.claude-opus-4-5-20251101-v1:0",
64
+ "azure": "claude-opus-4-5-20251101"
65
+ },
66
+ "claude-opus-4-6": {
67
+ "anthropic": "claude-opus-4-6",
68
+ "openrouter": "anthropic/claude-opus-4.6",
69
+ "bedrock": "anthropic.claude-opus-4-6-v1"
62
70
  },
63
71
  "claude-sonnet-4": {
64
72
  "anthropic": "claude-sonnet-4-20250514",
@@ -71,7 +79,8 @@
71
79
  "claude-sonnet-4-5": {
72
80
  "anthropic": "claude-sonnet-4-5-20250929",
73
81
  "openrouter": "anthropic/claude-sonnet-4.5",
74
- "bedrock": "anthropic.claude-sonnet-4-5-20250929-v1:0"
82
+ "bedrock": "anthropic.claude-sonnet-4-5-20250929-v1:0",
83
+ "azure": "claude-sonnet-4-5-20250929"
75
84
  },
76
85
  "deepseek-chat": {
77
86
  "deepseek": "deepseek-chat",
@@ -98,10 +107,6 @@
98
107
  "openrouter": "google/gemini-2.0-flash-001",
99
108
  "vertexai": "gemini-2.0-flash-001"
100
109
  },
101
- "gemini-2.0-flash-exp": {
102
- "gemini": "gemini-2.0-flash-exp",
103
- "vertexai": "gemini-2.0-flash-exp"
104
- },
105
110
  "gemini-2.0-flash-lite": {
106
111
  "gemini": "gemini-2.0-flash-lite",
107
112
  "vertexai": "gemini-2.0-flash-lite"
@@ -222,7 +227,8 @@
222
227
  },
223
228
  "gpt-4": {
224
229
  "openai": "gpt-4",
225
- "openrouter": "openai/gpt-4"
230
+ "openrouter": "openai/gpt-4",
231
+ "azure": "gpt-4"
226
232
  },
227
233
  "gpt-4-1106-preview": {
228
234
  "openai": "gpt-4-1106-preview",
@@ -238,31 +244,38 @@
238
244
  },
239
245
  "gpt-4.1": {
240
246
  "openai": "gpt-4.1",
241
- "openrouter": "openai/gpt-4.1"
247
+ "openrouter": "openai/gpt-4.1",
248
+ "azure": "gpt-4.1"
242
249
  },
243
250
  "gpt-4.1-mini": {
244
251
  "openai": "gpt-4.1-mini",
245
- "openrouter": "openai/gpt-4.1-mini"
252
+ "openrouter": "openai/gpt-4.1-mini",
253
+ "azure": "gpt-4.1-mini"
246
254
  },
247
255
  "gpt-4.1-nano": {
248
256
  "openai": "gpt-4.1-nano",
249
- "openrouter": "openai/gpt-4.1-nano"
257
+ "openrouter": "openai/gpt-4.1-nano",
258
+ "azure": "gpt-4.1-nano"
250
259
  },
251
260
  "gpt-4o": {
252
261
  "openai": "gpt-4o",
253
- "openrouter": "openai/gpt-4o"
262
+ "openrouter": "openai/gpt-4o",
263
+ "azure": "gpt-4o"
254
264
  },
255
265
  "gpt-4o-2024-05-13": {
256
266
  "openai": "gpt-4o-2024-05-13",
257
- "openrouter": "openai/gpt-4o-2024-05-13"
267
+ "openrouter": "openai/gpt-4o-2024-05-13",
268
+ "azure": "gpt-4o-2024-05-13"
258
269
  },
259
270
  "gpt-4o-2024-08-06": {
260
271
  "openai": "gpt-4o-2024-08-06",
261
- "openrouter": "openai/gpt-4o-2024-08-06"
272
+ "openrouter": "openai/gpt-4o-2024-08-06",
273
+ "azure": "gpt-4o-2024-08-06"
262
274
  },
263
275
  "gpt-4o-2024-11-20": {
264
276
  "openai": "gpt-4o-2024-11-20",
265
- "openrouter": "openai/gpt-4o-2024-11-20"
277
+ "openrouter": "openai/gpt-4o-2024-11-20",
278
+ "azure": "gpt-4o-2024-11-20"
266
279
  },
267
280
  "gpt-4o-audio-preview": {
268
281
  "openai": "gpt-4o-audio-preview",
@@ -270,11 +283,13 @@
270
283
  },
271
284
  "gpt-4o-mini": {
272
285
  "openai": "gpt-4o-mini",
273
- "openrouter": "openai/gpt-4o-mini"
286
+ "openrouter": "openai/gpt-4o-mini",
287
+ "azure": "gpt-4o-mini"
274
288
  },
275
289
  "gpt-4o-mini-2024-07-18": {
276
290
  "openai": "gpt-4o-mini-2024-07-18",
277
- "openrouter": "openai/gpt-4o-mini-2024-07-18"
291
+ "openrouter": "openai/gpt-4o-mini-2024-07-18",
292
+ "azure": "gpt-4o-mini-2024-07-18"
278
293
  },
279
294
  "gpt-4o-mini-search-preview": {
280
295
  "openai": "gpt-4o-mini-search-preview",
@@ -324,10 +339,6 @@
324
339
  "openai": "gpt-5.2",
325
340
  "openrouter": "openai/gpt-5.2"
326
341
  },
327
- "gpt-5.2-chat": {
328
- "openai": "gpt-5.2-chat-latest",
329
- "openrouter": "openai/gpt-5.2-chat-latest"
330
- },
331
342
  "gpt-5.2-codex": {
332
343
  "openai": "gpt-5.2-codex",
333
344
  "openrouter": "openai/gpt-5.2-codex"
@@ -336,13 +347,22 @@
336
347
  "openai": "gpt-5.2-pro",
337
348
  "openrouter": "openai/gpt-5.2-pro"
338
349
  },
350
+ "gpt-audio": {
351
+ "openai": "gpt-audio",
352
+ "openrouter": "openai/gpt-audio"
353
+ },
354
+ "gpt-audio-mini": {
355
+ "openai": "gpt-audio-mini",
356
+ "openrouter": "openai/gpt-audio-mini"
357
+ },
339
358
  "o1": {
340
359
  "openai": "o1",
341
360
  "openrouter": "openai/o1"
342
361
  },
343
362
  "o1-pro": {
344
363
  "openai": "o1-pro",
345
- "openrouter": "openai/o1-pro"
364
+ "openrouter": "openai/o1-pro",
365
+ "azure": "o1-pro"
346
366
  },
347
367
  "o3": {
348
368
  "openai": "o3",
@@ -354,7 +374,8 @@
354
374
  },
355
375
  "o3-mini": {
356
376
  "openai": "o3-mini",
357
- "openrouter": "openai/o3-mini"
377
+ "openrouter": "openai/o3-mini",
378
+ "azure": "o3-mini"
358
379
  },
359
380
  "o3-pro": {
360
381
  "openai": "o3-pro",
@@ -362,14 +383,11 @@
362
383
  },
363
384
  "o4-mini": {
364
385
  "openai": "o4-mini",
365
- "openrouter": "openai/o4-mini"
386
+ "openrouter": "openai/o4-mini",
387
+ "azure": "o4-mini"
366
388
  },
367
389
  "o4-mini-deep-research": {
368
390
  "openai": "o4-mini-deep-research",
369
391
  "openrouter": "openai/o4-mini-deep-research"
370
- },
371
- "text-embedding-004": {
372
- "gemini": "text-embedding-004",
373
- "vertexai": "text-embedding-004"
374
392
  }
375
393
  }