raif 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -5
  3. data/app/assets/builds/raif.css +4 -1
  4. data/app/assets/builds/raif_admin.css +13 -1
  5. data/app/assets/javascript/raif/controllers/conversations_controller.js +1 -1
  6. data/app/assets/stylesheets/raif/admin/conversation.scss +16 -0
  7. data/app/assets/stylesheets/raif/conversations.scss +3 -0
  8. data/app/assets/stylesheets/raif.scss +2 -1
  9. data/app/controllers/raif/admin/application_controller.rb +16 -0
  10. data/app/controllers/raif/admin/configs_controller.rb +94 -0
  11. data/app/controllers/raif/admin/model_completions_controller.rb +18 -1
  12. data/app/controllers/raif/admin/model_tool_invocations_controller.rb +7 -1
  13. data/app/controllers/raif/admin/stats/model_tool_invocations_controller.rb +21 -0
  14. data/app/controllers/raif/admin/stats/tasks_controller.rb +15 -6
  15. data/app/controllers/raif/admin/stats_controller.rb +32 -3
  16. data/app/controllers/raif/conversation_entries_controller.rb +1 -0
  17. data/app/controllers/raif/conversations_controller.rb +10 -2
  18. data/app/jobs/raif/conversation_entry_job.rb +8 -6
  19. data/app/models/raif/admin/task_stat.rb +7 -0
  20. data/app/models/raif/agent.rb +63 -2
  21. data/app/models/raif/agents/native_tool_calling_agent.rb +101 -56
  22. data/app/models/raif/application_record.rb +18 -0
  23. data/app/models/raif/concerns/agent_inference_stats.rb +35 -0
  24. data/app/models/raif/concerns/json_schema_definition.rb +40 -5
  25. data/app/models/raif/concerns/llms/anthropic/message_formatting.rb +28 -0
  26. data/app/models/raif/concerns/llms/anthropic/response_tool_calls.rb +24 -0
  27. data/app/models/raif/concerns/llms/anthropic/tool_formatting.rb +4 -0
  28. data/app/models/raif/concerns/llms/bedrock/message_formatting.rb +36 -0
  29. data/app/models/raif/concerns/llms/bedrock/response_tool_calls.rb +26 -0
  30. data/app/models/raif/concerns/llms/bedrock/tool_formatting.rb +4 -0
  31. data/app/models/raif/concerns/llms/google/message_formatting.rb +109 -0
  32. data/app/models/raif/concerns/llms/google/response_tool_calls.rb +32 -0
  33. data/app/models/raif/concerns/llms/google/tool_formatting.rb +72 -0
  34. data/app/models/raif/concerns/llms/message_formatting.rb +11 -5
  35. data/app/models/raif/concerns/llms/open_ai/json_schema_validation.rb +3 -3
  36. data/app/models/raif/concerns/llms/open_ai_completions/message_formatting.rb +22 -0
  37. data/app/models/raif/concerns/llms/open_ai_completions/response_tool_calls.rb +22 -0
  38. data/app/models/raif/concerns/llms/open_ai_completions/tool_formatting.rb +4 -0
  39. data/app/models/raif/concerns/llms/open_ai_responses/message_formatting.rb +17 -0
  40. data/app/models/raif/concerns/llms/open_ai_responses/response_tool_calls.rb +26 -0
  41. data/app/models/raif/concerns/llms/open_ai_responses/tool_formatting.rb +4 -0
  42. data/app/models/raif/concerns/run_with.rb +127 -0
  43. data/app/models/raif/conversation.rb +91 -8
  44. data/app/models/raif/conversation_entry.rb +32 -1
  45. data/app/models/raif/embedding_model.rb +2 -1
  46. data/app/models/raif/embedding_models/open_ai.rb +1 -1
  47. data/app/models/raif/llm.rb +27 -2
  48. data/app/models/raif/llms/anthropic.rb +7 -19
  49. data/app/models/raif/llms/bedrock.rb +6 -20
  50. data/app/models/raif/llms/google.rb +140 -0
  51. data/app/models/raif/llms/open_ai_base.rb +19 -5
  52. data/app/models/raif/llms/open_ai_completions.rb +6 -11
  53. data/app/models/raif/llms/open_ai_responses.rb +6 -16
  54. data/app/models/raif/llms/open_router.rb +7 -13
  55. data/app/models/raif/model_completion.rb +61 -0
  56. data/app/models/raif/model_tool.rb +10 -2
  57. data/app/models/raif/model_tool_invocation.rb +38 -6
  58. data/app/models/raif/model_tools/agent_final_answer.rb +2 -7
  59. data/app/models/raif/model_tools/provider_managed/code_execution.rb +4 -0
  60. data/app/models/raif/model_tools/provider_managed/image_generation.rb +4 -0
  61. data/app/models/raif/model_tools/provider_managed/web_search.rb +4 -0
  62. data/app/models/raif/streaming_responses/google.rb +71 -0
  63. data/app/models/raif/task.rb +55 -12
  64. data/app/models/raif/user_tool_invocation.rb +19 -0
  65. data/app/views/layouts/raif/admin.html.erb +12 -1
  66. data/app/views/raif/admin/agents/_agent.html.erb +8 -0
  67. data/app/views/raif/admin/agents/_conversation_message.html.erb +28 -6
  68. data/app/views/raif/admin/agents/index.html.erb +2 -0
  69. data/app/views/raif/admin/agents/show.html.erb +46 -1
  70. data/app/views/raif/admin/configs/show.html.erb +117 -0
  71. data/app/views/raif/admin/conversations/_conversation_entry.html.erb +29 -34
  72. data/app/views/raif/admin/conversations/show.html.erb +2 -0
  73. data/app/views/raif/admin/model_completions/_model_completion.html.erb +9 -0
  74. data/app/views/raif/admin/model_completions/index.html.erb +26 -0
  75. data/app/views/raif/admin/model_completions/show.html.erb +124 -61
  76. data/app/views/raif/admin/model_tool_invocations/index.html.erb +22 -1
  77. data/app/views/raif/admin/model_tools/_list.html.erb +16 -0
  78. data/app/views/raif/admin/model_tools/_model_tool.html.erb +36 -0
  79. data/app/views/raif/admin/stats/_stats_tile.html.erb +34 -0
  80. data/app/views/raif/admin/stats/index.html.erb +71 -88
  81. data/app/views/raif/admin/stats/model_tool_invocations/index.html.erb +43 -0
  82. data/app/views/raif/admin/stats/tasks/index.html.erb +20 -6
  83. data/app/views/raif/admin/tasks/index.html.erb +6 -1
  84. data/app/views/raif/admin/tasks/show.html.erb +36 -3
  85. data/app/views/raif/conversation_entries/_form.html.erb +3 -0
  86. data/app/views/raif/conversations/_conversation.html.erb +10 -0
  87. data/app/views/raif/conversations/_entry_processed.turbo_stream.erb +12 -0
  88. data/app/views/raif/conversations/index.html.erb +23 -0
  89. data/config/locales/admin.en.yml +33 -1
  90. data/config/locales/en.yml +33 -4
  91. data/config/routes.rb +2 -0
  92. data/db/migrate/20250904194456_add_generating_entry_response_to_raif_conversations.rb +7 -0
  93. data/db/migrate/20250911125234_add_source_to_raif_tasks.rb +7 -0
  94. data/db/migrate/20251020005853_add_source_to_raif_agents.rb +7 -0
  95. data/db/migrate/20251020011346_rename_task_run_args_to_run_with.rb +7 -0
  96. data/db/migrate/20251020011405_add_run_with_to_raif_agents.rb +13 -0
  97. data/db/migrate/20251024160119_add_llm_messages_max_length_to_raif_conversations.rb +14 -0
  98. data/db/migrate/20251124185033_add_provider_tool_call_id_to_raif_model_tool_invocations.rb +7 -0
  99. data/db/migrate/20251128202941_add_tool_choice_to_raif_model_completions.rb +7 -0
  100. data/db/migrate/20260118144846_add_source_to_raif_conversations.rb +7 -0
  101. data/db/migrate/20260119000000_add_failure_tracking_to_raif_model_completions.rb +10 -0
  102. data/db/migrate/20260119000001_add_completed_at_to_raif_model_completions.rb +8 -0
  103. data/db/migrate/20260119000002_add_started_at_to_raif_model_completions.rb +8 -0
  104. data/lib/generators/raif/agent/templates/agent.rb.tt +1 -1
  105. data/lib/generators/raif/agent/templates/application_agent.rb.tt +1 -1
  106. data/lib/generators/raif/conversation/templates/conversation.rb.tt +6 -0
  107. data/lib/generators/raif/install/templates/initializer.rb +78 -10
  108. data/lib/generators/raif/task/templates/task.rb.tt +1 -1
  109. data/lib/raif/configuration.rb +37 -2
  110. data/lib/raif/engine.rb +8 -0
  111. data/lib/raif/errors/instance_dependent_schema_error.rb +8 -0
  112. data/lib/raif/errors/streaming_error.rb +6 -3
  113. data/lib/raif/errors.rb +1 -0
  114. data/lib/raif/evals/llm_judge.rb +2 -2
  115. data/lib/raif/evals/llm_judges/binary.rb +3 -3
  116. data/lib/raif/evals/llm_judges/comparative.rb +3 -3
  117. data/lib/raif/evals/llm_judges/scored.rb +1 -1
  118. data/lib/raif/evals/llm_judges/summarization.rb +2 -2
  119. data/lib/raif/evals/run.rb +1 -0
  120. data/lib/raif/json_schema_builder.rb +14 -0
  121. data/lib/raif/llm_registry.rb +207 -37
  122. data/lib/raif/messages.rb +180 -0
  123. data/lib/raif/version.rb +1 -1
  124. data/lib/raif.rb +9 -0
  125. data/lib/tasks/annotate_rb.rake +10 -0
  126. data/spec/support/rspec_helpers.rb +8 -8
  127. metadata +44 -9
  128. data/app/models/raif/agents/re_act_agent.rb +0 -127
  129. data/app/models/raif/agents/re_act_step.rb +0 -32
  130. data/app/models/raif/concerns/task_run_args.rb +0 -62
