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,214 +0,0 @@
|
|
|
1
|
-
<%= render "ruby_llm/agents/shared/breadcrumbs", items: [
|
|
2
|
-
{ label: "Dashboard", path: ruby_llm_agents.root_path },
|
|
3
|
-
{ label: "API Configuration" }
|
|
4
|
-
] %>
|
|
5
|
-
|
|
6
|
-
<!-- Header -->
|
|
7
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 mb-6">
|
|
8
|
-
<div class="flex items-start justify-between">
|
|
9
|
-
<div class="flex items-center space-x-3">
|
|
10
|
-
<div class="flex-shrink-0 h-10 w-10 rounded-full bg-indigo-100 dark:bg-indigo-900/50 flex items-center justify-center">
|
|
11
|
-
<svg class="w-5 h-5 text-indigo-600 dark:text-indigo-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
12
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" />
|
|
13
|
-
</svg>
|
|
14
|
-
</div>
|
|
15
|
-
<div>
|
|
16
|
-
<div class="flex items-center gap-2">
|
|
17
|
-
<h1 class="text-2xl font-bold text-gray-900 dark:text-gray-100">API Configuration</h1>
|
|
18
|
-
<%= render "ruby_llm/agents/shared/doc_link" %>
|
|
19
|
-
</div>
|
|
20
|
-
<p class="text-sm text-gray-500 dark:text-gray-400">
|
|
21
|
-
Manage API keys and connection settings for LLM providers
|
|
22
|
-
</p>
|
|
23
|
-
</div>
|
|
24
|
-
</div>
|
|
25
|
-
|
|
26
|
-
<div>
|
|
27
|
-
<%= link_to edit_api_configuration_path, class: "inline-flex items-center px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors" do %>
|
|
28
|
-
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
29
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
|
30
|
-
</svg>
|
|
31
|
-
Edit Configuration
|
|
32
|
-
<% end %>
|
|
33
|
-
</div>
|
|
34
|
-
</div>
|
|
35
|
-
</div>
|
|
36
|
-
|
|
37
|
-
<!-- Provider Status Grid -->
|
|
38
|
-
<div class="mb-6">
|
|
39
|
-
<h3 class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-3">Provider Status</h3>
|
|
40
|
-
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
|
41
|
-
<% @provider_statuses.each do |provider| %>
|
|
42
|
-
<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-4">
|
|
43
|
-
<div class="flex items-center justify-between mb-2">
|
|
44
|
-
<h4 class="font-medium text-gray-900 dark:text-gray-100"><%= provider[:name] %></h4>
|
|
45
|
-
<% if provider[:configured] %>
|
|
46
|
-
<span class="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-green-100 dark:bg-green-900/50 text-green-800 dark:text-green-200">
|
|
47
|
-
Active
|
|
48
|
-
</span>
|
|
49
|
-
<% else %>
|
|
50
|
-
<span class="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400">
|
|
51
|
-
Not Set
|
|
52
|
-
</span>
|
|
53
|
-
<% end %>
|
|
54
|
-
</div>
|
|
55
|
-
|
|
56
|
-
<% if provider[:configured] %>
|
|
57
|
-
<div class="mb-2">
|
|
58
|
-
<code class="text-xs text-gray-600 dark:text-gray-400 bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded font-mono">
|
|
59
|
-
<%= provider[:masked_key] %>
|
|
60
|
-
</code>
|
|
61
|
-
</div>
|
|
62
|
-
<div class="flex items-center text-xs text-gray-500 dark:text-gray-400">
|
|
63
|
-
<svg class="w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20">
|
|
64
|
-
<path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd" />
|
|
65
|
-
</svg>
|
|
66
|
-
Source: <%= provider[:source].gsub("_", " ").titleize %>
|
|
67
|
-
</div>
|
|
68
|
-
<% else %>
|
|
69
|
-
<p class="text-xs text-gray-400 dark:text-gray-500">
|
|
70
|
-
Configure in settings or environment
|
|
71
|
-
</p>
|
|
72
|
-
<% end %>
|
|
73
|
-
|
|
74
|
-
<% if provider[:capabilities].present? %>
|
|
75
|
-
<div class="mt-2 flex flex-wrap gap-1">
|
|
76
|
-
<% provider[:capabilities].each do |cap| %>
|
|
77
|
-
<span class="text-xs px-1.5 py-0.5 bg-blue-50 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300 rounded">
|
|
78
|
-
<%= cap %>
|
|
79
|
-
</span>
|
|
80
|
-
<% end %>
|
|
81
|
-
</div>
|
|
82
|
-
<% end %>
|
|
83
|
-
</div>
|
|
84
|
-
<% end %>
|
|
85
|
-
</div>
|
|
86
|
-
</div>
|
|
87
|
-
|
|
88
|
-
<!-- Configuration Details -->
|
|
89
|
-
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
|
90
|
-
<!-- Default Models -->
|
|
91
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
|
92
|
-
<h3 class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-4">Default Models</h3>
|
|
93
|
-
<div class="space-y-4">
|
|
94
|
-
<div class="flex justify-between items-center">
|
|
95
|
-
<div>
|
|
96
|
-
<p class="text-sm font-medium text-gray-900 dark:text-gray-100">Chat Model</p>
|
|
97
|
-
<p class="text-xs text-gray-500 dark:text-gray-400">Default for chat completions</p>
|
|
98
|
-
</div>
|
|
99
|
-
<% if @resolved.default_model.present? %>
|
|
100
|
-
<code class="bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 px-2 py-1 rounded text-sm"><%= @resolved.default_model %></code>
|
|
101
|
-
<% else %>
|
|
102
|
-
<span class="text-xs text-gray-400 dark:text-gray-500">Not set</span>
|
|
103
|
-
<% end %>
|
|
104
|
-
</div>
|
|
105
|
-
|
|
106
|
-
<div class="flex justify-between items-center">
|
|
107
|
-
<div>
|
|
108
|
-
<p class="text-sm font-medium text-gray-900 dark:text-gray-100">Embedding Model</p>
|
|
109
|
-
<p class="text-xs text-gray-500 dark:text-gray-400">Default for embeddings</p>
|
|
110
|
-
</div>
|
|
111
|
-
<% if @resolved.default_embedding_model.present? %>
|
|
112
|
-
<code class="bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 px-2 py-1 rounded text-sm"><%= @resolved.default_embedding_model %></code>
|
|
113
|
-
<% else %>
|
|
114
|
-
<span class="text-xs text-gray-400 dark:text-gray-500">Not set</span>
|
|
115
|
-
<% end %>
|
|
116
|
-
</div>
|
|
117
|
-
|
|
118
|
-
<div class="flex justify-between items-center">
|
|
119
|
-
<div>
|
|
120
|
-
<p class="text-sm font-medium text-gray-900 dark:text-gray-100">Image Model</p>
|
|
121
|
-
<p class="text-xs text-gray-500 dark:text-gray-400">Default for image generation</p>
|
|
122
|
-
</div>
|
|
123
|
-
<% if @resolved.default_image_model.present? %>
|
|
124
|
-
<code class="bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 px-2 py-1 rounded text-sm"><%= @resolved.default_image_model %></code>
|
|
125
|
-
<% else %>
|
|
126
|
-
<span class="text-xs text-gray-400 dark:text-gray-500">Not set</span>
|
|
127
|
-
<% end %>
|
|
128
|
-
</div>
|
|
129
|
-
</div>
|
|
130
|
-
</div>
|
|
131
|
-
|
|
132
|
-
<!-- Connection Settings -->
|
|
133
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
|
134
|
-
<h3 class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-4">Connection Settings</h3>
|
|
135
|
-
<div class="space-y-4">
|
|
136
|
-
<div class="flex justify-between items-center">
|
|
137
|
-
<div>
|
|
138
|
-
<p class="text-sm font-medium text-gray-900 dark:text-gray-100">Request Timeout</p>
|
|
139
|
-
<p class="text-xs text-gray-500 dark:text-gray-400">Seconds before timing out</p>
|
|
140
|
-
</div>
|
|
141
|
-
<% if @resolved.request_timeout.present? %>
|
|
142
|
-
<code class="bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 px-2 py-1 rounded text-sm"><%= @resolved.request_timeout %>s</code>
|
|
143
|
-
<% else %>
|
|
144
|
-
<span class="text-xs text-gray-400 dark:text-gray-500">Default</span>
|
|
145
|
-
<% end %>
|
|
146
|
-
</div>
|
|
147
|
-
|
|
148
|
-
<div class="flex justify-between items-center">
|
|
149
|
-
<div>
|
|
150
|
-
<p class="text-sm font-medium text-gray-900 dark:text-gray-100">Max Retries</p>
|
|
151
|
-
<p class="text-xs text-gray-500 dark:text-gray-400">Retry attempts on failure</p>
|
|
152
|
-
</div>
|
|
153
|
-
<% if @resolved.max_retries.present? %>
|
|
154
|
-
<code class="bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 px-2 py-1 rounded text-sm"><%= @resolved.max_retries %></code>
|
|
155
|
-
<% else %>
|
|
156
|
-
<span class="text-xs text-gray-400 dark:text-gray-500">Default</span>
|
|
157
|
-
<% end %>
|
|
158
|
-
</div>
|
|
159
|
-
|
|
160
|
-
<div class="flex justify-between items-center">
|
|
161
|
-
<div>
|
|
162
|
-
<p class="text-sm font-medium text-gray-900 dark:text-gray-100">HTTP Proxy</p>
|
|
163
|
-
<p class="text-xs text-gray-500 dark:text-gray-400">Proxy for API requests</p>
|
|
164
|
-
</div>
|
|
165
|
-
<% if @resolved.http_proxy.present? %>
|
|
166
|
-
<code class="bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 px-2 py-1 rounded text-sm truncate max-w-[200px]"><%= @resolved.http_proxy %></code>
|
|
167
|
-
<% else %>
|
|
168
|
-
<span class="text-xs text-gray-400 dark:text-gray-500">None</span>
|
|
169
|
-
<% end %>
|
|
170
|
-
</div>
|
|
171
|
-
</div>
|
|
172
|
-
</div>
|
|
173
|
-
</div>
|
|
174
|
-
|
|
175
|
-
<!-- Custom Endpoints -->
|
|
176
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 mb-6">
|
|
177
|
-
<h3 class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-4">Custom Endpoints</h3>
|
|
178
|
-
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
179
|
-
<% [
|
|
180
|
-
{ attr: :openai_api_base, label: "OpenAI Base URL" },
|
|
181
|
-
{ attr: :gemini_api_base, label: "Gemini Base URL" },
|
|
182
|
-
{ attr: :ollama_api_base, label: "Ollama Base URL" },
|
|
183
|
-
{ attr: :gpustack_api_base, label: "GPUStack Base URL" },
|
|
184
|
-
{ attr: :xai_api_base, label: "xAI Base URL" }
|
|
185
|
-
].each do |endpoint| %>
|
|
186
|
-
<div class="flex justify-between items-center">
|
|
187
|
-
<p class="text-sm font-medium text-gray-900 dark:text-gray-100"><%= endpoint[:label] %></p>
|
|
188
|
-
<% value = @resolved.send(endpoint[:attr]) %>
|
|
189
|
-
<% if value.present? %>
|
|
190
|
-
<code class="bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 px-2 py-1 rounded text-xs truncate max-w-[250px]"><%= value %></code>
|
|
191
|
-
<% else %>
|
|
192
|
-
<span class="text-xs text-gray-400 dark:text-gray-500">Default</span>
|
|
193
|
-
<% end %>
|
|
194
|
-
</div>
|
|
195
|
-
<% end %>
|
|
196
|
-
</div>
|
|
197
|
-
</div>
|
|
198
|
-
|
|
199
|
-
<!-- Security Notice -->
|
|
200
|
-
<div class="bg-amber-50 dark:bg-amber-900/20 rounded-lg p-4 border border-amber-200 dark:border-amber-800">
|
|
201
|
-
<div class="flex">
|
|
202
|
-
<svg class="w-5 h-5 text-amber-400 mr-3 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
|
|
203
|
-
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
|
|
204
|
-
</svg>
|
|
205
|
-
<div>
|
|
206
|
-
<h4 class="text-sm font-medium text-amber-800 dark:text-amber-200">Security Note</h4>
|
|
207
|
-
<p class="text-sm text-amber-700 dark:text-amber-300 mt-1">
|
|
208
|
-
API keys stored in the database are encrypted at rest using Rails encrypted attributes.
|
|
209
|
-
For highest security, consider using environment variables for production deployments.
|
|
210
|
-
Database-stored keys are useful for multi-tenant configurations or dynamic key management.
|
|
211
|
-
</p>
|
|
212
|
-
</div>
|
|
213
|
-
</div>
|
|
214
|
-
</div>
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
<%= render "ruby_llm/agents/shared/breadcrumbs", items: [
|
|
2
|
-
{ label: "Dashboard", path: ruby_llm_agents.root_path },
|
|
3
|
-
{ label: "Tenants", path: tenants_path },
|
|
4
|
-
{ label: @tenant_budget&.display_name || @tenant_id, path: @tenant_budget ? tenant_path(@tenant_budget) : nil },
|
|
5
|
-
{ label: "API Configuration" }
|
|
6
|
-
] %>
|
|
7
|
-
|
|
8
|
-
<!-- Header -->
|
|
9
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 mb-6">
|
|
10
|
-
<div class="flex items-start justify-between">
|
|
11
|
-
<div class="flex items-center space-x-3">
|
|
12
|
-
<div class="flex-shrink-0 h-10 w-10 rounded-full bg-indigo-100 dark:bg-indigo-900/50 flex items-center justify-center">
|
|
13
|
-
<svg class="w-5 h-5 text-indigo-600 dark:text-indigo-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
14
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" />
|
|
15
|
-
</svg>
|
|
16
|
-
</div>
|
|
17
|
-
<div>
|
|
18
|
-
<h1 class="text-2xl font-bold text-gray-900 dark:text-gray-100">
|
|
19
|
-
API Configuration
|
|
20
|
-
</h1>
|
|
21
|
-
<p class="text-sm text-gray-500 dark:text-gray-400">
|
|
22
|
-
Tenant: <span class="font-medium"><%= @tenant_budget&.display_name || @tenant_id %></span>
|
|
23
|
-
</p>
|
|
24
|
-
</div>
|
|
25
|
-
<% if @config.inherit_global_defaults %>
|
|
26
|
-
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 dark:bg-blue-900/50 text-blue-800 dark:text-blue-200">
|
|
27
|
-
Inheriting Global
|
|
28
|
-
</span>
|
|
29
|
-
<% end %>
|
|
30
|
-
</div>
|
|
31
|
-
|
|
32
|
-
<div class="flex items-center space-x-3">
|
|
33
|
-
<% if @tenant_budget %>
|
|
34
|
-
<%= link_to tenant_path(@tenant_budget), class: "inline-flex items-center px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors" do %>
|
|
35
|
-
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
36
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
|
|
37
|
-
</svg>
|
|
38
|
-
Tenant Details
|
|
39
|
-
<% end %>
|
|
40
|
-
<% end %>
|
|
41
|
-
|
|
42
|
-
<%= link_to edit_tenant_api_configuration_path(@tenant_id), class: "inline-flex items-center px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors" do %>
|
|
43
|
-
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
44
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
|
45
|
-
</svg>
|
|
46
|
-
Edit Configuration
|
|
47
|
-
<% end %>
|
|
48
|
-
</div>
|
|
49
|
-
</div>
|
|
50
|
-
</div>
|
|
51
|
-
|
|
52
|
-
<!-- Provider Status Grid -->
|
|
53
|
-
<div class="mb-6">
|
|
54
|
-
<h3 class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-3">Provider Status</h3>
|
|
55
|
-
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
|
56
|
-
<% @provider_statuses.each do |provider| %>
|
|
57
|
-
<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-4">
|
|
58
|
-
<div class="flex items-center justify-between mb-2">
|
|
59
|
-
<h4 class="font-medium text-gray-900 dark:text-gray-100"><%= provider[:name] %></h4>
|
|
60
|
-
<% if provider[:configured] %>
|
|
61
|
-
<span class="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-green-100 dark:bg-green-900/50 text-green-800 dark:text-green-200">
|
|
62
|
-
Active
|
|
63
|
-
</span>
|
|
64
|
-
<% else %>
|
|
65
|
-
<span class="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400">
|
|
66
|
-
Not Set
|
|
67
|
-
</span>
|
|
68
|
-
<% end %>
|
|
69
|
-
</div>
|
|
70
|
-
|
|
71
|
-
<% if provider[:configured] %>
|
|
72
|
-
<div class="mb-2">
|
|
73
|
-
<code class="text-xs text-gray-600 dark:text-gray-400 bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded font-mono">
|
|
74
|
-
<%= provider[:masked_key] %>
|
|
75
|
-
</code>
|
|
76
|
-
</div>
|
|
77
|
-
<div class="flex items-center text-xs text-gray-500 dark:text-gray-400">
|
|
78
|
-
<% source = provider[:source] %>
|
|
79
|
-
<% source_class = case source
|
|
80
|
-
when /^tenant:/ then "text-indigo-600 dark:text-indigo-400"
|
|
81
|
-
when "global_db" then "text-blue-600 dark:text-blue-400"
|
|
82
|
-
else "text-gray-500 dark:text-gray-400"
|
|
83
|
-
end %>
|
|
84
|
-
<svg class="w-3 h-3 mr-1 <%= source_class %>" fill="currentColor" viewBox="0 0 20 20">
|
|
85
|
-
<path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd" />
|
|
86
|
-
</svg>
|
|
87
|
-
<span class="<%= source_class %>">
|
|
88
|
-
<%= case source
|
|
89
|
-
when /^tenant:/ then "Tenant Override"
|
|
90
|
-
when "global_db" then "Global DB"
|
|
91
|
-
when "ruby_llm_config" then "Config File"
|
|
92
|
-
else "Unknown"
|
|
93
|
-
end %>
|
|
94
|
-
</span>
|
|
95
|
-
</div>
|
|
96
|
-
<% else %>
|
|
97
|
-
<p class="text-xs text-gray-400 dark:text-gray-500">
|
|
98
|
-
Not configured
|
|
99
|
-
</p>
|
|
100
|
-
<% end %>
|
|
101
|
-
</div>
|
|
102
|
-
<% end %>
|
|
103
|
-
</div>
|
|
104
|
-
</div>
|
|
105
|
-
|
|
106
|
-
<!-- Configuration Details -->
|
|
107
|
-
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
|
108
|
-
<!-- Default Models -->
|
|
109
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
|
110
|
-
<h3 class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-4">Default Models</h3>
|
|
111
|
-
<div class="space-y-4">
|
|
112
|
-
<% [
|
|
113
|
-
{ attr: :default_model, label: "Chat Model", desc: "Default for chat completions" },
|
|
114
|
-
{ attr: :default_embedding_model, label: "Embedding Model", desc: "Default for embeddings" },
|
|
115
|
-
{ attr: :default_image_model, label: "Image Model", desc: "Default for image generation" }
|
|
116
|
-
].each do |model_info| %>
|
|
117
|
-
<div class="flex justify-between items-center">
|
|
118
|
-
<div>
|
|
119
|
-
<p class="text-sm font-medium text-gray-900 dark:text-gray-100"><%= model_info[:label] %></p>
|
|
120
|
-
<p class="text-xs text-gray-500 dark:text-gray-400"><%= model_info[:desc] %></p>
|
|
121
|
-
</div>
|
|
122
|
-
<% value = @resolved.send(model_info[:attr]) %>
|
|
123
|
-
<% source = @resolved.source_for(model_info[:attr]) %>
|
|
124
|
-
<% if value.present? %>
|
|
125
|
-
<div class="text-right">
|
|
126
|
-
<code class="bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 px-2 py-1 rounded text-sm"><%= value %></code>
|
|
127
|
-
<p class="text-xs text-gray-400 dark:text-gray-500 mt-1"><%= source.gsub("_", " ").titleize %></p>
|
|
128
|
-
</div>
|
|
129
|
-
<% else %>
|
|
130
|
-
<span class="text-xs text-gray-400 dark:text-gray-500">Not set</span>
|
|
131
|
-
<% end %>
|
|
132
|
-
</div>
|
|
133
|
-
<% end %>
|
|
134
|
-
</div>
|
|
135
|
-
</div>
|
|
136
|
-
|
|
137
|
-
<!-- Connection Settings -->
|
|
138
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
|
139
|
-
<h3 class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-4">Connection Settings</h3>
|
|
140
|
-
<div class="space-y-4">
|
|
141
|
-
<% [
|
|
142
|
-
{ attr: :request_timeout, label: "Request Timeout", suffix: "s" },
|
|
143
|
-
{ attr: :max_retries, label: "Max Retries" },
|
|
144
|
-
{ attr: :http_proxy, label: "HTTP Proxy" }
|
|
145
|
-
].each do |setting| %>
|
|
146
|
-
<div class="flex justify-between items-center">
|
|
147
|
-
<p class="text-sm font-medium text-gray-900 dark:text-gray-100"><%= setting[:label] %></p>
|
|
148
|
-
<% value = @resolved.send(setting[:attr]) %>
|
|
149
|
-
<% if value.present? %>
|
|
150
|
-
<code class="bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 px-2 py-1 rounded text-sm"><%= value %><%= setting[:suffix] %></code>
|
|
151
|
-
<% else %>
|
|
152
|
-
<span class="text-xs text-gray-400 dark:text-gray-500">Default</span>
|
|
153
|
-
<% end %>
|
|
154
|
-
</div>
|
|
155
|
-
<% end %>
|
|
156
|
-
</div>
|
|
157
|
-
</div>
|
|
158
|
-
</div>
|
|
159
|
-
|
|
160
|
-
<!-- Inheritance Info -->
|
|
161
|
-
<div class="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-4 border border-blue-200 dark:border-blue-800">
|
|
162
|
-
<div class="flex">
|
|
163
|
-
<svg class="w-5 h-5 text-blue-400 mr-3 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
|
|
164
|
-
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"/>
|
|
165
|
-
</svg>
|
|
166
|
-
<div>
|
|
167
|
-
<h4 class="text-sm font-medium text-blue-800 dark:text-blue-200">Configuration Resolution</h4>
|
|
168
|
-
<p class="text-sm text-blue-700 dark:text-blue-300 mt-1">
|
|
169
|
-
<% if @config.inherit_global_defaults %>
|
|
170
|
-
This tenant inherits unset values from the global configuration.
|
|
171
|
-
Values shown with "Global DB" or "Config File" source will be used unless overridden.
|
|
172
|
-
<% else %>
|
|
173
|
-
This tenant does <strong>not</strong> inherit from global configuration.
|
|
174
|
-
Only values explicitly set for this tenant will be used.
|
|
175
|
-
<% end %>
|
|
176
|
-
</p>
|
|
177
|
-
</div>
|
|
178
|
-
</div>
|
|
179
|
-
</div>
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
<%
|
|
2
|
-
# Combine all stats into a single sorted leaderboard
|
|
3
|
-
all_stats = [
|
|
4
|
-
@agent_stats,
|
|
5
|
-
@embedder_stats,
|
|
6
|
-
@transcriber_stats,
|
|
7
|
-
@speaker_stats,
|
|
8
|
-
@image_generator_stats,
|
|
9
|
-
@moderator_stats,
|
|
10
|
-
@workflow_stats
|
|
11
|
-
].flatten.compact
|
|
12
|
-
.select { |a| a[:executions].to_i > 0 }
|
|
13
|
-
.sort_by { |a| -a[:executions].to_i }
|
|
14
|
-
.first(10)
|
|
15
|
-
%>
|
|
16
|
-
|
|
17
|
-
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 h-full">
|
|
18
|
-
<div class="px-4 py-3 border-b border-gray-100 dark:border-gray-700">
|
|
19
|
-
<div class="flex items-center justify-between">
|
|
20
|
-
<h3 class="text-base font-semibold text-gray-900 dark:text-gray-100">Performance</h3>
|
|
21
|
-
<span class="text-xs text-gray-500 dark:text-gray-400">Top 10 by runs</span>
|
|
22
|
-
</div>
|
|
23
|
-
</div>
|
|
24
|
-
|
|
25
|
-
<% if all_stats.empty? %>
|
|
26
|
-
<div class="px-4 py-8 text-center text-gray-500 dark:text-gray-400">
|
|
27
|
-
<p class="text-sm">No performance data yet</p>
|
|
28
|
-
</div>
|
|
29
|
-
<% else %>
|
|
30
|
-
<div class="overflow-x-auto">
|
|
31
|
-
<table class="w-full text-sm">
|
|
32
|
-
<thead>
|
|
33
|
-
<tr class="text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
34
|
-
<th class="px-4 py-2 w-8">#</th>
|
|
35
|
-
<th class="px-4 py-2">Agent</th>
|
|
36
|
-
<th class="px-4 py-2 text-right">Runs</th>
|
|
37
|
-
<th class="px-4 py-2 text-right">Cost</th>
|
|
38
|
-
<th class="px-4 py-2 text-right">Success</th>
|
|
39
|
-
</tr>
|
|
40
|
-
</thead>
|
|
41
|
-
<tbody class="divide-y divide-gray-100 dark:divide-gray-700">
|
|
42
|
-
<% all_stats.each_with_index do |item, index| %>
|
|
43
|
-
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50">
|
|
44
|
-
<td class="px-4 py-2">
|
|
45
|
-
<span class="text-xs font-medium text-gray-400 dark:text-gray-500"><%= index + 1 %></span>
|
|
46
|
-
</td>
|
|
47
|
-
<td class="px-4 py-2">
|
|
48
|
-
<span class="text-sm font-medium text-gray-900 dark:text-gray-100 truncate max-w-[140px] block" title="<%= item[:agent_type] %>">
|
|
49
|
-
<% if item[:is_workflow] %>
|
|
50
|
-
<%= item[:agent_type].to_s.demodulize.gsub(/Workflow$|Pipeline$|Parallel$|Router$/, '') %>
|
|
51
|
-
<% else %>
|
|
52
|
-
<%= item[:agent_type].to_s.demodulize %>
|
|
53
|
-
<% end %>
|
|
54
|
-
</span>
|
|
55
|
-
</td>
|
|
56
|
-
<td class="px-4 py-2 text-right text-gray-600 dark:text-gray-300">
|
|
57
|
-
<%= number_with_delimiter(item[:executions]) %>
|
|
58
|
-
</td>
|
|
59
|
-
<td class="px-4 py-2 text-right text-gray-600 dark:text-gray-300">
|
|
60
|
-
$<%= number_with_precision(item[:total_cost], precision: 2) %>
|
|
61
|
-
</td>
|
|
62
|
-
<td class="px-4 py-2 text-right">
|
|
63
|
-
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium <%= item[:success_rate] >= 95 ? 'bg-green-100 dark:bg-green-900 text-green-700 dark:text-green-300' : item[: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' %>">
|
|
64
|
-
<%= item[:success_rate].round %>%
|
|
65
|
-
</span>
|
|
66
|
-
</td>
|
|
67
|
-
</tr>
|
|
68
|
-
<% end %>
|
|
69
|
-
</tbody>
|
|
70
|
-
</table>
|
|
71
|
-
</div>
|
|
72
|
-
<% end %>
|
|
73
|
-
</div>
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
<%# Alerts feed for budget and breaker events %>
|
|
2
|
-
<% if recent_alerts.any? %>
|
|
3
|
-
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 p-4 mb-6">
|
|
4
|
-
<div class="flex items-center justify-between mb-3">
|
|
5
|
-
<h3 class="text-sm font-semibold text-gray-700 dark:text-gray-200">Recent Alerts</h3>
|
|
6
|
-
<span class="text-xs text-gray-500 dark:text-gray-400">
|
|
7
|
-
Last <%= recent_alerts.size %> events
|
|
8
|
-
</span>
|
|
9
|
-
</div>
|
|
10
|
-
|
|
11
|
-
<div class="space-y-2 max-h-48 overflow-y-auto">
|
|
12
|
-
<% recent_alerts.each do |alert| %>
|
|
13
|
-
<%
|
|
14
|
-
icon_color = case alert[:type].to_s
|
|
15
|
-
when /budget/ then "text-amber-500"
|
|
16
|
-
when /breaker_open/ then "text-red-500"
|
|
17
|
-
when /breaker_closed/ then "text-green-500"
|
|
18
|
-
else "text-blue-500"
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
bg_color = case alert[:type].to_s
|
|
22
|
-
when /budget/ then "bg-amber-50 dark:bg-amber-900/20"
|
|
23
|
-
when /breaker_open/ then "bg-red-50 dark:bg-red-900/20"
|
|
24
|
-
when /breaker_closed/ then "bg-green-50 dark:bg-green-900/20"
|
|
25
|
-
else "bg-blue-50 dark:bg-blue-900/20"
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
icon_path = case alert[:type].to_s
|
|
29
|
-
when /budget/
|
|
30
|
-
"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"
|
|
31
|
-
when /breaker/
|
|
32
|
-
"M13 10V3L4 14h7v7l9-11h-7z"
|
|
33
|
-
else
|
|
34
|
-
"M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"
|
|
35
|
-
end
|
|
36
|
-
%>
|
|
37
|
-
<div class="flex items-start gap-3 p-2 rounded-lg <%= bg_color %>">
|
|
38
|
-
<svg class="w-4 h-4 mt-0.5 <%= icon_color %> flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
39
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="<%= icon_path %>"/>
|
|
40
|
-
</svg>
|
|
41
|
-
<div class="flex-1 min-w-0">
|
|
42
|
-
<p class="text-sm text-gray-700 dark:text-gray-300">
|
|
43
|
-
<%= alert[:message] || alert[:type].to_s.humanize %>
|
|
44
|
-
</p>
|
|
45
|
-
<div class="flex items-center gap-2 mt-0.5">
|
|
46
|
-
<% if alert[:agent_type] %>
|
|
47
|
-
<span class="text-xs text-gray-500 dark:text-gray-400">
|
|
48
|
-
<%= alert[:agent_type].gsub(/Agent$/, '') %>
|
|
49
|
-
</span>
|
|
50
|
-
<% end %>
|
|
51
|
-
<% if alert[:timestamp] %>
|
|
52
|
-
<span class="text-xs text-gray-400 dark:text-gray-500">
|
|
53
|
-
<%= alert[:timestamp].is_a?(Time) ? time_ago_in_words(alert[:timestamp]) + " ago" : alert[:timestamp] %>
|
|
54
|
-
</span>
|
|
55
|
-
<% end %>
|
|
56
|
-
</div>
|
|
57
|
-
</div>
|
|
58
|
-
</div>
|
|
59
|
-
<% end %>
|
|
60
|
-
</div>
|
|
61
|
-
</div>
|
|
62
|
-
<% end %>
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
<%# Circuit breaker status strip %>
|
|
2
|
-
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 p-4 mb-6">
|
|
3
|
-
<div class="flex items-center justify-between mb-3">
|
|
4
|
-
<h3 class="text-sm font-semibold text-gray-700 dark:text-gray-200">Circuit Breakers</h3>
|
|
5
|
-
<% if open_breakers.any? %>
|
|
6
|
-
<span class="flex items-center text-xs text-red-600 dark:text-red-400">
|
|
7
|
-
<span class="w-2 h-2 bg-red-500 rounded-full mr-1 animate-pulse"></span>
|
|
8
|
-
<%= open_breakers.size %> open
|
|
9
|
-
</span>
|
|
10
|
-
<% else %>
|
|
11
|
-
<span class="flex items-center text-xs text-green-600 dark:text-green-400">
|
|
12
|
-
<span class="w-2 h-2 bg-green-500 rounded-full mr-1"></span>
|
|
13
|
-
All healthy
|
|
14
|
-
</span>
|
|
15
|
-
<% end %>
|
|
16
|
-
</div>
|
|
17
|
-
|
|
18
|
-
<% if open_breakers.any? %>
|
|
19
|
-
<div class="flex flex-wrap gap-2">
|
|
20
|
-
<% open_breakers.each do |breaker| %>
|
|
21
|
-
<%= link_to ruby_llm_agents.agent_path(breaker[:agent_type]),
|
|
22
|
-
class: "inline-flex items-center px-3 py-1.5 bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-800 rounded-full text-sm hover:bg-red-100 dark:hover:bg-red-900/50 transition-colors" do %>
|
|
23
|
-
<span class="w-2 h-2 bg-red-500 rounded-full mr-2 animate-pulse"></span>
|
|
24
|
-
<span class="text-red-700 dark:text-red-300 font-medium">
|
|
25
|
-
<%= breaker[:agent_type].gsub(/Agent$/, '') %>
|
|
26
|
-
</span>
|
|
27
|
-
<span class="text-red-500 dark:text-red-400 mx-1">/</span>
|
|
28
|
-
<span class="text-red-600 dark:text-red-300 text-xs">
|
|
29
|
-
<%= breaker[:model_id].split('/').last %>
|
|
30
|
-
</span>
|
|
31
|
-
<% if breaker[:cooldown_remaining] && breaker[:cooldown_remaining] > 0 %>
|
|
32
|
-
<span class="ml-2 text-xs text-red-500 dark:text-red-400 tabular-nums" data-cooldown="<%= breaker[:cooldown_remaining] %>">
|
|
33
|
-
<%= breaker[:cooldown_remaining] %>s
|
|
34
|
-
</span>
|
|
35
|
-
<% end %>
|
|
36
|
-
<% end %>
|
|
37
|
-
<% end %>
|
|
38
|
-
</div>
|
|
39
|
-
<% else %>
|
|
40
|
-
<div class="flex items-center justify-center py-2 text-gray-500 dark:text-gray-400">
|
|
41
|
-
<svg class="w-4 h-4 mr-2 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
42
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
43
|
-
</svg>
|
|
44
|
-
<span class="text-sm">All circuit breakers are closed</span>
|
|
45
|
-
</div>
|
|
46
|
-
<% end %>
|
|
47
|
-
</div>
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
<%# Budget bars for daily and monthly spending %>
|
|
2
|
-
<% if budget_status[:enabled] %>
|
|
3
|
-
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 p-4 mb-6">
|
|
4
|
-
<div class="flex items-center justify-between mb-3">
|
|
5
|
-
<h3 class="text-sm font-semibold text-gray-700 dark:text-gray-200">Budget Usage</h3>
|
|
6
|
-
<span class="text-xs text-gray-500 dark:text-gray-400">
|
|
7
|
-
<%= budget_status[:enforcement] == :hard ? "Hard Enforcement" : "Soft Enforcement" %>
|
|
8
|
-
</span>
|
|
9
|
-
</div>
|
|
10
|
-
|
|
11
|
-
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
12
|
-
<%# Daily Budget %>
|
|
13
|
-
<% if budget_status[:global_daily] %>
|
|
14
|
-
<% daily = budget_status[:global_daily] %>
|
|
15
|
-
<% percentage = daily[:percentage_used] %>
|
|
16
|
-
<% bar_color = percentage >= 100 ? "bg-red-500" : (percentage >= 80 ? "bg-yellow-500" : "bg-green-500") %>
|
|
17
|
-
<div>
|
|
18
|
-
<div class="flex justify-between items-center mb-1">
|
|
19
|
-
<span class="text-xs font-medium text-gray-600 dark:text-gray-300">Daily</span>
|
|
20
|
-
<span class="text-xs text-gray-500 dark:text-gray-400">
|
|
21
|
-
$<%= number_with_precision(daily[:current], precision: 2) %> / $<%= number_with_precision(daily[:limit], precision: 2) %>
|
|
22
|
-
</span>
|
|
23
|
-
</div>
|
|
24
|
-
<div class="relative w-full h-2 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
|
|
25
|
-
<div class="absolute h-full <%= bar_color %> rounded-full transition-all duration-300" style="width: <%= [percentage, 100].min %>%"></div>
|
|
26
|
-
<%# Soft cap marker at 80% %>
|
|
27
|
-
<div class="absolute h-full w-0.5 bg-yellow-600 dark:bg-yellow-400" style="left: 80%"></div>
|
|
28
|
-
</div>
|
|
29
|
-
<div class="flex justify-between items-center mt-1">
|
|
30
|
-
<span class="text-xs <%= percentage >= 100 ? 'text-red-600 dark:text-red-400 font-medium' : 'text-gray-400 dark:text-gray-500' %>">
|
|
31
|
-
<%= percentage.round(1) %>% used
|
|
32
|
-
</span>
|
|
33
|
-
<span class="text-xs text-gray-400 dark:text-gray-500">
|
|
34
|
-
$<%= number_with_precision(daily[:remaining], precision: 2) %> remaining
|
|
35
|
-
</span>
|
|
36
|
-
</div>
|
|
37
|
-
</div>
|
|
38
|
-
<% end %>
|
|
39
|
-
|
|
40
|
-
<%# Monthly Budget %>
|
|
41
|
-
<% if budget_status[:global_monthly] %>
|
|
42
|
-
<% monthly = budget_status[:global_monthly] %>
|
|
43
|
-
<% percentage = monthly[:percentage_used] %>
|
|
44
|
-
<% bar_color = percentage >= 100 ? "bg-red-500" : (percentage >= 80 ? "bg-yellow-500" : "bg-green-500") %>
|
|
45
|
-
<div>
|
|
46
|
-
<div class="flex justify-between items-center mb-1">
|
|
47
|
-
<span class="text-xs font-medium text-gray-600 dark:text-gray-300">Monthly</span>
|
|
48
|
-
<span class="text-xs text-gray-500 dark:text-gray-400">
|
|
49
|
-
$<%= number_with_precision(monthly[:current], precision: 2) %> / $<%= number_with_precision(monthly[:limit], precision: 2) %>
|
|
50
|
-
</span>
|
|
51
|
-
</div>
|
|
52
|
-
<div class="relative w-full h-2 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
|
|
53
|
-
<div class="absolute h-full <%= bar_color %> rounded-full transition-all duration-300" style="width: <%= [percentage, 100].min %>%"></div>
|
|
54
|
-
<%# Soft cap marker at 80% %>
|
|
55
|
-
<div class="absolute h-full w-0.5 bg-yellow-600 dark:bg-yellow-400" style="left: 80%"></div>
|
|
56
|
-
</div>
|
|
57
|
-
<div class="flex justify-between items-center mt-1">
|
|
58
|
-
<span class="text-xs <%= percentage >= 100 ? 'text-red-600 dark:text-red-400 font-medium' : 'text-gray-400 dark:text-gray-500' %>">
|
|
59
|
-
<%= percentage.round(1) %>% used
|
|
60
|
-
</span>
|
|
61
|
-
<span class="text-xs text-gray-400 dark:text-gray-500">
|
|
62
|
-
$<%= number_with_precision(monthly[:remaining], precision: 2) %> remaining
|
|
63
|
-
</span>
|
|
64
|
-
</div>
|
|
65
|
-
</div>
|
|
66
|
-
<% end %>
|
|
67
|
-
</div>
|
|
68
|
-
|
|
69
|
-
<% unless budget_status[:global_daily] || budget_status[:global_monthly] %>
|
|
70
|
-
<p class="text-sm text-gray-500 dark:text-gray-400 text-center py-2">
|
|
71
|
-
No budget limits configured
|
|
72
|
-
</p>
|
|
73
|
-
<% end %>
|
|
74
|
-
</div>
|
|
75
|
-
<% end %>
|