meta_workflows 0.8.6 → 0.8.8

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: 6b77a7e4c35c915d045693675b711c52ba87a3eac9ba5f16d2dcff556131ebda
4
- data.tar.gz: 0d519f2f1e2999a52ecb161a20a3761e9acf441b254303f4607fc225cace5b98
3
+ metadata.gz: 9b59bf863e285b8d7f6b88f4d7547745bf89776f6e8c6ee1b7a77feee3fbe4d8
4
+ data.tar.gz: 1e005cddde9031f92399c506f3ca84f61db4703b6784c2aa5b4e496f218a2ea9
5
5
  SHA512:
6
- metadata.gz: c87dee53a2e8c5f3dfb2f2ec96b1888499a2ce850327910a6062c9e1fede1b727846f29e065a274204c7a233c35e67752665cad46c812227b61256d39588fbca
7
- data.tar.gz: 88c0bd57ebc8310bee5dba554672167714159c152dd2952c05566053c6e262b5567c05f04b8ffe5fb22e8bd33e2f15ee5bde8ccd196e2d343fead326b86d5123
6
+ metadata.gz: 70110e137cc53c66b295d90b8a02485498029f30c62db4d4b7c5eedfe7c7b0273141745a30172ee1d7a0e6bb86aa32abf7446a921514c0b940c6c59d47c03a85
7
+ data.tar.gz: d10e39347a37ae1b1747a19bc3baa501fe84698940726238229302f8504a53a5b01365e47c878b3aceb2add999dec9c1d723094b4c8551166526a03d2ca84f7b
@@ -10,16 +10,19 @@ module TrayConfigurable
10
10
  def tray_config
11
11
  @tray_config ||= {
12
12
  beta: {
13
- visible: false,
14
- collapsible: true
13
+ visible: true,
14
+ collapsible: true,
15
+ lexi: false
15
16
  },
16
17
  gamma: {
17
- visible: false,
18
- collapsible: true
18
+ visible: true,
19
+ collapsible: true,
20
+ lexi: false
19
21
  },
20
22
  delta: {
21
- visible: false,
22
- collapsible: true
23
+ visible: true,
24
+ collapsible: true,
25
+ lexi: false
23
26
  }
24
27
  }
25
28
  end
@@ -16,11 +16,11 @@ module MetaWorkflows
16
16
  end
17
17
 
18
18
  def meta_response_form
19
- 'meta_workflows/response_form'
19
+ 'meta_workflows/response_form_lexi'
20
20
  end
21
21
 
22
22
  def meta_response
23
- 'meta_workflows/response'
23
+ 'meta_workflows/response_lexi'
24
24
  end
25
25
 
26
26
  def meta_redirect
@@ -13,20 +13,34 @@ module MetaWorkflows
13
13
 
14
14
  private
15
15
 
16
- def broadcast_response(response)
16
+ def broadcast_response(chat, full_response)
17
+ user_messages = chat.messages.where(role: 'user').order(:created_at)
18
+ messages = chat.messages.order(:created_at)
19
+ last_message = messages.last
20
+ is_streaming = full_response.present?
21
+
17
22
  Turbo::StreamsChannel.broadcast_replace_to(turbo_stream_name(record),
18
23
  target: target_frame_id(record),
19
- locals: { record: record, response: response },
24
+ locals: {
25
+ record: record,
26
+ chat: chat,
27
+ full_response: full_response,
28
+ user_messages: user_messages,
29
+ messages: messages,
30
+ last_message: last_message,
31
+ is_streaming: is_streaming
32
+ },
20
33
  partial: meta_response)
21
34
  end
22
35
 
23
36
  def broadcast_form(chat)
37
+ workflow_execution = active_workflow_execution
24
38
  Turbo::StreamsChannel.broadcast_replace_to(
25
39
  turbo_stream_name(record),
26
40
  target: target_frame_id(record, form: true),
27
41
  partial: meta_response_form,
28
42
  locals: { record: record,
29
- workflow_execution_id: record.workflow_execution.id,
43
+ workflow_execution_id: workflow_execution&.id,
30
44
  response_enabled: true,
31
45
  chat_id: chat.id }
32
46
  )
@@ -9,6 +9,7 @@ module MetaWorkflows
9
9
  def setup(user_id, record)
10
10
  @user_id = user_id
11
11
  @record = record.class.find(record.id)
12
+ @full_response = +''
12
13
  end
13
14
 
14
15
  def initialize_conversation(params)
@@ -22,12 +23,10 @@ module MetaWorkflows
22
23
  def initialize_new_conversation(params)
