little_ghost 0.1.0 → 0.2.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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +50 -47
  3. data/docs/guides/{Core Concepts.md → core_concepts.md} +40 -20
  4. data/docs/guides/getting_started.md +164 -0
  5. data/lib/little_ghost/ag_ui/adapter.rb +1 -1
  6. data/lib/little_ghost/agent/context_management.rb +1 -4
  7. data/lib/little_ghost/agent.rb +94 -26
  8. data/lib/little_ghost/agent_builder.rb +1 -1
  9. data/lib/little_ghost/configuration.rb +210 -29
  10. data/lib/little_ghost/data/model_catalog.json +19178 -0
  11. data/lib/little_ghost/errors.rb +6 -0
  12. data/lib/little_ghost/invocation.rb +2 -17
  13. data/lib/little_ghost/model.rb +53 -33
  14. data/lib/little_ghost/model_capabilities.rb +6 -5
  15. data/lib/little_ghost/model_resolver.rb +316 -0
  16. data/lib/little_ghost/models/catalog/models_dev_source.rb +62 -0
  17. data/lib/little_ghost/models/catalog/source.rb +30 -0
  18. data/lib/little_ghost/models/catalog.rb +154 -0
  19. data/lib/little_ghost/models/catalog_snapshot.rb +44 -0
  20. data/lib/little_ghost/models/configuration.rb +43 -0
  21. data/lib/little_ghost/models/details.rb +48 -0
  22. data/lib/little_ghost/models/target.rb +29 -0
  23. data/lib/little_ghost/provider_registry.rb +75 -0
  24. data/lib/little_ghost/providers/anthropic/catalog_source.rb +31 -0
  25. data/lib/little_ghost/providers/anthropic.rb +222 -0
  26. data/lib/little_ghost/providers/base.rb +46 -0
  27. data/lib/little_ghost/providers/bedrock/aws_protocol.rb +132 -0
  28. data/lib/little_ghost/providers/bedrock/catalog_source.rb +200 -0
  29. data/lib/little_ghost/providers/bedrock/credential_resolver.rb +123 -0
  30. data/lib/little_ghost/providers/bedrock/http_client.rb +78 -0
  31. data/lib/little_ghost/providers/bedrock.rb +23 -15
  32. data/lib/little_ghost/providers/configuration.rb +79 -0
  33. data/lib/little_ghost/providers/gemini/catalog_source.rb +35 -0
  34. data/lib/little_ghost/providers/gemini.rb +204 -0
  35. data/lib/little_ghost/providers/open_router/catalog_source.rb +42 -0
  36. data/lib/little_ghost/providers/open_router.rb +6 -2
  37. data/lib/little_ghost/providers/openai_compatible.rb +19 -23
  38. data/lib/little_ghost/providers/vertex_ai/credential_resolver.rb +90 -0
  39. data/lib/little_ghost/providers/vertex_ai.rb +38 -0
  40. data/lib/little_ghost/run.rb +7 -7
  41. data/lib/little_ghost/runtime.rb +10 -7
  42. data/lib/little_ghost/sandbox.rb +5 -5
  43. data/lib/little_ghost/session_store.rb +3 -3
  44. data/lib/little_ghost/structured_output.rb +2 -8
  45. data/lib/little_ghost/support/http_client.rb +186 -0
  46. data/lib/little_ghost/{providers → support}/sse_parser.rb +1 -1
  47. data/lib/little_ghost/tool.rb +5 -1
  48. data/lib/little_ghost/tool_registry.rb +2 -3
  49. data/lib/little_ghost/version.rb +1 -1
  50. data/lib/little_ghost/workflow.rb +1 -1
  51. data/lib/little_ghost.rb +21 -15
  52. metadata +28 -7
  53. data/docs/guides/Getting Started.md +0 -187
  54. data/lib/little_ghost/default_model_registry.rb +0 -71
  55. data/lib/little_ghost/model_registry.rb +0 -173
  56. data/lib/little_ghost/providers/http_transport.rb +0 -149
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16d2003f8e5b79a2532ab16b9a0a1d95a9ee65b21025371290aeccacacd0f8a1
4
- data.tar.gz: 0c4db0710a8650ebb1ae159bc59a62078c442a035b64c3f549cbfb686c881c32
3
+ metadata.gz: cfcd337b2268f123e2e64e4bdb63a68e3f093395888dac5b0e4fe18ce0f800c6
4
+ data.tar.gz: 31dc093cea43764a4f8526c140bb57d56752da34c2cbced63d96f2a68eeab41b
5
5
  SHA512:
