raif 1.3.0 → 1.4.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.
- checksums.yaml +4 -4
- data/README.md +6 -5
- data/app/assets/builds/raif.css +4 -1
- data/app/assets/builds/raif_admin.css +13 -1
- data/app/assets/javascript/raif/controllers/conversations_controller.js +1 -1
- data/app/assets/stylesheets/raif/admin/conversation.scss +16 -0
- data/app/assets/stylesheets/raif/conversations.scss +3 -0
- data/app/assets/stylesheets/raif.scss +2 -1
- data/app/controllers/raif/admin/application_controller.rb +16 -0
- data/app/controllers/raif/admin/configs_controller.rb +94 -0
- data/app/controllers/raif/admin/model_completions_controller.rb +18 -1
- data/app/controllers/raif/admin/model_tool_invocations_controller.rb +7 -1
- data/app/controllers/raif/admin/stats/model_tool_invocations_controller.rb +21 -0
- data/app/controllers/raif/admin/stats/tasks_controller.rb +15 -6
- data/app/controllers/raif/admin/stats_controller.rb +32 -3
- data/app/controllers/raif/conversation_entries_controller.rb +1 -0
- data/app/controllers/raif/conversations_controller.rb +10 -2
- data/app/jobs/raif/conversation_entry_job.rb +8 -6
- data/app/models/raif/admin/task_stat.rb +7 -0
- data/app/models/raif/agent.rb +63 -2
- data/app/models/raif/agents/native_tool_calling_agent.rb +101 -56
- data/app/models/raif/application_record.rb +18 -0
- data/app/models/raif/concerns/agent_inference_stats.rb +35 -0
- data/app/models/raif/concerns/json_schema_definition.rb +40 -5
- data/app/models/raif/concerns/llms/anthropic/message_formatting.rb +28 -0
- data/app/models/raif/concerns/llms/anthropic/response_tool_calls.rb +24 -0
- data/app/models/raif/concerns/llms/anthropic/tool_formatting.rb +4 -0
- data/app/models/raif/concerns/llms/bedrock/message_formatting.rb +36 -0
- data/app/models/raif/concerns/llms/bedrock/response_tool_calls.rb +26 -0
- data/app/models/raif/concerns/llms/bedrock/tool_formatting.rb +4 -0
- data/app/models/raif/concerns/llms/google/message_formatting.rb +109 -0
- data/app/models/raif/concerns/llms/google/response_tool_calls.rb +32 -0
- data/app/models/raif/concerns/llms/google/tool_formatting.rb +72 -0
- data/app/models/raif/concerns/llms/message_formatting.rb +11 -5
- data/app/models/raif/concerns/llms/open_ai/json_schema_validation.rb +3 -3
- data/app/models/raif/concerns/llms/open_ai_completions/message_formatting.rb +22 -0
- data/app/models/raif/concerns/llms/open_ai_completions/response_tool_calls.rb +22 -0
- data/app/models/raif/concerns/llms/open_ai_completions/tool_formatting.rb +4 -0
- data/app/models/raif/concerns/llms/open_ai_responses/message_formatting.rb +17 -0
- data/app/models/raif/concerns/llms/open_ai_responses/response_tool_calls.rb +26 -0
- data/app/models/raif/concerns/llms/open_ai_responses/tool_formatting.rb +4 -0
- data/app/models/raif/concerns/run_with.rb +127 -0
- data/app/models/raif/conversation.rb +91 -8
- data/app/models/raif/conversation_entry.rb +32 -1
- data/app/models/raif/embedding_model.rb +2 -1
- data/app/models/raif/embedding_models/open_ai.rb +1 -1
- data/app/models/raif/llm.rb +27 -2
- data/app/models/raif/llms/anthropic.rb +7 -19
- data/app/models/raif/llms/bedrock.rb +6 -20
- data/app/models/raif/llms/google.rb +140 -0
- data/app/models/raif/llms/open_ai_base.rb +19 -5
- data/app/models/raif/llms/open_ai_completions.rb +6 -11
- data/app/models/raif/llms/open_ai_responses.rb +6 -16
- data/app/models/raif/llms/open_router.rb +7 -13
- data/app/models/raif/model_completion.rb +61 -0
- data/app/models/raif/model_tool.rb +10 -2
- data/app/models/raif/model_tool_invocation.rb +38 -6
- data/app/models/raif/model_tools/agent_final_answer.rb +2 -7
- data/app/models/raif/model_tools/provider_managed/code_execution.rb +4 -0
- data/app/models/raif/model_tools/provider_managed/image_generation.rb +4 -0
- data/app/models/raif/model_tools/provider_managed/web_search.rb +4 -0
- data/app/models/raif/streaming_responses/google.rb +71 -0
- data/app/models/raif/task.rb +55 -12
- data/app/models/raif/user_tool_invocation.rb +19 -0
- data/app/views/layouts/raif/admin.html.erb +12 -1
- data/app/views/raif/admin/agents/_agent.html.erb +8 -0
- data/app/views/raif/admin/agents/_conversation_message.html.erb +28 -6
- data/app/views/raif/admin/agents/index.html.erb +2 -0
- data/app/views/raif/admin/agents/show.html.erb +46 -1
- data/app/views/raif/admin/configs/show.html.erb +117 -0
- data/app/views/raif/admin/conversations/_conversation_entry.html.erb +29 -34
- data/app/views/raif/admin/conversations/show.html.erb +2 -0
- data/app/views/raif/admin/model_completions/_model_completion.html.erb +9 -0
- data/app/views/raif/admin/model_completions/index.html.erb +26 -0
- data/app/views/raif/admin/model_completions/show.html.erb +124 -61
- data/app/views/raif/admin/model_tool_invocations/index.html.erb +22 -1
- data/app/views/raif/admin/model_tools/_list.html.erb +16 -0
- data/app/views/raif/admin/model_tools/_model_tool.html.erb +36 -0
- data/app/views/raif/admin/stats/_stats_tile.html.erb +34 -0
- data/app/views/raif/admin/stats/index.html.erb +71 -88
- data/app/views/raif/admin/stats/model_tool_invocations/index.html.erb +43 -0
- data/app/views/raif/admin/stats/tasks/index.html.erb +20 -6
- data/app/views/raif/admin/tasks/index.html.erb +6 -1
- data/app/views/raif/admin/tasks/show.html.erb +36 -3
- data/app/views/raif/conversation_entries/_form.html.erb +3 -0
- data/app/views/raif/conversations/_conversation.html.erb +10 -0
- data/app/views/raif/conversations/_entry_processed.turbo_stream.erb +12 -0
- data/app/views/raif/conversations/index.html.erb +23 -0
- data/config/locales/admin.en.yml +33 -1
- data/config/locales/en.yml +33 -4
- data/config/routes.rb +2 -0
- data/db/migrate/20250904194456_add_generating_entry_response_to_raif_conversations.rb +7 -0
- data/db/migrate/20250911125234_add_source_to_raif_tasks.rb +7 -0
- data/db/migrate/20251020005853_add_source_to_raif_agents.rb +7 -0
- data/db/migrate/20251020011346_rename_task_run_args_to_run_with.rb +7 -0
- data/db/migrate/20251020011405_add_run_with_to_raif_agents.rb +13 -0
- data/db/migrate/20251024160119_add_llm_messages_max_length_to_raif_conversations.rb +14 -0
- data/db/migrate/20251124185033_add_provider_tool_call_id_to_raif_model_tool_invocations.rb +7 -0
- data/db/migrate/20251128202941_add_tool_choice_to_raif_model_completions.rb +7 -0
- data/db/migrate/20260118144846_add_source_to_raif_conversations.rb +7 -0
- data/db/migrate/20260119000000_add_failure_tracking_to_raif_model_completions.rb +10 -0
- data/db/migrate/20260119000001_add_completed_at_to_raif_model_completions.rb +8 -0
- data/db/migrate/20260119000002_add_started_at_to_raif_model_completions.rb +8 -0
- data/lib/generators/raif/agent/templates/agent.rb.tt +1 -1
- data/lib/generators/raif/agent/templates/application_agent.rb.tt +1 -1
- data/lib/generators/raif/conversation/templates/conversation.rb.tt +6 -0
- data/lib/generators/raif/install/templates/initializer.rb +78 -10
- data/lib/generators/raif/task/templates/task.rb.tt +1 -1
- data/lib/raif/configuration.rb +37 -2
- data/lib/raif/engine.rb +8 -0
- data/lib/raif/errors/instance_dependent_schema_error.rb +8 -0
- data/lib/raif/errors/streaming_error.rb +6 -3
- data/lib/raif/errors.rb +1 -0
- data/lib/raif/evals/llm_judge.rb +2 -2
- data/lib/raif/evals/llm_judges/binary.rb +3 -3
- data/lib/raif/evals/llm_judges/comparative.rb +3 -3
- data/lib/raif/evals/llm_judges/scored.rb +1 -1
- data/lib/raif/evals/llm_judges/summarization.rb +2 -2
- data/lib/raif/evals/run.rb +1 -0
- data/lib/raif/json_schema_builder.rb +14 -0
- data/lib/raif/llm_registry.rb +207 -37
- data/lib/raif/messages.rb +180 -0
- data/lib/raif/version.rb +1 -1
- data/lib/raif.rb +9 -0
- data/lib/tasks/annotate_rb.rake +10 -0
- data/spec/support/rspec_helpers.rb +8 -8
- metadata +44 -9
- data/app/models/raif/agents/re_act_agent.rb +0 -127
- data/app/models/raif/agents/re_act_step.rb +0 -32
- data/app/models/raif/concerns/task_run_args.rb +0 -62
data/lib/raif/llm_registry.rb
CHANGED
|
@@ -41,6 +41,41 @@ module Raif
|
|
|
41
41
|
|
|
42
42
|
def self.default_llms
|
|
43
43
|
open_ai_models = [
|
|
44
|
+
{
|
|
45
|
+
key: :open_ai_gpt_5_2,
|
|
46
|
+
api_name: "gpt-5.2",
|
|
47
|
+
input_token_cost: 1.75 / 1_000_000,
|
|
48
|
+
output_token_cost: 14.0 / 1_000_000,
|
|
49
|
+
model_provider_settings: { supports_temperature: false },
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
key: :open_ai_gpt_5_1,
|
|
53
|
+
api_name: "gpt-5.1",
|
|
54
|
+
input_token_cost: 1.25 / 1_000_000,
|
|
55
|
+
output_token_cost: 10.0 / 1_000_000,
|
|
56
|
+
model_provider_settings: { supports_temperature: false },
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
key: :open_ai_gpt_5,
|
|
60
|
+
api_name: "gpt-5",
|
|
61
|
+
input_token_cost: 1.25 / 1_000_000,
|
|
62
|
+
output_token_cost: 10.0 / 1_000_000,
|
|
63
|
+
model_provider_settings: { supports_temperature: false },
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
key: :open_ai_gpt_5_mini,
|
|
67
|
+
api_name: "gpt-5-mini",
|
|
68
|
+
input_token_cost: 0.25 / 1_000_000,
|
|
69
|
+
output_token_cost: 2.0 / 1_000_000,
|
|
70
|
+
model_provider_settings: { supports_temperature: false },
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
key: :open_ai_gpt_5_nano,
|
|
74
|
+
api_name: "gpt-5-nano",
|
|
75
|
+
input_token_cost: 0.05 / 1_000_000,
|
|
76
|
+
output_token_cost: 0.4 / 1_000_000,
|
|
77
|
+
model_provider_settings: { supports_temperature: false },
|
|
78
|
+
},
|
|
44
79
|
{
|
|
45
80
|
key: :open_ai_gpt_4o_mini,
|
|
46
81
|
api_name: "gpt-4o-mini",
|
|
@@ -113,27 +148,6 @@ module Raif
|
|
|
113
148
|
output_token_cost: 4.4 / 1_000_000,
|
|
114
149
|
model_provider_settings: { supports_temperature: false },
|
|
115
150
|
},
|
|
116
|
-
{
|
|
117
|
-
key: :open_ai_gpt_5,
|
|
118
|
-
api_name: "gpt-5",
|
|
119
|
-
input_token_cost: 1.25 / 1_000_000,
|
|
120
|
-
output_token_cost: 10.0 / 1_000_000,
|
|
121
|
-
model_provider_settings: { supports_temperature: false },
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
key: :open_ai_gpt_5_mini,
|
|
125
|
-
api_name: "gpt-5-mini",
|
|
126
|
-
input_token_cost: 0.25 / 1_000_000,
|
|
127
|
-
output_token_cost: 2.0 / 1_000_000,
|
|
128
|
-
model_provider_settings: { supports_temperature: false },
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
key: :open_ai_gpt_5_nano,
|
|
132
|
-
api_name: "gpt-5-nano",
|
|
133
|
-
input_token_cost: 0.05 / 1_000_000,
|
|
134
|
-
output_token_cost: 0.4 / 1_000_000,
|
|
135
|
-
model_provider_settings: { supports_temperature: false },
|
|
136
|
-
}
|
|
137
151
|
]
|
|
138
152
|
|
|
139
153
|
open_ai_responses_models = open_ai_models.dup.map.with_index do |model, _index|
|
|
@@ -172,11 +186,33 @@ module Raif
|
|
|
172
186
|
Raif::Llms::OpenAiResponses => open_ai_responses_models,
|
|
173
187
|
Raif::Llms::Anthropic => [
|
|
174
188
|
{
|
|
175
|
-
key: :
|
|
176
|
-
api_name: "claude-sonnet-4-
|
|
189
|
+
key: :anthropic_claude_4_5_sonnet,
|
|
190
|
+
api_name: "claude-sonnet-4-5",
|
|
177
191
|
input_token_cost: 3.0 / 1_000_000,
|
|
178
192
|
output_token_cost: 15.0 / 1_000_000,
|
|
179
|
-
max_completion_tokens:
|
|
193
|
+
max_completion_tokens: 64_000,
|
|
194
|
+
supported_provider_managed_tools: [
|
|
195
|
+
Raif::ModelTools::ProviderManaged::WebSearch,
|
|
196
|
+
Raif::ModelTools::ProviderManaged::CodeExecution
|
|
197
|
+
]
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
key: :anthropic_claude_4_5_haiku,
|
|
201
|
+
api_name: "claude-haiku-4-5",
|
|
202
|
+
input_token_cost: 1.0 / 1_000_000,
|
|
203
|
+
output_token_cost: 5.0 / 1_000_000,
|
|
204
|
+
max_completion_tokens: 64_000,
|
|
205
|
+
supported_provider_managed_tools: [
|
|
206
|
+
Raif::ModelTools::ProviderManaged::WebSearch,
|
|
207
|
+
Raif::ModelTools::ProviderManaged::CodeExecution
|
|
208
|
+
]
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
key: :anthropic_claude_4_1_opus,
|
|
212
|
+
api_name: "claude-opus-4-1",
|
|
213
|
+
input_token_cost: 15.0 / 1_000_000,
|
|
214
|
+
output_token_cost: 75.0 / 1_000_000,
|
|
215
|
+
max_completion_tokens: 32_000,
|
|
180
216
|
supported_provider_managed_tools: [
|
|
181
217
|
Raif::ModelTools::ProviderManaged::WebSearch,
|
|
182
218
|
Raif::ModelTools::ProviderManaged::CodeExecution
|
|
@@ -193,6 +229,17 @@ module Raif
|
|
|
193
229
|
Raif::ModelTools::ProviderManaged::CodeExecution
|
|
194
230
|
]
|
|
195
231
|
},
|
|
232
|
+
{
|
|
233
|
+
key: :anthropic_claude_4_sonnet,
|
|
234
|
+
api_name: "claude-sonnet-4-20250514",
|
|
235
|
+
input_token_cost: 3.0 / 1_000_000,
|
|
236
|
+
output_token_cost: 15.0 / 1_000_000,
|
|
237
|
+
max_completion_tokens: 8192,
|
|
238
|
+
supported_provider_managed_tools: [
|
|
239
|
+
Raif::ModelTools::ProviderManaged::WebSearch,
|
|
240
|
+
Raif::ModelTools::ProviderManaged::CodeExecution
|
|
241
|
+
]
|
|
242
|
+
},
|
|
196
243
|
{
|
|
197
244
|
key: :anthropic_claude_3_7_sonnet,
|
|
198
245
|
api_name: "claude-3-7-sonnet-latest",
|
|
@@ -235,6 +282,27 @@ module Raif
|
|
|
235
282
|
},
|
|
236
283
|
],
|
|
237
284
|
Raif::Llms::Bedrock => [
|
|
285
|
+
{
|
|
286
|
+
key: :bedrock_claude_4_5_sonnet,
|
|
287
|
+
api_name: "anthropic.claude-sonnet-4-5-20250929-v1:0",
|
|
288
|
+
input_token_cost: 0.003 / 1000,
|
|
289
|
+
output_token_cost: 0.015 / 1000,
|
|
290
|
+
max_completion_tokens: 64_000
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
key: :bedrock_claude_4_5_haiku,
|
|
294
|
+
api_name: "anthropic.claude-haiku-4-5-20251001-v1:0",
|
|
295
|
+
input_token_cost: 0.001 / 1000,
|
|
296
|
+
output_token_cost: 0.005 / 1000,
|
|
297
|
+
max_completion_tokens: 64_000
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
key: :bedrock_claude_4_1_opus,
|
|
301
|
+
api_name: "anthropic.claude-opus-4-1-20250805-v1:0",
|
|
302
|
+
input_token_cost: 0.015 / 1000,
|
|
303
|
+
output_token_cost: 0.075 / 1000,
|
|
304
|
+
max_completion_tokens: 32_000
|
|
305
|
+
},
|
|
238
306
|
{
|
|
239
307
|
key: :bedrock_claude_4_sonnet,
|
|
240
308
|
api_name: "anthropic.claude-sonnet-4-20250514-v1:0",
|
|
@@ -250,15 +318,15 @@ module Raif
|
|
|
250
318
|
max_completion_tokens: 8192
|
|
251
319
|
},
|
|
252
320
|
{
|
|
253
|
-
key: :
|
|
254
|
-
api_name: "anthropic.claude-3-
|
|
321
|
+
key: :bedrock_claude_3_7_sonnet,
|
|
322
|
+
api_name: "anthropic.claude-3-7-sonnet-20250219-v1:0",
|
|
255
323
|
input_token_cost: 0.003 / 1000,
|
|
256
324
|
output_token_cost: 0.015 / 1000,
|
|
257
325
|
max_completion_tokens: 8192
|
|
258
326
|
},
|
|
259
327
|
{
|
|
260
|
-
key: :
|
|
261
|
-
api_name: "anthropic.claude-3-
|
|
328
|
+
key: :bedrock_claude_3_5_sonnet,
|
|
329
|
+
api_name: "anthropic.claude-3-5-sonnet-20241022-v2:0",
|
|
262
330
|
input_token_cost: 0.003 / 1000,
|
|
263
331
|
output_token_cost: 0.015 / 1000,
|
|
264
332
|
max_completion_tokens: 8192
|
|
@@ -306,6 +374,60 @@ module Raif
|
|
|
306
374
|
input_token_cost: 3.0 / 1_000_000,
|
|
307
375
|
output_token_cost: 15.0 / 1_000_000,
|
|
308
376
|
},
|
|
377
|
+
{
|
|
378
|
+
key: :open_router_deepseek_chat_v3,
|
|
379
|
+
api_name: "deepseek/deepseek-chat-v3-0324",
|
|
380
|
+
input_token_cost: 0.27 / 1_000_000,
|
|
381
|
+
output_token_cost: 1.1 / 1_000_000,
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
key: :open_router_deepseek_v3_1,
|
|
385
|
+
api_name: "deepseek/deepseek-chat-v3.1",
|
|
386
|
+
input_token_cost: 0.25 / 1_000_000,
|
|
387
|
+
output_token_cost: 1.0 / 1_000_000,
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
key: :open_router_gemini_2_0_flash,
|
|
391
|
+
api_name: "google/gemini-2.0-flash-001",
|
|
392
|
+
input_token_cost: 0.1 / 1_000_000,
|
|
393
|
+
output_token_cost: 0.4 / 1_000_000,
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
key: :open_router_gemini_2_5_flash,
|
|
397
|
+
api_name: "google/gemini-2.5-flash",
|
|
398
|
+
input_token_cost: 0.3 / 1_000_000,
|
|
399
|
+
output_token_cost: 2.5 / 1_000_000,
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
key: :open_router_gemini_2_5_pro,
|
|
403
|
+
api_name: "google/gemini-2.5-pro",
|
|
404
|
+
input_token_cost: 1.25 / 1_000_000,
|
|
405
|
+
output_token_cost: 10.0 / 1_000_000,
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
key: :open_router_gemini_3_pro_preview,
|
|
409
|
+
api_name: "google/gemini-3-pro-preview",
|
|
410
|
+
input_token_cost: 2.0 / 1_000_000,
|
|
411
|
+
output_token_cost: 12.0 / 1_000_000,
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
key: :open_router_grok_4,
|
|
415
|
+
api_name: "x-ai/grok-4",
|
|
416
|
+
input_token_cost: 3.0 / 1_000_000,
|
|
417
|
+
output_token_cost: 15.0 / 1_000_000,
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
key: :open_router_grok_4_1_fast,
|
|
421
|
+
api_name: "x-ai/grok-4.1-fast",
|
|
422
|
+
input_token_cost: 0.2 / 1_000_000,
|
|
423
|
+
output_token_cost: 0.5 / 1_000_000,
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
key: :open_router_kimi_k2_thinking,
|
|
427
|
+
api_name: "moonshotai/kimi-k2-thinking",
|
|
428
|
+
input_token_cost: 0.45 / 1_000_000,
|
|
429
|
+
output_token_cost: 2.35 / 1_000_000,
|
|
430
|
+
},
|
|
309
431
|
{
|
|
310
432
|
key: :open_router_llama_3_3_70b_instruct,
|
|
311
433
|
api_name: "meta-llama/llama-3.3-70b-instruct",
|
|
@@ -331,16 +453,22 @@ module Raif
|
|
|
331
453
|
output_token_cost: 0.30 / 1_000_000,
|
|
332
454
|
},
|
|
333
455
|
{
|
|
334
|
-
key: :
|
|
335
|
-
api_name: "
|
|
336
|
-
input_token_cost: 0.
|
|
337
|
-
output_token_cost:
|
|
456
|
+
key: :open_router_minimax_m2,
|
|
457
|
+
api_name: "minimax/minimax-m2",
|
|
458
|
+
input_token_cost: 0.255 / 1_000_000,
|
|
459
|
+
output_token_cost: 1.02 / 1_000_000,
|
|
338
460
|
},
|
|
339
461
|
{
|
|
340
|
-
key: :
|
|
341
|
-
api_name: "
|
|
342
|
-
input_token_cost: 0.
|
|
343
|
-
output_token_cost: 1.
|
|
462
|
+
key: :open_router_mistral_large_3_2512,
|
|
463
|
+
api_name: "mistralai/mistral-large-2512",
|
|
464
|
+
input_token_cost: 0.50 / 1_000_000,
|
|
465
|
+
output_token_cost: 1.5 / 1_000_000,
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
key: :open_router_mistral_small_3_2_24b,
|
|
469
|
+
api_name: "mistralai/mistral-small-3.2-24b-instruct",
|
|
470
|
+
input_token_cost: 0.06 / 1_000_000,
|
|
471
|
+
output_token_cost: 0.18 / 1_000_000,
|
|
344
472
|
},
|
|
345
473
|
{
|
|
346
474
|
key: :open_router_open_ai_gpt_oss_120b,
|
|
@@ -353,7 +481,49 @@ module Raif
|
|
|
353
481
|
api_name: "gpt-oss-20b",
|
|
354
482
|
input_token_cost: 0.05 / 1_000_000,
|
|
355
483
|
output_token_cost: 0.2 / 1_000_000,
|
|
356
|
-
}
|
|
484
|
+
},
|
|
485
|
+
],
|
|
486
|
+
Raif::Llms::Google => [
|
|
487
|
+
{
|
|
488
|
+
key: :google_gemini_3_0_pro,
|
|
489
|
+
api_name: "gemini-3-pro-preview",
|
|
490
|
+
input_token_cost: 2.0 / 1_000_000,
|
|
491
|
+
output_token_cost: 12.0 / 1_000_000,
|
|
492
|
+
supported_provider_managed_tools: [
|
|
493
|
+
Raif::ModelTools::ProviderManaged::WebSearch,
|
|
494
|
+
Raif::ModelTools::ProviderManaged::CodeExecution
|
|
495
|
+
]
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
key: :google_gemini_3_0_flash,
|
|
499
|
+
api_name: "gemini-3-flash-preview",
|
|
500
|
+
input_token_cost: 0.5 / 1_000_000,
|
|
501
|
+
output_token_cost: 3.0 / 1_000_000,
|
|
502
|
+
supported_provider_managed_tools: [
|
|
503
|
+
Raif::ModelTools::ProviderManaged::WebSearch,
|
|
504
|
+
Raif::ModelTools::ProviderManaged::CodeExecution
|
|
505
|
+
]
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
key: :google_gemini_2_5_pro,
|
|
509
|
+
api_name: "gemini-2.5-pro-preview-06-05",
|
|
510
|
+
input_token_cost: 1.25 / 1_000_000,
|
|
511
|
+
output_token_cost: 10.0 / 1_000_000,
|
|
512
|
+
supported_provider_managed_tools: [
|
|
513
|
+
Raif::ModelTools::ProviderManaged::WebSearch,
|
|
514
|
+
Raif::ModelTools::ProviderManaged::CodeExecution
|
|
515
|
+
]
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
key: :google_gemini_2_5_flash,
|
|
519
|
+
api_name: "gemini-2.5-flash",
|
|
520
|
+
input_token_cost: 0.3 / 1_000_000,
|
|
521
|
+
output_token_cost: 2.5 / 1_000_000,
|
|
522
|
+
supported_provider_managed_tools: [
|
|
523
|
+
Raif::ModelTools::ProviderManaged::WebSearch,
|
|
524
|
+
Raif::ModelTools::ProviderManaged::CodeExecution
|
|
525
|
+
]
|
|
526
|
+
},
|
|
357
527
|
]
|
|
358
528
|
}
|
|
359
529
|
end
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Raif
|
|
4
|
+
# Message types for agent conversation_history and conversation llm_messages.
|
|
5
|
+
#
|
|
6
|
+
# These classes provide a structured API for creating messages that get stored
|
|
7
|
+
# as JSONB and passed to LLM providers. Each class has:
|
|
8
|
+
# - Named parameters for initialization
|
|
9
|
+
# - `to_h` for converting to hash format (for storage/API calls)
|
|
10
|
+
# - `from_h` class method for deserializing from stored hashes
|
|
11
|
+
#
|
|
12
|
+
# @example Creating messages
|
|
13
|
+
# message = Raif::Messages::ToolCall.new(
|
|
14
|
+
# name: "wikipedia_search",
|
|
15
|
+
# arguments: { query: "Ruby" },
|
|
16
|
+
# provider_tool_call_id: "call_123"
|
|
17
|
+
# )
|
|
18
|
+
# conversation_history << message.to_h
|
|
19
|
+
#
|
|
20
|
+
# @example Deserializing stored messages
|
|
21
|
+
# messages = Raif::Messages.from_array(agent.conversation_history)
|
|
22
|
+
# messages.each do |msg|
|
|
23
|
+
# case msg
|
|
24
|
+
# when Raif::Messages::ToolCall
|
|
25
|
+
# puts "Tool: #{msg.name}"
|
|
26
|
+
# when Raif::Messages::UserMessage
|
|
27
|
+
# puts "User: #{msg.content}"
|
|
28
|
+
# end
|
|
29
|
+
# end
|
|
30
|
+
module Messages
|
|
31
|
+
# User role message
|
|
32
|
+
class UserMessage
|
|
33
|
+
attr_reader :content
|
|
34
|
+
|
|
35
|
+
# @param content [String] The user's message content
|
|
36
|
+
def initialize(content:)
|
|
37
|
+
@content = content
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @return [Hash] Hash representation for JSONB storage and LLM APIs
|
|
41
|
+
def to_h
|
|
42
|
+
{ "role" => "user", "content" => content }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Deserialize from a hash
|
|
46
|
+
# @param hash [Hash] A hash with "content" key
|
|
47
|
+
# @return [UserMessage]
|
|
48
|
+
def self.from_h(hash)
|
|
49
|
+
new(content: hash["content"])
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Assistant role message
|
|
54
|
+
class AssistantMessage
|
|
55
|
+
attr_reader :content
|
|
56
|
+
|
|
57
|
+
# @param content [String] The assistant's message content
|
|
58
|
+
def initialize(content:)
|
|
59
|
+
@content = content
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @return [Hash] Hash representation for JSONB storage and LLM APIs
|
|
63
|
+
def to_h
|
|
64
|
+
{ "role" => "assistant", "content" => content }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Deserialize from a hash
|
|
68
|
+
# @param hash [Hash] A hash with "content" key
|
|
69
|
+
# @return [AssistantMessage]
|
|
70
|
+
def self.from_h(hash)
|
|
71
|
+
new(content: hash["content"])
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Tool invocation request from the assistant
|
|
76
|
+
class ToolCall
|
|
77
|
+
attr_reader :provider_tool_call_id, :name, :arguments, :assistant_message, :provider_metadata
|
|
78
|
+
|
|
79
|
+
# @param name [String] The tool name (snake_case)
|
|
80
|
+
# @param arguments [Hash] The arguments passed to the tool
|
|
81
|
+
# @param provider_tool_call_id [String, nil] Provider-assigned ID for the tool call
|
|
82
|
+
# @param assistant_message [String, nil] Optional assistant message accompanying the tool call
|
|
83
|
+
# @param provider_metadata [Hash, nil] Provider-specific metadata (e.g., Google's thoughtSignature)
|
|
84
|
+
def initialize(name:, arguments:, provider_tool_call_id: nil, assistant_message: nil, provider_metadata: nil)
|
|
85
|
+
@provider_tool_call_id = provider_tool_call_id
|
|
86
|
+
@name = name
|
|
87
|
+
@arguments = arguments
|
|
88
|
+
@assistant_message = assistant_message
|
|
89
|
+
@provider_metadata = provider_metadata
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# @return [Hash] Hash representation for JSONB storage and LLM APIs
|
|
93
|
+
def to_h
|
|
94
|
+
{
|
|
95
|
+
"type" => "tool_call",
|
|
96
|
+
"provider_tool_call_id" => provider_tool_call_id,
|
|
97
|
+
"name" => name,
|
|
98
|
+
"arguments" => arguments,
|
|
99
|
+
"assistant_message" => assistant_message,
|
|
100
|
+
"provider_metadata" => provider_metadata
|
|
101
|
+
}.compact
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Deserialize from a hash
|
|
105
|
+
# @param hash [Hash] A hash with tool call fields
|
|
106
|
+
# @return [ToolCall]
|
|
107
|
+
def self.from_h(hash)
|
|
108
|
+
new(
|
|
109
|
+
name: hash["name"],
|
|
110
|
+
arguments: hash["arguments"],
|
|
111
|
+
provider_tool_call_id: hash["provider_tool_call_id"],
|
|
112
|
+
assistant_message: hash["assistant_message"],
|
|
113
|
+
provider_metadata: hash["provider_metadata"]
|
|
114
|
+
)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Result of a tool invocation
|
|
119
|
+
class ToolCallResult
|
|
120
|
+
attr_reader :provider_tool_call_id, :name, :result
|
|
121
|
+
|
|
122
|
+
# @param result [Hash, String] The result returned by the tool
|
|
123
|
+
# @param provider_tool_call_id [String, nil] Provider-assigned ID matching the tool call
|
|
124
|
+
# @param name [String, nil] The tool name (required by some providers like Google)
|
|
125
|
+
def initialize(result:, provider_tool_call_id: nil, name: nil)
|
|
126
|
+
@provider_tool_call_id = provider_tool_call_id
|
|
127
|
+
@name = name
|
|
128
|
+
@result = result
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# @return [Hash] Hash representation for JSONB storage and LLM APIs
|
|
132
|
+
def to_h
|
|
133
|
+
{
|
|
134
|
+
"type" => "tool_call_result",
|
|
135
|
+
"provider_tool_call_id" => provider_tool_call_id,
|
|
136
|
+
"name" => name,
|
|
137
|
+
"result" => result
|
|
138
|
+
}.compact
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Deserialize from a hash
|
|
142
|
+
# @param hash [Hash] A hash with tool call result fields
|
|
143
|
+
# @return [ToolCallResult]
|
|
144
|
+
def self.from_h(hash)
|
|
145
|
+
new(
|
|
146
|
+
provider_tool_call_id: hash["provider_tool_call_id"],
|
|
147
|
+
name: hash["name"],
|
|
148
|
+
result: hash["result"]
|
|
149
|
+
)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
class << self
|
|
154
|
+
# Deserialize a single message hash into the appropriate message class
|
|
155
|
+
# @param hash [Hash] A message hash with either "role" or "type" key
|
|
156
|
+
# @return [UserMessage, AssistantMessage, ToolCall, ToolCallResult]
|
|
157
|
+
# @raise [ArgumentError] if the hash doesn't match a known message type
|
|
158
|
+
def from_h(hash)
|
|
159
|
+
if hash["type"] == "tool_call"
|
|
160
|
+
ToolCall.from_h(hash)
|
|
161
|
+
elsif hash["type"] == "tool_call_result"
|
|
162
|
+
ToolCallResult.from_h(hash)
|
|
163
|
+
elsif hash["role"] == "user"
|
|
164
|
+
UserMessage.from_h(hash)
|
|
165
|
+
elsif hash["role"] == "assistant"
|
|
166
|
+
AssistantMessage.from_h(hash)
|
|
167
|
+
else
|
|
168
|
+
raise ArgumentError, "Unknown message type: #{hash.inspect}"
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Deserialize an array of message hashes
|
|
173
|
+
# @param messages [Array<Hash>] Array of message hashes
|
|
174
|
+
# @return [Array<UserMessage, AssistantMessage, ToolCall, ToolCallResult>]
|
|
175
|
+
def from_array(messages)
|
|
176
|
+
messages.map { |msg| from_h(msg) }
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
data/lib/raif/version.rb
CHANGED
data/lib/raif.rb
CHANGED
|
@@ -10,6 +10,7 @@ require "raif/llm_registry"
|
|
|
10
10
|
require "raif/embedding_model_registry"
|
|
11
11
|
require "raif/json_schema_builder"
|
|
12
12
|
require "raif/migration_checker"
|
|
13
|
+
require "raif/messages"
|
|
13
14
|
|
|
14
15
|
require "faraday"
|
|
15
16
|
require "event_stream_parser"
|
|
@@ -41,4 +42,12 @@ module Raif
|
|
|
41
42
|
def self.running_evals?
|
|
42
43
|
ENV["RAIF_RUNNING_EVALS"] == "true"
|
|
43
44
|
end
|
|
45
|
+
|
|
46
|
+
def self.default_request_options
|
|
47
|
+
{
|
|
48
|
+
open_timeout: config.request_open_timeout,
|
|
49
|
+
read_timeout: config.request_read_timeout,
|
|
50
|
+
write_timeout: config.request_write_timeout
|
|
51
|
+
}.compact
|
|
52
|
+
end
|
|
44
53
|
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This rake task was added by annotate_rb gem.
|
|
4
|
+
|
|
5
|
+
# Can set `ANNOTATERB_SKIP_ON_DB_TASKS` to be anything to skip this
|
|
6
|
+
if Rails.env.development? && ENV["ANNOTATERB_SKIP_ON_DB_TASKS"].nil?
|
|
7
|
+
require "annotate_rb"
|
|
8
|
+
|
|
9
|
+
AnnotateRb::Core.load_rake_tasks
|
|
10
|
+
end
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
module Raif
|
|
4
4
|
module RspecHelpers
|
|
5
5
|
|
|
6
|
-
def stubbed_llm(llm_model_key, &block)
|
|
6
|
+
def stubbed_llm(llm_model_key, source_instance, &block)
|
|
7
7
|
test_llm = Raif.llm(llm_model_key.to_sym)
|
|
8
8
|
|
|
9
9
|
allow(test_llm).to receive(:perform_model_completion!) do |model_completion|
|
|
10
|
-
result = block.call(model_completion.messages, model_completion)
|
|
10
|
+
result = block.call(model_completion.messages, model_completion, source_instance)
|
|
11
11
|
model_completion.raw_response = result if result.is_a?(String)
|
|
12
12
|
model_completion.completion_tokens = rand(100..2000)
|
|
13
13
|
model_completion.prompt_tokens = rand(100..2000)
|
|
@@ -24,10 +24,10 @@ module Raif
|
|
|
24
24
|
allow(Raif.config).to receive(:llm_api_requests_enabled){ true }
|
|
25
25
|
|
|
26
26
|
if task.is_a?(Raif::Task)
|
|
27
|
-
allow(task).to receive(:llm){ stubbed_llm(task.llm_model_key, &block) }
|
|
27
|
+
allow(task).to receive(:llm){ stubbed_llm(task.llm_model_key, task, &block) }
|
|
28
28
|
else
|
|
29
29
|
allow_any_instance_of(task).to receive(:llm) do |task_instance|
|
|
30
|
-
stubbed_llm(task_instance.llm_model_key, &block)
|
|
30
|
+
stubbed_llm(task_instance.llm_model_key, task_instance, &block)
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
end
|
|
@@ -36,10 +36,10 @@ module Raif
|
|
|
36
36
|
allow(Raif.config).to receive(:llm_api_requests_enabled){ true }
|
|
37
37
|
|
|
38
38
|
if conversation.is_a?(Raif::Conversation)
|
|
39
|
-
allow(conversation).to receive(:llm){ stubbed_llm(conversation.llm_model_key, &block) }
|
|
39
|
+
allow(conversation).to receive(:llm){ stubbed_llm(conversation.llm_model_key, conversation, &block) }
|
|
40
40
|
else
|
|
41
41
|
allow_any_instance_of(conversation).to receive(:llm) do |conversation_instance|
|
|
42
|
-
stubbed_llm(conversation_instance.llm_model_key, &block)
|
|
42
|
+
stubbed_llm(conversation_instance.llm_model_key, conversation_instance, &block)
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
45
|
end
|
|
@@ -48,10 +48,10 @@ module Raif
|
|
|
48
48
|
allow(Raif.config).to receive(:llm_api_requests_enabled){ true }
|
|
49
49
|
|
|
50
50
|
if agent.is_a?(Raif::Agent)
|
|
51
|
-
allow(agent).to receive(:llm){ stubbed_llm(agent.llm_model_key, &block) }
|
|
51
|
+
allow(agent).to receive(:llm){ stubbed_llm(agent.llm_model_key, agent, &block) }
|
|
52
52
|
else
|
|
53
53
|
allow_any_instance_of(agent).to receive(:llm) do |agent_instance|
|
|
54
|
-
stubbed_llm(agent_instance.llm_model_key, &block)
|
|
54
|
+
stubbed_llm(agent_instance.llm_model_key, agent_instance, &block)
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
end
|