23
24
  inputs = params.symbolize_keys[:inputs]
24
25
 
25
- workflow_execution = record.workflow_execution
26
- current_step = workflow_execution.workflow_steps.find_by(step: workflow_execution.current_step)
26
+ workflow_execution = active_workflow_execution
27
+ current_step = workflow_execution.workflow_steps&.find_by(step: workflow_execution.current_step)
27
28
  @chat = current_step&.chat
28
29
 
29
- @full_response = +''
30
-
31
30
  conversation = RubyConversations::Conversation.new(chat:)
32
31
  conversation.with_prompt(params[:prompt_id], inputs: inputs, description: "Running #{params[:prompt_id]} process")
33
32
  end
@@ -35,11 +34,8 @@ module MetaWorkflows
35
34
  def continue_existing_conversation(params)
36
35
  @chat = MetaWorkflows::Chat.find(params[:chat_id])
37
36
  conversation = RubyConversations::Conversation.new(chat:, id: chat.conversation_id)
38
-
39
37
  new_message = params[:inputs]
40
38
  conversation.with_user_message(new_message, description: "Following up on #{params[:prompt_id]} process")
41
- @full_response = chat.messages[1..].map(&:content).join + format_user_message(new_message)
42
-
43
39
  conversation
44
40
  end
45
41
 
@@ -62,14 +58,13 @@ module MetaWorkflows
62
58
 
63
59
  def execute_llm_conversation(conversation)
64
60
  conversation.call_llm do |chunk|
65
- full_response << (chunk.content || '')
66
- broadcast_response(full_response)
61
+ @full_response << (chunk.content || '')
62
+ broadcast_response(chat, full_response)
67
63
  end
68
64
  end
69
65
 
70
66
  def finalize_conversation(conversation)
71
67
  chat.update!(conversation_id: conversation.id) if chat.conversation_id.blank?
72
- broadcast_response(full_response)
73
68
  broadcast_form(chat)
74
69
  end
75
70
 
@@ -94,28 +89,28 @@ module MetaWorkflows
94
89
  end
95
90
 
96
91
  def current_workflow_step
97
- return nil unless record&.workflow_execution
92
+ workflow_execution = active_workflow_execution
93
+ return nil unless workflow_execution
98
94
 
99
- workflow_execution = record.workflow_execution
100
95
  workflow_execution.workflow_steps.find_by(step: workflow_execution.current_step)
101
96
  end
102
97
 
103
98
  def broadcast_error_response(error)
104
- error_message = "Error occurred during LLM processing: #{error.message}"
105
- broadcast_response(error_message)
99
+ error_message = "Error occured during LLM processing: #{error.message}"
100
+ broadcast_response(chat, error_message)
106
101
  end
107
102
 
108
- def broadcast_response(response)
103
+ def broadcast_form(chat)
109
104
  raise NotImplementedError
110
105
  end
111
106
 
112
- def broadcast_form(chat)
107
+ def broadcast_response(chat, full_response)
113
108
  raise NotImplementedError
114
109
  end
115
110
 
116
- def format_user_message(message)
117
- '<div class="flex p-4 border border-slate-300 rounded-xl bg-white text-xl ' \
118
- "font-semibold leading-snug\">#{message}</div>\n\n"
111
+ # Get the most recent (active) workflow execution for the record
112
+ def active_workflow_execution
113
+ record.workflow_executions.order(created_at: :desc).first
119
114
  end
120
115
  end
121
116
  end
