raif 1.2.2 → 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/has_llm.rb +1 -1
- 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 +96 -9
- data/app/models/raif/conversation_entry.rb +37 -8
- 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 +28 -3
- 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 +10 -14
- 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 +74 -18
- 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 +4 -1
- 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/_full_conversation.html.erb +3 -6
- data/app/views/raif/conversations/_initial_chat_message.html.erb +5 -0
- data/app/views/raif/conversations/index.html.erb +23 -0
- data/config/locales/admin.en.yml +33 -1
- data/config/locales/en.yml +41 -4
- data/config/routes.rb +2 -0
- data/db/migrate/20250804013843_add_task_run_args_to_raif_tasks.rb +13 -0
- data/db/migrate/20250811171150_make_raif_task_creator_optional.rb +8 -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/exe/raif +7 -0
- data/lib/generators/raif/agent/agent_generator.rb +22 -7
- data/lib/generators/raif/agent/templates/agent.rb.tt +20 -24
- data/lib/generators/raif/agent/templates/agent_eval_set.rb.tt +48 -0
- data/lib/generators/raif/agent/templates/application_agent.rb.tt +1 -3
- data/lib/generators/raif/base_generator.rb +19 -0
- data/lib/generators/raif/conversation/conversation_generator.rb +21 -2
- data/lib/generators/raif/conversation/templates/application_conversation.rb.tt +0 -2
- data/lib/generators/raif/conversation/templates/conversation.rb.tt +34 -32
- data/lib/generators/raif/conversation/templates/conversation_eval_set.rb.tt +70 -0
- data/lib/generators/raif/eval_set/eval_set_generator.rb +28 -0
- data/lib/generators/raif/eval_set/templates/eval_set.rb.tt +21 -0
- data/lib/generators/raif/evals/setup/setup_generator.rb +47 -0
- data/lib/generators/raif/install/install_generator.rb +15 -0
- data/lib/generators/raif/install/templates/initializer.rb +89 -10
- data/lib/generators/raif/model_tool/model_tool_generator.rb +5 -5
- data/lib/generators/raif/model_tool/templates/model_tool.rb.tt +78 -78
- data/lib/generators/raif/model_tool/templates/model_tool_invocation_partial.html.erb.tt +1 -1
- data/lib/generators/raif/task/task_generator.rb +22 -3
- data/lib/generators/raif/task/templates/application_task.rb.tt +0 -2
- data/lib/generators/raif/task/templates/task.rb.tt +55 -59
- data/lib/generators/raif/task/templates/task_eval_set.rb.tt +54 -0
- data/lib/raif/cli/base.rb +39 -0
- data/lib/raif/cli/evals.rb +47 -0
- data/lib/raif/cli/evals_setup.rb +27 -0
- data/lib/raif/cli.rb +67 -0
- data/lib/raif/configuration.rb +57 -8
- 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/eval.rb +30 -0
- data/lib/raif/evals/eval_set.rb +111 -0
- data/lib/raif/evals/eval_sets/expectations.rb +53 -0
- data/lib/raif/evals/eval_sets/llm_judge_expectations.rb +255 -0
- data/lib/raif/evals/expectation_result.rb +39 -0
- data/lib/raif/evals/llm_judge.rb +32 -0
- data/lib/raif/evals/llm_judges/binary.rb +94 -0
- data/lib/raif/evals/llm_judges/comparative.rb +89 -0
- data/lib/raif/evals/llm_judges/scored.rb +63 -0
- data/lib/raif/evals/llm_judges/summarization.rb +166 -0
- data/lib/raif/evals/run.rb +202 -0
- data/lib/raif/evals/scoring_rubric.rb +174 -0
- data/lib/raif/evals.rb +26 -0
- data/lib/raif/json_schema_builder.rb +14 -0
- data/lib/raif/llm_registry.rb +218 -15
- data/lib/raif/messages.rb +180 -0
- data/lib/raif/migration_checker.rb +3 -3
- data/lib/raif/utils/colors.rb +23 -0
- data/lib/raif/utils.rb +1 -0
- data/lib/raif/version.rb +1 -1
- data/lib/raif.rb +13 -0
- data/lib/tasks/annotate_rb.rake +10 -0
- data/spec/support/current_temperature_test_tool.rb +34 -0
- data/spec/support/rspec_helpers.rb +8 -8
- data/spec/support/test_conversation.rb +1 -1
- metadata +77 -10
- data/app/models/raif/agents/re_act_agent.rb +0 -127
- data/app/models/raif/agents/re_act_step.rb +0 -33
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",
|
|
@@ -151,11 +186,33 @@ module Raif
|
|
|
151
186
|
Raif::Llms::OpenAiResponses => open_ai_responses_models,
|
|
152
187
|
Raif::Llms::Anthropic => [
|
|
153
188
|
{
|
|
154
|
-
key: :
|
|
155
|
-
api_name: "claude-sonnet-4-
|
|
189
|
+
key: :anthropic_claude_4_5_sonnet,
|
|
190
|
+
api_name: "claude-sonnet-4-5",
|
|
156
191
|
input_token_cost: 3.0 / 1_000_000,
|
|
157
192
|
output_token_cost: 15.0 / 1_000_000,
|
|
158
|
-
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,
|
|
159
216
|
supported_provider_managed_tools: [
|
|
160
217
|
Raif::ModelTools::ProviderManaged::WebSearch,
|
|
161
218
|
Raif::ModelTools::ProviderManaged::CodeExecution
|
|
@@ -172,6 +229,17 @@ module Raif
|
|
|
172
229
|
Raif::ModelTools::ProviderManaged::CodeExecution
|
|
173
230
|
]
|
|
174
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
|
+
},
|
|
175
243
|
{
|
|
176
244
|
key: :anthropic_claude_3_7_sonnet,
|
|
177
245
|
api_name: "claude-3-7-sonnet-latest",
|
|
@@ -214,6 +282,27 @@ module Raif
|
|
|
214
282
|
},
|
|
215
283
|
],
|
|
216
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
|
+
},
|
|
217
306
|
{
|
|
218
307
|
key: :bedrock_claude_4_sonnet,
|
|
219
308
|
api_name: "anthropic.claude-sonnet-4-20250514-v1:0",
|
|
@@ -229,15 +318,15 @@ module Raif
|
|
|
229
318
|
max_completion_tokens: 8192
|
|
230
319
|
},
|
|
231
320
|
{
|
|
232
|
-
key: :
|
|
233
|
-
api_name: "anthropic.claude-3-
|
|
321
|
+
key: :bedrock_claude_3_7_sonnet,
|
|
322
|
+
api_name: "anthropic.claude-3-7-sonnet-20250219-v1:0",
|
|
234
323
|
input_token_cost: 0.003 / 1000,
|
|
235
324
|
output_token_cost: 0.015 / 1000,
|
|
236
325
|
max_completion_tokens: 8192
|
|
237
326
|
},
|
|
238
327
|
{
|
|
239
|
-
key: :
|
|
240
|
-
api_name: "anthropic.claude-3-
|
|
328
|
+
key: :bedrock_claude_3_5_sonnet,
|
|
329
|
+
api_name: "anthropic.claude-3-5-sonnet-20241022-v2:0",
|
|
241
330
|
input_token_cost: 0.003 / 1000,
|
|
242
331
|
output_token_cost: 0.015 / 1000,
|
|
243
332
|
max_completion_tokens: 8192
|
|
@@ -285,6 +374,60 @@ module Raif
|
|
|
285
374
|
input_token_cost: 3.0 / 1_000_000,
|
|
286
375
|
output_token_cost: 15.0 / 1_000_000,
|
|
287
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
|
+
},
|
|
288
431
|
{
|
|
289
432
|
key: :open_router_llama_3_3_70b_instruct,
|
|
290
433
|
api_name: "meta-llama/llama-3.3-70b-instruct",
|
|
@@ -310,16 +453,76 @@ module Raif
|
|
|
310
453
|
output_token_cost: 0.30 / 1_000_000,
|
|
311
454
|
},
|
|
312
455
|
{
|
|
313
|
-
key: :
|
|
314
|
-
api_name: "
|
|
315
|
-
input_token_cost: 0.
|
|
316
|
-
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,
|
|
317
460
|
},
|
|
318
461
|
{
|
|
319
|
-
key: :
|
|
320
|
-
api_name: "
|
|
321
|
-
input_token_cost: 0.
|
|
322
|
-
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,
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
key: :open_router_open_ai_gpt_oss_120b,
|
|
475
|
+
api_name: "gpt-oss-120b",
|
|
476
|
+
input_token_cost: 0.15 / 1_000_000,
|
|
477
|
+
output_token_cost: 0.6 / 1_000_000,
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
key: :open_router_open_ai_gpt_oss_20b,
|
|
481
|
+
api_name: "gpt-oss-20b",
|
|
482
|
+
input_token_cost: 0.05 / 1_000_000,
|
|
483
|
+
output_token_cost: 0.2 / 1_000_000,
|
|
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
|
+
]
|
|
323
526
|
},
|
|
324
527
|
]
|
|
325
528
|
}
|
|
@@ -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
|
|
@@ -53,8 +53,7 @@ module Raif
|
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def build_warning_message(uninstalled_migration_names)
|
|
56
|
-
<<~WARNING
|
|
57
|
-
\e[33m
|
|
56
|
+
msg = <<~WARNING
|
|
58
57
|
⚠️ RAIF MIGRATION WARNING ⚠️
|
|
59
58
|
|
|
60
59
|
The following Raif migrations have not been run in your application:
|
|
@@ -66,8 +65,9 @@ module Raif
|
|
|
66
65
|
rails raif:install:migrations
|
|
67
66
|
rails db:migrate
|
|
68
67
|
|
|
69
|
-
\e[0m
|
|
70
68
|
WARNING
|
|
69
|
+
|
|
70
|
+
Raif::Utils::Colors.yellow(msg)
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
73
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Raif
|
|
4
|
+
module Utils
|
|
5
|
+
module Colors
|
|
6
|
+
def self.green(text)
|
|
7
|
+
"\e[32m#{text}\e[0m"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.red(text)
|
|
11
|
+
"\e[31m#{text}\e[0m"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.yellow(text)
|
|
15
|
+
"\e[33m#{text}\e[0m"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.blue(text)
|
|
19
|
+
"\e[34m#{text}\e[0m"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/raif/utils.rb
CHANGED
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"
|
|
@@ -37,4 +38,16 @@ module Raif
|
|
|
37
38
|
def self.logger
|
|
38
39
|
@logger ||= Rails.logger
|
|
39
40
|
end
|
|
41
|
+
|
|
42
|
+
def self.running_evals?
|
|
43
|
+
ENV["RAIF_RUNNING_EVALS"] == "true"
|
|
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
|
|
40
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
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Raif::ModelTools::CurrentTemperatureTestTool < Raif::ModelTool
|
|
4
|
+
tool_arguments_schema do
|
|
5
|
+
string :zip_code, description: "The zip code to get the current temperature for"
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
tool_description do
|
|
9
|
+
"A tool to get the current temperature for a given zip code"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def process_invocation(tool_invocation)
|
|
14
|
+
tool_invocation.update!(
|
|
15
|
+
result: {
|
|
16
|
+
temperature: 72
|
|
17
|
+
}
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
tool_invocation.result
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def triggers_observation_to_model?
|
|
24
|
+
true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def observation_for_invocation(tool_invocation)
|
|
28
|
+
zip_code = tool_invocation.tool_arguments["zip_code"]
|
|
29
|
+
temperature = tool_invocation.result["temperature"]
|
|
30
|
+
|
|
31
|
+
"The current temperature for zip code #{zip_code} is #{temperature} degrees Fahrenheit."
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
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
|