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.
Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -5
  3. data/app/assets/builds/raif.css +4 -1
  4. data/app/assets/builds/raif_admin.css +13 -1
  5. data/app/assets/javascript/raif/controllers/conversations_controller.js +1 -1
  6. data/app/assets/stylesheets/raif/admin/conversation.scss +16 -0
  7. data/app/assets/stylesheets/raif/conversations.scss +3 -0
  8. data/app/assets/stylesheets/raif.scss +2 -1
  9. data/app/controllers/raif/admin/application_controller.rb +16 -0
  10. data/app/controllers/raif/admin/configs_controller.rb +94 -0
  11. data/app/controllers/raif/admin/model_completions_controller.rb +18 -1
  12. data/app/controllers/raif/admin/model_tool_invocations_controller.rb +7 -1
  13. data/app/controllers/raif/admin/stats/model_tool_invocations_controller.rb +21 -0
  14. data/app/controllers/raif/admin/stats/tasks_controller.rb +15 -6
  15. data/app/controllers/raif/admin/stats_controller.rb +32 -3
  16. data/app/controllers/raif/conversation_entries_controller.rb +1 -0
  17. data/app/controllers/raif/conversations_controller.rb +10 -2
  18. data/app/jobs/raif/conversation_entry_job.rb +8 -6
  19. data/app/models/raif/admin/task_stat.rb +7 -0
  20. data/app/models/raif/agent.rb +63 -2
  21. data/app/models/raif/agents/native_tool_calling_agent.rb +101 -56
  22. data/app/models/raif/application_record.rb +18 -0
  23. data/app/models/raif/concerns/agent_inference_stats.rb +35 -0
  24. data/app/models/raif/concerns/has_llm.rb +1 -1
  25. data/app/models/raif/concerns/json_schema_definition.rb +40 -5
  26. data/app/models/raif/concerns/llms/anthropic/message_formatting.rb +28 -0
  27. data/app/models/raif/concerns/llms/anthropic/response_tool_calls.rb +24 -0
  28. data/app/models/raif/concerns/llms/anthropic/tool_formatting.rb +4 -0
  29. data/app/models/raif/concerns/llms/bedrock/message_formatting.rb +36 -0
  30. data/app/models/raif/concerns/llms/bedrock/response_tool_calls.rb +26 -0
  31. data/app/models/raif/concerns/llms/bedrock/tool_formatting.rb +4 -0
  32. data/app/models/raif/concerns/llms/google/message_formatting.rb +109 -0
  33. data/app/models/raif/concerns/llms/google/response_tool_calls.rb +32 -0
  34. data/app/models/raif/concerns/llms/google/tool_formatting.rb +72 -0
  35. data/app/models/raif/concerns/llms/message_formatting.rb +11 -5
  36. data/app/models/raif/concerns/llms/open_ai/json_schema_validation.rb +3 -3
  37. data/app/models/raif/concerns/llms/open_ai_completions/message_formatting.rb +22 -0
  38. data/app/models/raif/concerns/llms/open_ai_completions/response_tool_calls.rb +22 -0
  39. data/app/models/raif/concerns/llms/open_ai_completions/tool_formatting.rb +4 -0
  40. data/app/models/raif/concerns/llms/open_ai_responses/message_formatting.rb +17 -0
  41. data/app/models/raif/concerns/llms/open_ai_responses/response_tool_calls.rb +26 -0
  42. data/app/models/raif/concerns/llms/open_ai_responses/tool_formatting.rb +4 -0
  43. data/app/models/raif/concerns/run_with.rb +127 -0
  44. data/app/models/raif/conversation.rb +96 -9
  45. data/app/models/raif/conversation_entry.rb +37 -8
  46. data/app/models/raif/embedding_model.rb +2 -1
  47. data/app/models/raif/embedding_models/open_ai.rb +1 -1
  48. data/app/models/raif/llm.rb +28 -3
  49. data/app/models/raif/llms/anthropic.rb +7 -19
  50. data/app/models/raif/llms/bedrock.rb +6 -20
  51. data/app/models/raif/llms/google.rb +140 -0
  52. data/app/models/raif/llms/open_ai_base.rb +19 -5
  53. data/app/models/raif/llms/open_ai_completions.rb +6 -11
  54. data/app/models/raif/llms/open_ai_responses.rb +6 -16
  55. data/app/models/raif/llms/open_router.rb +10 -14
  56. data/app/models/raif/model_completion.rb +61 -0
  57. data/app/models/raif/model_tool.rb +10 -2
  58. data/app/models/raif/model_tool_invocation.rb +38 -6
  59. data/app/models/raif/model_tools/agent_final_answer.rb +2 -7
  60. data/app/models/raif/model_tools/provider_managed/code_execution.rb +4 -0
  61. data/app/models/raif/model_tools/provider_managed/image_generation.rb +4 -0
  62. data/app/models/raif/model_tools/provider_managed/web_search.rb +4 -0
  63. data/app/models/raif/streaming_responses/google.rb +71 -0
  64. data/app/models/raif/task.rb +74 -18
  65. data/app/models/raif/user_tool_invocation.rb +19 -0
  66. data/app/views/layouts/raif/admin.html.erb +12 -1
  67. data/app/views/raif/admin/agents/_agent.html.erb +8 -0
  68. data/app/views/raif/admin/agents/_conversation_message.html.erb +28 -6
  69. data/app/views/raif/admin/agents/index.html.erb +2 -0
  70. data/app/views/raif/admin/agents/show.html.erb +46 -1
  71. data/app/views/raif/admin/configs/show.html.erb +117 -0
  72. data/app/views/raif/admin/conversations/_conversation_entry.html.erb +29 -34
  73. data/app/views/raif/admin/conversations/show.html.erb +2 -0
  74. data/app/views/raif/admin/model_completions/_model_completion.html.erb +9 -0
  75. data/app/views/raif/admin/model_completions/index.html.erb +26 -0
  76. data/app/views/raif/admin/model_completions/show.html.erb +124 -61
  77. data/app/views/raif/admin/model_tool_invocations/index.html.erb +22 -1
  78. data/app/views/raif/admin/model_tools/_list.html.erb +16 -0
  79. data/app/views/raif/admin/model_tools/_model_tool.html.erb +36 -0
  80. data/app/views/raif/admin/stats/_stats_tile.html.erb +34 -0
  81. data/app/views/raif/admin/stats/index.html.erb +71 -88
  82. data/app/views/raif/admin/stats/model_tool_invocations/index.html.erb +43 -0
  83. data/app/views/raif/admin/stats/tasks/index.html.erb +20 -6
  84. data/app/views/raif/admin/tasks/index.html.erb +6 -1
  85. data/app/views/raif/admin/tasks/show.html.erb +36 -3
  86. data/app/views/raif/conversation_entries/_form.html.erb +4 -1
  87. data/app/views/raif/conversations/_conversation.html.erb +10 -0
  88. data/app/views/raif/conversations/_entry_processed.turbo_stream.erb +12 -0
  89. data/app/views/raif/conversations/_full_conversation.html.erb +3 -6
  90. data/app/views/raif/conversations/_initial_chat_message.html.erb +5 -0
  91. data/app/views/raif/conversations/index.html.erb +23 -0
  92. data/config/locales/admin.en.yml +33 -1
  93. data/config/locales/en.yml +41 -4
  94. data/config/routes.rb +2 -0
  95. data/db/migrate/20250804013843_add_task_run_args_to_raif_tasks.rb +13 -0
  96. data/db/migrate/20250811171150_make_raif_task_creator_optional.rb +8 -0
  97. data/db/migrate/20250904194456_add_generating_entry_response_to_raif_conversations.rb +7 -0
  98. data/db/migrate/20250911125234_add_source_to_raif_tasks.rb +7 -0
  99. data/db/migrate/20251020005853_add_source_to_raif_agents.rb +7 -0
  100. data/db/migrate/20251020011346_rename_task_run_args_to_run_with.rb +7 -0
  101. data/db/migrate/20251020011405_add_run_with_to_raif_agents.rb +13 -0
  102. data/db/migrate/20251024160119_add_llm_messages_max_length_to_raif_conversations.rb +14 -0
  103. data/db/migrate/20251124185033_add_provider_tool_call_id_to_raif_model_tool_invocations.rb +7 -0
  104. data/db/migrate/20251128202941_add_tool_choice_to_raif_model_completions.rb +7 -0
  105. data/db/migrate/20260118144846_add_source_to_raif_conversations.rb +7 -0
  106. data/db/migrate/20260119000000_add_failure_tracking_to_raif_model_completions.rb +10 -0
  107. data/db/migrate/20260119000001_add_completed_at_to_raif_model_completions.rb +8 -0
  108. data/db/migrate/20260119000002_add_started_at_to_raif_model_completions.rb +8 -0
  109. data/exe/raif +7 -0
  110. data/lib/generators/raif/agent/agent_generator.rb +22 -7
  111. data/lib/generators/raif/agent/templates/agent.rb.tt +20 -24
  112. data/lib/generators/raif/agent/templates/agent_eval_set.rb.tt +48 -0
  113. data/lib/generators/raif/agent/templates/application_agent.rb.tt +1 -3
  114. data/lib/generators/raif/base_generator.rb +19 -0
  115. data/lib/generators/raif/conversation/conversation_generator.rb +21 -2
  116. data/lib/generators/raif/conversation/templates/application_conversation.rb.tt +0 -2
  117. data/lib/generators/raif/conversation/templates/conversation.rb.tt +34 -32
  118. data/lib/generators/raif/conversation/templates/conversation_eval_set.rb.tt +70 -0
  119. data/lib/generators/raif/eval_set/eval_set_generator.rb +28 -0
  120. data/lib/generators/raif/eval_set/templates/eval_set.rb.tt +21 -0
  121. data/lib/generators/raif/evals/setup/setup_generator.rb +47 -0
  122. data/lib/generators/raif/install/install_generator.rb +15 -0
  123. data/lib/generators/raif/install/templates/initializer.rb +89 -10
  124. data/lib/generators/raif/model_tool/model_tool_generator.rb +5 -5
  125. data/lib/generators/raif/model_tool/templates/model_tool.rb.tt +78 -78
  126. data/lib/generators/raif/model_tool/templates/model_tool_invocation_partial.html.erb.tt +1 -1
  127. data/lib/generators/raif/task/task_generator.rb +22 -3
  128. data/lib/generators/raif/task/templates/application_task.rb.tt +0 -2
  129. data/lib/generators/raif/task/templates/task.rb.tt +55 -59
  130. data/lib/generators/raif/task/templates/task_eval_set.rb.tt +54 -0
  131. data/lib/raif/cli/base.rb +39 -0
  132. data/lib/raif/cli/evals.rb +47 -0
  133. data/lib/raif/cli/evals_setup.rb +27 -0
  134. data/lib/raif/cli.rb +67 -0
  135. data/lib/raif/configuration.rb +57 -8
  136. data/lib/raif/engine.rb +8 -0
  137. data/lib/raif/errors/instance_dependent_schema_error.rb +8 -0
  138. data/lib/raif/errors/streaming_error.rb +6 -3
  139. data/lib/raif/errors.rb +1 -0
  140. data/lib/raif/evals/eval.rb +30 -0
  141. data/lib/raif/evals/eval_set.rb +111 -0
  142. data/lib/raif/evals/eval_sets/expectations.rb +53 -0
  143. data/lib/raif/evals/eval_sets/llm_judge_expectations.rb +255 -0
  144. data/lib/raif/evals/expectation_result.rb +39 -0
  145. data/lib/raif/evals/llm_judge.rb +32 -0
  146. data/lib/raif/evals/llm_judges/binary.rb +94 -0
  147. data/lib/raif/evals/llm_judges/comparative.rb +89 -0
  148. data/lib/raif/evals/llm_judges/scored.rb +63 -0
  149. data/lib/raif/evals/llm_judges/summarization.rb +166 -0
  150. data/lib/raif/evals/run.rb +202 -0
  151. data/lib/raif/evals/scoring_rubric.rb +174 -0
  152. data/lib/raif/evals.rb +26 -0
  153. data/lib/raif/json_schema_builder.rb +14 -0
  154. data/lib/raif/llm_registry.rb +218 -15
  155. data/lib/raif/messages.rb +180 -0
  156. data/lib/raif/migration_checker.rb +3 -3
  157. data/lib/raif/utils/colors.rb +23 -0
  158. data/lib/raif/utils.rb +1 -0
  159. data/lib/raif/version.rb +1 -1
  160. data/lib/raif.rb +13 -0
  161. data/lib/tasks/annotate_rb.rake +10 -0
  162. data/spec/support/current_temperature_test_tool.rb +34 -0
  163. data/spec/support/rspec_helpers.rb +8 -8
  164. data/spec/support/test_conversation.rb +1 -1
  165. metadata +77 -10
  166. data/app/models/raif/agents/re_act_agent.rb +0 -127
  167. data/app/models/raif/agents/re_act_step.rb +0 -33
