klue-langcraft 0.0.6 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d602a256a1c785e201aa855cbd20a48dc8400c358589a8e9fa3d899c8ae1249f
4
- data.tar.gz: 8c78bb231cc1b13f3a50d315036bf895123f365b1e11b22940324c803a179f70
3
+ metadata.gz: 6f512e176bfdc09449895ba8775adc469ef781527160585aa61a0c14b4436041
4
+ data.tar.gz: 1a36e20159e2d80413bd440abd42f70f5e033ac08ed14ad018b567b3c76e6ad0
5
5
  SHA512:
6
- metadata.gz: de1535dabb901c06d46310dd2991ce5188b35690d5008ac9304a45493a335bbd4b482693048ac59ac23f0deaad87cfaf56c229edc5ffedf6c7e95899ce1192b5
7
- data.tar.gz: 66d3bc952009ea2e761bab7a878dc80869e06bde74fdfef962e80ecec9a3aa0532a6a16370b4da366d5c23b05387a4366f44c57758c725648a812eacec5b15fd
6
+ metadata.gz: a1f4d04d74426eae5675a1d45df6a6de92a23e913c1ab75fedc8762e763f4b31435d3f7f30e36c9ef0511015b201f90fe1c5b2735a94f305cdcd80f5abbe8922
7
+ data.tar.gz: 2f102d4c6f195477cd524c9cf919637d8cbdaa5e7ac0cbfad7ed4b7cbf5d24e65793d36838e4414e5bded37278f03c9b56f7d3096fd3db6d9a6950802ae107dc
@@ -8,6 +8,11 @@ KManager.action :project_plan do
8
8
  grid_layout(y: 190, direction: :horizontal, grid_h: 80, grid_w: 320, wrap_at: 3, grid: 0)
9
9
 
10
10
  todo(title: 'DSLs as Code sample DSL')
11
+ todo(title: 'Automate Base DSL setup')
12
+ todo(title: 'Automate DSL definion/designer for target DSL')
13
+ # todo(title: '')
14
+ # todo(title: '')
15
+ # todo(title: '')
11
16
 
12
17
  end