@@ -0,0 +1,53 @@
1
+ <%= turbo_frame_tag target_frame_id(record, form: true) do %>
2
+ <% if local_assigns[:response_enabled] %>
3
+ <div>
4
+ <%= form_with url: (workflow_execution_id.present? ? meta_workflows.human_path(workflow_execution_id) : "#"), method: :patch do |form| %>
5
+ <%= form.hidden_field :chat_id, value: chat_id %>
6
+ <fieldset>
7
+ <div class="flex flex-col gap-1">
8
+ <!-- Input Container -->
9
+ <div class="flex flex-col max-h-[200px] border border-gray-300 rounded-lg bg-white/80 p-2">
10
+ <!-- Input area with icons -->
11
+ <div class="flex-1 relative">
12
+ <%= form.text_area :message, rows: 1, placeholder: random_chat_placeholder, disabled: local_assigns[:responding], class: "w-full min-h-[50px] border-0 resize-none focus:outline-none focus:ring-0 bg-transparent text-base overflow-y-auto" %>
13
+ </div>
14
+
15
+ <div class="flex justify-between">
16
+ <div class="flex gap-1 items-end justify-end">
17
+ <button type="button" class="p-2 rounded-xl hover:bg-gray-100" disabled>
18
+ <i class="fa-solid fa-microphone text-lg"></i>
19
+ </button>
20
+ <button type="button" class="p-2 rounded-xl hover:bg-gray-100" disabled>
21
+ <i class="fa-solid fa-headphones text-lg"></i>
22
+ </button>
23
+ <button type="button" class="p-2 rounded-xl hover:bg-gray-100" disabled>
24
+ <i class="fa-solid fa-waveform-lines text-lg"></i>
25
+ </button>
26
+ </div>
27
+ <div>
28
+ <!-- Send button -->
29
+ <% if local_assigns[:responding] %>
30
+ <button type="button" class="w-12 h-12 rounded-full flex items-center justify-center text-white transition-colors bg-purple-600 opacity-50 cursor-not-allowed" disabled>
31
+ <i class="fa-solid fa-arrow-up text-lg"></i>
32
+ </button>
33
+ <% else %>
34
+ <%= form.button type: "submit", class: "w-12 h-12 rounded-full flex items-center justify-center text-white bg-purple-600 transition-colors focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2" do %>
35
+ <i class="fa-solid fa-arrow-up text-lg"></i>
36
+ <% end %>
37
+ <% end %>
38
+ </div>
39
+ </div>
40
+ </div>
41
+
42
+ <!-- Next Button (outside input container) -->
43
+ <div class="flex justify-end">
44
+ <%= form.button type: "submit", name: "advance", value: "true", class: "text-xs mb-2 #{local_assigns[:responding] ? 'opacity-50 cursor-not-allowed' : ''}", disabled: local_assigns[:responding] do %>
45
+ <i class="fa-light fa-arrow-right"></i> Next
46
+ <% end %>
47
+ </div>
48
+ </div>
49
+ </fieldset>
50
+ <% end %>
51
+ </div>
52
+ <% end %>
53
+ <% end %>
@@ -0,0 +1,57 @@
1
+ <%= turbo_frame_tag target_frame_id(record) do %>
2
+ <div id="response-content-container" class="space-y-4" data-controller="response-scroll">
3
+ <% if chat.present? && chat.messages.any? %>
4
+
5
+ <!-- Show all saved messages except the first user message -->
6
+ <% messages.each_with_index do |message, index| %>
7
+ <% next if message.role == 'system' %>
8
+ <% next if message.role == 'user' && message == user_messages.first %>
9
+ <% next if is_streaming && message.role == 'assistant' && message == last_message %>
10
+
11
+ <% if message.role == 'user' %>
12
+ <!-- User message bubble (right-aligned, amber background) -->
13
+ <div class="flex justify-end">
14
+ <div class="max-w-[80%] bg-amber-100 rounded-xl px-4 py-2">
15
+ <div class="text-sm">
16
+ <%= simple_format(message.content) %>
17
+ </div>
18
+ </div>
19
+ </div>
20
+
21
+ <% elsif message.role == 'assistant' %>
22
+ <!-- Lexi message bubble (left-aligned with avatar) -->
23
+ <div class="flex items-start space-x-3">
24
+ <!-- Lexi Avatar -->
25
+ <div class="flex-shrink-0">
26
+ <%= image_tag 'lexi-chaticon.png', alt: 'Lexi', class: 'w-auto h-10' %>
27
+ </div>
28
+
29
+ <!-- Message Bubble -->
30
+ <div class="max-w-[80%] bg-slate-100 rounded-xl px-4 py-2">
31
+ <div class="prose prose-sm max-w-none text-sm">
32
+ <%= markdown(message.content) %>
33
+ </div>
34
+ </div>
35
+ </div>
36
+ <% end %>
37
+ <% end %>
38
+
39
+ <!-- Show streaming response -->
40
+ <% if is_streaming %>
41
+ <div class="flex items-start space-x-3">
42
+ <!-- Lexi Avatar -->
43
+ <div class="flex-shrink-0">
44
+ <%= image_tag 'lexi-chaticon.png', alt: 'Lexi', class: 'w-auto h-10' %>
45
+ </div>
46
+
47
+ <!-- Streaming Message Bubble -->
48
+ <div class="max-w-[80%] bg-slate-100 rounded-xl px-4 py-2">
49
+ <div class="prose prose-sm max-w-none text-sm">
50
+ <%= markdown(full_response) %>
51
+ </div>
52
+ </div>
53
+ </div>
54
+ <% end %>
55
+ <% end %>
56
+ </div>
57
+ <% end %>
@@ -3,7 +3,7 @@
3
3
  module MetaWorkflows