@@ -6,10 +6,7 @@ en:
6
6
  errors:
7
7
  available_model_tools:
8
8
  too_short: must have at least 1 tool in addition to the agent_final_answer tool
9
- re_act_agent:
10
- errors:
11
- available_model_tools:
12
- too_short: must have at least 1 tool
9
+ final_answer_warning: 'Warning: This is your final iteration. You must provide your final answer using the agent_final_answer tool.'
13
10
  common:
14
11
  send: Send
15
12
  sources: Sources
@@ -18,6 +15,14 @@ en:
18
15
  type_your_message: Type your message...
19
16
  conversation:
20
17
  initial_chat_message: Hello, how can I help you today?
18
+ conversations:
19
+ index:
20
+ no_conversations: No conversations found.
21
+ table:
22
+ actions: Actions
23
+ started: Started
24
+ view: View
25
+ title: Past Conversations
21
26
  embedding_model_names:
22
27
  bedrock_titan_embed_text_v2: AWS Bedrock Titan Text Embeddings v2
23
28
  open_ai_text_embedding_3_large: OpenAI Text Embedding 3 Large
@@ -51,6 +56,9 @@ en:
51
56
  anthropic_claude_3_5_sonnet: Anthropic Claude 3.5 Sonnet
52
57
  anthropic_claude_3_7_sonnet: Anthropic Claude 3.7 Sonnet