@@ -2,6 +2,31 @@
2
2
 
3
3
  <div class="row">
4
4
  <div class="col-12">
5
+ <%= form_tag raif.admin_model_completions_path, method: :get, class: "mb-4" do %>
6
+ <div class="row align-items-end">
7
+ <div class="col-md-4">
8
+ <div class="form-group">
9
+ <label for="status"><%= t("raif.admin.common.status") %></label>
10
+ <%= select_tag :status,
11
+ options_for_select(
12
+ [
13
+ [t("raif.admin.common.all"), :all],
14
+ [t("raif.admin.common.completed"), :completed],
15
+ [t("raif.admin.common.failed"), :failed],
16
+ [t("raif.admin.common.in_progress"), :started],
17
+ [t("raif.admin.common.pending"), :pending]
18
+ ],
19
+ @selected_status
20
+ ),
21
+ { class: "form-select" } %>
22
+ </div>
23
+ </div>
24
+ <div class="col-md-2">
25
+ <%= submit_tag t("raif.admin.common.filter"), class: "btn btn-primary" %>
26
+ </div>
27
+ </div>
28
+ <% end %>
29
+
5
30
  <% if @model_completions.any? %>
6
31
  <div class="table-responsive">
7
32
  <table class="table table-striped table-hover">
@@ -12,6 +37,7 @@
12
37
  <th><%= t("raif.admin.common.source") %></th>
13
38
  <th><%= t("raif.admin.common.model") %></th>
14
39
  <th><%= t("raif.admin.common.response_format") %></th>
40
+ <th><%= t("raif.admin.common.status") %></th>
15
41
  <th><%= t("raif.admin.common.total_tokens") %></th>
16
42
  <th><%= t("raif.admin.common.total_cost") %></th>
17
43
  <th><%= t("raif.admin.common.citations") %></th>
@@ -8,69 +8,112 @@
8
8
  <h5 class="mb-0"><%= t("raif.admin.common.details") %></h5>
9
9
  </div>
10
10
  <div class="card-body">
