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.
- checksums.yaml +4 -4
- data/.claude/commands/docs/write-comments.md +36 -0
- data/.github/CODEOWNERS +1 -1
- data/.github/workflows/ci.yaml +10 -6
- data/.gitignore +0 -1
- data/.rubocop.yml +7 -1
- data/CLAUDE.md +2 -2
- data/CONTRIBUTING.md +2 -0
- data/Gemfile +19 -18
- data/Gemfile.lock +35 -58
- data/README.md +118 -1432
- data/README_LEGACY.md +1464 -0
- data/Rakefile +39 -4
- data/dev.yml +29 -0
- data/dsl/agent_sessions.rb +20 -0
- data/dsl/async_cogs.rb +49 -0
- data/dsl/async_cogs_complex.rb +67 -0
- data/dsl/call.rb +44 -0
- data/dsl/collect_from.rb +72 -0
- data/dsl/json_output.rb +28 -0
- data/dsl/map.rb +55 -0
- data/dsl/map_reduce.rb +37 -0
- data/dsl/map_with_index.rb +49 -0
- data/dsl/next_break.rb +40 -0
- data/dsl/next_break_parallel.rb +44 -0
- data/dsl/outputs.rb +39 -0
- data/dsl/outputs_bang.rb +36 -0
- data/dsl/parallel_map.rb +37 -0
- data/dsl/prompts/simple_prompt.md.erb +3 -0
- data/dsl/prototype.rb +5 -7
- data/dsl/repeat_loop_results.rb +53 -0
- data/dsl/ruby_cog.rb +72 -0
- data/dsl/simple_agent.rb +18 -0
- data/dsl/simple_chat.rb +15 -1
- data/dsl/simple_repeat.rb +29 -0
- data/dsl/skip.rb +36 -0
- data/dsl/step_communication.rb +2 -3
- data/dsl/targets_and_params.rb +57 -0
- data/dsl/temperature.rb +17 -0
- data/dsl/temporary_directory.rb +22 -0
- data/dsl/tutorial/01_your_first_workflow/README.md +179 -0
- data/dsl/tutorial/01_your_first_workflow/configured_chat.rb +33 -0
- data/dsl/tutorial/01_your_first_workflow/hello.rb +23 -0
- data/dsl/tutorial/02_chaining_cogs/README.md +310 -0
- data/dsl/tutorial/02_chaining_cogs/code_review.rb +104 -0
- data/dsl/tutorial/02_chaining_cogs/session_resumption.rb +92 -0
- data/dsl/tutorial/02_chaining_cogs/simple_chain.rb +84 -0
- data/dsl/tutorial/03_targets_and_params/README.md +230 -0
- data/dsl/tutorial/03_targets_and_params/multiple_targets.rb +65 -0
- data/dsl/tutorial/03_targets_and_params/single_target.rb +65 -0
- data/dsl/tutorial/04_configuration_options/README.md +209 -0
- data/dsl/tutorial/04_configuration_options/control_display_and_temperature.rb +104 -0
- data/dsl/tutorial/04_configuration_options/simple_config.rb +68 -0
- data/dsl/tutorial/05_control_flow/README.md +156 -0
- data/dsl/tutorial/05_control_flow/conditional_execution.rb +62 -0
- data/dsl/tutorial/05_control_flow/handling_failures.rb +77 -0
- data/dsl/tutorial/06_reusable_scopes/README.md +172 -0
- data/dsl/tutorial/06_reusable_scopes/accessing_scope_outputs.rb +126 -0
- data/dsl/tutorial/06_reusable_scopes/basic_scope.rb +63 -0
- data/dsl/tutorial/06_reusable_scopes/parameterized_scope.rb +78 -0
- data/dsl/tutorial/07_processing_collections/README.md +152 -0
- data/dsl/tutorial/07_processing_collections/basic_map.rb +70 -0
- data/dsl/tutorial/07_processing_collections/parallel_map.rb +74 -0
- data/dsl/tutorial/08_iterative_workflows/README.md +231 -0
- data/dsl/tutorial/08_iterative_workflows/basic_repeat.rb +57 -0
- data/dsl/tutorial/08_iterative_workflows/conditional_break.rb +57 -0
- data/dsl/tutorial/09_async_cogs/README.md +197 -0
- data/dsl/tutorial/09_async_cogs/basic_async.rb +38 -0
- data/dsl/tutorial/README.md +222 -0
- data/dsl/working_directory.rb +16 -0
- data/exe/roast +1 -1
- data/internal/documentation/architectural-notes.md +115 -0
- data/internal/documentation/doc-comments-external.md +686 -0
- data/internal/documentation/doc-comments-internal.md +342 -0
- data/internal/documentation/doc-comments.md +211 -0
- data/lib/roast/dsl/cog/config.rb +274 -3
- data/lib/roast/dsl/cog/input.rb +53 -10
- data/lib/roast/dsl/cog/output.rb +297 -8
- data/lib/roast/dsl/cog/registry.rb +35 -3
- data/lib/roast/dsl/cog/stack.rb +1 -1
- data/lib/roast/dsl/cog/store.rb +5 -5
- data/lib/roast/dsl/cog.rb +70 -14
- data/lib/roast/dsl/cog_input_context.rb +36 -1
- data/lib/roast/dsl/cog_input_manager.rb +116 -7
- data/lib/roast/dsl/cogs/agent/config.rb +465 -0
- data/lib/roast/dsl/cogs/agent/input.rb +81 -0
- data/lib/roast/dsl/cogs/agent/output.rb +59 -0
- data/lib/roast/dsl/cogs/agent/provider.rb +51 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/claude_invocation.rb +185 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/message.rb +73 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/assistant_message.rb +36 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/result_message.rb +61 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/system_message.rb +47 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/text_message.rb +36 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/tool_result_message.rb +47 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/tool_use_message.rb +46 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/unknown_message.rb +27 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/user_message.rb +37 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/tool_result.rb +51 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/tool_use.rb +48 -0
- data/lib/roast/dsl/cogs/agent/providers/claude.rb +31 -0
- data/lib/roast/dsl/cogs/agent/stats.rb +92 -0
- data/lib/roast/dsl/cogs/agent/usage.rb +62 -0
- data/lib/roast/dsl/cogs/agent.rb +75 -0
- data/lib/roast/dsl/cogs/chat/config.rb +453 -0
- data/lib/roast/dsl/cogs/chat/input.rb +92 -0
- data/lib/roast/dsl/cogs/chat/output.rb +64 -0
- data/lib/roast/dsl/cogs/chat/session.rb +68 -0
- data/lib/roast/dsl/cogs/chat.rb +59 -56
- data/lib/roast/dsl/cogs/cmd.rb +248 -61
- data/lib/roast/dsl/cogs/ruby.rb +171 -0
- data/lib/roast/dsl/command_runner.rb +191 -0
- data/lib/roast/dsl/config_manager.rb +58 -11
- data/lib/roast/dsl/control_flow.rb +41 -0
- data/lib/roast/dsl/execution_manager.rb +162 -32
- data/lib/roast/dsl/nil_assertions.rb +23 -0
- data/lib/roast/dsl/system_cog/params.rb +32 -0
- data/lib/roast/dsl/system_cog.rb +36 -0
- data/lib/roast/dsl/system_cogs/call.rb +162 -0
- data/lib/roast/dsl/system_cogs/map.rb +448 -0
- data/lib/roast/dsl/system_cogs/repeat.rb +242 -0
- data/lib/roast/dsl/workflow.rb +26 -16
- data/lib/roast/dsl/workflow_context.rb +20 -0
- data/lib/roast/dsl/workflow_params.rb +24 -0
- data/lib/roast/sorbet_runtime_stub.rb +154 -0
- data/lib/roast/tools/apply_diff.rb +1 -3
- data/lib/roast/tools/cmd.rb +4 -3
- data/lib/roast/tools/read_file.rb +1 -1
- data/lib/roast/tools/update_files.rb +1 -1
- data/lib/roast/tools/write_file.rb +1 -1
- data/lib/roast/version.rb +1 -1
- data/lib/roast/workflow/base_workflow.rb +4 -0
- data/lib/roast/workflow/step_loader.rb +14 -2
- data/lib/roast-ai.rb +4 -0
- data/lib/roast.rb +58 -21
- data/{roast.gemspec → roast-ai.gemspec} +9 -13
- data/sorbet/rbi/gems/async@2.34.0.rbi +1577 -0
- data/sorbet/rbi/gems/cli-kit@5.2.0.rbi +2063 -0
- data/sorbet/rbi/gems/{cli-ui@2.3.0.rbi → cli-ui@2.7.0-6bdefd1d06305e5d6ae312ac76f9c88f88658dda.rbi} +1418 -1013
- data/sorbet/rbi/gems/console@1.34.2.rbi +1193 -0
- data/sorbet/rbi/gems/fiber-annotation@0.2.0.rbi +50 -0
- data/sorbet/rbi/gems/fiber-local@1.1.0.rbi +35 -0
- data/sorbet/rbi/gems/fiber-storage@1.0.1.rbi +41 -0
- data/sorbet/rbi/gems/io-event@1.14.0.rbi +724 -0
- data/sorbet/rbi/gems/metrics@0.15.0.rbi +9 -0
- data/sorbet/rbi/gems/traces@0.18.2.rbi +9 -0
- data/sorbet/rbi/shims/lib/roast/dsl/cog_input_context.rbi +1185 -5
- data/sorbet/rbi/shims/lib/roast/dsl/config_context.rbi +311 -5
- data/sorbet/rbi/shims/lib/roast/dsl/execution_context.rbi +486 -5
- data/sorbet/tapioca/config.yml +6 -0
- data/sorbet/tapioca/require.rb +2 -0
- metadata +157 -30
- data/dsl/less_simple.rb +0 -112
- data/dsl/scoped_executors.rb +0 -28
- data/dsl/simple.rb +0 -8
- data/lib/roast/dsl/cogs/execute.rb +0 -46
- data/lib/roast/dsl/cogs/graph.rb +0 -53
- data/sorbet/rbi/gems/cgi@0.5.0.rbi +0 -2961
- data/sorbet/rbi/gems/claude_swarm@0.1.19.rbi +0 -568
- data/sorbet/rbi/gems/cli-kit@5.0.1.rbi +0 -1991
- data/sorbet/rbi/gems/dry-configurable@1.3.0.rbi +0 -672
- data/sorbet/rbi/gems/dry-core@1.1.0.rbi +0 -1894
- data/sorbet/rbi/gems/dry-inflector@1.2.0.rbi +0 -659
- data/sorbet/rbi/gems/dry-initializer@3.2.0.rbi +0 -781
- data/sorbet/rbi/gems/dry-logic@1.6.0.rbi +0 -1127
- data/sorbet/rbi/gems/dry-schema@1.14.1.rbi +0 -3727
- data/sorbet/rbi/gems/dry-types@1.8.3.rbi +0 -3969
- data/sorbet/rbi/gems/fast-mcp-annotations@1.5.3.rbi +0 -1588
- data/sorbet/rbi/gems/mime-types-data@3.2025.0617.rbi +0 -136
- data/sorbet/rbi/gems/mime-types@3.7.0.rbi +0 -1342
- data/sorbet/rbi/gems/rack@2.2.19.rbi +0 -5676
- data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +0 -435
- data/sorbet/rbi/gems/yard@0.9.37.rbi +0 -18492
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#: self as Roast::DSL::Workflow
|
|
5
|
+
|
|
6
|
+
config do
|
|
7
|
+
chat { no_display! }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
execute(:refine_text) do
|
|
11
|
+
chat(:improve) do |_, text|
|
|
12
|
+
<<~PROMPT
|
|
13
|
+
Improve this text by making it more concise and clear.
|
|
14
|
+
Keep the same meaning but use fewer words.
|
|
15
|
+
|
|
16
|
+
Text: #{text}
|
|
17
|
+
|
|
18
|
+
Return only the improved text, nothing else.
|
|
19
|
+
PROMPT
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
ruby do |_, _, index|
|
|
23
|
+
puts "\n--- Iteration #{index} ---"
|
|
24
|
+
puts chat!(:improve).response
|
|
25
|
+
break! if index >= 3
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
outputs { chat!(:improve).text }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
execute do
|
|
32
|
+
initial_text = <<~TEXT
|
|
33
|
+
It is widely known and commonly accepted by many people that
|
|
34
|
+
regular physical exercise and activity is generally beneficial
|
|
35
|
+
and helpful for maintaining good health and overall wellness.
|
|
36
|
+
TEXT
|
|
37
|
+
|
|
38
|
+
puts "=== Original Text ==="
|
|
39
|
+
puts initial_text
|
|
40
|
+
|
|
41
|
+
repeat(:refinement, run: :refine_text) { initial_text }
|
|
42
|
+
|
|
43
|
+
ruby do
|
|
44
|
+
puts "\n=== Final Result ==="
|
|
45
|
+
puts repeat!(:refinement).value
|
|
46
|
+
|
|
47
|
+
puts "\n=== All Iterations ==="
|
|
48
|
+
all_versions = collect(repeat!(:refinement).results) do
|
|
49
|
+
chat!(:improve).response
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
all_versions.each_with_index do |version, i|
|
|
53
|
+
puts "\nIteration #{i}:"
|
|
54
|
+
puts version
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
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
|
+
chat { display! }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
execute(:guess_number) do
|
|
11
|
+
ruby { |_, _, index| puts "\n--- Iteration #{index} ---" }
|
|
12
|
+
|
|
13
|
+
chat(:make_initial_guess) do |_, _, index|
|
|
14
|
+
skip! unless index == 0
|
|
15
|
+
|
|
16
|
+
<<~PROMPT
|
|
17
|
+
I'm thinking of a number between 1 and 100.
|
|
18
|
+
Take a guess! Respond with just the number.
|
|
19
|
+
PROMPT
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
chat(:make_guess) do |my, state, index|
|
|
23
|
+
skip! if index == 0
|
|
24
|
+
|
|
25
|
+
guess, session, target = state.values_at(:guess, :session, :target)
|
|
26
|
+
break! if guess == target
|
|
27
|
+
|
|
28
|
+
my.session = session
|
|
29
|
+
my.prompt = "Too #{guess > target ? "high" : "low"}. Guess again"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
outputs do |_, target|
|
|
33
|
+
result = chat(:make_guess) || chat!(:make_initial_guess)
|
|
34
|
+
{ guess: result.integer, session: result.session, target: }
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
execute do
|
|
39
|
+
ruby do
|
|
40
|
+
fail! "Target number out of range" if target!.to_i < 1 || target!.to_i > 100
|
|
41
|
+
|
|
42
|
+
puts "=== Number Guessing Game ==="
|
|
43
|
+
puts "Target number: #{target!} (hidden from the LLM)\n"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
repeat(:guessing, run: :guess_number) do |my|
|
|
47
|
+
my.value = target!.to_i
|
|
48
|
+
my.max_iterations = 7
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
ruby do
|
|
52
|
+
iteration_count = collect(repeat!(:guessing).results).length
|
|
53
|
+
|
|
54
|
+
puts "\n=== Game Complete ==="
|
|
55
|
+
puts "Found the number in #{iteration_count} guesses!"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Chapter 9: Asynchronous Cogs
|
|
2
|
+
|
|
3
|
+
Learn how to run cogs asynchronously in the background while other work continues. This is different from `map` with
|
|
4
|
+
parallel execution, which can run multiple invocations of an execution scope in parallel, each processing a different
|
|
5
|
+
input. Async cogs let you run all sorts of independent tasks concurrently, __within the same execution scope__ without
|
|
6
|
+
waiting for one task to complete before starting another.
|
|
7
|
+
|
|
8
|
+
## What Are Async Cogs?
|
|
9
|
+
|
|
10
|
+
By default, cogs run synchronously: each cog must complete before the next one starts. With async cogs, you can kick off
|
|
11
|
+
long-running tasks in the background and continue with other work immediately.
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
config do
|
|
15
|
+
agent(:analyze_code) { async! }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
execute do
|
|
19
|
+
# This agent starts running in the background
|
|
20
|
+
agent(:analyze_code) do
|
|
21
|
+
"Analyze the Ruby files in src/ for code quality issues. List the top three issues."
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# This starts immediately without waiting for the agent to finish
|
|
25
|
+
chat(:draft_email) do
|
|
26
|
+
"Draft a brief status update email about the current sprint"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# This blocks until the agent completes, then uses its output
|
|
30
|
+
chat(:finish_email) do |my|
|
|
31
|
+
my.session = chat!(:draft_email).session
|
|
32
|
+
my.prompt = <<~PROMPT
|
|
33
|
+
"Update the status email with a summary of the issues we'll tackle next week:
|
|
34
|
+
#{agent!(:analyze_code).text}"
|
|
35
|
+
PROMPT
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Key Differences from Parallel Map
|
|
41
|
+
|
|
42
|
+
- **Async cogs**: Run different, independent tasks concurrently in the same scope (e.g., analyze code + draft email)
|
|
43
|
+
- **Parallel map**: Apply the same operation to multiple items concurrently (e.g., process 10 files in parallel), each in their own scope
|
|
44
|
+
|
|
45
|
+
Use async cogs when you have separate tasks that can happen at the same time. Use parallel map when you're processing a
|
|
46
|
+
collection.
|
|
47
|
+
|
|
48
|
+
## Configuring Async Cogs
|
|
49
|
+
|
|
50
|
+
Configure cogs to run asynchronously in the `config` block:
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
config do
|
|
54
|
+
# Make specific cog async
|
|
55
|
+
agent(:background_task) { async! }
|
|
56
|
+
|
|
57
|
+
# Make all agent cogs async
|
|
58
|
+
agent { async! }
|
|
59
|
+
|
|
60
|
+
# Pattern-based configuration
|
|
61
|
+
agent(/analyze_/) { async! }
|
|
62
|
+
|
|
63
|
+
# Override a more general config to make a cog explicitly synchronous (this is the default)
|
|
64
|
+
chat(:critical_step) { no_async! }
|
|
65
|
+
end
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## How Async Execution Works
|
|
69
|
+
|
|
70
|
+
When an async cog is invoked:
|
|
71
|
+
|
|
72
|
+
1. It starts running in the background
|
|
73
|
+
2. The next cog starts immediately (doesn't wait)
|
|
74
|
+
3. If you try access the async cog's output from a later cog, that will block until the async cog completes
|
|
75
|
+
4. The workflow doesn't exit until all async cogs have finished
|
|
76
|
+
5. A named execute scope doesn't complete until all async cogs within it have finished
|
|
77
|
+
|
|
78
|
+
```ruby
|
|
79
|
+
config do
|
|
80
|
+
agent { async! }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
execute do
|
|
84
|
+
agent(:task1) { "Read and summarize README.md" }
|
|
85
|
+
agent(:task2) { "Count lines of code in src/" }
|
|
86
|
+
agent(:task3) { "List all TODO comments" }
|
|
87
|
+
|
|
88
|
+
# Accessing outputs blocks until each completes
|
|
89
|
+
ruby do
|
|
90
|
+
puts "\nTask 1: #{agent!(:task1).response}"
|
|
91
|
+
puts "\nTask 2: #{agent!(:task2).response}"
|
|
92
|
+
puts "\nTask 3: #{agent!(:task3).response}"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Accessing Async Cog Outputs
|
|
98
|
+
|
|
99
|
+
All three accessor patterns (the `!`, `?` and normal cog methods) work with async cogs and all will block if needed:
|
|
100
|
+
|
|
101
|
+
```ruby
|
|
102
|
+
config do
|
|
103
|
+
agent { async! }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
execute do
|
|
107
|
+
agent(:background) { "Long running task" }
|
|
108
|
+
|
|
109
|
+
ruby do
|
|
110
|
+
# All of these block until the async cog completes:
|
|
111
|
+
|
|
112
|
+
# Check if it ran (blocks)
|
|
113
|
+
if agent?(:background)
|
|
114
|
+
puts "Completed!"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Get output or nil (blocks)
|
|
118
|
+
result = agent(:background)
|
|
119
|
+
|
|
120
|
+
# Get output or raise error (blocks)
|
|
121
|
+
result = agent!(:background)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Real-World Example: Parallel Code Analysis
|
|
127
|
+
|
|
128
|
+
Run multiple independent analyses concurrently:
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
config do
|
|
132
|
+
agent { async! }
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
execute do
|
|
136
|
+
# All three start immediately
|
|
137
|
+
agent(:security) do
|
|
138
|
+
"Review files in src/ for security vulnerabilities"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
agent(:performance) do
|
|
142
|
+
"Identify performance bottlenecks in src/"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
agent(:style) do
|
|
146
|
+
"Check code style and formatting in src/"
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Collect results as they complete
|
|
150
|
+
ruby do
|
|
151
|
+
puts "\n=== Security Issues ==="
|
|
152
|
+
puts agent!(:security).response
|
|
153
|
+
|
|
154
|
+
puts "\n=== Performance Issues ==="
|
|
155
|
+
puts agent!(:performance).response
|
|
156
|
+
|
|
157
|
+
puts "\n=== Style Issues ==="
|
|
158
|
+
puts agent!(:style).response
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## When to Use Async Cogs
|
|
164
|
+
|
|
165
|
+
**Good use cases:**
|
|
166
|
+
|
|
167
|
+
- Running multiple independent agent tasks that don't depend on each other
|
|
168
|
+
- Starting a long-running background task while doing other work
|
|
169
|
+
- Parallelizing unrelated API calls or file operations
|
|
170
|
+
|
|
171
|
+
**Not needed when:**
|
|
172
|
+
|
|
173
|
+
- Cogs already run fast enough sequentially
|
|
174
|
+
- Cogs depend on each other's outputs (they'll block anyway)
|
|
175
|
+
- You're processing a collection (use `map` with `parallel!` instead)
|
|
176
|
+
|
|
177
|
+
## Important Notes
|
|
178
|
+
|
|
179
|
+
- Async cogs use Ruby's fiber-based concurrency (efficient and lightweight)
|
|
180
|
+
- The workflow waits for all async cogs to complete before exiting
|
|
181
|
+
- Accessing an async cog's output blocks until that cog finishes
|
|
182
|
+
- All cog types (`agent`, `chat`, `cmd`, `ruby`, and even `call`, `map`, and `repeat`) can be async
|
|
183
|
+
|
|
184
|
+
## Running the Workflows
|
|
185
|
+
|
|
186
|
+
Try these examples to see async cogs in action:
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
# Basic async execution
|
|
190
|
+
bin/roast execute --executor=dsl dsl/tutorial/09_async_cogs/basic_async.rb
|
|
191
|
+
|
|
192
|
+
# Parallel analysis with multiple agents
|
|
193
|
+
bin/roast execute --executor=dsl dsl/tutorial/09_async_cogs/parallel_analysis.rb
|
|
194
|
+
|
|
195
|
+
# Synchronous barriers controlling execution order
|
|
196
|
+
bin/roast execute --executor=dsl dsl/tutorial/09_async_cogs/sync_barriers.rb
|
|
197
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#: self as Roast::DSL::Workflow
|
|
5
|
+
|
|
6
|
+
config do
|
|
7
|
+
agent do
|
|
8
|
+
async!
|
|
9
|
+
display!
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
execute do
|
|
14
|
+
# All three agents start immediately and run in the background
|
|
15
|
+
agent(:readme) do
|
|
16
|
+
"Read the README.md file and give me a one-sentence summary of what this project does."
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
agent(:gemfile) do
|
|
20
|
+
"Look at the Gemfile and list the 3 most important dependencies (just their names)."
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
agent(:structure) do
|
|
24
|
+
"Look at the directory structure and tell me what programming language this project uses."
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# When we access outputs, we block until that agent completes
|
|
28
|
+
ruby do
|
|
29
|
+
puts "=== Project Summary ==="
|
|
30
|
+
puts agent!(:readme).response
|
|
31
|
+
|
|
32
|
+
puts "=== Key Dependencies ==="
|
|
33
|
+
puts agent!(:gemfile).response
|
|
34
|
+
|
|
35
|
+
puts "=== Language ==="
|
|
36
|
+
puts agent!(:structure).response
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
# Roast Tutorial
|
|
4
|
+
|
|
5
|
+
🔥 _version 1.0 feature preview_ 🔥
|
|
6
|
+
|
|
7
|
+
Welcome to the Roast tutorial! This guide will teach you how to build AI workflows using the Roast DSL.
|
|
8
|
+
|
|
9
|
+
## What is Roast?
|
|
10
|
+
|
|
11
|
+
Roast is a Ruby-based domain-specific language for creating structured AI workflows
|
|
12
|
+
and building complex AI-powered automation. You write workflows in a simple Ruby syntax to orchestrate LLM calls,
|
|
13
|
+
run coding agents, process data, and so much more.
|
|
14
|
+
|
|
15
|
+
## Prerequisites
|
|
16
|
+
|
|
17
|
+
- Ruby installed (3.4.2+)
|
|
18
|
+
- Roast gem installed
|
|
19
|
+
- API keys for your AI providers of choice (to use LLM chat)
|
|
20
|
+
- Claude Code CLI installed and configured (to use coding agents)
|
|
21
|
+
|
|
22
|
+
## How to Use This Tutorial
|
|
23
|
+
|
|
24
|
+
Each chapter is a self-contained lesson with:
|
|
25
|
+
|
|
26
|
+
- **README.md** - Lesson content with explanations and code snippets
|
|
27
|
+
- **Workflow files** (`.rb`) - Complete, runnable examples
|
|
28
|
+
- **data/** folder - Sample data files (when needed)
|
|
29
|
+
|
|
30
|
+
To run any example:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
bin/roast execute --executor=dsl dsl/tutorial/CHAPTER_NAME/workflow_name.rb
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Tutorial Chapters
|
|
37
|
+
|
|
38
|
+
### Chapter 1: Your First Workflow
|
|
39
|
+
|
|
40
|
+
Quickly learn the basics: how to create and run a simple workflow with a single chat cog.
|
|
41
|
+
|
|
42
|
+
**You'll learn:**
|
|
43
|
+
|
|
44
|
+
- Basic workflow file structure
|
|
45
|
+
- Using the `chat` cog
|
|
46
|
+
- Running workflows
|
|
47
|
+
- Simple configuration
|
|
48
|
+
|
|
49
|
+
**Files:**
|
|
50
|
+
|
|
51
|
+
- `01_your_first_workflow/hello.rb` - Simplest possible workflow
|
|
52
|
+
- `01_your_first_workflow/configured_chat.rb` - Adding configuration
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
### Chapter 2: Chaining Cogs Together
|
|
57
|
+
|
|
58
|
+
Learn how to build multi-step workflows by chaining cogs together, where the output of one step becomes the input to the
|
|
59
|
+
next.
|
|
60
|
+
|
|
61
|
+
**You'll learn:**
|
|
62
|
+
|
|
63
|
+
- How to name cogs and reference their outputs
|
|
64
|
+
- Using the `!` suffix to access previous results
|
|
65
|
+
- The difference between `agent` and `chat` cogs
|
|
66
|
+
- Using the `ruby` cog for custom logic
|
|
67
|
+
- How data flows through a workflow
|
|
68
|
+
- Resuming conversations with session management
|
|
69
|
+
|
|
70
|
+
**Files:**
|
|
71
|
+
|
|
72
|
+
- `02_chaining_cogs/simple_chain.rb` - Basic chaining with `chat` cogs
|
|
73
|
+
- `02_chaining_cogs/code_review.rb` - Realistic workflow with `agent`, `chat`, and `ruby`
|
|
74
|
+
- `02_chaining_cogs/session_resumption.rb` - Multi-turn conversations with session resumption
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
### Chapter 3: Targets and Parameters
|
|
79
|
+
|
|
80
|
+
Learn how to make workflows flexible by accepting targets and custom parameters from the command line.
|
|
81
|
+
|
|
82
|
+
**You'll learn:**
|
|
83
|
+
|
|
84
|
+
- Using `target!` for single-target workflows (file, url, etc.)
|
|
85
|
+
- Using `targets` for a workflow that can accept multiple targets in one shot
|
|
86
|
+
- Passing custom arguments with `args` and `kwargs`
|
|
87
|
+
- Checking for argument presence
|
|
88
|
+
- Combining targets with arguments
|
|
89
|
+
|
|
90
|
+
**Files:**
|
|
91
|
+
|
|
92
|
+
- `03_targets_and_params/single_target.rb` - Processing a single URL with `target!`
|
|
93
|
+
- `03_targets_and_params/multiple_targets.rb` - Processing multiple files with arguments
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
### Chapter 4: Configuration Options
|
|
98
|
+
|
|
99
|
+
Learn how to configure cogs to control their behavior, use different models for different steps, and manage what
|
|
100
|
+
gets displayed during execution.
|
|
101
|
+
|
|
102
|
+
**You'll learn:**
|
|
103
|
+
|
|
104
|
+
- Global vs per-step configuration
|
|
105
|
+
- Pattern-based configuration with regex
|
|
106
|
+
- Display options (`show_prompt!`, `no_show_response!`)
|
|
107
|
+
- Using different models for different steps
|
|
108
|
+
|
|
109
|
+
**Files:**
|
|
110
|
+
|
|
111
|
+
- `04_configuration_options/simple_config.rb` - Basic configuration and overrides
|
|
112
|
+
- `04_configuration_options/control_display_and_temperature.rb` - Configuring multiple parameters for multiple steps
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
### Chapter 5: Control Flow
|
|
117
|
+
|
|
118
|
+
Learn how to create dynamic workflows that adapt based on conditions: skipping steps, handling failures, and checking
|
|
119
|
+
whether steps actually ran.
|
|
120
|
+
|
|
121
|
+
**You'll learn:**
|
|
122
|
+
|
|
123
|
+
- Conditional execution with `skip!`
|
|
124
|
+
- Early termination with `fail!`
|
|
125
|
+
- Checking if cogs ran with `?` accessor
|
|
126
|
+
- The difference between `!`, `?`, and non-bang accessors
|
|
127
|
+
|
|
128
|
+
**Files:**
|
|
129
|
+
|
|
130
|
+
- `05_control_flow/conditional_execution.rb` - Using `skip!` and `?` accessor
|
|
131
|
+
- `05_control_flow/handling_failures.rb` - Command failures with `no_abort_on_failure!`, `no_fail_on_error!`, and
|
|
132
|
+
explicit `fail!`
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
### Chapter 6: Reusable Scopes
|
|
137
|
+
|
|
138
|
+
Learn how to create reusable `execute` scopes that can be called multiple times with different inputs, making your
|
|
139
|
+
workflows more modular and maintainable.
|
|
140
|
+
|
|
141
|
+
**You'll learn:**
|
|
142
|
+
|
|
143
|
+
- Defining named execute scopes
|
|
144
|
+
- Calling scopes with the `call` cog
|
|
145
|
+
- Passing values to scopes
|
|
146
|
+
- Returning values with `outputs`
|
|
147
|
+
- Extracting outputs with `from()`
|
|
148
|
+
|
|
149
|
+
**Files:**
|
|
150
|
+
|
|
151
|
+
- `06_reusable_scopes/basic_scope.rb` - Defining and calling named scopes
|
|
152
|
+
- `06_reusable_scopes/parameterized_scope.rb` - Passing values to scopes
|
|
153
|
+
- `06_reusable_scopes/accessing_scope_outputs.rb` - Using `from()` with a block to access specific cog outputs
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
### Chapter 7: Processing Collections
|
|
158
|
+
|
|
159
|
+
Learn how to process arrays and collections by applying a named execute scope to each item with the `map` cog.
|
|
160
|
+
|
|
161
|
+
**You'll learn:**
|
|
162
|
+
|
|
163
|
+
- Using the `map` cog to process collections
|
|
164
|
+
- Collecting and reducing results with `collect` and `reduce`
|
|
165
|
+
- Parallel execution with `parallel` configuration
|
|
166
|
+
- Accessing specific iteration outputs
|
|
167
|
+
- Working with iteration indices
|
|
168
|
+
|
|
169
|
+
**Files:**
|
|
170
|
+
|
|
171
|
+
- `07_processing_collections/basic_map.rb` - Basic map usage with collect, reduce, and accessing specific iterations
|
|
172
|
+
- `07_processing_collections/parallel_map.rb` - Serial, limited parallel, and unlimited parallel execution
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
### Chapter 8: Iterative Workflows
|
|
177
|
+
|
|
178
|
+
Learn how to create iterative workflows with the `repeat` cog, which executes a scope repeatedly until a condition is
|
|
179
|
+
met, with each iteration's output becoming the next iteration's input.
|
|
180
|
+
|
|
181
|
+
**You'll learn:**
|
|
182
|
+
|
|
183
|
+
- Using the `repeat` cog for iterative transformations
|
|
184
|
+
- How output from one iteration becomes input to the next
|
|
185
|
+
- Breaking out of loops with `break!`
|
|
186
|
+
- Skipping to the next iteration with `next!`
|
|
187
|
+
- Accessing specific iterations and the final result with `from`
|
|
188
|
+
- Processing all iterations with `collect` and `reduce`
|
|
189
|
+
- Using the `outputs` block for controlling iteration values
|
|
190
|
+
|
|
191
|
+
**Files:**
|
|
192
|
+
|
|
193
|
+
- `08_iterative_workflows/basic_repeat.rb` - Iterative text refinement showing basic repeat patterns
|
|
194
|
+
- `08_iterative_workflows/conditional_break.rb` - Number guessing game with conditional termination
|
|
195
|
+
- `08_iterative_workflows/skip_with_next.rb` - Processing numbers while skipping even values
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
### Chapter 9: Asynchronous Cogs
|
|
200
|
+
|
|
201
|
+
Learn how to run cogs asynchronously to improve workflow performance when you have independent tasks
|
|
202
|
+
that can run concurrently.
|
|
203
|
+
|
|
204
|
+
**You'll learn:**
|
|
205
|
+
|
|
206
|
+
- How async cogs differ from parallel map execution
|
|
207
|
+
- Configuring cogs to run asynchronously with `async!`
|
|
208
|
+
- How async execution works and when cogs block
|
|
209
|
+
- Using synchronous cogs as synchronization barriers
|
|
210
|
+
- Real-world patterns for parallel code analysis
|
|
211
|
+
- When to use async cogs vs other parallelization methods
|
|
212
|
+
|
|
213
|
+
**Files:**
|
|
214
|
+
|
|
215
|
+
- `09_async_cogs/basic_async.rb` - Simple async execution with multiple independent agent tasks
|
|
216
|
+
- `09_async_cogs/parallel_analysis.rb` - Parallel code analysis using multiple agents
|
|
217
|
+
- `09_async_cogs/sync_barriers.rb` - Using synchronous cogs to control execution phases
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
Let's get started with
|
|
222
|
+
[Chapter 1](https://github.com/Shopify/roast/blob/edge/dsl/tutorial/01_your_first_workflow/README.md)!
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#: self as Roast::DSL::Workflow
|
|
5
|
+
|
|
6
|
+
config do
|
|
7
|
+
cmd { display! }
|
|
8
|
+
cmd(:alt) { working_directory "/tmp" }
|
|
9
|
+
cmd(:orig) { use_current_working_directory! }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
execute do
|
|
13
|
+
cmd(:cwd) { "echo Current working directory: `pwd`" }
|
|
14
|
+
cmd(:alt) { "echo Alternate working directory: `pwd`" }
|
|
15
|
+
cmd(:orig) { "echo Back to original working directory: `pwd`" }
|
|
16
|
+
end
|
data/exe/roast
CHANGED