Class: LittleGhost::Runtime
- Inherits:
-
Object
- Object
- LittleGhost::Runtime
- Defined in:
- lib/little_ghost/runtime.rb,
lib/little_ghost/runtime/hook.rb
Overview
Prepare the shared services that agents and workflows use across many runs. A runtime owns model resolution, loading, persistence, lookup paths, hooks, and resource factories for one Ruby setup.
configuration = LittleGhost::Configuration.new(
root: Dir.pwd,
models: CustomerSupportModels,
default_model: "customer_support",
service_name: "support-api"
)
runtime = LittleGhost::Runtime.new(configuration: configuration)
runtime.service_name # => "support-api"
runtime.root == Pathname.new(File.realpath(Dir.pwd)) # => true
Without explicit settings, construction canonicalizes the root, loads
config/little_ghost.rb once through the Configuration, snapshots settings,
configures instrumentation, eager-loads application constants, and builds the
selected model registry and session store. Supplying settings is the
lower-level path used to create a sibling runtime from an existing snapshot.
Reuse a runtime across runs. build_run creates any missing workspace and
sandbox, transfers ownership only after both are built, and closes partial
resources if construction fails. build creates a sibling with explicit
overrides and reuses the loader only when the application root is unchanged.
Startup emits structured lifecycle instrumentation; a failed phase emits a failure event, flushes instrumentation, and re-raises the original exception. Session actor resolution must use trusted authenticated identity for tenant isolation. The default UnrestrictedSandbox is convenient application plumbing, not a security boundary for untrusted work.
Defined Under Namespace
Classes: Hook
Instance Attribute Summary collapse
-
#configuration ⇒ Object
readonly
The snapshotted setup and materialized services used by new runs.
-
#loader ⇒ Object
readonly
The snapshotted setup and materialized services used by new runs.
-
#models ⇒ Object
readonly
The snapshotted setup and materialized services used by new runs.
-
#prompt_paths ⇒ Object
readonly
The snapshotted setup and materialized services used by new runs.
-
#root ⇒ Object
readonly
The snapshotted setup and materialized services used by new runs.
-
#runtime_hooks ⇒ Object
readonly
The snapshotted setup and materialized services used by new runs.
-
#sandbox_class ⇒ Object
readonly
The snapshotted setup and materialized services used by new runs.
-
#session_store ⇒ Object
readonly
The snapshotted setup and materialized services used by new runs.
-
#settings ⇒ Object
readonly
The snapshotted setup and materialized services used by new runs.
-
#skill_paths ⇒ Object
readonly
The snapshotted setup and materialized services used by new runs.
-
#skill_resource_root ⇒ Object
readonly
The snapshotted setup and materialized services used by new runs.
-
#workspace_class ⇒ Object
readonly
The snapshotted setup and materialized services used by new runs.
Instance Method Summary collapse
-
#build(**overrides) ⇒ Object
Creates a sibling runtime with explicit setting overrides.
-
#build_agent(agent_class_or_name, run:, model: nil, tools: [], agent_path: Subagents::AgentPath::ROOT) ⇒ Object
:nodoc:.
-
#build_run(payload, agent_class:, entrypoint_class: agent_class, workspace: nil, sandbox: nil) ⇒ Object
Creates a Run and transfers ownership of newly created workspace and sandbox resources to it.
-
#build_sandbox(workspace:) ⇒ Object
Instantiates the configured sandbox around
workspace, or an unrestricted sandbox by default. -
#build_workspace ⇒ Object
Instantiates the configured workspace, or a root-scoped Workspace by default.
-
#default_error_message(error, _run) ⇒ Object
:nodoc:.
-
#error_message(error, run) ⇒ Object
:nodoc:.
-
#initialize(configuration:, settings: nil) ⇒ Runtime
constructor
Starts a runtime from
configurationor an existing settings snapshot. -
#model_for(agent_class, run) ⇒ Object
:nodoc:.
-
#open_session(run) ⇒ Object
:nodoc:.
-
#open_subagent_session(run, conversation_id) ⇒ Object
:nodoc:.
-
#parse(payload) ⇒ Object
Coerces an application payload into the configured Invocation class.
-
#prepare_interruption(run, payload) ⇒ Object
:nodoc:.
-
#prepare_run(run) ⇒ Object
:nodoc:.
-
#resolve_agent(value) ⇒ Object
:nodoc:.
-
#service_name ⇒ Object
The low-cardinality service name attached to runtime telemetry.
-
#session_actor_for(invocation) ⇒ Object
:nodoc:.
-
#template_locals(run:, agent:) ⇒ Object
:nodoc:.
Constructor Details
#initialize(configuration:, settings: nil) ⇒ Runtime
Starts a runtime from configuration or an existing settings snapshot.
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/little_ghost/runtime.rb', line 45 def initialize(configuration:, settings: nil) @startup_started_at = monotonic_time @startup_phase = "configuration" @startup_reported = false begin raise ArgumentError, "configuration must be a LittleGhost::Configuration" unless configuration.is_a?(Configuration) @configuration = configuration if settings @settings = settings else bootstrap_root = canonical_application_root(configuration.root) configuration.load_file!(root: bootstrap_root) @settings = configuration.settings(root: bootstrap_root) end report_startup(status: "starting") @startup_reported = true @root = canonical_application_root(@settings.fetch(:root)) @skill_resource_root = @settings[:skill_resource_root] @workspace_class = @settings.fetch(:workspace) @sandbox_class = @settings.fetch(:sandbox) @runtime_hooks = build_runtime_hooks(@settings[:runtime_hooks]) @startup_phase = "instrumentation" subscribe_instrumentation(@settings[:instrumentation_subscribers]) emit_startup(:runtime_start) @startup_phase = "loader" @loader = @settings[:loader] || Support::Loader.new(root: @root) loader.setup loader.eager_load @startup_phase = "models" @invocation_class = @settings[:invocation] || Invocation @models = build_service(@settings[:models], default: -> { DefaultModelRegistry.new }) @default_model = @settings.fetch(:default_model, "default").to_s @startup_phase = "session_store" @session_store = build_session_store(@settings[:session_store]) @session_actor = @settings[:session_actor] @startup_phase = "prompts" @prompt_paths = build_lookup_paths(:prompt_paths) @skill_paths = build_lookup_paths(:skill_paths) @startup_phase = "agent_builder" @agent_builder = AgentBuilder.new( runtime: self, prompt_paths: @prompt_paths, resolve_agent: method(:resolve_agent_class) ) @startup_phase = "complete" emit_startup(:runtime_stop, outcome: "ready") report_startup(status: "ready") rescue => error unless @startup_reported report_startup(status: "starting") @startup_reported = true end emit_startup(:runtime_stop, outcome: "failed", error:) Instrumentation.flush report_startup(status: "failed", error:) raise end end |
Instance Attribute Details
#configuration ⇒ Object (readonly)
The snapshotted setup and materialized services used by new runs.
40 41 42 |
# File 'lib/little_ghost/runtime.rb', line 40 def configuration @configuration end |
#loader ⇒ Object (readonly)
The snapshotted setup and materialized services used by new runs.
40 41 42 |
# File 'lib/little_ghost/runtime.rb', line 40 def loader @loader end |
#models ⇒ Object (readonly)
The snapshotted setup and materialized services used by new runs.
40 41 42 |
# File 'lib/little_ghost/runtime.rb', line 40 def models @models end |
#prompt_paths ⇒ Object (readonly)
The snapshotted setup and materialized services used by new runs.
40 41 42 |
# File 'lib/little_ghost/runtime.rb', line 40 def prompt_paths @prompt_paths end |
#root ⇒ Object (readonly)
The snapshotted setup and materialized services used by new runs.
40 41 42 |
# File 'lib/little_ghost/runtime.rb', line 40 def root @root end |
#runtime_hooks ⇒ Object (readonly)
The snapshotted setup and materialized services used by new runs.
40 41 42 |
# File 'lib/little_ghost/runtime.rb', line 40 def runtime_hooks @runtime_hooks end |
#sandbox_class ⇒ Object (readonly)
The snapshotted setup and materialized services used by new runs.
40 41 42 |
# File 'lib/little_ghost/runtime.rb', line 40 def sandbox_class @sandbox_class end |
#session_store ⇒ Object (readonly)
The snapshotted setup and materialized services used by new runs.
40 41 42 |
# File 'lib/little_ghost/runtime.rb', line 40 def session_store @session_store end |
#settings ⇒ Object (readonly)
The snapshotted setup and materialized services used by new runs.
40 41 42 |
# File 'lib/little_ghost/runtime.rb', line 40 def settings @settings end |
#skill_paths ⇒ Object (readonly)
The snapshotted setup and materialized services used by new runs.
40 41 42 |
# File 'lib/little_ghost/runtime.rb', line 40 def skill_paths @skill_paths end |
#skill_resource_root ⇒ Object (readonly)
The snapshotted setup and materialized services used by new runs.
40 41 42 |
# File 'lib/little_ghost/runtime.rb', line 40 def skill_resource_root @skill_resource_root end |
#workspace_class ⇒ Object (readonly)
The snapshotted setup and materialized services used by new runs.
40 41 42 |
# File 'lib/little_ghost/runtime.rb', line 40 def workspace_class @workspace_class end |
Instance Method Details
#build(**overrides) ⇒ Object
Creates a sibling runtime with explicit setting overrides.
114 115 116 117 118 119 120 121 122 |
# File 'lib/little_ghost/runtime.rb', line 114 def build(**overrides) values = @settings.merge(overrides) values[:root] = canonical_application_root(values.fetch(:root)) values[:loader] = loader unless overrides.key?(:loader) || overrides.key?(:root) self.class.new( configuration:, settings: values ) end |
#build_agent(agent_class_or_name, run:, model: nil, tools: [], agent_path: Subagents::AgentPath::ROOT) ⇒ Object
:nodoc:
176 177 178 179 180 181 182 183 184 |
# File 'lib/little_ghost/runtime.rb', line 176 def build_agent( agent_class_or_name, run:, model: nil, tools: [], agent_path: Subagents::AgentPath::ROOT ) @agent_builder.build(agent_class_or_name, run:, model:, tools:, agent_path:) end |
#build_run(payload, agent_class:, entrypoint_class: agent_class, workspace: nil, sandbox: nil) ⇒ Object
Creates a Run and transfers ownership of newly created workspace and sandbox resources to it.
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 |
# File 'lib/little_ghost/runtime.rb', line 131 def build_run( payload, agent_class:, entrypoint_class: agent_class, workspace: nil, sandbox: nil ) owned_resources = [] workspace ||= build_workspace.tap { |resource| owned_resources << resource } sandbox ||= build_sandbox(workspace:).tap { |resource| owned_resources << resource } run = Run.new( invocation: parse(payload), runtime: self, agent_class:, entrypoint_class:, workspace:, sandbox: ) owned_resources.each { |resource| run.register(resource) } prepare_run(run) rescue if run run.close else close_resources(owned_resources) end raise end |
#build_sandbox(workspace:) ⇒ Object
Instantiates the configured sandbox around workspace, or an unrestricted
sandbox by default.
169 170 171 172 173 |
# File 'lib/little_ghost/runtime.rb', line 169 def build_sandbox(workspace:) return sandbox_class.new(workspace:) if sandbox_class UnrestrictedSandbox.new(workspace:) end |
#build_workspace ⇒ Object
Instantiates the configured workspace, or a root-scoped Workspace by default.
161 162 163 164 165 |
# File 'lib/little_ghost/runtime.rb', line 161 def build_workspace return workspace_class.new if workspace_class Workspace.new(root: root) end |
#default_error_message(error, _run) ⇒ Object
:nodoc:
248 249 250 251 252 253 254 255 256 257 |
# File 'lib/little_ghost/runtime.rb', line 248 def (error, _run) # :nodoc: return error. if error.is_a?(UnsupportedInputError) return error. if error.is_a?(ToolLoopError) return "The model reached its output limit before completing a response. Please retry with a narrower request." if error.is_a?(OutputLimitError) if error.is_a?(MalformedToolCallError) return "The model returned an invalid tool call before completing the response. Please retry with a narrower request." end "Agent failed: #{error.class}" end |
#error_message(error, run) ⇒ Object
:nodoc:
239 240 241 242 243 244 245 246 |
# File 'lib/little_ghost/runtime.rb', line 239 def (error, run) # :nodoc: runtime_hooks.each do |hook| = hook.(error, run) return if end (error, run) end |
#model_for(agent_class, run) ⇒ Object
:nodoc:
191 192 193 194 |
# File 'lib/little_ghost/runtime.rb', line 191 def model_for(agent_class, run) # :nodoc: role = agent_class.model_role(run.invocation) || @default_model models.resolve(role, invocation: run.invocation, run:) end |
#open_session(run) ⇒ Object
:nodoc:
196 197 198 199 200 201 202 203 |
# File 'lib/little_ghost/runtime.rb', line 196 def open_session(run) # :nodoc: Session.new( id: run.invocation.session_id, actor_id: session_actor_for(run.invocation), store: session_store, operation_id: run.operation_id ) end |
#open_subagent_session(run, conversation_id) ⇒ Object
:nodoc:
216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/little_ghost/runtime.rb', line 216 def open_subagent_session(run, conversation_id) # :nodoc: parent_link = Subagents::Manager.parent_link(run.session) Session.new( id: Subagents::Manager.conversation_session_id(conversation_id), actor_id: session_actor_for(run.invocation), store: session_store, operation_id: run.operation_id, metadata: { "little_ghost_kind" => "subagent_conversation", "little_ghost_parent_link" => parent_link, "little_ghost_conversation_id" => conversation_id } ) end |
#parse(payload) ⇒ Object
Coerces an application payload into the configured Invocation class.
125 126 127 |
# File 'lib/little_ghost/runtime.rb', line 125 def parse(payload) payload.is_a?(@invocation_class) ? payload : @invocation_class.new(payload) end |
#prepare_interruption(run, payload) ⇒ Object
:nodoc:
210 211 212 213 214 |
# File 'lib/little_ghost/runtime.rb', line 210 def prepare_interruption(run, payload) # :nodoc: runtime_hooks.reduce(payload) do |prepared, hook| hook.prepare_interruption(run, prepared) end end |
#prepare_run(run) ⇒ Object
:nodoc:
205 206 207 208 |
# File 'lib/little_ghost/runtime.rb', line 205 def prepare_run(run) # :nodoc: runtime_hooks.each { |hook| hook.prepare_run(run) } run end |
#resolve_agent(value) ⇒ Object
:nodoc:
259 260 261 |
# File 'lib/little_ghost/runtime.rb', line 259 def resolve_agent(value) # :nodoc: resolve_agent_class(value) end |
#service_name ⇒ Object
The low-cardinality service name attached to runtime telemetry.
187 188 189 |
# File 'lib/little_ghost/runtime.rb', line 187 def service_name @settings&.[](:service_name) || default_service_name end |
#session_actor_for(invocation) ⇒ Object
:nodoc:
231 232 233 |
# File 'lib/little_ghost/runtime.rb', line 231 def session_actor_for(invocation) # :nodoc: @session_actor ? @session_actor.call(invocation) : invocation.actor_id end |
#template_locals(run:, agent:) ⇒ Object
:nodoc:
235 236 237 |
# File 'lib/little_ghost/runtime.rb', line 235 def template_locals(run:, agent:) # :nodoc: {invocation: run.invocation, run:, agent:}.merge(agent.prompt_locals) end |