raif 1.4.0 → 1.5.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 (137) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/app/assets/builds/raif_admin.css +40 -2
  4. data/app/assets/builds/raif_admin_sprockets.js +2709 -0
  5. data/app/assets/javascript/raif/admin/copy_to_clipboard_controller.js +132 -0
  6. data/app/assets/javascript/raif/admin/cost_estimate_controller.js +80 -0
  7. data/app/assets/javascript/raif/admin/judge_config_controller.js +23 -0
  8. data/app/assets/javascript/raif/admin/select_all_checkboxes_controller.js +33 -0
  9. data/app/assets/javascript/raif/admin/sortable_table_controller.js +51 -0
  10. data/app/assets/javascript/raif/admin/table_search_controller.js +15 -0
  11. data/app/assets/javascript/raif/admin/tom_select_controller.js +33 -0
  12. data/app/assets/javascript/raif_admin.js +23 -0
  13. data/app/assets/javascript/raif_admin_sprockets.js +24 -0
  14. data/app/assets/stylesheets/raif_admin.scss +50 -1
  15. data/app/controllers/raif/admin/agents_controller.rb +27 -1
  16. data/app/controllers/raif/admin/configs_controller.rb +1 -0
  17. data/app/controllers/raif/admin/llms_controller.rb +27 -0
  18. data/app/controllers/raif/admin/model_completions_controller.rb +6 -0
  19. data/app/controllers/raif/admin/prompt_studio/agents_controller.rb +25 -0
  20. data/app/controllers/raif/admin/prompt_studio/base_controller.rb +32 -0
  21. data/app/controllers/raif/admin/prompt_studio/batch_runs_controller.rb +102 -0
  22. data/app/controllers/raif/admin/prompt_studio/conversations_controller.rb +25 -0
  23. data/app/controllers/raif/admin/prompt_studio/tasks_controller.rb +64 -0
  24. data/app/controllers/raif/admin/tasks_controller.rb +5 -0
  25. data/app/helpers/raif/application_helper.rb +40 -0
  26. data/app/jobs/raif/prompt_studio_batch_run_item_job.rb +11 -0
  27. data/app/jobs/raif/prompt_studio_batch_run_job.rb +15 -0
  28. data/app/jobs/raif/prompt_studio_task_run_job.rb +36 -0
  29. data/app/models/raif/agent.rb +36 -5
  30. data/app/models/raif/agents/native_tool_calling_agent.rb +101 -19
  31. data/app/models/raif/concerns/has_prompt_templates.rb +88 -0
  32. data/app/models/raif/concerns/has_runtime_duration.rb +41 -0
  33. data/app/models/raif/concerns/json_schema_definition.rb +16 -3
  34. data/app/models/raif/concerns/llm_prompt_caching.rb +20 -0
  35. data/app/models/raif/concerns/llms/anthropic/message_formatting.rb +6 -0
  36. data/app/models/raif/concerns/llms/anthropic/tool_formatting.rb +5 -1
  37. data/app/models/raif/concerns/llms/bedrock/message_formatting.rb +7 -0
  38. data/app/models/raif/concerns/llms/bedrock/tool_formatting.rb +4 -0
  39. data/app/models/raif/concerns/llms/google/message_formatting.rb +5 -2
  40. data/app/models/raif/concerns/llms/google/tool_formatting.rb +4 -0
  41. data/app/models/raif/concerns/llms/message_formatting.rb +30 -0
  42. data/app/models/raif/concerns/llms/open_ai_completions/response_tool_calls.rb +1 -1
  43. data/app/models/raif/concerns/llms/open_ai_completions/tool_formatting.rb +4 -0
  44. data/app/models/raif/concerns/llms/open_ai_responses/tool_formatting.rb +4 -0
  45. data/app/models/raif/concerns/provider_managed_tool_calls.rb +162 -0
  46. data/app/models/raif/conversation.rb +24 -3
  47. data/app/models/raif/conversation_entry.rb +6 -3
  48. data/app/models/raif/embedding_models/bedrock.rb +10 -1
  49. data/app/models/raif/embedding_models/google.rb +37 -0
  50. data/app/models/raif/evals/llm_judge.rb +70 -0
  51. data/{lib → app/models}/raif/evals/llm_judges/binary.rb +38 -0
  52. data/{lib → app/models}/raif/evals/llm_judges/comparative.rb +38 -0
  53. data/{lib → app/models}/raif/evals/llm_judges/scored.rb +38 -0
  54. data/{lib → app/models}/raif/evals/llm_judges/summarization.rb +38 -0
  55. data/app/models/raif/llm.rb +82 -7
  56. data/app/models/raif/llms/anthropic.rb +26 -4
  57. data/app/models/raif/llms/bedrock.rb +59 -5
  58. data/app/models/raif/llms/google.rb +28 -2
  59. data/app/models/raif/llms/open_ai_base.rb +4 -0
  60. data/app/models/raif/llms/open_ai_completions.rb +9 -2
  61. data/app/models/raif/llms/open_ai_responses.rb +9 -2
  62. data/app/models/raif/llms/open_router.rb +10 -3
  63. data/app/models/raif/model_completion.rb +75 -34
  64. data/app/models/raif/model_tool.rb +45 -3
  65. data/app/models/raif/model_tool_invocation.rb +31 -1
  66. data/app/models/raif/prompt_studio_batch_run.rb +155 -0
  67. data/app/models/raif/prompt_studio_batch_run_item.rb +220 -0
  68. data/app/models/raif/streaming_responses/bedrock.rb +60 -1
  69. data/app/models/raif/task.rb +30 -6
  70. data/app/views/layouts/raif/admin.html.erb +31 -1
  71. data/app/views/raif/admin/agents/_agent.html.erb +1 -0
  72. data/app/views/raif/admin/agents/index.html.erb +48 -0
  73. data/app/views/raif/admin/agents/show.html.erb +4 -0
  74. data/app/views/raif/admin/llms/index.html.erb +110 -0
  75. data/app/views/raif/admin/model_completions/_model_completion.html.erb +3 -7
  76. data/app/views/raif/admin/model_completions/index.html.erb +14 -1
  77. data/app/views/raif/admin/model_completions/show.html.erb +164 -55
  78. data/app/views/raif/admin/model_tool_invocations/index.html.erb +1 -1
  79. data/app/views/raif/admin/model_tool_invocations/show.html.erb +18 -0
  80. data/app/views/raif/admin/prompt_studio/agents/index.html.erb +56 -0
  81. data/app/views/raif/admin/prompt_studio/agents/show.html.erb +57 -0
  82. data/app/views/raif/admin/prompt_studio/batch_runs/_batch_run_item.html.erb +54 -0
  83. data/app/views/raif/admin/prompt_studio/batch_runs/_judge_config_fields.html.erb +76 -0
  84. data/app/views/raif/admin/prompt_studio/batch_runs/_judge_detail_modal.html.erb +27 -0
  85. data/app/views/raif/admin/prompt_studio/batch_runs/_modal.html.erb +35 -0
  86. data/app/views/raif/admin/prompt_studio/batch_runs/_progress.html.erb +78 -0
  87. data/app/views/raif/admin/prompt_studio/batch_runs/show.html.erb +49 -0
  88. data/app/views/raif/admin/prompt_studio/conversations/index.html.erb +48 -0
  89. data/app/views/raif/admin/prompt_studio/conversations/show.html.erb +36 -0
  90. data/app/views/raif/admin/prompt_studio/shared/_nav_tabs.html.erb +17 -0
  91. data/app/views/raif/admin/prompt_studio/shared/_prompt_comparison.html.erb +87 -0
  92. data/app/views/raif/admin/prompt_studio/shared/_type_filter.html.erb +54 -0
  93. data/app/views/raif/admin/prompt_studio/tasks/_task_result.html.erb +145 -0
  94. data/app/views/raif/admin/prompt_studio/tasks/_task_row.html.erb +12 -0
  95. data/app/views/raif/admin/prompt_studio/tasks/_task_type_filter.html.erb +58 -0
  96. data/app/views/raif/admin/prompt_studio/tasks/_tasks_table.html.erb +22 -0
  97. data/app/views/raif/admin/prompt_studio/tasks/index.html.erb +35 -0
  98. data/app/views/raif/admin/prompt_studio/tasks/show.html.erb +19 -0
  99. data/app/views/raif/admin/tasks/_task.html.erb +1 -0
  100. data/app/views/raif/admin/tasks/index.html.erb +17 -5
  101. data/app/views/raif/admin/tasks/show.html.erb +20 -0
  102. data/app/views/raif/conversation_entries/_message.html.erb +10 -6
  103. data/config/importmap.rb +8 -0
  104. data/config/locales/admin.en.yml +128 -0
  105. data/config/locales/en.yml +36 -2
  106. data/config/routes.rb +8 -0
  107. data/db/migrate/20260307000000_add_prompt_studio_run_to_raif_tasks.rb +7 -0
  108. data/db/migrate/20260308000000_create_raif_prompt_studio_batch_runs.rb +27 -0
  109. data/db/migrate/20260308000001_create_raif_prompt_studio_batch_run_items.rb +24 -0
  110. data/db/migrate/20260407000000_add_cache_token_columns_to_raif_model_completions.rb +8 -0
  111. data/lib/generators/raif/agent/agent_generator.rb +18 -0
  112. data/lib/generators/raif/agent/templates/agent.rb.tt +7 -5
  113. data/lib/generators/raif/agent/templates/system_prompt.erb.tt +3 -0
  114. data/lib/generators/raif/conversation/conversation_generator.rb +19 -1
  115. data/lib/generators/raif/conversation/templates/system_prompt.erb.tt +4 -0
  116. data/lib/generators/raif/install/templates/initializer.rb +68 -27
  117. data/lib/generators/raif/task/task_generator.rb +18 -0
  118. data/lib/generators/raif/task/templates/prompt.erb.tt +4 -0
  119. data/lib/generators/raif/task/templates/task.rb.tt +9 -8
  120. data/lib/raif/configuration.rb +10 -0
  121. data/lib/raif/embedding_model_registry.rb +8 -0
  122. data/lib/raif/engine.rb +16 -1
  123. data/lib/raif/errors/blank_response_error.rb +8 -0
  124. data/lib/raif/errors/prompt_template_error.rb +15 -0
  125. data/lib/raif/errors.rb +2 -0
  126. data/lib/raif/evals.rb +0 -6
  127. data/lib/raif/llm_registry.rb +230 -9
  128. data/lib/raif/prompt_studio_comparison_builder.rb +138 -0
  129. data/lib/raif/token_estimator.rb +28 -0
  130. data/lib/raif/version.rb +1 -1
  131. data/lib/raif.rb +2 -0
  132. data/spec/support/rspec_helpers.rb +7 -1
  133. data/spec/support/test_task.rb +9 -0
  134. data/spec/support/test_template_task.rb +41 -0
  135. metadata +65 -7
  136. data/lib/raif/evals/llm_judge.rb +0 -32
  137. /data/{lib → app/models}/raif/evals/scoring_rubric.rb +0 -0
