ruby_llm-agents 0.3.1 → 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/README.md +88 -0
- 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/scopes.rb +10 -0
- data/app/models/ruby_llm/agents/execution.rb +26 -58
- data/app/views/layouts/rubyllm/agents/application.html.erb +103 -352
- data/app/views/rubyllm/agents/agents/_agent.html.erb +87 -0
- data/app/views/rubyllm/agents/agents/index.html.erb +2 -71
- data/app/views/rubyllm/agents/agents/show.html.erb +349 -416
- data/app/views/rubyllm/agents/dashboard/_action_center.html.erb +7 -7
- 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/_execution_item.html.erb +54 -39
- 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 -151
- data/app/views/rubyllm/agents/executions/show.html.erb +256 -93
- 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/generators/ruby_llm_agents/templates/add_tool_calls_migration.rb.tt +28 -0
- data/lib/generators/ruby_llm_agents/templates/migration.rb.tt +7 -0
- data/lib/generators/ruby_llm_agents/upgrade_generator.rb +13 -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 +19 -619
- data/lib/ruby_llm/agents/instrumentation.rb +36 -3
- data/lib/ruby_llm/agents/result.rb +235 -0
- data/lib/ruby_llm/agents/version.rb +1 -1
- data/lib/ruby_llm/agents.rb +1 -0
- metadata +15 -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
|
@@ -4,224 +4,149 @@
|
|
|
4
4
|
<!-- Now Strip -->
|
|
5
5
|
<%= render partial: "rubyllm/agents/dashboard/now_strip", locals: { now_strip: @now_strip } %>
|
|
6
6
|
|
|
7
|
-
<!--
|
|
8
|
-
<div
|
|
9
|
-
class="
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
>
|
|
14
|
-
<div class="px-6 py-4 border-b border-gray-100 dark:border-gray-700">
|
|
15
|
-
<div class="flex justify-between items-center">
|
|
16
|
-
<div class="flex items-center space-x-2">
|
|
17
|
-
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100">
|
|
18
|
-
Activity
|
|
19
|
-
</h3>
|
|
20
|
-
|
|
21
|
-
<span class="flex items-center text-xs text-green-600 dark:text-green-400">
|
|
22
|
-
<span class="w-1.5 h-1.5 bg-green-500 rounded-full mr-1 animate-pulse"></span>
|
|
23
|
-
Live
|
|
24
|
-
</span>
|
|
25
|
-
</div>
|
|
26
|
-
|
|
27
|
-
<span class="text-xs text-gray-400 dark:text-gray-500">
|
|
28
|
-
Updates every 5s
|
|
29
|
-
</span>
|
|
7
|
+
<!-- Activity Chart -->
|
|
8
|
+
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 mb-6">
|
|
9
|
+
<div class="px-6 py-3 border-b border-gray-100 dark:border-gray-700">
|
|
10
|
+
<div class="flex items-center justify-between">
|
|
11
|
+
<h3 class="text-base font-semibold text-gray-900 dark:text-gray-100">Activity</h3>
|
|
12
|
+
<span id="chart-totals" class="text-sm text-gray-500 dark:text-gray-400"></span>
|
|
30
13
|
</div>
|
|
31
14
|
</div>
|
|
32
15
|
|
|
33
|
-
<div
|
|
34
|
-
id="hourly-chart"
|
|
35
|
-
class="p-6 pb-3 pt-8"
|
|
36
|
-
style="height: 400px;"
|
|
37
|
-
data-chart-url="<%= ruby_llm_agents.chart_data_path %>"
|
|
38
|
-
>
|
|
16
|
+
<div id="activity-chart-container" class="p-4 h-[320px] sm:h-[380px]">
|
|
39
17
|
<div id="activity-chart" style="width: 100%; height: 100%;"></div>
|
|
40
18
|
</div>
|
|
41
19
|
|
|
42
20
|
<script>
|
|
43
21
|
(function() {
|
|
44
|
-
const
|
|
45
|
-
|
|
22
|
+
const range = '<%= @selected_range %>';
|
|
23
|
+
const chartUrl = '<%= ruby_llm_agents.chart_data_path %>?range=' + range;
|
|
46
24
|
const hourMs = 3600000;
|
|
47
25
|
const dayMs = 24 * hourMs;
|
|
48
26
|
|
|
49
|
-
|
|
50
|
-
function convertToDatetimePoints(data) {
|
|
27
|
+
function convertToDatetimePoints(data, range) {
|
|
51
28
|
const now = Date.now();
|
|
52
29
|
const numPoints = data.length;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
30
|
+
if (range === 'today') {
|
|
31
|
+
return data.map((val, i) => [now - (numPoints - 1 - i) * hourMs, val]);
|
|
32
|
+
} else {
|
|
33
|
+
return data.map((val, i) => [now - (numPoints - 1 - i) * dayMs, val]);
|
|
34
|
+
}
|
|
57
35
|
}
|
|
58
36
|
|
|
59
|
-
function
|
|
37
|
+
function formatNumber(num) {
|
|
38
|
+
return num.toLocaleString();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function initChart() {
|
|
60
42
|
fetch(chartUrl)
|
|
61
43
|
.then(res => res.json())
|
|
62
44
|
.then(data => {
|
|
63
45
|
const now = Date.now();
|
|
64
|
-
const successData = convertToDatetimePoints(data.series[0].data);
|
|
65
|
-
const failedData = convertToDatetimePoints(data.series[1].data);
|
|
46
|
+
const successData = convertToDatetimePoints(data.series[0].data, range);
|
|
47
|
+
const failedData = convertToDatetimePoints(data.series[1].data, range);
|
|
48
|
+
const costData = convertToDatetimePoints(data.series[2].data, range);
|
|
66
49
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
50
|
+
// Update totals in header
|
|
51
|
+
const totals = data.totals;
|
|
52
|
+
const totalExecs = totals.success + totals.failed;
|
|
53
|
+
document.getElementById('chart-totals').textContent =
|
|
54
|
+
formatNumber(totalExecs) + ' executions • $' + totals.cost.toFixed(2);
|
|
55
|
+
|
|
56
|
+
const timeRange = range === 'today' ? dayMs : (range === '7d' ? 7 * dayMs : 30 * dayMs);
|
|
57
|
+
const dateFormat = range === 'today' ? '%H:%M' : '%b %d';
|
|
58
|
+
|
|
59
|
+
Highcharts.chart('activity-chart', {
|
|
60
|
+
chart: { type: 'areaspline', backgroundColor: 'transparent' },
|
|
73
61
|
title: { text: null },
|
|
74
62
|
xAxis: {
|
|
75
63
|
type: 'datetime',
|
|
76
|
-
|
|
77
|
-
min: now - dayMs,
|
|
64
|
+
min: now - timeRange,
|
|
78
65
|
max: now,
|
|
79
|
-
labels: { style: { color: '#9CA3AF' } },
|
|
66
|
+
labels: { style: { color: '#9CA3AF' }, format: '{value:' + dateFormat + '}' },
|
|
80
67
|
lineColor: 'rgba(156, 163, 175, 0.2)'
|
|
81
68
|
},
|
|
82
|
-
yAxis: {
|
|
69
|
+
yAxis: [{
|
|
83
70
|
title: { text: null },
|
|
84
71
|
min: 0,
|
|
85
72
|
allowDecimals: false,
|
|
86
73
|
labels: { style: { color: '#9CA3AF' } },
|
|
87
74
|
gridLineColor: 'rgba(156, 163, 175, 0.2)'
|
|
88
|
-
},
|
|
75
|
+
}, {
|
|
76
|
+
title: { text: null },
|
|
77
|
+
min: 0,
|
|
78
|
+
labels: { style: { color: '#F59E0B' }, format: '${value}' },
|
|
79
|
+
opposite: true,
|
|
80
|
+
gridLineWidth: 0
|
|
81
|
+
}],
|
|
89
82
|
legend: {
|
|
83
|
+
enabled: true,
|
|
90
84
|
align: 'center',
|
|
91
85
|
verticalAlign: 'bottom',
|
|
92
|
-
itemStyle: { color: '#9CA3AF' }
|
|
86
|
+
itemStyle: { color: '#9CA3AF', fontWeight: 'normal' },
|
|
87
|
+
itemHoverStyle: { color: '#D1D5DB' }
|
|
93
88
|
},
|
|
94
89
|
credits: { enabled: false },
|
|
95
90
|
tooltip: {
|
|
96
|
-
|
|
91
|
+
shared: true,
|
|
92
|
+
backgroundColor: 'rgba(17, 24, 39, 0.95)',
|
|
97
93
|
borderColor: 'rgba(75, 85, 99, 0.5)',
|
|
98
94
|
style: { color: '#F3F4F6' },
|
|
99
|
-
|
|
100
|
-
pointFormat: '{point.x:%H:%M} - {point.y}'
|
|
95
|
+
xDateFormat: range === 'today' ? '%H:%M' : '%b %d'
|
|
101
96
|
},
|
|
102
97
|
plotOptions: {
|
|
98
|
+
areaspline: {
|
|
99
|
+
fillOpacity: 0.15,
|
|
100
|
+
lineWidth: 2,
|
|
101
|
+
marker: { enabled: false, states: { hover: { enabled: true, radius: 4 } } }
|
|
102
|
+
},
|
|
103
103
|
spline: {
|
|
104
104
|
lineWidth: 2,
|
|
105
|
-
marker: {
|
|
106
|
-
enabled: true,
|
|
107
|
-
radius: 4,
|
|
108
|
-
symbol: 'circle'
|
|
109
|
-
}
|
|
105
|
+
marker: { enabled: false, states: { hover: { enabled: true, radius: 4 } } }
|
|
110
106
|
}
|
|
111
107
|
},
|
|
112
|
-
series: [
|
|
113
|
-
name: 'Success',
|
|
114
|
-
color: '#
|
|
115
|
-
data:
|
|
116
|
-
|
|
117
|
-
name: 'Failed',
|
|
118
|
-
color: '#EF4444',
|
|
119
|
-
data: failedData
|
|
120
|
-
}]
|
|
108
|
+
series: [
|
|
109
|
+
{ name: 'Success', type: 'areaspline', color: '#10B981', data: successData, yAxis: 0 },
|
|
110
|
+
{ name: 'Failed', type: 'areaspline', color: '#EF4444', data: failedData, yAxis: 0 },
|
|
111
|
+
{ name: 'Cost', type: 'spline', color: '#F59E0B', data: costData, yAxis: 1, tooltip: { valuePrefix: '$' } }
|
|
112
|
+
]
|
|
121
113
|
});
|
|
122
|
-
|
|
123
|
-
// Update chart every 5 seconds with fresh data
|
|
124
|
-
setInterval(function() {
|
|
125
|
-
fetch(chartUrl)
|
|
126
|
-
.then(res => res.json())
|
|
127
|
-
.then(newData => {
|
|
128
|
-
const now = Date.now();
|
|
129
|
-
const newSuccessData = convertToDatetimePoints(newData.series[0].data);
|
|
130
|
-
const newFailedData = convertToDatetimePoints(newData.series[1].data);
|
|
131
|
-
|
|
132
|
-
// Update x-axis range
|
|
133
|
-
activityChart.xAxis[0].setExtremes(now - dayMs, now, false);
|
|
134
|
-
|
|
135
|
-
// Replace series data
|
|
136
|
-
activityChart.series[0].setData(newSuccessData, false);
|
|
137
|
-
activityChart.series[1].setData(newFailedData, true);
|
|
138
|
-
});
|
|
139
|
-
}, 5000);
|
|
140
114
|
})
|
|
141
|
-
.catch(err => console.log('[RubyLLM::Agents] Chart
|
|
115
|
+
.catch(err => console.log('[RubyLLM::Agents] Chart error:', err));
|
|
142
116
|
}
|
|
143
117
|
|
|
144
118
|
if (document.readyState === 'loading') {
|
|
145
|
-
document.addEventListener('DOMContentLoaded',
|
|
119
|
+
document.addEventListener('DOMContentLoaded', initChart);
|
|
146
120
|
} else {
|
|
147
|
-
|
|
121
|
+
initChart();
|
|
148
122
|
}
|
|
149
123
|
})();
|
|
150
124
|
</script>
|
|
151
125
|
</div>
|
|
152
126
|
|
|
153
127
|
<!-- Live Activity Feed -->
|
|
154
|
-
<div
|
|
155
|
-
class="
|
|
156
|
-
bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100
|
|
157
|
-
dark:border-gray-700
|
|
158
|
-
"
|
|
159
|
-
>
|
|
160
|
-
<div class="px-6 py-4 border-b border-gray-100 dark:border-gray-700">
|
|
128
|
+
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 mb-6">
|
|
129
|
+
<div class="px-6 py-3 border-b border-gray-100 dark:border-gray-700">
|
|
161
130
|
<div class="flex justify-between items-center">
|
|
162
|
-
<
|
|
163
|
-
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100">
|
|
164
|
-
Live Activity
|
|
165
|
-
</h3>
|
|
166
|
-
|
|
167
|
-
<div
|
|
168
|
-
class="
|
|
169
|
-
flex items-center space-x-3 text-xs text-gray-400
|
|
170
|
-
dark:text-gray-500
|
|
171
|
-
"
|
|
172
|
-
>
|
|
173
|
-
<span class="flex items-center">
|
|
174
|
-
<span class="w-2 h-2 bg-blue-500 rounded-full mr-1 animate-pulse"></span>
|
|
175
|
-
running
|
|
176
|
-
</span>
|
|
177
|
-
|
|
178
|
-
<span class="flex items-center">
|
|
179
|
-
<span class="w-2 h-2 bg-green-500 rounded-full mr-1"></span>
|
|
180
|
-
success
|
|
181
|
-
</span>
|
|
182
|
-
|
|
183
|
-
<span class="flex items-center">
|
|
184
|
-
<span class="w-2 h-2 bg-red-500 rounded-full mr-1"></span>
|
|
185
|
-
error
|
|
186
|
-
</span>
|
|
187
|
-
|
|
188
|
-
<span class="flex items-center">
|
|
189
|
-
<span class="w-2 h-2 bg-yellow-500 rounded-full mr-1"></span>
|
|
190
|
-
timeout
|
|
191
|
-
</span>
|
|
192
|
-
</div>
|
|
193
|
-
</div>
|
|
194
|
-
|
|
131
|
+
<h3 class="text-base font-semibold text-gray-900 dark:text-gray-100">Recent Activity</h3>
|
|
195
132
|
<%= link_to "View All", ruby_llm_agents.executions_path, data: { turbo: false }, class: "text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 text-sm font-medium" %>
|
|
196
133
|
</div>
|
|
197
134
|
</div>
|
|
198
|
-
|
|
199
135
|
<div id="activity-feed" class="px-6 py-2">
|
|
200
136
|
<% if @recent_executions.any? %>
|
|
201
|
-
<% @recent_executions.each do |execution| %>
|
|
137
|
+
<% @recent_executions.first(5).each do |execution| %>
|
|
202
138
|
<%= render partial: "rubyllm/agents/dashboard/execution_item", locals: { execution: execution } %>
|
|
203
139
|
<% end %>
|
|
204
140
|
<% else %>
|
|
205
|
-
<div
|
|
206
|
-
|
|
207
|
-
class="py-10 text-center text-gray-500 dark:text-gray-400"
|
|
208
|
-
>
|
|
209
|
-
<svg
|
|
210
|
-
class="w-12 h-12 mx-auto mb-3 text-gray-300 dark:text-gray-600"
|
|
211
|
-
fill="none"
|
|
212
|
-
stroke="currentColor"
|
|
213
|
-
viewBox="0 0 24 24"
|
|
214
|
-
>
|
|
215
|
-
<path
|
|
216
|
-
stroke-linecap="round"
|
|
217
|
-
stroke-linejoin="round"
|
|
218
|
-
stroke-width="1.5"
|
|
219
|
-
d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"
|
|
220
|
-
/>
|
|
221
|
-
</svg>
|
|
222
|
-
|
|
223
|
-
<p>No executions yet. Create an agent and make a call!</p>
|
|
141
|
+
<div class="py-8 text-center text-gray-500 dark:text-gray-400">
|
|
142
|
+
<p class="text-sm">No executions yet</p>
|
|
224
143
|
</div>
|
|
225
144
|
<% end %>
|
|
226
145
|
</div>
|
|
227
146
|
</div>
|
|
147
|
+
|
|
148
|
+
<!-- Agent Comparison + Top Errors -->
|
|
149
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
150
|
+
<%= render partial: "rubyllm/agents/dashboard/agent_comparison", locals: { agent_stats: @agent_stats } %>
|
|
151
|
+
<%= render partial: "rubyllm/agents/dashboard/top_errors", locals: { top_errors: @top_errors } %>
|
|
152
|
+
</div>
|