roast-ai 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +0 -2
- data/Gemfile.lock +2 -2
- data/examples/demo/Gemfile.lock +1 -1
- data/examples/plugin-gem-example/Gemfile.lock +1 -1
- data/lib/roast/cogs/agent/config.rb +43 -0
- data/lib/roast/cogs/agent/providers/claude/message.rb +4 -2
- data/lib/roast/cogs/agent/stats.rb +15 -0
- data/lib/roast/cogs/agent/usage.rb +7 -0
- data/lib/roast/cogs/agent.rb +57 -4
- data/lib/roast/cogs/cmd.rb +35 -0
- data/lib/roast/command_runner.rb +3 -1
- data/lib/roast/version.rb +1 -1
- data/sorbet/rbi/shims/lib/roast/config_context.rbi +8 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a1acd0533e7f7ca21ccb4d8fd27f00bd1e838953130a5e0dd779e8be4a61c15e
|
|
4
|
+
data.tar.gz: cf9cb0909d17aefdf6dd066422251565a13d9abc152e202f9143080eec2b8fdd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: df929fe8cd3a22bdbb5d22660a0ebec7461c67cb6d9880b13862f8bbc059ccc3e964fb34d142a631bbcf0e5a3c3ea88e5c19eb2727486d5fc82c2832f7f79355
|
|
7
|
+
data.tar.gz: 8245dca9407394314434006628279cb26a74dd9c72b8819cc21ba4c45728a790d91f4ec95e10ba999f6958e576981a493fdb8a3d419dc067873d6b40c0764d64
|
data/CONTRIBUTING.md
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# How to contribute
|
|
2
2
|
|
|
3
|
-
> **📢 Code Freeze Notice**: This project is currently under a limited code freeze while we work on an alternative frontend (a DSL). We are still accepting **bug fixes**, but **new features** will not be merged until the DSL is released. Thank you for your understanding!
|
|
4
|
-
|
|
5
3
|
## Things we will merge
|
|
6
4
|
|
|
7
5
|
* Bugfixes
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
roast-ai (1.
|
|
4
|
+
roast-ai (1.4.0)
|
|
5
5
|
activesupport (~> 8.0)
|
|
6
6
|
async (>= 2.34)
|
|
7
7
|
rainbow (>= 3.0.0)
|
|
@@ -317,7 +317,7 @@ CHECKSUMS
|
|
|
317
317
|
regexp_parser (2.10.0) sha256=cb6f0ddde88772cd64bff1dbbf68df66d376043fe2e66a9ef77fcb1b0c548c61
|
|
318
318
|
require-hooks (0.2.3) sha256=224be5b4be0fd2a47cb73286c500da366704a54ec195b6627366380c950efac8
|
|
319
319
|
rexml (3.4.2) sha256=1384268554a37af5da5279431ca3f2f37d46f09ffdd6c95e17cc84c83ea7c417
|
|
320
|
-
roast-ai (1.
|
|
320
|
+
roast-ai (1.4.0)
|
|
321
321
|
rubocop (1.77.0) sha256=1f360b4575ef7a124be27b0dfffa227a2b2d9420d22d4fd8bf179d702bcc88c0
|
|
322
322
|
rubocop-ast (1.45.1) sha256=94042e49adc17f187ba037b33f941ba7398fede77cdf4bffafba95190a473a3e
|
|
323
323
|
rubocop-shopify (2.17.1) sha256=03850eb1a9c4d1f9f0ac1d8d5aa51bb47a149e532cfb5e8d02ac6a90c8800a5f
|
data/examples/demo/Gemfile.lock
CHANGED
|
@@ -554,6 +554,49 @@ module Roast
|
|
|
554
554
|
Pathname.new(@values[:dump_raw_agent_messages_to]) if @values[:dump_raw_agent_messages_to]
|
|
555
555
|
end
|
|
556
556
|
|
|
557
|
+
# Configure the cog to append a record of each agent run to a file, as one JSON object per line
|
|
558
|
+
#
|
|
559
|
+
# Each record holds the run's start and finish times, the cog's name and its path in the workflow,
|
|
560
|
+
# the provider and model, the prompts and any appended or replacement system prompt, the final
|
|
561
|
+
# response, the session identifier, the execution statistics, and the run's status (`succeeded`,
|
|
562
|
+
# `failed`, or `timed_out`) with the error class and message if it did not succeed.
|
|
563
|
+
#
|
|
564
|
+
# Records include the full prompts and response, so they may contain sensitive text.
|
|
565
|
+
# Disabled by default.
|
|
566
|
+
#
|
|
567
|
+
# #### See Also
|
|
568
|
+
# - `no_record_agent_runs!`
|
|
569
|
+
#
|
|
570
|
+
#: (String) -> void
|
|
571
|
+
def record_agent_runs_to(filename)
|
|
572
|
+
@values[:record_agent_runs_to] = filename
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
# Configure the cog __not__ to record its agent runs
|
|
576
|
+
#
|
|
577
|
+
# This is the default behaviour.
|
|
578
|
+
#
|
|
579
|
+
# #### See Also
|
|
580
|
+
# - `record_agent_runs_to`
|
|
581
|
+
#
|
|
582
|
+
#: () -> void
|
|
583
|
+
def no_record_agent_runs!
|
|
584
|
+
@values[:record_agent_runs_to] = nil
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
# Get the validated, configured path to which agent run records should be appended
|
|
588
|
+
#
|
|
589
|
+
# Returns `nil` if agent runs should not be recorded.
|
|
590
|
+
#
|
|
591
|
+
# #### See Also
|
|
592
|
+
# - `record_agent_runs_to`
|
|
593
|
+
# - `no_record_agent_runs!`
|
|
594
|
+
#
|
|
595
|
+
#: () -> Pathname?
|
|
596
|
+
def valid_record_agent_runs_to_path
|
|
597
|
+
Pathname.new(@values[:record_agent_runs_to]) if @values[:record_agent_runs_to].present?
|
|
598
|
+
end
|
|
599
|
+
|
|
557
600
|
alias_method(:skip_permissions!, :no_apply_permissions!)
|
|
558
601
|
alias_method(:no_skip_permissions!, :apply_permissions!)
|
|
559
602
|
alias_method(:quiet!, :no_display!)
|
|
@@ -14,8 +14,10 @@ module Roast
|
|
|
14
14
|
class << self
|
|
15
15
|
#: (String, ?raw_dump_file: Pathname?) -> Message
|
|
16
16
|
def from_json(json, raw_dump_file: nil)
|
|
17
|
-
raw_dump_file
|
|
18
|
-
|
|
17
|
+
if raw_dump_file
|
|
18
|
+
raw_dump_file.dirname.mkpath
|
|
19
|
+
File.write(raw_dump_file.to_s, "#{json}\n", mode: "a")
|
|
20
|
+
end
|
|
19
21
|
from_hash(JSON.parse(json, symbolize_names: true))
|
|
20
22
|
end
|
|
21
23
|
|
|
@@ -100,6 +100,21 @@ module Roast
|
|
|
100
100
|
lines.join("\n")
|
|
101
101
|
end
|
|
102
102
|
|
|
103
|
+
# Get the statistics as a hash, with aggregate and per-model usage as nested hashes
|
|
104
|
+
#
|
|
105
|
+
# #### See Also
|
|
106
|
+
# - `Agent::Usage#to_h`
|
|
107
|
+
#
|
|
108
|
+
#: () -> Hash[Symbol, untyped]
|
|
109
|
+
def to_h
|
|
110
|
+
{
|
|
111
|
+
duration_ms:,
|
|
112
|
+
num_turns:,
|
|
113
|
+
usage: usage.to_h,
|
|
114
|
+
model_usage: model_usage.transform_values(&:to_h),
|
|
115
|
+
}
|
|
116
|
+
end
|
|
117
|
+
|
|
103
118
|
private
|
|
104
119
|
|
|
105
120
|
#: (Numeric?, Numeric?) -> Numeric?
|
data/lib/roast/cogs/agent.rb
CHANGED
|
@@ -45,17 +45,70 @@ module Roast
|
|
|
45
45
|
# The agent may make multiple turns (back-and-forth exchanges) during execution,
|
|
46
46
|
# especially when using tools. Each turn is counted in the execution statistics.
|
|
47
47
|
#
|
|
48
|
+
# If the cog is configured with `record_agent_runs_to`, a record of the run is appended to that file
|
|
49
|
+
# whether the run succeeds or fails.
|
|
50
|
+
#
|
|
48
51
|
#: (Input) -> Output
|
|
49
52
|
def execute(input)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
started_at = Time.now
|
|
54
|
+
begin
|
|
55
|
+
output = provider.invoke(input)
|
|
56
|
+
if config.show_stats?
|
|
57
|
+
Event << { block: { header: "AGENT STATS", content: "#{output.stats}\nSession ID: #{output.session}" } }
|
|
58
|
+
end
|
|
59
|
+
output
|
|
60
|
+
rescue StandardError => e
|
|
61
|
+
error = e
|
|
62
|
+
raise
|
|
63
|
+
ensure
|
|
64
|
+
record_run(input, output, error, started_at) if config.valid_record_agent_runs_to_path
|
|
53
65
|
end
|
|
54
|
-
output
|
|
55
66
|
end
|
|
56
67
|
|
|
57
68
|
private
|
|
58
69
|
|
|
70
|
+
#: (Input, Output?, StandardError?, Time) -> void
|
|
71
|
+
def record_run(input, output, error, started_at)
|
|
72
|
+
record = {
|
|
73
|
+
started_at: started_at.utc.iso8601(3),
|
|
74
|
+
finished_at: Time.now.utc.iso8601(3),
|
|
75
|
+
cog: { name: anonymous? ? nil : name, path: record_path },
|
|
76
|
+
provider: config.valid_provider!,
|
|
77
|
+
model: config.valid_model,
|
|
78
|
+
prompts: input.prompts,
|
|
79
|
+
system_prompt: { append: config.valid_append_system_prompt, replace: config.valid_replace_system_prompt },
|
|
80
|
+
status: run_status(output, error),
|
|
81
|
+
error: error && { class: error.class.name, message: error.message },
|
|
82
|
+
response: output&.response,
|
|
83
|
+
session: output&.session,
|
|
84
|
+
stats: output&.stats&.to_h,
|
|
85
|
+
}
|
|
86
|
+
path = config.valid_record_agent_runs_to_path.not_nil!
|
|
87
|
+
path.dirname.mkpath
|
|
88
|
+
File.write(path.to_s, "#{JSON.generate(record)}\n", mode: "a")
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
#: () -> Array[Hash[Symbol, untyped]]
|
|
92
|
+
def record_path
|
|
93
|
+
TaskContext.path.filter_map do |element|
|
|
94
|
+
cog = element.cog
|
|
95
|
+
execution_manager = element.execution_manager
|
|
96
|
+
if cog.present?
|
|
97
|
+
{ cog: cog.type, name: cog.anonymous? ? nil : cog.name }
|
|
98
|
+
elsif execution_manager&.scope
|
|
99
|
+
{ scope: execution_manager.scope, index: execution_manager.scope_index }
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
#: (Output?, StandardError?) -> String
|
|
105
|
+
def run_status(output, error)
|
|
106
|
+
return "timed_out" if error.is_a?(CommandRunner::TimeoutError)
|
|
107
|
+
return "failed" if error || output.nil?
|
|
108
|
+
|
|
109
|
+
"succeeded"
|
|
110
|
+
end
|
|
111
|
+
|
|
59
112
|
#: () -> Provider
|
|
60
113
|
def provider
|
|
61
114
|
@provider ||= case config.valid_provider!
|
data/lib/roast/cogs/cmd.rb
CHANGED
|
@@ -168,6 +168,40 @@ module Roast
|
|
|
168
168
|
show_stdout? || show_stderr?
|
|
169
169
|
end
|
|
170
170
|
|
|
171
|
+
# Configure the cog to run the command with these environment variables set
|
|
172
|
+
#
|
|
173
|
+
# Variables not named here are inherited from Roast's own environment. A `nil` value unsets
|
|
174
|
+
# the variable for the command.
|
|
175
|
+
#
|
|
176
|
+
# #### See Also
|
|
177
|
+
# - `no_env!`
|
|
178
|
+
#
|
|
179
|
+
#: (Hash[String | Symbol, String?]) -> void
|
|
180
|
+
def env(vars)
|
|
181
|
+
@values[:env] = vars
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Configure the cog to run the command with Roast's own environment, unchanged (the default)
|
|
185
|
+
#
|
|
186
|
+
# #### See Also
|
|
187
|
+
# - `env`
|
|
188
|
+
#
|
|
189
|
+
#: () -> void
|
|
190
|
+
def no_env!
|
|
191
|
+
@values[:env] = nil
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Get the validated environment variables the command will be run with, keyed by name
|
|
195
|
+
#
|
|
196
|
+
# #### See Also
|
|
197
|
+
# - `env`
|
|
198
|
+
# - `no_env!`
|
|
199
|
+
#
|
|
200
|
+
#: () -> Hash[String, String?]
|
|
201
|
+
def valid_env
|
|
202
|
+
(@values[:env] || {}).to_h { |name, value| [name.to_s, value&.to_s] }
|
|
203
|
+
end
|
|
204
|
+
|
|
171
205
|
alias_method(:quiet!, :no_display!)
|
|
172
206
|
end
|
|
173
207
|
|
|
@@ -281,6 +315,7 @@ module Roast
|
|
|
281
315
|
.execute(
|
|
282
316
|
[input.command] + input.args,
|
|
283
317
|
working_directory: config.valid_working_directory,
|
|
318
|
+
env: config.valid_env,
|
|
284
319
|
stdin_content: input.stdin,
|
|
285
320
|
stdout_handler: stdout_handler,
|
|
286
321
|
stderr_handler: stderr_handler,
|
data/lib/roast/command_runner.rb
CHANGED
|
@@ -45,6 +45,7 @@ module Roast
|
|
|
45
45
|
#: (
|
|
46
46
|
#| Array[String],
|
|
47
47
|
#| ?working_directory: (Pathname | String)?,
|
|
48
|
+
#| ?env: Hash[String, String?],
|
|
48
49
|
#| ?timeout: (Integer | Float)?,
|
|
49
50
|
#| ?stdin_content: String?,
|
|
50
51
|
#| ?stdout_handler: (^(String) -> void)?,
|
|
@@ -53,6 +54,7 @@ module Roast
|
|
|
53
54
|
def execute(
|
|
54
55
|
args,
|
|
55
56
|
working_directory: nil,
|
|
57
|
+
env: {},
|
|
56
58
|
timeout: nil,
|
|
57
59
|
stdin_content: nil,
|
|
58
60
|
stdout_handler: nil,
|
|
@@ -69,7 +71,7 @@ module Roast
|
|
|
69
71
|
stdin, stdout, stderr, wait_thread = Bundler.with_unbundled_env do
|
|
70
72
|
Open3 #: as untyped
|
|
71
73
|
.popen3(
|
|
72
|
-
|
|
74
|
+
env.merge(env_overrides, { "PWD" => working_directory&.to_s }.compact),
|
|
73
75
|
*args.map(&:to_s),
|
|
74
76
|
{ chdir: working_directory }.compact,
|
|
75
77
|
)
|
data/lib/roast/version.rb
CHANGED
|
@@ -184,6 +184,10 @@ module Roast
|
|
|
184
184
|
# #### Configure debugging
|
|
185
185
|
# - `dump_raw_agent_messages_to(filename)` - Dump raw agent messages to a file for debugging
|
|
186
186
|
#
|
|
187
|
+
# #### Configure run records
|
|
188
|
+
# - `record_agent_runs_to(filename)` - Append a JSON record of each agent run (prompts, response, session, stats, status) to a file
|
|
189
|
+
# - `no_record_agent_runs!` - Don't record agent runs (default)
|
|
190
|
+
#
|
|
187
191
|
# #### Configure asynchronous execution
|
|
188
192
|
# - `async!` - Run the cog asynchronously in the background
|
|
189
193
|
# - `no_async!` (alias `sync!`) - Run the cog synchronously (default)
|
|
@@ -273,6 +277,10 @@ module Roast
|
|
|
273
277
|
# - `fail_on_error!` - Consider the cog failed if the command returns a non-zero exit status (default)
|
|
274
278
|
# - `no_fail_on_error!` - Don't fail the cog on non-zero exit status (exit status still available in output)
|
|
275
279
|
#
|
|
280
|
+
# #### Configure the environment
|
|
281
|
+
# - `env(hash)` - Set environment variables for the command (a `nil` value unsets one)
|
|
282
|
+
# - `no_env!` - Run the command with Roast's own environment, unchanged (default)
|
|
283
|
+
#
|
|
276
284
|
# #### Configure display output
|
|
277
285
|
# - `show_stdout!` - Write STDOUT to the console
|
|
278
286
|
# - `no_show_stdout!` - Don't write STDOUT to the console (default)
|
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: 1.
|
|
4
|
+
version: 1.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shopify
|
|
@@ -408,7 +408,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
408
408
|
- !ruby/object:Gem::Version
|
|
409
409
|
version: '0'
|
|
410
410
|
requirements: []
|
|
411
|
-
rubygems_version: 4.0.
|
|
411
|
+
rubygems_version: 4.0.22
|
|
412
412
|
specification_version: 4
|
|
413
413
|
summary: A framework for executing structured AI workflows in Ruby
|
|
414
414
|
test_files: []
|