6
- metadata.gz: c435a0f6ef9b9d4508d66f72610b32b58f45ed724af5d8b9fbf8cc20fc27eaa8a41dcffc1c4fe0e109fbfc3603dffdf393f0bc38444a554009b41d8302b74f1a
7
- data.tar.gz: 1945188f5019cd2b115f74df481a344124d793eaa74ace0e6efc57b84d9bc117ee20879a2764818c78918e1dcd0609f3c1230e27b9a6b2885b4db4288a9e8b6b
6
+ metadata.gz: b1ad15e0aa91583641dcd752a0b4157151b216672c0431c77d6b76293ee73e2841e9e39b49988c9517533f10f22014e376bb5c86d41929c62f6d2de83df3a81e
7
+ data.tar.gz: d39231bad7f80f01db55a6c8ba6af68292205f81f06f887149cb03bc68f777d4e759bc1c64bae661ebe25ba8b3118240c19a6876a2cf0e24712652776de52426
data/README.md CHANGED
@@ -6,26 +6,13 @@
6
6
 
7
7
  <hr>
8
8
 
9
- Bring agents and agentic workflows into an existing Ruby system, or use them as the core of a dedicated AI service. LittleGhost brings model providers, tools, streaming, sessions, delegation, deterministic composition, and observability together behind one coherent set of Ruby APIs.
9
+ Bring agents and agentic workflows into an existing Ruby system, or use them as the core of a dedicated AI service. LittleGhost connects model providers, tools, streaming, sessions, delegation, deterministic workflows, and observability through Ruby APIs.
10
10
 
11
- Here is the shape of a small customer support agent. `CustomerSupportModels` keeps provider details out of agent behavior, `HelpCenterLookupTool` exposes one narrow, validated help center lookup, and `CustomerSupportAgent` brings them together:
11
+ Set `OPENAI_API_KEY`, then paste this customer support agent into a Ruby console or file. It selects a model directly and exposes one validated application tool:
12
12
 
13
13
  ```ruby
14
14
  require "little_ghost"
15
15
 
16
- class CustomerSupportModels < LittleGhost::ModelRegistry
17
- def initialize
18
- super
19
- provider(:openai) do |model:, **|
20
- LittleGhost::Providers::OpenAI.new(
21
- api_key: ENV.fetch("OPENAI_API_KEY"),
22
- model:
23
- )
24
- end
25
- profile "customer_support", provider: :openai, model: "gpt-5"
26
- end
27
- end
28
-
29
16
  class HelpCenterLookupTool < LittleGhost::Tool
30
17
  description "Look up a help center entry by topic."
31
18
  input_schema(
@@ -43,80 +30,96 @@ end
43
30
 
44
31
  class CustomerSupportAgent < LittleGhost::Agent
45
32
  description "Answers customer support questions."
46
- model "customer_support"
33
+ model "openai:gpt-5.6-luna"
47
34
  system_prompt "Answer clearly. Check the help center before stating company guidance."
48
35
  tools HelpCenterLookupTool
49
36
  end
50
37
 
51
- LittleGhost.configure { |config| config.models CustomerSupportModels }
52
-
53
38
  run = CustomerSupportAgent.ask("Can I get a refund after two weeks?")
54
39
  puts run.response
55
40
  # One possible response:
56
41
  # Refunds are available within 30 days, so your purchase is eligible.
57
42
  ```
58
43
 
59
- The result is a normal Ruby object. You can inspect its outcome, final response, normalized messages, token usage, and error instead of parsing provider-specific payloads.
60
-
61
- LittleGhost keeps the model loop provider-neutral. Tools can run in parallel, mutating tools can opt into a run-wide exclusive lock, and oversized tool results are truncated to the configured limit before they enter model context. Strict result schemas, cancellation, deadlines, session checkpoints, structured events, and OpenTelemetry integration are available when an application needs them; none are required to define the first agent.
44
+ The completed `LittleGhost::Run` exposes its outcome, final response, normalized messages, token usage, and terminal error. Streaming callers receive `LittleGhost::StreamEvent` objects instead of provider-specific payloads:
62
45
 
