meta_workflows 0.9.19 → 0.9.21

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf7568849f397d177f74890b1036e8a8f6ab845ff7f68b67f53641688637ad8b
4
- data.tar.gz: da332e7bd731c441e4a80ffa53b5ba398edca8843d459cc11aa51af81de7caa6
3
+ metadata.gz: 5ece4bc1842d69733d2b951c9406fda3e83f070ca2f6603e97a826d8fd0cd87d
4
+ data.tar.gz: 8208fa70b0e19259a21eaad10aa915d111b4cdc4135c16d9bc7e7929186fa7c6
5
5
  SHA512:
6
- metadata.gz: cfdca5467ebd4caa0872c2bde60cc7b7b800d7caabf78673711fb0480e7f297e583f276527f95598c017d097617aff4648db156226865f82b1791aa9f9c1d123
7
- data.tar.gz: b68da4e79a390b42dc74952987f09822093424190d2373d87a0a49067a226ff04e55325db5d1c6f501f3fd8341c16fd7a85a9d4ef70041e8dbcb32aeb58f19bf
6
+ metadata.gz: be5e764d6188da7ea34398d3ab7649ee0c8bf3964deab167ee1c778b21ea2b3e8f4f86018bfdf2a3a6aea549f44187549ddc6803c76516c886c7f06184ba16d8
7
+ data.tar.gz: '028a513a4c38ea854f5f15798f3c6d8546653b94edf5bb41a14938f3dddf1c1ba6e40421956fd9f0a8c4e9982fea17879f53557911caefd1928012c72c36b97c'
@@ -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
+ }
@@ -17,9 +17,14 @@ module MetaWorkflows
17
17
 
18
18
  Turbo::StreamsChannel.broadcast_replace_to(
19
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 }
20
+ target: target_frame_id(record, streaming: true),
21
+ partial: meta_streaming_response,
22
+ locals: {
23
+ record: record,
24
+ show_loader: true,
25
+ step_progress: step_progress,
26
+ full_response: nil
27
+ }
23
28
  )
24
29
  end
25
30
 
@@ -27,22 +32,11 @@ module MetaWorkflows
27
32
  def broadcast_error_response(workflow_execution)
28
33
  record = workflow_execution.record
29
34
 
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 }
35
+ broadcast_streaming_response(
36
+ record: record,
37
+ full_response: nil,
38
+ show_loader: false,
39
+ show_error: true
46
40
  )
47
41
  end
48
42
 
@@ -66,5 +60,51 @@ module MetaWorkflows
66
60
  step_has_repetitions: current_step_has_repetitions?(workflow_execution)
67
61
  }
68
62
  end
63
+
64
+ # Builds chat history stream for instant user message display
65
+ def build_chat_history_stream(record:, workflow_execution:)
66
+ chat_history = workflow_execution.execution_chat_history
67
+
68
+ turbo_stream.replace(
69
+ target_frame_id(record, chat_history: true),
70
+ partial: meta_chat_history,
71
+ locals: {
72
+ record: record,
73
+ chat_history: chat_history,
74
+ current_user_messages: []
75
+ }
76
+ )
77
+ end
78
+
79
+ # Builds streaming response stream for loader display
80
+ def build_streaming_response_stream(record:, show_loader: false, step_progress: nil)
81
+ turbo_stream.replace(
82
+ target_frame_id(record, streaming: true),
83
+ partial: meta_streaming_response,
84
+ locals: {
85
+ record: record,
86
+ show_loader: show_loader,
87
+ step_progress: step_progress || default_step_progress,
88
+ full_response: nil
89
+ }
90
+ )
91
+ end
92
+
93
+ # Broadcasts streaming response to the streaming frame
94
+ def broadcast_streaming_response(record:, full_response: nil, show_loader: false,
95
+ show_error: false, error_message: nil)
96
+ Turbo::StreamsChannel.broadcast_replace_to(
97
+ turbo_stream_name(record),
98
+ target: target_frame_id(record, streaming: true),
99
+ partial: meta_streaming_response,
100
+ locals: {
101
+ record: record,
102
+ full_response: full_response,
103
+ show_loader: show_loader,
104
+ show_error: show_error,
105
+ error_message: error_message
106
+ }
107
+ )
108
+ end
69
109
  end
70
110
  end
@@ -2,19 +2,19 @@
2
2
 
