roast-ai 0.4.9 → 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 (194) 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 +18 -18
  10. data/Gemfile.lock +46 -57
  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/demo/Gemfile +4 -0
  21. data/dsl/demo/Gemfile.lock +120 -0
  22. data/dsl/demo/cogs/local.rb +15 -0
  23. data/dsl/demo/simple_external_cog.rb +17 -0
  24. data/dsl/json_output.rb +28 -0
  25. data/dsl/map.rb +55 -0
  26. data/dsl/map_reduce.rb +37 -0
  27. data/dsl/map_with_index.rb +49 -0
  28. data/dsl/next_break.rb +40 -0
  29. data/dsl/next_break_parallel.rb +44 -0
  30. data/dsl/outputs.rb +39 -0
  31. data/dsl/outputs_bang.rb +36 -0
  32. data/dsl/parallel_map.rb +37 -0
  33. data/dsl/plugin-gem-example/.gitignore +8 -0
  34. data/dsl/plugin-gem-example/Gemfile +13 -0
  35. data/dsl/plugin-gem-example/Gemfile.lock +178 -0
  36. data/dsl/plugin-gem-example/lib/other.rb +17 -0
  37. data/dsl/plugin-gem-example/lib/plugin_gem_example.rb +5 -0
  38. data/dsl/plugin-gem-example/lib/simple.rb +15 -0
  39. data/dsl/plugin-gem-example/lib/version.rb +10 -0
  40. data/dsl/plugin-gem-example/plugin-gem-example.gemspec +28 -0
  41. data/dsl/prompts/simple_prompt.md.erb +3 -0
  42. data/dsl/prototype.rb +10 -4
  43. data/dsl/repeat_loop_results.rb +53 -0
  44. data/dsl/ruby_cog.rb +72 -0
  45. data/dsl/simple_agent.rb +18 -0
  46. data/dsl/simple_chat.rb +26 -0
  47. data/dsl/simple_repeat.rb +29 -0
  48. data/dsl/skip.rb +36 -0
  49. data/dsl/step_communication.rb +10 -5
  50. data/dsl/targets_and_params.rb +57 -0
  51. data/dsl/temperature.rb +17 -0
  52. data/dsl/temporary_directory.rb +22 -0
  53. data/dsl/tutorial/01_your_first_workflow/README.md +179 -0
  54. data/dsl/tutorial/01_your_first_workflow/configured_chat.rb +33 -0
  55. data/dsl/tutorial/01_your_first_workflow/hello.rb +23 -0
  56. data/dsl/tutorial/02_chaining_cogs/README.md +310 -0
  57. data/dsl/tutorial/02_chaining_cogs/code_review.rb +104 -0
  58. data/dsl/tutorial/02_chaining_cogs/session_resumption.rb +92 -0
  59. data/dsl/tutorial/02_chaining_cogs/simple_chain.rb +84 -0
  60. data/dsl/tutorial/03_targets_and_params/README.md +230 -0
  61. data/dsl/tutorial/03_targets_and_params/multiple_targets.rb +65 -0
  62. data/dsl/tutorial/03_targets_and_params/single_target.rb +65 -0
  63. data/dsl/tutorial/04_configuration_options/README.md +209 -0
  64. data/dsl/tutorial/04_configuration_options/control_display_and_temperature.rb +104 -0
  65. data/dsl/tutorial/04_configuration_options/simple_config.rb +68 -0
  66. data/dsl/tutorial/05_control_flow/README.md +156 -0
  67. data/dsl/tutorial/05_control_flow/conditional_execution.rb +62 -0
  68. data/dsl/tutorial/05_control_flow/handling_failures.rb +77 -0
  69. data/dsl/tutorial/06_reusable_scopes/README.md +172 -0
  70. data/dsl/tutorial/06_reusable_scopes/accessing_scope_outputs.rb +126 -0
  71. data/dsl/tutorial/06_reusable_scopes/basic_scope.rb +63 -0
  72. data/dsl/tutorial/06_reusable_scopes/parameterized_scope.rb +78 -0
  73. data/dsl/tutorial/07_processing_collections/README.md +152 -0
  74. data/dsl/tutorial/07_processing_collections/basic_map.rb +70 -0
  75. data/dsl/tutorial/07_processing_collections/parallel_map.rb +74 -0
  76. data/dsl/tutorial/08_iterative_workflows/README.md +231 -0
  77. data/dsl/tutorial/08_iterative_workflows/basic_repeat.rb +57 -0
  78. data/dsl/tutorial/08_iterative_workflows/conditional_break.rb +57 -0
  79. data/dsl/tutorial/09_async_cogs/README.md +197 -0
  80. data/dsl/tutorial/09_async_cogs/basic_async.rb +38 -0
  81. data/dsl/tutorial/README.md +222 -0
  82. data/dsl/working_directory.rb +16 -0
  83. data/exe/roast +1 -1
  84. data/internal/documentation/architectural-notes.md +115 -0
  85. data/internal/documentation/doc-comments-external.md +686 -0
  86. data/internal/documentation/doc-comments-internal.md +342 -0
  87. data/internal/documentation/doc-comments.md +211 -0
  88. data/lib/roast/dsl/cog/config.rb +280 -4
  89. data/lib/roast/dsl/cog/input.rb +73 -0
  90. data/lib/roast/dsl/cog/output.rb +313 -0
  91. data/lib/roast/dsl/cog/registry.rb +71 -0
  92. data/lib/roast/dsl/cog/stack.rb +3 -2
  93. data/lib/roast/dsl/cog/store.rb +11 -8
  94. data/lib/roast/dsl/cog.rb +108 -31
  95. data/lib/roast/dsl/cog_input_context.rb +44 -0
  96. data/lib/roast/dsl/cog_input_manager.rb +156 -0
  97. data/lib/roast/dsl/cogs/agent/config.rb +465 -0
  98. data/lib/roast/dsl/cogs/agent/input.rb +81 -0
  99. data/lib/roast/dsl/cogs/agent/output.rb +59 -0
  100. data/lib/roast/dsl/cogs/agent/provider.rb +51 -0
  101. data/lib/roast/dsl/cogs/agent/providers/claude/claude_invocation.rb +185 -0
  102. data/lib/roast/dsl/cogs/agent/providers/claude/message.rb +73 -0
  103. data/lib/roast/dsl/cogs/agent/providers/claude/messages/assistant_message.rb +36 -0
  104. data/lib/roast/dsl/cogs/agent/providers/claude/messages/result_message.rb +61 -0
  105. data/lib/roast/dsl/cogs/agent/providers/claude/messages/system_message.rb +47 -0
  106. data/lib/roast/dsl/cogs/agent/providers/claude/messages/text_message.rb +36 -0
  107. data/lib/roast/dsl/cogs/agent/providers/claude/messages/tool_result_message.rb +47 -0
  108. data/lib/roast/dsl/cogs/agent/providers/claude/messages/tool_use_message.rb +46 -0
  109. data/lib/roast/dsl/cogs/agent/providers/claude/messages/unknown_message.rb +27 -0
  110. data/lib/roast/dsl/cogs/agent/providers/claude/messages/user_message.rb +37 -0
  111. data/lib/roast/dsl/cogs/agent/providers/claude/tool_result.rb +51 -0
  112. data/lib/roast/dsl/cogs/agent/providers/claude/tool_use.rb +48 -0
  113. data/lib/roast/dsl/cogs/agent/providers/claude.rb +31 -0
  114. data/lib/roast/dsl/cogs/agent/stats.rb +92 -0
  115. data/lib/roast/dsl/cogs/agent/usage.rb +62 -0
  116. data/lib/roast/dsl/cogs/agent.rb +75 -0
  117. data/lib/roast/dsl/cogs/chat/config.rb +453 -0
  118. data/lib/roast/dsl/cogs/chat/input.rb +92 -0
  119. data/lib/roast/dsl/cogs/chat/output.rb +64 -0
  120. data/lib/roast/dsl/cogs/chat/session.rb +68 -0
  121. data/lib/roast/dsl/cogs/chat.rb +81 -0
  122. data/lib/roast/dsl/cogs/cmd.rb +291 -27
  123. data/lib/roast/dsl/cogs/ruby.rb +171 -0
  124. data/lib/roast/dsl/command_runner.rb +191 -0
  125. data/lib/roast/dsl/config_context.rb +2 -47
  126. data/lib/roast/dsl/config_manager.rb +143 -0
  127. data/lib/roast/dsl/control_flow.rb +41 -0
  128. data/lib/roast/dsl/execution_context.rb +9 -0
  129. data/lib/roast/dsl/execution_manager.rb +267 -0
  130. data/lib/roast/dsl/nil_assertions.rb +23 -0
  131. data/lib/roast/dsl/system_cog/params.rb +32 -0
  132. data/lib/roast/dsl/system_cog.rb +36 -0
  133. data/lib/roast/dsl/system_cogs/call.rb +162 -0
  134. data/lib/roast/dsl/system_cogs/map.rb +448 -0
  135. data/lib/roast/dsl/system_cogs/repeat.rb +242 -0
  136. data/lib/roast/dsl/workflow.rb +123 -0
  137. data/lib/roast/dsl/workflow_context.rb +20 -0
  138. data/lib/roast/dsl/workflow_params.rb +24 -0
  139. data/lib/roast/sorbet_runtime_stub.rb +154 -0
  140. data/lib/roast/tools/apply_diff.rb +1 -3
  141. data/lib/roast/tools/cmd.rb +4 -3
  142. data/lib/roast/tools/read_file.rb +1 -1
  143. data/lib/roast/tools/update_files.rb +1 -1
  144. data/lib/roast/tools/write_file.rb +1 -1
  145. data/lib/roast/version.rb +1 -1
  146. data/lib/roast/workflow/base_workflow.rb +4 -0
  147. data/lib/roast/workflow/step_loader.rb +14 -2
  148. data/lib/roast-ai.rb +4 -0
  149. data/lib/roast.rb +60 -22
  150. data/{roast.gemspec → roast-ai.gemspec} +10 -13
  151. data/sorbet/config +1 -0
  152. data/sorbet/rbi/gems/async@2.34.0.rbi +1577 -0
  153. data/sorbet/rbi/gems/cli-kit@5.2.0.rbi +2063 -0
  154. data/sorbet/rbi/gems/{cli-ui@2.3.0.rbi → cli-ui@2.7.0-6bdefd1d06305e5d6ae312ac76f9c88f88658dda.rbi} +1418 -1013
  155. data/sorbet/rbi/gems/console@1.34.2.rbi +1193 -0
  156. data/sorbet/rbi/gems/fiber-annotation@0.2.0.rbi +50 -0
  157. data/sorbet/rbi/gems/fiber-local@1.1.0.rbi +35 -0
  158. data/sorbet/rbi/gems/fiber-storage@1.0.1.rbi +41 -0
  159. data/sorbet/rbi/gems/io-event@1.14.0.rbi +724 -0
  160. data/sorbet/rbi/gems/marcel@1.1.0.rbi +239 -0
  161. data/sorbet/rbi/gems/metrics@0.15.0.rbi +9 -0
  162. data/sorbet/rbi/gems/ruby_llm@1.8.2.rbi +5703 -0
  163. data/sorbet/rbi/gems/traces@0.18.2.rbi +9 -0
  164. data/sorbet/rbi/shims/lib/roast/dsl/cog_input_context.rbi +1197 -0
  165. data/sorbet/rbi/shims/lib/roast/dsl/config_context.rbi +314 -2
  166. data/sorbet/rbi/shims/lib/roast/dsl/execution_context.rbi +498 -0
  167. data/sorbet/tapioca/config.yml +6 -0
  168. data/sorbet/tapioca/require.rb +2 -0
  169. metadata +198 -34
  170. data/dsl/less_simple.rb +0 -112
  171. data/dsl/simple.rb +0 -8
  172. data/lib/roast/dsl/cog_execution_context.rb +0 -29
  173. data/lib/roast/dsl/cogs/graph.rb +0 -53
  174. data/lib/roast/dsl/cogs.rb +0 -65
  175. data/lib/roast/dsl/executor.rb +0 -82
  176. data/lib/roast/dsl/workflow_execution_context.rb +0 -47
  177. data/sorbet/rbi/gems/cgi@0.5.0.rbi +0 -2961
  178. data/sorbet/rbi/gems/claude_swarm@0.1.19.rbi +0 -568
  179. data/sorbet/rbi/gems/cli-kit@5.0.1.rbi +0 -1991
  180. data/sorbet/rbi/gems/dry-configurable@1.3.0.rbi +0 -672
  181. data/sorbet/rbi/gems/dry-core@1.1.0.rbi +0 -1894
  182. data/sorbet/rbi/gems/dry-inflector@1.2.0.rbi +0 -659
  183. data/sorbet/rbi/gems/dry-initializer@3.2.0.rbi +0 -781
  184. data/sorbet/rbi/gems/dry-logic@1.6.0.rbi +0 -1127
  185. data/sorbet/rbi/gems/dry-schema@1.14.1.rbi +0 -3727
  186. data/sorbet/rbi/gems/dry-types@1.8.3.rbi +0 -3969
  187. data/sorbet/rbi/gems/fast-mcp-annotations@1.5.3.rbi +0 -1588
  188. data/sorbet/rbi/gems/mime-types-data@3.2025.0617.rbi +0 -136
  189. data/sorbet/rbi/gems/mime-types@3.7.0.rbi +0 -1342
  190. data/sorbet/rbi/gems/rack@2.2.18.rbi +0 -5659
  191. data/sorbet/rbi/gems/rbs-inline@0.12.0.rbi +0 -2170
  192. data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +0 -435
  193. data/sorbet/rbi/gems/yard@0.9.37.rbi +0 -18492
  194. data/sorbet/rbi/shims/lib/roast/dsl/workflow_execution_context.rbi +0 -11
