meta_workflows 0.9.20 → 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: 3b91afe608b3c5260eebee5a4cc1211621c85171cf2ddc3740f5c695bc6df73f
4
- data.tar.gz: cbafaa30a15e992936bdbe66ec444fb15740bbac999c2137e11f67d26870eea0
3
+ metadata.gz: 5ece4bc1842d69733d2b951c9406fda3e83f070ca2f6603e97a826d8fd0cd87d
4
+ data.tar.gz: 8208fa70b0e19259a21eaad10aa915d111b4cdc4135c16d9bc7e7929186fa7c6
5
5
  SHA512:
6
- metadata.gz: '0886296d806086437b57857c1e509ea76a6db6c784df7bee23ff54428220d819e94265083165a0e33a17715f9ab00a9f0efd0a553d365c21b5b68a93b7c56c4e'
7
- data.tar.gz: 76b74c3f7508be1ee96547583adaf43a13f41c8af6ba74023fdcd3cafd0cd59c34ff535eeb2490ee4ba0c03c23c75bfb6ed248630d179938e9dedd724fd50add
6
+ metadata.gz: be5e764d6188da7ea34398d3ab7649ee0c8bf3964deab167ee1c778b21ea2b3e8f4f86018bfdf2a3a6aea549f44187549ddc6803c76516c886c7f06184ba16d8
7
+ data.tar.gz: '028a513a4c38ea854f5f15798f3c6d8546653b94edf5bb41a14938f3dddf1c1ba6e40421956fd9f0a8c4e9982fea17879f53557911caefd1928012c72c36b97c'
@@ -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
12
  if params[:manual_advance].present? || auto_advancing
13
13
  @workflow_execution.increment_step
14
- render_loader_stream
14
+ render_processing_state_stream
15
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
@@ -44,31 +44,5 @@ module MetaWorkflows
44
44
  }
45
45
  )
46
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
47
  end
74
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
@@ -2,72 +2,48 @@
2
2
 
3
3
  module MetaWorkflows
4
4
  class StructuredHumansController < MetaController
5
- include MetaWorkflows::MetaWorkflowsHelper
6
- include MetaWorkflows::Streamable
7
5
  before_action :set_workflow_data, only: [:update]
8
6
 
9
7
  def update
10
- render_loader_stream
11
- process_structured_human_input
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)
12
13
  @workflow_execution.increment_step
13
14
  end
14
15
 
15
16
  private
16
17
 
17
- def process_structured_human_input
18
+ def process_structured_human_input(formatted_input)
18
19
  MetaWorkflows::HumanInputJob.perform_later(
19
20
  user_id: current_user.id,
20
21
  record: @record,
21
22
  auto_advancing: true,
22
23
  manual_advancing: false,
23
24
  params: {
24
- inputs: extract_structured_inputs,
25
+ inputs: formatted_input,
25
26
  chat_id: params[:chat_id],
26
27
  prompt_id: @prompt_id
27
28
  }
28
29
  )
29
30
  end
30
31
 
31
- def extract_structured_inputs
32
+ def extract_and_format_structured_input
32
33
  step_data = @workflow_execution.step_data(@workflow_execution.current_step)
33
34
  structured_input_config = step_data&.dig('structured_input')
34
- return params[:message] unless structured_input_config
35
+
36
+ return params[:message].to_s unless structured_input_config
35
37
 
36
38
  case structured_input_config['type']
37
39
  when 'single_choice'
38
- params[:single_choice_selection]
40
+ params[:single_choice_selection].to_s
39
41
  when 'multiple_choice'
40
- params[:multiple_choice_selections]
42
+ Array(params[:multiple_choice_selections]).join(', ')
41
43
  when 'range'
42
- params[:range_value]
44
+ params[:range_value].to_s
43
45
  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
46
+ params[:message].to_s
71
47
  end
72
48
  end
73
49
  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,12 +2,14 @@
2
2
 
3
3
  module MetaWorkflows
4
4
  module MetaWorkflowsHelper
5
- def target_frame_id(record, form: nil, loader: nil, structured_input: 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
10
- return "#{base_id}_structured_input" if structured_input
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]
11
13
 
12
14
  base_id
13
15
  end
@@ -36,6 +38,14 @@ module MetaWorkflows
36
38
  'meta_workflows/response_lexi'
37
39
  end
38
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
+
39
49
  def meta_redirect
40
50
  'meta_workflows/redirect'
41
51
  end
@@ -23,31 +23,14 @@ module MetaWorkflows
23
23
  @is_structured_input = structured_input_config.present? && current_step_data&.dig('action') == 'structured_human'
24
24
  end
25
25
 
26
- def broadcast_response(chat, full_response)
27
- user_messages = chat.messages.where(role: 'user').order(:created_at)
28
- messages = chat.messages.order(:created_at)
29
- last_message = messages.last
30
- is_streaming = full_response.present?
31
-
32
- # Remove loader message
33
- Turbo::StreamsChannel.broadcast_remove_to(
34
- turbo_stream_name(record),
35
- 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
36
33
  )
37
-
38
- # Update main chat content
39
- Turbo::StreamsChannel.broadcast_replace_to(turbo_stream_name(record),
40
- target: target_frame_id(record),
41
- locals: {
42
- record: record,
43
- chat: chat,
44
- full_response: full_response,
45
- user_messages: user_messages,
46
- messages: messages,
47
- last_message: last_message,
48
- is_streaming: is_streaming
49
- },
50
- partial: meta_response)
51
34
  end
52
35
 
53
36
  def broadcast_form(chat)
@@ -65,7 +65,7 @@ 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
 
@@ -99,7 +99,7 @@ module MetaWorkflows
99
99
  raise NotImplementedError
100
100
  end
101
101
 
102
- def broadcast_response(chat, full_response)
102
+ def broadcast_response(full_response)
103
103
  raise NotImplementedError
104
104
  end
105
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 %>
@@ -30,13 +30,13 @@
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
 
@@ -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
 
@@ -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,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 %>
@@ -3,7 +3,7 @@
3
3
  module MetaWorkflows
4
4
  MAJOR = 0
5
5
  MINOR = 9
6
- PATCH = 20 # 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
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.20
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-17 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
@@ -168,6 +168,7 @@ files:
168
168
  - app/sidekiq/meta_workflows/tools/meta_workflow_tool.rb
169
169
  - app/views/layouts/meta_workflows/application.html.erb
170
170
  - app/views/meta_workflows/_assistant_message_bubble.html.erb
171
+ - app/views/meta_workflows/_chat_history.html.erb
171
172
  - app/views/meta_workflows/_checkbox_input.html.erb
172
173
  - app/views/meta_workflows/_error_message.html.erb
173
174
  - app/views/meta_workflows/_lexi_chat_alpha_tray.html.erb
@@ -178,6 +179,7 @@ files:
178
179
  - app/views/meta_workflows/_response_form_lexi.html.erb
179
180
  - app/views/meta_workflows/_response_lexi.html.erb
180
181
  - app/views/meta_workflows/_slider_input.html.erb
182
+ - app/views/meta_workflows/_streaming_response.html.erb
181
183
  - app/views/meta_workflows/_structured_input.html.erb
182
184
  - app/views/meta_workflows/_user_message_bubble.html.erb
183
185
  - app/views/meta_workflows/debug/executions.html.erb