little_ghost 0.4.0 → 0.6.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 +28 -6
- data/docs/guides/assemblies.md +143 -17
- data/docs/guides/code_mode.md +276 -0
- data/docs/guides/core_concepts.md +46 -30
- data/docs/guides/getting_started.md +17 -6
- data/docs/guides/integrations.md +217 -0
- data/docs/guides/models_and_providers.md +125 -0
- data/docs/guides/production.md +190 -17
- data/docs/guides/prompt_views.md +14 -7
- data/docs/guides/sandboxing.md +282 -0
- data/docs/guides/skills.md +141 -0
- data/docs/guides/structured_outputs_and_content.md +135 -0
- data/docs/guides/tools.md +355 -0
- data/lib/little_ghost/ag_ui/adapter.rb +2 -2
- data/lib/little_ghost/agent/delegation.rb +2 -2
- data/lib/little_ghost/agent/skills.rb +6 -1
- data/lib/little_ghost/agent/tool_loop.rb +6 -2
- data/lib/little_ghost/agent.rb +250 -48
- data/lib/little_ghost/agent_builder.rb +61 -22
- data/lib/little_ghost/agent_stream_source.rb +262 -0
- data/lib/little_ghost/artifact.rb +182 -0
- data/lib/little_ghost/artifacts/presentation_budget.rb +44 -0
- data/lib/little_ghost/artifacts/workspace_store.rb +370 -0
- data/lib/little_ghost/artifacts.rb +11 -0
- data/lib/little_ghost/assembly.rb +64 -20
- data/lib/little_ghost/assembly_builder.rb +26 -26
- data/lib/little_ghost/assembly_execution.rb +23 -11
- data/lib/little_ghost/code_mode/broker.rb +168 -0
- data/lib/little_ghost/code_mode/catalog.rb +54 -0
- data/lib/little_ghost/code_mode/engine.rb +58 -0
- data/lib/little_ghost/code_mode/javascript/catalog.rb +118 -0
- data/lib/little_ghost/code_mode/javascript/client.rb +550 -0
- data/lib/little_ghost/code_mode/javascript/host.rb +347 -0
- data/lib/little_ghost/code_mode/javascript/session.rb +578 -0
- data/lib/little_ghost/code_mode/javascript_engine.rb +172 -0
- data/lib/little_ghost/code_mode/protocol.rb +78 -0
- data/lib/little_ghost/code_mode/ruby/catalog.rb +80 -0
- data/lib/little_ghost/code_mode/ruby/host.rb +102 -0
- data/lib/little_ghost/code_mode/ruby/session.rb +508 -0
- data/lib/little_ghost/code_mode/ruby_engine.rb +86 -0
- data/lib/little_ghost/code_mode/runtime.rb +335 -0
- data/lib/little_ghost/code_mode/session.rb +61 -0
- data/lib/little_ghost/code_mode/types.rb +58 -0
- data/lib/little_ghost/code_mode.rb +53 -0
- data/lib/little_ghost/configuration.rb +208 -21
- data/lib/little_ghost/content.rb +19 -8
- data/lib/little_ghost/data_map.rb +2 -2
- data/lib/little_ghost/errors.rb +26 -7
- data/lib/little_ghost/execution.rb +32 -27
- data/lib/little_ghost/execution_state.rb +3 -3
- data/lib/little_ghost/graph.rb +373 -210
- data/lib/little_ghost/instrumentation.rb +2 -1
- data/lib/little_ghost/mcp/client.rb +487 -88
- data/lib/little_ghost/mcp/toolset.rb +210 -0
- data/lib/little_ghost/mcp/types.rb +216 -0
- data/lib/little_ghost/mcp.rb +3 -0
- data/lib/little_ghost/model_capabilities.rb +9 -6
- data/lib/little_ghost/model_request.rb +0 -12
- data/lib/little_ghost/model_resolver.rb +13 -3
- data/lib/little_ghost/model_response.rb +3 -7
- data/lib/little_ghost/network/authorizer_server.rb +162 -0
- data/lib/little_ghost/network/certificate_authority.rb +104 -0
- data/lib/little_ghost/network/envoy_config.rb +362 -0
- data/lib/little_ghost/network/envoy_gateway.rb +418 -0
- data/lib/little_ghost/network/external_gateway.rb +68 -0
- data/lib/little_ghost/network.rb +96 -0
- data/lib/little_ghost/prompt_resolver.rb +7 -7
- data/lib/little_ghost/provider_registry.rb +3 -3
- data/lib/little_ghost/providers/anthropic.rb +8 -1
- data/lib/little_ghost/providers/gemini.rb +10 -1
- data/lib/little_ghost/providers/vertex_ai.rb +6 -1
- data/lib/little_ghost/run.rb +89 -25
- data/lib/little_ghost/run_context.rb +11 -1
- data/lib/little_ghost/run_result.rb +0 -7
- data/lib/little_ghost/runtime/hook.rb +5 -0
- data/lib/little_ghost/runtime/hooks/artifacts.rb +338 -0
- data/lib/little_ghost/runtime.rb +111 -34
- data/lib/little_ghost/sandbox/capabilities.rb +68 -0
- data/lib/little_ghost/sandbox/environment_policy.rb +41 -0
- data/lib/little_ghost/sandbox/filesystem.rb +377 -0
- data/lib/little_ghost/sandbox/isolated_backend.rb +163 -0
- data/lib/little_ghost/sandbox/limits.rb +48 -0
- data/lib/little_ghost/sandbox/mount.rb +129 -0
- data/lib/little_ghost/sandbox/network_policy.rb +108 -0
- data/lib/little_ghost/sandbox/policy.rb +88 -0
- data/lib/little_ghost/sandbox/process_runner.rb +103 -0
- data/lib/little_ghost/sandbox/process_session.rb +335 -0
- data/lib/little_ghost/sandbox/scope.rb +304 -0
- data/lib/little_ghost/sandbox.rb +158 -32
- data/lib/little_ghost/sandboxes/bubblewrap.rb +351 -0
- data/lib/little_ghost/sandboxes/native.rb +51 -0
- data/lib/little_ghost/sandboxes/seatbelt.rb +261 -0
- data/lib/little_ghost/sandboxes/unrestricted.rb +240 -0
- data/lib/little_ghost/session.rb +30 -6
- data/lib/little_ghost/session_stores/filesystem.rb +29 -13
- data/lib/little_ghost/skills/catalog.rb +94 -14
- data/lib/little_ghost/skills/resource_root.rb +42 -0
- data/lib/little_ghost/skills/skill.rb +0 -3
- data/lib/little_ghost/stream_event.rb +8 -13
- data/lib/little_ghost/subagents/control_tool.rb +8 -0
- data/lib/little_ghost/subagents/manager.rb +182 -79
- data/lib/little_ghost/support/callbacks.rb +3 -1
- data/lib/little_ghost/support/cancellation_token.rb +2 -2
- data/lib/little_ghost/support/content_capture.rb +3 -3
- data/lib/little_ghost/support/executor.rb +84 -15
- data/lib/little_ghost/support/http_client.rb +11 -4
- data/lib/little_ghost/support/interruptible_stream.rb +4 -2
- data/lib/little_ghost/support/pooled_thread_runner.rb +126 -0
- data/lib/little_ghost/support/redactor.rb +1 -1
- data/lib/little_ghost/support/serialized_dispatcher.rb +84 -0
- data/lib/little_ghost/support/task.rb +126 -0
- data/lib/little_ghost/support/task_runner.rb +74 -0
- data/lib/little_ghost/support.rb +4 -0
- data/lib/little_ghost/tool.rb +104 -53
- data/lib/little_ghost/tool_registry.rb +1 -1
- data/lib/little_ghost/tools/filesystem.rb +1 -1
- data/lib/little_ghost/tools/shell.rb +4 -3
- data/lib/little_ghost/version.rb +1 -1
- data/lib/little_ghost/workflow.rb +3 -3
- data/lib/little_ghost/workspace.rb +222 -8
- data/lib/little_ghost.rb +38 -12
- metadata +116 -2
- data/lib/little_ghost/unrestricted_sandbox.rb +0 -306
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Core Concepts
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Define an Agent in a Ruby class, then call it with `.ask`.
|
|
4
4
|
|
|
5
5
|
```ruby
|
|
6
6
|
class CustomerSupportAgent < LittleGhost::Agent
|
|
@@ -29,7 +29,8 @@ CustomerSupportAgent
|
|
|
29
29
|
└── limits and optional capabilities
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
An Agent can return text or checked, structured data.
|
|
32
|
+
An Agent can return text or checked, structured data. You can add streaming,
|
|
33
|
+
saved conversations, or callbacks later. None of them are required to begin.
|
|
33
34
|
|
|
34
35
|
## A Tool connects the model to Ruby
|
|
35
36
|
|
|
@@ -52,7 +53,12 @@ class HelpCenterLookupTool < LittleGhost::Tool
|
|
|
52
53
|
end
|
|
53
54
|
```
|
|
54
55
|
|
|
55
|
-
LittleGhost checks the model's arguments, calls the
|
|
56
|
+
LittleGhost checks the model's arguments, calls the Tool, and gives the result
|
|
57
|
+
back to the model. The schema checks shape, not permission. Check permission
|
|
58
|
+
inside the Tool using identity and account information from your application.
|
|
59
|
+
|
|
60
|
+
[Tools](tools.md) follows that path from model input to application code,
|
|
61
|
+
including run-scoped bindings, concurrency, retries, and sandbox delegation.
|
|
56
62
|
|
|
57
63
|
## A Run owns one top-level execution
|
|
58
64
|
|
|
@@ -81,11 +87,21 @@ Run
|
|
|
81
87
|
└── Agent and Tools ──> RunResult
|
|
82
88
|
```
|
|
83
89
|
|
|
84
|
-
An **Invocation** is the request in LittleGhost's standard shape. Its `context`
|
|
90
|
+
An **Invocation** is the request in LittleGhost's standard shape. Its `context`
|
|
91
|
+
contains current request values supplied by your application. A Tool can read
|
|
92
|
+
those values through `run.invocation.context` when it checks permission.
|
|
85
93
|
|
|
86
|
-
|
|
94
|
+
A **Session** stores conversation state between Runs when persistence is
|
|
95
|
+
configured. The **RunContext** carries mutable working state in `context.state`
|
|
96
|
+
during one Run. LittleGhost loads saved Session state before adding the current
|
|
97
|
+
Invocation context. Recheck saved values before using them for permission
|
|
98
|
+
decisions.
|
|
87
99
|
|
|
88
|
-
A Tool's **Binding** gives the Tool access to objects created for this run,
|
|
100
|
+
A Tool's **Binding** gives the Tool access to objects created for this run,
|
|
101
|
+
including the Agent, Run, Workspace, and Sandbox. These objects are separate
|
|
102
|
+
from arguments chosen by the model. [Tools](tools.md) explains the binding;
|
|
103
|
+
[Workspaces and Sandboxes](sandboxing.md) explains delegated files and child
|
|
104
|
+
processes.
|
|
89
105
|
|
|
90
106
|
The final **RunResult** keeps the complete assembly result. Its `text` is the final text answer. Its `output` returns structured data when the Agent declared a result schema, and text otherwise. The top-level `Run#response` is always the caller-facing text.
|
|
91
107
|
|
|
@@ -102,9 +118,13 @@ Top-level calls normally return a Run, even when execution fails. The terminal e
|
|
|
102
118
|
| Tool input or a `ToolError` failed | The model may recover | No terminal event by itself | Gives a safe error result back to the model |
|
|
103
119
|
| Input, configuration, or resources failed before a Run could start | No Run exists | None | Raises the exception |
|
|
104
120
|
|
|
105
|
-
Unexpected Tool exception messages are hidden from the model. The original
|
|
121
|
+
Unexpected Tool exception messages are hidden from the model. The original
|
|
122
|
+
exception remains available to application callbacks and diagnostics.
|
|
106
123
|
|
|
107
|
-
Failures while closing resources, delivering events, or reporting
|
|
124
|
+
Failures while closing resources, delivering events, or reporting
|
|
125
|
+
instrumentation sit outside the normal result path. They raise a Ruby exception
|
|
126
|
+
because LittleGhost can no longer promise that it delivered a clean ending.
|
|
127
|
+
[Running in Production](production.md) covers supervision and shutdown.
|
|
108
128
|
|
|
109
129
|
## An Assembly can look like one Agent
|
|
110
130
|
|
|
@@ -146,6 +166,10 @@ end
|
|
|
146
166
|
|
|
147
167
|
Use a subagent when delegation is part of one model's decision-making. Use a Workflow when application code must guarantee that a step happens.
|
|
148
168
|
|
|
169
|
+
When an Agent also uses code mode, subagent controls stay in the Agent's
|
|
170
|
+
conversation. Code-mode programs can compose ordinary Tools, while spawning,
|
|
171
|
+
messaging, and checking on subagents remain decisions for the parent model.
|
|
172
|
+
|
|
149
173
|
### Workflows make Ruby the coordinator
|
|
150
174
|
|
|
151
175
|
A **Workflow** coordinates work with ordinary Ruby. Its `perform` method can call an Agent or another Assembly, read a result, choose a branch, or run independent steps together.
|
|
@@ -185,7 +209,10 @@ class ProblemSolverSwarm < LittleGhost::Swarm
|
|
|
185
209
|
end
|
|
186
210
|
```
|
|
187
211
|
|
|
188
|
-
A Swarm is intentionally agent-to-agent. Its members are Agents, not other
|
|
212
|
+
A Swarm is intentionally agent-to-agent. Its members are Agents, not other
|
|
213
|
+
kinds of Assembly. Caller history and application context stay hidden unless a
|
|
214
|
+
member opts in. A handoff message comes from another model, so a receiving
|
|
215
|
+
Agent should use it as context rather than proof that an action is permitted.
|
|
189
216
|
|
|
190
217
|
### Graphs make routes visible
|
|
191
218
|
|
|
@@ -209,26 +236,12 @@ class SupportFlowGraph < LittleGhost::Graph
|
|
|
209
236
|
end
|
|
210
237
|
```
|
|
211
238
|
|
|
212
|
-
Graph nodes
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
Named classes are the default way to organize reusable behavior. They are readable, load through normal Ruby conventions, and give the coordination style a visible name such as `ResponseWorkflow` or `SupportFlowGraph`.
|
|
217
|
-
|
|
218
|
-
Every assembly class can also produce a mutable builder:
|
|
219
|
-
|
|
220
|
-
```ruby
|
|
221
|
-
graph = SupportFlowGraph.to_builder
|
|
222
|
-
graph.node :audit, AuditAgent
|
|
223
|
-
graph.edge :respond, :audit
|
|
224
|
-
graph.finish :audit
|
|
225
|
-
graph.validate!
|
|
226
|
-
run = graph.ask("Review order 481")
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
Use a builder when trusted application configuration decides the participants or routes. Each run gets a fixed copy of the builder as it looked when the run began, so later edits affect later runs. Ruby callbacks still see any application objects they captured.
|
|
239
|
+
Graph nodes receive the original task and results from the nodes immediately
|
|
240
|
+
before them. They do not receive caller history or application context unless
|
|
241
|
+
their declarations opt in. [Compose Agents](assemblies.md) explains parallel
|
|
242
|
+
routes, joins, input mapping, and data boundaries.
|
|
230
243
|
|
|
231
|
-
## One result,
|
|
244
|
+
## One result, even when several agents help
|
|
232
245
|
|
|
233
246
|
Every assembly produces the same top-level `Run` and final `RunResult`. Composite assemblies also keep a size-limited record of the participants that ran:
|
|
234
247
|
|
|
@@ -240,8 +253,11 @@ run.result.steps
|
|
|
240
253
|
run.result.trajectory.transitions
|
|
241
254
|
```
|
|
242
255
|
|
|
243
|
-
This shows
|
|
256
|
+
This record shows which participants ran. [Compose Agents](assemblies.md)
|
|
257
|
+
explains builders, detailed routing records, and live events from nested Agents.
|
|
244
258
|
|
|
245
259
|
The pieces now fit together: Agents define behavior. Tools connect them to Ruby. Runs record one execution. Assemblies let the system grow without changing the caller.
|
|
246
260
|
|
|
247
|
-
Continue with [
|
|
261
|
+
Continue with [Models and Providers](models_and_providers.md) to choose model
|
|
262
|
+
targets and configure provider connections. When you need several agents to
|
|
263
|
+
work together, [Compose Agents](assemblies.md) builds on the same concepts.
|
|
@@ -48,7 +48,7 @@ $ ruby customer_support_agent.rb
|
|
|
48
48
|
|
|
49
49
|
`CustomerSupportAgent.ask` creates a `LittleGhost::Run` for this request. When the work finishes, the Run holds the outcome and response.
|
|
50
50
|
|
|
51
|
-
The inline prompt keeps this first example
|
|
51
|
+
The inline prompt keeps this first example visible in one place. When the instructions grow, [Prompts as Views](prompt_views.md) moves them into a conventional ERB file without adding setup to the Agent.
|
|
52
52
|
|
|
53
53
|
The selected external provider may receive system instructions, caller input, conversation history, tool results, and attachments. Model wording can vary, so use application code—not a prompt—when a rule must always hold.
|
|
54
54
|
|
|
@@ -101,13 +101,18 @@ run.response
|
|
|
101
101
|
# Refunds are available within 30 days, so your purchase is eligible.
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
LittleGhost checks the model's arguments before it calls
|
|
104
|
+
LittleGhost checks the model's arguments before it calls
|
|
105
|
+
`HelpCenterLookupTool#call`. The Tool's result then becomes context for the
|
|
106
|
+
model.
|
|
105
107
|
|
|
106
|
-
### Use
|
|
108
|
+
### Use application context for private data
|
|
107
109
|
|
|
108
|
-
|
|
110
|
+
The schema checks shape, not permission. When a Tool reads private data or
|
|
111
|
+
changes something, use identity and account information established by your
|
|
112
|
+
application rather than asking the model to supply it.
|
|
109
113
|
|
|
110
|
-
While an Agent is working, LittleGhost binds each Tool instance to the current
|
|
114
|
+
While an Agent is working, LittleGhost binds each Tool instance to the current
|
|
115
|
+
Run. The Tool can read request values through its `run` accessor:
|
|
111
116
|
|
|
112
117
|
```ruby
|
|
113
118
|
class OrderStatusTool < LittleGhost::Tool
|
|
@@ -147,7 +152,13 @@ run = CustomerSupportAgent.ask(
|
|
|
147
152
|
)
|
|
148
153
|
```
|
|
149
154
|
|
|
150
|
-
Here, `order_number` came from the model. The application supplied `actor_id`
|
|
155
|
+
Here, `order_number` came from the model. The application supplied `actor_id`
|
|
156
|
+
and `account_id` after authenticating the caller. LittleGhost places those
|
|
157
|
+
request values on `run.invocation`; context keys become strings.
|
|
158
|
+
|
|
159
|
+
> **Safety note:** Treat model-selected Tool arguments like any other external
|
|
160
|
+
> input. Check permission using the current user and account before returning
|
|
161
|
+
> private data or performing a write.
|
|
151
162
|
|
|
152
163
|
That is enough to authorize the first Tool safely. [Core Concepts](core_concepts.md) names the request and working-state objects behind `run`, and [Running in Production](production.md) explains what changes when you add saved conversations.
|
|
153
164
|
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# Connect MCP, AG-UI, and OpenTelemetry
|
|
2
|
+
|
|
3
|
+
LittleGhost can load Tools from an MCP server, translate a Run stream for an
|
|
4
|
+
interactive interface, and publish traces. Each integration uses the same
|
|
5
|
+
Agents and Runs you already have.
|
|
6
|
+
|
|
7
|
+
## Load Tools from an MCP server
|
|
8
|
+
|
|
9
|
+
An MCP Toolset connects to one server and turns its published operations into
|
|
10
|
+
LittleGhost Tool classes. Add the Toolset through the same Agent `tools`
|
|
11
|
+
declaration used for local Tools:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
require "little_ghost/mcp"
|
|
15
|
+
|
|
16
|
+
class HelpCenterTools < LittleGhost::MCP::Toolset
|
|
17
|
+
connection url: "https://mcp.example/rpc", timeout: 20
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class CustomerSupportAgent < LittleGhost::Agent
|
|
21
|
+
system_prompt "Use help-center tools for published guidance."
|
|
22
|
+
tools HelpCenterTools
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
run = CustomerSupportAgent.ask("How long do refunds take?")
|
|
26
|
+
run.response
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`connection` requires `url` and also accepts `headers`, `timeout`, `signer`,
|
|
30
|
+
`allow_insecure_http`, and `max_response_bytes`. Pass a block when credentials
|
|
31
|
+
depend on the current Agent run:
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
connection do |binding|
|
|
35
|
+
token = McpAccessTokens.for_actor(binding.run.invocation.actor_id)
|
|
36
|
+
{
|
|
37
|
+
url: "https://mcp.example/rpc",
|
|
38
|
+
headers: {"Authorization" => "Bearer #{token}"},
|
|
39
|
+
timeout: 20
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The block's `binding` gives it access to the current Run. LittleGhost evaluates
|
|
45
|
+
the block before opening the MCP session, so each Agent run can use credentials
|
|
46
|
+
for its authenticated caller.
|
|
47
|
+
|
|
48
|
+
By default, the Agent receives every operation published by the server. Their
|
|
49
|
+
normalized server names, such as `search` and `fetch`, become Tool names.
|
|
50
|
+
|
|
51
|
+
Use `map_tool` when the Agent should receive only part of the server catalog or
|
|
52
|
+
when a generated Tool needs a different name or configuration:
|
|
53
|
+
|
|
54
|
+
```ruby
|
|
55
|
+
class CuratedHelpCenterTools < LittleGhost::MCP::Toolset
|
|
56
|
+
connection url: "https://mcp.example/rpc", timeout: 20
|
|
57
|
+
|
|
58
|
+
map_tool do |tool_class, definition:, binding:|
|
|
59
|
+
next unless %w[search fetch].include?(definition.source_name)
|
|
60
|
+
|
|
61
|
+
tool_class.tool_name "help_center_#{definition.source_name}"
|
|
62
|
+
tool_class
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`definition` describes the operation published by the server, and `binding`
|
|
68
|
+
identifies the current Agent run. Return the class after configuring it, or
|
|
69
|
+
return `nil` to omit the operation. Renaming a generated Tool does not change
|
|
70
|
+
the original `Definition#source_name` sent back to the server.
|
|
71
|
+
|
|
72
|
+
The Agent can call the generated Tools like local Tools. LittleGhost uses one
|
|
73
|
+
local client and transport for the Toolset during the Agent run. The built-in
|
|
74
|
+
HTTP transport does not send an MCP session-termination request. Configure
|
|
75
|
+
server-side expiry, or arrange explicit remote cleanup when the server requires
|
|
76
|
+
it.
|
|
77
|
+
|
|
78
|
+
Most MCP results need no mapping. LittleGhost returns `structuredContent` as a
|
|
79
|
+
Ruby Hash when present, otherwise it returns the server's text. Server images
|
|
80
|
+
become Artifacts.
|
|
81
|
+
|
|
82
|
+
Use `map_result` when one operation needs application-specific conversion. This
|
|
83
|
+
example turns the server's download identifier into a deferred Artifact:
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
map_result do |result, call:, binding:|
|
|
87
|
+
next result unless call.definition.source_name == "export"
|
|
88
|
+
|
|
89
|
+
LittleGhost::Tool::Result.new(
|
|
90
|
+
value: result.structured_content,
|
|
91
|
+
artifacts: [
|
|
92
|
+
LittleGhost::Artifact.deferred(
|
|
93
|
+
reference: result.metadata.fetch("download_id"),
|
|
94
|
+
media_type: "application/octet-stream"
|
|
95
|
+
)
|
|
96
|
+
]
|
|
97
|
+
)
|
|
98
|
+
end
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`map_result` receives the complete `MCP::Result`, the `MCP::Call` that produced
|
|
102
|
+
it, and the current binding. Return any Ruby value or `Tool::Result`. Returning
|
|
103
|
+
the supplied result unchanged keeps the default conversion described above.
|
|
104
|
+
MCP images and local Tool artifacts use the same storage and presentation
|
|
105
|
+
rules when `Configuration#artifacts` is enabled. Images and documents are sent
|
|
106
|
+
as model content; their stored references are fallback information rather than
|
|
107
|
+
a second representation. LittleGhost also checks results against
|
|
108
|
+
server-advertised JSON Schema Draft 2020-12 output schemas.
|
|
109
|
+
|
|
110
|
+
An optional server can fail discovery without preventing Agent construction:
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
class HelpCenterTools < LittleGhost::MCP::Toolset
|
|
114
|
+
connection { |binding| McpConnections.help_center(binding) }
|
|
115
|
+
optional true
|
|
116
|
+
on_error do |error, binding:|
|
|
117
|
+
McpAvailability.report(error, run_id: binding.run.invocation.run_id)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`optional true` converts expected provider and protocol discovery failures
|
|
123
|
+
into an empty Tool set. `on_error` observes only those caught failures.
|
|
124
|
+
Cancellation, deadlines, configuration errors, and application callback
|
|
125
|
+
failures still propagate.
|
|
126
|
+
|
|
127
|
+
LittleGhost limits the number and total size of discovered operations, the
|
|
128
|
+
complexity of their schemas, and the size and number of returned images.
|
|
129
|
+
`HTTPTransport` also limits each HTTP response and requires HTTPS unless local
|
|
130
|
+
HTTP is explicitly enabled.
|
|
131
|
+
|
|
132
|
+
> **Safety note:** An MCP server supplies descriptions and results that the model
|
|
133
|
+
> can see. Structural validation does not make that content trustworthy or
|
|
134
|
+
> authorize an operation it suggests. Expose only the operations the Agent
|
|
135
|
+
> needs, use narrowly scoped credentials, and have the server authorize every
|
|
136
|
+
> sensitive call. If a result becomes a deferred Artifact, its resolver must
|
|
137
|
+
> verify that the referenced file belongs to the authenticated caller, fetch
|
|
138
|
+
> only from an intended service, and limit the response size before returning
|
|
139
|
+
> bytes to LittleGhost.
|
|
140
|
+
|
|
141
|
+
LittleGhost implements its documented client behavior for the [MCP 2025-06-18
|
|
142
|
+
specification](https://modelcontextprotocol.io/specification/2025-06-18).
|
|
143
|
+
|
|
144
|
+
Use `LittleGhost::MCP::HTTPTransport` and `LittleGhost::MCP::Client` directly
|
|
145
|
+
when you need a custom transport. They produce the same generated Tool classes
|
|
146
|
+
and accept the same mapping callbacks as Toolset.
|
|
147
|
+
|
|
148
|
+
## Send a Run stream through AG-UI
|
|
149
|
+
|
|
150
|
+
The AG-UI adapter converts LittleGhost events into protocol event hashes:
|
|
151
|
+
|
|
152
|
+
```ruby
|
|
153
|
+
require "json"
|
|
154
|
+
require "little_ghost/ag_ui"
|
|
155
|
+
|
|
156
|
+
source = CustomerSupportAgent.stream_ask(
|
|
157
|
+
question,
|
|
158
|
+
actor_id: authenticated_user.id,
|
|
159
|
+
context: {account_id: authenticated_user.account_id}
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
events = LittleGhost::AGUI::Adapter.new.stream(
|
|
163
|
+
source,
|
|
164
|
+
thread_id: conversation.id,
|
|
165
|
+
run_id: request.request_id
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
events.each { |event| websocket.write(JSON.generate(event)) }
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The adapter translates text, reasoning, Tool activity, usage, retries, trace
|
|
172
|
+
context, subagent activity, and terminal outcomes. It is stateless between
|
|
173
|
+
calls. Your application still owns the connection, backpressure, disconnect
|
|
174
|
+
behavior, and any request state its callbacks need.
|
|
175
|
+
|
|
176
|
+
LittleGhost also emits namespaced custom events. Consumers should preserve or
|
|
177
|
+
deliberately ignore event types they don't recognize. See the [AG-UI event
|
|
178
|
+
documentation](https://docs.ag-ui.com/concepts/events) when implementing the
|
|
179
|
+
client.
|
|
180
|
+
|
|
181
|
+
> **Safety note:** A Run stream can include model output, Tool arguments and
|
|
182
|
+
> results, errors, and participant activity. Check that the connected user may
|
|
183
|
+
> see the complete Run, then filter fields before sending or storing events.
|
|
184
|
+
|
|
185
|
+
Calling `each` drives the source stream on the caller's fiber or thread. When a
|
|
186
|
+
client disconnects, stop enumerating and apply the cancellation behavior your
|
|
187
|
+
application needs. Closing the socket can't undo Tool work that already ran.
|
|
188
|
+
|
|
189
|
+
## Trace Runs with OpenTelemetry
|
|
190
|
+
|
|
191
|
+
Configure an OpenTelemetry SDK and exporter in the application, then register
|
|
192
|
+
the LittleGhost subscriber before the first Agent call:
|
|
193
|
+
|
|
194
|
+
```ruby
|
|
195
|
+
LittleGhost.configure do |config|
|
|
196
|
+
config.instrument LittleGhost::Tracing::OpenTelemetry.new
|
|
197
|
+
end
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
LittleGhost depends on `opentelemetry-api`, leaving the SDK, processor, and
|
|
201
|
+
exporter up to the application. It emits spans and events for Runs, Agents,
|
|
202
|
+
model calls, Tools, assemblies, sessions, usage, and failures. Active operations
|
|
203
|
+
can propagate W3C `traceparent` and `tracestate` fields.
|
|
204
|
+
|
|
205
|
+
Prompts, messages, responses, Tool arguments, and exception content are omitted
|
|
206
|
+
by default. If you intentionally need some of that content, install a
|
|
207
|
+
`LittleGhost::Support::ContentCapture` with a scrubber before enabling capture.
|
|
208
|
+
Avoid putting raw user, order, session, or request IDs in span attributes.
|
|
209
|
+
|
|
210
|
+
Attribute names follow the evolving [OpenTelemetry GenAI semantic
|
|
211
|
+
conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/) where they
|
|
212
|
+
apply. Flush or shut down `LittleGhost::Instrumentation` during application
|
|
213
|
+
shutdown when your backend buffers data.
|
|
214
|
+
|
|
215
|
+
See [Running in Production](production.md) for startup, shutdown, and observability,
|
|
216
|
+
[Tools](tools.md) for local and remote Tool behavior, and [Workspaces and
|
|
217
|
+
Sandboxes](sandboxing.md) for child processes and files.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Choose Models and Providers
|
|
2
|
+
|
|
3
|
+
An Agent needs a model target: a configured provider connection plus the
|
|
4
|
+
provider's model identifier. You can write that target directly while getting
|
|
5
|
+
started, then give it an application-facing name when several Agents share it.
|
|
6
|
+
|
|
7
|
+
## Start with one direct target
|
|
8
|
+
|
|
9
|
+
A direct target has the form `connection:model-id`:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
class CustomerSupportAgent < LittleGhost::Agent
|
|
13
|
+
model "openrouter:openai/gpt-5.6-luna"
|
|
14
|
+
system_prompt "Answer customer questions clearly and concisely."
|
|
15
|
+
end
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`openrouter` names a connection configured by the application. The remainder
|
|
19
|
+
is the model identifier understood by that provider. This is a good fit when
|
|
20
|
+
one Agent owns one stable choice.
|
|
21
|
+
|
|
22
|
+
## Give shared choices a role
|
|
23
|
+
|
|
24
|
+
A model role lets several Agents share a choice without knowing its provider
|
|
25
|
+
or model identifier:
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
LittleGhost.configure do |config|
|
|
29
|
+
config.providers = {
|
|
30
|
+
primary: {
|
|
31
|
+
adapter: :openrouter,
|
|
32
|
+
api_key: ENV.fetch("OPENROUTER_API_KEY")
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
config.models = {
|
|
36
|
+
customer_support: {
|
|
37
|
+
target: "primary:openai/gpt-5.6-luna",
|
|
38
|
+
settings: {temperature: 0.2}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
config.default_model = :customer_support
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class CustomerSupportAgent < LittleGhost::Agent
|
|
45
|
+
model :customer_support
|
|
46
|
+
end
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Here `customer_support` is the role, `primary` is the connection, and
|
|
50
|
+
`openrouter` is the adapter. You can move the role to another model or provider
|
|
51
|
+
without editing the Agent.
|
|
52
|
+
|
|
53
|
+
Profile settings are defaults. An individual call can override them:
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
run = CustomerSupportAgent.ask(
|
|
57
|
+
"Explain the refund decision.",
|
|
58
|
+
settings: {temperature: 0.0}
|
|
59
|
+
)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Build these settings in application code instead of passing request parameters
|
|
63
|
+
through unchanged. Settings can affect cost, latency, and model behavior.
|
|
64
|
+
|
|
65
|
+
## Configure connections in one place
|
|
66
|
+
|
|
67
|
+
LittleGhost includes adapters for OpenRouter, OpenAI-compatible APIs,
|
|
68
|
+
Anthropic, Gemini, Vertex AI, and Bedrock. Connections may live in an
|
|
69
|
+
initializer or in the conventional files under `config/little_ghost`.
|
|
70
|
+
|
|
71
|
+
Keep credentials in your application's secret manager. Agents refer to a role
|
|
72
|
+
or configured connection; they don't need to contain credentials. If your
|
|
73
|
+
application obtains short-lived credentials at runtime, configure a credential
|
|
74
|
+
resolver that returns them for the selected connection.
|
|
75
|
+
|
|
76
|
+
> **Safety note:** The selected provider may receive system instructions,
|
|
77
|
+
> caller input, conversation history, Tool results, schemas, and attachments.
|
|
78
|
+
> Choose a provider that is appropriate for that data, and keep credentials and
|
|
79
|
+
> provider endpoints under application control.
|
|
80
|
+
|
|
81
|
+
## Choose a role for each request
|
|
82
|
+
|
|
83
|
+
An Agent can select between configured roles using its `Invocation`:
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
class CustomerSupportAgent < LittleGhost::Agent
|
|
87
|
+
model do |invocation|
|
|
88
|
+
invocation.fetch(:premium_account, false) ? :premium_support : :customer_support
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Set `premium_account` from application state when creating the invocation. If
|
|
94
|
+
a public request offers a model choice, map that choice to one of your
|
|
95
|
+
configured roles rather than accepting an arbitrary provider target.
|
|
96
|
+
|
|
97
|
+
Trusted application code may also declare a selection inline:
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
class ResearchAgent < LittleGhost::Agent
|
|
101
|
+
model(
|
|
102
|
+
provider: "primary",
|
|
103
|
+
model: "openai/gpt-5.6-luna",
|
|
104
|
+
reasoning_effort: "high"
|
|
105
|
+
)
|
|
106
|
+
end
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`provider` still names a configured connection. The inline settings change the
|
|
110
|
+
selection; they don't create a new connection.
|
|
111
|
+
|
|
112
|
+
## Use model capabilities
|
|
113
|
+
|
|
114
|
+
`LittleGhost::ModelResolver` turns a role or target into an executable
|
|
115
|
+
`LittleGhost::Model`. Its catalog describes capabilities such as supported
|
|
116
|
+
input types, output limits, and structured results. LittleGhost uses that
|
|
117
|
+
information to reject unsupported attachments, constrain output limits, and
|
|
118
|
+
choose a structured-result strategy.
|
|
119
|
+
|
|
120
|
+
Provider capabilities can change. Handle failed Runs and provider errors even
|
|
121
|
+
when the catalog says a feature is supported.
|
|
122
|
+
|
|
123
|
+
Continue with [Prompts as Views](prompt_views.md) when an Agent's instructions
|
|
124
|
+
outgrow one string. See [Structured Results and Content](structured_outputs_and_content.md)
|
|
125
|
+
when you need checked result shapes, images, or documents.
|