@@ -19,6 +19,11 @@ module Raif
19
19
  default: false,
20
20
  desc: "Skip generating the corresponding eval set"
21
21
 
22
+ class_option :skip_prompt_template,
23
+ type: :boolean,
24
+ default: false,
25
+ desc: "Skip generating the system prompt template file"
26
+
22
27
  def create_application_conversation
23
28
  template "application_conversation.rb.tt",
24
29
  "app/models/raif/application_conversation.rb" unless File.exist?("app/models/raif/application_conversation.rb")
@@ -28,6 +33,12 @@ module Raif
28
33
  template "conversation.rb.tt", File.join("app/models/raif/conversations", class_path, "#{file_name}.rb")
29
34
  end
30
35
 
36
+ def create_system_prompt_template
37
+ return if options[:skip_prompt_template]
38
+
39
+ template "system_prompt.erb.tt", system_prompt_template_file_path
40
+ end
41
+
31
42
  def create_directory
32
43
  empty_directory "app/models/raif/conversations" unless File.directory?("app/models/raif/conversations")
33
44
  end
@@ -41,7 +52,10 @@ module Raif
41
52
  def success_message
42
53
  say_status :success, "Conversation type created successfully", :green
43
54
  say "\nYou can now implement your conversation type in:"
44
- say " app/models/raif/conversations/#{file_name}.rb\n\n"
55
+ say " app/models/raif/conversations/#{file_name}.rb"
56
+ unless options[:skip_prompt_template]
57
+ say " System prompt template: #{system_prompt_template_file_path}"
58
+ end
45
59
  say "\nDon't forget to add it to the config.conversation_types in your Raif configuration"