63
- The core stays dependency-light, while provider SDKs and deployment choices remain with the application.
46
+ ```ruby
47
+ CustomerSupportAgent.stream_ask("Can I get a refund?").each do |event|
48
+ print event.data.fetch(:text) if event.type == :text_delta
49
+ end
50
+ ```
64
51
 
65
- ## How it fits together
52
+ ## How the pieces fit
66
53
 
67
54
  ```text
68
- application configuration ──> CustomerSupportModels ──> provider
69
-
55
+ provider connections + model selections ──> ModelResolver ──> provider
56
+
70
57
  request ──> CustomerSupportAgent ──> HelpCenterLookupTool
71
58
 
72
- └────────> ResearchAgent (when delegated)
59
+ └────────> ResearchAgent subagent
73
60
 
74
61
  request ──> ResponseWorkflow ──> ResearchAgent ──> CustomerSupportAgent
75
62
  ```
76
63
 
77
- Configuration owns shared services such as model registries, sessions, lookup paths, workspaces, sandboxes, and instrumentation. Agent classes own behavior: their logical model role, prompt, tools, limits, structured result, and delegation policy. A tool is a validated boundary around application code. A subagent lets the model delegate work within configured turn, concurrency, and depth limits; a workflow uses ordinary Ruby when your application must choose the sequence.
64
+ Configuration owns shared services such as model resolution, sessions, lookup paths, workspaces, sandboxes, and instrumentation. Agent classes own behavior: their model selection, prompt, tools, limits, result schema, and delegation policy. Tools expose narrow application operations. A subagent lets the model delegate within configured limits; a workflow uses ordinary Ruby when the application must control ordering.
78
65
 
79
- `CustomerSupportAgent.ask(...)` creates a standalone entrypoint, consumes its run, and returns the completed `LittleGhost::Run`. Create an instance explicitly when reusing a runtime or when an interface should render progress as `LittleGhost::StreamEvent` objects:
66
+ Each top-level execution owns a run lifecycle. The run checkpoints session state, closes its resources, aggregates usage, and emits framework events whether the entrypoint is an agent or a workflow.
80
67
 
81
- `LittleGhost::Agent.ask("hi")` uses the built-in default model selection and the system prompt `You are a helpful agent.` for a general-purpose agent.
68
+ ## Installation and configuration
69
+
70
+ LittleGhost requires Ruby 3.3 or newer. Add it to your bundle:
82
71
 
83
72
  ```ruby
84
- CustomerSupportAgent.new.stream_ask("Can I get a refund?").each do |event|
85
- print event.data[:text] if event.type == :text_delta
86
- end
73
+ gem "little_ghost"
87
74
  ```
88
75
 
89
- Applications can add a `ResearchAgent` as a subagent for open-ended investigation, or place both agents behind a deterministic `ResponseWorkflow`. The same run lifecycle checkpoints session state, closes owned resources, aggregates usage, and emits framework events in each form.
76
+ Then run `bundle install`. Built-in OpenAI-compatible, OpenRouter, Anthropic, Gemini, Vertex AI, and Amazon Bedrock integrations use Ruby's standard library and normalize responses into the same protocol.
90
77
 
91
- ## Installation
78
+ Configuration does not require a particular directory layout. Provider connections and model profiles resolve independently in this order:
92
79
 
93
- LittleGhost requires Ruby 3.3 or newer. Add it to your bundle:
80
+ 1. An inline `config.providers` or `config.models` declaration.
81
+ 2. The corresponding explicit `config.providers_path` or `config.models_path`.
82
+ 3. The optional conventional file under `config/little_ghost/`.
83
+ 4. Environment-based provider selection and the built-in `default` profile.
94
84
 
