roast-ai 0.4.10 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/docs/write-comments.md +36 -0
  3. data/.github/CODEOWNERS +1 -1
  4. data/.github/workflows/ci.yaml +10 -6
  5. data/.gitignore +0 -1
  6. data/.rubocop.yml +7 -1
  7. data/CLAUDE.md +2 -2
  8. data/CONTRIBUTING.md +2 -0
  9. data/Gemfile +19 -18
  10. data/Gemfile.lock +35 -58
  11. data/README.md +118 -1432
  12. data/README_LEGACY.md +1464 -0
  13. data/Rakefile +39 -4
  14. data/dev.yml +29 -0
  15. data/dsl/agent_sessions.rb +20 -0
  16. data/dsl/async_cogs.rb +49 -0
  17. data/dsl/async_cogs_complex.rb +67 -0
  18. data/dsl/call.rb +44 -0
  19. data/dsl/collect_from.rb +72 -0
  20. data/dsl/json_output.rb +28 -0
  21. data/dsl/map.rb +55 -0
  22. data/dsl/map_reduce.rb +37 -0
  23. data/dsl/map_with_index.rb +49 -0
  24. data/dsl/next_break.rb +40 -0
  25. data/dsl/next_break_parallel.rb +44 -0
  26. data/dsl/outputs.rb +39 -0
  27. data/dsl/outputs_bang.rb +36 -0
  28. data/dsl/parallel_map.rb +37 -0
  29. data/dsl/prompts/simple_prompt.md.erb +3 -0
  30. data/dsl/prototype.rb +5 -7
  31. data/dsl/repeat_loop_results.rb +53 -0
  32. data/dsl/ruby_cog.rb +72 -0
  33. data/dsl/simple_agent.rb +18 -0
  34. data/dsl/simple_chat.rb +15 -1
  35. data/dsl/simple_repeat.rb +29 -0
  36. data/dsl/skip.rb +36 -0
  37. data/dsl/step_communication.rb +2 -3
  38. data/dsl/targets_and_params.rb +57 -0
  39. data/dsl/temperature.rb +17 -0
  40. data/dsl/temporary_directory.rb +22 -0
  41. data/dsl/tutorial/01_your_first_workflow/README.md +179 -0
  42. data/dsl/tutorial/01_your_first_workflow/configured_chat.rb +33 -0
  43. data/dsl/tutorial/01_your_first_workflow/hello.rb +23 -0
  44. data/dsl/tutorial/02_chaining_cogs/README.md +310 -0
  45. data/dsl/tutorial/02_chaining_cogs/code_review.rb +104 -0
  46. data/dsl/tutorial/02_chaining_cogs/session_resumption.rb +92 -0
  47. data/dsl/tutorial/02_chaining_cogs/simple_chain.rb +84 -0
  48. data/dsl/tutorial/03_targets_and_params/README.md +230 -0
  49. data/dsl/tutorial/03_targets_and_params/multiple_targets.rb +65 -0
  50. data/dsl/tutorial/03_targets_and_params/single_target.rb +65 -0
  51. data/dsl/tutorial/04_configuration_options/README.md +209 -0
  52. data/dsl/tutorial/04_configuration_options/control_display_and_temperature.rb +104 -0
  53. data/dsl/tutorial/04_configuration_options/simple_config.rb +68 -0
  54. data/dsl/tutorial/05_control_flow/README.md +156 -0
  55. data/dsl/tutorial/05_control_flow/conditional_execution.rb +62 -0
  56. data/dsl/tutorial/05_control_flow/handling_failures.rb +77 -0
  57. data/dsl/tutorial/06_reusable_scopes/README.md +172 -0
  58. data/dsl/tutorial/06_reusable_scopes/accessing_scope_outputs.rb +126 -0
  59. data/dsl/tutorial/06_reusable_scopes/basic_scope.rb +63 -0
  60. data/dsl/tutorial/06_reusable_scopes/parameterized_scope.rb +78 -0
  61. data/dsl/tutorial/07_processing_collections/README.md +152 -0
  62. data/dsl/tutorial/07_processing_collections/basic_map.rb +70 -0
  63. data/dsl/tutorial/07_processing_collections/parallel_map.rb +74 -0
  64. data/dsl/tutorial/08_iterative_workflows/README.md +231 -0
  65. data/dsl/tutorial/08_iterative_workflows/basic_repeat.rb +57 -0
  66. data/dsl/tutorial/08_iterative_workflows/conditional_break.rb +57 -0
  67. data/dsl/tutorial/09_async_cogs/README.md +197 -0
  68. data/dsl/tutorial/09_async_cogs/basic_async.rb +38 -0
  69. data/dsl/tutorial/README.md +222 -0
  70. data/dsl/working_directory.rb +16 -0
  71. data/exe/roast +1 -1
  72. data/internal/documentation/architectural-notes.md +115 -0
  73. data/internal/documentation/doc-comments-external.md +686 -0
  74. data/internal/documentation/doc-comments-internal.md +342 -0
  75. data/internal/documentation/doc-comments.md +211 -0
  76. data/lib/roast/dsl/cog/config.rb +274 -3
  77. data/lib/roast/dsl/cog/input.rb +53 -10
  78. data/lib/roast/dsl/cog/output.rb +297 -8
  79. data/lib/roast/dsl/cog/registry.rb +35 -3
  80. data/lib/roast/dsl/cog/stack.rb +1 -1
  81. data/lib/roast/dsl/cog/store.rb +5 -5
  82. data/lib/roast/dsl/cog.rb +70 -14
  83. data/lib/roast/dsl/cog_input_context.rb +36 -1
  84. data/lib/roast/dsl/cog_input_manager.rb +116 -7
  85. data/lib/roast/dsl/cogs/agent/config.rb +465 -0
  86. data/lib/roast/dsl/cogs/agent/input.rb +81 -0
  87. data/lib/roast/dsl/cogs/agent/output.rb +59 -0
  88. data/lib/roast/dsl/cogs/agent/provider.rb +51 -0
  89. data/lib/roast/dsl/cogs/agent/providers/claude/claude_invocation.rb +185 -0
  90. data/lib/roast/dsl/cogs/agent/providers/claude/message.rb +73 -0
  91. data/lib/roast/dsl/cogs/agent/providers/claude/messages/assistant_message.rb +36 -0
  92. data/lib/roast/dsl/cogs/agent/providers/claude/messages/result_message.rb +61 -0
  93. data/lib/roast/dsl/cogs/agent/providers/claude/messages/system_message.rb +47 -0
  94. data/lib/roast/dsl/cogs/agent/providers/claude/messages/text_message.rb +36 -0
  95. data/lib/roast/dsl/cogs/agent/providers/claude/messages/tool_result_message.rb +47 -0
  96. data/lib/roast/dsl/cogs/agent/providers/claude/messages/tool_use_message.rb +46 -0
  97. data/lib/roast/dsl/cogs/agent/providers/claude/messages/unknown_message.rb +27 -0
  98. data/lib/roast/dsl/cogs/agent/providers/claude/messages/user_message.rb +37 -0
  99. data/lib/roast/dsl/cogs/agent/providers/claude/tool_result.rb +51 -0
  100. data/lib/roast/dsl/cogs/agent/providers/claude/tool_use.rb +48 -0
  101. data/lib/roast/dsl/cogs/agent/providers/claude.rb +31 -0
  102. data/lib/roast/dsl/cogs/agent/stats.rb +92 -0
  103. data/lib/roast/dsl/cogs/agent/usage.rb +62 -0
  104. data/lib/roast/dsl/cogs/agent.rb +75 -0
  105. data/lib/roast/dsl/cogs/chat/config.rb +453 -0
  106. data/lib/roast/dsl/cogs/chat/input.rb +92 -0
  107. data/lib/roast/dsl/cogs/chat/output.rb +64 -0
  108. data/lib/roast/dsl/cogs/chat/session.rb +68 -0
  109. data/lib/roast/dsl/cogs/chat.rb +59 -56
  110. data/lib/roast/dsl/cogs/cmd.rb +248 -61
  111. data/lib/roast/dsl/cogs/ruby.rb +171 -0
  112. data/lib/roast/dsl/command_runner.rb +191 -0
  113. data/lib/roast/dsl/config_manager.rb +58 -11
  114. data/lib/roast/dsl/control_flow.rb +41 -0
  115. data/lib/roast/dsl/execution_manager.rb +162 -32
  116. data/lib/roast/dsl/nil_assertions.rb +23 -0
  117. data/lib/roast/dsl/system_cog/params.rb +32 -0
  118. data/lib/roast/dsl/system_cog.rb +36 -0
  119. data/lib/roast/dsl/system_cogs/call.rb +162 -0
  120. data/lib/roast/dsl/system_cogs/map.rb +448 -0
  121. data/lib/roast/dsl/system_cogs/repeat.rb +242 -0
  122. data/lib/roast/dsl/workflow.rb +26 -16
  123. data/lib/roast/dsl/workflow_context.rb +20 -0
  124. data/lib/roast/dsl/workflow_params.rb +24 -0
  125. data/lib/roast/sorbet_runtime_stub.rb +154 -0
  126. data/lib/roast/tools/apply_diff.rb +1 -3
  127. data/lib/roast/tools/cmd.rb +4 -3
  128. data/lib/roast/tools/read_file.rb +1 -1
  129. data/lib/roast/tools/update_files.rb +1 -1
  130. data/lib/roast/tools/write_file.rb +1 -1
  131. data/lib/roast/version.rb +1 -1
  132. data/lib/roast/workflow/base_workflow.rb +4 -0
  133. data/lib/roast/workflow/step_loader.rb +14 -2
  134. data/lib/roast-ai.rb +4 -0
  135. data/lib/roast.rb +58 -21
  136. data/{roast.gemspec → roast-ai.gemspec} +9 -13
  137. data/sorbet/rbi/gems/async@2.34.0.rbi +1577 -0
  138. data/sorbet/rbi/gems/cli-kit@5.2.0.rbi +2063 -0
  139. data/sorbet/rbi/gems/{cli-ui@2.3.0.rbi → cli-ui@2.7.0-6bdefd1d06305e5d6ae312ac76f9c88f88658dda.rbi} +1418 -1013
  140. data/sorbet/rbi/gems/console@1.34.2.rbi +1193 -0
  141. data/sorbet/rbi/gems/fiber-annotation@0.2.0.rbi +50 -0
  142. data/sorbet/rbi/gems/fiber-local@1.1.0.rbi +35 -0
  143. data/sorbet/rbi/gems/fiber-storage@1.0.1.rbi +41 -0
  144. data/sorbet/rbi/gems/io-event@1.14.0.rbi +724 -0
  145. data/sorbet/rbi/gems/metrics@0.15.0.rbi +9 -0
  146. data/sorbet/rbi/gems/traces@0.18.2.rbi +9 -0
  147. data/sorbet/rbi/shims/lib/roast/dsl/cog_input_context.rbi +1185 -5
  148. data/sorbet/rbi/shims/lib/roast/dsl/config_context.rbi +311 -5
  149. data/sorbet/rbi/shims/lib/roast/dsl/execution_context.rbi +486 -5
  150. data/sorbet/tapioca/config.yml +6 -0
  151. data/sorbet/tapioca/require.rb +2 -0
  152. metadata +157 -30
  153. data/dsl/less_simple.rb +0 -112
  154. data/dsl/scoped_executors.rb +0 -28
  155. data/dsl/simple.rb +0 -8
  156. data/lib/roast/dsl/cogs/execute.rb +0 -46
  157. data/lib/roast/dsl/cogs/graph.rb +0 -53
  158. data/sorbet/rbi/gems/cgi@0.5.0.rbi +0 -2961
  159. data/sorbet/rbi/gems/claude_swarm@0.1.19.rbi +0 -568
  160. data/sorbet/rbi/gems/cli-kit@5.0.1.rbi +0 -1991
  161. data/sorbet/rbi/gems/dry-configurable@1.3.0.rbi +0 -672
  162. data/sorbet/rbi/gems/dry-core@1.1.0.rbi +0 -1894
  163. data/sorbet/rbi/gems/dry-inflector@1.2.0.rbi +0 -659
  164. data/sorbet/rbi/gems/dry-initializer@3.2.0.rbi +0 -781
  165. data/sorbet/rbi/gems/dry-logic@1.6.0.rbi +0 -1127
  166. data/sorbet/rbi/gems/dry-schema@1.14.1.rbi +0 -3727
  167. data/sorbet/rbi/gems/dry-types@1.8.3.rbi +0 -3969
  168. data/sorbet/rbi/gems/fast-mcp-annotations@1.5.3.rbi +0 -1588
  169. data/sorbet/rbi/gems/mime-types-data@3.2025.0617.rbi +0 -136
  170. data/sorbet/rbi/gems/mime-types@3.7.0.rbi +0 -1342
  171. data/sorbet/rbi/gems/rack@2.2.19.rbi +0 -5676
  172. data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +0 -435
  173. data/sorbet/rbi/gems/yard@0.9.37.rbi +0 -18492