11
- <div class="row mb-3">
12
- <div class="col-md-3"><strong><%= t("raif.admin.common.id") %>:</strong></div>
13
- <div class="col-md-9"><%= @model_completion.id %></div>
14
- </div>
15
- <div class="row mb-3">
16
- <div class="col-md-3"><strong><%= t("raif.admin.common.source") %>:</strong></div>
17
- <div class="col-md-9"><%= @model_completion.source_type %> #<%= @model_completion.source_id %></div>
18
- </div>
19
- <div class="row mb-3">
20
- <div class="col-md-3"><strong><%= t("raif.admin.common.model") %>:</strong></div>
21
- <div class="col-md-9"><%= @model_completion.llm_model_key %></div>
22
- </div>
23
- <div class="row mb-3">
24
- <div class="col-md-3"><strong><%= t("raif.admin.common.response_format") %>:</strong></div>
25
- <div class="col-md-9"><%= @model_completion.response_format %></div>
26
- </div>
27
- <div class="row mb-3">
28
- <div class="col-md-3"><strong><%= t("raif.admin.common.response_format_parameter") %>:</strong></div>
29
- <div class="col-md-9"><%= @model_completion.response_format_parameter %></div>
30
- </div>
31
- <div class="row mb-3">
32
- <div class="col-md-3"><strong><%= t("raif.admin.common.created_at") %>:</strong></div>
33
- <div class="col-md-9"><%= @model_completion.created_at.rfc822 %></div>
34
- </div>
35
- <div class="row mb-3">
36
- <div class="col-md-3"><strong><%= t("raif.admin.common.prompt_tokens") %>:</strong></div>
37
- <div class="col-md-9">
38
- <% if @model_completion.prompt_tokens %>
39
- <%= number_with_delimiter(@model_completion.prompt_tokens) %>
40
- <% if @model_completion.prompt_token_cost %>
41
- <small>(<%= t("raif.admin.common.est_cost") %>: <%= "$" %><%= number_with_precision(@model_completion.prompt_token_cost, precision: 6) %>)</small>
42
- <% end %>
43
- <% else %>
44
- -
45
- <% end %>
11
+ <div class="row">
12
+ <div class="col-md-6">
13
+ <div class="row mb-2">
14
+ <div class="col-5"><strong><%= t("raif.admin.common.id") %>:</strong></div>
15
+ <div class="col-7"><%= @model_completion.id %></div>
16
+ </div>
17
+ <div class="row mb-2">
18
+ <div class="col-5"><strong><%= t("raif.admin.common.source") %>:</strong></div>
19
+ <div class="col-7"><%= @model_completion.source_type %> #<%= @model_completion.source_id %></div>
20
+ </div>
21
+ <div class="row mb-2">
22
+ <div class="col-5"><strong><%= t("raif.admin.common.model") %>:</strong></div>
23
+ <div class="col-7"><%= @model_completion.llm_model_key %></div>
24
+ </div>
25
+ <div class="row mb-2">
26
+ <div class="col-5"><strong><%= t("raif.admin.common.response_format") %>:</strong></div>
27
+ <div class="col-7"><%= @model_completion.response_format %><%= " (#{@model_completion.response_format_parameter})" if @model_completion.response_format_parameter.present? %></div>
28
+ </div>
29
+ <div class="row mb-2">
30
+ <div class="col-5"><strong><%= t("raif.admin.common.status") %>:</strong></div>
31
+ <div class="col-7">
32
+ <% if @model_completion.failed? %>
33
+ <span class="badge bg-danger"><%= t("raif.admin.common.failed") %></span>
34
+ <% elsif @model_completion.completed? %>
35
+ <span class="badge bg-success"><%= t("raif.admin.common.completed") %></span>
36
+ <% elsif @model_completion.started? %>
37
+ <span class="badge bg-warning text-dark"><%= t("raif.admin.common.in_progress") %></span>
38
+ <% else %>
39
+ <span class="badge bg-secondary"><%= t("raif.admin.common.pending") %></span>
40
+ <% end %>
41
+ </div>
42
+ </div>
43
+ <div class="row mb-2">
44
+ <div class="col-5"><strong><%= t("raif.admin.common.retry_count") %>:</strong></div>
45
+ <div class="col-7"><%= @model_completion.retry_count %></div>
46
+ </div>
46
47
  </div>
47
- </div>
48
- <div class="row mb-3">
49
- <div class="col-md-3"><strong><%= t("raif.admin.common.completion_tokens") %>:</strong></div>
50
- <div class="col-md-9">
51
- <% if @model_completion.completion_tokens %>
52
- <%= number_with_delimiter(@model_completion.completion_tokens) %>
53
- <% if @model_completion.output_token_cost %>
54
- <small>(<%= t("raif.admin.common.est_cost") %>: <%= "$" %><%= number_with_precision(@model_completion.output_token_cost, precision: 6) %>)</small>
55
- <% end %>
56
- <% else %>
57
- -
58
- <% end %>
48
+ <div class="col-md-6">
49
+ <div class="row mb-2">
50
+ <div class="col-5"><strong><%= t("raif.admin.common.created_at") %>:</strong></div>
51
+ <div class="col-7"><small><%= @model_completion.created_at.rfc822 %></small></div>
52
+ </div>
53
+ <div class="row mb-2">
54
+ <div class="col-5"><strong><%= t("raif.admin.common.started_at") %>:</strong></div>
55
+ <div class="col-7"><small><%= @model_completion.started_at&.rfc822 || "-" %></small></div>
56
+ </div>
57
+ <div class="row mb-2">
58
+ <div class="col-5"><strong><%= t("raif.admin.common.completed_at") %>:</strong></div>
59
+ <div class="col-7"><small><%= @model_completion.completed_at&.rfc822 || "-" %></small></div>
60
+ </div>
61
+ <div class="row mb-2">
62
+ <div class="col-5"><strong><%= t("raif.admin.common.prompt_tokens") %>:</strong></div>
63
+ <div class="col-7">
64
+ <% if @model_completion.prompt_tokens %>
65
+ <%= number_with_delimiter(@model_completion.prompt_tokens) %>
66
+ <% if @model_completion.prompt_token_cost %>
67
+ <small>(<%= t("raif.admin.common.est_cost") %>: <%= number_to_currency(@model_completion.prompt_token_cost, precision: 6) %>)</small>
68
+ <% end %>
69
+ <% else %>
70
+ -
71
+ <% end %>
72
+ </div>
73
+ </div>
74
+ <div class="row mb-2">
75
+ <div class="col-5"><strong><%= t("raif.admin.common.completion_tokens") %>:</strong></div>
76
+ <div class="col-7">
77
+ <% if @model_completion.completion_tokens %>
78
+ <%= number_with_delimiter(@model_completion.completion_tokens) %>
79
+ <% if @model_completion.output_token_cost %>
80
+ <small>(<%= t("raif.admin.common.est_cost") %>: <%= number_to_currency(@model_completion.output_token_cost, precision: 6) %>)</small>
81
+ <% end %>
82
+ <% else %>
83
+ -
84
+ <% end %>
85
+ </div>
86
+ </div>
87
+ <div class="row mb-2">
88
+ <div class="col-5"><strong><%= t("raif.admin.common.total_tokens") %>:</strong></div>
89
+ <div class="col-7">
90
+ <% if @model_completion.total_tokens %>
91
+ <%= number_with_delimiter(@model_completion.total_tokens) %>
92
+ <% if @model_completion.total_cost %>
93
+ <small>(<%= t("raif.admin.common.est_cost") %>: <%= "$" %><%= number_with_precision(@model_completion.total_cost, precision: 6) %>)</small>
94
+ <% end %>
95
+ <% else %>
96
+ -
97
+ <% end %>
98
+ </div>
99
+ </div>
59
100
  </div>