@@ -0,0 +1,29 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ #: self as Roast::DSL::Workflow
5
+
6
+ config do
7
+ end
8
+
9
+ execute do
10
+ # You can use `repeat` to repeat an executor multiple times
11
+ # The executor will run forever until it breaks according to its internal logic
12
+ repeat(:loop, run: :loop_body) {}
13
+ end
14
+
15
+ execute(:loop_body) do
16
+ ruby(:one) do |_, _, idx|
17
+ s = "iteration #{idx}"
18
+ puts s
19
+ s
20
+ end
21
+
22
+ # Use `break!` to terminate the repeat loop
23
+ # Cogs that occur after `break!` is called will not run on the final iteration
24
+ ruby { |_, _, idx| break! if idx >= 3 }
25
+
26
+ # The `outputs` block will *always* run, including on the iteration in which the `break!` was issued
27
+ # NOTE: Be careful not to depend on the output of cogs that only run after the break point
28
+ outputs { |_, idx| "output of iteration #{idx}" }
29
+ end
data/dsl/skip.rb ADDED
@@ -0,0 +1,36 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ #: self as Roast::DSL::Workflow
5
+
6
+ config do
7
+ cmd { display! }
8
+ end
9
+
10
+ execute do
11
+ cmd(:seconds) { "date +'%S'" }
12
+
13
+ cmd(:even) do
14
+ # `cmd!` output accessor bang method will raise an exception if the named cog did not complete successfully
15
+ seconds = cmd!(:seconds).out.to_i
16
+ # Use the `skip!` method in a cog's input proc to conditionally skip this cog
17
+ # If `skip!` is called, the input proc will immediately terminate
18
+ skip! if seconds.odd?
19
+ "echo '#{seconds} is even'"
20
+ end
21
+
22
+ cmd(:odd) do
23
+ seconds = cmd!(:seconds).out.to_i
24
+ skip! if seconds.even?
25
+ "echo '#{seconds} is odd'"
26
+ end
27
+
28
+ cmd do |my|
29
+ my.command = "echo"
30
+ # `cmd` output accessor non-bang method will return the cog's output or
31
+ # nil if the named cog did not run yet or did not complete successfully
32
+ my.args << "'even' cog ran" unless cmd(:even).nil?
33
+ # `cmd?` output accessor question method will return true/false if the cog did / did not complete successfully yet
34
+ my.args << "'odd' cog ran" if cmd?(:odd)
35
+ end
36
+ end
@@ -1,6 +1,8 @@
1
- # typed: false
1
+ # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
+ #: self as Roast::DSL::Workflow
5
+
4
6
  # How do we pass information between steps?
