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
@@ -58,6 +58,10 @@
58
58
  <div class="col-5"><strong><%= t("raif.admin.common.completed_at") %>:</strong></div>
59
59
  <div class="col-7"><small><%= @model_completion.completed_at&.rfc822 || "-" %></small></div>
60
60
  </div>
61
+ <div class="row mb-2">
62
+ <div class="col-5"><strong><%= t("raif.admin.common.duration") %>:</strong></div>
63
+ <div class="col-7"><small><%= @model_completion.runtime_duration %></small></div>
64
+ </div>
61
65
  <div class="row mb-2">
62
66
  <div class="col-5"><strong><%= t("raif.admin.common.prompt_tokens") %>:</strong></div>
63
67
  <div class="col-7">
@@ -97,71 +101,88 @@
97
101
  <% end %>
98
102
  </div>
99
103
  </div>
104
+ <hr class="my-2">
105
+ <div class="row mb-2">
106
+ <div class="col-5"><strong><%= t("raif.admin.common.cache_read_input_tokens") %>:</strong></div>
107
+ <div class="col-7"><%= @model_completion.cache_read_input_tokens ? number_with_delimiter(@model_completion.cache_read_input_tokens) : "-" %></div>
108
+ </div>
109
+ <div class="row mb-2">
110
+ <div class="col-5"><strong><%= t("raif.admin.common.cache_creation_input_tokens") %>:</strong></div>
111
+ <div class="col-7"><%= @model_completion.cache_creation_input_tokens ? number_with_delimiter(@model_completion.cache_creation_input_tokens) : "-" %></div>
112
+ </div>
100
113
  </div>
101
114
  </div>
102
115
  <% if @model_completion.failed? %>
103
- <hr>
104
- <div class="row mb-2">
105
- <div class="col-md-2"><strong><%= t("raif.admin.common.failed_at") %>:</strong></div>
106
- <div class="col-md-10"><small><%= @model_completion.failed_at&.rfc822 || "-" %></small></div>
107
- </div>
108
- <div class="row mb-2">
109
- <div class="col-md-2"><strong><%= t("raif.admin.common.failure_error") %>:</strong></div>
110
- <div class="col-md-10"><code><%= @model_completion.failure_error %></code></div>
111
- </div>
112
- <div class="row mb-2">
113
- <div class="col-md-2"><strong><%= t("raif.admin.common.failure_reason") %>:</strong></div>
114
- <div class="col-md-10"><%= @model_completion.failure_reason %></div>
115
- </div>
116
+ <hr>
117
+ <div class="row mb-2">
118
+ <div class="col-md-2"><strong><%= t("raif.admin.common.failed_at") %>:</strong></div>
119
+ <div class="col-md-10"><small><%= @model_completion.failed_at&.rfc822 || "-" %></small></div>
120
+ </div>
121
+ <div class="row mb-2">
122
+ <div class="col-md-2"><strong><%= t("raif.admin.common.failure_error") %>:</strong></div>
123
+ <div class="col-md-10"><code><%= @model_completion.failure_error %></code></div>
124
+ </div>
125
+ <div class="row mb-2">
126
+ <div class="col-md-2"><strong><%= t("raif.admin.common.failure_reason") %>:</strong></div>
127
+ <div class="col-md-10"><%= @model_completion.failure_reason %></div>
128
+ </div>
116
129
  <% end %>
117
130
  </div>
118
131
  </div>
119
132
 
120
133
  <% if @model_completion.messages.present? %>
