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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e40b7ab08d8d4c70d9d884c2c76027ad1426bcdacbae92121b162cc33aa45548
4
- data.tar.gz: 936ab6e40dfbfbcef29f68edbb1a7ea41a895d719782f2125c24f47e71005eeb
3
+ metadata.gz: bc0fd34418eef0f9546d55ab30407287745c6f96664b57b29050eeecfd0751e9
4
+ data.tar.gz: ee09de492428972903a8d69a768b9b661e8668c1aebe51fa5d156e01d41beb41
5
5
  SHA512:
6
- metadata.gz: 7c8de8b0ac1ee9496a869d056fb75de2101b4f1b674bde76c703a913ae749d22c650b44c783e8ebff4127862baa168e4ba1680f1e62bd60574068ba53c88f154
7
- data.tar.gz: f770140fee92dd287d171c52b61d437bbbb3ad7bbe0edc121c68cdb71d4b7628ebbbe1e56946cd8a251ecb991e803e79e62f7d084d59a0efc0efc2ce30b13769
6
+ metadata.gz: c8458190fd1fe4b21e80f1fe038f104c55f40a9bfcd0cef5ef1d66aacf25d783b7ff4c82940adeda90cc60914906e03ef3f83373c97e3880e87e62a7e5da0f17
7
+ data.tar.gz: 34f621a2ebc7f5074d161271e5babf1c2f20e5492c483e8b45a2be217c241d577e46251db19356e59c818d54c1e999b35225e1793f6380d639a04358b94e36eb
@@ -16,7 +16,7 @@ jobs:
16
16
  fail-fast: false
17
17
  matrix:
18
18
  os: [ubuntu]
19
- ruby: ['3.4', 'ruby-head']
19
+ ruby: ['3.4']
20
20
  runs-on: ${{ matrix.os }}-latest
21
21
  continue-on-error: ${{ matrix.ruby == 'ruby-head' }}
22
22
  steps:
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.3] - 2024-05-10
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
@@ -7,6 +7,7 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
7
7
  # Specify your gem's dependencies in roast.gemspec
8
8
  gemspec
9
9
 
10
+ gem "cgi"
10
11
  gem "dotenv"
11
12
  gem "guard-rspec"
12
13
  gem "guard"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- roast-ai (0.1.3)
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 the codebase.
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 two primary ways:
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
- 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.
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,3 @@
1
+ Generate a nicely formatted report based on the following metadata:
2
+
3
+ <%= workflow.output[:extract_info] %>
@@ -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
- Roast::Tools::WriteFile.call(params[:path], params[:content]).tap do |_result|
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?("test/")
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
- Roast::Helpers::Logger.error("😳 Path must start with 'test/' to use the write_file tool\n")
50
- "Error: Path must start with 'test/' to use the write_file tool, try again."
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Roast
4
- VERSION = "0.1.3"
4
+ VERSION = "0.1.5"
5
5
  end
@@ -100,6 +100,10 @@ module Roast
100
100
  raise
101
101
  end
102
102
 
103
+ def workflow
104
+ self
105
+ end
106
+
103
107
  private
104
108
 
105
109
  # Determine the directory where the actual class is defined, not BaseWorkflow
@@ -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
- workflow.output[name] = execute_step(command)
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
- execute_step(step)
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.3
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