silas 0.6.1 → 0.6.2

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: d6fb5d6a2e04f91a4bc0ac0569a37da43017f6adae7209148878a2cb8ac41a66
4
- data.tar.gz: 9d8ed143aac8d96d264a10ed3cf87c8ecee1ed838ba01ca67075a4a9a07e3b09
3
+ metadata.gz: 7a34eec0f8444f42ef91b1db155bc2786c28ce0de5ed345d9e63b39af9b29756
4
+ data.tar.gz: 94c9b0464629b6f23eab8f4811e5dd63cdec63d480388cf01c8b0b9111a43376
5
5
  SHA512:
6
- metadata.gz: e01f7110a093b91b30af1c08d4d8167aaaa24861812702c10ba5078f952e7eea4ddb5851e484f6cb2815d171271de9a45d8b7cc275e3d59dcaa2541ad9731628
7
- data.tar.gz: c84142c165b45db56d5761a64a62993e7332a2209593ff1c59afd58723beafc5e2bd2c14f1495fbb8963d664a69469f28829dece7d6e973e8a5e123963ca526b
6
+ metadata.gz: 62c5e530ccc6cfbc3cbfa7b1388b72e53545172acc054b7565930eef56541f8e839846435435585db3fcc3161d40994345b0da5e24fcdc0de1710988efc7ab35
7
+ data.tar.gz: e8c83c1733efd76810fe2d0cd5e5e57c8b1d929d1a5556d685e376c704108c550494f07e6eb332d6968e458f1bfbb85b5fc024016d1f951dfb1d6a351b6b4683
data/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.2 (2026-07-26)
4
+
5
+ The providers guide and the remaining community files.
6
+
7
+ ### Added
8
+
9
+ - **`docs/providers.md` — Providers & gateways.** How a model id picks a
10
+ provider (and why Silas stamps the resolved provider on every step row),
11
+ the OpenRouter recipe — one key, 300+ models, slash-form ids, with cost
12
+ lines and `compact_at` following the registry entry for the route you
13
+ actually run — plus OpenAI-compatible gateways (LiteLLM, Vercel AI
14
+ Gateway) via `openai_api_base`/`openrouter_api_base`, the
15
+ Bedrock/Vertex/Azure key map, local runtimes with the honest
16
+ cost-unavailable story, and `around_model_call` for failover. Every
17
+ mechanical claim verified against ruby_llm 1.16 source and a live
18
+ resolution run; Vercel endpoint details from their docs as of 2026-07-07.
19
+ Ships in the gem; linked from the configuration reference, the README,
20
+ and the installer's `ruby_llm.rb` initializer comment.
21
+ - **`CODE_OF_CONDUCT.md`** (Contributor Covenant 2.1), linked from
22
+ CONTRIBUTING and the README.
23
+ - **A pull request template** mirroring CONTRIBUTING's actual gates: both
24
+ stores, RuboCop, the chaos gate when the loop is touched, the
25
+ templates-smoke run when generators change, and a durability-notes
26
+ section for replay-path changes.
27
+
28
+ ### Fixed
29
+
30
+ - README's status line claimed 0.5.x.
31
+
3
32
  ## 0.6.1 (2026-07-26)
4
33
 
5
34
  Two live-inbox fixes found by driving real apps, a security hardening pass,
