ruby_llm-agents 0.3.3 → 0.3.4
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.
- checksums.yaml +4 -4
- data/app/controllers/ruby_llm/agents/dashboard_controller.rb +68 -4
- data/app/models/ruby_llm/agents/execution/analytics.rb +114 -13
- data/app/models/ruby_llm/agents/execution.rb +19 -58
- data/app/views/layouts/rubyllm/agents/application.html.erb +92 -350
- data/app/views/rubyllm/agents/agents/show.html.erb +331 -385
- data/app/views/rubyllm/agents/dashboard/_agent_comparison.html.erb +46 -0
- data/app/views/rubyllm/agents/dashboard/_budgets_bar.html.erb +0 -90
- data/app/views/rubyllm/agents/dashboard/_now_strip.html.erb +79 -5
- data/app/views/rubyllm/agents/dashboard/_top_errors.html.erb +49 -0
- data/app/views/rubyllm/agents/dashboard/index.html.erb +76 -121
- data/app/views/rubyllm/agents/executions/show.html.erb +134 -85
- data/app/views/rubyllm/agents/settings/show.html.erb +1 -1
- data/app/views/rubyllm/agents/shared/_breadcrumbs.html.erb +48 -0
- data/app/views/rubyllm/agents/shared/_nav_link.html.erb +27 -0
- data/config/routes.rb +2 -0
- data/lib/ruby_llm/agents/base/caching.rb +43 -0
- data/lib/ruby_llm/agents/base/cost_calculation.rb +103 -0
- data/lib/ruby_llm/agents/base/dsl.rb +261 -0
- data/lib/ruby_llm/agents/base/execution.rb +206 -0
- data/lib/ruby_llm/agents/base/reliability_execution.rb +131 -0
- data/lib/ruby_llm/agents/base/response_building.rb +86 -0
- data/lib/ruby_llm/agents/base/tool_tracking.rb +57 -0
- data/lib/ruby_llm/agents/base.rb +15 -805
- data/lib/ruby_llm/agents/version.rb +1 -1
- metadata +12 -20
- data/app/channels/ruby_llm/agents/executions_channel.rb +0 -46
- data/app/javascript/ruby_llm/agents/controllers/filter_controller.js +0 -56
- data/app/javascript/ruby_llm/agents/controllers/index.js +0 -12
- data/app/javascript/ruby_llm/agents/controllers/refresh_controller.js +0 -83
- data/app/views/rubyllm/agents/dashboard/_now_strip_values.html.erb +0 -71
|
@@ -1,23 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
fill="none"
|
|
7
|
-
stroke="currentColor"
|
|
8
|
-
viewBox="0 0 24 24"
|
|
9
|
-
>
|
|
10
|
-
<path
|
|
11
|
-
stroke-linecap="round"
|
|
12
|
-
stroke-linejoin="round"
|
|
13
|
-
stroke-width="2"
|
|
14
|
-
d="M10 19l-7-7m0 0l7-7m-7 7h18"
|
|
15
|
-
/>
|
|
16
|
-
</svg>
|
|
17
|
-
Back to Agents
|
|
18
|
-
</span>
|
|
19
|
-
<% end %>
|
|
20
|
-
</div>
|
|
1
|
+
<%= render "rubyllm/agents/shared/breadcrumbs", items: [
|
|
2
|
+
{ label: "Dashboard", path: ruby_llm_agents.root_path },
|
|
3
|
+
{ label: "Agents", path: ruby_llm_agents.agents_path },
|
|
4
|
+
{ label: @agent_type.gsub(/Agent$/, '') }
|
|
5
|
+
] %>
|
|
21
6
|
|
|
22
7
|
<!-- Header -->
|
|
23
8
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 mb-6">
|
|
@@ -49,7 +34,9 @@
|
|
|
49
34
|
<% end %>
|
|
50
35
|
|
|
51
36
|
<% if @config %>
|
|
52
|
-
<span class="text-sm text-gray-500 dark:text-gray-400">
|
|
37
|
+
<span class="text-sm text-gray-500 dark:text-gray-400">
|
|
38
|
+
v<%= @config[:version] %>
|
|
39
|
+
</span>
|
|
53
40
|
<% end %>
|
|
54
41
|
</div>
|
|
55
42
|
|
|
@@ -65,6 +52,7 @@
|
|
|
65
52
|
<p class="text-sm text-gray-500 dark:text-gray-400">
|
|
66
53
|
<%= number_with_delimiter(@stats[:count]) %> total executions
|
|
67
54
|
</p>
|
|
55
|
+
|
|
68
56
|
<div class="flex items-center justify-end gap-3 mt-1">
|
|
69
57
|
<% status_colors = {
|
|
70
58
|
"success" => "bg-green-500",
|
|
@@ -72,9 +60,11 @@
|
|
|
72
60
|
"timeout" => "bg-yellow-500",
|
|
73
61
|
"running" => "bg-blue-500"
|
|
74
62
|
} %>
|
|
63
|
+
|
|
75
64
|
<% @status_distribution.each do |status, count| %>
|
|
76
65
|
<div class="flex items-center gap-1">
|
|
77
66
|
<span class="w-2 h-2 rounded-full <%= status_colors[status] || 'bg-gray-400' %> <%= status == 'running' ? 'animate-pulse' : '' %>"></span>
|
|
67
|
+
|
|
78
68
|
<span class="text-xs text-gray-600 dark:text-gray-400">
|
|
79
69
|
<%= number_with_delimiter(count) %>
|
|
80
70
|
</span>
|
|
@@ -89,8 +79,18 @@
|
|
|
89
79
|
<% if @config && @circuit_breaker_status.present? %>
|
|
90
80
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4 mb-6">
|
|
91
81
|
<div class="flex items-center justify-between mb-3">
|
|
92
|
-
<h3
|
|
93
|
-
|
|
82
|
+
<h3
|
|
83
|
+
class="
|
|
84
|
+
text-sm font-medium text-gray-700 dark:text-gray-300 uppercase
|
|
85
|
+
tracking-wider
|
|
86
|
+
"
|
|
87
|
+
>
|
|
88
|
+
Circuit Breaker Status
|
|
89
|
+
</h3>
|
|
90
|
+
|
|
91
|
+
<span class="text-xs text-gray-400 dark:text-gray-500">
|
|
92
|
+
Auto-refreshes every 30s
|
|
93
|
+
</span>
|
|
94
94
|
</div>
|
|
95
95
|
|
|
96
96
|
<div class="flex flex-wrap gap-3">
|
|
@@ -99,11 +99,21 @@
|
|
|
99
99
|
<% if status[:open] %>
|
|
100
100
|
<!-- Open/Tripped indicator -->
|
|
101
101
|
<span class="relative flex h-3 w-3">
|
|
102
|
-
<span
|
|
102
|
+
<span
|
|
103
|
+
class="
|
|
104
|
+
animate-ping absolute inline-flex h-full w-full
|
|
105
|
+
rounded-full bg-red-400 opacity-75
|
|
106
|
+
"
|
|
107
|
+
></span>
|
|
108
|
+
|
|
103
109
|
<span class="relative inline-flex rounded-full h-3 w-3 bg-red-500"></span>
|
|
104
110
|
</span>
|
|
105
|
-
<span class="text-sm font-medium text-red-700 dark:text-red-300"
|
|
106
|
-
|
|
111
|
+
<span class="text-sm font-medium text-red-700 dark:text-red-300">
|
|
112
|
+
<%= model_id %>
|
|
113
|
+
</span>
|
|
114
|
+
<span class="text-xs text-red-500 dark:text-red-400 ml-1">
|
|
115
|
+
OPEN
|
|
116
|
+
</span>
|
|
107
117
|
<% if status[:cooldown_remaining] %>
|
|
108
118
|
<span class="text-xs text-red-400 dark:text-red-500 ml-1">
|
|
109
119
|
(resets in <%= status[:cooldown_remaining] %>s)
|
|
@@ -112,11 +122,16 @@
|
|
|
112
122
|
<% else %>
|
|
113
123
|
<!-- Closed/Healthy indicator -->
|
|
114
124
|
<span class="inline-flex rounded-full h-3 w-3 bg-green-500"></span>
|
|
115
|
-
<span class="text-sm font-medium text-green-700 dark:text-green-300"
|
|
116
|
-
|
|
125
|
+
<span class="text-sm font-medium text-green-700 dark:text-green-300">
|
|
126
|
+
<%= model_id %>
|
|
127
|
+
</span>
|
|
128
|
+
<span class="text-xs text-green-500 dark:text-green-400 ml-1">
|
|
129
|
+
OK
|
|
130
|
+
</span>
|
|
117
131
|
<% if status[:failure_count] && status[:failure_count] > 0 %>
|
|
118
132
|
<span class="text-xs text-yellow-500 dark:text-yellow-400 ml-1">
|
|
119
|
-
(<%= status[:failure_count] %>/<%= status[:threshold] %>
|
|
133
|
+
(<%= status[:failure_count] %>/<%= status[:threshold] %>
|
|
134
|
+
failures)
|
|
120
135
|
</span>
|
|
121
136
|
<% end %>
|
|
122
137
|
<% end %>
|
|
@@ -126,8 +141,8 @@
|
|
|
126
141
|
|
|
127
142
|
<% if @circuit_breaker_status.values.any? { |s| s[:open] } %>
|
|
128
143
|
<p class="mt-3 text-xs text-gray-500 dark:text-gray-400">
|
|
129
|
-
Open circuit breakers will skip the model and try fallbacks (if
|
|
130
|
-
They automatically reset after the cooldown period.
|
|
144
|
+
Open circuit breakers will skip the model and try fallbacks (if
|
|
145
|
+
configured). They automatically reset after the cooldown period.
|
|
131
146
|
</p>
|
|
132
147
|
<% end %>
|
|
133
148
|
</div>
|
|
@@ -136,6 +151,7 @@
|
|
|
136
151
|
<!-- Stats Grid -->
|
|
137
152
|
<% success_rate = @stats[:success_rate] || 0 %>
|
|
138
153
|
<% success_rate_color = success_rate >= 95 ? 'text-green-600' : success_rate >= 80 ? 'text-yellow-600' : 'text-red-600' %>
|
|
154
|
+
|
|
139
155
|
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4 mb-6">
|
|
140
156
|
<%= render "rubyllm/agents/shared/stat_card",
|
|
141
157
|
title: "Executions",
|
|
@@ -182,46 +198,60 @@
|
|
|
182
198
|
<!-- Charts Section -->
|
|
183
199
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
|
184
200
|
<!-- Executions Over Time -->
|
|
185
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6
|
|
201
|
+
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 overflow-hidden">
|
|
186
202
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
|
|
187
203
|
Executions (30 days)
|
|
188
204
|
</h3>
|
|
189
|
-
|
|
190
|
-
<div id="executions-chart" style="height: 250px;">
|
|
191
|
-
<% success_data = @trend_data.map { |d| [d[:date].strftime("%b %d"), d[:count] - (d[:error_count] || 0)] }.to_h
|
|
192
|
-
failed_data = @trend_data.map { |d| [d[:date].strftime("%b %d"), d[:error_count] || 0] }.to_h %>
|
|
193
|
-
|
|
194
|
-
<%= area_chart [
|
|
195
|
-
{ name: "Success", data: success_data },
|
|
196
|
-
{ name: "Failed", data: failed_data }
|
|
197
|
-
], colors: ["#10B981", "#EF4444"], stacked: true, library: {
|
|
198
|
-
yAxis: { min: 0 },
|
|
199
|
-
legend: { align: "center", verticalAlign: "bottom" },
|
|
200
|
-
plotOptions: { area: { stacking: "normal" } }
|
|
201
|
-
} %>
|
|
202
|
-
</div>
|
|
205
|
+
<div id="executions-chart" style="height: 220px;"></div>
|
|
203
206
|
</div>
|
|
204
207
|
|
|
205
208
|
<!-- Cost Over Time -->
|
|
206
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6
|
|
207
|
-
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
<%= line_chart cost_data, colors: ["#10B981"], prefix: "$", library: {
|
|
213
|
-
yAxis: { min: 0 },
|
|
214
|
-
legend: { enabled: false }
|
|
215
|
-
} %>
|
|
216
|
-
</div>
|
|
209
|
+
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 overflow-hidden">
|
|
210
|
+
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
|
|
211
|
+
Cost (30 days)
|
|
212
|
+
</h3>
|
|
213
|
+
<div id="cost-chart" style="height: 220px;"></div>
|
|
217
214
|
</div>
|
|
218
215
|
</div>
|
|
219
216
|
|
|
217
|
+
<script>
|
|
218
|
+
(function() {
|
|
219
|
+
const trendData = <%= raw @trend_data.to_json %>;
|
|
220
|
+
const categories = trendData.map(d => d.date.split('-').slice(1).join('/'));
|
|
221
|
+
|
|
222
|
+
// Executions chart
|
|
223
|
+
Highcharts.chart('executions-chart', {
|
|
224
|
+
chart: { type: 'areaspline', backgroundColor: 'transparent' },
|
|
225
|
+
xAxis: { categories: categories, labels: { style: { color: '#9CA3AF' } } },
|
|
226
|
+
yAxis: { min: 0, title: { text: null }, labels: { style: { color: '#9CA3AF' } } },
|
|
227
|
+
legend: { enabled: false },
|
|
228
|
+
plotOptions: { areaspline: { fillOpacity: 0.2, marker: { enabled: false } } },
|
|
229
|
+
series: [
|
|
230
|
+
{ name: 'Success', color: '#10B981', data: trendData.map(d => d.count - (d.error_count || 0)) },
|
|
231
|
+
{ name: 'Failed', color: '#EF4444', data: trendData.map(d => d.error_count || 0) }
|
|
232
|
+
]
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Cost chart
|
|
236
|
+
Highcharts.chart('cost-chart', {
|
|
237
|
+
chart: { type: 'spline', backgroundColor: 'transparent' },
|
|
238
|
+
xAxis: { categories: categories, labels: { style: { color: '#9CA3AF' } } },
|
|
239
|
+
yAxis: { min: 0, title: { text: null }, labels: { style: { color: '#9CA3AF' }, format: '${value}' } },
|
|
240
|
+
legend: { enabled: false },
|
|
241
|
+
plotOptions: { spline: { marker: { enabled: false } } },
|
|
242
|
+
tooltip: { valuePrefix: '$' },
|
|
243
|
+
series: [{ name: 'Cost', color: '#10B981', data: trendData.map(d => parseFloat(d.total_cost) || 0) }]
|
|
244
|
+
});
|
|
245
|
+
})();
|
|
246
|
+
</script>
|
|
247
|
+
|
|
220
248
|
<!-- Finish Reason Distribution -->
|
|
221
249
|
<% if @finish_reason_distribution.present? && @finish_reason_distribution.any? %>
|
|
222
250
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4 mb-6">
|
|
223
251
|
<div class="flex items-center justify-between">
|
|
224
|
-
<p class="text-sm text-gray-500 dark:text-gray-400 uppercase">
|
|
252
|
+
<p class="text-sm text-gray-500 dark:text-gray-400 uppercase">
|
|
253
|
+
Finish Reasons
|
|
254
|
+
</p>
|
|
225
255
|
|
|
226
256
|
<div class="flex flex-wrap gap-4">
|
|
227
257
|
<% finish_colors = {
|
|
@@ -239,7 +269,9 @@
|
|
|
239
269
|
style="background-color: <%= finish_colors[reason] || '#6B7280' %>"
|
|
240
270
|
></span>
|
|
241
271
|
|
|
242
|
-
<span class="text-sm text-gray-700 dark:text-gray-300"
|
|
272
|
+
<span class="text-sm text-gray-700 dark:text-gray-300">
|
|
273
|
+
<%= reason || 'unknown' %>
|
|
274
|
+
</span>
|
|
243
275
|
|
|
244
276
|
<span class="text-sm font-medium text-gray-900 dark:text-gray-100 ml-1">
|
|
245
277
|
(<%= number_with_delimiter(count) %>)
|
|
@@ -258,31 +290,48 @@
|
|
|
258
290
|
<% if @config %>
|
|
259
291
|
<!-- Configuration -->
|
|
260
292
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 mb-6">
|
|
261
|
-
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
|
|
293
|
+
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
|
|
294
|
+
Configuration
|
|
295
|
+
</h3>
|
|
262
296
|
|
|
263
297
|
<!-- Basic Configuration -->
|
|
264
|
-
<p class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-3">
|
|
298
|
+
<p class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-3">
|
|
299
|
+
Basic
|
|
300
|
+
</p>
|
|
301
|
+
|
|
265
302
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
|
|
266
303
|
<div>
|
|
267
304
|
<p class="text-sm text-gray-500 dark:text-gray-400">Model</p>
|
|
268
|
-
|
|
305
|
+
|
|
306
|
+
<p class="font-medium text-gray-900 dark:text-gray-100">
|
|
307
|
+
<%= @config[:model] %>
|
|
308
|
+
</p>
|
|
269
309
|
</div>
|
|
270
310
|
|
|
271
311
|
<div>
|
|
272
312
|
<p class="text-sm text-gray-500 dark:text-gray-400">Temperature</p>
|
|
273
|
-
|
|
313
|
+
|
|
314
|
+
<p class="font-medium text-gray-900 dark:text-gray-100">
|
|
315
|
+
<%= @config[:temperature] %>
|
|
316
|
+
</p>
|
|
274
317
|
</div>
|
|
275
318
|
|
|
276
319
|
<div>
|
|
277
320
|
<p class="text-sm text-gray-500 dark:text-gray-400">Timeout</p>
|
|
278
|
-
|
|
321
|
+
|
|
322
|
+
<p class="font-medium text-gray-900 dark:text-gray-100">
|
|
323
|
+
<%= @config[:timeout] %> seconds
|
|
324
|
+
</p>
|
|
279
325
|
</div>
|
|
280
326
|
|
|
281
327
|
<div>
|
|
282
328
|
<p class="text-sm text-gray-500 dark:text-gray-400">Cache</p>
|
|
329
|
+
|
|
283
330
|
<p class="font-medium text-gray-900 dark:text-gray-100">
|
|
284
331
|
<% if @config[:cache_enabled] %>
|
|
285
|
-
Enabled (
|
|
332
|
+
Enabled (
|
|
333
|
+
<%= @config[:cache_ttl].inspect %>
|
|
334
|
+
)
|
|
286
335
|
<% else %>
|
|
287
336
|
<span class="text-gray-400 dark:text-gray-500">Disabled</span>
|
|
288
337
|
<% end %>
|
|
@@ -291,16 +340,22 @@
|
|
|
291
340
|
</div>
|
|
292
341
|
|
|
293
342
|
<!-- Reliability Configuration -->
|
|
294
|
-
<%
|
|
295
|
-
retries_config = @config[:retries] || {}
|
|
343
|
+
<% retries_config = @config[:retries] || {}
|
|
296
344
|
has_retries = (retries_config[:max] || 0) > 0
|
|
297
345
|
has_fallbacks = @config[:fallback_models].present? && @config[:fallback_models].any?
|
|
298
346
|
has_total_timeout = @config[:total_timeout].present?
|
|
299
347
|
has_circuit_breaker = @config[:circuit_breaker].present?
|
|
300
|
-
has_any_reliability = has_retries || has_fallbacks || has_total_timeout || has_circuit_breaker
|
|
301
|
-
|
|
348
|
+
has_any_reliability = has_retries || has_fallbacks || has_total_timeout || has_circuit_breaker %>
|
|
349
|
+
|
|
302
350
|
<div class="border-t border-gray-100 dark:border-gray-700 pt-4 mb-6">
|
|
303
|
-
<p
|
|
351
|
+
<p
|
|
352
|
+
class="
|
|
353
|
+
text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider
|
|
354
|
+
mb-3
|
|
355
|
+
"
|
|
356
|
+
>
|
|
357
|
+
Reliability
|
|
358
|
+
</p>
|
|
304
359
|
|
|
305
360
|
<% if has_any_reliability %>
|
|
306
361
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
@@ -308,26 +363,48 @@
|
|
|
308
363
|
<div class="flex items-start gap-3 p-3 rounded-lg <%= has_retries ? 'bg-green-50 dark:bg-green-900/20' : 'bg-gray-50 dark:bg-gray-700/50' %>">
|
|
309
364
|
<div class="flex-shrink-0 mt-0.5">
|
|
310
365
|
<% if has_retries %>
|
|
311
|
-
<svg
|
|
312
|
-
|
|
366
|
+
<svg
|
|
367
|
+
class="w-5 h-5 text-green-500"
|
|
368
|
+
fill="currentColor"
|
|
369
|
+
viewBox="0 0 20 20"
|
|
370
|
+
>
|
|
371
|
+
<path
|
|
372
|
+
fill-rule="evenodd"
|
|
373
|
+
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
|
|
374
|
+
clip-rule="evenodd"
|
|
375
|
+
/>
|
|
313
376
|
</svg>
|
|
314
377
|
<% else %>
|
|
315
|
-
<svg
|
|
316
|
-
|
|
378
|
+
<svg
|
|
379
|
+
class="w-5 h-5 text-gray-400"
|
|
380
|
+
fill="currentColor"
|
|
381
|
+
viewBox="0 0 20 20"
|
|
382
|
+
>
|
|
383
|
+
<path
|
|
384
|
+
fill-rule="evenodd"
|
|
385
|
+
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
|
|
386
|
+
clip-rule="evenodd"
|
|
387
|
+
/>
|
|
317
388
|
</svg>
|
|
318
389
|
<% end %>
|
|
319
390
|
</div>
|
|
391
|
+
|
|
320
392
|
<div>
|
|
321
|
-
<p class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
393
|
+
<p class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
394
|
+
Retries
|
|
395
|
+
</p>
|
|
396
|
+
|
|
322
397
|
<% if has_retries %>
|
|
323
398
|
<p class="text-xs text-gray-600 dark:text-gray-400 mt-1">
|
|
324
|
-
Max: <%= retries_config[:max] %> ·
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
399
|
+
Max: <%= retries_config[:max] %> · Backoff:
|
|
400
|
+
<%= retries_config[:backoff] %> · Base:
|
|
401
|
+
<%= retries_config[:base] %>s · Max delay:
|
|
402
|
+
<%= retries_config[:max_delay] %>s
|
|
328
403
|
</p>
|
|
329
404
|
<% else %>
|
|
330
|
-
<p class="text-xs text-gray-400 dark:text-gray-500 mt-1">
|
|
405
|
+
<p class="text-xs text-gray-400 dark:text-gray-500 mt-1">
|
|
406
|
+
Not configured
|
|
407
|
+
</p>
|
|
331
408
|
<% end %>
|
|
332
409
|
</div>
|
|
333
410
|
</div>
|
|
@@ -336,23 +413,45 @@
|
|
|
336
413
|
<div class="flex items-start gap-3 p-3 rounded-lg <%= has_fallbacks ? 'bg-green-50 dark:bg-green-900/20' : 'bg-gray-50 dark:bg-gray-700/50' %>">
|
|
337
414
|
<div class="flex-shrink-0 mt-0.5">
|
|
338
415
|
<% if has_fallbacks %>
|
|
339
|
-
<svg
|
|
340
|
-
|
|
416
|
+
<svg
|
|
417
|
+
class="w-5 h-5 text-green-500"
|
|
418
|
+
fill="currentColor"
|
|
419
|
+
viewBox="0 0 20 20"
|
|
420
|
+
>
|
|
421
|
+
<path
|
|
422
|
+
fill-rule="evenodd"
|
|
423
|
+
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
|
|
424
|
+
clip-rule="evenodd"
|
|
425
|
+
/>
|
|
341
426
|
</svg>
|
|
342
427
|
<% else %>
|
|
343
|
-
<svg
|
|
344
|
-
|
|
428
|
+
<svg
|
|
429
|
+
class="w-5 h-5 text-gray-400"
|
|
430
|
+
fill="currentColor"
|
|
431
|
+
viewBox="0 0 20 20"
|
|
432
|
+
>
|
|
433
|
+
<path
|
|
434
|
+
fill-rule="evenodd"
|
|
435
|
+
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
|
|
436
|
+
clip-rule="evenodd"
|
|
437
|
+
/>
|
|
345
438
|
</svg>
|
|
346
439
|
<% end %>
|
|
347
440
|
</div>
|
|
441
|
+
|
|
348
442
|
<div>
|
|
349
|
-
<p class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
443
|
+
<p class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
444
|
+
Fallback Models
|
|
445
|
+
</p>
|
|
446
|
+
|
|
350
447
|
<% if has_fallbacks %>
|
|
351
448
|
<p class="text-xs text-gray-600 dark:text-gray-400 mt-1">
|
|
352
449
|
<%= @config[:fallback_models].join(" → ") %>
|
|
353
450
|
</p>
|
|
354
451
|
<% else %>
|
|
355
|
-
<p class="text-xs text-gray-400 dark:text-gray-500 mt-1">
|
|
452
|
+
<p class="text-xs text-gray-400 dark:text-gray-500 mt-1">
|
|
453
|
+
Not configured
|
|
454
|
+
</p>
|
|
356
455
|
<% end %>
|
|
357
456
|
</div>
|
|
358
457
|
</div>
|
|
@@ -361,23 +460,45 @@
|
|
|
361
460
|
<div class="flex items-start gap-3 p-3 rounded-lg <%= has_total_timeout ? 'bg-green-50 dark:bg-green-900/20' : 'bg-gray-50 dark:bg-gray-700/50' %>">
|
|
362
461
|
<div class="flex-shrink-0 mt-0.5">
|
|
363
462
|
<% if has_total_timeout %>
|
|
364
|
-
<svg
|
|
365
|
-
|
|
463
|
+
<svg
|
|
464
|
+
class="w-5 h-5 text-green-500"
|
|
465
|
+
fill="currentColor"
|
|
466
|
+
viewBox="0 0 20 20"
|
|
467
|
+
>
|
|
468
|
+
<path
|
|
469
|
+
fill-rule="evenodd"
|
|
470
|
+
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
|
|
471
|
+
clip-rule="evenodd"
|
|
472
|
+
/>
|
|
366
473
|
</svg>
|
|
367
474
|
<% else %>
|
|
368
|
-
<svg
|
|
369
|
-
|
|
475
|
+
<svg
|
|
476
|
+
class="w-5 h-5 text-gray-400"
|
|
477
|
+
fill="currentColor"
|
|
478
|
+
viewBox="0 0 20 20"
|
|
479
|
+
>
|
|
480
|
+
<path
|
|
481
|
+
fill-rule="evenodd"
|
|
482
|
+
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
|
|
483
|
+
clip-rule="evenodd"
|
|
484
|
+
/>
|
|
370
485
|
</svg>
|
|
371
486
|
<% end %>
|
|
372
487
|
</div>
|
|
488
|
+
|
|
373
489
|
<div>
|
|
374
|
-
<p class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
490
|
+
<p class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
491
|
+
Total Timeout
|
|
492
|
+
</p>
|
|
493
|
+
|
|
375
494
|
<% if has_total_timeout %>
|
|
376
495
|
<p class="text-xs text-gray-600 dark:text-gray-400 mt-1">
|
|
377
496
|
<%= @config[:total_timeout] %> seconds across all attempts
|
|
378
497
|
</p>
|
|
379
498
|
<% else %>
|
|
380
|
-
<p class="text-xs text-gray-400 dark:text-gray-500 mt-1">
|
|
499
|
+
<p class="text-xs text-gray-400 dark:text-gray-500 mt-1">
|
|
500
|
+
Not configured
|
|
501
|
+
</p>
|
|
381
502
|
<% end %>
|
|
382
503
|
</div>
|
|
383
504
|
</div>
|
|
@@ -386,43 +507,79 @@
|
|
|
386
507
|
<div class="flex items-start gap-3 p-3 rounded-lg <%= has_circuit_breaker ? 'bg-green-50 dark:bg-green-900/20' : 'bg-gray-50 dark:bg-gray-700/50' %>">
|
|
387
508
|
<div class="flex-shrink-0 mt-0.5">
|
|
388
509
|
<% if has_circuit_breaker %>
|
|
389
|
-
<svg
|
|
390
|
-
|
|
510
|
+
<svg
|
|
511
|
+
class="w-5 h-5 text-green-500"
|
|
512
|
+
fill="currentColor"
|
|
513
|
+
viewBox="0 0 20 20"
|
|
514
|
+
>
|
|
515
|
+
<path
|
|
516
|
+
fill-rule="evenodd"
|
|
517
|
+
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
|
|
518
|
+
clip-rule="evenodd"
|
|
519
|
+
/>
|
|
391
520
|
</svg>
|
|
392
521
|
<% else %>
|
|
393
|
-
<svg
|
|
394
|
-
|
|
522
|
+
<svg
|
|
523
|
+
class="w-5 h-5 text-gray-400"
|
|
524
|
+
fill="currentColor"
|
|
525
|
+
viewBox="0 0 20 20"
|
|
526
|
+
>
|
|
527
|
+
<path
|
|
528
|
+
fill-rule="evenodd"
|
|
529
|
+
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
|
|
530
|
+
clip-rule="evenodd"
|
|
531
|
+
/>
|
|
395
532
|
</svg>
|
|
396
533
|
<% end %>
|
|
397
534
|
</div>
|
|
535
|
+
|
|
398
536
|
<div>
|
|
399
|
-
<p class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
537
|
+
<p class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
538
|
+
Circuit Breaker
|
|
539
|
+
</p>
|
|
540
|
+
|
|
400
541
|
<% if has_circuit_breaker %>
|
|
401
542
|
<% cb = @config[:circuit_breaker] %>
|
|
402
543
|
<p class="text-xs text-gray-600 dark:text-gray-400 mt-1">
|
|
403
|
-
Opens after <%= cb[:errors] %> errors within
|
|
404
|
-
Cooldown: <%= cb[:cooldown] %>s
|
|
544
|
+
Opens after <%= cb[:errors] %> errors within
|
|
545
|
+
<%= cb[:within] %>s · Cooldown: <%= cb[:cooldown] %>s
|
|
405
546
|
</p>
|
|
406
547
|
<% else %>
|
|
407
|
-
<p class="text-xs text-gray-400 dark:text-gray-500 mt-1">
|
|
548
|
+
<p class="text-xs text-gray-400 dark:text-gray-500 mt-1">
|
|
549
|
+
Not configured
|
|
550
|
+
</p>
|
|
408
551
|
<% end %>
|
|
409
552
|
</div>
|
|
410
553
|
</div>
|
|
411
554
|
</div>
|
|
412
555
|
<% else %>
|
|
413
|
-
<p class="text-sm text-gray-400 dark:text-gray-500">
|
|
556
|
+
<p class="text-sm text-gray-400 dark:text-gray-500">
|
|
557
|
+
No reliability features configured
|
|
558
|
+
</p>
|
|
414
559
|
<% end %>
|
|
415
560
|
</div>
|
|
416
561
|
|
|
417
562
|
<!-- Parameters -->
|
|
418
563
|
<% if @config[:params].present? && @config[:params].any? %>
|
|
419
564
|
<div class="border-t border-gray-100 dark:border-gray-700 pt-4">
|
|
420
|
-
<p
|
|
565
|
+
<p
|
|
566
|
+
class="
|
|
567
|
+
text-xs text-gray-500 dark:text-gray-400 uppercase
|
|
568
|
+
tracking-wider mb-3
|
|
569
|
+
"
|
|
570
|
+
>
|
|
571
|
+
Parameters
|
|
572
|
+
</p>
|
|
421
573
|
|
|
422
574
|
<div class="space-y-2">
|
|
423
575
|
<% @config[:params].each do |name, opts| %>
|
|
424
576
|
<div class="flex items-center text-sm">
|
|
425
|
-
<code
|
|
577
|
+
<code
|
|
578
|
+
class="
|
|
579
|
+
bg-gray-100 dark:bg-gray-700 dark:text-gray-200 px-2
|
|
580
|
+
py-0.5 rounded font-mono
|
|
581
|
+
"
|
|
582
|
+
>
|
|
426
583
|
<%= name %>
|
|
427
584
|
</code>
|
|
428
585
|
|
|
@@ -435,7 +592,9 @@
|
|
|
435
592
|
default: <%= opts[:default].inspect %>
|
|
436
593
|
</span>
|
|
437
594
|
<% else %>
|
|
438
|
-
<span class="ml-2 text-xs text-gray-400 dark:text-gray-500">
|
|
595
|
+
<span class="ml-2 text-xs text-gray-400 dark:text-gray-500">
|
|
596
|
+
optional
|
|
597
|
+
</span>
|
|
439
598
|
<% end %>
|
|
440
599
|
</div>
|
|
441
600
|
<% end %>
|
|
@@ -447,7 +606,9 @@
|
|
|
447
606
|
|
|
448
607
|
<!-- Executions -->
|
|
449
608
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
|
450
|
-
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
|
|
609
|
+
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
|
|
610
|
+
Executions
|
|
611
|
+
</h3>
|
|
451
612
|
|
|
452
613
|
<%= turbo_frame_tag "executions_table" do %>
|
|
453
614
|
<%
|
|
@@ -456,217 +617,84 @@
|
|
|
456
617
|
selected_versions = params[:versions].present? ? (params[:versions].is_a?(Array) ? params[:versions] : params[:versions].split(",")) : []
|
|
457
618
|
selected_models = params[:models].present? ? (params[:models].is_a?(Array) ? params[:models] : params[:models].split(",")) : []
|
|
458
619
|
selected_temperatures = params[:temperatures].present? ? (params[:temperatures].is_a?(Array) ? params[:temperatures] : params[:temperatures].split(",")).map(&:to_s) : []
|
|
620
|
+
|
|
621
|
+
status_options = [
|
|
622
|
+
{ value: "success", label: "Success", color: "bg-green-500" },
|
|
623
|
+
{ value: "error", label: "Error", color: "bg-red-500" },
|
|
624
|
+
{ value: "running", label: "Running", color: "bg-blue-500" },
|
|
625
|
+
{ value: "timeout", label: "Timeout", color: "bg-yellow-500" }
|
|
626
|
+
]
|
|
627
|
+
version_options = @versions.map { |v| { value: v.to_s, label: "v#{v}" } }
|
|
628
|
+
model_options = @models.map { |m| { value: m, label: m } }
|
|
629
|
+
temperature_options = @temperatures.map { |t| { value: t.to_s, label: t.to_s } }
|
|
630
|
+
days_options = [
|
|
631
|
+
{ value: "", label: "All Time" },
|
|
632
|
+
{ value: "1", label: "Today" },
|
|
633
|
+
{ value: "7", label: "Last 7 Days" },
|
|
634
|
+
{ value: "30", label: "Last 30 Days" }
|
|
635
|
+
]
|
|
459
636
|
%>
|
|
460
637
|
|
|
461
|
-
<%= form_with url: ruby_llm_agents.agent_path(@agent_type), method: :get, data: { turbo_frame: "executions_table" }
|
|
638
|
+
<%= form_with url: ruby_llm_agents.agent_path(@agent_type), method: :get, data: { turbo_frame: "executions_table" } do |f| %>
|
|
462
639
|
<div class="flex flex-wrap items-center gap-3 mb-4 pb-4 border-b border-gray-100 dark:border-gray-700">
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
end
|
|
474
|
-
else
|
|
475
|
-
status_color = 'bg-gray-400'
|
|
476
|
-
end %>
|
|
477
|
-
<span class="w-2 h-2 rounded-full <%= status_color %>"></span>
|
|
478
|
-
<span class="dropdown-label text-gray-700 dark:text-gray-200">
|
|
479
|
-
<% if selected_statuses.empty? %>
|
|
480
|
-
All Statuses
|
|
481
|
-
<% elsif selected_statuses.length == 1 %>
|
|
482
|
-
<%= selected_statuses.first.capitalize %>
|
|
483
|
-
<% else %>
|
|
484
|
-
<%= selected_statuses.length %> Statuses
|
|
485
|
-
<% end %>
|
|
486
|
-
</span>
|
|
487
|
-
<svg class="w-4 h-4 text-gray-400 dark:text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
488
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
|
489
|
-
</svg>
|
|
490
|
-
</button>
|
|
491
|
-
<div class="dropdown-menu hidden absolute z-10 mt-1 w-44 bg-white dark:bg-gray-700 rounded-lg shadow-lg border border-gray-200 dark:border-gray-600 py-1">
|
|
492
|
-
<div class="px-3 py-2 border-b border-gray-100 dark:border-gray-600">
|
|
493
|
-
<label class="flex items-center gap-2 cursor-pointer">
|
|
494
|
-
<input type="checkbox" class="select-all-checkbox rounded border-gray-300 dark:border-gray-500 text-blue-600 focus:ring-blue-500 dark:bg-gray-600" onchange="toggleAllOptions(this, 'statuses')" <%= selected_statuses.empty? ? 'checked' : '' %>>
|
|
495
|
-
<span class="text-sm font-medium text-gray-700 dark:text-gray-200">All Statuses</span>
|
|
496
|
-
</label>
|
|
497
|
-
</div>
|
|
498
|
-
<label class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-600 cursor-pointer">
|
|
499
|
-
<input type="checkbox" name="statuses[]" value="success" class="filter-checkbox rounded border-gray-300 dark:border-gray-500 text-blue-600 focus:ring-blue-500 dark:bg-gray-600" onchange="updateMultiSelect('statuses')" <%= selected_statuses.include?('success') ? 'checked' : '' %>>
|
|
500
|
-
<span class="w-2 h-2 rounded-full bg-green-500"></span>
|
|
501
|
-
Success
|
|
502
|
-
</label>
|
|
503
|
-
<label class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-600 cursor-pointer">
|
|
504
|
-
<input type="checkbox" name="statuses[]" value="error" class="filter-checkbox rounded border-gray-300 dark:border-gray-500 text-blue-600 focus:ring-blue-500 dark:bg-gray-600" onchange="updateMultiSelect('statuses')" <%= selected_statuses.include?('error') ? 'checked' : '' %>>
|
|
505
|
-
<span class="w-2 h-2 rounded-full bg-red-500"></span>
|
|
506
|
-
Error
|
|
507
|
-
</label>
|
|
508
|
-
<label class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-600 cursor-pointer">
|
|
509
|
-
<input type="checkbox" name="statuses[]" value="running" class="filter-checkbox rounded border-gray-300 dark:border-gray-500 text-blue-600 focus:ring-blue-500 dark:bg-gray-600" onchange="updateMultiSelect('statuses')" <%= selected_statuses.include?('running') ? 'checked' : '' %>>
|
|
510
|
-
<span class="w-2 h-2 rounded-full bg-blue-500 animate-pulse"></span>
|
|
511
|
-
Running
|
|
512
|
-
</label>
|
|
513
|
-
<label class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-600 cursor-pointer">
|
|
514
|
-
<input type="checkbox" name="statuses[]" value="timeout" class="filter-checkbox rounded border-gray-300 dark:border-gray-500 text-blue-600 focus:ring-blue-500 dark:bg-gray-600" onchange="updateMultiSelect('statuses')" <%= selected_statuses.include?('timeout') ? 'checked' : '' %>>
|
|
515
|
-
<span class="w-2 h-2 rounded-full bg-yellow-500"></span>
|
|
516
|
-
Timeout
|
|
517
|
-
</label>
|
|
518
|
-
</div>
|
|
519
|
-
</div>
|
|
520
|
-
|
|
521
|
-
<!-- Version Filter (Multi-select) -->
|
|
640
|
+
<%# Status Filter (Multi-select) %>
|
|
641
|
+
<%= render "rubyllm/agents/shared/filter_dropdown",
|
|
642
|
+
name: "statuses[]",
|
|
643
|
+
filter_id: "statuses",
|
|
644
|
+
label: "Status",
|
|
645
|
+
all_label: "All Statuses",
|
|
646
|
+
options: status_options,
|
|
647
|
+
selected: selected_statuses %>
|
|
648
|
+
|
|
649
|
+
<%# Version Filter (Multi-select) %>
|
|
522
650
|
<% if @versions.any? %>
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
<% elsif selected_versions.length == 1 %>
|
|
532
|
-
v<%= selected_versions.first %>
|
|
533
|
-
<% else %>
|
|
534
|
-
<%= selected_versions.length %> Versions
|
|
535
|
-
<% end %>
|
|
536
|
-
</span>
|
|
537
|
-
<svg class="w-4 h-4 text-gray-400 dark:text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
538
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
|
539
|
-
</svg>
|
|
540
|
-
</button>
|
|
541
|
-
<div class="dropdown-menu hidden absolute z-10 mt-1 w-44 bg-white dark:bg-gray-700 rounded-lg shadow-lg border border-gray-200 dark:border-gray-600 py-1 max-h-64 overflow-y-auto">
|
|
542
|
-
<div class="px-3 py-2 border-b border-gray-100 dark:border-gray-600">
|
|
543
|
-
<label class="flex items-center gap-2 cursor-pointer">
|
|
544
|
-
<input type="checkbox" class="select-all-checkbox rounded border-gray-300 dark:border-gray-500 text-blue-600 focus:ring-blue-500 dark:bg-gray-600" onchange="toggleAllOptions(this, 'versions')" <%= selected_versions.empty? ? 'checked' : '' %>>
|
|
545
|
-
<span class="text-sm font-medium text-gray-700 dark:text-gray-200">All Versions</span>
|
|
546
|
-
</label>
|
|
547
|
-
</div>
|
|
548
|
-
<% @versions.each do |version| %>
|
|
549
|
-
<label class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-600 cursor-pointer">
|
|
550
|
-
<input type="checkbox" name="versions[]" value="<%= version %>" class="filter-checkbox rounded border-gray-300 dark:border-gray-500 text-blue-600 focus:ring-blue-500 dark:bg-gray-600" onchange="updateMultiSelect('versions')" <%= selected_versions.include?(version.to_s) ? 'checked' : '' %>>
|
|
551
|
-
v<%= version %>
|
|
552
|
-
</label>
|
|
553
|
-
<% end %>
|
|
554
|
-
</div>
|
|
555
|
-
</div>
|
|
651
|
+
<%= render "rubyllm/agents/shared/filter_dropdown",
|
|
652
|
+
name: "versions[]",
|
|
653
|
+
filter_id: "versions",
|
|
654
|
+
label: "Version",
|
|
655
|
+
all_label: "All Versions",
|
|
656
|
+
options: version_options,
|
|
657
|
+
selected: selected_versions,
|
|
658
|
+
icon: "M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" %>
|
|
556
659
|
<% end %>
|
|
557
660
|
|
|
558
|
-
|
|
661
|
+
<%# Model Filter (Multi-select) %>
|
|
559
662
|
<% if @models.length > 1 %>
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
<% elsif selected_models.length == 1 %>
|
|
569
|
-
<%= selected_models.first %>
|
|
570
|
-
<% else %>
|
|
571
|
-
<%= selected_models.length %> Models
|
|
572
|
-
<% end %>
|
|
573
|
-
</span>
|
|
574
|
-
<svg class="w-4 h-4 text-gray-400 dark:text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
575
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
|
576
|
-
</svg>
|
|
577
|
-
</button>
|
|
578
|
-
<div class="dropdown-menu hidden absolute z-10 mt-1 w-56 bg-white dark:bg-gray-700 rounded-lg shadow-lg border border-gray-200 dark:border-gray-600 py-1 max-h-64 overflow-y-auto">
|
|
579
|
-
<div class="px-3 py-2 border-b border-gray-100 dark:border-gray-600">
|
|
580
|
-
<label class="flex items-center gap-2 cursor-pointer">
|
|
581
|
-
<input type="checkbox" class="select-all-checkbox rounded border-gray-300 dark:border-gray-500 text-blue-600 focus:ring-blue-500 dark:bg-gray-600" onchange="toggleAllOptions(this, 'models')" <%= selected_models.empty? ? 'checked' : '' %>>
|
|
582
|
-
<span class="text-sm font-medium text-gray-700 dark:text-gray-200">All Models</span>
|
|
583
|
-
</label>
|
|
584
|
-
</div>
|
|
585
|
-
<% @models.each do |model| %>
|
|
586
|
-
<label class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-600 cursor-pointer">
|
|
587
|
-
<input type="checkbox" name="models[]" value="<%= model %>" class="filter-checkbox rounded border-gray-300 dark:border-gray-500 text-blue-600 focus:ring-blue-500 dark:bg-gray-600" onchange="updateMultiSelect('models')" <%= selected_models.include?(model) ? 'checked' : '' %>>
|
|
588
|
-
<%= model %>
|
|
589
|
-
</label>
|
|
590
|
-
<% end %>
|
|
591
|
-
</div>
|
|
592
|
-
</div>
|
|
663
|
+
<%= render "rubyllm/agents/shared/filter_dropdown",
|
|
664
|
+
name: "models[]",
|
|
665
|
+
filter_id: "models",
|
|
666
|
+
label: "Model",
|
|
667
|
+
all_label: "All Models",
|
|
668
|
+
options: model_options,
|
|
669
|
+
selected: selected_models,
|
|
670
|
+
icon: "M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" %>
|
|
593
671
|
<% end %>
|
|
594
672
|
|
|
595
|
-
|
|
673
|
+
<%# Temperature Filter (Multi-select) %>
|
|
596
674
|
<% if @temperatures.length > 1 %>
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
<% elsif selected_temperatures.length == 1 %>
|
|
606
|
-
<%= selected_temperatures.first %>
|
|
607
|
-
<% else %>
|
|
608
|
-
<%= selected_temperatures.length %> Temps
|
|
609
|
-
<% end %>
|
|
610
|
-
</span>
|
|
611
|
-
<svg class="w-4 h-4 text-gray-400 dark:text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
612
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
|
613
|
-
</svg>
|
|
614
|
-
</button>
|
|
615
|
-
<div class="dropdown-menu hidden absolute z-10 mt-1 w-36 bg-white dark:bg-gray-700 rounded-lg shadow-lg border border-gray-200 dark:border-gray-600 py-1 max-h-64 overflow-y-auto">
|
|
616
|
-
<div class="px-3 py-2 border-b border-gray-100 dark:border-gray-600">
|
|
617
|
-
<label class="flex items-center gap-2 cursor-pointer">
|
|
618
|
-
<input type="checkbox" class="select-all-checkbox rounded border-gray-300 dark:border-gray-500 text-blue-600 focus:ring-blue-500 dark:bg-gray-600" onchange="toggleAllOptions(this, 'temperatures')" <%= selected_temperatures.empty? ? 'checked' : '' %>>
|
|
619
|
-
<span class="text-sm font-medium text-gray-700 dark:text-gray-200">All Temps</span>
|
|
620
|
-
</label>
|
|
621
|
-
</div>
|
|
622
|
-
<% @temperatures.each do |temp| %>
|
|
623
|
-
<label class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-600 cursor-pointer">
|
|
624
|
-
<input type="checkbox" name="temperatures[]" value="<%= temp %>" class="filter-checkbox rounded border-gray-300 dark:border-gray-500 text-blue-600 focus:ring-blue-500 dark:bg-gray-600" onchange="updateMultiSelect('temperatures')" <%= selected_temperatures.include?(temp.to_s) ? 'checked' : '' %>>
|
|
625
|
-
<%= temp %>
|
|
626
|
-
</label>
|
|
627
|
-
<% end %>
|
|
628
|
-
</div>
|
|
629
|
-
</div>
|
|
675
|
+
<%= render "rubyllm/agents/shared/filter_dropdown",
|
|
676
|
+
name: "temperatures[]",
|
|
677
|
+
filter_id: "temperatures",
|
|
678
|
+
label: "Temp",
|
|
679
|
+
all_label: "All Temps",
|
|
680
|
+
options: temperature_options,
|
|
681
|
+
selected: selected_temperatures,
|
|
682
|
+
icon: "M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" %>
|
|
630
683
|
<% end %>
|
|
631
684
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
<% case params[:days]
|
|
640
|
-
when '1' then %>Today<%
|
|
641
|
-
when '7' then %>Last 7 Days<%
|
|
642
|
-
when '30' then %>Last 30 Days<%
|
|
643
|
-
else %>All Time<%
|
|
644
|
-
end %>
|
|
645
|
-
</span>
|
|
646
|
-
<svg class="w-4 h-4 text-gray-400 dark:text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
647
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
|
648
|
-
</svg>
|
|
649
|
-
</button>
|
|
650
|
-
<div class="dropdown-menu hidden absolute z-10 mt-1 w-40 bg-white dark:bg-gray-700 rounded-lg shadow-lg border border-gray-200 dark:border-gray-600 py-1">
|
|
651
|
-
<a href="#" onclick="selectSingleFilter('days', '', 'All Time'); return false;" class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-600 <%= params[:days].blank? ? 'bg-blue-50 dark:bg-blue-900/50 text-blue-700 dark:text-blue-300' : '' %>">
|
|
652
|
-
All Time
|
|
653
|
-
</a>
|
|
654
|
-
<a href="#" onclick="selectSingleFilter('days', '1', 'Today'); return false;" class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-600 <%= params[:days] == '1' ? 'bg-blue-50 dark:bg-blue-900/50 text-blue-700 dark:text-blue-300' : '' %>">
|
|
655
|
-
Today
|
|
656
|
-
</a>
|
|
657
|
-
<a href="#" onclick="selectSingleFilter('days', '7', 'Last 7 Days'); return false;" class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-600 <%= params[:days] == '7' ? 'bg-blue-50 dark:bg-blue-900/50 text-blue-700 dark:text-blue-300' : '' %>">
|
|
658
|
-
Last 7 Days
|
|
659
|
-
</a>
|
|
660
|
-
<a href="#" onclick="selectSingleFilter('days', '30', 'Last 30 Days'); return false;" class="flex items-center gap-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-600 <%= params[:days] == '30' ? 'bg-blue-50 dark:bg-blue-900/50 text-blue-700 dark:text-blue-300' : '' %>">
|
|
661
|
-
Last 30 Days
|
|
662
|
-
</a>
|
|
663
|
-
</div>
|
|
664
|
-
<%= f.hidden_field :days, value: params[:days], id: "filter_days" %>
|
|
665
|
-
</div>
|
|
685
|
+
<%# Time Range Filter (Single-select) %>
|
|
686
|
+
<%= render "rubyllm/agents/shared/select_dropdown",
|
|
687
|
+
name: "days",
|
|
688
|
+
filter_id: "days",
|
|
689
|
+
options: days_options,
|
|
690
|
+
selected: params[:days].to_s,
|
|
691
|
+
icon: "M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" %>
|
|
666
692
|
|
|
667
|
-
|
|
693
|
+
<%# Clear Filters %>
|
|
668
694
|
<% if has_filters %>
|
|
669
|
-
<%= link_to ruby_llm_agents.agent_path(@agent_type),
|
|
695
|
+
<%= link_to ruby_llm_agents.agent_path(@agent_type),
|
|
696
|
+
data: { turbo_frame: "executions_table" },
|
|
697
|
+
class: "flex items-center gap-1 px-3 py-2 text-sm text-red-500 dark:text-red-400 hover:text-red-600 dark:hover:text-red-300 hover:bg-red-50 dark:hover:bg-red-900/30 rounded-lg transition-colors" do %>
|
|
670
698
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
671
699
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
|
672
700
|
</svg>
|
|
@@ -674,7 +702,7 @@
|
|
|
674
702
|
<% end %>
|
|
675
703
|
<% end %>
|
|
676
704
|
|
|
677
|
-
|
|
705
|
+
<%# Stats Summary (right aligned) %>
|
|
678
706
|
<div class="ml-auto flex items-center gap-4 text-sm text-gray-500 dark:text-gray-400">
|
|
679
707
|
<span><%= number_with_delimiter(@filter_stats[:total_count]) %> executions</span>
|
|
680
708
|
<span class="text-gray-300 dark:text-gray-600">|</span>
|
|
@@ -688,85 +716,3 @@
|
|
|
688
716
|
<%= render "rubyllm/agents/shared/executions_table", executions: @executions, pagination: @pagination %>
|
|
689
717
|
<% end %>
|
|
690
718
|
</div>
|
|
691
|
-
|
|
692
|
-
<script>
|
|
693
|
-
function toggleDropdown(button) {
|
|
694
|
-
document.querySelectorAll('.filter-dropdown .dropdown-menu').forEach(menu => {
|
|
695
|
-
if (menu !== button.nextElementSibling) {
|
|
696
|
-
menu.classList.add('hidden');
|
|
697
|
-
}
|
|
698
|
-
});
|
|
699
|
-
button.nextElementSibling.classList.toggle('hidden');
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
function selectSingleFilter(name, value, label) {
|
|
703
|
-
document.getElementById('filter_' + name).value = value;
|
|
704
|
-
const dropdown = document.querySelector(`[data-filter="${name}"]`);
|
|
705
|
-
dropdown.querySelector('.dropdown-label').textContent = label;
|
|
706
|
-
const button = dropdown.querySelector('button');
|
|
707
|
-
if (value) {
|
|
708
|
-
button.classList.add('ring-2', 'ring-blue-500', 'ring-offset-1');
|
|
709
|
-
} else {
|
|
710
|
-
button.classList.remove('ring-2', 'ring-blue-500', 'ring-offset-1');
|
|
711
|
-
}
|
|
712
|
-
dropdown.querySelector('.dropdown-menu').classList.add('hidden');
|
|
713
|
-
document.getElementById('agent-filters-form').requestSubmit();
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
function toggleAllOptions(checkbox, filterName) {
|
|
717
|
-
const dropdown = document.querySelector(`[data-filter="${filterName}"]`);
|
|
718
|
-
const checkboxes = dropdown.querySelectorAll('.filter-checkbox');
|
|
719
|
-
|
|
720
|
-
if (checkbox.checked) {
|
|
721
|
-
checkboxes.forEach(cb => cb.checked = false);
|
|
722
|
-
updateMultiSelect(filterName);
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
function updateMultiSelect(filterName) {
|
|
727
|
-
const dropdown = document.querySelector(`[data-filter="${filterName}"]`);
|
|
728
|
-
const checkboxes = dropdown.querySelectorAll('.filter-checkbox:checked');
|
|
729
|
-
const selectAllCheckbox = dropdown.querySelector('.select-all-checkbox');
|
|
730
|
-
const button = dropdown.querySelector('button');
|
|
731
|
-
const label = dropdown.querySelector('.dropdown-label');
|
|
732
|
-
|
|
733
|
-
const count = checkboxes.length;
|
|
734
|
-
|
|
735
|
-
selectAllCheckbox.checked = (count === 0);
|
|
736
|
-
|
|
737
|
-
const labelMap = {
|
|
738
|
-
'statuses': { all: 'All Statuses', plural: 'Statuses' },
|
|
739
|
-
'versions': { all: 'All Versions', plural: 'Versions' },
|
|
740
|
-
'models': { all: 'All Models', plural: 'Models' },
|
|
741
|
-
'temperatures': { all: 'All Temps', plural: 'Temps' }
|
|
742
|
-
};
|
|
743
|
-
|
|
744
|
-
if (count === 0) {
|
|
745
|
-
label.textContent = labelMap[filterName]?.all || 'All';
|
|
746
|
-
button.classList.remove('ring-2', 'ring-blue-500', 'ring-offset-1');
|
|
747
|
-
} else if (count === 1) {
|
|
748
|
-
const value = checkboxes[0].value;
|
|
749
|
-
if (filterName === 'statuses') {
|
|
750
|
-
label.textContent = value.charAt(0).toUpperCase() + value.slice(1);
|
|
751
|
-
} else if (filterName === 'versions') {
|
|
752
|
-
label.textContent = 'v' + value;
|
|
753
|
-
} else {
|
|
754
|
-
label.textContent = value;
|
|
755
|
-
}
|
|
756
|
-
button.classList.add('ring-2', 'ring-blue-500', 'ring-offset-1');
|
|
757
|
-
} else {
|
|
758
|
-
label.textContent = `${count} ${labelMap[filterName]?.plural || 'Items'}`;
|
|
759
|
-
button.classList.add('ring-2', 'ring-blue-500', 'ring-offset-1');
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
document.getElementById('agent-filters-form').requestSubmit();
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
document.addEventListener('click', function(e) {
|
|
766
|
-
if (!e.target.closest('.filter-dropdown')) {
|
|
767
|
-
document.querySelectorAll('.filter-dropdown .dropdown-menu').forEach(menu => {
|
|
768
|
-
menu.classList.add('hidden');
|
|
769
|
-
});
|
|
770
|
-
}
|
|
771
|
-
});
|
|
772
|
-
</script>
|