13
18
  .page('To Do', theme: :style_02, margin_left: 0, margin_top: 0) do
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [0.0.7](https://github.com/appydave/klue-langcraft/compare/v0.0.6...v0.0.7) (2024-09-21)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * update documentation for usecases ([077689e](https://github.com/appydave/klue-langcraft/commit/077689e085b667b3ac29dd2798b6b38e39a28923))
7
+
8
+ ## [0.0.6](https://github.com/appydave/klue-langcraft/compare/v0.0.5...v0.0.6) (2024-09-20)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * update documentation for usecases ([298f85a](https://github.com/appydave/klue-langcraft/commit/298f85aabf2a8acfad4926f2127017174df03ce4))
14
+
1
15
  ## [0.0.5](https://github.com/appydave/klue-langcraft/compare/v0.0.4...v0.0.5) (2024-09-20)
2
16
 
3
17
 
data/README.md CHANGED
@@ -50,8 +50,6 @@ You can also run `bin/console` for an interactive prompt that will allow you to
50
50
  ```bash
51
51
  bin/console
52
52
 
53
- Aaa::Bbb::Program.execute()
54
- # => ""
55
53
  ```
56
54
 
57
55
  `klue-langcraft` is setup with Guard, run `guard`, this will watch development file changes and run tests automatically, if successful, it will then run rubocop for style quality.
@@ -0,0 +1,105 @@
1
+ # DSL Examples
2
+
3
+ The following examples will build on top of one another to create an Agent as Code DSL designed for Agent Workflows.
4
+
5
+ The example workflow will ben implementation of the agent as code for YouTube Launch Optimization.
6
+
7
+ ### Root Node (#1).
8
+
9
+ The root node for a DSL is named `definition`, it behaves like any other `node` accept that you provide the DSL name.
10
+
11
+ An agentic workflow might take a name and optional description.
12
+
13
+ ```ruby
14
+ # Sample DSL
15
+ workflow :youtube_launch_optimizer, 'Optimize the video publishing for YouTube Titles, Descriptions, Thumbnails and social content' do
16
+ end
17
+ ```
18
+
19
+ ```ruby
20
+ # DSL Definition
21
+ definition :workflow do
22
+ param :name, type: :positional
23
+ param :description, type: :positional, default: nil
24
+ end
25
+ ```
26
+
27
+ ### Root Node (#2).
28
+
29
+ An agentic workflow might take a name only and use a child node for description using a self closing syntax.
30
+
31
+ ```ruby
32
+ # Sample DSL
33
+ workflow :youtube_launch_optimizer do
34
+ description 'Optimize the video publishing for YouTube Titles, Descriptions, Thumbnails and social content'
35
+ end
36
+ ```
37
+
38
+ ```ruby
39
+ # DSL Definition
40
+ definition :workflow do
41
+ param :name, type: :positional
42
+ node :description do
43
+ param :description, type: :positional
44
+ end
45
+ end
46
+ ```
47
+
48
+ ### Single Node with List (#3)
49
+
50
+ Here we have a single node called settings with a list of setting that contains a key and a value.
51
+ Notice `repeat: true` is used to indicate that the setting is a repeatable element.
52
+
53
+ ```ruby
54
+ # Sample DSL
55
+
56
+ workflow :youtube_launch_optimizer do
57
+ settings do
58
+ setting :prompt_path, 'prompts/youtube/launch_optimizer'
59
+ setting :default_llm, :gpt4o
60
+ end
61
+ end
62
+ ```
63
+
64
+ ```ruby
65
+ # DSL Definition
66
+ definition :workflow do
67
+ param :name, type: :positional
68
+ node :settings do
69
+ node :setting, repeat: true do
70
+ param :key, type: :positional
71
+ param :value, type: :positional
72
+ end
73
+ end
74
+ end
75
+ ```
76
+
77
+ ### Single Node with list - declaritve syntax (#4)
78
+
79
+ Like before, we have a single node called settings with a list of setting that contains a key and a value.
80
+ This time the you can use the method name as the first paramater value because it is of type declarative.
81
+ This can make some DSL usage a little easier to understand
82
+
83
+ ```ruby
84
+ # Sample DSL
85
+
86
+ workflow :youtube_launch_optimizer do
87
+ settings do
88
+ prompt_path 'prompts/youtube/launch_optimizer'
89
+ default_llm :gpt4o
90
+ end
91
+ end
92
+ ```
93
+
94
+ ```ruby
95
+ # DSL Definition
96
+ definition :workflow do
97
+ param :name, type: :positional
98
+ node :settings do
99
+ node :setting, repeat: true do
100
+ param :key, type: :declaritive # the method name typed in will be come the value for the key paramater
101
+ param :value, type: :positional
102
+ end
103
+ end
104
+ end
105
+ ```
data/docs/dsl-rules.md ADDED
@@ -0,0 +1,155 @@
1
+ # DSL Rules
2
+
3
+ The DSL using ruby like syntax that maps paramaters and values in fashion similar to the Ruby programming language.
4
+
5
+ ## Data Types in DSLs
6
+
7
+ DSLs can use various types of data as input and provide flexibility in handling different kinds of values. Below are the main data types supported:
8
+
9
+ - **Strings:** Represent textual data and are enclosed in quotes. They are typically used for descriptive content or text-based parameters.
10
+ - **Symbols:** Identifiers often used for keys or labels in configuration. Symbols are concise and unique, making them ideal for representing options or identifiers.
11
+ - **Booleans:** Represent true/false values and are useful for toggling features or conditions within the DSL.
12
+ - **Integers:** Whole numbers without fractions, often used for quantities, counts, or IDs.
13
+ - **Floats:** Numbers that include decimal points, used when more precise values are required, such as for time durations or percentages.
14
+
15
+ These data types allow the DSL to handle a wide variety of input values, making it versatile for different use cases and configurations.
16
+
17
+ Note: Data types are currently only cosmetic, everything will get stored as a string internally, but we might put in some validation that can at least let us know if we've got the wrong sort of data provided.
18
+
19
+
20
+ ## DSL argument rules
21
+
22
+ In Ruby, parameters (or "arguments") come in various types such as positional, optional, splat (*), and keyword arguments (**).
23
+ Each parameter type follows specific conventions, and the Ruby language uses distinct terms for each.
24
+ Below is an overview of these types and their corresponding metadata fields.
25
+
26
+ ### Methods and Arguements
27
+
28
+ #### 1. Positional Parameters
29
+ - **Description**: These are the most common type of parameters. They are passed in the exact order defined in the method signature.
30
+ - **Ruby Example**: `def method(a, b)`
31
+ - **Metadata Fields**:
32
+ - `type`: positional
33
+ - `name`: a (or the parameter name)
34
+ - **Inferred Fields**:
35
+ - `required`: true
36
+
37
+ ```ruby
38
+ param :a, type: :positional # required: true
39
+ param :b, type: :positional # required: true
40
+ ```
41
+
42
+ #### 2. Optional Parameters
43
+ - **Description**: These are positional parameters with default values, making them optional.
44
+ - **Ruby Example**: `def method(a = 1)`
45
+ - **Metadata Fields**:
46
+ - `type`: positional
47
+ - `name`: a
48
+ - `default_value`: 1
49
+ - **Inferred Fields**:
50
+ - `required`: false
51
+
52
+ ```ruby
53
+ param :a, type: :positional, default: 1 # required: false
54
+ ```
55
+
56
+ #### 3. Splat Parameters (*)
57
+ - **Description**: The splat operator `*` captures any number of additional positional arguments into an array.
58
+ - **Ruby Example**: `def method(*args)`
59
+ - **Metadata Fields**:
60
+ - `type`: splat
61
+ - `name`: args
62
+ - **Inferred Fields**:
63
+ - `required`: false
64
+ - `variadic`: true
65
+
66
+ ```ruby
67
+ param :args, type: :splat # required: false, variadic: true
68
+ ```
69
+
70
+ #### 4. Keyword Parameters
71
+ - **Description**: Keyword arguments are named arguments passed as a hash or individual key-value pairs. They can be required or optional.
72
+ - **Ruby Example**: `def method(a:, b: 1)`
73
+ - **For required keyword argument**:
74
+ - **Metadata Fields**:
75
+ - `type`: keyword
76
+ - `name`: a
77
+ - **Inferred Fields**:
78
+ - `required`: true
79
+ - **For optional keyword argument**:
80
+ - **Metadata Fields**:
81
+ - `type`: keyword
82
+ - `name`: b
83
+ - `default_value`: 1
84
+ - **Inferred Fields**:
85
+ - `required`: false
86
+
87
+ ```ruby
88
+ param :a, type: :keyword # required: true
89
+ param :b, type: :keyword, default: 1 # required: true
90
+ ```
91
+
92
+
93
+ #### 5. Double Splat Parameters (**)
94
+ - **Description**: The double splat operator `**` captures any number of additional keyword arguments into a hash.
95
+ - **Ruby Example**: `def method(**opts)`
96
+ - **Metadata Fields**:
97
+ - `type`: double_splat
98
+ - `name`: opts
99
+ - **Inferred Fields**:
100
+ - `required`: false
101
+ - `variadic`: true
102
+
103
+ ```ruby
104
+ param :opts, type: :double_splat # required: false, variadic: true
105
+ ```
106
+
107
+ #### Block Paramaters
108
+
109
+ The idea of ruby block parameters have been deprectated, instead the syntax has been retained for containers, see below.
110
+
111
+ ## DSL Containers
112
+
113
+ Containers represent a named block that can take parameters and contain aditional nested elements.
114
+
115
+ They follow usage that is similar in concept to an XML element, in that they have a name, then can take paramaters (aka attributes) and then can be self closing.
116
+
117
+ The container will be represented as a `do ... end` when hosting child containers
118
+ The container can also be self closing, in which case there is no `do ... end`, there is just an element with zero or more parameters followed by a newline.
119
+
120
+ The root containers will be called `definition`, all other containers will be known as `node`
121
+
122
+ ### Rules
123
+
124
+ These rules should be observed when dealing with different parameters and blocks
125
+
126
+ **Required vs Optional Parameters:**
127
+
128
+ - **Positional required parameters**: Defined without a default value.
129
+ - **Optional positional parameters**: Have default values or are represented with splat (`*`).
130
+ - **Required keyword arguments**: Must be passed explicitly unless a default value is provided.
131
+ - **Optional keyword arguments**: Have default values or can be omitted.
132
+
133
+ ### Order and constraints
134
+
135
+ A container can take parameters based on the defined styles, but they must follow an order and adhere to constraints.
136
+
137
+ **Order/Construction:**
138
+
139
+ 1. Declarative parameters (if present, the method name acts as the first positional parameter)
140
+ 2. Required positional parameters
141
+ 3. Optional positional parameters
142
+ 4. Splat parameters, zero or one
143
+ 5. Required keyword parameters
144
+ 6. Optional keyword parameters
145
+ 7. Double splat parameters, zero or one
146
+
147
+ **Constraints:**
148
+
149
+ 1. Declarative parameters, if present, replace the first positional parameter, and no additional required positional parameters can come before it.
150
+ 2. Required positional parameters must come before any optional positional parameters.
151
+ 3. The splat parameter, if present, must come after all positional parameters (required and optional).
152
+ 4. Keyword parameters (both required and optional) must come after all positional and splat parameters.
153
+ 5. The double splat parameter, if present, must be the last parameter.
154
+ 6. You can't have more than one splat or double splat parameter in a container.
155
+ 7. Once you start using keyword parameters, you can't use positional parameters anymore in that container.
@@ -0,0 +1,4 @@
1
+ Printspeak DSL
2
+ Project Plan
3
+ Agent As Code
4
+ Make Chapters out of my recordings using a folder and glob pattern geared to my naming convention
@@ -0,0 +1,300 @@
1
+ dsl = Agent.create(:youtube_launch_optimizer) do
2
+ description 'This workflow optimizes video launch by analyzing, preparing, and generating content for various platforms.'
3
+ settings do
4
+ prompt_path Ad::AgentArchitecture.gem_relative_file('prompts/youtube/launch_optimizer')
5
+ default_llm :gpt4o
6
+ end
7
+
8
+ prompts do
9
+ prompt :video_summary_prompt , content: load_file('1-1-summarize-video.txt')
10
+ prompt :video_abridgement_prompt , content: load_file('1-2-1-abridge-video.txt')
11
+ prompt :abridgement_descrepencies_prompt , content: load_file('1-2-2-abridge-video-descrepencies.txt')
12
+ prompt :identify_chapters_prompt , content: load_file('2-1-identify-chapters.txt')
13
+ prompt :refine_chapters_prompt , content: load_file('2-2-refine-chapters.txt')
14
+ prompt :create_chapters_prompt , content: load_file('2-3-create-chapters.txt')
15
+ prompt :analyze_content_essence_prompt , content: load_file('3-1-analyze-content-essence.txt')
16
+ prompt :analyze_audience_engagement_prompt, content: load_file('3-2-analyze-audience-engagement.txt')
17
+ prompt :analyze_cta_competitors_prompt , content: load_file('3-3-analyze-cta-competitors.txt')
18
+ prompt :title_generation_prompt , content: load_file('4-1-generate-title.txt')
19
+ prompt :thumbnail_text_prompt , content: load_file('4-2-generate-thumbnail-text.txt')
20
+ prompt :thumbnail_visual_elements_prompt , content: load_file('4-3-thumbnail-visual-elements.txt')
21
+ prompt :yt_simple_description_prompt , content: load_file('5-1-yt-simple-description.txt')
22
+ prompt :yt_description_prompt , content: load_file('5-2-yt-write-description.txt')
23
+ prompt :yt_format_description_prompt , content: load_file('5-3-yt-format-description.txt')
24
+ prompt :yt_description_custom_gpt_prompt , content: load_file('5-4-yt-description-custom-gpt.txt')
25
+ prompt :yt_pinned_comment_prompt , content: load_file('5-5-yt-pinned-comment.txt')
26
+ prompt :yt_metadata_prompt , content: load_file('5-6-yt-meta-data.txt')
27
+ prompt :tweet_prompt , content: load_file('6-1-create-tweet.txt')
28
+ prompt :facebook_post_prompt , content: load_file('6-2-create-fb-post.txt')
29
+ prompt :linkedin_post_prompt , content: load_file('6-3-create-linkedin-post.txt')
30
+ end
31
+
32
+ attributes do
33
+ attribute :short_title, type: :string
34
+ attribute :transcript, type: :string
35
+ attribute :srt, type: :string
36
+ attribute :summary, type: :string
37
+ attribute :abridgement, type: :string
38
+ attribute :abridgement_descrepencies, type: :string
39
+ attribute :analyze_content_essence, type: :string
40
+ attribute :analyze_audience_engagement, type: :string
41
+ attribute :analyze_cta_competitors, type: :string
42
+ attribute :video_title, type: :string
43
+ attribute :video_url, type: :string
44
+ attribute :video_topic_theme, type: :string
45
+ attribute :video_related_links, type: :array
46
+ attribute :video_keywords, type: :array
47
+ attribute :video_description, type: :string
48
+ attribute :video_description_custom_gpt, type: :string
49
+ attribute :video_pinned_comment, type: :string
50
+ attribute :video_metadata, type: :string
51
+ attribute :content_highlights
52
+ attribute :identify_chapters, type: :string
53
+ attribute :refine_chapters, type: :string
54
+ attribute :chapters, type: :string
55
+ attribute :fold_cta, type: :string
56
+ attribute :primary_cta, type: :string
57
+ attribute :affiliate_cta, type: :array
58
+ attribute :brand_info, type: :string
59
+ attribute :title_ideas, type: :array
60
+ attribute :thumbnail_text, type: :string
61
+ attribute :thumbnail_visual_elements, type: :string
62
+ attribute :tweet_content, type: :string
63
+ attribute :facebook_post, type: :string
64
+ attribute :linkedin_post, type: :string
65
+ end
66
+
67
+ # Clean SRT
68
+ # Intro/Outro/Content Seperation
69
+ # Chapter Name Suggeestions
70
+ # B-Roll Suggestions
71
+ # section('Transcript Analysis') do
72
+ # step('')
73
+ # end
74
+
75
+ section('Video Preparation') do
76
+ step('Configure') do
77
+ input :brand_info
78
+ input :short_title
79
+ input :video_title
80
+ input :video_url
81
+ input :video_related_links
82
+ input :video_keywords
83
+ input :fold_cta
84
+ input :primary_cta
85
+ input :affiliate_cta
86
+ input :chapters
87
+ input :transcript
88
+ input :abridgement
89
+ input :summary
90
+ end
91
+
92
+ step('Script Summary') do
93
+ input :transcript
94
+ prompt :video_summary_prompt
95
+ output :summary
96
+ end
97
+
98
+ step('Script Abridgement') do
99
+ input :transcript
100
+ prompt :video_abridgement_prompt
101
+ output :abridgement
102
+ end
103
+
104
+ step('Abridge QA') do
105
+ input :transcript
106
+ input :abridgement
107
+ prompt :abridgement_descrepencies_prompt
108
+ output :abridgement_descrepencies
109
+ end
110
+ end
111
+
112
+ section('Build Chapters') do
113
+ step('Find Chapters') do
114
+ input :transcript
115
+ input :abridgement
116
+ prompt :identify_chapters_prompt
117
+ output :identify_chapters
118
+ end
119
+
120
+ step('Refine Chapters') do
121
+ input :identify_chapters
122
+ input :refine_chapters
123
+ prompt :refine_chapters_prompt
124
+ output :chapters
125
+ end
126
+
127
+ step('Create Chapters') do
128
+ input :chapters
129
+ input :srt
130
+ prompt :create_chapters_prompt
131
+ output :chapters
132
+ end
133
+ end
134
+
135
+ section('Content Analysis') do
136
+ step('Content Essence') do
137
+ input :abridgement
138
+ prompt :analyze_content_essence_prompt
139
+ output :analyze_content_essence
140
+ end
141
+
142
+ step('Audience Engagement') do
143
+ input :abridgement
144
+ prompt :analyze_audience_engagement_prompt
145
+ output :analyze_audience_engagement
146
+ end
147
+
148
+ step('CTA/Competitors') do
149
+ input :abridgement
150
+ prompt :analyze_cta_competitors_prompt
151
+ output :analyze_cta_competitors
152
+ end
153
+
154
+ # step('Analyze Video') do
155
+ # input :transcript
156
+ # prompt :analyze_content_essence_prompt
157
+ # output :video_topic
158
+ # output :video_keywords
159
+ # end
160
+ end
161
+
162
+ section('Title & Thumbnail') do
163
+ step('Title Ideas') do
164
+ input :short_title #
165
+ input :video_topic_theme # (Main Topic or Theme)
166
+ input :content_highlights # (Keywords/Insites/Takeaways/Audience-Related Insights)
167
+ prompt :title_generation_prompt
168
+ output :title_ideas
169
+ end
170
+
171
+ # Analyze Title Short List
172
+ # Here are the types of prompts or questions you've been writing:
173
+
174
+ # Title Analysis: Requesting evaluations of YouTube titles.
175
+ # Title Suggestions: Asking for new or varied title options.
176
+ # Title Optimization: Seeking ways to improve existing titles.
177
+ # Comparative Analysis: Comparing and analyzing different title versions.
178
+ # Content Strategy: Discussing factors influencing YouTube CTR.
179
+ # Instructional Requests: Asking for concise, actionable recommendations.
180
+ # These types cover the main focus areas of your queries related to optimizing and refining YouTube content titles.
181
+
182
+ step('Thumb Text Ideas') do
183
+ input :video_title
184
+ input :video_topic_theme
185
+ input :content_highlights
186
+ input :title_ideas
187
+ prompt :thumbnail_text_prompt
188
+ output :thumbnail_text
189
+ end
190
+
191
+ step('Visual Element Ideas') do
192
+ input :video_title
193
+ input :content_highlights
194
+ input :title_ideas
195
+ prompt :thumbnail_visual_elements_prompt
196
+ output :thumbnail_visual_elements
197
+ end
198
+
199
+ # Does the Title, Thumbnail Text and Visual Elements align with the content or is it misleading?
200
+ end
201
+
202
+ section('YouTube Meta Data') do
203
+ step('Simple Description') do
204
+ input :video_title
205
+ input :video_keywords
206
+ input :abridgement
207
+ prompt :yt_simple_description_prompt
208
+ output :video_simple_description
209
+ end
210
+
211
+ step('Write Description') do
212
+ input :video_title
213
+ input :chapters
214
+ input :video_simple_description
215
+ input :brand_info
216
+ input :fold_cta
217
+ input :primary_cta
218
+ input :affiliate_cta
219
+ input :video_related_links
220
+ input :video_keywords
221
+ prompt :yt_description_prompt
222
+ output :video_description
223
+ end
224
+
225
+ step('Format Description') do
226
+ input :video_description
227
+ prompt :yt_format_description_prompt
228
+ output :video_description
229
+ end
230
+
231
+ step('Custom GPT Description') do
232
+ input :video_title
233
+ input :chapters
234
+ input :abridgement
235
+ input :brand_info
236
+ input :fold_cta
237
+ input :primary_cta
238
+ input :affiliate_cta
239
+ input :video_related_links
240
+ input :video_keywords
241
+ prompt :yt_description_custom_gpt_prompt
242
+ output :video_description_custom_gpt
243
+ end
244
+
245
+ step('Pinned Comment') do
246
+ # I don't need all these, but not sure which ones I do need yet
247
+ input :video_title
248
+ input :abridgement
249
+ input :chapters
250
+ input :brand_info
251
+ input :fold_cta
252
+ input :primary_cta
253
+ input :affiliate_cta
254
+ input :video_related_links
255
+ input :video_keywords
256
+ input :video_description
257
+ prompt :yt_pinned_comment_prompt
258
+ output :video_pinned_comment
259
+ end
260
+
261
+ step('Extra Metadata') do
262
+ input :video_title
263
+ input :abridgement
264
+ prompt :yt_metadata_prompt
265
+ output :video_metadata
266
+ end
267
+ end
268
+
269
+ section('Social Media Posts') do
270
+ step('Create Tweet') do
271
+ input :video_title
272
+ input :video_link
273
+ input :video_keywords
274
+ input :summary
275
+ prompt :tweet_prompt
276
+ output :tweet_content
277
+ end
278
+
279
+ step('Create FB Post') do
280
+ input :summary
281
+ input :video_keywords
282
+ prompt :facebook_post_prompt
283
+ output :facebook_post
284
+ end
285
+
286
+ step('Create LinkedIn Post') do
287
+ input :video_title
288
+ input :video_link
289
+ input :video_keywords
290
+ input :abridgement
291
+ prompt :linkedin_post_prompt
292
+ output :linkedin_post
293
+ end
294
+ end
295
+ end
296
+
297
+ file = '/Users/davidcruwys/dev/sites/working-with-sean/gpt-agents/src/content/gpt-workflows/youtube-launch-optimizer.json'
298
+ dsl
299
+ .save
300
+ .save_json(file)