46
60
  say "For example: config.conversation_types += ['Raif::Conversations::#{class_name}']\n\n"
47
61
  end
@@ -51,6 +65,10 @@ module Raif
51
65
  def eval_set_file_path
52
66
  File.join("raif_evals", "eval_sets", "conversations", class_path, "#{file_name}_eval_set.rb")
53
67
  end
68
+
69
+ def system_prompt_template_file_path
70
+ File.join("app/views/raif/conversations", class_path, "#{file_name}.system_prompt.erb")
71
+ end
54
72
  end
55
73
  end
56
74
  end
@@ -0,0 +1,4 @@
1
+ <%% # Define the system prompt for Raif::Conversations::<%= class_name %> here. %>
2
+ <%% # All instance methods are available in this template. %>
3
+ <%% # You can also use Rails view helpers and render partials. %>
4
+ <%% # If this file is removed, the default system prompt from Raif.config.conversation_system_prompt_intro will be used. %>
@@ -60,75 +60,115 @@ Raif.configure do |config|
60
60
  # config.google_api_key = ENV["GOOGLE_AI_API_KEY"].presence || ENV["GOOGLE_API_KEY"]
61
61
 
62
62
  # Whether Google models are enabled.
63
- # config.google_models_enabled = ENV["GOOGLE_API_KEY"].present?
63
+ # config.google_models_enabled = ENV["GOOGLE_AI_API_KEY"].present? || ENV["GOOGLE_API_KEY"].present?
64
+
65
+ # Whether Google embedding models are enabled. Defaults to false
66
+ # config.google_embedding_models_enabled = false
64
67
 