60
101
  </div>
61
- <div class="row mb-3">
62
- <div class="col-md-3"><strong><%= t("raif.admin.common.total_tokens") %>:</strong></div>
63
- <div class="col-md-9">
64
- <% if @model_completion.total_tokens %>
65
- <%= number_with_delimiter(@model_completion.total_tokens) %>
66
- <% if @model_completion.total_cost %>
67
- <small>(<%= t("raif.admin.common.est_cost") %>: <%= "$" %><%= number_with_precision(@model_completion.total_cost, precision: 6) %>)</small>
68
- <% end %>
69
- <% else %>
70
- -
71
- <% end %>
72
- </div>
102
+ <% 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>
73
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
+ <% end %>
74
117
  </div>
75
118
  </div>
76
119
 
@@ -82,8 +125,28 @@
82
125
  <div class="card-body">
83
126
  <% @model_completion.messages.each do |message| %>
84
127
  <div class="mb-3">
85
- <strong><%= message["role"].titleize %>:</strong>
86
- <pre class="mt-2"><%= message["content"] %></pre>
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
+
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>
146
+ </div>
147
+ <% end %>
148
+ </div>
149
+ <% end %>
87
150
  </div>
88
151
  <% end %>
89
152
  </div>
@@ -1,7 +1,28 @@
1
- <h1 class="my-4"><%= t("raif.admin.common.model_tool_invocations") %></h1>
1
+ <div class="d-flex justify-content-between align-items-center my-4">
2
+ <h1 class="mb-0"><%= t("raif.admin.common.model_tool_invocations") %></h1>
3
+ <%= link_to raif.admin_stats_model_tool_invocations_path, class: "btn btn-outline-primary" do %>
4
+ <%= t("raif.admin.common.stats") %> &raquo;
5
+ <% end %>
6
+ </div>
2
7
 
3
8
  <div class="row">
4
9
  <div class="col-12">
10
+ <%= form_tag raif.admin_model_tool_invocations_path, method: :get, class: "mb-4" do %>
11
+ <div class="row align-items-end">
12
+ <div class="col-md-4">
13
+ <div class="form-group">
14
+ <label for="tool_types"><%= t("raif.admin.common.tool_type") %></label>
15
+ <%= select_tag :tool_types,
16
+ options_for_select([[t("raif.admin.common.all"), "all"]] + @tool_types.map{|type| [type, type] }, @selected_type),
17
+ { class: "form-select" } %>
18
+ </div>
19
+ </div>
20
+ <div class="col-md-2">
21
+ <%= submit_tag t("raif.admin.common.filter"), class: "btn btn-primary" %>
22
+ </div>
23
+ </div>
24
+ <% end %>
25
+
5
26
  <% if @model_tool_invocations.any? %>
6
27
  <div class="table-responsive">
7
28
  <table class="table table-striped table-hover">
