ruby_llm-agents 1.0.0 → 1.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/app/controllers/concerns/ruby_llm/agents/paginatable.rb +9 -3
- data/app/controllers/concerns/ruby_llm/agents/sortable.rb +58 -0
- data/app/controllers/ruby_llm/agents/agents_controller.rb +59 -16
- data/app/controllers/ruby_llm/agents/dashboard_controller.rb +144 -20
- data/app/controllers/ruby_llm/agents/executions_controller.rb +13 -16
- data/app/controllers/ruby_llm/agents/workflows_controller.rb +279 -90
- data/app/helpers/ruby_llm/agents/application_helper.rb +100 -0
- data/app/mailers/ruby_llm/agents/alert_mailer.rb +84 -0
- data/app/mailers/ruby_llm/agents/application_mailer.rb +28 -0
- data/app/models/ruby_llm/agents/execution/analytics.rb +170 -20
- data/app/models/ruby_llm/agents/execution/scopes.rb +0 -31
- data/app/models/ruby_llm/agents/execution/workflow.rb +0 -129
- data/app/models/ruby_llm/agents/execution.rb +50 -14
- data/app/services/ruby_llm/agents/agent_registry.rb +18 -12
- data/app/views/layouts/ruby_llm/agents/application.html.erb +72 -76
- data/app/views/ruby_llm/agents/agents/_agent.html.erb +0 -12
- data/app/views/ruby_llm/agents/agents/_sortable_header.html.erb +56 -0
- data/app/views/ruby_llm/agents/agents/_workflow.html.erb +5 -15
- data/app/views/ruby_llm/agents/agents/index.html.erb +271 -100
- data/app/views/ruby_llm/agents/agents/show.html.erb +1 -0
- data/app/views/ruby_llm/agents/alert_mailer/alert_notification.html.erb +107 -0
- data/app/views/ruby_llm/agents/alert_mailer/alert_notification.text.erb +18 -0
- data/app/views/ruby_llm/agents/api_configurations/show.html.erb +4 -1
- data/app/views/ruby_llm/agents/dashboard/_agent_comparison.html.erb +66 -359
- data/app/views/ruby_llm/agents/dashboard/_model_comparison.html.erb +56 -0
- data/app/views/ruby_llm/agents/dashboard/_model_cost_breakdown.html.erb +115 -0
- data/app/views/ruby_llm/agents/dashboard/_now_strip.html.erb +35 -60
- data/app/views/ruby_llm/agents/dashboard/_top_errors.html.erb +17 -6
- data/app/views/ruby_llm/agents/dashboard/index.html.erb +373 -72
- data/app/views/ruby_llm/agents/executions/_execution.html.erb +0 -1
- data/app/views/ruby_llm/agents/executions/_filters.html.erb +51 -39
- data/app/views/ruby_llm/agents/executions/_list.html.erb +53 -195
- data/app/views/ruby_llm/agents/executions/_workflow_summary.html.erb +5 -20
- data/app/views/ruby_llm/agents/executions/index.html.erb +7 -83
- data/app/views/ruby_llm/agents/executions/show.html.erb +10 -20
- data/app/views/ruby_llm/agents/shared/_agent_type_badge.html.erb +2 -1
- data/app/views/ruby_llm/agents/shared/_doc_link.html.erb +12 -0
- data/app/views/ruby_llm/agents/shared/_executions_table.html.erb +3 -15
- data/app/views/ruby_llm/agents/shared/_filter_dropdown.html.erb +1 -1
- data/app/views/ruby_llm/agents/shared/_select_dropdown.html.erb +1 -1
- data/app/views/ruby_llm/agents/shared/_sortable_header.html.erb +53 -0
- data/app/views/ruby_llm/agents/shared/_status_badge.html.erb +7 -0
- data/app/views/ruby_llm/agents/shared/_status_dot.html.erb +1 -1
- data/app/views/ruby_llm/agents/shared/_workflow_type_badge.html.erb +9 -35
- data/app/views/ruby_llm/agents/system_config/show.html.erb +4 -1
- data/app/views/ruby_llm/agents/tenants/index.html.erb +4 -1
- data/app/views/ruby_llm/agents/workflows/_step_performance.html.erb +7 -15
- data/app/views/ruby_llm/agents/workflows/_structure_dsl.html.erb +539 -0
- data/app/views/ruby_llm/agents/workflows/_workflow_diagram.html.erb +920 -0
- data/app/views/ruby_llm/agents/workflows/index.html.erb +179 -0
- data/app/views/ruby_llm/agents/workflows/show.html.erb +164 -139
- data/config/routes.rb +1 -1
- data/lib/generators/ruby_llm_agents/agent_generator.rb +6 -36
- data/lib/generators/ruby_llm_agents/background_remover_generator.rb +7 -37
- data/lib/generators/ruby_llm_agents/embedder_generator.rb +5 -38
- data/lib/generators/ruby_llm_agents/image_analyzer_generator.rb +7 -37
- data/lib/generators/ruby_llm_agents/image_editor_generator.rb +7 -37
- data/lib/generators/ruby_llm_agents/image_generator_generator.rb +8 -41
- data/lib/generators/ruby_llm_agents/image_pipeline_generator.rb +18 -46
- data/lib/generators/ruby_llm_agents/image_transformer_generator.rb +7 -37
- data/lib/generators/ruby_llm_agents/image_upscaler_generator.rb +7 -37
- data/lib/generators/ruby_llm_agents/image_variator_generator.rb +7 -37
- data/lib/generators/ruby_llm_agents/install_generator.rb +33 -56
- data/lib/generators/ruby_llm_agents/migrate_structure_generator.rb +480 -0
- data/lib/generators/ruby_llm_agents/restructure_generator.rb +2 -2
- data/lib/generators/ruby_llm_agents/speaker_generator.rb +8 -39
- data/lib/generators/ruby_llm_agents/templates/agent.rb.tt +5 -8
- data/lib/generators/ruby_llm_agents/templates/application_agent.rb.tt +40 -42
- data/lib/generators/ruby_llm_agents/templates/application_background_remover.rb.tt +20 -22
- data/lib/generators/ruby_llm_agents/templates/application_embedder.rb.tt +24 -26
- data/lib/generators/ruby_llm_agents/templates/application_image_analyzer.rb.tt +20 -22
- data/lib/generators/ruby_llm_agents/templates/application_image_editor.rb.tt +19 -17
- data/lib/generators/ruby_llm_agents/templates/application_image_generator.rb.tt +31 -33
- data/lib/generators/ruby_llm_agents/templates/application_image_pipeline.rb.tt +125 -127
- data/lib/generators/ruby_llm_agents/templates/application_image_transformer.rb.tt +20 -18
- data/lib/generators/ruby_llm_agents/templates/application_image_upscaler.rb.tt +19 -17
- data/lib/generators/ruby_llm_agents/templates/application_image_variator.rb.tt +19 -17
- data/lib/generators/ruby_llm_agents/templates/application_speaker.rb.tt +38 -40
- data/lib/generators/ruby_llm_agents/templates/application_transcriber.rb.tt +42 -44
- data/lib/generators/ruby_llm_agents/templates/application_workflow.rb.tt +48 -0
- data/lib/generators/ruby_llm_agents/templates/background_remover.rb.tt +19 -21
- data/lib/generators/ruby_llm_agents/templates/embedder.rb.tt +19 -21
- data/lib/generators/ruby_llm_agents/templates/image_analyzer.rb.tt +20 -22
- data/lib/generators/ruby_llm_agents/templates/image_editor.rb.tt +15 -17
- data/lib/generators/ruby_llm_agents/templates/image_generator.rb.tt +25 -27
- data/lib/generators/ruby_llm_agents/templates/image_pipeline.rb.tt +19 -21
- data/lib/generators/ruby_llm_agents/templates/image_transformer.rb.tt +20 -22
- data/lib/generators/ruby_llm_agents/templates/image_upscaler.rb.tt +17 -19
- data/lib/generators/ruby_llm_agents/templates/image_variator.rb.tt +15 -17
- data/lib/generators/ruby_llm_agents/templates/skills/AGENTS.md.tt +87 -24
- data/lib/generators/ruby_llm_agents/templates/skills/BACKGROUND_REMOVERS.md.tt +21 -27
- data/lib/generators/ruby_llm_agents/templates/skills/EMBEDDERS.md.tt +46 -54
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_ANALYZERS.md.tt +31 -39
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_EDITORS.md.tt +22 -28
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_GENERATORS.md.tt +53 -63
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_PIPELINES.md.tt +46 -56
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_TRANSFORMERS.md.tt +23 -31
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_UPSCALERS.md.tt +22 -30
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_VARIATORS.md.tt +23 -31
- data/lib/generators/ruby_llm_agents/templates/skills/SPEAKERS.md.tt +38 -46
- data/lib/generators/ruby_llm_agents/templates/skills/TOOLS.md.tt +7 -7
- data/lib/generators/ruby_llm_agents/templates/skills/TRANSCRIBERS.md.tt +59 -71
- data/lib/generators/ruby_llm_agents/templates/skills/WORKFLOWS.md.tt +274 -23
- data/lib/generators/ruby_llm_agents/templates/speaker.rb.tt +29 -31
- data/lib/generators/ruby_llm_agents/templates/transcriber.rb.tt +28 -30
- data/lib/generators/ruby_llm_agents/transcriber_generator.rb +10 -43
- data/lib/ruby_llm/agents/core/configuration.rb +55 -43
- data/lib/ruby_llm/agents/core/version.rb +1 -1
- data/lib/ruby_llm/agents/infrastructure/alert_manager.rb +26 -0
- data/lib/ruby_llm/agents/pipeline.rb +69 -0
- data/lib/ruby_llm/agents/workflow/approval.rb +205 -0
- data/lib/ruby_llm/agents/workflow/approval_store.rb +179 -0
- data/lib/ruby_llm/agents/workflow/dsl/executor.rb +467 -0
- data/lib/ruby_llm/agents/workflow/dsl/input_schema.rb +244 -0
- data/lib/ruby_llm/agents/workflow/dsl/iteration_executor.rb +289 -0
- data/lib/ruby_llm/agents/workflow/dsl/parallel_group.rb +107 -0
- data/lib/ruby_llm/agents/workflow/dsl/route_builder.rb +150 -0
- data/lib/ruby_llm/agents/workflow/dsl/schedule_helpers.rb +187 -0
- data/lib/ruby_llm/agents/workflow/dsl/step_config.rb +352 -0
- data/lib/ruby_llm/agents/workflow/dsl/step_executor.rb +415 -0
- data/lib/ruby_llm/agents/workflow/dsl/wait_config.rb +257 -0
- data/lib/ruby_llm/agents/workflow/dsl/wait_executor.rb +317 -0
- data/lib/ruby_llm/agents/workflow/dsl.rb +576 -0
- data/lib/ruby_llm/agents/workflow/instrumentation.rb +2 -7
- data/lib/ruby_llm/agents/workflow/notifiers/base.rb +117 -0
- data/lib/ruby_llm/agents/workflow/notifiers/email.rb +117 -0
- data/lib/ruby_llm/agents/workflow/notifiers/slack.rb +180 -0
- data/lib/ruby_llm/agents/workflow/notifiers/webhook.rb +121 -0
- data/lib/ruby_llm/agents/workflow/notifiers.rb +70 -0
- data/lib/ruby_llm/agents/workflow/orchestrator.rb +190 -23
- data/lib/ruby_llm/agents/workflow/result.rb +202 -0
- data/lib/ruby_llm/agents/workflow/throttle_manager.rb +206 -0
- data/lib/ruby_llm/agents/workflow/wait_result.rb +213 -0
- metadata +37 -6
- data/app/views/ruby_llm/agents/dashboard/_execution_item.html.erb +0 -66
- data/lib/ruby_llm/agents/workflow/parallel.rb +0 -299
- data/lib/ruby_llm/agents/workflow/pipeline.rb +0 -306
- data/lib/ruby_llm/agents/workflow/router.rb +0 -429
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
<div class="mb-6">
|
|
2
|
+
<div class="flex items-center gap-2">
|
|
3
|
+
<h1 class="text-2xl font-bold text-gray-900 dark:text-gray-100">Workflows</h1>
|
|
4
|
+
<%= render "ruby_llm/agents/shared/doc_link" %>
|
|
5
|
+
</div>
|
|
6
|
+
<p class="text-gray-500 dark:text-gray-400 mt-1">Orchestrated multi-agent workflows with execution statistics</p>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<% if @workflows.empty? %>
|
|
10
|
+
<%= render "ruby_llm/agents/agents/empty_state", type: "workflows" %>
|
|
11
|
+
<% else %>
|
|
12
|
+
<div class="bg-white dark:bg-gray-800 rounded-lg shadow overflow-hidden">
|
|
13
|
+
<div class="overflow-x-auto">
|
|
14
|
+
<table class="w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
15
|
+
<thead class="bg-gray-50 dark:bg-gray-900">
|
|
16
|
+
<tr>
|
|
17
|
+
<%= render "ruby_llm/agents/agents/sortable_header",
|
|
18
|
+
column: "name", label: "Name",
|
|
19
|
+
current_sort: @sort_params[:column], current_direction: @sort_params[:direction] %>
|
|
20
|
+
|
|
21
|
+
<th scope="col" class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400">
|
|
22
|
+
Status
|
|
23
|
+
</th>
|
|
24
|
+
|
|
25
|
+
<th scope="col" class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400 hidden md:table-cell">
|
|
26
|
+
Steps
|
|
27
|
+
</th>
|
|
28
|
+
|
|
29
|
+
<%= render "ruby_llm/agents/agents/sortable_header",
|
|
30
|
+
column: "execution_count", label: "Executions",
|
|
31
|
+
current_sort: @sort_params[:column], current_direction: @sort_params[:direction] %>
|
|
32
|
+
|
|
33
|
+
<%= render "ruby_llm/agents/agents/sortable_header",
|
|
34
|
+
column: "total_cost", label: "Cost",
|
|
35
|
+
current_sort: @sort_params[:column], current_direction: @sort_params[:direction],
|
|
36
|
+
th_class: "hidden md:table-cell" %>
|
|
37
|
+
|
|
38
|
+
<%= render "ruby_llm/agents/agents/sortable_header",
|
|
39
|
+
column: "success_rate", label: "Success",
|
|
40
|
+
current_sort: @sort_params[:column], current_direction: @sort_params[:direction] %>
|
|
41
|
+
|
|
42
|
+
<%= render "ruby_llm/agents/agents/sortable_header",
|
|
43
|
+
column: "last_executed", label: "Last Run",
|
|
44
|
+
current_sort: @sort_params[:column], current_direction: @sort_params[:direction] %>
|
|
45
|
+
</tr>
|
|
46
|
+
</thead>
|
|
47
|
+
|
|
48
|
+
<% @workflows.each_with_index do |workflow, index| %>
|
|
49
|
+
<%
|
|
50
|
+
row_bg = index.even? ? '' : 'bg-gray-50/50 dark:bg-gray-900/30'
|
|
51
|
+
success_rate = workflow[:success_rate] || 0
|
|
52
|
+
steps_count = workflow[:workflow_children]&.size || 0
|
|
53
|
+
has_steps = workflow[:workflow_children].present? && workflow[:workflow_children].any?
|
|
54
|
+
%>
|
|
55
|
+
<tbody x-data="{ expanded: false }" class="<%= index > 0 ? 'border-t border-gray-100/50 dark:border-gray-700/50' : '' %>">
|
|
56
|
+
<tr class="<%= row_bg %> hover:bg-emerald-50/50 dark:hover:bg-emerald-900/20 transition-colors">
|
|
57
|
+
<!-- Name -->
|
|
58
|
+
<td class="px-4 py-3">
|
|
59
|
+
<a href="<%= ruby_llm_agents.workflow_path(ERB::Util.url_encode(workflow[:name])) %>" class="block">
|
|
60
|
+
<div class="flex items-center gap-2">
|
|
61
|
+
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
62
|
+
<% name_parts = workflow[:name].split('::') %>
|
|
63
|
+
<% if name_parts.length > 1 %>
|
|
64
|
+
<span class="text-gray-400 dark:text-gray-500 font-normal"><%= name_parts[0..-2].join('::') %>::</span>
|
|
65
|
+
<% end %>
|
|
66
|
+
<%= name_parts.last %>
|
|
67
|
+
</span>
|
|
68
|
+
<span class="hidden lg:inline text-xs text-gray-400 dark:text-gray-500">v<%= workflow[:version] %></span>
|
|
69
|
+
</div>
|
|
70
|
+
<% if workflow[:description].present? %>
|
|
71
|
+
<p class="text-xs text-gray-500 dark:text-gray-400 truncate max-w-64 mt-0.5" title="<%= workflow[:description] %>">
|
|
72
|
+
<%= workflow[:description] %>
|
|
73
|
+
</p>
|
|
74
|
+
<% end %>
|
|
75
|
+
</a>
|
|
76
|
+
</td>
|
|
77
|
+
|
|
78
|
+
<!-- Status -->
|
|
79
|
+
<td class="px-4 py-3 whitespace-nowrap">
|
|
80
|
+
<% if workflow[:active] %>
|
|
81
|
+
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 dark:bg-green-900/50 text-green-800 dark:text-green-300">
|
|
82
|
+
Active
|
|
83
|
+
</span>
|
|
84
|
+
<% else %>
|
|
85
|
+
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400">
|
|
86
|
+
Deleted
|
|
87
|
+
</span>
|
|
88
|
+
<% end %>
|
|
89
|
+
</td>
|
|
90
|
+
|
|
91
|
+
<!-- Steps -->
|
|
92
|
+
<td class="px-4 py-3 whitespace-nowrap hidden md:table-cell">
|
|
93
|
+
<% if steps_count > 0 %>
|
|
94
|
+
<button
|
|
95
|
+
type="button"
|
|
96
|
+
@click.stop="expanded = !expanded"
|
|
97
|
+
class="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs font-medium bg-emerald-100 dark:bg-emerald-900/50 text-emerald-700 dark:text-emerald-300 hover:bg-emerald-200 dark:hover:bg-emerald-800 transition-colors"
|
|
98
|
+
>
|
|
99
|
+
<%= steps_count %> steps
|
|
100
|
+
<svg class="w-3 h-3 transition-transform duration-200" :class="{ 'rotate-180': expanded }" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
101
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
|
102
|
+
</svg>
|
|
103
|
+
</button>
|
|
104
|
+
<% else %>
|
|
105
|
+
<span class="text-sm text-gray-400 dark:text-gray-500">-</span>
|
|
106
|
+
<% end %>
|
|
107
|
+
</td>
|
|
108
|
+
|
|
109
|
+
<!-- Executions -->
|
|
110
|
+
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700 dark:text-gray-300 font-mono">
|
|
111
|
+
<%= number_with_delimiter(workflow[:execution_count] || 0) %>
|
|
112
|
+
</td>
|
|
113
|
+
|
|
114
|
+
<!-- Cost -->
|
|
115
|
+
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-700 dark:text-gray-300 font-mono hidden md:table-cell">
|
|
116
|
+
$<%= number_with_precision(workflow[:total_cost] || 0, precision: 4) %>
|
|
117
|
+
</td>
|
|
118
|
+
|
|
119
|
+
<!-- Success Rate -->
|
|
120
|
+
<td class="px-4 py-3 whitespace-nowrap">
|
|
121
|
+
<div class="flex items-center gap-1.5">
|
|
122
|
+
<% if success_rate >= 95 %>
|
|
123
|
+
<span class="w-2 h-2 rounded-full bg-green-500"></span>
|
|
124
|
+
<span class="text-sm text-green-600 dark:text-green-400"><%= success_rate %>%</span>
|
|
125
|
+
<% elsif success_rate >= 80 %>
|
|
126
|
+
<span class="w-2 h-2 rounded-full bg-yellow-500"></span>
|
|
127
|
+
<span class="text-sm text-yellow-600 dark:text-yellow-400"><%= success_rate %>%</span>
|
|
128
|
+
<% else %>
|
|
129
|
+
<span class="w-2 h-2 rounded-full bg-red-500"></span>
|
|
130
|
+
<span class="text-sm text-red-600 dark:text-red-400"><%= success_rate %>%</span>
|
|
131
|
+
<% end %>
|
|
132
|
+
</div>
|
|
133
|
+
</td>
|
|
134
|
+
|
|
135
|
+
<!-- Last Executed -->
|
|
136
|
+
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
|
137
|
+
<% if workflow[:last_executed] %>
|
|
138
|
+
<%= time_ago_in_words(workflow[:last_executed]) %> ago
|
|
139
|
+
<% else %>
|
|
140
|
+
<span class="text-gray-400 dark:text-gray-500">Never</span>
|
|
141
|
+
<% end %>
|
|
142
|
+
</td>
|
|
143
|
+
</tr>
|
|
144
|
+
|
|
145
|
+
<!-- Expandable Steps Row -->
|
|
146
|
+
<% if has_steps %>
|
|
147
|
+
<tr
|
|
148
|
+
x-show="expanded"
|
|
149
|
+
x-cloak
|
|
150
|
+
x-transition:enter="transition ease-out duration-200"
|
|
151
|
+
x-transition:enter-start="opacity-0"
|
|
152
|
+
x-transition:enter-end="opacity-100"
|
|
153
|
+
class="bg-gray-50/80 dark:bg-gray-900/50"
|
|
154
|
+
>
|
|
155
|
+
<td colspan="7" class="px-4 py-3">
|
|
156
|
+
<div class="flex flex-wrap gap-2">
|
|
157
|
+
<% workflow[:workflow_children].each_with_index do |child, step_index| %>
|
|
158
|
+
<div class="inline-flex items-center gap-1.5 px-2 py-1 rounded bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 text-xs">
|
|
159
|
+
<span class="w-4 h-4 flex items-center justify-center rounded-full bg-emerald-100 dark:bg-emerald-900/50 text-emerald-600 dark:text-emerald-300 text-[10px] font-medium">
|
|
160
|
+
<%= step_index + 1 %>
|
|
161
|
+
</span>
|
|
162
|
+
<span class="font-medium text-gray-700 dark:text-gray-300"><%= child[:name] %></span>
|
|
163
|
+
<span class="text-gray-400 dark:text-gray-500">-></span>
|
|
164
|
+
<span class="text-gray-500 dark:text-gray-400 font-mono"><%= child[:agent] %></span>
|
|
165
|
+
<% if child[:optional] %>
|
|
166
|
+
<span class="text-gray-400 dark:text-gray-500 italic">(optional)</span>
|
|
167
|
+
<% end %>
|
|
168
|
+
</div>
|
|
169
|
+
<% end %>
|
|
170
|
+
</div>
|
|
171
|
+
</td>
|
|
172
|
+
</tr>
|
|
173
|
+
<% end %>
|
|
174
|
+
</tbody>
|
|
175
|
+
<% end %>
|
|
176
|
+
</table>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
<% end %>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<%= render "ruby_llm/agents/shared/breadcrumbs", items: [
|
|
2
2
|
{ label: "Dashboard", path: ruby_llm_agents.root_path },
|
|
3
3
|
{ label: "Workflows", path: ruby_llm_agents.agents_path(tab: "workflows") },
|
|
4
|
-
{ label: @workflow_type.
|
|
4
|
+
{ label: @workflow_type.split('::').last }
|
|
5
5
|
] %>
|
|
6
6
|
|
|
7
7
|
<!-- Header -->
|
|
@@ -11,12 +11,13 @@
|
|
|
11
11
|
<div class="flex items-center space-x-3">
|
|
12
12
|
<%= render "ruby_llm/agents/shared/workflow_type_badge", workflow_type: @workflow_type_kind, size: :md %>
|
|
13
13
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-gray-100">
|
|
14
|
-
<% name_parts = @workflow_type.
|
|
14
|
+
<% name_parts = @workflow_type.split('::') %>
|
|
15
15
|
<% if name_parts.length > 1 %>
|
|
16
16
|
<span class="text-gray-400 dark:text-gray-500 font-normal"><%= name_parts[0..-2].join('::') %>::</span>
|
|
17
17
|
<% end %>
|
|
18
18
|
<%= name_parts.last %>
|
|
19
19
|
</h1>
|
|
20
|
+
<%= render "ruby_llm/agents/shared/doc_link" %>
|
|
20
21
|
|
|
21
22
|
<% if @workflow_active %>
|
|
22
23
|
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 dark:bg-green-900/50 text-green-800 dark:text-green-300">
|
|
@@ -40,32 +41,6 @@
|
|
|
40
41
|
<%= @config[:description] %>
|
|
41
42
|
</p>
|
|
42
43
|
<% end %>
|
|
43
|
-
|
|
44
|
-
<!-- Workflow Summary Line -->
|
|
45
|
-
<div class="flex items-center gap-4 mt-3 text-sm text-gray-500 dark:text-gray-400">
|
|
46
|
-
<% if @workflow_type_kind == "pipeline" && @steps.present? %>
|
|
47
|
-
<span><%= @steps.size %> steps</span>
|
|
48
|
-
<% elsif @workflow_type_kind == "parallel" && @branches.present? %>
|
|
49
|
-
<span><%= @branches.size %> branches</span>
|
|
50
|
-
<% elsif @workflow_type_kind == "router" && @routes.present? %>
|
|
51
|
-
<span><%= @routes.size %> routes</span>
|
|
52
|
-
<% end %>
|
|
53
|
-
|
|
54
|
-
<% if @config && @config[:timeout] %>
|
|
55
|
-
<span class="text-gray-300 dark:text-gray-600">|</span>
|
|
56
|
-
<span>timeout <%= @config[:timeout] %>s</span>
|
|
57
|
-
<% end %>
|
|
58
|
-
|
|
59
|
-
<% if @config && @config[:max_cost] %>
|
|
60
|
-
<span class="text-gray-300 dark:text-gray-600">|</span>
|
|
61
|
-
<span>max cost $<%= @config[:max_cost] %></span>
|
|
62
|
-
<% end %>
|
|
63
|
-
|
|
64
|
-
<% if @workflow_type_kind == "router" && @config && @config[:classifier_model] %>
|
|
65
|
-
<span class="text-gray-300 dark:text-gray-600">|</span>
|
|
66
|
-
<span>classifier: <%= @config[:classifier_model] %></span>
|
|
67
|
-
<% end %>
|
|
68
|
-
</div>
|
|
69
44
|
</div>
|
|
70
45
|
|
|
71
46
|
<div class="text-right">
|
|
@@ -94,43 +69,160 @@
|
|
|
94
69
|
</div>
|
|
95
70
|
</div>
|
|
96
71
|
|
|
72
|
+
<!-- Workflow Details Panel -->
|
|
73
|
+
<% if @config %>
|
|
74
|
+
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4 mb-6">
|
|
75
|
+
<div class="flex flex-wrap items-center gap-x-6 gap-y-2">
|
|
76
|
+
<!-- Structure Summary -->
|
|
77
|
+
<div class="flex items-center gap-4 text-sm">
|
|
78
|
+
<% if @steps.present? %>
|
|
79
|
+
<span class="text-gray-700 dark:text-gray-300">
|
|
80
|
+
<span class="font-medium"><%= @steps.size %></span>
|
|
81
|
+
<span class="text-gray-500 dark:text-gray-400">steps</span>
|
|
82
|
+
</span>
|
|
83
|
+
<% end %>
|
|
84
|
+
<% if @parallel_groups.present? && @parallel_groups.any? %>
|
|
85
|
+
<span class="text-purple-600 dark:text-purple-400">
|
|
86
|
+
<span class="font-medium"><%= @parallel_groups.size %></span> parallel
|
|
87
|
+
</span>
|
|
88
|
+
<% end %>
|
|
89
|
+
<% if @config[:has_routing] %>
|
|
90
|
+
<span class="text-amber-600 dark:text-amber-400">routing</span>
|
|
91
|
+
<% end %>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<!-- Divider -->
|
|
95
|
+
<span class="hidden sm:block w-px h-5 bg-gray-200 dark:bg-gray-700"></span>
|
|
96
|
+
|
|
97
|
+
<!-- Constraints -->
|
|
98
|
+
<div class="flex items-center gap-4 text-sm text-gray-600 dark:text-gray-400">
|
|
99
|
+
<% if @config[:timeout] %>
|
|
100
|
+
<span class="inline-flex items-center gap-1">
|
|
101
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
|
102
|
+
<%= @config[:timeout] %>s timeout
|
|
103
|
+
</span>
|
|
104
|
+
<% end %>
|
|
105
|
+
<% if @config[:max_cost] %>
|
|
106
|
+
<span class="inline-flex items-center gap-1">
|
|
107
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1"/></svg>
|
|
108
|
+
$<%= @config[:max_cost] %> max
|
|
109
|
+
</span>
|
|
110
|
+
<% end %>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<!-- Divider -->
|
|
114
|
+
<% if @config[:has_lifecycle_hooks] || @config[:has_conditions] || @config[:has_retries] || @config[:has_fallbacks] || @config[:has_input_schema] %>
|
|
115
|
+
<span class="hidden sm:block w-px h-5 bg-gray-200 dark:bg-gray-700"></span>
|
|
116
|
+
<% end %>
|
|
117
|
+
|
|
118
|
+
<!-- Features -->
|
|
119
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
120
|
+
<% if @config[:has_input_schema] %>
|
|
121
|
+
<span class="text-xs px-2 py-1 rounded-full bg-green-100 dark:bg-green-900/50 text-green-700 dark:text-green-300">
|
|
122
|
+
Input Schema
|
|
123
|
+
</span>
|
|
124
|
+
<% end %>
|
|
125
|
+
<% if @config[:has_lifecycle_hooks] %>
|
|
126
|
+
<span class="text-xs px-2 py-1 rounded-full bg-emerald-100 dark:bg-emerald-900/50 text-emerald-700 dark:text-emerald-300">
|
|
127
|
+
Lifecycle Hooks
|
|
128
|
+
</span>
|
|
129
|
+
<% end %>
|
|
130
|
+
<% if @config[:has_conditions] %>
|
|
131
|
+
<span class="text-xs px-2 py-1 rounded-full bg-cyan-100 dark:bg-cyan-900/50 text-cyan-700 dark:text-cyan-300">
|
|
132
|
+
Conditional
|
|
133
|
+
</span>
|
|
134
|
+
<% end %>
|
|
135
|
+
<% if @config[:has_retries] %>
|
|
136
|
+
<span class="text-xs px-2 py-1 rounded-full bg-orange-100 dark:bg-orange-900/50 text-orange-700 dark:text-orange-300">
|
|
137
|
+
Retries
|
|
138
|
+
</span>
|
|
139
|
+
<% end %>
|
|
140
|
+
<% if @config[:has_fallbacks] %>
|
|
141
|
+
<span class="text-xs px-2 py-1 rounded-full bg-yellow-100 dark:bg-yellow-900/50 text-yellow-700 dark:text-yellow-300">
|
|
142
|
+
Fallbacks
|
|
143
|
+
</span>
|
|
144
|
+
<% end %>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
<% end %>
|
|
149
|
+
|
|
97
150
|
<!-- Workflow Structure -->
|
|
98
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 mb-6">
|
|
151
|
+
<div id="structure" class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 mb-6 scroll-mt-16" x-data="{ expanded: false }">
|
|
99
152
|
<div class="flex items-center justify-between mb-4">
|
|
100
153
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100">
|
|
101
154
|
Workflow Structure
|
|
102
155
|
</h3>
|
|
103
156
|
|
|
104
|
-
|
|
105
|
-
<span
|
|
106
|
-
|
|
157
|
+
<button @click="expanded = !expanded" class="inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors cursor-pointer">
|
|
158
|
+
<span x-text="expanded ? 'Collapse' : 'Expand'">Collapse</span>
|
|
159
|
+
<svg class="w-4 h-4 transition-transform duration-200" :class="expanded ? 'rotate-180' : ''" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
160
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
|
161
|
+
</svg>
|
|
162
|
+
</button>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<!-- Collapsed: Compact Flow Summary -->
|
|
166
|
+
<div x-show="!expanded" x-collapse class="py-4">
|
|
167
|
+
<div class="flex items-center justify-center gap-2 flex-wrap">
|
|
168
|
+
<!-- Start -->
|
|
169
|
+
<span class="px-3 py-1 rounded-full bg-green-100 dark:bg-green-900/50 text-green-700 dark:text-green-300 text-sm font-medium">
|
|
170
|
+
Start
|
|
107
171
|
</span>
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
172
|
+
|
|
173
|
+
<% (@steps || []).each_with_index do |step, idx| %>
|
|
174
|
+
<svg class="w-4 h-4 text-gray-400 dark:text-gray-500 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
175
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
|
176
|
+
</svg>
|
|
177
|
+
|
|
178
|
+
<%
|
|
179
|
+
step_bg = if step[:workflow]
|
|
180
|
+
"bg-emerald-100 dark:bg-emerald-900/50 text-emerald-700 dark:text-emerald-300"
|
|
181
|
+
elsif step[:iteration]
|
|
182
|
+
"bg-blue-100 dark:bg-blue-900/50 text-blue-700 dark:text-blue-300"
|
|
183
|
+
elsif step[:routing]
|
|
184
|
+
"bg-amber-100 dark:bg-amber-900/50 text-amber-700 dark:text-amber-300"
|
|
185
|
+
elsif step[:parallel_group]
|
|
186
|
+
"bg-purple-100 dark:bg-purple-900/50 text-purple-700 dark:text-purple-300"
|
|
187
|
+
else
|
|
188
|
+
"bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300"
|
|
189
|
+
end
|
|
190
|
+
%>
|
|
191
|
+
<span class="px-3 py-1 rounded-full <%= step_bg %> text-sm font-medium">
|
|
192
|
+
<%= step[:name].to_s.titleize.truncate(20) %>
|
|
193
|
+
</span>
|
|
194
|
+
<% end %>
|
|
195
|
+
|
|
196
|
+
<svg class="w-4 h-4 text-gray-400 dark:text-gray-500 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
197
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
|
198
|
+
</svg>
|
|
199
|
+
|
|
200
|
+
<!-- End -->
|
|
201
|
+
<span class="px-3 py-1 rounded-full bg-red-100 dark:bg-red-900/50 text-red-700 dark:text-red-300 text-sm font-medium">
|
|
202
|
+
End
|
|
111
203
|
</span>
|
|
112
|
-
|
|
113
|
-
</div>
|
|
204
|
+
</div>
|
|
114
205
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
<%= render "ruby_llm/agents/workflows/structure_pipeline", steps: @steps || [] %>
|
|
118
|
-
<% when "parallel" %>
|
|
119
|
-
<%= render "ruby_llm/agents/workflows/structure_parallel", branches: @branches || [] %>
|
|
120
|
-
<% when "router" %>
|
|
121
|
-
<%= render "ruby_llm/agents/workflows/structure_router", routes: @routes || [] %>
|
|
122
|
-
<% else %>
|
|
123
|
-
<p class="text-gray-500 dark:text-gray-400 italic">
|
|
124
|
-
Workflow structure unavailable
|
|
206
|
+
<p class="text-center text-xs text-gray-500 dark:text-gray-400 mt-3">
|
|
207
|
+
Click "Expand" to see full diagram with details
|
|
125
208
|
</p>
|
|
126
|
-
|
|
209
|
+
</div>
|
|
210
|
+
|
|
211
|
+
<!-- Expanded: Full Diagram -->
|
|
212
|
+
<div x-show="expanded" x-collapse>
|
|
213
|
+
<%= render "ruby_llm/agents/workflows/workflow_diagram",
|
|
214
|
+
steps: @steps || [],
|
|
215
|
+
parallel_groups: @parallel_groups || [],
|
|
216
|
+
input_schema_fields: @input_schema_fields || {},
|
|
217
|
+
lifecycle_hooks: @lifecycle_hooks || {} %>
|
|
218
|
+
</div>
|
|
127
219
|
</div>
|
|
128
220
|
|
|
129
221
|
<!-- Stats Grid -->
|
|
130
222
|
<% success_rate = @stats[:success_rate] || 0 %>
|
|
131
223
|
<% success_rate_color = success_rate >= 95 ? 'text-green-600' : success_rate >= 80 ? 'text-yellow-600' : 'text-red-600' %>
|
|
132
224
|
|
|
133
|
-
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4 mb-6">
|
|
225
|
+
<div id="stats" class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4 mb-6 scroll-mt-16">
|
|
134
226
|
<%= render "ruby_llm/agents/shared/stat_card",
|
|
135
227
|
title: "Executions",
|
|
136
228
|
value: number_with_delimiter(@stats[:count]),
|
|
@@ -167,30 +259,33 @@
|
|
|
167
259
|
icon_color: "text-purple-500" %>
|
|
168
260
|
</div>
|
|
169
261
|
|
|
170
|
-
<!-- Step
|
|
262
|
+
<!-- Step Performance -->
|
|
171
263
|
<% if @step_stats.present? %>
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
264
|
+
<div id="performance" class="scroll-mt-16">
|
|
265
|
+
<%= render "ruby_llm/agents/workflows/step_performance",
|
|
266
|
+
workflow_type: @workflow_type_kind,
|
|
267
|
+
step_stats: @step_stats %>
|
|
268
|
+
</div>
|
|
176
269
|
<% end %>
|
|
177
270
|
|
|
178
271
|
<!-- Charts Section -->
|
|
179
|
-
<div class="
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
Executions (30 days)
|
|
184
|
-
</h3>
|
|
185
|
-
<div id="executions-chart" style="height: 220px;"></div>
|
|
186
|
-
</div>
|
|
272
|
+
<div id="trends" class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 mb-6 scroll-mt-16">
|
|
273
|
+
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
|
|
274
|
+
Trends (30 days)
|
|
275
|
+
</h3>
|
|
187
276
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
<
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
277
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
278
|
+
<!-- Executions Over Time -->
|
|
279
|
+
<div>
|
|
280
|
+
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">Executions</h4>
|
|
281
|
+
<div id="executions-chart" style="height: 200px;"></div>
|
|
282
|
+
</div>
|
|
283
|
+
|
|
284
|
+
<!-- Cost Over Time -->
|
|
285
|
+
<div>
|
|
286
|
+
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">Cost</h4>
|
|
287
|
+
<div id="cost-chart" style="height: 200px;"></div>
|
|
288
|
+
</div>
|
|
194
289
|
</div>
|
|
195
290
|
</div>
|
|
196
291
|
|
|
@@ -258,79 +353,9 @@
|
|
|
258
353
|
</div>
|
|
259
354
|
<% end %>
|
|
260
355
|
|
|
261
|
-
<% if @config %>
|
|
262
|
-
<!-- Configuration -->
|
|
263
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 mb-6">
|
|
264
|
-
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
|
|
265
|
-
Configuration
|
|
266
|
-
</h3>
|
|
267
|
-
|
|
268
|
-
<!-- Basic Configuration -->
|
|
269
|
-
<p class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-3">
|
|
270
|
-
Basic
|
|
271
|
-
</p>
|
|
272
|
-
|
|
273
|
-
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
|
|
274
|
-
<div>
|
|
275
|
-
<p class="text-sm text-gray-500 dark:text-gray-400">Version</p>
|
|
276
|
-
<p class="font-medium text-gray-900 dark:text-gray-100">
|
|
277
|
-
<%= @config[:version] || 'N/A' %>
|
|
278
|
-
</p>
|
|
279
|
-
</div>
|
|
280
|
-
|
|
281
|
-
<div>
|
|
282
|
-
<p class="text-sm text-gray-500 dark:text-gray-400">Timeout</p>
|
|
283
|
-
<p class="font-medium text-gray-900 dark:text-gray-100">
|
|
284
|
-
<%= @config[:timeout] ? "#{@config[:timeout]}s" : 'N/A' %>
|
|
285
|
-
</p>
|
|
286
|
-
</div>
|
|
287
|
-
|
|
288
|
-
<div>
|
|
289
|
-
<p class="text-sm text-gray-500 dark:text-gray-400">Max Cost</p>
|
|
290
|
-
<p class="font-medium text-gray-900 dark:text-gray-100">
|
|
291
|
-
<%= @config[:max_cost] ? "$#{@config[:max_cost]}" : 'N/A' %>
|
|
292
|
-
</p>
|
|
293
|
-
</div>
|
|
294
|
-
|
|
295
|
-
<% if @workflow_type_kind == "parallel" %>
|
|
296
|
-
<div>
|
|
297
|
-
<p class="text-sm text-gray-500 dark:text-gray-400">Fail Fast</p>
|
|
298
|
-
<p class="font-medium text-gray-900 dark:text-gray-100">
|
|
299
|
-
<%= @config[:fail_fast] ? 'Yes' : 'No' %>
|
|
300
|
-
</p>
|
|
301
|
-
</div>
|
|
302
|
-
<% end %>
|
|
303
|
-
</div>
|
|
304
|
-
|
|
305
|
-
<% if @workflow_type_kind == "router" && @config[:classifier_model] %>
|
|
306
|
-
<!-- Router Settings -->
|
|
307
|
-
<div class="border-t border-gray-100 dark:border-gray-700 pt-4">
|
|
308
|
-
<p class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-3">
|
|
309
|
-
Router Settings
|
|
310
|
-
</p>
|
|
311
|
-
|
|
312
|
-
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
313
|
-
<div>
|
|
314
|
-
<p class="text-sm text-gray-500 dark:text-gray-400">Classifier Model</p>
|
|
315
|
-
<p class="font-medium text-gray-900 dark:text-gray-100">
|
|
316
|
-
<%= @config[:classifier_model] %>
|
|
317
|
-
</p>
|
|
318
|
-
</div>
|
|
319
|
-
|
|
320
|
-
<div>
|
|
321
|
-
<p class="text-sm text-gray-500 dark:text-gray-400">Temperature</p>
|
|
322
|
-
<p class="font-medium text-gray-900 dark:text-gray-100">
|
|
323
|
-
<%= @config[:classifier_temperature] || 0.0 %>
|
|
324
|
-
</p>
|
|
325
|
-
</div>
|
|
326
|
-
</div>
|
|
327
|
-
</div>
|
|
328
|
-
<% end %>
|
|
329
|
-
</div>
|
|
330
|
-
<% end %>
|
|
331
356
|
|
|
332
357
|
<!-- Executions -->
|
|
333
|
-
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
|
358
|
+
<div id="executions" class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 scroll-mt-16">
|
|
334
359
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
|
|
335
360
|
Executions
|
|
336
361
|
</h3>
|
data/config/routes.rb
CHANGED
|
@@ -5,7 +5,7 @@ RubyLLM::Agents::Engine.routes.draw do
|
|
|
5
5
|
get "chart_data", to: "dashboard#chart_data"
|
|
6
6
|
|
|
7
7
|
resources :agents, only: [:index, :show]
|
|
8
|
-
resources :workflows, only: [:show]
|
|
8
|
+
resources :workflows, only: [:index, :show]
|
|
9
9
|
|
|
10
10
|
resources :executions, only: [:index, :show] do
|
|
11
11
|
collection do
|
|
@@ -7,10 +7,9 @@ module RubyLlmAgents
|
|
|
7
7
|
#
|
|
8
8
|
# Usage:
|
|
9
9
|
# rails generate ruby_llm_agents:agent SearchIntent query:required limit:10
|
|
10
|
-
# rails generate ruby_llm_agents:agent SearchIntent query:required --root=ai
|
|
11
10
|
#
|
|
12
11
|
# This will create:
|
|
13
|
-
# - app/
|
|
12
|
+
# - app/agents/search_intent_agent.rb
|
|
14
13
|
#
|
|
15
14
|
# Parameter syntax:
|
|
16
15
|
# name - Optional parameter
|
|
@@ -28,18 +27,9 @@ module RubyLlmAgents
|
|
|
28
27
|
desc: "The temperature setting (0.0-1.0)"
|
|
29
28
|
class_option :cache, type: :string, default: nil,
|
|
30
29
|
desc: "Cache TTL (e.g., '1.hour', '30.minutes')"
|
|
31
|
-
class_option :root,
|
|
32
|
-
type: :string,
|
|
33
|
-
default: nil,
|
|
34
|
-
desc: "Root directory name (default: uses config or 'llm')"
|
|
35
|
-
class_option :namespace,
|
|
36
|
-
type: :string,
|
|
37
|
-
default: nil,
|
|
38
|
-
desc: "Root namespace (default: camelized root or config)"
|
|
39
30
|
|
|
40
31
|
def ensure_base_class_and_skill_file
|
|
41
|
-
|
|
42
|
-
agents_dir = "app/#{root_directory}/agents"
|
|
32
|
+
agents_dir = "app/agents"
|
|
43
33
|
|
|
44
34
|
# Create directory if needed
|
|
45
35
|
empty_directory agents_dir
|
|
@@ -58,17 +48,15 @@ module RubyLlmAgents
|
|
|
58
48
|
end
|
|
59
49
|
|
|
60
50
|
def create_agent_file
|
|
61
|
-
# Support nested paths: "chat/support" -> "app/
|
|
62
|
-
# Rails' class_name handles namespacing: "chat/support" -> "Chat::Support"
|
|
63
|
-
@root_namespace = root_namespace
|
|
51
|
+
# Support nested paths: "chat/support" -> "app/agents/chat/support_agent.rb"
|
|
64
52
|
agent_path = name.underscore
|
|
65
|
-
template "agent.rb.tt", "app
|
|
53
|
+
template "agent.rb.tt", "app/agents/#{agent_path}_agent.rb"
|
|
66
54
|
end
|
|
67
55
|
|
|
68
56
|
def show_usage
|
|
69
|
-
# Build full class name from path (e.g., "chat/support" -> "Chat::
|
|
57
|
+
# Build full class name from path (e.g., "chat/support" -> "Chat::SupportAgent")
|
|
70
58
|
agent_class_name = name.split("/").map(&:camelize).join("::")
|
|
71
|
-
full_class_name = "#{
|
|
59
|
+
full_class_name = "#{agent_class_name}Agent"
|
|
72
60
|
say ""
|
|
73
61
|
say "Agent #{full_class_name} created!", :green
|
|
74
62
|
say ""
|
|
@@ -80,24 +68,6 @@ module RubyLlmAgents
|
|
|
80
68
|
|
|
81
69
|
private
|
|
82
70
|
|
|
83
|
-
def root_directory
|
|
84
|
-
@root_directory ||= options[:root] || RubyLLM::Agents.configuration.root_directory
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def root_namespace
|
|
88
|
-
@root_namespace ||= options[:namespace] || camelize(root_directory)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def camelize(str)
|
|
92
|
-
# Handle special cases for common abbreviations
|
|
93
|
-
return "AI" if str.downcase == "ai"
|
|
94
|
-
return "ML" if str.downcase == "ml"
|
|
95
|
-
return "LLM" if str.downcase == "llm"
|
|
96
|
-
|
|
97
|
-
# Standard camelization
|
|
98
|
-
str.split(/[-_]/).map(&:capitalize).join
|
|
99
|
-
end
|
|
100
|
-
|
|
101
71
|
def parsed_params
|
|
102
72
|
@parsed_params ||= params.map do |param|
|
|
103
73
|
name, modifier = param.split(":")
|