65
68
  # The default LLM model to use. Defaults to "open_ai_gpt_4o"
66
69
  # Available keys:
67
- # open_ai_gpt_4o_mini
68
- # open_ai_gpt_4o
69
- # open_ai_gpt_3_5_turbo
70
+ # open_ai_gpt_5_4
71
+ # open_ai_gpt_5_2
72
+ # open_ai_gpt_5_1
73
+ # open_ai_gpt_5
74
+ # open_ai_gpt_5_mini
75
+ # open_ai_gpt_5_nano
70
76
  # open_ai_gpt_4_1
71
77
  # open_ai_gpt_4_1_mini
72
78
  # open_ai_gpt_4_1_nano
73
- # open_ai_o1
74
- # open_ai_o1_mini
79
+ # open_ai_gpt_4o
80
+ # open_ai_gpt_4o_mini
81
+ # open_ai_gpt_3_5_turbo
82
+ # open_ai_o4_mini
75
83
  # open_ai_o3
76
84
  # open_ai_o3_mini
77
- # open_ai_o4_mini
78
- # open_ai_gpt_5
79
- # open_ai_gpt_5_mini
80
- # open_ai_gpt_5_nano
81
- # open_ai_responses_gpt_4o_mini
82
- # open_ai_responses_gpt_4o
83
- # open_ai_responses_gpt_3_5_turbo
85
+ # open_ai_o1
86
+ # open_ai_o1_mini
87
+ # open_ai_responses_gpt_5_4
88
+ # open_ai_responses_gpt_5_4_pro
89
+ # open_ai_responses_gpt_5_2
90
+ # open_ai_responses_gpt_5_2_pro
91
+ # open_ai_responses_gpt_5_1
92
+ # open_ai_responses_gpt_5
93
+ # open_ai_responses_gpt_5_pro
94
+ # open_ai_responses_gpt_5_mini
95
+ # open_ai_responses_gpt_5_nano
84
96
  # open_ai_responses_gpt_4_1
