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
@@ -0,0 +1,78 @@
1
+ <%= tag.div id: dom_id(batch_run, :progress) do %>
2
+ <div class="card mb-4">
3
+ <div class="card-body">
4
+ <div class="d-flex justify-content-between align-items-center mb-2">
5
+ <div>
6
+ <strong><%= t("raif.admin.prompt_studio.batch_runs.show.progress") %></strong>
7
+ <% case batch_run.status %>
8
+ <% when :pending %>
9
+ <span class="badge bg-secondary ms-2"><%= t("raif.admin.prompt_studio.batch_runs.status.pending") %></span>
10
+ <% when :in_progress %>
11
+ <span class="badge bg-warning text-dark ms-2">
12
+ <span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>
13
+ <%= t("raif.admin.prompt_studio.batch_runs.status.in_progress") %>
14
+ </span>
15
+ <% when :completed %>
16
+ <span class="badge bg-success ms-2"><%= t("raif.admin.prompt_studio.batch_runs.status.completed") %></span>
17
+ <% when :failed %>
18
+ <span class="badge bg-danger ms-2"><%= t("raif.admin.prompt_studio.batch_runs.status.failed") %></span>
19
+ <% end %>
20
+ </div>
21
+ <div class="text-muted">
22
+ <%= batch_run.completed_count + batch_run.failed_count %> / <%= batch_run.total_count %>
23
+ <% if batch_run.failed_count > 0 %>
24
+ (<span class="text-danger"><%= batch_run.failed_count %> <%= t("raif.admin.common.failed").downcase %></span>)
25
+ <% end %>
26
+ </div>
27
+ </div>
28
+ <div class="progress" style="height: 20px;">
29
+ <div class="progress-bar bg-success" role="progressbar"
30
+ style="width: <%= batch_run.progress_percentage %>%"
31
+ aria-valuenow="<%= batch_run.progress_percentage %>"
32
+ aria-valuemin="0"
33
+ aria-valuemax="100">
34
+ <%= batch_run.progress_percentage %>%
35
+ </div>
36
+ </div>
37
+
38
+ <% if batch_run.has_judge? && batch_run.completed? %>
39
+ <div class="mt-3">
40
+ <strong><%= t("raif.admin.prompt_studio.batch_runs.show.summary") %>:</strong>
41
+ <% case batch_run.judge_type %>
42
+ <% when "Raif::Evals::LlmJudges::Binary" %>
43
+ <% if batch_run.judge_pass_rate %>
44
+ <%= t("raif.admin.prompt_studio.batch_runs.show.pass_rate") %>: <%= batch_run.judge_pass_rate %>
45
+ <% end %>
46
+ <% when "Raif::Evals::LlmJudges::Scored" %>
47
+ <% if batch_run.judge_average_score %>
48
+ <%= t("raif.admin.prompt_studio.batch_runs.show.average_score") %>: <%= batch_run.judge_average_score %>
49
+ <% end %>
50
+ <% when "Raif::Evals::LlmJudges::Comparative" %>
51
+ <% summary = batch_run.judge_comparative_summary %>
52
+ <% if summary %>
53
+ <div class="d-flex gap-4 mt-2">
54
+ <div>
55
+ <span class="badge bg-success"><%= summary[:new_win_pct] %>%</span>
56
+ <%= t("raif.admin.prompt_studio.batch_runs.judge.new_response") %>
57
+ <span class="text-muted">(<%= summary[:new_wins] %>/<%= summary[:total] %>)</span>
58
+ </div>
59
+ <div>
60
+ <span class="badge bg-secondary"><%= summary[:original_win_pct] %>%</span>
61
+ <%= t("raif.admin.prompt_studio.batch_runs.judge.original_response") %>
62
+ <span class="text-muted">(<%= summary[:original_wins] %>/<%= summary[:total] %>)</span>
63
+ </div>
64
+ <% if summary[:ties] > 0 %>
65
+ <div>
66
+ <span class="badge bg-warning text-dark"><%= summary[:tie_pct] %>%</span>
67
+ <%= t("raif.admin.prompt_studio.batch_runs.judge.tie") %>
68
+ <span class="text-muted">(<%= summary[:ties] %>/<%= summary[:total] %>)</span>
69
+ </div>
70
+ <% end %>
71
+ </div>
72
+ <% end %>
73
+ <% end %>
74
+ </div>
75
+ <% end %>
76
+ </div>
77
+ </div>
78
+ <% end %>
@@ -0,0 +1,49 @@
1
+ <div class="d-flex justify-content-between align-items-center my-4">
2
+ <h1 class="mb-0"><%= t("raif.admin.prompt_studio.batch_runs.show.page_title", id: @batch_run.id) %></h1>
3
+ <%= link_to t("raif.admin.prompt_studio.common.back"),
4
+ raif.admin_prompt_studio_tasks_path(task_type: @batch_run.task_type),
5
+ class: "btn btn-outline-secondary" %>
6
+ </div>
7
+
8
+ <%= turbo_stream_from @batch_run %>
9
+
10
+ <%= render "raif/admin/prompt_studio/batch_runs/progress", batch_run: @batch_run %>
11
+
12
+ <div class="card mb-4">
13
+ <div class="card-header">
14
+ <h5 class="mb-0"><%= t("raif.admin.prompt_studio.batch_runs.show.results") %></h5>
15
+ </div>
16
+ <div class="card-body p-0">
17
+ <div class="table-responsive">
18
+ <table class="table table-striped mb-0">
19
+ <thead>
20
+ <tr>
21
+ <th><%= t("raif.admin.prompt_studio.batch_runs.show.source_task") %></th>
22
+ <th><%= t("raif.admin.common.status") %></th>
23
+ <th><%= t("raif.admin.prompt_studio.batch_runs.show.original_response") %></th>
24
+ <th><%= t("raif.admin.prompt_studio.batch_runs.show.new_response") %></th>
25
+ <% if @batch_run.has_judge? %>
26
+ <th><%= t("raif.admin.prompt_studio.batch_runs.show.judge_result") %></th>
27
+ <th><%= t("raif.admin.prompt_studio.batch_runs.show.judge_reasoning") %></th>
28
+ <% end %>
29
+ </tr>
30
+ </thead>
31
+ <tbody>
32
+ <% @items.each do |item| %>
33
+ <%= render "raif/admin/prompt_studio/batch_runs/batch_run_item", item: item %>
34
+ <% end %>
35
+ </tbody>
36
+ </table>
37
+ </div>
38
+ </div>
39
+ </div>
40
+
41
+ <div class="mt-3"><%== pagy_bootstrap_nav(@pagy) if @pagy.pages > 1 %></div>
42
+
43
+ <% if @batch_run.has_judge? %>
44
+ <% @items.each do |item| %>
45
+ <% if item.judge_summary.present? || item.judge_reasoning.present? %>
46
+ <%= render "raif/admin/prompt_studio/batch_runs/judge_detail_modal", item: item %>
47
+ <% end %>
48
+ <% end %>
49
+ <% end %>
@@ -0,0 +1,48 @@
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: :conversations %>
6
+
7
+ <%= render "raif/admin/prompt_studio/shared/type_filter",
8
+ filter_path: raif.admin_prompt_studio_conversations_path,
9
+ param_name: :conversation_type,
10
+ types: @conversation_types,
11
+ selected_type: @selected_type,
12
+ llm_model_keys: @llm_model_keys %>
13
+
14
+ <% if @selected_type.present? && @conversations.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.entries_count") %></th>
24
+ <th></th>
25
+ </tr>
26
+ </thead>
27
+ <tbody>
28
+ <% @conversations.each do |conversation| %>
29
+ <tr>
30
+ <td><%= conversation.id %></td>
31
+ <td><%= conversation.type %></td>
32
+ <td><%= conversation.created_at.rfc822 %></td>
33
+ <td><%= conversation.llm_model_key %></td>
34
+ <td><%= conversation.conversation_entries_count %></td>
35
+ <td>
36
+ <%= link_to "View", raif.admin_prompt_studio_conversation_path(conversation), class: "btn btn-sm btn-outline-primary" %>
37
+ </td>
38
+ </tr>
39
+ <% end %>
40
+ </tbody>
41
+ </table>
42
+ </div>
43
+ <div class="mt-3"><%== pagy_bootstrap_nav(@pagy) if @pagy.pages > 1 %></div>
44
+ <% elsif @selected_type.present? %>
45
+ <div class="alert alert-info">
46
+ <%= t("raif.admin.prompt_studio.common.no_instances") %>
47
+ </div>
48
+ <% end %>
@@ -0,0 +1,36 @@
1
+ <div class="d-flex justify-content-between align-items-center my-4">
2
+ <h1 class="mb-0"><%= t("raif.admin.prompt_studio.conversations.show.page_title", id: @conversation.id) %></h1>
3
+ <%= link_to t("raif.admin.prompt_studio.common.back"),
4
+ raif.admin_prompt_studio_conversations_path(conversation_type: @conversation.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.conversations.show.conversation_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"><%= @conversation.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"><%= @conversation.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"><%= @conversation.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"><%= @conversation.created_at.rfc822 %></div>
28
+ </div>
29
+ <div class="row mb-3">
30
+ <div class="col-md-2"><strong><%= t("raif.admin.common.entries_count") %>:</strong></div>
31
+ <div class="col-md-10"><%= @conversation.conversation_entries_count %></div>
32
+ </div>
33
+ </div>
34
+ </div>
35
+
36
+ <%= render "raif/admin/prompt_studio/shared/prompt_comparison", comparison: @comparison %>
@@ -0,0 +1,17 @@
1
+ <ul class="nav nav-tabs mb-4">
2
+ <li class="nav-item">
3
+ <%= link_to t("raif.admin.common.tasks"),
4
+ raif.admin_prompt_studio_tasks_path,
5
+ class: "nav-link #{active_tab == :tasks ? "active" : ""}" %>
6
+ </li>
7
+ <li class="nav-item">
8
+ <%= link_to t("raif.admin.common.conversations"),
9
+ raif.admin_prompt_studio_conversations_path,
10
+ class: "nav-link #{active_tab == :conversations ? "active" : ""}" %>
11
+ </li>
12
+ <li class="nav-item">
13
+ <%= link_to t("raif.admin.common.agents"),
14
+ raif.admin_prompt_studio_agents_path,
15
+ class: "nav-link #{active_tab == :agents ? "active" : ""}" %>
16
+ </li>
17
+ </ul>
@@ -0,0 +1,87 @@
1
+ <% if comparison[:warnings].any? %>
2
+ <div class="alert <%= comparison[:has_stale_references] ? "alert-danger" : "alert-warning" %>">
3
+ <ul class="mb-0">
4
+ <% comparison[:warnings].each do |warning| %>
5
+ <li><%= warning %></li>
6
+ <% end %>
7
+ </ul>
8
+ </div>
9
+ <% end %>
10
+
11
+ <% if comparison[:original_system_prompt].present? || comparison[:current_system_prompt].present? %>
12
+ <div class="card mb-4">
13
+ <div class="card-header d-flex justify-content-between align-items-center">
14
+ <h5 class="mb-0"><%= t("raif.admin.common.system_prompt") %></h5>
15
+ <% if comparison[:system_prompt_changed] %>
16
+ <span class="badge bg-warning text-dark"><%= t("raif.admin.prompt_studio.common.system_prompt_changed") %></span>
17
+ <% elsif comparison[:current_system_prompt].present? %>
18
+ <span class="badge bg-success"><%= t("raif.admin.prompt_studio.common.system_prompt_unchanged") %></span>
19
+ <% end %>
20
+ </div>
21
+ <div class="card-body">
22
+ <% if comparison[:system_prompt_changed] %>
23
+ <div class="row">
24
+ <div class="col-md-6">
25
+ <h6><%= t("raif.admin.prompt_studio.common.original_system_prompt") %></h6>
26
+ <pre class="pre-wrap border rounded p-3 bg-light"><%= comparison[:original_system_prompt] %></pre>
27
+ </div>
28
+ <div class="col-md-6">
29
+ <h6><%= t("raif.admin.prompt_studio.common.current_system_prompt") %></h6>
30
+ <pre class="pre-wrap border rounded p-3 bg-light"><%= comparison[:current_system_prompt] %></pre>
31
+ </div>
32
+ </div>
33
+ <% else %>
34
+ <pre class="pre-wrap"><%= comparison[:current_system_prompt] || comparison[:original_system_prompt] %></pre>
35
+ <% end %>
36
+ </div>
37
+ </div>
38
+ <% end %>
39
+
40
+ <% if comparison[:original_prompt].present? || comparison[:current_prompt].present? %>
41
+ <div class="card mb-4">
42
+ <div class="card-header d-flex justify-content-between align-items-center">
43
+ <h5 class="mb-0"><%= t("raif.admin.common.prompt") %></h5>
44
+ <% if comparison[:prompt_changed] %>
45
+ <span class="badge bg-warning text-dark"><%= t("raif.admin.prompt_studio.common.prompt_changed") %></span>
46
+ <% elsif comparison[:current_prompt].present? %>
47
+ <span class="badge bg-success"><%= t("raif.admin.prompt_studio.common.prompt_unchanged") %></span>
48
+ <% end %>
49
+ </div>
50
+ <div class="card-body">
51
+ <% if comparison[:prompt_changed] %>
52
+ <div class="row">
53
+ <div class="col-md-6">
54
+ <h6 class="d-flex justify-content-between align-items-center">
55
+ <span><%= t("raif.admin.prompt_studio.common.original_prompt") %></span>
56
+ <span>
57
+ <% if comparison[:original_prompt_tokens].present? %>
58
+ <span class="badge bg-secondary fw-normal"><%= t("raif.admin.prompt_studio.common.prompt_tokens", count: number_with_delimiter(comparison[:original_prompt_tokens])) %></span>
59
+ <% end %>
60
+ <% if comparison[:original_prompt_token_cost].present? %>
61
+ <span class="badge bg-secondary fw-normal"><%= number_to_currency(comparison[:original_prompt_token_cost], precision: 4) %></span>
62
+ <% end %>
63
+ </span>
64
+ </h6>
65
+ <pre class="pre-wrap border rounded p-3 bg-light"><%= comparison[:original_prompt] %></pre>
66
+ </div>
67
+ <div class="col-md-6">
68
+ <h6 class="d-flex justify-content-between align-items-center">
69
+ <span><%= t("raif.admin.prompt_studio.common.current_prompt") %></span>
70
+ <span>
71
+ <% if comparison[:current_prompt_token_estimate].present? %>
72
+ <span class="badge bg-secondary fw-normal"><%= t("raif.admin.prompt_studio.common.estimated_tokens", count: number_with_delimiter(comparison[:current_prompt_token_estimate])) %></span>
73
+ <% end %>
74
+ <% if comparison[:current_prompt_cost_estimate].present? %>
75
+ <span class="badge bg-secondary fw-normal"><%= t("raif.admin.prompt_studio.common.estimated_cost", cost: number_to_currency(comparison[:current_prompt_cost_estimate], precision: 4)) %></span>
76
+ <% end %>
77
+ </span>
78
+ </h6>
79
+ <pre class="pre-wrap border rounded p-3 bg-light"><%= comparison[:current_prompt] %></pre>
80
+ </div>
81
+ </div>
82
+ <% else %>
83
+ <pre class="pre-wrap"><%= comparison[:current_prompt] || comparison[:original_prompt] %></pre>
84
+ <% end %>
85
+ </div>
86
+ </div>
87
+ <% end %>
@@ -0,0 +1,54 @@
1
+ <div class="row mb-4">
2
+ <div class="col-12">
3
+ <%= form_tag filter_path, method: :get, class: "mb-4" do %>
4
+ <div class="row align-items-end">
5
+ <div class="col-md-3">
6
+ <div class="form-group">
7
+ <label for="<%= param_name %>"><%= t("raif.admin.prompt_studio.common.type") %></label>
8
+ <%= select_tag param_name,
9
+ options_for_select(
10
+ types.map { |type| [type, type] },
11
+ selected_type
12
+ ),
13
+ include_blank: t("raif.admin.prompt_studio.common.select_type"),
14
+ class: "form-select",
15
+ data: { controller: "raif--tom-select" } %>
16
+ </div>
17
+ </div>
18
+ <% if local_assigns[:llm_model_keys]&.any? %>
19
+ <div class="col-md-2">
20
+ <div class="form-group">
21
+ <label for="llm_model_key"><%= t("raif.admin.common.model") %></label>
22
+ <%= select_tag :llm_model_key,
23
+ options_for_select(
24
+ llm_model_keys.map { |key| [key, key] },
25
+ params[:llm_model_key]
26
+ ),
27
+ include_blank: t("raif.admin.common.all"),
28
+ class: "form-select",
29
+ data: { controller: "raif--tom-select" } %>
30
+ </div>
31
+ </div>
32
+ <% end %>
33
+ <div class="col-md-2">
34
+ <div class="form-group">
35
+ <label for="created_after"><%= t("raif.admin.prompt_studio.common.created_after") %></label>
36
+ <%= date_field_tag :created_after, params[:created_after], class: "form-control", autocomplete: "off" %>
37
+ </div>
38
+ </div>
39
+ <div class="col-md-2">
40
+ <div class="form-group">
41
+ <label for="created_before"><%= t("raif.admin.prompt_studio.common.created_before") %></label>
42
+ <%= date_field_tag :created_before, params[:created_before], class: "form-control", autocomplete: "off" %>
43
+ </div>
44
+ </div>
45
+ <div class="col-md-auto">
46
+ <%= submit_tag t("raif.admin.common.filter"), class: "btn btn-primary" %>
47
+ </div>
48
+ <% if local_assigns[:extra_buttons] %>
49
+ <%= extra_buttons %>
50
+ <% end %>
51
+ </div>
52
+ <% end %>
53
+ </div>
54
+ </div>
@@ -0,0 +1,145 @@
1
+ <%= tag.div id: dom_id(task, :result) do %>
2
+ <div class="row mb-4">
3
+ <div class="<%= Raif.config.prompt_studio_runs_enabled ? "col-md-7" : "col-12" %>">
4
+ <div class="card h-100">
5
+ <div class="card-header d-flex justify-content-between align-items-center">
6
+ <h5 class="mb-0"><%= t("raif.admin.prompt_studio.tasks.show.task_details") %></h5>
7
+ <% if task.prompt_studio_run? %>
8
+ <span class="badge bg-info"><%= t("raif.admin.prompt_studio.common.run_in_prompt_studio") %></span>
9
+ <% end %>
10
+ </div>
11
+ <div class="card-body">
12
+ <div class="row mb-3">
13
+ <div class="col-md-3"><strong><%= t("raif.admin.common.id") %>:</strong></div>
14
+ <div class="col-md-9"><%= task.id %></div>
15
+ </div>
16
+ <div class="row mb-3">
17
+ <div class="col-md-3"><strong><%= t("raif.admin.common.type") %>:</strong></div>
18
+ <div class="col-md-9"><%= task.type %></div>
19
+ </div>
20
+ <div class="row mb-3">
21
+ <div class="col-md-3"><strong><%= t("raif.admin.common.model") %>:</strong></div>
22
+ <div class="col-md-9"><%= task.llm_model_key %></div>
23
+ </div>
24
+ <div class="row mb-3">
25
+ <div class="col-md-3"><strong><%= t("raif.admin.common.status") %>:</strong></div>
26
+ <div class="col-md-9">
27
+ <% if task.status == :in_progress %>
28
+ <span class="badge bg-warning text-dark">
29
+ <span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>
30
+ <%= t("raif.admin.common.running") %>
31
+ </span>
32
+ <% elsif task.completed_at? %>
33
+ <span class="badge bg-success"><%= t("raif.admin.common.completed") %></span>
34
+ <% if task.raw_response.present? %>
35
+ <a href="#task-response" class="ms-2 small"><%= t("raif.admin.prompt_studio.tasks.show.view_response") %></a>
36
+ <% end %>
37
+ <% elsif task.failed_at? %>
38
+ <span class="badge bg-danger"><%= t("raif.admin.common.failed") %></span>
39
+ <% else %>
40
+ <span class="badge bg-secondary"><%= t("raif.admin.common.pending") %></span>
41
+ <% end %>
42
+ </div>
43
+ </div>
44
+ <div class="row mb-3">
45
+ <div class="col-md-3"><strong><%= t("raif.admin.common.created_at") %>:</strong></div>
46
+ <div class="col-md-9"><%= task.created_at.rfc822 %></div>
47
+ </div>
48
+ <% if task.run_with.present? %>
49
+ <div class="row mb-3">
50
+ <div class="col-md-3"><strong><%= t("raif.admin.common.run_with") %>:</strong></div>
51
+ <div class="col-md-9">
52
+ <pre class="pre-wrap mb-0"><%= JSON.pretty_generate(task.run_with) %></pre>
53
+ </div>
54
+ </div>
55
+ <% end %>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ <% if Raif.config.prompt_studio_runs_enabled %>
60
+ <div class="col-md-5">
61
+ <div class="card h-100">
62
+ <div class="card-header">
63
+ <h5 class="mb-0"><%= t("raif.admin.prompt_studio.tasks.rerun.title") %></h5>
64
+ </div>
65
+ <div class="card-body">
66
+ <p class="text-muted"><%= t("raif.admin.prompt_studio.tasks.rerun.description") %></p>
67
+ <div class="mb-3">
68
+ <% if comparison[:current_system_prompt].present? %>
69
+ <% if comparison[:system_prompt_changed] %>
70
+ <span class="badge bg-warning text-dark"><%= t("raif.admin.prompt_studio.common.system_prompt_changed") %></span>
71
+ <% else %>
72
+ <span class="badge bg-success"><%= t("raif.admin.prompt_studio.common.system_prompt_unchanged") %></span>
73
+ <% end %>
74
+ <% end %>
75
+ <% if comparison[:current_prompt].present? %>
76
+ <% if comparison[:prompt_changed] %>
77
+ <span class="badge bg-warning text-dark"><%= t("raif.admin.prompt_studio.common.prompt_changed") %></span>
78
+ <% else %>
79
+ <span class="badge bg-success"><%= t("raif.admin.prompt_studio.common.prompt_unchanged") %></span>
80
+ <% end %>
81
+ <% end %>
82
+ </div>
83
+ <% if comparison[:has_stale_references] %>
84
+ <div class="alert alert-danger mb-0">
85
+ <%= t("raif.admin.prompt_studio.common.warning_stale_reference") %>
86
+ </div>
87
+ <% else %>
88
+ <%= form_tag raif.admin_prompt_studio_tasks_path(source_task_id: task.id), method: :post do %>
89
+ <div class="mb-3">
90
+ <label for="llm_model_key"><%= t("raif.admin.common.model") %></label>
91
+ <%= select_tag :llm_model_key,
92
+ llm_model_options(selected: task.llm_model_key),
93
+ class: "form-select",
94
+ data: { controller: "raif--tom-select" } %>
95
+ </div>
96
+ <%= submit_tag t("raif.admin.prompt_studio.tasks.rerun.submit"),
97
+ class: "btn btn-primary",
98
+ data: { confirm: t("raif.admin.prompt_studio.tasks.rerun.confirm"), disable_with: t("raif.admin.prompt_studio.tasks.rerun.running") } %>
99
+ <% end %>
100
+ <% end %>
101
+ </div>
102
+ </div>
103
+ </div>
104
+ <% end %>
105
+ </div>
106
+
107
+ <%= render "raif/admin/prompt_studio/shared/prompt_comparison", comparison: comparison %>
108
+
109
+ <% if task.raw_response.present? && original_task&.raw_response.present? %>
110
+ <div class="card mb-4" id="task-response">
111
+ <div class="card-header">
112
+ <h5 class="mb-0"><%= t("raif.admin.prompt_studio.tasks.show.response_comparison") %></h5>
113
+ </div>
114
+ <div class="card-body">
115
+ <div class="row">
116
+ <div class="col-md-6">
117
+ <h6>
118
+ <%= t("raif.admin.prompt_studio.tasks.show.original_response") %>
119
+ <small class="text-muted">
120
+ (<%= link_to "##{original_task.id}", raif.admin_prompt_studio_task_path(original_task) %> &mdash; <%= original_task.llm_model_key %>)
121
+ </small>
122
+ </h6>
123
+ <pre class="pre-wrap border rounded p-3 bg-light"><%= format_task_response(original_task) %></pre>
124
+ </div>
125
+ <div class="col-md-6">
126
+ <h6>
127
+ <%= t("raif.admin.prompt_studio.tasks.show.new_response") %>
128
+ <small class="text-muted">(<%= task.llm_model_key %>)</small>
129
+ </h6>
130
+ <pre class="pre-wrap border rounded p-3 bg-light"><%= format_task_response(task) %></pre>
131
+ </div>
132
+ </div>
133
+ </div>
134
+ </div>
135
+ <% elsif task.raw_response.present? %>
136
+ <div class="card mb-4" id="task-response">
137
+ <div class="card-header">
138
+ <h5 class="mb-0"><%= t("raif.admin.common.response") %> (<%= task.response_format %>)</h5>
139
+ </div>
140
+ <div class="card-body">
141
+ <pre class="pre-wrap"><%= format_task_response(task) %></pre>
142
+ </div>
143
+ </div>
144
+ <% end %>
145
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <tr>
2
+ <% if show_checkbox %>
3
+ <td><input type="checkbox" name="source_task_ids[]" value="<%= task.id %>" class="form-check-input task-checkbox" data-raif--select-all-checkboxes-target="checkbox" data-raif--cost-estimate-target="checkbox" data-action="raif--select-all-checkboxes#updateCount raif--cost-estimate#calculate" data-prompt-tokens="<%= task.raif_model_completion&.prompt_tokens || 0 %>" data-completion-tokens="<%= task.raif_model_completion&.completion_tokens || 0 %>"></td>
4
+ <% end %>
5
+ <td><%= task.id %></td>
6
+ <td><%= task.type %></td>
7
+ <td><%= task.created_at.rfc822 %></td>
8
+ <td><%= task.llm_model_key %></td>
9
+ <td>
10
+ <%= link_to "View", raif.admin_prompt_studio_task_path(task), class: "btn btn-sm btn-outline-primary" %>
11
+ </td>
12
+ </tr>
@@ -0,0 +1,58 @@
1
+ <div class="row mb-4">
2
+ <div class="col-12">
3
+ <%= form_tag raif.admin_prompt_studio_tasks_path, method: :get, class: "mb-4" do %>
4
+ <div class="row align-items-end">
5
+ <div class="col-md-3">
6
+ <div class="form-group">
7
+ <label for="task_type"><%= t("raif.admin.prompt_studio.common.type") %></label>
8
+ <%= select_tag :task_type,
9
+ options_for_select(
10
+ task_types.map { |type| [type, type] },
11
+ selected_type
12
+ ),
13
+ include_blank: t("raif.admin.prompt_studio.common.select_type"),
14
+ class: "form-select",
15
+ data: { controller: "raif--tom-select" } %>
16
+ </div>
17
+ </div>
18
+ <% if local_assigns[:llm_model_keys]&.any? %>
19
+ <div class="col-md-2">
20
+ <div class="form-group">
21
+ <label for="llm_model_key"><%= t("raif.admin.common.model") %></label>
22
+ <%= select_tag :llm_model_key,
23
+ options_for_select(
24
+ llm_model_keys.map { |key| [key, key] },
25
+ params[:llm_model_key]
26
+ ),
27
+ include_blank: t("raif.admin.common.all"),
28
+ class: "form-select",
29
+ data: { controller: "raif--tom-select" } %>
30
+ </div>
31
+ </div>
32
+ <% end %>
33
+ <div class="col-md-2">
34
+ <div class="form-group">
35
+ <label for="created_after"><%= t("raif.admin.prompt_studio.common.created_after") %></label>
36
+ <%= date_field_tag :created_after, params[:created_after], class: "form-control", autocomplete: "off" %>
37
+ </div>
38
+ </div>
39
+ <div class="col-md-2">
40
+ <div class="form-group">
41
+ <label for="created_before"><%= t("raif.admin.prompt_studio.common.created_before") %></label>
42
+ <%= date_field_tag :created_before, params[:created_before], class: "form-control", autocomplete: "off" %>
43
+ </div>
44
+ </div>
45
+ <div class="col-md-auto">
46
+ <%= submit_tag t("raif.admin.common.filter"), class: "btn btn-primary" %>
47
+ </div>
48
+ <% if show_batch_runs %>
49
+ <div class="col-md-auto ms-auto">
50
+ <button type="button" class="btn btn-outline-primary" disabled data-bs-toggle="modal" data-bs-target="#batch-run-modal" data-raif--select-all-checkboxes-target="batchButton">
51
+ <%= t("raif.admin.prompt_studio.batch_runs.create.new_batch_run") %>
52
+ </button>
53
+ </div>
54
+ <% end %>
55
+ </div>
56
+ <% end %>
57
+ </div>
58
+ </div>
@@ -0,0 +1,22 @@
1
+ <div class="table-responsive">
2
+ <table class="table table-striped">
3
+ <thead>
4
+ <tr>
5
+ <% if show_checkbox %>
6
+ <th><input type="checkbox" class="form-check-input" data-raif--select-all-checkboxes-target="selectAll" data-action="raif--select-all-checkboxes#toggle"></th>
7
+ <% end %>
8
+ <th><%= t("raif.admin.common.id") %></th>
9
+ <th><%= t("raif.admin.common.type") %></th>
10
+ <th><%= t("raif.admin.common.created_at") %></th>
11
+ <th><%= t("raif.admin.common.model") %></th>
12
+ <th></th>
13
+ </tr>
14
+ </thead>
15
+ <tbody>
16
+ <% tasks.each do |task| %>
17
+ <%= render "raif/admin/prompt_studio/tasks/task_row", task: task, show_checkbox: show_checkbox %>
18
+ <% end %>
19
+ </tbody>
20
+ </table>
21
+ </div>
22
+ <div class="mt-3"><%== pagy_bootstrap_nav(pagy) if pagy.pages > 1 %></div>