5
7
  # Demonstrate by passing result of a command output to another step
6
8
 
@@ -10,9 +12,12 @@ end
10
12
 
11
13
  execute do
12
14
  cmd(:ls) { "ls -al" }
13
- cmd(:echo) do
14
- # TODO: this is a bespoke output object for cmd, is there a generic one we can offer
15
- first_line = cmd(:ls).command_output.split("\n").second
16
- "echo '#{first_line}'"
15
+ cmd(:echo) do |my|
16
+ my.command = "echo"
17
+ first_line = cmd!(:ls).lines.second
18
+ last_line = cmd!(:ls).lines.last
19
+ my.args << first_line unless first_line.blank?
20
+ my.args << "\n---\n"
21
+ my.args << last_line if last_line != first_line && last_line.present?
17
22
  end
18
23
  end
@@ -0,0 +1,57 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ #: self as Roast::DSL::Workflow
5
+
6
+ config do
7
+ end
8
+
9
+ execute do
10
+ ruby do |_, params|
11
+ # Any files listed after the standard roast command-line args are collected as your workflows targets
12
+ # (shell globs are expanded as you would expect)
13
+ # e.g., `roast execute --executor=dsl dsl/targets_and_params.rb Gemfile Gemfile.lock`
14
+ # or, `roast execute --executor=dsl dsl/targets_and_params.rb Gemfile*`
15
+ puts "workflow targets: #{params.targets}"
16
+
17
+ # You can specify custom arguments for your workflow. Anything coming after `--` on the roast command
18
+ # line will be parsed as custom arguments.
19
+ # Simple word tokens are collected as `args`. Tokens in the form `key=value` are parsed into the `kwargs` hash.
20
+ # e.g., `roast execute --executor=dsl dsl/targets_and_params.rb Gemfile* -- foo=bar abc=pqr hello world`
21
+ puts "workflow args: #{params.args}" # [:hello, :world] (args are parsed as symbols)
22
+
23
+ # {abc: "pqr", foo: "bar"} (keys are parsed as symbols, values as strings)
24
+ # (Note: using explicit formatting for compatibility with Ruby versions < 3.4)
25
+ puts "workflow kwargs: {#{params.kwargs.map { |k, v| "#{k}: #{v.inspect}" }.join(", ")}}"
26
+ end
27
+
28
+ # There are convenience methods to access the workflow params from any cog input context in any scope
29
+ ruby do
30
+ puts
31
+ # `target!` will raise an exception unless exactly one target is provided
32
+ # puts "Explicit target = #{target!}"
33
+
34
+ # `targets` will return the (possibly empty) array of targets
35
+ puts "All targets = #{targets}"
36
+
37
+ # `arg?` will return a boolean indicating whether a specific value / flag argument is present
38
+ puts "Argument 'foo' provided? #{arg?(:foo) ? "yes" : "no"}"
39
+
40
+ # `args` will return the (possibly empty) array of simple value / flag arguments
41
+ puts "All args = #{args}"
42
+
43
+ # `kwarg` will return the value associated with a specific keyword argument,
44
+ # or nil if that keyword argument was not provided
45
+ puts "Keyword argument 'name': '#{kwarg(:name)}'"
46
+
47
+ # `kwarg!` will return the value associated with a specific keyword argument,
48
+ # or raise an exception if that keyword argument was not provided
49
+ # puts "Keyword argument 'name': #{kwarg!(:name)}"
50
+
51
+ # `kwarg?` will return a boolean indicating whether a specific keyword argument was provided
52
+ puts "Keyword argument 'name' provided: #{kwarg?(:name) ? "yes" : "no"}"
53
+
54
+ # `kwargs` will return the (possibly empty) hash of all keyword arguments
55
+ # puts "All kwargs = #{kwargs}"
56
+ end
57
+ end
@@ -0,0 +1,17 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ config do
5
+ chat(:low_temp) do
6
+ temperature(0.0)
7
+ end
8
+
9
+ chat(:high_temp) do
10
+ temperature(1.0)
11
+ end
12
+ end
13
+
14
+ execute do
15
+ chat(:low_temp) { "Write a haiku about the capital of France." }
16
+ chat(:high_temp) { "Write a haiku about the capital of France." }
17
+ end
@@ -0,0 +1,22 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ #: self as Roast::DSL::Workflow
5
+
6
+ config do
7
+ end
8
+
9
+ execute do
10
+ ruby do
11
+ # The workflow automatically gets a temporary directory created for it
12
+ # unique to a single execution of the workflow, but shared across all executor scopes.
13
+ # This working directory will be cleaned up automatically when the workflow completes or fails.
14
+ puts tmpdir
15
+
16
+ # The value of `tmpdir` will always be a directory that exists
17
+ raise StandardError, "temporary directory does not exist" unless tmpdir.exist?
18
+
19
+ # The value of `tmpdir` will always be an absolute path
20
+ raise StandardError, "temporary directory is not an absolute path" unless tmpdir.absolute?
21
+ end
22
+ end
@@ -0,0 +1,179 @@
1
+ # Chapter 1: Your First Workflow
2
+
3
+ In this chapter, you'll create and run your first Roast workflow. We'll start with the absolute simplest example, then
4
+ show you how to add basic configuration.
5
+
6
+ ## What You'll Learn
7
+
8
+ - The basic structure of a Roast workflow file
9
+ - How to use the `chat` cog to interact with an LLM
10
+ - How to run workflows from the command line
11
+ - How to configure model settings
12
+
13
+ ## Understanding Workflows
14
+
15
+ Before we dive into code, let's understand what a Roast workflow is:
16
+
17
+ **A workflow is a sequence of steps that accomplish a task.** Think of it like a recipe: you define each step in order,
18
+ specify what inputs each step needs, and describe how they connect together.
19
+
20
+ ### Key Concepts
21
+
22
+ - **Steps** - Individual units of work (like "analyze this code" or "summarize this text")
23
+ - **Cogs** - The building blocks that implement step types (Roast comes with a comprehensive set of basic cogs for
24
+ interacting with LLMs, invoking coding agents, running shell commands, processing data, as wells as complex flow
25
+ control)
26
+ - **Declarative style** - You describe *what* you want done, the inputs each step needs, and how they connect together.
27
+ Roast takes care of the rest
28
+
29
+ For example, instead of writing code to open files, parse them, call APIs, handle errors, etc., you write:
30
+
31
+ ```ruby
32
+ execute do
33
+ chat { "Analyze this code and suggest improvements: #{my_code}" }
34
+ end
35
+ ```
36
+
37
+ Roast handles the details. You focus on the workflow.
38
+
39
+ In this chapter, we'll start with the simplest workflow: a single step using the `chat` cog. In later chapters, you'll
40
+ learn how to invoke coding agents, run shell commands, and chain multiple steps together to build sophisticated
41
+ workflows.
42
+
43
+ ## The Basics
44
+
45
+ Every Roast workflow is a Ruby file that defines what work to do. The simplest workflow has just one part:
46
+
47
+ 1. **Execute block** - Contains the work to perform
48
+
49
+ ### Minimal Syntax
50
+
51
+ ```ruby
52
+ execute do
53
+ # Your workflow goes here
54
+ end
55
+ ```
56
+
57
+ That's it! Everything happens inside the `execute do ... end` block.
58
+
59
+ ## Your First Chat Cog
60
+
61
+ The `chat` cog sends a prompt to a cloud-based LLM and gets a response back. Here's a simplest example:
62
+
63
+ ```ruby
64
+ execute do
65
+ chat { "Say hello!" }
66
+ end
67
+ ```
68
+
69
+ When you run this, Roast will:
70
+
71
+ 1. Send the prompt "Say hello!" to default LLM provider and model (OpenAI gpt-4o-mini)
72
+ 2. Wait for the response
73
+ 3. Display the response in your terminal, along with LLM usage statistics
74
+
75
+ ### Longer Prompts
76
+
77
+ For real workflows, you'll want more detailed prompts. Use Ruby's heredoc syntax for multi-line prompts:
78
+
79
+ ```ruby
80
+ execute do
81
+ chat do
82
+ <<~PROMPT
83
+ You are a helpful AI assistant.
84
+
85
+ Please introduce yourself and explain what Roast is in 2-3 sentences.
86
+ Keep your response friendly and concise.
87
+ PROMPT
88
+ end
89
+ end
90
+ ```
91
+
92
+ Or define your prompts in a template file using ERB syntax and include them in your workflow
93
+
94
+ ```ruby
95
+ execute do
96
+ chat { template("path_to_prompt_file.md.erb", { context: }) }
97
+ end
98
+ ```
99
+
100
+ ## Adding Configuration
101
+
102
+ You can configure which model to use, which provider, and other settings. Configuration goes in a `config do ... end`
103
+ block:
104
+
105
+ ```ruby
106
+ config do
107
+ chat do
108
+ model "gpt-4o-mini" # Use OpenAI's fast model
109
+ provider :openai # Use OpenAI (can also be :anthropic)
110
+ show_prompt! # Display the prompt before sending
111
+ end
112
+ end
113
+
114
+ execute do
115
+ chat do
116
+ <<~PROMPT
117
+ Explain what an AI workflow is in simple terms.
118
+ PROMPT
119
+ end
120
+ end
121
+ ```
122
+
123
+ ### Configuration Options
124
+
125
+ Common options you can set:
126
+
127
+ - `model "name"` - Which LLM model to use
128
+ - OpenAI: "gpt-4o", "gpt-4o-mini", "gpt-4-turbo"
129
+ - Anthropic: "claude-3-5-sonnet-20241022", "claude-3-5-haiku-20241022"
130
+ - `provider :name` - Which LLM provider
131
+ - `:openai` or `:anthropic`
132
+ - `show_prompt!` - Display the prompt being sent
133
+ - `show_response!` - Display the response (on by default)
134
+ - `show_stats!` - Display token usage statistics (on by default)
135
+ - `no_display!` - Turn off all output (useful when you just want to pass the output to a subsequent cog)
136
+
137
+ ## Running the Workflows
138
+
139
+ To run any workflow in this chapter:
140
+
141
+ ```bash
142
+ # From the project root
143
+ bin/roast execute --executor=dsl dsl/tutorial/01_your_first_workflow/hello.rb
144
+ ```
145
+
146
+ Or the configured version:
147
+
148
+ ```bash
149
+ bin/roast execute --executor=dsl dsl/tutorial/01_your_first_workflow/configured_chat.rb
150
+ ```
151
+
152
+ You should see:
153
+
154
+ 1. The prompt being sent (if you enabled `show_prompt!`)
155
+ 2. The LLM's response
156
+ 3. Statistics about token usage
157
+
158
+ ## Try It Yourself
159
+
160
+ 1. **Run both example workflows** in this chapter
161
+ 2. **Modify the prompts** - Ask different questions
162
+ 3. **Try different models** - Change "gpt-4o-mini" to "gpt-4o" in the config
163
+ 4. **Experiment with display settings** - Try `no_show_stats!` and see what happens
164
+
165
+ ## Key Takeaways
166
+
167
+ - The `execute do ... end` block contains your workflow logic
168
+ - The `chat` cog sends prompts to cloud-based LLMs
169
+ - Use `<<~PROMPT ... PROMPT` for multi-line prompts
170
+ - The `config do ... end` block (before execute) sets up cog behavior
171
+ - Run workflows with `bin/roast execute --executor=dsl path/to/workflow.rb`
172
+
173
+ ## What's Next?
174
+
175
+ In the next chapter, you'll learn how to chain multiple cogs together, so the output of one becomes the input to
176
+ another. This is where Roast becomes powerful!
177
+
178
+ But first, make sure you can successfully run both workflows in this chapter. Experiment with different prompts and
179
+ configurations until you're comfortable.
@@ -0,0 +1,33 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ #: self as Roast::DSL::Workflow
5
+
6
+ # This workflow demonstrates how to configure chat cogs.
7
+ # Configuration is set in a 'config' block before the 'execute' block.
8
+
9
+ config do
10
+ # Configure all chat cogs in this workflow
11
+ chat do
12
+ model "gpt-4o-mini" # Use OpenAI's fast, cost-effective model
13
+ provider :openai # Use OpenAI (alternative: :anthropic)
14
+ show_prompt! # Display the prompt before sending it
15
+ show_response! # Display the response (this is the default)
16
+ show_stats! # Display token usage statistics (default)
17
+ end
18
+ end
19
+
20
+ execute do
21
+ chat do
22
+ <<~PROMPT
23
+ You are a knowledgeable software development assistant.
24
+
25
+ Explain what a "workflow" is in the context of software automation,
26
+ using simple language that a beginner could understand.
27
+
28
+ Provide a brief example of when you might use a workflow.
29
+
30
+ Keep your response to 3-4 sentences.
31
+ PROMPT
32
+ end
33
+ end
@@ -0,0 +1,23 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ # This type annotation helps Sorbet type-check your workflow.
5
+ # It's optional if you don't want to use Sorbet.
6
+ #: self as Roast::DSL::Workflow
7
+
8
+ # This is the simplest possible Roast workflow.
9
+ # It sends a single prompt to a chat LLM and displays the response.
10
+
11
+ config {}
12
+
13
+ execute do
14
+ chat do
15
+ <<~PROMPT
16
+ You are a friendly AI assistant helping someone learn Roast,
17
+ a Ruby-based workflow system for AI tasks.
18
+
19
+ Say hello and give them one encouraging tip about learning
20
+ new tools. Keep it brief and friendly!
21
+ PROMPT
22
+ end
23
+ end