little_ghost 0.7.0 → 0.8.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/README.md +33 -5
- data/docs/guides/code_mode.md +2 -2
- data/docs/guides/getting_started.md +26 -1
- data/docs/guides/integrations.md +20 -161
- data/docs/guides/mcp.md +288 -0
- data/docs/guides/models_and_providers.md +21 -54
- data/docs/guides/production.md +14 -5
- data/docs/guides/providers.md +275 -0
- data/docs/guides/sandboxing.md +42 -7
- data/docs/guides/skills.md +1 -3
- data/docs/guides/tools.md +2 -0
- data/exe/little_ghost +12 -0
- data/lib/little_ghost/assembly.rb +11 -3
- data/lib/little_ghost/assembly_builder.rb +2 -2
- data/lib/little_ghost/cli.rb +138 -0
- data/lib/little_ghost/console.rb +17 -0
- data/lib/little_ghost/errors.rb +1 -1
- data/lib/little_ghost/generators/application_generator.rb +179 -0
- data/lib/little_ghost/generators/templates/application/Gemfile.tt +3 -0
- data/lib/little_ghost/generators/templates/application/README.md.tt +29 -0
- data/lib/little_ghost/generators/templates/application/app/agents/application_agent.rb.tt +5 -0
- data/lib/little_ghost/generators/templates/application/app/prompts/application/system.erb.tt +1 -0
- data/lib/little_ghost/generators/templates/application/bin/application.tt +10 -0
- data/lib/little_ghost/generators/templates/application/config/little_ghost.rb.tt +5 -0
- data/lib/little_ghost/generators/templates/application/gitignore.tt +2 -0
- data/lib/little_ghost/generators/templates/application/keep.tt +0 -0
- data/lib/little_ghost/mcp/client.rb +295 -606
- data/lib/little_ghost/mcp/toolset.rb +133 -95
- data/lib/little_ghost/mcp.rb +21 -3
- data/lib/little_ghost/model_resolver.rb +2 -0
- data/lib/little_ghost/models/catalog/models_dev_source.rb +4 -1
- data/lib/little_ghost/provider_registry.rb +1 -0
- data/lib/little_ghost/providers/lm_studio/catalog_source.rb +201 -0
- data/lib/little_ghost/providers/lm_studio.rb +52 -0
- data/lib/little_ghost/providers/openai.rb +3 -75
- data/lib/little_ghost/providers/openai_compatible.rb +78 -1
- data/lib/little_ghost/sandbox/environment_policy.rb +29 -4
- data/lib/little_ghost/sandbox/policy.rb +28 -6
- data/lib/little_ghost/sandboxes/unrestricted.rb +7 -3
- data/lib/little_ghost/skills/catalog.rb +13 -3
- data/lib/little_ghost/tool.rb +6 -1
- data/lib/little_ghost/version.rb +1 -1
- data/lib/little_ghost.rb +1 -0
- metadata +37 -7
- data/lib/little_ghost/mcp/types.rb +0 -216
|
@@ -62,11 +62,11 @@ run = CustomerSupportAgent.ask(
|
|
|
62
62
|
Build these settings in application code instead of passing request parameters
|
|
63
63
|
through unchanged. Settings can affect cost, latency, and model behavior.
|
|
64
64
|
|
|
65
|
-
##
|
|
65
|
+
## Connect the provider you chose
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
Connections may live in an initializer or in the conventional files under
|
|
68
|
+
`config/little_ghost`. [Provider Support](providers.md) has copyable settings
|
|
69
|
+
for each built-in adapter, compatible endpoints, Ollama, and LM Studio.
|
|
70
70
|
|
|
71
71
|
Keep credentials in your application's secret manager. Agents refer to a role
|
|
72
72
|
or configured connection; they don't need to contain credentials. If your
|
|
@@ -120,6 +120,11 @@ choose a structured-result strategy.
|
|
|
120
120
|
Provider capabilities can change. Handle failed Runs and provider errors even
|
|
121
121
|
when the catalog says a feature is supported.
|
|
122
122
|
|
|
123
|
+
Most application code does not call the resolver directly. Declaring an
|
|
124
|
+
Agent's model, or passing `model:` to generation or embedding, resolves the
|
|
125
|
+
selection for you. Inspect model details when an interface needs to explain
|
|
126
|
+
limits or your application wants to check an attachment before sending it.
|
|
127
|
+
|
|
123
128
|
## Call a model without an Agent
|
|
124
129
|
|
|
125
130
|
Some application work needs one model response rather than an Agent. Use
|
|
@@ -141,57 +146,23 @@ response.usage.total_tokens
|
|
|
141
146
|
```
|
|
142
147
|
|
|
143
148
|
The operation returns a `LittleGhost::RunResult`, the same result type returned
|
|
144
|
-
by an Agent invocation
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
creating a Run. Structured generation may make one additional repair request.
|
|
148
|
-
|
|
149
|
-
Pass a strict object schema when application code needs checked JSON:
|
|
149
|
+
by an Agent invocation. Application code can read `output`, `usage`, and the
|
|
150
|
+
final message in the same way. Plain generation makes one model request without
|
|
151
|
+
starting an Agent or creating a Run.
|
|
150
152
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
result_schema: {
|
|
156
|
-
name: "classification",
|
|
157
|
-
description: "Classify one support request",
|
|
158
|
-
schema: {
|
|
159
|
-
type: "object",
|
|
160
|
-
properties: {category: {type: "string"}},
|
|
161
|
-
required: ["category"],
|
|
162
|
-
additionalProperties: false
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
response.output
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
LittleGhost checks the result against the schema and gives the model one repair
|
|
171
|
-
attempt by default. Add `structured_result_repair_attempts: 3` to the call
|
|
172
|
-
above when a checked result warrants additional attempts. The setting accepts
|
|
173
|
-
integers from zero through three.
|
|
174
|
-
|
|
175
|
-
Read the checked value through `response.output`. If every permitted attempt is
|
|
176
|
-
invalid, the call raises `LittleGhost::StructuredResultError`.
|
|
177
|
-
|
|
178
|
-
A schema checks the shape of a value, not whether your application should act
|
|
179
|
-
on it. Check identifiers, permissions, and business rules before using the
|
|
180
|
-
result to change application state.
|
|
153
|
+
Pass `result_schema:` when application code needs checked JSON. [Structured
|
|
154
|
+
Results and Content](structured_outputs_and_content.md) covers strict schemas,
|
|
155
|
+
provider strategies, repair behavior, and the application checks that still
|
|
156
|
+
belong outside the schema.
|
|
181
157
|
|
|
182
158
|
## Create embeddings
|
|
183
159
|
|
|
184
160
|
Use `LittleGhost.embed` when your application needs numeric representations for
|
|
185
161
|
search, clustering, or another similarity-based feature:
|
|
186
162
|
|
|
187
|
-
The example assumes application startup maps `search_embeddings` to an
|
|
188
|
-
embedding model, following the role configuration shown earlier on this page.
|
|
189
|
-
The selected provider adapter's API reference lists the model-specific
|
|
190
|
-
settings.
|
|
191
|
-
|
|
192
163
|
```ruby
|
|
193
164
|
response = LittleGhost.embed(
|
|
194
|
-
model: :
|
|
165
|
+
model: "primary:openai/text-embedding-3-small",
|
|
195
166
|
inputs: ["Reset a password", "Track a transfer"]
|
|
196
167
|
)
|
|
197
168
|
|
|
@@ -202,14 +173,10 @@ response.usage.input_tokens
|
|
|
202
173
|
|
|
203
174
|
The response keeps vectors in the same order as the inputs. LittleGhost rejects
|
|
204
175
|
an incomplete or malformed response instead of returning a partial batch.
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
Embedding text is sent to the selected provider. Choose a provider that is
|
|
210
|
-
appropriate for that data, just as you would for an Agent request. See
|
|
211
|
-
`LittleGhost::Embeddings::Request` and your provider adapter's API reference for
|
|
212
|
-
the supported settings and request bounds.
|
|
176
|
+
Embedding text is sent to the selected provider, so choose one appropriate for
|
|
177
|
+
that data. [Provider Support](providers.md) identifies adapters with embedding
|
|
178
|
+
support; `LittleGhost::Embeddings::Request` documents settings and request
|
|
179
|
+
bounds.
|
|
213
180
|
|
|
214
181
|
Continue with [Prompts as Views](prompt_views.md) when an Agent's instructions
|
|
215
182
|
outgrow one string. See [Structured Results and Content](structured_outputs_and_content.md)
|
data/docs/guides/production.md
CHANGED
|
@@ -37,7 +37,15 @@ class CustomerSupportAgent < LittleGhost::Agent
|
|
|
37
37
|
end
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
Provider connections and model roles can
|
|
40
|
+
Provider connections and model roles can live in the Ruby initializer or in
|
|
41
|
+
YAML under `config/little_ghost`. Values set in Ruby take priority. See
|
|
42
|
+
`LittleGhost::Configuration` for custom files and the full precedence order.
|
|
43
|
+
|
|
44
|
+
[Provider Support](providers.md) lists the credentials and endpoint settings
|
|
45
|
+
for hosted and local providers. Choose connection names, model identifiers,
|
|
46
|
+
and custom headers during application startup. Read secrets from deployment
|
|
47
|
+
configuration or a secret manager; do not copy request parameters into these
|
|
48
|
+
settings.
|
|
41
49
|
|
|
42
50
|
Prompts, caller input and history, Tool results, and attachments may go to the
|
|
43
51
|
selected provider. Choose configured providers that are appropriate for that
|
|
@@ -289,14 +297,15 @@ LittleGhost.configure do |config|
|
|
|
289
297
|
end
|
|
290
298
|
config.sandbox = {
|
|
291
299
|
provider: :native,
|
|
292
|
-
files: {root: :read_write}
|
|
293
|
-
root_filesystem: :isolated,
|
|
294
|
-
environment: {inherit: false, set: {"LANG" => "C.UTF-8"}},
|
|
295
|
-
network: :none
|
|
300
|
+
files: {root: :read_write}
|
|
296
301
|
}
|
|
297
302
|
end
|
|
298
303
|
```
|
|
299
304
|
|
|
305
|
+
The native backend isolates the host root, removes child-process network
|
|
306
|
+
access, and starts with a scrubbed child environment unless you configure a
|
|
307
|
+
different policy.
|
|
308
|
+
|
|
300
309
|
The temporary Workspace above is useful when files should live for one Run. Use application-managed storage, with tenant isolation and concurrency control, when files must persist or several Runs share a root.
|
|
301
310
|
|
|
302
311
|
The Run opens a Runtime-created Workspace before its Sandbox and closes them in reverse order after every outcome. Closing a Workspace invokes its configured teardown; it does not delete files by default. Cleanup failures raise because LittleGhost cannot confirm a clean shutdown of every owned resource. Existing instances passed by the application remain caller-owned.
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# Provider Support
|
|
2
|
+
|
|
3
|
+
Use this page to connect a hosted provider or local model server. If you are
|
|
4
|
+
still deciding how Agents should choose models, start with [Models and
|
|
5
|
+
Providers](models_and_providers.md).
|
|
6
|
+
|
|
7
|
+
## Compare provider support
|
|
8
|
+
|
|
9
|
+
Every connection uses the same configuration shape:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
LittleGhost.configure do |config|
|
|
13
|
+
config.providers = {
|
|
14
|
+
primary: {
|
|
15
|
+
adapter: :openrouter,
|
|
16
|
+
api_key: ENV.fetch("OPENROUTER_API_KEY")
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
end
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`primary` is a name chosen by the application, and `adapter` selects the wire
|
|
23
|
+
protocol. An Agent can now use a target such as
|
|
24
|
+
`primary:openai/gpt-5.6-luna`.
|
|
25
|
+
|
|
26
|
+
| Provider or endpoint | Adapter | Generation | Embeddings |
|
|
27
|
+
| --- | --- | --- | --- |
|
|
28
|
+
| OpenRouter | `:openrouter` | Chat Completions | When the model supports it |
|
|
29
|
+
| OpenAI | `:openai` | Responses or Chat Completions | Yes |
|
|
30
|
+
| Compatible API | `:openai_compatible` | Responses or Chat Completions | When the endpoint implements it |
|
|
31
|
+
| Ollama | `:openai_compatible` | Responses or Chat Completions | When the model supports it |
|
|
32
|
+
| Anthropic | `:anthropic` | Messages | No |
|
|
33
|
+
| Gemini | `:gemini` | Gemini API | No |
|
|
34
|
+
| Vertex AI | `:vertex_ai` | Gemini on Vertex AI | No |
|
|
35
|
+
| Bedrock | `:bedrock` | Converse | Titan Text Embeddings V2 |
|
|
36
|
+
| LM Studio | `:lm_studio` | Responses or Chat Completions | Yes |
|
|
37
|
+
|
|
38
|
+
Capabilities can vary by model, account, and server version. Handle provider
|
|
39
|
+
failures even when a model is expected to support a feature.
|
|
40
|
+
|
|
41
|
+
## Hosted providers
|
|
42
|
+
|
|
43
|
+
### OpenRouter
|
|
44
|
+
|
|
45
|
+
OpenRouter gives one connection access to models from several providers:
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
config.providers = {
|
|
49
|
+
openrouter: {
|
|
50
|
+
adapter: :openrouter,
|
|
51
|
+
api_key: ENV.fetch("OPENROUTER_API_KEY"),
|
|
52
|
+
app_name: "Support Console",
|
|
53
|
+
site_url: "https://support.example.com"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Use OpenRouter's `publisher/model` form, such as
|
|
59
|
+
`openrouter:anthropic/claude-sonnet-4`. `app_name` and `site_url` are optional.
|
|
60
|
+
The selected model must support embeddings before you call
|
|
61
|
+
`LittleGhost.embed`.
|
|
62
|
+
|
|
63
|
+
### OpenAI
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
config.providers = {
|
|
67
|
+
openai: {
|
|
68
|
+
adapter: :openai,
|
|
69
|
+
api_key: ENV.fetch("OPENAI_API_KEY")
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Generation uses the Responses API by default. Use `openai:gpt-5.6-luna` for
|
|
75
|
+
generation or `openai:text-embedding-3-small` for embeddings. Set
|
|
76
|
+
`api: :chat_completions` only when required.
|
|
77
|
+
|
|
78
|
+
### Anthropic
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
config.providers = {
|
|
82
|
+
anthropic: {
|
|
83
|
+
adapter: :anthropic,
|
|
84
|
+
api_key: ENV.fetch("ANTHROPIC_API_KEY")
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Use a target such as `anthropic:claude-sonnet-4-6`. The adapter uses the
|
|
90
|
+
Messages API. LittleGhost does not provide Anthropic embeddings.
|
|
91
|
+
|
|
92
|
+
### Gemini
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
config.providers = {
|
|
96
|
+
gemini: {
|
|
97
|
+
adapter: :gemini,
|
|
98
|
+
api_key: ENV.fetch("GEMINI_API_KEY")
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Use `gemini:gemini-2.5-flash`, for example. LittleGhost does not provide Gemini
|
|
104
|
+
embeddings.
|
|
105
|
+
|
|
106
|
+
### Vertex AI
|
|
107
|
+
|
|
108
|
+
Vertex AI uses Gemini's request format with a Google Cloud project and
|
|
109
|
+
location:
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
config.providers = {
|
|
113
|
+
vertex: {
|
|
114
|
+
adapter: :vertex_ai,
|
|
115
|
+
project: ENV.fetch("GOOGLE_CLOUD_PROJECT"),
|
|
116
|
+
location: "global"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Use a target such as `vertex:gemini-2.5-flash`. The credential resolver can
|
|
122
|
+
obtain Google credentials, or startup can supply an access token. LittleGhost
|
|
123
|
+
does not provide Vertex AI embeddings.
|
|
124
|
+
|
|
125
|
+
### Amazon Bedrock
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
config.providers = {
|
|
129
|
+
bedrock: {
|
|
130
|
+
adapter: :bedrock,
|
|
131
|
+
region: ENV.fetch("AWS_REGION")
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Use a model available in the region, such as
|
|
137
|
+
`bedrock:us.anthropic.claude-sonnet-4-6-v1:0`. The adapter resolves AWS
|
|
138
|
+
credentials and signs requests without an AWS SDK.
|
|
139
|
+
|
|
140
|
+
Bedrock embeddings support `amazon.titan-embed-text-v2:0`. Its `dimensions`
|
|
141
|
+
setting accepts 256, 512, or 1024, and `normalize` defaults to `true`.
|
|
142
|
+
|
|
143
|
+
## Compatible APIs and local servers
|
|
144
|
+
|
|
145
|
+
### Another OpenAI-compatible API
|
|
146
|
+
|
|
147
|
+
Use `:openai_compatible` for a hosted service, gateway, or local server that
|
|
148
|
+
implements OpenAI-style endpoints:
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
config.providers = {
|
|
152
|
+
models: {
|
|
153
|
+
adapter: :openai_compatible,
|
|
154
|
+
base_url: "https://models.example.com/v1/",
|
|
155
|
+
api_key: ENV.fetch("MODEL_API_KEY"),
|
|
156
|
+
api: :responses
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Use the model identifier expected by the endpoint, such as
|
|
162
|
+
`models:example-model`. Choose `api: :responses` or
|
|
163
|
+
`api: :chat_completions` to match the server. `LittleGhost.embed` uses the
|
|
164
|
+
connection's `embeddings` endpoint and supports `dimensions` when the server
|
|
165
|
+
and model accept it.
|
|
166
|
+
|
|
167
|
+
Tool calls, structured results, reasoning, attachments, and embeddings depend
|
|
168
|
+
on the compatible server and model.
|
|
169
|
+
|
|
170
|
+
Ollama and LM Studio normally listen on loopback HTTP. LittleGhost requires
|
|
171
|
+
`allow_insecure_http: true` as an explicit opt-in because HTTP does not protect
|
|
172
|
+
prompts, output, or credentials in transit. Use this setting only for a local
|
|
173
|
+
or otherwise trusted endpoint; use HTTPS for a remote server.
|
|
174
|
+
|
|
175
|
+
### Ollama
|
|
176
|
+
|
|
177
|
+
Pull a model, start Ollama, and use the existing OpenAI-compatible adapter:
|
|
178
|
+
|
|
179
|
+
```sh
|
|
180
|
+
$ ollama pull qwen3
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
```ruby
|
|
184
|
+
config.providers = {
|
|
185
|
+
ollama: {
|
|
186
|
+
adapter: :openai_compatible,
|
|
187
|
+
base_url: "http://localhost:11434/v1/",
|
|
188
|
+
api_key: "ollama",
|
|
189
|
+
api: :responses,
|
|
190
|
+
allow_insecure_http: true
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Use the pulled model name in the target, such as `ollama:qwen3`. The `api_key`
|
|
196
|
+
is a placeholder for a local server without authentication.
|
|
197
|
+
|
|
198
|
+
Ollama added its Responses endpoint in version 0.13.3. Set
|
|
199
|
+
`api: :chat_completions` for an earlier release or when the selected model does
|
|
200
|
+
not support the Responses features you need. See Ollama's [OpenAI compatibility
|
|
201
|
+
reference](https://docs.ollama.com/api/openai-compatibility) for release-specific
|
|
202
|
+
fields.
|
|
203
|
+
|
|
204
|
+
For embeddings, pull an embedding model and pass its target to
|
|
205
|
+
`LittleGhost.embed`. LittleGhost does not list, pull, or delete Ollama models.
|
|
206
|
+
|
|
207
|
+
### LM Studio
|
|
208
|
+
|
|
209
|
+
Start LM Studio's local server after downloading or loading the model you want
|
|
210
|
+
to use. The dedicated adapter supplies the usual local endpoint and a
|
|
211
|
+
placeholder credential:
|
|
212
|
+
|
|
213
|
+
```ruby
|
|
214
|
+
config.providers = {
|
|
215
|
+
lm_studio: {
|
|
216
|
+
adapter: :lm_studio,
|
|
217
|
+
allow_insecure_http: true
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Use the model key shown by LM Studio, for example
|
|
223
|
+
`lm_studio:google/gemma-3-4b`. Generation uses the Responses API by default.
|
|
224
|
+
|
|
225
|
+
Set `api: :chat_completions` with LM Studio releases earlier than 0.3.29, or
|
|
226
|
+
when required by the selected model. `LittleGhost.embed` uses the same
|
|
227
|
+
connection with an embedding model. See LM Studio's [compatibility
|
|
228
|
+
reference](https://lmstudio.ai/docs/developer/openai-compat) for its current
|
|
229
|
+
generation and embedding endpoints.
|
|
230
|
+
|
|
231
|
+
When LM Studio authentication is enabled, add
|
|
232
|
+
`api_key: ENV.fetch("LM_STUDIO_API_TOKEN")` to the connection.
|
|
233
|
+
|
|
234
|
+
The native model list and API-token authentication require LM Studio 0.4.0 or
|
|
235
|
+
newer. LittleGhost reads the list; it does not load, unload, or download models.
|
|
236
|
+
|
|
237
|
+
## Discover current model details
|
|
238
|
+
|
|
239
|
+
Call `refresh!` when you need current availability, loaded state, or
|
|
240
|
+
capabilities. Ordinary model requests do not make discovery calls.
|
|
241
|
+
|
|
242
|
+
```ruby
|
|
243
|
+
resolver = LittleGhost.model_resolver
|
|
244
|
+
result = resolver.refresh!(target: "lm_studio:google/gemma-3-4b")
|
|
245
|
+
result[:errors]
|
|
246
|
+
|
|
247
|
+
details = resolver.details("lm_studio:google/gemma-3-4b")
|
|
248
|
+
details[:loaded]
|
|
249
|
+
details.context_window
|
|
250
|
+
details.input_modalities
|
|
251
|
+
details.supported_parameters
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Pass `target:` for one model, or omit it for every configured source. On
|
|
255
|
+
failure, `result[:errors]` describes the problem and previous details remain.
|
|
256
|
+
|
|
257
|
+
Hosted adapters can combine models.dev data with their provider's model API.
|
|
258
|
+
LM Studio reads its native model list. The generic compatible adapter and
|
|
259
|
+
Ollama do not have built-in model discovery.
|
|
260
|
+
|
|
261
|
+
LM Studio details can include downloaded and loaded state, active context
|
|
262
|
+
length, input types, tools, and reasoning. An unloaded model has no active
|
|
263
|
+
context length. See `LittleGhost::Models::Details` for every field.
|
|
264
|
+
|
|
265
|
+
## Keep connection settings in application code
|
|
266
|
+
|
|
267
|
+
Choose keys, tokens, headers, base URLs, model identifiers, and API variants at
|
|
268
|
+
startup. Read secrets from deployment configuration or a secret manager. Do
|
|
269
|
+
not copy public request parameters into provider settings.
|
|
270
|
+
|
|
271
|
+
The selected provider may receive prompts, conversation history, Tool results,
|
|
272
|
+
schemas, and attachments. Choose one appropriate for that data.
|
|
273
|
+
|
|
274
|
+
Continue with [Running in Production](production.md) to place connections,
|
|
275
|
+
roles, sessions, and observability into a long-running application.
|
data/docs/guides/sandboxing.md
CHANGED
|
@@ -23,10 +23,7 @@ LittleGhost.configure do |config|
|
|
|
23
23
|
|
|
24
24
|
config.sandbox = {
|
|
25
25
|
provider: :native,
|
|
26
|
-
files: {root: :read_write}
|
|
27
|
-
root_filesystem: :isolated,
|
|
28
|
-
environment: {inherit: false, set: {"LANG" => "C.UTF-8"}},
|
|
29
|
-
network: :none
|
|
26
|
+
files: {root: :read_write}
|
|
30
27
|
}
|
|
31
28
|
end
|
|
32
29
|
```
|
|
@@ -34,7 +31,9 @@ end
|
|
|
34
31
|
This example gives each Run a temporary writable root and removes it during
|
|
35
32
|
teardown. The `:native` Sandbox backend selects Seatbelt on macOS or Bubblewrap on
|
|
36
33
|
Linux. It raises instead of running without isolation when the native backend
|
|
37
|
-
is unavailable.
|
|
34
|
+
is unavailable. Unless you configure otherwise, it isolates the host root,
|
|
35
|
+
removes child-process network access, and starts child processes without
|
|
36
|
+
inheriting the host environment.
|
|
38
37
|
|
|
39
38
|
`LittleGhost::Tools::Filesystem` and `LittleGhost::Tools::Shell` use the Sandbox
|
|
40
39
|
assigned to the current Run. Code-mode interpreters also run inside their own
|
|
@@ -148,8 +147,7 @@ config.sandbox = {
|
|
|
148
147
|
},
|
|
149
148
|
runtime_paths: {
|
|
150
149
|
home: :read_write
|
|
151
|
-
}
|
|
152
|
-
network: :none
|
|
150
|
+
}
|
|
153
151
|
}
|
|
154
152
|
```
|
|
155
153
|
|
|
@@ -200,6 +198,39 @@ Workspace paths:
|
|
|
200
198
|
On Seatbelt, host-visible modes permit subprocesses because development tools
|
|
201
199
|
often need them. A Scope can remove `process_spawn` for a command that does not.
|
|
202
200
|
|
|
201
|
+
## Start child processes with only the values they need
|
|
202
|
+
|
|
203
|
+
When `environment` is omitted, sandboxed processes do not inherit the host
|
|
204
|
+
environment. LittleGhost starts with `LANG`, `LC_ALL`, and `PATH`, then adds
|
|
205
|
+
`LITTLE_GHOST_WORKSPACE_*` variables so the child can locate its Workspace.
|
|
206
|
+
Locale values come from the host when present and otherwise use `C.UTF-8`.
|
|
207
|
+
`PATH` keeps the host's absolute entries, removes empty, relative, and duplicate
|
|
208
|
+
entries, and falls back to `/usr/local/bin:/usr/bin:/bin` when nothing remains.
|
|
209
|
+
Credentials, provider keys, package-manager settings, and home-directory
|
|
210
|
+
variables are not copied into the child.
|
|
211
|
+
|
|
212
|
+
> **Safety note:** An absolute directory is not necessarily a trusted one. A
|
|
213
|
+
> host `PATH` may include project directories, temporary directories, or
|
|
214
|
+
> user-managed shims. Configure a fixed `PATH` when a model can invoke commands
|
|
215
|
+
> by name and you do not trust every host entry.
|
|
216
|
+
|
|
217
|
+
Declaring `environment` replaces the locale and path baseline instead of
|
|
218
|
+
merging with it. Use an empty Hash to omit all configurable values, or set
|
|
219
|
+
exactly what the process needs. Workspace routing variables remain available:
|
|
220
|
+
|
|
221
|
+
```ruby
|
|
222
|
+
config.sandbox = {
|
|
223
|
+
provider: :native,
|
|
224
|
+
environment: {
|
|
225
|
+
inherit: false,
|
|
226
|
+
set: {"LANG" => "C.UTF-8", "PATH" => "/opt/my-tool/bin:/usr/bin"}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Host inheritance requires both `inherit: true` in the Sandbox policy and an
|
|
232
|
+
individual process call that opts into inheritance.
|
|
233
|
+
|
|
203
234
|
## Choose an enforcement backend
|
|
204
235
|
|
|
205
236
|
| Sandbox backend | Host | What it enforces |
|
|
@@ -260,6 +291,10 @@ Sandbox networking has three modes:
|
|
|
260
291
|
- `:allowlist` requires an enforcing gateway: a supervised proxy that permits
|
|
261
292
|
only configured destinations.
|
|
262
293
|
|
|
294
|
+
Seatbelt, Bubblewrap, and `:native` default to `:none`; declare a network mode
|
|
295
|
+
only when the child needs a different policy. The unrestricted backend uses
|
|
296
|
+
`:inherit` because it is not a security boundary.
|
|
297
|
+
|
|
263
298
|
Proxy environment variables alone are not an allowlist. An external gateway
|
|
264
299
|
uses named, process-only Workspace paths and verifies that they still point to
|
|
265
300
|
the configured directories; it does not create a virtual path mapping.
|
data/docs/guides/skills.md
CHANGED
|
@@ -107,9 +107,7 @@ LittleGhost.configure do |config|
|
|
|
107
107
|
|
|
108
108
|
config.sandbox = {
|
|
109
109
|
provider: :native,
|
|
110
|
-
files: {root: :read_write, skills: :read_only}
|
|
111
|
-
root_filesystem: :isolated,
|
|
112
|
-
network: :none
|
|
110
|
+
files: {root: :read_write, skills: :read_only}
|
|
113
111
|
}
|
|
114
112
|
end
|
|
115
113
|
```
|
data/docs/guides/tools.md
CHANGED
|
@@ -79,6 +79,8 @@ end
|
|
|
79
79
|
Prefer `available_if` on an individual Tool when only that operation is
|
|
80
80
|
conditional. Use a provider when the collection itself owns discovery,
|
|
81
81
|
construction, or shared setup for an application or remote service.
|
|
82
|
+
The [MCP guide](mcp.md) shows how a Model Context Protocol server provides a
|
|
83
|
+
Toolset through the official Ruby client.
|
|
82
84
|
|
|
83
85
|
LittleGhost does not enforce class names. A useful application convention is
|
|
84
86
|
to end one model-callable operation with `Tool` and a provider of multiple
|
data/exe/little_ghost
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
lib = File.expand_path("../lib", __dir__)
|
|
5
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
6
|
+
|
|
7
|
+
require "little_ghost/cli"
|
|
8
|
+
|
|
9
|
+
source_path = File.expand_path("..", __dir__)
|
|
10
|
+
source_path = nil unless File.file?(File.join(source_path, "little_ghost.gemspec"))
|
|
11
|
+
|
|
12
|
+
exit LittleGhost::CLI.new(ARGV, source_path:).run
|
|
@@ -50,7 +50,8 @@ module LittleGhost
|
|
|
50
50
|
# +context+, +settings+, +metadata+, +session_id+, +actor_id+, and
|
|
51
51
|
# +deadline_at+.
|
|
52
52
|
def ask(message, **options)
|
|
53
|
-
definition
|
|
53
|
+
snapshot = definition
|
|
54
|
+
snapshot.implementation.new.bind_assembly_definition(snapshot).ask(message, **options)
|
|
54
55
|
end
|
|
55
56
|
|
|
56
57
|
# Lazily streams +message+ through a fresh standalone assembly.
|
|
@@ -66,7 +67,7 @@ module LittleGhost
|
|
|
66
67
|
snapshot = definition
|
|
67
68
|
stream = nil
|
|
68
69
|
Enumerator.new do |events|
|
|
69
|
-
stream ||= snapshot.implementation.new.stream_ask(message, **options)
|
|
70
|
+
stream ||= snapshot.implementation.new.bind_assembly_definition(snapshot).stream_ask(message, **options)
|
|
70
71
|
stream.each { |event| events << event }
|
|
71
72
|
end
|
|
72
73
|
end
|
|
@@ -157,6 +158,7 @@ module LittleGhost
|
|
|
157
158
|
@runtime = runtime || run&.runtime || LittleGhost.runtime
|
|
158
159
|
@workspace = workspace || run&.workspace
|
|
159
160
|
@sandbox = sandbox || run&.sandbox
|
|
161
|
+
@assembly_definition = nil
|
|
160
162
|
@standalone = standalone
|
|
161
163
|
@assembly_mutex = Mutex.new
|
|
162
164
|
@assembly_closed = false
|
|
@@ -169,6 +171,11 @@ module LittleGhost
|
|
|
169
171
|
self
|
|
170
172
|
end
|
|
171
173
|
|
|
174
|
+
def bind_assembly_definition(definition) # :nodoc:
|
|
175
|
+
@assembly_definition = definition
|
|
176
|
+
self
|
|
177
|
+
end
|
|
178
|
+
|
|
172
179
|
# Builds the top-level Run used by a standalone assembly.
|
|
173
180
|
def build_run(payload = nil, include_agent_events_by_default: false, **payload_options) # :nodoc:
|
|
174
181
|
payload = entrypoint_payload(payload, payload_options) unless payload_options.empty?
|
|
@@ -178,7 +185,8 @@ module LittleGhost
|
|
|
178
185
|
end
|
|
179
186
|
source_class = run_entrypoint_class
|
|
180
187
|
options = {entrypoint_class: source_class}
|
|
181
|
-
|
|
188
|
+
execution = @assembly_definition || self.class
|
|
189
|
+
options[:execution_class] = execution unless source_class.equal?(execution)
|
|
182
190
|
options[:agent_class] = source_class if is_a?(Agent)
|
|
183
191
|
options[:cancellation_token] = cancellation_token if cancellation_token
|
|
184
192
|
options[:workspace] = workspace if workspace
|
|
@@ -123,7 +123,7 @@ module LittleGhost
|
|
|
123
123
|
snapshot = definition
|
|
124
124
|
return (runtime || run.runtime).build_assembly(snapshot, run:) if run
|
|
125
125
|
|
|
126
|
-
snapshot.implementation.new(runtime:)
|
|
126
|
+
snapshot.implementation.new(runtime:).bind_assembly_definition(snapshot)
|
|
127
127
|
end
|
|
128
128
|
|
|
129
129
|
# Executes the current declarations and returns their Run.
|
|
@@ -133,7 +133,7 @@ module LittleGhost
|
|
|
133
133
|
def stream_ask(message, **options)
|
|
134
134
|
snapshot = definition
|
|
135
135
|
Enumerator.new do |events|
|
|
136
|
-
snapshot.implementation.new(runtime:).stream_ask(message, **options).each { |event| events << event }
|
|
136
|
+
snapshot.implementation.new(runtime:).bind_assembly_definition(snapshot).stream_ask(message, **options).each { |event| events << event }
|
|
137
137
|
end
|
|
138
138
|
end
|
|
139
139
|
|