roast-ai 0.1.5 → 0.1.6
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 +22 -0
- data/CLAUDE.md +118 -8
- data/Gemfile +1 -2
- data/Gemfile.lock +7 -22
- data/README.md +24 -2
- data/Rakefile +5 -1
- data/examples/interpolation/analyze_file/prompt.md +1 -13
- data/examples/interpolation/generate_report_for_js/prompt.md +2 -27
- data/examples/interpolation/generate_report_for_rb/prompt.md +1 -1
- data/examples/interpolation/workflow.yml +1 -3
- data/examples/openrouter_example/README.md +48 -0
- data/examples/openrouter_example/analyze_input/prompt.md +16 -0
- data/examples/openrouter_example/generate_response/prompt.md +9 -0
- data/examples/openrouter_example/workflow.yml +12 -0
- data/lib/roast/initializers.rb +39 -0
- data/lib/roast/tools/search_file.rb +22 -15
- data/lib/roast/version.rb +1 -1
- data/lib/roast/workflow/configuration.rb +28 -1
- data/lib/roast/workflow/configuration_parser.rb +10 -21
- data/lib/roast.rb +6 -0
- metadata +6 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9bdaada020320af4c7fead86184a7cd0b1c06a89e88bc15ac424b31f4b449fb6
|
4
|
+
data.tar.gz: 3f83f410a1f21992de59b19b8e2f0ea402b9fe50b314b82514723cf20f9a599f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c6dfdd57104e363735a6e24bf5beb43a042340f582f94d8126c3f8a0d30dd92b031c5201a7d3d136bfcd3b7095a51c49329b08c07c5c19544240b18e20ad9948
|
7
|
+
data.tar.gz: 02aec8d620c3fe0c57d0d4e06790a7513a4a1aa44f68462721b6aa7da3f22f377598b4c00453e4ec825b0adcac3c335d470f945599d39571edb67e8a380597ac
|
data/.github/workflows/ci.yaml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,28 @@ 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.6] - 2024-05-15
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- Support for OpenRouter as an API provider
|
12
|
+
- `api_provider` configuration option allowing choice between OpenAI and OpenRouter
|
13
|
+
- Added separate CI rake task for improved build pipeline
|
14
|
+
- Version command to check current Roast version
|
15
|
+
- Walking up to home folder for config root
|
16
|
+
- Improved initializer support for better project configuration
|
17
|
+
|
18
|
+
### Changed
|
19
|
+
- Enhanced search tool to work with globs for more flexible searches
|
20
|
+
- Improved error handling in configuration and initializers
|
21
|
+
- Fixed and simplified interpolation examples
|
22
|
+
|
23
|
+
### Fixed
|
24
|
+
- Better error messages for search file tool
|
25
|
+
- Improved initializer loading and error handling
|
26
|
+
- Fixed tests for nested .roast folders
|
27
|
+
|
28
|
+
[0.1.6]: https://github.com/Shopify/roast/compare/v0.1.5...v0.1.6
|
29
|
+
|
8
30
|
## [0.1.5] - 2024-05-13
|
9
31
|
|
10
32
|
### Added
|
data/CLAUDE.md
CHANGED
@@ -7,16 +7,15 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|
7
7
|
|
8
8
|
## Commands
|
9
9
|
|
10
|
-
- Build: `bundle exec rake build`
|
11
|
-
- Test all: `bundle exec rspec`
|
12
|
-
- Run single test: `bundle exec rspec spec/path/to/test_file.rb`
|
13
|
-
- Lint: `bundle exec rubocop -A`
|
14
10
|
- Default (tests + lint): `bundle exec rake`
|
15
|
-
-
|
11
|
+
- Test all: `bundle exec test`
|
12
|
+
- Run single test: `bundle exec ruby -Itest test/path/to/test_file.rb`
|
13
|
+
- Lint: `bundle exec rubocop`
|
14
|
+
- Lint (with autocorrect, preferred): `bundle exec rubocop -A`
|
16
15
|
|
17
16
|
## Tech stack
|
18
|
-
- `
|
19
|
-
- Testing: Use
|
17
|
+
- `thor` and `cli-ui` for the CLI tool
|
18
|
+
- Testing: Use Minitest, VCR for HTTP mocking, test files named with `_test.rb` suffix
|
20
19
|
|
21
20
|
## Code Style Guidelines
|
22
21
|
|
@@ -30,4 +29,115 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|
30
29
|
- Define class methods inside `class << self; end` declarations.
|
31
30
|
- Add runtime dependencies to `roast.gemspec`.
|
32
31
|
- Add development dependencies to `Gemfile`.
|
33
|
-
- Don't ever test private methods directly. Specs should test behavior, not implementation.
|
32
|
+
- Don't ever test private methods directly. Specs should test behavior, not implementation.
|
33
|
+
|
34
|
+
## Git Workflow Practices
|
35
|
+
|
36
|
+
1. **Amending Commits**:
|
37
|
+
- Use `git commit --amend --no-edit` to add staged changes to the last commit without changing the commit message
|
38
|
+
- This is useful for incorporating small fixes or changes that belong with the previous commit
|
39
|
+
- Be careful when amending commits that have already been pushed, as it will require a force push
|
40
|
+
|
41
|
+
2. **Force Pushing Safety**:
|
42
|
+
- Always use `git push --force-with-lease` rather than `git push --force` when pushing amended commits
|
43
|
+
- This prevents accidentally overwriting remote changes made by others that you haven't pulled yet
|
44
|
+
- It's a safer alternative that respects collaborative work environments
|
45
|
+
|
46
|
+
4. **PR Management**:
|
47
|
+
- Pay attention to linting results before pushing to avoid CI failures
|
48
|
+
|
49
|
+
## GitHub API Commands
|
50
|
+
To get comments from a Pull Request using the GitHub CLI:
|
51
|
+
|
52
|
+
```bash
|
53
|
+
# Get review comments from a PR
|
54
|
+
gh api repos/Shopify/roast/pulls/{pr_number}/comments
|
55
|
+
|
56
|
+
# Get issue-style comments
|
57
|
+
gh api repos/Shopify/roast/issues/{pr_number}/comments
|
58
|
+
|
59
|
+
# Filter comments from a specific user using jq
|
60
|
+
gh api repos/Shopify/roast/pulls/{pr_number}/comments | jq '.[] | select(.user.login == "username")'
|
61
|
+
|
62
|
+
# Get only the comment content
|
63
|
+
gh api repos/Shopify/roast/pulls/{pr_number}/comments | jq '.[].body'
|
64
|
+
```
|
65
|
+
|
66
|
+
### Creating and Managing Issues via API
|
67
|
+
|
68
|
+
```bash
|
69
|
+
# Create a new issue
|
70
|
+
gh api repos/Shopify/roast/issues -X POST -F title="Issue Title" -F body="Issue description"
|
71
|
+
|
72
|
+
# Update an existing issue
|
73
|
+
gh api repos/Shopify/roast/issues/{issue_number} -X PATCH -F body="Updated description"
|
74
|
+
|
75
|
+
# Add a comment to an issue
|
76
|
+
gh api repos/Shopify/roast/issues/{issue_number}/comments -X POST -F body="Comment text"
|
77
|
+
```
|
78
|
+
|
79
|
+
### Creating and Managing Pull Requests
|
80
|
+
|
81
|
+
```bash
|
82
|
+
# Create a new PR with a detailed description using heredoc
|
83
|
+
gh pr create --title "PR Title" --body "$(cat <<'EOF'
|
84
|
+
## Summary
|
85
|
+
|
86
|
+
Detailed description here...
|
87
|
+
|
88
|
+
## Testing
|
89
|
+
|
90
|
+
Testing instructions here...
|
91
|
+
EOF
|
92
|
+
)"
|
93
|
+
|
94
|
+
# Update an existing PR description
|
95
|
+
gh pr edit {pr_number} --body "$(cat <<'EOF'
|
96
|
+
Updated PR description...
|
97
|
+
EOF
|
98
|
+
)"
|
99
|
+
|
100
|
+
# Check PR details
|
101
|
+
gh pr view {pr_number}
|
102
|
+
|
103
|
+
# View PR diff
|
104
|
+
gh pr diff {pr_number}
|
105
|
+
```
|
106
|
+
|
107
|
+
#### Formatting Tips for GitHub API
|
108
|
+
1. Use literal newlines in the body text instead of `\n` escape sequences
|
109
|
+
2. When formatting is stripped (like backticks), use alternatives:
|
110
|
+
- **Bold text** instead of `code formatting`
|
111
|
+
- Add a follow-up comment with proper formatting
|
112
|
+
3. For complex issues, create the basic issue first, then enhance with formatted comments
|
113
|
+
4. Always verify the formatting in the created content
|
114
|
+
5. Use raw JSON for complex formatting requirements:
|
115
|
+
```bash
|
116
|
+
gh api repos/Shopify/roast/issues -X POST --raw-field '{"title":"Issue Title","body":"Complex **formatting** with `code` and lists:\n\n1. Item one\n2. Item two"}'
|
117
|
+
```
|
118
|
+
|
119
|
+
## PR Review Best Practices
|
120
|
+
1. **Always provide your honest opinion about the PR** - be candid about both strengths and concerns
|
121
|
+
2. Give a clear assessment of risks, architectural implications, and potential future issues
|
122
|
+
3. Don't be afraid to point out potential problems even in otherwise good PRs
|
123
|
+
4. When reviewing feature flag removal PRs, carefully inspect control flow changes, not just code branch removals
|
124
|
+
5. Pay special attention to control flow modifiers like `next`, `return`, and `break` which affect iteration behavior
|
125
|
+
6. Look for variable scope issues, especially for variables that persist across loop iterations
|
126
|
+
7. Analyze how code behavior changes in all cases, not just how code structure changes
|
127
|
+
8. Be skeptical of seemingly simple changes that simply remove conditional branches
|
128
|
+
9. When CI checks fail, look for subtle logic inversions or control flow changes
|
129
|
+
10. Examine every file changed in a PR with local code for context, focusing on both what's removed and what remains
|
130
|
+
11. Verify variable initialization, modification, and usage patterns remain consistent after refactoring
|
131
|
+
12. **Never try to directly check out PR branches** - instead, compare PR changes against the existing local codebase
|
132
|
+
13. Understand the broader system architecture to identify potential impacts beyond the changed files
|
133
|
+
14. Look at both the "before" and "after" state of the code when evaluating changes, not just the diff itself
|
134
|
+
15. Consider how the changes will interact with other components that depend on the modified code
|
135
|
+
16. Run searches or examine related files even if they're not directly modified by the PR
|
136
|
+
17. Look for optimization opportunities, especially in frequently-called methods:
|
137
|
+
- Unnecessary object creation in loops
|
138
|
+
- Redundant collection transformations
|
139
|
+
- Inefficient filtering methods that create temporary collections
|
140
|
+
18. Prioritize code readability while encouraging performance optimizations:
|
141
|
+
- Avoid premature optimization outside of hot paths
|
142
|
+
- Consider the tradeoff between readability and performance
|
143
|
+
- Suggest optimizations that improve both clarity and performance
|
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.6)
|
5
5
|
activesupport (~> 8.0)
|
6
6
|
faraday-retry
|
7
7
|
json-schema
|
@@ -37,7 +37,6 @@ GEM
|
|
37
37
|
crack (1.0.0)
|
38
38
|
bigdecimal
|
39
39
|
rexml
|
40
|
-
diff-lcs (1.6.1)
|
41
40
|
dotenv (3.1.8)
|
42
41
|
drb (2.2.1)
|
43
42
|
event_stream_parser (1.0.0)
|
@@ -53,6 +52,7 @@ GEM
|
|
53
52
|
faraday (~> 2.0)
|
54
53
|
ffi (1.17.2)
|
55
54
|
ffi (1.17.2-arm64-darwin)
|
55
|
+
ffi (1.17.2-x86_64-linux-gnu)
|
56
56
|
formatador (1.1.0)
|
57
57
|
guard (2.19.1)
|
58
58
|
formatador (>= 0.2.4)
|
@@ -66,10 +66,9 @@ GEM
|
|
66
66
|
shellany (~> 0.0)
|
67
67
|
thor (>= 0.18.1)
|
68
68
|
guard-compat (1.2.1)
|
69
|
-
guard-
|
70
|
-
guard (~> 2
|
71
|
-
|
72
|
-
rspec (>= 2.99.0, < 4.0)
|
69
|
+
guard-minitest (2.4.6)
|
70
|
+
guard-compat (~> 1.2)
|
71
|
+
minitest (>= 3.0)
|
73
72
|
hashdiff (1.1.2)
|
74
73
|
i18n (1.14.7)
|
75
74
|
concurrent-ruby (~> 1.0)
|
@@ -123,19 +122,6 @@ GEM
|
|
123
122
|
ffi (~> 1.0)
|
124
123
|
regexp_parser (2.10.0)
|
125
124
|
rexml (3.4.1)
|
126
|
-
rspec (3.13.0)
|
127
|
-
rspec-core (~> 3.13.0)
|
128
|
-
rspec-expectations (~> 3.13.0)
|
129
|
-
rspec-mocks (~> 3.13.0)
|
130
|
-
rspec-core (3.13.3)
|
131
|
-
rspec-support (~> 3.13.0)
|
132
|
-
rspec-expectations (3.13.3)
|
133
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
134
|
-
rspec-support (~> 3.13.0)
|
135
|
-
rspec-mocks (3.13.2)
|
136
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
137
|
-
rspec-support (~> 3.13.0)
|
138
|
-
rspec-support (3.13.2)
|
139
125
|
rubocop (1.75.3)
|
140
126
|
json (~> 2.3)
|
141
127
|
language_server-protocol (~> 3.17.0.2)
|
@@ -176,15 +162,14 @@ GEM
|
|
176
162
|
|
177
163
|
PLATFORMS
|
178
164
|
arm64-darwin-23
|
179
|
-
|
165
|
+
x86_64-linux
|
180
166
|
|
181
167
|
DEPENDENCIES
|
182
168
|
cgi
|
183
169
|
dotenv
|
184
170
|
guard
|
185
|
-
guard-
|
171
|
+
guard-minitest
|
186
172
|
mocha
|
187
|
-
pry
|
188
173
|
rake
|
189
174
|
roast-ai!
|
190
175
|
rubocop-shopify
|
data/README.md
CHANGED
@@ -312,9 +312,27 @@ Individual steps can override this setting with their own model parameter:
|
|
312
312
|
|
313
313
|
```yaml
|
314
314
|
analyze_data:
|
315
|
-
model: anthropic
|
315
|
+
model: anthropic/claude-3-haiku # Takes precedence over the global model
|
316
316
|
```
|
317
317
|
|
318
|
+
#### API Provider Configuration
|
319
|
+
|
320
|
+
Roast supports both OpenAI and OpenRouter as API providers. By default, Roast uses OpenAI, but you can specify OpenRouter:
|
321
|
+
|
322
|
+
```yaml
|
323
|
+
name: My Workflow
|
324
|
+
api_provider: openrouter
|
325
|
+
api_token: $(echo $OPENROUTER_API_KEY)
|
326
|
+
model: anthropic/claude-3-opus-20240229
|
327
|
+
```
|
328
|
+
|
329
|
+
Benefits of using OpenRouter:
|
330
|
+
- Access to multiple model providers through a single API
|
331
|
+
- Support for models from Anthropic, Meta, Mistral, and more
|
332
|
+
- Consistent API interface across different model providers
|
333
|
+
|
334
|
+
When using OpenRouter, specify fully qualified model names including the provider prefix (e.g., `anthropic/claude-3-opus-20240229`).
|
335
|
+
|
318
336
|
#### Dynamic API Tokens
|
319
337
|
|
320
338
|
Roast allows you to dynamically fetch API tokens using shell commands directly in your workflow configuration:
|
@@ -323,8 +341,12 @@ Roast allows you to dynamically fetch API tokens using shell commands directly i
|
|
323
341
|
# This will execute the shell command and use the result as the API token
|
324
342
|
api_token: $(print-token --key)
|
325
343
|
|
326
|
-
#
|
344
|
+
# For OpenAI (default)
|
327
345
|
api_token: $(echo $OPENAI_API_KEY)
|
346
|
+
|
347
|
+
# For OpenRouter (requires api_provider setting)
|
348
|
+
api_provider: openrouter
|
349
|
+
api_token: $(echo $OPENROUTER_API_KEY)
|
328
350
|
```
|
329
351
|
|
330
352
|
This makes it easy to use environment-specific tokens without hardcoding credentials, especially useful in development environments or CI/CD pipelines.
|
data/Rakefile
CHANGED
@@ -1,13 +1 @@
|
|
1
|
-
|
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?
|
1
|
+
Analyze the file at: <%= workflow.file %>
|
@@ -1,28 +1,3 @@
|
|
1
|
-
|
1
|
+
Generate a nicely formatted report based on the following metadata:
|
2
2
|
|
3
|
-
|
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?
|
3
|
+
<%= workflow.output[:analyze_patterns] %>
|
@@ -3,12 +3,10 @@ model: anthropic:claude-3-7-sonnet
|
|
3
3
|
|
4
4
|
tools:
|
5
5
|
- Roast::Tools::ReadFile
|
6
|
-
- Roast::Tools::WriteFile
|
7
|
-
- Roast::Tools::Grep
|
8
6
|
|
9
7
|
steps:
|
10
8
|
- analyze_file
|
11
|
-
-
|
9
|
+
- analyze_patterns
|
12
10
|
- generate_report_for_{{File.extname(workflow.file).sub('.', '')}}
|
13
11
|
- '$(echo "Processing completed for file: {{File.basename(workflow.file)}}")'
|
14
12
|
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# OpenRouter Example
|
2
|
+
|
3
|
+
This example demonstrates how to use OpenRouter with Roast to access models from different providers through a single API.
|
4
|
+
|
5
|
+
## Setup
|
6
|
+
|
7
|
+
1. Sign up for an account at [OpenRouter](https://openrouter.ai/)
|
8
|
+
2. Get your API key from the OpenRouter dashboard
|
9
|
+
3. Set the API key as an environment variable:
|
10
|
+
```bash
|
11
|
+
export OPENROUTER_API_KEY=your_api_key_here
|
12
|
+
```
|
13
|
+
|
14
|
+
## Running the Example
|
15
|
+
|
16
|
+
```bash
|
17
|
+
# Run without a specific target (general analysis)
|
18
|
+
roast execute workflow.yml
|
19
|
+
|
20
|
+
# Run with a specific file to analyze
|
21
|
+
roast execute workflow.yml path/to/your/file.txt
|
22
|
+
```
|
23
|
+
|
24
|
+
## How it Works
|
25
|
+
|
26
|
+
This example configures Roast to use OpenRouter as the API provider:
|
27
|
+
|
28
|
+
```yaml
|
29
|
+
api_provider: openrouter
|
30
|
+
api_token: $(echo $OPENROUTER_API_KEY)
|
31
|
+
model: anthropic/claude-3-haiku-20240307
|
32
|
+
```
|
33
|
+
|
34
|
+
The workflow consists of two steps:
|
35
|
+
1. `analyze_input`: Analyzes the provided content (or generates general insights if no target is provided)
|
36
|
+
2. `generate_response`: Creates a structured response based on the analysis
|
37
|
+
|
38
|
+
## Available Models
|
39
|
+
|
40
|
+
When using OpenRouter, you can access models from multiple providers by specifying the fully qualified model name, including the provider prefix. Some examples:
|
41
|
+
|
42
|
+
- `anthropic/claude-3-opus-20240229`
|
43
|
+
- `anthropic/claude-3-sonnet-20240229`
|
44
|
+
- `meta/llama-3-70b-instruct`
|
45
|
+
- `google/gemini-1.5-pro-latest`
|
46
|
+
- `mistral/mistral-large-latest`
|
47
|
+
|
48
|
+
Check the [OpenRouter documentation](https://openrouter.ai/docs) for the complete list of supported models.
|
@@ -0,0 +1,16 @@
|
|
1
|
+
I'd like you to analyze the following input and provide your insights.
|
2
|
+
|
3
|
+
<% if workflow.file && workflow.resource.content %>
|
4
|
+
Here is the content to analyze:
|
5
|
+
|
6
|
+
```
|
7
|
+
<%= workflow.resource.content %>
|
8
|
+
```
|
9
|
+
<% else %>
|
10
|
+
The workflow is running without a specific file target. Please provide general insights based on the context.
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
Please provide:
|
14
|
+
1. A summary of the key points
|
15
|
+
2. Any notable patterns or issues
|
16
|
+
3. Recommendations based on your analysis
|
@@ -0,0 +1,9 @@
|
|
1
|
+
Based on the analysis from the previous step, please generate a response that addresses the findings.
|
2
|
+
|
3
|
+
Your response should be well-structured and include:
|
4
|
+
1. An introduction summarizing the analysis
|
5
|
+
2. Detailed discussion of key points
|
6
|
+
3. Clear recommendations for improvements
|
7
|
+
4. A conclusion
|
8
|
+
|
9
|
+
Format the response in markdown for better readability.
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Roast
|
4
|
+
class Initializers
|
5
|
+
class << self
|
6
|
+
def config_root(starting_path = Dir.pwd, ending_path = File.dirname(Dir.home))
|
7
|
+
paths = []
|
8
|
+
candidate = starting_path
|
9
|
+
while candidate != ending_path
|
10
|
+
paths << File.join(candidate, ".roast")
|
11
|
+
candidate = File.dirname(candidate)
|
12
|
+
end
|
13
|
+
|
14
|
+
first_existing = paths.find { |path| Dir.exist?(path) }
|
15
|
+
first_existing || paths.first
|
16
|
+
end
|
17
|
+
|
18
|
+
def initializers_path
|
19
|
+
File.join(Roast::Initializers.config_root, "initializers")
|
20
|
+
end
|
21
|
+
|
22
|
+
def load_all
|
23
|
+
project_initializers = Roast::Initializers.initializers_path
|
24
|
+
return unless Dir.exist?(project_initializers)
|
25
|
+
|
26
|
+
$stderr.puts "Loading project initializers from #{project_initializers}"
|
27
|
+
pattern = File.join(project_initializers, "**/*.rb")
|
28
|
+
Dir.glob(pattern, sort: true).each do |file|
|
29
|
+
$stderr.puts "Loading initializer: #{file}"
|
30
|
+
require file
|
31
|
+
end
|
32
|
+
rescue => e
|
33
|
+
puts "ERROR: Error loading initializers: #{e.message}"
|
34
|
+
Roast::Helpers::Logger.error("Error loading initializers: #{e.message}")
|
35
|
+
# Don't fail the workflow if initializers can't be loaded
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -13,10 +13,11 @@ module Roast
|
|
13
13
|
base.class_eval do
|
14
14
|
function(
|
15
15
|
:search_for_file,
|
16
|
-
|
17
|
-
|
16
|
+
"Search for a file in the project using a glob pattern.",
|
17
|
+
glob_pattern: { type: "string", description: "A glob pattern to search for. Example: 'test/**/*_test.rb'" },
|
18
|
+
path: { type: "string", description: "path to search from" },
|
18
19
|
) do |params|
|
19
|
-
Roast::Tools::SearchFile.call(params[:
|
20
|
+
Roast::Tools::SearchFile.call(params[:glob_pattern], params[:path]).tap do |result|
|
20
21
|
Roast::Helpers::Logger.debug(result) if ENV["DEBUG"]
|
21
22
|
end
|
22
23
|
end
|
@@ -24,27 +25,33 @@ module Roast
|
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
27
|
-
def call(
|
28
|
-
Roast::Helpers::Logger.info("🔍 Searching for
|
29
|
-
search_for(
|
30
|
-
return "No results found for #{
|
31
|
-
return
|
28
|
+
def call(glob_pattern, path = ".")
|
29
|
+
Roast::Helpers::Logger.info("🔍 Searching for: '#{glob_pattern}' in '#{path}'\n")
|
30
|
+
search_for(glob_pattern, path).then do |results|
|
31
|
+
return "No results found for #{glob_pattern} in #{path}" if results.empty?
|
32
|
+
return read_contents(results.first) if results.size == 1
|
32
33
|
|
33
|
-
results.
|
34
|
+
results.join("\n") # purposely give the AI list of actual paths so that it can read without searching first
|
34
35
|
end
|
35
36
|
rescue StandardError => e
|
36
|
-
"Error searching for
|
37
|
+
"Error searching for '#{glob_pattern}' in '#{path}': #{e.message}".tap do |error_message|
|
37
38
|
Roast::Helpers::Logger.error(error_message + "\n")
|
38
39
|
Roast::Helpers::Logger.debug(e.backtrace.join("\n") + "\n") if ENV["DEBUG"]
|
39
40
|
end
|
40
41
|
end
|
41
42
|
|
42
|
-
def
|
43
|
-
|
44
|
-
|
43
|
+
def read_contents(path)
|
44
|
+
contents = File.read(path)
|
45
|
+
token_count = contents.size / 4
|
46
|
+
if token_count > 25_000
|
47
|
+
path
|
48
|
+
else
|
49
|
+
contents
|
50
|
+
end
|
51
|
+
end
|
45
52
|
|
46
|
-
|
47
|
-
|
53
|
+
def search_for(pattern, path)
|
54
|
+
Dir.glob(pattern, base: path)
|
48
55
|
end
|
49
56
|
end
|
50
57
|
end
|
data/lib/roast/version.rb
CHANGED
@@ -8,7 +8,7 @@ module Roast
|
|
8
8
|
# Encapsulates workflow configuration data and provides structured access
|
9
9
|
# to the configuration settings
|
10
10
|
class Configuration
|
11
|
-
attr_reader :config_hash, :workflow_path, :name, :steps, :tools, :function_configs, :api_token, :model, :resource
|
11
|
+
attr_reader :config_hash, :workflow_path, :name, :steps, :tools, :function_configs, :api_token, :api_provider, :model, :resource
|
12
12
|
attr_accessor :target
|
13
13
|
|
14
14
|
def initialize(workflow_path, options = {})
|
@@ -45,6 +45,9 @@ module Roast
|
|
45
45
|
@api_token = process_shell_command(@config_hash["api_token"])
|
46
46
|
end
|
47
47
|
|
48
|
+
# Determine API provider (defaults to OpenAI if not specified)
|
49
|
+
@api_provider = determine_api_provider
|
50
|
+
|
48
51
|
# Extract default model if provided
|
49
52
|
@model = @config_hash["model"]
|
50
53
|
end
|
@@ -138,8 +141,32 @@ module Roast
|
|
138
141
|
@function_configs[function_name.to_s] || {}
|
139
142
|
end
|
140
143
|
|
144
|
+
def openrouter?
|
145
|
+
@api_provider == :openrouter
|
146
|
+
end
|
147
|
+
|
148
|
+
def openai?
|
149
|
+
@api_provider == :openai
|
150
|
+
end
|
151
|
+
|
141
152
|
private
|
142
153
|
|
154
|
+
def determine_api_provider
|
155
|
+
return :openai unless @config_hash["api_provider"]
|
156
|
+
|
157
|
+
provider = @config_hash["api_provider"].to_s.downcase
|
158
|
+
|
159
|
+
case provider
|
160
|
+
when "openai"
|
161
|
+
:openai
|
162
|
+
when "openrouter"
|
163
|
+
:openrouter
|
164
|
+
else
|
165
|
+
Roast::Helpers::Logger.warn("Unknown API provider '#{provider}', defaulting to OpenAI")
|
166
|
+
:openai
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
143
170
|
def process_shell_command(command)
|
144
171
|
# If it's a bash command with the $(command) syntax
|
145
172
|
if command =~ /^\$\((.*)\)$/
|
@@ -6,6 +6,7 @@ require_relative "../helpers/function_caching_interceptor"
|
|
6
6
|
require "active_support"
|
7
7
|
require "active_support/isolated_execution_state"
|
8
8
|
require "active_support/notifications"
|
9
|
+
require "raix"
|
9
10
|
|
10
11
|
module Roast
|
11
12
|
module Workflow
|
@@ -98,35 +99,23 @@ module Roast
|
|
98
99
|
end
|
99
100
|
|
100
101
|
def load_roast_initializers
|
101
|
-
|
102
|
-
project_initializers = File.join(Dir.pwd, ".roast", "initializers")
|
103
|
-
|
104
|
-
if Dir.exist?(project_initializers)
|
105
|
-
$stderr.puts "Loading project initializers from #{project_initializers}"
|
106
|
-
Dir.glob(File.join(project_initializers, "**/*.rb")).sort.each do |file|
|
107
|
-
$stderr.puts "Loading initializer: #{file}"
|
108
|
-
require file
|
109
|
-
end
|
110
|
-
end
|
111
|
-
rescue => e
|
112
|
-
Roast::Helpers::Logger.error("Error loading initializers: #{e.message}")
|
113
|
-
# Don't fail the workflow if initializers can't be loaded
|
102
|
+
Roast::Initializers.load_all
|
114
103
|
end
|
115
104
|
|
116
105
|
def configure_api_client
|
117
106
|
return unless configuration.api_token
|
118
107
|
|
119
108
|
begin
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
109
|
+
case configuration.api_provider
|
110
|
+
when :openrouter
|
111
|
+
$stderr.puts "Configuring OpenRouter client with token from workflow"
|
112
|
+
require "open_router"
|
124
113
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
Raix.configuration.openai_client = OpenAI::Client.new(access_token: configuration.api_token)
|
114
|
+
Raix.configure do |config|
|
115
|
+
config.openrouter_client = OpenRouter::Client.new(api_key: configuration.api_token)
|
116
|
+
end
|
129
117
|
else
|
118
|
+
$stderr.puts "Configuring OpenAI client with token from workflow"
|
130
119
|
require "openai"
|
131
120
|
|
132
121
|
Raix.configure do |config|
|
data/lib/roast.rb
CHANGED
@@ -7,6 +7,7 @@ require "roast/tools"
|
|
7
7
|
require "roast/helpers"
|
8
8
|
require "roast/resources"
|
9
9
|
require "roast/workflow"
|
10
|
+
require "roast/initializers"
|
10
11
|
|
11
12
|
module Roast
|
12
13
|
ROOT = File.expand_path("../..", __FILE__)
|
@@ -28,6 +29,11 @@ module Roast
|
|
28
29
|
Roast::Workflow::ConfigurationParser.new(expanded_workflow_path, files, options.transform_keys(&:to_sym)).begin!
|
29
30
|
end
|
30
31
|
|
32
|
+
desc "version", "Display the current version of Roast"
|
33
|
+
def version
|
34
|
+
puts "Roast version #{Roast::VERSION}"
|
35
|
+
end
|
36
|
+
|
31
37
|
class << self
|
32
38
|
def exit_on_failure?
|
33
39
|
true
|
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.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
@@ -137,6 +137,10 @@ files:
|
|
137
137
|
- examples/interpolation/sample.rb
|
138
138
|
- examples/interpolation/workflow.md
|
139
139
|
- examples/interpolation/workflow.yml
|
140
|
+
- examples/openrouter_example/README.md
|
141
|
+
- examples/openrouter_example/analyze_input/prompt.md
|
142
|
+
- examples/openrouter_example/generate_response/prompt.md
|
143
|
+
- examples/openrouter_example/workflow.yml
|
140
144
|
- examples/rspec_to_minitest/README.md
|
141
145
|
- examples/rspec_to_minitest/analyze_spec/prompt.md
|
142
146
|
- examples/rspec_to_minitest/create_minitest/prompt.md
|
@@ -151,6 +155,7 @@ files:
|
|
151
155
|
- lib/roast/helpers/minitest_coverage_runner.rb
|
152
156
|
- lib/roast/helpers/path_resolver.rb
|
153
157
|
- lib/roast/helpers/prompt_loader.rb
|
158
|
+
- lib/roast/initializers.rb
|
154
159
|
- lib/roast/resources.rb
|
155
160
|
- lib/roast/resources/api_resource.rb
|
156
161
|
- lib/roast/resources/base_resource.rb
|