meta_workflows 0.9.18 → 0.9.20
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/assets/javascripts/meta_workflows/controllers/structured_form_submit_controller.js +18 -0
- data/app/assets/stylesheets/meta_workflows/application.css +221 -0
- data/app/controllers/meta_workflows/humans_controller.rb +3 -2
- data/app/controllers/meta_workflows/structured_humans_controller.rb +74 -0
- data/app/helpers/meta_workflows/meta_workflows_helper.rb +6 -1
- data/app/jobs/meta_workflows/concerns/error_handling.rb +31 -0
- data/app/jobs/meta_workflows/human_input_job.rb +25 -2
- data/app/jobs/meta_workflows/meta_job.rb +17 -21
- data/app/models/meta_workflows/execution_chat_history.rb +23 -0
- data/app/models/meta_workflows/workflow_execution.rb +1 -0
- data/app/services/meta_workflows/message_history_service.rb +42 -0
- data/app/views/meta_workflows/_assistant_message_bubble.html.erb +9 -0
- data/app/views/meta_workflows/_checkbox_input.html.erb +24 -0
- data/app/views/meta_workflows/_lexi_chat_alpha_tray.html.erb +7 -1
- data/app/views/meta_workflows/_radio_input.html.erb +26 -0
- data/app/views/meta_workflows/_response_form_lexi.html.erb +34 -31
- data/app/views/meta_workflows/_response_lexi.html.erb +27 -36
- data/app/views/meta_workflows/_slider_input.html.erb +60 -0
- data/app/views/meta_workflows/_structured_input.html.erb +28 -0
- data/app/views/meta_workflows/_user_message_bubble.html.erb +8 -0
- data/config/routes.rb +1 -0
- data/db/migrate/20250714192853_create_meta_workflows_execution_chat_histories.rb +12 -0
- data/lib/meta_workflows/version.rb +1 -1
- data/lib/services/meta_workflows/meta_workflow_service.rb +14 -0
- metadata +14 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b91afe608b3c5260eebee5a4cc1211621c85171cf2ddc3740f5c695bc6df73f
|
4
|
+
data.tar.gz: cbafaa30a15e992936bdbe66ec444fb15740bbac999c2137e11f67d26870eea0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0886296d806086437b57857c1e509ea76a6db6c784df7bee23ff54428220d819e94265083165a0e33a17715f9ab00a9f0efd0a553d365c21b5b68a93b7c56c4e'
|
7
|
+
data.tar.gz: 76b74c3f7508be1ee96547583adaf43a13f41c8af6ba74023fdcd3cafd0cd59c34ff535eeb2490ee4ba0c03c23c75bfb6ed248630d179938e9dedd724fd50add
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { Controller } from '@hotwired/stimulus';
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ['form', 'radioInput', 'checkboxInput', 'sliderInput'];
|
5
|
+
|
6
|
+
connect() {
|
7
|
+
this.submitted = false;
|
8
|
+
}
|
9
|
+
|
10
|
+
handleSubmit() {
|
11
|
+
if (this.submitted) {
|
12
|
+
return;
|
13
|
+
}
|
14
|
+
|
15
|
+
this.submitted = true;
|
16
|
+
this.formTarget.requestSubmit();
|
17
|
+
}
|
18
|
+
}
|
@@ -824,3 +824,224 @@
|
|
824
824
|
.lexi-error-content .lexi-loader-ellipses {
|
825
825
|
margin-left: 0.375rem;
|
826
826
|
}
|
827
|
+
|
828
|
+
/* Structured Input Styles */
|
829
|
+
.structured-input-container {
|
830
|
+
padding: 1rem;
|
831
|
+
background-color: rgba(255, 255, 255, 0.9);
|
832
|
+
border-radius: 0.75rem;
|
833
|
+
border: 1px solid var(--gray-300);
|
834
|
+
margin-bottom: 1rem;
|
835
|
+
}
|
836
|
+
|
837
|
+
.structured-input-options {
|
838
|
+
display: flex;
|
839
|
+
flex-direction: column;
|
840
|
+
gap: 0.75rem;
|
841
|
+
}
|
842
|
+
|
843
|
+
/* Radio Button Styles */
|
844
|
+
.structured-radio-option {
|
845
|
+
display: flex;
|
846
|
+
align-items: center;
|
847
|
+
gap: 0.75rem;
|
848
|
+
padding: 0.75rem 1rem;
|
849
|
+
border: 1px solid var(--gray-300);
|
850
|
+
border-radius: 0.5rem;
|
851
|
+
background-color: white;
|
852
|
+
cursor: pointer;
|
853
|
+
transition: all 0.2s ease;
|
854
|
+
}
|
855
|
+
|
856
|
+
.structured-radio-option:hover {
|
857
|
+
background-color: var(--gray-50);
|
858
|
+
border-color: var(--purple-300);
|
859
|
+
}
|
860
|
+
|
861
|
+
.structured-radio-option:has(.structured-radio-input:checked) {
|
862
|
+
background-color: var(--purple-50);
|
863
|
+
border-color: var(--purple-500);
|
864
|
+
}
|
865
|
+
|
866
|
+
.structured-radio-input {
|
867
|
+
width: 1.25rem;
|
868
|
+
height: 1.25rem;
|
869
|
+
accent-color: var(--purple-600);
|
870
|
+
cursor: pointer;
|
871
|
+
}
|
872
|
+
|
873
|
+
.structured-radio-label {
|
874
|
+
font-size: 1rem;
|
875
|
+
font-weight: 500;
|
876
|
+
color: var(--gray-800);
|
877
|
+
cursor: pointer;
|
878
|
+
flex: 1;
|
879
|
+
}
|
880
|
+
|
881
|
+
/* Checkbox Styles */
|
882
|
+
.structured-checkbox-option {
|
883
|
+
display: flex;
|
884
|
+
align-items: center;
|
885
|
+
gap: 0.75rem;
|
886
|
+
padding: 0.75rem 1rem;
|
887
|
+
border: 1px solid var(--gray-300);
|
888
|
+
border-radius: 0.5rem;
|
889
|
+
background-color: white;
|
890
|
+
cursor: pointer;
|
891
|
+
transition: all 0.2s ease;
|
892
|
+
}
|
893
|
+
|
894
|
+
.structured-checkbox-option:hover {
|
895
|
+
background-color: var(--gray-50);
|
896
|
+
border-color: var(--purple-300);
|
897
|
+
}
|
898
|
+
|
899
|
+
.structured-checkbox-option:has(.structured-checkbox-input:checked) {
|
900
|
+
background-color: var(--purple-50);
|
901
|
+
border-color: var(--purple-500);
|
902
|
+
}
|
903
|
+
|
904
|
+
.structured-checkbox-input {
|
905
|
+
width: 1.25rem;
|
906
|
+
height: 1.25rem;
|
907
|
+
accent-color: var(--purple-600);
|
908
|
+
cursor: pointer;
|
909
|
+
}
|
910
|
+
|
911
|
+
.structured-checkbox-label {
|
912
|
+
font-size: 1rem;
|
913
|
+
font-weight: 500;
|
914
|
+
color: var(--gray-800);
|
915
|
+
cursor: pointer;
|
916
|
+
flex: 1;
|
917
|
+
}
|
918
|
+
|
919
|
+
/* Slider Styles */
|
920
|
+
.structured-slider-container {
|
921
|
+
padding: 1rem;
|
922
|
+
}
|
923
|
+
|
924
|
+
.structured-slider-labels {
|
925
|
+
display: flex;
|
926
|
+
justify-content: space-between;
|
927
|
+
margin-bottom: 1rem;
|
928
|
+
font-size: 0.875rem;
|
929
|
+
color: var(--gray-600);
|
930
|
+
font-weight: 500;
|
931
|
+
}
|
932
|
+
|
933
|
+
.structured-slider-wrapper {
|
934
|
+
display: flex;
|
935
|
+
flex-direction: column;
|
936
|
+
gap: 0.5rem;
|
937
|
+
}
|
938
|
+
|
939
|
+
.structured-slider-input {
|
940
|
+
width: 100%;
|
941
|
+
height: 0.5rem;
|
942
|
+
border-radius: 0.25rem;
|
943
|
+
background: var(--gray-200);
|
944
|
+
outline: none;
|
945
|
+
-webkit-appearance: none;
|
946
|
+
appearance: none;
|
947
|
+
cursor: pointer;
|
948
|
+
}
|
949
|
+
|
950
|
+
.structured-slider-input::-webkit-slider-thumb {
|
951
|
+
-webkit-appearance: none;
|
952
|
+
appearance: none;
|
953
|
+
width: 1.5rem;
|
954
|
+
height: 1.5rem;
|
955
|
+
border-radius: 50%;
|
956
|
+
background: var(--purple-600);
|
957
|
+
cursor: pointer;
|
958
|
+
border: 2px solid white;
|
959
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
960
|
+
transition: all 0.2s ease;
|
961
|
+
}
|
962
|
+
|
963
|
+
.structured-slider-input::-webkit-slider-thumb:hover {
|
964
|
+
background: var(--purple-700);
|
965
|
+
transform: scale(1.1);
|
966
|
+
}
|
967
|
+
|
968
|
+
.structured-slider-input::-moz-range-thumb {
|
969
|
+
width: 1.5rem;
|
970
|
+
height: 1.5rem;
|
971
|
+
border-radius: 50%;
|
972
|
+
background: var(--purple-600);
|
973
|
+
cursor: pointer;
|
974
|
+
border: 2px solid white;
|
975
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
976
|
+
transition: all 0.2s ease;
|
977
|
+
}
|
978
|
+
|
979
|
+
.structured-slider-input::-moz-range-thumb:hover {
|
980
|
+
background: var(--purple-700);
|
981
|
+
transform: scale(1.1);
|
982
|
+
}
|
983
|
+
|
984
|
+
.structured-slider-value {
|
985
|
+
text-align: center;
|
986
|
+
font-size: 1.125rem;
|
987
|
+
font-weight: 600;
|
988
|
+
color: var(--purple-700);
|
989
|
+
padding: 0.5rem;
|
990
|
+
background-color: var(--purple-50);
|
991
|
+
border-radius: 0.375rem;
|
992
|
+
border: 1px solid var(--purple-200);
|
993
|
+
}
|
994
|
+
|
995
|
+
/* Structured Input Submit Button */
|
996
|
+
.structured-submit-button {
|
997
|
+
width: 3rem;
|
998
|
+
height: 3rem;
|
999
|
+
border-radius: 50%;
|
1000
|
+
display: flex;
|
1001
|
+
align-items: center;
|
1002
|
+
justify-content: center;
|
1003
|
+
background-color: var(--purple-600);
|
1004
|
+
color: white;
|
1005
|
+
transition: all 0.2s;
|
1006
|
+
border: none;
|
1007
|
+
cursor: pointer;
|
1008
|
+
margin-top: 1rem;
|
1009
|
+
align-self: flex-end;
|
1010
|
+
}
|
1011
|
+
|
1012
|
+
.structured-submit-button:hover {
|
1013
|
+
background-color: var(--purple-700);
|
1014
|
+
transform: scale(1.05);
|
1015
|
+
}
|
1016
|
+
|
1017
|
+
.structured-submit-button:disabled {
|
1018
|
+
background-color: var(--gray-400);
|
1019
|
+
cursor: not-allowed;
|
1020
|
+
transform: none;
|
1021
|
+
}
|
1022
|
+
|
1023
|
+
/* Structured Input Validation Styles */
|
1024
|
+
.structured-input-container[data-invalid="true"] {
|
1025
|
+
border-color: var(--red-500);
|
1026
|
+
background-color: var(--red-50);
|
1027
|
+
}
|
1028
|
+
|
1029
|
+
.structured-input-error {
|
1030
|
+
color: var(--red-600);
|
1031
|
+
font-size: 0.875rem;
|
1032
|
+
margin-top: 0.5rem;
|
1033
|
+
font-weight: 500;
|
1034
|
+
}
|
1035
|
+
|
1036
|
+
/* Screen reader only class for accessibility */
|
1037
|
+
.sr-only {
|
1038
|
+
position: absolute;
|
1039
|
+
width: 1px;
|
1040
|
+
height: 1px;
|
1041
|
+
padding: 0;
|
1042
|
+
margin: -1px;
|
1043
|
+
overflow: hidden;
|
1044
|
+
clip: rect(0, 0, 0, 0);
|
1045
|
+
white-space: nowrap;
|
1046
|
+
border: 0;
|
1047
|
+
}
|
@@ -9,10 +9,10 @@ module MetaWorkflows
|
|
9
9
|
def update
|
10
10
|
auto_advancing = should_auto_advance?
|
11
11
|
|
12
|
-
if params[:
|
12
|
+
if params[:manual_advance].present? || auto_advancing
|
13
13
|
@workflow_execution.increment_step
|
14
14
|
render_loader_stream
|
15
|
-
process_human_input(auto_advancing:, manual_advancing: params[:
|
15
|
+
process_human_input(auto_advancing:, manual_advancing: params[:manual_advance].present?)
|
16
16
|
else
|
17
17
|
render_response_form_stream
|
18
18
|
process_human_input
|
@@ -23,6 +23,7 @@ module MetaWorkflows
|
|
23
23
|
|
24
24
|
def should_auto_advance?
|
25
25
|
step_repetitions = current_step_repetitions
|
26
|
+
return true if params[:auto_advance].present?
|
26
27
|
return false unless step_repetitions
|
27
28
|
|
28
29
|
new_repetition = @workflow_step.increment_repetition!
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MetaWorkflows
|
4
|
+
class StructuredHumansController < MetaController
|
5
|
+
include MetaWorkflows::MetaWorkflowsHelper
|
6
|
+
include MetaWorkflows::Streamable
|
7
|
+
before_action :set_workflow_data, only: [:update]
|
8
|
+
|
9
|
+
def update
|
10
|
+
render_loader_stream
|
11
|
+
process_structured_human_input
|
12
|
+
@workflow_execution.increment_step
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def process_structured_human_input
|
18
|
+
MetaWorkflows::HumanInputJob.perform_later(
|
19
|
+
user_id: current_user.id,
|
20
|
+
record: @record,
|
21
|
+
auto_advancing: true,
|
22
|
+
manual_advancing: false,
|
23
|
+
params: {
|
24
|
+
inputs: extract_structured_inputs,
|
25
|
+
chat_id: params[:chat_id],
|
26
|
+
prompt_id: @prompt_id
|
27
|
+
}
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def extract_structured_inputs
|
32
|
+
step_data = @workflow_execution.step_data(@workflow_execution.current_step)
|
33
|
+
structured_input_config = step_data&.dig('structured_input')
|
34
|
+
return params[:message] unless structured_input_config
|
35
|
+
|
36
|
+
case structured_input_config['type']
|
37
|
+
when 'single_choice'
|
38
|
+
params[:single_choice_selection]
|
39
|
+
when 'multiple_choice'
|
40
|
+
params[:multiple_choice_selections]
|
41
|
+
when 'range'
|
42
|
+
params[:range_value]
|
43
|
+
else
|
44
|
+
params[:message]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def render_loader_stream
|
49
|
+
respond_to do |format|
|
50
|
+
format.turbo_stream do
|
51
|
+
render turbo_stream: [
|
52
|
+
build_loader_stream(record: @record, workflow_execution: @workflow_execution),
|
53
|
+
build_response_form_stream(record: @record, workflow_execution: @workflow_execution,
|
54
|
+
response_enabled: false, responding: false, chat_id: params[:chat_id])
|
55
|
+
]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def render_response_form_stream
|
61
|
+
respond_to do |format|
|
62
|
+
format.turbo_stream do
|
63
|
+
render turbo_stream: build_response_form_stream(
|
64
|
+
record: @record,
|
65
|
+
workflow_execution: @workflow_execution,
|
66
|
+
response_enabled: true,
|
67
|
+
responding: true,
|
68
|
+
chat_id: params[:chat_id]
|
69
|
+
)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -2,11 +2,12 @@
|
|
2
2
|
|
3
3
|
module MetaWorkflows
|
4
4
|
module MetaWorkflowsHelper
|
5
|
-
def target_frame_id(record, form: nil, loader: nil)
|
5
|
+
def target_frame_id(record, form: nil, loader: nil, structured_input: nil)
|
6
6
|
base_id = "#{record.class.name.downcase}_#{record.id}"
|
7
7
|
|
8
8
|
return "#{base_id}_loader" if loader
|
9
9
|
return "#{base_id}_form" if form
|
10
|
+
return "#{base_id}_structured_input" if structured_input
|
10
11
|
|
11
12
|
base_id
|
12
13
|
end
|
@@ -27,6 +28,10 @@ module MetaWorkflows
|
|
27
28
|
'meta_workflows/response_form_lexi'
|
28
29
|
end
|
29
30
|
|
31
|
+
def meta_structured_input
|
32
|
+
'meta_workflows/structured_input'
|
33
|
+
end
|
34
|
+
|
30
35
|
def meta_response
|
31
36
|
'meta_workflows/response_lexi'
|
32
37
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MetaWorkflows
|
4
|
+
module Concerns
|
5
|
+
module ErrorHandling
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def handle_llm_error(error, workflow_step, workflow_execution, conversation)
|
11
|
+
log_error(error)
|
12
|
+
store_error_in_workflow_step(error, workflow_step, conversation)
|
13
|
+
broadcast_error_response(workflow_execution)
|
14
|
+
end
|
15
|
+
|
16
|
+
def log_error(error)
|
17
|
+
Rails.logger.error("LLM Error in MetaJob: #{error.message}")
|
18
|
+
Rails.logger.error(error.backtrace.join("\n"))
|
19
|
+
end
|
20
|
+
|
21
|
+
def store_error_in_workflow_step(error, workflow_step, conversation)
|
22
|
+
workflow_step&.record_error(error, additional_details: {
|
23
|
+
job_class: self.class.name,
|
24
|
+
chat_id: chat&.id,
|
25
|
+
conversation_id: conversation&.id,
|
26
|
+
full_response_length: full_response&.length || 0
|
27
|
+
})
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -5,8 +5,11 @@ module MetaWorkflows
|
|
5
5
|
include MetaWorkflows::MetaWorkflowsHelper
|
6
6
|
queue_as :default
|
7
7
|
|
8
|
+
attr_accessor :current_step_data, :structured_input_config, :is_structured_input
|
9
|
+
|
8
10
|
def perform(user_id:, record:, params:, auto_advancing: false, manual_advancing: false)
|
9
11
|
setup(user_id, record, auto_advancing, manual_advancing)
|
12
|
+
set_workflow_step_variables
|
10
13
|
conversation = initialize_conversation(params)
|
11
14
|
process_and_broadcast(conversation) unless manual_advancing
|
12
15
|
advance_workflow(params) if advancing?
|
@@ -14,6 +17,12 @@ module MetaWorkflows
|
|
14
17
|
|
15
18
|
private
|
16
19
|
|
20
|
+
def set_workflow_step_variables
|
21
|
+
@current_step_data = workflow_execution&.step_data(workflow_execution.current_step)
|
22
|
+
@structured_input_config = current_step_data&.dig('structured_input')
|
23
|
+
@is_structured_input = structured_input_config.present? && current_step_data&.dig('action') == 'structured_human'
|
24
|
+
end
|
25
|
+
|
17
26
|
def broadcast_response(chat, full_response)
|
18
27
|
user_messages = chat.messages.where(role: 'user').order(:created_at)
|
19
28
|
messages = chat.messages.order(:created_at)
|
@@ -42,7 +51,6 @@ module MetaWorkflows
|
|
42
51
|
end
|
43
52
|
|
44
53
|
def broadcast_form(chat)
|
45
|
-
workflow_execution = active_workflow_execution
|
46
54
|
Turbo::StreamsChannel.broadcast_replace_to(
|
47
55
|
turbo_stream_name(record),
|
48
56
|
target: target_frame_id(record, form: true),
|
@@ -51,7 +59,22 @@ module MetaWorkflows
|
|
51
59
|
workflow_execution_id: workflow_execution&.id,
|
52
60
|
response_enabled: true,
|
53
61
|
chat_id: chat.id,
|
54
|
-
step_has_repetitions: current_step_has_repetitions?(workflow_execution)
|
62
|
+
step_has_repetitions: current_step_has_repetitions?(workflow_execution),
|
63
|
+
is_structured_input: is_structured_input }
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
def broadcast_structured_input(chat)
|
68
|
+
Turbo::StreamsChannel.broadcast_replace_to(
|
69
|
+
turbo_stream_name(record),
|
70
|
+
target: target_frame_id(record, structured_input: true),
|
71
|
+
partial: meta_structured_input,
|
72
|
+
locals: {
|
73
|
+
record: record,
|
74
|
+
chat_id: chat.id,
|
75
|
+
structured_input_config: structured_input_config,
|
76
|
+
is_structured_input: is_structured_input
|
77
|
+
}
|
55
78
|
)
|
56
79
|
end
|
57
80
|
|
@@ -2,9 +2,11 @@
|
|
2
2
|
|
3
3
|
module MetaWorkflows
|
4
4
|
class MetaJob < MetaWorkflows::ApplicationJob
|
5
|
+
include MetaWorkflows::Concerns::ErrorHandling
|
5
6
|
include MetaWorkflows::Streamable
|
6
7
|
|
7
|
-
attr_accessor :chat, :inputs, :full_response, :user_id, :record, :auto_advancing, :manual_advancing
|
8
|
+
attr_accessor :chat, :inputs, :full_response, :user_id, :record, :auto_advancing, :manual_advancing,
|
9
|
+
:workflow_execution
|
8
10
|
|
9
11
|
private
|
10
12
|
|
@@ -14,6 +16,7 @@ module MetaWorkflows
|
|
14
16
|
@full_response = +''
|
15
17
|
@auto_advancing = auto_advancing
|
16
18
|
@manual_advancing = manual_advancing
|
19
|
+
@workflow_execution = active_workflow_execution
|
17
20
|
end
|
18
21
|
|
19
22
|
def initialize_conversation(params)
|
@@ -27,7 +30,6 @@ module MetaWorkflows
|
|
27
30
|
def initialize_new_conversation(params)
|
28
31
|
inputs = params.symbolize_keys[:inputs]
|
29
32
|
|
30
|
-
workflow_execution = active_workflow_execution
|
31
33
|
current_step = workflow_execution.workflow_steps&.find_by(step: workflow_execution.current_step)
|
32
34
|
@chat = current_step&.chat
|
33
35
|
|
@@ -44,7 +46,6 @@ module MetaWorkflows
|
|
44
46
|
end
|
45
47
|
|
46
48
|
def process_and_broadcast(conversation)
|
47
|
-
workflow_execution = active_workflow_execution
|
48
49
|
workflow_step = current_workflow_step(workflow_execution)
|
49
50
|
|
50
51
|
begin
|
@@ -70,27 +71,18 @@ module MetaWorkflows
|
|
70
71
|
|
71
72
|
def finalize_conversation(conversation)
|
72
73
|
chat.update!(conversation_id: conversation.id) if chat.conversation_id.blank?
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
def handle_llm_error(error, workflow_step, workflow_execution, conversation)
|
77
|
-
log_error(error)
|
78
|
-
store_error_in_workflow_step(error, workflow_step, conversation)
|
79
|
-
broadcast_error_response(workflow_execution)
|
80
|
-
end
|
74
|
+
persist_messages_to_history
|
75
|
+
return if auto_advancing
|
81
76
|
|
82
|
-
|
83
|
-
|
84
|
-
Rails.logger.error(error.backtrace.join("\n"))
|
77
|
+
broadcast_form(chat)
|
78
|
+
broadcast_structured_input(chat)
|
85
79
|
end
|
86
80
|
|
87
|
-
def
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
full_response_length: full_response&.length || 0
|
93
|
-
})
|
81
|
+
def persist_messages_to_history
|
82
|
+
MetaWorkflows::MessageHistoryService.new(
|
83
|
+
chat: chat,
|
84
|
+
workflow_execution: workflow_execution
|
85
|
+
).persist_messages_to_history
|
94
86
|
end
|
95
87
|
|
96
88
|
def current_workflow_step(workflow_execution)
|
@@ -103,6 +95,10 @@ module MetaWorkflows
|
|
103
95
|
raise NotImplementedError
|
104
96
|
end
|
105
97
|
|
98
|
+
def broadcast_structured_input(chat)
|
99
|
+
raise NotImplementedError
|
100
|
+
end
|
101
|
+
|
106
102
|
def broadcast_response(chat, full_response)
|
107
103
|
raise NotImplementedError
|
108
104
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MetaWorkflows
|
4
|
+
class ExecutionChatHistory < ApplicationRecord
|
5
|
+
belongs_to :workflow_execution, class_name: 'MetaWorkflows::WorkflowExecution'
|
6
|
+
|
7
|
+
def append_message(message_data)
|
8
|
+
update!(history: history + [message_data])
|
9
|
+
end
|
10
|
+
|
11
|
+
def clear_history
|
12
|
+
update!(history: [])
|
13
|
+
end
|
14
|
+
|
15
|
+
def message_count
|
16
|
+
history.length
|
17
|
+
end
|
18
|
+
|
19
|
+
def last_message
|
20
|
+
history.last
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -7,6 +7,7 @@ module MetaWorkflows
|
|
7
7
|
belongs_to :workflow, class_name: 'MetaWorkflows::Workflow'
|
8
8
|
belongs_to :record, polymorphic: true
|
9
9
|
has_many :workflow_steps, dependent: :destroy, class_name: 'MetaWorkflows::WorkflowStep'
|
10
|
+
has_one :execution_chat_history, dependent: :destroy, class_name: 'MetaWorkflows::ExecutionChatHistory'
|
10
11
|
|
11
12
|
# Default values for fields
|
12
13
|
attribute :current_step, :integer, default: 0
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MetaWorkflows
|
4
|
+
class MessageHistoryService < MetaWorkflows::ApplicationService
|
5
|
+
def initialize(chat:, workflow_execution:)
|
6
|
+
super()
|
7
|
+
@chat = chat
|
8
|
+
@workflow_execution = workflow_execution
|
9
|
+
end
|
10
|
+
|
11
|
+
def persist_messages_to_history
|
12
|
+
# Ensure ExecutionChatHistory exists
|
13
|
+
chat_history = workflow_execution.execution_chat_history ||
|
14
|
+
workflow_execution.create_execution_chat_history!
|
15
|
+
|
16
|
+
# Get all messages from the current chat (minus the first message since that is the system message)
|
17
|
+
messages = chat.messages.order(:created_at)[1..]
|
18
|
+
|
19
|
+
# Convert messages to a format suitable for persistence
|
20
|
+
messages.each do |message|
|
21
|
+
message_data = {
|
22
|
+
role: message.role,
|
23
|
+
content: message.content,
|
24
|
+
model_id: message.model_id,
|
25
|
+
input_tokens: message.input_tokens,
|
26
|
+
output_tokens: message.output_tokens,
|
27
|
+
created_at: message.created_at.iso8601,
|
28
|
+
step: workflow_execution.current_step
|
29
|
+
}
|
30
|
+
|
31
|
+
# Only append if not already in history (to avoid duplicates)
|
32
|
+
unless chat_history.history.any? { |h| h['content'] == message.content && h['role'] == message.role }
|
33
|
+
chat_history.append_message(message_data)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
attr_reader :chat, :workflow_execution
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<!-- Lexi message bubble (left-aligned, white background) -->
|
2
|
+
<div class="lexi-message-assistant">
|
3
|
+
<!-- Message Bubble -->
|
4
|
+
<div class="lexi-message-bubble-assistant">
|
5
|
+
<div class="lexi-message-content-assistant">
|
6
|
+
<%= markdown(content) %>
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
</div>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<div class="structured-input-container"
|
2
|
+
role="group"
|
3
|
+
aria-labelledby="structured-checkbox-legend">
|
4
|
+
<legend id="structured-checkbox-legend" class="sr-only">Select one or more options</legend>
|
5
|
+
<div class="structured-input-options">
|
6
|
+
<% options.each_with_index do |option, index| %>
|
7
|
+
<div class="structured-checkbox-option">
|
8
|
+
<input
|
9
|
+
type="checkbox"
|
10
|
+
id="multiple_choice_<%= index %>"
|
11
|
+
name="multiple_choice_selections[]"
|
12
|
+
value="<%= option['value'] %>"
|
13
|
+
class="structured-checkbox-input"
|
14
|
+
data-action="change->meta-workflows--structured-form-submit#handleSubmit"
|
15
|
+
aria-describedby="structured-checkbox-help"
|
16
|
+
>
|
17
|
+
<label for="multiple_choice_<%= index %>" class="structured-checkbox-label">
|
18
|
+
<%= option['label'] %>
|
19
|
+
</label>
|
20
|
+
</div>
|
21
|
+
<% end %>
|
22
|
+
</div>
|
23
|
+
<div id="structured-checkbox-help" class="sr-only">Use space to select/deselect options</div>
|
24
|
+
</div>
|
@@ -1,6 +1,7 @@
|
|
1
1
|
<%# Lexi Chat Alpha Tray (Center Display) %>
|
2
2
|
<% active_execution = local_assigns[:record]&.workflow_executions&.order(created_at: :desc)&.first %>
|
3
3
|
<% active_chat = active_execution&.workflow_steps&.last&.chat %>
|
4
|
+
<% chat_history = active_execution&.execution_chat_history %>
|
4
5
|
|
5
6
|
<div class="lexi-chat-alpha-tray">
|
6
7
|
<%# Header with Lexi logo left and close button right %>
|
@@ -34,13 +35,18 @@
|
|
34
35
|
user_messages: active_chat&.messages&.where(role: 'user')&.order(:created_at) || [],
|
35
36
|
last_message: active_chat&.messages&.last,
|
36
37
|
full_response: nil,
|
37
|
-
is_streaming: false
|
38
|
+
is_streaming: false,
|
39
|
+
chat_history: chat_history
|
38
40
|
} %>
|
39
41
|
</div>
|
40
42
|
|
41
43
|
<%# Input area (fixed at bottom) %>
|
42
44
|
<div class="lexi-chat-alpha-input-area">
|
43
45
|
<div class="lexi-chat-alpha-input-container">
|
46
|
+
<!-- Structured Input (always rendered, conditionally displays content) -->
|
47
|
+
<%= render meta_structured_input, record: local_assigns[:record], structured_input_config: false, is_structured_input: false %>
|
48
|
+
|
49
|
+
<!-- Regular Form Input (always present) -->
|
44
50
|
<%= render partial: meta_response_form, locals: {record: local_assigns[:record], response_enabled: true, workflow_execution_id: active_execution&.id, chat_id: active_execution&.workflow_steps&.last&.chat&.id, step_has_repetitions: true } %>
|
45
51
|
</div>
|
46
52
|
</div>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<div class="structured-input-container"
|
2
|
+
role="radiogroup"
|
3
|
+
aria-labelledby="structured-radio-legend"
|
4
|
+
aria-required="true">
|
5
|
+
<legend id="structured-radio-legend" class="sr-only">Select one option</legend>
|
6
|
+
<div class="structured-input-options">
|
7
|
+
<% options.each_with_index do |option, index| %>
|
8
|
+
<div class="structured-radio-option">
|
9
|
+
<input
|
10
|
+
type="radio"
|
11
|
+
id="single_choice_<%= index %>"
|
12
|
+
name="single_choice_selection"
|
13
|
+
value="<%= option['value'] %>"
|
14
|
+
class="structured-radio-input"
|
15
|
+
data-action="change->meta-workflows--structured-form-submit#handleSubmit"
|
16
|
+
aria-describedby="structured-radio-help"
|
17
|
+
required
|
18
|
+
>
|
19
|
+
<label for="single_choice_<%= index %>" class="structured-radio-label">
|
20
|
+
<%= option['label'] %>
|
21
|
+
</label>
|
22
|
+
</div>
|
23
|
+
<% end %>
|
24
|
+
</div>
|
25
|
+
<div id="structured-radio-help" class="sr-only">Use arrow keys to navigate between options</div>
|
26
|
+
</div>
|
@@ -1,44 +1,47 @@
|
|
1
1
|
<%= turbo_frame_tag target_frame_id(record, form: true) do %>
|
2
2
|
<div>
|
3
|
-
<%= form_with url:
|
3
|
+
<%= form_with url: workflow_execution_id.present? ? meta_workflows.human_path(workflow_execution_id) : "#",
|
4
4
|
method: :patch,
|
5
5
|
id: "#{target_frame_id(record, form: true)}_lexi",
|
6
6
|
data: { controller: "meta-workflows--lexi-form-submit", "meta-workflows--lexi-form-submit-target": "form" } do |form| %>
|
7
7
|
<%= form.hidden_field :chat_id, value: chat_id %>
|
8
|
+
<% if local_assigns[:is_structured_input] %>
|
9
|
+
<%= form.hidden_field :auto_advance, value: true %>
|
10
|
+
<% end %>
|
8
11
|
<fieldset>
|
9
12
|
<div class="lexi-form-container">
|
10
13
|
<!-- Input Container -->
|
11
14
|
<div class="lexi-input-container lexi-input-max-height">
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
15
|
+
<!-- Text Input -->
|
16
|
+
<div class="lexi-textarea-wrapper">
|
17
|
+
<%= form.text_area :message, rows: 1, placeholder: random_chat_placeholder, disabled: local_assigns[:responding] || !local_assigns[:response_enabled], class: "lexi-textarea lexi-textarea-min-height", data: { action: "keypress.enter->meta-workflows--lexi-form-submit#handleKeyDown submit->meta-workflows--lexi-form-submit#handleSubmit", "meta-workflows--lexi-form-submit-target": "textarea" } %>
|
18
|
+
</div>
|
16
19
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
20
|
+
<div class="lexi-input-controls">
|
21
|
+
<div class="lexi-input-icons">
|
22
|
+
<button type="button" class="lexi-icon-button" disabled>
|
23
|
+
<i class="fa-solid fa-microphone text-lg"></i>
|
24
|
+
</button>
|
25
|
+
<button type="button" class="lexi-icon-button" disabled>
|
26
|
+
<i class="fa-solid fa-headphones text-lg"></i>
|
27
|
+
</button>
|
28
|
+
<button type="button" class="lexi-icon-button" disabled>
|
29
|
+
<i class="fa-solid fa-waveform-lines text-lg"></i>
|
30
|
+
</button>
|
31
|
+
</div>
|
32
|
+
<div>
|
33
|
+
<!-- Send button -->
|
34
|
+
<% if local_assigns[:responding] %>
|
35
|
+
<button type="button" class="lexi-send-button responding" disabled>
|
36
|
+
<i class="fa-solid fa-arrow-up text-lg"></i>
|
37
|
+
</button>
|
38
|
+
<% else %>
|
39
|
+
<%= form.button type: "submit", class: "lexi-send-button sm-btn sm-btn-primary" do %>
|
40
|
+
<i class="fa-solid fa-arrow-up text-lg"></i>
|
41
|
+
<% end %>
|
42
|
+
<% end %>
|
43
|
+
</div>
|
44
|
+
</div>
|
42
45
|
</div>
|
43
46
|
|
44
47
|
<p class="lexi-recording-notice">This chat is being recorded.</p>
|
@@ -48,7 +51,7 @@
|
|
48
51
|
<!-- Empty placeholder to prevent layout jank -->
|
49
52
|
<div aria-hidden="true"></div>
|
50
53
|
<% else %>
|
51
|
-
<%= form.button type: "submit", name: "
|
54
|
+
<%= form.button type: "submit", name: "manual_advance", value: "true", class: "lexi-advance-button #{'opacity-50 cursor-not-allowed' if local_assigns[:responding]}", disabled: local_assigns[:responding] do %>
|
52
55
|
<i class="fa-light fa-arrow-right"></i> Next
|
53
56
|
<% end %>
|
54
57
|
<% end %>
|
@@ -1,46 +1,37 @@
|
|
1
1
|
<%= turbo_frame_tag target_frame_id(record) do %>
|
2
2
|
<div id="response-content-container" class="lexi-response-container" data-controller="response-scroll">
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
<%
|
7
|
-
<% next if
|
8
|
-
<% next if message.role == 'user' && message == user_messages.first %>
|
9
|
-
<% next if is_streaming && message.role == 'assistant' && message == last_message %>
|
3
|
+
|
4
|
+
<!-- Show historical messages from ExecutionChatHistory -->
|
5
|
+
<% if local_assigns[:chat_history]&.history&.any? %>
|
6
|
+
<% local_assigns[:chat_history].history.each do |historical_message| %>
|
7
|
+
<% next if historical_message['role'] == 'system' %>
|
10
8
|
|
11
|
-
<% if
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
<div class="lexi-message-content-user">
|
16
|
-
<%= simple_format(message.content) %>
|
17
|
-
</div>
|
18
|
-
</div>
|
19
|
-
</div>
|
20
|
-
|
21
|
-
<% elsif message.role == 'assistant' %>
|
22
|
-
<!-- Lexi message bubble (left-aligned, white background) -->
|
23
|
-
<div class="lexi-message-assistant">
|
24
|
-
<!-- Message Bubble -->
|
25
|
-
<div class="lexi-message-bubble-assistant">
|
26
|
-
<div class="lexi-message-content-assistant">
|
27
|
-
<%= markdown(message.content) %>
|
28
|
-
</div>
|
29
|
-
</div>
|
30
|
-
</div>
|
9
|
+
<% if historical_message['role'] == 'user' %>
|
10
|
+
<%= render 'meta_workflows/user_message_bubble', content: historical_message['content'] %>
|
11
|
+
<% elsif historical_message['role'] == 'assistant' %>
|
12
|
+
<%= render 'meta_workflows/assistant_message_bubble', content: historical_message['content'] %>
|
31
13
|
<% end %>
|
32
14
|
<% end %>
|
33
|
-
|
15
|
+
<% end %>
|
16
|
+
|
17
|
+
<% if chat.present? && chat.messages.any? %>
|
34
18
|
<!-- Show streaming response -->
|
35
19
|
<% if is_streaming %>
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
20
|
+
|
21
|
+
<!-- Show all saved messages except the first user message -->
|
22
|
+
<% messages.each_with_index do |message, index| %>
|
23
|
+
<% next if message.role == 'system' %>
|
24
|
+
<% next if message.role == 'user' && message == user_messages.first %>
|
25
|
+
<% next if is_streaming && message.role == 'assistant' && message == last_message %>
|
26
|
+
|
27
|
+
<% if message.role == 'user' %>
|
28
|
+
<%= render 'meta_workflows/user_message_bubble', content: message.content %>
|
29
|
+
<% elsif message.role == 'assistant' %>
|
30
|
+
<%= render 'meta_workflows/assistant_message_bubble', content: message.content %>
|
31
|
+
<% end %>
|
32
|
+
<% end %>
|
33
|
+
|
34
|
+
<%= render 'meta_workflows/assistant_message_bubble', content: full_response %>
|
44
35
|
<% end %>
|
45
36
|
<% end %>
|
46
37
|
|
@@ -0,0 +1,60 @@
|
|
1
|
+
<div class="structured-input-container"
|
2
|
+
role="group"
|
3
|
+
aria-labelledby="structured-slider-legend">
|
4
|
+
<legend id="structured-slider-legend" class="sr-only">Select a value using the slider</legend>
|
5
|
+
<div class="structured-slider-container">
|
6
|
+
<div class="structured-slider-labels">
|
7
|
+
<span class="structured-slider-label-min"><%= options['min']['label'] %></span>
|
8
|
+
<span class="structured-slider-label-max"><%= options['max']['label'] %></span>
|
9
|
+
</div>
|
10
|
+
|
11
|
+
<div class="structured-slider-wrapper">
|
12
|
+
<input
|
13
|
+
type="range"
|
14
|
+
id="range_value"
|
15
|
+
name="range_value"
|
16
|
+
min="<%= options['min']['value'] %>"
|
17
|
+
max="<%= options['max']['value'] %>"
|
18
|
+
step="<%= options['increment'] %>"
|
19
|
+
value="<%= options['min']['value'] %>"
|
20
|
+
class="structured-slider-input"
|
21
|
+
data-action="change->meta-workflows--structured-form-submit#handleSubmit"
|
22
|
+
aria-describedby="structured-slider-help structured-slider-value-display"
|
23
|
+
aria-valuemin="<%= options['min']['value'] %>"
|
24
|
+
aria-valuemax="<%= options['max']['value'] %>"
|
25
|
+
aria-valuenow="<%= options['min']['value'] %>"
|
26
|
+
aria-valuetext="<%= options['min']['value'] %>"
|
27
|
+
required
|
28
|
+
>
|
29
|
+
</div>
|
30
|
+
|
31
|
+
<div class="structured-slider-value">
|
32
|
+
<span class="structured-slider-current-value"
|
33
|
+
id="structured-slider-value-display"
|
34
|
+
data-range-display="<%= options['min']['value'] %>"
|
35
|
+
aria-live="polite">
|
36
|
+
<%= options['min']['value'] %>
|
37
|
+
</span>
|
38
|
+
</div>
|
39
|
+
<div id="structured-slider-help" class="sr-only">Use arrow keys to adjust the value</div>
|
40
|
+
</div>
|
41
|
+
</div>
|
42
|
+
|
43
|
+
<script>
|
44
|
+
// Update the displayed value when slider changes
|
45
|
+
document.addEventListener('DOMContentLoaded', function() {
|
46
|
+
const slider = document.getElementById('range_value');
|
47
|
+
const valueDisplay = document.querySelector('.structured-slider-current-value');
|
48
|
+
|
49
|
+
if (slider && valueDisplay) {
|
50
|
+
slider.addEventListener('input', function() {
|
51
|
+
valueDisplay.textContent = this.value;
|
52
|
+
valueDisplay.setAttribute('data-range-display', this.value);
|
53
|
+
|
54
|
+
// Update ARIA attributes for accessibility
|
55
|
+
slider.setAttribute('aria-valuenow', this.value);
|
56
|
+
slider.setAttribute('aria-valuetext', this.value);
|
57
|
+
});
|
58
|
+
}
|
59
|
+
});
|
60
|
+
</script>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<%= turbo_frame_tag target_frame_id(record, structured_input: true) do %>
|
2
|
+
<% if local_assigns[:is_structured_input] && structured_input_config.present? %>
|
3
|
+
<%
|
4
|
+
# Get workflow execution for form submission
|
5
|
+
workflow_execution = record.workflow_executions.order(created_at: :desc).first
|
6
|
+
form_url = workflow_execution ? meta_workflows.structured_human_path(workflow_execution.id) : "#"
|
7
|
+
chat_id = local_assigns[:chat_id] || workflow_execution&.workflow_steps&.last&.chat&.id
|
8
|
+
%>
|
9
|
+
|
10
|
+
<%= form_with url: form_url,
|
11
|
+
method: :patch,
|
12
|
+
id: "#{target_frame_id(record, structured_input: true)}_form",
|
13
|
+
data: { controller: "meta-workflows--structured-form-submit", "meta-workflows--structured-form-submit-target": "form" } do |form| %>
|
14
|
+
<%= form.hidden_field :chat_id, value: chat_id %>
|
15
|
+
|
16
|
+
<div class="structured-input-wrapper">
|
17
|
+
<% case structured_input_config['type'] %>
|
18
|
+
<% when 'single_choice' %>
|
19
|
+
<%= render 'meta_workflows/radio_input', options: structured_input_config['options'] %>
|
20
|
+
<% when 'multiple_choice' %>
|
21
|
+
<%= render 'meta_workflows/checkbox_input', options: structured_input_config['options'] %>
|
22
|
+
<% when 'range' %>
|
23
|
+
<%= render 'meta_workflows/slider_input', options: structured_input_config['options'] %>
|
24
|
+
<% end %>
|
25
|
+
</div>
|
26
|
+
<% end %>
|
27
|
+
<% end %>
|
28
|
+
<% end %>
|
data/config/routes.rb
CHANGED
@@ -0,0 +1,12 @@
|
|
1
|
+
class CreateMetaWorkflowsExecutionChatHistories < ActiveRecord::Migration[7.2]
|
2
|
+
def change
|
3
|
+
create_table :meta_workflows_execution_chat_histories do |t|
|
4
|
+
t.references :workflow_execution, null: false, foreign_key: { to_table: :meta_workflows_workflow_executions }
|
5
|
+
t.jsonb :history, null: false, default: []
|
6
|
+
|
7
|
+
t.timestamps
|
8
|
+
end
|
9
|
+
|
10
|
+
add_index :meta_workflows_execution_chat_histories, :workflow_execution_id, unique: true, name: 'index_execution_chat_histories_on_workflow_execution_id'
|
11
|
+
end
|
12
|
+
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module MetaWorkflows
|
4
4
|
MAJOR = 0
|
5
5
|
MINOR = 9
|
6
|
-
PATCH =
|
6
|
+
PATCH = 20 # this is automatically incremented by the build process
|
7
7
|
|
8
8
|
VERSION = "#{MetaWorkflows::MAJOR}.#{MetaWorkflows::MINOR}.#{MetaWorkflows::PATCH}".freeze
|
9
9
|
end
|
@@ -45,6 +45,8 @@ module Services
|
|
45
45
|
process_agent_action(conversation, workflow_execution, workflow)
|
46
46
|
when 'human'
|
47
47
|
process_human_action(execution_step, workflow_execution)
|
48
|
+
when 'structured_human'
|
49
|
+
process_structured_human_action(execution_step, workflow_execution)
|
48
50
|
when 'record_redirect'
|
49
51
|
process_record_redirect_action(workflow_execution)
|
50
52
|
when 'collection_create'
|
@@ -133,6 +135,18 @@ module Services
|
|
133
135
|
# the step advancement is handled in the human input job
|
134
136
|
end
|
135
137
|
|
138
|
+
def process_structured_human_action(execution_step, workflow_execution)
|
139
|
+
::MetaWorkflows::HumanInputJob.perform_later(
|
140
|
+
user_id: user&.id,
|
141
|
+
record: workflow_execution.record,
|
142
|
+
params: {
|
143
|
+
inputs: inputs,
|
144
|
+
prompt_id: execution_step['prompt_id']
|
145
|
+
}
|
146
|
+
)
|
147
|
+
# the step advancement is handled in the human input job
|
148
|
+
end
|
149
|
+
|
136
150
|
def process_record_redirect_action(workflow_execution)
|
137
151
|
::MetaWorkflows::RecordRedirectJob.perform_later(workflow_execution: workflow_execution)
|
138
152
|
workflow_execution.increment_step
|
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.20
|
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-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -125,6 +125,7 @@ files:
|
|
125
125
|
- app/assets/javascripts/meta_workflows/controllers/onboarding_controller.js
|
126
126
|
- app/assets/javascripts/meta_workflows/controllers/redirect_controller.js
|
127
127
|
- app/assets/javascripts/meta_workflows/controllers/response_scroll_controller.js
|
128
|
+
- app/assets/javascripts/meta_workflows/controllers/structured_form_submit_controller.js
|
128
129
|
- app/assets/javascripts/meta_workflows/controllers/tray_controller.js
|
129
130
|
- app/assets/javascripts/meta_workflows_manifest.js
|
130
131
|
- app/assets/stylesheets/meta_workflows/application.css
|
@@ -135,6 +136,7 @@ files:
|
|
135
136
|
- app/controllers/meta_workflows/debug_controller.rb
|
136
137
|
- app/controllers/meta_workflows/humans_controller.rb
|
137
138
|
- app/controllers/meta_workflows/meta_controller.rb
|
139
|
+
- app/controllers/meta_workflows/structured_humans_controller.rb
|
138
140
|
- app/controllers/meta_workflows/workflow_imports_controller.rb
|
139
141
|
- app/helpers/meta_workflows/application_helper.rb
|
140
142
|
- app/helpers/meta_workflows/debug_helper.rb
|
@@ -143,6 +145,7 @@ files:
|
|
143
145
|
- app/helpers/meta_workflows/meta_workflows_helper.rb
|
144
146
|
- app/helpers/meta_workflows/status_badge_helper.rb
|
145
147
|
- app/jobs/meta_workflows/application_job.rb
|
148
|
+
- app/jobs/meta_workflows/concerns/error_handling.rb
|
146
149
|
- app/jobs/meta_workflows/human_input_job.rb
|
147
150
|
- app/jobs/meta_workflows/meta_job.rb
|
148
151
|
- app/jobs/meta_workflows/meta_workflow_job.rb
|
@@ -152,6 +155,7 @@ files:
|
|
152
155
|
- app/models/meta_workflows.rb
|
153
156
|
- app/models/meta_workflows/application_record.rb
|
154
157
|
- app/models/meta_workflows/chat.rb
|
158
|
+
- app/models/meta_workflows/execution_chat_history.rb
|
155
159
|
- app/models/meta_workflows/message.rb
|
156
160
|
- app/models/meta_workflows/tool_call.rb
|
157
161
|
- app/models/meta_workflows/workflow.rb
|
@@ -159,16 +163,23 @@ files:
|
|
159
163
|
- app/models/meta_workflows/workflow_step.rb
|
160
164
|
- app/services/meta_workflows/application_service.rb
|
161
165
|
- app/services/meta_workflows/execution_filter_service.rb
|
166
|
+
- app/services/meta_workflows/message_history_service.rb
|
162
167
|
- app/services/meta_workflows/workflow_import_service.rb
|
163
168
|
- app/sidekiq/meta_workflows/tools/meta_workflow_tool.rb
|
164
169
|
- app/views/layouts/meta_workflows/application.html.erb
|
170
|
+
- app/views/meta_workflows/_assistant_message_bubble.html.erb
|
171
|
+
- app/views/meta_workflows/_checkbox_input.html.erb
|
165
172
|
- app/views/meta_workflows/_error_message.html.erb
|
166
173
|
- app/views/meta_workflows/_lexi_chat_alpha_tray.html.erb
|
167
174
|
- app/views/meta_workflows/_lexi_chat_right_tray.html.erb
|
168
175
|
- app/views/meta_workflows/_loader_message.html.erb
|
176
|
+
- app/views/meta_workflows/_radio_input.html.erb
|
169
177
|
- app/views/meta_workflows/_redirect.html.erb
|
170
178
|
- app/views/meta_workflows/_response_form_lexi.html.erb
|
171
179
|
- app/views/meta_workflows/_response_lexi.html.erb
|
180
|
+
- app/views/meta_workflows/_slider_input.html.erb
|
181
|
+
- app/views/meta_workflows/_structured_input.html.erb
|
182
|
+
- app/views/meta_workflows/_user_message_bubble.html.erb
|
172
183
|
- app/views/meta_workflows/debug/executions.html.erb
|
173
184
|
- app/views/meta_workflows/debug/show_execution.html.erb
|
174
185
|
- app/views/meta_workflows/debug/show_workflow.html.erb
|
@@ -186,6 +197,7 @@ files:
|
|
186
197
|
- db/migrate/20250618175439_add_call_id_and_status_to_meta_workflows_tool_calls.rb
|
187
198
|
- db/migrate/20250626211926_add_repetition_to_meta_workflows_workflow_steps.rb
|
188
199
|
- db/migrate/20250709153017_add_recipe_to_meta_workflows_workflow_executions.rb
|
200
|
+
- db/migrate/20250714192853_create_meta_workflows_execution_chat_histories.rb
|
189
201
|
- lib/meta_workflows.rb
|
190
202
|
- lib/meta_workflows/asset_installer.rb
|
191
203
|
- lib/meta_workflows/configuration.rb
|