4
4
  MAJOR = 0
5
5
  MINOR = 8
6
- PATCH = 6 # this is automatically incremented by the build process
6
+ PATCH = 8 # this is automatically incremented by the build process
7
7
 
8
8
  VERSION = "#{MetaWorkflows::MAJOR}.#{MetaWorkflows::MINOR}.#{MetaWorkflows::PATCH}".freeze
9
9
  end
@@ -235,7 +235,7 @@ module Services
235
235
  end
236
236
 
237
237
  def validate_workflow_presence
238
- return unless workflow_name.nil? && record.workflow_execution.blank?
238
+ return unless workflow_name.nil? && active_workflow_execution.blank?
239
239
 
240
240
  raise ArgumentError, 'No workflow specified and record has no workflow attached'
241
241
  end
@@ -251,13 +251,18 @@ module Services
251
251
  workflow_params: workflow_params
252
252
  )
253
253
  else
254
- workflow_execution = record.workflow_execution
254
+ workflow_execution = active_workflow_execution
255
255
  workflow = workflow_execution.workflow
256
256
  end
257
257
 
258
258
  [workflow, workflow_execution]
259
259
  end
260
260
 
261
+ # Get the most recent (active) workflow execution for the record
262
+ def active_workflow_execution
263
+ record.workflow_executions.order(created_at: :desc).first
264
+ end
265
+
261
266
  def prepare_workflow_execution(workflow, workflow_execution)
262
267
  execution_step = workflow.step_data(workflow_execution.current_step)
263
268
  execution_output = workflow.step_output(workflow_execution.current_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.8.6
4
+ version: 0.8.8
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-06-24 00:00:00.000000000 Z
12
+ date: 2025-06-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -152,8 +152,8 @@ files:
152
152
  - app/views/layouts/meta_workflows/debug.html.erb
153
153
  - app/views/meta_workflows/_loader.html.erb
154
154
  - app/views/meta_workflows/_redirect.html.erb
155
- - app/views/meta_workflows/_response.html.erb
156
- - app/views/meta_workflows/_response_form.html.erb
155
+ - app/views/meta_workflows/_response_form_lexi.html.erb
156
+ - app/views/meta_workflows/_response_lexi.html.erb
157
157
  - app/views/meta_workflows/debug/executions.html.erb
158
158
  - app/views/meta_workflows/debug/show_execution.html.erb
159
159
  - app/views/meta_workflows/debug/show_workflow.html.erb
@@ -1,5 +0,0 @@
1
- <%= turbo_frame_tag target_frame_id(record) do %>
2
- <div id="response-content-container" class="meta-workflows chat" data-controller="response-scroll">
3
- <%= markdown(response) %>
4
- </div>
5
- <% end %>
@@ -1,36 +0,0 @@
1
- <%= turbo_frame_tag target_frame_id(record, form: true) do %>
2
- <% if local_assigns[:response_enabled] %>
3
- <div>
4
- <%= form_with url: meta_workflows.human_path(workflow_execution_id), method: :patch do |form| %>
5
- <%= form.hidden_field :chat_id, value: chat_id %>
6
- <fieldset>
7
- <div class="flex flex-col gap-5">
8
- <div class="flex gap-2">
9
- <%= form.text_area :message, rows: 3, placeholder: random_chat_placeholder, disabled: local_assigns[:responding], class: "flex-1" %>
10
-
11
- <% if local_assigns[:responding] %>
12
- <button type="button" class="sm-btn sm-btn-primary self-start" disabled>
13
- <i class="fa-light fa-spinner fa-spin mr-1"></i> Generating...
14
- </button>
15
- <% else %>
16
- <%= form.button type: "submit", class: "sm-btn sm-btn-outline" do %>
17
- <i class="fa-light fa-paper-plane"></i>
18
- <% end %>
19
- <% end %>
20
- </div>
21
-
22
- <hr/>
23
-
24
- <% unless local_assigns[:responding] %>
25
- <div class="flex justify-end">
26
- <%= form.button type: "submit", name: "advance", value: "true", class: "sm-btn sm-btn-primary sm-btn-large" do %>
27
- <i class="fa-light fa-arrow-right"></i> Next
28
- <% end %>
29
- </div>
30
- <% end %>
31
- </div>
32
- </fieldset>
33
- <% end %>
34
- </div>
35
- <% end %>
36
- <% end %>