@@ -0,0 +1,16 @@
1
+ <div class="card mb-4">
2
+ <div class="card-header">
3
+ <h5 class="mb-0"><%= t("raif.admin.common.available_tools") %></h5>
4
+ </div>
5
+ <div class="card-body">
6
+ <% if model_tools.blank? %>
7
+ <%= t("raif.admin.common.none") %>
8
+ <% else %>
9
+ <div class="accordion" id="modelToolsAccordion">
10
+ <% model_tools.each_with_index do |(tool_name, tool_class), index| %>
11
+ <%= render partial: "raif/admin/model_tools/model_tool", locals: { tool_name: tool_name, tool_class: tool_class, index: index } %>
12
+ <% end %>
13
+ </div>
14
+ <% end %>
15
+ </div>
16
+ </div>
@@ -0,0 +1,36 @@
1
+ <div class="accordion-item">
2
+ <h2 class="accordion-header" id="heading-<%= index %>">
3
+ <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-<%= index %>" aria-expanded="false" aria-controls="collapse-<%= index %>">
4
+ <strong><%= tool_name %></strong>
5
+ </button>
6
+ </h2>
7
+ <div id="collapse-<%= index %>" class="accordion-collapse collapse" aria-labelledby="heading-<%= index %>" data-bs-parent="#modelToolsAccordion">
8
+ <div class="accordion-body">
9
+ <div class="row mb-3">
10
+ <div class="col-md-2"><strong><%= t("raif.admin.common.tool_name") %>:</strong></div>
11
+ <div class="col-md-10"><code><%= tool_class.tool_name %></code></div>
12
+ </div>
13
+ <div class="row mb-3">
14
+ <div class="col-md-2"><strong><%= t("raif.admin.common.description") %>:</strong></div>
15
+ <div class="col-md-10"><%= tool_class.tool_description %></div>
16
+ </div>
17
+ <% if tool_class.respond_to?(:tool_arguments_schema) %>
18
+ <% if tool_class.schema_defined?(:tool_arguments) %>
19
+ <div class="row mb-3">
20
+ <div class="col-md-2"><strong><%= t("raif.admin.common.arguments_schema") %>:</strong></div>
21
+ <div class="col-md-10">
22
+ <pre class="pre-wrap"><%= JSON.pretty_generate(tool_class.tool_arguments_schema) %></pre>
23
+ </div>
24
+ </div>
25
+ <% else %>
26
+ <div class="row mb-3">
27
+ <div class="col-md-2"><strong><%= t("raif.admin.common.arguments_schema") %>:</strong></div>
28
+ <div class="col-md-10">
29
+ <em><%= t("raif.admin.common.none") %></em>
30
+ </div>
31
+ </div>
32
+ <% end %>
33
+ <% end %>
34
+ </div>
35
+ </div>
36
+ </div>
@@ -0,0 +1,34 @@
1
+ <div class="col-md-4 col-lg-4">
2
+ <div class="stats-card p-3 border rounded shadow-sm h-100 d-flex flex-column justify-content-between">
3
+ <div class="d-flex justify-content-between align-items-start<%= " mb-2" if local_assigns[:show_costs] %>">
4
+ <div class="d-flex align-items-center<%= " mb-2" unless local_assigns[:show_costs] %>">
5
+ <span class="stats-icon bg-<%= icon_color %>-subtle text-<%= icon_color %> rounded p-2 me-3">
6
+ <%= yield %>
7
+ </span>
8
+ <h6 class="mb-0 text-muted"><%= title %></h6>
9
+ </div>
10
+
11
+ <% if local_assigns[:details_link] %>
12
+ <div class="text-end">
13
+ <h3 class="fw-bold mb-0"><%= number_with_delimiter(count) %></h3>
14
+ <%= link_to details_link do %>
15
+ <small><%= t("raif.admin.common.details") %> &raquo;</small>
16
+ <% end %>
17
+ </div>
18
+ <% else %>
19
+ <h3 class="fw-bold mb-0"><%= number_with_delimiter(count) %></h3>
20
+ <% end %>
21
+ </div>
22
+
23
+ <% if local_assigns[:show_costs] %>
24
+ <div class="d-flex justify-content-between align-items-end text-muted">
25
+ <div>
26
+ <div><small><%= t("raif.admin.common.est_costs") %></small></div>
27
+ <div><small><%= t("raif.admin.common.input").downcase %>: <%= number_to_currency(input_cost, precision: 2) %></small></div>
28
+ </div>
29
+ <div><small><%= t("raif.admin.common.output").downcase %>: <%= number_to_currency(output_cost, precision: 2) %></small></div>
30
+ <div><strong><small><%= t("raif.admin.common.total").downcase %>: <%= number_to_currency(total_cost, precision: 2) %></small></strong></div>
31
+ </div>
32
+ <% end %>
33
+ </div>
34
+ </div>
@@ -25,102 +25,85 @@
25
25
  <div class="card-body">
26
26
  <div class="row g-4">
27
27
  <!-- Model Completions -->
28
- <div class="col-md-4 col-lg-4">
29
- <div class="stats-card p-3 border rounded shadow-sm h-100">
30
- <div class="d-flex align-items-center mb-2">
31
- <span class="stats-icon bg-primary-subtle text-primary rounded p-2 me-3">
32
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-robot" viewBox="0 0 16 16">
33
- <path d="M6 12.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5ZM3 8.062C3 6.76 4.235 5.765 5.53 5.886a26.58 26.58 0 0 0 4.94 0C11.765 5.765 13 6.76 13 8.062v1.157a.933.933 0 0 1-.765.935c-.845.147-2.34.346-4.235.346-1.895 0-3.39-.2-4.235-.346A.933.933 0 0 1 3 9.219V8.062Zm4.542-.827a.25.25 0 0 0-.217.068l-.92.9a24.767 24.767 0 0 1-1.871-.183.25.25 0 0 0-.068.495c.55.076 1.232.149 2.02.193a.25.25 0 0 0 .189-.071l.754-.736.847 1.71a.25.25 0 0 0 .404.062l.932-.97a25.286 25.286 0 0 0 1.922-.188.25.25 0 0 0-.068-.495c-.538.074-1.207.145-1.98.189a.25.25 0 0 0-.166.076l-.754.785-.842-1.7a.25.25 0 0 0-.182-.135Z" />
34
- <path d="M8.5 1.866a1 1 0 1 0-1 0V3h-2A4.5 4.5 0 0 0 1 7.5V8a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1v1a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-1a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1v-.5A4.5 4.5 0 0 0 10.5 3h-2V1.866ZM14 7.5V13a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V7.5A3.5 3.5 0 0 1 5.5 4h5A3.5 3.5 0 0 1 14 7.5Z" />
35
- </svg>
36
- </span>
37
- <div>
38
- <h6 class="mb-0 text-muted"><%= t("raif.admin.common.model_completions") %></h6>
39
- </div>
40
- </div>
41
- <div class="d-flex justify-content-between align-items-center">
42
- <h3 class="fw-bold mb-0"><%= number_with_delimiter(@model_completion_count) %></h3>
43
- <p class="text-muted mt-2 mb-0"><small><%= t("raif.admin.common.est_cost") %>: <%= number_to_currency(@model_completion_total_cost, precision: 6) %></small></p>
44
- </div>
45
- </div>
46
- </div>
28
+ <%= render "stats_tile",
29
+ icon_color: "primary",
30
+ title: t("raif.admin.common.model_completions"),
31
+ count: @model_completion_count,
32
+ show_costs: true,
33
+ input_cost: @model_completion_input_token_cost,
34
+ output_cost: @model_completion_output_token_cost,
35
+ total_cost: @model_completion_total_cost do %>
36
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-robot" viewBox="0 0 16 16">
37
+ <path d="M6 12.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5ZM3 8.062C3 6.76 4.235 5.765 5.53 5.886a26.58 26.58 0 0 0 4.94 0C11.765 5.765 13 6.76 13 8.062v1.157a.933.933 0 0 1-.765.935c-.845.147-2.34.346-4.235.346-1.895 0-3.39-.2-4.235-.346A.933.933 0 0 1 3 9.219V8.062Zm4.542-.827a.25.25 0 0 0-.217.068l-.92.9a24.767 24.767 0 0 1-1.871-.183.25.25 0 0 0-.068.495c.55.076 1.232.149 2.02.193a.25.25 0 0 0 .189-.071l.754-.736.847 1.71a.25.25 0 0 0 .404.062l.932-.97a25.286 25.286 0 0 0 1.922-.188.25.25 0 0 0-.068-.495c-.538.074-1.207.145-1.98.189a.25.25 0 0 0-.166.076l-.754.785-.842-1.7a.25.25 0 0 0-.182-.135Z" />
38
+ <path d="M8.5 1.866a1 1 0 1 0-1 0V3h-2A4.5 4.5 0 0 0 1 7.5V8a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1v1a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-1a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1v-.5A4.5 4.5 0 0 0 10.5 3h-2V1.866ZM14 7.5V13a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V7.5A3.5 3.5 0 0 1 5.5 4h5A3.5 3.5 0 0 1 14 7.5Z" />
39
+ </svg>
40
+ <% end %>
47
41
 
