ad-agent_architecture 0.0.3 → 0.0.5
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 +4 -4
- data/.rubocop.yml +1 -0
- data/CHANGELOG.md +14 -0
- data/README.md +2 -0
- data/docs/erd.svg +12 -0
- data/docs/images/sample-workflow.png +0 -0
- data/docs/requirements.md +618 -74
- data/docs/structure-youtube-script.yaml +177 -0
- data/lib/ad/agent_architecture/db/create_schema.rb +159 -0
- data/lib/ad/agent_architecture/db/models.rb +76 -0
- data/lib/ad/agent_architecture/version.rb +1 -1
- data/lib/ad/agent_architecture.rb +10 -0
- data/package-lock.json +2 -2
- data/package.json +1 -1
- metadata +35 -2
data/docs/requirements.md
CHANGED
@@ -1,99 +1,643 @@
|
|
1
|
-
### Analysis and Clarification of Workflow Documents
|
2
1
|
|
3
|
-
|
2
|
+
## Schema for AI Agents
|
4
3
|
|
5
|
-
###
|
4
|
+
### Entity Relationship Diagram
|
6
5
|
|
7
|
-
|
8
|
-
- The YouTube script workflow is a complete list of steps performed primarily by AI (ChatGPT), with minimal human input, passed through various prompts to generate the output.
|
9
|
-
- The Medium article workflow is similar but not fully documented, indicating the same concept for generating content from a transcript.
|
10
|
-
- These workflows are common in agent automation frameworks, where AI performs most tasks, with humans providing quality assurance.
|
6
|
+

|
11
7
|
|
12
|
-
|
13
|
-
- Parameters are collected and built over time, starting with a simple project title (simple_title) for YouTube and a transcript for Medium articles.
|
14
|
-
- Static input parameters like brand and target audience are crucial for defining the content's direction.
|
8
|
+
### Static Workflow Definition
|
15
9
|
|
16
|
-
|
17
|
-
- Both workflows consist of distinct phases: research and script/article writing.
|
18
|
-
- The research phase involves gathering information and generating potential content elements (titles, factsheets, topics).
|
19
|
-
- The writing phase focuses on drafting, revising, and finalizing the content.
|
10
|
+
Static workflow definition defines the structure of a workflow, including its sections, steps, attributes, and their relationships.
|
20
11
|
|
21
|
-
|
22
|
-
- Parameters range from simple (single values) to complex (arrays of values).
|
23
|
-
- Initial parameters are relatively simple but grow in complexity as the workflow progresses.
|
12
|
+