3
3
  module MetaWorkflows
4
4
  class HumansController < MetaController
5
- include MetaWorkflows::MetaWorkflowsHelper
6
- include MetaWorkflows::Streamable
7
5
  before_action :set_workflow_data, only: [:update]
8
6
 
9
7
  def update
8
+ append_user_message_to_history(params[:message]) if params[:message].present?
9
+
10
10
  auto_advancing = should_auto_advance?
11
11
 
12
- if params[:advance].present? || auto_advancing
12
+ if params[:manual_advance].present? || auto_advancing
13
13
  @workflow_execution.increment_step
14
- render_loader_stream
15
- process_human_input(auto_advancing:, manual_advancing: params[:advance].present?)
14
+ render_processing_state_stream
15
+ process_human_input(auto_advancing:, manual_advancing: params[:manual_advance].present?)
16
16
  else
17
- render_response_form_stream
17
+ render_processing_state_stream
18
18
  process_human_input
19
19
  end
20
20
  end
@@ -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!
@@ -43,31 +44,5 @@ module MetaWorkflows
43
44
  }
44
45
  )
45
46
  end
46
-
47
- def render_loader_stream
48
- respond_to do |format|
49
- format.turbo_stream do
50
- render turbo_stream: [
51
- build_loader_stream(record: @record, workflow_execution: @workflow_execution),
52
- build_response_form_stream(record: @record, workflow_execution: @workflow_execution,
53
- response_enabled: false, responding: false, chat_id: params[:chat_id])
54
- ]
55
- end
56
- end
57
- end
58
-
59
- def render_response_form_stream
60
- respond_to do |format|
61
- format.turbo_stream do
62
- render turbo_stream: build_response_form_stream(
63
- record: @record,
64
- workflow_execution: @workflow_execution,
65
- response_enabled: true,
66
- responding: true,
67
- chat_id: params[:chat_id]
68
- )
69
- end
70
- end
71
- end
72
47
  end
73
48
  end
@@ -3,6 +3,7 @@
3
3
  module MetaWorkflows
4
4
  class MetaController < MetaWorkflows::ApplicationController
5
5
  include MetaWorkflows::MetaWorkflowsHelper
6
+ include MetaWorkflows::Streamable
6
7
 
7
8
  def set_workflow_data
8
9
  @workflow_execution = MetaWorkflows::WorkflowExecution.includes(:record, :workflow,
@@ -21,5 +22,37 @@ module MetaWorkflows
21
22
  def current_step_repetitions
22
23
  @workflow_execution.step_repetitions(@workflow_execution.current_step)
23
24
  end
25
+
26
+ def append_user_message_to_history(user_message)
27
+ message_data = {
28
+ role: 'user',
29
+ content: user_message,
30
+ model_id: nil,
31
+ input_tokens: nil,
32
+ output_tokens: nil,
33
+ created_at: Time.current.iso8601,
34
+ step: @workflow_execution.current_step
35
+ }
36
+
37
+ # Ensure ExecutionChatHistory exists
38
+ chat_history = @workflow_execution.execution_chat_history ||
39
+ @workflow_execution.create_execution_chat_history!
40
+
41
+ # Append the user message to the history
42
+ chat_history.append_message(message_data)
43
+ end
44
+
45
+ def render_processing_state_stream
46
+ respond_to do |format|
47
+ format.turbo_stream do
48
+ render turbo_stream: [
49
+ build_chat_history_stream(record: @record, workflow_execution: @workflow_execution),
50
+ build_streaming_response_stream(record: @record, show_loader: true),
51
+ build_response_form_stream(record: @record, workflow_execution: @workflow_execution,
52
+ response_enabled: false, responding: false, chat_id: params[:chat_id])
53
+ ]
54
+ end
55
+ end
56
+ end
24
57
  end
25
58
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MetaWorkflows
4
+ class StructuredHumansController < MetaController
5
+ before_action :set_workflow_data, only: [:update]
6
+
7
+ def update
8
+ formatted_input = extract_and_format_structured_input
9
+
10
+ append_user_message_to_history(formatted_input) if formatted_input.present?
11
+ render_processing_state_stream
12
+ process_structured_human_input(formatted_input)
13
+ @workflow_execution.increment_step
14
+ end
15
+
16
+ private
17
+
18
+ def process_structured_human_input(formatted_input)
19
+ MetaWorkflows::HumanInputJob.perform_later(
20
+ user_id: current_user.id,
21
+ record: @record,
22
+ auto_advancing: true,
23
+ manual_advancing: false,
24
+ params: {
25
+ inputs: formatted_input,
26
+ chat_id: params[:chat_id],
27
+ prompt_id: @prompt_id
28
+ }
29
+ )
30
+ end
31
+
32
+ def extract_and_format_structured_input
33
+ step_data = @workflow_execution.step_data(@workflow_execution.current_step)
34
+ structured_input_config = step_data&.dig('structured_input')
35
+
36
+ return params[:message].to_s unless structured_input_config
37
+
38
+ case structured_input_config['type']
39
+ when 'single_choice'
40
+ params[:single_choice_selection].to_s
41
+ when 'multiple_choice'
42
+ Array(params[:multiple_choice_selections]).join(', ')
43
+ when 'range'
44
+ params[:range_value].to_s
45
+ else
46
+ params[:message].to_s
47
+ end
48
+ end
49
+ end
50
+ end
@@ -3,5 +3,6 @@
3
3
  module MetaWorkflows
4
4
  module ApplicationHelper
5
5
  include MetaWorkflows::MetaWorkflowsHelper
6
+ include ::ApplicationHelper
6
7
  end
7
8
  end
@@ -2,11 +2,14 @@
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, **options)
6
6
  base_id = "#{record.class.name.downcase}_#{record.id}"