53
58
  anthropic_claude_3_opus: Anthropic Claude 3 Opus
59
+ anthropic_claude_4_1_opus: Anthropic Claude 4.1 Opus
60
+ anthropic_claude_4_5_haiku: Anthropic Claude 4.5 Haiku
61
+ anthropic_claude_4_5_sonnet: Anthropic Claude 4.5 Sonnet
54
62
  anthropic_claude_4_opus: Anthropic Claude 4 Opus
55
63
  anthropic_claude_4_sonnet: Anthropic Claude 4 Sonnet
56
64
  bedrock_amazon_nova_lite: Amazon Nova Lite (via AWS Bedrock)
@@ -60,14 +68,26 @@ en:
60
68
  bedrock_claude_3_5_sonnet: Anthropic Claude 3.5 Sonnet (via AWS Bedrock)
61
69
  bedrock_claude_3_7_sonnet: Anthropic Claude 3.7 Sonnet (via AWS Bedrock)
62
70
  bedrock_claude_3_opus: Anthropic Claude 3 Opus (via AWS Bedrock)
71
+ bedrock_claude_4_1_opus: Claude 4.1 Opus (via AWS Bedrock)
72
+ bedrock_claude_4_5_haiku: Anthropic Claude 4.5 Haiku (via AWS Bedrock)
73
+ bedrock_claude_4_5_sonnet: Anthropic Claude 4.5 Sonnet (via AWS Bedrock)
63
74
  bedrock_claude_4_opus: Anthropic Claude 4 Opus (via AWS Bedrock)