95
- ```ruby
96
- gem "little_ghost"
85
+ An explicit path must exist. A missing conventional file is valid. The conventional form keeps connection policy separate from model roles:
86
+
87
+ ```yaml
88
+ # config/little_ghost/providers.yml
89
+ providers:
90
+ openai:
91
+ adapter: openai
92
+ api_key: <%= ENV.fetch("OPENAI_API_KEY") %>
97
93
  ```
98
94
 
99
- Then run `bundle install`. Provider SDKs and OpenTelemetry exporters are optional application dependencies. The built-in OpenAI, OpenAI-compatible, OpenRouter, and Amazon Bedrock integrations normalize their responses into the same LittleGhost protocol.
95
+ ```yaml
96
+ # config/little_ghost/models.yml
97
+ default_model: customer_support
98
+ models:
99
+ customer_support:
100
+ target: openai:gpt-5.6-luna
101
+ ```
100
102
 
101
- By default, LittleGhost maps the `default` role to GPT-5.6 Terra. It selects the first nonblank API key from `LITTLEGHOST_OPENROUTER_API_KEY`, `LITTLEGHOST_OPENAI_API_KEY`, `OPENROUTER_API_KEY`, and `OPENAI_API_KEY`, in that order. Setting one of these keys authorizes model inputs—including prompts, conversation history, tool data, and attachments—to be sent to the selected external provider. Applications with provider or data-residency requirements should configure a model registry explicitly.
103
+ By default, LittleGhost maps `default` to GPT-5.6 Luna. It configures conventional OpenRouter and OpenAI connections from nonblank `LITTLEGHOST_OPENROUTER_API_KEY`, `LITTLEGHOST_OPENAI_API_KEY`, `OPENROUTER_API_KEY`, and `OPENAI_API_KEY` values; that order determines the default when more than one provider is available. Model inputs—including prompts, history, tool data, and attachments—leave the application for the selected external provider. Configure providers explicitly when provider choice or data residency matters.
102
104
 
103
- By default, structured framework events have no console destination. Hosted applications can emit redacted JSON lines to standard output with `LittleGhost.configure { |config| config.log_events_to :stdout }`; `:stderr` selects standard error instead, and `nil` disables a configured destination. Event emission and severity levels are the same with or without a console destination.
105
+ Applications that need custom routing can subclass `LittleGhost::ModelResolver` and install the class with `config.model_resolver`. A custom resolver owns its profiles and default role; configuring `models`, `models_path`, or `default_model` at the same time is an error. Provider configuration remains available to the resolver.
104
106
 
105
- Prompts can stay inline while an agent is small, then move into conventional ERB templates under `app/prompts`. Agents and tools under `app/agents` and `app/tools` are loaded from the configured application root, keeping framework setup separate from product behavior.
107
+ LittleGhost runs inside the surrounding Ruby process; it does not prescribe an HTTP server, CLI, job system, or application layout. `config/little_ghost`, `app/agents`, `app/prompts`, `app/tools`, and `app/skills` are optional conventions. Every path can be configured, and agents, prompts, and tools may live wherever the application loads them.
106
108
 
107
109
  ## Documentation
108
110
 
109
- - [LittleGhost website](https://mattyr.github.io/little_ghost/) is the quickest way to meet the framework and find your next step.
110
- - [Getting Started](docs/guides/Getting%20Started.md) builds the customer support example from an empty application and runs it.
111
- - [Core Concepts](docs/guides/Core%20Concepts.md) explains models, agents, tools, delegation, workflows, sessions, and streaming through the same example.
112
- - [API reference](rdoc-ref:LittleGhost) covers exact signatures, options, and lifecycle details.
111
+ - [Getting Started](docs/guides/getting_started.md) builds and streams the customer support example.
112
+ - [Core Concepts](docs/guides/core_concepts.md) explains models, agents, tools, runs, delegation, workflows, and sessions.
113
+ - [API reference](rdoc-ref:LittleGhost) covers exact signatures, options, and lifecycle behavior.
113
114
 
114
- LittleGhost is licensed under the MIT License.
115
+ ## Contributing
115
116
 
116
- ## Development
117
+ See the [contributing guide](https://github.com/mattyr/little_ghost/blob/main/CONTRIBUTING.md), [Code of Conduct](https://github.com/mattyr/little_ghost/blob/main/CODE_OF_CONDUCT.md), and [security policy](https://github.com/mattyr/little_ghost/blob/main/SECURITY.md).
117
118
 
118
119
  ```sh