121
- <div class="card mb-4">
122
- <div class="card-header">
123
- <h5 class="mb-0"><%= t("raif.admin.common.messages") %></h5>
124
- </div>
125
- <div class="card-body">
126
- <% @model_completion.messages.each do |message| %>
127
- <div class="mb-3">
128
- <% if message["role"].present? %>
129
- <strong><%= message["role"].titleize %>:</strong>
130
- <% end %>
131
- <% if message["content"].present? %>
132
- <pre class="mt-2"><%= message["content"] %></pre>
133
- <% end %>
134
+ <div class="card mb-4">
135
+ <div class="card-header">
136
+ <h5 class="mb-0"><%= t("raif.admin.common.messages") %></h5>
137
+ </div>
138
+ <div class="card-body">
139
+ <% @model_completion.messages.each do |message| %>
140
+ <div class="mb-3">
141
+ <% if message["type"] == "function_call" %>
142
+ <strong><%= t("raif.admin.common.tool_call") %>:</strong>
143
+ <div class="border rounded p-2 mt-2 bg-light">
144
+ <div><strong><%= t("raif.admin.common.tool_name") %>:</strong> <%= message["name"] %></div>
145
+ <div><strong><%= t("raif.admin.common.arguments") %>:</strong></div>
146
+ <pre class="mb-0 mt-1"><%= pretty_json(message["arguments"]) %></pre>
147
+ </div>
148
+ <% elsif message["type"] == "function_call_output" %>
149
+ <strong><%= t("raif.admin.common.tool_result") %>:</strong>
150
+ <pre class="mt-2"><%= pretty_json(message["output"]) %></pre>
151
+ <% else %>
152
+ <% if message["role"].present? %>
153
+ <strong><%= message["role"].titleize %>:</strong>
154
+ <% end %>
155
+ <% if message["content"].present? %>
156
+ <pre class="mt-2"><%= message["content"] %></pre>
157
+ <% end %>
134
158
 
135
- <% if message["tool_calls"].present? %>
136
- <div class="mt-2">
137
- <% message["tool_calls"].each do |tool_call| %>
138
- <div class="border rounded p-2 mb-2 bg-light">
139
- <div><strong><%= t("raif.admin.common.tool_name") %>:</strong> <%= tool_call.dig("function", "name") %></div>
140
- <div><strong><%= t("raif.admin.common.arguments") %>:</strong></div>
141
- <pre class="mb-0 mt-1"><%= begin
142
- JSON.pretty_generate(JSON.parse(tool_call.dig("function", "arguments")))
143
- rescue StandardError
144
- tool_call.dig("function", "arguments")
145
- end %></pre>
159
+ <% if message["tool_calls"].present? %>
160
+ <div class="mt-2">
161
+ <% message["tool_calls"].each do |tool_call| %>
162
+ <div class="border rounded p-2 mb-2 bg-light">
163
+ <div><strong><%= t("raif.admin.common.tool_name") %>:</strong> <%= tool_call.dig("function", "name") %></div>
164
+ <div><strong><%= t("raif.admin.common.arguments") %>:</strong></div>
165
+ <pre class="mb-0 mt-1"><%= pretty_json(tool_call.dig("function", "arguments")) %></pre>
166
+ </div>
167
+ <% end %>
146
168
  </div>
147
169
  <% end %>
148
- </div>
149
- <% end %>
150
- </div>
151
- <% end %>
170
+ <% end %>
171
+ </div>
172
+ <% end %>
173
+ </div>
152
174
  </div>
153
- </div>
154
175
  <% end %>
155
176
 
156
177
  <% if @model_completion.system_prompt.present? %>
157
- <div class="card mb-4">
158
- <div class="card-header">
159
- <h5 class="mb-0"><%= t("raif.admin.common.system_prompt") %></h5>
160
- </div>
161
- <div class="card-body">
162
- <pre class="pre-wrap"><%= @model_completion.system_prompt %></pre>
178
+ <div class="card mb-4">
179
+ <div class="card-header">
180
+ <h5 class="mb-0"><%= t("raif.admin.common.system_prompt") %></h5>
181
+ </div>
182
+ <div class="card-body">
183
+ <pre class="pre-wrap"><%= @model_completion.system_prompt %></pre>
184
+ </div>
163
185
  </div>
164
- </div>
165
186
  <% end %>
166
187
 
167
188
  <div class="card mb-4">
@@ -176,7 +197,7 @@
176
197
  <h5 class="mb-0 mt-4"><%= t("raif.admin.common.prettified") %></h5>
177
198
  <% begin %>
178
199
  <pre class="pre-wrap"><%= JSON.pretty_generate(@model_completion.parsed_response) %></pre>
