ruby_llm-agents 1.3.4 → 2.1.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 +112 -336
- data/app/controllers/concerns/ruby_llm/agents/sortable.rb +0 -1
- data/app/controllers/ruby_llm/agents/agents_controller.rb +5 -56
- data/app/controllers/ruby_llm/agents/dashboard_controller.rb +22 -106
- data/app/controllers/ruby_llm/agents/executions_controller.rb +4 -114
- data/app/controllers/ruby_llm/agents/tenants_controller.rb +30 -2
- data/app/helpers/ruby_llm/agents/application_helper.rb +19 -53
- data/app/models/ruby_llm/agents/execution/analytics.rb +13 -54
- data/app/models/ruby_llm/agents/execution/scopes.rb +61 -14
- data/app/models/ruby_llm/agents/execution.rb +52 -12
- data/app/models/ruby_llm/agents/execution_detail.rb +18 -0
- data/app/models/ruby_llm/agents/tenant/budgetable.rb +132 -24
- data/app/models/ruby_llm/agents/tenant/incrementable.rb +117 -0
- data/app/models/ruby_llm/agents/tenant/resettable.rb +128 -0
- data/app/models/ruby_llm/agents/tenant/trackable.rb +46 -12
- data/app/models/ruby_llm/agents/tenant.rb +2 -3
- data/app/models/ruby_llm/agents/tenant_budget.rb +6 -3
- data/app/services/ruby_llm/agents/agent_registry.rb +6 -112
- data/app/views/layouts/ruby_llm/agents/application.html.erb +89 -252
- data/app/views/ruby_llm/agents/agents/_config_agent.html.erb +71 -218
- data/app/views/ruby_llm/agents/agents/_config_embedder.html.erb +20 -63
- data/app/views/ruby_llm/agents/agents/_config_image_generator.html.erb +44 -131
- data/app/views/ruby_llm/agents/agents/_config_moderator.html.erb +16 -57
- data/app/views/ruby_llm/agents/agents/_config_speaker.html.erb +39 -104
- data/app/views/ruby_llm/agents/agents/_config_transcriber.html.erb +29 -82
- data/app/views/ruby_llm/agents/agents/_empty_state.html.erb +4 -14
- data/app/views/ruby_llm/agents/agents/index.html.erb +105 -274
- data/app/views/ruby_llm/agents/agents/show.html.erb +248 -378
- data/app/views/ruby_llm/agents/dashboard/_action_center.html.erb +29 -52
- data/app/views/ruby_llm/agents/dashboard/_tenant_budget.html.erb +73 -99
- data/app/views/ruby_llm/agents/dashboard/index.html.erb +228 -433
- data/app/views/ruby_llm/agents/executions/_execution.html.erb +1 -1
- data/app/views/ruby_llm/agents/executions/_filters.html.erb +4 -25
- data/app/views/ruby_llm/agents/executions/_list.html.erb +111 -152
- data/app/views/ruby_llm/agents/executions/index.html.erb +5 -7
- data/app/views/ruby_llm/agents/executions/show.html.erb +526 -1037
- data/app/views/ruby_llm/agents/shared/_agent_type_badge.html.erb +5 -21
- data/app/views/ruby_llm/agents/shared/_executions_table.html.erb +70 -191
- data/app/views/ruby_llm/agents/shared/_filter_dropdown.html.erb +16 -44
- data/app/views/ruby_llm/agents/shared/_select_dropdown.html.erb +12 -41
- data/app/views/ruby_llm/agents/shared/_status_badge.html.erb +11 -65
- data/app/views/ruby_llm/agents/shared/_tenant_filter.html.erb +6 -5
- data/app/views/ruby_llm/agents/system_config/show.html.erb +240 -351
- data/app/views/ruby_llm/agents/tenants/_form.html.erb +67 -77
- data/app/views/ruby_llm/agents/tenants/edit.html.erb +7 -9
- data/app/views/ruby_llm/agents/tenants/index.html.erb +100 -122
- data/app/views/ruby_llm/agents/tenants/show.html.erb +146 -336
- data/config/routes.rb +0 -13
- data/lib/generators/ruby_llm_agents/install_generator.rb +13 -17
- data/lib/generators/ruby_llm_agents/migrate_structure_generator.rb +2 -12
- data/lib/generators/ruby_llm_agents/restructure_generator.rb +0 -2
- data/lib/generators/ruby_llm_agents/templates/add_usage_counters_to_tenants_migration.rb.tt +37 -0
- data/lib/generators/ruby_llm_agents/templates/agent.rb.tt +1 -2
- data/lib/generators/ruby_llm_agents/templates/application_agent.rb.tt +1 -1
- data/lib/generators/ruby_llm_agents/templates/application_image_pipeline.rb.tt +0 -1
- data/lib/generators/ruby_llm_agents/templates/create_execution_details_migration.rb.tt +27 -0
- data/lib/generators/ruby_llm_agents/templates/create_tenants_migration.rb.tt +25 -0
- data/lib/generators/ruby_llm_agents/templates/image_pipeline.rb.tt +0 -1
- data/lib/generators/ruby_llm_agents/templates/initializer.rb.tt +33 -12
- data/lib/generators/ruby_llm_agents/templates/migration.rb.tt +40 -71
- data/lib/generators/ruby_llm_agents/templates/remove_agent_version_migration.rb.tt +13 -0
- data/lib/generators/ruby_llm_agents/templates/remove_workflow_columns_migration.rb.tt +19 -0
- data/lib/generators/ruby_llm_agents/templates/skills/AGENTS.md.tt +2 -4
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_PIPELINES.md.tt +0 -1
- data/lib/generators/ruby_llm_agents/templates/split_execution_details_migration.rb.tt +232 -0
- data/lib/generators/ruby_llm_agents/upgrade_generator.rb +77 -259
- data/lib/ruby_llm/agents/audio/speaker.rb +0 -1
- data/lib/ruby_llm/agents/audio/transcriber.rb +0 -1
- data/lib/ruby_llm/agents/base_agent.rb +54 -23
- data/lib/ruby_llm/agents/core/base/callbacks.rb +142 -0
- data/lib/ruby_llm/agents/core/base.rb +23 -55
- data/lib/ruby_llm/agents/core/configuration.rb +97 -117
- data/lib/ruby_llm/agents/core/errors.rb +0 -58
- data/lib/ruby_llm/agents/core/instrumentation.rb +157 -110
- data/lib/ruby_llm/agents/core/llm_tenant.rb +8 -7
- data/lib/ruby_llm/agents/core/version.rb +1 -1
- data/lib/ruby_llm/agents/dsl/base.rb +157 -17
- data/lib/ruby_llm/agents/dsl/caching.rb +33 -2
- data/lib/ruby_llm/agents/dsl/reliability.rb +148 -0
- data/lib/ruby_llm/agents/dsl.rb +1 -2
- data/lib/ruby_llm/agents/image/analyzer/execution.rb +1 -2
- data/lib/ruby_llm/agents/image/background_remover/execution.rb +1 -2
- data/lib/ruby_llm/agents/image/concerns/image_operation_dsl.rb +1 -13
- data/lib/ruby_llm/agents/image/concerns/image_operation_execution.rb +2 -2
- data/lib/ruby_llm/agents/image/editor/dsl.rb +0 -14
- data/lib/ruby_llm/agents/image/editor/execution.rb +1 -10
- data/lib/ruby_llm/agents/image/editor.rb +0 -1
- data/lib/ruby_llm/agents/image/generator.rb +0 -21
- data/lib/ruby_llm/agents/image/pipeline/dsl.rb +0 -13
- data/lib/ruby_llm/agents/image/pipeline/execution.rb +0 -1
- data/lib/ruby_llm/agents/image/transformer/dsl.rb +0 -13
- data/lib/ruby_llm/agents/image/transformer/execution.rb +1 -10
- data/lib/ruby_llm/agents/image/transformer.rb +0 -1
- data/lib/ruby_llm/agents/image/upscaler/execution.rb +1 -2
- data/lib/ruby_llm/agents/image/variator/execution.rb +1 -2
- data/lib/ruby_llm/agents/infrastructure/alert_manager.rb +78 -173
- data/lib/ruby_llm/agents/infrastructure/budget/budget_query.rb +66 -2
- data/lib/ruby_llm/agents/infrastructure/budget/spend_recorder.rb +0 -12
- data/lib/ruby_llm/agents/infrastructure/circuit_breaker.rb +10 -13
- data/lib/ruby_llm/agents/infrastructure/execution_logger_job.rb +8 -0
- data/lib/ruby_llm/agents/pipeline/context.rb +0 -1
- data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +28 -4
- data/lib/ruby_llm/agents/pipeline/middleware/cache.rb +3 -10
- data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +88 -55
- data/lib/ruby_llm/agents/pipeline/middleware/tenant.rb +5 -41
- data/lib/ruby_llm/agents/rails/engine.rb +6 -6
- data/lib/ruby_llm/agents/results/base.rb +1 -49
- data/lib/ruby_llm/agents/text/embedder.rb +0 -1
- data/lib/ruby_llm/agents.rb +1 -9
- data/lib/tasks/ruby_llm_agents.rake +34 -0
- metadata +14 -83
- data/app/controllers/ruby_llm/agents/api_configurations_controller.rb +0 -214
- data/app/controllers/ruby_llm/agents/workflows_controller.rb +0 -544
- data/app/mailers/ruby_llm/agents/alert_mailer.rb +0 -84
- data/app/mailers/ruby_llm/agents/application_mailer.rb +0 -28
- data/app/models/ruby_llm/agents/api_configuration.rb +0 -386
- data/app/models/ruby_llm/agents/execution/workflow.rb +0 -170
- data/app/models/ruby_llm/agents/tenant/configurable.rb +0 -135
- data/app/views/ruby_llm/agents/agents/_agent.html.erb +0 -98
- data/app/views/ruby_llm/agents/agents/_version_comparison.html.erb +0 -186
- data/app/views/ruby_llm/agents/agents/_workflow.html.erb +0 -126
- data/app/views/ruby_llm/agents/alert_mailer/alert_notification.html.erb +0 -107
- data/app/views/ruby_llm/agents/alert_mailer/alert_notification.text.erb +0 -18
- data/app/views/ruby_llm/agents/api_configurations/_api_key_field.html.erb +0 -34
- data/app/views/ruby_llm/agents/api_configurations/_form.html.erb +0 -288
- data/app/views/ruby_llm/agents/api_configurations/edit.html.erb +0 -95
- data/app/views/ruby_llm/agents/api_configurations/edit_tenant.html.erb +0 -97
- data/app/views/ruby_llm/agents/api_configurations/show.html.erb +0 -214
- data/app/views/ruby_llm/agents/api_configurations/tenant.html.erb +0 -179
- data/app/views/ruby_llm/agents/dashboard/_agent_comparison.html.erb +0 -73
- data/app/views/ruby_llm/agents/dashboard/_alerts_feed.html.erb +0 -62
- data/app/views/ruby_llm/agents/dashboard/_breaker_strip.html.erb +0 -47
- data/app/views/ruby_llm/agents/dashboard/_budgets_bar.html.erb +0 -75
- data/app/views/ruby_llm/agents/dashboard/_model_comparison.html.erb +0 -56
- data/app/views/ruby_llm/agents/dashboard/_model_cost_breakdown.html.erb +0 -115
- data/app/views/ruby_llm/agents/dashboard/_now_strip.html.erb +0 -59
- data/app/views/ruby_llm/agents/dashboard/_top_errors.html.erb +0 -60
- data/app/views/ruby_llm/agents/executions/_workflow_summary.html.erb +0 -86
- data/app/views/ruby_llm/agents/executions/dry_run.html.erb +0 -149
- data/app/views/ruby_llm/agents/shared/_breadcrumbs.html.erb +0 -48
- data/app/views/ruby_llm/agents/shared/_nav_link.html.erb +0 -27
- data/app/views/ruby_llm/agents/shared/_stat_card.html.erb +0 -14
- data/app/views/ruby_llm/agents/shared/_workflow_type_badge.html.erb +0 -35
- data/app/views/ruby_llm/agents/workflows/_empty_state.html.erb +0 -22
- data/app/views/ruby_llm/agents/workflows/_step_performance.html.erb +0 -228
- data/app/views/ruby_llm/agents/workflows/_structure_dsl.html.erb +0 -539
- data/app/views/ruby_llm/agents/workflows/_structure_parallel.html.erb +0 -76
- data/app/views/ruby_llm/agents/workflows/_structure_pipeline.html.erb +0 -74
- data/app/views/ruby_llm/agents/workflows/_structure_router.html.erb +0 -108
- data/app/views/ruby_llm/agents/workflows/_workflow_diagram.html.erb +0 -920
- data/app/views/ruby_llm/agents/workflows/index.html.erb +0 -179
- data/app/views/ruby_llm/agents/workflows/show.html.erb +0 -467
- data/lib/generators/ruby_llm_agents/api_configuration_generator.rb +0 -100
- data/lib/generators/ruby_llm_agents/templates/add_workflow_migration.rb.tt +0 -38
- data/lib/generators/ruby_llm_agents/templates/application_workflow.rb.tt +0 -48
- data/lib/generators/ruby_llm_agents/templates/create_api_configurations_migration.rb.tt +0 -90
- data/lib/generators/ruby_llm_agents/templates/skills/WORKFLOWS.md.tt +0 -551
- data/lib/ruby_llm/agents/core/base/moderation_dsl.rb +0 -181
- data/lib/ruby_llm/agents/core/base/moderation_execution.rb +0 -274
- data/lib/ruby_llm/agents/core/resolved_config.rb +0 -348
- data/lib/ruby_llm/agents/image/generator/content_policy.rb +0 -95
- data/lib/ruby_llm/agents/infrastructure/redactor.rb +0 -130
- data/lib/ruby_llm/agents/results/moderation_result.rb +0 -158
- data/lib/ruby_llm/agents/text/moderator.rb +0 -237
- data/lib/ruby_llm/agents/workflow/approval.rb +0 -205
- data/lib/ruby_llm/agents/workflow/approval_store.rb +0 -179
- data/lib/ruby_llm/agents/workflow/async.rb +0 -220
- data/lib/ruby_llm/agents/workflow/async_executor.rb +0 -156
- data/lib/ruby_llm/agents/workflow/dsl/executor.rb +0 -467
- data/lib/ruby_llm/agents/workflow/dsl/input_schema.rb +0 -244
- data/lib/ruby_llm/agents/workflow/dsl/iteration_executor.rb +0 -289
- data/lib/ruby_llm/agents/workflow/dsl/parallel_group.rb +0 -107
- data/lib/ruby_llm/agents/workflow/dsl/route_builder.rb +0 -150
- data/lib/ruby_llm/agents/workflow/dsl/schedule_helpers.rb +0 -187
- data/lib/ruby_llm/agents/workflow/dsl/step_config.rb +0 -352
- data/lib/ruby_llm/agents/workflow/dsl/step_executor.rb +0 -415
- data/lib/ruby_llm/agents/workflow/dsl/wait_config.rb +0 -257
- data/lib/ruby_llm/agents/workflow/dsl/wait_executor.rb +0 -317
- data/lib/ruby_llm/agents/workflow/dsl.rb +0 -576
- data/lib/ruby_llm/agents/workflow/instrumentation.rb +0 -249
- data/lib/ruby_llm/agents/workflow/notifiers/base.rb +0 -117
- data/lib/ruby_llm/agents/workflow/notifiers/email.rb +0 -117
- data/lib/ruby_llm/agents/workflow/notifiers/slack.rb +0 -180
- data/lib/ruby_llm/agents/workflow/notifiers/webhook.rb +0 -121
- data/lib/ruby_llm/agents/workflow/notifiers.rb +0 -70
- data/lib/ruby_llm/agents/workflow/orchestrator.rb +0 -416
- data/lib/ruby_llm/agents/workflow/result.rb +0 -592
- data/lib/ruby_llm/agents/workflow/thread_pool.rb +0 -185
- data/lib/ruby_llm/agents/workflow/throttle_manager.rb +0 -206
- data/lib/ruby_llm/agents/workflow/wait_result.rb +0 -213
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 h-full">
|
|
2
|
-
<div class="px-4 py-3 border-b border-gray-100 dark:border-gray-700">
|
|
3
|
-
<div class="flex items-center justify-between">
|
|
4
|
-
<h3 class="text-base font-semibold text-gray-900 dark:text-gray-100">Model Performance</h3>
|
|
5
|
-
<span class="text-xs text-gray-500 dark:text-gray-400">By cost</span>
|
|
6
|
-
</div>
|
|
7
|
-
</div>
|
|
8
|
-
|
|
9
|
-
<% if model_stats.any? %>
|
|
10
|
-
<div class="overflow-x-auto">
|
|
11
|
-
<table class="w-full text-sm">
|
|
12
|
-
<thead>
|
|
13
|
-
<tr class="text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
14
|
-
<th class="px-4 py-2">Model</th>
|
|
15
|
-
<th class="px-4 py-2 text-right">Runs</th>
|
|
16
|
-
<th class="px-4 py-2 text-right">$/1K tok</th>
|
|
17
|
-
<th class="px-4 py-2 text-right">Avg Time</th>
|
|
18
|
-
<th class="px-4 py-2 text-right">Success</th>
|
|
19
|
-
</tr>
|
|
20
|
-
</thead>
|
|
21
|
-
<tbody class="divide-y divide-gray-100 dark:divide-gray-700">
|
|
22
|
-
<% model_stats.first(8).each do |model| %>
|
|
23
|
-
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50">
|
|
24
|
-
<td class="px-4 py-2">
|
|
25
|
-
<span class="font-mono text-xs font-medium text-gray-900 dark:text-gray-100 truncate max-w-[120px] block" title="<%= model[:model_id] %>">
|
|
26
|
-
<%= model[:model_id].to_s.split('/').last.truncate(20) %>
|
|
27
|
-
</span>
|
|
28
|
-
</td>
|
|
29
|
-
<td class="px-4 py-2 text-right text-gray-600 dark:text-gray-300">
|
|
30
|
-
<%= number_with_delimiter(model[:executions]) %>
|
|
31
|
-
</td>
|
|
32
|
-
<td class="px-4 py-2 text-right text-gray-600 dark:text-gray-300">
|
|
33
|
-
$<%= number_with_precision(model[:cost_per_1k_tokens], precision: 4) %>
|
|
34
|
-
</td>
|
|
35
|
-
<td class="px-4 py-2 text-right text-gray-600 dark:text-gray-300">
|
|
36
|
-
<%= format_duration_ms(model[:avg_duration_ms]) %>
|
|
37
|
-
</td>
|
|
38
|
-
<td class="px-4 py-2 text-right">
|
|
39
|
-
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium <%= model[:success_rate] >= 95 ? 'bg-green-100 dark:bg-green-900 text-green-700 dark:text-green-300' : model[:success_rate] >= 80 ? 'bg-yellow-100 dark:bg-yellow-900 text-yellow-700 dark:text-yellow-300' : 'bg-red-100 dark:bg-red-900 text-red-700 dark:text-red-300' %>">
|
|
40
|
-
<%= model[:success_rate].round %>%
|
|
41
|
-
</span>
|
|
42
|
-
</td>
|
|
43
|
-
</tr>
|
|
44
|
-
<% end %>
|
|
45
|
-
</tbody>
|
|
46
|
-
</table>
|
|
47
|
-
</div>
|
|
48
|
-
<% else %>
|
|
49
|
-
<div class="px-4 py-8 text-center text-gray-500 dark:text-gray-400">
|
|
50
|
-
<svg class="w-8 h-8 mx-auto mb-2 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
51
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z"/>
|
|
52
|
-
</svg>
|
|
53
|
-
<p class="text-sm">No model data yet</p>
|
|
54
|
-
</div>
|
|
55
|
-
<% end %>
|
|
56
|
-
</div>
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 h-full">
|
|
2
|
-
<div class="px-4 py-3 border-b border-gray-100 dark:border-gray-700">
|
|
3
|
-
<div class="flex items-center justify-between">
|
|
4
|
-
<h3 class="text-base font-semibold text-gray-900 dark:text-gray-100">Cost by Model</h3>
|
|
5
|
-
<span class="text-xs text-gray-500 dark:text-gray-400">
|
|
6
|
-
$<%= number_with_precision(model_stats.sum { |m| m[:total_cost] }, precision: 2) %> total
|
|
7
|
-
</span>
|
|
8
|
-
</div>
|
|
9
|
-
</div>
|
|
10
|
-
|
|
11
|
-
<% if model_stats.any? && model_stats.sum { |m| m[:total_cost] } > 0 %>
|
|
12
|
-
<div class="p-4">
|
|
13
|
-
<div id="model-cost-chart" style="width: 100%; height: 200px;"></div>
|
|
14
|
-
|
|
15
|
-
<!-- Legend -->
|
|
16
|
-
<div class="mt-4 space-y-2">
|
|
17
|
-
<% colors = ['#6366F1', '#8B5CF6', '#EC4899', '#F59E0B', '#10B981', '#3B82F6', '#EF4444', '#6B7280'] %>
|
|
18
|
-
<% model_stats.first(5).each_with_index do |model, i| %>
|
|
19
|
-
<div class="flex items-center justify-between text-sm">
|
|
20
|
-
<div class="flex items-center gap-2">
|
|
21
|
-
<span class="w-3 h-3 rounded-full flex-shrink-0" style="background-color: <%= colors[i % colors.length] %>"></span>
|
|
22
|
-
<span class="text-gray-700 dark:text-gray-300 truncate max-w-[140px]" title="<%= model[:model_id] %>">
|
|
23
|
-
<%= model[:model_id].to_s.split('/').last.truncate(18) %>
|
|
24
|
-
</span>
|
|
25
|
-
</div>
|
|
26
|
-
<div class="flex items-center gap-2">
|
|
27
|
-
<span class="text-gray-900 dark:text-gray-100 font-medium">$<%= number_with_precision(model[:total_cost], precision: 2) %></span>
|
|
28
|
-
<span class="text-gray-500 dark:text-gray-400 text-xs w-12 text-right"><%= model[:cost_percentage] %>%</span>
|
|
29
|
-
</div>
|
|
30
|
-
</div>
|
|
31
|
-
<% end %>
|
|
32
|
-
<% if model_stats.length > 5 %>
|
|
33
|
-
<% other_cost = model_stats[5..].sum { |m| m[:total_cost] } %>
|
|
34
|
-
<% other_percentage = model_stats[5..].sum { |m| m[:cost_percentage] } %>
|
|
35
|
-
<div class="flex items-center justify-between text-sm">
|
|
36
|
-
<div class="flex items-center gap-2">
|
|
37
|
-
<span class="w-3 h-3 rounded-full flex-shrink-0 bg-gray-400"></span>
|
|
38
|
-
<span class="text-gray-700 dark:text-gray-300">Other (<%= model_stats.length - 5 %> models)</span>
|
|
39
|
-
</div>
|
|
40
|
-
<div class="flex items-center gap-2">
|
|
41
|
-
<span class="text-gray-900 dark:text-gray-100 font-medium">$<%= number_with_precision(other_cost, precision: 2) %></span>
|
|
42
|
-
<span class="text-gray-500 dark:text-gray-400 text-xs w-12 text-right"><%= other_percentage.round(1) %>%</span>
|
|
43
|
-
</div>
|
|
44
|
-
</div>
|
|
45
|
-
<% end %>
|
|
46
|
-
</div>
|
|
47
|
-
</div>
|
|
48
|
-
|
|
49
|
-
<script>
|
|
50
|
-
(function() {
|
|
51
|
-
const colors = ['#6366F1', '#8B5CF6', '#EC4899', '#F59E0B', '#10B981', '#3B82F6', '#EF4444', '#6B7280'];
|
|
52
|
-
const data = [
|
|
53
|
-
<% model_stats.first(5).each_with_index do |model, i| %>
|
|
54
|
-
{ name: '<%= model[:model_id].to_s.split('/').last.truncate(15).gsub("'", "\\'") %>', y: <%= model[:total_cost] %>, color: colors[<%= i %>] },
|
|
55
|
-
<% end %>
|
|
56
|
-
<% if model_stats.length > 5 %>
|
|
57
|
-
{ name: 'Other', y: <%= model_stats[5..].sum { |m| m[:total_cost] } %>, color: '#9CA3AF' },
|
|
58
|
-
<% end %>
|
|
59
|
-
];
|
|
60
|
-
|
|
61
|
-
function initChart() {
|
|
62
|
-
if (typeof Highcharts === 'undefined') {
|
|
63
|
-
setTimeout(initChart, 100);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
Highcharts.chart('model-cost-chart', {
|
|
68
|
-
chart: {
|
|
69
|
-
type: 'pie',
|
|
70
|
-
backgroundColor: 'transparent',
|
|
71
|
-
spacing: [0, 0, 0, 0]
|
|
72
|
-
},
|
|
73
|
-
title: { text: null },
|
|
74
|
-
credits: { enabled: false },
|
|
75
|
-
tooltip: {
|
|
76
|
-
backgroundColor: 'rgba(17, 24, 39, 0.95)',
|
|
77
|
-
borderColor: 'transparent',
|
|
78
|
-
borderRadius: 8,
|
|
79
|
-
style: { color: '#F3F4F6', fontSize: '12px' },
|
|
80
|
-
pointFormat: '<b>${point.y:.2f}</b> ({point.percentage:.1f}%)'
|
|
81
|
-
},
|
|
82
|
-
plotOptions: {
|
|
83
|
-
pie: {
|
|
84
|
-
innerSize: '60%',
|
|
85
|
-
dataLabels: { enabled: false },
|
|
86
|
-
borderWidth: 0,
|
|
87
|
-
states: {
|
|
88
|
-
hover: { brightness: 0.1 }
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
series: [{
|
|
93
|
-
name: 'Cost',
|
|
94
|
-
data: data
|
|
95
|
-
}]
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (document.readyState === 'loading') {
|
|
100
|
-
document.addEventListener('DOMContentLoaded', initChart);
|
|
101
|
-
} else {
|
|
102
|
-
initChart();
|
|
103
|
-
}
|
|
104
|
-
})();
|
|
105
|
-
</script>
|
|
106
|
-
<% else %>
|
|
107
|
-
<div class="px-4 py-8 text-center text-gray-500 dark:text-gray-400">
|
|
108
|
-
<svg class="w-8 h-8 mx-auto mb-2 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
109
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M11 3.055A9.001 9.001 0 1020.945 13H11V3.055z"/>
|
|
110
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M20.488 9H15V3.512A9.025 9.025 0 0120.488 9z"/>
|
|
111
|
-
</svg>
|
|
112
|
-
<p class="text-sm">No cost data yet</p>
|
|
113
|
-
</div>
|
|
114
|
-
<% end %>
|
|
115
|
-
</div>
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
<div class="mb-6">
|
|
2
|
-
<div class="flex items-center justify-end mb-4">
|
|
3
|
-
<div class="flex space-x-1 bg-gray-100 dark:bg-gray-700 rounded-lg p-1">
|
|
4
|
-
<%= link_to "Today", ruby_llm_agents.root_path(range: "today"),
|
|
5
|
-
class: "px-3 py-1 text-xs font-medium rounded-md transition-colors #{@selected_range == 'today' ? 'bg-white dark:bg-gray-600 text-gray-900 dark:text-white shadow-sm' : 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white'}" %>
|
|
6
|
-
<%= link_to "7 Days", ruby_llm_agents.root_path(range: "7d"),
|
|
7
|
-
class: "px-3 py-1 text-xs font-medium rounded-md transition-colors #{@selected_range == '7d' ? 'bg-white dark:bg-gray-600 text-gray-900 dark:text-white shadow-sm' : 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white'}" %>
|
|
8
|
-
<%= link_to "30 Days", ruby_llm_agents.root_path(range: "30d"),
|
|
9
|
-
class: "px-3 py-1 text-xs font-medium rounded-md transition-colors #{@selected_range == '30d' ? 'bg-white dark:bg-gray-600 text-gray-900 dark:text-white shadow-sm' : 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white'}" %>
|
|
10
|
-
</div>
|
|
11
|
-
</div>
|
|
12
|
-
|
|
13
|
-
<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-4">
|
|
14
|
-
<!-- Success -->
|
|
15
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg p-5 shadow-sm border-t-2 border-green-500">
|
|
16
|
-
<p class="text-2xl font-bold text-gray-900 dark:text-gray-100">
|
|
17
|
-
<%= now_strip[:success_today] %>
|
|
18
|
-
<%= comparison_indicator(now_strip.dig(:comparisons, :success_change), metric_type: :success) %>
|
|
19
|
-
</p>
|
|
20
|
-
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">Success</p>
|
|
21
|
-
</div>
|
|
22
|
-
|
|
23
|
-
<!-- Errors -->
|
|
24
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg p-5 shadow-sm border-t-2 border-red-500">
|
|
25
|
-
<p class="text-2xl font-bold <%= now_strip[:errors_today] > 0 ? 'text-red-600 dark:text-red-400' : 'text-gray-900 dark:text-gray-100' %>">
|
|
26
|
-
<%= now_strip[:errors_today] %>
|
|
27
|
-
<%= comparison_indicator(now_strip.dig(:comparisons, :errors_change), metric_type: :errors) %>
|
|
28
|
-
</p>
|
|
29
|
-
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">Errors</p>
|
|
30
|
-
</div>
|
|
31
|
-
|
|
32
|
-
<!-- Cost -->
|
|
33
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg p-5 shadow-sm border-t-2 border-amber-500">
|
|
34
|
-
<p class="text-2xl font-bold text-gray-900 dark:text-gray-100">
|
|
35
|
-
$<%= number_with_precision(now_strip[:cost_today], precision: 4) %>
|
|
36
|
-
<%= comparison_indicator(now_strip.dig(:comparisons, :cost_change), metric_type: :cost) %>
|
|
37
|
-
</p>
|
|
38
|
-
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">Cost</p>
|
|
39
|
-
</div>
|
|
40
|
-
|
|
41
|
-
<!-- Avg Duration -->
|
|
42
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg p-5 shadow-sm border-t-2 border-purple-500">
|
|
43
|
-
<p class="text-2xl font-bold text-gray-900 dark:text-gray-100">
|
|
44
|
-
<%= format_duration_ms(now_strip[:avg_duration_ms]) %>
|
|
45
|
-
<%= comparison_indicator(now_strip.dig(:comparisons, :duration_change), metric_type: :duration) %>
|
|
46
|
-
</p>
|
|
47
|
-
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">Avg Duration</p>
|
|
48
|
-
</div>
|
|
49
|
-
|
|
50
|
-
<!-- Tokens -->
|
|
51
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg p-5 shadow-sm border-t-2 border-indigo-500">
|
|
52
|
-
<p class="text-2xl font-bold text-gray-900 dark:text-gray-100">
|
|
53
|
-
<%= number_to_human_short(now_strip[:total_tokens]) %>
|
|
54
|
-
<%= comparison_indicator(now_strip.dig(:comparisons, :tokens_change), metric_type: :tokens) %>
|
|
55
|
-
</p>
|
|
56
|
-
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">Tokens</p>
|
|
57
|
-
</div>
|
|
58
|
-
</div>
|
|
59
|
-
</div>
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 h-full">
|
|
2
|
-
<div class="px-4 py-3 border-b border-gray-100 dark:border-gray-700">
|
|
3
|
-
<h3 class="text-base font-semibold text-gray-900 dark:text-gray-100">Top Errors</h3>
|
|
4
|
-
</div>
|
|
5
|
-
|
|
6
|
-
<% if top_errors.any? %>
|
|
7
|
-
<div class="overflow-x-auto">
|
|
8
|
-
<table class="w-full text-sm">
|
|
9
|
-
<thead>
|
|
10
|
-
<tr class="text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
11
|
-
<th class="px-4 py-2">Error</th>
|
|
12
|
-
<th class="px-4 py-2 text-right">Count</th>
|
|
13
|
-
<th class="px-4 py-2 text-right">Last Seen</th>
|
|
14
|
-
</tr>
|
|
15
|
-
</thead>
|
|
16
|
-
<tbody class="divide-y divide-gray-100 dark:divide-gray-700">
|
|
17
|
-
<% top_errors.first(5).each do |error| %>
|
|
18
|
-
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50">
|
|
19
|
-
<td class="px-4 py-2">
|
|
20
|
-
<div>
|
|
21
|
-
<span class="text-sm font-medium text-gray-900 dark:text-gray-100 truncate block max-w-[180px]" title="<%= error[:error_class] %>">
|
|
22
|
-
<%= error[:error_class].to_s.split("::").last %>
|
|
23
|
-
</span>
|
|
24
|
-
<!-- Progress bar showing error distribution -->
|
|
25
|
-
<div class="mt-1 w-full h-1.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
|
|
26
|
-
<div class="h-full bg-red-500 rounded-full transition-all duration-300" style="width: <%= error[:percentage] %>%"></div>
|
|
27
|
-
</div>
|
|
28
|
-
</div>
|
|
29
|
-
</td>
|
|
30
|
-
<td class="px-4 py-2 text-right">
|
|
31
|
-
<div class="flex items-center justify-end gap-1">
|
|
32
|
-
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-red-100 dark:bg-red-900 text-red-700 dark:text-red-300">
|
|
33
|
-
<%= number_with_delimiter(error[:count]) %>
|
|
34
|
-
</span>
|
|
35
|
-
<span class="text-xs text-gray-500 dark:text-gray-400">
|
|
36
|
-
(<%= error[:percentage] %>%)
|
|
37
|
-
</span>
|
|
38
|
-
</div>
|
|
39
|
-
</td>
|
|
40
|
-
<td class="px-4 py-2 text-right text-xs text-gray-500 dark:text-gray-400">
|
|
41
|
-
<% if error[:last_seen] %>
|
|
42
|
-
<%= time_ago_in_words(error[:last_seen]) %>
|
|
43
|
-
<% else %>
|
|
44
|
-
-
|
|
45
|
-
<% end %>
|
|
46
|
-
</td>
|
|
47
|
-
</tr>
|
|
48
|
-
<% end %>
|
|
49
|
-
</tbody>
|
|
50
|
-
</table>
|
|
51
|
-
</div>
|
|
52
|
-
<% else %>
|
|
53
|
-
<div class="px-4 py-8 text-center text-green-500 dark:text-green-400">
|
|
54
|
-
<svg class="w-8 h-8 mx-auto mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
55
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
56
|
-
</svg>
|
|
57
|
-
<p class="text-sm">No errors</p>
|
|
58
|
-
</div>
|
|
59
|
-
<% end %>
|
|
60
|
-
</div>
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
<%# Simplified Workflow Summary - Clean table approach %>
|
|
2
|
-
<%# @param execution [RubyLLM::Agents::Execution] The workflow execution to display %>
|
|
3
|
-
|
|
4
|
-
<% if execution.root_workflow? %>
|
|
5
|
-
<% stats = execution.workflow_aggregate_stats %>
|
|
6
|
-
<% steps = execution.workflow_steps.to_a %>
|
|
7
|
-
|
|
8
|
-
<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden mb-6">
|
|
9
|
-
<!-- Header -->
|
|
10
|
-
<div class="px-4 py-3 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
|
11
|
-
<div class="flex items-center gap-2">
|
|
12
|
-
<span class="text-emerald-600 dark:text-emerald-400">◈</span>
|
|
13
|
-
<span class="font-medium text-gray-900 dark:text-gray-100">Workflow</span>
|
|
14
|
-
<span class="text-gray-500 dark:text-gray-400">· <%= stats[:steps_count] %> steps</span>
|
|
15
|
-
</div>
|
|
16
|
-
|
|
17
|
-
<% overall_status = execution.workflow_overall_status %>
|
|
18
|
-
<%= render "ruby_llm/agents/shared/status_badge", status: overall_status.to_s, size: :sm %>
|
|
19
|
-
</div>
|
|
20
|
-
|
|
21
|
-
<!-- Table -->
|
|
22
|
-
<div class="overflow-x-auto">
|
|
23
|
-
<table class="min-w-full text-sm">
|
|
24
|
-
<thead class="bg-gray-50 dark:bg-gray-900/50">
|
|
25
|
-
<tr>
|
|
26
|
-
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Step</th>
|
|
27
|
-
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Status</th>
|
|
28
|
-
<th class="px-4 py-2 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Duration</th>
|
|
29
|
-
<th class="px-4 py-2 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Tokens</th>
|
|
30
|
-
<th class="px-4 py-2 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Cost</th>
|
|
31
|
-
</tr>
|
|
32
|
-
</thead>
|
|
33
|
-
<tbody class="divide-y divide-gray-100 dark:divide-gray-700">
|
|
34
|
-
<% steps.each_with_index do |step, index| %>
|
|
35
|
-
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50">
|
|
36
|
-
<td class="px-4 py-2">
|
|
37
|
-
<%= link_to ruby_llm_agents.execution_path(step.id), class: "text-blue-600 dark:text-blue-400 hover:underline" do %>
|
|
38
|
-
<span class="text-gray-400 dark:text-gray-500"><%= index + 1 %>.</span>
|
|
39
|
-
<%= step.workflow_step || step.agent_type.gsub(/Agent$/, "") %>
|
|
40
|
-
<% end %>
|
|
41
|
-
</td>
|
|
42
|
-
<td class="px-4 py-2">
|
|
43
|
-
<% case step.status
|
|
44
|
-
when "success" %>
|
|
45
|
-
<span class="text-green-600 dark:text-green-400">✓</span>
|
|
46
|
-
<% when "error" %>
|
|
47
|
-
<span class="text-red-600 dark:text-red-400">✗</span>
|
|
48
|
-
<% when "timeout" %>
|
|
49
|
-
<span class="text-orange-600 dark:text-orange-400">⏱</span>
|
|
50
|
-
<% when "running" %>
|
|
51
|
-
<span class="text-blue-600 dark:text-blue-400 animate-pulse">●</span>
|
|
52
|
-
<% else %>
|
|
53
|
-
<span class="text-gray-400">○</span>
|
|
54
|
-
<% end %>
|
|
55
|
-
</td>
|
|
56
|
-
<td class="px-4 py-2 text-right text-gray-600 dark:text-gray-300 tabular-nums">
|
|
57
|
-
<%= step.duration_ms ? "#{number_with_delimiter(step.duration_ms)}ms" : "-" %>
|
|
58
|
-
</td>
|
|
59
|
-
<td class="px-4 py-2 text-right text-gray-600 dark:text-gray-300 tabular-nums">
|
|
60
|
-
<%= number_with_delimiter(step.total_tokens || 0) %>
|
|
61
|
-
</td>
|
|
62
|
-
<td class="px-4 py-2 text-right text-gray-600 dark:text-gray-300 tabular-nums">
|
|
63
|
-
$<%= number_with_precision(step.total_cost || 0, precision: 4) %>
|
|
64
|
-
</td>
|
|
65
|
-
</tr>
|
|
66
|
-
<% end %>
|
|
67
|
-
</tbody>
|
|
68
|
-
<tfoot class="bg-gray-50 dark:bg-gray-900/50 font-medium">
|
|
69
|
-
<tr>
|
|
70
|
-
<td class="px-4 py-2 text-gray-700 dark:text-gray-300">Total</td>
|
|
71
|
-
<td class="px-4 py-2"></td>
|
|
72
|
-
<td class="px-4 py-2 text-right text-gray-700 dark:text-gray-300 tabular-nums">
|
|
73
|
-
<%= stats[:wall_clock_ms] ? "#{number_with_delimiter(stats[:wall_clock_ms])}ms" : "-" %>
|
|
74
|
-
</td>
|
|
75
|
-
<td class="px-4 py-2 text-right text-gray-700 dark:text-gray-300 tabular-nums">
|
|
76
|
-
<%= number_with_delimiter(stats[:total_tokens]) %>
|
|
77
|
-
</td>
|
|
78
|
-
<td class="px-4 py-2 text-right text-gray-700 dark:text-gray-300 tabular-nums">
|
|
79
|
-
$<%= number_with_precision(stats[:total_cost], precision: 4) %>
|
|
80
|
-
</td>
|
|
81
|
-
</tr>
|
|
82
|
-
</tfoot>
|
|
83
|
-
</table>
|
|
84
|
-
</div>
|
|
85
|
-
</div>
|
|
86
|
-
<% end %>
|
|
@@ -1,149 +0,0 @@
|
|
|
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,48 +0,0 @@
|
|
|
1
|
-
<%
|
|
2
|
-
# Breadcrumb navigation partial
|
|
3
|
-
# Usage:
|
|
4
|
-
# render "ruby_llm/agents/shared/breadcrumbs", items: [
|
|
5
|
-
# { label: "Dashboard", path: ruby_llm_agents.root_path },
|
|
6
|
-
# { label: "Executions", path: ruby_llm_agents.executions_path },
|
|
7
|
-
# { label: "#123" } # Current page (no path)
|
|
8
|
-
# ]
|
|
9
|
-
#
|
|
10
|
-
# Or with simple array:
|
|
11
|
-
# render "ruby_llm/agents/shared/breadcrumbs", items: [
|
|
12
|
-
# ["Dashboard", ruby_llm_agents.root_path],
|
|
13
|
-
# ["Executions", ruby_llm_agents.executions_path],
|
|
14
|
-
# ["#123"]
|
|
15
|
-
# ]
|
|
16
|
-
|
|
17
|
-
items = local_assigns[:items] || []
|
|
18
|
-
%>
|
|
19
|
-
<nav class="flex items-center text-sm mb-4" aria-label="Breadcrumb">
|
|
20
|
-
<ol class="flex items-center space-x-1">
|
|
21
|
-
<% items.each_with_index do |item, index| %>
|
|
22
|
-
<%
|
|
23
|
-
# Normalize item format
|
|
24
|
-
if item.is_a?(Array)
|
|
25
|
-
label = item[0]
|
|
26
|
-
path = item[1]
|
|
27
|
-
else
|
|
28
|
-
label = item[:label]
|
|
29
|
-
path = item[:path]
|
|
30
|
-
end
|
|
31
|
-
is_last = index == items.length - 1
|
|
32
|
-
%>
|
|
33
|
-
<li class="flex items-center">
|
|
34
|
-
<% if index > 0 %>
|
|
35
|
-
<svg class="w-4 h-4 text-gray-400 dark:text-gray-500 mx-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
36
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
|
37
|
-
</svg>
|
|
38
|
-
<% end %>
|
|
39
|
-
|
|
40
|
-
<% if path.present? && !is_last %>
|
|
41
|
-
<%= link_to label, path, class: "text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 transition-colors" %>
|
|
42
|
-
<% else %>
|
|
43
|
-
<span class="text-gray-900 dark:text-gray-100 font-medium"><%= label %></span>
|
|
44
|
-
<% end %>
|
|
45
|
-
</li>
|
|
46
|
-
<% end %>
|
|
47
|
-
</ol>
|
|
48
|
-
</nav>
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
<%# Reusable navigation link for desktop and mobile %>
|
|
2
|
-
<%
|
|
3
|
-
# Determine if link is active
|
|
4
|
-
is_active = if path == ruby_llm_agents.root_path
|
|
5
|
-
current_page?(path)
|
|
6
|
-
elsif path == ruby_llm_agents.agents_path
|
|
7
|
-
request.path.start_with?(path)
|
|
8
|
-
else
|
|
9
|
-
current_page?(path)
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
# Style classes
|
|
13
|
-
base_classes = mobile ? "flex items-center px-3 py-2 text-base font-medium rounded-md" : "inline-flex items-center px-3 py-1.5 text-sm font-medium rounded-md"
|
|
14
|
-
active_classes = "bg-gray-200 dark:bg-gray-700 text-gray-900 dark:text-gray-100"
|
|
15
|
-
inactive_classes = "text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-700"
|
|
16
|
-
icon_classes = mobile ? "w-5 h-5 mr-3" : "w-4 h-4 mr-1.5"
|
|
17
|
-
|
|
18
|
-
link_options = { class: "#{base_classes} #{is_active ? active_classes : inactive_classes}" }
|
|
19
|
-
link_options["x-on:click"] = "mobileMenuOpen = false" if mobile
|
|
20
|
-
%>
|
|
21
|
-
|
|
22
|
-
<%= link_to path, **link_options do %>
|
|
23
|
-
<svg class="<%= icon_classes %>" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
24
|
-
<%= icon.html_safe %>
|
|
25
|
-
</svg>
|
|
26
|
-
<%= label %>
|
|
27
|
-
<% end %>
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-4">
|
|
2
|
-
<div class="flex items-center justify-between">
|
|
3
|
-
<p class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide font-medium"><%= title %></p>
|
|
4
|
-
<span class="<%= local_assigns[:icon_color] || 'text-gray-400 dark:text-gray-500' %>">
|
|
5
|
-
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
6
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="<%= icon %>"/>
|
|
7
|
-
</svg>
|
|
8
|
-
</span>
|
|
9
|
-
</div>
|
|
10
|
-
<p class="text-xl font-semibold <%= local_assigns[:value_color] || 'text-gray-900 dark:text-gray-100' %> mt-2"><%= value %></p>
|
|
11
|
-
<% if local_assigns[:subtitle].present? %>
|
|
12
|
-
<p class="text-xs text-gray-400 dark:text-gray-500"><%= subtitle %></p>
|
|
13
|
-
<% end %>
|
|
14
|
-
</div>
|