meta_workflows 0.7.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 +7 -0
- data/CHANGELOG.md +36 -0
- data/README.md +498 -0
- data/Rakefile +10 -0
- data/app/assets/javascripts/meta_workflows/controllers/loading_phrases_controller.js +31 -0
- data/app/assets/javascripts/meta_workflows/controllers/redirect_controller.js +15 -0
- data/app/assets/javascripts/meta_workflows/controllers/response_scroll_controller.js +67 -0
- data/app/assets/javascripts/meta_workflows_manifest.js +13 -0
- data/app/assets/stylesheets/meta_workflows/application.css +161 -0
- data/app/controllers/meta_workflows/application_controller.rb +10 -0
- data/app/controllers/meta_workflows/debug_controller.rb +101 -0
- data/app/controllers/meta_workflows/humans_controller.rb +96 -0
- data/app/controllers/meta_workflows/meta_controller.rb +21 -0
- data/app/helpers/meta_workflows/application_helper.rb +7 -0
- data/app/helpers/meta_workflows/debug_helper.rb +54 -0
- data/app/helpers/meta_workflows/execution_helper.rb +77 -0
- data/app/helpers/meta_workflows/formatting_helper.rb +30 -0
- data/app/helpers/meta_workflows/meta_workflows_helper.rb +41 -0
- data/app/helpers/meta_workflows/status_badge_helper.rb +66 -0
- data/app/jobs/meta_workflows/application_job.rb +14 -0
- data/app/jobs/meta_workflows/human_input_job.rb +35 -0
- data/app/jobs/meta_workflows/meta_job.rb +121 -0
- data/app/jobs/meta_workflows/meta_workflow_job.rb +20 -0
- data/app/jobs/meta_workflows/record_redirect_job.rb +36 -0
- data/app/mailers/meta_workflows/application_mailer.rb +8 -0
- data/app/models/meta_workflows/application_record.rb +7 -0
- data/app/models/meta_workflows/chat.rb +20 -0
- data/app/models/meta_workflows/message.rb +11 -0
- data/app/models/meta_workflows/tool_call.rb +7 -0
- data/app/models/meta_workflows/workflow.rb +32 -0
- data/app/models/meta_workflows/workflow_execution.rb +23 -0
- data/app/models/meta_workflows/workflow_step.rb +49 -0
- data/app/models/meta_workflows.rb +7 -0
- data/app/services/meta_workflows/execution_filter_service.rb +80 -0
- data/app/sidekiq/meta_workflows/tools/meta_workflow_tool.rb +86 -0
- data/app/views/layouts/meta_workflows/application.html.erb +17 -0
- data/app/views/layouts/meta_workflows/debug.html.erb +47 -0
- data/app/views/meta_workflows/_loader.html.erb +22 -0
- data/app/views/meta_workflows/_redirect.html.erb +1 -0
- data/app/views/meta_workflows/_response.html.erb +5 -0
- data/app/views/meta_workflows/_response_form.html.erb +36 -0
- data/app/views/meta_workflows/debug/executions.html.erb +187 -0
- data/app/views/meta_workflows/debug/show_execution.html.erb +283 -0
- data/app/views/meta_workflows/debug/show_workflow.html.erb +148 -0
- data/app/views/meta_workflows/debug/workflows.html.erb +110 -0
- data/config/routes.rb +13 -0
- data/db/migrate/20250530220618_create_meta_workflows_workflows.rb +16 -0
- data/db/migrate/20250530220634_create_meta_workflows_workflow_executions.rb +18 -0
- data/db/migrate/20250530220704_create_meta_workflows_chats.rb +16 -0
- data/db/migrate/20250530220722_create_meta_workflows_messages.rb +19 -0
- data/db/migrate/20250530220737_create_meta_workflows_tool_calls.rb +18 -0
- data/db/migrate/20250530220750_create_meta_workflows_workflow_steps.rb +17 -0
- data/db/migrate/20250613213159_add_error_fields_to_workflow_steps.rb +8 -0
- data/lib/meta_workflows/asset_installer.rb +509 -0
- data/lib/meta_workflows/configuration.rb +39 -0
- data/lib/meta_workflows/engine.rb +47 -0
- data/lib/meta_workflows/export_execution_service.rb +56 -0
- data/lib/meta_workflows/version.rb +5 -0
- data/lib/meta_workflows.rb +9 -0
- data/lib/services/meta_workflows/application_service.rb +11 -0
- data/lib/services/meta_workflows/meta_workflow_service.rb +277 -0
- data/lib/services/meta_workflows/updaters/meta_service.rb +39 -0
- data/lib/tasks/meta_workflows_tasks.rake +153 -0
- metadata +219 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
<%= turbo_frame_tag target_frame_id(record, form: true) do %>
|
2
|
+
<% if local_assigns[:response_enabled] %>
|
3
|
+
<div>
|
4
|
+
<%= form_with url: meta_workflows.human_path(workflow_execution_id), method: :patch do |form| %>
|
5
|
+
<%= form.hidden_field :chat_id, value: chat_id %>
|
6
|
+
<fieldset>
|
7
|
+
<div class="flex flex-col gap-5">
|
8
|
+
<div class="flex gap-2">
|
9
|
+
<%= form.text_area :message, rows: 3, placeholder: random_chat_placeholder, disabled: local_assigns[:responding], class: "flex-1" %>
|
10
|
+
|
11
|
+
<% if local_assigns[:responding] %>
|
12
|
+
<button type="button" class="sm-btn sm-btn-primary self-start" disabled>
|
13
|
+
<i class="fa-light fa-spinner fa-spin mr-1"></i> Generating...
|
14
|
+
</button>
|
15
|
+
<% else %>
|
16
|
+
<%= form.button type: "submit", class: "sm-btn sm-btn-outline" do %>
|
17
|
+
<i class="fa-light fa-paper-plane"></i>
|
18
|
+
<% end %>
|
19
|
+
<% end %>
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<hr/>
|
23
|
+
|
24
|
+
<% unless local_assigns[:responding] %>
|
25
|
+
<div class="flex justify-end">
|
26
|
+
<%= form.button type: "submit", name: "advance", value: "true", class: "sm-btn sm-btn-primary sm-btn-large" do %>
|
27
|
+
<i class="fa-light fa-arrow-right"></i> Next
|
28
|
+
<% end %>
|
29
|
+
</div>
|
30
|
+
<% end %>
|
31
|
+
</div>
|
32
|
+
</fieldset>
|
33
|
+
<% end %>
|
34
|
+
</div>
|
35
|
+
<% end %>
|
36
|
+
<% end %>
|
@@ -0,0 +1,187 @@
|
|
1
|
+
<div class="bg-white shadow rounded-lg">
|
2
|
+
<!-- Header -->
|
3
|
+
<div class="px-6 py-4 border-b border-gray-200">
|
4
|
+
<div class="flex items-center justify-between">
|
5
|
+
<div>
|
6
|
+
<h2 class="text-lg font-medium text-gray-900">Executions</h2>
|
7
|
+
<p class="mt-1 text-sm text-gray-500">
|
8
|
+
Browse workflow execution history and details
|
9
|
+
</p>
|
10
|
+
</div>
|
11
|
+
|
12
|
+
<!-- Search and Filter -->
|
13
|
+
<div class="flex space-x-4">
|
14
|
+
<%= form_with url: executions_path, method: :get, local: true, class: "flex items-center space-x-2" do |form| %>
|
15
|
+
<%= form.text_field :search,
|
16
|
+
placeholder: "Search executions...",
|
17
|
+
value: params[:search],
|
18
|
+
class: "block w-48 rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" %>
|
19
|
+
<%= form.hidden_field :workflow_id, value: params[:workflow_id] %>
|
20
|
+
<%= form.submit "Search",
|
21
|
+
class: "inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" %>
|
22
|
+
<% end %>
|
23
|
+
|
24
|
+
<% if params.slice(:workflow_id, :status, :date_from, :date_to).values.any?(&:present?) %>
|
25
|
+
<%= link_to "Clear Filters", executions_path,
|
26
|
+
class: "inline-flex items-center px-3 py-2 border border-gray-300 text-sm leading-4 font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" %>
|
27
|
+
<% end %>
|
28
|
+
</div>
|
29
|
+
</div>
|
30
|
+
|
31
|
+
<!-- Advanced Filters -->
|
32
|
+
<div class="mt-4 border-t border-gray-200 pt-4">
|
33
|
+
<%= form_with url: executions_path, method: :get, local: true, class: "space-y-4" do |form| %>
|
34
|
+
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
35
|
+
<!-- Status Filter -->
|
36
|
+
<div>
|
37
|
+
<%= form.label :status, "Status", class: "block text-sm font-medium text-gray-700" %>
|
38
|
+
<%= form.select :status,
|
39
|
+
options_for_select([
|
40
|
+
['All Statuses', ''],
|
41
|
+
['Running', 'running'],
|
42
|
+
['Completed', 'completed'],
|
43
|
+
['Failed', 'failed']
|
44
|
+
], params[:status]),
|
45
|
+
{ prompt: false },
|
46
|
+
{ class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" } %>
|
47
|
+
</div>
|
48
|
+
|
49
|
+
<!-- Date From -->
|
50
|
+
<div>
|
51
|
+
<%= form.label :date_from, "From Date", class: "block text-sm font-medium text-gray-700" %>
|
52
|
+
<%= form.date_field :date_from,
|
53
|
+
value: params[:date_from],
|
54
|
+
class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" %>
|
55
|
+
</div>
|
56
|
+
|
57
|
+
<!-- Date To -->
|
58
|
+
<div>
|
59
|
+
<%= form.label :date_to, "To Date", class: "block text-sm font-medium text-gray-700" %>
|
60
|
+
<%= form.date_field :date_to,
|
61
|
+
value: params[:date_to],
|
62
|
+
class: "mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" %>
|
63
|
+
</div>
|
64
|
+
|
65
|
+
<!-- Apply Filters Button -->
|
66
|
+
<div class="flex items-end">
|
67
|
+
<%= form.hidden_field :search, value: params[:search] %>
|
68
|
+
<%= form.hidden_field :workflow_id, value: params[:workflow_id] %>
|
69
|
+
<%= form.submit "Apply Filters",
|
70
|
+
class: "w-full inline-flex justify-center items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" %>
|
71
|
+
</div>
|
72
|
+
</div>
|
73
|
+
<% end %>
|
74
|
+
</div>
|
75
|
+
|
76
|
+
<!-- Active Filters Display -->
|
77
|
+
<% active_filters = [] %>
|
78
|
+
<% active_filters << "Workflow: #{safe_workflow_name(params[:workflow_id])}" if params[:workflow_id].present? %>
|
79
|
+
<% active_filters << "Status: #{params[:status].humanize}" if params[:status].present? %>
|
80
|
+
<% active_filters << "From: #{params[:date_from]}" if params[:date_from].present? %>
|
81
|
+
<% active_filters << "To: #{params[:date_to]}" if params[:date_to].present? %>
|
82
|
+
|
83
|
+
<% if active_filters.any? %>
|
84
|
+
<div class="mt-3 flex flex-wrap gap-2">
|
85
|
+
<span class="text-sm font-medium text-gray-700">Active Filters:</span>
|
86
|
+
<% active_filters.each do |filter| %>
|
87
|
+
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
|
88
|
+
<%= filter %>
|
89
|
+
</span>
|
90
|
+
<% end %>
|
91
|
+
</div>
|
92
|
+
<% end %>
|
93
|
+
</div>
|
94
|
+
|
95
|
+
<!-- Executions Table -->
|
96
|
+
<div class="overflow-hidden">
|
97
|
+
<table class="min-w-full divide-y divide-gray-200">
|
98
|
+
<thead class="bg-gray-50">
|
99
|
+
<tr>
|
100
|
+
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
101
|
+
ID
|
102
|
+
</th>
|
103
|
+
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
104
|
+
Workflow
|
105
|
+
</th>
|
106
|
+
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
107
|
+
Status
|
108
|
+
</th>
|
109
|
+
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
110
|
+
Current Step
|
111
|
+
</th>
|
112
|
+
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
113
|
+
Steps
|
114
|
+
</th>
|
115
|
+
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
116
|
+
Started
|
117
|
+
</th>
|
118
|
+
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
119
|
+
Updated
|
120
|
+
</th>
|
121
|
+
<th class="relative px-6 py-3">
|
122
|
+
<span class="sr-only">Actions</span>
|
123
|
+
</th>
|
124
|
+
</tr>
|
125
|
+
</thead>
|
126
|
+
<tbody class="bg-white divide-y divide-gray-200">
|
127
|
+
<% @executions.each do |execution| %>
|
128
|
+
<tr class="hover:bg-gray-50">
|
129
|
+
<td class="px-6 py-4 whitespace-nowrap">
|
130
|
+
<div class="text-sm font-medium text-gray-900">
|
131
|
+
#<%= execution.id %>
|
132
|
+
</div>
|
133
|
+
</td>
|
134
|
+
<td class="px-6 py-4 whitespace-nowrap">
|
135
|
+
<div class="text-sm text-gray-900">
|
136
|
+
<%= link_to execution.workflow.name, workflow_path(execution.workflow),
|
137
|
+
class: "text-blue-600 hover:text-blue-900" %>
|
138
|
+
</div>
|
139
|
+
</td>
|
140
|
+
<td class="px-6 py-4 whitespace-nowrap">
|
141
|
+
<%= execution_status_display(execution) %>
|
142
|
+
</td>
|
143
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
144
|
+
Step <%= execution.current_step + 1 %>
|
145
|
+
</td>
|
146
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
147
|
+
<%= execution.workflow_steps.count %> steps
|
148
|
+
</td>
|
149
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
150
|
+
<%= time_with_tooltip(execution.created_at) %>
|
151
|
+
</td>
|
152
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
153
|
+
<%= time_with_tooltip(execution.updated_at) %>
|
154
|
+
</td>
|
155
|
+
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
156
|
+
<%= link_to "View Details", execution_path(execution),
|
157
|
+
class: "text-blue-600 hover:text-blue-900" %>
|
158
|
+
</td>
|
159
|
+
</tr>
|
160
|
+
<% end %>
|
161
|
+
|
162
|
+
<% if @executions.empty? %>
|
163
|
+
<tr>
|
164
|
+
<td colspan="8" class="px-6 py-12 text-center">
|
165
|
+
<div class="text-gray-500">
|
166
|
+
<% if params[:search].present? %>
|
167
|
+
No executions found matching "<%= params[:search] %>"
|
168
|
+
<% elsif params[:workflow_id].present? %>
|
169
|
+
No executions found for this workflow
|
170
|
+
<% else %>
|
171
|
+
No executions found
|
172
|
+
<% end %>
|
173
|
+
</div>
|
174
|
+
</td>
|
175
|
+
</tr>
|
176
|
+
<% end %>
|
177
|
+
</tbody>
|
178
|
+
</table>
|
179
|
+
</div>
|
180
|
+
|
181
|
+
<!-- Pagination -->
|
182
|
+
<% if respond_to?(:paginate) && @executions.respond_to?(:current_page) %>
|
183
|
+
<div class="px-6 py-3 border-t border-gray-200">
|
184
|
+
<%= paginate @executions, theme: 'twitter_bootstrap_4' %>
|
185
|
+
</div>
|
186
|
+
<% end %>
|
187
|
+
</div>
|
@@ -0,0 +1,283 @@
|
|
1
|
+
<!-- Execution Detail Header -->
|
2
|
+
<div class="bg-white shadow rounded-lg mb-6">
|
3
|
+
<div class="px-6 py-4 border-b border-gray-200">
|
4
|
+
<div class="flex items-center justify-between">
|
5
|
+
<div>
|
6
|
+
<h1 class="text-2xl font-bold text-gray-900">Execution #<%= @execution.id %></h1>
|
7
|
+
<p class="mt-1 text-sm text-gray-500">
|
8
|
+
Workflow: <%= link_to @execution.workflow.name, workflow_path(@execution.workflow), class: "text-blue-600 hover:text-blue-900" %>
|
9
|
+
</p>
|
10
|
+
</div>
|
11
|
+
|
12
|
+
<div class="flex space-x-3">
|
13
|
+
<%= link_to "← Back to Executions", executions_path,
|
14
|
+
class: "inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" %>
|
15
|
+
<%= link_to "Export JSON", export_execution_path(@execution, format: :json),
|
16
|
+
class: "inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" %>
|
17
|
+
<%= link_to "View Workflow", workflow_path(@execution.workflow),
|
18
|
+
class: "inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" %>
|
19
|
+
</div>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<!-- Execution Metadata -->
|
24
|
+
<div class="px-6 py-4">
|
25
|
+
<dl class="grid grid-cols-1 gap-x-4 gap-y-4 sm:grid-cols-4">
|
26
|
+
<div>
|
27
|
+
<dt class="text-sm font-medium text-gray-500">Status</dt>
|
28
|
+
<dd class="mt-1">
|
29
|
+
<%= execution_status_display(@execution) %>
|
30
|
+
</dd>
|
31
|
+
</div>
|
32
|
+
<div>
|
33
|
+
<dt class="text-sm font-medium text-gray-500">Progress</dt>
|
34
|
+
<dd class="mt-1">
|
35
|
+
<%= execution_progress_display(@execution) %>
|
36
|
+
</dd>
|
37
|
+
</div>
|
38
|
+
<div>
|
39
|
+
<dt class="text-sm font-medium text-gray-500">Record</dt>
|
40
|
+
<dd class="mt-1 text-sm text-gray-900">
|
41
|
+
<%= record_info_display(@execution) %>
|
42
|
+
</dd>
|
43
|
+
</div>
|
44
|
+
<div>
|
45
|
+
<dt class="text-sm font-medium text-gray-500">Created</dt>
|
46
|
+
<dd class="mt-1 text-sm text-gray-900">
|
47
|
+
<%= time_with_tooltip(@execution.created_at) %>
|
48
|
+
</dd>
|
49
|
+
</div>
|
50
|
+
</dl>
|
51
|
+
</div>
|
52
|
+
</div>
|
53
|
+
|
54
|
+
<!-- Workflow Parameters -->
|
55
|
+
<% if @execution.workflow_params.present? %>
|
56
|
+
<div class="bg-white shadow rounded-lg mb-6">
|
57
|
+
<div class="px-6 py-4 border-b border-gray-200">
|
58
|
+
<h2 class="text-lg font-medium text-gray-900">Workflow Parameters</h2>
|
59
|
+
<p class="mt-1 text-sm text-gray-500">
|
60
|
+
Parameters passed to this workflow execution
|
61
|
+
</p>
|
62
|
+
</div>
|
63
|
+
|
64
|
+
<div class="px-6 py-4">
|
65
|
+
<%= json_display(@execution.workflow_params) %>
|
66
|
+
</div>
|
67
|
+
</div>
|
68
|
+
<% end %>
|
69
|
+
|
70
|
+
<!-- Workflow Steps -->
|
71
|
+
<div class="bg-white shadow rounded-lg mb-6">
|
72
|
+
<div class="px-6 py-4 border-b border-gray-200">
|
73
|
+
<h2 class="text-lg font-medium text-gray-900">Workflow Steps</h2>
|
74
|
+
<p class="mt-1 text-sm text-gray-500">
|
75
|
+
Detailed view of each step in this execution
|
76
|
+
</p>
|
77
|
+
</div>
|
78
|
+
|
79
|
+
<div class="divide-y divide-gray-200">
|
80
|
+
<% @execution.workflow_steps.includes(:chat).order(:step).each do |workflow_step| %>
|
81
|
+
<div class="px-6 py-4">
|
82
|
+
<div class="flex items-start justify-between">
|
83
|
+
<div class="flex-1">
|
84
|
+
<div class="flex items-center">
|
85
|
+
<h3 class="text-sm font-medium text-gray-900">
|
86
|
+
Step <%= workflow_step.step %>
|
87
|
+
</h3>
|
88
|
+
<%= workflow_step_status(workflow_step, @execution.current_step) %>
|
89
|
+
<% if workflow_step.error? %>
|
90
|
+
<span class="ml-2 inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">
|
91
|
+
<svg class="w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20">
|
92
|
+
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
|
93
|
+
</svg>
|
94
|
+
Error
|
95
|
+
</span>
|
96
|
+
<% end %>
|
97
|
+
</div>
|
98
|
+
|
99
|
+
<% step_data = safe_step_data(@execution.workflow, workflow_step.step) %>
|
100
|
+
<% if step_data %>
|
101
|
+
<div class="mt-2 text-sm text-gray-600">
|
102
|
+
<% if step_data['error'] %>
|
103
|
+
<span class="text-red-600">Error: <%= step_data['error'] %></span>
|
104
|
+
<% else %>
|
105
|
+
<strong>Action:</strong> <%= step_data['action'] %>
|
106
|
+
<% if step_data['prompt_id'] %>
|
107
|
+
<br><strong>Prompt ID:</strong> <%= step_data['prompt_id'] %>
|
108
|
+
<% end %>
|
109
|
+
<% if step_data['name'] %>
|
110
|
+
<br><strong>Name:</strong> <%= step_data['name'] %>
|
111
|
+
<% end %>
|
112
|
+
<% end %>
|
113
|
+
</div>
|
114
|
+
<% end %>
|
115
|
+
|
116
|
+
<!-- Error Information Display -->
|
117
|
+
<% if workflow_step.error? %>
|
118
|
+
<div class="mt-3 p-3 bg-red-50 border border-red-200 rounded-md">
|
119
|
+
<div class="flex items-start">
|
120
|
+
<div class="flex-shrink-0">
|
121
|
+
<svg class="h-5 w-5 text-red-400" fill="currentColor" viewBox="0 0 20 20">
|
122
|
+
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
|
123
|
+
</svg>
|
124
|
+
</div>
|
125
|
+
<div class="ml-3 flex-1">
|
126
|
+
<h4 class="text-sm font-medium text-red-800">
|
127
|
+
LLM Processing Error
|
128
|
+
</h4>
|
129
|
+
<div class="mt-2 text-sm text-red-700">
|
130
|
+
<p><strong>Type:</strong> <%= workflow_step.error_type %></p>
|
131
|
+
<p><strong>Message:</strong> <%= workflow_step.error_message %></p>
|
132
|
+
<% if workflow_step.error_occurred_at %>
|
133
|
+
<p><strong>Occurred:</strong> <%= time_with_tooltip(workflow_step.error_occurred_at) %></p>
|
134
|
+
<% end %>
|
135
|
+
</div>
|
136
|
+
|
137
|
+
<% if workflow_step.error_details.present? %>
|
138
|
+
<div class="mt-3">
|
139
|
+
<button class="text-sm text-red-600 hover:text-red-800 font-medium"
|
140
|
+
onclick="toggleErrorDetails('error-details-<%= workflow_step.id %>')">
|
141
|
+
Show Error Details ▼
|
142
|
+
</button>
|
143
|
+
<div id="error-details-<%= workflow_step.id %>" class="hidden mt-2">
|
144
|
+
<%= json_display(workflow_step.error_details, classes: 'bg-red-100 p-2 rounded text-xs overflow-x-auto') %>
|
145
|
+
</div>
|
146
|
+
</div>
|
147
|
+
<% end %>
|
148
|
+
</div>
|
149
|
+
</div>
|
150
|
+
</div>
|
151
|
+
<% end %>
|
152
|
+
|
153
|
+
<% if workflow_step.chat.present? %>
|
154
|
+
<div class="mt-2">
|
155
|
+
<span class="text-xs text-gray-500">
|
156
|
+
Chat ID: <%= workflow_step.chat.id %> |
|
157
|
+
Messages: <%= workflow_step.chat.messages.count %> |
|
158
|
+
Created: <%= time_with_tooltip(workflow_step.chat.created_at) %>
|
159
|
+
</span>
|
160
|
+
</div>
|
161
|
+
<% end %>
|
162
|
+
</div>
|
163
|
+
|
164
|
+
<div class="text-xs text-gray-500">
|
165
|
+
<%= time_with_tooltip(workflow_step.created_at) %>
|
166
|
+
</div>
|
167
|
+
</div>
|
168
|
+
</div>
|
169
|
+
<% end %>
|
170
|
+
|
171
|
+
<% if @execution.workflow_steps.empty? %>
|
172
|
+
<div class="px-6 py-12 text-center text-gray-500">
|
173
|
+
No workflow steps found
|
174
|
+
</div>
|
175
|
+
<% end %>
|
176
|
+
</div>
|
177
|
+
</div>
|
178
|
+
|
179
|
+
<!-- Chat History -->
|
180
|
+
<% chats_with_messages = @execution.workflow_steps.includes(chat: { messages: :tool_calls }).map(&:chat).compact.select { |c| c.messages.any? } %>
|
181
|
+
<% if chats_with_messages.any? %>
|
182
|
+
<div class="bg-white shadow rounded-lg">
|
183
|
+
<div class="px-6 py-4 border-b border-gray-200">
|
184
|
+
<h2 class="text-lg font-medium text-gray-900">Chat History</h2>
|
185
|
+
<p class="mt-1 text-sm text-gray-500">
|
186
|
+
Chronological view of all conversations in this execution
|
187
|
+
</p>
|
188
|
+
</div>
|
189
|
+
|
190
|
+
<div class="divide-y divide-gray-200">
|
191
|
+
<% chats_with_messages.each do |chat| %>
|
192
|
+
<div class="px-6 py-4">
|
193
|
+
<div class="mb-4">
|
194
|
+
<h3 class="text-sm font-medium text-gray-900">
|
195
|
+
Chat #<%= chat.id %>
|
196
|
+
<span class="ml-2 text-xs text-gray-500">
|
197
|
+
(<%= chat.provider %> - <%= chat.model_id %>)
|
198
|
+
</span>
|
199
|
+
</h3>
|
200
|
+
<% if chat.user.present? %>
|
201
|
+
<p class="text-xs text-gray-500">User: <%= user_display_name(chat.user) %></p>
|
202
|
+
<% end %>
|
203
|
+
</div>
|
204
|
+
|
205
|
+
<!-- Messages -->
|
206
|
+
<div class="space-y-3">
|
207
|
+
<% chat.messages.order(:created_at).each do |message| %>
|
208
|
+
<div class="border rounded-lg p-3 <%= message_role_badge(message.role) %>">
|
209
|
+
<div class="flex items-center justify-between mb-2">
|
210
|
+
<div class="flex items-center space-x-2">
|
211
|
+
<span class="text-xs font-medium text-gray-900 uppercase"><%= message.role %></span>
|
212
|
+
<% if message.model_id.present? %>
|
213
|
+
<span class="text-xs text-gray-500">(<%= message.model_id %>)</span>
|
214
|
+
<% end %>
|
215
|
+
</div>
|
216
|
+
<div class="text-xs text-gray-500">
|
217
|
+
<%= time_with_tooltip(message.created_at) %>
|
218
|
+
<% token_info = format_token_usage(message.input_tokens, message.output_tokens) %>
|
219
|
+
<% if token_info.present? %>
|
220
|
+
| <%= token_info %>
|
221
|
+
<% end %>
|
222
|
+
</div>
|
223
|
+
</div>
|
224
|
+
|
225
|
+
<% if message.content.present? %>
|
226
|
+
<div class="text-sm text-gray-800 whitespace-pre-wrap"><%= message.content %></div>
|
227
|
+
<% end %>
|
228
|
+
|
229
|
+
<!-- Tool Calls -->
|
230
|
+
<% if message.tool_calls.any? %>
|
231
|
+
<div class="mt-3 space-y-2">
|
232
|
+
<h4 class="text-xs font-medium text-gray-700">Tool Calls:</h4>
|
233
|
+
<% message.tool_calls.each do |tool_call| %>
|
234
|
+
<div class="bg-white border rounded p-2">
|
235
|
+
<div class="flex items-center justify-between mb-1">
|
236
|
+
<span class="text-xs font-medium text-gray-800"><%= tool_call.name %></span>
|
237
|
+
<span class="text-xs text-gray-500">ID: <%= tool_call.tool_call_id %></span>
|
238
|
+
</div>
|
239
|
+
<% if tool_call.arguments.present? %>
|
240
|
+
<div class="text-xs">
|
241
|
+
<strong>Arguments:</strong>
|
242
|
+
<%= json_display(tool_call.arguments, classes: 'bg-gray-50 p-2 rounded text-xs overflow-x-auto mt-1') %>
|
243
|
+
</div>
|
244
|
+
<% end %>
|
245
|
+
</div>
|
246
|
+
<% end %>
|
247
|
+
</div>
|
248
|
+
<% end %>
|
249
|
+
</div>
|
250
|
+
<% end %>
|
251
|
+
</div>
|
252
|
+
</div>
|
253
|
+
<% end %>
|
254
|
+
</div>
|
255
|
+
</div>
|
256
|
+
<% else %>
|
257
|
+
<div class="bg-white shadow rounded-lg">
|
258
|
+
<div class="px-6 py-4 border-b border-gray-200">
|
259
|
+
<h2 class="text-lg font-medium text-gray-900">Chat History</h2>
|
260
|
+
<p class="mt-1 text-sm text-gray-500">
|
261
|
+
No chat conversations found for this execution
|
262
|
+
</p>
|
263
|
+
</div>
|
264
|
+
<div class="px-6 py-12 text-center text-gray-500">
|
265
|
+
This execution has no associated chat conversations
|
266
|
+
</div>
|
267
|
+
</div>
|
268
|
+
<% end %>
|
269
|
+
|
270
|
+
<script>
|
271
|
+
function toggleErrorDetails(elementId) {
|
272
|
+
const element = document.getElementById(elementId);
|
273
|
+
const button = element.previousElementSibling;
|
274
|
+
|
275
|
+
if (element.classList.contains('hidden')) {
|
276
|
+
element.classList.remove('hidden');
|
277
|
+
button.innerHTML = 'Hide Error Details ▲';
|
278
|
+
} else {
|
279
|
+
element.classList.add('hidden');
|
280
|
+
button.innerHTML = 'Show Error Details ▼';
|
281
|
+
}
|
282
|
+
}
|
283
|
+
</script>
|
@@ -0,0 +1,148 @@
|
|
1
|
+
<!-- Workflow Detail Header -->
|
2
|
+
<div class="bg-white shadow rounded-lg mb-6">
|
3
|
+
<div class="px-6 py-4 border-b border-gray-200">
|
4
|
+
<div class="flex items-center justify-between">
|
5
|
+
<div>
|
6
|
+
<h1 class="text-2xl font-bold text-gray-900"><%= @workflow.name %></h1>
|
7
|
+
<p class="mt-1 text-sm text-gray-500">
|
8
|
+
Workflow Details
|
9
|
+
</p>
|
10
|
+
</div>
|
11
|
+
|
12
|
+
<div class="flex space-x-3">
|
13
|
+
<%= link_to "← Back to Workflows", workflows_path,
|
14
|
+
class: "inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" %>
|
15
|
+
<%= link_to "View Executions", executions_path(workflow_id: @workflow.id),
|
16
|
+
class: "inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" %>
|
17
|
+
</div>
|
18
|
+
</div>
|
19
|
+
</div>
|
20
|
+
|
21
|
+
<!-- Workflow Metadata -->
|
22
|
+
<div class="px-6 py-4">
|
23
|
+
<dl class="grid grid-cols-1 gap-x-4 gap-y-4 sm:grid-cols-3">
|
24
|
+
<div>
|
25
|
+
<dt class="text-sm font-medium text-gray-500">Total Steps</dt>
|
26
|
+
<dd class="mt-1 text-sm text-gray-900">
|
27
|
+
<% begin %>
|
28
|
+
<%= @workflow.total_steps %> steps
|
29
|
+
<% rescue => e %>
|
30
|
+
<span class="text-red-600">Error: <%= e.message %></span>
|
31
|
+
<% end %>
|
32
|
+
</dd>
|
33
|
+
</div>
|
34
|
+
<div>
|
35
|
+
<dt class="text-sm font-medium text-gray-500">Total Executions</dt>
|
36
|
+
<dd class="mt-1 text-sm text-gray-900">
|
37
|
+
<%= @workflow.workflow_executions.count %> executions
|
38
|
+
</dd>
|
39
|
+
</div>
|
40
|
+
<div>
|
41
|
+
<dt class="text-sm font-medium text-gray-500">Created</dt>
|
42
|
+
<dd class="mt-1 text-sm text-gray-900">
|
43
|
+
<%= time_with_tooltip(@workflow.created_at) %>
|
44
|
+
</dd>
|
45
|
+
</div>
|
46
|
+
</dl>
|
47
|
+
</div>
|
48
|
+
</div>
|
49
|
+
|
50
|
+
<!-- Workflow Recipe -->
|
51
|
+
<div class="bg-white shadow rounded-lg mb-6">
|
52
|
+
<div class="px-6 py-4 border-b border-gray-200">
|
53
|
+
<h2 class="text-lg font-medium text-gray-900">Workflow Recipe</h2>
|
54
|
+
<p class="mt-1 text-sm text-gray-500">
|
55
|
+
The JSON configuration that defines this workflow's behavior
|
56
|
+
</p>
|
57
|
+
</div>
|
58
|
+
|
59
|
+
<div class="px-6 py-4">
|
60
|
+
<% if @workflow.recipe.present? %>
|
61
|
+
<%= json_display(@workflow.recipe) %>
|
62
|
+
<% else %>
|
63
|
+
<div class="text-gray-500 italic">No recipe data available</div>
|
64
|
+
<% end %>
|
65
|
+
</div>
|
66
|
+
</div>
|
67
|
+
|
68
|
+
<!-- Recent Executions -->
|
69
|
+
<div class="bg-white shadow rounded-lg">
|
70
|
+
<div class="px-6 py-4 border-b border-gray-200">
|
71
|
+
<div class="flex items-center justify-between">
|
72
|
+
<div>
|
73
|
+
<h2 class="text-lg font-medium text-gray-900">Recent Executions</h2>
|
74
|
+
<p class="mt-1 text-sm text-gray-500">
|
75
|
+
Latest workflow executions for this workflow
|
76
|
+
</p>
|
77
|
+
</div>
|
78
|
+
<% if @executions.any? %>
|
79
|
+
<%= link_to "View All Executions", executions_path(workflow_id: @workflow.id),
|
80
|
+
class: "text-sm text-blue-600 hover:text-blue-900" %>
|
81
|
+
<% end %>
|
82
|
+
</div>
|
83
|
+
</div>
|
84
|
+
|
85
|
+
<!-- Executions Table -->
|
86
|
+
<div class="overflow-hidden">
|
87
|
+
<table class="min-w-full divide-y divide-gray-200">
|
88
|
+
<thead class="bg-gray-50">
|
89
|
+
<tr>
|
90
|
+
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
91
|
+
Status
|
92
|
+
</th>
|
93
|
+
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
94
|
+
Current Step
|
95
|
+
</th>
|
96
|
+
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
97
|
+
Record
|
98
|
+
</th>
|
99
|
+
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
100
|
+
Created
|
101
|
+
</th>
|
102
|
+
<th class="relative px-6 py-3">
|
103
|
+
<span class="sr-only">Actions</span>
|
104
|
+
</th>
|
105
|
+
</tr>
|
106
|
+
</thead>
|
107
|
+
<tbody class="bg-white divide-y divide-gray-200">
|
108
|
+
<% @executions.each do |execution| %>
|
109
|
+
<tr class="hover:bg-gray-50">
|
110
|
+
<td class="px-6 py-4 whitespace-nowrap">
|
111
|
+
<%= execution_status_display(execution) %>
|
112
|
+
</td>
|
113
|
+
<td class="px-6 py-4 whitespace-nowrap">
|
114
|
+
<%= execution_progress_display(execution) %>
|
115
|
+
</td>
|
116
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
117
|
+
<%= record_info_display(execution) %>
|
118
|
+
</td>
|
119
|
+
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
120
|
+
<%= time_with_tooltip(execution.created_at) %>
|
121
|
+
</td>
|
122
|
+
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
123
|
+
<%= link_to "View Details", execution_path(execution),
|
124
|
+
class: "text-blue-600 hover:text-blue-900" %>
|
125
|
+
</td>
|
126
|
+
</tr>
|
127
|
+
<% end %>
|
128
|
+
|
129
|
+
<% if @executions.empty? %>
|
130
|
+
<tr>
|
131
|
+
<td colspan="5" class="px-6 py-12 text-center">
|
132
|
+
<div class="text-gray-500">
|
133
|
+
No executions found for this workflow
|
134
|
+
</div>
|
135
|
+
</td>
|
136
|
+
</tr>
|
137
|
+
<% end %>
|
138
|
+
</tbody>
|
139
|
+
</table>
|
140
|
+
</div>
|
141
|
+
|
142
|
+
<!-- Pagination -->
|
143
|
+
<% if respond_to?(:paginate) && @executions.respond_to?(:current_page) %>
|
144
|
+
<div class="px-6 py-3 border-t border-gray-200">
|
145
|
+
<%= paginate @executions, theme: 'twitter_bootstrap_4' %>
|
146
|
+
</div>
|
147
|
+
<% end %>
|
148
|
+
</div>
|