179
- <% rescue StandardError %>
200
+ <% rescue StandardError %>
180
201
  <%= t("raif.admin.common.invalid_json") %>
181
202
  <% end %>
182
203
  <% elsif @model_completion.response_format_html? %>
@@ -206,22 +227,95 @@
206
227
  </div>
207
228
  </div>
208
229
 
230
+ <% if @model_completion.provider_managed_tool_calls.present? %>
231
+ <div class="card mb-4">
232
+ <div class="card-header">
233
+ <h5 class="mb-0"><%= t("raif.admin.common.provider_managed_tool_calls") %></h5>
234
+ </div>
235
+ <div class="card-body">
236
+ <% @model_completion.provider_managed_tool_calls.each do |tool_call| %>
237
+ <div class="border rounded p-3 mb-3 bg-light">
238
+ <div class="d-flex justify-content-between align-items-start gap-3">
239
+ <div>
240
+ <div><strong><%= t("raif.admin.common.tool_name") %>:</strong> <%= tool_call["tool_name"].to_s.titleize %></div>
241
+ <% if tool_call["provider_tool_call_id"].present? %>
242
+ <div><strong><%= t("raif.admin.common.provider_tool_call_id") %>:</strong> <code><%= tool_call["provider_tool_call_id"] %></code></div>
243
+ <% end %>
244
+ <% if tool_call["inferred"] %>
245
+ <div><small class="text-muted"><%= t("raif.admin.common.inferred_from_citations") %></small></div>
246
+ <% end %>
247
+ </div>
248
+ <% if tool_call["status"].present? %>
249
+ <span class="badge bg-info text-dark"><%= tool_call["status"].to_s.titleize %></span>
250
+ <% end %>
251
+ </div>
252
+
253
+ <% if tool_call["arguments"].present? %>
254
+ <div class="mt-3"><strong><%= t("raif.admin.common.arguments") %>:</strong></div>
255
+ <pre class="mb-0 mt-1"><%= begin
256
+ JSON.pretty_generate(tool_call["arguments"])
257
+ rescue StandardError
258
+ tool_call["arguments"]
259
+ end %></pre>
260
+ <% end %>
261
+
262
+ <% if tool_call["sources"].present? %>
263
+ <div class="mt-3">
264
+ <strong><%= t("raif.admin.common.sources") %>:</strong>
265
+ <div class="mt-2">
266
+ <% tool_call["sources"].each do |source| %>
267
+ <div class="border rounded p-2 mb-2 bg-white">
268
+ <% if source["url"].present? %>
269
+ <div>
270
+ <a href="<%= source["url"] %>" target="_blank" rel="noopener" class="text-decoration-none">
271
+ <%= source["title"].presence || source["url"] %>
272
+ </a>
273
+ </div>
274
+ <small class="text-muted d-block"><%= source["url"] %></small>
275
+ <% else %>
276
+ <div><%= source["title"] %></div>
277
+ <% end %>
278
+ <% if source["page_age"].present? %>
279
+ <small class="text-muted d-block"><%= source["page_age"] %></small>
280
+ <% end %>
281
+ </div>
282
+ <% end %>
283
+ </div>
284
+ </div>
285
+ <% elsif tool_call["raw_result"].present? %>
286
+ <div class="mt-3"><strong><%= t("raif.admin.common.result") %>:</strong></div>
287
+ <pre class="mb-0 mt-1"><%= begin
288
+ JSON.pretty_generate(tool_call["raw_result"])
289
+ rescue StandardError
290
+ tool_call["raw_result"]
291
+ end %></pre>
292
+ <% end %>
293
+ </div>
294
+ <% end %>
295
+ </div>
296
+ </div>
297
+ <% end %>
298
+
209
299
  <div class="card mb-4">
210
300
  <div class="card-header">
211
301
  <h5 class="mb-0"><%= t("raif.admin.common.citations") %></h5>
212
302
  </div>
213
303
  <div class="card-body">
214
- <% if @model_completion.citations.present? %>
215
- <% @model_completion.citations.each_with_index do |citation, index| %>
304
+ <% if @model_completion.sanitized_citations.present? %>
305
+ <% @model_completion.sanitized_citations.each_with_index do |citation, index| %>
216
306
  <div class="mb-3 p-3 border rounded">
