ruby_llm-agents 1.3.4 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +112 -336
- data/app/controllers/concerns/ruby_llm/agents/sortable.rb +0 -1
- data/app/controllers/ruby_llm/agents/agents_controller.rb +5 -56
- data/app/controllers/ruby_llm/agents/dashboard_controller.rb +22 -106
- data/app/controllers/ruby_llm/agents/executions_controller.rb +4 -114
- data/app/controllers/ruby_llm/agents/tenants_controller.rb +30 -2
- data/app/helpers/ruby_llm/agents/application_helper.rb +19 -53
- data/app/models/ruby_llm/agents/execution/analytics.rb +13 -54
- data/app/models/ruby_llm/agents/execution/scopes.rb +61 -14
- data/app/models/ruby_llm/agents/execution.rb +52 -12
- data/app/models/ruby_llm/agents/execution_detail.rb +18 -0
- data/app/models/ruby_llm/agents/tenant/budgetable.rb +132 -24
- data/app/models/ruby_llm/agents/tenant/incrementable.rb +117 -0
- data/app/models/ruby_llm/agents/tenant/resettable.rb +128 -0
- data/app/models/ruby_llm/agents/tenant/trackable.rb +46 -12
- data/app/models/ruby_llm/agents/tenant.rb +2 -3
- data/app/models/ruby_llm/agents/tenant_budget.rb +6 -3
- data/app/services/ruby_llm/agents/agent_registry.rb +6 -112
- data/app/views/layouts/ruby_llm/agents/application.html.erb +89 -252
- data/app/views/ruby_llm/agents/agents/_config_agent.html.erb +71 -218
- data/app/views/ruby_llm/agents/agents/_config_embedder.html.erb +20 -63
- data/app/views/ruby_llm/agents/agents/_config_image_generator.html.erb +44 -131
- data/app/views/ruby_llm/agents/agents/_config_moderator.html.erb +16 -57
- data/app/views/ruby_llm/agents/agents/_config_speaker.html.erb +39 -104
- data/app/views/ruby_llm/agents/agents/_config_transcriber.html.erb +29 -82
- data/app/views/ruby_llm/agents/agents/_empty_state.html.erb +4 -14
- data/app/views/ruby_llm/agents/agents/index.html.erb +105 -274
- data/app/views/ruby_llm/agents/agents/show.html.erb +248 -378
- data/app/views/ruby_llm/agents/dashboard/_action_center.html.erb +29 -52
- data/app/views/ruby_llm/agents/dashboard/_tenant_budget.html.erb +73 -99
- data/app/views/ruby_llm/agents/dashboard/index.html.erb +228 -433
- data/app/views/ruby_llm/agents/executions/_execution.html.erb +1 -1
- data/app/views/ruby_llm/agents/executions/_filters.html.erb +4 -25
- data/app/views/ruby_llm/agents/executions/_list.html.erb +111 -152
- data/app/views/ruby_llm/agents/executions/index.html.erb +5 -7
- data/app/views/ruby_llm/agents/executions/show.html.erb +526 -1037
- data/app/views/ruby_llm/agents/shared/_agent_type_badge.html.erb +5 -21
- data/app/views/ruby_llm/agents/shared/_executions_table.html.erb +70 -191
- data/app/views/ruby_llm/agents/shared/_filter_dropdown.html.erb +16 -44
- data/app/views/ruby_llm/agents/shared/_select_dropdown.html.erb +12 -41
- data/app/views/ruby_llm/agents/shared/_status_badge.html.erb +11 -65
- data/app/views/ruby_llm/agents/shared/_tenant_filter.html.erb +6 -5
- data/app/views/ruby_llm/agents/system_config/show.html.erb +240 -351
- data/app/views/ruby_llm/agents/tenants/_form.html.erb +67 -77
- data/app/views/ruby_llm/agents/tenants/edit.html.erb +7 -9
- data/app/views/ruby_llm/agents/tenants/index.html.erb +100 -122
- data/app/views/ruby_llm/agents/tenants/show.html.erb +146 -336
- data/config/routes.rb +0 -13
- data/lib/generators/ruby_llm_agents/install_generator.rb +13 -17
- data/lib/generators/ruby_llm_agents/migrate_structure_generator.rb +2 -12
- data/lib/generators/ruby_llm_agents/restructure_generator.rb +0 -2
- data/lib/generators/ruby_llm_agents/templates/add_usage_counters_to_tenants_migration.rb.tt +37 -0
- data/lib/generators/ruby_llm_agents/templates/agent.rb.tt +1 -2
- data/lib/generators/ruby_llm_agents/templates/application_agent.rb.tt +1 -1
- data/lib/generators/ruby_llm_agents/templates/application_image_pipeline.rb.tt +0 -1
- data/lib/generators/ruby_llm_agents/templates/create_execution_details_migration.rb.tt +27 -0
- data/lib/generators/ruby_llm_agents/templates/create_tenants_migration.rb.tt +25 -0
- data/lib/generators/ruby_llm_agents/templates/image_pipeline.rb.tt +0 -1
- data/lib/generators/ruby_llm_agents/templates/initializer.rb.tt +33 -12
- data/lib/generators/ruby_llm_agents/templates/migration.rb.tt +40 -71
- data/lib/generators/ruby_llm_agents/templates/remove_agent_version_migration.rb.tt +13 -0
- data/lib/generators/ruby_llm_agents/templates/remove_workflow_columns_migration.rb.tt +19 -0
- data/lib/generators/ruby_llm_agents/templates/skills/AGENTS.md.tt +2 -4
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_PIPELINES.md.tt +0 -1
- data/lib/generators/ruby_llm_agents/templates/split_execution_details_migration.rb.tt +232 -0
- data/lib/generators/ruby_llm_agents/upgrade_generator.rb +77 -259
- data/lib/ruby_llm/agents/audio/speaker.rb +0 -1
- data/lib/ruby_llm/agents/audio/transcriber.rb +0 -1
- data/lib/ruby_llm/agents/base_agent.rb +54 -23
- data/lib/ruby_llm/agents/core/base/callbacks.rb +142 -0
- data/lib/ruby_llm/agents/core/base.rb +23 -55
- data/lib/ruby_llm/agents/core/configuration.rb +97 -117
- data/lib/ruby_llm/agents/core/errors.rb +0 -58
- data/lib/ruby_llm/agents/core/instrumentation.rb +157 -110
- data/lib/ruby_llm/agents/core/llm_tenant.rb +8 -7
- data/lib/ruby_llm/agents/core/version.rb +1 -1
- data/lib/ruby_llm/agents/dsl/base.rb +157 -17
- data/lib/ruby_llm/agents/dsl/caching.rb +33 -2
- data/lib/ruby_llm/agents/dsl/reliability.rb +148 -0
- data/lib/ruby_llm/agents/dsl.rb +1 -2
- data/lib/ruby_llm/agents/image/analyzer/execution.rb +1 -2
- data/lib/ruby_llm/agents/image/background_remover/execution.rb +1 -2
- data/lib/ruby_llm/agents/image/concerns/image_operation_dsl.rb +1 -13
- data/lib/ruby_llm/agents/image/concerns/image_operation_execution.rb +2 -2
- data/lib/ruby_llm/agents/image/editor/dsl.rb +0 -14
- data/lib/ruby_llm/agents/image/editor/execution.rb +1 -10
- data/lib/ruby_llm/agents/image/editor.rb +0 -1
- data/lib/ruby_llm/agents/image/generator.rb +0 -21
- data/lib/ruby_llm/agents/image/pipeline/dsl.rb +0 -13
- data/lib/ruby_llm/agents/image/pipeline/execution.rb +0 -1
- data/lib/ruby_llm/agents/image/transformer/dsl.rb +0 -13
- data/lib/ruby_llm/agents/image/transformer/execution.rb +1 -10
- data/lib/ruby_llm/agents/image/transformer.rb +0 -1
- data/lib/ruby_llm/agents/image/upscaler/execution.rb +1 -2
- data/lib/ruby_llm/agents/image/variator/execution.rb +1 -2
- data/lib/ruby_llm/agents/infrastructure/alert_manager.rb +78 -173
- data/lib/ruby_llm/agents/infrastructure/budget/budget_query.rb +66 -2
- data/lib/ruby_llm/agents/infrastructure/budget/spend_recorder.rb +0 -12
- data/lib/ruby_llm/agents/infrastructure/circuit_breaker.rb +10 -13
- data/lib/ruby_llm/agents/infrastructure/execution_logger_job.rb +8 -0
- data/lib/ruby_llm/agents/pipeline/context.rb +0 -1
- data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +28 -4
- data/lib/ruby_llm/agents/pipeline/middleware/cache.rb +3 -10
- data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +88 -55
- data/lib/ruby_llm/agents/pipeline/middleware/tenant.rb +5 -41
- data/lib/ruby_llm/agents/rails/engine.rb +6 -6
- data/lib/ruby_llm/agents/results/base.rb +1 -49
- data/lib/ruby_llm/agents/text/embedder.rb +0 -1
- data/lib/ruby_llm/agents.rb +1 -9
- data/lib/tasks/ruby_llm_agents.rake +34 -0
- metadata +14 -83
- data/app/controllers/ruby_llm/agents/api_configurations_controller.rb +0 -214
- data/app/controllers/ruby_llm/agents/workflows_controller.rb +0 -544
- data/app/mailers/ruby_llm/agents/alert_mailer.rb +0 -84
- data/app/mailers/ruby_llm/agents/application_mailer.rb +0 -28
- data/app/models/ruby_llm/agents/api_configuration.rb +0 -386
- data/app/models/ruby_llm/agents/execution/workflow.rb +0 -170
- data/app/models/ruby_llm/agents/tenant/configurable.rb +0 -135
- data/app/views/ruby_llm/agents/agents/_agent.html.erb +0 -98
- data/app/views/ruby_llm/agents/agents/_version_comparison.html.erb +0 -186
- data/app/views/ruby_llm/agents/agents/_workflow.html.erb +0 -126
- data/app/views/ruby_llm/agents/alert_mailer/alert_notification.html.erb +0 -107
- data/app/views/ruby_llm/agents/alert_mailer/alert_notification.text.erb +0 -18
- data/app/views/ruby_llm/agents/api_configurations/_api_key_field.html.erb +0 -34
- data/app/views/ruby_llm/agents/api_configurations/_form.html.erb +0 -288
- data/app/views/ruby_llm/agents/api_configurations/edit.html.erb +0 -95
- data/app/views/ruby_llm/agents/api_configurations/edit_tenant.html.erb +0 -97
- data/app/views/ruby_llm/agents/api_configurations/show.html.erb +0 -214
- data/app/views/ruby_llm/agents/api_configurations/tenant.html.erb +0 -179
- data/app/views/ruby_llm/agents/dashboard/_agent_comparison.html.erb +0 -73
- data/app/views/ruby_llm/agents/dashboard/_alerts_feed.html.erb +0 -62
- data/app/views/ruby_llm/agents/dashboard/_breaker_strip.html.erb +0 -47
- data/app/views/ruby_llm/agents/dashboard/_budgets_bar.html.erb +0 -75
- data/app/views/ruby_llm/agents/dashboard/_model_comparison.html.erb +0 -56
- data/app/views/ruby_llm/agents/dashboard/_model_cost_breakdown.html.erb +0 -115
- data/app/views/ruby_llm/agents/dashboard/_now_strip.html.erb +0 -59
- data/app/views/ruby_llm/agents/dashboard/_top_errors.html.erb +0 -60
- data/app/views/ruby_llm/agents/executions/_workflow_summary.html.erb +0 -86
- data/app/views/ruby_llm/agents/executions/dry_run.html.erb +0 -149
- data/app/views/ruby_llm/agents/shared/_breadcrumbs.html.erb +0 -48
- data/app/views/ruby_llm/agents/shared/_nav_link.html.erb +0 -27
- data/app/views/ruby_llm/agents/shared/_stat_card.html.erb +0 -14
- data/app/views/ruby_llm/agents/shared/_workflow_type_badge.html.erb +0 -35
- data/app/views/ruby_llm/agents/workflows/_empty_state.html.erb +0 -22
- data/app/views/ruby_llm/agents/workflows/_step_performance.html.erb +0 -228
- data/app/views/ruby_llm/agents/workflows/_structure_dsl.html.erb +0 -539
- data/app/views/ruby_llm/agents/workflows/_structure_parallel.html.erb +0 -76
- data/app/views/ruby_llm/agents/workflows/_structure_pipeline.html.erb +0 -74
- data/app/views/ruby_llm/agents/workflows/_structure_router.html.erb +0 -108
- data/app/views/ruby_llm/agents/workflows/_workflow_diagram.html.erb +0 -920
- data/app/views/ruby_llm/agents/workflows/index.html.erb +0 -179
- data/app/views/ruby_llm/agents/workflows/show.html.erb +0 -467
- data/lib/generators/ruby_llm_agents/api_configuration_generator.rb +0 -100
- data/lib/generators/ruby_llm_agents/templates/add_workflow_migration.rb.tt +0 -38
- data/lib/generators/ruby_llm_agents/templates/application_workflow.rb.tt +0 -48
- data/lib/generators/ruby_llm_agents/templates/create_api_configurations_migration.rb.tt +0 -90
- data/lib/generators/ruby_llm_agents/templates/skills/WORKFLOWS.md.tt +0 -551
- data/lib/ruby_llm/agents/core/base/moderation_dsl.rb +0 -181
- data/lib/ruby_llm/agents/core/base/moderation_execution.rb +0 -274
- data/lib/ruby_llm/agents/core/resolved_config.rb +0 -348
- data/lib/ruby_llm/agents/image/generator/content_policy.rb +0 -95
- data/lib/ruby_llm/agents/infrastructure/redactor.rb +0 -130
- data/lib/ruby_llm/agents/results/moderation_result.rb +0 -158
- data/lib/ruby_llm/agents/text/moderator.rb +0 -237
- data/lib/ruby_llm/agents/workflow/approval.rb +0 -205
- data/lib/ruby_llm/agents/workflow/approval_store.rb +0 -179
- data/lib/ruby_llm/agents/workflow/async.rb +0 -220
- data/lib/ruby_llm/agents/workflow/async_executor.rb +0 -156
- data/lib/ruby_llm/agents/workflow/dsl/executor.rb +0 -467
- data/lib/ruby_llm/agents/workflow/dsl/input_schema.rb +0 -244
- data/lib/ruby_llm/agents/workflow/dsl/iteration_executor.rb +0 -289
- data/lib/ruby_llm/agents/workflow/dsl/parallel_group.rb +0 -107
- data/lib/ruby_llm/agents/workflow/dsl/route_builder.rb +0 -150
- data/lib/ruby_llm/agents/workflow/dsl/schedule_helpers.rb +0 -187
- data/lib/ruby_llm/agents/workflow/dsl/step_config.rb +0 -352
- data/lib/ruby_llm/agents/workflow/dsl/step_executor.rb +0 -415
- data/lib/ruby_llm/agents/workflow/dsl/wait_config.rb +0 -257
- data/lib/ruby_llm/agents/workflow/dsl/wait_executor.rb +0 -317
- data/lib/ruby_llm/agents/workflow/dsl.rb +0 -576
- data/lib/ruby_llm/agents/workflow/instrumentation.rb +0 -249
- data/lib/ruby_llm/agents/workflow/notifiers/base.rb +0 -117
- data/lib/ruby_llm/agents/workflow/notifiers/email.rb +0 -117
- data/lib/ruby_llm/agents/workflow/notifiers/slack.rb +0 -180
- data/lib/ruby_llm/agents/workflow/notifiers/webhook.rb +0 -121
- data/lib/ruby_llm/agents/workflow/notifiers.rb +0 -70
- data/lib/ruby_llm/agents/workflow/orchestrator.rb +0 -416
- data/lib/ruby_llm/agents/workflow/result.rb +0 -592
- data/lib/ruby_llm/agents/workflow/thread_pool.rb +0 -185
- data/lib/ruby_llm/agents/workflow/throttle_manager.rb +0 -206
- data/lib/ruby_llm/agents/workflow/wait_result.rb +0 -213
|
@@ -21,12 +21,6 @@ module RubyLLM
|
|
|
21
21
|
#
|
|
22
22
|
# @api public
|
|
23
23
|
class AgentRegistry
|
|
24
|
-
# Base workflow classes to exclude from listings
|
|
25
|
-
# These are abstract parent classes, not concrete workflows
|
|
26
|
-
BASE_WORKFLOW_CLASSES = [
|
|
27
|
-
"RubyLLM::Agents::Workflow"
|
|
28
|
-
].freeze
|
|
29
|
-
|
|
30
24
|
class << self
|
|
31
25
|
# Returns all unique agent type names
|
|
32
26
|
#
|
|
@@ -66,22 +60,17 @@ module RubyLLM
|
|
|
66
60
|
#
|
|
67
61
|
# @return [Array<String>] Agent class names
|
|
68
62
|
def file_system_agents
|
|
69
|
-
# Ensure all agent
|
|
63
|
+
# Ensure all agent classes are loaded
|
|
70
64
|
eager_load_agents!
|
|
71
65
|
|
|
72
66
|
# Find all descendants of all base classes
|
|
73
67
|
agents = RubyLLM::Agents::Base.descendants.map(&:name).compact
|
|
74
|
-
workflows = RubyLLM::Agents::Workflow.descendants.map(&:name).compact
|
|
75
68
|
embedders = RubyLLM::Agents::Embedder.descendants.map(&:name).compact
|
|
76
|
-
moderators = RubyLLM::Agents::Moderator.descendants.map(&:name).compact
|
|
77
69
|
speakers = RubyLLM::Agents::Speaker.descendants.map(&:name).compact
|
|
78
70
|
transcribers = RubyLLM::Agents::Transcriber.descendants.map(&:name).compact
|
|
79
71
|
image_generators = RubyLLM::Agents::ImageGenerator.descendants.map(&:name).compact
|
|
80
72
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
# Filter out base workflow classes
|
|
84
|
-
all_agents.reject { |name| BASE_WORKFLOW_CLASSES.include?(name) }
|
|
73
|
+
(agents + embedders + speakers + transcribers + image_generators).uniq
|
|
85
74
|
rescue StandardError => e
|
|
86
75
|
Rails.logger.error("[RubyLLM::Agents] Error loading agents from file system: #{e.message}")
|
|
87
76
|
[]
|
|
@@ -97,7 +86,7 @@ module RubyLLM
|
|
|
97
86
|
[]
|
|
98
87
|
end
|
|
99
88
|
|
|
100
|
-
# Eager loads all agent
|
|
89
|
+
# Eager loads all agent files to register descendants
|
|
101
90
|
#
|
|
102
91
|
# Uses the configured autoload paths from RubyLLM::Agents.configuration
|
|
103
92
|
# to ensure agents are discovered in the correct directories.
|
|
@@ -116,28 +105,6 @@ module RubyLLM
|
|
|
116
105
|
end
|
|
117
106
|
end
|
|
118
107
|
|
|
119
|
-
# Returns only regular agents (non-workflows)
|
|
120
|
-
#
|
|
121
|
-
# @return [Array<Hash>] Agent info hashes for non-workflow agents
|
|
122
|
-
def agents_only
|
|
123
|
-
all_with_details.reject { |a| a[:is_workflow] }
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
# Returns only workflows
|
|
127
|
-
#
|
|
128
|
-
# @return [Array<Hash>] Agent info hashes for workflows only
|
|
129
|
-
def workflows_only
|
|
130
|
-
all_with_details.select { |a| a[:is_workflow] }
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
# Returns workflows filtered by type
|
|
134
|
-
#
|
|
135
|
-
# @param type [String, Symbol] The workflow type (pipeline, parallel, router)
|
|
136
|
-
# @return [Array<Hash>] Filtered workflow info hashes
|
|
137
|
-
def workflows_by_type(type)
|
|
138
|
-
workflows_only.select { |w| w[:workflow_type] == type.to_s }
|
|
139
|
-
end
|
|
140
|
-
|
|
141
108
|
# Builds detailed info hash for an agent
|
|
142
109
|
#
|
|
143
110
|
# @param agent_type [String] The agent class name
|
|
@@ -146,27 +113,17 @@ module RubyLLM
|
|
|
146
113
|
agent_class = find(agent_type)
|
|
147
114
|
stats = fetch_stats(agent_type)
|
|
148
115
|
|
|
149
|
-
# Detect the agent type (agent,
|
|
116
|
+
# Detect the agent type (agent, embedder, speaker, transcriber, image_generator)
|
|
150
117
|
detected_type = detect_agent_type(agent_class)
|
|
151
118
|
|
|
152
|
-
# Check if this is a workflow class vs a regular agent
|
|
153
|
-
is_workflow = detected_type == "workflow"
|
|
154
|
-
|
|
155
|
-
# Determine specific workflow type and children
|
|
156
|
-
workflow_type = is_workflow ? detect_workflow_type(agent_class) : nil
|
|
157
|
-
workflow_children = is_workflow ? extract_workflow_children(agent_class) : []
|
|
158
|
-
|
|
159
119
|
{
|
|
160
120
|
name: agent_type,
|
|
161
121
|
class: agent_class,
|
|
162
122
|
active: agent_class.present?,
|
|
163
123
|
agent_type: detected_type,
|
|
164
|
-
is_workflow: is_workflow,
|
|
165
|
-
workflow_type: workflow_type,
|
|
166
|
-
workflow_children: workflow_children,
|
|
167
124
|
version: safe_call(agent_class, :version) || "N/A",
|
|
168
125
|
description: safe_call(agent_class, :description),
|
|
169
|
-
model: safe_call(agent_class, :model) ||
|
|
126
|
+
model: safe_call(agent_class, :model) || "N/A",
|
|
170
127
|
temperature: safe_call(agent_class, :temperature),
|
|
171
128
|
timeout: safe_call(agent_class, :timeout),
|
|
172
129
|
cache_enabled: safe_call(agent_class, :cache_enabled?) || false,
|
|
@@ -216,22 +173,10 @@ module RubyLLM
|
|
|
216
173
|
nil
|
|
217
174
|
end
|
|
218
175
|
|
|
219
|
-
# Detects the specific workflow type from class hierarchy
|
|
220
|
-
#
|
|
221
|
-
# @param agent_class [Class, nil] The agent class
|
|
222
|
-
# @return [String, nil] "workflow" for DSL workflows, or nil
|
|
223
|
-
def detect_workflow_type(agent_class)
|
|
224
|
-
return nil unless agent_class
|
|
225
|
-
|
|
226
|
-
if agent_class.respond_to?(:step_configs) && agent_class.step_configs.any?
|
|
227
|
-
"workflow"
|
|
228
|
-
end
|
|
229
|
-
end
|
|
230
|
-
|
|
231
176
|
# Detects the agent type from class hierarchy
|
|
232
177
|
#
|
|
233
178
|
# @param agent_class [Class, nil] The agent class
|
|
234
|
-
# @return [String] "agent", "
|
|
179
|
+
# @return [String] "agent", "embedder", "speaker", "transcriber", or "image_generator"
|
|
235
180
|
def detect_agent_type(agent_class)
|
|
236
181
|
return "agent" unless agent_class
|
|
237
182
|
|
|
@@ -239,67 +184,16 @@ module RubyLLM
|
|
|
239
184
|
|
|
240
185
|
if ancestors.include?("RubyLLM::Agents::Embedder")
|
|
241
186
|
"embedder"
|
|
242
|
-
elsif ancestors.include?("RubyLLM::Agents::Moderator")
|
|
243
|
-
"moderator"
|
|
244
187
|
elsif ancestors.include?("RubyLLM::Agents::Speaker")
|
|
245
188
|
"speaker"
|
|
246
189
|
elsif ancestors.include?("RubyLLM::Agents::Transcriber")
|
|
247
190
|
"transcriber"
|
|
248
191
|
elsif ancestors.include?("RubyLLM::Agents::ImageGenerator")
|
|
249
192
|
"image_generator"
|
|
250
|
-
elsif ancestors.include?("RubyLLM::Agents::Workflow")
|
|
251
|
-
"workflow"
|
|
252
193
|
else
|
|
253
194
|
"agent"
|
|
254
195
|
end
|
|
255
196
|
end
|
|
256
|
-
|
|
257
|
-
# Extracts child agents from workflow DSL configuration
|
|
258
|
-
#
|
|
259
|
-
# @param agent_class [Class, nil] The workflow class
|
|
260
|
-
# @return [Array<Hash>] Array of child info hashes with :name, :agent, :type, :optional keys
|
|
261
|
-
def extract_workflow_children(agent_class)
|
|
262
|
-
return [] unless agent_class
|
|
263
|
-
|
|
264
|
-
children = []
|
|
265
|
-
|
|
266
|
-
if agent_class.respond_to?(:steps) && agent_class.steps.any?
|
|
267
|
-
# Pipeline workflow - extract steps
|
|
268
|
-
agent_class.steps.each do |name, config|
|
|
269
|
-
children << {
|
|
270
|
-
name: name,
|
|
271
|
-
agent: config[:agent]&.name,
|
|
272
|
-
type: "step",
|
|
273
|
-
optional: config[:continue_on_error] || false
|
|
274
|
-
}
|
|
275
|
-
end
|
|
276
|
-
elsif agent_class.respond_to?(:branches) && agent_class.branches.any?
|
|
277
|
-
# Parallel workflow - extract branches
|
|
278
|
-
agent_class.branches.each do |name, config|
|
|
279
|
-
children << {
|
|
280
|
-
name: name,
|
|
281
|
-
agent: config[:agent]&.name,
|
|
282
|
-
type: "branch",
|
|
283
|
-
optional: config[:optional] || false
|
|
284
|
-
}
|
|
285
|
-
end
|
|
286
|
-
elsif agent_class.respond_to?(:routes) && agent_class.routes.any?
|
|
287
|
-
# Router workflow - extract routes
|
|
288
|
-
agent_class.routes.each do |name, config|
|
|
289
|
-
children << {
|
|
290
|
-
name: name,
|
|
291
|
-
agent: config[:agent]&.name,
|
|
292
|
-
type: "route",
|
|
293
|
-
description: config[:description]
|
|
294
|
-
}
|
|
295
|
-
end
|
|
296
|
-
end
|
|
297
|
-
|
|
298
|
-
children
|
|
299
|
-
rescue StandardError => e
|
|
300
|
-
Rails.logger.error("[RubyLLM::Agents] Error extracting workflow children: #{e.message}")
|
|
301
|
-
[]
|
|
302
|
-
end
|
|
303
197
|
end
|
|
304
198
|
end
|
|
305
199
|
end
|
|
@@ -37,26 +37,26 @@
|
|
|
37
37
|
credits: { enabled: false },
|
|
38
38
|
chart: {
|
|
39
39
|
backgroundColor: 'transparent',
|
|
40
|
-
style: { fontFamily: '
|
|
40
|
+
style: { fontFamily: 'ui-monospace, monospace' }
|
|
41
41
|
},
|
|
42
42
|
title: { text: null },
|
|
43
43
|
xAxis: {
|
|
44
|
-
labels: { style: { color: '#
|
|
45
|
-
lineColor: 'rgba(
|
|
46
|
-
tickColor: 'rgba(
|
|
44
|
+
labels: { style: { color: '#6B7280' } },
|
|
45
|
+
lineColor: 'rgba(107, 114, 128, 0.1)',
|
|
46
|
+
tickColor: 'rgba(107, 114, 128, 0.1)'
|
|
47
47
|
},
|
|
48
48
|
yAxis: {
|
|
49
|
-
labels: { style: { color: '#
|
|
50
|
-
gridLineColor: 'rgba(
|
|
49
|
+
labels: { style: { color: '#6B7280' } },
|
|
50
|
+
gridLineColor: 'rgba(107, 114, 128, 0.1)'
|
|
51
51
|
},
|
|
52
52
|
legend: {
|
|
53
|
-
itemStyle: { color: '#
|
|
54
|
-
itemHoverStyle: { color: '#
|
|
53
|
+
itemStyle: { color: '#6B7280' },
|
|
54
|
+
itemHoverStyle: { color: '#9CA3AF' }
|
|
55
55
|
},
|
|
56
56
|
tooltip: {
|
|
57
|
-
backgroundColor: 'rgba(
|
|
58
|
-
borderColor: '
|
|
59
|
-
style: { color: '#
|
|
57
|
+
backgroundColor: 'rgba(0, 0, 0, 0.85)',
|
|
58
|
+
borderColor: 'transparent',
|
|
59
|
+
style: { color: '#E5E7EB', fontFamily: 'ui-monospace, monospace' }
|
|
60
60
|
}
|
|
61
61
|
});
|
|
62
62
|
</script>
|
|
@@ -71,12 +71,9 @@
|
|
|
71
71
|
|
|
72
72
|
<!-- Clickable rows -->
|
|
73
73
|
<script type="module">
|
|
74
|
-
// Initialize on page load
|
|
75
74
|
document.addEventListener('DOMContentLoaded', function() {
|
|
76
|
-
// Handle data-href clickable rows (semantic alternative to onclick)
|
|
77
75
|
document.querySelectorAll('[data-href]').forEach(function(element) {
|
|
78
76
|
element.addEventListener('click', function(e) {
|
|
79
|
-
// Don't navigate if clicking on a link or button inside the row
|
|
80
77
|
if (e.target.closest('a, button')) return;
|
|
81
78
|
window.location = element.dataset.href;
|
|
82
79
|
});
|
|
@@ -109,291 +106,131 @@
|
|
|
109
106
|
}
|
|
110
107
|
</script>
|
|
111
108
|
|
|
112
|
-
<style>
|
|
113
|
-
/* Custom styles for the dashboard */
|
|
114
|
-
|
|
115
|
-
/* Standard card components */
|
|
116
|
-
.card {
|
|
117
|
-
@apply bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-6 mb-6;
|
|
118
|
-
}
|
|
119
|
-
.card-compact {
|
|
120
|
-
@apply bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-4 mb-4;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
.stat-card {
|
|
124
|
-
@apply bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 p-4 transition-shadow;
|
|
125
|
-
}
|
|
126
|
-
.stat-card:hover {
|
|
127
|
-
@apply shadow-md;
|
|
128
|
-
}
|
|
129
|
-
.stat-value {
|
|
130
|
-
@apply text-2xl font-bold text-gray-900 dark:text-gray-100 mt-2;
|
|
131
|
-
}
|
|
132
|
-
@media (min-width: 1024px) {
|
|
133
|
-
.stat-value {
|
|
134
|
-
font-size: 1.5rem;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
.stat-label {
|
|
138
|
-
@apply text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide font-medium;
|
|
139
|
-
}
|
|
140
|
-
.chart-card {
|
|
141
|
-
@apply bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 p-6;
|
|
142
|
-
}
|
|
143
|
-
.nav-link {
|
|
144
|
-
@apply px-4 py-2 text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md transition-colors;
|
|
145
|
-
}
|
|
146
|
-
.nav-link.active {
|
|
147
|
-
@apply bg-blue-50 dark:bg-blue-900 text-blue-700 dark:text-blue-300;
|
|
148
|
-
}
|
|
109
|
+
<style type="text/tailwindcss">
|
|
149
110
|
.badge {
|
|
150
111
|
@apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium;
|
|
151
112
|
}
|
|
152
113
|
.badge-running {
|
|
153
|
-
@apply bg-blue-100 dark:bg-blue-
|
|
114
|
+
@apply bg-blue-100 dark:bg-blue-500/20 text-blue-700 dark:text-blue-300;
|
|
154
115
|
}
|
|
155
116
|
.badge-success {
|
|
156
|
-
@apply bg-green-100 dark:bg-green-
|
|
117
|
+
@apply bg-green-100 dark:bg-green-500/20 text-green-700 dark:text-green-300;
|
|
157
118
|
}
|
|
158
119
|
.badge-error {
|
|
159
|
-
@apply bg-red-100 dark:bg-red-
|
|
120
|
+
@apply bg-red-100 dark:bg-red-500/20 text-red-700 dark:text-red-300;
|
|
160
121
|
}
|
|
161
122
|
.badge-timeout {
|
|
162
|
-
@apply bg-yellow-100 dark:bg-yellow-
|
|
123
|
+
@apply bg-yellow-100 dark:bg-yellow-500/20 text-yellow-700 dark:text-yellow-300;
|
|
163
124
|
}
|
|
164
125
|
.badge-cyan {
|
|
165
|
-
@apply bg-cyan-100 dark:bg-cyan-
|
|
126
|
+
@apply bg-cyan-100 dark:bg-cyan-500/20 text-cyan-700 dark:text-cyan-300;
|
|
166
127
|
}
|
|
167
128
|
.badge-purple {
|
|
168
|
-
@apply bg-purple-100 dark:bg-purple-
|
|
129
|
+
@apply bg-purple-100 dark:bg-purple-500/20 text-purple-700 dark:text-purple-300;
|
|
169
130
|
}
|
|
170
131
|
.badge-orange {
|
|
171
|
-
@apply bg-orange-100 dark:bg-orange-
|
|
132
|
+
@apply bg-orange-100 dark:bg-orange-500/20 text-orange-700 dark:text-orange-300;
|
|
172
133
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
.badge-pipeline {
|
|
176
|
-
@apply bg-indigo-100 dark:bg-indigo-900/50 text-indigo-700 dark:text-indigo-300;
|
|
177
|
-
}
|
|
178
|
-
.badge-parallel {
|
|
179
|
-
@apply bg-cyan-100 dark:bg-cyan-900/50 text-cyan-700 dark:text-cyan-300;
|
|
180
|
-
}
|
|
181
|
-
.badge-router {
|
|
182
|
-
@apply bg-amber-100 dark:bg-amber-900/50 text-amber-700 dark:text-amber-300;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/* Alpine.js utilities */
|
|
186
|
-
[x-cloak] {
|
|
187
|
-
display: none !important;
|
|
134
|
+
.badge-sm {
|
|
135
|
+
@apply inline-flex items-center px-1.5 py-px rounded-full text-[10px] font-medium;
|
|
188
136
|
}
|
|
189
|
-
|
|
190
|
-
</style>
|
|
137
|
+
</style>
|
|
191
138
|
</head>
|
|
192
139
|
|
|
193
|
-
<body class="bg-gray-50 dark:bg-gray-
|
|
140
|
+
<body class="bg-gray-50 dark:bg-gray-950 min-h-screen flex flex-col font-sans">
|
|
194
141
|
<!-- Header -->
|
|
195
|
-
<header
|
|
196
|
-
class="
|
|
197
|
-
|
|
198
|
-
dark:
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
<
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
<%
|
|
142
|
+
<header class="border-b border-gray-200 dark:border-gray-800" x-data="{ mobileMenuOpen: false }">
|
|
143
|
+
<div class="max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8 h-12 flex items-center">
|
|
144
|
+
<!-- Brand -->
|
|
145
|
+
<%= link_to ruby_llm_agents.root_path, class: "font-mono text-sm font-medium text-gray-900 dark:text-gray-100 hover:text-gray-600 dark:hover:text-gray-400 transition-colors" do %>
|
|
146
|
+
ruby_llm<span class="text-gray-400 dark:text-gray-600">::</span>agents
|
|
147
|
+
<% end %>
|
|
148
|
+
|
|
149
|
+
<!-- Desktop Navigation -->
|
|
150
|
+
<nav class="hidden md:flex items-center ml-8 gap-0.5 font-mono text-xs">
|
|
151
|
+
<% nav_items = [
|
|
152
|
+
[ruby_llm_agents.root_path, "dashboard"],
|
|
153
|
+
[ruby_llm_agents.agents_path, "agents"],
|
|
154
|
+
[ruby_llm_agents.executions_path, "executions"]
|
|
155
|
+
]
|
|
156
|
+
nav_items << [ruby_llm_agents.tenants_path, "tenants"] if tenant_filter_enabled?
|
|
157
|
+
nav_items.each do |path, label| %>
|
|
158
|
+
<% active = current_page?(path) %>
|
|
159
|
+
<%= link_to label, path, class: "px-2.5 py-1 rounded transition-colors #{active ? 'text-gray-900 dark:text-gray-100 bg-gray-100 dark:bg-gray-800' : 'text-gray-400 dark:text-gray-500 hover:text-gray-700 dark:hover:text-gray-300'}" %>
|
|
160
|
+
<% end %>
|
|
161
|
+
</nav>
|
|
212
162
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
]
|
|
222
|
-
%>
|
|
223
|
-
<nav class="hidden md:flex items-center space-x-1">
|
|
224
|
-
<% nav_items.each do |item| %>
|
|
225
|
-
<%= render "ruby_llm/agents/shared/nav_link", path: item[:path], label: item[:label], icon: item[:icon], mobile: false %>
|
|
226
|
-
<% end %>
|
|
227
|
-
</nav>
|
|
228
|
-
</div>
|
|
163
|
+
<!-- Right Side -->
|
|
164
|
+
<div class="ml-auto flex items-center gap-1">
|
|
165
|
+
<%= link_to ruby_llm_agents.system_config_path, class: "hidden md:flex p-1.5 text-gray-400 dark:text-gray-600 hover:text-gray-700 dark:hover:text-gray-300 rounded transition-colors", title: "Config" do %>
|
|
166
|
+
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
167
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
|
|
168
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
|
169
|
+
</svg>
|
|
170
|
+
<% end %>
|
|
229
171
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
"
|
|
172
|
+
<!-- Mobile menu button -->
|
|
173
|
+
<button
|
|
174
|
+
type="button"
|
|
175
|
+
@click="mobileMenuOpen = !mobileMenuOpen"
|
|
176
|
+
:aria-expanded="mobileMenuOpen"
|
|
177
|
+
class="md:hidden p-1.5 text-gray-400 dark:text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 rounded transition-colors"
|
|
235
178
|
>
|
|
236
|
-
|
|
237
|
-
<
|
|
238
|
-
<
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
type="button"
|
|
242
|
-
class="p-2 rounded-md text-gray-500 hover:text-gray-700 hover:bg-gray-100 dark:text-gray-400 dark:hover:text-gray-200 dark:hover:bg-gray-700 transition-colors"
|
|
243
|
-
aria-label="Settings"
|
|
244
|
-
>
|
|
245
|
-
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
246
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
|
|
247
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
|
248
|
-
</svg>
|
|
249
|
-
</button>
|
|
250
|
-
|
|
251
|
-
<div
|
|
252
|
-
x-show="settingsOpen"
|
|
253
|
-
x-cloak
|
|
254
|
-
x-transition:enter="transition ease-out duration-100"
|
|
255
|
-
x-transition:enter-start="opacity-0 scale-95"
|
|
256
|
-
x-transition:enter-end="opacity-100 scale-100"
|
|
257
|
-
x-transition:leave="transition ease-in duration-75"
|
|
258
|
-
x-transition:leave-start="opacity-100 scale-100"
|
|
259
|
-
x-transition:leave-end="opacity-0 scale-95"
|
|
260
|
-
class="absolute right-0 mt-2 w-48 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50 py-1"
|
|
261
|
-
>
|
|
262
|
-
<a href="<%= ruby_llm_agents.system_config_path %>"
|
|
263
|
-
class="flex items-center gap-2 px-4 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 <%= 'bg-gray-100 dark:bg-gray-700' if request.path == ruby_llm_agents.system_config_path %>">
|
|
264
|
-
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
265
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"/>
|
|
266
|
-
</svg>
|
|
267
|
-
System Config
|
|
268
|
-
</a>
|
|
269
|
-
<a href="<%= ruby_llm_agents.api_configuration_path %>"
|
|
270
|
-
class="flex items-center gap-2 px-4 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 <%= 'bg-gray-100 dark:bg-gray-700' if request.path == ruby_llm_agents.api_configuration_path %>">
|
|
271
|
-
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
272
|
-
<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"/>
|
|
273
|
-
</svg>
|
|
274
|
-
API Keys
|
|
275
|
-
</a>
|
|
276
|
-
<div class="border-t border-gray-100 dark:border-gray-700 my-1"></div>
|
|
277
|
-
<a href="#" class="flex items-center gap-2 px-4 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700">
|
|
278
|
-
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
279
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"/>
|
|
280
|
-
</svg>
|
|
281
|
-
Logout
|
|
282
|
-
</a>
|
|
283
|
-
</div>
|
|
284
|
-
</div>
|
|
285
|
-
|
|
286
|
-
<!-- Mobile menu button -->
|
|
287
|
-
<button
|
|
288
|
-
type="button"
|
|
289
|
-
@click="mobileMenuOpen = !mobileMenuOpen"
|
|
290
|
-
:aria-expanded="mobileMenuOpen"
|
|
291
|
-
aria-controls="mobile-menu"
|
|
292
|
-
class="
|
|
293
|
-
md:hidden inline-flex items-center justify-center p-2
|
|
294
|
-
rounded-md text-gray-500 dark:text-gray-400
|
|
295
|
-
hover:text-gray-900 dark:hover:text-gray-100
|
|
296
|
-
hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none
|
|
297
|
-
focus:ring-2 focus:ring-inset focus:ring-blue-500
|
|
298
|
-
"
|
|
299
|
-
>
|
|
300
|
-
<span class="sr-only">Open main menu</span>
|
|
301
|
-
|
|
302
|
-
<svg
|
|
303
|
-
class="w-5 h-5"
|
|
304
|
-
fill="none"
|
|
305
|
-
stroke="currentColor"
|
|
306
|
-
viewBox="0 0 24 24"
|
|
307
|
-
>
|
|
308
|
-
<path
|
|
309
|
-
stroke-linecap="round"
|
|
310
|
-
stroke-linejoin="round"
|
|
311
|
-
stroke-width="2"
|
|
312
|
-
d="M4 6h16M4 12h16M4 18h16"
|
|
313
|
-
/>
|
|
314
|
-
</svg>
|
|
315
|
-
</button>
|
|
316
|
-
</div>
|
|
179
|
+
<span class="sr-only">Open main menu</span>
|
|
180
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
181
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16"/>
|
|
182
|
+
</svg>
|
|
183
|
+
</button>
|
|
317
184
|
</div>
|
|
318
185
|
</div>
|
|
319
186
|
|
|
320
|
-
<!-- Mobile Navigation
|
|
187
|
+
<!-- Mobile Navigation -->
|
|
321
188
|
<div
|
|
322
|
-
id="mobile-menu"
|
|
323
189
|
x-show="mobileMenuOpen"
|
|
324
190
|
x-cloak
|
|
325
|
-
x-transition:enter="transition ease-out duration-
|
|
191
|
+
x-transition:enter="transition ease-out duration-150"
|
|
326
192
|
x-transition:enter-start="opacity-0 -translate-y-1"
|
|
327
193
|
x-transition:enter-end="opacity-100 translate-y-0"
|
|
328
|
-
x-transition:leave="transition ease-in duration-
|
|
194
|
+
x-transition:leave="transition ease-in duration-100"
|
|
329
195
|
x-transition:leave-start="opacity-100 translate-y-0"
|
|
330
196
|
x-transition:leave-end="opacity-0 -translate-y-1"
|
|
331
197
|
@click.outside="mobileMenuOpen = false"
|
|
332
|
-
class="
|
|
333
|
-
md:hidden border-t border-gray-200 dark:border-gray-700 bg-white
|
|
334
|
-
dark:bg-gray-800
|
|
335
|
-
"
|
|
198
|
+
class="md:hidden border-t border-gray-200 dark:border-gray-800"
|
|
336
199
|
>
|
|
337
|
-
<nav class="max-w-7xl mx-auto px-4 py-
|
|
338
|
-
<%
|
|
339
|
-
|
|
200
|
+
<nav class="max-w-7xl mx-auto px-4 py-2 font-mono text-xs space-y-0.5">
|
|
201
|
+
<% mobile_nav_items = [
|
|
202
|
+
[ruby_llm_agents.root_path, "dashboard"],
|
|
203
|
+
[ruby_llm_agents.agents_path, "agents"],
|
|
204
|
+
[ruby_llm_agents.executions_path, "executions"]
|
|
205
|
+
]
|
|
206
|
+
mobile_nav_items << [ruby_llm_agents.tenants_path, "tenants"] if tenant_filter_enabled?
|
|
207
|
+
mobile_nav_items.each do |path, label| %>
|
|
208
|
+
<% active = current_page?(path) %>
|
|
209
|
+
<%= link_to label, path, class: "block px-3 py-2 rounded transition-colors #{active ? 'text-gray-900 dark:text-gray-100 bg-gray-100 dark:bg-gray-800' : 'text-gray-400 dark:text-gray-500 hover:text-gray-700 dark:hover:text-gray-300'}" %>
|
|
340
210
|
<% end %>
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
<div class="border-t border-gray-200 dark:border-gray-700 my-2 pt-2">
|
|
344
|
-
<span class="px-3 text-xs font-semibold text-gray-400 dark:text-gray-500 uppercase tracking-wider">Settings</span>
|
|
345
|
-
</div>
|
|
346
|
-
|
|
347
|
-
<%= render "ruby_llm/agents/shared/nav_link",
|
|
348
|
-
path: ruby_llm_agents.system_config_path,
|
|
349
|
-
label: "System Config",
|
|
350
|
-
icon: '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"/>',
|
|
351
|
-
mobile: true %>
|
|
352
|
-
|
|
353
|
-
<%= render "ruby_llm/agents/shared/nav_link",
|
|
354
|
-
path: ruby_llm_agents.api_configuration_path,
|
|
355
|
-
label: "API Keys",
|
|
356
|
-
icon: '<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"/>',
|
|
357
|
-
mobile: true %>
|
|
358
|
-
|
|
359
|
-
<a href="#" class="flex items-center px-3 py-2 text-sm font-medium text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md">
|
|
360
|
-
<svg class="w-5 h-5 mr-3 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
361
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"/>
|
|
362
|
-
</svg>
|
|
363
|
-
Logout
|
|
364
|
-
</a>
|
|
211
|
+
<div class="border-t border-gray-200 dark:border-gray-800 my-1.5"></div>
|
|
212
|
+
<%= link_to "config", ruby_llm_agents.system_config_path, class: "block px-3 py-2 rounded text-gray-400 dark:text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition-colors" %>
|
|
365
213
|
</nav>
|
|
366
214
|
</div>
|
|
367
215
|
</header>
|
|
368
216
|
|
|
369
217
|
<!-- Main content -->
|
|
370
|
-
<main class="max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8 py-
|
|
218
|
+
<main class="max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8 py-6 flex-1">
|
|
371
219
|
<%= yield %>
|
|
372
220
|
</main>
|
|
373
221
|
|
|
374
222
|
<!-- Footer -->
|
|
375
|
-
<footer
|
|
376
|
-
class="
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
<
|
|
383
|
-
<
|
|
384
|
-
id="theme-select"
|
|
385
|
-
x-model="preference"
|
|
386
|
-
@change="setTheme($event.target.value)"
|
|
387
|
-
class="text-sm border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 py-1 px-2"
|
|
388
|
-
>
|
|
389
|
-
<option value="light">Light</option>
|
|
390
|
-
<option value="dark">Dark</option>
|
|
391
|
-
<option value="auto">Auto</option>
|
|
392
|
-
</select>
|
|
223
|
+
<footer class="border-t border-gray-200 dark:border-gray-800 mt-auto" x-data="themeManager()">
|
|
224
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-3">
|
|
225
|
+
<div class="flex items-center justify-between font-mono text-[10px] text-gray-400 dark:text-gray-600 uppercase tracking-widest">
|
|
226
|
+
<div class="flex items-center gap-1">
|
|
227
|
+
<button @click="setTheme('light')" :class="preference === 'light' && 'text-gray-700 dark:text-gray-300'" class="px-1.5 py-0.5 rounded hover:text-gray-600 dark:hover:text-gray-400 transition-colors">light</button>
|
|
228
|
+
<span class="text-gray-300 dark:text-gray-700">·</span>
|
|
229
|
+
<button @click="setTheme('dark')" :class="preference === 'dark' && 'text-gray-700 dark:text-gray-300'" class="px-1.5 py-0.5 rounded hover:text-gray-600 dark:hover:text-gray-400 transition-colors">dark</button>
|
|
230
|
+
<span class="text-gray-300 dark:text-gray-700">·</span>
|
|
231
|
+
<button @click="setTheme('auto')" :class="preference === 'auto' && 'text-gray-700 dark:text-gray-300'" class="px-1.5 py-0.5 rounded hover:text-gray-600 dark:hover:text-gray-400 transition-colors">auto</button>
|
|
393
232
|
</div>
|
|
394
|
-
<
|
|
395
|
-
Powered by <a href="https://github.com/adham90/ruby_llm-agents" class="text-blue-600 dark:text-blue-400 hover:underline">ruby_llm-agents</a>
|
|
396
|
-
</p>
|
|
233
|
+
<a href="https://github.com/adham90/ruby_llm-agents" class="hover:text-gray-600 dark:hover:text-gray-400 transition-colors normal-case tracking-normal">ruby_llm-agents</a>
|
|
397
234
|
</div>
|
|
398
235
|
</div>
|
|
399
236
|
</footer>
|