Class: Silas::Configuration
- Inherits:
-
Object
- Object
- Silas::Configuration
- Defined in:
- lib/silas/configuration.rb
Instance Attribute Summary collapse
-
#adapter ⇒ Object
Inference adapter seam: :ruby_llm, or any object responding to #execute_step.
-
#agent_override ⇒ Object
Subagents: the roster (name+description) and per-name scope builders, plus the active-agent overrides swapped in during a nested run.
-
#api_actor ⇒ Object
config.auth and the agent_sdk_* options were removed with the :agent_sdk adapter in 0.2 (warning no-ops for one release) and hard-removed in 0.3 — a leftover write now raises NoMethodError.
-
#api_auth ⇒ Object
config.auth and the agent_sdk_* options were removed with the :agent_sdk adapter in 0.2 (warning no-ops for one release) and hard-removed in 0.3 — a leftover write now raises NoMethodError.
-
#api_stream_max_duration ⇒ Object
config.auth and the agent_sdk_* options were removed with the :agent_sdk adapter in 0.2 (warning no-ops for one release) and hard-removed in 0.3 — a leftover write now raises NoMethodError.
-
#api_stream_poll_interval ⇒ Object
config.auth and the agent_sdk_* options were removed with the :agent_sdk adapter in 0.2 (warning no-ops for one release) and hard-removed in 0.3 — a leftover write now raises NoMethodError.
-
#approval_ttl ⇒ Object
How long a parked approval (or in-doubt invocation) waits before expiring.
-
#around_model_call ⇒ Object
Optional hook wrapping every model call — e.g.
-
#ask_question ⇒ Object
The ask_question builtin (agent parks to ask the operator something; answered from the inbox/API).
-
#channel_resolver ⇒ Object
Channels: name -> Channel subclass (wired by the Registry).
-
#compact_at ⇒ Object
Context compaction trigger.
-
#default_model ⇒ Object
Default model when agent.yml doesn't specify one.
-
#definitions_digest ⇒ Object
Injectable seams until/beyond the Registry: name -> tool object, the tool schema list for the model, and the definitions digest for the nondeterminism guard.
-
#eval_dir ⇒ Object
Evals: where *_eval.rb scenarios live, and an optional custom LLM grader.
-
#eval_grader ⇒ Object
Evals: where *_eval.rb scenarios live, and an optional custom LLM grader.
-
#inbox_actor ⇒ Object
Inbox (mountable UI at /silas/inbox).
-
#inbox_auth ⇒ Object
Inbox (mountable UI at /silas/inbox).
-
#inbox_public_read ⇒ Object
Inbox (mountable UI at /silas/inbox).
-
#inbox_streaming ⇒ Object
Force-disable live broadcasting even when turbo-rails is present (nil = auto).
-
#instructions_dir ⇒ Object
Subagents: the roster (name+description) and per-name scope builders, plus the active-agent overrides swapped in during a nested run.
-
#isolate_steps ⇒ Object
Continuation isolation for loop steps.
-
#max_steps ⇒ Object
Hard cap on model calls per turn (agent.yml can lower it per-agent).
-
#mcp_client_factory ⇒ Object
Connections: a test seam for injecting a fake MCP client ->(connection){ client }.
-
#mcp_server_host ⇒ Object
Bind host for the in-process MCP server (Mcp::Server — the "mount your tools as MCP" seam).
-
#memory ⇒ Object
Memory (silas_memories): memory=false disables entirely; memory_approval :always parks every remember for a human (default), :never auto-approves.
-
#memory_approval ⇒ Object
Memory (silas_memories): memory=false disables entirely; memory_approval :always parks every remember for a human (default), :never auto-approves.
-
#memory_injection_limit ⇒ Object
Memory (silas_memories): memory=false disables entirely; memory_approval :always parks every remember for a human (default), :never auto-approves.
-
#model_prices ⇒ Object
Inbox (mountable UI at /silas/inbox).
-
#named_agent_scopes ⇒ Object
Named top-level agents (app/agents/
/): lambda -> { name => AgentScope }. -
#queue_name ⇒ Object
Active Job queue for agent turns.
-
#sandbox ⇒ Object
Sandbox: :none (default, code exec off) | :docker | any object responding to #run.
-
#sandbox_cpus ⇒ Object
Sandbox: :none (default, code exec off) | :docker | any object responding to #run.
-
#sandbox_docker_bin ⇒ Object
Sandbox: :none (default, code exec off) | :docker | any object responding to #run.
-
#sandbox_image ⇒ Object
Sandbox: :none (default, code exec off) | :docker | any object responding to #run.
-
#sandbox_memory ⇒ Object
Sandbox: :none (default, code exec off) | :docker | any object responding to #run.
-
#sandbox_network ⇒ Object
Sandbox: :none (default, code exec off) | :docker | any object responding to #run.
-
#sandbox_pids ⇒ Object
Sandbox: :none (default, code exec off) | :docker | any object responding to #run.
-
#sandbox_timeout ⇒ Object
Sandbox: :none (default, code exec off) | :docker | any object responding to #run.
-
#sandbox_workdir ⇒ Object
Sandbox: :none (default, code exec off) | :docker | any object responding to #run.
-
#schedules ⇒ Object
Injectable seams until/beyond the Registry: name -> tool object, the tool schema list for the model, and the definitions digest for the nondeterminism guard.
-
#skills ⇒ Object
Injectable seams until/beyond the Registry: name -> tool object, the tool schema list for the model, and the definitions digest for the nondeterminism guard.
- #slack_bot_token ⇒ Object
- #slack_signing_secret ⇒ Object
-
#subagent_index ⇒ Object
Subagents: the roster (name+description) and per-name scope builders, plus the active-agent overrides swapped in during a nested run.
-
#subagent_scopes ⇒ Object
Subagents: the roster (name+description) and per-name scope builders, plus the active-agent overrides swapped in during a nested run.
-
#tool_definitions ⇒ Object
Injectable seams until/beyond the Registry: name -> tool object, the tool schema list for the model, and the definitions digest for the nondeterminism guard.
-
#tool_resolver ⇒ Object
Injectable seams until/beyond the Registry: name -> tool object, the tool schema list for the model, and the definitions digest for the nondeterminism guard.
Instance Method Summary collapse
-
#boot_guard! ⇒ Object
Fail-loud misconfiguration checks, run from Silas.configure and at boot.
-
#check_provider_credentials! ⇒ Object
The most common first-run failure: no provider key configured, so the first turn dies deep inside RubyLLM with a third-party error.
-
#engine ⇒ Object
Renamed in 0.4, removed in 2.0.
- #engine=(value) ⇒ Object
-
#initialize ⇒ Configuration
constructor
A new instance of Configuration.
- #validate! ⇒ Object
-
#warn_unsafe_queue_adapter! ⇒ Object
Silas's durability and exactly-once guarantees rest on the queue adapter: a turn's jobs must run one-at-a-time and continuation retries must run in a FRESH execution after the prior one committed.
Constructor Details
#initialize ⇒ Configuration
Returns a new instance of Configuration.
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/silas/configuration.rb', line 114 def initialize @adapter = :ruby_llm # Must be resolvable by the installed ruby_llm's model registry — newer # Claude models may need `RubyLLM.models.refresh!` before they resolve. # (Sonnet 4.5 ships in every supported registry; never default a first # run to the priciest model.) @default_model = "claude-sonnet-4-5" @queue_name = :default @around_model_call = nil @approval_ttl = 7.days @max_steps = 25 @compact_at = 0.9 @isolate_steps = true @tool_resolver = nil @tool_definitions = nil @definitions_digest = nil @skills = nil @schedules = nil @subagent_index = nil @subagent_scopes = nil @agent_override = nil @instructions_dir = nil @channel_resolver = nil @slack_signing_secret = nil @slack_bot_token = nil @mcp_server_host = "127.0.0.1" @eval_dir = "test/agent_evals" @eval_grader = nil @sandbox = :none @memory = true @memory_approval = :always @memory_injection_limit = 8 @ask_question = true @sandbox_image = nil @sandbox_network = "none" @sandbox_memory = "512m" @sandbox_cpus = "1" @sandbox_pids = 256 @sandbox_workdir = "/workspace" @sandbox_docker_bin = "docker" @sandbox_timeout = 30 @api_auth = ->(controller) { controller.head :not_found } # deny by default @api_actor = ->(_controller) { "api" } @api_stream_poll_interval = 0.5 @api_stream_max_duration = 300 @inbox_auth = ->(controller) { controller.head :not_found } # deny by default @inbox_public_read = false @inbox_actor = ->(controller) { controller.try(:current_user)&.try(:email) || "inbox" } # OVERRIDE map only — pricing comes from RubyLLM's model registry # (1,100+ models, refreshed upstream from models.dev). List a model here # to beat the registry: fine-tunes, custom deployments, models newer # than the installed registry. Units: per 1k tokens, 1e6 units = $1. @model_prices = {} end |
Instance Attribute Details
#adapter ⇒ Object
Inference adapter seam: :ruby_llm, or any object responding to
#execute_step. (Named engine before 0.4 — see the deprecated alias
below; "engine" already meant the Rails engine at Silas::Engine.)
6 7 8 |
# File 'lib/silas/configuration.rb', line 6 def adapter @adapter end |
#agent_override ⇒ Object
Subagents: the roster (name+description) and per-name scope builders, plus the active-agent overrides swapped in during a nested run.
36 37 38 |
# File 'lib/silas/configuration.rb', line 36 def agent_override @agent_override end |
#api_actor ⇒ Object
config.auth and the agent_sdk_* options were removed with the :agent_sdk adapter in 0.2 (warning no-ops for one release) and hard-removed in 0.3 — a leftover write now raises NoMethodError. Delete them from your initializer. JSON API (mounted under /silas/api/v1). api_auth — deny-by-default lambda, same contract as inbox_auth: the host DENIES by rendering (or head-ing) and PASSES by not rendering. Wire a token check, Devise, whatever you run. api_actor — controller -> identity string recorded on approvals made through the API (approved_by / declined by). api_stream_poll_interval — seconds between SSE row polls. api_stream_max_duration — seconds before an SSE stream closes itself (clients reconnect with Last-Event-ID); bounds thread hold.
84 85 86 |
# File 'lib/silas/configuration.rb', line 84 def api_actor @api_actor end |
#api_auth ⇒ Object
config.auth and the agent_sdk_* options were removed with the :agent_sdk adapter in 0.2 (warning no-ops for one release) and hard-removed in 0.3 — a leftover write now raises NoMethodError. Delete them from your initializer. JSON API (mounted under /silas/api/v1). api_auth — deny-by-default lambda, same contract as inbox_auth: the host DENIES by rendering (or head-ing) and PASSES by not rendering. Wire a token check, Devise, whatever you run. api_actor — controller -> identity string recorded on approvals made through the API (approved_by / declined by). api_stream_poll_interval — seconds between SSE row polls. api_stream_max_duration — seconds before an SSE stream closes itself (clients reconnect with Last-Event-ID); bounds thread hold.
84 85 86 |
# File 'lib/silas/configuration.rb', line 84 def api_auth @api_auth end |
#api_stream_max_duration ⇒ Object
config.auth and the agent_sdk_* options were removed with the :agent_sdk adapter in 0.2 (warning no-ops for one release) and hard-removed in 0.3 — a leftover write now raises NoMethodError. Delete them from your initializer. JSON API (mounted under /silas/api/v1). api_auth — deny-by-default lambda, same contract as inbox_auth: the host DENIES by rendering (or head-ing) and PASSES by not rendering. Wire a token check, Devise, whatever you run. api_actor — controller -> identity string recorded on approvals made through the API (approved_by / declined by). api_stream_poll_interval — seconds between SSE row polls. api_stream_max_duration — seconds before an SSE stream closes itself (clients reconnect with Last-Event-ID); bounds thread hold.
84 85 86 |
# File 'lib/silas/configuration.rb', line 84 def api_stream_max_duration @api_stream_max_duration end |
#api_stream_poll_interval ⇒ Object
config.auth and the agent_sdk_* options were removed with the :agent_sdk adapter in 0.2 (warning no-ops for one release) and hard-removed in 0.3 — a leftover write now raises NoMethodError. Delete them from your initializer. JSON API (mounted under /silas/api/v1). api_auth — deny-by-default lambda, same contract as inbox_auth: the host DENIES by rendering (or head-ing) and PASSES by not rendering. Wire a token check, Devise, whatever you run. api_actor — controller -> identity string recorded on approvals made through the API (approved_by / declined by). api_stream_poll_interval — seconds between SSE row polls. api_stream_max_duration — seconds before an SSE stream closes itself (clients reconnect with Last-Event-ID); bounds thread hold.
84 85 86 |
# File 'lib/silas/configuration.rb', line 84 def api_stream_poll_interval @api_stream_poll_interval end |
#approval_ttl ⇒ Object
How long a parked approval (or in-doubt invocation) waits before expiring. eve parks forever; Silas expires (parked-forever ghosts are a bug, not a feature).
16 17 18 |
# File 'lib/silas/configuration.rb', line 16 def approval_ttl @approval_ttl end |
#around_model_call ⇒ Object
Optional hook wrapping every model call — e.g. ruby_llm-resilience:
config.around_model_call = ->(ctx, &call) { RubyLLM::Resilience.chain(:anthropic) { call.() } }
13 14 15 |
# File 'lib/silas/configuration.rb', line 13 def around_model_call @around_model_call end |
#ask_question ⇒ Object
The ask_question builtin (agent parks to ask the operator something; answered from the inbox/API). false removes it from the toolset — note that adding/removing a builtin changes the definitions digest, so turns PARKED across that change fail loudly on resume (the nondeterminism guard working as designed). Settle parked turns before flipping this.
49 50 51 |
# File 'lib/silas/configuration.rb', line 49 def ask_question @ask_question end |
#channel_resolver ⇒ Object
Channels: name -> Channel subclass (wired by the Registry). Slack creds default to credentials.dig(:silas, :slack, ...); nil disables Slack.
52 53 54 |
# File 'lib/silas/configuration.rb', line 52 def channel_resolver @channel_resolver end |
#compact_at ⇒ Object
Context compaction trigger. A Float in (0, 1] compacts when the measured context passes that fraction of the model's registry context window (models the registry doesn't know are never compacted). An Integer is an absolute token threshold — the form custom adapters need, since they have no registry entry. nil/false disables. Compaction summarises PRIOR turns into a persisted row (exactly-once, replay-deterministic); the current turn is never compacted.
26 27 28 |
# File 'lib/silas/configuration.rb', line 26 def compact_at @compact_at end |
#default_model ⇒ Object
Default model when agent.yml doesn't specify one.
8 9 10 |
# File 'lib/silas/configuration.rb', line 8 def default_model @default_model end |
#definitions_digest ⇒ Object
Injectable seams until/beyond the Registry: name -> tool object, the tool schema list for the model, and the definitions digest for the nondeterminism guard. The Registry wires real defaults at boot.
33 34 35 |
# File 'lib/silas/configuration.rb', line 33 def definitions_digest @definitions_digest end |
#eval_dir ⇒ Object
Evals: where *_eval.rb scenarios live, and an optional custom LLM grader.
98 99 100 |
# File 'lib/silas/configuration.rb', line 98 def eval_dir @eval_dir end |
#eval_grader ⇒ Object
Evals: where *_eval.rb scenarios live, and an optional custom LLM grader.
98 99 100 |
# File 'lib/silas/configuration.rb', line 98 def eval_grader @eval_grader end |
#inbox_actor ⇒ Object
Inbox (mountable UI at /silas/inbox). inbox_auth — deny-by-default lambda; the host renders/head-404s to DENY and passes by NOT rendering (resilience pattern). inbox_public_read — reads render for anyone; approve/decline still gated. inbox_actor — controller -> identity string (approved_by/decline by:). model_prices — OVERRIDE map: model id -> out: cost-units per 1k tokens, 1e6 units = $1 (a $3/M-token rate is 3000). Beats the RubyLLM registry, which prices everything else per (model, provider).
94 95 96 |
# File 'lib/silas/configuration.rb', line 94 def inbox_actor @inbox_actor end |
#inbox_auth ⇒ Object
Inbox (mountable UI at /silas/inbox). inbox_auth — deny-by-default lambda; the host renders/head-404s to DENY and passes by NOT rendering (resilience pattern). inbox_public_read — reads render for anyone; approve/decline still gated. inbox_actor — controller -> identity string (approved_by/decline by:). model_prices — OVERRIDE map: model id -> out: cost-units per 1k tokens, 1e6 units = $1 (a $3/M-token rate is 3000). Beats the RubyLLM registry, which prices everything else per (model, provider).
94 95 96 |
# File 'lib/silas/configuration.rb', line 94 def inbox_auth @inbox_auth end |
#inbox_public_read ⇒ Object
Inbox (mountable UI at /silas/inbox). inbox_auth — deny-by-default lambda; the host renders/head-404s to DENY and passes by NOT rendering (resilience pattern). inbox_public_read — reads render for anyone; approve/decline still gated. inbox_actor — controller -> identity string (approved_by/decline by:). model_prices — OVERRIDE map: model id -> out: cost-units per 1k tokens, 1e6 units = $1 (a $3/M-token rate is 3000). Beats the RubyLLM registry, which prices everything else per (model, provider).
94 95 96 |
# File 'lib/silas/configuration.rb', line 94 def inbox_public_read @inbox_public_read end |
#inbox_streaming ⇒ Object
Force-disable live broadcasting even when turbo-rails is present (nil = auto).
96 97 98 |
# File 'lib/silas/configuration.rb', line 96 def inbox_streaming @inbox_streaming end |
#instructions_dir ⇒ Object
Subagents: the roster (name+description) and per-name scope builders, plus the active-agent overrides swapped in during a nested run.
36 37 38 |
# File 'lib/silas/configuration.rb', line 36 def instructions_dir @instructions_dir end |
#isolate_steps ⇒ Object
Continuation isolation for loop steps. true in production (persistence per step — the durability contract); specs may disable for inline runs.
29 30 31 |
# File 'lib/silas/configuration.rb', line 29 def isolate_steps @isolate_steps end |
#max_steps ⇒ Object
Hard cap on model calls per turn (agent.yml can lower it per-agent).
18 19 20 |
# File 'lib/silas/configuration.rb', line 18 def max_steps @max_steps end |
#mcp_client_factory ⇒ Object
Connections: a test seam for injecting a fake MCP client ->(connection){ client }.
100 101 102 |
# File 'lib/silas/configuration.rb', line 100 def mcp_client_factory @mcp_client_factory end |
#mcp_server_host ⇒ Object
Bind host for the in-process MCP server (Mcp::Server — the "mount your tools as MCP" seam).
56 57 58 |
# File 'lib/silas/configuration.rb', line 56 def mcp_server_host @mcp_server_host end |
#memory ⇒ Object
Memory (silas_memories): memory=false disables entirely; memory_approval :always parks every remember for a human (default), :never auto-approves.
43 44 45 |
# File 'lib/silas/configuration.rb', line 43 def memory @memory end |
#memory_approval ⇒ Object
Memory (silas_memories): memory=false disables entirely; memory_approval :always parks every remember for a human (default), :never auto-approves.
43 44 45 |
# File 'lib/silas/configuration.rb', line 43 def memory_approval @memory_approval end |
#memory_injection_limit ⇒ Object
Memory (silas_memories): memory=false disables entirely; memory_approval :always parks every remember for a human (default), :never auto-approves.
43 44 45 |
# File 'lib/silas/configuration.rb', line 43 def memory_injection_limit @memory_injection_limit end |
#model_prices ⇒ Object
Inbox (mountable UI at /silas/inbox). inbox_auth — deny-by-default lambda; the host renders/head-404s to DENY and passes by NOT rendering (resilience pattern). inbox_public_read — reads render for anyone; approve/decline still gated. inbox_actor — controller -> identity string (approved_by/decline by:). model_prices — OVERRIDE map: model id -> out: cost-units per 1k tokens, 1e6 units = $1 (a $3/M-token rate is 3000). Beats the RubyLLM registry, which prices everything else per (model, provider).
94 95 96 |
# File 'lib/silas/configuration.rb', line 94 def model_prices @model_prices end |
#named_agent_scopes ⇒ Object
Named top-level agents (app/agents/
39 40 41 |
# File 'lib/silas/configuration.rb', line 39 def named_agent_scopes @named_agent_scopes end |
#queue_name ⇒ Object
Active Job queue for agent turns.
10 11 12 |
# File 'lib/silas/configuration.rb', line 10 def queue_name @queue_name end |
#sandbox ⇒ Object
Sandbox: :none (default, code exec off) | :docker | any object responding to #run. Docker knobs are inert unless :docker.
103 104 105 |
# File 'lib/silas/configuration.rb', line 103 def sandbox @sandbox end |
#sandbox_cpus ⇒ Object
Sandbox: :none (default, code exec off) | :docker | any object responding to #run. Docker knobs are inert unless :docker.
103 104 105 |
# File 'lib/silas/configuration.rb', line 103 def sandbox_cpus @sandbox_cpus end |
#sandbox_docker_bin ⇒ Object
Sandbox: :none (default, code exec off) | :docker | any object responding to #run. Docker knobs are inert unless :docker.
103 104 105 |
# File 'lib/silas/configuration.rb', line 103 def sandbox_docker_bin @sandbox_docker_bin end |
#sandbox_image ⇒ Object
Sandbox: :none (default, code exec off) | :docker | any object responding to #run. Docker knobs are inert unless :docker.
103 104 105 |
# File 'lib/silas/configuration.rb', line 103 def sandbox_image @sandbox_image end |
#sandbox_memory ⇒ Object
Sandbox: :none (default, code exec off) | :docker | any object responding to #run. Docker knobs are inert unless :docker.
103 104 105 |
# File 'lib/silas/configuration.rb', line 103 def sandbox_memory @sandbox_memory end |
#sandbox_network ⇒ Object
Sandbox: :none (default, code exec off) | :docker | any object responding to #run. Docker knobs are inert unless :docker.
103 104 105 |
# File 'lib/silas/configuration.rb', line 103 def sandbox_network @sandbox_network end |
#sandbox_pids ⇒ Object
Sandbox: :none (default, code exec off) | :docker | any object responding to #run. Docker knobs are inert unless :docker.
103 104 105 |
# File 'lib/silas/configuration.rb', line 103 def sandbox_pids @sandbox_pids end |
#sandbox_timeout ⇒ Object
Sandbox: :none (default, code exec off) | :docker | any object responding to #run. Docker knobs are inert unless :docker.
103 104 105 |
# File 'lib/silas/configuration.rb', line 103 def sandbox_timeout @sandbox_timeout end |
#sandbox_workdir ⇒ Object
Sandbox: :none (default, code exec off) | :docker | any object responding to #run. Docker knobs are inert unless :docker.
103 104 105 |
# File 'lib/silas/configuration.rb', line 103 def sandbox_workdir @sandbox_workdir end |
#schedules ⇒ Object
Injectable seams until/beyond the Registry: name -> tool object, the tool schema list for the model, and the definitions digest for the nondeterminism guard. The Registry wires real defaults at boot.
33 34 35 |
# File 'lib/silas/configuration.rb', line 33 def schedules @schedules end |
#skills ⇒ Object
Injectable seams until/beyond the Registry: name -> tool object, the tool schema list for the model, and the definitions digest for the nondeterminism guard. The Registry wires real defaults at boot.
33 34 35 |
# File 'lib/silas/configuration.rb', line 33 def skills @skills end |
#slack_bot_token ⇒ Object
110 111 112 |
# File 'lib/silas/configuration.rb', line 110 def slack_bot_token @slack_bot_token || Rails.application.credentials.dig(:silas, :slack, :bot_token) end |
#slack_signing_secret ⇒ Object
106 107 108 |
# File 'lib/silas/configuration.rb', line 106 def slack_signing_secret @slack_signing_secret || Rails.application.credentials.dig(:silas, :slack, :signing_secret) end |
#subagent_index ⇒ Object
Subagents: the roster (name+description) and per-name scope builders, plus the active-agent overrides swapped in during a nested run.
36 37 38 |
# File 'lib/silas/configuration.rb', line 36 def subagent_index @subagent_index end |
#subagent_scopes ⇒ Object
Subagents: the roster (name+description) and per-name scope builders, plus the active-agent overrides swapped in during a nested run.
36 37 38 |
# File 'lib/silas/configuration.rb', line 36 def subagent_scopes @subagent_scopes end |
#tool_definitions ⇒ Object
Injectable seams until/beyond the Registry: name -> tool object, the tool schema list for the model, and the definitions digest for the nondeterminism guard. The Registry wires real defaults at boot.
33 34 35 |
# File 'lib/silas/configuration.rb', line 33 def tool_definitions @tool_definitions end |
#tool_resolver ⇒ Object
Injectable seams until/beyond the Registry: name -> tool object, the tool schema list for the model, and the definitions digest for the nondeterminism guard. The Registry wires real defaults at boot.
33 34 35 |
# File 'lib/silas/configuration.rb', line 33 def tool_resolver @tool_resolver end |
Instance Method Details
#boot_guard! ⇒ Object
Fail-loud misconfiguration checks, run from Silas.configure and at boot.
175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/silas/configuration.rb', line 175 def boot_guard! if adapter == :agent_sdk raise BootGuardError, "the :agent_sdk adapter was removed in Silas 0.2 — the claude -p subprocess " \ "integration is gone (its subscription-auth rationale was unreachable). " \ "Use adapter :ruby_llm, the production path." end check_provider_credentials! warn_unsafe_queue_adapter! end |
#check_provider_credentials! ⇒ Object
The most common first-run failure: no provider key configured, so the first turn dies deep inside RubyLLM with a third-party error. Surface it at boot with the fix. Raises in production (a keyless prod deploy is always a misconfiguration); warns in development so a fresh app can still boot and browse the inbox before a key exists.
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/silas/configuration.rb', line 192 def check_provider_credentials! return unless adapter == :ruby_llm && defined?(::RubyLLM) providers = ::RubyLLM::Provider.providers.values configured = providers.any? do |provider| requirements = provider.configuration_requirements requirements.any? && requirements.all? { |key| ::RubyLLM.config.public_send(key).present? } end return if configured = "[Silas] adapter :ruby_llm has no configured provider — no API key is set on " \ "RubyLLM.config. Set one in config/initializers/ruby_llm.rb, e.g. " \ "RubyLLM.configure { |c| c.anthropic_api_key = ENV[\"ANTHROPIC_API_KEY\"] } " \ "— the first agent turn will fail without it." raise BootGuardError, if defined?(::Rails) && ::Rails.env.production? (defined?(::Rails) && ::Rails.logger ? ::Rails.logger.warn() : nil) || Kernel.warn() rescue BootGuardError raise rescue StandardError nil # a diagnostic must never break boot end |
#engine ⇒ Object
Renamed in 0.4, removed in 2.0. "engine" meant two unrelated things — the Rails engine (Silas::Engine) and the inference backend — the exact collision ActiveJob avoids by calling its seam QueueAdapters.
61 62 63 64 |
# File 'lib/silas/configuration.rb', line 61 def engine Silas.deprecator.warn("config.engine is deprecated; use config.adapter") adapter end |
#engine=(value) ⇒ Object
66 67 68 69 |
# File 'lib/silas/configuration.rb', line 66 def engine=(value) Silas.deprecator.warn("config.engine= is deprecated; use config.adapter=") self.adapter = value end |
#validate! ⇒ Object
169 170 171 172 |
# File 'lib/silas/configuration.rb', line 169 def validate! boot_guard! self end |
#warn_unsafe_queue_adapter! ⇒ Object
Silas's durability and exactly-once guarantees rest on the queue adapter: a turn's jobs must run one-at-a-time and continuation retries must run in a FRESH execution after the prior one committed. The in-process Async adapter (ActiveJob's dev default) breaks both — it runs a re-enqueued continuation on a thread pool concurrently with the original, which double-executes steps and violates the single-active-turn invariant (a re-run model call mints new tool_call ids the ledger cannot dedup). Solid Queue — or any durable, serializing, DB-backed adapter — is required to run agents; the synchronous :inline adapter is safe for scripts and demos (no durability).
RAISES in production — running agents on the Async adapter there silently voids the durability contract. Warns in development (Rails' dev default is :async and a fresh app must still boot).
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
# File 'lib/silas/configuration.rb', line 228 def warn_unsafe_queue_adapter! return unless defined?(::ActiveJob::Base) name = ::ActiveJob::Base.queue_adapter.class.name.to_s return unless name.include?("AsyncAdapter") = "[Silas] ActiveJob is using the in-process Async queue adapter. " \ "This runs continuation retries on a thread pool concurrently with " \ "the original job, which double-executes agent steps and breaks " \ "exactly-once tool effects. Use :solid_queue (production) or " \ ":inline (scripts/demos). See Silas DEPLOY.md." raise BootGuardError, if defined?(::Rails) && ::Rails.env.production? (defined?(::Rails) && ::Rails.logger ? ::Rails.logger.warn() : nil) || Kernel.warn() rescue BootGuardError raise rescue StandardError # A diagnostic must never break boot. nil end |