ruby_llm-agents 0.2.3 → 0.3.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.
- checksums.yaml +4 -4
- data/README.md +273 -0
- data/app/channels/ruby_llm/agents/executions_channel.rb +24 -1
- data/app/controllers/concerns/ruby_llm/agents/filterable.rb +81 -0
- data/app/controllers/concerns/ruby_llm/agents/paginatable.rb +51 -0
- data/app/controllers/ruby_llm/agents/agents_controller.rb +228 -59
- data/app/controllers/ruby_llm/agents/dashboard_controller.rb +167 -12
- data/app/controllers/ruby_llm/agents/executions_controller.rb +189 -31
- data/app/controllers/ruby_llm/agents/settings_controller.rb +20 -0
- data/app/helpers/ruby_llm/agents/application_helper.rb +307 -7
- data/app/models/ruby_llm/agents/execution/analytics.rb +224 -20
- data/app/models/ruby_llm/agents/execution/metrics.rb +41 -25
- data/app/models/ruby_llm/agents/execution/scopes.rb +234 -14
- data/app/models/ruby_llm/agents/execution.rb +259 -16
- data/app/services/ruby_llm/agents/agent_registry.rb +49 -12
- data/app/views/layouts/rubyllm/agents/application.html.erb +351 -85
- data/app/views/rubyllm/agents/agents/_version_comparison.html.erb +186 -0
- data/app/views/rubyllm/agents/agents/show.html.erb +233 -10
- data/app/views/rubyllm/agents/dashboard/_action_center.html.erb +62 -0
- data/app/views/rubyllm/agents/dashboard/_alerts_feed.html.erb +62 -0
- data/app/views/rubyllm/agents/dashboard/_breaker_strip.html.erb +47 -0
- data/app/views/rubyllm/agents/dashboard/_budgets_bar.html.erb +165 -0
- data/app/views/rubyllm/agents/dashboard/_now_strip.html.erb +10 -0
- data/app/views/rubyllm/agents/dashboard/_now_strip_values.html.erb +71 -0
- data/app/views/rubyllm/agents/dashboard/index.html.erb +215 -109
- data/app/views/rubyllm/agents/executions/_filters.html.erb +152 -155
- data/app/views/rubyllm/agents/executions/_list.html.erb +103 -12
- data/app/views/rubyllm/agents/executions/dry_run.html.erb +149 -0
- data/app/views/rubyllm/agents/executions/index.html.erb +17 -72
- data/app/views/rubyllm/agents/executions/index.turbo_stream.erb +16 -2
- data/app/views/rubyllm/agents/executions/show.html.erb +693 -14
- data/app/views/rubyllm/agents/settings/show.html.erb +369 -0
- data/app/views/rubyllm/agents/shared/_filter_dropdown.html.erb +121 -0
- data/app/views/rubyllm/agents/shared/_select_dropdown.html.erb +85 -0
- data/config/routes.rb +7 -0
- data/lib/generators/ruby_llm_agents/templates/add_attempts_migration.rb.tt +27 -0
- data/lib/generators/ruby_llm_agents/templates/add_caching_migration.rb.tt +23 -0
- data/lib/generators/ruby_llm_agents/templates/add_finish_reason_migration.rb.tt +19 -0
- data/lib/generators/ruby_llm_agents/templates/add_routing_migration.rb.tt +19 -0
- data/lib/generators/ruby_llm_agents/templates/add_streaming_migration.rb.tt +8 -0
- data/lib/generators/ruby_llm_agents/templates/add_tracing_migration.rb.tt +34 -0
- data/lib/generators/ruby_llm_agents/templates/agent.rb.tt +66 -4
- data/lib/generators/ruby_llm_agents/templates/application_agent.rb.tt +53 -6
- data/lib/generators/ruby_llm_agents/templates/initializer.rb.tt +139 -8
- data/lib/generators/ruby_llm_agents/templates/migration.rb.tt +38 -1
- data/lib/generators/ruby_llm_agents/upgrade_generator.rb +78 -0
- data/lib/ruby_llm/agents/alert_manager.rb +207 -0
- data/lib/ruby_llm/agents/attempt_tracker.rb +295 -0
- data/lib/ruby_llm/agents/base.rb +580 -112
- data/lib/ruby_llm/agents/budget_tracker.rb +360 -0
- data/lib/ruby_llm/agents/circuit_breaker.rb +197 -0
- data/lib/ruby_llm/agents/configuration.rb +279 -1
- data/lib/ruby_llm/agents/engine.rb +59 -6
- data/lib/ruby_llm/agents/execution_logger_job.rb +17 -6
- data/lib/ruby_llm/agents/inflections.rb +13 -2
- data/lib/ruby_llm/agents/instrumentation.rb +538 -87
- data/lib/ruby_llm/agents/redactor.rb +130 -0
- data/lib/ruby_llm/agents/reliability.rb +185 -0
- data/lib/ruby_llm/agents/version.rb +3 -1
- data/lib/ruby_llm/agents.rb +52 -0
- metadata +41 -2
- data/app/controllers/ruby_llm/agents/application_controller.rb +0 -37
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
<div class="mb-6">
|
|
2
|
+
<%= link_to ruby_llm_agents.execution_path(@execution), class: "inline-flex items-center text-sm text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200" do %>
|
|
3
|
+
<svg
|
|
4
|
+
class="w-4 h-4 mr-1"
|
|
5
|
+
fill="none"
|
|
6
|
+
stroke="currentColor"
|
|
7
|
+
viewBox="0 0 24 24"
|
|
8
|
+
>
|
|
9
|
+
<path
|
|
10
|
+
stroke-linecap="round"
|
|
11
|
+
stroke-linejoin="round"
|
|
12
|
+
stroke-width="2"
|
|
13
|
+
d="M10 19l-7-7m0 0l7-7m-7 7h18"
|
|
14
|
+
/>
|
|
15
|
+
</svg>
|
|
16
|
+
Back to Execution
|
|
17
|
+
<% end %>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<!-- Header -->
|
|
21
|
+
<div class="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-xl p-6 mb-6">
|
|
22
|
+
<div class="flex items-center gap-3 mb-2">
|
|
23
|
+
<svg class="w-6 h-6 text-blue-600 dark:text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
24
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
|
25
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
|
|
26
|
+
</svg>
|
|
27
|
+
<h2 class="text-xl font-bold text-blue-900 dark:text-blue-100">Dry Run Preview</h2>
|
|
28
|
+
</div>
|
|
29
|
+
<p class="text-sm text-blue-700 dark:text-blue-300">
|
|
30
|
+
This is a preview of what would be sent to the LLM. No API call was made and no execution was recorded.
|
|
31
|
+
</p>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<!-- Agent Info -->
|
|
35
|
+
<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-6 mb-6">
|
|
36
|
+
<h3 class="text-sm font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-4">Agent Configuration</h3>
|
|
37
|
+
|
|
38
|
+
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
39
|
+
<div>
|
|
40
|
+
<p class="text-xs text-gray-500 dark:text-gray-400">Agent</p>
|
|
41
|
+
<p class="text-sm font-medium text-gray-900 dark:text-gray-100"><%= @dry_run_result[:agent] || @execution.agent_type %></p>
|
|
42
|
+
</div>
|
|
43
|
+
<div>
|
|
44
|
+
<p class="text-xs text-gray-500 dark:text-gray-400">Model</p>
|
|
45
|
+
<p class="text-sm font-medium text-gray-900 dark:text-gray-100"><%= @dry_run_result[:model] || 'N/A' %></p>
|
|
46
|
+
</div>
|
|
47
|
+
<div>
|
|
48
|
+
<p class="text-xs text-gray-500 dark:text-gray-400">Temperature</p>
|
|
49
|
+
<p class="text-sm font-medium text-gray-900 dark:text-gray-100"><%= @dry_run_result[:temperature] || 'N/A' %></p>
|
|
50
|
+
</div>
|
|
51
|
+
<div>
|
|
52
|
+
<p class="text-xs text-gray-500 dark:text-gray-400">Version</p>
|
|
53
|
+
<p class="text-sm font-medium text-gray-900 dark:text-gray-100"><%= @dry_run_result[:version] || 'N/A' %></p>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<!-- System Prompt -->
|
|
59
|
+
<% if @dry_run_result[:system_prompt].present? %>
|
|
60
|
+
<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-6 mb-6">
|
|
61
|
+
<div class="flex items-center justify-between mb-4">
|
|
62
|
+
<h3 class="text-sm font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide">System Prompt</h3>
|
|
63
|
+
<button
|
|
64
|
+
type="button"
|
|
65
|
+
onclick="copyToClipboard(this, 'system-prompt-content')"
|
|
66
|
+
class="inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md transition-colors"
|
|
67
|
+
>
|
|
68
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
69
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/>
|
|
70
|
+
</svg>
|
|
71
|
+
<span>Copy</span>
|
|
72
|
+
</button>
|
|
73
|
+
</div>
|
|
74
|
+
<pre id="system-prompt-content" class="bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 rounded-lg p-4 text-sm overflow-x-auto whitespace-pre-wrap"><%= @dry_run_result[:system_prompt] %></pre>
|
|
75
|
+
</div>
|
|
76
|
+
<% end %>
|
|
77
|
+
|
|
78
|
+
<!-- User Prompt -->
|
|
79
|
+
<% if @dry_run_result[:user_prompt].present? %>
|
|
80
|
+
<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-6 mb-6">
|
|
81
|
+
<div class="flex items-center justify-between mb-4">
|
|
82
|
+
<h3 class="text-sm font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide">User Prompt</h3>
|
|
83
|
+
<button
|
|
84
|
+
type="button"
|
|
85
|
+
onclick="copyToClipboard(this, 'user-prompt-content')"
|
|
86
|
+
class="inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md transition-colors"
|
|
87
|
+
>
|
|
88
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
89
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/>
|
|
90
|
+
</svg>
|
|
91
|
+
<span>Copy</span>
|
|
92
|
+
</button>
|
|
93
|
+
</div>
|
|
94
|
+
<pre id="user-prompt-content" class="bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 rounded-lg p-4 text-sm overflow-x-auto whitespace-pre-wrap"><%= @dry_run_result[:user_prompt] %></pre>
|
|
95
|
+
</div>
|
|
96
|
+
<% end %>
|
|
97
|
+
|
|
98
|
+
<!-- Parameters -->
|
|
99
|
+
<% if @dry_run_result[:parameters].present? %>
|
|
100
|
+
<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-6 mb-6">
|
|
101
|
+
<div class="flex items-center justify-between mb-4">
|
|
102
|
+
<h3 class="text-sm font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide">Parameters</h3>
|
|
103
|
+
</div>
|
|
104
|
+
<pre class="bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 rounded-lg p-4 text-sm overflow-x-auto font-mono"><%= highlight_json(@dry_run_result[:parameters]) %></pre>
|
|
105
|
+
</div>
|
|
106
|
+
<% end %>
|
|
107
|
+
|
|
108
|
+
<!-- Actions -->
|
|
109
|
+
<div class="flex items-center gap-4">
|
|
110
|
+
<%= link_to execution_path(@execution), class: "inline-flex items-center gap-1.5 px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors" do %>
|
|
111
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
112
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/>
|
|
113
|
+
</svg>
|
|
114
|
+
Back to Execution
|
|
115
|
+
<% end %>
|
|
116
|
+
|
|
117
|
+
<%= button_to rerun_execution_path(@execution),
|
|
118
|
+
method: :post,
|
|
119
|
+
class: "inline-flex items-center gap-1.5 px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-lg transition-colors",
|
|
120
|
+
data: { confirm: "This will make a real API call and create a new execution. Are you sure?" } do %>
|
|
121
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
122
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
|
|
123
|
+
</svg>
|
|
124
|
+
Execute for Real
|
|
125
|
+
<% end %>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<script>
|
|
129
|
+
function copyToClipboard(button, elementId) {
|
|
130
|
+
const content = document.getElementById(elementId).textContent;
|
|
131
|
+
const span = button.querySelector('span');
|
|
132
|
+
|
|
133
|
+
navigator.clipboard.writeText(content).then(function() {
|
|
134
|
+
span.textContent = 'Copied!';
|
|
135
|
+
button.classList.add('text-green-600');
|
|
136
|
+
|
|
137
|
+
setTimeout(function() {
|
|
138
|
+
span.textContent = 'Copy';
|
|
139
|
+
button.classList.remove('text-green-600');
|
|
140
|
+
}, 2000);
|
|
141
|
+
}).catch(function(err) {
|
|
142
|
+
console.error('Failed to copy:', err);
|
|
143
|
+
span.textContent = 'Failed';
|
|
144
|
+
setTimeout(function() {
|
|
145
|
+
span.textContent = 'Copy';
|
|
146
|
+
}, 2000);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
</script>
|
|
@@ -1,83 +1,28 @@
|
|
|
1
1
|
<h2 class="text-2xl font-bold text-gray-900 dark:text-gray-100 mb-6">Executions</h2>
|
|
2
2
|
|
|
3
3
|
<%= turbo_frame_tag "executions_content" do %>
|
|
4
|
-
<%= render partial: "rubyllm/agents/executions/filters", locals: { agent_types: @agent_types, filter_stats: @filter_stats } %>
|
|
4
|
+
<%= render partial: "rubyllm/agents/executions/filters", locals: { agent_types: @agent_types, model_ids: @model_ids, filter_stats: @filter_stats } %>
|
|
5
5
|
<%= render partial: "rubyllm/agents/executions/list", locals: { executions: @executions, pagination: @pagination, filter_stats: @filter_stats } %>
|
|
6
6
|
<% end %>
|
|
7
7
|
|
|
8
8
|
<script>
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
// Toggle attempts expansion (used by executions list)
|
|
10
|
+
function toggleAttempts(executionId) {
|
|
11
|
+
const attemptsRow = document.getElementById('attempts-row-' + executionId);
|
|
12
|
+
const expandBtn = document.querySelector(`button[data-execution-id="${executionId}"]`);
|
|
13
|
+
|
|
14
|
+
if (attemptsRow) {
|
|
15
|
+
const isHidden = attemptsRow.classList.contains('hidden');
|
|
16
|
+
attemptsRow.classList.toggle('hidden');
|
|
17
|
+
|
|
18
|
+
// Rotate the chevron
|
|
19
|
+
if (expandBtn) {
|
|
20
|
+
const svg = expandBtn.querySelector('svg');
|
|
21
|
+
if (svg) {
|
|
22
|
+
svg.classList.toggle('rotate-90', isHidden);
|
|
23
|
+
}
|
|
24
|
+
expandBtn.setAttribute('aria-expanded', isHidden ? 'true' : 'false');
|
|
13
25
|
}
|
|
14
|
-
});
|
|
15
|
-
button.nextElementSibling.classList.toggle('hidden');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function selectSingleFilter(name, value, label) {
|
|
19
|
-
document.getElementById('filter_' + name).value = value;
|
|
20
|
-
// Update button label
|
|
21
|
-
const dropdown = document.querySelector(`[data-filter="${name}"]`);
|
|
22
|
-
dropdown.querySelector('.dropdown-label').textContent = label;
|
|
23
|
-
// Update ring
|
|
24
|
-
const button = dropdown.querySelector('button');
|
|
25
|
-
if (value) {
|
|
26
|
-
button.classList.add('ring-2', 'ring-blue-500', 'ring-offset-1');
|
|
27
|
-
} else {
|
|
28
|
-
button.classList.remove('ring-2', 'ring-blue-500', 'ring-offset-1');
|
|
29
|
-
}
|
|
30
|
-
// Close dropdown and submit
|
|
31
|
-
dropdown.querySelector('.dropdown-menu').classList.add('hidden');
|
|
32
|
-
document.getElementById('filters-form').requestSubmit();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function toggleAllOptions(checkbox, filterName) {
|
|
36
|
-
const dropdown = document.querySelector(`[data-filter="${filterName}"]`);
|
|
37
|
-
const checkboxes = dropdown.querySelectorAll('.filter-checkbox');
|
|
38
|
-
|
|
39
|
-
if (checkbox.checked) {
|
|
40
|
-
// Uncheck all specific options
|
|
41
|
-
checkboxes.forEach(cb => cb.checked = false);
|
|
42
|
-
updateMultiSelect(filterName);
|
|
43
26
|
}
|
|
44
27
|
}
|
|
45
|
-
|
|
46
|
-
function updateMultiSelect(filterName) {
|
|
47
|
-
const dropdown = document.querySelector(`[data-filter="${filterName}"]`);
|
|
48
|
-
const checkboxes = dropdown.querySelectorAll('.filter-checkbox:checked');
|
|
49
|
-
const selectAllCheckbox = dropdown.querySelector('.select-all-checkbox');
|
|
50
|
-
const button = dropdown.querySelector('button');
|
|
51
|
-
const label = dropdown.querySelector('.dropdown-label');
|
|
52
|
-
|
|
53
|
-
const count = checkboxes.length;
|
|
54
|
-
|
|
55
|
-
// Update "All" checkbox
|
|
56
|
-
selectAllCheckbox.checked = (count === 0);
|
|
57
|
-
|
|
58
|
-
// Update label
|
|
59
|
-
if (count === 0) {
|
|
60
|
-
label.textContent = filterName === 'agent_types' ? 'All Agents' : 'All Statuses';
|
|
61
|
-
button.classList.remove('ring-2', 'ring-blue-500', 'ring-offset-1');
|
|
62
|
-
} else if (count === 1) {
|
|
63
|
-
const value = checkboxes[0].value;
|
|
64
|
-
label.textContent = filterName === 'agent_types' ? value.replace(/Agent$/, '') : value.charAt(0).toUpperCase() + value.slice(1);
|
|
65
|
-
button.classList.add('ring-2', 'ring-blue-500', 'ring-offset-1');
|
|
66
|
-
} else {
|
|
67
|
-
label.textContent = `${count} ${filterName === 'agent_types' ? 'Agents' : 'Statuses'}`;
|
|
68
|
-
button.classList.add('ring-2', 'ring-blue-500', 'ring-offset-1');
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Submit form
|
|
72
|
-
document.getElementById('filters-form').requestSubmit();
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Close dropdowns when clicking outside
|
|
76
|
-
document.addEventListener('click', function(e) {
|
|
77
|
-
if (!e.target.closest('.filter-dropdown')) {
|
|
78
|
-
document.querySelectorAll('.filter-dropdown .dropdown-menu').forEach(menu => {
|
|
79
|
-
menu.classList.add('hidden');
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
28
|
</script>
|
|
@@ -1,4 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
<%# Only update the stats (not the whole filter container to preserve Alpine.js state) %>
|
|
2
|
+
<%= turbo_stream.replace "filter-stats" do %>
|
|
3
|
+
<div id="filter-stats" class="flex items-center justify-between md:justify-end gap-3 py-2 px-3 md:p-0 bg-gray-50 md:bg-transparent dark:bg-gray-700/50 md:dark:bg-transparent rounded-lg md:rounded-none">
|
|
4
|
+
<span class="text-xs text-gray-500 dark:text-gray-400 md:hidden">Results</span>
|
|
5
|
+
<div class="flex items-center gap-3 text-xs text-gray-500 dark:text-gray-400">
|
|
6
|
+
<span class="tabular-nums"><%= number_to_human_short(@filter_stats[:total_count]) %><span class="md:hidden"> executions</span></span>
|
|
7
|
+
<span class="text-gray-300 dark:text-gray-600">|</span>
|
|
8
|
+
<span class="tabular-nums"><%= number_to_human_short(@filter_stats[:total_cost], prefix: "$", precision: 2) %></span>
|
|
9
|
+
<span class="hidden md:inline text-gray-300 dark:text-gray-600">|</span>
|
|
10
|
+
<span class="hidden md:inline tabular-nums"><%= number_to_human_short(@filter_stats[:total_tokens]) %>t</span>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
<% end %>
|
|
14
|
+
|
|
15
|
+
<%# Update the executions list %>
|
|
16
|
+
<%= turbo_stream.replace "executions-list" do %>
|
|
3
17
|
<%= render partial: "rubyllm/agents/executions/list", locals: { executions: @executions, pagination: @pagination, filter_stats: @filter_stats } %>
|
|
4
18
|
<% end %>
|