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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: afd80be43cbf526411e03bbf7ced8c539b12d81e82d19b2e805dc2a85045aa83
|
4
|
+
data.tar.gz: 4ce850bc8e751d2271ea049724bce5ddbf82d55b9d83749e7d06cfb1d0aebb2c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4fda18b86bf518cfbace011470987b8aa6fb7d1ebbfbb8e95933498736a6792b9c90183a8e820a2868ed8a34dab8cb0a32e194f054315d6bfbbdd4f6c067feb6
|
7
|
+
data.tar.gz: 45148c5baac802a9c4c330206724b4e8a867df7886a2fc984b9f5b5854a14d926336492c88c0e0f718f84a3d0b8973373f682eaf20d628929dd03d7bc4c5a9ee
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to the MetaWorkflows engine will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## [Unreleased]
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- Initial Rails engine structure
|
12
|
+
- Configuration system for engine customization
|
13
|
+
- Support for Rails 7.2+
|
14
|
+
- Integration with Sidekiq for background job processing
|
15
|
+
- Integration with ruby_conversations for AI chat functionality
|
16
|
+
- ViewComponent support for UI components
|
17
|
+
- Turbo and Stimulus integration for frontend interactivity
|
18
|
+
- PostgreSQL database support following host application patterns
|
19
|
+
|
20
|
+
### Changed
|
21
|
+
- Extracted from course-builder monolith into standalone engine
|
22
|
+
- Updated dependencies for Rails 7.2 compatibility
|
23
|
+
- Switched from SQLite3 to PostgreSQL for database backend
|
24
|
+
- Updated dummy application to use PostgreSQL configuration
|
25
|
+
- Aligned database setup with host application (course-builder) patterns
|
26
|
+
|
27
|
+
## [0.1.0] - 2025-01-XX
|
28
|
+
|
29
|
+
### Added
|
30
|
+
- Initial release of MetaWorkflows as a Rails engine
|
31
|
+
- Core workflow execution framework
|
32
|
+
- AI-powered conversation management
|
33
|
+
- Human interaction points in workflows
|
34
|
+
- Background job processing with Sidekiq
|
35
|
+
- Modern UI components with TailwindCSS and Stimulus
|
36
|
+
- PostgreSQL database backend for reliability and scalability
|
data/README.md
ADDED
@@ -0,0 +1,498 @@
|
|
1
|
+
# MetaWorkflows
|
2
|
+
|
3
|
+
<div align="center">
|
4
|
+
<img src="spec/dummy/public/icon.svg" alt="MetaWorkflows Logo" width="128" height="128">
|
5
|
+
</div>
|
6
|
+
|
7
|
+
A Rails engine for managing AI-powered meta workflows with human interaction points, extracted from course-builder into a standalone, reusable Rails gem.
|
8
|
+
|
9
|
+
## Overview
|
10
|
+
|
11
|
+
MetaWorkflows provides a flexible framework for creating and executing AI-powered workflows that can include human interaction points, background job processing, and modern UI components. Built as a Rails engine for easy integration into existing applications, it enables sophisticated multi-step processes that combine AI automation with human oversight.
|
12
|
+
|
13
|
+
Originally developed as part of the course-builder application and extracted to improve modularity and enable reuse across multiple applications. The system orchestrates complex workflows requiring both AI agent and human interactions through a configurable YAML-based workflow definition system.
|
14
|
+
|
15
|
+
## Key Features
|
16
|
+
|
17
|
+
- **AI-Powered Workflows**: Integration with `ruby_conversations` for AI chat functionality and tool calling
|
18
|
+
- **Human Interaction Points**: Pause workflows for human input, review, and feedback with built-in UI components
|
19
|
+
- **Background Processing**: Sidekiq integration for reliable, asynchronous job execution
|
20
|
+
- **Modern UI**: TailwindCSS styling with Turbo and Stimulus for real-time interactivity
|
21
|
+
- **Flexible Configuration**: YAML-based workflow definitions with multiple action types
|
22
|
+
- **Rails Engine Architecture**: Clean integration as a mountable engine with proper namespacing
|
23
|
+
- **Atomic Operations**: Database transactions ensure data integrity during batch operations
|
24
|
+
- **Extensible Service Layer**: Pluggable services for record updates and collection creation
|
25
|
+
|
26
|
+
## System Architecture
|
27
|
+
|
28
|
+
### Core Components
|
29
|
+
|
30
|
+
The MetaWorkflows system consists of several interconnected components:
|
31
|
+
|
32
|
+
#### Database Models
|
33
|
+
- **MetaWorkflows::Workflow**: Stores workflow definitions with named steps and JSON recipes
|
34
|
+
- **MetaWorkflows::WorkflowExecution**: Tracks active workflow instances and their state
|
35
|
+
- **MetaWorkflows::WorkflowStep**: Represents individual steps within a workflow execution
|
36
|
+
- **MetaWorkflows::Chat**: Manages LLM conversation contexts for workflow steps
|
37
|
+
- **MetaWorkflows::Message**: Stores individual messages within workflow conversations
|
38
|
+
- **MetaWorkflows::ToolCall**: Records tool calls made during LLM interactions
|
39
|
+
|
40
|
+
#### Controllers
|
41
|
+
- **MetaWorkflows::MetaController**: Base controller with shared workflow functionality
|
42
|
+
- **MetaWorkflows::HumansController**: Manages human interactions within workflows
|
43
|
+
|
44
|
+
#### Background Jobs
|
45
|
+
- **MetaWorkflows::MetaWorkflowJob**: Entry point for workflow processing and step coordination
|
46
|
+
- **MetaWorkflows::MetaJob**: Base job class for workflow-related processing
|
47
|
+
- **MetaWorkflows::HumanInputJob**: Handles human input processing within workflows
|
48
|
+
- **MetaWorkflows::RecordRedirectJob**: Manages post-workflow redirections
|
49
|
+
|
50
|
+
#### Services
|
51
|
+
- **Services::MetaWorkflows::MetaWorkflowService**: Central workflow coordinator that processes all workflow steps and actions
|
52
|
+
- **Services::MetaWorkflows::Updaters::MetaService**: Generic record update service for modifying model attributes
|
53
|
+
|
54
|
+
## Workflow Action Types
|
55
|
+
|
56
|
+
MetaWorkflows supports several action types for different workflow needs:
|
57
|
+
|
58
|
+
### `agent`
|
59
|
+
- Executes LLM interactions using specified prompts and tools
|
60
|
+
- Requires `prompt_id` configuration
|
61
|
+
- Supports tool calling and complex reasoning tasks
|
62
|
+
- Output is extracted and stored for subsequent steps
|
63
|
+
|
64
|
+
### `human`
|
65
|
+
- Presents UI for human interaction and input collection
|
66
|
+
- Requires `prompt_id` for context presentation
|
67
|
+
- Collects user feedback through forms
|
68
|
+
- Workflow pauses until human input is provided
|
69
|
+
|
70
|
+
### `collection_create`
|
71
|
+
- Creates collections of related records atomically
|
72
|
+
- Requires `collection_creator` service class name
|
73
|
+
- Handles batch creation operations with all-or-nothing semantics
|
74
|
+
- Automatically halts workflow on any creation failure
|
75
|
+
|
76
|
+
### `record_update`
|
77
|
+
- Updates the associated record with specified attributes
|
78
|
+
- Requires `record_updater` service class name and `attributes_to_update` list
|
79
|
+
- Uses values from `workflow_params` to update the record
|
80
|
+
- Supports both generic and custom updater services
|
81
|
+
|
82
|
+
### `record_redirect`
|
83
|
+
- Redirects users to specified paths
|
84
|
+
- Marks workflow as complete
|
85
|
+
- Requires `redirect_path` or `redirect_method` configuration
|
86
|
+
|
87
|
+
## Requirements
|
88
|
+
|
89
|
+
- **Rails 7.2+**: Compatible with modern Rails applications
|
90
|
+
- **PostgreSQL**: Database backend (9.3 and up supported)
|
91
|
+
- **Sidekiq**: Background job processing with Redis
|
92
|
+
- **Ruby 3.2+**: Modern Ruby version
|
93
|
+
- **TailwindCSS**: For UI styling (configured automatically)
|
94
|
+
|
95
|
+
## Installation
|
96
|
+
|
97
|
+
Add this line to your application's Gemfile:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
gem 'meta_workflows'
|
101
|
+
```
|
102
|
+
|
103
|
+
And then execute:
|
104
|
+
|
105
|
+
```bash
|
106
|
+
bundle install
|
107
|
+
```
|
108
|
+
|
109
|
+
Or install it yourself as:
|
110
|
+
|
111
|
+
```bash
|
112
|
+
gem install meta_workflows
|
113
|
+
```
|
114
|
+
|
115
|
+
## Setup
|
116
|
+
|
117
|
+
### 1. Database Setup
|
118
|
+
|
119
|
+
Ensure PostgreSQL is installed and running on your system. The engine uses PostgreSQL for reliable data storage and supports the same configuration as your host Rails application.
|
120
|
+
|
121
|
+
### 2. Environment Configuration
|
122
|
+
|
123
|
+
For development and testing, configure the required environment variables:
|
124
|
+
|
125
|
+
```bash
|
126
|
+
# Copy the example environment file
|
127
|
+
cp spec/dummy/.env.example spec/dummy/.env
|
128
|
+
|
129
|
+
# Edit the .env file with your actual values
|
130
|
+
```
|
131
|
+
|
132
|
+
The following environment variables are required for StrongMind Identity SSO integration:
|
133
|
+
|
134
|
+
| Variable | Description | Example |
|
135
|
+
|:--------:|:----------:|:-------:|
|
136
|
+
| `IDENTITY_CLIENT_ID` | Your StrongMind Identity client ID | `course-builder-development` |
|
137
|
+
| `IDENTITY_CLIENT_SECRET` | Your StrongMind Identity client secret | `your-secret-key` |
|
138
|
+
| `IDENTITY_BASE_URL` | StrongMind Identity service URL | `https://devlogin.strongmind.com` |
|
139
|
+
|
140
|
+
> 💡 **Tip:** Contact your system administrator for the appropriate values for your environment.
|
141
|
+
|
142
|
+
### 3. Install and Run Migrations
|
143
|
+
|
144
|
+
```bash
|
145
|
+
rails meta_workflows:install:migrations
|
146
|
+
rails db:migrate
|
147
|
+
```
|
148
|
+
|
149
|
+
### 4. Mount the Engine
|
150
|
+
|
151
|
+
Add to your `config/routes.rb`:
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
Rails.application.routes.draw do
|
155
|
+
mount MetaWorkflows::Engine, at: '/meta_workflows'
|
156
|
+
# ... your other routes
|
157
|
+
end
|
158
|
+
```
|
159
|
+
|
160
|
+
### 5. Configure Sidekiq
|
161
|
+
|
162
|
+
Ensure Sidekiq is configured in your application for background job processing:
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
# config/application.rb or config/initializers/sidekiq.rb
|
166
|
+
require 'sidekiq'
|
167
|
+
|
168
|
+
# Configure Redis connection and other Sidekiq settings
|
169
|
+
```
|
170
|
+
|
171
|
+
## Usage
|
172
|
+
|
173
|
+
### Creating Workflow Definitions
|
174
|
+
|
175
|
+
Define workflows in YAML files within your configured workflow definitions path:
|
176
|
+
|
177
|
+
```yaml
|
178
|
+
# config/workflows/example_workflow.yml
|
179
|
+
name: "example_workflow"
|
180
|
+
description: "An example workflow demonstrating multiple action types"
|
181
|
+
steps:
|
182
|
+
- name: "initial_generation"
|
183
|
+
action: "agent"
|
184
|
+
prompt_id: "generation_prompt"
|
185
|
+
step_progress:
|
186
|
+
- "Analyzing input parameters..."
|
187
|
+
- "Generating initial content..."
|
188
|
+
- "Refining results..."
|
189
|
+
input:
|
190
|
+
title:
|
191
|
+
type: "string"
|
192
|
+
required: true
|
193
|
+
description: "The title for processing"
|
194
|
+
output:
|
195
|
+
generated_content:
|
196
|
+
type: "string"
|
197
|
+
description: "The generated content"
|
198
|
+
|
199
|
+
- name: "human_review"
|
200
|
+
action: "human"
|
201
|
+
prompt_id: "review_prompt"
|
202
|
+
step_progress:
|
203
|
+
- "Preparing content for review..."
|
204
|
+
input:
|
205
|
+
generated_content:
|
206
|
+
type: "string"
|
207
|
+
from_step: "initial_generation"
|
208
|
+
output:
|
209
|
+
user_feedback:
|
210
|
+
type: "string"
|
211
|
+
description: "User feedback on the content"
|
212
|
+
|
213
|
+
- name: "record_update"
|
214
|
+
action: "record_update"
|
215
|
+
record_updater: "Services::MetaWorkflows::Updaters::MetaService"
|
216
|
+
attributes_to_update:
|
217
|
+
- "title"
|
218
|
+
- "topic"
|
219
|
+
step_progress:
|
220
|
+
- "Updating record..."
|
221
|
+
- "Saving changes..."
|
222
|
+
workflow_params:
|
223
|
+
- name: "title"
|
224
|
+
type: string
|
225
|
+
description: "The new title for the record"
|
226
|
+
|
227
|
+
- name: "completion_redirect"
|
228
|
+
action: "record_redirect"
|
229
|
+
redirect_path: "/your/completion/path"
|
230
|
+
```
|
231
|
+
|
232
|
+
### Executing Workflows
|
233
|
+
|
234
|
+
#### From Controller Actions
|
235
|
+
|
236
|
+
```ruby
|
237
|
+
class YourController < ApplicationController
|
238
|
+
def start_workflow
|
239
|
+
@record = YourModel.find(params[:id])
|
240
|
+
|
241
|
+
MetaWorkflows::MetaWorkflowJob.perform_later(
|
242
|
+
workflow_name: "example_workflow",
|
243
|
+
record_type: @record.class.name,
|
244
|
+
record_id: @record.id,
|
245
|
+
user_id: current_user.id,
|
246
|
+
inputs: {
|
247
|
+
title: @record.title,
|
248
|
+
# other initial parameters
|
249
|
+
}
|
250
|
+
)
|
251
|
+
|
252
|
+
redirect_to workflow_path(@record)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
```
|
256
|
+
|
257
|
+
#### Direct Job Execution
|
258
|
+
|
259
|
+
```ruby
|
260
|
+
# Start a workflow
|
261
|
+
MetaWorkflows::MetaWorkflowJob.perform_later(
|
262
|
+
'example_workflow',
|
263
|
+
context: {
|
264
|
+
user_id: 1,
|
265
|
+
record_id: 123,
|
266
|
+
record_type: 'YourModel'
|
267
|
+
}
|
268
|
+
)
|
269
|
+
```
|
270
|
+
|
271
|
+
### Using Meta Workflow UI Components
|
272
|
+
|
273
|
+
Instead of creating custom views from scratch, use the provided Meta Workflow partials:
|
274
|
+
|
275
|
+
#### Complete View Example
|
276
|
+
|
277
|
+
```erb
|
278
|
+
<%= turbo_stream_from turbo_stream_name(@record) %>
|
279
|
+
<div class="max-w-3xl m-auto flex flex-col p-10 gap-6">
|
280
|
+
<!-- Workflow progress and chat messages area -->
|
281
|
+
<%= render partial: 'meta_workflows/loader', locals: {
|
282
|
+
record: @record,
|
283
|
+
step_progress: ["Generating draft content...", "Processing with AI..."]
|
284
|
+
} %>
|
285
|
+
|
286
|
+
<!-- User input area for human interaction steps -->
|
287
|
+
<%= render partial: 'meta_workflows/response_form', locals: {
|
288
|
+
record: @record,
|
289
|
+
response_enabled: false
|
290
|
+
} %>
|
291
|
+
</div>
|
292
|
+
```
|
293
|
+
|
294
|
+
## File Structure Overview
|
295
|
+
|
296
|
+
The MetaWorkflows gem follows a structured organization:
|
297
|
+
|
298
|
+
```
|
299
|
+
meta_workflows/
|
300
|
+
├── app/
|
301
|
+
│ ├── controllers/meta_workflows/ # Workflow controllers
|
302
|
+
│ ├── jobs/meta_workflows/ # Background jobs
|
303
|
+
│ ├── models/meta_workflows/ # Database models
|
304
|
+
│ └── views/meta_workflows/ # UI partials and templates
|
305
|
+
├── lib/
|
306
|
+
│ └── services/meta_workflows/ # Business logic services
|
307
|
+
│ ├── updaters/ # Record update services
|
308
|
+
│ └── creators/ # Collection creation services
|
309
|
+
├── config/
|
310
|
+
│ └── workflows/ # YAML workflow definitions
|
311
|
+
└── spec/
|
312
|
+
└── dummy/ # Test Rails application
|
313
|
+
```
|
314
|
+
|
315
|
+
## Development
|
316
|
+
|
317
|
+
### Setting Up Development Environment
|
318
|
+
|
319
|
+
After checking out the repo, run:
|
320
|
+
|
321
|
+
```bash
|
322
|
+
bundle install
|
323
|
+
```
|
324
|
+
|
325
|
+
Set up the test database:
|
326
|
+
|
327
|
+
```bash
|
328
|
+
cd spec/dummy
|
329
|
+
rails db:create
|
330
|
+
rails db:migrate
|
331
|
+
```
|
332
|
+
|
333
|
+
### Running Tests
|
334
|
+
|
335
|
+
To run the test suite:
|
336
|
+
|
337
|
+
```bash
|
338
|
+
bundle exec rspec
|
339
|
+
```
|
340
|
+
|
341
|
+
### Dummy Application
|
342
|
+
|
343
|
+
The gem includes a complete Rails application in `spec/dummy/` for testing and development:
|
344
|
+
|
345
|
+
```bash
|
346
|
+
# Set up environment variables
|
347
|
+
cp spec/dummy/.env.example spec/dummy/.env
|
348
|
+
# Edit spec/dummy/.env with your actual values
|
349
|
+
|
350
|
+
# Start the dummy application
|
351
|
+
cd spec/dummy
|
352
|
+
rails server
|
353
|
+
```
|
354
|
+
|
355
|
+
The dummy app demonstrates MetaWorkflows integration with:
|
356
|
+
- User authentication (Devise)
|
357
|
+
- Post and Comment models with associations
|
358
|
+
- Complete CRUD operations
|
359
|
+
- MetaWorkflows engine mounted at `/meta_workflows`
|
360
|
+
- StrongMind Identity SSO integration
|
361
|
+
|
362
|
+
## Extending the System
|
363
|
+
|
364
|
+
### Creating Custom Record Updater Services
|
365
|
+
|
366
|
+
```ruby
|
367
|
+
# lib/services/meta_workflows/updaters/your_model_service.rb
|
368
|
+
module Services
|
369
|
+
module MetaWorkflows
|
370
|
+
module Updaters
|
371
|
+
class YourModelService < ApplicationService
|
372
|
+
def initialize(record, attributes_to_update, workflow_params)
|
373
|
+
@record = record
|
374
|
+
@attributes_to_update = attributes_to_update
|
375
|
+
@workflow_params = workflow_params
|
376
|
+
end
|
377
|
+
|
378
|
+
def call
|
379
|
+
update_attributes = build_update_attributes
|
380
|
+
record.update!(update_attributes)
|
381
|
+
record
|
382
|
+
end
|
383
|
+
|
384
|
+
private
|
385
|
+
|
386
|
+
def build_update_attributes
|
387
|
+
# Custom logic for building update attributes
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
```
|
394
|
+
|
395
|
+
### Creating Custom Collection Creator Services
|
396
|
+
|
397
|
+
```ruby
|
398
|
+
# lib/services/meta_workflows/creators/your_collection_service.rb
|
399
|
+
module Services
|
400
|
+
module MetaWorkflows
|
401
|
+
module Creators
|
402
|
+
class YourCollectionService
|
403
|
+
def self.create_collection(parent, items, options = {})
|
404
|
+
created_items = []
|
405
|
+
|
406
|
+
ActiveRecord::Base.transaction do
|
407
|
+
items.each do |item_data|
|
408
|
+
created_items << create_item(parent, item_data, options)
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
created_items
|
413
|
+
end
|
414
|
+
|
415
|
+
private_class_method
|
416
|
+
|
417
|
+
def self.create_item(parent, data, options = {})
|
418
|
+
YourModel.create!(
|
419
|
+
content: data[:content],
|
420
|
+
parent: parent,
|
421
|
+
organization_id: parent.organization_id
|
422
|
+
)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|
427
|
+
end
|
428
|
+
```
|
429
|
+
|
430
|
+
### Adding New Action Types
|
431
|
+
|
432
|
+
To add a new action type:
|
433
|
+
|
434
|
+
1. Add the action to `ACTIONS_WITHOUT_CONVERSATION` in `Services::MetaWorkflows::MetaWorkflowService` if it doesn't require conversation tracking
|
435
|
+
2. Implement a `process_[action_name]_action` method in the service
|
436
|
+
3. Add the action handling to the `process_action` method
|
437
|
+
4. Create any necessary supporting services
|
438
|
+
5. Add comprehensive tests for the new action type
|
439
|
+
|
440
|
+
## Best Practices
|
441
|
+
|
442
|
+
### Workflow Design
|
443
|
+
- Keep steps focused and single-purpose
|
444
|
+
- Design clear input/output schemas
|
445
|
+
- Provide meaningful progress messages
|
446
|
+
- Handle error cases gracefully
|
447
|
+
- Use `record_update` steps to persist intermediate workflow results
|
448
|
+
|
449
|
+
### Performance
|
450
|
+
- Use background jobs for long-running processes
|
451
|
+
- Implement appropriate caching strategies
|
452
|
+
- Monitor workflow execution times
|
453
|
+
- Set reasonable timeouts for LLM interactions
|
454
|
+
- Use bulk operations in collection creators when possible
|
455
|
+
|
456
|
+
### UI/UX Considerations
|
457
|
+
- Use the provided meta partials for consistent UI
|
458
|
+
- Provide meaningful step progress messages in workflow definitions
|
459
|
+
- Ensure proper Turbo Stream configuration for real-time updates
|
460
|
+
- Handle edge cases and error states appropriately
|
461
|
+
|
462
|
+
## Architecture Details
|
463
|
+
|
464
|
+
### Engine Isolation
|
465
|
+
- All classes properly namespaced under `MetaWorkflows::`
|
466
|
+
- Controllers inherit from `MetaWorkflows::ApplicationController`
|
467
|
+
- Jobs inherit from `MetaWorkflows::ApplicationJob`
|
468
|
+
- Models use engine-specific associations and validations
|
469
|
+
- Services organized under `Services::MetaWorkflows::` namespace
|
470
|
+
|
471
|
+
### Database Compatibility
|
472
|
+
- Migration files include `table_exists?` checks to prevent conflicts
|
473
|
+
- Exact schema compatibility maintained with existing applications
|
474
|
+
- Proper foreign key references and indexes included
|
475
|
+
- Migration timestamps ordered correctly for dependency resolution
|
476
|
+
|
477
|
+
## Contributing
|
478
|
+
|
479
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/strongmind/meta_workflows.
|
480
|
+
|
481
|
+
For development contributions:
|
482
|
+
1. Fork the repository
|
483
|
+
2. Create a feature branch
|
484
|
+
3. Add comprehensive tests
|
485
|
+
4. Follow the established code patterns and namespacing
|
486
|
+
5. Update documentation as needed
|
487
|
+
6. Submit a pull request
|
488
|
+
|
489
|
+
## Documentation
|
490
|
+
|
491
|
+
For detailed information about the MetaWorkflows system:
|
492
|
+
- [Setup Guide](https://strongmind.atlassian.net/wiki/spaces/MW/pages/3929833507) - Configuration and workflow setup
|
493
|
+
- [File Structure Guide](https://strongmind.atlassian.net/wiki/spaces/MW/pages/3930521627) - Complete file organization reference
|
494
|
+
- [Dummy App Guide](https://strongmind.atlassian.net/wiki/spaces/MW/pages/3941072927) - Test application documentation
|
495
|
+
|
496
|
+
## License
|
497
|
+
|
498
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
// Connects to data-controller="loading-phrases"
|
4
|
+
export default class extends Controller {
|
5
|
+
static targets = ["message"]
|
6
|
+
static values = {
|
7
|
+
phrases: Array
|
8
|
+
}
|
9
|
+
|
10
|
+
connect() {
|
11
|
+
if (!this.hasPhrasesValue || this.phrasesValue.length === 0) {
|
12
|
+
this.phrasesValue = ["Talking to the LLM..."]
|
13
|
+
}
|
14
|
+
|
15
|
+
this.currentIndex = 0
|
16
|
+
this.startCycling()
|
17
|
+
}
|
18
|
+
|
19
|
+
disconnect() {
|
20
|
+
if (this.intervalId) {
|
21
|
+
clearInterval(this.intervalId)
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
startCycling() {
|
26
|
+
this.intervalId = setInterval(() => {
|
27
|
+
this.currentIndex = (this.currentIndex + 1) % this.phrasesValue.length
|
28
|
+
this.messageTarget.textContent = this.phrasesValue[this.currentIndex]
|
29
|
+
}, 3000) // Change phrase every 3 seconds
|
30
|
+
}
|
31
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
// Connects to data-controller="redirect"
|
4
|
+
export default class extends Controller {
|
5
|
+
static values = {
|
6
|
+
url: String,
|
7
|
+
delay: { type: Number, default: 500 }
|
8
|
+
}
|
9
|
+
|
10
|
+
connect() {
|
11
|
+
setTimeout(() => {
|
12
|
+
Turbo.visit(this.urlValue)
|
13
|
+
}, this.delayValue)
|
14
|
+
}
|
15
|
+
}
|
@@ -0,0 +1,67 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
connect() {
|
5
|
+
this.setupObserver()
|
6
|
+
this.setupTurboListener()
|
7
|
+
this.scrollToBottom()
|
8
|
+
}
|
9
|
+
|
10
|
+
disconnect() {
|
11
|
+
if (this.observer) {
|
12
|
+
this.observer.disconnect()
|
13
|
+
}
|
14
|
+
document.removeEventListener("turbo:frame-render", this.handleTurboRender)
|
15
|
+
}
|
16
|
+
|
17
|
+
setupObserver() {
|
18
|
+
this.observer = new MutationObserver((mutations) => {
|
19
|
+
if (mutations.some(mutation =>
|
20
|
+
mutation.type === 'childList' ||
|
21
|
+
mutation.type === 'characterData'
|
22
|
+
)) {
|
23
|
+
setTimeout(() => this.scrollToBottom(), 10)
|
24
|
+
}
|
25
|
+
})
|
26
|
+
|
27
|
+
const contentContainer = this.element.closest('#response-content-container')
|
28
|
+
if (contentContainer) {
|
29
|
+
this.observer.observe(contentContainer, {
|
30
|
+
childList: true,
|
31
|
+
subtree: true,
|
32
|
+
characterData: true,
|
33
|
+
attributes: true
|
34
|
+
})
|
35
|
+
} else {
|
36
|
+
this.observer.observe(this.element, {
|
37
|
+
childList: true,
|
38
|
+
subtree: true,
|
39
|
+
characterData: true,
|
40
|
+
attributes: true
|
41
|
+
})
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
setupTurboListener() {
|
46
|
+
this.handleTurboRender = () => {
|
47
|
+
setTimeout(() => this.scrollToBottom(), 10)
|
48
|
+
}
|
49
|
+
document.addEventListener("turbo:frame-render", this.handleTurboRender)
|
50
|
+
}
|
51
|
+
|
52
|
+
scrollToBottom() {
|
53
|
+
const mainContainer = document.querySelector('#main-scroll-container')
|
54
|
+
if (!mainContainer) return
|
55
|
+
|
56
|
+
const scrollHeight = mainContainer.scrollHeight
|
57
|
+
const clientHeight = mainContainer.clientHeight
|
58
|
+
const maxScroll = scrollHeight - clientHeight
|
59
|
+
|
60
|
+
if (maxScroll > 0) {
|
61
|
+
mainContainer.scrollTo({
|
62
|
+
top: maxScroll,
|
63
|
+
behavior: 'smooth'
|
64
|
+
})
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
//= require_tree ./meta_workflows/controllers
|
2
|
+
|
3
|
+
// MetaWorkflows Stimulus Controllers
|
4
|
+
import LoadingPhrasesController from "./meta_workflows/controllers/loading_phrases_controller"
|
5
|
+
import ResponseScrollController from "./meta_workflows/controllers/response_scroll_controller"
|
6
|
+
import RedirectController from "./meta_workflows/controllers/redirect_controller"
|
7
|
+
|
8
|
+
// Register controllers with Stimulus
|
9
|
+
const application = window.Stimulus || Application.start()
|
10
|
+
|
11
|
+
application.register("loading-phrases", LoadingPhrasesController)
|
12
|
+
application.register("response-scroll", ResponseScrollController)
|
13
|
+
application.register("redirect", RedirectController)
|