217
307
  <div class="d-flex align-items-start">
218
308
  <span class="badge bg-primary me-3"><%= index + 1 %></span>
219
309
  <div class="flex-grow-1">
220
310
  <h6 class="mb-1">
221
- <a href="<%= citation["url"] %>" target="_blank" rel="noopener" class="text-decoration-none">
311
+ <% if citation["url"].present? %>
312
+ <a href="<%= citation["url"] %>" target="_blank" rel="noopener" class="text-decoration-none">
313
+ <%= citation["title"] %>
314
+ <i class="bi bi-box-arrow-up-right ms-1" style="font-size: 0.8em;"></i>
315
+ </a>
316
+ <% else %>
222
317
  <%= citation["title"] %>
223
- <i class="bi bi-box-arrow-up-right ms-1" style="font-size: 0.8em;"></i>
224
- </a>
318
+ <% end %>
225
319
  </h6>
226
320
  <small class="text-muted"><%= citation["url"] %></small>
227
321
  </div>
@@ -233,3 +327,18 @@
233
327
  <% end %>
234
328
  </div>
235
329
  </div>
330
+
331
+ <% if @model_completion.response_array.present? %>
332
+ <div class="card mb-4">
333
+ <div class="card-header">
334
+ <h5 class="mb-0"><%= t("raif.admin.common.response_array") %></h5>
335
+ </div>
336
+ <div class="card-body">
337
+ <pre class="pre-wrap"><%= begin
338
+ JSON.pretty_generate(@model_completion.response_array)
339
+ rescue StandardError
340
+ @model_completion.response_array
341
+ end %></pre>
342
+ </div>
343
+ </div>
344
+ <% end %>
@@ -14,7 +14,7 @@
14
14
  <label for="tool_types"><%= t("raif.admin.common.tool_type") %></label>
15
15
  <%= select_tag :tool_types,
16
16
  options_for_select([[t("raif.admin.common.all"), "all"]] + @tool_types.map{|type| [type, type] }, @selected_type),
17
- { class: "form-select" } %>
17
+ { class: "form-select", data: { controller: "raif--tom-select" } } %>
18
18
  </div>
19
19
  </div>
20
20
  <div class="col-md-2">
@@ -64,3 +64,21 @@
64
64
  </div>
65
65
  </div>
66
66
  <% end %>
