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.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +36 -0
  3. data/README.md +498 -0
  4. data/Rakefile +10 -0
  5. data/app/assets/javascripts/meta_workflows/controllers/loading_phrases_controller.js +31 -0
  6. data/app/assets/javascripts/meta_workflows/controllers/redirect_controller.js +15 -0
  7. data/app/assets/javascripts/meta_workflows/controllers/response_scroll_controller.js +67 -0
  8. data/app/assets/javascripts/meta_workflows_manifest.js +13 -0
  9. data/app/assets/stylesheets/meta_workflows/application.css +161 -0
  10. data/app/controllers/meta_workflows/application_controller.rb +10 -0
  11. data/app/controllers/meta_workflows/debug_controller.rb +101 -0
  12. data/app/controllers/meta_workflows/humans_controller.rb +96 -0
  13. data/app/controllers/meta_workflows/meta_controller.rb +21 -0
  14. data/app/helpers/meta_workflows/application_helper.rb +7 -0
  15. data/app/helpers/meta_workflows/debug_helper.rb +54 -0
  16. data/app/helpers/meta_workflows/execution_helper.rb +77 -0
  17. data/app/helpers/meta_workflows/formatting_helper.rb +30 -0
  18. data/app/helpers/meta_workflows/meta_workflows_helper.rb +41 -0
  19. data/app/helpers/meta_workflows/status_badge_helper.rb +66 -0
  20. data/app/jobs/meta_workflows/application_job.rb +14 -0
  21. data/app/jobs/meta_workflows/human_input_job.rb +35 -0
  22. data/app/jobs/meta_workflows/meta_job.rb +121 -0
  23. data/app/jobs/meta_workflows/meta_workflow_job.rb +20 -0
  24. data/app/jobs/meta_workflows/record_redirect_job.rb +36 -0
  25. data/app/mailers/meta_workflows/application_mailer.rb +8 -0
  26. data/app/models/meta_workflows/application_record.rb +7 -0
  27. data/app/models/meta_workflows/chat.rb +20 -0
  28. data/app/models/meta_workflows/message.rb +11 -0
  29. data/app/models/meta_workflows/tool_call.rb +7 -0
  30. data/app/models/meta_workflows/workflow.rb +32 -0
  31. data/app/models/meta_workflows/workflow_execution.rb +23 -0
  32. data/app/models/meta_workflows/workflow_step.rb +49 -0
  33. data/app/models/meta_workflows.rb +7 -0
  34. data/app/services/meta_workflows/execution_filter_service.rb +80 -0
  35. data/app/sidekiq/meta_workflows/tools/meta_workflow_tool.rb +86 -0
  36. data/app/views/layouts/meta_workflows/application.html.erb +17 -0
  37. data/app/views/layouts/meta_workflows/debug.html.erb +47 -0
  38. data/app/views/meta_workflows/_loader.html.erb +22 -0
  39. data/app/views/meta_workflows/_redirect.html.erb +1 -0
  40. data/app/views/meta_workflows/_response.html.erb +5 -0
  41. data/app/views/meta_workflows/_response_form.html.erb +36 -0
  42. data/app/views/meta_workflows/debug/executions.html.erb +187 -0
  43. data/app/views/meta_workflows/debug/show_execution.html.erb +283 -0
  44. data/app/views/meta_workflows/debug/show_workflow.html.erb +148 -0
  45. data/app/views/meta_workflows/debug/workflows.html.erb +110 -0
  46. data/config/routes.rb +13 -0
  47. data/db/migrate/20250530220618_create_meta_workflows_workflows.rb +16 -0
  48. data/db/migrate/20250530220634_create_meta_workflows_workflow_executions.rb +18 -0
  49. data/db/migrate/20250530220704_create_meta_workflows_chats.rb +16 -0
  50. data/db/migrate/20250530220722_create_meta_workflows_messages.rb +19 -0
  51. data/db/migrate/20250530220737_create_meta_workflows_tool_calls.rb +18 -0
  52. data/db/migrate/20250530220750_create_meta_workflows_workflow_steps.rb +17 -0
  53. data/db/migrate/20250613213159_add_error_fields_to_workflow_steps.rb +8 -0
  54. data/lib/meta_workflows/asset_installer.rb +509 -0
  55. data/lib/meta_workflows/configuration.rb +39 -0
  56. data/lib/meta_workflows/engine.rb +47 -0
  57. data/lib/meta_workflows/export_execution_service.rb +56 -0
  58. data/lib/meta_workflows/version.rb +5 -0
  59. data/lib/meta_workflows.rb +9 -0
  60. data/lib/services/meta_workflows/application_service.rb +11 -0
  61. data/lib/services/meta_workflows/meta_workflow_service.rb +277 -0
  62. data/lib/services/meta_workflows/updaters/meta_service.rb +39 -0
  63. data/lib/tasks/meta_workflows_tasks.rake +153 -0
  64. metadata +219 -0