119
120
  bundle install
120
121
  bundle exec rake test
121
122
  bundle exec standardrb --no-fix
122
123
  ```
124
+
125
+ LittleGhost is licensed under the MIT License.
@@ -1,10 +1,10 @@
1
1
  # Core Concepts
2
2
 
3
- LittleGhost gives Ruby software two ways to compose AI behavior. Agents can choose among validated tools and delegated specialists, while agentic workflows keep required ordering and branching under application control. The customer support example makes that boundary visible: CustomerSupportModels chooses provider-backed models, CustomerSupportAgent owns behavior, HelpCenterLookupTool exposes a narrow help center lookup, ResearchAgent handles delegated investigation, and ResponseWorkflow imposes a deterministic sequence when the surrounding system requires one.
3
+ LittleGhost gives Ruby software two ways to compose AI behavior. Agents can choose among validated tools and delegated specialists, while agentic workflows keep required ordering and branching under application control. The customer support example makes that boundary visible: ModelResolver chooses provider-backed models, CustomerSupportAgent owns behavior, HelpCenterLookupTool exposes a narrow help center lookup, ResearchAgent handles delegated investigation, and ResponseWorkflow imposes a deterministic sequence when the surrounding system requires one.
4
4
 
5
5
  ```text
6
6
  shared configuration
7
- └── CustomerSupportModels ── resolves logical roles ──> provider clients
7
+ └── ModelResolver ── resolves model selections ──> provider clients
8
8
 
9
9
  one request
10
10
  └── Run
@@ -17,28 +17,48 @@ one deterministic request
17
17
  └── Run ──> ResponseWorkflow ──> ResearchAgent ──> CustomerSupportAgent
18
18
  ```
19
19
 
20
- ## Models are selected by role
20
+ ## Models can be selected directly or by role
21
21
 
22
- An agent names a logical role such as `customer_support`, not a vendor model. `CustomerSupportModels` maps that stable application vocabulary to a provider and model identifier:
22
+ An agent can name a canonical target directly when the choice belongs beside its behavior:
23
23
 
24
24
  ```ruby
25
- class CustomerSupportModels < LittleGhost::ModelRegistry
26
- def initialize
27
- super
28
- provider(:openai) do |model:, **|
29
- LittleGhost::Providers::OpenAI.new(
30
- api_key: ENV.fetch("OPENAI_API_KEY"),
31
- model:
32
- )
33
- end
34
- profile "customer_support", provider: :openai, model: "gpt-5"
35
- end
25
+ class CustomerSupportAgent < LittleGhost::Agent
26
+ model "openai:gpt-5.6-luna"
27
+ end
28
+ ```
29
+
30
+ It can also attach trusted model settings without defining a shared profile:
31
+
32
+ ```ruby
33
+ class DeliberateSupportAgent < LittleGhost::Agent
34
+ model(provider: "openai", model: "gpt-5.6-luna", reasoning_effort: "high")
35
+ end
36
+ ```
37
+
38
+ In both forms, `provider` is the name of a configured connection. A role such as `customer_support` adds stable application vocabulary when several agents or deployments should share routing policy:
39
+
40
+ ```ruby
41
+ LittleGhost.configure do |config|
42
+ config.providers = {
43
+ openai: {adapter: :openai, api_key: ENV.fetch("OPENAI_API_KEY")}
44
+ }
45
+ config.models = {
46
+ customer_support: {target: "openai:gpt-5.6-luna"}
47
+ }
36
48
  end