|
24
13
|
|
25
|
-
|
26
|
-
- Prompts are human-written text files with input parameter placeholders.
|
27
|
-
- Outputs can be simple (text) or complex (arrays of values such as engaging_titles[], keywords[], topics[]).
|
28
|
-
- Complex outputs generate new parameters for further steps.
|
14
|
+
### Static Workflow Entities
|
29
15
|
|
30
|
-
|
31
|
-
- Certain steps require human decisions, such as selecting the focus_video_type from multiple generated topics.
|
32
|
-
- Human input is crucial for quality assurance and final content selection.
|
16
|
+
#### Table: workflows
|
33
17
|
|
34
|
-
|
35
|
-
- Workflows often involve iterative steps, refining outputs until satisfactory results are achieved.
|
36
|
-
- Parallel workflows can be initiated to explore multiple options simultaneously, using the same initial parameters but diverging paths.
|
18
|
+
This table stores the basic information about each workflow, including its unique identifier, name, and description.
|
37
19
|
|
38
|
-
|
20
|
+
| Field | Type | Description |
|
21
|
+
|-------------|--------|-------------------------------------|
|
22
|
+
| id | string | Unique identifier for the workflow |
|
23
|
+
| name | string | The name of the workflow |
|
24
|
+
| description | string | A brief description of the workflow |
|
39
25
|
|
40
|
-
####
|
26
|
+
#### Table: sections
|
27
|
+
This table stores the sections that belong to a workflow. Each section has an order within its workflow.
|
41
28
|
|
42
|
-
|
43
|
-
|
44
|
-
|
29
|
+
| Field | Type | Description |
|
30
|
+
|-------------|---------|----------------------------------------|
|
31
|
+
| id | string | Unique identifier for the section |
|
32
|
+
| workflow_id | string | Foreign key referencing workflows |
|
33
|
+
| name | string | The name of the section |
|
34
|
+
| description | string | A brief description of the section |
|
35
|
+
| order | integer | The order of the section in the workflow|
|
45
36
|
|
46
|
-
|
47
|
-
- **Research Phase**:
|
48
|
-
1. Generate potential titles using YouTube search.
|
49
|
-
2. Create a detailed factsheet using web search and AI tools.
|
50
|
-
3. Identify video types based on factsheet.
|
51
|
-
4. Expand factsheet with focus video type.
|
52
|
-
5. Generate engaging titles, keywords, and topics.
|
53
|
-
- **Script Writing Phase**:
|
54
|
-
1. Create basic script.
|
55
|
-
2. Clean and revise transcript.
|
56
|
-
3. Fact-check and finalize transcript.
|
37
|
+
#### Table: steps
|
57
38
|
|
58
|
-
|
59
|
-
- Decision on focus_video_type.
|
60
|
-
- Final review and quality assurance.
|
39
|
+
This table stores the steps that belong to a section. Each step has an order within its section and an associated prompt.
|
61
40
|
|
62
|
-
|
41
|
+
Example workflows include "YouTube Video Script", "YouTube Title Creator" and "YouTube Transcription to Medium Article".
|
63
42
|
|
64
|
-
|
65
|
-
|
66
|
-
|
43
|
+
| Field | Type | Description |
|
44
|
+
|-------------|---------|-------------------------------------|
|
45
|
+
| id | string | Unique identifier for the step |
|
46
|
+
| section_id | string | Foreign key referencing sections |
|
47
|
+
| name | string | The name of the step |
|
48
|
+
| action | string | The action to be performed by the step, default ('gpt') |
|
49
|
+
| description | string | A brief description of the step |
|
50
|
+
| order | integer | The order of the step in the section|
|
51
|
+
| prompt | string | The template string for the prompt |
|
67
52
|
|
68
|
-
|
69
|
-
- **Research Phase**:
|
70
|
-
1. Generate article recommendations from transcript.
|
71
|
-
2. Draft the article based on target audience and recommendations.
|
72
|
-
3. Create preliminary outline from transcript.
|
73
|
-
- **Writing Phase**:
|
74
|
-
1. Write the first draft following the outline.
|
75
|
-
2. Generate and critique various introductions.
|
76
|
-
3. Revise and update outline with selected introduction.
|
53
|
+
#### Table: attributes
|
77
54
|
|
78
|
-
|
79
|
-
- Selection of introduction.
|
80
|
-
- Final review and quality assurance.
|
55
|
+
This table stores the attributes associated with a workflow. Each attribute can be a simple value or an array.
|
81
56
|
|
82
|
-
|
57
|
+
Example attributes include "simple_title", "basic_factsheet" and "working_title".
|
83
58
|
|
84
|
-
|
85
|
-
|
59
|
+
| Field | Type | Description |
|
60
|
+
|-------------|---------|---------------------------------------------|
|
61
|
+
| id | string | Unique identifier for the attribute |
|
62
|
+
| workflow_id | string | Foreign key referencing workflows |
|
63
|
+
| name | string | The name of the attribute |
|
64
|
+
| type | string | The type of the attribute (e.g., string) |
|
65
|
+
| is_array | boolean | Indicates whether the attribute is an array |
|
86
66
|
|
87
|
-
|
88
|
-
- Both workflows benefit from iterative refinement, ensuring high-quality outputs before moving to the next phase.
|
67
|
+
#### Table: input_attributes
|
89
68
|
|
90
|
-
|
91
|
-
- Exploring multiple options simultaneously (e.g., different video types) can provide richer content and better decision-making.
|
69
|
+
This table maps input attributes to steps. This becomes the input parameters for the AI agent.
|
92
70
|
|
93
|
-
|
94
|
-
|
71
|
+
| Field | Type | Description |
|
72
|
+
|-------------|---------|------------------------------------|
|
73
|
+
| step_id | string | Foreign key referencing steps |
|
74
|
+
| attribute_id| string | Foreign key referencing attributes |
|
75
|
+
| required | boolean | Indicates whether the attribute is required |
|
95
76
|
|
96
|
-
|
97
|
-
- Iterative cycles help refine outputs continuously until optimal results are achieved, demonstrating a robust approach to content creation.
|
77
|
+
#### Table: output_attributes
|
98
78
|
|
99
|
-
This
|
79
|
+
This table maps output attributes to steps. This becomes a result of the AI agent.
|
80
|
+
|
81
|
+
| Field | Type | Description |
|
82
|
+
|-------------|---------|------------------------------------|
|
83
|
+
| step_id | string | Foreign key referencing steps |
|
84
|
+
| attribute_id| string | Foreign key referencing attributes |
|
85
|
+
|
86
|
+
### Dynamic Workflow Execution
|
87
|
+
|
88
|
+
Dynamic workflow execution captures the actual execution of a workflow, including the workflow runs, section runs, step runs, and attribute values.
|
89
|
+
|
90
|
+
### Dynamic Workflow Entities
|
91
|
+
|
92
|
+
#### Table: workflow_runs
|
93
|
+
|
94
|
+
This table stores instances of workflow executions.
|
95
|
+
|
96
|
+
| Field | Type | Description |
|
97
|
+
|-------------|--------|---------------------------------------|
|
98
|
+
| id | string | Unique identifier for the workflow run|
|
99
|
+
| workflow_id | string | Foreign key referencing workflows |
|
100
|
+
|
101
|
+
#### Table: section_runs
|
102
|
+
This table stores instances of section executions within a workflow run.
|
103
|
+
|
104
|
+
| Field | Type | Description |
|
105
|
+
|-----------------|--------|----------------------------------------|
|
106
|
+
| id | string | Unique identifier for the section run |
|
107
|
+
| section_id | string | Foreign key referencing sections |
|
108
|
+
| workflow_run_id | string | Foreign key referencing workflow_runs |
|
109
|
+
|
110
|
+
#### Table: step_runs
|
111
|
+
This table stores instances of step executions within a section run. Each step run can have multiple branches.
|
112
|
+
|
113
|
+
| Field | Type | Description |
|
114
|
+
|------------------|---------|---------------------------------------------------------------------|
|
115
|
+
| id | string | Unique identifier for the step run |
|
116
|
+
| section_run_id | string | Foreign key referencing section_runs |
|
117
|
+
| step_id | string | Foreign key referencing steps |
|
118
|
+
| branch_number | integer | Branch number to distinguish different instances (branches) of the same step |
|
119
|
+
|
120
|
+
#### Table: attribute_values
|
121
|
+
This table stores the values of attributes during step executions.
|
122
|
+
|
123
|
+
| Field | Type | Description |
|
124
|
+
|---------------|--------|-----------------------------------------|
|
125
|
+
| id | string | Unique identifier for the attribute value|
|
126
|
+
| attribute_id | string | Foreign key referencing attributes |
|
127
|
+
| step_run_id | string | Foreign key referencing step_runs |
|
128
|
+
| value | text | The actual value of the attribute during the step execution |
|
129
|
+
|
130
|
+
## Gemfile
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
|
134
|
+
# Gemfile
|
135
|
+
|
136
|
+
source 'https://rubygems.org'
|
137
|
+
|
138
|
+
gem 'sequel'
|
139
|
+
gem 'sqlite3'
|
140
|
+
```
|
141
|
+
|
142
|
+
## Database Setup
|
143
|
+
|
144
|
+
|
145
|
+
```ruby
|
146
|
+
# db_setup.rb
|
147
|
+
|
148
|
+
require 'sequel'
|
149
|
+
|
150
|
+
DB = Sequel.sqlite('workflow.db') # Creates an SQLite database called workflow.db
|
151
|
+
|
152
|
+
# Create the tables for static workflow definition
|
153
|
+
DB.create_table :workflows do
|
154
|
+
primary_key :id
|
155
|
+
String :name, null: false
|
156
|
+
String :description
|
157
|
+
end
|
158
|
+
|
159
|
+
DB.create_table :sections do
|
160
|
+
primary_key :id
|
161
|
+
String :name, null: false
|
162
|
+
String :description
|
163
|
+
Integer :order
|
164
|
+
foreign_key :workflow_id, :workflows
|
165
|
+
end
|
166
|
+
|
167
|
+
DB.create_table :steps do
|
168
|
+
primary_key :id
|
169
|
+
String :name, null: false
|
170
|
+
String :description
|
171
|
+
Integer :order
|
172
|
+
foreign_key :section_id, :sections
|
173
|
+
String :prompt
|
174
|
+
end
|
175
|
+
|
176
|
+
DB.create_table :attributes do
|
177
|
+
primary_key :id
|
178
|
+
String :name, null: false
|
179
|
+
String :type
|
180
|
+
Boolean :is_array
|
181
|
+
foreign_key :workflow_id, :workflows
|
182
|
+
end
|
183
|
+
|
184
|
+
DB.create_table :input_attributes do
|
185
|
+
foreign_key :step_id, :steps
|
186
|
+
foreign_key :attribute_id, :attributes
|
187
|
+
end
|
188
|
+
|
189
|
+
DB.create_table :output_attributes do
|
190
|
+
foreign_key :step_id, :steps
|
191
|
+
foreign_key :attribute_id, :attributes
|
192
|
+
end
|
193
|
+
|
194
|
+
# Create the tables for dynamic workflow execution
|
195
|
+
DB.create_table :workflow_runs do
|
196
|
+
primary_key :id
|
197
|
+
foreign_key :workflow_id, :workflows
|
198
|
+
end
|
199
|
+
|
200
|
+
DB.create_table :section_runs do
|
201
|
+
primary_key :id
|
202
|
+
foreign_key :workflow_run_id, :workflow_runs
|
203
|
+
foreign_key :section_id, :sections
|
204
|
+
end
|
205
|
+
|
206
|
+
DB.create_table :step_runs do
|
207
|
+
primary_key :id
|
208
|
+
foreign_key :section_run_id, :section_runs
|
209
|
+
foreign_key :step_id, :steps
|
210
|
+
Integer :branch_number
|
211
|
+
end
|
212
|
+
|
213
|
+
DB.create_table :attribute_values do
|
214
|
+
primary_key :id
|
215
|
+
foreign_key :attribute_id, :attributes
|
216
|
+
foreign_key :step_run_id, :step_runs
|
217
|
+
String :value
|
218
|
+
end
|
219
|
+
```
|
220
|
+
|
221
|
+
## Models
|
222
|
+
|
223
|
+
```ruby
|
224
|
+
# models.rb
|
225
|
+
|
226
|
+
require 'sequel'
|
227
|
+
|
228
|
+
DB = Sequel.sqlite('workflow.db') # Connect to the SQLite database
|
229
|
+
|
230
|
+
# Static Workflow Definition
|
231
|
+
|
232
|
+
class Workflow < Sequel::Model
|
233
|
+
one_to_many :sections
|
234
|
+
one_to_many :attributes
|
235
|
+
one_to_many :workflow_runs
|
236
|
+
end
|
237
|
+
|
238
|
+
class Section < Sequel::Model
|
239
|
+
many_to_one :workflow
|
240
|
+
one_to_many :steps
|
241
|
+
one_to_many :section_runs
|
242
|
+
end
|
243
|
+
|
244
|
+
class Step < Sequel::Model
|
245
|
+
many_to_one :section
|
246
|
+
one_to_many :input_attributes, class: :StepInputAttribute
|
247
|
+
one_to_many :output_attributes, class: :StepOutputAttribute
|
248
|
+
one_to_many :step_runs
|
249
|
+
end
|
250
|
+
|
251
|
+
class Attribute < Sequel::Model
|
252
|
+
many_to_one :workflow
|
253
|
+
one_to_many :input_attributes, class: :StepInputAttribute
|
254
|
+
one_to_many :output_attributes, class: :StepOutputAttribute
|
255
|
+
end
|
256
|
+
|
257
|
+
class StepInputAttribute < Sequel::Model
|
258
|
+
many_to_one :step
|
259
|
+
many_to_one :attribute
|
260
|
+
end
|
261
|
+
|
262
|
+
class StepOutputAttribute < Sequel::Model
|
263
|
+
many_to_one :step
|
264
|
+
many_to_one :attribute
|
265
|
+
end
|
266
|
+
|
267
|
+
# Dynamic Workflow Execution
|
268
|
+
|
269
|
+
class WorkflowRun < Sequel::Model
|
270
|
+
many_to_one :workflow
|
271
|
+
one_to_many :section_runs
|
272
|
+
end
|
273
|
+
|
274
|
+
class SectionRun < Sequel::Model
|
275
|
+
many_to_one :workflow_run
|
276
|
+
many_to_one :section
|
277
|
+
one_to_many :step_runs
|
278
|
+
end
|
279
|
+
|
280
|
+
class StepRun < Sequel::Model
|
281
|
+
many_to_one :section_run
|
282
|
+
many_to_one :step
|
283
|
+
one_to_many :attribute_values
|
284
|
+
end
|
285
|
+
|
286
|
+
class AttributeValue < Sequel::Model
|
287
|
+
many_to_one :attribute
|
288
|
+
many_to_one :step_run
|
289
|
+
end
|
290
|
+
|
291
|
+
```
|
292
|
+
|
293
|
+
## Usage via Models
|
294
|
+
|
295
|
+
```ruby
|
296
|
+
# example_usage.rb
|
297
|
+
|
298
|
+
require_relative 'models'
|
299
|
+
|
300
|
+
# Create a new workflow
|
301
|
+
workflow = Workflow.create(name: "YouTube Video Script", description: "Workflow for creating a YouTube video script")
|
302
|
+
|
303
|
+
# Create sections for the workflow
|
304
|
+
section1 = Section.create(name: "Research", description: "Research phase", order: 1, workflow: workflow)
|
305
|
+
section2 = Section.create(name: "Script Writing", description: "Script writing phase", order: 2, workflow: workflow)
|
306
|
+
|
307
|
+
# Create steps for the sections
|
308
|
+
step1 = Step.create(name: "01-1-basic-meta", description: "Basic metadata for Fotor AI tool", order: 1, section: section1, prompt: "Generate 5 titles to get started")
|
309
|
+
step2 = Step.create(name: "01-2-basic-factsheet", description: "Detailed factsheet about Fotor AI Tool", order: 2, section: section1, prompt: "Generate detailed factsheet")
|
310
|
+
|
311
|
+
# Create attributes
|
312
|
+
attribute1 = Attribute.create(name: "simple_title", type: "string", is_array: false, workflow: workflow)
|
313
|
+
attribute2 = Attribute.create(name: "basic_factsheet", type: "string", is_array: false, workflow: workflow)
|
314
|
+
|
315
|
+
# Associate input and output attributes with steps
|
316
|
+
StepInputAttribute.create(step: step1, attribute: attribute1)
|
317
|
+
StepOutputAttribute.create(step: step1, attribute: attribute2)
|
318
|
+
|
319
|
+
# Create a workflow run
|
320
|
+
workflow_run = WorkflowRun.create(workflow: workflow)
|
321
|
+
|
322
|
+
# Create section runs
|
323
|
+
section_run1 = SectionRun.create(workflow_run: workflow_run, section: section1)
|
324
|
+
section_run2 = SectionRun.create(workflow_run: workflow_run, section: section2)
|
325
|
+
|
326
|
+
# Create step runs
|
327
|
+
step_run1 = StepRun.create(section_run: section_run1, step: step1, branch_number: 1)
|
328
|
+
step_run2 = StepRun.create(section_run: section_run1, step: step2, branch_number: 1)
|
329
|
+
|
330
|
+
# Add attribute values to step runs
|
331
|
+
AttributeValue.create(attribute: attribute1, step_run: step_run1, value: "Fotor AI tool")
|
332
|
+
AttributeValue.create(attribute: attribute2, step_run: step_run2, value: "Detailed factsheet about Fotor AI Tool")
|
333
|
+
```
|
334
|
+
|
335
|
+
## Exporting to JSON
|
336
|
+
|
337
|
+
```ruby
|
338
|
+
# export_to_json.rb
|
339
|
+
|
340
|
+
require 'json'
|
341
|
+
require_relative 'models'
|
342
|
+
|
343
|
+
# Export all workflows to JSON files
|
344
|
+
Workflow.all.each do |workflow|
|
345
|
+
workflow_data = {
|
346
|
+
id: workflow.id,
|
347
|
+
name: workflow.name,
|
348
|
+
description: workflow.description,
|
349
|
+
sections: workflow.sections.map do |section|
|
350
|
+
{
|
351
|
+
id: section.id,
|
352
|
+
name: section.name,
|
353
|
+
description: section.description,
|
354
|
+
order: section.order,
|
355
|
+
steps: section.steps.map do |step|
|
356
|
+
{
|
357
|
+
id: step.id,
|
358
|
+
name: step.name,
|
359
|
+
description: step.description,
|
360
|
+
order: step.order,
|
361
|
+
prompt: step.prompt,
|
362
|
+
input_attributes: step.input_attributes.map { |ia| { id: ia.attribute.id, name: ia.attribute.name } },
|
363
|
+
output_attributes: step.output_attributes.map { |oa| { id: oa.attribute.id, name: oa.attribute.name } }
|
364
|
+
}
|
365
|
+
end
|
366
|
+
}
|
367
|
+
end,
|
368
|
+
attributes: workflow.attributes.map { |attr| { id: attr.id, name: attr.name, type: attr.type, is_array: attr.is_array } }
|
369
|
+
}
|
370
|
+
|
371
|
+
File.write("workflow_#{workflow.id}.json", JSON.pretty_generate(workflow_data))
|
372
|
+
end
|
373
|
+
|
374
|
+
# Export all workflow runs to JSON files
|
375
|
+
WorkflowRun.all.each do |workflow_run|
|
376
|
+
workflow_run_data = {
|
377
|
+
id: workflow_run.id,
|
378
|
+
workflow_id: workflow_run.workflow_id,
|
379
|
+
section_runs: workflow_run.section_runs.map do |section_run|
|
380
|
+
{
|
381
|
+
id: section_run.id,
|
382
|
+
section_id: section_run.section_id,
|
383
|
+
step_runs: section_run.step_runs.map do |step_run|
|
384
|
+
{
|
385
|
+
id: step_run.id,
|
386
|
+
step_id: step_run.step_id,
|
387
|
+
branch_number: step_run.branch_number,
|
388
|
+
attribute_values: step_run.attribute_values.map { |av| { id: av.attribute.id, name: av.attribute.name, value: av.value } }
|
389
|
+
}
|
390
|
+
end
|
391
|
+
}
|
392
|
+
end
|
393
|
+
}
|
394
|
+
|
395
|
+
File.write("workflow_run_#{workflow_run.id}.json", JSON.pretty_generate(workflow_run_data))
|
396
|
+
end
|
397
|
+
```
|
398
|
+
|
399
|
+
## Importing from JSON
|
400
|
+
|
401
|
+
```ruby
|
402
|
+
# import_from_json.rb
|
403
|
+
|
404
|
+
require 'json'
|
405
|
+
require_relative 'models'
|
406
|
+
|
407
|
+
# Helper function to find or create an attribute
|
408
|
+
def find_or_create_attribute(attr_data, workflow)
|
409
|
+
Attribute.find_or_create(name: attr_data['name'], workflow: workflow) do |attribute|
|
410
|
+
attribute.type = attr_data['type']
|
411
|
+
attribute.is_array = attr_data['is_array']
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
# Restore workflows from JSON files
|
416
|
+
Dir.glob('workflow_*.json').each do |file|
|
417
|
+
data = JSON.parse(File.read(file))
|
418
|
+
|
419
|
+
workflow = Workflow.find_or_create(id: data['id']) do |w|
|
420
|
+
w.name = data['name']
|
421
|
+
w.description = data['description']
|
422
|
+
end
|
423
|
+
|
424
|
+
data['sections'].each do |section_data|
|
425
|
+
section = Section.find_or_create(id: section_data['id'], workflow: workflow) do |s|
|
426
|
+
s.name = section_data['name']
|
427
|
+
s.description = section_data['description']
|
428
|
+
s.order = section_data['order']
|
429
|
+
end
|
430
|
+
|
431
|
+
section_data['steps'].each do |step_data|
|
432
|
+
step = Step.find_or_create(id: step_data['id'], section: section) do |s|
|
433
|
+
s.name = step_data['name']
|
434
|
+
s.description = step_data['description']
|
435
|
+
s.order = step_data['order']
|
436
|
+
s.prompt = step_data['prompt']
|
437
|
+
end
|
438
|
+
|
439
|
+
step_data['input_attributes'].each do |attr_data|
|
440
|
+
attribute = find_or_create_attribute(attr_data, workflow)
|
441
|
+
StepInputAttribute.find_or_create(step: step, attribute: attribute)
|
442
|
+
end
|
443
|
+
|
444
|
+
step_data['output_attributes'].each do |attr_data|
|
445
|
+
attribute = find_or_create_attribute(attr_data, workflow)
|
446
|
+
StepOutputAttribute.find_or_create(step: step, attribute: attribute)
|
447
|
+
end
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
data['attributes'].each do |attr_data|
|
452
|
+
find_or_create_attribute(attr_data, workflow)
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
# Restore workflow runs from JSON files
|
457
|
+
Dir.glob('workflow_run_*.json').each do |file|
|
458
|
+
data = JSON.parse(File.read(file))
|
459
|
+
|
460
|
+
workflow_run = WorkflowRun.find_or_create(id: data['id'], workflow_id: data['workflow_id'])
|
461
|
+
|
462
|
+
data['section_runs'].each do |section_run_data|
|
463
|
+
section_run = SectionRun.find_or_create(id: section_run_data['id'], workflow_run: workflow_run, section_id: section_run_data['section_id'])
|
464
|
+
|
465
|
+
section_run_data['step_runs'].each do |step_run_data|
|
466
|
+
step_run = StepRun.find_or_create(id: step_run_data['id'], section_run: section_run, step_id: step_run_data['step_id'], branch_number: step_run_data['branch_number'])
|
467
|
+
|
468
|
+
step_run_data['attribute_values'].each do |attr_value_data|
|
469
|
+
attribute = Attribute.find(id: attr_value_data['id'])
|
470
|
+
AttributeValue.find_or_create(attribute: attribute, step_run: step_run) do |av|
|
471
|
+
av.value = attr_value_data['value']
|
472
|
+
end
|
473
|
+
end
|
474
|
+
end
|
475
|
+
end
|
476
|
+
end
|
477
|
+
```
|
478
|
+
|
479
|
+
## Sample Implementation of Workflow Builder (DSL)
|
480
|
+
|
481
|
+
```ruby
|
482
|
+
require 'yaml'
|
483
|
+
require 'sequel'
|
484
|
+
|
485
|
+
# Assuming the database and models are already set up
|
486
|
+
DB = Sequel.sqlite('workflow.db')
|
487
|
+
|
488
|
+
# Models
|
489
|
+
class Workflow < Sequel::Model
|
490
|
+
one_to_many :sections
|
491
|
+
one_to_many :attributes
|
492
|
+
one_to_many :workflow_runs
|
493
|
+
end
|
494
|
+
|
495
|
+
class Section < Sequel::Model
|
496
|
+
many_to_one :workflow
|
497
|
+
one_to_many :steps
|
498
|
+
one_to_many :section_runs
|
499
|
+
end
|
500
|
+
|
501
|
+
class Step < Sequel::Model
|
502
|
+
many_to_one :section
|
503
|
+
one_to_many :input_attributes, class: :StepInputAttribute
|
504
|
+
one_to_many :output_attributes, class: :StepOutputAttribute
|
505
|
+
one_to_many :step_runs
|
506
|
+
end
|
507
|
+
|
508
|
+
class Attribute < Sequel::Model
|
509
|
+
many_to_one :workflow
|
510
|
+
one_to_many :input_attributes, class: :StepInputAttribute
|
511
|
+
one_to_many :output_attributes, class: :StepOutputAttribute
|
512
|
+
end
|
513
|
+
|
514
|
+
class StepInputAttribute < Sequel::Model
|
515
|
+
many_to_one :step
|
516
|
+
many_to_one :attribute
|
517
|
+
end
|
518
|
+
|
519
|
+
class StepOutputAttribute < Sequel::Model
|
520
|
+
many_to_one :step
|
521
|
+
many_to_one :attribute
|
522
|
+
end
|
523
|
+
|
524
|
+
# WorkflowBuilder
|
525
|
+
class WorkflowBuilder
|
526
|
+
def initialize(name:, description: nil)
|
527
|
+
@workflow = Workflow.new(name: name, description: description)
|
528
|
+
@current_section_order = 1
|
529
|
+
end
|
530
|
+
|
531
|
+
def description(desc)
|
532
|
+
@workflow.description = desc
|
533
|
+
end
|
534
|
+
|
535
|
+
def section(name:, &block)
|
536
|
+
@current_step_order = 1
|
537
|
+
@current_section = Section.new(name: name, order: @current_section_order)
|
538
|
+
@current_section_order += 1
|
539
|
+
instance_eval(&block) if block_given?
|
540
|
+
@workflow.add_section(@current_section)
|
541
|
+
end
|
542
|
+
|
543
|
+
def step(name:, &block)
|
544
|
+
step = Step.new(name: name, order: @current_step_order)
|
545
|
+
@current_step_order += 1
|
546
|
+
instance_eval(&block) if block_given?
|
547
|
+
@current_section.add_step(step)
|
548
|
+
end
|
549
|
+
|
550
|
+
def prompt(prompt)
|
551
|
+
@current_section.steps.last.prompt = prompt
|
552
|
+
end
|
553
|
+
|
554
|
+
def save
|
555
|
+
@workflow.save
|
556
|
+
end
|
557
|
+
|
558
|
+
def to_yaml
|
559
|
+
workflow_data = {
|
560
|
+
id: @workflow.id,
|
561
|
+
name: @workflow.name,
|
562
|
+
description: @workflow.description,
|
563
|
+
sections: @workflow.sections.map do |section|
|
564
|
+
{
|
565
|
+
id: section.id,
|
566
|
+
name: section.name,
|
567
|
+
description: section.description,
|
568
|
+
order: section.order,
|
569
|
+
steps: section.steps.map do |step|
|
570
|
+
{
|
571
|
+
id: step.id,
|
572
|
+
name: step.name,
|
573
|
+
description: step.description,
|
574
|
+
order: step.order,
|
575
|
+
prompt: step.prompt
|
576
|
+
}
|
577
|
+
end
|
578
|
+
}
|
579
|
+
end
|
580
|
+
}
|
581
|
+
workflow_data.to_yaml
|
582
|
+
end
|
583
|
+
end
|
584
|
+
|
585
|
+
```
|
586
|
+
|
587
|
+
## Usage via DSL
|
588
|
+
|
589
|
+
```ruby
|
590
|
+
builder = WorkflowBuilder.new(name: 'YouTube Video Script')
|
591
|
+
|
592
|
+
builder.section(name: 'Research') do
|
593
|
+
step(name: '01-1-basic-meta') do
|
594
|
+
prompt 'Generate 5 titles to get started'
|
595
|
+
end
|
596
|
+
|
597
|
+
step(name: '01-2-basic-factsheet') do
|
598
|
+
prompt 'Generate detailed factsheet'
|
599
|
+
end
|
600
|
+
end
|
601
|
+
|
602
|
+
builder.section(name: 'Script Writing') do
|
603
|
+
step(name: '02-1-create-script') do
|
604
|
+
prompt 'Generate basic script'
|
605
|
+
end
|
606
|
+
end
|
607
|
+
|
608
|
+
puts builder.to_yaml
|
609
|
+
|
610
|
+
# Save to the database
|
611
|
+
builder.save
|
612
|
+
|
613
|
+
|
614
|
+
builder = WorkflowBuilder.new(name: 'YouTube Title Creator')
|
615
|
+
|
616
|
+
builder.section(name: 'Research') do
|
617
|
+
step(name: '01-1-working-title') do
|
618
|
+
prompt 'Come up with a simple working title for the YouTube video.'
|
619
|
+
end
|
620
|
+
|
621
|
+
step(name: '01-2-keyword-research') do
|
622
|
+
prompt 'Perform basic keyword research to identify relevant keywords.'
|
623
|
+
end
|
624
|
+
|
625
|
+
step(name: '01-3-topic-research') do
|
626
|
+
prompt 'Conduct basic topic research to gather information on the subject.'
|
627
|
+
end
|
628
|
+
|
629
|
+
step(name: '01-4-powerful-titles') do
|
630
|
+
prompt 'Ask GPT for 10 powerful titles based on the research.'
|
631
|
+
end
|
632
|
+
|
633
|
+
step(name: '01-5-title-rules') do
|
634
|
+
prompt 'Follow specific rules for title creation to ensure effectiveness.'
|
635
|
+
end
|
636
|
+
end
|
637
|
+
|
638
|
+
puts builder.to_yaml
|
639
|
+
|
640
|
+
# Save to the database
|
641
|
+
builder.save
|
642
|
+
|
643
|
+
```
|