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,35 +0,0 @@
|
|
|
1
|
-
<%
|
|
2
|
-
# Workflow type badge with icon and color coding
|
|
3
|
-
# Usage: render "ruby_llm/agents/shared/workflow_type_badge", workflow_type: "workflow"
|
|
4
|
-
# Options:
|
|
5
|
-
# size: :xs, :sm (default), :md
|
|
6
|
-
# show_label: true (default) or false for icon-only mode
|
|
7
|
-
|
|
8
|
-
workflow_type = local_assigns[:workflow_type]
|
|
9
|
-
size = local_assigns[:size] || :sm
|
|
10
|
-
show_label = local_assigns.fetch(:show_label, true)
|
|
11
|
-
|
|
12
|
-
# All workflows use unified DSL workflow style
|
|
13
|
-
config = {
|
|
14
|
-
icon: "workflow",
|
|
15
|
-
label: "Workflow",
|
|
16
|
-
bg: "bg-emerald-100 dark:bg-emerald-900/50",
|
|
17
|
-
text: "text-emerald-700 dark:text-emerald-300",
|
|
18
|
-
icon_char: "◈"
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
size_classes = case size
|
|
22
|
-
when :xs
|
|
23
|
-
{ badge: "px-1.5 py-0.5", icon: "text-[10px]", text: "text-[10px]" }
|
|
24
|
-
when :md
|
|
25
|
-
{ badge: "px-2.5 py-1", icon: "text-sm", text: "text-sm" }
|
|
26
|
-
else # :sm
|
|
27
|
-
{ badge: "px-2 py-0.5", icon: "text-xs", text: "text-xs" }
|
|
28
|
-
end
|
|
29
|
-
%>
|
|
30
|
-
<span class="inline-flex items-center gap-1 rounded-md font-medium <%= config[:bg] %> <%= config[:text] %> <%= size_classes[:badge] %>">
|
|
31
|
-
<span class="<%= size_classes[:icon] %>" aria-hidden="true"><%= config[:icon_char] %></span>
|
|
32
|
-
<% if show_label %>
|
|
33
|
-
<span class="<%= size_classes[:text] %>"><%= config[:label] %></span>
|
|
34
|
-
<% end %>
|
|
35
|
-
</span>
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
<div class="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-8 text-center">
|
|
2
|
-
<!-- Workflow Icon - Emerald -->
|
|
3
|
-
<svg class="mx-auto h-16 w-16 text-emerald-500 dark:text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
4
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"/>
|
|
5
|
-
</svg>
|
|
6
|
-
|
|
7
|
-
<h3 class="mt-4 text-lg font-medium text-gray-900 dark:text-gray-100">
|
|
8
|
-
No workflows yet
|
|
9
|
-
</h3>
|
|
10
|
-
|
|
11
|
-
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400 max-w-md mx-auto">
|
|
12
|
-
Create a workflow by inheriting from <code class="bg-emerald-100 dark:bg-emerald-900/50 text-emerald-700 dark:text-emerald-300 px-1.5 py-0.5 rounded font-mono text-xs">ApplicationWorkflow</code> and defining steps
|
|
13
|
-
</p>
|
|
14
|
-
|
|
15
|
-
<!-- Code Example -->
|
|
16
|
-
<div class="mt-6 max-w-sm mx-auto">
|
|
17
|
-
<pre class="bg-gray-900 dark:bg-gray-950 rounded-lg p-4 text-left text-sm overflow-x-auto"><code class="text-gray-300"><span class="text-pink-400">class</span> <span class="text-yellow-300">MyWorkflow</span> <span class="text-pink-400"><</span> <span class="text-yellow-300">ApplicationWorkflow</span>
|
|
18
|
-
<span class="text-purple-400">step</span> <span class="text-emerald-400">:analyze</span>, <span class="text-yellow-300">AnalyzerAgent</span>
|
|
19
|
-
<span class="text-purple-400">step</span> <span class="text-emerald-400">:summarize</span>, <span class="text-yellow-300">SummarizerAgent</span>
|
|
20
|
-
<span class="text-pink-400">end</span></code></pre>
|
|
21
|
-
</div>
|
|
22
|
-
</div>
|
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
<%
|
|
2
|
-
# Step Performance Analytics
|
|
3
|
-
# Shows per-step metrics in a table format
|
|
4
|
-
workflow_type = local_assigns[:workflow_type]
|
|
5
|
-
step_stats = local_assigns[:step_stats] || []
|
|
6
|
-
route_distribution = local_assigns[:route_distribution] || {}
|
|
7
|
-
|
|
8
|
-
# Unified labels
|
|
9
|
-
section_title = "Step Performance"
|
|
10
|
-
column_label = "Step"
|
|
11
|
-
|
|
12
|
-
# Calculate max values for bar charts
|
|
13
|
-
max_duration = step_stats.map { |s| s[:avg_duration_ms] }.compact.max || 1
|
|
14
|
-
max_cost = step_stats.map { |s| s[:avg_cost] }.compact.max || 0.001
|
|
15
|
-
|
|
16
|
-
# Check if any steps are parallel
|
|
17
|
-
has_parallel = step_stats.any? { |s| s[:name].to_s.include?("parallel") || workflow_type == "parallel" }
|
|
18
|
-
%>
|
|
19
|
-
|
|
20
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 mb-6">
|
|
21
|
-
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
|
|
22
|
-
<%= section_title %> <span class="text-sm font-normal text-gray-500 dark:text-gray-400">(last 30 days)</span>
|
|
23
|
-
</h3>
|
|
24
|
-
|
|
25
|
-
<% if workflow_type == "router" && route_distribution.present? %>
|
|
26
|
-
<!-- Route Distribution Bar Chart -->
|
|
27
|
-
<div class="mb-6">
|
|
28
|
-
<div class="space-y-3">
|
|
29
|
-
<% route_distribution.each do |route_name, data| %>
|
|
30
|
-
<div class="flex items-center gap-3">
|
|
31
|
-
<span class="text-sm font-medium text-gray-700 dark:text-gray-300 w-24 truncate" title="<%= route_name %>">
|
|
32
|
-
<%= route_name %>
|
|
33
|
-
</span>
|
|
34
|
-
<div class="flex-1 h-6 bg-gray-100 dark:bg-gray-700 rounded-lg overflow-hidden">
|
|
35
|
-
<div class="h-full bg-amber-500 dark:bg-amber-400 rounded-lg transition-all duration-300"
|
|
36
|
-
style="width: <%= data[:percentage] %>%">
|
|
37
|
-
</div>
|
|
38
|
-
</div>
|
|
39
|
-
<span class="text-sm text-gray-600 dark:text-gray-400 w-24 text-right">
|
|
40
|
-
<%= data[:count] %> (<%= data[:percentage] %>%)
|
|
41
|
-
</span>
|
|
42
|
-
</div>
|
|
43
|
-
<% end %>
|
|
44
|
-
</div>
|
|
45
|
-
</div>
|
|
46
|
-
<% end %>
|
|
47
|
-
|
|
48
|
-
<% if step_stats.any? %>
|
|
49
|
-
<!-- Performance Table -->
|
|
50
|
-
<div class="overflow-x-auto">
|
|
51
|
-
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
52
|
-
<thead>
|
|
53
|
-
<tr>
|
|
54
|
-
<th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
55
|
-
<%= column_label %>
|
|
56
|
-
</th>
|
|
57
|
-
<th scope="col" class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
58
|
-
Executions
|
|
59
|
-
</th>
|
|
60
|
-
<th scope="col" class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
61
|
-
Success
|
|
62
|
-
</th>
|
|
63
|
-
<th scope="col" class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
64
|
-
Avg Duration
|
|
65
|
-
</th>
|
|
66
|
-
<th scope="col" class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
67
|
-
Avg Cost
|
|
68
|
-
</th>
|
|
69
|
-
<th scope="col" class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
70
|
-
Avg Tokens
|
|
71
|
-
</th>
|
|
72
|
-
<% if workflow_type == "parallel" %>
|
|
73
|
-
<th scope="col" class="px-4 py-3 text-center text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
74
|
-
Notes
|
|
75
|
-
</th>
|
|
76
|
-
<% end %>
|
|
77
|
-
</tr>
|
|
78
|
-
</thead>
|
|
79
|
-
<tbody class="divide-y divide-gray-100 dark:divide-gray-700">
|
|
80
|
-
<%
|
|
81
|
-
# For parallel workflows, find fastest/slowest
|
|
82
|
-
durations = step_stats.map { |s| s[:avg_duration_ms] }.compact
|
|
83
|
-
min_duration = durations.min
|
|
84
|
-
max_duration_val = durations.max
|
|
85
|
-
%>
|
|
86
|
-
|
|
87
|
-
<% step_stats.each do |stat| %>
|
|
88
|
-
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50">
|
|
89
|
-
<td class="px-4 py-3">
|
|
90
|
-
<div class="flex items-center gap-2">
|
|
91
|
-
<span class="font-medium text-gray-900 dark:text-gray-100">
|
|
92
|
-
<%= stat[:name] %>
|
|
93
|
-
</span>
|
|
94
|
-
<% if stat[:agent_type].present? %>
|
|
95
|
-
<span class="text-xs text-gray-400 dark:text-gray-500 font-mono">
|
|
96
|
-
(<%= stat[:agent_type].gsub(/Agent$/, '') %>)
|
|
97
|
-
</span>
|
|
98
|
-
<% end %>
|
|
99
|
-
</div>
|
|
100
|
-
</td>
|
|
101
|
-
<td class="px-4 py-3 text-right text-sm text-gray-600 dark:text-gray-300">
|
|
102
|
-
<%= number_with_delimiter(stat[:execution_count]) %>
|
|
103
|
-
</td>
|
|
104
|
-
<td class="px-4 py-3 text-right">
|
|
105
|
-
<% rate = stat[:success_rate] %>
|
|
106
|
-
<span class="text-sm font-medium <%= rate >= 95 ? 'text-green-600 dark:text-green-400' : rate >= 80 ? 'text-yellow-600 dark:text-yellow-400' : 'text-red-600 dark:text-red-400' %>">
|
|
107
|
-
<%= rate %>%
|
|
108
|
-
</span>
|
|
109
|
-
</td>
|
|
110
|
-
<td class="px-4 py-3 text-right">
|
|
111
|
-
<div class="flex items-center justify-end gap-2">
|
|
112
|
-
<div class="w-16 h-2 bg-gray-100 dark:bg-gray-700 rounded-full overflow-hidden">
|
|
113
|
-
<div class="h-full bg-purple-500 rounded-full"
|
|
114
|
-
style="width: <%= max_duration > 0 ? (stat[:avg_duration_ms].to_f / max_duration * 100).round : 0 %>%">
|
|
115
|
-
</div>
|
|
116
|
-
</div>
|
|
117
|
-
<span class="text-sm text-gray-600 dark:text-gray-300 w-20 text-right">
|
|
118
|
-
<%= number_with_delimiter(stat[:avg_duration_ms]) %> ms
|
|
119
|
-
</span>
|
|
120
|
-
</div>
|
|
121
|
-
</td>
|
|
122
|
-
<td class="px-4 py-3 text-right">
|
|
123
|
-
<div class="flex items-center justify-end gap-2">
|
|
124
|
-
<div class="w-12 h-2 bg-gray-100 dark:bg-gray-700 rounded-full overflow-hidden">
|
|
125
|
-
<div class="h-full bg-amber-500 rounded-full"
|
|
126
|
-
style="width: <%= max_cost > 0 ? (stat[:avg_cost].to_f / max_cost * 100).round : 0 %>%">
|
|
127
|
-
</div>
|
|
128
|
-
</div>
|
|
129
|
-
<span class="text-sm text-gray-600 dark:text-gray-300 w-16 text-right">
|
|
130
|
-
$<%= number_with_precision(stat[:avg_cost], precision: 4) %>
|
|
131
|
-
</span>
|
|
132
|
-
</div>
|
|
133
|
-
</td>
|
|
134
|
-
<td class="px-4 py-3 text-right text-sm text-gray-600 dark:text-gray-300">
|
|
135
|
-
<%= number_with_delimiter(stat[:avg_tokens]) %>
|
|
136
|
-
</td>
|
|
137
|
-
<% if workflow_type == "parallel" %>
|
|
138
|
-
<td class="px-4 py-3 text-center">
|
|
139
|
-
<% if step_stats.size > 1 %>
|
|
140
|
-
<% if stat[:avg_duration_ms] == min_duration %>
|
|
141
|
-
<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-700 dark:text-green-300">
|
|
142
|
-
fastest
|
|
143
|
-
</span>
|
|
144
|
-
<% elsif stat[:avg_duration_ms] == max_duration_val && min_duration != max_duration_val %>
|
|
145
|
-
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 dark:bg-yellow-900/50 text-yellow-700 dark:text-yellow-300">
|
|
146
|
-
slowest
|
|
147
|
-
</span>
|
|
148
|
-
<% end %>
|
|
149
|
-
<% end %>
|
|
150
|
-
</td>
|
|
151
|
-
<% end %>
|
|
152
|
-
</tr>
|
|
153
|
-
<% end %>
|
|
154
|
-
|
|
155
|
-
<!-- Totals Row -->
|
|
156
|
-
<%
|
|
157
|
-
total_executions = step_stats.sum { |s| s[:execution_count] }
|
|
158
|
-
total_cost = step_stats.sum { |s| s[:total_cost] }
|
|
159
|
-
total_tokens = step_stats.sum { |s| s[:total_tokens] }
|
|
160
|
-
avg_duration_all = step_stats.any? ? (step_stats.sum { |s| s[:avg_duration_ms] * s[:execution_count] }.to_f / total_executions).round : 0
|
|
161
|
-
avg_cost_all = total_executions > 0 ? total_cost / total_executions : 0
|
|
162
|
-
avg_tokens_all = total_executions > 0 ? total_tokens / total_executions : 0
|
|
163
|
-
|
|
164
|
-
# Calculate overall success rate
|
|
165
|
-
total_success = step_stats.sum { |s| (s[:success_rate] * s[:execution_count] / 100.0).round }
|
|
166
|
-
overall_success_rate = total_executions > 0 ? (total_success.to_f / total_executions * 100).round(1) : 0
|
|
167
|
-
%>
|
|
168
|
-
<tr class="bg-gray-50 dark:bg-gray-700/50 font-medium">
|
|
169
|
-
<td class="px-4 py-3 text-gray-700 dark:text-gray-300">
|
|
170
|
-
Totals
|
|
171
|
-
</td>
|
|
172
|
-
<td class="px-4 py-3 text-right text-sm text-gray-700 dark:text-gray-300">
|
|
173
|
-
<%= number_with_delimiter(total_executions) %>
|
|
174
|
-
</td>
|
|
175
|
-
<td class="px-4 py-3 text-right">
|
|
176
|
-
<span class="text-sm <%= overall_success_rate >= 95 ? 'text-green-600 dark:text-green-400' : overall_success_rate >= 80 ? 'text-yellow-600 dark:text-yellow-400' : 'text-red-600 dark:text-red-400' %>">
|
|
177
|
-
<%= overall_success_rate %>%
|
|
178
|
-
</span>
|
|
179
|
-
</td>
|
|
180
|
-
<td class="px-4 py-3 text-right text-sm text-gray-700 dark:text-gray-300">
|
|
181
|
-
<%= number_with_delimiter(avg_duration_all) %> ms
|
|
182
|
-
</td>
|
|
183
|
-
<td class="px-4 py-3 text-right text-sm text-gray-700 dark:text-gray-300">
|
|
184
|
-
$<%= number_with_precision(avg_cost_all, precision: 4) %>
|
|
185
|
-
</td>
|
|
186
|
-
<td class="px-4 py-3 text-right text-sm text-gray-700 dark:text-gray-300">
|
|
187
|
-
<%= number_with_delimiter(avg_tokens_all.round) %>
|
|
188
|
-
</td>
|
|
189
|
-
<% if workflow_type == "parallel" %>
|
|
190
|
-
<td class="px-4 py-3"></td>
|
|
191
|
-
<% end %>
|
|
192
|
-
</tr>
|
|
193
|
-
</tbody>
|
|
194
|
-
</table>
|
|
195
|
-
</div>
|
|
196
|
-
|
|
197
|
-
<% if workflow_type == "parallel" && step_stats.size > 1 %>
|
|
198
|
-
<%
|
|
199
|
-
sum_duration = step_stats.sum { |s| s[:avg_duration_ms] }
|
|
200
|
-
wall_clock = step_stats.map { |s| s[:avg_duration_ms] }.max
|
|
201
|
-
time_saved = sum_duration - wall_clock
|
|
202
|
-
%>
|
|
203
|
-
<div class="mt-4 pt-4 border-t border-gray-100 dark:border-gray-700">
|
|
204
|
-
<p class="text-sm text-gray-500 dark:text-gray-400">
|
|
205
|
-
Wall-clock avg: <span class="font-medium text-gray-700 dark:text-gray-300"><%= number_with_delimiter(wall_clock) %> ms</span>
|
|
206
|
-
<span class="text-gray-400 dark:text-gray-500 mx-2">|</span>
|
|
207
|
-
Parallel saves <span class="font-medium text-green-600 dark:text-green-400"><%= number_with_delimiter(time_saved) %> ms</span> vs sequential
|
|
208
|
-
</p>
|
|
209
|
-
</div>
|
|
210
|
-
<% end %>
|
|
211
|
-
|
|
212
|
-
<% if workflow_type == "router" && route_distribution.present? %>
|
|
213
|
-
<%
|
|
214
|
-
# Calculate classification cost if we have stats
|
|
215
|
-
# This is approximate since we don't have exact classification costs
|
|
216
|
-
%>
|
|
217
|
-
<div class="mt-4 pt-4 border-t border-gray-100 dark:border-gray-700">
|
|
218
|
-
<p class="text-sm text-gray-500 dark:text-gray-400">
|
|
219
|
-
Route distribution based on <%= route_distribution.values.sum { |v| v[:count] } %> classified requests
|
|
220
|
-
</p>
|
|
221
|
-
</div>
|
|
222
|
-
<% end %>
|
|
223
|
-
<% else %>
|
|
224
|
-
<p class="text-gray-500 dark:text-gray-400 italic py-4">
|
|
225
|
-
No <%= column_label.downcase %> performance data available for the last 30 days.
|
|
226
|
-
</p>
|
|
227
|
-
<% end %>
|
|
228
|
-
</div>
|