7
7
 
8
- return "#{base_id}_loader" if loader
9
- return "#{base_id}_form" if form
8
+ return "#{base_id}_loader" if options[:loader]
9
+ return "#{base_id}_form" if options[:form]
10
+ return "#{base_id}_structured_input" if options[:structured_input]
11
+ return "#{base_id}_chat_history" if options[:chat_history]
12
+ return "#{base_id}_streaming" if options[:streaming]
10
13
 
11
14
  base_id
12
15
  end
@@ -27,10 +30,22 @@ module MetaWorkflows
27
30
  'meta_workflows/response_form_lexi'
28
31
  end
29
32
 
33
+ def meta_structured_input
34
+ 'meta_workflows/structured_input'
35
+ end
36
+
30
37
  def meta_response
31
38
  'meta_workflows/response_lexi'
32
39
  end
33
40
 
41
+ def meta_chat_history
42
+ 'meta_workflows/chat_history'
43
+ end
44
+
45
+ def meta_streaming_response
46
+ 'meta_workflows/streaming_response'
47
+ end
48
+
34
49
  def meta_redirect
35
50
  'meta_workflows/redirect'
36
51
  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,35 +17,23 @@ module MetaWorkflows
14
17
 
15
18
  private
16
19
 
17
- def broadcast_response(chat, full_response)
18
- user_messages = chat.messages.where(role: 'user').order(:created_at)
19
- messages = chat.messages.order(:created_at)
20
- last_message = messages.last
21
- is_streaming = full_response.present?
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
22
25
 
23
- # Remove loader message
24
- Turbo::StreamsChannel.broadcast_remove_to(
25
- turbo_stream_name(record),
26
- target: target_frame_id(record, loader: true)
26
+ def broadcast_response(full_response)
27
+ # Update streaming response frame with LLM content
28
+ broadcast_streaming_response(
29
+ record: record,
30
+ full_response: full_response,
31
+ show_loader: false,
32
+ show_error: false
27
33
  )
28
-
29
- # Update main chat content
30
- Turbo::StreamsChannel.broadcast_replace_to(turbo_stream_name(record),
31
- target: target_frame_id(record),
32
- locals: {
33
- record: record,
34
- chat: chat,
35
- full_response: full_response,
36
- user_messages: user_messages,
37
- messages: messages,
38
- last_message: last_message,
39
- is_streaming: is_streaming
40
- },
41
- partial: meta_response)
42
34
  end
43
35
 
44
36
  def broadcast_form(chat)
45
- workflow_execution = active_workflow_execution
46
37
  Turbo::StreamsChannel.broadcast_replace_to(
47
38
  turbo_stream_name(record),
48
39
  target: target_frame_id(record, form: true),
@@ -51,7 +42,22 @@ module MetaWorkflows
51
42
  workflow_execution_id: workflow_execution&.id,
52
43
  response_enabled: true,
53
44
  chat_id: chat.id,
54
- step_has_repetitions: current_step_has_repetitions?(workflow_execution) }
45
+ step_has_repetitions: current_step_has_repetitions?(workflow_execution),
46
+ is_structured_input: is_structured_input }
47
+ )
48
+ end
49
+
50
+ def broadcast_structured_input(chat)
51
+ Turbo::StreamsChannel.broadcast_replace_to(
52
+ turbo_stream_name(record),
53
+ target: target_frame_id(record, structured_input: true),
54
+ partial: meta_structured_input,
55
+ locals: {
56
+ record: record,
57
+ chat_id: chat.id,
58
+ structured_input_config: structured_input_config,
59
+ is_structured_input: is_structured_input
60
+ }
55
61
  )