64
75
  bedrock_claude_4_sonnet: Anthropic Claude 4 Sonnet (via AWS Bedrock)
76
+ google_gemini_2_5_flash: Google Gemini 2.5 Flash
77
+ google_gemini_2_5_pro: Google Gemini 2.5 Pro
78
+ google_gemini_3_0_flash: Google Gemini 3.0 Flash
79
+ google_gemini_3_0_pro: Google Gemini 3.0 Pro
65
80
  open_ai_gpt_3_5_turbo: OpenAI GPT-3.5 Turbo
66
81
  open_ai_gpt_4_1: OpenAI GPT-4.1
67
82
  open_ai_gpt_4_1_mini: OpenAI GPT-4.1 Mini
68
83
  open_ai_gpt_4_1_nano: OpenAI GPT-4.1 Nano
69
84
  open_ai_gpt_4o: OpenAI GPT-4o
70
85
  open_ai_gpt_4o_mini: OpenAI GPT-4o Mini
86
+ open_ai_gpt_5: OpenAI GPT-5
87
+ open_ai_gpt_5_1: OpenAI GPT-5.1
88
+ open_ai_gpt_5_2: OpenAI GPT-5.2
89
+ open_ai_gpt_5_mini: OpenAI GPT-5 Mini
90
+ open_ai_gpt_5_nano: OpenAI GPT-5 Nano
71
91
  open_ai_o1: OpenAI o1
72
92
  open_ai_o1_mini: OpenAI o1 Mini
73
93
  open_ai_o3: OpenAI o3
@@ -79,6 +99,11 @@ en:
79
99
  open_ai_responses_gpt_4_1_nano: OpenAI GPT-4.1 Nano (Responses API)
80
100
  open_ai_responses_gpt_4o: OpenAI GPT-4o (Responses API)
81
101
  open_ai_responses_gpt_4o_mini: OpenAI GPT-4o Mini (Responses API)
102
+ open_ai_responses_gpt_5: OpenAI GPT-5 (Responses API)
103
+ open_ai_responses_gpt_5_1: OpenAI GPT-5.1 (Responses API)
104
+ open_ai_responses_gpt_5_2: OpenAI GPT-5.2 (Responses API)
105
+ open_ai_responses_gpt_5_mini: OpenAI GPT-5 Mini (Responses API)
106
+ open_ai_responses_gpt_5_nano: OpenAI GPT-5 Nano (Responses API)
82
107
  open_ai_responses_o1: OpenAI o1 (Responses API)
83
108
  open_ai_responses_o1_mini: OpenAI o1 Mini (Responses API)
84
109
  open_ai_responses_o1_pro: OpenAI o1 Pro (Responses API)
@@ -88,9 +113,21 @@ en:
88
113
  open_ai_responses_o4_mini: OpenAI o4 Mini (Responses API)
89
114
  open_router_claude_3_7_sonnet: Anthropic Claude 3.7 Sonnet (via OpenRouter)
90
115
  open_router_deepseek_chat_v3: DeepSeek Chat v3 (via OpenRouter)
116
+ open_router_deepseek_v3_1: DeepSeek v3.1 (via OpenRouter)
91
117
  open_router_gemini_2_0_flash: Google Gemini 2.0 Flash (via OpenRouter)
118
+ open_router_gemini_2_5_flash: Gemini 2.5 Flash (via OpenRouter)
119
+ open_router_gemini_2_5_pro: Gemini 2.5 Pro (via OpenRouter)
120
+ open_router_gemini_3_pro_preview: Gemini 3 Pro Preview (via OpenRouter)
121
+ open_router_grok_4: Grok 4 (via OpenRouter)
122
+ open_router_grok_4_1_fast: Grok 4.1 Fast (via OpenRouter)
123
+ open_router_kimi_k2_thinking: Kimi K2 Thinking (via OpenRouter)
92
124
  open_router_llama_3_1_8b_instruct: Meta Llama 3.1 8B Instruct (via OpenRouter)