85
97
  # open_ai_responses_gpt_4_1_mini
86
98
  # open_ai_responses_gpt_4_1_nano
87
- # open_ai_responses_o1
88
- # open_ai_responses_o1_mini
99
+ # open_ai_responses_gpt_4o
100
+ # open_ai_responses_gpt_4o_mini
101
+ # open_ai_responses_gpt_3_5_turbo
102
+ # open_ai_responses_o4_mini
89
103
  # open_ai_responses_o3
104
+ # open_ai_responses_o3_pro
90
105
  # open_ai_responses_o3_mini
91
- # open_ai_responses_o4_mini
92
- # open_ai_responses_gpt_5
93
- # open_ai_responses_gpt_5_mini
94
- # open_ai_responses_gpt_5_nano
106
+ # open_ai_responses_o1
95
107
  # open_ai_responses_o1_pro
96
- # open_ai_responses_o3_pro
97
- # anthropic_claude_4_sonnet
108
+ # anthropic_claude_4_7_opus
109
+ # anthropic_claude_4_6_opus
110
+ # anthropic_claude_4_6_sonnet
111
+ # anthropic_claude_4_5_opus
98
112
  # anthropic_claude_4_5_sonnet
99
- # anthropic_claude_4_opus
113
+ # anthropic_claude_4_5_haiku
100
114
  # anthropic_claude_4_1_opus
115
+ # anthropic_claude_4_opus
116
+ # anthropic_claude_4_sonnet
101
117
  # anthropic_claude_3_7_sonnet
102
118
  # anthropic_claude_3_5_sonnet
103
119
  # anthropic_claude_3_5_haiku
104
120
  # anthropic_claude_3_opus
105
- # bedrock_claude_4_sonnet
121
+ # bedrock_claude_4_7_opus
122
+ # bedrock_claude_4_6_opus
123
+ # bedrock_claude_4_6_sonnet
124
+ # bedrock_claude_4_5_opus
106
125
  # bedrock_claude_4_5_sonnet
107
- # bedrock_claude_4_opus
126
+ # bedrock_claude_4_5_haiku
108
127
  # bedrock_claude_4_1_opus
109
- # bedrock_claude_3_5_sonnet
128
+ # bedrock_claude_4_opus
129
+ # bedrock_claude_4_sonnet
110
130
  # bedrock_claude_3_7_sonnet
131
+ # bedrock_claude_3_5_sonnet
111
132
  # bedrock_claude_3_5_haiku
112
133
  # bedrock_claude_3_opus
113
134
  # bedrock_amazon_nova_micro
114
135
  # bedrock_amazon_nova_lite
115
136
  # bedrock_amazon_nova_pro
137
+ # bedrock_deepseek_v3_2
138
+ # bedrock_deepseek_r1
139
+ # bedrock_gpt_oss_120b
140
+ # bedrock_gpt_oss_20b
116
141
  # open_router_claude_3_7_sonnet
117
142
  # open_router_deepseek_chat_v3
118
143
  # open_router_deepseek_v3_1
144
+ # open_router_deepseek_v3_2
119
145
  # open_router_gemini_2_0_flash
146
+ # open_router_gemini_2_5_flash
120
147
  # open_router_gemini_2_5_pro
148
+ # open_router_gemini_3_pro_preview
121
149
  # open_router_grok_4
150
+ # open_router_grok_4_1_fast
151
+ # open_router_kimi_k2_thinking
152
+ # open_router_kimi_k2_5
122
153
  # open_router_llama_3_1_8b_instruct
123
154
  # open_router_llama_3_3_70b_instruct
124
155
  # open_router_llama_4_maverick
125
156
  # open_router_llama_4_scout
