silas 0.1.7 → 0.3.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/CHANGELOG.md +153 -0
- data/README.md +81 -14
- data/app/controllers/concerns/silas/api/serialization.rb +63 -0
- data/app/controllers/silas/api/base_controller.rb +24 -0
- data/app/controllers/silas/api/v1/approvals_controller.rb +35 -0
- data/app/controllers/silas/api/v1/sessions_controller.rb +39 -0
- data/app/controllers/silas/api/v1/streams_controller.rb +117 -0
- data/app/controllers/silas/api/v1/turns_controller.rb +32 -0
- data/app/controllers/silas/inbox/sessions_controller.rb +39 -2
- data/app/controllers/silas/inbox/turns_controller.rb +51 -0
- data/app/helpers/silas/inbox/trace_helper.rb +5 -0
- data/app/jobs/silas/agent_loop_job.rb +46 -43
- data/app/jobs/silas/dead_job_rescuer_job.rb +25 -4
- data/app/models/silas/session.rb +6 -0
- data/app/models/silas/tool_invocation.rb +13 -1
- data/app/models/silas/turn.rb +10 -0
- data/app/views/layouts/silas/inbox.html.erb +20 -0
- data/app/views/silas/inbox/invocations/_invocation.html.erb +18 -2
- data/app/views/silas/inbox/sessions/index.html.erb +31 -5
- data/app/views/silas/inbox/sessions/show.html.erb +11 -0
- data/app/views/silas/inbox/steps/_step.html.erb +9 -1
- data/app/views/silas/inbox/turns/_header.html.erb +19 -1
- data/config/routes.rb +28 -1
- data/db/migrate/20260724000001_drop_agent_sdk_columns_from_silas_turns.rb +9 -0
- data/db/migrate/20260725000001_add_provider_to_silas_steps.rb +13 -0
- data/lib/generators/silas/install/install_generator.rb +34 -14
- data/lib/generators/silas/install/templates/agent.yml +10 -1
- data/lib/generators/silas/install/templates/bin_ci +2 -2
- data/lib/generators/silas/install/templates/initializer.rb +30 -5
- data/lib/generators/silas/install/templates/ruby_llm.rb +4 -0
- data/lib/silas/agent.rb +4 -0
- data/lib/silas/chat.rb +47 -13
- data/lib/silas/configuration.rb +77 -37
- data/lib/silas/delta_buffer.rb +50 -0
- data/lib/silas/doctor.rb +155 -0
- data/lib/silas/engine.rb +6 -0
- data/lib/silas/engines/base.rb +6 -8
- data/lib/silas/engines/ruby_llm.rb +28 -6
- data/lib/silas/errors.rb +3 -3
- data/lib/silas/eval/assertions.rb +18 -0
- data/lib/silas/eval/scripted_engine.rb +0 -2
- data/lib/silas/eval/transcript.rb +1 -0
- data/lib/silas/inbox/cost.rb +37 -13
- data/lib/silas/inbox/delta_broadcaster.rb +38 -0
- data/lib/silas/ledger.rb +26 -10
- data/lib/silas/mcp/handler.rb +6 -5
- data/lib/silas/mcp/server.rb +9 -9
- data/lib/silas/message_builder.rb +9 -2
- data/lib/silas/named_agent.rb +1 -0
- data/lib/silas/registry.rb +18 -8
- data/lib/silas/step_runner.rb +33 -6
- data/lib/silas/tool.rb +5 -0
- data/lib/silas/version.rb +1 -1
- data/lib/silas.rb +10 -9
- data/lib/tasks/silas_doctor.rake +17 -0
- metadata +14 -6
- data/lib/silas/agent_sdk/cli.rb +0 -59
- data/lib/silas/agent_sdk/stream_parser.rb +0 -86
- data/lib/silas/agent_sdk/version_guard.rb +0 -26
- data/lib/silas/engines/agent_sdk.rb +0 -75
- data/lib/silas/subprocess_runner.rb +0 -41
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: silas
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel St Paul
|
|
@@ -111,12 +111,19 @@ files:
|
|
|
111
111
|
- CHANGELOG.md
|
|
112
112
|
- LICENSE
|
|
113
113
|
- README.md
|
|
114
|
+
- app/controllers/concerns/silas/api/serialization.rb
|
|
115
|
+
- app/controllers/silas/api/base_controller.rb
|
|
116
|
+
- app/controllers/silas/api/v1/approvals_controller.rb
|
|
117
|
+
- app/controllers/silas/api/v1/sessions_controller.rb
|
|
118
|
+
- app/controllers/silas/api/v1/streams_controller.rb
|
|
119
|
+
- app/controllers/silas/api/v1/turns_controller.rb
|
|
114
120
|
- app/controllers/silas/channels/approvals_controller.rb
|
|
115
121
|
- app/controllers/silas/channels/base_controller.rb
|
|
116
122
|
- app/controllers/silas/channels/slack_controller.rb
|
|
117
123
|
- app/controllers/silas/inbox/base_controller.rb
|
|
118
124
|
- app/controllers/silas/inbox/invocations_controller.rb
|
|
119
125
|
- app/controllers/silas/inbox/sessions_controller.rb
|
|
126
|
+
- app/controllers/silas/inbox/turns_controller.rb
|
|
120
127
|
- app/helpers/silas/inbox/trace_helper.rb
|
|
121
128
|
- app/jobs/silas/agent_loop_job.rb
|
|
122
129
|
- app/jobs/silas/channel_delivery_job.rb
|
|
@@ -152,6 +159,8 @@ files:
|
|
|
152
159
|
- db/migrate/20260716000001_add_budget_overrides_to_silas_turns.rb
|
|
153
160
|
- db/migrate/20260716000002_add_cancel_requested_to_silas_turns.rb
|
|
154
161
|
- db/migrate/20260721000001_create_silas_memories.rb
|
|
162
|
+
- db/migrate/20260724000001_drop_agent_sdk_columns_from_silas_turns.rb
|
|
163
|
+
- db/migrate/20260725000001_add_provider_to_silas_steps.rb
|
|
155
164
|
- lib/generators/silas/install/install_generator.rb
|
|
156
165
|
- lib/generators/silas/install/templates/agent.yml
|
|
157
166
|
- lib/generators/silas/install/templates/bin_ci
|
|
@@ -167,17 +176,15 @@ files:
|
|
|
167
176
|
- lib/silas.rb
|
|
168
177
|
- lib/silas/agent.rb
|
|
169
178
|
- lib/silas/agent_scope.rb
|
|
170
|
-
- lib/silas/agent_sdk/cli.rb
|
|
171
|
-
- lib/silas/agent_sdk/stream_parser.rb
|
|
172
|
-
- lib/silas/agent_sdk/version_guard.rb
|
|
173
179
|
- lib/silas/budget.rb
|
|
174
180
|
- lib/silas/channel.rb
|
|
175
181
|
- lib/silas/chat.rb
|
|
176
182
|
- lib/silas/configuration.rb
|
|
177
183
|
- lib/silas/connection.rb
|
|
178
184
|
- lib/silas/connections.rb
|
|
185
|
+
- lib/silas/delta_buffer.rb
|
|
186
|
+
- lib/silas/doctor.rb
|
|
179
187
|
- lib/silas/engine.rb
|
|
180
|
-
- lib/silas/engines/agent_sdk.rb
|
|
181
188
|
- lib/silas/engines/base.rb
|
|
182
189
|
- lib/silas/engines/ruby_llm.rb
|
|
183
190
|
- lib/silas/errors.rb
|
|
@@ -192,6 +199,7 @@ files:
|
|
|
192
199
|
- lib/silas/eval/transcript.rb
|
|
193
200
|
- lib/silas/inbox.rb
|
|
194
201
|
- lib/silas/inbox/cost.rb
|
|
202
|
+
- lib/silas/inbox/delta_broadcaster.rb
|
|
195
203
|
- lib/silas/instructions.rb
|
|
196
204
|
- lib/silas/ledger.rb
|
|
197
205
|
- lib/silas/mcp/client.rb
|
|
@@ -209,7 +217,6 @@ files:
|
|
|
209
217
|
- lib/silas/skill.rb
|
|
210
218
|
- lib/silas/slack.rb
|
|
211
219
|
- lib/silas/step_runner.rb
|
|
212
|
-
- lib/silas/subprocess_runner.rb
|
|
213
220
|
- lib/silas/tool.rb
|
|
214
221
|
- lib/silas/tools/delegate.rb
|
|
215
222
|
- lib/silas/tools/handoff.rb
|
|
@@ -219,6 +226,7 @@ files:
|
|
|
219
226
|
- lib/silas/tools/run_code.rb
|
|
220
227
|
- lib/silas/version.rb
|
|
221
228
|
- lib/tasks/silas_chat.rake
|
|
229
|
+
- lib/tasks/silas_doctor.rake
|
|
222
230
|
- lib/tasks/silas_eval.rake
|
|
223
231
|
- lib/tasks/silas_schedules.rake
|
|
224
232
|
homepage: https://github.com/danielstpaul/silas
|
data/lib/silas/agent_sdk/cli.rb
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
require "json"
|
|
2
|
-
|
|
3
|
-
module Silas
|
|
4
|
-
module AgentSdk
|
|
5
|
-
# Builds the verified claude -p argv and manages the subprocess. --bare is
|
|
6
|
-
# the API-key-only auth guard (never subscription OAuth); alwaysLoad + a
|
|
7
|
-
# non-zero MCP_TIMEOUT are BOTH mandatory or the single-shot -p turn races
|
|
8
|
-
# ahead of the MCP handshake and never sees the tools (spike trap #1).
|
|
9
|
-
class Cli
|
|
10
|
-
def initialize(bin:, prompt:, system:, model:, mcp_url:, allowed:, resume_session_id: nil)
|
|
11
|
-
@bin = bin
|
|
12
|
-
@prompt = prompt
|
|
13
|
-
@system = system
|
|
14
|
-
@model = model
|
|
15
|
-
@mcp_url = mcp_url
|
|
16
|
-
@allowed = allowed
|
|
17
|
-
@resume_session_id = resume_session_id
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# Spawns claude, yields each stdout line, returns the exit status integer.
|
|
21
|
-
def stream
|
|
22
|
-
@io = IO.popen(env, argv, err: %i[child out], pgroup: true)
|
|
23
|
-
@pid = @io.pid
|
|
24
|
-
@io.each_line { |line| yield line }
|
|
25
|
-
@io.close
|
|
26
|
-
$?.exitstatus
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def terminate
|
|
30
|
-
return unless @pid
|
|
31
|
-
|
|
32
|
-
Process.kill("-TERM", @pid)
|
|
33
|
-
Process.kill("-KILL", @pid)
|
|
34
|
-
rescue Errno::ESRCH, Errno::EPERM
|
|
35
|
-
# already gone
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def argv
|
|
39
|
-
args = [ @bin, "-p", @prompt,
|
|
40
|
-
"--output-format", "stream-json", "--verbose", "--bare",
|
|
41
|
-
"--mcp-config", mcp_config_json,
|
|
42
|
-
"--allowedTools", @allowed.join(","),
|
|
43
|
-
"--model", @model ]
|
|
44
|
-
args += [ "--append-system-prompt", @system ] if @system.present?
|
|
45
|
-
args += [ "--resume", @resume_session_id ] if @resume_session_id.present?
|
|
46
|
-
args
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def mcp_config_json
|
|
50
|
-
JSON.generate("mcpServers" => { "silas" => { "type" => "http", "url" => @mcp_url, "alwaysLoad" => true } })
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def env
|
|
54
|
-
{ "ANTHROPIC_API_KEY" => ENV["ANTHROPIC_API_KEY"],
|
|
55
|
-
"MCP_TIMEOUT" => Silas.config.agent_sdk_mcp_timeout_ms.to_s }
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
require "json"
|
|
2
|
-
|
|
3
|
-
module Silas
|
|
4
|
-
module AgentSdk
|
|
5
|
-
# Tolerant NDJSON parser for `claude -p --output-format stream-json`. The
|
|
6
|
-
# event schema is under-documented and shifts between versions, so unknown
|
|
7
|
-
# lines/types must never raise. Yields Silas::Event objects; accumulates the
|
|
8
|
-
# terminal Result (final text, tool_use blocks, usage, session_id).
|
|
9
|
-
class StreamParser
|
|
10
|
-
attr_reader :session_id, :final_text, :usage, :stop_reason, :blocks, :tool_calls
|
|
11
|
-
|
|
12
|
-
def initialize
|
|
13
|
-
@blocks = []
|
|
14
|
-
@tool_calls = []
|
|
15
|
-
@final_text = nil
|
|
16
|
-
@usage = nil
|
|
17
|
-
@session_id = nil
|
|
18
|
-
@stop_reason = nil
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def ingest(line)
|
|
22
|
-
line = line.to_s.strip
|
|
23
|
-
return if line.empty?
|
|
24
|
-
|
|
25
|
-
event = JSON.parse(line)
|
|
26
|
-
@session_id ||= event["session_id"] if event["session_id"]
|
|
27
|
-
|
|
28
|
-
case event["type"]
|
|
29
|
-
when "system"
|
|
30
|
-
@session_id ||= event["session_id"]
|
|
31
|
-
yield Event.new(type: :"system.#{event['subtype']}", payload: symbolize(event))
|
|
32
|
-
when "assistant"
|
|
33
|
-
ingest_assistant(event) { |e| yield e }
|
|
34
|
-
when "user"
|
|
35
|
-
yield Event.new(type: :tool_result, payload: symbolize(event))
|
|
36
|
-
when "result"
|
|
37
|
-
@final_text = event["result"]
|
|
38
|
-
@stop_reason = event["subtype"]
|
|
39
|
-
@usage = extract_usage(event)
|
|
40
|
-
# The result text usually repeats the last assistant text — only add a
|
|
41
|
-
# block if it isn't already captured.
|
|
42
|
-
if @final_text && @blocks.none? { |b| b["type"] == "text" && b["text"] == @final_text }
|
|
43
|
-
@blocks << { "type" => "text", "text" => @final_text }
|
|
44
|
-
end
|
|
45
|
-
yield Event.new(type: :result, payload: symbolize(event))
|
|
46
|
-
else
|
|
47
|
-
yield Event.new(type: :unknown, payload: symbolize(event))
|
|
48
|
-
end
|
|
49
|
-
rescue JSON::ParserError
|
|
50
|
-
# A non-JSON line (banner, warning) — ignore.
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# tool_calls stays [] on the Result: in the engine-owned path Claude Code
|
|
54
|
-
# already executed the tools (through our MCP endpoint), so the framework
|
|
55
|
-
# loop must not try to run them again.
|
|
56
|
-
def to_result
|
|
57
|
-
Engines::Result.new(blocks: @blocks, tool_calls: [], stop_reason: @stop_reason || "end_turn", usage: @usage)
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
private
|
|
61
|
-
|
|
62
|
-
def ingest_assistant(event)
|
|
63
|
-
Array(event.dig("message", "content")).each do |block|
|
|
64
|
-
case block["type"]
|
|
65
|
-
when "text"
|
|
66
|
-
@blocks << { "type" => "text", "text" => block["text"] }
|
|
67
|
-
yield Event.new(type: :text, payload: { text: block["text"] })
|
|
68
|
-
when "tool_use"
|
|
69
|
-
@tool_calls << block["id"]
|
|
70
|
-
@blocks << { "type" => "tool_call", "id" => block["id"], "name" => block["name"], "arguments" => block["input"] }
|
|
71
|
-
yield Event.new(type: :tool_call, payload: symbolize(block))
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def extract_usage(event)
|
|
77
|
-
u = event["usage"] || {}
|
|
78
|
-
{ input_tokens: u["input_tokens"], output_tokens: u["output_tokens"], cost_usd: event["total_cost_usd"] }
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def symbolize(hash)
|
|
82
|
-
hash.transform_keys(&:to_sym)
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
end
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
module Silas
|
|
2
|
-
module AgentSdk
|
|
3
|
-
# The adapter wraps an external CLI whose JSON stream can shift between
|
|
4
|
-
# versions, so pin a tested range and fail loudly outside it.
|
|
5
|
-
module VersionGuard
|
|
6
|
-
module_function
|
|
7
|
-
|
|
8
|
-
def assert!(bin, requirement: Silas.config.agent_sdk_cli_version_range)
|
|
9
|
-
version = detect(bin)
|
|
10
|
-
raise Silas::Error, "could not determine `#{bin}` version (is the Claude CLI installed?)" if version.nil?
|
|
11
|
-
|
|
12
|
-
unless Gem::Requirement.new(requirement.split(",").map(&:strip)).satisfied_by?(Gem::Version.new(version))
|
|
13
|
-
raise Silas::Error, "claude CLI #{version} is outside the tested range (#{requirement}); pin or upgrade"
|
|
14
|
-
end
|
|
15
|
-
version
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def detect(bin)
|
|
19
|
-
out = `#{bin} --version 2>/dev/null`
|
|
20
|
-
out[/\d+\.\d+\.\d+/]
|
|
21
|
-
rescue StandardError
|
|
22
|
-
nil
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
module Silas
|
|
2
|
-
module Engines
|
|
3
|
-
# The engine-owned adapter: one `claude -p` subprocess runs a whole agentic
|
|
4
|
-
# turn, calling back into Silas tools over an in-worker HTTP MCP endpoint
|
|
5
|
-
# whose tools/call goes THROUGH the Ledger (exactly-once within the run).
|
|
6
|
-
# The mirror image of :ruby_llm — Claude Code owns the loop; Silas hosts the
|
|
7
|
-
# tools and maps the NDJSON stream onto durable rows.
|
|
8
|
-
#
|
|
9
|
-
# v1 contract (honestly weaker than :ruby_llm): exactly-once WITHIN a run,
|
|
10
|
-
# approval :never tools only, and fail-closed on mid-subprocess worker kill.
|
|
11
|
-
class AgentSdk < Base
|
|
12
|
-
def self.loop_ownership = :engine
|
|
13
|
-
|
|
14
|
-
def execute_step(context, &on_event)
|
|
15
|
-
turn = context[:turn]
|
|
16
|
-
Silas::AgentSdk::VersionGuard.assert!(Silas.config.agent_sdk_claude_bin)
|
|
17
|
-
assert_api_key!
|
|
18
|
-
|
|
19
|
-
tools = allowed_tools(context[:tools])
|
|
20
|
-
server = Mcp::Server.start(turn: turn, step: context[:step], tools: tools, resolver: Silas.tool_resolver)
|
|
21
|
-
cli = nil
|
|
22
|
-
begin
|
|
23
|
-
server.await_ready!
|
|
24
|
-
cli = build_cli(context, server, tools)
|
|
25
|
-
parser = Silas::AgentSdk::StreamParser.new
|
|
26
|
-
cli.stream do |line|
|
|
27
|
-
parser.ingest(line) do |event|
|
|
28
|
-
persist_session_id!(turn, parser)
|
|
29
|
-
on_event&.call(event)
|
|
30
|
-
ActiveSupport::Notifications.instrument("silas.agent_sdk.event", turn_id: turn.id, type: event.type)
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
parser.to_result
|
|
34
|
-
ensure
|
|
35
|
-
cli&.terminate
|
|
36
|
-
server.stop
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
private
|
|
41
|
-
|
|
42
|
-
def build_cli(context, server, tools)
|
|
43
|
-
Silas::AgentSdk::Cli.new(
|
|
44
|
-
bin: Silas.config.agent_sdk_claude_bin,
|
|
45
|
-
prompt: context[:turn].input,
|
|
46
|
-
system: context[:system],
|
|
47
|
-
model: Silas.config.agent_sdk_model || context[:model],
|
|
48
|
-
mcp_url: server.mcp_url,
|
|
49
|
-
allowed: tools.map { |d| "mcp__silas__#{d['name']}" }
|
|
50
|
-
)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# v1 excludes approval-gated tools entirely (both tools/list and
|
|
54
|
-
# --allowedTools) — an engine-owned subprocess can't park cheaply.
|
|
55
|
-
def allowed_tools(definitions)
|
|
56
|
-
kept = definitions.select { |d| Silas.tool_resolver.call(d["name"]).approval_policy == :never }
|
|
57
|
-
dropped = definitions.map { |d| d["name"] } - kept.map { |d| d["name"] }
|
|
58
|
-
Rails.logger&.info("silas: :agent_sdk excludes approval-gated tools #{dropped.inspect}") if dropped.any?
|
|
59
|
-
kept
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def persist_session_id!(turn, parser)
|
|
63
|
-
return unless turn.cli_session_id.nil? && parser.session_id.present?
|
|
64
|
-
|
|
65
|
-
turn.update_columns(cli_session_id: parser.session_id)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def assert_api_key!
|
|
69
|
-
return if ENV["ANTHROPIC_API_KEY"].present?
|
|
70
|
-
|
|
71
|
-
raise Silas::BootGuardError, ":agent_sdk uses --bare (API-key auth only); set ANTHROPIC_API_KEY"
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
end
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
module Silas
|
|
2
|
-
# The engine-owned analog of StepRunner: it wraps one whole subprocess run in
|
|
3
|
-
# a single anchor Step and persists the durable result. Replay-aware and
|
|
4
|
-
# fail-closed — a resumed run whose subprocess got far enough to register a
|
|
5
|
-
# CLI session but did not finish is FAILED rather than re-spawned, because an
|
|
6
|
-
# engine-owned subprocess can't be replayed exactly-once (design risk #1).
|
|
7
|
-
module SubprocessRunner
|
|
8
|
-
module_function
|
|
9
|
-
|
|
10
|
-
# Returns :terminal or :failed.
|
|
11
|
-
def call(turn)
|
|
12
|
-
step = Step.find_or_create_by!(turn: turn, index: 0)
|
|
13
|
-
return :terminal if step.completed?
|
|
14
|
-
|
|
15
|
-
if turn.cli_session_id.present?
|
|
16
|
-
# A prior execution spawned a subprocess that never completed the step.
|
|
17
|
-
turn.finish!(:failed, reason: "agent_sdk_interrupted")
|
|
18
|
-
return :failed
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
result = Silas.resolved_engine.execute_step(engine_context(turn, step))
|
|
22
|
-
step.update!(
|
|
23
|
-
status: "completed", terminal: true,
|
|
24
|
-
response_blocks: result.blocks, stop_reason: result.stop_reason,
|
|
25
|
-
model: Silas.agent.model,
|
|
26
|
-
input_tokens: result.usage&.dig(:input_tokens),
|
|
27
|
-
output_tokens: result.usage&.dig(:output_tokens)
|
|
28
|
-
)
|
|
29
|
-
:terminal
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def engine_context(turn, step)
|
|
33
|
-
{ turn: turn, step: step, index: 0,
|
|
34
|
-
system: turn.instructions_snapshot,
|
|
35
|
-
messages: MessageBuilder.call(turn, upto_index: nil),
|
|
36
|
-
tools: Silas.tool_definitions,
|
|
37
|
-
model: Silas.agent.model,
|
|
38
|
-
limits: { max_steps: Silas.agent.max_steps } }
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|