ruby_llm-agents 3.11.0 → 3.12.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/app/controllers/ruby_llm/agents/agents_controller.rb +74 -0
- data/app/controllers/ruby_llm/agents/analytics_controller.rb +304 -0
- data/app/controllers/ruby_llm/agents/tenants_controller.rb +74 -2
- data/app/models/ruby_llm/agents/agent_override.rb +47 -0
- data/app/models/ruby_llm/agents/execution/analytics.rb +37 -16
- data/app/services/ruby_llm/agents/agent_registry.rb +8 -1
- data/app/views/layouts/ruby_llm/agents/application.html.erb +4 -2
- data/app/views/ruby_llm/agents/agents/_config_agent.html.erb +89 -4
- data/app/views/ruby_llm/agents/agents/show.html.erb +14 -0
- data/app/views/ruby_llm/agents/analytics/index.html.erb +398 -0
- data/app/views/ruby_llm/agents/tenants/index.html.erb +3 -2
- data/app/views/ruby_llm/agents/tenants/show.html.erb +225 -0
- data/config/routes.rb +12 -4
- data/lib/generators/ruby_llm_agents/templates/create_overrides_migration.rb.tt +28 -0
- data/lib/generators/ruby_llm_agents/templates/skills/AGENTS.md.tt +1 -1
- data/lib/generators/ruby_llm_agents/templates/skills/TOOLS.md.tt +1 -1
- data/lib/generators/ruby_llm_agents/upgrade_generator.rb +14 -0
- data/lib/ruby_llm/agents/base_agent.rb +90 -133
- data/lib/ruby_llm/agents/core/base.rb +9 -0
- data/lib/ruby_llm/agents/core/configuration.rb +5 -1
- data/lib/ruby_llm/agents/core/version.rb +1 -1
- data/lib/ruby_llm/agents/dsl/base.rb +131 -4
- data/lib/ruby_llm/agents/dsl/knowledge.rb +157 -0
- data/lib/ruby_llm/agents/dsl.rb +1 -1
- data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +32 -20
- data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +22 -1
- data/lib/ruby_llm/agents/pipeline/middleware/reliability.rb +1 -1
- data/lib/ruby_llm/agents/stream_event.rb +2 -10
- data/lib/ruby_llm/agents/tool.rb +1 -1
- data/lib/ruby_llm/agents.rb +0 -3
- metadata +6 -3
- data/lib/ruby_llm/agents/agent_tool.rb +0 -143
- data/lib/ruby_llm/agents/dsl/agents.rb +0 -141
|
@@ -34,6 +34,11 @@
|
|
|
34
34
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
|
35
35
|
</svg>
|
|
36
36
|
<% end %>
|
|
37
|
+
<%= button_to refresh_counters_tenant_path(@tenant), method: :post, class: "text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-300 transition-colors", title: "Refresh budget counters", form: { style: "display:inline" } do %>
|
|
38
|
+
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
39
|
+
<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" />
|
|
40
|
+
</svg>
|
|
41
|
+
<% end %>
|
|
37
42
|
<%= render "ruby_llm/agents/shared/doc_link" %>
|
|
38
43
|
</div>
|
|
39
44
|
<div class="font-mono text-xs text-gray-400 dark:text-gray-500 flex items-center gap-1.5 flex-wrap">
|
|
@@ -70,6 +75,153 @@
|
|
|
70
75
|
</div>
|
|
71
76
|
<div class="border-t border-gray-200 dark:border-gray-800 mb-2"></div>
|
|
72
77
|
|
|
78
|
+
<!-- ── period comparison ──────────────── -->
|
|
79
|
+
<% if @period_comparison %>
|
|
80
|
+
<%
|
|
81
|
+
pc = @period_comparison
|
|
82
|
+
tm = pc[:this_month]
|
|
83
|
+
lm = pc[:last_month]
|
|
84
|
+
%>
|
|
85
|
+
<div class="flex items-center gap-3 mt-6 mb-3">
|
|
86
|
+
<span class="text-[10px] font-medium text-gray-400 dark:text-gray-600 uppercase tracking-widest font-mono">this month vs last month</span>
|
|
87
|
+
<div class="flex-1 border-t border-gray-200 dark:border-gray-800"></div>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<div class="grid grid-cols-2 sm:grid-cols-4 gap-4 font-mono text-xs">
|
|
91
|
+
<% [
|
|
92
|
+
["cost", tm[:cost], lm[:cost], pc[:cost_change], "$"],
|
|
93
|
+
["avg cost/run", pc[:avg_cost_this], pc[:avg_cost_last], pc[:avg_cost_change], "$"],
|
|
94
|
+
["runs", tm[:executions], lm[:executions], pc[:executions_change], ""],
|
|
95
|
+
["tokens", tm[:tokens], lm[:tokens], pc[:tokens_change], ""]
|
|
96
|
+
].each do |label, current, previous, change, prefix| %>
|
|
97
|
+
<div class="space-y-0.5">
|
|
98
|
+
<div class="text-[10px] text-gray-400 dark:text-gray-600 uppercase tracking-wider"><%= label %></div>
|
|
99
|
+
<div class="text-gray-900 dark:text-gray-200">
|
|
100
|
+
<% if prefix == "$" %>
|
|
101
|
+
$<%= number_with_precision(current, precision: 4) %>
|
|
102
|
+
<% else %>
|
|
103
|
+
<%= number_with_delimiter(current) %>
|
|
104
|
+
<% end %>
|
|
105
|
+
</div>
|
|
106
|
+
<div class="flex items-center gap-1">
|
|
107
|
+
<% if change != 0 %>
|
|
108
|
+
<span class="<%= change > 0 ? 'text-red-500' : 'text-green-500' %>">
|
|
109
|
+
<%= change > 0 ? "+" : "" %><%= change %>%
|
|
110
|
+
</span>
|
|
111
|
+
<% else %>
|
|
112
|
+
<span class="text-gray-400 dark:text-gray-600">—</span>
|
|
113
|
+
<% end %>
|
|
114
|
+
<span class="text-gray-400 dark:text-gray-600">
|
|
115
|
+
vs
|
|
116
|
+
<% if prefix == "$" %>
|
|
117
|
+
$<%= number_with_precision(previous, precision: 4) %>
|
|
118
|
+
<% else %>
|
|
119
|
+
<%= number_with_delimiter(previous) %>
|
|
120
|
+
<% end %>
|
|
121
|
+
</span>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
<% end %>
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
<% if @error_count > 0 %>
|
|
128
|
+
<div class="mt-3 font-mono text-xs text-gray-500 dark:text-gray-400">
|
|
129
|
+
<span class="text-red-500">$<%= number_with_precision(@error_cost, precision: 4) %></span>
|
|
130
|
+
wasted on <span class="text-red-500"><%= number_with_delimiter(@error_count) %></span> failed executions
|
|
131
|
+
<% error_pct = total_cost.to_f > 0 ? (@error_cost.to_f / total_cost * 100).round(1) : 0 %>
|
|
132
|
+
<% if error_pct > 0 %>
|
|
133
|
+
(<%= error_pct %>% of total cost)
|
|
134
|
+
<% end %>
|
|
135
|
+
</div>
|
|
136
|
+
<% end %>
|
|
137
|
+
<% end %>
|
|
138
|
+
|
|
139
|
+
<!-- ── 30d cost trend ──────────────── -->
|
|
140
|
+
<% if @daily_trend.present? %>
|
|
141
|
+
<div class="flex items-center gap-3 mt-6 mb-2">
|
|
142
|
+
<span class="text-[10px] font-medium text-gray-400 dark:text-gray-600 uppercase tracking-widest font-mono">last 30 days</span>
|
|
143
|
+
<div class="flex-1 border-t border-gray-200 dark:border-gray-800"></div>
|
|
144
|
+
</div>
|
|
145
|
+
<div id="tenant-trend-chart" style="width: 100%; height: 160px;"></div>
|
|
146
|
+
<script>
|
|
147
|
+
(function() {
|
|
148
|
+
const trendData = <%= raw @daily_trend.map { |date, stats|
|
|
149
|
+
{ date: date.to_s, cost: (stats[:cost] || 0).to_f.round(6),
|
|
150
|
+
tokens: (stats[:tokens] || 0).to_i, count: (stats[:count] || 0).to_i }
|
|
151
|
+
}.to_json %>;
|
|
152
|
+
|
|
153
|
+
const costData = trendData.map(d => [new Date(d.date).getTime(), d.cost]);
|
|
154
|
+
const countData = trendData.map(d => [new Date(d.date).getTime(), d.count]);
|
|
155
|
+
|
|
156
|
+
function renderChart() {
|
|
157
|
+
window.__initHighchartsDefaults && window.__initHighchartsDefaults();
|
|
158
|
+
Highcharts.chart('tenant-trend-chart', {
|
|
159
|
+
chart: { backgroundColor: 'transparent', spacing: [5, 0, 0, 0] },
|
|
160
|
+
title: { text: null },
|
|
161
|
+
xAxis: {
|
|
162
|
+
type: 'datetime',
|
|
163
|
+
labels: { style: { color: chartColor('#6B7280', '#928374'), fontSize: '9px', fontFamily: 'ui-monospace, monospace' }, format: '{value:%b %d}' },
|
|
164
|
+
lineColor: 'transparent', tickLength: 0, gridLineWidth: 0
|
|
165
|
+
},
|
|
166
|
+
yAxis: [
|
|
167
|
+
{
|
|
168
|
+
title: { text: null }, min: 0,
|
|
169
|
+
labels: { style: { color: chartColor('#6B7280', '#928374'), fontSize: '9px', fontFamily: 'ui-monospace, monospace' }, format: '${value}' },
|
|
170
|
+
gridLineColor: chartColorAlpha('rgba(107, 114, 128, 0.08)', 146, 131, 116, 0.08)
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
title: { text: null }, min: 0, opposite: true, allowDecimals: false,
|
|
174
|
+
labels: { style: { color: chartColor('#6B7280', '#928374'), fontSize: '9px', fontFamily: 'ui-monospace, monospace' } },
|
|
175
|
+
gridLineWidth: 0
|
|
176
|
+
}
|
|
177
|
+
],
|
|
178
|
+
legend: { enabled: false },
|
|
179
|
+
credits: { enabled: false },
|
|
180
|
+
tooltip: {
|
|
181
|
+
backgroundColor: chartColorAlpha('rgba(0, 0, 0, 0.85)', 40, 40, 40, 0.95),
|
|
182
|
+
borderColor: 'transparent', borderRadius: 3,
|
|
183
|
+
style: { color: chartColor('#E5E7EB', '#ebdbb2'), fontSize: '10px', fontFamily: 'ui-monospace, monospace' },
|
|
184
|
+
shared: true,
|
|
185
|
+
formatter: function() {
|
|
186
|
+
let html = '<span style="color:' + chartColor('#9CA3AF', '#bdae93') + '">' + Highcharts.dateFormat('%b %d', this.x) + '</span>';
|
|
187
|
+
let d = trendData.find(d => new Date(d.date).getTime() === this.x);
|
|
188
|
+
if (d) {
|
|
189
|
+
html += '<br/>cost: <b>$' + d.cost.toFixed(4) + '</b>';
|
|
190
|
+
html += '<br/>runs: <b>' + d.count + '</b>';
|
|
191
|
+
html += '<br/>tokens: <b>' + d.tokens.toLocaleString() + '</b>';
|
|
192
|
+
}
|
|
193
|
+
return html;
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
series: [
|
|
197
|
+
{
|
|
198
|
+
name: 'cost', type: 'areaspline', yAxis: 0, data: costData,
|
|
199
|
+
color: chartColor('#8B5CF6', '#d3869b'),
|
|
200
|
+
lineWidth: 1.5, marker: { enabled: false, states: { hover: { enabled: true, radius: 2 } } },
|
|
201
|
+
fillColor: { linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 },
|
|
202
|
+
stops: [[0, chartColorAlpha('rgba(139, 92, 246, 0.12)', 211, 134, 155, 0.12)], [1, chartColorAlpha('rgba(139, 92, 246, 0)', 211, 134, 155, 0)]] }
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
name: 'runs', type: 'column', yAxis: 1, data: countData,
|
|
206
|
+
color: chartColorAlpha('rgba(107, 114, 128, 0.15)', 146, 131, 116, 0.15),
|
|
207
|
+
borderWidth: 0, pointPadding: 0.1, groupPadding: 0
|
|
208
|
+
}
|
|
209
|
+
]
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function waitForHighcharts(attempts) {
|
|
214
|
+
if (typeof Highcharts !== 'undefined') { renderChart(); }
|
|
215
|
+
else if (attempts > 0) { setTimeout(function() { waitForHighcharts(attempts - 1); }, 100); }
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
document.readyState === 'loading'
|
|
219
|
+
? document.addEventListener('DOMContentLoaded', function() { waitForHighcharts(50); })
|
|
220
|
+
: waitForHighcharts(50);
|
|
221
|
+
})();
|
|
222
|
+
</script>
|
|
223
|
+
<% end %>
|
|
224
|
+
|
|
73
225
|
<% if has_any_limit %>
|
|
74
226
|
<!-- ── budget ──────────────────────── -->
|
|
75
227
|
<div class="flex items-center gap-3 mt-6 mb-3">
|
|
@@ -166,6 +318,79 @@
|
|
|
166
318
|
</div>
|
|
167
319
|
<% end %>
|
|
168
320
|
|
|
321
|
+
<% if @usage_by_agent.present? %>
|
|
322
|
+
<!-- ── usage by agent ──────────────────── -->
|
|
323
|
+
<div class="flex items-center gap-3 mt-6 mb-3">
|
|
324
|
+
<span class="text-[10px] font-medium text-gray-400 dark:text-gray-600 uppercase tracking-widest font-mono">usage by agent</span>
|
|
325
|
+
<div class="flex-1 border-t border-gray-200 dark:border-gray-800"></div>
|
|
326
|
+
</div>
|
|
327
|
+
|
|
328
|
+
<!-- Column headers -->
|
|
329
|
+
<div class="flex items-center gap-3 px-2 -mx-2 font-mono text-[10px] text-gray-400 dark:text-gray-600 uppercase tracking-wider mb-1">
|
|
330
|
+
<span class="flex-[2] min-w-0">agent</span>
|
|
331
|
+
<span class="w-16 flex-shrink-0 text-right">runs</span>
|
|
332
|
+
<span class="w-24 flex-shrink-0 text-right">cost</span>
|
|
333
|
+
<span class="w-20 flex-shrink-0 text-right">avg cost</span>
|
|
334
|
+
<span class="w-24 flex-shrink-0 text-right">tokens</span>
|
|
335
|
+
<span class="w-20 flex-shrink-0 text-right">avg tokens</span>
|
|
336
|
+
</div>
|
|
337
|
+
|
|
338
|
+
<div class="font-mono text-xs space-y-px">
|
|
339
|
+
<% @usage_by_agent.sort_by { |_, v| -v[:cost] }.each do |agent_type, stats| %>
|
|
340
|
+
<%
|
|
341
|
+
avg_cost = stats[:count] > 0 ? (stats[:cost].to_f / stats[:count]) : 0
|
|
342
|
+
avg_tokens = stats[:count] > 0 ? (stats[:tokens].to_f / stats[:count]).round : 0
|
|
343
|
+
%>
|
|
344
|
+
<div class="flex items-center gap-3 py-1 px-2 -mx-2 rounded hover:bg-gray-100 dark:hover:bg-gray-800/50">
|
|
345
|
+
<span class="flex-[2] min-w-0 truncate">
|
|
346
|
+
<%= link_to agent_type.to_s.demodulize, ruby_llm_agents.agent_path(agent_type),
|
|
347
|
+
class: "text-gray-900 dark:text-gray-200 hover:text-gray-600 dark:hover:text-gray-400" %>
|
|
348
|
+
</span>
|
|
349
|
+
<span class="w-16 flex-shrink-0 text-right text-gray-500 dark:text-gray-400"><%= number_with_delimiter(stats[:count]) %></span>
|
|
350
|
+
<span class="w-24 flex-shrink-0 text-right text-gray-800 dark:text-gray-200">$<%= number_with_precision(stats[:cost], precision: 4) %></span>
|
|
351
|
+
<span class="w-20 flex-shrink-0 text-right text-gray-500 dark:text-gray-400">$<%= number_with_precision(avg_cost, precision: 4) %></span>
|
|
352
|
+
<span class="w-24 flex-shrink-0 text-right text-gray-600 dark:text-gray-400"><%= number_with_delimiter(stats[:tokens]) %></span>
|
|
353
|
+
<span class="w-20 flex-shrink-0 text-right text-gray-500 dark:text-gray-400"><%= number_with_delimiter(avg_tokens) %></span>
|
|
354
|
+
</div>
|
|
355
|
+
<% end %>
|
|
356
|
+
</div>
|
|
357
|
+
<% end %>
|
|
358
|
+
|
|
359
|
+
<% if @usage_by_model.present? %>
|
|
360
|
+
<!-- ── usage by model ──────────────────── -->
|
|
361
|
+
<div class="flex items-center gap-3 mt-6 mb-3">
|
|
362
|
+
<span class="text-[10px] font-medium text-gray-400 dark:text-gray-600 uppercase tracking-widest font-mono">usage by model</span>
|
|
363
|
+
<div class="flex-1 border-t border-gray-200 dark:border-gray-800"></div>
|
|
364
|
+
</div>
|
|
365
|
+
|
|
366
|
+
<!-- Column headers -->
|
|
367
|
+
<div class="flex items-center gap-3 px-2 -mx-2 font-mono text-[10px] text-gray-400 dark:text-gray-600 uppercase tracking-wider mb-1">
|
|
368
|
+
<span class="flex-[2] min-w-0">model</span>
|
|
369
|
+
<span class="w-16 flex-shrink-0 text-right">runs</span>
|
|
370
|
+
<span class="w-24 flex-shrink-0 text-right">cost</span>
|
|
371
|
+
<span class="w-20 flex-shrink-0 text-right">avg cost</span>
|
|
372
|
+
<span class="w-24 flex-shrink-0 text-right">tokens</span>
|
|
373
|
+
<span class="w-16 flex-shrink-0 text-right">% of cost</span>
|
|
374
|
+
</div>
|
|
375
|
+
|
|
376
|
+
<div class="font-mono text-xs space-y-px">
|
|
377
|
+
<% @usage_by_model.sort_by { |_, v| -v[:cost] }.each do |model_id, stats| %>
|
|
378
|
+
<%
|
|
379
|
+
avg_cost = stats[:count] > 0 ? (stats[:cost].to_f / stats[:count]) : 0
|
|
380
|
+
cost_pct = total_cost.to_f > 0 ? (stats[:cost].to_f / total_cost * 100).round(1) : 0
|
|
381
|
+
%>
|
|
382
|
+
<div class="flex items-center gap-3 py-1 px-2 -mx-2 rounded hover:bg-gray-100 dark:hover:bg-gray-800/50">
|
|
383
|
+
<span class="flex-[2] min-w-0 truncate text-gray-900 dark:text-gray-200"><%= model_id %></span>
|
|
384
|
+
<span class="w-16 flex-shrink-0 text-right text-gray-500 dark:text-gray-400"><%= number_with_delimiter(stats[:count]) %></span>
|
|
385
|
+
<span class="w-24 flex-shrink-0 text-right text-gray-800 dark:text-gray-200">$<%= number_with_precision(stats[:cost], precision: 4) %></span>
|
|
386
|
+
<span class="w-20 flex-shrink-0 text-right text-gray-500 dark:text-gray-400">$<%= number_with_precision(avg_cost, precision: 4) %></span>
|
|
387
|
+
<span class="w-24 flex-shrink-0 text-right text-gray-600 dark:text-gray-400"><%= number_with_delimiter(stats[:tokens]) %></span>
|
|
388
|
+
<span class="w-16 flex-shrink-0 text-right text-gray-400 dark:text-gray-600"><%= cost_pct %>%</span>
|
|
389
|
+
</div>
|
|
390
|
+
<% end %>
|
|
391
|
+
</div>
|
|
392
|
+
<% end %>
|
|
393
|
+
|
|
169
394
|
<!-- ── recent executions ──────────────────── -->
|
|
170
395
|
<div class="flex items-center gap-3 mt-6 mb-3">
|
|
171
396
|
<span class="text-[10px] font-medium text-gray-400 dark:text-gray-600 uppercase tracking-widest font-mono">recent executions</span>
|
data/config/routes.rb
CHANGED
|
@@ -4,7 +4,11 @@ RubyLLM::Agents::Engine.routes.draw do
|
|
|
4
4
|
root to: "dashboard#index"
|
|
5
5
|
get "chart_data", to: "dashboard#chart_data"
|
|
6
6
|
|
|
7
|
-
resources :agents, only: [:index, :show]
|
|
7
|
+
resources :agents, only: [:index, :show, :update] do
|
|
8
|
+
member do
|
|
9
|
+
delete :reset_overrides
|
|
10
|
+
end
|
|
11
|
+
end
|
|
8
12
|
|
|
9
13
|
resources :executions, only: [:index, :show] do
|
|
10
14
|
collection do
|
|
@@ -15,9 +19,13 @@ RubyLLM::Agents::Engine.routes.draw do
|
|
|
15
19
|
|
|
16
20
|
resources :requests, only: [:index, :show]
|
|
17
21
|
|
|
18
|
-
resources :tenants, only: [:index, :show, :edit, :update]
|
|
22
|
+
resources :tenants, only: [:index, :show, :edit, :update] do
|
|
23
|
+
member do
|
|
24
|
+
post :refresh_counters
|
|
25
|
+
end
|
|
26
|
+
end
|
|
19
27
|
|
|
20
|
-
|
|
21
|
-
get "analytics", to:
|
|
28
|
+
get "analytics", to: "analytics#index", as: :analytics
|
|
29
|
+
get "analytics/chart_data", to: "analytics#chart_data", as: :analytics_chart_data
|
|
22
30
|
resource :system_config, only: [:show], controller: "system_config"
|
|
23
31
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Migration to create the agent overrides table for dashboard-managed settings
|
|
4
|
+
#
|
|
5
|
+
# This table stores per-agent setting overrides that are managed through
|
|
6
|
+
# the dashboard UI. Only fields declared as `overridable: true` in the
|
|
7
|
+
# agent DSL can be overridden.
|
|
8
|
+
#
|
|
9
|
+
# Run with: rails db:migrate
|
|
10
|
+
class CreateRubyLLMAgentsOverrides < ActiveRecord::Migration<%= migration_version %>
|
|
11
|
+
def change
|
|
12
|
+
create_table :ruby_llm_agents_overrides do |t|
|
|
13
|
+
# The agent class name (e.g., "SupportAgent")
|
|
14
|
+
t.string :agent_type, null: false
|
|
15
|
+
|
|
16
|
+
# JSON hash of overridden settings
|
|
17
|
+
# Format: { "model" => "gpt-4o-mini", "temperature" => 0.5 }
|
|
18
|
+
t.json :settings, null: false, default: {}
|
|
19
|
+
|
|
20
|
+
# Who last changed the override (optional audit trail)
|
|
21
|
+
t.string :updated_by
|
|
22
|
+
|
|
23
|
+
t.timestamps
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
add_index :ruby_llm_agents_overrides, :agent_type, unique: true
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -110,7 +110,7 @@ streaming true # Enable streaming by default
|
|
|
110
110
|
### Tools
|
|
111
111
|
|
|
112
112
|
```ruby
|
|
113
|
-
tools
|
|
113
|
+
tools SearchTool, CalculatorTool # Make tools available to agent
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
### Extended Thinking
|
|
@@ -117,6 +117,20 @@ module RubyLlmAgents
|
|
|
117
117
|
)
|
|
118
118
|
end
|
|
119
119
|
|
|
120
|
+
# Create overrides table for dashboard-managed agent settings
|
|
121
|
+
def create_overrides_migration
|
|
122
|
+
if table_exists?(:ruby_llm_agents_overrides)
|
|
123
|
+
say_status :skip, "ruby_llm_agents_overrides table already exists", :yellow
|
|
124
|
+
return
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
say_status :upgrade, "Creating agent overrides table", :blue
|
|
128
|
+
migration_template(
|
|
129
|
+
"create_overrides_migration.rb.tt",
|
|
130
|
+
File.join(db_migrate_path, "create_ruby_llm_agents_overrides.rb")
|
|
131
|
+
)
|
|
132
|
+
end
|
|
133
|
+
|
|
120
134
|
def suggest_config_consolidation
|
|
121
135
|
ruby_llm_initializer = File.join(destination_root, "config/initializers/ruby_llm.rb")
|
|
122
136
|
agents_initializer = File.join(destination_root, "config/initializers/ruby_llm_agents.rb")
|