93
125
  open_router_llama_3_3_70b_instruct: Meta Llama 3.3 70B Instruct (via OpenRouter)
94
126
  open_router_llama_4_maverick: Meta Llama 4 Maverick (via OpenRouter)
95
127
  open_router_llama_4_scout: Meta Llama 4 Scout (via OpenRouter)
128
+ open_router_minimax_m2: Minimax M2 (via OpenRouter)
129
+ open_router_mistral_large_3_2512: Mistral Large 3 (via OpenRouter)
130
+ open_router_mistral_small_3_2_24b: Mistral Small 3.2 24B (via OpenRouter)
131
+ open_router_open_ai_gpt_oss_120b: OpenAI GPT-OSS 120B (via OpenRouter)
132
+ open_router_open_ai_gpt_oss_20b: OpenAI GPT-OSS 20B (via OpenRouter)
96
133
  raif_test_llm: Raif Test LLM
data/config/routes.rb CHANGED
@@ -17,6 +17,7 @@ Raif::Engine.routes.draw do
17
17
 
18
18
  namespace :stats do
19
19
  resources :tasks, only: [:index]
20
+ resources :model_tool_invocations, only: [:index]
20
21
  end
21
22
 
22
23
  resources :tasks, only: [:index, :show]
@@ -24,5 +25,6 @@ Raif::Engine.routes.draw do
24
25
  resources :model_completions, only: [:index, :show]
25
26
  resources :agents, only: [:index, :show]
26
27
  resources :model_tool_invocations, only: [:index, :show]
28
+ resource :config, only: [:show]
27
29
  end
28
30
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddTaskRunArgsToRaifTasks < ActiveRecord::Migration[7.1]
4
+ def change
5
+ json_column_type = if connection.adapter_name.downcase.include?("postgresql")
6
+ :jsonb
7
+ else
8
+ :json
9
+ end
10
+
11
+ add_column :raif_tasks, :task_run_args, json_column_type
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MakeRaifTaskCreatorOptional < ActiveRecord::Migration[7.1]
4
+ def change
5
+ change_column_null :raif_tasks, :creator_id, true
6
+ change_column_null :raif_tasks, :creator_type, true
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddGeneratingEntryResponseToRaifConversations < ActiveRecord::Migration[7.1]
4
+ def change
5
+ add_column :raif_conversations, :generating_entry_response, :boolean, default: false, null: false
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddSourceToRaifTasks < ActiveRecord::Migration[7.1]
4
+ def change
5
+ add_reference :raif_tasks, :source, polymorphic: true, index: true
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddSourceToRaifAgents < ActiveRecord::Migration[7.1]
4
+ def change
5
+ add_reference :raif_agents, :source, polymorphic: true, index: true
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RenameTaskRunArgsToRunWith < ActiveRecord::Migration[7.1]
4
+ def change
5
+ rename_column :raif_tasks, :task_run_args, :run_with
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddRunWithToRaifAgents < ActiveRecord::Migration[7.1]
4
+ def change
5
+ json_column_type = if connection.adapter_name.downcase.include?("postgresql")
6
+ :jsonb
7
+ else
8
+ :json
9
+ end
10
+
11
+ add_column :raif_agents, :run_with, json_column_type
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddLlmMessagesMaxLengthToRaifConversations < ActiveRecord::Migration[7.1]
4
+ def change
5
+ add_column :raif_conversations, :llm_messages_max_length, :integer
6
+
7
+ reversible do |dir|
8
+ dir.up do
9
+ # Set default value for existing conversations
10
+ execute "UPDATE raif_conversations SET llm_messages_max_length = 50 WHERE llm_messages_max_length IS NULL"
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddProviderToolCallIdToRaifModelToolInvocations < ActiveRecord::Migration[7.2]
4
+ def change
5
+ add_column :raif_model_tool_invocations, :provider_tool_call_id, :string
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddToolChoiceToRaifModelCompletions < ActiveRecord::Migration[7.2]
4
+ def change
5
+ add_column :raif_model_completions, :tool_choice, :string
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddSourceToRaifConversations < ActiveRecord::Migration[7.1]
4
+ def change
5
+ add_reference :raif_conversations, :source, polymorphic: true, index: true
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddFailureTrackingToRaifModelCompletions < ActiveRecord::Migration[7.1]
4
+ def change
5
+ add_column :raif_model_completions, :failed_at, :datetime
6
+ add_column :raif_model_completions, :failure_error, :string
7
+ add_column :raif_model_completions, :failure_reason, :text
8
+ add_index :raif_model_completions, :failed_at
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddCompletedAtToRaifModelCompletions < ActiveRecord::Migration[7.1]
4
+ def change
5
+ add_column :raif_model_completions, :completed_at, :datetime
6
+ add_index :raif_model_completions, :completed_at
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddStartedAtToRaifModelCompletions < ActiveRecord::Migration[7.1]
4
+ def change
5
+ add_column :raif_model_completions, :started_at, :datetime
6
+ add_index :raif_model_completions, :started_at
7
+ end
8
+ end
data/exe/raif ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/raif/cli"
5
+
6
+ # Run the CLI
7
+ Raif::CLI::Runner.new(ARGV).run
@@ -1,32 +1,47 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../base_generator"
4
+
3
5
  module Raif