48
42
  <!-- Tasks -->
49
- <div class="col-md-4 col-lg-4">
50
- <div class="stats-card p-3 border rounded shadow-sm h-100">
51
- <div class="d-flex align-items-center mb-2">
52
- <span class="stats-icon bg-success-subtle text-success rounded p-2 me-3">
53
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-list-check" viewBox="0 0 16 16">
54
- <path fill-rule="evenodd" d="M5 11.5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zM3.854 2.146a.5.5 0 0 1 0 .708l-1.5 1.5a.5.5 0 0 1-.708 0l-.5-.5a.5.5 0 1 1 .708-.708L2 3.293l1.146-1.147a.5.5 0 0 1 .708 0zm0 4a.5.5 0 0 1 0 .708l-1.5 1.5a.5.5 0 0 1-.708 0l-.5-.5a.5.5 0 1 1 .708-.708L2 7.293l1.146-1.147a.5.5 0 0 1 .708 0zm0 4a.5.5 0 0 1 0 .708l-1.5 1.5a.5.5 0 0 1-.708 0l-.5-.5a.5.5 0 0 1 .708-.708l.146.147 1.146-1.147a.5.5 0 0 1 .708 0z" />
55
- </svg>
56
- </span>
57
- <div>
58
- <h6 class="mb-0 text-muted"><%= t("raif.admin.common.tasks") %></h6>
59
- </div>
60
- </div>
61
- <div class="d-flex justify-content-between align-items-center">
62
- <h3 class="fw-bold mb-0"><%= number_with_delimiter(@task_count) %></h3>
63
- <div class="mt-2">
64
- <%= link_to raif.admin_stats_tasks_path(period: @selected_period) do %>
65
- <%= t("raif.admin.common.details") %> &raquo;
66
- <% end %>
67
- </div>
68
- </div>
69
- </div>
70
- </div>
43
+ <%= render "stats_tile",
44
+ icon_color: "success",
45
+ title: t("raif.admin.common.tasks"),
46
+ count: @task_count,
47
+ details_link: raif.admin_stats_tasks_path(period: @selected_period),
48
+ show_costs: true,
49
+ input_cost: @task_input_token_cost,
50
+ output_cost: @task_output_token_cost,
51
+ total_cost: @task_total_cost do %>
52
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-list-check" viewBox="0 0 16 16">
53
+ <path fill-rule="evenodd" d="M5 11.5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zM3.854 2.146a.5.5 0 0 1 0 .708l-1.5 1.5a.5.5 0 0 1-.708 0l-.5-.5a.5.5 0 1 1 .708-.708L2 3.293l1.146-1.147a.5.5 0 0 1 .708 0zm0 4a.5.5 0 0 1 0 .708l-1.5 1.5a.5.5 0 0 1-.708 0l-.5-.5a.5.5 0 1 1 .708-.708L2 7.293l1.146-1.147a.5.5 0 0 1 .708 0zm0 4a.5.5 0 0 1 0 .708l-1.5 1.5a.5.5 0 0 1-.708 0l-.5-.5a.5.5 0 0 1 .708-.708l.146.147 1.146-1.147a.5.5 0 0 1 .708 0z" />
54
+ </svg>
55
+ <% end %>
71
56
 
72
57
  <!-- Agents -->