37
49
  ```
38
50
 
39
- Dotted roles inherit from the nearest registered parent. `ResearchAgent` can request `customer_support.research` and initially use the `customer_support` profile; registering `customer_support.research` later specializes it. Per-invocation profile overrides can vary a request without mutating the registry or agent class. Because an override can select a different registered provider, model, and settings, it is trusted application configuration and must be constructed or allowlisted by the application rather than copied from unchecked request data.
51
+ ```ruby
52
+ class CustomerSupportAgent < LittleGhost::Agent
53
+ model :customer_support
54
+ end
55
+ ```
56
+
57
+ Strings and symbols without a colon are roles; strings containing a colon are canonical targets; mappings require `provider` and `model`, with remaining keys treated as model settings. Role names cannot contain a colon. Direct targets and mappings bypass role inheritance and overlays.
58
+
59
+ Dotted roles inherit from the nearest registered parent. `ResearchAgent` can request `customer_support.research` and initially use the `customer_support` profile; registering `customer_support.research` later specializes it. A resolver caller may pass an explicit `profiles:` overlay without mutating the configured profiles or agent class. Because an overlay can select a different registered provider, model, and settings, it is trusted application configuration and must be constructed or allowlisted by the application rather than copied from unchecked request data. The base resolver does not inspect application-specific invocation fields.
40
60
 
41
- The provider performs model I/O. The registry resolves application intent into a `LittleGhost::Model`, which carries the provider, settings, metadata, model identifier, and role for a run.
61
+ The provider performs model I/O. `LittleGhost::ModelResolver` resolves application intent into a `LittleGhost::Model`, which carries the provider, target, settings, details, and role for a run.
42
62
 
43
63
  ## Agents declare behavior
44
64
 
@@ -56,7 +76,7 @@ end
56
76
 
57
77
  The class-level DSL is inheritable. It can declare prompts, limits, callbacks, tool classes, structured results, context management, skills, and delegation. A capability mixin may be included in `LittleGhost::Agent`, but its behavior remains inactive until the corresponding DSL is called.
58
78
 
59
- `CustomerSupportAgent.ask` creates a standalone, console-friendly entrypoint, builds and consumes a `LittleGhost::Run`, and returns that run. Create `CustomerSupportAgent.new` explicitly to reuse a runtime or call `#stream_ask` for the run's events. Agents built by a runtime are instead scoped to their owning run and return a `LittleGhost::RunResult` from `#call`.
79
+ `CustomerSupportAgent.ask` creates a standalone entrypoint, builds and consumes a `LittleGhost::Run`, and returns that run. `CustomerSupportAgent.stream_ask` creates the same kind of entrypoint and yields the run's events. Create `CustomerSupportAgent.new(runtime:)` explicitly when several calls should reuse one runtime. Agents built by a runtime are instead scoped to their owning run and return a `LittleGhost::RunResult` from `#call`.
60
80
 
61
81
  That distinction explains two useful return paths:
62
82
 
@@ -195,9 +215,9 @@ Streams expose generic framework events rather than provider wire formats. Consu
195
215
 
196
216
  The core design can be summarized as four choices:
197
217
 
198
- - Put shared construction and provider policy in configuration and model registries.
218
+ - Put shared construction and provider policy in configuration; use inline declarations or independent YAML files according to the application's needs.
199
219
  - Put model behavior and available capabilities on agent classes.
200
220
  - Put privileged application operations behind narrow, authorized tools.
201
221
  - Put mandatory ordering in workflows; leave optional delegation to subagents.
202
222
 
