ruby_llm-agents 1.3.3 → 2.0.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 +101 -334
- 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 +46 -10
- 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 +87 -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 +528 -989
- 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 +9 -14
- 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 +9 -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 +58 -262
- 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 +52 -6
- 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 +58 -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/attempt_tracker.rb +1 -0
- 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/reliability.rb +37 -2
- 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 +12 -81
- 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,179 +0,0 @@
|
|
|
1
|
-
<div class="mb-6">
|
|
2
|
-
<div class="flex items-center gap-2">
|
|
3
|
-
<h1 class="text-2xl font-bold text-gray-900 dark:text-gray-100">Workflows</h1>
|
|
4
|
-
<%= render "ruby_llm/agents/shared/doc_link" %>
|
|
5
|
-
</div>
|
|
6
|
-
<p class="text-gray-500 dark:text-gray-400 mt-1">Orchestrated multi-agent workflows with execution statistics</p>
|
|
7
|
-
</div>
|
|
8
|
-
|
|
9
|
-
<% if @workflows.empty? %>
|
|
10
|
-
<%= render "ruby_llm/agents/workflows/empty_state" %>
|
|
11
|
-
<% else %>
|
|
12
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg shadow overflow-hidden">
|
|
13
|
-
<div class="overflow-x-auto">
|
|
14
|
-
<table class="w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
15
|
-
<thead class="bg-gray-50 dark:bg-gray-900">
|
|
16
|
-
<tr>
|
|
17
|
-
<%= render "ruby_llm/agents/agents/sortable_header",
|
|
18
|
-
column: "name", label: "Name",
|
|
19
|
-
current_sort: @sort_params[:column], current_direction: @sort_params[:direction] %>
|
|
20
|
-
|
|
21
|
-
<th scope="col" class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400">
|
|
22
|
-
Status
|
|
23
|
-
</th>
|
|
24
|
-
|
|
25
|
-
<th scope="col" class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400 hidden md:table-cell">
|
|
26
|
-
Steps
|
|
27
|
-
</th>
|
|
28
|
-
|
|
29
|
-
<%= render "ruby_llm/agents/agents/sortable_header",
|
|
30
|
-
column: "execution_count", label: "Executions",
|
|
31
|
-
current_sort: @sort_params[:column], current_direction: @sort_params[:direction] %>
|
|
32
|
-
|
|
33
|
-
<%= render "ruby_llm/agents/agents/sortable_header",
|
|
34
|
-
column: "total_cost", label: "Cost",
|
|
35
|
-
current_sort: @sort_params[:column], current_direction: @sort_params[:direction],
|
|
36
|
-
th_class: "hidden md:table-cell" %>
|
|
37
|
-
|
|
38
|
-
<%= render "ruby_llm/agents/agents/sortable_header",
|
|
39
|
-
column: "success_rate", label: "Success",
|
|
40
|
-
current_sort: @sort_params[:column], current_direction: @sort_params[:direction] %>
|
|
41
|
-
|
|
42
|
-
<%= render "ruby_llm/agents/agents/sortable_header",
|
|
43
|
-
column: "last_executed", label: "Last Run",
|
|
44
|
-
current_sort: @sort_params[:column], current_direction: @sort_params[:direction] %>
|
|
45
|
-
</tr>
|
|
46
|
-
</thead>
|
|
47
|
-
|
|
48
|
-
<% @workflows.each_with_index do |workflow, index| %>
|
|
49
|
-
<%
|
|
50
|
-
row_bg = index.even? ? '' : 'bg-gray-50/50 dark:bg-gray-900/30'
|
|
51
|
-
success_rate = workflow[:success_rate] || 0
|
|
52
|
-
steps_count = workflow[:workflow_children]&.size || 0
|
|
53
|
-
has_steps = workflow[:workflow_children].present? && workflow[:workflow_children].any?
|
|
54
|
-
%>
|
|
55
|
-
<tbody x-data="{ expanded: false }" class="<%= index > 0 ? 'border-t border-gray-100/50 dark:border-gray-700/50' : '' %>">
|
|
56
|
-
<tr class="<%= row_bg %> hover:bg-emerald-50/50 dark:hover:bg-emerald-900/20 transition-colors">
|
|
57
|
-
<!-- Name -->
|
|
58
|
-
<td class="px-4 py-3">
|
|
59
|
-
<a href="<%= ruby_llm_agents.workflow_path(ERB::Util.url_encode(workflow[:name])) %>" class="block">
|
|
60
|
-
<div class="flex items-center gap-2">
|
|
61
|
-
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
62
|
-
<% name_parts = workflow[:name].split('::') %>
|
|
63
|
-
<% if name_parts.length > 1 %>
|
|
64
|
-
<span class="text-gray-400 dark:text-gray-500 font-normal"><%= name_parts[0..-2].join('::') %>::</span>
|
|
65
|
-
<% end %>
|
|
66
|
-
<%= name_parts.last %>
|
|
67
|
-
</span>
|
|
68
|
-
<span class="hidden lg:inline text-xs text-gray-400 dark:text-gray-500">v<%= workflow[:version] %></span>
|
|
69
|
-
</div>
|
|
70
|
-
<% if workflow[:description].present? %>
|
|
71
|
-
<p class="text-xs text-gray-500 dark:text-gray-400 truncate max-w-64 mt-0.5" title="<%= workflow[:description] %>">
|
|
72
|
-
<%= workflow[:description] %>
|
|
73
|
-
</p>
|
|
74
|
-
<% end %>
|
|
75
|
-
</a>
|
|
76
|
-
</td>
|
|
77
|
-
|
|
78
|
-
<!-- Status -->
|
|
79
|
-
<td class="px-4 py-3 whitespace-nowrap">
|
|
80
|
-
<% if workflow[:active] %>
|
|
81
|
-
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 dark:bg-green-900/50 text-green-800 dark:text-green-300">
|
|
82
|
-
Active
|
|
83
|
-
</span>
|
|
84
|
-
<% else %>
|
|
85
|
-
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400">
|
|
86
|
-
Deleted
|
|
87
|
-
</span>
|
|
88
|
-
<% end %>
|
|
89
|
-
</td>
|
|
90
|
-
|
|
91
|
-
<!-- Steps -->
|
|
92
|
-
<td class="px-4 py-3 whitespace-nowrap hidden md:table-cell">
|
|
93
|
-
<% if steps_count > 0 %>
|
|
94
|
-
<button
|
|
95
|
-
type="button"
|
|
96
|
-
@click.stop="expanded = !expanded"
|
|
97
|
-
class="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs font-medium bg-emerald-100 dark:bg-emerald-900/50 text-emerald-700 dark:text-emerald-300 hover:bg-emerald-200 dark:hover:bg-emerald-800 transition-colors"
|
|
98
|
-
>
|
|
99
|
-
<%= steps_count %> steps
|
|
100
|
-
<svg class="w-3 h-3 transition-transform duration-200" :class="{ 'rotate-180': expanded }" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
101
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
|
102
|
-
</svg>
|
|
103
|
-
</button>
|
|
104
|
-
<% else %>
|
|
105
|
-
<span class="text-sm text-gray-400 dark:text-gray-500">-</span>
|
|
106
|
-
<% end %>
|
|
107
|
-
</td>
|
|
108
|
-
|
|
109
|
-
<!-- Executions -->
|
|
110
|
-
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700 dark:text-gray-300 font-mono">
|
|
111
|
-
<%= number_with_delimiter(workflow[:execution_count] || 0) %>
|
|
112
|
-
</td>
|
|
113
|
-
|
|
114
|
-
<!-- Cost -->
|
|
115
|
-
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700 dark:text-gray-300 font-mono hidden md:table-cell">
|
|
116
|
-
$<%= number_with_precision(workflow[:total_cost] || 0, precision: 4) %>
|
|
117
|
-
</td>
|
|
118
|
-
|
|
119
|
-
<!-- Success Rate -->
|
|
120
|
-
<td class="px-4 py-3 whitespace-nowrap">
|
|
121
|
-
<div class="flex items-center gap-1.5">
|
|
122
|
-
<% if success_rate >= 95 %>
|
|
123
|
-
<span class="w-2 h-2 rounded-full bg-green-500"></span>
|
|
124
|
-
<span class="text-sm text-green-600 dark:text-green-400"><%= success_rate %>%</span>
|
|
125
|
-
<% elsif success_rate >= 80 %>
|
|
126
|
-
<span class="w-2 h-2 rounded-full bg-yellow-500"></span>
|
|
127
|
-
<span class="text-sm text-yellow-600 dark:text-yellow-400"><%= success_rate %>%</span>
|
|
128
|
-
<% else %>
|
|
129
|
-
<span class="w-2 h-2 rounded-full bg-red-500"></span>
|
|
130
|
-
<span class="text-sm text-red-600 dark:text-red-400"><%= success_rate %>%</span>
|
|
131
|
-
<% end %>
|
|
132
|
-
</div>
|
|
133
|
-
</td>
|
|
134
|
-
|
|
135
|
-
<!-- Last Executed -->
|
|
136
|
-
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
|
137
|
-
<% if workflow[:last_executed] %>
|
|
138
|
-
<%= time_ago_in_words(workflow[:last_executed]) %> ago
|
|
139
|
-
<% else %>
|
|
140
|
-
<span class="text-gray-400 dark:text-gray-500">Never</span>
|
|
141
|
-
<% end %>
|
|
142
|
-
</td>
|
|
143
|
-
</tr>
|
|
144
|
-
|
|
145
|
-
<!-- Expandable Steps Row -->
|
|
146
|
-
<% if has_steps %>
|
|
147
|
-
<tr
|
|
148
|
-
x-show="expanded"
|
|
149
|
-
x-cloak
|
|
150
|
-
x-transition:enter="transition ease-out duration-200"
|
|
151
|
-
x-transition:enter-start="opacity-0"
|
|
152
|
-
x-transition:enter-end="opacity-100"
|
|
153
|
-
class="bg-gray-50/80 dark:bg-gray-900/50"
|
|
154
|
-
>
|
|
155
|
-
<td colspan="7" class="px-4 py-3">
|
|
156
|
-
<div class="flex flex-wrap gap-2">
|
|
157
|
-
<% workflow[:workflow_children].each_with_index do |child, step_index| %>
|
|
158
|
-
<div class="inline-flex items-center gap-1.5 px-2 py-1 rounded bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 text-xs">
|
|
159
|
-
<span class="w-4 h-4 flex items-center justify-center rounded-full bg-emerald-100 dark:bg-emerald-900/50 text-emerald-600 dark:text-emerald-300 text-[10px] font-medium">
|
|
160
|
-
<%= step_index + 1 %>
|
|
161
|
-
</span>
|
|
162
|
-
<span class="font-medium text-gray-700 dark:text-gray-300"><%= child[:name] %></span>
|
|
163
|
-
<span class="text-gray-400 dark:text-gray-500">-></span>
|
|
164
|
-
<span class="text-gray-500 dark:text-gray-400 font-mono"><%= child[:agent] %></span>
|
|
165
|
-
<% if child[:optional] %>
|
|
166
|
-
<span class="text-gray-400 dark:text-gray-500 italic">(optional)</span>
|
|
167
|
-
<% end %>
|
|
168
|
-
</div>
|
|
169
|
-
<% end %>
|
|
170
|
-
</div>
|
|
171
|
-
</td>
|
|
172
|
-
</tr>
|
|
173
|
-
<% end %>
|
|
174
|
-
</tbody>
|
|
175
|
-
<% end %>
|
|
176
|
-
</table>
|
|
177
|
-
</div>
|
|
178
|
-
</div>
|
|
179
|
-
<% end %>
|
|
@@ -1,467 +0,0 @@
|
|
|
1
|
-
<%= render "ruby_llm/agents/shared/breadcrumbs", items: [
|
|
2
|
-
{ label: "Dashboard", path: ruby_llm_agents.root_path },
|
|
3
|
-
{ label: "Workflows", path: ruby_llm_agents.agents_path(tab: "workflows") },
|
|
4
|
-
{ label: @workflow_type.split('::').last }
|
|
5
|
-
] %>
|
|
6
|
-
|
|
7
|
-
<!-- Header -->
|
|
8
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 mb-6">
|
|
9
|
-
<div class="flex items-start justify-between">
|
|
10
|
-
<div>
|
|
11
|
-
<div class="flex items-center space-x-3">
|
|
12
|
-
<%= render "ruby_llm/agents/shared/workflow_type_badge", workflow_type: @workflow_type_kind, size: :md %>
|
|
13
|
-
<h1 class="text-2xl font-bold text-gray-900 dark:text-gray-100">
|
|
14
|
-
<% name_parts = @workflow_type.split('::') %>
|
|
15
|
-
<% if name_parts.length > 1 %>
|
|
16
|
-
<span class="text-gray-400 dark:text-gray-500 font-normal"><%= name_parts[0..-2].join('::') %>::</span>
|
|
17
|
-
<% end %>
|
|
18
|
-
<%= name_parts.last %>
|
|
19
|
-
</h1>
|
|
20
|
-
<%= render "ruby_llm/agents/shared/doc_link" %>
|
|
21
|
-
|
|
22
|
-
<% if @workflow_active %>
|
|
23
|
-
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 dark:bg-green-900/50 text-green-800 dark:text-green-300">
|
|
24
|
-
Active
|
|
25
|
-
</span>
|
|
26
|
-
<% else %>
|
|
27
|
-
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400">
|
|
28
|
-
Deleted
|
|
29
|
-
</span>
|
|
30
|
-
<% end %>
|
|
31
|
-
|
|
32
|
-
<% if @config %>
|
|
33
|
-
<span class="text-sm text-gray-500 dark:text-gray-400">
|
|
34
|
-
v<%= @config[:version] %>
|
|
35
|
-
</span>
|
|
36
|
-
<% end %>
|
|
37
|
-
</div>
|
|
38
|
-
|
|
39
|
-
<% if @config && @config[:description].present? %>
|
|
40
|
-
<p class="text-gray-600 dark:text-gray-300 mt-2 max-w-2xl">
|
|
41
|
-
<%= @config[:description] %>
|
|
42
|
-
</p>
|
|
43
|
-
<% end %>
|
|
44
|
-
</div>
|
|
45
|
-
|
|
46
|
-
<div class="text-right">
|
|
47
|
-
<p class="text-sm text-gray-500 dark:text-gray-400">
|
|
48
|
-
<%= number_with_delimiter(@stats[:count]) %> total executions
|
|
49
|
-
</p>
|
|
50
|
-
|
|
51
|
-
<div class="flex items-center justify-end gap-3 mt-1">
|
|
52
|
-
<% status_colors = {
|
|
53
|
-
"success" => "bg-green-500",
|
|
54
|
-
"error" => "bg-red-500",
|
|
55
|
-
"timeout" => "bg-yellow-500",
|
|
56
|
-
"running" => "bg-blue-500"
|
|
57
|
-
} %>
|
|
58
|
-
|
|
59
|
-
<% @status_distribution.each do |status, count| %>
|
|
60
|
-
<div class="flex items-center gap-1">
|
|
61
|
-
<span class="w-2 h-2 rounded-full <%= status_colors[status] || 'bg-gray-400' %> <%= status == 'running' ? 'animate-pulse' : '' %>"></span>
|
|
62
|
-
<span class="text-xs text-gray-600 dark:text-gray-400">
|
|
63
|
-
<%= number_with_delimiter(count) %>
|
|
64
|
-
</span>
|
|
65
|
-
</div>
|
|
66
|
-
<% end %>
|
|
67
|
-
</div>
|
|
68
|
-
</div>
|
|
69
|
-
</div>
|
|
70
|
-
</div>
|
|
71
|
-
|
|
72
|
-
<!-- Workflow Details Panel -->
|
|
73
|
-
<% if @config %>
|
|
74
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4 mb-6">
|
|
75
|
-
<div class="flex flex-wrap items-center gap-x-6 gap-y-2">
|
|
76
|
-
<!-- Structure Summary -->
|
|
77
|
-
<div class="flex items-center gap-4 text-sm">
|
|
78
|
-
<% if @steps.present? %>
|
|
79
|
-
<span class="text-gray-700 dark:text-gray-300">
|
|
80
|
-
<span class="font-medium"><%= @steps.size %></span>
|
|
81
|
-
<span class="text-gray-500 dark:text-gray-400">steps</span>
|
|
82
|
-
</span>
|
|
83
|
-
<% end %>
|
|
84
|
-
<% if @parallel_groups.present? && @parallel_groups.any? %>
|
|
85
|
-
<span class="text-purple-600 dark:text-purple-400">
|
|
86
|
-
<span class="font-medium"><%= @parallel_groups.size %></span> parallel
|
|
87
|
-
</span>
|
|
88
|
-
<% end %>
|
|
89
|
-
<% if @config[:has_routing] %>
|
|
90
|
-
<span class="text-amber-600 dark:text-amber-400">routing</span>
|
|
91
|
-
<% end %>
|
|
92
|
-
</div>
|
|
93
|
-
|
|
94
|
-
<!-- Divider -->
|
|
95
|
-
<span class="hidden sm:block w-px h-5 bg-gray-200 dark:bg-gray-700"></span>
|
|
96
|
-
|
|
97
|
-
<!-- Constraints -->
|
|
98
|
-
<div class="flex items-center gap-4 text-sm text-gray-600 dark:text-gray-400">
|
|
99
|
-
<% if @config[:timeout] %>
|
|
100
|
-
<span class="inline-flex items-center gap-1">
|
|
101
|
-
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
|
102
|
-
<%= @config[:timeout] %>s timeout
|
|
103
|
-
</span>
|
|
104
|
-
<% end %>
|
|
105
|
-
<% if @config[:max_cost] %>
|
|
106
|
-
<span class="inline-flex items-center gap-1">
|
|
107
|
-
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1"/></svg>
|
|
108
|
-
$<%= @config[:max_cost] %> max
|
|
109
|
-
</span>
|
|
110
|
-
<% end %>
|
|
111
|
-
</div>
|
|
112
|
-
|
|
113
|
-
<!-- Divider -->
|
|
114
|
-
<% if @config[:has_lifecycle_hooks] || @config[:has_conditions] || @config[:has_retries] || @config[:has_fallbacks] || @config[:has_input_schema] %>
|
|
115
|
-
<span class="hidden sm:block w-px h-5 bg-gray-200 dark:bg-gray-700"></span>
|
|
116
|
-
<% end %>
|
|
117
|
-
|
|
118
|
-
<!-- Features -->
|
|
119
|
-
<div class="flex flex-wrap items-center gap-2">
|
|
120
|
-
<% if @config[:has_input_schema] %>
|
|
121
|
-
<span class="text-xs px-2 py-1 rounded-full bg-green-100 dark:bg-green-900/50 text-green-700 dark:text-green-300">
|
|
122
|
-
Input Schema
|
|
123
|
-
</span>
|
|
124
|
-
<% end %>
|
|
125
|
-
<% if @config[:has_lifecycle_hooks] %>
|
|
126
|
-
<span class="text-xs px-2 py-1 rounded-full bg-emerald-100 dark:bg-emerald-900/50 text-emerald-700 dark:text-emerald-300">
|
|
127
|
-
Lifecycle Hooks
|
|
128
|
-
</span>
|
|
129
|
-
<% end %>
|
|
130
|
-
<% if @config[:has_conditions] %>
|
|
131
|
-
<span class="text-xs px-2 py-1 rounded-full bg-cyan-100 dark:bg-cyan-900/50 text-cyan-700 dark:text-cyan-300">
|
|
132
|
-
Conditional
|
|
133
|
-
</span>
|
|
134
|
-
<% end %>
|
|
135
|
-
<% if @config[:has_retries] %>
|
|
136
|
-
<span class="text-xs px-2 py-1 rounded-full bg-orange-100 dark:bg-orange-900/50 text-orange-700 dark:text-orange-300">
|
|
137
|
-
Retries
|
|
138
|
-
</span>
|
|
139
|
-
<% end %>
|
|
140
|
-
<% if @config[:has_fallbacks] %>
|
|
141
|
-
<span class="text-xs px-2 py-1 rounded-full bg-yellow-100 dark:bg-yellow-900/50 text-yellow-700 dark:text-yellow-300">
|
|
142
|
-
Fallbacks
|
|
143
|
-
</span>
|
|
144
|
-
<% end %>
|
|
145
|
-
</div>
|
|
146
|
-
</div>
|
|
147
|
-
</div>
|
|
148
|
-
<% end %>
|
|
149
|
-
|
|
150
|
-
<!-- Workflow Structure -->
|
|
151
|
-
<div id="structure" class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 mb-6 scroll-mt-16" x-data="{ expanded: false }">
|
|
152
|
-
<div class="flex items-center justify-between mb-4">
|
|
153
|
-
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100">
|
|
154
|
-
Workflow Structure
|
|
155
|
-
</h3>
|
|
156
|
-
|
|
157
|
-
<button @click="expanded = !expanded" class="inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors cursor-pointer">
|
|
158
|
-
<span x-text="expanded ? 'Collapse' : 'Expand'">Collapse</span>
|
|
159
|
-
<svg class="w-4 h-4 transition-transform duration-200" :class="expanded ? 'rotate-180' : ''" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
160
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
|
161
|
-
</svg>
|
|
162
|
-
</button>
|
|
163
|
-
</div>
|
|
164
|
-
|
|
165
|
-
<!-- Collapsed: Compact Flow Summary -->
|
|
166
|
-
<div x-show="!expanded" x-collapse class="py-4">
|
|
167
|
-
<div class="flex items-center justify-center gap-2 flex-wrap">
|
|
168
|
-
<!-- Start -->
|
|
169
|
-
<span class="px-3 py-1 rounded-full bg-green-100 dark:bg-green-900/50 text-green-700 dark:text-green-300 text-sm font-medium">
|
|
170
|
-
Start
|
|
171
|
-
</span>
|
|
172
|
-
|
|
173
|
-
<% (@steps || []).each_with_index do |step, idx| %>
|
|
174
|
-
<svg class="w-4 h-4 text-gray-400 dark:text-gray-500 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
175
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
|
176
|
-
</svg>
|
|
177
|
-
|
|
178
|
-
<%
|
|
179
|
-
step_bg = if step[:workflow]
|
|
180
|
-
"bg-emerald-100 dark:bg-emerald-900/50 text-emerald-700 dark:text-emerald-300"
|
|
181
|
-
elsif step[:iteration]
|
|
182
|
-
"bg-blue-100 dark:bg-blue-900/50 text-blue-700 dark:text-blue-300"
|
|
183
|
-
elsif step[:routing]
|
|
184
|
-
"bg-amber-100 dark:bg-amber-900/50 text-amber-700 dark:text-amber-300"
|
|
185
|
-
elsif step[:parallel_group]
|
|
186
|
-
"bg-purple-100 dark:bg-purple-900/50 text-purple-700 dark:text-purple-300"
|
|
187
|
-
else
|
|
188
|
-
"bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300"
|
|
189
|
-
end
|
|
190
|
-
%>
|
|
191
|
-
<span class="px-3 py-1 rounded-full <%= step_bg %> text-sm font-medium">
|
|
192
|
-
<%= step[:name].to_s.titleize.truncate(20) %>
|
|
193
|
-
</span>
|
|
194
|
-
<% end %>
|
|
195
|
-
|
|
196
|
-
<svg class="w-4 h-4 text-gray-400 dark:text-gray-500 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
197
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
|
198
|
-
</svg>
|
|
199
|
-
|
|
200
|
-
<!-- End -->
|
|
201
|
-
<span class="px-3 py-1 rounded-full bg-red-100 dark:bg-red-900/50 text-red-700 dark:text-red-300 text-sm font-medium">
|
|
202
|
-
End
|
|
203
|
-
</span>
|
|
204
|
-
</div>
|
|
205
|
-
|
|
206
|
-
<p class="text-center text-xs text-gray-500 dark:text-gray-400 mt-3">
|
|
207
|
-
Click "Expand" to see full diagram with details
|
|
208
|
-
</p>
|
|
209
|
-
</div>
|
|
210
|
-
|
|
211
|
-
<!-- Expanded: Full Diagram -->
|
|
212
|
-
<div x-show="expanded" x-collapse>
|
|
213
|
-
<%= render "ruby_llm/agents/workflows/workflow_diagram",
|
|
214
|
-
steps: @steps || [],
|
|
215
|
-
parallel_groups: @parallel_groups || [],
|
|
216
|
-
input_schema_fields: @input_schema_fields || {},
|
|
217
|
-
lifecycle_hooks: @lifecycle_hooks || {} %>
|
|
218
|
-
</div>
|
|
219
|
-
</div>
|
|
220
|
-
|
|
221
|
-
<!-- Stats Grid -->
|
|
222
|
-
<% success_rate = @stats[:success_rate] || 0 %>
|
|
223
|
-
<% success_rate_color = success_rate >= 95 ? 'text-green-600' : success_rate >= 80 ? 'text-yellow-600' : 'text-red-600' %>
|
|
224
|
-
|
|
225
|
-
<div id="stats" class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4 mb-6 scroll-mt-16">
|
|
226
|
-
<%= render "ruby_llm/agents/shared/stat_card",
|
|
227
|
-
title: "Executions",
|
|
228
|
-
value: number_with_delimiter(@stats[:count]),
|
|
229
|
-
subtitle: "Today: #{@stats_today[:count]}",
|
|
230
|
-
icon: "M13 10V3L4 14h7v7l9-11h-7z",
|
|
231
|
-
icon_color: "text-blue-500" %>
|
|
232
|
-
|
|
233
|
-
<%= render "ruby_llm/agents/shared/stat_card",
|
|
234
|
-
title: "Success Rate",
|
|
235
|
-
value: "#{success_rate}%",
|
|
236
|
-
subtitle: "Error rate: #{@stats[:error_rate] || 0}%",
|
|
237
|
-
icon: "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z",
|
|
238
|
-
icon_color: "text-green-500",
|
|
239
|
-
value_color: success_rate_color %>
|
|
240
|
-
|
|
241
|
-
<%= render "ruby_llm/agents/shared/stat_card",
|
|
242
|
-
title: "Total Cost",
|
|
243
|
-
value: "$#{number_with_precision(@stats[:total_cost] || 0, precision: 4)}",
|
|
244
|
-
subtitle: "Avg: $#{number_with_precision(@stats[:avg_cost] || 0, precision: 6)}",
|
|
245
|
-
icon: "M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z",
|
|
246
|
-
icon_color: "text-amber-500" %>
|
|
247
|
-
|
|
248
|
-
<%= render "ruby_llm/agents/shared/stat_card",
|
|
249
|
-
title: "Total Tokens",
|
|
250
|
-
value: number_with_delimiter(@stats[:total_tokens] || 0),
|
|
251
|
-
subtitle: "Avg: #{number_with_delimiter(@stats[:avg_tokens] || 0)}",
|
|
252
|
-
icon: "M7 20l4-16m2 16l4-16M6 9h14M4 15h14",
|
|
253
|
-
icon_color: "text-indigo-500" %>
|
|
254
|
-
|
|
255
|
-
<%= render "ruby_llm/agents/shared/stat_card",
|
|
256
|
-
title: "Avg Duration",
|
|
257
|
-
value: "#{number_with_delimiter(@stats[:avg_duration_ms] || 0)} ms",
|
|
258
|
-
icon: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z",
|
|
259
|
-
icon_color: "text-purple-500" %>
|
|
260
|
-
</div>
|
|
261
|
-
|
|
262
|
-
<!-- Step Performance -->
|
|
263
|
-
<% if @step_stats.present? %>
|
|
264
|
-
<div id="performance" class="scroll-mt-16">
|
|
265
|
-
<%= render "ruby_llm/agents/workflows/step_performance",
|
|
266
|
-
workflow_type: @workflow_type_kind,
|
|
267
|
-
step_stats: @step_stats %>
|
|
268
|
-
</div>
|
|
269
|
-
<% end %>
|
|
270
|
-
|
|
271
|
-
<!-- Charts Section -->
|
|
272
|
-
<div id="trends" class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 mb-6 scroll-mt-16">
|
|
273
|
-
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
|
|
274
|
-
Trends (30 days)
|
|
275
|
-
</h3>
|
|
276
|
-
|
|
277
|
-
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
278
|
-
<!-- Executions Over Time -->
|
|
279
|
-
<div>
|
|
280
|
-
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">Executions</h4>
|
|
281
|
-
<div id="executions-chart" style="height: 200px;"></div>
|
|
282
|
-
</div>
|
|
283
|
-
|
|
284
|
-
<!-- Cost Over Time -->
|
|
285
|
-
<div>
|
|
286
|
-
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">Cost</h4>
|
|
287
|
-
<div id="cost-chart" style="height: 200px;"></div>
|
|
288
|
-
</div>
|
|
289
|
-
</div>
|
|
290
|
-
</div>
|
|
291
|
-
|
|
292
|
-
<script>
|
|
293
|
-
(function() {
|
|
294
|
-
const trendData = <%= raw @trend_data.to_json %>;
|
|
295
|
-
const categories = trendData.map(d => d.date.split('-').slice(1).join('/'));
|
|
296
|
-
|
|
297
|
-
// Executions chart
|
|
298
|
-
Highcharts.chart('executions-chart', {
|
|
299
|
-
chart: { type: 'areaspline', backgroundColor: 'transparent' },
|
|
300
|
-
xAxis: { categories: categories, labels: { style: { color: '#9CA3AF' } } },
|
|
301
|
-
yAxis: { min: 0, title: { text: null }, labels: { style: { color: '#9CA3AF' } } },
|
|
302
|
-
legend: { enabled: false },
|
|
303
|
-
plotOptions: { areaspline: { fillOpacity: 0.2, marker: { enabled: false } } },
|
|
304
|
-
series: [
|
|
305
|
-
{ name: 'Success', color: '#10B981', data: trendData.map(d => d.count - (d.error_count || 0)) },
|
|
306
|
-
{ name: 'Failed', color: '#EF4444', data: trendData.map(d => d.error_count || 0) }
|
|
307
|
-
]
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
// Cost chart
|
|
311
|
-
Highcharts.chart('cost-chart', {
|
|
312
|
-
chart: { type: 'spline', backgroundColor: 'transparent' },
|
|
313
|
-
xAxis: { categories: categories, labels: { style: { color: '#9CA3AF' } } },
|
|
314
|
-
yAxis: { min: 0, title: { text: null }, labels: { style: { color: '#9CA3AF' }, format: '${value}' } },
|
|
315
|
-
legend: { enabled: false },
|
|
316
|
-
plotOptions: { spline: { marker: { enabled: false } } },
|
|
317
|
-
tooltip: { valuePrefix: '$' },
|
|
318
|
-
series: [{ name: 'Cost', color: '#10B981', data: trendData.map(d => parseFloat(d.total_cost) || 0) }]
|
|
319
|
-
});
|
|
320
|
-
})();
|
|
321
|
-
</script>
|
|
322
|
-
|
|
323
|
-
<!-- Finish Reason Distribution -->
|
|
324
|
-
<% if @finish_reason_distribution.present? && @finish_reason_distribution.any? %>
|
|
325
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4 mb-6">
|
|
326
|
-
<div class="flex items-center justify-between">
|
|
327
|
-
<p class="text-sm text-gray-500 dark:text-gray-400 uppercase">
|
|
328
|
-
Finish Reasons
|
|
329
|
-
</p>
|
|
330
|
-
|
|
331
|
-
<div class="flex flex-wrap gap-4">
|
|
332
|
-
<% finish_colors = {
|
|
333
|
-
'stop' => '#10B981',
|
|
334
|
-
'length' => '#F59E0B',
|
|
335
|
-
'content_filter' => '#EF4444',
|
|
336
|
-
'tool_calls' => '#3B82F6',
|
|
337
|
-
nil => '#6B7280'
|
|
338
|
-
} %>
|
|
339
|
-
|
|
340
|
-
<% @finish_reason_distribution.each do |reason, count| %>
|
|
341
|
-
<div class="flex items-center">
|
|
342
|
-
<span class="w-2 h-2 rounded-full mr-1.5" style="background-color: <%= finish_colors[reason] || '#6B7280' %>"></span>
|
|
343
|
-
<span class="text-sm text-gray-700 dark:text-gray-300">
|
|
344
|
-
<%= reason || 'unknown' %>
|
|
345
|
-
</span>
|
|
346
|
-
<span class="text-sm font-medium text-gray-900 dark:text-gray-100 ml-1">
|
|
347
|
-
(<%= number_with_delimiter(count) %>)
|
|
348
|
-
</span>
|
|
349
|
-
</div>
|
|
350
|
-
<% end %>
|
|
351
|
-
</div>
|
|
352
|
-
</div>
|
|
353
|
-
</div>
|
|
354
|
-
<% end %>
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
<!-- Executions -->
|
|
358
|
-
<div id="executions" class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 scroll-mt-16">
|
|
359
|
-
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
|
|
360
|
-
Executions
|
|
361
|
-
</h3>
|
|
362
|
-
|
|
363
|
-
<div id="executions_table">
|
|
364
|
-
<%
|
|
365
|
-
has_filters = params[:statuses].present? || params[:versions].present? || params[:models].present? || params[:temperatures].present? || params[:days].present?
|
|
366
|
-
selected_statuses = params[:statuses].present? ? (params[:statuses].is_a?(Array) ? params[:statuses] : params[:statuses].split(",")) : []
|
|
367
|
-
selected_versions = params[:versions].present? ? (params[:versions].is_a?(Array) ? params[:versions] : params[:versions].split(",")) : []
|
|
368
|
-
selected_models = params[:models].present? ? (params[:models].is_a?(Array) ? params[:models] : params[:models].split(",")) : []
|
|
369
|
-
selected_temperatures = params[:temperatures].present? ? (params[:temperatures].is_a?(Array) ? params[:temperatures] : params[:temperatures].split(",")).map(&:to_s) : []
|
|
370
|
-
|
|
371
|
-
status_options = [
|
|
372
|
-
{ value: "success", label: "Success", color: "bg-green-500" },
|
|
373
|
-
{ value: "error", label: "Error", color: "bg-red-500" },
|
|
374
|
-
{ value: "running", label: "Running", color: "bg-blue-500" },
|
|
375
|
-
{ value: "timeout", label: "Timeout", color: "bg-yellow-500" }
|
|
376
|
-
]
|
|
377
|
-
version_options = @versions.map { |v| { value: v.to_s, label: "v#{v}" } }
|
|
378
|
-
model_options = @models.map { |m| { value: m, label: m } }
|
|
379
|
-
temperature_options = @temperatures.map { |t| { value: t.to_s, label: t.to_s } }
|
|
380
|
-
days_options = [
|
|
381
|
-
{ value: "", label: "All Time" },
|
|
382
|
-
{ value: "1", label: "Today" },
|
|
383
|
-
{ value: "7", label: "Last 7 Days" },
|
|
384
|
-
{ value: "30", label: "Last 30 Days" }
|
|
385
|
-
]
|
|
386
|
-
%>
|
|
387
|
-
|
|
388
|
-
<%= form_with url: ruby_llm_agents.workflow_path(@workflow_type), method: :get, local: true do |f| %>
|
|
389
|
-
<div class="flex flex-wrap items-center gap-3 mb-4 pb-4 border-b border-gray-100 dark:border-gray-700">
|
|
390
|
-
<%# Status Filter (Multi-select) %>
|
|
391
|
-
<%= render "ruby_llm/agents/shared/filter_dropdown",
|
|
392
|
-
name: "statuses[]",
|
|
393
|
-
filter_id: "statuses",
|
|
394
|
-
label: "Status",
|
|
395
|
-
all_label: "All Statuses",
|
|
396
|
-
options: status_options,
|
|
397
|
-
selected: selected_statuses %>
|
|
398
|
-
|
|
399
|
-
<%# Version Filter (Multi-select) %>
|
|
400
|
-
<% if @versions.any? %>
|
|
401
|
-
<%= render "ruby_llm/agents/shared/filter_dropdown",
|
|
402
|
-
name: "versions[]",
|
|
403
|
-
filter_id: "versions",
|
|
404
|
-
label: "Version",
|
|
405
|
-
all_label: "All Versions",
|
|
406
|
-
options: version_options,
|
|
407
|
-
selected: selected_versions,
|
|
408
|
-
icon: "M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" %>
|
|
409
|
-
<% end %>
|
|
410
|
-
|
|
411
|
-
<%# Model Filter (Multi-select) %>
|
|
412
|
-
<% if @models.length > 1 %>
|
|
413
|
-
<%= render "ruby_llm/agents/shared/filter_dropdown",
|
|
414
|
-
name: "models[]",
|
|
415
|
-
filter_id: "models",
|
|
416
|
-
label: "Model",
|
|
417
|
-
all_label: "All Models",
|
|
418
|
-
options: model_options,
|
|
419
|
-
selected: selected_models,
|
|
420
|
-
icon: "M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" %>
|
|
421
|
-
<% end %>
|
|
422
|
-
|
|
423
|
-
<%# Temperature Filter (Multi-select) %>
|
|
424
|
-
<% if @temperatures.length > 1 %>
|
|
425
|
-
<%= render "ruby_llm/agents/shared/filter_dropdown",
|
|
426
|
-
name: "temperatures[]",
|
|
427
|
-
filter_id: "temperatures",
|
|
428
|
-
label: "Temp",
|
|
429
|
-
all_label: "All Temps",
|
|
430
|
-
options: temperature_options,
|
|
431
|
-
selected: selected_temperatures,
|
|
432
|
-
icon: "M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" %>
|
|
433
|
-
<% end %>
|
|
434
|
-
|
|
435
|
-
<%# Time Range Filter (Single-select) %>
|
|
436
|
-
<%= render "ruby_llm/agents/shared/select_dropdown",
|
|
437
|
-
name: "days",
|
|
438
|
-
filter_id: "days",
|
|
439
|
-
options: days_options,
|
|
440
|
-
selected: params[:days].to_s,
|
|
441
|
-
icon: "M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" %>
|
|
442
|
-
|
|
443
|
-
<%# Clear Filters %>
|
|
444
|
-
<% if has_filters %>
|
|
445
|
-
<%= link_to ruby_llm_agents.workflow_path(@workflow_type),
|
|
446
|
-
class: "flex items-center gap-1 px-3 py-2 text-sm text-red-500 dark:text-red-400 hover:text-red-600 dark:hover:text-red-300 hover:bg-red-50 dark:hover:bg-red-900/30 rounded-lg transition-colors" do %>
|
|
447
|
-
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
448
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
|
449
|
-
</svg>
|
|
450
|
-
Clear
|
|
451
|
-
<% end %>
|
|
452
|
-
<% end %>
|
|
453
|
-
|
|
454
|
-
<%# Stats Summary (right aligned) %>
|
|
455
|
-
<div class="ml-auto flex items-center gap-4 text-sm text-gray-500 dark:text-gray-400">
|
|
456
|
-
<span><%= number_with_delimiter(@filter_stats[:total_count]) %> executions</span>
|
|
457
|
-
<span class="text-gray-300 dark:text-gray-600">|</span>
|
|
458
|
-
<span>$<%= number_with_precision(@filter_stats[:total_cost] || 0, precision: 4) %></span>
|
|
459
|
-
<span class="text-gray-300 dark:text-gray-600">|</span>
|
|
460
|
-
<span><%= number_with_delimiter(@filter_stats[:total_tokens] || 0) %> tokens</span>
|
|
461
|
-
</div>
|
|
462
|
-
</div>
|
|
463
|
-
<% end %>
|
|
464
|
-
|
|
465
|
-
<%= render "ruby_llm/agents/shared/executions_table", executions: @executions, pagination: @pagination %>
|
|
466
|
-
</div>
|
|
467
|
-
</div>
|