157
+ # open_router_minimax_m2
158
+ # open_router_minimax_m2_1
159
+ # open_router_minimax_m2_5
160
+ # open_router_mistral_large_3_2512
161
+ # open_router_mistral_small_3_2_24b
162
+ # open_router_gemini_3_1_pro_preview
163
+ # open_router_gemini_3_1_flash_lite_preview
126
164
  # open_router_open_ai_gpt_oss_120b
127
165
  # open_router_open_ai_gpt_oss_20b
128
- # google_gemini_2_5_pro
129
- # google_gemini_2_5_flash
166
+ # google_gemini_3_1_pro
167
+ # google_gemini_3_1_flash_lite
130
168
  # google_gemini_3_0_pro
131
169
  # google_gemini_3_0_flash
170
+ # google_gemini_2_5_pro
171
+ # google_gemini_2_5_flash
132
172
  #
133
173
  # config.default_llm_model_key = "open_ai_gpt_4o"
134
174
 
@@ -139,6 +179,7 @@ Raif.configure do |config|
139
179
  # open_ai_text_embedding_3_large
140
180
  # open_ai_text_embedding_ada_002
141
181
  # bedrock_titan_embed_text_v2
182
+ # google_gemini_embedding_2
142
183
  #
143
184
  # config.default_embedding_model_key = "open_ai_text_embedding_3_small"
144
185
 
@@ -17,6 +17,11 @@ module Raif
17
17
  default: false,
18
18
  desc: "Skip generating the corresponding eval set"
19
19
 
20
+ class_option :skip_prompt_template,
21
+ type: :boolean,
22
+ default: false,
23
+ desc: "Skip generating the prompt template file"
24
+
20
25
  def create_application_task
21
26
  template "application_task.rb.tt", "app/models/raif/application_task.rb" unless File.exist?("app/models/raif/application_task.rb")
22
27
  end
@@ -25,6 +30,12 @@ module Raif
25
30
  template "task.rb.tt", File.join("app/models/raif/tasks", class_path, "#{file_name}.rb")
26
31
  end
27
32
 
33
+ def create_prompt_template
34
+ return if options[:skip_prompt_template]
35
+
36
+ template "prompt.erb.tt", prompt_template_file_path
37
+ end
38
+
28
39
  def create_eval_set
29
40
  return if options[:skip_eval_set]
30
41
 
@@ -33,6 +44,9 @@ module Raif
33
44
 
34
45
  def show_instructions
35
46
  say "\nTask created!"
47
+ unless options[:skip_prompt_template]
48
+ say " Prompt template: #{prompt_template_file_path}"
49
+ end
36
50
  say ""
37
51
  end
38
52
 
@@ -42,6 +56,10 @@ module Raif
42
56
  File.join("raif_evals", "eval_sets", "tasks", class_path, "#{file_name}_eval_set.rb")
43
57
  end
44
58
 
59
+ def prompt_template_file_path
60
+ File.join("app/views/raif/tasks", class_path, "#{file_name}.prompt.erb")
61
+ end
62
+
45
63
  end
46
64
  end
47
65
  end
@@ -0,0 +1,4 @@
1
+ <%% # Implement the LLM prompt for Raif::Tasks::<%= class_name %> here. %>
2
+ <%% # All instance methods and run_with attributes are available in this template. %>
3
+ <%% # You can also use Rails view helpers (content_tag, strip_tags, truncate, etc.) %>
4
+ <%% # and render partials with: <%%= render partial: "raif/shared/my_partial" %> %>
@@ -44,14 +44,15 @@
44
44
  end
45
45
  <%- end -%>
46
46
 
47
- def build_prompt
48
- # Implement the LLM prompt for this task.
49
- raise NotImplementedError, "Implement #build_prompt in #{self.class.name}"
50
- end
51
-
52
- # Optional: Override build_system_prompt if you need custom system instructions.
53
- # The default implementation, which you'll get if you call super, will use Raif.config.task_system_prompt_intro
54
- # and append the system_prompt_language_preference if the task's requested_language_key is set.
47
+ # Prompt is defined in app/views/raif/tasks/<%= class_path.any? ? class_path.join("/") + "/" : "" %><%= file_name %>.prompt.erb
48
+ # You can also define a system prompt template at:
49
+ # app/views/raif/tasks/<%= class_path.any? ? class_path.join("/") + "/" : "" %><%= file_name %>.system_prompt.erb
50
+ #
51
+ # Alternatively, override build_prompt and/or build_system_prompt methods directly:
52
+ # def build_prompt
53
+ # "Your prompt here"
54
+ # end
55
+ #
55
56
  # def build_system_prompt