4
6
  module Generators
5
- class AgentGenerator < Rails::Generators::NamedBase
7
+ class AgentGenerator < BaseGenerator
6
8
  source_root File.expand_path("templates", __dir__)
7
9
  desc "Creates a new Raif::Agent subclass in app/models/raif/agents"
8
10
 
11
+ class_option :skip_eval_set,
12
+ type: :boolean,
13
+ default: false,
14
+ desc: "Skip generating the corresponding eval set"
15
+
9
16
  def create_application_agent
10
17
  template "application_agent.rb.tt", "app/models/raif/application_agent.rb" unless File.exist?("app/models/raif/application_agent.rb")
11
18
  end
12
19
 
13
20
  def create_agent
14
- template "agent.rb.tt", "app/models/raif/agents/#{file_name}.rb"
21
+ template "agent.rb.tt", File.join("app/models/raif/agents", class_path, "#{file_name}.rb")
15
22
  end
16
23
 
17
24
  def create_directory
18
25
  empty_directory "app/models/raif/agents" unless File.directory?("app/models/raif/agents")
19
26
  end
20
27
 
21
- private
28
+ def create_eval_set
29
+ return if options[:skip_eval_set]
30
+
31
+ template "agent_eval_set.rb.tt", eval_set_file_path
32
+ end
22
33
 
23
- def class_name
24
- name.classify
34
+ def show_instructions
35
+ say "\nAgent created!"
36
+ say ""
25
37
  end
26
38
 
27
- def file_name
28
- name.underscore
39
+ private
40
+
41
+ def eval_set_file_path
42
+ File.join("raif_evals", "eval_sets", "agents", class_path, "#{file_name}_eval_set.rb")
29
43
  end
44
+
30
45
  end
31
46
  end
32
47
  end
@@ -1,28 +1,24 @@
1
- # frozen_string_literal: true
1
+ <% raif_module_namespacing(["Agents"]) do -%>
2
+ class <%= class_name.demodulize %> < Raif::ApplicationAgent
3
+ # If you want to always include a certain set of model tools with this agent type,
4
+ # uncomment this callback to populate the available_model_tools attribute with your desired model tools.
5
+ # def populate_default_model_tools
6
+ # self.available_model_tools = [
7
+ # Raif::ModelTools::WikipediaSearch,
8
+ # Raif::ModelTools::FetchUrl
9
+ # ]
10
+ # end
2
11
 
3
- module Raif
4
- module Agents
5
- class <%= class_name %> < Raif::ApplicationAgent
6
- # If you want to always include a certain set of model tools with this agent type,
7
- # uncomment this callback to populate the available_model_tools attribute with your desired model tools.
8
- # def populate_default_model_tools
9
- # self.available_model_tools ||= [
10
- # Raif::ModelTools::WikipediaSearchTool,
11
- # Raif::ModelTools::FetchUrlTool
12
- # ]
13
- # end
14
-
15
- # Enter your agent's system prompt here. Alternatively, you can change your agent's superclass
16
- # to an existing agent types (like Raif::Agents::ReActAgent) to utilize an existing system prompt.
17
- def build_system_prompt
18
- # TODO: Implement your system prompt here
19
- end
12
+ # Enter your agent's system prompt here. Alternatively, you can change your agent's superclass
13
+ # to an existing agent types (like Raif::Agents::NativeToolCallingAgent) to utilize an existing system prompt.
14
+ def build_system_prompt
15
+ # TODO: Implement your system prompt here
16
+ end
20
17
 
21
- # Each iteration of the agent loop will generate a new Raif::ModelCompletion record and
22
- # then call this method with it as an argument.
23
- def process_iteration_model_completion(model_completion)
24
- # TODO: Implement your iteration processing here
25
- end
18
+ # Each iteration of the agent loop will generate a new Raif::ModelCompletion record and
19
+ # then call this method with it as an argument.
20
+ def process_iteration_model_completion(model_completion)
21
+ # TODO: Implement your iteration processing here
26
22
  end
27
23
  end
