Class: LittleGhost::Configuration
- Inherits:
-
Object
- Object
- LittleGhost::Configuration
- Defined in:
- lib/little_ghost/configuration.rb
Overview
Configure shared services and lookup rules before agents start. A configuration collects model profiles, persistence, paths, instrumentation, and runtime hooks for an application.
LittleGhost.configure do |config|
config.default_model :customer_support
config.service_name "support-api"
end
LittleGhost.configuration.default_model # => "customer_support"
LittleGhost.configuration.service_name # => "support-api"
Prompt and skill lookup paths default to app/prompts and app/skills
under the application root. Applications may append shared roots or replace
the arrays entirely.
Configuration is a mutable application builder until its shared Runtime is first used. A successful #runtime call locks the builder so standalone Agents and Assemblies keep one stable setup. Configure the application before its first entrypoint call. Explicit Runtime construction remains an advanced way to take an independent snapshot without selecting the shared default.
Session actor resolvers belong at an authentication boundary. Multi-tenant applications should derive actor identity from trusted authenticated state, not from an unverified request field.
Constant Summary collapse
- FILE_LOAD_MUTEX =
:nodoc:
Mutex.new
- CONFIGURATION_KEYS =
:nodoc:
%i[invocation service_name].freeze
- DEFAULT_PROMPT_PATHS =
:nodoc:
["app/prompts"].freeze
- DEFAULT_SKILL_PATHS =
:nodoc:
["app/skills"].freeze
Instance Method Summary collapse
-
#[](name) ⇒ Object
Looks up an arbitrary setting by symbol or string-compatible name.
-
#[]=(name, value) ⇒ Object
Adds or replaces an arbitrary setting.
-
#catalog_source(source) ⇒ Object
Adds an explicit catalog source.
-
#configure ⇒ Object
Yields this builder for setup and returns the same instance.
-
#default_model(value = :__read__) ⇒ Object
Fallback logical role for the default resolver.
-
#default_model=(value) ⇒ Object
Replaces the fallback logical role and normalizes it to a String.
-
#initialize(values = {}) ⇒ Configuration
constructor
Starts a mutable builder with optional
values. -
#instrument(subscriber) ⇒ Object
Adds an Instrumentation::Subscriber to each new runtime and returns it.
-
#load_file!(root: nil) ⇒ Object
:nodoc:.
-
#log_events_to(destination = :__read__) ⇒ Object
:call-seq: log_events_to() -> :stdout, :stderr, nil log_events_to(destination) -> destination.
-
#log_events_to=(destination) ⇒ Object
Replaces the console destination for structured framework events.
-
#model_resolver(value = :__read__) ⇒ Object
Installs a complete resolver override for subsequently built runtimes.
- #model_resolver=(value) ⇒ Object
-
#models(value = :__read__) ⇒ Object
Logical model profiles for the default resolver.
-
#models=(value) ⇒ Object
Replaces logical model profiles for subsequently built runtimes.
-
#models_path(value = :__read__) ⇒ Object
Model YAML path.
-
#models_path=(value) ⇒ Object
Replaces the model YAML path for subsequently built runtimes.
-
#name ⇒ Object
Replaces the service name attached to telemetry from new runtimes.
-
#prompt_paths ⇒ Object
Prompt lookup paths in precedence order.
-
#prompt_paths=(value) ⇒ Object
Replaces prompt lookup paths with
valueconverted to an Array. -
#provider_adapter(name, callable = nil, &factory) ⇒ Object
Registers a provider adapter factory under
name. -
#provider_credentials(callable = nil, &resolver) ⇒ Object
Installs a trusted callable that returns credential options for a named provider connection when each executable model is constructed.
-
#providers(value = :__read__) ⇒ Object
Trusted provider connections for the default or custom resolver.
-
#providers=(value) ⇒ Object
Replaces trusted provider connections for subsequently built runtimes.
-
#providers_path(value = :__read__) ⇒ Object
Provider YAML path.
-
#providers_path=(value) ⇒ Object
Replaces the provider YAML path for subsequently built runtimes.
-
#root(value = :__read__) ⇒ Object
:call-seq: root() -> Pathname root(path) -> Pathname.
-
#root=(value) ⇒ Object
Replaces the application root after resolving it to a stable real path.
-
#runtime ⇒ Object
Returns the shared Runtime for this configuration, building it on first use.
-
#runtime_hook(hook_class) ⇒ Object
Adds a Runtime::Hook subclass to each new runtime and returns it.
-
#sandbox ⇒ Object
Sandbox declaration used for subsequently built runtimes.
-
#sandbox=(value) ⇒ Object
Selects the Sandbox subclass instantiated around each run's workspace.
-
#session_actor(value = :__read__, &resolver) ⇒ Object
:call-seq: session_actor() -> callable, nil session_actor(callable) -> callable session_actor { |invocation| ... } -> callable.
-
#session_store ⇒ Object
Session-store declaration used for subsequently built runtimes.
-
#session_store=(value) ⇒ Object
Selects session persistence with a
:providerand its constructor options. -
#settings(root: nil) ⇒ Object
:nodoc:.
-
#skill_paths ⇒ Object
Skill lookup paths in precedence order.
-
#skill_paths=(value) ⇒ Object
Replaces skill lookup paths with
valueconverted to an Array. -
#skill_resource_root ⇒ Object
Optional trusted root exposed to skills for resource lookup.
-
#skill_resource_root=(value) ⇒ Object
Replaces the trusted skill resource root for new runtimes.
-
#workspace ⇒ Object
Workspace declaration used for subsequently built runtimes.
-
#workspace=(value) ⇒ Object
Selects the Workspace subclass instantiated for each run.
Constructor Details
#initialize(values = {}) ⇒ Configuration
Starts a mutable builder with optional values.
Prompt paths default to app/prompts and skill paths to app/skills.
Collection settings are copied so callers can safely reuse their input
arrays after construction.
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/little_ghost/configuration.rb', line 89 def initialize(values = {}) @lifecycle_monitor = Monitor.new @runtime_condition = @lifecycle_monitor.new_cond @configuration_values = { prompt_paths: DEFAULT_PROMPT_PATHS.dup, skill_paths: DEFAULT_SKILL_PATHS.dup, skill_resource_root: nil, workspace: nil, sandbox: nil, instrumentation_subscribers: [], runtime_hooks: [] }.merge(values) @configuration_values[:prompt_paths] = Array(@configuration_values[:prompt_paths]).dup @configuration_values[:skill_paths] = Array(@configuration_values[:skill_paths]).dup @configuration_values[:instrumentation_subscribers] = Array( @configuration_values[:instrumentation_subscribers] ).dup @configuration_values[:runtime_hooks] = Array(@configuration_values[:runtime_hooks]).dup @configuration_values[:provider_adapters] = @configuration_values.fetch(:provider_adapters, {}).dup @configuration_values[:catalog_sources] = Array(@configuration_values[:catalog_sources]).dup @configuration_values[:provider_credentials] ||= nil end |
Instance Method Details
#[](name) ⇒ Object
Looks up an arbitrary setting by symbol or string-compatible name.
388 389 390 |
# File 'lib/little_ghost/configuration.rb', line 388 def [](name) configuration_values.fetch(name.to_sym) end |
#[]=(name, value) ⇒ Object
Adds or replaces an arbitrary setting.
393 394 395 |
# File 'lib/little_ghost/configuration.rb', line 393 def []=(name, value) change_configuration { configuration_values[name.to_sym] = value } end |
#catalog_source(source) ⇒ Object
Adds an explicit catalog source. Sources refresh only when callers invoke ModelResolver#refresh!.
329 330 331 332 333 334 335 336 337 338 |
# File 'lib/little_ghost/configuration.rb', line 329 def catalog_source(source) ensure_configuration_open! raise ArgumentError, "catalog source must be a Models::Catalog::Source" unless source.is_a?(Models::Catalog::Source) change_configuration do configuration_values[:catalog_sources] << source @resolved_model_resolver = nil end source end |
#configure ⇒ Object
Yields this builder for setup and returns the same instance.
113 114 115 116 |
# File 'lib/little_ghost/configuration.rb', line 113 def configure change_configuration { yield self } if block_given? self end |
#default_model(value = :__read__) ⇒ Object
Fallback logical role for the default resolver.
229 230 231 232 233 234 235 236 237 |
# File 'lib/little_ghost/configuration.rb', line 229 def default_model(value = :__read__) return configuration_values[:default_model] if value == :__read__ change_configuration do configuration_values[:default_model] = value.to_s reset_model_resolver end value.to_s end |
#default_model=(value) ⇒ Object
Replaces the fallback logical role and normalizes it to a String.
240 241 242 |
# File 'lib/little_ghost/configuration.rb', line 240 def default_model=(value) default_model(value) end |
#instrument(subscriber) ⇒ Object
Adds an Instrumentation::Subscriber to each new runtime and returns it.
403 404 405 406 407 408 409 410 411 |
# File 'lib/little_ghost/configuration.rb', line 403 def instrument(subscriber) ensure_configuration_open! unless subscriber.is_a?(Instrumentation::Subscriber) raise ArgumentError, "instrumentation subscriber must be a LittleGhost::Instrumentation::Subscriber" end change_configuration { configuration_values[:instrumentation_subscribers] << subscriber } subscriber end |
#load_file!(root: nil) ⇒ Object
:nodoc:
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 |
# File 'lib/little_ghost/configuration.rb', line 522 def load_file!(root: nil) # :nodoc: requested_root = canonical_root(root || self.root) FILE_LOAD_MUTEX.synchronize do (@configuration_file_mutex ||= Mutex.new).synchronize do if @configuration_file_root return self if @configuration_file_root == requested_root raise ConfigurationError, "configuration file is already loaded for #{@configuration_file_root}" end path = File.join(requested_root, "config/little_ghost.rb") load_configuration_file(path) if File.file?(path) @configuration_file_root = requested_root end end self end |
#log_events_to(destination = :__read__) ⇒ Object
:call-seq:
log_events_to() -> :stdout, :stderr, nil
log_events_to(destination) -> destination
Sends structured framework events to :stdout or :stderr. This setting
controls the process-wide Events console destination; the most recent
setting replaces it without changing other event listeners. By default,
events have no console destination. Passing nil disables console output.
The console listener redacts sensitive values and writes one JSON object
per line.
423 424 425 426 427 |
# File 'lib/little_ghost/configuration.rb', line 423 def log_events_to(destination = :__read__) return Events.console_output if destination == :__read__ change_configuration { Events.console_output = destination } end |
#log_events_to=(destination) ⇒ Object
Replaces the console destination for structured framework events.
430 431 432 |
# File 'lib/little_ghost/configuration.rb', line 430 def log_events_to=(destination) log_events_to(destination) end |
#model_resolver(value = :__read__) ⇒ Object
Installs a complete resolver override for subsequently built runtimes.
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
# File 'lib/little_ghost/configuration.rb', line 263 def model_resolver(value = :__read__) if value != :__read__ ensure_configuration_open! validate_model_resolver_class!(value) change_configuration do configuration_values[:model_resolver] = value @resolved_model_resolver = nil end return value end @model_resolver_mutex ||= Mutex.new @model_resolver_mutex.synchronize do @resolved_model_resolver ||= begin providers = resolved_providers credential_resolver = configuration_values[:provider_credentials] || providers&.method(:credentials) configured = configuration_values[:model_resolver] if configured validate_model_resolver_configuration! configured.new( providers:, provider_adapters: configuration_values[:provider_adapters], catalog_sources: configuration_values[:catalog_sources], credential_resolver: ) else profiles, file_default = resolved_models ModelResolver.new( providers:, profiles:, default_model: configuration_values.fetch(:default_model, file_default), provider_adapters: configuration_values[:provider_adapters], catalog_sources: configuration_values[:catalog_sources], credential_resolver: ) end end end end |
#model_resolver=(value) ⇒ Object
304 305 306 |
# File 'lib/little_ghost/configuration.rb', line 304 def model_resolver=(value) model_resolver(value) end |
#models(value = :__read__) ⇒ Object
Logical model profiles for the default resolver. Role names cannot contain a colon because that syntax identifies a canonical model target.
211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/little_ghost/configuration.rb', line 211 def models(value = :__read__) return configuration_values[:models] if value == :__read__ ensure_configuration_open! raise ArgumentError, "models must be a Hash" unless value.is_a?(Hash) change_configuration do configuration_values[:models] = value reset_model_resolver end value end |
#models=(value) ⇒ Object
Replaces logical model profiles for subsequently built runtimes.
224 225 226 |
# File 'lib/little_ghost/configuration.rb', line 224 def models=(value) models(value) end |
#models_path(value = :__read__) ⇒ Object
Model YAML path. The conventional path is optional; an explicitly set path must exist when a runtime is built.
255 |
# File 'lib/little_ghost/configuration.rb', line 255 def models_path(value = :__read__) = configuration_path(:models_path, "models.yml", value) |
#models_path=(value) ⇒ Object
Replaces the model YAML path for subsequently built runtimes.
258 259 260 |
# File 'lib/little_ghost/configuration.rb', line 258 def models_path=(value) models_path(value) end |
#name ⇒ Object
Replaces the service name attached to telemetry from new runtimes. :method: service_name= :call-seq:
service_name=(value) -> value
54 55 56 57 58 59 60 |
# File 'lib/little_ghost/configuration.rb', line 54 CONFIGURATION_KEYS.each do |name| define_method(name) do |value = :__read__| return configuration_values[name] if value == :__read__ change_configuration { configuration_values[name] = value } end end |
#prompt_paths ⇒ Object
Prompt lookup paths in precedence order. The Array is mutable until the shared Runtime is built.
485 |
# File 'lib/little_ghost/configuration.rb', line 485 def prompt_paths = configuration_values[:prompt_paths] |
#prompt_paths=(value) ⇒ Object
Replaces prompt lookup paths with value converted to an Array.
488 489 490 |
# File 'lib/little_ghost/configuration.rb', line 488 def prompt_paths=(value) change_configuration { configuration_values[:prompt_paths] = Array(value) } end |
#provider_adapter(name, callable = nil, &factory) ⇒ Object
Registers a provider adapter factory under name.
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/little_ghost/configuration.rb', line 309 def provider_adapter(name, callable = nil, &factory) ensure_configuration_open! implementation = factory || callable if implementation.is_a?(Class) unless implementation <= Providers::Base raise ArgumentError, "provider adapter class must inherit LittleGhost::Providers::Base" end elsif !implementation.respond_to?(:call) raise ArgumentError, "provider adapter must be a Providers::Base class or callable factory" end change_configuration do configuration_values[:provider_adapters][name.to_s] = implementation @resolved_model_resolver = nil end implementation end |
#provider_credentials(callable = nil, &resolver) ⇒ Object
Installs a trusted callable that returns credential options for a named provider connection when each executable model is constructed.
342 343 344 345 346 347 348 349 350 351 352 353 |
# File 'lib/little_ghost/configuration.rb', line 342 def provider_credentials(callable = nil, &resolver) value = resolver || callable return configuration_values[:provider_credentials] unless value ensure_configuration_open! raise ArgumentError, "provider credential resolver must be callable" unless value.respond_to?(:call) change_configuration do configuration_values[:provider_credentials] = value @resolved_model_resolver = nil end value end |
#providers(value = :__read__) ⇒ Object
Trusted provider connections for the default or custom resolver.
189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/little_ghost/configuration.rb', line 189 def providers(value = :__read__) return configuration_values[:providers] if value == :__read__ ensure_configuration_open! unless value.is_a?(Hash) || value.is_a?(Providers::Configuration) raise ArgumentError, "providers must be a Hash or LittleGhost::Providers::Configuration" end change_configuration do configuration_values[:providers] = value reset_model_resolver end value end |
#providers=(value) ⇒ Object
Replaces trusted provider connections for subsequently built runtimes.
205 206 207 |
# File 'lib/little_ghost/configuration.rb', line 205 def providers=(value) providers(value) end |
#providers_path(value = :__read__) ⇒ Object
Provider YAML path. The conventional path is optional; an explicitly set path must exist when a runtime is built.
246 |
# File 'lib/little_ghost/configuration.rb', line 246 def providers_path(value = :__read__) = configuration_path(:providers_path, "providers.yml", value) |
#providers_path=(value) ⇒ Object
Replaces the provider YAML path for subsequently built runtimes.
249 250 251 |
# File 'lib/little_ghost/configuration.rb', line 249 def providers_path=(value) providers_path(value) end |
#root(value = :__read__) ⇒ Object
:call-seq:
root() -> Pathname
root(path) -> Pathname
The resolved application root, defaulting to Dir.pwd.
Setting or reading an invalid root raises ConfigurationError. Symlinks are resolved so runtimes and lookup paths share one stable boundary.
474 475 476 477 478 479 480 481 |
# File 'lib/little_ghost/configuration.rb', line 474 def root(value = :__read__) if value != :__read__ return change_configuration { configuration_values[:root] = canonical_root(value) } end configured = configuration_values[:root] configured ? canonical_root(configured) : inferred_root end |
#root=(value) ⇒ Object
Replaces the application root after resolving it to a stable real path.
398 399 400 |
# File 'lib/little_ghost/configuration.rb', line 398 def root=(value) root(value) end |
#runtime ⇒ Object
Returns the shared Runtime for this configuration, building it on first use. Once construction succeeds, the configuration is locked so every standalone entrypoint continues to use one stable application setup. The conventional configuration file may finish loading during construction; other writes are rejected. A failed build leaves the configuration editable for a later attempt.
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/little_ghost/configuration.rb', line 124 def runtime build_generation = @lifecycle_monitor.synchronize do loop do return @default_runtime if @default_runtime if @runtime_building if @runtime_builder_thread.equal?(Thread.current) raise ConfigurationError, "LittleGhost.runtime cannot be called while the shared Runtime is starting" end waiting_generation = @runtime_generation @runtime_condition.wait return @default_runtime if @default_runtime if @runtime_failure_generation == waiting_generation raise @runtime_failure end else sealed_values = freeze_configuration_copy(@configuration_values) @runtime_generation = @runtime_generation.to_i + 1 @runtime_building = true @runtime_builder_thread = Thread.current @configuration_values = sealed_values break @runtime_generation end end end begin runtime_root = root load_file!(root: runtime_root) runtime_settings = settings(root: runtime_root) built = Runtime.new(configuration: self, settings: runtime_settings) @lifecycle_monitor.synchronize do configuration_values.freeze @default_runtime = built end built rescue => error editable_values = if @configuration_values.frozen? copy_configuration_value(@configuration_values) else @configuration_values end @lifecycle_monitor.synchronize do @configuration_values = editable_values @runtime_failure = error @runtime_failure_generation = build_generation end raise ensure @lifecycle_monitor.synchronize do @runtime_building = false @runtime_builder_thread = nil @runtime_condition.broadcast end end end |
#runtime_hook(hook_class) ⇒ Object
Adds a Runtime::Hook subclass to each new runtime and returns it.
435 436 437 438 439 440 441 442 443 |
# File 'lib/little_ghost/configuration.rb', line 435 def runtime_hook(hook_class) ensure_configuration_open! unless hook_class.is_a?(Class) && hook_class <= Runtime::Hook raise ArgumentError, "runtime_hook must be a LittleGhost::Runtime::Hook class" end change_configuration { configuration_values[:runtime_hooks] << hook_class } hook_class end |
#sandbox ⇒ Object
Sandbox declaration used for subsequently built runtimes.
184 185 |
# File 'lib/little_ghost/configuration.rb', line 184 def sandbox = configuration_values[:sandbox] # Session-store declaration used for subsequently built runtimes. |
#sandbox=(value) ⇒ Object
Selects the Sandbox subclass instantiated around each run's workspace.
363 364 365 366 367 |
# File 'lib/little_ghost/configuration.rb', line 363 def sandbox=(value) change_configuration do @configuration_values[:sandbox] = component_class(value, Sandbox, :sandbox) end end |
#session_actor(value = :__read__, &resolver) ⇒ Object
:call-seq:
session_actor() -> callable, nil
session_actor(callable) -> callable
session_actor { |invocation| ... } -> callable
The callable that derives the persistence actor for each invocation.
Pass either a callable or a block. The configured resolver should use trusted authenticated identity in multi-tenant applications.
454 455 456 457 458 459 460 461 462 463 464 |
# File 'lib/little_ghost/configuration.rb', line 454 def session_actor(value = :__read__, &resolver) return configuration_values[:session_actor] if value == :__read__ && !resolver ensure_configuration_open! raise ArgumentError, "Provide a session actor resolver or a block, not both" if value != :__read__ && resolver configured = resolver || value raise ArgumentError, "session_actor must be callable" unless configured.respond_to?(:call) change_configuration { configuration_values[:session_actor] = configured } end |
#session_store ⇒ Object
Session-store declaration used for subsequently built runtimes.
186 |
# File 'lib/little_ghost/configuration.rb', line 186 def session_store = configuration_values[:session_store] |
#session_store=(value) ⇒ Object
Selects session persistence with a :provider and its constructor options.
The provider must be a SessionStore subclass. Runtime construction creates and owns the store instance.
373 374 375 376 377 378 379 380 381 382 383 384 385 |
# File 'lib/little_ghost/configuration.rb', line 373 def session_store=(value) ensure_configuration_open! unless value.is_a?(Hash) raise ArgumentError, "session_store must be a hash with a provider" end provider = value[:provider] change_configuration do @configuration_values[:session_store] = value.merge( provider: component_class(provider, SessionStore, :session_store) ) end end |
#settings(root: nil) ⇒ Object
:nodoc:
509 510 511 512 513 514 515 516 517 518 519 520 |
# File 'lib/little_ghost/configuration.rb', line 509 def settings(root: nil) # :nodoc: requested_root = root && canonical_root(root) values = configuration_values.dup values[:prompt_paths] = Array(values[:prompt_paths]).dup values[:skill_paths] = Array(values[:skill_paths]).dup values[:instrumentation_subscribers] = Array(values[:instrumentation_subscribers]).dup values[:runtime_hooks] = Array(values[:runtime_hooks]).dup values[:model_resolver] = model_resolver values[:default_model] = values[:model_resolver].default_model values[:root] = requested_root || values[:root] || self.root values end |
#skill_paths ⇒ Object
Skill lookup paths in precedence order. The Array is mutable until the shared Runtime is built.
494 |
# File 'lib/little_ghost/configuration.rb', line 494 def skill_paths = configuration_values[:skill_paths] |
#skill_paths=(value) ⇒ Object
Replaces skill lookup paths with value converted to an Array.
497 498 499 |
# File 'lib/little_ghost/configuration.rb', line 497 def skill_paths=(value) change_configuration { configuration_values[:skill_paths] = Array(value) } end |
#skill_resource_root ⇒ Object
Optional trusted root exposed to skills for resource lookup.
502 |
# File 'lib/little_ghost/configuration.rb', line 502 def skill_resource_root = configuration_values[:skill_resource_root] |
#skill_resource_root=(value) ⇒ Object
Replaces the trusted skill resource root for new runtimes.
505 506 507 |
# File 'lib/little_ghost/configuration.rb', line 505 def skill_resource_root=(value) change_configuration { configuration_values[:skill_resource_root] = value } end |
#workspace ⇒ Object
Workspace declaration used for subsequently built runtimes.
182 183 |
# File 'lib/little_ghost/configuration.rb', line 182 def workspace = configuration_values[:workspace] # Sandbox declaration used for subsequently built runtimes. |
#workspace=(value) ⇒ Object
Selects the Workspace subclass instantiated for each run.
356 357 358 359 360 |
# File 'lib/little_ghost/configuration.rb', line 356 def workspace=(value) change_configuration do @configuration_values[:workspace] = component_class(value, Workspace, :workspace) end end |