data/README.md CHANGED
@@ -100,9 +100,11 @@ release): [guarantees](https://danielstpaul.github.io/silas/guarantees).
100
100
 
101
101
  ## Status
102
102
 
103
- Early (0.5.x) and moving fast. Requires Rails >= 8.1 (Active Job
103
+ Early (0.6.x) and moving fast. Requires Rails >= 8.1 (Active Job
104
104
  Continuations) and Solid Queue >= 1.2; any model provider
105
- [RubyLLM](https://rubyllm.com) supports.
105
+ [RubyLLM](https://rubyllm.com) supports — Anthropic direct, OpenRouter,
106
+ OpenAI-compatible gateways, local runtimes
107
+ ([providers guide](https://github.com/danielstpaul/silas/blob/main/docs/providers.md)).
106
108
 
107
109
  ## Community
108
110
 
@@ -113,7 +115,9 @@ Questions, ideas, and bug reports →
113
115
 
114
116
  See [CONTRIBUTING.md](https://github.com/danielstpaul/silas/blob/main/CONTRIBUTING.md)
115
117
  — including the short list of things Silas deliberately doesn't do, and the
116
- chaos gate that protects the durability contract.
118
+ chaos gate that protects the durability contract. Everyone interacting here
119
+ is expected to follow the
120
+ [code of conduct](https://github.com/danielstpaul/silas/blob/main/CODE_OF_CONDUCT.md).
117
121
 
118
122
  ## Security
119
123
 
@@ -18,6 +18,10 @@ end
18
18
  | `around_model_call` | `nil` | Wrap every model call — e.g. `->(ctx, &call) { RubyLLM::Resilience.chain(:anthropic) { call.() } }`. |
19
19
  | `queue_name` | `:default` | Active Job queue for agent turns. |
20
20
 
21
+ Which provider serves a turn is decided by the model id — OpenRouter,
22
+ OpenAI-compatible gateways, Bedrock/Vertex/Azure, and local runtimes are all
23
+ recipes in [providers](providers.md).
24
+
21
25
  ## The loop
22
26
 
23
27
  | Option | Default | Meaning |
data/docs/providers.md ADDED
@@ -0,0 +1,171 @@
1
+ # Providers & gateways
2
+
3
+ Silas has exactly one inference seam: the `:ruby_llm` adapter. Every model
4
+ call goes through [RubyLLM](https://rubyllm.com), so every provider RubyLLM
5
+ speaks — Anthropic, OpenAI, Gemini, Bedrock, Vertex AI, Azure, Mistral,
6
+ DeepSeek, Perplexity, xAI, OpenRouter, local runtimes — is available to your
7
+ agents with zero Silas-specific glue. Keys live in
8
+ `config/initializers/ruby_llm.rb`; **which provider serves a turn is decided
9
+ by the model id**. Nothing in `app/agent/` changes when the provider does.
10
+
11
+ ## How a model id picks a provider
12
+
13
+ RubyLLM ships a model registry (1,100+ entries, refreshed upstream from
14
+ [models.dev](https://models.dev)). Silas resolves the agent's `model:` — from
15
+ `agent.yml`, falling back to `config.default_model` — against that registry;
16
+ the matching entry names the provider, and the adapter builds that provider's
17
+ client. The same entry supplies the numbers Silas runs on, per
18
+ (model, provider):
19
+
20
+ - **pricing** — the cost lines in the inbox and the `max_cost` budget;
21
+ - **context window** — the `compact_at` compaction threshold.
22
+
23
+ So switching provider is switching model id. A model the registry doesn't
24
+ know fails fast at the first step with the fix in the error
25
+ (`RubyLLM.models.refresh!`, or pick a registry model). And because a
26
+ registry's tie-breaks can change across upgrades, Silas stamps the resolved
27
+ provider on every step row — historical cost lines price against the
28
+ (model, provider) that actually served them, forever.
29
+
30
+ ## Direct providers
31
+
32
+ The installer's `config/initializers/ruby_llm.rb` maps environment keys in —
33
+ RubyLLM never reads provider keys from the environment by itself:
34
+
35
+ ```ruby
36
+ RubyLLM.configure do |c|
37
+ c.anthropic_api_key = ENV["ANTHROPIC_API_KEY"]
38
+ end
39
+ ```
40
+
41
+ Every provider follows the same pattern (`openai_api_key`,
42
+ `gemini_api_key`, …— the full list is in
43
+ [RubyLLM's configuration docs](https://rubyllm.com/configuration/)). For
44
+ cloud-platform shops the "enterprise gateway" is usually just the native
45
+ provider:
46
+
47
+ | Platform | Keys |
48
+ |---|---|
49
+ | AWS Bedrock | `bedrock_api_key`, `bedrock_secret_key`, `bedrock_region` (+ optional `bedrock_session_token`) |
50
+ | GCP Vertex AI | `vertexai_project_id`, `vertexai_location` (+ optional `vertexai_service_account_key`) |
51
+ | Azure OpenAI | `azure_api_base`, `azure_api_key` (or `azure_ai_auth_token`) |
52
+
53
+ Verify any of this with `bin/rails silas:doctor` — it reports which providers
54
+ have credentials configured and resolves your default model with its price:
55
+
56
+ ```
57
+ ✓ provider credentials — anthropic
58
+ ✓ model claude-sonnet-4-5 — anthropic · $3/$15 per MTok
59
+ ```
60
+
61
+ ## OpenRouter: one key, 300+ models
62
+
63
+ [OpenRouter](https://openrouter.ai) is a first-class RubyLLM provider, and
64
+ the registry ships its catalog (341 models as of ruby_llm 1.16) — one key
65
+ buys your agents Claude, GPT, Gemini, Llama, DeepSeek and the rest, billed
66
+ in one place.
67
+
68
+ ```ruby
69
+ # config/initializers/ruby_llm.rb
70
+ RubyLLM.configure do |c|
71
+ c.openrouter_api_key = ENV["OPENROUTER_API_KEY"]
72
+ end
73
+ ```
74
+
75
+ Routed models use slash-form ids — `creator/model`, exactly as OpenRouter
76
+ lists them:
77
+
78
+ ```yaml
79
+ # app/agent/agent.yml
80
+ model: anthropic/claude-sonnet-4.5 # via OpenRouter
81
+ ```
82
+
83
+ or globally:
84
+
85
+ ```ruby
86
+ Silas.configure do |c|
87
+ c.default_model = "anthropic/claude-sonnet-4.5"
88
+ end
89
+ ```
90
+
91
+ The two id families never collide: `claude-sonnet-4-5` is Anthropic direct,
92
+ `anthropic/claude-sonnet-4.5` is the same model via OpenRouter, and each
93
+ resolves to its own registry entry. That per-route entry is the one your
94
+ cost lines and compaction thresholds follow — the route you run, not the
95
+ origin provider. Concretely, in the shipped registry the direct entry lists
96
+ a 200K context window while OpenRouter's route lists 1M, so `compact_at`
97
+ triggers where the route actually overflows.
98
+
99
+ `silas:doctor` confirms the whole chain:
100
+
101
+ ```
102
+ ✓ provider credentials — openrouter
103
+ ✓ model anthropic/claude-sonnet-4.5 — openrouter · $3/$15 per MTok
104
+ ```
105
+
106
+ ## OpenAI-compatible gateways
107
+
108
+ Self-hosted and enterprise gateways (LiteLLM, Vercel AI Gateway, an internal
109
+ proxy) mostly speak the OpenAI chat-completions dialect. Two shapes:
110
+
111
+ **The gateway serves OpenAI model ids** (`gpt-5.2`, …) — point the OpenAI
112
+ provider at it:
113
+
114
+ ```ruby
115
+ RubyLLM.configure do |c|
116
+ c.openai_api_key = ENV["GATEWAY_API_KEY"]
117
+ c.openai_api_base = "https://gateway.internal/v1"
118
+ end
119
+ ```
120
+
121
+ **The gateway serves models.dev slash ids** (`anthropic/claude-sonnet-4.5`,
122
+ …, as Vercel's AI Gateway does) — repoint the OpenRouter provider, which
123
+ already speaks plain chat-completions against exactly those ids:
124
+
125
+ ```ruby
126
+ RubyLLM.configure do |c|
127
+ c.openrouter_api_key = ENV["AI_GATEWAY_API_KEY"]
128
+ c.openrouter_api_base = "https://ai-gateway.vercel.sh/v1"
129
+ end
130
+ ```
131
+
132
+ Either way the model id must still resolve in the registry — the registry
133
+ entry is where Silas gets pricing and the context window, and a gateway
134
+ that bills differently can be corrected per model with the
135
+ `config.model_prices` override ([configuration](configuration.md)).
136
+
137
+ Two gateway footnotes, both cheap to check with one real turn:
138
+
139
+ - RubyLLM renders system messages as role `developer` (OpenAI's current
140
+ dialect) through these providers. OpenRouter normalises it; if your
141
+ gateway insists on `system`, set `c.openai_use_system_role = true`.
142
+ - Streaming must pass through as SSE. If the operator inbox shows a turn
143
+ completing without live text, the gateway buffered the stream.
144
+
145
+ ## Local runtimes
146
+
147
+ Ollama and GPUStack are RubyLLM providers too (`ollama_api_base`,
148
+ `gpustack_api_base`/`gpustack_api_key`). Local models aren't in the shipped
149
+ registry, so refresh it after configuring — `RubyLLM.models.refresh!` asks
150
+ every configured provider for its live model list and merges the results —
151
+ then use the id it lists. Local models carry no registry pricing: the inbox
152
+ shows their token counts with "cost unavailable", and a `max_cost` budget
153
+ can't bind (only priced tokens count toward it) — list the model in
154
+ `config.model_prices` to restore both.
155
+
156
+ ## Failover and wrapping
157
+
158
+ Provider outages, rate-limit retries and model failover belong at the
159
+ inference seam, not in your tools. `config.around_model_call` wraps every
160
+ model call the loop makes:
161
+
162
+ ```ruby
163
+ Silas.configure do |c|
164
+ c.around_model_call = ->(ctx, &call) do
165
+ RubyLLM::Resilience.chain(:anthropic) { call.() }
166
+ end
167
+ end
168
+ ```
169
+
170
+ Whatever runs inside still lands in the same durable step — a failover
171
+ retry that succeeds is recorded exactly like a first-try success.
@@ -3,7 +3,9 @@
3
3
  #
4
4
  # export ANTHROPIC_API_KEY=sk-ant-...
5
5
  #
6
- # Any provider RubyLLM supports works the same way (openai_api_key, etc.).
6
+ # Any provider RubyLLM supports works the same way (openai_api_key, etc.)
7
+ # OpenRouter, OpenAI-compatible gateways, and local runtimes are covered in
8
+ # the Silas guide docs/providers.md (shipped in the gem: `bundle show silas`).
7
9
  RubyLLM.configure do |c|
8
10
  # Silas never uses RubyLLM's acts_as_* ActiveRecord mixins (it owns its own
9
11
  # durable schema), so opt into the new API to silence the legacy deprecation
data/lib/silas/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Silas
2
- VERSION = "0.6.1"
2
+ VERSION = "0.6.2"
3
3
  end
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.6.1
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel St Paul
@@ -177,6 +177,7 @@ files:
177
177
  - docs/guarantees.md
178
178
  - docs/inbox-and-api.md
179
179
  - docs/memory.md
180
+ - docs/providers.md
180
181
  - docs/sandbox.md
181
182
  - docs/tools.md
182
183
  - docs/tutorial.md