28
- end
24
+ <% end -%>
@@ -0,0 +1,48 @@
1
+ <% raif_module_namespacing(["Evals", "Agents"]) do -%>
2
+ class <%= class_name.demodulize %>EvalSet < Raif::Evals::EvalSet
3
+ # Run this eval set with:
4
+ # bundle exec raif evals ./<%= eval_set_file_path %>
5
+
6
+ # Setup method runs before each eval
7
+ setup do
8
+ # Common setup code
9
+ # @user = User.create!(email: "test@example.com")
10
+ end
11
+
12
+ # Teardown runs after each eval
13
+ teardown do
14
+ # Cleanup code
15
+ end
16
+
17
+ eval "<%= class_name %> completes task successfully" do
18
+ # agent = Raif::Agents::<%= class_name %>.create!(
19
+ # creator: @user,
20
+ # task: "Your specific task here",
21
+ # available_model_tools: [] # Add your tools here if needed
22
+ # )
23
+
24
+ # agent.run!
25
+
26
+ # expect "agent completes successfully" do
27
+ # agent.completed?
28
+ # end
29
+
30
+ # expect "produces expected output" do
31
+ # agent.final_answer.include?("expected content")
32
+ # end
33
+ end
34
+
35
+ eval "<%= class_name %> uses tools correctly" do
36
+ # agent = Raif::Agents::<%= class_name %>.create!(
37
+ # creator: @user,
38
+ # task: "A task that requires tool usage",
39
+ # available_model_tools: ["expected_tool_name"]
40
+ # )
41
+
42
+ # agent.run!
43
+
44
+ # expect_tool_invocation(agent, "expected_tool_name")
45
+ end
46
+
47
+ end
48
+ <% end -%>
@@ -1,7 +1,5 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Raif
4
- class ApplicationAgent < Raif::Agent
2
+ class ApplicationAgent < Raif::Agents::NativeToolCallingAgent
5
3
  # Add any shared agent behavior here
6
4
  end
7
5
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Raif
4
+ class BaseGenerator < Rails::Generators::NamedBase
5
+ private
6
+
7
+ def raif_module_namespacing(intermediate_modules = [], &block)
8
+ content = capture(&block).rstrip
9
+
10
+ modules_names = intermediate_modules + class_path.map(&:camelize)
11
+ modules_names.reverse.each do |module_name|
12
+ content = indent "module #{module_name}\n#{content}\nend", 2
13
+ end
14
+
15
+ concat("module Raif\n#{content}\nend\n")
16
+ end
17
+
18
+ end
19
+ end
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../base_generator"
4
+
3
5
  module Raif
4
6
  module Generators
5
- class ConversationGenerator < Rails::Generators::NamedBase
7
+ class ConversationGenerator < BaseGenerator
6
8
  source_root File.expand_path("templates", __dir__)
7
9
 
8
10
  desc "Creates a new conversation type in the app/models/raif/conversations directory"
@@ -12,19 +14,30 @@ module Raif
12
14
  default: "text",
13
15
  desc: "Response format for the task (text, html, or json)"
14
16
 
17
+ class_option :skip_eval_set,
18
+ type: :boolean,
19
+ default: false,
20
+ desc: "Skip generating the corresponding eval set"
21
+
15
22
  def create_application_conversation
16
23
  template "application_conversation.rb.tt",
17
24
  "app/models/raif/application_conversation.rb" unless File.exist?("app/models/raif/application_conversation.rb")
18
25
  end
19
26
 
20
27
  def create_conversation_file
21
- template "conversation.rb.tt", File.join("app/models/raif/conversations", "#{file_name}.rb")
28
+ template "conversation.rb.tt", File.join("app/models/raif/conversations", class_path, "#{file_name}.rb")
22
29
  end
23
30
 
24
31
  def create_directory
25
32
  empty_directory "app/models/raif/conversations" unless File.directory?("app/models/raif/conversations")
26
33
  end
27
34
 
35
+ def create_eval_set
36
+ return if options[:skip_eval_set]
37
+
38
+ template "conversation_eval_set.rb.tt", eval_set_file_path
39
+ end
40
+
28
41
  def success_message
29
42
  say_status :success, "Conversation type created successfully", :green
30
43
  say "\nYou can now implement your conversation type in:"
@@ -32,6 +45,12 @@ module Raif
32
45
  say "\nDon't forget to add it to the config.conversation_types in your Raif configuration"
33
46
  say "For example: config.conversation_types += ['Raif::Conversations::#{class_name}']\n\n"
34
47
  end
48
+
49
+ private
50
+
51
+ def eval_set_file_path
52
+ File.join("raif_evals", "eval_sets", "conversations", class_path, "#{file_name}_eval_set.rb")
53
+ end
35
54
  end
36
55
  end
37
56
  end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Raif
4
2
  class ApplicationConversation < Raif::Conversation
5
3
  # Add any shared conversation behavior here
@@ -1,39 +1,41 @@
1
- # frozen_string_literal: true
1
+ <% raif_module_namespacing(["Conversations"]) do -%>
2
+ class <%= class_name.demodulize %> < Raif::ApplicationConversation
3
+ # Set the response format for the conversation. Options are :html, :text, or :json.
4
+ # If you set this to something other than :text, make sure to include instructions to the model in your system prompt
5
+ llm_response_format :<%= options[:response_format] %>
2
6
 
3
- module Raif
4
- module Conversations
5
- class <%= class_name %> < Raif::ApplicationConversation
6
- # Set the response format for the task. Options are :html, :text, or :json.
7
- # If you set this to something other than :text, make sure to include instructions to the model in your system prompt
8
- llm_response_format :<%= options[:response_format] %>
7
+ # If you want to always include a certain set of model tools with this conversation type,
8
+ # uncomment this callback to populate the available_model_tools attribute with your desired model tools.
9
+ # before_create -> { self.available_model_tools = ["Raif::ModelTools::Example"] }
9
10
 