203
- Return to [Getting Started](Getting%20Started.md) for the complete first-run setup. The API reference covers exact signatures and lifecycle details for `LittleGhost::Runtime`, `LittleGhost::Run`, `LittleGhost::Agent`, `LittleGhost::Tool`, `LittleGhost::Workflow`, and `LittleGhost::ModelRegistry`.
223
+ Return to [Getting Started](getting_started.md) for the complete first-run setup. The API reference covers exact signatures and lifecycle details for `LittleGhost::Runtime`, `LittleGhost::Run`, `LittleGhost::Agent`, `LittleGhost::Tool`, `LittleGhost::Workflow`, and `LittleGhost::ModelResolver`.
@@ -0,0 +1,164 @@
1
+ # Getting Started with LittleGhost
2
+
3
+ This guide builds a customer support agent that checks a small help center before answering. It focuses on the Ruby objects needed for one useful run: a tool, an agent, and the call that starts it.
4
+
5
+ LittleGhost runs inside your Ruby process. It does not choose how your application is hosted or where these definitions live.
6
+
7
+ ## Install LittleGhost
8
+
9
+ LittleGhost requires Ruby 3.3 or newer. Add the gem to your `Gemfile`:
10
+
11
+ ```ruby
12
+ gem "little_ghost"
13
+ ```
14
+
15
+ Install the bundle and set a provider credential:
16
+
17
+ ```sh
18
+ $ bundle install
19
+ $ export OPENAI_API_KEY="..."
20
+ ```
21
+
22
+ With `OPENAI_API_KEY` present, LittleGhost can connect the `openai` provider name used below. OpenRouter credentials work as well. Use application secret management outside a local shell, and do not commit provider credentials.
23
+
24
+ ## Give the agent a tool
25
+
26
+ Start by requiring LittleGhost and defining a narrow help center lookup:
27
+
28
+ ```ruby
29
+ require "little_ghost"
30
+
31
+ class HelpCenterLookupTool < LittleGhost::Tool
32
+ HELP_CENTER_ENTRIES = {
33
+ "refunds" => "Refunds are available within 30 days of purchase.",
34
+ "shipping" => "Standard shipping takes three to five business days."
35
+ }.freeze
36
+
37
+ description "Look up a help center entry by topic."
38
+ input_schema(
39
+ type: "object",
40
+ properties: {
41
+ topic: {type: "string", enum: HELP_CENTER_ENTRIES.keys}
42
+ },
43
+ required: ["topic"],
44
+ additionalProperties: false
45
+ )
46
+
47
+ def call(input)
48
+ HELP_CENTER_ENTRIES.fetch(input.fetch("topic"))
49
+ end
50
+ end
51
+ ```
52
+
53
+ A tool gives the model one application operation with a name, description, and validated JSON input. LittleGhost validates the input before calling `#call` and turns the returned value into model context.
54
+
55
+ The schema checks shape, not authorization. A tool that reads customer data or performs an action must enforce the application's trust rules inside its implementation.
56
+
57
+ ## Define the agent
58
+
59
+ Now describe the agent's behavior and make the lookup available to it:
60
+
61
+ ```ruby
62
+ class CustomerSupportAgent < LittleGhost::Agent
63
+ description "Answers customer support questions."
64
+ model "openai:gpt-5.6-luna"
65
+ system_prompt <<~PROMPT
66
+ Answer clearly and do not invent company guidance.
67
+ Use the help center lookup tool before stating company guidance.
68
+ PROMPT
69
+
70
+ tools HelpCenterLookupTool
71
+ end
72
+ ```
73
+
74
+ An agent class is a reusable behavior definition. It owns its prompt, tools, model selection, limits, and other capabilities. This agent names an OpenAI connection and model directly, so the first example does not need a separate model profile.
75
+
76
+ ## Ask a question
77
+
78
+ Call the agent class to run one request to completion:
79
+
80
+ ```ruby
81
+ run = CustomerSupportAgent.ask(
82
+ "I bought an item two weeks ago. Can I get a refund?"
83
+ )
84
+
85
+ if run.completed?
86
+ puts run.response
87
+ else
88
+ warn "Support request ended as #{run.outcome}: #{run.error&.class}"
89
+ end
90
+ ```
91
+
92
+ `CustomerSupportAgent.ask` creates and consumes a `LittleGhost::Run`. A successful run exposes its final text through `#response`; it also retains the normalized result, outcome, usage, messages, and any terminal error.
93
+
94
+ The model can call `HelpCenterLookupTool` with `{"topic":"refunds"}` and answer along these lines:
95
+
96
+ ```text
97
+ Refunds are available within 30 days, so a purchase from two weeks ago is eligible.
98
+ ```
99
+
100
+ Model wording and tool selection are not deterministic. The prompt directs the agent to ground company guidance in the validated lookup; applications that must enforce a lookup should put that ordering in a workflow.
101
+
102
+ ## Stream the same agent
103
+
104
+ Use `.stream_ask` when a console, HTTP response, or user interface should receive progress while the run is active:
105
+
106
+ ```ruby
107
+ CustomerSupportAgent.stream_ask("Can I get a refund?").each do |event|
108
+ case event.type
109
+ when :text_delta
110
+ print event.data.fetch(:text)
111
+ when :run_error
112
+ warn event.data.fetch(:message)
113
+ end
114
+ end
115
+ ```
116
+
117
+ The stream yields `LittleGhost::StreamEvent` objects. Text, tool activity, usage, traces, and terminal lifecycle facts share this interface, so callers do not need provider-specific response handling.
118
+
119
+ Both calls use the same agent definition and active LittleGhost configuration. Core Concepts explains reusable runtimes when an application needs more control over shared services.
120
+
121
+ ## Add a model role when the application grows
122
+
123
+ Direct targets keep a small application easy to read. A larger application can give the same selection a stable role, then change the underlying provider, model, and defaults without editing each agent class:
124
+
125
+ ```ruby
126
+ LittleGhost.configure do |config|
127
+ config.providers = {
128
+ openai: {adapter: :openai, api_key: ENV.fetch("OPENAI_API_KEY")}
129
+ }
130
+ config.models = {
131
+ customer_support: {
132
+ target: "openai:gpt-5.6-luna",
133
+ settings: {temperature: 0.2}
134
+ }
135
+ }
136
+ config.default_model = :customer_support
137
+ end
138
+
139
+ class CustomerSupportAgent < LittleGhost::Agent
140
+ model :customer_support
141
+ end
142
+ ```
143
+
144
+ An agent may also keep a small amount of model-specific configuration beside its behavior:
145
+
146
+ ```ruby
147
+ class DeliberateSupportAgent < LittleGhost::Agent
148
+ model(
149
+ provider: "openai",
150
+ model: "gpt-5.6-luna",
151
+ reasoning_effort: "high"
152
+ )
153
+ end
154
+ ```
155
+
156
+ Here, `provider` names a configured connection and every other key after `model` is a trusted model setting. Provider connections and model profiles may instead come from independent YAML files under `config/little_ghost`, or from paths selected in `LittleGhost.configure`. Inline declarations take precedence over explicit paths, which take precedence over conventional files; environment-based selection and the built-in default are the final fallback. `LittleGhost::Agent` and `LittleGhost::Configuration` document the complete shapes and precedence.
157
+
158
+ ## Fit LittleGhost into your application
159
+
160
+ LittleGhost does not require an application layout. Keep agents and tools beside related application code when your framework or loader already has a home for them. If you want LittleGhost to eager-load these definitions, `app/agents` and `app/tools` are available conventions, and every lookup path is configurable.
161
+
162
+ Hosting remains the surrounding application's responsibility. A Rails controller, Rack endpoint, background job, CLI, or another Ruby entrypoint can call the same agent APIs shown above.
163
+
164
+ Read [Core Concepts](core_concepts.md) next for model selection, reusable runtimes, subagents, workflows, sessions, and the boundaries between them. The API reference covers exact signatures for `LittleGhost::Agent`, `LittleGhost::Tool`, `LittleGhost::Configuration`, and `LittleGhost::Run`.
@@ -9,7 +9,7 @@ module LittleGhost
9
9
  # Adapter turns a LittleGhost stream into AG-UI event hashes. It lets a Ruby
10
10
  # agent drive compatible chat interfaces without changing the agent itself.
11
11
  #
12
- # events = CustomerSupportAgent.new.stream_ask("Where is my order?")
12
+ # events = CustomerSupportAgent.stream_ask("Where is my order?")
13
13
  # adapter = LittleGhost::AGUI::Adapter.new
14
14
  # adapter.stream(events, thread_id: "thread-1", run_id: "run-1").each do |event|
15
15
  # websocket.write(JSON.generate(event))
@@ -259,10 +259,7 @@ module LittleGhost
259
259
  end
260
260
 
261
261
  def model_context_window_tokens(configuration)
262
- value = model.respond_to?(:metadata) && (
263
- model.metadata[:context_window_tokens] || model.metadata["context_window_tokens"] ||
264
- model.metadata[:context_window] || model.metadata["context_window"]
265
- )
262
+ value = model.details.context_window
266
263
  value = Integer(value) if value
267
264
  value&.positive? ? value : configuration.fetch(:context_window_tokens)
268
265
  rescue ArgumentError, TypeError