meta_workflows 0.9.15 → 0.9.17
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 +69 -15
- data/app/assets/javascripts/meta_workflows/controllers/loading_phrases_controller.js +1 -1
- data/app/controllers/concerns/meta_workflows/streamable.rb +70 -0
- data/app/controllers/meta_workflows/humans_controller.rb +11 -28
- data/app/jobs/meta_workflows/human_input_job.rb +0 -2
- data/app/jobs/meta_workflows/meta_job.rb +8 -18
- data/app/views/meta_workflows/_error_message.html.erb +3 -1
- data/app/views/meta_workflows/_loader_message.html.erb +3 -1
- data/lib/meta_workflows/version.rb +1 -1
- data/lib/services/meta_workflows/meta_workflow_service.rb +8 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 628e1aa6509bd6913af6ecd6c2fb8b9097d5699598624543c30e0e759a835e39
|
4
|
+
data.tar.gz: f4388abaaa5e93aeec97c9b00156c87dc93c21348710812f0b068098c90bc9a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e495070ef645745e8387d21e92dfb4792cdc7dfa8a8cd5680518f1e01454d394772d5a05eeff87e343ec922923b7eea85b4845db866a1ca91017c40b3d81cb1
|
7
|
+
data.tar.gz: b8e34fe5aa9a31d19f05f597432859087bb5d5bd2c9de8a2afc4822a29dbbedfb5a7cd2b236d8d805f32bc04b10edd2ed66027e3dfe5964f9fa2a37f8a6dfd5b
|
data/README.md
CHANGED
@@ -233,10 +233,7 @@ steps:
|
|
233
233
|
action: "agent"
|
234
234
|
prompt_id: "generation_prompt"
|
235
235
|
model: "your-preferred-model-id" # Use appropriate model from RubyLLM documentation
|
236
|
-
step_progress:
|
237
|
-
- "Analyzing input parameters..."
|
238
|
-
- "Generating initial content..."
|
239
|
-
- "Refining results..."
|
236
|
+
step_progress: ["Analyzing input parameters", "Generating initial content", "Refining results"]
|
240
237
|
input:
|
241
238
|
title:
|
242
239
|
type: "string"
|
@@ -250,8 +247,7 @@ steps:
|
|
250
247
|
- name: "human_review"
|
251
248
|
action: "human"
|
252
249
|
prompt_id: "review_prompt"
|
253
|
-
step_progress:
|
254
|
-
- "Preparing content for review..."
|
250
|
+
step_progress: ["Preparing content for review"]
|
255
251
|
input:
|
256
252
|
generated_content:
|
257
253
|
type: "string"
|
@@ -267,9 +263,7 @@ steps:
|
|
267
263
|
attributes_to_update:
|
268
264
|
- "title"
|
269
265
|
- "topic"
|
270
|
-
step_progress:
|
271
|
-
- "Updating record..."
|
272
|
-
- "Saving changes..."
|
266
|
+
step_progress: ["Updating record", "Saving changes"]
|
273
267
|
workflow_params:
|
274
268
|
- name: "title"
|
275
269
|
type: string
|
@@ -307,27 +301,87 @@ end
|
|
307
301
|
|
308
302
|
### Using Meta Workflow UI Components
|
309
303
|
|
310
|
-
Instead of creating custom views from scratch, use the provided Meta Workflow partials:
|
304
|
+
Instead of creating custom views from scratch, use the provided Meta Workflow partials. The system now uses in-chat messaging for loader states and error handling, providing a seamless conversational experience:
|
305
|
+
|
306
|
+
#### Chat-Based UI Components
|
307
|
+
|
308
|
+
The workflow UI now consists of three main components:
|
309
|
+
|
310
|
+
1. **Chat Messages (`meta_response`)**: Displays conversation history and responses
|
311
|
+
2. **Loader Messages (`meta_loader_message`)**: Shows loading states as chat messages
|
312
|
+
3. **Error Messages (`meta_error_message`)**: Displays errors as friendly chat messages
|
313
|
+
4. **Response Form (`meta_response_form`)**: Handles user input during human interaction steps
|
311
314
|
|
312
315
|
#### Complete View Example
|
313
316
|
|
314
317
|
```erb
|
315
318
|
<%= turbo_stream_from turbo_stream_name(@record) %>
|
316
319
|
<div class="max-w-3xl m-auto flex flex-col p-10 gap-6">
|
317
|
-
<!--
|
318
|
-
<%= render partial: 'meta_workflows/
|
320
|
+
<!-- Chat messages area with existing conversation -->
|
321
|
+
<%= render partial: 'meta_workflows/response_lexi', locals: {
|
319
322
|
record: @record,
|
320
|
-
|
323
|
+
chat: @active_chat,
|
324
|
+
messages: @active_chat&.messages&.order(:created_at) || [],
|
325
|
+
user_messages: @active_chat&.messages&.where(role: 'user')&.order(:created_at) || [],
|
326
|
+
last_message: @active_chat&.messages&.last,
|
327
|
+
full_response: nil,
|
328
|
+
is_streaming: false
|
321
329
|
} %>
|
322
330
|
|
323
331
|
<!-- User input area for human interaction steps -->
|
324
332
|
<%= render partial: 'meta_workflows/response_form', locals: {
|
325
333
|
record: @record,
|
326
|
-
response_enabled:
|
334
|
+
response_enabled: true,
|
335
|
+
workflow_execution_id: @workflow_execution&.id,
|
336
|
+
chat_id: @active_chat&.id,
|
337
|
+
step_has_repetitions: true
|
327
338
|
} %>
|
328
339
|
</div>
|
329
340
|
```
|
330
341
|
|
342
|
+
#### Built-in Chat Tray Components
|
343
|
+
|
344
|
+
For a complete chat experience, use the pre-built chat tray components:
|
345
|
+
|
346
|
+
**Right Tray (Gamma) - Sidebar Chat:**
|
347
|
+
```erb
|
348
|
+
<%= render partial: 'meta_workflows/lexi_chat_right_tray', locals: {
|
349
|
+
record: @record
|
350
|
+
} %>
|
351
|
+
```
|
352
|
+
|
353
|
+
**Alpha Tray (Main) - Full-Screen Chat:**
|
354
|
+
```erb
|
355
|
+
<%= render partial: 'meta_workflows/lexi_chat_alpha_tray', locals: {
|
356
|
+
record: @record
|
357
|
+
} %>
|
358
|
+
```
|
359
|
+
|
360
|
+
#### Individual Component Usage
|
361
|
+
|
362
|
+
**Loader Message (appears as chat message):**
|
363
|
+
```erb
|
364
|
+
<%= render partial: 'meta_workflows/loader_message', locals: {
|
365
|
+
record: @record,
|
366
|
+
step_progress: ["Generating content", "Processing with AI", "Finalizing results"]
|
367
|
+
} %>
|
368
|
+
```
|
369
|
+
|
370
|
+
**Error Message (appears as chat message):**
|
371
|
+
```erb
|
372
|
+
<%= render partial: 'meta_workflows/error_message', locals: {
|
373
|
+
record: @record
|
374
|
+
} %>
|
375
|
+
```
|
376
|
+
|
377
|
+
#### Key Features
|
378
|
+
|
379
|
+
- **In-Chat Loading**: Loader messages appear as animated chat bubbles from the assistant
|
380
|
+
- **Friendly Error Messages**: Errors display as conversational messages with friendly styling
|
381
|
+
- **Seamless Integration**: Chat history persists while new interactions are processed
|
382
|
+
- **Responsive Design**: Components adapt to different tray layouts (sidebar, main, bottom)
|
383
|
+
- **Turbo Stream Updates**: Real-time updates without page refreshes using dedicated frame targeting
|
384
|
+
|
331
385
|
## File Structure Overview
|
332
386
|
|
333
387
|
The MetaWorkflows gem follows a structured organization:
|
@@ -478,7 +532,7 @@ To add a new action type:
|
|
478
532
|
### Workflow Design
|
479
533
|
- Keep steps focused and single-purpose
|
480
534
|
- Design clear input/output schemas
|
481
|
-
- Provide meaningful progress messages
|
535
|
+
- Provide meaningful progress messages without ellipsis (the loader UI handles animated dots automatically)
|
482
536
|
- Handle error cases gracefully
|
483
537
|
- Use `record_update` steps to persist intermediate workflow results
|
484
538
|
- Choose appropriate AI models for each step based on task complexity and cost considerations
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MetaWorkflows
|
4
|
+
module Streamable
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
include MetaWorkflows::MetaWorkflowsHelper
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
# Broadcasts loader message with step progress
|
14
|
+
def broadcast_loader_stream(workflow_execution)
|
15
|
+
step_progress = workflow_execution.step_progress(workflow_execution.current_step) || default_step_progress
|
16
|
+
record = workflow_execution.record
|
17
|
+
|
18
|
+
Turbo::StreamsChannel.broadcast_replace_to(
|
19
|
+
turbo_stream_name(record),
|
20
|
+
target: target_frame_id(record, loader: true),
|
21
|
+
partial: meta_loader_message,
|
22
|
+
locals: { record: record, step_progress: step_progress }
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Broadcasts error response when workflow fails
|
27
|
+
def broadcast_error_response(workflow_execution)
|
28
|
+
record = workflow_execution.record
|
29
|
+
|
30
|
+
Turbo::StreamsChannel.broadcast_replace_to(
|
31
|
+
turbo_stream_name(record),
|
32
|
+
target: target_frame_id(record, loader: true),
|
33
|
+
partial: meta_error_message,
|
34
|
+
locals: { record: record }
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Builds loader stream for direct response
|
39
|
+
def build_loader_stream(record:, workflow_execution:)
|
40
|
+
step_progress = workflow_execution.step_progress(workflow_execution.current_step) || default_step_progress
|
41
|
+
|
42
|
+
turbo_stream.replace(
|
43
|
+
target_frame_id(record, loader: true),
|
44
|
+
partial: meta_loader_message,
|
45
|
+
locals: { record: record, step_progress: step_progress }
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Builds response form stream for direct response
|
50
|
+
def build_response_form_stream(record:, workflow_execution:, response_enabled:, responding:, chat_id:)
|
51
|
+
turbo_stream.replace(
|
52
|
+
target_frame_id(record, form: true),
|
53
|
+
partial: meta_response_form,
|
54
|
+
locals: build_response_form_locals(record:, workflow_execution:, response_enabled:, responding:, chat_id:)
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Builds locals hash for response form
|
59
|
+
def build_response_form_locals(record:, workflow_execution:, response_enabled:, responding:, chat_id:)
|
60
|
+
{
|
61
|
+
record: record,
|
62
|
+
workflow_execution_id: workflow_execution.id,
|
63
|
+
chat_id: chat_id,
|
64
|
+
response_enabled: response_enabled,
|
65
|
+
responding: responding,
|
66
|
+
step_has_repetitions: current_step_has_repetitions?(workflow_execution)
|
67
|
+
}
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -3,10 +3,12 @@
|
|
3
3
|
module MetaWorkflows
|
4
4
|
class HumansController < MetaController
|
5
5
|
include MetaWorkflows::MetaWorkflowsHelper
|
6
|
+
include MetaWorkflows::Streamable
|
6
7
|
before_action :set_workflow_data, only: [:update]
|
7
8
|
|
8
9
|
def update
|
9
10
|
if params[:advance].present? || should_auto_advance?
|
11
|
+
@workflow_execution.increment_step
|
10
12
|
render_loader_stream
|
11
13
|
process_human_input(auto_advancing: should_auto_advance?, manual_advancing: params[:advance].present?)
|
12
14
|
else
|
@@ -44,19 +46,9 @@ module MetaWorkflows
|
|
44
46
|
respond_to do |format|
|
45
47
|
format.turbo_stream do
|
46
48
|
render turbo_stream: [
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
locals: {
|
51
|
-
record: @record,
|
52
|
-
step_progress: fetch_step_progress
|
53
|
-
}
|
54
|
-
),
|
55
|
-
turbo_stream.replace(
|
56
|
-
target_frame_id(@record, form: true),
|
57
|
-
partial: meta_response_form,
|
58
|
-
locals: response_form_locals(response_enabled: false)
|
59
|
-
)
|
49
|
+
build_loader_stream(record: @record, workflow_execution: @workflow_execution),
|
50
|
+
build_response_form_stream(record: @record, workflow_execution: @workflow_execution,
|
51
|
+
response_enabled: false, responding: false, chat_id: params[:chat_id])
|
60
52
|
]
|
61
53
|
end
|
62
54
|
end
|
@@ -65,24 +57,15 @@ module MetaWorkflows
|
|
65
57
|
def render_response_form_stream
|
66
58
|
respond_to do |format|
|
67
59
|
format.turbo_stream do
|
68
|
-
render turbo_stream:
|
69
|
-
|
70
|
-
|
71
|
-
|
60
|
+
render turbo_stream: build_response_form_stream(
|
61
|
+
record: @record,
|
62
|
+
workflow_execution: @workflow_execution,
|
63
|
+
response_enabled: true,
|
64
|
+
responding: true,
|
65
|
+
chat_id: params[:chat_id]
|
72
66
|
)
|
73
67
|
end
|
74
68
|
end
|
75
69
|
end
|
76
|
-
|
77
|
-
def response_form_locals(response_enabled: true, responding: false)
|
78
|
-
{
|
79
|
-
record: @record,
|
80
|
-
workflow_execution_id: @workflow_execution.id,
|
81
|
-
chat_id: params[:chat_id],
|
82
|
-
response_enabled: response_enabled,
|
83
|
-
responding: responding,
|
84
|
-
step_has_repetitions: current_step_has_repetitions?(@workflow_execution)
|
85
|
-
}
|
86
|
-
end
|
87
70
|
end
|
88
71
|
end
|
@@ -57,8 +57,6 @@ module MetaWorkflows
|
|
57
57
|
|
58
58
|
def advance_workflow(params)
|
59
59
|
dialogue = collect_messages_from_chat(params)
|
60
|
-
workflow_execution = active_workflow_execution
|
61
|
-
workflow_execution.increment_step
|
62
60
|
|
63
61
|
MetaWorkflows::MetaWorkflowJob.perform_later(
|
64
62
|
record_id: @record.id,
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module MetaWorkflows
|
4
4
|
class MetaJob < MetaWorkflows::ApplicationJob
|
5
|
+
include MetaWorkflows::Streamable
|
6
|
+
|
5
7
|
attr_accessor :chat, :inputs, :full_response, :user_id, :record, :auto_advancing, :manual_advancing
|
6
8
|
|
7
9
|
private
|
@@ -42,14 +44,15 @@ module MetaWorkflows
|
|
42
44
|
end
|
43
45
|
|
44
46
|
def process_and_broadcast(conversation)
|
45
|
-
|
47
|
+
workflow_execution = active_workflow_execution
|
48
|
+
workflow_step = current_workflow_step(workflow_execution)
|
46
49
|
|
47
50
|
begin
|
48
51
|
clear_previous_errors(workflow_step)
|
49
52
|
execute_llm_conversation(conversation)
|
50
53
|
finalize_conversation(conversation)
|
51
54
|
rescue StandardError => e
|
52
|
-
handle_llm_error(e, workflow_step, conversation)
|
55
|
+
handle_llm_error(e, workflow_step, workflow_execution, conversation)
|
53
56
|
raise
|
54
57
|
end
|
55
58
|
end
|
@@ -70,10 +73,10 @@ module MetaWorkflows
|
|
70
73
|
broadcast_form(chat) unless auto_advancing
|
71
74
|
end
|
72
75
|
|
73
|
-
def handle_llm_error(error, workflow_step, conversation)
|
76
|
+
def handle_llm_error(error, workflow_step, workflow_execution, conversation)
|
74
77
|
log_error(error)
|
75
78
|
store_error_in_workflow_step(error, workflow_step, conversation)
|
76
|
-
broadcast_error_response
|
79
|
+
broadcast_error_response(workflow_execution)
|
77
80
|
end
|
78
81
|
|
79
82
|
def log_error(error)
|
@@ -90,25 +93,12 @@ module MetaWorkflows
|
|
90
93
|
})
|
91
94
|
end
|
92
95
|
|
93
|
-
def current_workflow_step
|
94
|
-
workflow_execution = active_workflow_execution
|
96
|
+
def current_workflow_step(workflow_execution)
|
95
97
|
return nil unless workflow_execution
|
96
98
|
|
97
99
|
workflow_execution.workflow_steps.find_by(step: workflow_execution.current_step)
|
98
100
|
end
|
99
101
|
|
100
|
-
def broadcast_error_response
|
101
|
-
# Replace loader with error message
|
102
|
-
Turbo::StreamsChannel.broadcast_replace_to(
|
103
|
-
turbo_stream_name(record),
|
104
|
-
target: target_frame_id(record, loader: true),
|
105
|
-
partial: 'meta_workflows/error_message',
|
106
|
-
locals: {
|
107
|
-
record: record
|
108
|
-
}
|
109
|
-
)
|
110
|
-
end
|
111
|
-
|
112
102
|
def broadcast_form(chat)
|
113
103
|
raise NotImplementedError
|
114
104
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
<%= turbo_frame_tag target_frame_id(record, loader: true) do %>
|
1
2
|
<div class="lexi-message-assistant">
|
2
3
|
<div class="lexi-message-bubble-assistant">
|
3
4
|
<div class="lexi-message-content-assistant">
|
@@ -11,4 +12,5 @@
|
|
11
12
|
</div>
|
12
13
|
</div>
|
13
14
|
</div>
|
14
|
-
</div>
|
15
|
+
</div>
|
16
|
+
<% end %>
|
@@ -1,3 +1,4 @@
|
|
1
|
+
<%= turbo_frame_tag target_frame_id(record, loader: true) do %>
|
1
2
|
<div class="lexi-message-assistant">
|
2
3
|
<div class="lexi-loader-bubble">
|
3
4
|
<div class="lexi-message-content-assistant"
|
@@ -13,4 +14,5 @@
|
|
13
14
|
</span>
|
14
15
|
</div>
|
15
16
|
</div>
|
16
|
-
</div>
|
17
|
+
</div>
|
18
|
+
<% end %>
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module MetaWorkflows
|
4
4
|
MAJOR = 0
|
5
5
|
MINOR = 9
|
6
|
-
PATCH =
|
6
|
+
PATCH = 17 # this is automatically incremented by the build process
|
7
7
|
|
8
8
|
VERSION = "#{MetaWorkflows::MAJOR}.#{MetaWorkflows::MINOR}.#{MetaWorkflows::PATCH}".freeze
|
9
9
|
end
|
@@ -3,6 +3,9 @@
|
|
3
3
|
module Services
|
4
4
|
module MetaWorkflows
|
5
5
|
class MetaWorkflowService
|
6
|
+
include ::MetaWorkflows::MetaWorkflowsHelper
|
7
|
+
include ::MetaWorkflows::Streamable
|
8
|
+
|
6
9
|
ACTIONS_WITHOUT_CONVERSATION = %w[record_redirect collection_create record_update].freeze
|
7
10
|
|
8
11
|
attr_reader :record, :workflow_name, :user, :inputs, :workflow_params
|
@@ -35,6 +38,8 @@ module Services
|
|
35
38
|
private
|
36
39
|
|
37
40
|
def process_action(execution_step, conversation, workflow_execution, workflow)
|
41
|
+
broadcast_loader_stream(workflow_execution)
|
42
|
+
|
38
43
|
case execution_step['action']
|
39
44
|
when 'agent'
|
40
45
|
process_agent_action(conversation, workflow_execution, workflow)
|
@@ -102,6 +107,7 @@ module Services
|
|
102
107
|
Rails.logger.error(error.backtrace.join("\n"))
|
103
108
|
|
104
109
|
record_error_details(error, workflow_step, workflow, workflow_execution) if workflow_step
|
110
|
+
broadcast_error_response(workflow_execution)
|
105
111
|
end
|
106
112
|
|
107
113
|
def record_error_details(error, workflow_step, workflow, workflow_execution)
|
@@ -145,6 +151,7 @@ module Services
|
|
145
151
|
continue_workflow(workflow_execution)
|
146
152
|
rescue StandardError => e
|
147
153
|
Rails.logger.error("Error creating collection: #{e.message}")
|
154
|
+
broadcast_error_response(workflow_execution)
|
148
155
|
# The workflow should halt on error per requirements - do not increment step
|
149
156
|
end
|
150
157
|
end
|
@@ -158,6 +165,7 @@ module Services
|
|
158
165
|
continue_workflow(workflow_execution)
|
159
166
|
rescue StandardError => e
|
160
167
|
Rails.logger.error("Error updating record: #{e.message}")
|
168
|
+
broadcast_error_response(workflow_execution)
|
161
169
|
# Let Sidekiq handle retry - reraise the error
|
162
170
|
raise
|
163
171
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: meta_workflows
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.17
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Leonid Medovyy
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2025-07-
|
12
|
+
date: 2025-07-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -128,6 +128,7 @@ files:
|
|
128
128
|
- app/assets/javascripts/meta_workflows/controllers/tray_controller.js
|
129
129
|
- app/assets/javascripts/meta_workflows_manifest.js
|
130
130
|
- app/assets/stylesheets/meta_workflows/application.css
|
131
|
+
- app/controllers/concerns/meta_workflows/streamable.rb
|
131
132
|
- app/controllers/concerns/tray_configurable.rb
|
132
133
|
- app/controllers/meta_workflows/application_controller.rb
|
133
134
|
- app/controllers/meta_workflows/base_debug_controller.rb
|