klue-langcraft 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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)