roast-ai 0.1.3 → 0.1.5
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 +19 -1
- data/Gemfile +1 -0
- data/Gemfile.lock +3 -1
- data/README.md +19 -5
- data/examples/interpolation/README.md +50 -0
- data/examples/interpolation/analyze_file/prompt.md +13 -0
- data/examples/interpolation/analyze_patterns/prompt.md +27 -0
- data/examples/interpolation/generate_report_for_js/prompt.md +28 -0
- data/examples/interpolation/generate_report_for_rb/prompt.md +3 -0
- data/examples/interpolation/sample.js +48 -0
- data/examples/interpolation/sample.rb +42 -0
- data/examples/interpolation/workflow.md +1 -0
- data/examples/interpolation/workflow.yml +23 -0
- data/lib/roast/tools/write_file.rb +8 -6
- data/lib/roast/version.rb +1 -1
- data/lib/roast/workflow/base_workflow.rb +4 -0
- data/lib/roast/workflow/workflow_executor.rb +33 -2
- metadata +10 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc0fd34418eef0f9546d55ab30407287745c6f96664b57b29050eeecfd0751e9
|
4
|
+
data.tar.gz: ee09de492428972903a8d69a768b9b661e8668c1aebe51fa5d156e01d41beb41
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c8458190fd1fe4b21e80f1fe038f104c55f40a9bfcd0cef5ef1d66aacf25d783b7ff4c82940adeda90cc60914906e03ef3f83373c97e3880e87e62a7e5da0f17
|
7
|
+
data.tar.gz: 34f621a2ebc7f5074d161271e5babf1c2f20e5492c483e8b45a2be217c241d577e46251db19356e59c818d54c1e999b35225e1793f6380d639a04358b94e36eb
|
data/.github/workflows/ci.yaml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -5,7 +5,25 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
-
## [0.1.
|
8
|
+
## [0.1.5] - 2024-05-13
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- Interpolation feature for dynamic workflows using `{{}}` syntax
|
12
|
+
- Support for injecting values from workflow context into step names and commands
|
13
|
+
- Ability to access file metadata and step outputs using interpolation expressions
|
14
|
+
- Examples demonstrating interpolation usage with different file types
|
15
|
+
|
16
|
+
[0.1.5]: https://github.com/Shopify/roast/releases/tag/v0.1.5
|
17
|
+
|
18
|
+
## [0.1.4] - 2024-05-13
|
19
|
+
|
20
|
+
### Fixed
|
21
|
+
- Remove test directory restriction from WriteTool. (Thank you @endoze)
|
22
|
+
|
23
|
+
[0.1.4]: https://github.com/Shopify/roast/releases/tag/v0.1.4
|
24
|
+
|
25
|
+
|
26
|
+
## [0.1.3] - 2024-05-12
|
9
27
|
|
10
28
|
### Fixed
|
11
29
|
- ReadFile tool now handles absolute and relative paths correctly
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
roast-ai (0.1.
|
4
|
+
roast-ai (0.1.5)
|
5
5
|
activesupport (~> 8.0)
|
6
6
|
faraday-retry
|
7
7
|
json-schema
|
@@ -30,6 +30,7 @@ GEM
|
|
30
30
|
base64 (0.2.0)
|
31
31
|
benchmark (0.4.0)
|
32
32
|
bigdecimal (3.1.9)
|
33
|
+
cgi (0.4.2)
|
33
34
|
coderay (1.1.3)
|
34
35
|
concurrent-ruby (1.3.5)
|
35
36
|
connection_pool (2.5.1)
|
@@ -178,6 +179,7 @@ PLATFORMS
|
|
178
179
|
ruby
|
179
180
|
|
180
181
|
DEPENDENCIES
|
182
|
+
cgi
|
181
183
|
dotenv
|
182
184
|
guard
|
183
185
|
guard-rspec
|
data/README.md
CHANGED
@@ -53,15 +53,15 @@ steps:
|
|
53
53
|
- generate_final_report
|
54
54
|
```
|
55
55
|
|
56
|
-
Workflows can include steps that run bash commands (wrap in `$()`) and even simple inlined prompts as a natural language string.
|
56
|
+
Workflows can include steps that run bash commands (wrap in `$()`), use interpolation with `{{}}` syntax, and even simple inlined prompts as a natural language string.
|
57
57
|
|
58
58
|
```yaml
|
59
59
|
steps:
|
60
60
|
- analyze_spec
|
61
61
|
- create_minitest
|
62
62
|
- run_and_improve
|
63
|
-
- $(bundle exec rubocop -A)
|
64
|
-
- Summarize the changes made to
|
63
|
+
- $(bundle exec rubocop -A {{file}})
|
64
|
+
- Summarize the changes made to {{File.basename(file)}}.
|
65
65
|
```
|
66
66
|
|
67
67
|
## How to use Roast
|
@@ -122,7 +122,7 @@ Roast supports several types of steps:
|
|
122
122
|
|
123
123
|
#### Data Flow Between Steps
|
124
124
|
|
125
|
-
Roast handles data flow between steps in
|
125
|
+
Roast handles data flow between steps in three primary ways:
|
126
126
|
|
127
127
|
1. **Conversation Context (Implicit)**: The LLM naturally remembers the entire conversation history, including all previous prompts and responses. In most cases, this is all you need for a step to reference and build upon previous results. This is the preferred approach for most prompt-oriented workflows.
|
128
128
|
|
@@ -131,7 +131,21 @@ Roast handles data flow between steps in two primary ways:
|
|
131
131
|
- You're writing custom output logic
|
132
132
|
- You need to access specific values for presentation or logging
|
133
133
|
|
134
|
-
|
134
|
+
3. **Interpolation (Dynamic)**: You can use `{{expression}}` syntax to inject values from the workflow context directly into step names, commands, or prompt text. For example:
|
135
|
+
```yaml
|
136
|
+
steps:
|
137
|
+
- analyze_file
|
138
|
+
- $(rubocop -A {{file}})
|
139
|
+
- Generate a summary for {{file}}
|
140
|
+
- result_for_{{file}}: store_results
|
141
|
+
```
|
142
|
+
|
143
|
+
Interpolation supports:
|
144
|
+
- Simple variable access: `{{file}}`, `{{resource.target}}`
|
145
|
+
- Access to step outputs: `{{output['previous_step']}}`
|
146
|
+
- Any valid Ruby expression evaluated in the workflow context: `{{File.basename(file)}}`
|
147
|
+
|
148
|
+
For typical AI workflows, the continuous conversation history provides seamless data flow without requiring explicit access to the output hash. Steps can simply refer to previous information in their prompts, and the AI model will use its memory of the conversation to provide context-aware responses. For more dynamic requirements, the interpolation syntax provides a convenient way to inject context-specific values into steps.
|
135
149
|
|
136
150
|
### Command Line Options
|
137
151
|
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Interpolation Example
|
2
|
+
|
3
|
+
This example demonstrates how to use Roast's interpolation feature to create dynamic workflows.
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
The workflow in this example:
|
8
|
+
1. Analyzes a file and extracts its metadata
|
9
|
+
2. Extracts patterns based on the file type
|
10
|
+
3. Dynamically selects a report generation step based on the file extension
|
11
|
+
4. Outputs a completion message using the file's basename
|
12
|
+
|
13
|
+
## Interpolation Examples
|
14
|
+
|
15
|
+
The workflow demonstrates several types of interpolation:
|
16
|
+
|
17
|
+
- `{{ }}` syntax for embedding dynamic values
|
18
|
+
- Access to file metadata via expressions like `{{file_basename}}` and `{{file_ext}}`
|
19
|
+
- Dynamic step selection with `generate_report_for_{{file_ext}}`
|
20
|
+
- Shell command interpolation with `$(echo "Processing completed for file: {{file_basename}}")`
|
21
|
+
|
22
|
+
## Running the Example
|
23
|
+
|
24
|
+
To run this example with a Ruby file:
|
25
|
+
|
26
|
+
```bash
|
27
|
+
roast execute workflow.yml /path/to/some_file.rb
|
28
|
+
```
|
29
|
+
|
30
|
+
Or with a JavaScript file:
|
31
|
+
|
32
|
+
```bash
|
33
|
+
roast execute workflow.yml /path/to/some_file.js
|
34
|
+
```
|
35
|
+
|
36
|
+
The workflow will:
|
37
|
+
1. Extract the file's basename and extension
|
38
|
+
2. Store these in the workflow context
|
39
|
+
3. Dynamically choose a report generator based on file extension
|
40
|
+
4. Create a markdown report file
|
41
|
+
5. Output a completion message with the filename
|
42
|
+
|
43
|
+
## How Interpolation Works
|
44
|
+
|
45
|
+
1. When Roast processes a step name or shell command, it looks for `{{ }}` patterns
|
46
|
+
2. Expressions inside `{{ }}` are evaluated in the workflow's context using Ruby's `instance_eval`
|
47
|
+
3. This allows access to the workflow's variables, methods, and output hash
|
48
|
+
4. The evaluated expressions replace the `{{ }}` patterns in the step name or command
|
49
|
+
|
50
|
+
This makes workflows dynamic and able to respond to different inputs without code changes.
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# File Analysis
|
2
|
+
|
3
|
+
I'm going to analyze the file at: <%= workflow.file %>
|
4
|
+
|
5
|
+
First, let me gather some basic information about the file:
|
6
|
+
|
7
|
+
1. File name: `<%= File.basename(workflow.file) %>`
|
8
|
+
2. File extension: `<%= File.extname(workflow.file).sub('.', '') %>`
|
9
|
+
3. File size: `<%= File.size(workflow.file) %> bytes`
|
10
|
+
|
11
|
+
---
|
12
|
+
|
13
|
+
Can you read the file for me so I can analyze it?
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Extract some patterns about this file and return in json format like this:
|
2
|
+
|
3
|
+
<json>
|
4
|
+
{
|
5
|
+
"code_patterns": {
|
6
|
+
"class_structure": {
|
7
|
+
"name": "Calculator",
|
8
|
+
"instance_variables": ["@memory"],
|
9
|
+
"method_count": 7,
|
10
|
+
"method_types": {
|
11
|
+
"constructor": ["initialize"],
|
12
|
+
"operations": ["add", "subtract", "multiply", "divide"],
|
13
|
+
"accessors": ["memory"],
|
14
|
+
"utility": ["clear"]
|
15
|
+
}
|
16
|
+
},
|
17
|
+
"error_handling": {
|
18
|
+
"techniques": ["conditional raise", "zero check"],
|
19
|
+
"examples": ["raise \"Division by zero!\" if number.zero?"]
|
20
|
+
},
|
21
|
+
"design_patterns": {
|
22
|
+
"state": "Uses instance variable to maintain calculator state",
|
23
|
+
"command": "Each operation method modifies the internal state"
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
27
|
+
</json>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# JavaScript File Report Generation
|
2
|
+
|
3
|
+
I'm generating a report for a JavaScript file:
|
4
|
+
|
5
|
+
- File name: `<%= workflow.output['file_basename'] %>`
|
6
|
+
- Lines: `<%= workflow.output['patterns_found']['lines'] %>`
|
7
|
+
|
8
|
+
## JavaScript-Specific Report
|
9
|
+
|
10
|
+
JavaScript files typically contain functions, classes, and import/export statements. I'll generate a report focused on JavaScript code analysis.
|
11
|
+
|
12
|
+
### Recommendations for JavaScript Code
|
13
|
+
|
14
|
+
1. Follow a consistent style guide (ESLint)
|
15
|
+
2. Use modern JavaScript features (ES6+)
|
16
|
+
3. Prefer const and let over var
|
17
|
+
4. Use async/await for asynchronous code
|
18
|
+
5. Write unit tests with Jest or Mocha
|
19
|
+
|
20
|
+
### Summary
|
21
|
+
|
22
|
+
This JavaScript file has been analyzed and basic metrics have been collected. A more detailed analysis would involve parsing the JavaScript code to extract functions, classes, and import/export statements.
|
23
|
+
|
24
|
+
I'll create a report file with this information.
|
25
|
+
|
26
|
+
I'll use my WriteFile tool to generate the report.
|
27
|
+
|
28
|
+
Can you please write the report to `report_<%= workflow.output['file_basename'] %>.md` in the current directory?
|
@@ -0,0 +1,48 @@
|
|
1
|
+
// Sample JavaScript file for testing interpolation in workflows
|
2
|
+
|
3
|
+
class Calculator {
|
4
|
+
constructor() {
|
5
|
+
this.memory = 0;
|
6
|
+
}
|
7
|
+
|
8
|
+
add(number) {
|
9
|
+
this.memory += number;
|
10
|
+
return this.memory;
|
11
|
+
}
|
12
|
+
|
13
|
+
subtract(number) {
|
14
|
+
this.memory -= number;
|
15
|
+
return this.memory;
|
16
|
+
}
|
17
|
+
|
18
|
+
multiply(number) {
|
19
|
+
this.memory *= number;
|
20
|
+
return this.memory;
|
21
|
+
}
|
22
|
+
|
23
|
+
divide(number) {
|
24
|
+
if (number === 0) {
|
25
|
+
throw new Error("Division by zero!");
|
26
|
+
}
|
27
|
+
this.memory /= number;
|
28
|
+
return this.memory;
|
29
|
+
}
|
30
|
+
|
31
|
+
getMemory() {
|
32
|
+
return this.memory;
|
33
|
+
}
|
34
|
+
|
35
|
+
clear() {
|
36
|
+
this.memory = 0;
|
37
|
+
return this.memory;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
// Example usage
|
42
|
+
if (require.main === module) {
|
43
|
+
const calc = new Calculator();
|
44
|
+
calc.add(10);
|
45
|
+
calc.multiply(2);
|
46
|
+
calc.subtract(5);
|
47
|
+
console.log(`Result: ${calc.getMemory()}`);
|
48
|
+
}
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Sample Ruby file for testing interpolation in workflows
|
4
|
+
|
5
|
+
class Calculator
|
6
|
+
def initialize
|
7
|
+
@memory = 0
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(number)
|
11
|
+
@memory += number
|
12
|
+
end
|
13
|
+
|
14
|
+
def subtract(number)
|
15
|
+
@memory -= number
|
16
|
+
end
|
17
|
+
|
18
|
+
def multiply(number)
|
19
|
+
@memory *= number
|
20
|
+
end
|
21
|
+
|
22
|
+
def divide(number)
|
23
|
+
raise "Division by zero!" if number.zero?
|
24
|
+
|
25
|
+
@memory /= number
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :memory
|
29
|
+
|
30
|
+
def clear
|
31
|
+
@memory = 0
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Example usage
|
36
|
+
if __FILE__ == $PROGRAM_NAME
|
37
|
+
calc = Calculator.new
|
38
|
+
calc.add(10)
|
39
|
+
calc.multiply(2)
|
40
|
+
calc.subtract(5)
|
41
|
+
puts "Result: #{calc.memory}"
|
42
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
You are a good robot.
|
@@ -0,0 +1,23 @@
|
|
1
|
+
name: interpolation_example
|
2
|
+
model: anthropic:claude-3-7-sonnet
|
3
|
+
|
4
|
+
tools:
|
5
|
+
- Roast::Tools::ReadFile
|
6
|
+
- Roast::Tools::WriteFile
|
7
|
+
- Roast::Tools::Grep
|
8
|
+
|
9
|
+
steps:
|
10
|
+
- analyze_file
|
11
|
+
- extract_info: analyze_patterns
|
12
|
+
- generate_report_for_{{File.extname(workflow.file).sub('.', '')}}
|
13
|
+
- '$(echo "Processing completed for file: {{File.basename(workflow.file)}}")'
|
14
|
+
|
15
|
+
analyze_patterns:
|
16
|
+
json: true
|
17
|
+
|
18
|
+
generate_report_for_rb:
|
19
|
+
print_response: true
|
20
|
+
|
21
|
+
generate_report_for_md:
|
22
|
+
print_response: true
|
23
|
+
|
@@ -21,7 +21,9 @@ module Roast
|
|
21
21
|
},
|
22
22
|
content: { type: "string", description: "The content to write to the file" },
|
23
23
|
) do |params|
|
24
|
-
|
24
|
+
restrict_path = params[:params]&.dig("restrict")
|
25
|
+
|
26
|
+
Roast::Tools::WriteFile.call(params[:path], params[:content], restrict_path).tap do |_result|
|
25
27
|
Roast::Helpers::Logger.info(params[:content])
|
26
28
|
end
|
27
29
|
end
|
@@ -29,9 +31,8 @@ module Roast
|
|
29
31
|
end
|
30
32
|
end
|
31
33
|
|
32
|
-
def call(path, content)
|
33
|
-
if path.start_with?(
|
34
|
-
|
34
|
+
def call(path, content, restrict_path = nil)
|
35
|
+
if restrict_path.nil? || restrict_path.empty? || path.start_with?(restrict_path)
|
35
36
|
Roast::Helpers::Logger.info("📝 Writing to file: #{path}\n")
|
36
37
|
|
37
38
|
# Ensure the directory exists
|
@@ -46,8 +47,9 @@ module Roast
|
|
46
47
|
|
47
48
|
"Successfully wrote #{content.lines.count} lines to #{path}"
|
48
49
|
else
|
49
|
-
|
50
|
-
|
50
|
+
restriction_message = "😳 Path must start with '#{restrict_path}' to use the write_file tool\n"
|
51
|
+
Roast::Helpers::Logger.error(restriction_message)
|
52
|
+
"Error: Path must start with '#{restrict_path}' to use the write_file tool, try again."
|
51
53
|
end
|
52
54
|
rescue StandardError => e
|
53
55
|
"Error writing file: #{e.message}".tap do |error_message|
|
data/lib/roast/version.rb
CHANGED
@@ -33,6 +33,25 @@ module Roast
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
+
# Interpolates {{expression}} in a string with values from the workflow context
|
37
|
+
def interpolate(text)
|
38
|
+
return text unless text.is_a?(String) && text.include?("{{") && text.include?("}}")
|
39
|
+
|
40
|
+
# Replace all {{expression}} with their evaluated values
|
41
|
+
text.gsub(/\{\{([^}]+)\}\}/) do |match|
|
42
|
+
expression = Regexp.last_match(1).strip
|
43
|
+
begin
|
44
|
+
# Evaluate the expression in the workflow's context
|
45
|
+
workflow.instance_eval(expression).to_s
|
46
|
+
rescue => e
|
47
|
+
# If evaluation fails, provide a more detailed error message but preserve the original expression
|
48
|
+
error_msg = "Error interpolating {{#{expression}}}: #{e.message}. This variable is not defined in the workflow context. Please define it before using it in a step name."
|
49
|
+
$stderr.puts "ERROR: #{error_msg}"
|
50
|
+
match # Return the original match to preserve it in the string
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
36
55
|
def execute_step(name)
|
37
56
|
start_time = Time.now
|
38
57
|
# For tests, make sure that we handle this gracefully
|
@@ -95,10 +114,16 @@ module Roast
|
|
95
114
|
def execute_hash_step(step)
|
96
115
|
# execute a command and store the output in a variable
|
97
116
|
name, command = step.to_a.flatten
|
117
|
+
|
118
|
+
# Interpolate variable name if it contains {{}}
|
119
|
+
interpolated_name = interpolate(name)
|
120
|
+
|
98
121
|
if command.is_a?(Hash)
|
99
122
|
execute_steps([command])
|
100
123
|
else
|
101
|
-
|
124
|
+
# Interpolate command value
|
125
|
+
interpolated_command = interpolate(command)
|
126
|
+
workflow.output[interpolated_name] = execute_step(interpolated_command)
|
102
127
|
end
|
103
128
|
end
|
104
129
|
|
@@ -110,7 +135,9 @@ module Roast
|
|
110
135
|
end
|
111
136
|
|
112
137
|
def execute_string_step(step)
|
113
|
-
|
138
|
+
# Interpolate any {{}} expressions before executing the step
|
139
|
+
interpolated_step = interpolate(step)
|
140
|
+
execute_step(interpolated_step)
|
114
141
|
end
|
115
142
|
|
116
143
|
def find_and_load_step(step_name)
|
@@ -172,7 +199,11 @@ module Roast
|
|
172
199
|
|
173
200
|
def strip_and_execute(step)
|
174
201
|
if step.match?(/^\$\((.*)\)$/)
|
202
|
+
# Extract the command from the $(command) syntax
|
175
203
|
command = step.strip.match(/^\$\((.*)\)$/)[1]
|
204
|
+
|
205
|
+
# NOTE: We don't need to call interpolate here as it's already been done
|
206
|
+
# in execute_string_step before this method is called
|
176
207
|
%x(#{command})
|
177
208
|
else
|
178
209
|
raise "Missing closing parentheses: #{step}"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: roast-ai
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
@@ -128,6 +128,15 @@ files:
|
|
128
128
|
- examples/grading/workflow.ts+tsx.md
|
129
129
|
- examples/grading/workflow.yml
|
130
130
|
- examples/instrumentation.rb
|
131
|
+
- examples/interpolation/README.md
|
132
|
+
- examples/interpolation/analyze_file/prompt.md
|
133
|
+
- examples/interpolation/analyze_patterns/prompt.md
|
134
|
+
- examples/interpolation/generate_report_for_js/prompt.md
|
135
|
+
- examples/interpolation/generate_report_for_rb/prompt.md
|
136
|
+
- examples/interpolation/sample.js
|
137
|
+
- examples/interpolation/sample.rb
|
138
|
+
- examples/interpolation/workflow.md
|
139
|
+
- examples/interpolation/workflow.yml
|
131
140
|
- examples/rspec_to_minitest/README.md
|
132
141
|
- examples/rspec_to_minitest/analyze_spec/prompt.md
|
133
142
|
- examples/rspec_to_minitest/create_minitest/prompt.md
|