56
62
  end
57
63
 
@@ -5,7 +5,8 @@ module MetaWorkflows
5
5
  include MetaWorkflows::Concerns::ErrorHandling
6
6
  include MetaWorkflows::Streamable
7
7
 
8
- 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
9
10
 
10
11
  private
11
12
 
@@ -15,6 +16,7 @@ module MetaWorkflows
15
16
  @full_response = +''
16
17
  @auto_advancing = auto_advancing
17
18
  @manual_advancing = manual_advancing
19
+ @workflow_execution = active_workflow_execution
18
20
  end
19
21
 
20
22
  def initialize_conversation(params)
@@ -28,7 +30,6 @@ module MetaWorkflows
28
30
  def initialize_new_conversation(params)
29
31
  inputs = params.symbolize_keys[:inputs]
30
32
 
31
- workflow_execution = active_workflow_execution
32
33
  current_step = workflow_execution.workflow_steps&.find_by(step: workflow_execution.current_step)
33
34
  @chat = current_step&.chat
34
35
 
@@ -45,7 +46,6 @@ module MetaWorkflows
45
46
  end
46
47
 
47
48
  def process_and_broadcast(conversation)
48
- workflow_execution = active_workflow_execution
49
49
  workflow_step = current_workflow_step(workflow_execution)
50
50
 
51
51
  begin
@@ -65,20 +65,23 @@ module MetaWorkflows
65
65
  def execute_llm_conversation(conversation)
66
66
  conversation.call_llm do |chunk|
67
67
  @full_response << (chunk.content || '')
68
- broadcast_response(chat, full_response) unless auto_advancing
68
+ broadcast_response(full_response) unless auto_advancing
69
69
  end
70
70
  end
71
71
 
72
72
  def finalize_conversation(conversation)
73
73
  chat.update!(conversation_id: conversation.id) if chat.conversation_id.blank?
74
74
  persist_messages_to_history
75
- broadcast_form(chat) unless auto_advancing
75
+ return if auto_advancing
76
+
77
+ broadcast_form(chat)
78
+ broadcast_structured_input(chat)
76
79
  end
77
80
 
78
81
  def persist_messages_to_history
79
82
  MetaWorkflows::MessageHistoryService.new(
80
83
  chat: chat,
81
- workflow_execution: active_workflow_execution
84
+ workflow_execution: workflow_execution
82
85
  ).persist_messages_to_history
83
86
  end
84
87
 
@@ -92,7 +95,11 @@ module MetaWorkflows
92
95
  raise NotImplementedError
93
96
  end
94
97
 
95
- def broadcast_response(chat, full_response)
98
+ def broadcast_structured_input(chat)
99
+ raise NotImplementedError
100
+ end
101
+
102
+ def broadcast_response(full_response)
96
103
  raise NotImplementedError
97
104
  end
98
105
 
@@ -0,0 +1,23 @@
1
+ <%= turbo_frame_tag target_frame_id(record, chat_history: true) do %>
2
+ <div class="lexi-chat-history">
3
+ <!-- Show historical messages from ExecutionChatHistory -->
4
+ <% if local_assigns[:chat_history]&.history&.any? %>
5
+ <% local_assigns[:chat_history].history.each do |historical_message| %>
6
+ <% next if historical_message['role'] == 'system' %>
7
+
8
+ <% if historical_message['role'] == 'user' %>
9
+ <%= render 'meta_workflows/user_message_bubble', content: historical_message['content'] %>
10
+ <% elsif historical_message['role'] == 'assistant' %>
11
+ <%= render 'meta_workflows/assistant_message_bubble', content: historical_message['content'] %>
12
+ <% end %>
13
+ <% end %>
14
+ <% end %>
15
+
16
+ <!-- Show current user messages (for instant display) -->
17
+ <% if local_assigns[:current_user_messages]&.any? %>
18
+ <% local_assigns[:current_user_messages].each do |user_message| %>
19
+ <%= render 'meta_workflows/user_message_bubble', content: user_message %>
20
+ <% end %>
21
+ <% end %>
22
+ </div>
23
+ <% end %>
@@ -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>
@@ -30,19 +30,23 @@
30
30
  <div id="main-scroll-container" class="lexi-chat-alpha-messages">