56
57
  # super + "\nAdditional system instructions..."
57
58
  # end
@@ -22,6 +22,7 @@ module Raif
22
22
  :evals_default_llm_judge_model_key,
23
23
  :evals_verbose_output,
24
24
  :google_api_key,
25
+ :google_embedding_models_enabled,
25
26
  :google_models_enabled,
26
27
  :llm_api_requests_enabled,
27
28
  :llm_request_max_retries,
@@ -43,6 +44,7 @@ module Raif
43
44
  :request_write_timeout,
44
45
  :streaming_update_chunk_size_threshold,
45
46
  :task_creator_optional,
47
+ :prompt_studio_runs_enabled,
46
48
  :task_system_prompt_intro,
47
49
  :user_tool_types
48
50
 
@@ -75,6 +77,7 @@ module Raif
75
77
  @evals_verbose_output = false
76
78
  google_api_key = ENV["GOOGLE_AI_API_KEY"].presence || ENV["GOOGLE_API_KEY"]
77
79
  @google_api_key = default_disable_llm_api_requests? ? "placeholder-google-api-key" : google_api_key
80
+ @google_embedding_models_enabled = false
78
81
  @google_models_enabled = @google_api_key.present?
79
82
  @llm_api_requests_enabled = !default_disable_llm_api_requests?
80
83
  @llm_request_max_retries = 2
@@ -84,6 +87,7 @@ module Raif
84
87
  Faraday::ServerError,
85
88
  Net::ReadTimeout,
86
89
  Net::OpenTimeout,
90
+ Raif::Errors::BlankResponseError,
87
91
  ]
88
92
  @model_superclass = "ApplicationRecord"
89
93
  @open_ai_api_key = default_disable_llm_api_requests? ? "placeholder-open-ai-api-key" : ENV["OPENAI_API_KEY"]
@@ -96,6 +100,7 @@ module Raif
96
100
  open_router_api_key = ENV["OPEN_ROUTER_API_KEY"].presence || ENV["OPENROUTER_API_KEY"]
97
101
  @open_router_api_key = default_disable_llm_api_requests? ? "placeholder-open-router-api-key" : open_router_api_key
98
102
  @open_router_models_enabled = @open_router_api_key.present?
103
+ @prompt_studio_runs_enabled = Rails.env.development?
99
104
  @open_router_app_name = nil
100
105
  @open_router_site_url = nil
101
106
  @request_open_timeout = nil
@@ -176,6 +181,11 @@ module Raif
176
181
  raise Raif::Errors::InvalidConfigError,
177
182
  "Raif.config.google_api_key is required when Raif.config.google_models_enabled is true. Set it via Raif.config.google_api_key or ENV['GOOGLE_API_KEY']" # rubocop:disable Layout/LineLength
178
183
  end
184
+
185
+ if google_embedding_models_enabled && google_api_key.blank?
186
+ raise Raif::Errors::InvalidConfigError,
187
+ "Raif.config.google_api_key is required when Raif.config.google_embedding_models_enabled is true. Set it via Raif.config.google_api_key or ENV['GOOGLE_API_KEY']" # rubocop:disable Layout/LineLength
188
+ end
179
189
  end
180
190
 
181
191
  private
@@ -77,6 +77,14 @@ module Raif
77
77
  input_token_cost: 0.01 / 1_000_000,
78
78
  default_output_vector_size: 1024,
79
79
  },
80
+ ],
81
+ Raif::EmbeddingModels::Google => [
82
+ {
83
+ key: :google_gemini_embedding_2,
84
+ api_name: "gemini-embedding-2-preview",
85
+ input_token_cost: 0.20 / 1_000_000,
86
+ default_output_vector_size: 3072,
87
+ },
80
88
  ]
