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.
- checksums.yaml +4 -4
- data/README.md +50 -47
- data/docs/guides/{Core Concepts.md → core_concepts.md} +40 -20
- data/docs/guides/getting_started.md +164 -0
- data/lib/little_ghost/ag_ui/adapter.rb +1 -1
- data/lib/little_ghost/agent/context_management.rb +1 -4
- data/lib/little_ghost/agent.rb +94 -26
- data/lib/little_ghost/agent_builder.rb +1 -1
- data/lib/little_ghost/configuration.rb +210 -29
- data/lib/little_ghost/data/model_catalog.json +19178 -0
- data/lib/little_ghost/errors.rb +6 -0
- data/lib/little_ghost/invocation.rb +2 -17
- data/lib/little_ghost/model.rb +53 -33
- data/lib/little_ghost/model_capabilities.rb +6 -5
- data/lib/little_ghost/model_resolver.rb +316 -0
- data/lib/little_ghost/models/catalog/models_dev_source.rb +62 -0
- data/lib/little_ghost/models/catalog/source.rb +30 -0
- data/lib/little_ghost/models/catalog.rb +154 -0
- data/lib/little_ghost/models/catalog_snapshot.rb +44 -0
- data/lib/little_ghost/models/configuration.rb +43 -0
- data/lib/little_ghost/models/details.rb +48 -0
- data/lib/little_ghost/models/target.rb +29 -0
- data/lib/little_ghost/provider_registry.rb +75 -0
- data/lib/little_ghost/providers/anthropic/catalog_source.rb +31 -0
- data/lib/little_ghost/providers/anthropic.rb +222 -0
- data/lib/little_ghost/providers/base.rb +46 -0
- data/lib/little_ghost/providers/bedrock/aws_protocol.rb +132 -0
- data/lib/little_ghost/providers/bedrock/catalog_source.rb +200 -0
- data/lib/little_ghost/providers/bedrock/credential_resolver.rb +123 -0
- data/lib/little_ghost/providers/bedrock/http_client.rb +78 -0
- data/lib/little_ghost/providers/bedrock.rb +23 -15
- data/lib/little_ghost/providers/configuration.rb +79 -0
- data/lib/little_ghost/providers/gemini/catalog_source.rb +35 -0
- data/lib/little_ghost/providers/gemini.rb +204 -0
- data/lib/little_ghost/providers/open_router/catalog_source.rb +42 -0
- data/lib/little_ghost/providers/open_router.rb +6 -2
- data/lib/little_ghost/providers/openai_compatible.rb +19 -23
- data/lib/little_ghost/providers/vertex_ai/credential_resolver.rb +90 -0
- data/lib/little_ghost/providers/vertex_ai.rb +38 -0
- data/lib/little_ghost/run.rb +7 -7
- data/lib/little_ghost/runtime.rb +10 -7
- data/lib/little_ghost/sandbox.rb +5 -5
- data/lib/little_ghost/session_store.rb +3 -3
- data/lib/little_ghost/structured_output.rb +2 -8
- data/lib/little_ghost/support/http_client.rb +186 -0
- data/lib/little_ghost/{providers → support}/sse_parser.rb +1 -1
- data/lib/little_ghost/tool.rb +5 -1
- data/lib/little_ghost/tool_registry.rb +2 -3
- data/lib/little_ghost/version.rb +1 -1
- data/lib/little_ghost/workflow.rb +1 -1
- data/lib/little_ghost.rb +21 -15
- metadata +28 -7
- data/docs/guides/Getting Started.md +0 -187
- data/lib/little_ghost/default_model_registry.rb +0 -71
- data/lib/little_ghost/model_registry.rb +0 -173
- data/lib/little_ghost/providers/http_transport.rb +0 -149
data/lib/little_ghost/runtime.rb
CHANGED
|
@@ -10,7 +10,10 @@ module LittleGhost
|
|
|
10
10
|
#
|
|
11
11
|
# configuration = LittleGhost::Configuration.new(
|
|
12
12
|
# root: Dir.pwd,
|
|
13
|
-
#
|
|
13
|
+
# providers: {
|
|
14
|
+
# openai: {adapter: :openai, api_key: ENV.fetch("OPENAI_API_KEY")}
|
|
15
|
+
# },
|
|
16
|
+
# models: {customer_support: {target: "openai:gpt-5.6-luna"}},
|
|
14
17
|
# default_model: "customer_support",
|
|
15
18
|
# service_name: "support-api"
|
|
16
19
|
# )
|
|
@@ -22,7 +25,7 @@ module LittleGhost
|
|
|
22
25
|
# Without explicit +settings+, construction canonicalizes the root, loads
|
|
23
26
|
# +config/little_ghost.rb+ once through the Configuration, snapshots settings,
|
|
24
27
|
# configures instrumentation, eager-loads application constants, and builds the
|
|
25
|
-
# selected model
|
|
28
|
+
# selected model resolver and session store. Supplying +settings+ is the
|
|
26
29
|
# lower-level path used to create a sibling runtime from an existing snapshot.
|
|
27
30
|
#
|
|
28
31
|
# Reuse a runtime across runs. +build_run+ creates any missing workspace and
|
|
@@ -38,7 +41,7 @@ module LittleGhost
|
|
|
38
41
|
class Runtime
|
|
39
42
|
# The snapshotted setup and materialized services used by new runs.
|
|
40
43
|
attr_reader :configuration, :settings, :root, :loader, :prompt_paths, :skill_paths,
|
|
41
|
-
:skill_resource_root, :
|
|
44
|
+
:skill_resource_root, :model_resolver, :session_store, :workspace_class, :sandbox_class,
|
|
42
45
|
:runtime_hooks
|
|
43
46
|
|
|
44
47
|
# Starts a runtime from +configuration+ or an existing settings snapshot.
|
|
@@ -75,9 +78,9 @@ module LittleGhost
|
|
|
75
78
|
loader.setup
|
|
76
79
|
loader.eager_load
|
|
77
80
|
|
|
78
|
-
@startup_phase = "
|
|
81
|
+
@startup_phase = "model_resolver"
|
|
79
82
|
@invocation_class = @settings[:invocation] || Invocation
|
|
80
|
-
@
|
|
83
|
+
@model_resolver = @settings.fetch(:model_resolver)
|
|
81
84
|
@default_model = @settings.fetch(:default_model, "default").to_s
|
|
82
85
|
|
|
83
86
|
@startup_phase = "session_store"
|
|
@@ -189,8 +192,8 @@ module LittleGhost
|
|
|
189
192
|
end
|
|
190
193
|
|
|
191
194
|
def model_for(agent_class, run) # :nodoc:
|
|
192
|
-
|
|
193
|
-
|
|
195
|
+
selection = agent_class.model_selection(run.invocation) || @default_model
|
|
196
|
+
model_resolver.resolve(selection, invocation: run.invocation, context: run)
|
|
194
197
|
end
|
|
195
198
|
|
|
196
199
|
def open_session(run) # :nodoc:
|
data/lib/little_ghost/sandbox.rb
CHANGED
|
@@ -90,22 +90,22 @@ module LittleGhost
|
|
|
90
90
|
|
|
91
91
|
# Reads UTF-8 text at a workspace-relative +path+.
|
|
92
92
|
def read(path, context: nil)
|
|
93
|
-
raise
|
|
93
|
+
raise AbstractMethodError, "#{self.class} does not support filesystem reads"
|
|
94
94
|
end
|
|
95
95
|
|
|
96
96
|
# Lists entries at a workspace-relative directory +path+.
|
|
97
97
|
def list(path = ".", context: nil)
|
|
98
|
-
raise
|
|
98
|
+
raise AbstractMethodError, "#{self.class} does not support filesystem listings"
|
|
99
99
|
end
|
|
100
100
|
|
|
101
101
|
# Writes +content+ to a workspace-relative +path+.
|
|
102
102
|
def write(path, content, context: nil)
|
|
103
|
-
raise
|
|
103
|
+
raise AbstractMethodError, "#{self.class} does not support filesystem writes"
|
|
104
104
|
end
|
|
105
105
|
|
|
106
106
|
# Replaces one exact +old_text+ occurrence with +new_text+.
|
|
107
107
|
def replace(path, old_text, new_text, context: nil)
|
|
108
|
-
raise
|
|
108
|
+
raise AbstractMethodError, "#{self.class} does not support filesystem edits"
|
|
109
109
|
end
|
|
110
110
|
|
|
111
111
|
# Executes +command+ through +/bin/sh+.
|
|
@@ -127,7 +127,7 @@ module LittleGhost
|
|
|
127
127
|
# Implementations must enforce +timeout+ and +max_output_bytes+. Environment
|
|
128
128
|
# inheritance is disabled by default to avoid leaking process credentials.
|
|
129
129
|
def execute_program(command, timeout:, context: nil, max_output_bytes: 1_000_000, environment: {}, inherit_environment: false)
|
|
130
|
-
raise
|
|
130
|
+
raise AbstractMethodError, "#{self.class} does not support program execution"
|
|
131
131
|
end
|
|
132
132
|
|
|
133
133
|
# Releases sandbox resources. Runs close the sandbox before its workspace.
|
|
@@ -36,19 +36,19 @@ module LittleGhost
|
|
|
36
36
|
|
|
37
37
|
# Finds the snapshot for +id+, or returns nil when it does not exist.
|
|
38
38
|
def load(_id, actor_id: nil)
|
|
39
|
-
raise
|
|
39
|
+
raise AbstractMethodError, "#{self.class} must implement #load"
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
# Atomically appends sanitized messages and stores state and metadata.
|
|
43
43
|
# Implementations raise ProtocolError if the persisted message count differs
|
|
44
44
|
# from +expected_count+.
|
|
45
45
|
def append(_id, messages:, state:, metadata:, expected_count:, actor_id: nil)
|
|
46
|
-
raise
|
|
46
|
+
raise AbstractMethodError, "#{self.class} must implement #append"
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
# Replaces the complete snapshot for +id+ atomically.
|
|
50
50
|
def replace(_id, messages:, state:, metadata:, actor_id: nil)
|
|
51
|
-
raise
|
|
51
|
+
raise AbstractMethodError, "#{self.class} must implement #replace"
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
# Stores may expose a clean conversational view without changing the stored
|
|
@@ -56,11 +56,7 @@ module LittleGhost
|
|
|
56
56
|
return unless configuration
|
|
57
57
|
|
|
58
58
|
requested = configuration.fetch(:strategy, :auto).to_sym
|
|
59
|
-
capabilities =
|
|
60
|
-
model.capabilities
|
|
61
|
-
else
|
|
62
|
-
ModelCapabilities.legacy
|
|
63
|
-
end
|
|
59
|
+
capabilities = model.capabilities
|
|
64
60
|
strategy = case requested
|
|
65
61
|
when :auto then automatic_strategy(model, capabilities, ordinary_tools)
|
|
66
62
|
when :provider
|
|
@@ -117,9 +113,7 @@ module LittleGhost
|
|
|
117
113
|
private_class_method :validate_explicit_tool!
|
|
118
114
|
|
|
119
115
|
def model_identity(model)
|
|
120
|
-
|
|
121
|
-
id = model.respond_to?(:id) ? model.id : nil
|
|
122
|
-
[provider, id].compact.join("/")
|
|
116
|
+
[model.target.provider, model.model_id].join("/")
|
|
123
117
|
end
|
|
124
118
|
private_class_method :model_identity
|
|
125
119
|
end
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "openssl"
|
|
5
|
+
require "uri"
|
|
6
|
+
|
|
7
|
+
module LittleGhost
|
|
8
|
+
module Support
|
|
9
|
+
# HTTPClient gives integrations a shared, bounded streaming HTTP layer.
|
|
10
|
+
# It applies cancellation, deadlines, timeouts, and response-size limits while
|
|
11
|
+
# yielding response chunks as they arrive.
|
|
12
|
+
#
|
|
13
|
+
# === Security and trust
|
|
14
|
+
#
|
|
15
|
+
# HTTPS is required by default. Enabling +allow_insecure_http+ can expose API
|
|
16
|
+
# keys and model content in transit; use it only with a trusted local
|
|
17
|
+
# development endpoint.
|
|
18
|
+
class HTTPClient
|
|
19
|
+
# Default upper bound for a complete provider response (50 MiB).
|
|
20
|
+
DEFAULT_MAX_RESPONSE_BYTES = 50 * 1024 * 1024
|
|
21
|
+
DEFAULT_MAX_ERROR_BODY_BYTES = 4 * 1024 # :nodoc:
|
|
22
|
+
TRANSIENT_NETWORK_ERRORS = [
|
|
23
|
+
Net::OpenTimeout,
|
|
24
|
+
Net::ReadTimeout,
|
|
25
|
+
Net::WriteTimeout,
|
|
26
|
+
EOFError,
|
|
27
|
+
SocketError,
|
|
28
|
+
SystemCallError,
|
|
29
|
+
IOError,
|
|
30
|
+
OpenSSL::SSL::SSLError,
|
|
31
|
+
Net::ProtocolError,
|
|
32
|
+
Net::HTTPBadResponse,
|
|
33
|
+
Net::HTTPHeaderSyntaxError
|
|
34
|
+
].freeze # :nodoc:
|
|
35
|
+
|
|
36
|
+
# Configures +base_url+ with connection, read, and response
|
|
37
|
+
# size limits.
|
|
38
|
+
def initialize(
|
|
39
|
+
base_url: nil,
|
|
40
|
+
open_timeout: 10,
|
|
41
|
+
read_timeout: 120,
|
|
42
|
+
allow_insecure_http: false,
|
|
43
|
+
max_response_bytes: DEFAULT_MAX_RESPONSE_BYTES,
|
|
44
|
+
max_error_body_bytes: DEFAULT_MAX_ERROR_BODY_BYTES
|
|
45
|
+
)
|
|
46
|
+
if base_url
|
|
47
|
+
@base_url = URI(base_url.end_with?("/") ? base_url : "#{base_url}/")
|
|
48
|
+
validate_uri!(@base_url, allow_insecure_http:)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
@open_timeout = open_timeout
|
|
52
|
+
@read_timeout = read_timeout
|
|
53
|
+
@allow_insecure_http = allow_insecure_http
|
|
54
|
+
@max_response_bytes = positive_integer(max_response_bytes, :max_response_bytes)
|
|
55
|
+
@max_error_body_bytes = positive_integer(max_error_body_bytes, :max_error_body_bytes)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Posts +body+ and yields response chunks until completion.
|
|
59
|
+
#
|
|
60
|
+
# Cancellation and +deadline+ interrupt the request. Without a block, this
|
|
61
|
+
# method returns an Enumerator.
|
|
62
|
+
def stream(path:, headers:, body:, cancellation_token:, deadline: nil)
|
|
63
|
+
return enum_for(__method__, path:, headers:, body:, cancellation_token:, deadline:) unless block_given?
|
|
64
|
+
raise ConfigurationError, "HTTP client requires base_url for streaming" unless @base_url
|
|
65
|
+
|
|
66
|
+
stream = Support::InterruptibleStream.new(cancellation_token:, deadline:) do |emit|
|
|
67
|
+
uri = URI.join(@base_url.to_s, path.sub(%r{\A/}, ""))
|
|
68
|
+
each_chunk(
|
|
69
|
+
uri:,
|
|
70
|
+
method: :post,
|
|
71
|
+
headers:,
|
|
72
|
+
body:,
|
|
73
|
+
deadline:,
|
|
74
|
+
allow_insecure_http: @allow_insecure_http,
|
|
75
|
+
label: "Provider"
|
|
76
|
+
) { |chunk| emit.call(chunk) }
|
|
77
|
+
end
|
|
78
|
+
stream.each { |chunk| yield chunk }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Executes a bounded request and returns the complete response body.
|
|
82
|
+
def request(uri:, method: :get, headers: {}, body: nil, allow_insecure_http: false,
|
|
83
|
+
cancellation_token: nil, deadline: nil)
|
|
84
|
+
response_body = +""
|
|
85
|
+
each_chunk(
|
|
86
|
+
uri:,
|
|
87
|
+
method:,
|
|
88
|
+
headers:,
|
|
89
|
+
body:,
|
|
90
|
+
cancellation_token:,
|
|
91
|
+
deadline: deadline || Time.now + @read_timeout,
|
|
92
|
+
allow_insecure_http:
|
|
93
|
+
) { |chunk| response_body << chunk }
|
|
94
|
+
response_body
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Executes a bounded request and yields response chunks as they arrive.
|
|
98
|
+
def each_chunk(uri:, method: :get, headers: {}, body: nil, deadline: nil,
|
|
99
|
+
cancellation_token: nil, allow_insecure_http: false, label: "HTTP request")
|
|
100
|
+
unless block_given?
|
|
101
|
+
return enum_for(
|
|
102
|
+
__method__, uri:, method:, headers:, body:, deadline:, cancellation_token:, allow_insecure_http:, label:
|
|
103
|
+
)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
uri = URI(uri.to_s)
|
|
107
|
+
validate_uri!(uri, allow_insecure_http:)
|
|
108
|
+
check_control!(cancellation_token, deadline)
|
|
109
|
+
request_class = {get: Net::HTTP::Get, post: Net::HTTP::Post, put: Net::HTTP::Put}.fetch(method.to_sym) do
|
|
110
|
+
raise ArgumentError, "unsupported HTTP method: #{method}"
|
|
111
|
+
end
|
|
112
|
+
request = request_class.new(uri)
|
|
113
|
+
headers.each { |name, value| request[name] = value unless value.to_s.empty? }
|
|
114
|
+
request.body = body if body
|
|
115
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
116
|
+
http.use_ssl = uri.scheme == "https"
|
|
117
|
+
http.open_timeout = remaining_timeout(deadline, @open_timeout)
|
|
118
|
+
http.read_timeout = remaining_timeout(deadline, @read_timeout)
|
|
119
|
+
http.write_timeout = remaining_timeout(deadline, @read_timeout)
|
|
120
|
+
http.request(request) do |response|
|
|
121
|
+
unless response.is_a?(Net::HTTPSuccess)
|
|
122
|
+
response_body = read_limited(response, @max_error_body_bytes)
|
|
123
|
+
raise Providers::HTTPError.new(
|
|
124
|
+
"#{label} failed with HTTP #{response.code}",
|
|
125
|
+
status: response.code.to_i,
|
|
126
|
+
body: response_body
|
|
127
|
+
)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
bytes_read = 0
|
|
131
|
+
response.read_body do |chunk|
|
|
132
|
+
check_control!(cancellation_token, deadline)
|
|
133
|
+
bytes_read += chunk.bytesize
|
|
134
|
+
raise ProtocolError, "#{label} response exceeded #{@max_response_bytes} bytes" if bytes_read > @max_response_bytes
|
|
135
|
+
|
|
136
|
+
yield chunk
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
rescue *TRANSIENT_NETWORK_ERRORS => error
|
|
140
|
+
raise Providers::HTTPError, "#{label} failed (#{error.class})"
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
private
|
|
144
|
+
|
|
145
|
+
def check_control!(cancellation_token, deadline)
|
|
146
|
+
cancellation_token&.raise_if_cancelled!
|
|
147
|
+
raise DeadlineExceededError, "The request deadline was reached" if deadline && Time.now >= deadline
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def validate_uri!(uri, allow_insecure_http:)
|
|
151
|
+
unless %w[http https].include?(uri.scheme) && uri.host
|
|
152
|
+
raise ConfigurationError, "HTTP endpoint must be an HTTP(S) URL"
|
|
153
|
+
end
|
|
154
|
+
if uri.scheme == "http" && !allow_insecure_http
|
|
155
|
+
raise ConfigurationError, "HTTP endpoint must use HTTPS unless allow_insecure_http is enabled"
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def remaining_timeout(deadline, maximum)
|
|
160
|
+
return maximum unless deadline
|
|
161
|
+
|
|
162
|
+
remaining = deadline - Time.now
|
|
163
|
+
raise DeadlineExceededError, "The run deadline was reached" unless remaining.positive?
|
|
164
|
+
|
|
165
|
+
[remaining, maximum].min
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def read_limited(response, limit)
|
|
169
|
+
body = +""
|
|
170
|
+
response.read_body do |chunk|
|
|
171
|
+
remaining = limit - body.bytesize
|
|
172
|
+
body << chunk.byteslice(0, remaining) if remaining.positive?
|
|
173
|
+
break if body.bytesize >= limit
|
|
174
|
+
end
|
|
175
|
+
body
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def positive_integer(value, name)
|
|
179
|
+
integer = Integer(value)
|
|
180
|
+
raise ArgumentError, "#{name} must be positive" unless integer.positive?
|
|
181
|
+
|
|
182
|
+
integer
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
data/lib/little_ghost/tool.rb
CHANGED
|
@@ -306,7 +306,11 @@ module LittleGhost
|
|
|
306
306
|
# Subclasses must override this method. The current RunContext is available
|
|
307
307
|
# through +context+ while the call executes.
|
|
308
308
|
def call(_input)
|
|
309
|
-
raise
|
|
309
|
+
raise AbstractMethodError, "#{self.class} must implement #call"
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# Releases resources owned by this tool. Subclasses may override it.
|
|
313
|
+
def close
|
|
310
314
|
end
|
|
311
315
|
|
|
312
316
|
protected
|
|
@@ -12,7 +12,7 @@ module LittleGhost
|
|
|
12
12
|
# Entries may be Tool instances, Tool subclasses, nested arrays, or provider
|
|
13
13
|
# classes that implement <tt>tools(binding)</tt>. Names must be unique, contain only
|
|
14
14
|
# letters, numbers, underscores, or hyphens, and be at most 64 characters.
|
|
15
|
-
# Owned tools
|
|
15
|
+
# Owned tools are closed once in reverse order.
|
|
16
16
|
class ToolRegistry
|
|
17
17
|
MAX_NAME_LENGTH = 64 # :nodoc:
|
|
18
18
|
NAME_PATTERN = /\A[a-zA-Z0-9_-]+\z/ # :nodoc:
|
|
@@ -76,7 +76,7 @@ module LittleGhost
|
|
|
76
76
|
raise error
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
-
# Closes every owned tool
|
|
79
|
+
# Closes every owned tool.
|
|
80
80
|
def close
|
|
81
81
|
return if @closed
|
|
82
82
|
|
|
@@ -113,7 +113,6 @@ module LittleGhost
|
|
|
113
113
|
def close_instances(instances)
|
|
114
114
|
first_error = nil
|
|
115
115
|
instances.reverse_each do |instance|
|
|
116
|
-
next unless instance.respond_to?(:close)
|
|
117
116
|
next if @closed_tool_ids[instance.object_id]
|
|
118
117
|
|
|
119
118
|
@closed_tool_ids[instance.object_id] = true
|
data/lib/little_ghost/version.rb
CHANGED
|
@@ -246,7 +246,7 @@ module LittleGhost
|
|
|
246
246
|
# Implements the composition and returns its final unconsumed invocation.
|
|
247
247
|
# Subclasses must override this hook.
|
|
248
248
|
def perform
|
|
249
|
-
raise
|
|
249
|
+
raise AbstractMethodError, "#{self.class} must implement #perform"
|
|
250
250
|
end
|
|
251
251
|
|
|
252
252
|
# :doc:
|
data/lib/little_ghost.rb
CHANGED
|
@@ -18,15 +18,27 @@ require_relative "little_ghost/model_request"
|
|
|
18
18
|
require_relative "little_ghost/model_response"
|
|
19
19
|
require_relative "little_ghost/run_result"
|
|
20
20
|
require_relative "little_ghost/model_capabilities"
|
|
21
|
+
require_relative "little_ghost/models/target"
|
|
22
|
+
require_relative "little_ghost/models/details"
|
|
21
23
|
require_relative "little_ghost/model"
|
|
22
|
-
require_relative "little_ghost/
|
|
23
|
-
require_relative "little_ghost/
|
|
24
|
-
require_relative "little_ghost/providers/
|
|
24
|
+
require_relative "little_ghost/support/sse_parser"
|
|
25
|
+
require_relative "little_ghost/support/http_client"
|
|
26
|
+
require_relative "little_ghost/providers/base"
|
|
27
|
+
require_relative "little_ghost/providers/configuration"
|
|
28
|
+
require_relative "little_ghost/models/catalog/source"
|
|
29
|
+
require_relative "little_ghost/models/catalog/models_dev_source"
|
|
25
30
|
require_relative "little_ghost/providers/openai_compatible"
|
|
26
31
|
require_relative "little_ghost/providers/openai"
|
|
27
32
|
require_relative "little_ghost/providers/open_router"
|
|
28
33
|
require_relative "little_ghost/providers/bedrock"
|
|
29
|
-
require_relative "little_ghost/
|
|
34
|
+
require_relative "little_ghost/providers/anthropic"
|
|
35
|
+
require_relative "little_ghost/providers/gemini"
|
|
36
|
+
require_relative "little_ghost/providers/vertex_ai"
|
|
37
|
+
require_relative "little_ghost/models/catalog"
|
|
38
|
+
require_relative "little_ghost/models/catalog_snapshot"
|
|
39
|
+
require_relative "little_ghost/models/configuration"
|
|
40
|
+
require_relative "little_ghost/provider_registry"
|
|
41
|
+
require_relative "little_ghost/model_resolver"
|
|
30
42
|
require_relative "little_ghost/run_context"
|
|
31
43
|
require_relative "little_ghost/workspace"
|
|
32
44
|
require_relative "little_ghost/sandbox"
|
|
@@ -60,19 +72,14 @@ require_relative "little_ghost/runtime"
|
|
|
60
72
|
# A customer support agent can answer directly, use an application tool, or ask a
|
|
61
73
|
# specialist for research while the caller observes one run:
|
|
62
74
|
#
|
|
63
|
-
# LittleGhost.configure do |config|
|
|
64
|
-
# config.models CustomerSupportModels
|
|
65
|
-
# config.default_model :customer_support
|
|
66
|
-
# end
|
|
67
|
-
#
|
|
68
75
|
# class ResearchAgent < LittleGhost::Agent
|
|
69
76
|
# description "Researches difficult support questions"
|
|
70
|
-
# model "
|
|
77
|
+
# model "openai:gpt-5.6-luna"
|
|
71
78
|
# end
|
|
72
79
|
#
|
|
73
80
|
# class CustomerSupportAgent < LittleGhost::Agent
|
|
74
81
|
# description "Handles support requests"
|
|
75
|
-
# model :
|
|
82
|
+
# model "openai:gpt-5.6-luna"
|
|
76
83
|
# tools AccountTool
|
|
77
84
|
# subagent ResearchAgent, kind: "research"
|
|
78
85
|
# end
|
|
@@ -97,10 +104,6 @@ module LittleGhost
|
|
|
97
104
|
ExecutionState[:configuration] || (@configuration ||= Configuration.new)
|
|
98
105
|
end
|
|
99
106
|
|
|
100
|
-
# Alias for .configuration. Without an execution-scoped override, this is the
|
|
101
|
-
# process-wide default Configuration.
|
|
102
|
-
alias_method :default_configuration, :configuration
|
|
103
|
-
|
|
104
107
|
# Opens the active Configuration for application setup and returns it.
|
|
105
108
|
#
|
|
106
109
|
# Configuration files are loaded lazily when a runtime is first built, so
|
|
@@ -109,6 +112,9 @@ module LittleGhost
|
|
|
109
112
|
configuration.configure(&block)
|
|
110
113
|
end
|
|
111
114
|
|
|
115
|
+
# Returns the model resolver owned by the active process configuration.
|
|
116
|
+
def model_resolver = configuration.model_resolver
|
|
117
|
+
|
|
112
118
|
# Makes +configuration+ current only while the block runs.
|
|
113
119
|
#
|
|
114
120
|
# Execution state restores the previous configuration even when the block
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: little_ghost
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Matt Robinson
|
|
@@ -117,8 +117,8 @@ extra_rdoc_files: []
|
|
|
117
117
|
files:
|
|
118
118
|
- LICENSE.txt
|
|
119
119
|
- README.md
|
|
120
|
-
- docs/guides/
|
|
121
|
-
- docs/guides/
|
|
120
|
+
- docs/guides/core_concepts.md
|
|
121
|
+
- docs/guides/getting_started.md
|
|
122
122
|
- lib/little_ghost.rb
|
|
123
123
|
- lib/little_ghost/ag_ui.rb
|
|
124
124
|
- lib/little_ghost/ag_ui/adapter.rb
|
|
@@ -131,7 +131,7 @@ files:
|
|
|
131
131
|
- lib/little_ghost/agent_interruptions.rb
|
|
132
132
|
- lib/little_ghost/configuration.rb
|
|
133
133
|
- lib/little_ghost/content.rb
|
|
134
|
-
- lib/little_ghost/
|
|
134
|
+
- lib/little_ghost/data/model_catalog.json
|
|
135
135
|
- lib/little_ghost/errors.rb
|
|
136
136
|
- lib/little_ghost/events.rb
|
|
137
137
|
- lib/little_ghost/execution_state.rb
|
|
@@ -143,17 +143,36 @@ files:
|
|
|
143
143
|
- lib/little_ghost/message.rb
|
|
144
144
|
- lib/little_ghost/model.rb
|
|
145
145
|
- lib/little_ghost/model_capabilities.rb
|
|
146
|
-
- lib/little_ghost/model_registry.rb
|
|
147
146
|
- lib/little_ghost/model_request.rb
|
|
147
|
+
- lib/little_ghost/model_resolver.rb
|
|
148
148
|
- lib/little_ghost/model_response.rb
|
|
149
|
+
- lib/little_ghost/models/catalog.rb
|
|
150
|
+
- lib/little_ghost/models/catalog/models_dev_source.rb
|
|
151
|
+
- lib/little_ghost/models/catalog/source.rb
|
|
152
|
+
- lib/little_ghost/models/catalog_snapshot.rb
|
|
153
|
+
- lib/little_ghost/models/configuration.rb
|
|
154
|
+
- lib/little_ghost/models/details.rb
|
|
155
|
+
- lib/little_ghost/models/target.rb
|
|
149
156
|
- lib/little_ghost/path_set.rb
|
|
150
157
|
- lib/little_ghost/prompt_resolver.rb
|
|
158
|
+
- lib/little_ghost/provider_registry.rb
|
|
159
|
+
- lib/little_ghost/providers/anthropic.rb
|
|
160
|
+
- lib/little_ghost/providers/anthropic/catalog_source.rb
|
|
161
|
+
- lib/little_ghost/providers/base.rb
|
|
151
162
|
- lib/little_ghost/providers/bedrock.rb
|
|
152
|
-
- lib/little_ghost/providers/
|
|
163
|
+
- lib/little_ghost/providers/bedrock/aws_protocol.rb
|
|
164
|
+
- lib/little_ghost/providers/bedrock/catalog_source.rb
|
|
165
|
+
- lib/little_ghost/providers/bedrock/credential_resolver.rb
|
|
166
|
+
- lib/little_ghost/providers/bedrock/http_client.rb
|
|
167
|
+
- lib/little_ghost/providers/configuration.rb
|
|
168
|
+
- lib/little_ghost/providers/gemini.rb
|
|
169
|
+
- lib/little_ghost/providers/gemini/catalog_source.rb
|
|
153
170
|
- lib/little_ghost/providers/open_router.rb
|
|
171
|
+
- lib/little_ghost/providers/open_router/catalog_source.rb
|
|
154
172
|
- lib/little_ghost/providers/openai.rb
|
|
155
173
|
- lib/little_ghost/providers/openai_compatible.rb
|
|
156
|
-
- lib/little_ghost/providers/
|
|
174
|
+
- lib/little_ghost/providers/vertex_ai.rb
|
|
175
|
+
- lib/little_ghost/providers/vertex_ai/credential_resolver.rb
|
|
157
176
|
- lib/little_ghost/run.rb
|
|
158
177
|
- lib/little_ghost/run_context.rb
|
|
159
178
|
- lib/little_ghost/run_result.rb
|
|
@@ -178,10 +197,12 @@ files:
|
|
|
178
197
|
- lib/little_ghost/support/class_attributes.rb
|
|
179
198
|
- lib/little_ghost/support/content_capture.rb
|
|
180
199
|
- lib/little_ghost/support/executor.rb
|
|
200
|
+
- lib/little_ghost/support/http_client.rb
|
|
181
201
|
- lib/little_ghost/support/interruptible_stream.rb
|
|
182
202
|
- lib/little_ghost/support/loader.rb
|
|
183
203
|
- lib/little_ghost/support/output_truncation.rb
|
|
184
204
|
- lib/little_ghost/support/redactor.rb
|
|
205
|
+
- lib/little_ghost/support/sse_parser.rb
|
|
185
206
|
- lib/little_ghost/tool.rb
|
|
186
207
|
- lib/little_ghost/tool_execution.rb
|
|
187
208
|
- lib/little_ghost/tool_registry.rb
|