67
+
68
+ <% if @model_tool_invocation.admin_observation_available? %>
69
+ <div class="card mb-4">
70
+ <div class="card-header">
71
+ <h5 class="mb-0"><%= t(".observation_sent_to_model") %></h5>
72
+ </div>
73
+ <% if @model_tool_invocation.admin_observation_error.present? %>
74
+ <div class="card-body text-muted">
75
+ <%= t(".observation_unavailable", error: @model_tool_invocation.admin_observation_error) %>
76
+ </div>
77
+ <% else %>
78
+ <div class="card-body">
79
+ <p class="text-muted small"><%= t(".observation_disclaimer") %></p>
80
+ <pre class="pre-wrap"><%= @model_tool_invocation.admin_observation %></pre>
81
+ </div>
82
+ <% end %>
83
+ </div>
84
+ <% end %>
@@ -0,0 +1,56 @@
1
+ <div class="d-flex justify-content-between align-items-center my-4">
2
+ <h1 class="mb-0"><%= t("raif.admin.prompt_studio.common.prompt_studio") %></h1>
3
+ </div>
4
+
5
+ <%= render "raif/admin/prompt_studio/shared/nav_tabs", active_tab: :agents %>
6
+
7
+ <%= render "raif/admin/prompt_studio/shared/type_filter",
8
+ filter_path: raif.admin_prompt_studio_agents_path,
9
+ param_name: :agent_type,
10
+ types: @agent_types,
11
+ selected_type: @selected_type,
12
+ llm_model_keys: @llm_model_keys %>
13
+
14
+ <% if @selected_type.present? && @agents.present? %>
15
+ <div class="table-responsive">
16
+ <table class="table table-striped">
17
+ <thead>
18
+ <tr>
19
+ <th><%= t("raif.admin.common.id") %></th>
20
+ <th><%= t("raif.admin.common.type") %></th>
21
+ <th><%= t("raif.admin.common.created_at") %></th>
22
+ <th><%= t("raif.admin.common.model") %></th>
23
+ <th><%= t("raif.admin.common.status") %></th>
24
+ <th></th>
25
+ </tr>
26
+ </thead>
27
+ <tbody>
28
+ <% @agents.each do |agent| %>
29
+ <tr>
30
+ <td><%= agent.id %></td>
31
+ <td><%= agent.type %></td>
32
+ <td><%= agent.created_at.rfc822 %></td>
33
+ <td><%= agent.llm_model_key %></td>
34
+ <td>
35
+ <% if agent.completed_at? %>
36
+ <span class="badge bg-success"><%= t("raif.admin.common.completed") %></span>
37
+ <% elsif agent.failed_at? %>
38
+ <span class="badge bg-danger"><%= t("raif.admin.common.failed") %></span>
39
+ <% elsif agent.started_at? %>
40
+ <span class="badge bg-warning text-dark"><%= t("raif.admin.common.in_progress") %></span>
41
+ <% end %>
42
+ </td>
43
+ <td>
44
+ <%= link_to "View", raif.admin_prompt_studio_agent_path(agent), class: "btn btn-sm btn-outline-primary" %>
45
+ </td>
46
+ </tr>
47
+ <% end %>
48
+ </tbody>
49
+ </table>
50
+ </div>
51
+ <div class="mt-3"><%== pagy_bootstrap_nav(@pagy) if @pagy.pages > 1 %></div>
52
+ <% elsif @selected_type.present? %>
53
+ <div class="alert alert-info">
54
+ <%= t("raif.admin.prompt_studio.common.no_instances") %>
55
+ </div>
56
+ <% end %>
@@ -0,0 +1,57 @@
1
+ <div class="d-flex justify-content-between align-items-center my-4">
2
+ <h1 class="mb-0"><%= t("raif.admin.prompt_studio.agents.show.page_title", id: @agent.id) %></h1>
3
+ <%= link_to t("raif.admin.prompt_studio.common.back"),
4
+ raif.admin_prompt_studio_agents_path(agent_type: @agent.type),
5
+ class: "btn btn-outline-secondary" %>
6
+ </div>
7
+
8
+ <div class="card mb-4">
9
+ <div class="card-header">
10
+ <h5 class="mb-0"><%= t("raif.admin.prompt_studio.agents.show.agent_details") %></h5>
11
+ </div>
12
+ <div class="card-body">
13
+ <div class="row mb-3">
14
+ <div class="col-md-2"><strong><%= t("raif.admin.common.id") %>:</strong></div>
15
+ <div class="col-md-10"><%= @agent.id %></div>
16
+ </div>
17
+ <div class="row mb-3">
18
+ <div class="col-md-2"><strong><%= t("raif.admin.common.type") %>:</strong></div>
19
+ <div class="col-md-10"><%= @agent.type %></div>
20
+ </div>
21
+ <div class="row mb-3">
22
+ <div class="col-md-2"><strong><%= t("raif.admin.common.model") %>:</strong></div>
23
+ <div class="col-md-10"><%= @agent.llm_model_key %></div>
24
+ </div>
25
+ <div class="row mb-3">
26
+ <div class="col-md-2"><strong><%= t("raif.admin.common.created_at") %>:</strong></div>
27
+ <div class="col-md-10"><%= @agent.created_at.rfc822 %></div>
28
+ </div>
29
+ <% if @agent.task.present? %>
30
+ <div class="row mb-3">
31
+ <div class="col-md-2"><strong><%= t("raif.admin.common.task") %>:</strong></div>
32
+ <div class="col-md-10"><%= @agent.task %></div>
33
+ </div>
34
+ <% end %>
35
+ <% if @agent.run_with.present? %>
36
+ <div class="row mb-3">
37
+ <div class="col-md-2"><strong><%= t("raif.admin.common.run_with") %>:</strong></div>
38
+ <div class="col-md-10">
39
+ <pre class="pre-wrap mb-0"><%= JSON.pretty_generate(@agent.run_with) %></pre>
40
+ </div>
41
+ </div>
42
+ <% end %>
43
+ </div>
44
+ </div>
45
+
46
+ <%= render "raif/admin/prompt_studio/shared/prompt_comparison", comparison: @comparison %>
47
+
48
+ <% if @agent.final_answer.present? %>
49
+ <div class="card mb-4">
50
+ <div class="card-header">
51
+ <h5 class="mb-0"><%= t("raif.admin.common.final_answer") %></h5>
52
+ </div>
53
+ <div class="card-body">
54
+ <pre class="pre-wrap"><%= @agent.final_answer %></pre>
55
+ </div>
56
+ </div>
57
+ <% end %>
@@ -0,0 +1,54 @@
1
+ <%= tag.tr id: dom_id(item) do %>
2
+ <td>
3
+ <%= link_to "##{item.source_task_id}", raif.admin_prompt_studio_task_path(item.source_task_id), class: "text-decoration-none" %>
4
+ </td>
5
+ <td>
6
+ <% case item.status %>
7
+ <% when "pending" %>
8
+ <span class="badge bg-secondary"><%= t("raif.admin.prompt_studio.batch_runs.status.pending") %></span>
9
+ <% when "running" %>
10
+ <span class="badge bg-warning text-dark">
11
+ <span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>
12
+ <%= t("raif.admin.prompt_studio.batch_runs.status.running") %>
13
+ </span>
14
+ <% when "judging" %>
15
+ <span class="badge bg-info text-dark">
16
+ <span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>
17
+ <%= t("raif.admin.prompt_studio.batch_runs.status.judging") %>
18
+ </span>
19
+ <% when "completed" %>
20
+ <span class="badge bg-success"><%= t("raif.admin.prompt_studio.batch_runs.status.completed") %></span>
21
+ <% when "failed" %>
22
+ <span class="badge bg-danger"><%= t("raif.admin.prompt_studio.batch_runs.status.failed") %></span>
23
+ <% end %>
24
+ </td>
25
+ <td>
26
+ <% if item.source_task&.raw_response.present? %>
27
+ <%= tag.span truncate(item.source_task.raw_response, length: 80), title: item.source_task.raw_response %>
28
+ <% end %>
29
+ </td>
30
+ <td>
31
+ <% if item.result_task&.raw_response.present? %>
32
+ <%= link_to truncate(item.result_task.raw_response, length: 80),
33
+ raif.admin_prompt_studio_task_path(item.result_task),
34
+ class: "text-decoration-none",
35
+ title: item.result_task.raw_response %>
36
+ <% end %>
37
+ </td>
38
+ <% if item.batch_run.has_judge? %>
39
+ <td>
40
+ <% if item.judge_summary.present? %>
41
+ <a href="#" class="text-decoration-none" data-bs-toggle="modal" data-bs-target="#judge-detail-modal-<%= item.id %>">
42
+ <strong><%= item.judge_summary %></strong>
43
+ </a>
44
+ <% end %>
45
+ </td>
46
+ <td>
47
+ <% if item.judge_reasoning.present? %>
48
+ <a href="#" class="text-decoration-none text-body" data-bs-toggle="modal" data-bs-target="#judge-detail-modal-<%= item.id %>">
49
+ <%= truncate(item.judge_reasoning, length: 80) %>
50
+ </a>
51
+ <% end %>
52
+ </td>
53
+ <% end %>
54
+ <% end %>
@@ -0,0 +1,76 @@
1
+ <div id="judge-config-section" data-controller="raif--judge-config">
2
+ <h6 class="mb-2"><%= t("raif.admin.prompt_studio.batch_runs.judge_config.title") %></h6>
3
+ <p class="text-muted small"><%= t("raif.admin.prompt_studio.batch_runs.judge_config.description") %></p>
4
+
5
+ <div class="mb-3">
6
+ <label for="judge_type" class="form-label"><%= t("raif.admin.prompt_studio.batch_runs.judge_config.type_label") %></label>
7
+ <%= select_tag :judge_type,
8
+ options_for_select([
9
+ [t("raif.admin.prompt_studio.batch_runs.judge_config.type_none"), ""],
10
+ [t("raif.admin.prompt_studio.batch_runs.judge_config.type_binary"), "Raif::Evals::LlmJudges::Binary"],
11
+ [t("raif.admin.prompt_studio.batch_runs.judge_config.type_scored"), "Raif::Evals::LlmJudges::Scored"],
12
+ [t("raif.admin.prompt_studio.batch_runs.judge_config.type_comparative"), "Raif::Evals::LlmJudges::Comparative"],
13
+ [t("raif.admin.prompt_studio.batch_runs.judge_config.type_summarization"), "Raif::Evals::LlmJudges::Summarization"]
14
+ ]),
15
+ class: "form-select",
16
+ data: { controller: "raif--tom-select", action: "raif--judge-config#toggle raif--cost-estimate#calculate", raif__cost_estimate_target: "judgeTypeSelect" } %>
17
+ </div>
18
+
19
+ <div class="d-none" data-raif--judge-config-target="binary">
20
+ <div class="mb-3">
21
+ <label for="judge_criteria" class="form-label"><%= t("raif.admin.prompt_studio.batch_runs.judge_config.criteria_label") %></label>
22
+ <%= text_area_tag :judge_criteria,
23
+ nil,
24
+ class: "form-control",
25
+ rows: 2,
26
+ placeholder: t("raif.admin.prompt_studio.batch_runs.judge_config.criteria_placeholder") %>
27
+ </div>
28
+ <div class="form-check mb-3">
29
+ <%= check_box_tag :judge_strict_mode, "1", false, class: "form-check-input" %>
30
+ <label for="judge_strict_mode" class="form-check-label"><%= t("raif.admin.prompt_studio.batch_runs.judge_config.strict_mode_label") %></label>
31
+ </div>
32
+ </div>
33
+
34
+ <div class="d-none" data-raif--judge-config-target="scored">
35
+ <div class="mb-3">
36
+ <label for="judge_scoring_rubric" class="form-label"><%= t("raif.admin.prompt_studio.batch_runs.judge_config.rubric_label") %></label>
37
+ <%= select_tag :judge_scoring_rubric,
38
+ options_for_select([
39
+ [t("raif.admin.prompt_studio.batch_runs.judge_config.rubric_accuracy"), "accuracy"],
40
+ [t("raif.admin.prompt_studio.batch_runs.judge_config.rubric_helpfulness"), "helpfulness"],
41
+ [t("raif.admin.prompt_studio.batch_runs.judge_config.rubric_clarity"), "clarity"]
42
+ ]),
43
+ class: "form-select" %>
44
+ </div>
45
+ </div>
46
+
47
+ <div class="d-none" data-raif--judge-config-target="comparative">
48
+ <div class="mb-3">
49
+ <label for="judge_comparison_criteria" class="form-label"><%= t("raif.admin.prompt_studio.batch_runs.judge_config.comparison_criteria_label") %></label>
50
+ <%= text_area_tag :judge_comparison_criteria,
51
+ nil,
52
+ class: "form-control",
53
+ rows: 2,
54
+ placeholder: t("raif.admin.prompt_studio.batch_runs.judge_config.comparison_criteria_placeholder") %>
55
+ </div>
56
+ </div>
57
+
58
+ <div class="d-none" data-raif--judge-config-target="summarization">
59
+ <p class="text-muted small"><%= t("raif.admin.prompt_studio.batch_runs.judge_config.summarization_description") %></p>
60
+ </div>
61
+
62
+ <div class="d-none" data-raif--judge-config-target="sharedOptions">
63
+ <div class="form-check mb-3">
64
+ <%= check_box_tag :judge_include_original_prompt_as_context, "1", true, class: "form-check-input" %>
65
+ <label for="judge_include_original_prompt_as_context" class="form-check-label"><%= t("raif.admin.prompt_studio.batch_runs.judge_config.include_original_prompt_as_context_label") %></label>
66
+ <div class="form-text"><%= t("raif.admin.prompt_studio.batch_runs.judge_config.include_original_prompt_as_context_description") %></div>
67
+ </div>
68
+ <div class="mb-3">
69
+ <label for="judge_llm_model_key" class="form-label"><%= t("raif.admin.prompt_studio.batch_runs.judge_config.judge_model_label") %></label>
70
+ <%= select_tag :judge_llm_model_key,
71
+ llm_model_options,
72
+ class: "form-select",
73
+ data: { controller: "raif--tom-select", raif__cost_estimate_target: "judgeModelSelect", action: "raif--cost-estimate#calculate" } %>
74
+ </div>
75
+ </div>
76
+ </div>
@@ -0,0 +1,27 @@
1
+ <div class="modal fade" id="judge-detail-modal-<%= item.id %>" tabindex="-1" aria-hidden="true">
2
+ <div class="modal-dialog modal-lg">
3
+ <div class="modal-content">
4
+ <div class="modal-header">
5
+ <h5 class="modal-title"><%= t("raif.admin.prompt_studio.batch_runs.show.judge_result") %> &mdash; <%= t("raif.admin.common.task") %> #<%= item.source_task_id %></h5>
6
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
7
+ </div>
8
+ <div class="modal-body">
9
+ <% if item.judge_summary.present? %>
10
+ <div class="mb-3">
11
+ <h6><%= t("raif.admin.prompt_studio.batch_runs.show.judge_result") %></h6>
12
+ <span class="badge bg-primary fs-6"><%= item.judge_summary %></span>
13
+ </div>
14
+ <% end %>
15
+ <% if item.judge_reasoning.present? %>
16
+ <div>
17
+ <h6><%= t("raif.admin.prompt_studio.batch_runs.show.judge_reasoning") %></h6>
18
+ <pre class="pre-wrap border rounded p-3 bg-light"><%= item.judge_reasoning %></pre>
19
+ </div>
20
+ <% end %>
21
+ </div>
22
+ <div class="modal-footer">
23
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><%= t("raif.admin.prompt_studio.common.back") %></button>
24
+ </div>
25
+ </div>
26
+ </div>
27
+ </div>
@@ -0,0 +1,35 @@
1
+ <div class="modal fade" id="batch-run-modal" tabindex="-1" aria-labelledby="batch-run-modal-label" aria-hidden="true">
2
+ <div class="modal-dialog modal-lg">
3
+ <div class="modal-content">
4
+ <div class="modal-header">
5
+ <h5 class="modal-title" id="batch-run-modal-label"><%= t("raif.admin.prompt_studio.batch_runs.create.title") %></h5>
6
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
7
+ </div>
8
+ <div class="modal-body">
9
+ <div class="mb-3">
10
+ <label for="llm_model_key" class="form-label"><%= t("raif.admin.common.model") %></label>
11
+ <%= select_tag :llm_model_key,
12
+ llm_model_options,
13
+ class: "form-select",
14
+ data: { controller: "raif--tom-select", raif__cost_estimate_target: "modelSelect", action: "raif--cost-estimate#calculate" } %>
15
+ </div>
16
+
17
+ <%= render "raif/admin/prompt_studio/batch_runs/judge_config_fields" %>
18
+
19
+ <div class="alert alert-light border mb-0 mt-3 d-none" data-raif--cost-estimate-target="estimate"></div>
20
+ </div>
21
+ <div class="modal-footer d-flex justify-content-between">
22
+ <span class="text-muted">
23
+ <span data-raif--select-all-checkboxes-target="selectedCount">0</span>
24
+ <%= t("raif.admin.prompt_studio.batch_runs.create.tasks_selected") %>
25
+ </span>
26
+ <div class="d-flex gap-2">
27
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><%= t("raif.admin.prompt_studio.common.back") %></button>
28
+ <%= submit_tag t("raif.admin.prompt_studio.batch_runs.create.submit"),
29
+ class: "btn btn-primary",
30
+ data: { confirm: t("raif.admin.prompt_studio.batch_runs.create.confirm") } %>
31
+ </div>
32
+ </div>
33
+ </div>
34
+ </div>
35
+ </div>