10
- # If you want to always include a certain set of model tools with this conversation type,
11
- # uncomment this callback to populate the available_model_tools attribute with your desired model tools.
12
- # before_create -> { self.available_model_tools = ["Raif::ModelTools::Example"] }
11
+ # Use this callback to run custom logic before prompting the model for an entry response.
12
+ # The block receives the entry as an argument, and `self` is the conversation instance.
13
+ # before_prompt_model_for_entry_response do |entry|
14
+ # # Custom logic here
15
+ # end
13
16
 
14
- # Override the methods below to customize the system prompt for this conversation type.
15
- # def system_prompt_intro
16
- # Raif.config.conversation_system_prompt_intro
17
- # end
17
+ # Override the methods below to customize the system prompt for this conversation type.
18
+ # def system_prompt_intro
19
+ # Raif.config.conversation_system_prompt_intro
20
+ # end
18
21
 
19
- # def build_system_prompt
20
- # <<~PROMPT
21
- # #{system_prompt_intro}
22
- # #{system_prompt_language_preference}
23
- # PROMPT
24
- # end
22
+ # def build_system_prompt
23
+ # <<~PROMPT
24
+ # #{system_prompt_intro}
25
+ # #{system_prompt_language_preference}
26
+ # PROMPT
27
+ # end
25
28
 
26
- # Override this method to set the initial message shown to the user.
27
- # def initial_chat_message
28
- # I18n.t("#{self.class.name.underscore.gsub("/", ".")}.initial_chat_message")
29
- # end
29
+ # Override this method to set the initial message shown to the user.
30
+ # def initial_chat_message
31
+ # I18n.t("#{self.class.name.underscore.gsub("/", ".")}.initial_chat_message")
32
+ # end
30
33
 
31
- # This method will be called when receing a model response to a Raif::ConversationEntry
32
- # By default, it just passes the model response message through, but you can override
33
- # for custom response message processing
34
- # def process_model_response_message(message:, entry:)
35
- # message
36
- # end
37
- end
34
+ # This method will be called when receing a model response to a Raif::ConversationEntry
35
+ # By default, it just passes the model response message through, but you can override
36
+ # for custom response message processing
37
+ # def process_model_response_message(message:, entry:)
38
+ # message
39
+ # end
38
40
  end
39
- end
41
+ <% end -%>
@@ -0,0 +1,70 @@
1
+ <% raif_module_namespacing(["Evals", "Conversations"]) do -%>
2
+ class <%= class_name.demodulize %>EvalSet < Raif::Evals::EvalSet
3
+ # Run this eval set with:
4
+ # bundle exec raif evals ./<%= eval_set_file_path %>
5
+
6
+ # Setup method runs before each eval
7
+ setup do
8
+ # Common setup code
9
+ # @user = User.create!(email: "test@example.com")
10
+ # @conversation = Raif::Conversations::<%= class_name %>.create!(creator: @user)
11
+ end
12
+
13
+ # Teardown runs after each eval
14
+ teardown do
15
+ # Cleanup code
16
+ end
17
+
18
+ eval "<%= class_name %> responds appropriately to user greeting" do
19
+ # entry = @conversation.entries.create!(
20
+ # user_message: "Hello, how are you?",
21
+ # creator: @user
22
+ # )
23
+
24
+ # entry.process_entry!
25
+
26
+ # expect "generates a response" do
27
+ # entry.model_response_message.present?
28
+ # end
29
+
30
+ # expect "response is friendly" do
31
+ # entry.model_response_message.match?(/hello|hi|greetings/i)
32
+ # end
33
+ end
34
+
35
+ eval "<%= class_name %> maintains conversation context" do
36
+ # First message establishes context
37
+ # first_entry = @conversation.entries.create!(
38
+ # user_message: "My name is Alice",
39
+ # creator: @user
40
+ # )
41
+ # first_entry.process_entry!
42
+
43
+ # Second message references context
44
+ # second_entry = @conversation.entries.create!(
45
+ # user_message: "What's my name?",
46
+ # creator: @user
47
+ # )
48
+ # second_entry.process_entry!
49
+
50
+ # expect "remembers the user's name" do
51
+ # second_entry.model_response_message.include?("Alice")
52
+ # end
53
+ end
54
+
55
+ eval "<%= class_name %> handles tool invocations correctly" do
56
+ # Test if your conversation uses tools
57
+ # @conversation.update!(available_model_tools: [ "Raif::ModelTools::FetchUrl" ])
58
+
59
+ # entry = @conversation.entries.create!(
60
+ # user_message: "What can you tell me about the content of https://en.wikipedia.org/wiki/Moon",
61
+ # creator: @user
62
+ # )
63
+
64
+ # entry.process_entry!
65
+
66
+ # expect_tool_invocation(entry, "Raif::ModelTools::FetchUrl", with: { url: "https://en.wikipedia.org/wiki/Moon" })
67
+ end
68
+
69
+ end
70
+ <% end -%>