roast-ai 0.1.7 → 0.2.1
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/.github/workflows/ci.yaml +1 -1
- data/CHANGELOG.md +49 -1
- data/CLAUDE.md +20 -0
- data/CLAUDE_NOTES.md +68 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +9 -6
- data/README.md +159 -26
- data/bin/roast +27 -0
- data/docs/ITERATION_SYNTAX.md +147 -0
- data/examples/case_when/README.md +58 -0
- data/examples/case_when/detect_language/prompt.md +16 -0
- data/examples/case_when/workflow.yml +58 -0
- data/examples/conditional/README.md +161 -0
- data/examples/conditional/check_condition/prompt.md +1 -0
- data/examples/conditional/simple_workflow.yml +15 -0
- data/examples/conditional/workflow.yml +23 -0
- data/examples/direct_coerce_syntax/README.md +32 -0
- data/examples/direct_coerce_syntax/workflow.yml +36 -0
- data/examples/dot_notation/README.md +37 -0
- data/examples/dot_notation/workflow.yml +44 -0
- data/examples/exit_on_error/README.md +50 -0
- data/examples/exit_on_error/analyze_lint_output/prompt.md +9 -0
- data/examples/exit_on_error/apply_fixes/prompt.md +2 -0
- data/examples/exit_on_error/workflow.yml +19 -0
- data/examples/grading/workflow.yml +10 -4
- data/examples/iteration/IMPLEMENTATION.md +88 -0
- data/examples/iteration/README.md +68 -0
- data/examples/iteration/analyze_complexity/prompt.md +22 -0
- data/examples/iteration/generate_recommendations/prompt.md +21 -0
- data/examples/iteration/generate_report/prompt.md +129 -0
- data/examples/iteration/implement_fix/prompt.md +25 -0
- data/examples/iteration/prioritize_issues/prompt.md +24 -0
- data/examples/iteration/prompts/analyze_file.md +28 -0
- data/examples/iteration/prompts/generate_summary.md +24 -0
- data/examples/iteration/prompts/update_report.md +29 -0
- data/examples/iteration/prompts/write_report.md +22 -0
- data/examples/iteration/read_file/prompt.md +9 -0
- data/examples/iteration/select_next_issue/prompt.md +25 -0
- data/examples/iteration/simple_workflow.md +39 -0
- data/examples/iteration/simple_workflow.yml +58 -0
- data/examples/iteration/update_fix_count/prompt.md +26 -0
- data/examples/iteration/verify_fix/prompt.md +29 -0
- data/examples/iteration/workflow.yml +42 -0
- data/examples/json_handling/README.md +32 -0
- data/examples/json_handling/workflow.yml +52 -0
- data/examples/openrouter_example/workflow.yml +2 -2
- data/examples/smart_coercion_defaults/README.md +65 -0
- data/examples/smart_coercion_defaults/workflow.yml +44 -0
- data/examples/step_configuration/README.md +87 -0
- data/examples/step_configuration/workflow.yml +60 -0
- data/examples/workflow_generator/README.md +27 -0
- data/examples/workflow_generator/analyze_user_request/prompt.md +34 -0
- data/examples/workflow_generator/create_workflow_files/prompt.md +32 -0
- data/examples/workflow_generator/get_user_input/prompt.md +14 -0
- data/examples/workflow_generator/info_from_roast.rb +22 -0
- data/examples/workflow_generator/workflow.yml +35 -0
- data/lib/roast/errors.rb +9 -0
- data/lib/roast/factories/api_provider_factory.rb +61 -0
- data/lib/roast/helpers/function_caching_interceptor.rb +1 -1
- data/lib/roast/helpers/minitest_coverage_runner.rb +1 -1
- data/lib/roast/helpers/prompt_loader.rb +50 -1
- data/lib/roast/resources/base_resource.rb +7 -0
- data/lib/roast/resources.rb +6 -6
- data/lib/roast/tools/ask_user.rb +40 -0
- data/lib/roast/tools/cmd.rb +1 -1
- data/lib/roast/tools/search_file.rb +1 -1
- data/lib/roast/tools.rb +11 -1
- data/lib/roast/value_objects/api_token.rb +49 -0
- data/lib/roast/value_objects/step_name.rb +39 -0
- data/lib/roast/value_objects/workflow_path.rb +77 -0
- data/lib/roast/value_objects.rb +5 -0
- data/lib/roast/version.rb +1 -1
- data/lib/roast/workflow/api_configuration.rb +61 -0
- data/lib/roast/workflow/base_iteration_step.rb +184 -0
- data/lib/roast/workflow/base_step.rb +44 -27
- data/lib/roast/workflow/base_workflow.rb +76 -73
- data/lib/roast/workflow/case_executor.rb +49 -0
- data/lib/roast/workflow/case_step.rb +82 -0
- data/lib/roast/workflow/command_executor.rb +88 -0
- data/lib/roast/workflow/conditional_executor.rb +50 -0
- data/lib/roast/workflow/conditional_step.rb +59 -0
- data/lib/roast/workflow/configuration.rb +35 -158
- data/lib/roast/workflow/configuration_loader.rb +78 -0
- data/lib/roast/workflow/configuration_parser.rb +13 -248
- data/lib/roast/workflow/context_path_resolver.rb +43 -0
- data/lib/roast/workflow/dot_access_hash.rb +198 -0
- data/lib/roast/workflow/each_step.rb +86 -0
- data/lib/roast/workflow/error_handler.rb +97 -0
- data/lib/roast/workflow/expression_evaluator.rb +78 -0
- data/lib/roast/workflow/expression_utils.rb +36 -0
- data/lib/roast/workflow/file_state_repository.rb +3 -2
- data/lib/roast/workflow/interpolator.rb +34 -0
- data/lib/roast/workflow/iteration_executor.rb +103 -0
- data/lib/roast/workflow/llm_boolean_coercer.rb +55 -0
- data/lib/roast/workflow/output_handler.rb +35 -0
- data/lib/roast/workflow/output_manager.rb +77 -0
- data/lib/roast/workflow/parallel_executor.rb +49 -0
- data/lib/roast/workflow/prompt_step.rb +4 -1
- data/lib/roast/workflow/repeat_step.rb +75 -0
- data/lib/roast/workflow/replay_handler.rb +123 -0
- data/lib/roast/workflow/resource_resolver.rb +77 -0
- data/lib/roast/workflow/session_manager.rb +6 -2
- data/lib/roast/workflow/state_manager.rb +97 -0
- data/lib/roast/workflow/step_executor_coordinator.rb +221 -0
- data/lib/roast/workflow/step_executor_factory.rb +47 -0
- data/lib/roast/workflow/step_executor_registry.rb +79 -0
- data/lib/roast/workflow/step_executors/base_step_executor.rb +23 -0
- data/lib/roast/workflow/step_executors/hash_step_executor.rb +43 -0
- data/lib/roast/workflow/step_executors/parallel_step_executor.rb +54 -0
- data/lib/roast/workflow/step_executors/string_step_executor.rb +29 -0
- data/lib/roast/workflow/step_finder.rb +97 -0
- data/lib/roast/workflow/step_loader.rb +155 -0
- data/lib/roast/workflow/step_orchestrator.rb +45 -0
- data/lib/roast/workflow/step_runner.rb +23 -0
- data/lib/roast/workflow/step_type_resolver.rb +133 -0
- data/lib/roast/workflow/workflow_context.rb +60 -0
- data/lib/roast/workflow/workflow_executor.rb +90 -209
- data/lib/roast/workflow/workflow_initializer.rb +112 -0
- data/lib/roast/workflow/workflow_runner.rb +87 -0
- data/lib/roast/workflow.rb +3 -0
- data/lib/roast.rb +96 -3
- data/roast.gemspec +2 -1
- data/schema/workflow.json +112 -0
- metadata +112 -4
@@ -0,0 +1,58 @@
|
|
1
|
+
name: Ruby Method Counter
|
2
|
+
tools:
|
3
|
+
- Roast::Tools::ReadFile
|
4
|
+
- Roast::Tools::Grep
|
5
|
+
- Roast::Tools::WriteFile
|
6
|
+
- Roast::Tools::CodingAgent
|
7
|
+
|
8
|
+
steps:
|
9
|
+
# Find all Ruby files in the specified directory
|
10
|
+
- find_ruby_files:
|
11
|
+
$(find lib/roast/workflow -type f -name "*.rb")
|
12
|
+
|
13
|
+
# Initialize a report object to store our results
|
14
|
+
- initialize_report:
|
15
|
+
$(echo '{"files_analyzed": 0, "total_methods": 0, "results": []}')
|
16
|
+
|
17
|
+
# Process each file found
|
18
|
+
- each: "{{output['find_ruby_files'].split('\n')}}"
|
19
|
+
as: "current_file"
|
20
|
+
steps:
|
21
|
+
# Read the file content
|
22
|
+
- read_file:
|
23
|
+
prompt: examples/iteration/prompts/analyze_file
|
24
|
+
model: claude-3-haiku-20240307
|
25
|
+
vars:
|
26
|
+
file_path: "{{ current_file }}"
|
27
|
+
|
28
|
+
# Update the report with the analysis result
|
29
|
+
- update_report:
|
30
|
+
prompt: examples/iteration/prompts/update_report
|
31
|
+
model: claude-3-haiku-20240307
|
32
|
+
vars:
|
33
|
+
file_path: "{{ current_file }}"
|
34
|
+
method_count: "{{ output['read_file']['method_count'] }}"
|
35
|
+
current_report: "{{ output['initialize_report'] }}"
|
36
|
+
|
37
|
+
# Generate the summary report after processing all files
|
38
|
+
- report_count:
|
39
|
+
$(echo "{{ output.initialize_report.files_analyzed }}")
|
40
|
+
|
41
|
+
# Repeat to generate the final summary - demonstrates the repeat construct
|
42
|
+
- repeat:
|
43
|
+
steps:
|
44
|
+
- generate_summary:
|
45
|
+
prompt: examples/iteration/prompts/generate_summary
|
46
|
+
model: claude-3-haiku-20240307
|
47
|
+
vars:
|
48
|
+
report_data: "{{ output['initialize_report'] }}"
|
49
|
+
until: "{{true}}"
|
50
|
+
max_iterations: 1
|
51
|
+
|
52
|
+
# Write the report to a file
|
53
|
+
- write_report:
|
54
|
+
prompt: examples/iteration/prompts/write_report
|
55
|
+
model: claude-3-haiku-20240307
|
56
|
+
vars:
|
57
|
+
report_data: "{{ output['initialize_report'] }}"
|
58
|
+
summary: "{{ output['generate_summary']['summary'] }}"
|
@@ -0,0 +1,26 @@
|
|
1
|
+
I'll update the count of fixes that have been successfully applied.
|
2
|
+
|
3
|
+
Current fix count:
|
4
|
+
```
|
5
|
+
{{output.update_fix_count || 0}}
|
6
|
+
```
|
7
|
+
|
8
|
+
Verification result from the previous step:
|
9
|
+
```json
|
10
|
+
{{output.verify_fix}}
|
11
|
+
```
|
12
|
+
|
13
|
+
I'll increment the fix count if the verification was successful or partial, but not if it failed.
|
14
|
+
|
15
|
+
```javascript
|
16
|
+
let currentCount = parseInt({{output.update_fix_count || 0}});
|
17
|
+
let verificationStatus = "{{output.verify_fix.status}}";
|
18
|
+
|
19
|
+
if (verificationStatus === "success" || verificationStatus === "partial") {
|
20
|
+
currentCount += 1;
|
21
|
+
}
|
22
|
+
|
23
|
+
return { fixes_applied: currentCount };
|
24
|
+
```
|
25
|
+
|
26
|
+
This updated count will be used to determine whether we've met our target for the number of fixes to implement.
|
@@ -0,0 +1,29 @@
|
|
1
|
+
I'll verify that the fix implemented in the previous step correctly addresses the identified issue.
|
2
|
+
|
3
|
+
Here are the details of the issue that was fixed:
|
4
|
+
```json
|
5
|
+
{{output.select_next_issue}}
|
6
|
+
```
|
7
|
+
|
8
|
+
And here is the implementation of the fix:
|
9
|
+
```json
|
10
|
+
{{output.implement_fix}}
|
11
|
+
```
|
12
|
+
|
13
|
+
Now I'll read the updated file to verify the changes:
|
14
|
+
```ruby
|
15
|
+
{{read_file(output.select_next_issue.file_path)}}
|
16
|
+
```
|
17
|
+
|
18
|
+
I'll evaluate the fix based on these criteria:
|
19
|
+
1. Does it fully address the identified issue?
|
20
|
+
2. Did it introduce any new issues or regressions?
|
21
|
+
3. Does it maintain the original functionality?
|
22
|
+
4. Does it follow Ruby best practices and style conventions?
|
23
|
+
5. Is it minimal and focused (changing only what was necessary)?
|
24
|
+
|
25
|
+
Based on this evaluation, I'll provide:
|
26
|
+
1. A verification status (success, partial, failure)
|
27
|
+
2. Detailed reasoning for the status
|
28
|
+
3. Any recommendations for further improvements or adjustments
|
29
|
+
4. An overall assessment of the code quality improvement
|
@@ -0,0 +1,42 @@
|
|
1
|
+
name: Code Quality Analyzer
|
2
|
+
tools:
|
3
|
+
- Roast::Tools::ReadFile
|
4
|
+
- Roast::Tools::Grep
|
5
|
+
- Roast::Tools::WriteFile
|
6
|
+
- Roast::Tools::UpdateFiles
|
7
|
+
- Roast::Tools::CodingAgent
|
8
|
+
- Roast::Tools::Cmd
|
9
|
+
|
10
|
+
steps:
|
11
|
+
# Get all Ruby files from the target directory
|
12
|
+
- get_files_to_analyze:
|
13
|
+
$(find {{resource.target}} -name "*.rb" -not -path "*/vendor/*" | grep -v "test")
|
14
|
+
|
15
|
+
# Analyze each file and generate improvement recommendations
|
16
|
+
- each: "{{output['get_files_to_analyze'].split('\n')}}"
|
17
|
+
as: "current_file"
|
18
|
+
steps:
|
19
|
+
- read_file:
|
20
|
+
$(cat {{current_file}})
|
21
|
+
- analyze_complexity
|
22
|
+
- generate_recommendations
|
23
|
+
|
24
|
+
# After analyzing all files, sort issues by priority
|
25
|
+
- prioritize_issues
|
26
|
+
|
27
|
+
# Process the highest priority issues first, until we've addressed a sufficient number
|
28
|
+
# or reached our iteration limit
|
29
|
+
- initialize_fixes:
|
30
|
+
$(echo "0")
|
31
|
+
|
32
|
+
- repeat:
|
33
|
+
steps:
|
34
|
+
- select_next_issue
|
35
|
+
- implement_fix
|
36
|
+
- verify_fix
|
37
|
+
- update_fix_count
|
38
|
+
until: "{{output['update_fix_count']['fixes_applied'] >= 5 || output['select_next_issue']['no_issues_left'] == true}}"
|
39
|
+
max_iterations: 10
|
40
|
+
|
41
|
+
# Generate a summary report of all changes made
|
42
|
+
- generate_report
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# JSON Handling Example
|
2
|
+
|
3
|
+
This example demonstrates how Roast handles JSON responses from LLM steps.
|
4
|
+
|
5
|
+
## Key Features
|
6
|
+
|
7
|
+
1. **JSON Arrays**: When a step has `json: true` and returns an array, the result is `flatten.first` - the first element after flattening the array. This is useful when the LLM returns an array with a single object.
|
8
|
+
|
9
|
+
2. **JSON Objects**: Hash/object responses are maintained as proper Ruby hashes in the workflow output.
|
10
|
+
|
11
|
+
3. **Replay Support**: JSON data structures are properly serialized and deserialized when saving/loading workflow state for replay functionality.
|
12
|
+
|
13
|
+
## Running the Example
|
14
|
+
|
15
|
+
```bash
|
16
|
+
bin/roast examples/json_handling/workflow.yml
|
17
|
+
```
|
18
|
+
|
19
|
+
## How It Works
|
20
|
+
|
21
|
+
1. The `fetch_users` step generates a JSON array of user objects
|
22
|
+
2. The `fetch_metadata` step generates a JSON object with metadata
|
23
|
+
3. The `process_data` step can access the structured data using `{{output.fetch_users}}` and `{{output.fetch_metadata}}`
|
24
|
+
4. The structured data is preserved through the workflow, including during replay scenarios
|
25
|
+
|
26
|
+
## Implementation Details
|
27
|
+
|
28
|
+
When `json: true` is set on a step:
|
29
|
+
- Array responses return `flatten.first` - the first element after flattening
|
30
|
+
- Object responses are preserved as hashes
|
31
|
+
- Non-JSON array responses (when `json: false`) are joined with newlines
|
32
|
+
- The data maintains its structure when saved to and loaded from workflow state files
|
@@ -0,0 +1,52 @@
|
|
1
|
+
name: JSON Data Handling Example
|
2
|
+
api_provider: openai
|
3
|
+
model: gpt-4
|
4
|
+
|
5
|
+
tools:
|
6
|
+
- type: write_file
|
7
|
+
allowed_paths:
|
8
|
+
- examples/json_handling/
|
9
|
+
|
10
|
+
steps:
|
11
|
+
- name: fetch_users
|
12
|
+
prompt: |
|
13
|
+
Generate a JSON array of 3 user objects. Each user should have:
|
14
|
+
- id (number)
|
15
|
+
- name (string)
|
16
|
+
- email (string)
|
17
|
+
- active (boolean)
|
18
|
+
json: true
|
19
|
+
|
20
|
+
- name: fetch_metadata
|
21
|
+
prompt: |
|
22
|
+
Generate a JSON object with metadata about a dataset:
|
23
|
+
- total_records (number)
|
24
|
+
- last_updated (ISO date string)
|
25
|
+
- categories (array of strings)
|
26
|
+
- filters (object with status and sort fields)
|
27
|
+
json: true
|
28
|
+
|
29
|
+
- name: process_data
|
30
|
+
prompt: |
|
31
|
+
Based on the users data: {{output.fetch_users}}
|
32
|
+
And metadata: {{output.fetch_metadata}}
|
33
|
+
|
34
|
+
Create a summary report describing:
|
35
|
+
1. How many active users there are
|
36
|
+
2. The categories available
|
37
|
+
3. When the data was last updated
|
38
|
+
|
39
|
+
- name: save_report
|
40
|
+
tool: write_file
|
41
|
+
path: examples/json_handling/report.txt
|
42
|
+
content: |
|
43
|
+
# JSON Data Processing Report
|
44
|
+
|
45
|
+
## Users Data
|
46
|
+
{{output.fetch_users}}
|
47
|
+
|
48
|
+
## Metadata
|
49
|
+
{{output.fetch_metadata}}
|
50
|
+
|
51
|
+
## Summary
|
52
|
+
{{output.process_data}}
|
@@ -1,7 +1,7 @@
|
|
1
1
|
name: OpenRouter Example
|
2
2
|
api_provider: openrouter
|
3
3
|
api_token: $(echo $OPENROUTER_API_KEY)
|
4
|
-
model:
|
4
|
+
model: google/gemini-2.0-flash-001
|
5
5
|
|
6
6
|
tools:
|
7
7
|
- Roast::Tools::ReadFile
|
@@ -9,4 +9,4 @@ tools:
|
|
9
9
|
|
10
10
|
steps:
|
11
11
|
- analyze_input
|
12
|
-
- generate_response
|
12
|
+
- generate_response
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# Smart Coercion Defaults
|
2
|
+
|
3
|
+
This example demonstrates how Roast applies intelligent defaults for boolean coercion based on the type of expression being evaluated.
|
4
|
+
|
5
|
+
## Default Coercion Rules
|
6
|
+
|
7
|
+
When a step is used in a boolean context (like `if`, `unless`, or `until` conditions) and no explicit `coerce_to` is specified, Roast applies these smart defaults:
|
8
|
+
|
9
|
+
1. **Ruby Expressions** (`{{expression}}`) → Regular boolean coercion (`!!value`)
|
10
|
+
- `nil` and `false` are falsy
|
11
|
+
- Everything else is truthy (including 0, empty arrays, etc.)
|
12
|
+
|
13
|
+
2. **Bash Commands** (`$(command)`) → Exit code interpretation
|
14
|
+
- Exit code 0 = true (success)
|
15
|
+
- Non-zero exit code = false (failure)
|
16
|
+
|
17
|
+
3. **Prompt/Step Names** → LLM boolean interpretation
|
18
|
+
- Analyzes natural language responses for yes/no intent
|
19
|
+
- "Yes", "True", "Affirmative" → true
|
20
|
+
- "No", "False", "Negative" → false
|
21
|
+
|
22
|
+
4. **Non-string Values** → Regular boolean coercion
|
23
|
+
|
24
|
+
## Examples
|
25
|
+
|
26
|
+
### Ruby Expression (Regular Boolean)
|
27
|
+
```yaml
|
28
|
+
- repeat:
|
29
|
+
until: "{{counter >= 5}}" # Uses !! coercion
|
30
|
+
steps:
|
31
|
+
- increment: counter
|
32
|
+
```
|
33
|
+
|
34
|
+
### Bash Command (Exit Code)
|
35
|
+
```yaml
|
36
|
+
- repeat:
|
37
|
+
until: "$(test -f /tmp/done)" # True when file exists (exit 0)
|
38
|
+
steps:
|
39
|
+
- wait: 1
|
40
|
+
```
|
41
|
+
|
42
|
+
### Prompt Response (LLM Boolean)
|
43
|
+
```yaml
|
44
|
+
- if: "Should we continue?" # Interprets "Yes, let's continue" as true
|
45
|
+
then:
|
46
|
+
- proceed: "Continuing..."
|
47
|
+
```
|
48
|
+
|
49
|
+
## Overriding Defaults
|
50
|
+
|
51
|
+
You can always override the default coercion by specifying `coerce_to` directly in the step:
|
52
|
+
|
53
|
+
```yaml
|
54
|
+
- each: "get_items"
|
55
|
+
as: "item"
|
56
|
+
coerce_to: iterable # Override default to split into array
|
57
|
+
steps:
|
58
|
+
- process: "{{item}}"
|
59
|
+
```
|
60
|
+
|
61
|
+
## Supported Coercion Types
|
62
|
+
|
63
|
+
- `boolean` - Standard Ruby truthiness (!! operator)
|
64
|
+
- `llm_boolean` - Natural language yes/no interpretation
|
65
|
+
- `iterable` - Convert to array (splits strings on newlines)
|
@@ -0,0 +1,44 @@
|
|
1
|
+
name: Smart Coercion Defaults Demo
|
2
|
+
description: Demonstrates how different step types get smart boolean coercion defaults
|
3
|
+
|
4
|
+
steps:
|
5
|
+
# Example 1: Ruby expressions default to regular boolean coercion
|
6
|
+
- set_counter:
|
7
|
+
value: 3
|
8
|
+
|
9
|
+
- repeat:
|
10
|
+
until: "{{counter >= 5}}" # Ruby expression defaults to boolean
|
11
|
+
steps:
|
12
|
+
- increment_counter:
|
13
|
+
value: "{{counter + 1}}"
|
14
|
+
- log: "Counter is now {{counter}}"
|
15
|
+
|
16
|
+
# Example 2: Bash commands default to exit code interpretation
|
17
|
+
- check_file_exists:
|
18
|
+
repeat:
|
19
|
+
until: "$(ls /tmp/important_file 2>/dev/null)" # Bash command defaults to exit code
|
20
|
+
max_iterations: 3
|
21
|
+
steps:
|
22
|
+
- create_file: "$(mkdir -p /tmp && touch /tmp/important_file)"
|
23
|
+
- log: "Waiting for file to exist..."
|
24
|
+
|
25
|
+
# Example 3: Prompt/step names default to LLM boolean interpretation
|
26
|
+
- ask_user_ready:
|
27
|
+
prompt: "Are you ready to continue? Please respond yes or no."
|
28
|
+
|
29
|
+
- conditional:
|
30
|
+
if: "ask_user_ready" # Step name defaults to llm_boolean
|
31
|
+
then:
|
32
|
+
- proceed: "Great! Let's continue..."
|
33
|
+
else:
|
34
|
+
- wait: "Okay, take your time."
|
35
|
+
|
36
|
+
# Example 4: Explicit coerce_to overrides smart defaults
|
37
|
+
- get_items:
|
38
|
+
prompt: "List three fruits, one per line"
|
39
|
+
|
40
|
+
- each: "get_items"
|
41
|
+
as: "fruit"
|
42
|
+
coerce_to: iterable # Override default llm_boolean to iterable
|
43
|
+
steps:
|
44
|
+
- process_fruit: "Processing {{fruit}}"
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# Step Configuration Example
|
2
|
+
|
3
|
+
This example demonstrates how to configure various step types in Roast workflows, including:
|
4
|
+
- Inline prompts
|
5
|
+
- Iterator steps (each/repeat)
|
6
|
+
- Regular steps
|
7
|
+
|
8
|
+
## Configuration Options
|
9
|
+
|
10
|
+
All step types support the following configuration options:
|
11
|
+
|
12
|
+
- `model`: The AI model to use (e.g., "gpt-4o", "claude-3-opus")
|
13
|
+
- `loop`: Whether to enable auto-looping (true/false)
|
14
|
+
- `print_response`: Whether to print the response to stdout (true/false)
|
15
|
+
- `json`: Whether to expect a JSON response (true/false)
|
16
|
+
- `params`: Additional parameters to pass to the model (e.g., temperature, max_tokens)
|
17
|
+
- `coerce_to`: How to convert the step result (options: "boolean", "llm_boolean", "iterable")
|
18
|
+
|
19
|
+
## Configuration Precedence
|
20
|
+
|
21
|
+
1. **Step-specific configuration** takes highest precedence
|
22
|
+
2. **Global configuration** (defined at the workflow level) applies to all steps without specific configuration
|
23
|
+
3. **Default values** are used when no configuration is provided
|
24
|
+
|
25
|
+
## Inline Prompt Configuration
|
26
|
+
|
27
|
+
Inline prompts can be configured in two ways:
|
28
|
+
|
29
|
+
### As a top-level key:
|
30
|
+
```yaml
|
31
|
+
analyze the code:
|
32
|
+
model: gpt-4o
|
33
|
+
loop: false
|
34
|
+
print_response: true
|
35
|
+
```
|
36
|
+
|
37
|
+
### Inline in the steps array:
|
38
|
+
```yaml
|
39
|
+
steps:
|
40
|
+
- suggest improvements:
|
41
|
+
model: claude-3-opus
|
42
|
+
params:
|
43
|
+
temperature: 0.9
|
44
|
+
```
|
45
|
+
|
46
|
+
## Iterator Configuration
|
47
|
+
|
48
|
+
Both `each` and `repeat` steps support configuration:
|
49
|
+
|
50
|
+
```yaml
|
51
|
+
each:
|
52
|
+
each: "{{files}}"
|
53
|
+
as: file
|
54
|
+
model: gpt-3.5-turbo
|
55
|
+
loop: false
|
56
|
+
steps:
|
57
|
+
- process {{file}}
|
58
|
+
|
59
|
+
repeat:
|
60
|
+
repeat: true
|
61
|
+
until: "{{done}}"
|
62
|
+
model: gpt-4o
|
63
|
+
print_response: true
|
64
|
+
steps:
|
65
|
+
- check status
|
66
|
+
```
|
67
|
+
|
68
|
+
## Coercion Types
|
69
|
+
|
70
|
+
The `coerce_to` option allows you to convert step results to specific types:
|
71
|
+
|
72
|
+
- **`boolean`**: Converts any value to true/false (nil, false, empty string → false; everything else → true)
|
73
|
+
- **`llm_boolean`**: Interprets natural language responses as boolean (e.g., "Yes", "Definitely!" → true; "No", "Not yet" → false)
|
74
|
+
- **`iterable`**: Ensures the result can be iterated over (splits strings by newlines if needed)
|
75
|
+
|
76
|
+
This is particularly useful when:
|
77
|
+
- Using steps in conditional contexts (if/unless)
|
78
|
+
- Using steps as conditions in repeat loops
|
79
|
+
- Processing step results in each loops
|
80
|
+
|
81
|
+
## Running the Example
|
82
|
+
|
83
|
+
```bash
|
84
|
+
bin/roast examples/step_configuration/workflow.yml
|
85
|
+
```
|
86
|
+
|
87
|
+
Note: This example is for demonstration purposes and shows the configuration syntax. You'll need to adapt it to your specific use case with appropriate prompts and logic.
|
@@ -0,0 +1,60 @@
|
|
1
|
+
name: Step Configuration Example
|
2
|
+
description: Demonstrates how to configure various step types including inline prompts, iterators, and regular steps
|
3
|
+
|
4
|
+
# Global configuration that applies to all steps
|
5
|
+
model: openai/gpt-4o-mini
|
6
|
+
loop: true
|
7
|
+
|
8
|
+
# Configuration for specific steps
|
9
|
+
summarize the code:
|
10
|
+
model: claude-3-opus # Override global model
|
11
|
+
loop: false # Disable auto-loop for this step
|
12
|
+
print_response: true # Print the response
|
13
|
+
json: false # Don't expect JSON response
|
14
|
+
params:
|
15
|
+
temperature: 0.7 # Custom temperature
|
16
|
+
|
17
|
+
analyze complexity:
|
18
|
+
model: gpt-4o
|
19
|
+
json: true # Expect JSON response
|
20
|
+
params:
|
21
|
+
temperature: 0.2 # Lower temperature for more consistent analysis
|
22
|
+
|
23
|
+
# Iterator step configuration
|
24
|
+
each:
|
25
|
+
each: "{{files}}"
|
26
|
+
as: file
|
27
|
+
model: gpt-3.5-turbo # Use a faster model for iteration
|
28
|
+
loop: false # Don't loop on each iteration
|
29
|
+
steps:
|
30
|
+
- process {{file}}
|
31
|
+
|
32
|
+
repeat:
|
33
|
+
repeat: true
|
34
|
+
until: "are all tests passing" # This will be evaluated as a step
|
35
|
+
max_iterations: 5
|
36
|
+
model: gpt-4o
|
37
|
+
print_response: true
|
38
|
+
coerce_to: llm_boolean # Interpret natural language response as boolean
|
39
|
+
steps:
|
40
|
+
- run tests
|
41
|
+
- fix failing tests
|
42
|
+
|
43
|
+
# Step used in boolean context
|
44
|
+
are all tests passing:
|
45
|
+
model: gpt-4o
|
46
|
+
coerce_to: llm_boolean # Convert "Yes, all tests are passing!" to true
|
47
|
+
params:
|
48
|
+
temperature: 0.2
|
49
|
+
|
50
|
+
# Steps can mix configured and unconfigured inline prompts
|
51
|
+
steps:
|
52
|
+
- list all source files # Uses global configuration
|
53
|
+
- summarize the code # Uses step-specific configuration
|
54
|
+
- analyze complexity # Uses step-specific configuration
|
55
|
+
- suggest improvements: # Step-specific configuration inline
|
56
|
+
model: claude-3-opus
|
57
|
+
params:
|
58
|
+
temperature: 0.9
|
59
|
+
- each # Iterator with configuration
|
60
|
+
- repeat # Iterator with configuration
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Workflow Generator
|
2
|
+
|
3
|
+
This workflow generates new Roast workflows based on user descriptions. It's the engine behind the "New from prompt" option in `roast init`.
|
4
|
+
|
5
|
+
## How It Works
|
6
|
+
|
7
|
+
The workflow generator takes a user description and workflow name, then:
|
8
|
+
|
9
|
+
1. **Analyzes the request** - Understands what type of workflow is needed, what steps are required, and what tools should be used
|
10
|
+
2. **Generates structure** - Creates a complete workflow configuration including YAML and step prompts
|
11
|
+
3. **Creates files** - Writes all the necessary files and directories to disk
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
This workflow is typically invoked automatically by the `roast init` command, but can also be run directly:
|
16
|
+
|
17
|
+
```bash
|
18
|
+
# Run the generator workflow
|
19
|
+
roast execute examples/workflow_generator/workflow.yml
|
20
|
+
```
|
21
|
+
|
22
|
+
## Generated Output
|
23
|
+
|
24
|
+
The workflow creates a new directory with:
|
25
|
+
- `workflow.yml` - Main workflow configuration
|
26
|
+
- `step_name/prompt.md` - Individual step prompts
|
27
|
+
- `README.md` - Documentation for the generated workflow
|
@@ -0,0 +1,34 @@
|
|
1
|
+
You are an assistant that analyzes user requests to understand what kind of workflow they want to create.
|
2
|
+
|
3
|
+
Based on the user input from the previous step:
|
4
|
+
<%= workflow.output["get_user_input"] %>
|
5
|
+
|
6
|
+
Roast information from previous step:
|
7
|
+
<%= workflow.output["info_from_roast"] %>
|
8
|
+
|
9
|
+
First, explore existing workflow examples in the examples directory to understand common patterns and structures. Look for ones that may be related to the user's intention.
|
10
|
+
|
11
|
+
Then analyze the user's request and determine:
|
12
|
+
|
13
|
+
1. **Required Steps**: Break down the workflow into logical steps. Each step should be a discrete task that can be accomplished with an AI prompt.
|
14
|
+
|
15
|
+
2. **Tools Needed**: What Roast tools will be needed? Base this on the actual tools you read from info provided above.
|
16
|
+
|
17
|
+
3. **Target Strategy**: Will this workflow:
|
18
|
+
- Process specific files (needs target configuration)
|
19
|
+
- Be targetless (works without specific input files)
|
20
|
+
- Use shell commands to find targets dynamically
|
21
|
+
|
22
|
+
4. **Model Requirements**: Should this use a specific model, or is the default (gpt-4o-mini) sufficient?
|
23
|
+
|
24
|
+
Respond with a structured analysis in this format:
|
25
|
+
|
26
|
+
```
|
27
|
+
STEPS: [list of 3-5 logical steps]
|
28
|
+
TOOLS: [list of required tools]
|
29
|
+
TARGET_STRATEGY: [targetless/files/dynamic]
|
30
|
+
MODEL: [model recommendation]
|
31
|
+
COMPLEXITY: [simple/moderate/complex]
|
32
|
+
```
|
33
|
+
|
34
|
+
Be specific and actionable in your analysis.
|
@@ -0,0 +1,32 @@
|
|
1
|
+
You are an assistant that creates the actual workflow files in the filesystem.
|
2
|
+
|
3
|
+
Based on the user input:
|
4
|
+
<%= workflow.output["get_user_input"] %>
|
5
|
+
|
6
|
+
And the generated workflow structure from the previous step:
|
7
|
+
<%= workflow.output["analyze_user_request"] %>
|
8
|
+
|
9
|
+
And info from roast:
|
10
|
+
<%= workflow.output["info_from_roast"] %>
|
11
|
+
|
12
|
+
Your task is to create all the necessary files and directories for the workflow.
|
13
|
+
|
14
|
+
Extract the workflow name from the user input JSON and create the workflow in the current directory under that folder name.
|
15
|
+
|
16
|
+
Steps to complete:
|
17
|
+
|
18
|
+
1. **Create the main directory**: Use Cmd to create the "{{ workflow_name }}" directory
|
19
|
+
2. **Create step directories**: Create subdirectories for each workflow step
|
20
|
+
3. **Create workflow.yml**: Write the main workflow configuration file
|
21
|
+
4. **Create step prompt files**: Write each step's prompt.md file
|
22
|
+
5. **Create README.md**: Generate a helpful README explaining the workflow
|
23
|
+
|
24
|
+
When writing files, extract the content from the structured response and write each file separately.
|
25
|
+
|
26
|
+
Important notes:
|
27
|
+
- Make sure all directories exist before writing files to them
|
28
|
+
- Follow the exact structure specified in the previous step
|
29
|
+
- Include helpful comments in the workflow.yml file
|
30
|
+
- Make the README.md informative and include usage instructions
|
31
|
+
|
32
|
+
At the end, confirm that all files have been created by listing the directory structure.
|
@@ -0,0 +1,14 @@
|
|
1
|
+
You need to collect user input for generating a new workflow.
|
2
|
+
|
3
|
+
Step 1: Ask for the workflow description using ask_user tool with prompt: "What should your workflow do?"
|
4
|
+
|
5
|
+
Step 2: Ask for the workflow name using ask_user tool with prompt: "Enter workflow directory name:"
|
6
|
+
|
7
|
+
Step 3: Return the result in JSON format:
|
8
|
+
|
9
|
+
<json>
|
10
|
+
{
|
11
|
+
"user_description": "what the user wants the workflow to do",
|
12
|
+
"workflow_name": "directory_name_provided_by_user"
|
13
|
+
}
|
14
|
+
</json>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class InfoFromRoast < Roast::Workflow::BaseStep
|
4
|
+
def call
|
5
|
+
examples_path = File.join(Roast::ROOT, "examples")
|
6
|
+
tools_path = File.join(Roast::ROOT, "lib", "roast", "tools")
|
7
|
+
|
8
|
+
# Get list of available tools
|
9
|
+
available_tools = Dir.entries(tools_path)
|
10
|
+
.select { |file| file.end_with?(".rb") }
|
11
|
+
.map { |file| file.gsub(".rb", "") }
|
12
|
+
.reject { |tool| tool == "." || tool == ".." }
|
13
|
+
.sort
|
14
|
+
|
15
|
+
{
|
16
|
+
examples_directory: examples_path,
|
17
|
+
tools_directory: tools_path,
|
18
|
+
available_tools: available_tools,
|
19
|
+
message: "Examples directory and available tools provided for analysis step",
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|