81
89
  }
82
90
  end
data/lib/raif/engine.rb CHANGED
@@ -9,6 +9,11 @@ module Raif
9
9
  class Engine < ::Rails::Engine
10
10
  isolate_namespace Raif
11
11
 
12
+ initializer "raif.prompt_template_formats", before: :add_view_paths do
13
+ Mime::Type.register "text/plain", :prompt unless Mime::Type.lookup_by_extension(:prompt)
14
+ Mime::Type.register "text/plain", :system_prompt unless Mime::Type.lookup_by_extension(:system_prompt)
15
+ end
16
+
12
17
  # If the host app is using FactoryBot, add the factories to the host app so they can be used in host apptests
13
18
  if defined?(FactoryBotRails)
14
19
  config.factory_bot.definition_file_paths += [File.expand_path("../../../spec/factories/shared", __FILE__)]
@@ -80,6 +85,14 @@ module Raif
80
85
  end
81
86
  end
82
87
 
88
+ config.after_initialize do
89
+ next unless Raif.config.google_embedding_models_enabled
90
+
91
+ Raif.default_embedding_models[Raif::EmbeddingModels::Google].each do |embedding_model_config|
92
+ Raif.register_embedding_model(Raif::EmbeddingModels::Google, **embedding_model_config)
93
+ end
94
+ end
95
+
83
96
  config.after_initialize do
84
97
  next unless Raif.config.bedrock_embedding_models_enabled
85
98
 
@@ -93,7 +106,8 @@ module Raif
93
106
  config.after_initialize do
94
107
  next unless Rails.env.test?
95
108
 
96
- Raif.config.conversation_types += ["Raif::TestConversation"]
109
+ Raif.config.conversation_types += ["Raif::TestConversation", "Raif::TestTemplateConversation"]
110
+ Raif.config.agent_types += ["Raif::TestTemplateAgent"]
97
111
 
98
112
  require "#{Raif::Engine.root}/spec/support/test_llm"
99
113
  Raif.register_llm(Raif::Llms::TestLlm, key: :raif_test_llm, api_name: "raif-test-llm")
@@ -128,6 +142,7 @@ module Raif
128
142
  Rails.application.config.assets.precompile += [
129
143
  "raif.js",
130
144
  "raif.css",
145
+ "raif_admin_sprockets.js",
131
146
  "raif_admin.css",
132
147
  "raif-logo-white.svg"
133
148
  ]
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Raif
4
+ module Errors
5
+ class BlankResponseError < StandardError
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Raif
4
+ module Errors
5
+ class PromptTemplateError < StandardError
6
+ attr_reader :template_path, :original_error
7
+
8
+ def initialize(template_path:, original_error:)
9
+ @template_path = template_path
10
+ @original_error = original_error
11
+ super("Error rendering prompt template '#{template_path}': #{original_error.class}: #{original_error.message}")
12
+ end
13
+ end
14
+ end
15
+ end
data/lib/raif/errors.rb CHANGED
@@ -10,3 +10,5 @@ require "raif/errors/invalid_model_file_input_error"
10
10
  require "raif/errors/unsupported_feature_error"
11
11
  require "raif/errors/streaming_error"
12
12
  require "raif/errors/instance_dependent_schema_error"
13
+ require "raif/errors/prompt_template_error"
14
+ require "raif/errors/blank_response_error"
data/lib/raif/evals.rb CHANGED
@@ -4,12 +4,6 @@ require "raif/evals/expectation_result"
4
4
  require "raif/evals/eval"
5
5
  require "raif/evals/eval_set"
6
6
  require "raif/evals/run"
7
- require "raif/evals/llm_judge"
8
- require "raif/evals/llm_judges/binary"
9
- require "raif/evals/llm_judges/comparative"
10
- require "raif/evals/llm_judges/scored"
11
- require "raif/evals/llm_judges/summarization"
12
- require "raif/evals/scoring_rubric"
13
7
 
14
8
  module Raif
15
9
  module Evals