31
31
  <%= render partial: meta_response, locals: {
32
32
  record: local_assigns[:record],
33
- chat: active_chat,
34
- messages: active_chat&.messages&.order(:created_at) || [],
35
- user_messages: active_chat&.messages&.where(role: 'user')&.order(:created_at) || [],
36
- last_message: active_chat&.messages&.last,
33
+ chat_history: chat_history,
34
+ current_user_messages: [],
35
+ show_loader: false,
36
+ show_error: false,
37
+ step_progress: nil,
37
38
  full_response: nil,
38
- is_streaming: false,
39
- chat_history: chat_history
39
+ error_message: nil
40
40
  } %>
41
41
  </div>
42
42
 
43
43
  <%# Input area (fixed at bottom) %>
44
44
  <div class="lexi-chat-alpha-input-area">
45
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) -->
46
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 } %>
47
51
  </div>
48
52
  </div>
@@ -1,6 +1,7 @@
1
1
  <%# Lexi Chat Tray %>
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-tray lexi-chat-container-full">
6
7
  <!-- Header with Lexi logo and action icons -->
@@ -22,12 +23,13 @@
22
23
  <div id="main-scroll-container" class="lexi-chat-messages">
23
24
  <%= render partial: meta_response, locals: {
24
25
  record: local_assigns[:record],
25
- chat: active_chat,
26
- messages: active_chat&.messages&.order(:created_at) || [],
27
- user_messages: active_chat&.messages&.where(role: 'user')&.order(:created_at) || [],
28
- last_message: active_chat&.messages&.last,
26
+ chat_history: chat_history,
27
+ current_user_messages: [],
28
+ show_loader: false,
29
+ show_error: false,
30
+ step_progress: nil,
29
31
  full_response: nil,
30
- is_streaming: false
32
+ error_message: nil
31
33
  } %>
32
34
  </div>
33
35
 
@@ -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: (workflow_execution_id.present? ? meta_workflows.human_path(workflow_execution_id) : "#"),
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
- <!-- Input area with icons -->
13
- <div class="lexi-textarea-wrapper">
14
- <%= 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" } %>
15
- </div>
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
- <div class="lexi-input-controls">
18
- <div class="lexi-input-icons">
19
- <button type="button" class="lexi-icon-button" disabled>
20
- <i class="fa-solid fa-microphone text-lg"></i>
21
- </button>
22
- <button type="button" class="lexi-icon-button" disabled>
23
- <i class="fa-solid fa-headphones text-lg"></i>
24
- </button>
25
- <button type="button" class="lexi-icon-button" disabled>
26
- <i class="fa-solid fa-waveform-lines text-lg"></i>
27
- </button>
28
- </div>
29
- <div>
30
- <!-- Send button -->
31
- <% if local_assigns[:responding] %>
32
- <button type="button" class="lexi-send-button responding" disabled>
33
- <i class="fa-solid fa-arrow-up text-lg"></i>
34
- </button>
35
- <% else %>
36
- <%= form.button type: "submit", class: "lexi-send-button sm-btn sm-btn-primary" do %>
37
- <i class="fa-solid fa-arrow-up text-lg"></i>
38
- <% end %>
39
- <% end %>
40
- </div>
41
- </div>
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: "advance", value: "true", class: "lexi-advance-button #{'opacity-50 cursor-not-allowed' if local_assigns[:responding]}", disabled: local_assigns[:responding] do %>
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,49 +1,22 @@
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
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' %>
8
-
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'] %>
13
- <% end %>
14
- <% end %>
15
- <% end %>
4
+ <!-- Persistent Chat History Frame -->
5
+ <%= render partial: meta_chat_history, locals: {
6
+ record: record,
7
+ chat_history: local_assigns[:chat_history],
8
+ current_user_messages: local_assigns[:current_user_messages] || []
9
+ } %>
16
10
 