73
- <div class="col-md-4 col-lg-4">
74
- <div class="stats-card p-3 border rounded shadow-sm h-100">
75
- <div class="d-flex align-items-center mb-2">
76
- <span class="stats-icon bg-danger-subtle text-danger rounded p-2 me-3">
77
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-cpu" viewBox="0 0 16 16">
78
- <path d="M5 0a.5.5 0 0 1 .5.5V2h1V.5a.5.5 0 0 1 1 0V2h1V.5a.5.5 0 0 1 1 0V2h1V.5a.5.5 0 0 1 1 0V2A2.5 2.5 0 0 1 14 4.5h1.5a.5.5 0 0 1 0 1H14v1h1.5a.5.5 0 0 1 0 1H14v1h1.5a.5.5 0 0 1 0 1H14v1h1.5a.5.5 0 0 1 0 1H14a2.5 2.5 0 0 1-2.5 2.5v1.5a.5.5 0 0 1-1 0V14h-1v1.5a.5.5 0 0 1-1 0V14h-1v1.5a.5.5 0 0 1-1 0V14h-1v1.5a.5.5 0 0 1-1 0V14A2.5 2.5 0 0 1 2 11.5H.5a.5.5 0 0 1 0-1H2v-1H.5a.5.5 0 0 1 0-1H2v-1H.5a.5.5 0 0 1 0-1H2v-1H.5a.5.5 0 0 1 0-1H2A2.5 2.5 0 0 1 4.5 2V.5A.5.5 0 0 1 5 0zm-.5 3A1.5 1.5 0 0 0 3 4.5v7A1.5 1.5 0 0 0 4.5 13h7a1.5 1.5 0 0 0 1.5-1.5v-7A1.5 1.5 0 0 0 11.5 3h-7zM5 6.5A1.5 1.5 0 0 1 6.5 5h3A1.5 1.5 0 0 1 11 6.5v3A1.5 1.5 0 0 1 9.5 11h-3A1.5 1.5 0 0 1 5 9.5v-3zM6.5 6a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3z" />
79
- </svg>
80
- </span>
81
- <div>
82
- <h6 class="mb-0 text-muted"><%= t("raif.admin.common.agents") %></h6>
83
- </div>
84
- </div>
85
- <h3 class="fw-bold mb-0"><%= number_with_delimiter(@agent_count) %></h3>
86
- </div>
87
- </div>
58
+ <%= render "stats_tile",
59
+ icon_color: "danger",
60
+ title: t("raif.admin.common.agents"),
61
+ count: @agent_count,
62
+ show_costs: true,
63
+ input_cost: @agent_input_token_cost,
64
+ output_cost: @agent_output_token_cost,
65
+ total_cost: @agent_total_cost do %>
66
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-cpu" viewBox="0 0 16 16">
67
+ <path d="M5 0a.5.5 0 0 1 .5.5V2h1V.5a.5.5 0 0 1 1 0V2h1V.5a.5.5 0 0 1 1 0V2h1V.5a.5.5 0 0 1 1 0V2A2.5 2.5 0 0 1 14 4.5h1.5a.5.5 0 0 1 0 1H14v1h1.5a.5.5 0 0 1 0 1H14v1h1.5a.5.5 0 0 1 0 1H14v1h1.5a.5.5 0 0 1 0 1H14a2.5 2.5 0 0 1-2.5 2.5v1.5a.5.5 0 0 1-1 0V14h-1v1.5a.5.5 0 0 1-1 0V14h-1v1.5a.5.5 0 0 1-1 0V14h-1v1.5a.5.5 0 0 1-1 0V14A2.5 2.5 0 0 1 2 11.5H.5a.5.5 0 0 1 0-1H2v-1H.5a.5.5 0 0 1 0-1H2v-1H.5a.5.5 0 0 1 0-1H2v-1H.5a.5.5 0 0 1 0-1H2A2.5 2.5 0 0 1 4.5 2V.5A.5.5 0 0 1 5 0zm-.5 3A1.5 1.5 0 0 0 3 4.5v7A1.5 1.5 0 0 0 4.5 13h7a1.5 1.5 0 0 0 1.5-1.5v-7A1.5 1.5 0 0 0 11.5 3h-7zM5 6.5A1.5 1.5 0 0 1 6.5 5h3A1.5 1.5 0 0 1 11 6.5v3A1.5 1.5 0 0 1 9.5 11h-3A1.5 1.5 0 0 1 5 9.5v-3zM6.5 6a.5.5 0 0 0-.5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 0-.5-.5h-3z" />
68
+ </svg>
69
+ <% end %>
88
70
 
89
71
  <!-- Conversations -->
90
- <div class="col-md-4 col-lg-4">
91
- <div class="stats-card p-3 border rounded shadow-sm h-100">
92
- <div class="d-flex align-items-center mb-2">
93
- <span class="stats-icon bg-info-subtle text-info rounded p-2 me-3">
94
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-chat-dots" viewBox="0 0 16 16">
95
- <path d="M5 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm4 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm3 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2z" />
96
- <path d="m2.165 15.803.02-.004c1.83-.363 2.948-.842 3.468-1.105A9.06 9.06 0 0 0 8 15c4.418 0 8-3.134 8-7s-3.582-7-8-7-8 3.134-8 7c0 1.76.743 3.37 1.97 4.6a10.437 10.437 0 0 1-.524 2.318l-.003.011a10.722 10.722 0 0 1-.244.637c-.079.186.074.394.273.362a21.673 21.673 0 0 0 .693-.125zm.8-3.108a1 1 0 0 0-.287-.801C1.618 10.83 1 9.468 1 8c0-3.192 3.004-6 7-6s7 2.808 7 6c0 3.193-3.004 6-7 6a8.06 8.06 0 0 1-2.088-.272a1 1 0 0 0-.711.074c-.387.196-1.24.57-2.634.893a10.97 10.97 0 0 0 .398-2z" />
97
- </svg>
98
- </span>
99
- <div>
100
- <h6 class="mb-0 text-muted"><%= t("raif.admin.common.conversations") %></h6>
101
- </div>
102
- </div>
103
- <h3 class="fw-bold mb-0"><%= number_with_delimiter(@conversation_count) %></h3>
104
- </div>
105
- </div>
72
+ <%= render "stats_tile",
73
+ icon_color: "info",
74
+ title: t("raif.admin.common.conversations"),
75
+ count: @conversation_count do %>
76
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-chat-dots" viewBox="0 0 16 16">
77
+ <path d="M5 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm4 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm3 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2z" />
78
+ <path d="m2.165 15.803.02-.004c1.83-.363 2.948-.842 3.468-1.105A9.06 9.06 0 0 0 8 15c4.418 0 8-3.134 8-7s-3.582-7-8-7-8 3.134-8 7c0 1.76.743 3.37 1.97 4.6a10.437 10.437 0 0 1-.524 2.318l-.003.011a10.722 10.722 0 0 1-.244.637c-.079.186.074.394.273.362a21.673 21.673 0 0 0 .693-.125zm.8-3.108a1 1 0 0 0-.287-.801C1.618 10.83 1 9.468 1 8c0-3.192 3.004-6 7-6s7 2.808 7 6c0 3.193-3.004 6-7 6a8.06 8.06 0 0 1-2.088-.272a1 1 0 0 0-.711.074c-.387.196-1.24.57-2.634.893a10.97 10.97 0 0 0 .398-2z" />
79
+ </svg>
80
+ <% end %>
106
81
 
107
82
  <!-- Conversation Entries -->