@@ -0,0 +1,230 @@
1
+ # Chapter 3: Targets and Parameters
2
+
3
+ In the previous chapters, you learned how to create workflows and chain cogs together. Now you'll learn how to make your
4
+ workflows more flexible by accepting targets and custom parameters from the command line.
5
+
6
+ ## What You'll Learn
7
+
8
+ - How to pass targets (files, URLs, etc.) to workflows
9
+ - How to use `target!` for single-target workflows
10
+ - How to access multiple targets with `targets`
11
+ - How to pass custom arguments with `args`
12
+ - How to pass key-value parameters with `kwargs`
13
+ - How to check for the presence of arguments and parameters
14
+
15
+ ## Workflow Targets
16
+
17
+ Targets are inputs that you want your workflow to process—files, URLs, or any other data. They're specified on the
18
+ command line after your workflow file path:
19
+
20
+ ```bash
21
+ bin/roast execute --executor=dsl workflow.rb https://example.com
22
+ bin/roast execute --executor=dsl workflow.rb README.md
23
+ bin/roast execute --executor=dsl workflow.rb src/*.rb
24
+ bin/roast execute --executor=dsl workflow.rb Gemfile Gemfile.lock
25
+ ```
26
+
27
+ Shell globs are expanded automatically, so `src/*.rb` will pass all Ruby files in the `src/` directory.
28
+
29
+ ### Accessing a Single Target
30
+
31
+ Use `target!` when your workflow expects exactly one target:
32
+
33
+ ```ruby
34
+ execute do
35
+ cmd(:fetch) do
36
+ "curl -sL #{target!}"
37
+ end
38
+
39
+ ruby do
40
+ puts "Target: #{target!}"
41
+ puts "Content length: #{cmd!(:fetch).out.length} bytes"
42
+ end
43
+ end
44
+ ```
45
+
46
+ Run it with:
47
+ ```bash
48
+ bin/roast execute --executor=dsl workflow.rb https://example.com
49
+ ```
50
+
51
+ **Important:** `target!` raises an error if zero or multiple targets are provided. Use it when your workflow is
52
+ designed to process exactly one target.
53
+
54
+ ### Accessing Multiple Targets
55
+
56
+ Use `targets` (plural) when your workflow can handle any number of files:
57
+
58
+ ```ruby
59
+ execute do
60
+ ruby do
61
+ if targets.empty?
62
+ puts "No files provided"
63
+ else
64
+ puts "Processing #{targets.length} files:"
65
+ targets.each { |file| puts " - #{file}" }
66
+ end
67
+ end
68
+ end
69
+ ```
70
+
71
+ The `targets` method always returns an array, which will be empty if the workflow is invoked with no targets specified.
72
+
73
+ ## Custom Arguments
74
+
75
+ Custom arguments let you pass additional data to your workflows. They come after `--` on the command line:
76
+
77
+ ```bash
78
+ bin/roast execute --executor=dsl workflow.rb -- hello world
79
+ ```
80
+
81
+ ### Simple Arguments (args)
82
+
83
+ Simple word tokens become arguments, accessible as symbols:
84
+
85
+ ```ruby
86
+ execute do
87
+ ruby do
88
+ puts "Arguments: #{args.inspect}" # [:hello, :world]
89
+
90
+ # Check if a specific argument is present
91
+ if arg?(:save_data)
92
+ puts "Will save incremental data files"
93
+ end
94
+ end
95
+ end
96
+ ```
97
+
98
+ Run it with:
99
+ ```bash
100
+ bin/roast execute --executor=dsl workflow.rb -- hello world
101
+ ```
102
+ Or:
103
+ ```bash
104
+ bin/roast execute --executor=dsl workflow.rb -- save_data something_else
105
+ ```
106
+
107
+ ### Key-Value Arguments (kwargs)
108
+
109
+ Use `key=value` format for key-value parameters:
110
+
111
+ ```ruby
112
+ execute do
113
+ ruby do
114
+ # Check if a kwarg is present
115
+ if kwarg?(:format)
116
+ puts "Format: #{kwarg(:format)}"
117
+ end
118
+
119
+ # Access all kwargs
120
+ puts "All kwargs: #{kwargs.inspect}"
121
+
122
+ # Access a specific kwarg (returns nil if not present)
123
+ name = kwarg(:name)
124
+ puts "Name: #{name}"
125
+
126
+ # Access with error if missing
127
+ email = kwarg!(:email) # raises error if 'email' not provided
128
+ puts "Email: #{email}"
129
+ end
130
+ end
131
+ ```
132
+
133
+ Run it with:
134
+ ```bash
135
+ bin/roast execute --executor=dsl workflow.rb -- name=Alice format=json
136
+ ```
137
+ Or:
138
+ ```bash
139
+ bin/roast execute --executor=dsl workflow.rb -- name=Alice email=alice@example.net
140
+ ```
141
+
142
+ **Notes:**
143
+ - Kwarg keys are parsed as symbols
144
+ - Kwarg values are always strings
145
+ - Simple words (without `=`) become args, not kwargs
146
+
147
+ ## Combining Targets and Arguments
148
+
149
+ You can use targets and custom arguments together:
150
+
151
+ ```bash
152
+ bin/roast execute --executor=dsl workflow.rb file1.rb file2.rb -- save_data format=summary
153
+ ```
154
+
155
+ In your workflow:
156
+
157
+ ```ruby
158
+ execute do
159
+ ruby do
160
+ puts "Processing #{targets.length} files"
161
+ puts "Save data: #{arg?(:save_data)}"
162
+ puts "Format: #{kwarg(:format) || "default"}"
163
+ end
164
+ end
165
+ ```
166
+
167
+ ## Accessing Parameters from Any Cog
168
+
169
+ All parameter accessors are available in any cog's input block:
170
+
171
+ ```ruby
172
+ execute do
173
+ chat(:analyze) do
174
+ "Analyze this file: #{target!}"
175
+ end
176
+
177
+ agent(:process) do
178
+ files = targets.join("\n")
179
+ format = kwarg(:format) || "detailed"
180
+ "Process these files and provide a #{format} report:\n#{files}) "
181
+ end
182
+
183
+ cmd(:grep_pattern) do
184
+ pattern = kwarg!(:pattern) # Error if not provided
185
+ "grep '#{pattern}' #{targets.join(" ")}"
186
+ end
187
+ end
188
+ ```
189
+
190
+ ## Running the Workflows
191
+
192
+ To run the examples in this chapter:
193
+
194
+ ```bash
195
+ # Single target workflow (with a URL)
196
+ bin/roast execute --executor=dsl dsl/tutorial/03_targets_and_params/single_target.rb https://example.com
197
+ ```
198
+
199
+ ```bash
200
+ # Multiple targets with arguments
201
+ bin/roast execute --executor=dsl dsl/tutorial/03_targets_and_params/multiple_targets.rb \
202
+ Gemfile Gemfile.lock -- save_data format=summary
203
+ ```
204
+
205
+ ## Try It Yourself
206
+
207
+ 1. **Single target processing** - Run the single_target workflow with different URLs
208
+ 2. **Multiple files** - Use shell globs to pass multiple files at once
209
+ 3. **Add arguments** - Try different combinations of args and kwargs
210
+ 4. **Error handling** - See what happens when you use `target!` with multiple files
211
+ 5. **Mix and match** - Combine targets with different argument combinations
212
+
213
+ ## Key Takeaways
214
+
215
+ - Use `target!` when your workflow expects exactly one file/url/etc. (errors otherwise)
216
+ - Use `targets` to access an array of all targets (can be empty)
217
+ - Custom arguments come after `--` on the command line
218
+ - Simple words become `args` (as symbols)
219
+ - `key=value` pairs become `kwargs` (keys as symbols, values as strings)
220
+ - Use `arg?(:name)` to check if an argument is present
221
+ - Use `kwarg(:name)` to get a kwarg value (returns nil if missing)
222
+ - Use `kwarg!(:name)` to require a kwarg (errors if missing)
223
+ - All accessors work in any cog's input block
224
+
225
+ ## What's Next?
226
+
227
+ In the next chapter, you'll learn about configuration options: how to fine-tune cog behavior, control what gets
228
+ displayed, and set up different models for different tasks.
229
+
230
+ But first, experiment with targets and parameters to see how they make your workflows more flexible and reusable!
@@ -0,0 +1,65 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ #: self as Roast::DSL::Workflow
5
+
6
+ # This workflow demonstrates processing multiple targets with custom arguments.
7
+ # It accepts any number of files and optional arguments for controlling output.
8
+ #
9
+ # Run it with:
10
+ # bin/roast execute --executor=dsl dsl/tutorial/03_targets_and_params/multiple_targets.rb \
11
+ # Gemfile Gemfile.lock
12
+ #
13
+ # Or try:
14
+ # bin/roast execute --executor=dsl dsl/tutorial/03_targets_and_params/multiple_targets.rb \
15
+ # dsl/**/*.rb -- count format=detailed
16
+ #
17
+ # Or get a little crazy:
18
+ # bin/roast execute --executor=dsl dsl/tutorial/03_targets_and_params/multiple_targets.rb \
19
+ # dsl/**/*.md -- format=json
20
+
21
+ config do
22
+ cmd { display! }
23
+ end
24
+
25
+ execute do
26
+ # Show what we're processing
27
+ ruby do
28
+ puts "Processing #{targets.length} file(s):\n - #{targets.join("\n - ")}\n"
29
+ puts "Counting file lines" if arg?(:count)
30
+ puts "Arguments: #{args.inspect}" if args.any?
31
+ puts "Keyword arguments: #{kwargs.inspect}" if kwargs.any?
32
+ end
33
+
34
+ # Analyze each file
35
+ chat(:analyze_files) do
36
+ format = kwarg(:format) || "summary"
37
+ <<~PROMPT
38
+ Please analyze these files and provide an #{format} overview:
39
+
40
+ #{targets.map { |f| "- #{f}" }.join("\n")}
41
+
42
+ Based on the filenames, what can you infer about this project?
43
+ What do you think these files are used for?
44
+
45
+ #{format == "detailed" ? "Provide detailed insights for each file." : "Keep it brief (2-3 sentences)."}
46
+ PROMPT
47
+ end
48
+
49
+ # Count total lines across all files
50
+ cmd(:total_lines) do |my|
51
+ my.stdin = targets.join("\n")
52
+ "xargs wc -l | awk '/total$/{print $1}'"
53
+ end
54
+
55
+ # Display results
56
+ ruby do
57
+ puts "\n" + "=" * 60
58
+ puts "ANALYSIS RESULTS"
59
+ puts "=" * 60
60
+ puts "Total lines: #{arg?(:count) ? cmd!(:total_lines).out : "---"}"
61
+ puts "Format: #{kwarg(:format)}" if kwarg?(:format)
62
+ puts chat!(:analyze_files).text
63
+ puts "=" * 60
64
+ end
65
+ end
@@ -0,0 +1,65 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ #: self as Roast::DSL::Workflow
5
+
6
+ # This workflow demonstrates using target! to process a single URL.
7
+ # It expects exactly one URL to be provided on the command line.
8
+ #
9
+ # Run it with:
10
+ # bin/roast execute --executor=dsl dsl/tutorial/03_targets_and_params/single_target.rb \
11
+ # https://example.com
12
+ #
13
+ # Try running it with no URLs or multiple URLs to see how target! validates input.
14
+
15
+ config do
16
+ chat { no_show_response! }
17
+ end
18
+
19
+ execute do
20
+ # Fetch the target URL
21
+ cmd(:fetch_url) do
22
+ "curl -sL #{target!}"
23
+ end
24
+
25
+ # Count words and lines in the content
26
+ cmd(:count_words) do
27
+ content = cmd!(:fetch_url).out
28
+ "echo #{content.shellescape} | wc -w"
29
+ end
30
+
31
+ cmd(:count_lines) do |my|
32
+ content = cmd!(:fetch_url).out
33
+ my.stdin = content
34
+ "wc -l"
35
+ end
36
+
37
+ # Analyze the content with an LLM
38
+ chat(:analyze) do
39
+ content = cmd!(:fetch_url).out
40
+ <<~PROMPT
41
+ Please analyze this web page content and provide a brief summary (2-3 sentences):
42
+
43
+ URL: #{target!}
44
+
45
+ Content:
46
+ #{content}
47
+
48
+ Focus on what the page contains and its purpose.
49
+ PROMPT
50
+ end
51
+
52
+ # Display the results
53
+ ruby do
54
+ puts "\n" + "=" * 60
55
+ puts "WEB PAGE ANALYSIS"
56
+ puts "=" * 60
57
+ puts "URL: #{target!}"
58
+ puts "Words: #{cmd!(:count_words).out.strip}"
59
+ puts "Lines: #{cmd!(:count_lines).out.strip}"
60
+ puts
61
+ puts "Summary:"
62
+ puts chat!(:analyze).response
63
+ puts "=" * 60
64
+ end
65
+ end
@@ -0,0 +1,209 @@
1
+ # Chapter 4: Configuration Options
2
+
3
+ In previous chapters, you learned the basics of creating workflows and chaining cogs together. Now you'll learn how to
4
+ configure cogs to control their behavior, use different models for different steps, and manage what gets displayed
5
+ during execution.
6
+
7
+ ## What You'll Learn
8
+
9
+ - How to configure models and providers
10
+ - The difference between global and per-step configuration
11
+ - How to use different models for different steps
12
+ - How to control what gets displayed during execution
13
+ - Common model parameters like temperature
14
+
15
+ ## Global Configuration
16
+
17
+ The `config` block at the top of your workflow sets defaults for all cogs of a given type:
18
+
19
+ ```ruby
20
+ config do
21
+ chat do
22
+ model "gpt-4o-mini"
23
+ provider :openai
24
+ end
25
+ end
26
+
27
+ execute do
28
+ # All chat cogs will use gpt-4o-mini unless overridden
29
+ chat(:first) { "Analyze this data..." }
30
+ chat(:second) { "Summarize that analysis..." }
31
+ end
32
+ ```
33
+
34
+ Both chat cogs will use `gpt-4o-mini` from OpenAI because that's the global default you specified in the `config` block.
35
+
36
+ ### Configuring Multiple Cog Types
37
+
38
+ You can set defaults for different cog types in the same config block:
39
+
40
+ ```ruby
41
+ config do
42
+ chat do
43
+ model "gpt-4o-mini"
44
+ provider :openai
45
+ end
46
+
47
+ agent do
48
+ model "claude-3-5-haiku-20241022"
49
+ provider :claude
50
+ end
51
+ end
52
+ ```
53
+
54
+ Now all `chat` cogs use "gpt-4o-mini" and all `agent` cogs use Claude Code with the "haiku" model (unless you override
55
+ them individually).
56
+
57
+ ## Per-Step Configuration
58
+
59
+ Override global configuration for specific named cogs by configuring them explicitly in the `config` block:
60
+
61
+ ```ruby
62
+ config do
63
+ # Global default for all chat cogs
64
+ chat do
65
+ model "gpt-4o-mini"
66
+ provider :openai
67
+ end
68
+
69
+ # Override for a specific named cog
70
+ chat(:complex_task) do
71
+ model "gpt-5"
72
+ # any options you don't set here (we're not specifying 'provider' again, for instance) will be inherited from
73
+ # the global config, or will use Roast's default values.
74
+ end
75
+ end
76
+
77
+ execute do
78
+ # Uses global config (gpt-4o-mini)
79
+ chat(:simple_task) { "Categorize this: #{data}" }
80
+
81
+ # Uses the specific override (gpt-5)
82
+ chat(:complex_task) { "Perform deep analysis of this data: #{data}" }
83
+ end
84
+ ```
85
+
86
+ The `:simple_task` cog uses the configured global default, but `:complex_task` has its own configuration that overrides.
87
+
88
+ ### Pattern-Based Configuration
89
+
90
+ You can also configure multiple cogs at once using a regex pattern:
91
+
92
+ ```ruby
93
+ config do
94
+ chat do
95
+ model "gpt-4o-mini"
96
+ provider :openai
97
+ end
98
+
99
+ # All cogs with names matching this pattern use gpt-4o
100
+ chat(/analyze_/) do
101
+ model "gpt-4o"
102
+ end
103
+ end
104
+
105
+ execute do
106
+ chat(:extract_data) { "..." } # Uses gpt-4o-mini
107
+ chat(:analyze_deep) { "..." } # Uses gpt-4o (matches pattern)
108
+ chat(:analyze_trends) { "..." } # Uses gpt-4o (matches pattern)
109
+ end
110
+ ```
111
+
112
+ This is useful when you have multiple similar cogs that need the same configuration.
113
+
114
+ ## Display Options
115
+
116
+ Control what gets printed during workflow execution using display methods in your `config` block:
117
+
118
+ Different cogs have different display options. For the `chat` and `agent` cogs, you can control the display of
119
+ the prompt, response, usage statistics, and (for the agent cog) incremental progress messages.
120
+
121
+ - `show_prompt!` / `no_show_prompt!` - Control prompt display
122
+ - `show_response!` / `no_show_response!` - Control response display
123
+ - `show_stats!` / `no_show_stats!` - Control statistics display
124
+
125
+ For the `cmd` cog, you can control the display of standard output and standard error.
126
+
127
+ - `show_stdout!` / `no_show_stdout!` - Control stdout display
128
+ - `show_stderr!` / `no_show_stderr!` - Control stdout display
129
+
130
+ And for all cog types, you can quickly apply some typical configurations
131
+
132
+ - `display!` - Show everything (prompt, response, stats / standard output and error / etc.)
133
+ - `no_display!` / `quiet!` - Hide everything
134
+
135
+ ```ruby
136
+ config do
137
+ chat do
138
+ no_show_response! # Hide all responses by default
139
+ end
140
+
141
+ chat(:summarize) do
142
+ show_response! # But show the final summary
143
+ end
144
+ end
145
+ ```
146
+
147
+ This is useful for hiding intermediate steps while showing only the final output. See `control_display_and_temperature.rb` for more examples.
148
+
149
+ ## Model Parameters
150
+
151
+ Fine-tune model behavior with additional parameters:
152
+
153
+ ### Temperature
154
+
155
+ Controls randomness (0.0-1.0, where 0.0 = deterministic, 1.0 = more random):
156
+
157
+ ```ruby
158
+ config do
159
+ chat(:creative_writing) do
160
+ temperature(0.9)
161
+ end
162
+
163
+ chat(:data_extraction) do
164
+ temperature(0.0)
165
+ end
166
+ end
167
+
168
+ execute do
169
+ chat(:creative_writing) { "Write a creative story about: #{topic}" }
170
+ chat(:data_extraction) { "Extract structured data from: #{text}" }
171
+ end
172
+ ```
173
+
174
+ ## Running the Workflows
175
+
176
+ To run the examples in this chapter:
177
+
178
+ ```bash
179
+ # Simple configuration example
180
+ bin/roast execute --executor=dsl dsl/tutorial/04_configuration_options/simple_config.rb
181
+ ```
182
+
183
+ ```bash
184
+ # Configuring multiple parameters for multiple steps
185
+ bin/roast execute --executor=dsl dsl/tutorial/04_configuration_options/control_display_and_temperature.rb
186
+ ```
187
+
188
+ ## Try It Yourself
189
+
190
+ 1. **Experiment with models** - Try different models for the same prompt and compare results
191
+ 2. **Adjust temperature** - See how temperature affects creative vs factual outputs
192
+ 3. **Control display** - Hide intermediate steps and only show final output
193
+ 4. **Mix providers** - Use OpenAI for some steps and Anthropic for others
194
+ 5. **Use patterns** - Try pattern-based configuration to configure multiple cogs at once
195
+
196
+ ## Key Takeaways
197
+
198
+ - Use `config` blocks to set global defaults for cog types
199
+ - Override global config by naming specific cogs in the `config` block: `chat(:name) do ... end`
200
+ - Use pattern-based config with regex: `chat(/pattern/) do ... end`
201
+ - Use display methods like `show_prompt!` and `no_show_response!` to control what gets printed
202
+ - Different cog types can use different default configurations
203
+
204
+ ## What's Next?
205
+
206
+ In the next chapter, you'll learn about control flow: how to conditionally skip steps, handle failures, and create
207
+ dynamic workflows that adapt based on intermediate results.
208
+
209
+ But first, experiment with different configurations to understand how they affect workflow behavior!
@@ -0,0 +1,104 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ #: self as Roast::DSL::Workflow
5
+
6
+ # This workflow demonstrates controlling what gets displayed during execution.
7
+ # Shows how to use show_prompt!, no_show_prompt!, show_response!, no_show_response!, and temperature.
8
+
9
+ config do
10
+ chat do
11
+ model "gpt-4o-mini"
12
+ show_prompt! # Show all prompts by default
13
+ no_show_response! # But hide responses by default
14
+ end
15
+
16
+ # Configure specific cogs with custom settings
17
+ chat(:generate_ideas) do
18
+ temperature(0.9) # Higher temperature for creative brainstorming
19
+ end
20
+
21
+ chat(:extract_names) do
22
+ temperature(0.0) # Low temperature for reliable extraction
23
+ end
24
+
25
+ chat(:evaluate_names) do
26
+ no_show_prompt! # Hide prompt for this step
27
+ end
28
+
29
+ chat(:pick_best) do
30
+ show_prompt! # Explicit show (redundant but clear)
31
+ show_response! # Show the final decision
32
+ temperature(0.3) # Low-medium for consistent evaluation
33
+ end
34
+ end
35
+
36
+ execute do
37
+ # Step 1: Generate creative content (high temperature configured above)
38
+ # The prompt is shown, response is hidden (our configured global default)
39
+ chat(:generate_ideas) do
40
+ <<~PROMPT
41
+ Brainstorm 5 creative names for a new project management tool focused on AI workflows.
42
+
43
+ Make them memorable, pronounceable, and available as .com domains (don't verify, just guess).
44
+ PROMPT
45
+ end
46
+
47
+ # Step 2: Extract structured data (low temperature configured above)
48
+ # The prompt is shown, response is hidden (our configured global default)
49
+ chat(:extract_names) do
50
+ ideas = chat!(:generate_ideas).response
51
+ <<~PROMPT
52
+ Extract just the names from this brainstorming output, format as JSON: `{ names: [...] }`
53
+
54
+ #{ideas}
55
+ PROMPT
56
+ end
57
+
58
+ # Step 3: Evaluate each name
59
+ # Prompt hidden (configured above), response hidden (global default)
60
+ chat(:evaluate_names) do
61
+ names = chat!(:extract_names).json![:names]
62
+ <<~PROMPT
63
+ Evaluate these product names for:
64
+ 1. Memorability (1-10)
65
+ 2. Professionalism (1-10)
66
+ 3. Relevance to AI workflows (1-10)
67
+
68
+ Names:
69
+ - #{names.join("\n- ")}
70
+ PROMPT
71
+ end
72
+
73
+ # Step 4: Pick the best name
74
+ # Both prompt and response shown (configured above)
75
+ chat(:pick_best) do
76
+ <<~PROMPT
77
+ Based on these evaluations, pick the single best name and explain why in 2-3 sentences:
78
+
79
+ #{chat!(:evaluate_names).text}
80
+ PROMPT
81
+ end
82
+
83
+ # Summary of what was displayed:
84
+ ruby(:display_summary) do
85
+ puts "\n" + "=" * 70
86
+ puts "DISPLAY CONTROL DEMONSTRATION"
87
+ puts "=" * 70
88
+ puts <<~SUMMARY
89
+
90
+ What you saw:
91
+ - ✓ Prompts for steps 1-2 (global show_prompt!)
92
+ - ✗ Responses for steps 1-3 (global no_show_response!)
93
+ - ✗ Prompt for step 3 (configured with no_show_prompt!)
94
+ - ✓ Prompt and response for step 4 (configured with show_response!)
95
+ - ✓ LLM stats for every step (unconfigured; Roast default)
96
+
97
+ Temperature settings (all configured in config block):
98
+ - Step 1: 0.9 (higher, for creative brainstorming)
99
+ - Step 2: 0.0 (low, for reliable extraction)
100
+ - Step 4: 0.3 (low-medium, for consistent evaluation)
101
+ SUMMARY
102
+ puts "=" * 70 + "\n"
103
+ end
104
+ end