meta_workflows 0.9.26 → 0.9.28
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 +74 -2
- data/app/assets/stylesheets/meta_workflows/application.css +17 -10
- data/app/jobs/meta_workflows/concerns/workflow_params_mergeable.rb +37 -0
- data/app/jobs/meta_workflows/meta_job.rb +6 -1
- data/app/views/meta_workflows/_response_form_lexi.html.erb +8 -21
- data/lib/meta_workflows/version.rb +1 -1
- data/lib/services/meta_workflows/meta_workflow_service.rb +11 -3
- 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: f07cda61b8fdd4e757dc33dd1115ce19949fce1f08547ab86b1fe1b16d9a6940
|
4
|
+
data.tar.gz: b3d24c6d2b7dffbc6b47efb2dcb673ef07701446fefb4da9279b50041aa9cfb0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cbd3d21ca6163e39df0808847c4bf2f7fc11a0de8bf7aa9d59cc9226f01176c0837babc6feedd9d19c2117dafe62e8aff12f85723e27b95f84247a515ff0eed8
|
7
|
+
data.tar.gz: 99858cf6ed8c38f0fe1d7bc9149787036c18b40db0c04738e495ba902b95c02d1fc3c7978c0b8a1ce0f7e1ed573a47f7d1bd91cd29394809954f0f82246e6f95
|
data/README.md
CHANGED
@@ -347,6 +347,39 @@ steps:
|
|
347
347
|
redirect_path: "/your/completion/path"
|
348
348
|
```
|
349
349
|
|
350
|
+
### Workflow Parameters vs Inputs
|
351
|
+
|
352
|
+
MetaWorkflows supports two ways to pass data into workflows: `inputs` and `workflow_params`. Understanding the difference is crucial for proper workflow design.
|
353
|
+
|
354
|
+
#### `inputs` Hash from Service Instantiation
|
355
|
+
- **Scope**: Only available to the **very next step** of the workflow
|
356
|
+
- **Use Case**: Initial data needed to start the workflow
|
357
|
+
- **Lifetime**: Single-step only - not passed to subsequent steps
|
358
|
+
|
359
|
+
#### `workflow_params` Hash
|
360
|
+
- **Scope**: Available to **all steps** throughout the entire workflow execution
|
361
|
+
- **Use Case**: Data that needs to persist and be accessible across multiple workflow steps
|
362
|
+
- **Lifetime**: Entire workflow execution - accessible in every step
|
363
|
+
|
364
|
+
#### Priority and Conflicts
|
365
|
+
|
366
|
+
When both `inputs` and `workflow_params` contain the same attribute name:
|
367
|
+
- **`inputs` takes precedence** for the first step, but workflow params are still passed
|
368
|
+
- **This is not ideal** - avoid having duplicate keys in both hashes
|
369
|
+
- Choose one approach based on whether the data needs to persist throughout the workflow
|
370
|
+
|
371
|
+
#### When to Use Each
|
372
|
+
|
373
|
+
**Use `inputs` when:**
|
374
|
+
- Data is only needed for workflow initialization
|
375
|
+
- You want to pass temporary parameters to start the process
|
376
|
+
- The data doesn't need to be available in later steps
|
377
|
+
|
378
|
+
**Use `workflow_params` when:**
|
379
|
+
- Data needs to be available across multiple workflow steps
|
380
|
+
- You want to maintain state throughout the entire execution
|
381
|
+
- The information is core to the workflow's purpose
|
382
|
+
|
350
383
|
### Executing Workflows
|
351
384
|
|
352
385
|
#### From Controller Actions
|
@@ -362,8 +395,13 @@ class YourController < ApplicationController
|
|
362
395
|
record_id: @record.id,
|
363
396
|
user_id: current_user.id,
|
364
397
|
inputs: {
|
365
|
-
|
366
|
-
# other initial parameters
|
398
|
+
initial_title: @record.title, # Only available to first step
|
399
|
+
# other initial parameters for first step only
|
400
|
+
},
|
401
|
+
workflow_params: {
|
402
|
+
organization_id: @record.organization_id, # Available to all steps
|
403
|
+
user_role: current_user.role, # Available to all steps
|
404
|
+
# other parameters needed throughout workflow
|
367
405
|
}
|
368
406
|
)
|
369
407
|
|
@@ -372,6 +410,40 @@ class YourController < ApplicationController
|
|
372
410
|
end
|
373
411
|
```
|
374
412
|
|
413
|
+
#### Example: Workflow Definition Using Both
|
414
|
+
|
415
|
+
```yaml
|
416
|
+
name: "content_creation_workflow"
|
417
|
+
steps:
|
418
|
+
- name: "initial_analysis"
|
419
|
+
action: "agent"
|
420
|
+
prompt_id: "analyze_content"
|
421
|
+
input:
|
422
|
+
# This step can access both inputs (first step only) and workflow_params
|
423
|
+
- initial_title: "Starting title from inputs hash"
|
424
|
+
type: "string"
|
425
|
+
- organization_id: "Organization context from workflow_params"
|
426
|
+
type: "string"
|
427
|
+
|
428
|
+
- name: "content_generation"
|
429
|
+
action: "agent"
|
430
|
+
prompt_id: "generate_content"
|
431
|
+
input:
|
432
|
+
# This step can only access workflow_params (inputs not available)
|
433
|
+
- organization_id: "Organization context from workflow_params"
|
434
|
+
type: "string"
|
435
|
+
- user_role: "User role from workflow_params"
|
436
|
+
type: "string"
|
437
|
+
|
438
|
+
- name: "final_review"
|
439
|
+
action: "human"
|
440
|
+
prompt_id: "review_content"
|
441
|
+
input:
|
442
|
+
# This step can also only access workflow_params
|
443
|
+
- organization_id: "Organization context from workflow_params"
|
444
|
+
type: "string"
|
445
|
+
```
|
446
|
+
|
375
447
|
### Using Meta Workflow UI Components
|
376
448
|
|
377
449
|
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:
|
@@ -60,7 +60,7 @@
|
|
60
60
|
ol {
|
61
61
|
display: flex;
|
62
62
|
flex-direction: column;
|
63
|
-
gap: .5rem;
|
63
|
+
gap: 0.5rem;
|
64
64
|
padding-left: 0;
|
65
65
|
list-style-type: none;
|
66
66
|
|
@@ -85,7 +85,7 @@
|
|
85
85
|
counter-reset: list-counter;
|
86
86
|
|
87
87
|
li::before {
|
88
|
-
content: counter(list-counter)
|
88
|
+
content: counter(list-counter) '.';
|
89
89
|
counter-increment: list-counter;
|
90
90
|
display: inline-block;
|
91
91
|
width: 1.5em;
|
@@ -180,7 +180,9 @@
|
|
180
180
|
}
|
181
181
|
|
182
182
|
@keyframes dot-bounce {
|
183
|
-
0%,
|
183
|
+
0%,
|
184
|
+
80%,
|
185
|
+
100% {
|
184
186
|
transform: translateY(0);
|
185
187
|
opacity: 0.7;
|
186
188
|
}
|
@@ -192,7 +194,7 @@
|
|
192
194
|
|
193
195
|
.lexi-loader-text {
|
194
196
|
font-size: 1.25rem;
|
195
|
-
background: linear-gradient(135deg, #
|
197
|
+
background: linear-gradient(135deg, #7b5ba3 0%, #3a9999 50%, #7b5ba3 100%);
|
196
198
|
background-size: 200% 200%;
|
197
199
|
background-clip: text;
|
198
200
|
-webkit-background-clip: text;
|
@@ -211,13 +213,14 @@
|
|
211
213
|
.lexi-dot {
|
212
214
|
display: inline-block;
|
213
215
|
font-size: 1.25rem;
|
214
|
-
background: linear-gradient(135deg, #
|
216
|
+
background: linear-gradient(135deg, #7b5ba3 0%, #3a9999 50%, #7b5ba3 100%);
|
215
217
|
background-size: 200% 200%;
|
216
218
|
background-clip: text;
|
217
219
|
-webkit-background-clip: text;
|
218
220
|
-webkit-text-fill-color: transparent;
|
219
221
|
color: transparent;
|
220
|
-
animation: gradient-shift 2s ease-in-out infinite,
|
222
|
+
animation: gradient-shift 2s ease-in-out infinite,
|
223
|
+
dot-bounce 1.4s ease-in-out infinite;
|
221
224
|
-webkit-text-stroke: 0.75px rgba(0, 0, 0, 0.15);
|
222
225
|
text-stroke: 0.75px rgba(0, 0, 0, 0.15);
|
223
226
|
margin-left: -0.125rem;
|
@@ -339,7 +342,8 @@
|
|
339
342
|
|
340
343
|
.lexi-input-container {
|
341
344
|
display: flex;
|
342
|
-
flex-direction:
|
345
|
+
flex-direction: row;
|
346
|
+
gap: 0.5rem;
|
343
347
|
border: 1px solid var(--gray-300);
|
344
348
|
border-radius: 0.5rem;
|
345
349
|
background-color: rgba(255, 255, 255, 0.8);
|
@@ -353,6 +357,7 @@
|
|
353
357
|
|
354
358
|
.lexi-textarea {
|
355
359
|
width: 100%;
|
360
|
+
height: 100%;
|
356
361
|
min-height: 35px;
|
357
362
|
border: none;
|
358
363
|
resize: none;
|
@@ -647,7 +652,8 @@
|
|
647
652
|
}
|
648
653
|
|
649
654
|
/* Hide standard collapse button when Lexi tray is collapsed */
|
650
|
-
.gamma-tray.lexi-tray.collapsed
|
655
|
+
.gamma-tray.lexi-tray.collapsed
|
656
|
+
> button[data-action='click->tray#toggle']:not(.lexi-floating-avatar) {
|
651
657
|
display: none;
|
652
658
|
}
|
653
659
|
|
@@ -683,7 +689,8 @@
|
|
683
689
|
height: 100vh;
|
684
690
|
}
|
685
691
|
|
686
|
-
.welcome-screen,
|
692
|
+
.welcome-screen,
|
693
|
+
.chat-screen {
|
687
694
|
grid-column: 1;
|
688
695
|
grid-row: 1;
|
689
696
|
}
|
@@ -1028,7 +1035,7 @@
|
|
1028
1035
|
}
|
1029
1036
|
|
1030
1037
|
/* Structured Input Validation Styles */
|
1031
|
-
.structured-input-container[data-invalid=
|
1038
|
+
.structured-input-container[data-invalid='true'] {
|
1032
1039
|
border-color: var(--red-500);
|
1033
1040
|
background-color: var(--red-50);
|
1034
1041
|
}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MetaWorkflows
|
4
|
+
module Concerns
|
5
|
+
module WorkflowParamsMergeable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def merge_matching_workflow_params(workflow_execution, execution_step, inputs = {})
|
9
|
+
merged_inputs = inputs.deep_symbolize_keys
|
10
|
+
return merged_inputs if workflow_execution.workflow_params.blank?
|
11
|
+
|
12
|
+
step_input_names = extract_input_variable_names(execution_step['input'] || [])
|
13
|
+
|
14
|
+
workflow_execution.workflow_params.each do |param_name, param_value|
|
15
|
+
param_string = param_name.to_s
|
16
|
+
param_symbol = param_name.to_sym
|
17
|
+
|
18
|
+
next unless step_input_names.include?(param_string) &&
|
19
|
+
!merged_inputs.key?(param_symbol) &&
|
20
|
+
!merged_inputs.key?(param_string)
|
21
|
+
|
22
|
+
merged_inputs[param_symbol] = param_value
|
23
|
+
end
|
24
|
+
|
25
|
+
merged_inputs
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def extract_input_variable_names(input_definitions)
|
31
|
+
input_definitions.filter_map do |input_def|
|
32
|
+
input_def.keys.find { |key| key != 'type' }&.to_s if input_def.is_a?(Hash) && input_def.keys.any?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -4,6 +4,7 @@ module MetaWorkflows
|
|
4
4
|
class MetaJob < MetaWorkflows::ApplicationJob
|
5
5
|
include MetaWorkflows::Concerns::ErrorHandling
|
6
6
|
include MetaWorkflows::Streamable
|
7
|
+
include MetaWorkflows::Concerns::WorkflowParamsMergeable
|
7
8
|
|
8
9
|
attr_accessor :chat, :inputs, :full_response, :user_id, :record, :auto_advancing, :manual_advancing,
|
9
10
|
:workflow_execution
|
@@ -33,8 +34,12 @@ module MetaWorkflows
|
|
33
34
|
current_step = workflow_execution.workflow_steps&.find_by(step: workflow_execution.current_step)
|
34
35
|
@chat = current_step&.chat
|
35
36
|
|
37
|
+
current_step_data = workflow_execution.step_data(workflow_execution.current_step)
|
38
|
+
merged_inputs = merge_matching_workflow_params(workflow_execution, current_step_data, inputs)
|
39
|
+
|
36
40
|
conversation = RubyConversations::Conversation.new(chat:)
|
37
|
-
conversation.with_prompt(params[:prompt_id], inputs:
|
41
|
+
conversation.with_prompt(params[:prompt_id], inputs: merged_inputs,
|
42
|
+
description: "Running #{params[:prompt_id]} process")
|
38
43
|
end
|
39
44
|
|
40
45
|
def continue_existing_conversation(params)
|
@@ -17,29 +17,16 @@
|
|
17
17
|
</div>
|
18
18
|
|
19
19
|
<div class="lexi-input-controls">
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
<!-- Send button -->
|
21
|
+
<% if local_assigns[:responding] %>
|
22
|
+
<button type="button" class="lexi-send-button responding" disabled>
|
23
|
+
<i class="fa-solid fa-arrow-up text-lg"></i>
|
23
24
|
</button>
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
<button type="button" class="lexi-icon-button" disabled>
|
28
|
-
<i class="fa-solid fa-waveform-lines text-lg"></i>
|
29
|
-
</button>
|
30
|
-
</div>
|
31
|
-
<div>
|
32
|
-
<!-- Send button -->
|
33
|
-
<% if local_assigns[:responding] %>
|
34
|
-
<button type="button" class="lexi-send-button responding" disabled>
|
35
|
-
<i class="fa-solid fa-arrow-up text-lg"></i>
|
36
|
-
</button>
|
37
|
-
<% else %>
|
38
|
-
<%= form.button type: "button", class: "lexi-send-button sm-btn sm-btn-primary", data: { "meta-workflows--lexi-form-submit-target": "submitButton", action: "click->meta-workflows--lexi-form-submit#handleSubmit" } do %>
|
39
|
-
<i class="fa-solid fa-arrow-up text-lg"></i>
|
40
|
-
<% end %>
|
25
|
+
<% else %>
|
26
|
+
<%= form.button type: "button", class: "lexi-send-button sm-btn sm-btn-primary", data: { "meta-workflows--lexi-form-submit-target": "submitButton", action: "click->meta-workflows--lexi-form-submit#handleSubmit" } do %>
|
27
|
+
<i class="fa-solid fa-arrow-up text-lg"></i>
|
41
28
|
<% end %>
|
42
|
-
|
29
|
+
<% end %>
|
43
30
|
</div>
|
44
31
|
</div>
|
45
32
|
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module MetaWorkflows
|
4
4
|
MAJOR = 0
|
5
5
|
MINOR = 9
|
6
|
-
PATCH =
|
6
|
+
PATCH = 28 # this is automatically incremented by the build process
|
7
7
|
|
8
8
|
VERSION = "#{MetaWorkflows::MAJOR}.#{MetaWorkflows::MINOR}.#{MetaWorkflows::PATCH}".freeze
|
9
9
|
end
|
@@ -5,6 +5,7 @@ module Services
|
|
5
5
|
class MetaWorkflowService
|
6
6
|
include ::MetaWorkflows::MetaWorkflowsHelper
|
7
7
|
include ::MetaWorkflows::Streamable
|
8
|
+
include ::MetaWorkflows::Concerns::WorkflowParamsMergeable
|
8
9
|
|
9
10
|
ACTIONS_WITHOUT_CONVERSATION = %w[record_redirect collection_create record_update].freeze
|
10
11
|
|
@@ -124,11 +125,13 @@ module Services
|
|
124
125
|
end
|
125
126
|
|
126
127
|
def process_human_action(execution_step, workflow_execution)
|
128
|
+
merged_inputs = merge_matching_workflow_params(workflow_execution, execution_step, inputs)
|
129
|
+
|
127
130
|
::MetaWorkflows::HumanInputJob.perform_later(
|
128
131
|
user_id: user&.id,
|
129
132
|
record: workflow_execution.record,
|
130
133
|
params: {
|
131
|
-
inputs:
|
134
|
+
inputs: merged_inputs,
|
132
135
|
prompt_id: execution_step['prompt_id']
|
133
136
|
}
|
134
137
|
)
|
@@ -136,11 +139,13 @@ module Services
|
|
136
139
|
end
|
137
140
|
|
138
141
|
def process_structured_human_action(execution_step, workflow_execution)
|
142
|
+
merged_inputs = merge_matching_workflow_params(workflow_execution, execution_step, inputs)
|
143
|
+
|
139
144
|
::MetaWorkflows::HumanInputJob.perform_later(
|
140
145
|
user_id: user&.id,
|
141
146
|
record: workflow_execution.record,
|
142
147
|
params: {
|
143
|
-
inputs:
|
148
|
+
inputs: merged_inputs,
|
144
149
|
prompt_id: execution_step['prompt_id']
|
145
150
|
}
|
146
151
|
)
|
@@ -235,9 +240,12 @@ module Services
|
|
235
240
|
def create_and_configure_conversation(chat, workflow_execution, execution_output, execution_step)
|
236
241
|
conversation = RubyConversations::Conversation.new(chat: chat)
|
237
242
|
conversation.tool = ::MetaWorkflows::Tools::MetaWorkflowTool.build(workflow_execution, execution_output)
|
243
|
+
|
244
|
+
merged_inputs = merge_matching_workflow_params(workflow_execution, execution_step, inputs)
|
245
|
+
|
238
246
|
conversation.with_prompt(
|
239
247
|
execution_step['prompt_id'],
|
240
|
-
inputs:
|
248
|
+
inputs: merged_inputs,
|
241
249
|
description: "Executing step #{workflow_execution.current_step} of " \
|
242
250
|
"workflow #{workflow_execution.workflow.name}"
|
243
251
|
)
|
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.28
|
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-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -146,6 +146,7 @@ files:
|
|
146
146
|
- app/helpers/meta_workflows/status_badge_helper.rb
|
147
147
|
- app/jobs/meta_workflows/application_job.rb
|
148
148
|
- app/jobs/meta_workflows/concerns/error_handling.rb
|
149
|
+
- app/jobs/meta_workflows/concerns/workflow_params_mergeable.rb
|
149
150
|
- app/jobs/meta_workflows/human_input_job.rb
|
150
151
|
- app/jobs/meta_workflows/meta_job.rb
|
151
152
|
- app/jobs/meta_workflows/meta_workflow_job.rb
|