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.
- 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 +18 -18
- data/Gemfile.lock +46 -57
- 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/demo/Gemfile +4 -0
- data/dsl/demo/Gemfile.lock +120 -0
- data/dsl/demo/cogs/local.rb +15 -0
- data/dsl/demo/simple_external_cog.rb +17 -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/plugin-gem-example/.gitignore +8 -0
- data/dsl/plugin-gem-example/Gemfile +13 -0
- data/dsl/plugin-gem-example/Gemfile.lock +178 -0
- data/dsl/plugin-gem-example/lib/other.rb +17 -0
- data/dsl/plugin-gem-example/lib/plugin_gem_example.rb +5 -0
- data/dsl/plugin-gem-example/lib/simple.rb +15 -0
- data/dsl/plugin-gem-example/lib/version.rb +10 -0
- data/dsl/plugin-gem-example/plugin-gem-example.gemspec +28 -0
- data/dsl/prompts/simple_prompt.md.erb +3 -0
- data/dsl/prototype.rb +10 -4
- 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 +26 -0
- data/dsl/simple_repeat.rb +29 -0
- data/dsl/skip.rb +36 -0
- data/dsl/step_communication.rb +10 -5
- 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 +280 -4
- data/lib/roast/dsl/cog/input.rb +73 -0
- data/lib/roast/dsl/cog/output.rb +313 -0
- data/lib/roast/dsl/cog/registry.rb +71 -0
- data/lib/roast/dsl/cog/stack.rb +3 -2
- data/lib/roast/dsl/cog/store.rb +11 -8
- data/lib/roast/dsl/cog.rb +108 -31
- data/lib/roast/dsl/cog_input_context.rb +44 -0
- data/lib/roast/dsl/cog_input_manager.rb +156 -0
- 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 +81 -0
- data/lib/roast/dsl/cogs/cmd.rb +291 -27
- data/lib/roast/dsl/cogs/ruby.rb +171 -0
- data/lib/roast/dsl/command_runner.rb +191 -0
- data/lib/roast/dsl/config_context.rb +2 -47
- data/lib/roast/dsl/config_manager.rb +143 -0
- data/lib/roast/dsl/control_flow.rb +41 -0
- data/lib/roast/dsl/execution_context.rb +9 -0
- data/lib/roast/dsl/execution_manager.rb +267 -0
- 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 +123 -0
- 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 +60 -22
- data/{roast.gemspec → roast-ai.gemspec} +10 -13
- data/sorbet/config +1 -0
- 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/marcel@1.1.0.rbi +239 -0
- data/sorbet/rbi/gems/metrics@0.15.0.rbi +9 -0
- data/sorbet/rbi/gems/ruby_llm@1.8.2.rbi +5703 -0
- data/sorbet/rbi/gems/traces@0.18.2.rbi +9 -0
- data/sorbet/rbi/shims/lib/roast/dsl/cog_input_context.rbi +1197 -0
- data/sorbet/rbi/shims/lib/roast/dsl/config_context.rbi +314 -2
- data/sorbet/rbi/shims/lib/roast/dsl/execution_context.rbi +498 -0
- data/sorbet/tapioca/config.yml +6 -0
- data/sorbet/tapioca/require.rb +2 -0
- metadata +198 -34
- data/dsl/less_simple.rb +0 -112
- data/dsl/simple.rb +0 -8
- data/lib/roast/dsl/cog_execution_context.rb +0 -29
- data/lib/roast/dsl/cogs/graph.rb +0 -53
- data/lib/roast/dsl/cogs.rb +0 -65
- data/lib/roast/dsl/executor.rb +0 -82
- data/lib/roast/dsl/workflow_execution_context.rb +0 -47
- 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.18.rbi +0 -5659
- data/sorbet/rbi/gems/rbs-inline@0.12.0.rbi +0 -2170
- data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +0 -435
- data/sorbet/rbi/gems/yard@0.9.37.rbi +0 -18492
- 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
|
data/dsl/step_communication.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
# typed:
|
|
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
|
-
|
|
15
|
-
first_line = cmd(:ls).
|
|
16
|
-
|
|
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
|
data/dsl/temperature.rb
ADDED
|
@@ -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
|