ruby_llm-agents 3.5.5 → 3.7.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 +21 -0
- data/app/controllers/ruby_llm/agents/dashboard_controller.rb +155 -10
- data/app/controllers/ruby_llm/agents/executions_controller.rb +1 -3
- data/app/helpers/ruby_llm/agents/application_helper.rb +15 -28
- data/app/models/ruby_llm/agents/execution/replayable.rb +124 -0
- data/app/models/ruby_llm/agents/execution/scopes.rb +42 -1
- data/app/models/ruby_llm/agents/execution.rb +50 -1
- data/app/models/ruby_llm/agents/tenant/budgetable.rb +28 -4
- data/app/views/layouts/ruby_llm/agents/application.html.erb +41 -28
- data/app/views/ruby_llm/agents/agents/show.html.erb +16 -1
- data/app/views/ruby_llm/agents/dashboard/_top_tenants.html.erb +47 -0
- data/app/views/ruby_llm/agents/dashboard/index.html.erb +404 -107
- data/app/views/ruby_llm/agents/system_config/show.html.erb +0 -13
- data/lib/generators/ruby_llm_agents/rename_agent_generator.rb +53 -0
- data/lib/generators/ruby_llm_agents/templates/initializer.rb.tt +0 -15
- data/lib/generators/ruby_llm_agents/templates/rename_agent_migration.rb.tt +19 -0
- data/lib/ruby_llm/agents/agent_tool.rb +125 -0
- data/lib/ruby_llm/agents/audio/speaker.rb +5 -3
- data/lib/ruby_llm/agents/audio/speech_pricing.rb +63 -187
- data/lib/ruby_llm/agents/audio/transcriber.rb +5 -3
- data/lib/ruby_llm/agents/audio/transcription_pricing.rb +5 -7
- data/lib/ruby_llm/agents/base_agent.rb +144 -5
- data/lib/ruby_llm/agents/core/configuration.rb +178 -53
- data/lib/ruby_llm/agents/core/errors.rb +3 -77
- data/lib/ruby_llm/agents/core/instrumentation.rb +0 -17
- data/lib/ruby_llm/agents/core/version.rb +1 -1
- data/lib/ruby_llm/agents/dsl/base.rb +0 -8
- data/lib/ruby_llm/agents/dsl/queryable.rb +124 -0
- data/lib/ruby_llm/agents/dsl.rb +1 -0
- data/lib/ruby_llm/agents/eval/eval_result.rb +73 -0
- data/lib/ruby_llm/agents/eval/eval_run.rb +124 -0
- data/lib/ruby_llm/agents/eval/eval_suite.rb +264 -0
- data/lib/ruby_llm/agents/eval.rb +5 -0
- data/lib/ruby_llm/agents/image/concerns/image_operation_execution.rb +2 -1
- data/lib/ruby_llm/agents/image/generator/pricing.rb +75 -217
- data/lib/ruby_llm/agents/image/generator.rb +5 -3
- data/lib/ruby_llm/agents/infrastructure/attempt_tracker.rb +8 -0
- data/lib/ruby_llm/agents/infrastructure/circuit_breaker.rb +4 -2
- data/lib/ruby_llm/agents/pipeline/builder.rb +43 -0
- data/lib/ruby_llm/agents/pipeline/context.rb +11 -1
- data/lib/ruby_llm/agents/pipeline/executor.rb +1 -25
- data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +26 -1
- data/lib/ruby_llm/agents/pipeline/middleware/cache.rb +18 -0
- data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +90 -0
- data/lib/ruby_llm/agents/pipeline/middleware/reliability.rb +29 -0
- data/lib/ruby_llm/agents/pipeline/middleware/tenant.rb +11 -4
- data/lib/ruby_llm/agents/pipeline.rb +0 -92
- data/lib/ruby_llm/agents/results/background_removal_result.rb +11 -1
- data/lib/ruby_llm/agents/results/base.rb +23 -1
- data/lib/ruby_llm/agents/results/embedding_result.rb +14 -1
- data/lib/ruby_llm/agents/results/image_analysis_result.rb +11 -1
- data/lib/ruby_llm/agents/results/image_edit_result.rb +11 -1
- data/lib/ruby_llm/agents/results/image_generation_result.rb +12 -3
- data/lib/ruby_llm/agents/results/image_pipeline_result.rb +11 -1
- data/lib/ruby_llm/agents/results/image_transform_result.rb +11 -1
- data/lib/ruby_llm/agents/results/image_upscale_result.rb +11 -1
- data/lib/ruby_llm/agents/results/image_variation_result.rb +11 -1
- data/lib/ruby_llm/agents/results/speech_result.rb +20 -1
- data/lib/ruby_llm/agents/results/transcription_result.rb +20 -1
- data/lib/ruby_llm/agents/text/embedder.rb +23 -18
- data/lib/ruby_llm/agents.rb +73 -5
- data/lib/tasks/ruby_llm_agents.rake +21 -0
- metadata +11 -6
- data/lib/ruby_llm/agents/infrastructure/reliability/breaker_manager.rb +0 -80
- data/lib/ruby_llm/agents/infrastructure/reliability/execution_constraints.rb +0 -69
- data/lib/ruby_llm/agents/infrastructure/reliability/executor.rb +0 -125
- data/lib/ruby_llm/agents/infrastructure/reliability/fallback_routing.rb +0 -72
- data/lib/ruby_llm/agents/infrastructure/reliability/retry_strategy.rb +0 -82
|
@@ -125,26 +125,32 @@ module RubyLLM
|
|
|
125
125
|
|
|
126
126
|
# Returns the effective per-agent daily limit
|
|
127
127
|
#
|
|
128
|
+
# Checks the current name and all aliases for matching limits.
|
|
129
|
+
#
|
|
128
130
|
# @param agent_type [String] The agent class name
|
|
129
131
|
# @return [Float, nil] The limit or nil if not set
|
|
130
132
|
def effective_per_agent_daily(agent_type)
|
|
131
|
-
|
|
133
|
+
names = resolve_agent_names(agent_type)
|
|
134
|
+
limit = names.lazy.filter_map { |n| per_agent_daily&.dig(n) }.first
|
|
132
135
|
return limit if limit.present?
|
|
133
136
|
return nil unless inherit_global_defaults
|
|
134
137
|
|
|
135
|
-
global_config&.dig(:per_agent_daily,
|
|
138
|
+
names.lazy.filter_map { |n| global_config&.dig(:per_agent_daily, n) }.first
|
|
136
139
|
end
|
|
137
140
|
|
|
138
141
|
# Returns the effective per-agent monthly limit
|
|
139
142
|
#
|
|
143
|
+
# Checks the current name and all aliases for matching limits.
|
|
144
|
+
#
|
|
140
145
|
# @param agent_type [String] The agent class name
|
|
141
146
|
# @return [Float, nil] The limit or nil if not set
|
|
142
147
|
def effective_per_agent_monthly(agent_type)
|
|
143
|
-
|
|
148
|
+
names = resolve_agent_names(agent_type)
|
|
149
|
+
limit = names.lazy.filter_map { |n| per_agent_monthly&.dig(n) }.first
|
|
144
150
|
return limit if limit.present?
|
|
145
151
|
return nil unless inherit_global_defaults
|
|
146
152
|
|
|
147
|
-
global_config&.dig(:per_agent_monthly,
|
|
153
|
+
names.lazy.filter_map { |n| global_config&.dig(:per_agent_monthly, n) }.first
|
|
148
154
|
end
|
|
149
155
|
|
|
150
156
|
# Budget status checks
|
|
@@ -352,6 +358,24 @@ module RubyLLM
|
|
|
352
358
|
RubyLLM::Agents.configuration.budgets
|
|
353
359
|
end
|
|
354
360
|
|
|
361
|
+
# Resolves all known names for an agent (current name + aliases)
|
|
362
|
+
#
|
|
363
|
+
# @param agent_type [String] The agent class name
|
|
364
|
+
# @return [Array<String>] All names to check
|
|
365
|
+
def resolve_agent_names(agent_type)
|
|
366
|
+
name = agent_type.to_s
|
|
367
|
+
klass = begin
|
|
368
|
+
name.constantize
|
|
369
|
+
rescue NameError
|
|
370
|
+
nil
|
|
371
|
+
end
|
|
372
|
+
if klass&.respond_to?(:all_agent_names)
|
|
373
|
+
klass.all_agent_names
|
|
374
|
+
else
|
|
375
|
+
[name]
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
|
|
355
379
|
# Merges per-agent daily limits with global defaults
|
|
356
380
|
#
|
|
357
381
|
# @return [Hash]
|
|
@@ -7,6 +7,13 @@
|
|
|
7
7
|
|
|
8
8
|
<title>RubyLLM Agents Dashboard</title>
|
|
9
9
|
|
|
10
|
+
<!-- Favicons -->
|
|
11
|
+
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96">
|
|
12
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
|
13
|
+
<link rel="shortcut icon" href="/favicon.ico">
|
|
14
|
+
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
|
15
|
+
<link rel="manifest" href="/site.webmanifest">
|
|
16
|
+
|
|
10
17
|
<!-- Prevent flash of wrong theme - must run before any rendering -->
|
|
11
18
|
<script>
|
|
12
19
|
(function() {
|
|
@@ -152,8 +159,9 @@
|
|
|
152
159
|
}
|
|
153
160
|
</script>
|
|
154
161
|
|
|
155
|
-
<!-- Highcharts for charts -->
|
|
156
|
-
<script src="https://code.highcharts.com/highcharts.js"
|
|
162
|
+
<!-- Highcharts for charts (primary CDN, fallback to jsDelivr) -->
|
|
163
|
+
<script src="https://code.highcharts.com/highcharts.js"
|
|
164
|
+
onerror="var s=document.createElement('script');s.src='https://cdn.jsdelivr.net/npm/highcharts@11.4.0/highcharts.min.js';s.onload=window.__initHighchartsDefaults;document.head.appendChild(s);"></script>
|
|
157
165
|
|
|
158
166
|
<!-- Chart color helpers + Highcharts defaults -->
|
|
159
167
|
<script>
|
|
@@ -164,32 +172,37 @@
|
|
|
164
172
|
return document.documentElement.classList.contains('dark')
|
|
165
173
|
? 'rgba(' + darkR + ',' + darkG + ',' + darkB + ',' + alpha + ')' : lightRgba;
|
|
166
174
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
175
|
+
window.__initHighchartsDefaults = function() {
|
|
176
|
+
if (typeof Highcharts === 'undefined') return;
|
|
177
|
+
Highcharts.setOptions({
|
|
178
|
+
accessibility: { enabled: false },
|
|
179
|
+
credits: { enabled: false },
|
|
180
|
+
chart: {
|
|
181
|
+
backgroundColor: 'transparent',
|
|
182
|
+
style: { fontFamily: 'ui-monospace, monospace' }
|
|
183
|
+
},
|
|
184
|
+
title: { text: null },
|
|
185
|
+
xAxis: {
|
|
186
|
+
labels: { style: { color: chartColor('#6B7280', '#928374') } },
|
|
187
|
+
lineColor: chartColorAlpha('rgba(107, 114, 128, 0.1)', 146, 131, 116, 0.1),
|
|
188
|
+
tickColor: chartColorAlpha('rgba(107, 114, 128, 0.1)', 146, 131, 116, 0.1)
|
|
189
|
+
},
|
|
190
|
+
yAxis: {
|
|
191
|
+
labels: { style: { color: chartColor('#6B7280', '#928374') } },
|
|
192
|
+
gridLineColor: chartColorAlpha('rgba(107, 114, 128, 0.1)', 146, 131, 116, 0.1)
|
|
193
|
+
},
|
|
194
|
+
legend: {
|
|
195
|
+
itemStyle: { color: chartColor('#6B7280', '#928374') },
|
|
196
|
+
itemHoverStyle: { color: chartColor('#9CA3AF', '#bdae93') }
|
|
197
|
+
},
|
|
198
|
+
tooltip: {
|
|
199
|
+
backgroundColor: chartColorAlpha('rgba(0, 0, 0, 0.85)', 40, 40, 40, 0.95),
|
|
200
|
+
borderColor: 'transparent',
|
|
201
|
+
style: { color: chartColor('#E5E7EB', '#ebdbb2'), fontFamily: 'ui-monospace, monospace' }
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
};
|
|
205
|
+
window.__initHighchartsDefaults();
|
|
193
206
|
</script>
|
|
194
207
|
|
|
195
208
|
<!-- Alpine.js -->
|
|
@@ -104,7 +104,9 @@
|
|
|
104
104
|
const successData = trendData.map(d => [new Date(d.date).getTime(), d.count - d.errors]);
|
|
105
105
|
const errorData = trendData.map(d => [new Date(d.date).getTime(), d.errors]);
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
function renderChart() {
|
|
108
|
+
window.__initHighchartsDefaults && window.__initHighchartsDefaults();
|
|
109
|
+
Highcharts.chart('agent-trend-chart', {
|
|
108
110
|
chart: { type: 'areaspline', backgroundColor: 'transparent', spacing: [5, 0, 0, 0] },
|
|
109
111
|
title: { text: null },
|
|
110
112
|
xAxis: {
|
|
@@ -159,6 +161,19 @@
|
|
|
159
161
|
}
|
|
160
162
|
]
|
|
161
163
|
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function waitForHighcharts(attempts) {
|
|
167
|
+
if (typeof Highcharts !== 'undefined') {
|
|
168
|
+
renderChart();
|
|
169
|
+
} else if (attempts > 0) {
|
|
170
|
+
setTimeout(function() { waitForHighcharts(attempts - 1); }, 100);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
document.readyState === 'loading'
|
|
175
|
+
? document.addEventListener('DOMContentLoaded', function() { waitForHighcharts(50); })
|
|
176
|
+
: waitForHighcharts(50);
|
|
162
177
|
})();
|
|
163
178
|
</script>
|
|
164
179
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<%# Top Tenants Widget - shows top tenants by monthly spend %>
|
|
2
|
+
<%# @param top_tenants [Array<Hash>, nil] Tenant data from controller %>
|
|
3
|
+
|
|
4
|
+
<% if top_tenants.present? %>
|
|
5
|
+
<div class="mt-8">
|
|
6
|
+
<div class="flex items-center gap-3 mb-3">
|
|
7
|
+
<span class="text-[10px] font-medium text-gray-400 dark:text-gray-600 uppercase tracking-widest font-mono">tenants</span>
|
|
8
|
+
<div class="flex-1 border-t border-gray-200 dark:border-gray-800"></div>
|
|
9
|
+
<%= link_to "all →", ruby_llm_agents.tenants_path, class: "text-[10px] font-mono text-gray-400 dark:text-gray-600 hover:text-gray-600 dark:hover:text-gray-400" %>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div class="font-mono text-xs space-y-px">
|
|
13
|
+
<% top_tenants.each do |tenant| %>
|
|
14
|
+
<div class="group flex items-center gap-3 py-1.5 px-2 -mx-2 rounded hover:bg-gray-100 dark:hover:bg-gray-800/50 cursor-pointer"
|
|
15
|
+
onclick="window.location='<%= ruby_llm_agents.tenant_path(tenant[:id]) %>'">
|
|
16
|
+
<span class="w-32 truncate text-gray-900 dark:text-gray-200"><%= tenant[:name] %></span>
|
|
17
|
+
<span class="text-gray-500 dark:text-gray-400">$<%= number_with_precision(tenant[:monthly_spend], precision: 2) %></span>
|
|
18
|
+
<% if tenant[:monthly_limit] && tenant[:monthly_limit] > 0 %>
|
|
19
|
+
<%
|
|
20
|
+
pct = [tenant[:monthly_percentage], 100].min
|
|
21
|
+
bar_color = if pct >= 100 then "bg-red-500"
|
|
22
|
+
elsif pct >= 80 then "bg-yellow-500"
|
|
23
|
+
else "bg-blue-500"
|
|
24
|
+
end
|
|
25
|
+
%>
|
|
26
|
+
<div class="w-20 bg-gray-200 dark:bg-gray-800 rounded-full h-1 hidden sm:block">
|
|
27
|
+
<div class="<%= bar_color %> h-1 rounded-full transition-all" style="width: <%= pct %>%"></div>
|
|
28
|
+
</div>
|
|
29
|
+
<span class="w-10 text-right text-gray-400 dark:text-gray-600 hidden sm:inline"><%= tenant[:monthly_percentage] %>%</span>
|
|
30
|
+
<% else %>
|
|
31
|
+
<span class="text-gray-400 dark:text-gray-600 hidden sm:inline">(no limit)</span>
|
|
32
|
+
<% end %>
|
|
33
|
+
<%
|
|
34
|
+
enforcement = tenant[:enforcement].to_s
|
|
35
|
+
badge_class = case enforcement
|
|
36
|
+
when "hard" then "text-red-400 dark:text-red-500/70"
|
|
37
|
+
when "soft" then "text-yellow-400 dark:text-yellow-500/70"
|
|
38
|
+
else "text-gray-400 dark:text-gray-600"
|
|
39
|
+
end
|
|
40
|
+
%>
|
|
41
|
+
<span class="<%= badge_class %> hidden md:inline"><%= enforcement %></span>
|
|
42
|
+
<span class="ml-auto text-gray-400 dark:text-gray-600"><%= number_with_delimiter(tenant[:monthly_executions]) %><span class="text-gray-400 dark:text-gray-600">r</span></span>
|
|
43
|
+
</div>
|
|
44
|
+
<% end %>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
<% end %>
|