17
- <% if chat.present? && chat.messages.any? %>
18
- <!-- Show streaming response -->
19
- <% if is_streaming %>
11
+ <!-- Streaming Response Frame -->
12
+ <%= render partial: meta_streaming_response, locals: {
13
+ record: record,
14
+ show_loader: local_assigns[:show_loader] || true,
15
+ show_error: local_assigns[:show_error] || false,
16
+ step_progress: local_assigns[:step_progress],
17
+ full_response: local_assigns[:full_response],
18
+ error_message: local_assigns[:error_message]
19
+ } %>
20
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 %>
35
- <% end %>
36
- <% end %>
37
-
38
- <div class="lexi-loader-container">
39
- <%= turbo_frame_tag target_frame_id(record, loader: true) do %>
40
- <% if chat.blank? || chat.messages.empty? %>
41
- <%= render partial: meta_loader_message, locals: {
42
- record: local_assigns[:record],
43
- step_progress: default_step_progress
44
- } %>
45
- <% end %>
46
- <% end %>
47
- </div>
48
21
  </div>
49
22
  <% end %>
@@ -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,17 @@
1
+ <%= turbo_frame_tag target_frame_id(record, streaming: true) do %>
2
+ <div class="lexi-streaming-response">
3
+ <% if local_assigns[:show_loader] %>
4
+ <!-- Show loader message -->
5
+ <%= render partial: meta_loader_message, locals: {
6
+ record: record,
7
+ step_progress: local_assigns[:step_progress] || default_step_progress
8
+ } %>
9
+ <% elsif local_assigns[:show_error] %>
10
+ <!-- Show error message -->
11
+ <%= render partial: meta_error_message, locals: { record: record } %>
12
+ <% elsif local_assigns[:full_response].present? %>
13
+ <!-- Show streaming LLM response -->
14
+ <%= render 'meta_workflows/assistant_message_bubble', content: local_assigns[:full_response] %>
15
+ <% end %>
16
+ </div>
17
+ <% end %>
@@ -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
@@ -2,6 +2,7 @@
2
2
 
3
3
  MetaWorkflows::Engine.routes.draw do
4
4
  resources :humans, only: [:update]
5
+ resources :structured_humans, only: [:update]
5
6
 
6
7
  # Workflow imports - RESTful routes
7
8
  resources :workflow_imports, only: %i[new create], path: 'workflow_imports' do
@@ -3,7 +3,7 @@
3
3
  module MetaWorkflows
4
4
  MAJOR = 0
5
5
  MINOR = 9
6
- PATCH = 19 # this is automatically incremented by the build process
6
+ PATCH = 21 # 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.19
4
+ version: 0.9.21
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-14 00:00:00.000000000 Z
12
+ date: 2025-07-18 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
@@ -166,13 +168,19 @@ files:
166
168
  - app/sidekiq/meta_workflows/tools/meta_workflow_tool.rb
167
169
  - app/views/layouts/meta_workflows/application.html.erb
168
170
  - app/views/meta_workflows/_assistant_message_bubble.html.erb
171
+ - app/views/meta_workflows/_chat_history.html.erb
172
+ - app/views/meta_workflows/_checkbox_input.html.erb
169
173
  - app/views/meta_workflows/_error_message.html.erb
170
174
  - app/views/meta_workflows/_lexi_chat_alpha_tray.html.erb
171
175
  - app/views/meta_workflows/_lexi_chat_right_tray.html.erb
172
176
  - app/views/meta_workflows/_loader_message.html.erb
177
+ - app/views/meta_workflows/_radio_input.html.erb
173
178
  - app/views/meta_workflows/_redirect.html.erb
174
179
  - app/views/meta_workflows/_response_form_lexi.html.erb
175
180
  - app/views/meta_workflows/_response_lexi.html.erb
181
+ - app/views/meta_workflows/_slider_input.html.erb
182
+ - app/views/meta_workflows/_streaming_response.html.erb
183
+ - app/views/meta_workflows/_structured_input.html.erb
176
184
  - app/views/meta_workflows/_user_message_bubble.html.erb
177
185
  - app/views/meta_workflows/debug/executions.html.erb
178
186
  - app/views/meta_workflows/debug/show_execution.html.erb