meta_workflows 0.7.0
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 +7 -0
- data/CHANGELOG.md +36 -0
- data/README.md +498 -0
- data/Rakefile +10 -0
- data/app/assets/javascripts/meta_workflows/controllers/loading_phrases_controller.js +31 -0
- data/app/assets/javascripts/meta_workflows/controllers/redirect_controller.js +15 -0
- data/app/assets/javascripts/meta_workflows/controllers/response_scroll_controller.js +67 -0
- data/app/assets/javascripts/meta_workflows_manifest.js +13 -0
- data/app/assets/stylesheets/meta_workflows/application.css +161 -0
- data/app/controllers/meta_workflows/application_controller.rb +10 -0
- data/app/controllers/meta_workflows/debug_controller.rb +101 -0
- data/app/controllers/meta_workflows/humans_controller.rb +96 -0
- data/app/controllers/meta_workflows/meta_controller.rb +21 -0
- data/app/helpers/meta_workflows/application_helper.rb +7 -0
- data/app/helpers/meta_workflows/debug_helper.rb +54 -0
- data/app/helpers/meta_workflows/execution_helper.rb +77 -0
- data/app/helpers/meta_workflows/formatting_helper.rb +30 -0
- data/app/helpers/meta_workflows/meta_workflows_helper.rb +41 -0
- data/app/helpers/meta_workflows/status_badge_helper.rb +66 -0
- data/app/jobs/meta_workflows/application_job.rb +14 -0
- data/app/jobs/meta_workflows/human_input_job.rb +35 -0
- data/app/jobs/meta_workflows/meta_job.rb +121 -0
- data/app/jobs/meta_workflows/meta_workflow_job.rb +20 -0
- data/app/jobs/meta_workflows/record_redirect_job.rb +36 -0
- data/app/mailers/meta_workflows/application_mailer.rb +8 -0
- data/app/models/meta_workflows/application_record.rb +7 -0
- data/app/models/meta_workflows/chat.rb +20 -0
- data/app/models/meta_workflows/message.rb +11 -0
- data/app/models/meta_workflows/tool_call.rb +7 -0
- data/app/models/meta_workflows/workflow.rb +32 -0
- data/app/models/meta_workflows/workflow_execution.rb +23 -0
- data/app/models/meta_workflows/workflow_step.rb +49 -0
- data/app/models/meta_workflows.rb +7 -0
- data/app/services/meta_workflows/execution_filter_service.rb +80 -0
- data/app/sidekiq/meta_workflows/tools/meta_workflow_tool.rb +86 -0
- data/app/views/layouts/meta_workflows/application.html.erb +17 -0
- data/app/views/layouts/meta_workflows/debug.html.erb +47 -0
- data/app/views/meta_workflows/_loader.html.erb +22 -0
- data/app/views/meta_workflows/_redirect.html.erb +1 -0
- data/app/views/meta_workflows/_response.html.erb +5 -0
- data/app/views/meta_workflows/_response_form.html.erb +36 -0
- data/app/views/meta_workflows/debug/executions.html.erb +187 -0
- data/app/views/meta_workflows/debug/show_execution.html.erb +283 -0
- data/app/views/meta_workflows/debug/show_workflow.html.erb +148 -0
- data/app/views/meta_workflows/debug/workflows.html.erb +110 -0
- data/config/routes.rb +13 -0
- data/db/migrate/20250530220618_create_meta_workflows_workflows.rb +16 -0
- data/db/migrate/20250530220634_create_meta_workflows_workflow_executions.rb +18 -0
- data/db/migrate/20250530220704_create_meta_workflows_chats.rb +16 -0
- data/db/migrate/20250530220722_create_meta_workflows_messages.rb +19 -0
- data/db/migrate/20250530220737_create_meta_workflows_tool_calls.rb +18 -0
- data/db/migrate/20250530220750_create_meta_workflows_workflow_steps.rb +17 -0
- data/db/migrate/20250613213159_add_error_fields_to_workflow_steps.rb +8 -0
- data/lib/meta_workflows/asset_installer.rb +509 -0
- data/lib/meta_workflows/configuration.rb +39 -0
- data/lib/meta_workflows/engine.rb +47 -0
- data/lib/meta_workflows/export_execution_service.rb +56 -0
- data/lib/meta_workflows/version.rb +5 -0
- data/lib/meta_workflows.rb +9 -0
- data/lib/services/meta_workflows/application_service.rb +11 -0
- data/lib/services/meta_workflows/meta_workflow_service.rb +277 -0
- data/lib/services/meta_workflows/updaters/meta_service.rb +39 -0
- data/lib/tasks/meta_workflows_tasks.rake +153 -0
- metadata +219 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MetaWorkflows
|
4
|
+
class HumanInputJob < MetaJob
|
5
|
+
include MetaWorkflows::MetaWorkflowsHelper
|
6
|
+
queue_as :default
|
7
|
+
|
8
|
+
def perform(user_id:, record:, params:)
|
9
|
+
setup(user_id, record)
|
10
|
+
conversation = initialize_conversation(params)
|
11
|
+
process_and_broadcast(conversation)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def broadcast_response(response)
|
17
|
+
Turbo::StreamsChannel.broadcast_replace_to(turbo_stream_name(record),
|
18
|
+
target: target_frame_id(record),
|
19
|
+
locals: { record: record, response: response },
|
20
|
+
partial: meta_response)
|
21
|
+
end
|
22
|
+
|
23
|
+
def broadcast_form(chat)
|
24
|
+
Turbo::StreamsChannel.broadcast_replace_to(
|
25
|
+
turbo_stream_name(record),
|
26
|
+
target: target_frame_id(record, form: true),
|
27
|
+
partial: meta_response_form,
|
28
|
+
locals: { record: record,
|
29
|
+
workflow_execution_id: record.workflow_execution.id,
|
30
|
+
response_enabled: true,
|
31
|
+
chat_id: chat.id }
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MetaWorkflows
|
4
|
+
class MetaJob < MetaWorkflows::ApplicationJob
|
5
|
+
attr_accessor :chat, :inputs, :full_response, :user_id, :record
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def setup(user_id, record)
|
10
|
+
@user_id = user_id
|
11
|
+
@record = record.class.find(record.id)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize_conversation(params)
|
15
|
+
if params[:chat_id].blank?
|
16
|
+
initialize_new_conversation(params)
|
17
|
+
else
|
18
|
+
continue_existing_conversation(params)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize_new_conversation(params)
|
23
|
+
inputs = params.symbolize_keys[:inputs]
|
24
|
+
|
25
|
+
workflow_execution = record.workflow_execution
|
26
|
+
current_step = workflow_execution.workflow_steps.find_by(step: workflow_execution.current_step)
|
27
|
+
@chat = current_step&.chat
|
28
|
+
|
29
|
+
@full_response = +''
|
30
|
+
|
31
|
+
conversation = RubyConversations::Conversation.new(chat:)
|
32
|
+
conversation.with_prompt(params[:prompt_id], inputs: inputs, description: "Running #{params[:prompt_id]} process")
|
33
|
+
end
|
34
|
+
|
35
|
+
def continue_existing_conversation(params)
|
36
|
+
@chat = MetaWorkflows::Chat.find(params[:chat_id])
|
37
|
+
conversation = RubyConversations::Conversation.new(chat:, id: chat.conversation_id)
|
38
|
+
|
39
|
+
new_message = params[:inputs]
|
40
|
+
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
|
+
conversation
|
44
|
+
end
|
45
|
+
|
46
|
+
def process_and_broadcast(conversation)
|
47
|
+
workflow_step = current_workflow_step
|
48
|
+
|
49
|
+
begin
|
50
|
+
clear_previous_errors(workflow_step)
|
51
|
+
execute_llm_conversation(conversation)
|
52
|
+
finalize_conversation(conversation)
|
53
|
+
rescue StandardError => e
|
54
|
+
handle_llm_error(e, workflow_step, conversation)
|
55
|
+
raise
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def clear_previous_errors(workflow_step)
|
60
|
+
workflow_step&.clear_error if workflow_step&.error?
|
61
|
+
end
|
62
|
+
|
63
|
+
def execute_llm_conversation(conversation)
|
64
|
+
conversation.call_llm do |chunk|
|
65
|
+
full_response << (chunk.content || '')
|
66
|
+
broadcast_response(full_response)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def finalize_conversation(conversation)
|
71
|
+
chat.update!(conversation_id: conversation.id) if chat.conversation_id.blank?
|
72
|
+
broadcast_response(full_response)
|
73
|
+
broadcast_form(chat)
|
74
|
+
end
|
75
|
+
|
76
|
+
def handle_llm_error(error, workflow_step, conversation)
|
77
|
+
log_error(error)
|
78
|
+
store_error_in_workflow_step(error, workflow_step, conversation)
|
79
|
+
broadcast_error_response(error)
|
80
|
+
end
|
81
|
+
|
82
|
+
def log_error(error)
|
83
|
+
Rails.logger.error("LLM Error in MetaJob: #{error.message}")
|
84
|
+
Rails.logger.error(error.backtrace.join("\n"))
|
85
|
+
end
|
86
|
+
|
87
|
+
def store_error_in_workflow_step(error, workflow_step, conversation)
|
88
|
+
workflow_step&.record_error(error, additional_details: {
|
89
|
+
job_class: self.class.name,
|
90
|
+
chat_id: chat&.id,
|
91
|
+
conversation_id: conversation&.id,
|
92
|
+
full_response_length: full_response&.length || 0
|
93
|
+
})
|
94
|
+
end
|
95
|
+
|
96
|
+
def current_workflow_step
|
97
|
+
return nil unless record&.workflow_execution
|
98
|
+
|
99
|
+
workflow_execution = record.workflow_execution
|
100
|
+
workflow_execution.workflow_steps.find_by(step: workflow_execution.current_step)
|
101
|
+
end
|
102
|
+
|
103
|
+
def broadcast_error_response(error)
|
104
|
+
error_message = "Error occurred during LLM processing: #{error.message}"
|
105
|
+
broadcast_response(error_message)
|
106
|
+
end
|
107
|
+
|
108
|
+
def broadcast_response(response)
|
109
|
+
raise NotImplementedError
|
110
|
+
end
|
111
|
+
|
112
|
+
def broadcast_form(chat)
|
113
|
+
raise NotImplementedError
|
114
|
+
end
|
115
|
+
|
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"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MetaWorkflows
|
4
|
+
class MetaWorkflowJob < MetaWorkflows::ApplicationJob
|
5
|
+
queue_as :default
|
6
|
+
|
7
|
+
def perform(record_id:, record_type:, workflow_name:, user_id:, inputs: {}, workflow_params: nil)
|
8
|
+
record = record_type.constantize.find(record_id)
|
9
|
+
user = User.find(user_id)
|
10
|
+
|
11
|
+
Services::MetaWorkflows::MetaWorkflowService.new(
|
12
|
+
record: record,
|
13
|
+
workflow_name: workflow_name,
|
14
|
+
user: user,
|
15
|
+
inputs: inputs,
|
16
|
+
workflow_params: workflow_params
|
17
|
+
).call
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MetaWorkflows
|
4
|
+
class RecordRedirectJob < MetaWorkflows::ApplicationJob
|
5
|
+
include MetaWorkflows::MetaWorkflowsHelper
|
6
|
+
queue_as :default
|
7
|
+
|
8
|
+
def perform(workflow_execution:)
|
9
|
+
@workflow_execution = workflow_execution
|
10
|
+
params = @workflow_execution.workflow_params || {}
|
11
|
+
broadcast_redirect(params)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def broadcast_redirect(params)
|
17
|
+
record_id = params['record_id']
|
18
|
+
record_class = params['record_class']
|
19
|
+
redirect_method = params['redirect_method']
|
20
|
+
|
21
|
+
record_class_constant = record_class.to_s.safe_constantize
|
22
|
+
record = record_class_constant&.find_by(id: record_id)
|
23
|
+
|
24
|
+
redirect_path = Rails.application.routes.url_helpers.send(redirect_method.to_s, record)
|
25
|
+
|
26
|
+
Turbo::StreamsChannel.broadcast_append_to(
|
27
|
+
turbo_stream_name(@workflow_execution.record),
|
28
|
+
target: target_frame_id(@workflow_execution.record, form: true),
|
29
|
+
partial: meta_redirect,
|
30
|
+
locals: {
|
31
|
+
redirect_path: redirect_path
|
32
|
+
}
|
33
|
+
)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MetaWorkflows
|
4
|
+
class Chat < ApplicationRecord
|
5
|
+
acts_as_chat(message_class: 'MetaWorkflows::Message', tool_call_class: 'MetaWorkflows::ToolCall')
|
6
|
+
|
7
|
+
belongs_to :user, optional: true
|
8
|
+
has_many :workflow_steps, dependent: :nullify, class_name: 'MetaWorkflows::WorkflowStep'
|
9
|
+
|
10
|
+
validates :model_id, presence: true
|
11
|
+
validates :provider, presence: true
|
12
|
+
|
13
|
+
after_initialize :set_chat
|
14
|
+
|
15
|
+
def set_chat
|
16
|
+
RubyConversations::Conversation.configure_llm_credentials
|
17
|
+
@chat = RubyLLM::Chat.new(model: model_id, provider:)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MetaWorkflows
|
4
|
+
class Workflow < ApplicationRecord
|
5
|
+
validates :name, presence: true, uniqueness: true
|
6
|
+
validates :recipe, presence: true
|
7
|
+
|
8
|
+
has_many :workflow_executions, dependent: :destroy, class_name: 'MetaWorkflows::WorkflowExecution'
|
9
|
+
has_many :courses, through: :workflow_executions
|
10
|
+
has_many :workflow_steps, through: :workflow_executions, class_name: 'MetaWorkflows::WorkflowStep'
|
11
|
+
|
12
|
+
def step_progress(step)
|
13
|
+
recipe.dig('steps', step, 'step_progress')
|
14
|
+
end
|
15
|
+
|
16
|
+
def prompt_id(step)
|
17
|
+
recipe.dig('steps', step, 'prompt_id')
|
18
|
+
end
|
19
|
+
|
20
|
+
def step_data(step)
|
21
|
+
recipe.dig('steps', step)
|
22
|
+
end
|
23
|
+
|
24
|
+
def step_output(step)
|
25
|
+
recipe.dig('steps', step, 'output')
|
26
|
+
end
|
27
|
+
|
28
|
+
def total_steps
|
29
|
+
recipe['steps'].size
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MetaWorkflows
|
4
|
+
class WorkflowExecution < ApplicationRecord
|
5
|
+
belongs_to :workflow, class_name: 'MetaWorkflows::Workflow'
|
6
|
+
belongs_to :record, polymorphic: true
|
7
|
+
has_many :workflow_steps, dependent: :destroy, class_name: 'MetaWorkflows::WorkflowStep'
|
8
|
+
|
9
|
+
# Default values for fields
|
10
|
+
attribute :current_step, :integer, default: 0
|
11
|
+
attribute :repetition, :integer, default: 0
|
12
|
+
attribute :completed, :boolean, default: false
|
13
|
+
attribute :workflow_params, :json, default: -> { {} }
|
14
|
+
|
15
|
+
validates :current_step, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
|
16
|
+
validates :repetition, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
|
17
|
+
validates :completed, inclusion: { in: [true, false] }
|
18
|
+
|
19
|
+
def increment_step
|
20
|
+
update(current_step: current_step + 1)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MetaWorkflows
|
4
|
+
class WorkflowStep < ApplicationRecord
|
5
|
+
belongs_to :workflow_execution, class_name: 'MetaWorkflows::WorkflowExecution'
|
6
|
+
belongs_to :chat, class_name: 'MetaWorkflows::Chat', optional: true
|
7
|
+
|
8
|
+
validates :step, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
|
9
|
+
validates :workflow_execution_id, uniqueness: { scope: :step }
|
10
|
+
|
11
|
+
has_one :workflow, through: :workflow_execution, class_name: 'MetaWorkflows::Workflow'
|
12
|
+
|
13
|
+
# Scopes for error handling
|
14
|
+
scope :with_errors, -> { where.not(error_message: nil) }
|
15
|
+
scope :without_errors, -> { where(error_message: nil) }
|
16
|
+
|
17
|
+
# Error handling methods
|
18
|
+
def error?
|
19
|
+
error_message.present?
|
20
|
+
end
|
21
|
+
|
22
|
+
def record_error(error, additional_details: {})
|
23
|
+
update!(
|
24
|
+
error_message: error.message,
|
25
|
+
error_type: error.class.name,
|
26
|
+
error_details: {
|
27
|
+
backtrace: error.backtrace&.first(10),
|
28
|
+
additional_details: additional_details
|
29
|
+
}.compact,
|
30
|
+
error_occurred_at: Time.current
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def clear_error
|
35
|
+
update!(
|
36
|
+
error_message: nil,
|
37
|
+
error_type: nil,
|
38
|
+
error_details: nil,
|
39
|
+
error_occurred_at: nil
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
def error_summary
|
44
|
+
return nil unless error?
|
45
|
+
|
46
|
+
"#{error_type}: #{error_message}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MetaWorkflows
|
4
|
+
class ExecutionFilterService
|
5
|
+
def initialize(executions, params)
|
6
|
+
@executions = executions
|
7
|
+
@params = params
|
8
|
+
end
|
9
|
+
|
10
|
+
def apply_filters
|
11
|
+
executions = @executions
|
12
|
+
executions = filter_by_workflow(executions)
|
13
|
+
executions = filter_by_status(executions)
|
14
|
+
executions = filter_by_date_range(executions)
|
15
|
+
search_executions(executions)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :params
|
21
|
+
|
22
|
+
def filter_by_workflow(executions)
|
23
|
+
return executions if params[:workflow_id].blank?
|
24
|
+
|
25
|
+
executions.where(workflow_id: params[:workflow_id])
|
26
|
+
end
|
27
|
+
|
28
|
+
def filter_by_status(executions)
|
29
|
+
return executions if params[:status].blank?
|
30
|
+
|
31
|
+
case params[:status]
|
32
|
+
when 'completed'
|
33
|
+
executions.where(completed: true)
|
34
|
+
when 'running'
|
35
|
+
executions.where(completed: false).where(updated_at: 1.hour.ago..)
|
36
|
+
when 'failed'
|
37
|
+
executions.where(completed: false).where(updated_at: ...1.hour.ago)
|
38
|
+
else
|
39
|
+
executions
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def filter_by_date_range(executions)
|
44
|
+
executions = filter_by_date_from(executions)
|
45
|
+
filter_by_date_to(executions)
|
46
|
+
end
|
47
|
+
|
48
|
+
def filter_by_date_from(executions)
|
49
|
+
return executions if params[:date_from].blank?
|
50
|
+
|
51
|
+
begin
|
52
|
+
date_from = Date.parse(params[:date_from])
|
53
|
+
executions.where(created_at: date_from.beginning_of_day..)
|
54
|
+
rescue ArgumentError
|
55
|
+
executions
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def filter_by_date_to(executions)
|
60
|
+
return executions if params[:date_to].blank?
|
61
|
+
|
62
|
+
begin
|
63
|
+
date_to = Date.parse(params[:date_to])
|
64
|
+
executions.where(created_at: ..date_to.end_of_day)
|
65
|
+
rescue ArgumentError
|
66
|
+
executions
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def search_executions(executions)
|
71
|
+
return executions if params[:search].blank?
|
72
|
+
|
73
|
+
executions.joins(:workflow)
|
74
|
+
.where(
|
75
|
+
'meta_workflows_workflows.name ILIKE ? OR meta_workflows_workflow_executions.id::text ILIKE ?',
|
76
|
+
"%#{params[:search]}%", "%#{params[:search]}%"
|
77
|
+
)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MetaWorkflows
|
4
|
+
module Tools
|
5
|
+
class MetaWorkflowTool < RubyLLM::Tool
|
6
|
+
ALLOWED_PARAM_TYPES = %w[array string integer number boolean object].freeze
|
7
|
+
|
8
|
+
attr_reader :workflow_execution, :step_output
|
9
|
+
|
10
|
+
def initialize(workflow_execution:, step_output:)
|
11
|
+
super()
|
12
|
+
@workflow_execution = workflow_execution
|
13
|
+
@step_output = step_output
|
14
|
+
|
15
|
+
self.class.description("Workflow step output for #{workflow_execution.workflow.name}")
|
16
|
+
self.class.setup_params(step_output) if step_output.present?
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.setup_params(step_output)
|
20
|
+
step_output.each do |output_param|
|
21
|
+
process_param(output_param)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.process_param(param_def)
|
26
|
+
common_args = process_param_definition(param_def)
|
27
|
+
param param_def['name'].to_sym, **common_args
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.process_array_items(items_array)
|
31
|
+
items_hash = {}
|
32
|
+
|
33
|
+
items_array.each do |param_def|
|
34
|
+
common_args = process_param_definition(param_def)
|
35
|
+
items_hash[param_def['name'].to_sym] = common_args
|
36
|
+
end
|
37
|
+
|
38
|
+
items_hash
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.process_object_properties(properties_hash)
|
42
|
+
properties = {}
|
43
|
+
|
44
|
+
properties_hash.each do |key, property_def|
|
45
|
+
common_args = process_param_definition(property_def)
|
46
|
+
properties[key.to_sym] = common_args
|
47
|
+
end
|
48
|
+
|
49
|
+
properties
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.process_param_definition(param_def)
|
53
|
+
type = param_def['type']
|
54
|
+
|
55
|
+
validate_type!(type)
|
56
|
+
|
57
|
+
common_args = {
|
58
|
+
type: type,
|
59
|
+
desc: param_def['description']
|
60
|
+
}
|
61
|
+
|
62
|
+
if type == 'array'
|
63
|
+
common_args[:items] = process_array_items(param_def['items'])
|
64
|
+
elsif type == 'object' && param_def['properties'].present?
|
65
|
+
common_args[:properties] = process_object_properties(param_def['properties'])
|
66
|
+
end
|
67
|
+
|
68
|
+
common_args
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.validate_type!(type)
|
72
|
+
return if ALLOWED_PARAM_TYPES.include?(type)
|
73
|
+
|
74
|
+
raise ArgumentError, "Invalid type: #{type}. Allowed types are: #{ALLOWED_PARAM_TYPES.join(', ')}"
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.build(workflow_execution, step_output)
|
78
|
+
new(workflow_execution: workflow_execution, step_output: step_output)
|
79
|
+
end
|
80
|
+
|
81
|
+
def execute(**_params)
|
82
|
+
{ success: true, message: 'Output parameters saved successfully' }.to_json
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Meta workflows</title>
|
5
|
+
<%= csrf_meta_tags %>
|
6
|
+
<%= csp_meta_tag %>
|
7
|
+
|
8
|
+
<%= yield :head %>
|
9
|
+
|
10
|
+
<%= stylesheet_link_tag "meta_workflows/application", media: "all" %>
|
11
|
+
</head>
|
12
|
+
<body>
|
13
|
+
|
14
|
+
<%= yield %>
|
15
|
+
|
16
|
+
</body>
|
17
|
+
</html>
|
@@ -0,0 +1,47 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>MetaWorkflows Debug Tool</title>
|
5
|
+
<%= csrf_meta_tags %>
|
6
|
+
<%= csp_meta_tag %>
|
7
|
+
|
8
|
+
<%= yield :head %>
|
9
|
+
|
10
|
+
<%= stylesheet_link_tag "meta_workflows/application", media: "all" %>
|
11
|
+
|
12
|
+
<!-- Tailwind CSS -->
|
13
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
14
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
15
|
+
</head>
|
16
|
+
<body class="bg-gray-50 min-h-screen">
|
17
|
+
<!-- Header Navigation -->
|
18
|
+
<nav class="bg-white shadow-sm border-b border-gray-200">
|
19
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
20
|
+
<div class="flex justify-between items-center h-16">
|
21
|
+
<!-- Logo/Title -->
|
22
|
+
<div class="flex items-center">
|
23
|
+
<h1 class="text-xl font-semibold text-gray-900">
|
24
|
+
MetaWorkflows Debug Tool
|
25
|
+
</h1>
|
26
|
+
</div>
|
27
|
+
|
28
|
+
<!-- Navigation Links -->
|
29
|
+
<div class="flex space-x-8">
|
30
|
+
<%= link_to "Workflows", workflows_path,
|
31
|
+
class: "#{current_page?(workflows_path) || params[:controller].include?('workflow') ? 'text-blue-600 border-b-2 border-blue-600' : 'text-gray-500 hover:text-gray-700'} px-3 py-2 text-sm font-medium" %>
|
32
|
+
<%= link_to "Executions", executions_path,
|
33
|
+
class: "#{current_page?(executions_path) || params[:controller].include?('execution') ? 'text-blue-600 border-b-2 border-blue-600' : 'text-gray-500 hover:text-gray-700'} px-3 py-2 text-sm font-medium" %>
|
34
|
+
</div>
|
35
|
+
</div>
|
36
|
+
</div>
|
37
|
+
</nav>
|
38
|
+
|
39
|
+
<!-- Main Content -->
|
40
|
+
<main class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
|
41
|
+
<%= yield %>
|
42
|
+
</main>
|
43
|
+
|
44
|
+
<!-- Optional JavaScript -->
|
45
|
+
<%= yield :javascripts %>
|
46
|
+
</body>
|
47
|
+
</html>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<%= turbo_frame_tag target_frame_id(record) do %>
|
2
|
+
<div class="flex flex-col items-center justify-center gap-2"
|
3
|
+
data-controller="loading-phrases"
|
4
|
+
data-loading-phrases-phrases-value="<%= (local_assigns[:step_progress] || ["Processing your request...", "Talking to the LLM..."]).to_json %>">
|
5
|
+
<h2
|
6
|
+
class="text-center font-semibold
|
7
|
+
bg-gradient-to-r
|
8
|
+
bg-[length:300%_100%] bg-right-bottom
|
9
|
+
bg-clip-text [-webkit-text-fill-color:transparent]
|
10
|
+
animate-gradient-shift"
|
11
|
+
style="--tw-gradient-stops: #3182ce, #805ad5, #d53f8c, #3182ce;"
|
12
|
+
data-loading-phrases-target="message"
|
13
|
+
>
|
14
|
+
Processing your request...
|
15
|
+
</h2>
|
16
|
+
<div class="sm-sprite loader">
|
17
|
+
<span class="image-container">
|
18
|
+
<img class="" src="https://cdn.strongmind.com/backpack-ui/latest/assets/images/png/loaders/CB_loader.png" alt="loaders/CB_loader.png">
|
19
|
+
</span>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
<% end %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<div data-controller="redirect" data-redirect-url-value="<%= redirect_path %>" data-redirect-delay-value="0"></div>
|