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,150 +1,140 @@
|
|
|
1
1
|
<%# Form partial for tenant budget editing %>
|
|
2
|
-
<%= form_with model: tenant, url: tenant_path(tenant), method: :patch, class: "space-y-
|
|
2
|
+
<%= form_with model: tenant, url: tenant_path(tenant), method: :patch, class: "space-y-8" do |f| %>
|
|
3
3
|
<% if tenant.errors.any? %>
|
|
4
|
-
<div class="
|
|
5
|
-
|
|
6
|
-
<
|
|
7
|
-
|
|
8
|
-
</svg>
|
|
9
|
-
<h3 class="text-sm font-medium text-red-800 dark:text-red-200">
|
|
10
|
-
Please fix the following errors:
|
|
11
|
-
</h3>
|
|
12
|
-
</div>
|
|
13
|
-
<ul class="mt-2 ml-7 list-disc list-inside text-sm text-red-700 dark:text-red-300">
|
|
14
|
-
<% tenant.errors.full_messages.each do |message| %>
|
|
15
|
-
<li><%= message %></li>
|
|
16
|
-
<% end %>
|
|
17
|
-
</ul>
|
|
4
|
+
<div class="font-mono text-xs text-red-500 dark:text-red-400 space-y-1 py-2">
|
|
5
|
+
<% tenant.errors.full_messages.each do |message| %>
|
|
6
|
+
<div>· <%= message %></div>
|
|
7
|
+
<% end %>
|
|
18
8
|
</div>
|
|
19
9
|
<% end %>
|
|
20
10
|
|
|
21
|
-
<!--
|
|
22
|
-
<div
|
|
23
|
-
<
|
|
11
|
+
<!-- ── basic info ──────────────────── -->
|
|
12
|
+
<div>
|
|
13
|
+
<div class="flex items-center gap-3 mb-4">
|
|
14
|
+
<span class="text-[10px] font-medium text-gray-400 dark:text-gray-600 uppercase tracking-widest font-mono">basic info</span>
|
|
15
|
+
<div class="flex-1 border-t border-gray-200 dark:border-gray-800"></div>
|
|
16
|
+
</div>
|
|
24
17
|
|
|
25
18
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
26
19
|
<div>
|
|
27
|
-
<%= f.label :name, "
|
|
20
|
+
<%= f.label :name, "display name", class: "block text-xs font-mono text-gray-500 dark:text-gray-400 mb-1" %>
|
|
28
21
|
<%= f.text_field :name,
|
|
29
|
-
class: "w-full px-3 py-2 border border-gray-
|
|
22
|
+
class: "w-full px-3 py-2 bg-transparent border border-gray-200 dark:border-gray-800 rounded text-sm font-mono text-gray-900 dark:text-gray-100 focus:ring-1 focus:ring-gray-400 dark:focus:ring-gray-600 focus:border-gray-400 dark:focus:border-gray-600 placeholder-gray-400 dark:placeholder-gray-600",
|
|
30
23
|
placeholder: "e.g., Acme Corporation" %>
|
|
31
|
-
<p class="mt-1 text-
|
|
32
|
-
|
|
24
|
+
<p class="mt-1 text-[10px] font-mono text-gray-400 dark:text-gray-600">
|
|
25
|
+
Falls back to tenant ID if not set.
|
|
33
26
|
</p>
|
|
34
27
|
</div>
|
|
35
28
|
|
|
36
29
|
<div>
|
|
37
|
-
<label class="block text-
|
|
38
|
-
<p class="w-full px-3 py-2 bg-gray-50 dark:bg-gray-
|
|
30
|
+
<label class="block text-xs font-mono text-gray-500 dark:text-gray-400 mb-1">tenant id</label>
|
|
31
|
+
<p class="w-full px-3 py-2 bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded text-sm font-mono text-gray-500 dark:text-gray-400">
|
|
39
32
|
<%= tenant.tenant_id %>
|
|
40
33
|
</p>
|
|
41
|
-
<p class="mt-1 text-
|
|
34
|
+
<p class="mt-1 text-[10px] font-mono text-gray-400 dark:text-gray-600">
|
|
42
35
|
System identifier. Cannot be changed.
|
|
43
36
|
</p>
|
|
44
37
|
</div>
|
|
45
38
|
</div>
|
|
46
39
|
</div>
|
|
47
40
|
|
|
48
|
-
<!--
|
|
49
|
-
<div
|
|
50
|
-
<
|
|
41
|
+
<!-- ── budget limits ──────────────────── -->
|
|
42
|
+
<div>
|
|
43
|
+
<div class="flex items-center gap-3 mb-4">
|
|
44
|
+
<span class="text-[10px] font-medium text-gray-400 dark:text-gray-600 uppercase tracking-widest font-mono">budget limits (usd)</span>
|
|
45
|
+
<div class="flex-1 border-t border-gray-200 dark:border-gray-800"></div>
|
|
46
|
+
</div>
|
|
51
47
|
|
|
52
48
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
53
49
|
<div>
|
|
54
|
-
<%= f.label :daily_limit, "
|
|
50
|
+
<%= f.label :daily_limit, "daily limit", class: "block text-xs font-mono text-gray-500 dark:text-gray-400 mb-1" %>
|
|
55
51
|
<div class="relative">
|
|
56
|
-
<span class="absolute inset-y-0 left-0 pl-3 flex items-center text-gray-
|
|
52
|
+
<span class="absolute inset-y-0 left-0 pl-3 flex items-center text-gray-400 dark:text-gray-600 font-mono text-sm">$</span>
|
|
57
53
|
<%= f.number_field :daily_limit,
|
|
58
|
-
class: "w-full pl-7 pr-3 py-2 border border-gray-
|
|
54
|
+
class: "w-full pl-7 pr-3 py-2 bg-transparent border border-gray-200 dark:border-gray-800 rounded text-sm font-mono text-gray-900 dark:text-gray-100 focus:ring-1 focus:ring-gray-400 dark:focus:ring-gray-600 focus:border-gray-400 dark:focus:border-gray-600 placeholder-gray-400 dark:placeholder-gray-600",
|
|
59
55
|
step: "0.01",
|
|
60
56
|
min: "0",
|
|
61
|
-
placeholder: "
|
|
57
|
+
placeholder: "50.00" %>
|
|
62
58
|
</div>
|
|
63
|
-
<p class="mt-1 text-
|
|
64
|
-
|
|
59
|
+
<p class="mt-1 text-[10px] font-mono text-gray-400 dark:text-gray-600">
|
|
60
|
+
Leave blank for no limit.
|
|
65
61
|
</p>
|
|
66
62
|
</div>
|
|
67
63
|
|
|
68
64
|
<div>
|
|
69
|
-
<%= f.label :monthly_limit, "
|
|
65
|
+
<%= f.label :monthly_limit, "monthly limit", class: "block text-xs font-mono text-gray-500 dark:text-gray-400 mb-1" %>
|
|
70
66
|
<div class="relative">
|
|
71
|
-
<span class="absolute inset-y-0 left-0 pl-3 flex items-center text-gray-
|
|
67
|
+
<span class="absolute inset-y-0 left-0 pl-3 flex items-center text-gray-400 dark:text-gray-600 font-mono text-sm">$</span>
|
|
72
68
|
<%= f.number_field :monthly_limit,
|
|
73
|
-
class: "w-full pl-7 pr-3 py-2 border border-gray-
|
|
69
|
+
class: "w-full pl-7 pr-3 py-2 bg-transparent border border-gray-200 dark:border-gray-800 rounded text-sm font-mono text-gray-900 dark:text-gray-100 focus:ring-1 focus:ring-gray-400 dark:focus:ring-gray-600 focus:border-gray-400 dark:focus:border-gray-600 placeholder-gray-400 dark:placeholder-gray-600",
|
|
74
70
|
step: "0.01",
|
|
75
71
|
min: "0",
|
|
76
|
-
placeholder: "
|
|
72
|
+
placeholder: "500.00" %>
|
|
77
73
|
</div>
|
|
78
|
-
<p class="mt-1 text-
|
|
79
|
-
|
|
74
|
+
<p class="mt-1 text-[10px] font-mono text-gray-400 dark:text-gray-600">
|
|
75
|
+
Leave blank for no limit.
|
|
80
76
|
</p>
|
|
81
77
|
</div>
|
|
82
78
|
</div>
|
|
83
79
|
</div>
|
|
84
80
|
|
|
85
|
-
<!--
|
|
86
|
-
<div
|
|
87
|
-
<
|
|
81
|
+
<!-- ── token limits ──────────────────── -->
|
|
82
|
+
<div>
|
|
83
|
+
<div class="flex items-center gap-3 mb-4">
|
|
84
|
+
<span class="text-[10px] font-medium text-gray-400 dark:text-gray-600 uppercase tracking-widest font-mono">token limits</span>
|
|
85
|
+
<div class="flex-1 border-t border-gray-200 dark:border-gray-800"></div>
|
|
86
|
+
</div>
|
|
88
87
|
|
|
89
88
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
90
89
|
<div>
|
|
91
|
-
<%= f.label :daily_token_limit, "
|
|
90
|
+
<%= f.label :daily_token_limit, "daily token limit", class: "block text-xs font-mono text-gray-500 dark:text-gray-400 mb-1" %>
|
|
92
91
|
<%= f.number_field :daily_token_limit,
|
|
93
|
-
class: "w-full px-3 py-2 border border-gray-
|
|
92
|
+
class: "w-full px-3 py-2 bg-transparent border border-gray-200 dark:border-gray-800 rounded text-sm font-mono text-gray-900 dark:text-gray-100 focus:ring-1 focus:ring-gray-400 dark:focus:ring-gray-600 focus:border-gray-400 dark:focus:border-gray-600 placeholder-gray-400 dark:placeholder-gray-600",
|
|
94
93
|
step: "1",
|
|
95
94
|
min: "0",
|
|
96
|
-
placeholder: "
|
|
97
|
-
<p class="mt-1 text-
|
|
98
|
-
|
|
95
|
+
placeholder: "1000000" %>
|
|
96
|
+
<p class="mt-1 text-[10px] font-mono text-gray-400 dark:text-gray-600">
|
|
97
|
+
Leave blank for no limit.
|
|
99
98
|
</p>
|
|
100
99
|
</div>
|
|
101
100
|
|
|
102
101
|
<div>
|
|
103
|
-
<%= f.label :monthly_token_limit, "
|
|
102
|
+
<%= f.label :monthly_token_limit, "monthly token limit", class: "block text-xs font-mono text-gray-500 dark:text-gray-400 mb-1" %>
|
|
104
103
|
<%= f.number_field :monthly_token_limit,
|
|
105
|
-
class: "w-full px-3 py-2 border border-gray-
|
|
104
|
+
class: "w-full px-3 py-2 bg-transparent border border-gray-200 dark:border-gray-800 rounded text-sm font-mono text-gray-900 dark:text-gray-100 focus:ring-1 focus:ring-gray-400 dark:focus:ring-gray-600 focus:border-gray-400 dark:focus:border-gray-600 placeholder-gray-400 dark:placeholder-gray-600",
|
|
106
105
|
step: "1",
|
|
107
106
|
min: "0",
|
|
108
|
-
placeholder: "
|
|
109
|
-
<p class="mt-1 text-
|
|
110
|
-
|
|
107
|
+
placeholder: "10000000" %>
|
|
108
|
+
<p class="mt-1 text-[10px] font-mono text-gray-400 dark:text-gray-600">
|
|
109
|
+
Leave blank for no limit.
|
|
111
110
|
</p>
|
|
112
111
|
</div>
|
|
113
112
|
</div>
|
|
114
113
|
</div>
|
|
115
114
|
|
|
116
|
-
<!--
|
|
117
|
-
<div
|
|
118
|
-
<
|
|
115
|
+
<!-- ── enforcement ──────────────────── -->
|
|
116
|
+
<div>
|
|
117
|
+
<div class="flex items-center gap-3 mb-4">
|
|
118
|
+
<span class="text-[10px] font-medium text-gray-400 dark:text-gray-600 uppercase tracking-widest font-mono">enforcement</span>
|
|
119
|
+
<div class="flex-1 border-t border-gray-200 dark:border-gray-800"></div>
|
|
120
|
+
</div>
|
|
119
121
|
|
|
120
|
-
<div>
|
|
121
|
-
<%= f.label :enforcement, "
|
|
122
|
+
<div class="max-w-md">
|
|
123
|
+
<%= f.label :enforcement, "enforcement mode", class: "block text-xs font-mono text-gray-500 dark:text-gray-400 mb-1" %>
|
|
122
124
|
<%= f.select :enforcement,
|
|
123
125
|
options_for_select([
|
|
124
|
-
["
|
|
125
|
-
["
|
|
126
|
-
["
|
|
126
|
+
["none — tracking only", "none"],
|
|
127
|
+
["soft — log warnings, allow requests", "soft"],
|
|
128
|
+
["hard — block requests when exceeded", "hard"]
|
|
127
129
|
], tenant.enforcement || "soft"),
|
|
128
130
|
{},
|
|
129
|
-
class: "w-full px-3 py-2 border border-gray-
|
|
130
|
-
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
|
131
|
-
Controls what happens when budget limits are exceeded.
|
|
132
|
-
</p>
|
|
133
|
-
</div>
|
|
134
|
-
|
|
135
|
-
<div class="mt-4 p-4 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
|
|
136
|
-
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Enforcement Mode Details</h4>
|
|
137
|
-
<ul class="text-xs text-gray-600 dark:text-gray-400 space-y-1">
|
|
138
|
-
<li><strong>None:</strong> Budget tracking only, no enforcement actions taken.</li>
|
|
139
|
-
<li><strong>Soft:</strong> Logs warnings and triggers alerts when limits are exceeded, but allows requests to proceed.</li>
|
|
140
|
-
<li><strong>Hard:</strong> Blocks agent executions when budget limits are exceeded. Use with caution.</li>
|
|
141
|
-
</ul>
|
|
131
|
+
class: "w-full px-3 py-2 bg-transparent border border-gray-200 dark:border-gray-800 rounded text-sm font-mono text-gray-900 dark:text-gray-100 focus:ring-1 focus:ring-gray-400 dark:focus:ring-gray-600 focus:border-gray-400 dark:focus:border-gray-600" %>
|
|
142
132
|
</div>
|
|
143
133
|
</div>
|
|
144
134
|
|
|
145
135
|
<!-- Actions -->
|
|
146
|
-
<div class="flex items-center
|
|
147
|
-
<%=
|
|
148
|
-
<%=
|
|
136
|
+
<div class="flex items-center gap-4 pt-4 border-t border-gray-200 dark:border-gray-800">
|
|
137
|
+
<%= f.submit "save changes", class: "px-4 py-2 text-xs font-mono text-white bg-gray-900 dark:bg-gray-100 dark:text-gray-900 rounded hover:bg-gray-700 dark:hover:bg-gray-300 cursor-pointer transition-colors" %>
|
|
138
|
+
<%= link_to "cancel", tenant_path(tenant), class: "text-xs font-mono text-gray-400 dark:text-gray-500 hover:text-gray-700 dark:hover:text-gray-300" %>
|
|
149
139
|
</div>
|
|
150
140
|
<% end %>
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
{ label: "Edit" }
|
|
6
|
-
] %>
|
|
1
|
+
<!-- Back link -->
|
|
2
|
+
<nav class="font-mono text-xs mb-6">
|
|
3
|
+
<%= link_to "← #{@tenant.display_name}", ruby_llm_agents.tenant_path(@tenant), class: "text-gray-400 dark:text-gray-500 hover:text-gray-700 dark:hover:text-gray-300" %>
|
|
4
|
+
</nav>
|
|
7
5
|
|
|
8
|
-
<div class="mb-6">
|
|
9
|
-
<
|
|
10
|
-
<
|
|
6
|
+
<div class="flex items-center gap-3 mb-6">
|
|
7
|
+
<span class="text-[10px] font-medium text-gray-400 dark:text-gray-600 uppercase tracking-widest font-mono">edit tenant</span>
|
|
8
|
+
<div class="flex-1 border-t border-gray-200 dark:border-gray-800"></div>
|
|
11
9
|
</div>
|
|
12
10
|
|
|
13
11
|
<%= render "form", tenant: @tenant %>
|
|
@@ -1,132 +1,110 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
<%
|
|
2
|
+
sc = @sort_params[:column]
|
|
3
|
+
sd = @sort_params[:direction]
|
|
4
|
+
sort_url = ->(col) {
|
|
5
|
+
dir = (col == sc && sd == "asc") ? "desc" : "asc"
|
|
6
|
+
url_for(request.query_parameters.merge(sort: col, direction: dir))
|
|
7
|
+
}
|
|
8
|
+
sort_active = ->(col) { col == sc ? "text-gray-600 dark:text-gray-300" : "" }
|
|
9
|
+
sort_arrow = ->(col) { col == sc ? (sd == "asc" ? " ↑" : " ↓") : "" }
|
|
10
|
+
%>
|
|
11
|
+
|
|
12
|
+
<!-- ── tenants ──────────────────────────── -->
|
|
13
|
+
<div class="flex items-center gap-3 mb-4">
|
|
14
|
+
<span class="text-[10px] font-medium text-gray-400 dark:text-gray-600 uppercase tracking-widest font-mono">tenants</span>
|
|
15
|
+
<div class="font-mono text-xs text-gray-400 dark:text-gray-500 flex items-center gap-1.5">
|
|
16
|
+
<span><%= @tenants.size %></span>
|
|
4
17
|
<%= render "ruby_llm/agents/shared/doc_link" %>
|
|
5
18
|
</div>
|
|
6
|
-
<
|
|
19
|
+
<div class="flex-1 border-t border-gray-200 dark:border-gray-800"></div>
|
|
20
|
+
|
|
21
|
+
<!-- Search -->
|
|
22
|
+
<%= form_with url: tenants_path, method: :get, class: "flex items-center", data: { turbo: false } do %>
|
|
23
|
+
<% request.query_parameters.except("q").each do |key, value| %>
|
|
24
|
+
<input type="hidden" name="<%= key %>" value="<%= value %>">
|
|
25
|
+
<% end %>
|
|
26
|
+
<input type="text"
|
|
27
|
+
name="q"
|
|
28
|
+
value="<%= @search_query %>"
|
|
29
|
+
placeholder="search tenants…"
|
|
30
|
+
class="w-40 px-2 py-0.5 bg-transparent border border-gray-200 dark:border-gray-800 rounded text-xs font-mono text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-600 focus:ring-1 focus:ring-gray-400 dark:focus:ring-gray-600 focus:border-gray-400 dark:focus:border-gray-600"
|
|
31
|
+
autocomplete="off">
|
|
32
|
+
<% end %>
|
|
7
33
|
</div>
|
|
8
34
|
|
|
9
35
|
<% if @tenants.empty? %>
|
|
10
|
-
<div class="
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
36
|
+
<div class="font-mono text-xs text-gray-400 dark:text-gray-600 py-4 px-2">
|
|
37
|
+
<% if @search_query.present? %>
|
|
38
|
+
no tenants matching "<%= @search_query %>"
|
|
39
|
+
<% else %>
|
|
40
|
+
no tenants configured
|
|
41
|
+
<% end %>
|
|
16
42
|
</div>
|
|
17
43
|
<% else %>
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
80
|
-
$<%= number_with_precision(tenant.effective_monthly_limit, precision: 2) %>
|
|
81
|
-
</span>
|
|
82
|
-
<% else %>
|
|
83
|
-
<span class="text-sm text-gray-400 dark:text-gray-500">No limit</span>
|
|
84
|
-
<% end %>
|
|
85
|
-
</td>
|
|
86
|
-
<td class="px-6 py-4 whitespace-nowrap">
|
|
87
|
-
<% if tenant.effective_daily_token_limit %>
|
|
88
|
-
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
89
|
-
<%= number_with_delimiter(tenant.effective_daily_token_limit) %>
|
|
90
|
-
</span>
|
|
91
|
-
<% else %>
|
|
92
|
-
<span class="text-sm text-gray-400 dark:text-gray-500">No limit</span>
|
|
93
|
-
<% end %>
|
|
94
|
-
</td>
|
|
95
|
-
<td class="px-6 py-4 whitespace-nowrap">
|
|
96
|
-
<% if tenant.effective_monthly_token_limit %>
|
|
97
|
-
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
98
|
-
<%= number_with_delimiter(tenant.effective_monthly_token_limit) %>
|
|
99
|
-
</span>
|
|
100
|
-
<% else %>
|
|
101
|
-
<span class="text-sm text-gray-400 dark:text-gray-500">No limit</span>
|
|
102
|
-
<% end %>
|
|
103
|
-
</td>
|
|
104
|
-
<td class="px-6 py-4 whitespace-nowrap">
|
|
105
|
-
<%
|
|
106
|
-
enforcement = tenant.effective_enforcement.to_s
|
|
107
|
-
badge_class = case enforcement
|
|
108
|
-
when "hard" then "bg-red-100 dark:bg-red-900/50 text-red-800 dark:text-red-200"
|
|
109
|
-
when "soft" then "bg-yellow-100 dark:bg-yellow-900/50 text-yellow-800 dark:text-yellow-200"
|
|
110
|
-
else "bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400"
|
|
111
|
-
end
|
|
112
|
-
%>
|
|
113
|
-
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium <%= badge_class %>">
|
|
114
|
-
<%= enforcement.capitalize %>
|
|
115
|
-
</span>
|
|
116
|
-
</td>
|
|
117
|
-
<td class="px-6 py-4 whitespace-nowrap text-right text-sm">
|
|
118
|
-
<%= link_to tenant_path(tenant), class: "text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300" do %>
|
|
119
|
-
View
|
|
120
|
-
<% end %>
|
|
121
|
-
<span class="text-gray-300 dark:text-gray-600 mx-2">|</span>
|
|
122
|
-
<%= link_to edit_tenant_path(tenant), class: "text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300" do %>
|
|
123
|
-
Edit
|
|
124
|
-
<% end %>
|
|
125
|
-
</td>
|
|
126
|
-
</tr>
|
|
44
|
+
<!-- Column headers -->
|
|
45
|
+
<div class="flex items-center gap-3 px-2 -mx-2 font-mono text-[10px] text-gray-400 dark:text-gray-600 uppercase tracking-wider mb-1">
|
|
46
|
+
<%= link_to "name#{sort_arrow.call('name')}".html_safe, sort_url.call("name"),
|
|
47
|
+
class: "flex-[2] min-w-0 hover:text-gray-600 dark:hover:text-gray-300 #{sort_active.call('name')}" %>
|
|
48
|
+
<%= link_to "daily#{sort_arrow.call('daily_limit')}".html_safe, sort_url.call("daily_limit"),
|
|
49
|
+
class: "w-20 flex-shrink-0 text-right hidden sm:block hover:text-gray-600 dark:hover:text-gray-300 #{sort_active.call('daily_limit')}" %>
|
|
50
|
+
<%= link_to "monthly#{sort_arrow.call('monthly_limit')}".html_safe, sort_url.call("monthly_limit"),
|
|
51
|
+
class: "w-20 flex-shrink-0 text-right hidden sm:block hover:text-gray-600 dark:hover:text-gray-300 #{sort_active.call('monthly_limit')}" %>
|
|
52
|
+
<%= link_to "enforcement#{sort_arrow.call('enforcement')}".html_safe, sort_url.call("enforcement"),
|
|
53
|
+
class: "w-16 flex-shrink-0 hidden md:block hover:text-gray-600 dark:hover:text-gray-300 #{sort_active.call('enforcement')}" %>
|
|
54
|
+
<span class="w-16 flex-shrink-0 text-right hidden md:block">cost</span>
|
|
55
|
+
<span class="w-24 flex-shrink-0 text-right">last run</span>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<!-- Data rows -->
|
|
59
|
+
<div class="font-mono text-xs space-y-px">
|
|
60
|
+
<% @tenants.each do |tenant| %>
|
|
61
|
+
<%
|
|
62
|
+
enforcement = tenant.effective_enforcement.to_s
|
|
63
|
+
enforcement_badge = case enforcement
|
|
64
|
+
when "hard" then "badge-error"
|
|
65
|
+
when "soft" then "badge-timeout"
|
|
66
|
+
else ""
|
|
67
|
+
end
|
|
68
|
+
last_execution = tenant.executions.order(created_at: :desc).pick(:created_at)
|
|
69
|
+
%>
|
|
70
|
+
<div class="group flex items-center gap-3 py-1.5 px-2 -mx-2 rounded hover:bg-gray-100 dark:hover:bg-gray-800/50 cursor-pointer"
|
|
71
|
+
onclick="window.location='<%= tenant_path(tenant) %>'">
|
|
72
|
+
<span class="flex-[2] min-w-0 truncate">
|
|
73
|
+
<span class="text-gray-900 dark:text-gray-200"><%= tenant.display_name %></span>
|
|
74
|
+
<% if tenant.name.present? && tenant.name != tenant.tenant_id %>
|
|
75
|
+
<span class="text-gray-400 dark:text-gray-600 ml-1 text-[10px]"><%= tenant.tenant_id %></span>
|
|
76
|
+
<% end %>
|
|
77
|
+
</span>
|
|
78
|
+
<span class="w-20 flex-shrink-0 text-right hidden sm:block">
|
|
79
|
+
<% if tenant.effective_daily_limit %>
|
|
80
|
+
<span class="text-gray-500 dark:text-gray-400">$<%= number_with_precision(tenant.effective_daily_limit, precision: 2) %></span>
|
|
81
|
+
<% else %>
|
|
82
|
+
<span class="text-gray-300 dark:text-gray-700">—</span>
|
|
83
|
+
<% end %>
|
|
84
|
+
</span>
|
|
85
|
+
<span class="w-20 flex-shrink-0 text-right hidden sm:block">
|
|
86
|
+
<% if tenant.effective_monthly_limit %>
|
|
87
|
+
<span class="text-gray-500 dark:text-gray-400">$<%= number_with_precision(tenant.effective_monthly_limit, precision: 2) %></span>
|
|
88
|
+
<% else %>
|
|
89
|
+
<span class="text-gray-300 dark:text-gray-700">—</span>
|
|
90
|
+
<% end %>
|
|
91
|
+
</span>
|
|
92
|
+
<span class="w-16 flex-shrink-0 hidden md:block">
|
|
93
|
+
<% if enforcement_badge.present? %>
|
|
94
|
+
<span class="badge badge-sm <%= enforcement_badge %>"><%= enforcement %></span>
|
|
95
|
+
<% else %>
|
|
96
|
+
<span class="text-gray-300 dark:text-gray-700">none</span>
|
|
97
|
+
<% end %>
|
|
98
|
+
</span>
|
|
99
|
+
<span class="w-16 flex-shrink-0 text-right text-gray-500 dark:text-gray-400 hidden md:inline">$<%= number_with_precision(tenant.cost, precision: 2) %></span>
|
|
100
|
+
<span class="w-24 flex-shrink-0 text-gray-400 dark:text-gray-600 text-right whitespace-nowrap">
|
|
101
|
+
<% if last_execution %>
|
|
102
|
+
<%= time_ago_in_words(last_execution) %>
|
|
103
|
+
<% else %>
|
|
104
|
+
—
|
|
127
105
|
<% end %>
|
|
128
|
-
</
|
|
129
|
-
</
|
|
130
|
-
|
|
106
|
+
</span>
|
|
107
|
+
</div>
|
|
108
|
+
<% end %>
|
|
131
109
|
</div>
|
|
132
110
|
<% end %>
|