@@ -0,0 +1,161 @@
1
+ /* AI Generated Content */
2
+ .meta-workflows.chat {
3
+ display: flex;
4
+ flex-direction: column;
5
+ gap: 1.5rem;
6
+ color: var(--slate-800);
7
+ font-size: 1.125rem;
8
+ line-height: 1.75;
9
+
10
+ h1 {
11
+ color: var(--slate-800);
12
+ font-weight: 600;
13
+ margin-bottom: 0.5rem;
14
+ font-size: 1.875rem;
15
+ line-height: 1.25;
16
+ }
17
+
18
+ h2 {
19
+ color: var(--slate-800);
20
+ font-weight: 600;
21
+ margin-bottom: 0.5rem;
22
+ font-size: 1.5rem;
23
+ line-height: 1.25;
24
+ }
25
+
26
+ h3 {
27
+ color: var(--slate-800);
28
+ font-weight: 600;
29
+ margin-bottom: 0.5rem;
30
+ font-size: 1.25rem;
31
+ line-height: 1.25;
32
+ }
33
+
34
+ h4 {
35
+ color: var(--slate-800);
36
+ font-weight: 600;
37
+ margin-bottom: 0.5rem;
38
+ font-size: 1.125rem;
39
+ line-height: 1.25;
40
+ }
41
+
42
+ h5 {
43
+ color: var(--slate-800);
44
+ font-weight: 600;
45
+ margin-bottom: 0.5rem;
46
+ font-size: 1rem;
47
+ line-height: 1.25;
48
+ }
49
+
50
+ h6 {
51
+ color: var(--slate-800);
52
+ font-weight: 600;
53
+ margin-bottom: 0.5rem;
54
+ font-size: 0.875rem;
55
+ line-height: 1.25;
56
+ }
57
+
58
+ /* Combined list styles */
59
+ ul,
60
+ ol {
61
+ display: flex;
62
+ flex-direction: column;
63
+ gap: .5rem;
64
+ padding-left: 0;
65
+ list-style-type: none;
66
+
67
+ li {
68
+ display: flex;
69
+ padding: 1rem 1.375rem;
70
+ border: 1px solid var(--slate-300);
71
+ border-radius: 0.875rem;
72
+ background-color: white;
73
+ font-size: 1.25rem;
74
+ font-weight: 600;
75
+ line-height: 1.375;
76
+
77
+ &::before {
78
+ display: none;
79
+ }
80
+ }
81
+ }
82
+
83
+ /* Ordered list specific styles */
84
+ ol {
85
+ counter-reset: list-counter;
86
+
87
+ li::before {
88
+ content: counter(list-counter) ".";
89
+ counter-increment: list-counter;
90
+ display: inline-block;
91
+ width: 1.5em;
92
+ margin-right: 0.5em;
93
+ color: var(--slate-400);
94
+ }
95
+ }
96
+
97
+ a {
98
+ color: #2563eb;
99
+ text-decoration: underline;
100
+
101
+ &:hover {
102
+ color: #1d4ed8;
103
+ }
104
+ }
105
+
106
+ pre {
107
+ background-color: #f9fafb;
108
+ padding: 1rem;
109
+ border-radius: 0.5rem;
110
+ overflow-x: auto;
111
+ margin: 1rem 0;
112
+ }
113
+
114
+ code {
115
+ background-color: #f9fafb;
116
+ padding: 0.125rem 0.375rem;
117
+ border-radius: 0.25rem;
118
+ font-size: 0.875rem;
119
+ font-family: ui-monospace, monospace;
120
+ }
121
+
122
+ blockquote {
123
+ border-left: 4px solid #e5e7eb;
124
+ padding-left: 1rem;
125
+ font-style: italic;
126
+ color: #374151;
127
+ margin: 1rem 0;
128
+ }
129
+
130
+ table {
131
+ width: 100%;
132
+ border-collapse: collapse;
133
+ margin: 1rem 0;
134
+ }
135
+
136
+ th,
137
+ td {
138
+ border: 1px solid #e5e7eb;
139
+ padding: 0.5rem;
140
+ text-align: left;
141
+ }
142
+
143
+ th {
144
+ background-color: #f9fafb;
145
+ font-weight: 600;
146
+ }
147
+
148
+ img {
149
+ border-radius: 0.5rem;
150
+ margin: 1rem 0;
151
+ }
152
+
153
+ strong {
154
+ font-weight: 600;
155
+ color: var(--slate-800);
156
+ }
157
+
158
+ em {
159
+ font-style: italic;
160
+ }
161
+ }
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MetaWorkflows
4
+ class ApplicationController < ActionController::Base
5
+ protect_from_forgery with: :exception
6
+
7
+ # Add any MetaWorkflows-specific controller functionality here
8
+ # This ensures proper engine isolation
9
+ end
10
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MetaWorkflows
4
+ class DebugController < MetaWorkflows::ApplicationController
5
+ include MetaWorkflows::DebugHelper
6
+
7
+ layout 'meta_workflows/debug'
8
+
9
+ # Apply authentication only in production environment
10
+ before_action :authenticate_with_basic_auth, if: :production_environment?
11
+
12
+ # Skip CSRF protection for this debug interface
13
+ skip_before_action :verify_authenticity_token
14
+
15
+ def index
16
+ redirect_to workflows_path
17
+ end
18
+
19
+ def workflows
20
+ @workflows = MetaWorkflows::Workflow.includes(:workflow_executions)
21
+ .order(created_at: :desc)
22
+
23
+ # Simple search functionality - search by name only
24
+ @workflows = @workflows.where('name ILIKE ?', "%#{params[:search]}%") if params[:search].present?
25
+
26
+ # Apply pagination if available
27
+ @workflows = paginate_collection(@workflows)
28
+ end
29
+
30
+ def show_workflow
31
+ @workflow = MetaWorkflows::Workflow.find(params[:id])
32
+ @executions = @workflow.workflow_executions
33
+ .includes(workflow_steps: :chat)
34
+ .order(created_at: :desc)
35
+
36
+ @executions = paginate_collection(@executions)
37
+ end
38
+
39
+ def executions
40
+ @executions = MetaWorkflows::WorkflowExecution.includes(:workflow, workflow_steps: :chat)
41
+ .order(created_at: :desc)
42
+
43
+ @executions = MetaWorkflows::ExecutionFilterService.new(@executions, params).apply_filters
44
+
45
+ # Apply pagination
46
+ @executions = paginate_collection(@executions)
47
+ end
48
+
49
+ def show_execution
50
+ @execution = MetaWorkflows::WorkflowExecution.includes(
51
+ :workflow,
52
+ workflow_steps: { chat: { messages: :tool_calls } }
53
+ ).find(params[:id])
54
+ end
55
+
56
+ def export_execution
57
+ @execution = MetaWorkflows::WorkflowExecution.includes(
58
+ :workflow,
59
+ workflow_steps: { chat: { messages: :tool_calls } }
60
+ ).find(params[:id])
61
+
62
+ export_data = MetaWorkflows::ExportExecutionService.new(@execution).export_data
63
+
64
+ respond_to do |format|
65
+ format.json { render json: export_data }
66
+ format.html { redirect_to execution_path(@execution), alert: t('meta_workflows.export_only_json') }
67
+ end
68
+ end
69
+
70
+ protected
71
+
72
+ def authenticate_with_basic_auth
73
+ return unless production_environment?
74
+ return unless authentication_required?
75
+
76
+ authenticate_or_request_with_http_basic do |username, password|
77
+ username == ENV.fetch('SIDEKIQ_USER', nil) &&
78
+ password == ENV.fetch('SIDEKIQ_PASSWORD', nil)
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ def production_environment?
85
+ Rails.env.production?
86
+ end
87
+
88
+ def authentication_required?
89
+ ENV['SIDEKIQ_USER'].present? && ENV['SIDEKIQ_PASSWORD'].present?
90
+ end
91
+
92
+ def paginate_collection(collection)
93
+ # Use Kaminari if available, otherwise limit to 25 records
94
+ if defined?(Kaminari) && collection.respond_to?(:page)
95
+ collection.page(params[:page]).per(25)
96
+ else
97
+ collection.limit(25).offset(((params[:page]&.to_i || 1) - 1) * 25)
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MetaWorkflows
4
+ class HumansController < MetaController
5
+ include MetaWorkflows::MetaWorkflowsHelper
6
+ before_action :set_workflow_data, only: [:update]
7
+
8
+ def update
9
+ if params[:advance].present?
10
+ advance_workflow
11
+ render_loader_stream
12
+ else
13
+ process_human_input
14
+ render_response_form_stream
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def render_loader_stream
21
+ respond_to do |format|
22
+ format.turbo_stream do
23
+ render turbo_stream: [
24
+ turbo_stream.replace(
25
+ target_frame_id(@record),
26
+ partial: meta_loader,
27
+ locals: {
28
+ record: @record,
29
+ step_progress: fetch_step_progress
30
+ }
31
+ ),
32
+ turbo_stream.replace(
33
+ target_frame_id(@record, form: true),
34
+ partial: meta_response_form,
35
+ locals: response_form_locals(response_enabled: false)
36
+ )
37
+ ]
38
+ end
39
+ end
40
+ end
41
+
42
+ def render_response_form_stream
43
+ respond_to do |format|
44
+ format.turbo_stream do
45
+ render turbo_stream: turbo_stream.replace(
46
+ target_frame_id(@record, form: true),
47
+ partial: meta_response_form,
48
+ locals: response_form_locals(response_enabled: true, responding: true)
49
+ )
50
+ end
51
+ end
52
+ end
53
+
54
+ def advance_workflow
55
+ dialogue = collect_messages_from_chat
56
+ @workflow_execution.increment_step
57
+
58
+ MetaWorkflows::MetaWorkflowJob.perform_later(
59
+ record_id: @record.id,
60
+ record_type: @record.class.name,
61
+ workflow_name: nil,
62
+ user_id: current_user.id,
63
+ inputs: { dialogue: dialogue }
64
+ )
65
+ end
66
+
67
+ def process_human_input
68
+ MetaWorkflows::HumanInputJob.perform_later(
69
+ user_id: current_user.id,
70
+ record: @record,
71
+ params: {
72
+ inputs: params[:message],
73
+ chat_id: params[:chat_id],
74
+ prompt_id: @prompt_id
75
+ }
76
+ )
77
+ end
78
+
79
+ def collect_messages_from_chat
80
+ messages = MetaWorkflows::Chat.find(params[:chat_id]).messages
81
+ messages.map do |m|
82
+ { role: m.role, content: m.content.strip }
83
+ end
84
+ end
85
+
86
+ def response_form_locals(response_enabled: true, responding: false)
87
+ {
88
+ record: @record,
89
+ workflow_execution_id: @workflow_execution.id,
90
+ chat_id: params[:chat_id],
91
+ response_enabled: response_enabled,
92
+ responding: responding
93
+ }
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MetaWorkflows
4
+ class MetaController < MetaWorkflows::ApplicationController
5
+ include MetaWorkflows::MetaWorkflowsHelper
6
+
7
+ def set_workflow_data
8
+ @workflow_execution = MetaWorkflows::WorkflowExecution.find(params[:id])
9
+ @record = @workflow_execution.record
10
+ @workflow = @workflow_execution.workflow
11
+ @prompt_id = @workflow.prompt_id(@workflow_execution.current_step)
12
+ end
13
+
14
+ protected
15
+
16
+ def fetch_step_progress
17
+ @workflow.step_progress(@workflow_execution.current_step) || ['Processing your request...',
18
+ 'Talking to the LLM...']
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MetaWorkflows
4
+ module ApplicationHelper
5
+ include MetaWorkflows::MetaWorkflowsHelper
6
+ end
7
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MetaWorkflows
4
+ module DebugHelper
5
+ include StatusBadgeHelper
6
+ include ExecutionHelper
7
+ include FormattingHelper
8
+
9
+ # Helper for truncating long text with tooltip
10
+ def truncate_with_tooltip(text, length: 50)
11
+ return '' if text.blank?
12
+
13
+ if text.length > length
14
+ content_tag :span, truncate(text, length: length), title: text, class: 'cursor-help'
15
+ else
16
+ text
17
+ end
18
+ end
19
+
20
+ # Helper for relative time with absolute time tooltip
21
+ def time_with_tooltip(time)
22
+ return 'N/A' if time.blank?
23
+
24
+ content_tag :span, "#{time_ago_in_words(time)} ago",
25
+ title: time.strftime('%Y-%m-%d %H:%M:%S %Z'),
26
+ class: 'cursor-help'
27
+ end
28
+
29
+ # Helper to safely get step data from workflow
30
+ def safe_step_data(workflow, step_number)
31
+ workflow.step_data(step_number)
32
+ rescue StandardError => e
33
+ { 'error' => e.message }
34
+ end
35
+
36
+ # Helper to safely get workflow name by ID
37
+ def safe_workflow_name(workflow_id)
38
+ MetaWorkflows::Workflow.find(workflow_id).name
39
+ rescue ActiveRecord::RecordNotFound
40
+ "Workflow ##{workflow_id}"
41
+ rescue StandardError
42
+ 'Unknown Workflow'
43
+ end
44
+
45
+ # Helper to safely display user information
46
+ def user_display_name(user)
47
+ return 'Unknown User' if user.blank?
48
+
49
+ user.try(:name) || user.try(:email) || "User ##{user.id}"
50
+ rescue StandardError
51
+ 'Unknown User'
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MetaWorkflows
4
+ module ExecutionHelper
5
+ # Helper to calculate execution progress percentage
6
+ def execution_progress_percentage(execution)
7
+ return 0 if execution.workflow.total_steps.zero?
8
+
9
+ ((execution.current_step.to_f / execution.workflow.total_steps) * 100).round(1)
10
+ rescue StandardError
11
+ 0
12
+ end
13
+
14
+ # Helper to format execution progress display
15
+ def execution_progress_display(execution)
16
+ total_steps = execution.workflow.total_steps
17
+ current_step = execution.current_step
18
+ percentage = execution_progress_percentage(execution)
19
+
20
+ content_tag :div do
21
+ concat content_tag(:div, "#{current_step} / #{total_steps}", class: 'text-sm text-gray-900')
22
+ concat content_tag(:div, "#{percentage}% complete", class: 'text-xs text-gray-500')
23
+ end
24
+ rescue StandardError
25
+ content_tag :span, 'N/A', class: 'text-sm text-gray-500'
26
+ end
27
+
28
+ # Helper to display record info
29
+ def record_info_display(execution)
30
+ content_tag :div do
31
+ concat content_tag(:div, execution.record_type, class: 'font-medium')
32
+ concat content_tag(:div, "##{execution.record_id}", class: 'text-xs text-gray-500')
33
+ end
34
+ end
35
+
36
+ # Helper to determine execution status with better logic including errors
37
+ def execution_status(execution)
38
+ return 'completed' if execution.completed?
39
+ return 'error' if execution.workflow_steps.with_errors.any?
40
+ return 'failed' if execution.updated_at < 1.hour.ago
41
+
42
+ 'running'
43
+ end
44
+
45
+ # Helper to get error count for execution
46
+ def execution_error_count(execution)
47
+ execution.workflow_steps.with_errors.count
48
+ end
49
+
50
+ # Helper to display execution status with error awareness
51
+ def execution_status_display(execution)
52
+ status = execution_status(execution)
53
+ error_count = execution_error_count(execution)
54
+
55
+ case status
56
+ when 'completed'
57
+ if error_count.positive?
58
+ content_tag :div do
59
+ concat status_badge('completed')
60
+ concat content_tag(:span, " (#{error_count} error#{'s' if error_count > 1})",
61
+ class: 'text-xs text-red-600 ml-1')
62
+ end
63
+ else
64
+ status_badge('completed')
65
+ end
66
+ when 'error'
67
+ content_tag :div do
68
+ concat status_badge('error')
69
+ concat content_tag(:span, " (#{error_count} error#{'s' if error_count > 1})",
70
+ class: 'text-xs text-red-600 ml-1')
71
+ end
72
+ else
73
+ status_badge(status)
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MetaWorkflows
4
+ module FormattingHelper
5
+ # Format JSON data for readable display
6
+ def pretty_json(data)
7
+ return 'No data' if data.blank?
8
+
9
+ begin
10
+ formatted = data.is_a?(String) ? JSON.parse(data) : data
11
+ JSON.pretty_generate(formatted)
12
+ rescue JSON::ParserError
13
+ data.to_s
14
+ end
15
+ end
16
+
17
+ # Generate a formatted JSON display with syntax highlighting
18
+ def json_display(data, classes: 'bg-gray-100 p-4 rounded-lg text-sm overflow-x-auto')
19
+ content_tag :pre, pretty_json(data), class: classes
20
+ end
21
+
22
+ # Helper to format token usage
23
+ def format_token_usage(input_tokens, output_tokens)
24
+ tokens = [input_tokens, output_tokens].compact
25
+ return '' if tokens.empty?
26
+
27
+ "Tokens: #{tokens.join('/')}"
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MetaWorkflows
4
+ module MetaWorkflowsHelper
5
+ def target_frame_id(record, form: nil)
6
+ base_id = "#{record.class.name.downcase}_#{record.id}"
7
+ form ? "#{base_id}_form" : base_id
8
+ end
9
+
10
+ def turbo_stream_name(record)
11
+ [record.class.name.downcase, record.id]
12
+ end
13
+
14
+ def meta_loader
15
+ 'meta_workflows/loader'
16
+ end
17
+
18
+ def meta_response_form
19
+ 'meta_workflows/response_form'
20
+ end
21
+
22
+ def meta_response
23
+ 'meta_workflows/response'
24
+ end
25
+
26
+ def meta_redirect
27
+ 'meta_workflows/redirect'
28
+ end
29
+
30
+ def random_chat_placeholder
31
+ [
32
+ 'What would you like to improve?',
33
+ 'Suggest an edit or ask for changes...',
34
+ 'Let me know how I can improve this.',
35
+ 'Not quite right? Let me know.',
36
+ 'Edit, clarify, or request a change.',
37
+ 'Happy with this, or want changes?'
38
+ ].sample
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MetaWorkflows
4
+ module StatusBadgeHelper
5
+ # Helper for status badges
6
+ def status_badge(status)
7
+ case status&.to_s&.downcase
8
+ when 'completed', 'success'
9
+ content_tag :span, status.capitalize,
10
+ class: 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ' \
11
+ 'bg-green-100 text-green-800'
12
+ when 'running', 'in_progress', 'processing'
13
+ content_tag :span, status.capitalize,
14
+ class: 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ' \
15
+ 'bg-blue-100 text-blue-800'
16
+ when 'failed', 'error'
17
+ content_tag :span, status.capitalize,
18
+ class: 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ' \
19
+ 'bg-red-100 text-red-800'
20
+ when 'pending', 'waiting'
21
+ content_tag :span, status.capitalize,
22
+ class: 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ' \
23
+ 'bg-yellow-100 text-yellow-800'
24
+ else
25
+ content_tag :span, status.to_s.capitalize,
26
+ class: 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ' \
27
+ 'bg-gray-100 text-gray-800'
28
+ end
29
+ end
30
+
31
+ # Helper to display workflow step status
32
+ def workflow_step_status(workflow_step, current_step)
33
+ if workflow_step.error?
34
+ content_tag :span, 'Error',
35
+ class: 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ' \
36
+ 'bg-red-100 text-red-800'
37
+ elsif workflow_step.step == current_step
38
+ content_tag :span, 'Current',
39
+ class: 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ' \
40
+ 'bg-blue-100 text-blue-800'
41
+ elsif workflow_step.step < current_step
42
+ content_tag :span, 'Completed',
43
+ class: 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ' \
44
+ 'bg-green-100 text-green-800'
45
+ else
46
+ content_tag :span, 'Pending',
47
+ class: 'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ' \
48
+ 'bg-gray-100 text-gray-800'
49
+ end
50
+ end
51
+
52
+ # Helper to format message role with proper styling
53
+ def message_role_badge(role)
54
+ case role&.downcase
55
+ when 'user'
56
+ 'bg-blue-50 border-blue-200'
57
+ when 'assistant'
58
+ 'bg-green-50 border-green-200'
59
+ when 'system'
60
+ 'bg-purple-50 border-purple-200'
61
+ else
62
+ 'bg-gray-50 border-gray-200'
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MetaWorkflows
4
+ class ApplicationJob < ActiveJob::Base
5
+ # Automatically retry jobs that encountered a deadlock
6
+ # retry_on ActiveRecord::Deadlocked
7
+
8
+ # Most jobs are safe to ignore if the underlying records are no longer available
9
+ # discard_on ActiveJob::DeserializationError
10
+
11
+ # Add any MetaWorkflows-specific job functionality here
12
+ # This ensures proper engine isolation
13
+ end
14
+ end