108
- <div class="col-md-4 col-lg-4">
109
- <div class="stats-card p-3 border rounded shadow-sm h-100">
110
- <div class="d-flex align-items-center mb-2">
111
- <span class="stats-icon bg-warning-subtle text-warning rounded p-2 me-3">
112
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-chat-text" viewBox="0 0 16 16">
113
- <path d="M2.678 11.894a1 1 0 0 1 .287.801 10.97 10.97 0 0 1-.398 2c1.395-.323 2.247-.697 2.634-.893a1 1 0 0 1 .71-.074A8.06 8.06 0 0 0 8 14c3.996 0 7-2.807 7-6 0-3.192-3.004-6-7-6S1 4.808 1 8c0 1.468.617 2.83 1.678 3.894zm-.493 3.905a21.682 21.682 0 0 1-.713.129c-.2.032-.352-.176-.273-.362a9.68 9.68 0 0 0 .244-.637l.003-.01c.248-.72.45-1.548.524-2.319C.743 11.37 0 9.76 0 8c0-3.866 3.582-7 8-7s8 3.134 8 7-3.582 7-8 7a9.06 9.06 0 0 1-2.347-.306c-.52.263-1.639.742-3.468 1.105z" />
114
- <path d="M4 5.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zM4 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 4 8zm0 2.5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5z" />
115
- </svg>
116
- </span>
117
- <div>
118
- <h6 class="mb-0 text-muted"><%= t("raif.admin.common.conversation_entries") %></h6>
119
- </div>
120
- </div>
121
- <h3 class="fw-bold mb-0"><%= number_with_delimiter(@conversation_entry_count) %></h3>
122
- </div>
123
- </div>
83
+ <%= render "stats_tile",
84
+ icon_color: "warning",
85
+ title: t("raif.admin.common.conversation_entries"),
86
+ count: @conversation_entry_count,
87
+ show_costs: true,
88
+ input_cost: @conversation_entry_input_token_cost,
89
+ output_cost: @conversation_entry_output_token_cost,
90
+ total_cost: @conversation_entry_total_cost do %>
91
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-chat-text" viewBox="0 0 16 16">
92
+ <path d="M2.678 11.894a1 1 0 0 1 .287.801 10.97 10.97 0 0 1-.398 2c1.395-.323 2.247-.697 2.634-.893a1 1 0 0 1 .71-.074A8.06 8.06 0 0 0 8 14c3.996 0 7-2.807 7-6 0-3.192-3.004-6-7-6S1 4.808 1 8c0 1.468.617 2.83 1.678 3.894zm-.493 3.905a21.682 21.682 0 0 1-.713.129c-.2.032-.352-.176-.273-.362a9.68 9.68 0 0 0 .244-.637l.003-.01c.248-.72.45-1.548.524-2.319C.743 11.37 0 9.76 0 8c0-3.866 3.582-7 8-7s8 3.134 8 7-3.582 7-8 7a9.06 9.06 0 0 1-2.347-.306c-.52.263-1.639.742-3.468 1.105z" />
93
+ <path d="M4 5.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zM4 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 4 8zm0 2.5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5z" />
94
+ </svg>
95
+ <% end %>
96
+
97
+ <!-- Model Tool Invocations -->
98
+ <%= render "stats_tile",
99
+ icon_color: "secondary",
100
+ title: t("raif.admin.common.model_tool_invocations"),
101
+ count: @model_tool_invocation_count,
102
+ details_link: raif.admin_stats_model_tool_invocations_path(period: @selected_period) do %>
103
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-tools" viewBox="0 0 16 16">
104
+ <path d="M1 0 0 1l2.2 3.081a1 1 0 0 0 .815.419h.07a1 1 0 0 1 .708.293l2.675 2.675-2.617 2.654A3.003 3.003 0 0 0 0 13a3 3 0 1 0 5.878-.851l2.654-2.617.968.968-.305.914a1 1 0 0 0 .242 1.023l3.27 3.27a.997.997 0 0 0 1.414 0l1.586-1.586a.997.997 0 0 0 0-1.414l-3.27-3.27a1 1 0 0 0-1.023-.242L10.5 9.5l-.96-.96 2.68-2.643A3.005 3.005 0 0 0 16 3c0-.269-.035-.53-.102-.777l-2.14 2.141L12 4l-.364-1.757L13.777.102a3 3 0 0 0-3.675 3.68L7.462 6.46 4.793 3.793a1 1 0 0 1-.293-.707v-.071a1 1 0 0 0-.419-.814L1 0Zm9.646 10.646a.5.5 0 0 1 .708 0l2.914 2.915a.5.5 0 0 1-.707.707l-2.915-2.914a.5.5 0 0 1 0-.708ZM3 11l.471.242.529.026.287.445.445.287.026.529L5 13l-.242.471-.026.529-.445.287-.287.445-.529.026L3 15l-.471-.242L2 14.732l-.287-.445L1.268 14l-.026-.529L1 13l.242-.471.026-.529.445-.287.287-.445.529-.026L3 11Z" />
105
+ </svg>
106
+ <% end %>
124
107
  </div>
125
108
  </div>
126
109
  </div>
@@ -0,0 +1,43 @@
1
+ <%= link_to raif.admin_stats_path do %>
2
+ &laquo; <%= t("raif.admin.stats.model_tool_invocations.back_to_stats") %>
3
+ <% end %>
4
+
5
+ <div class="d-flex justify-content-between align-items-center my-4">
6
+ <h1 class="mb-0"><%= t("raif.admin.stats.model_tool_invocations.title") %></h1>
7
+
8
+ <div class="period-filter">
9
+ <%= form_tag raif.admin_stats_model_tool_invocations_path, method: :get, id: "period_filter_form", class: "d-flex align-items-center" do %>
10
+ <%= select_tag :period,
11
+ options_for_select(
12
+ [
13
+ [t("raif.admin.common.period_day"), "day"],
14
+ [t("raif.admin.common.period_week"), "week"],
15
+ [t("raif.admin.common.period_month"), "month"],
16
+ [t("raif.admin.common.period_all"), "all"]
17
+ ],
18
+ @selected_period
19
+ ),
20
+ class: "form-select form-select-sm me-2" %>
21
+ <%= submit_tag t("raif.admin.common.update"), class: "btn btn-sm btn-primary" %>
22
+ <% end %>
23
+ </div>
24
+ </div>
25
+
26
+ <div class="table-responsive">
27
+ <table class="table table-striped table-hover">
28
+ <thead class="table-light">
29
+ <tr>
30
+ <th><%= t("raif.admin.common.tool_type") %></th>
31
+ <th class="text-end"><%= t("raif.admin.common.count") %></th>
32
+ </tr>
33
+ </thead>
34
+ <tbody>
35
+ <% @model_tool_invocation_stats_by_type.each do |tool_type, count| %>
36
+ <tr>
37
+ <td><%= tool_type %></td>
38
+ <td class="text-end"><%= number_with_delimiter(count) %></td>
39
+ </tr>
40
+ <% end %>
41
+ </tbody>
42
+ </table>
43
+ </div>