class LittleGhost::Assembly
Gives one agent or a coordinated group the same callable entrypoint.
An assembly is anything callers can invoke like one Agent. An Agent is the smallest assembly because it owns one model loop. Workflow, Swarm, and Graph subclasses coordinate several participants while preserving the same ask, stream_ask, call, and stream interface.
agent_run = CustomerSupportAgent.ask("Why is my transfer pending?") graph_run = SupportFlowGraph.ask("Why is my transfer pending?") agent_run.response graph_run.response
Applications normally subclass Agent, Workflow, Swarm, or Graph rather than Assembly directly. Each standalone call returns a top-level Run. Participants called inside another Assembly return a RunResult to their parent.
Advanced construction
Named subclasses are the usual form. to_builder creates a mutable definition for applications that discover participants at runtime. definition returns the fixed snapshot used by one execution. Composite results record their steps in RunResult#trajectory.
Return values
CustomerSupportAgent.ask(...)-
A named class creates and returns a top-level
Run. CustomerSupportAgent.new(runtime: runtime).ask(...)-
A standalone instance also creates and returns a top-level
Run. runtime.build_assembly(..., run: run).call(...)-
A participant already bound to a
Runreturns its childRunResult. stream_ask(...).each { |event| ... }-
A standalone stream returns its top-level
Runafter enumeration. A run-scoped stream ends with aninvocation_stopevent carryingRunResult.
Attributes
Runtime used to resolve participants and build Runs.
Public Class Methods
Source
# File lib/little_ghost/assembly.rb, line 52 def ask(message, **options) snapshot = definition snapshot.implementation.new.bind_assembly_definition(snapshot).ask(message, **options) end
Executes message through a fresh standalone assembly and returns its Run.
options become Invocation fields. Common values include history, context, settings, metadata, session_id, actor_id, and deadline_at.
# File lib/little_ghost/assembly.rb, line 111 def assembly_id(*values) return assembly_id_value || default_assembly_id if values.empty? self.assembly_id_value = values.fetch(0).to_s end
The stable identifier used for tools and telemetry. Named subclasses derive it from their underscored class name without their type suffix.
Source
# File lib/little_ghost/assembly.rb, line 129 def assembly_kind return :agent if defined?(Agent) && self <= Agent return :workflow if defined?(Workflow) && self <= Workflow return :swarm if defined?(Swarm) && self <= Swarm return :graph if defined?(Graph) && self <= Graph :assembly end
Returns :agent, :workflow, :swarm, :graph, or :assembly.
Source
# File lib/little_ghost/assembly.rb, line 77 def definition if assembly_kind == :assembly implementation = dup implementation.assembly_id(assembly_id) implementation.description(description) implementation.freeze return AssemblyDefinition.new( kind: :assembly, assembly_id:, description:, implementation: ) end to_builder.definition end
Returns an immutable definition for this class.
# File lib/little_ghost/assembly.rb, line 122 def description(*values) return description_value.to_s if values.empty? self.description_value = values.fetch(0).to_s end
The human-readable description used when exposing the assembly as a tool.
# File lib/little_ghost/assembly.rb, line 67 def stream_ask(message, **options) snapshot = definition stream = nil Enumerator.new do |events| stream ||= snapshot.implementation.new.bind_assembly_definition(snapshot).stream_ask(message, **options) stream.each { |event| events << event } end end
Lazily streams message through a fresh standalone assembly.
Enumeration yields StreamEvent objects and returns the terminal Run. The same Invocation fields accepted by .ask may be supplied as options. Agent progress arrives once through :agent_stream, with source metadata identifying every Agent in the Run, including nested participants. Lifecycle and terminal results remain separate events. Filter sources before forwarding progress to a destination that may see only some participants. Set include_agent_events: false to omit all Agent progress without changing execution or final results. See Run.
Source
# File lib/little_ghost/assembly.rb, line 95 def to_builder builder_class = { agent: AgentBuilder, workflow: WorkflowBuilder, swarm: SwarmBuilder, graph: GraphBuilder }.fetch(assembly_kind) builder_class.new(base: self) end
Returns a mutable dynamic builder seeded by this class.
Public Instance Methods
Source
# File lib/little_ghost/assembly.rb, line 266 def as_tool(name: self.class.assembly_id, description: self.class.description, preserve_context: false) build_tool(name:, description:, preserve_context:, factory: nil) end
Exposes this assembly as a Tool instance.
By default, calls do not remember earlier conversation history. Set preserve_context: true to carry that history from one tool call to the next. This option does not control working state: every call receives the invoking Tool’s current RunContext#state, which may include current request values or values restored from a Session. Nested tools must still authorize privileged work with current, application-established values. A directly bound Agent remains the target for every call, so overlapping calls raise AgentBusyError. Class-level Agent#agent_as_tool declarations build a fresh Agent per context-free call. Within the same Run, progress from the target and its descendants carries a :tool boundary in AgentStreamSource#assembly_path. It cannot be mistaken for root Agent progress, including when this instance is reused across calls.
Source
# File lib/little_ghost/assembly.rb, line 226 def ask(message, **options) call(message, **options) end
# File lib/little_ghost/assembly.rb, line 212 def call(input = nil, **options) return build_run(entrypoint_payload(input, options)).call if standalone? result = nil stream(input, **options).each do |event| result = event.data[:result] if event.type == :invocation_stop end result end
Source
# File lib/little_ghost/assembly.rb, line 374 def close @assembly_mutex.synchronize do return if @assembly_closed @assembly_closed = true end end
Closes resources owned directly by this assembly.
# File lib/little_ghost/assembly.rb, line 359 def interject(message, **options) child = @assembly_mutex.synchronize do active = @active_assemblies.dup if active.empty? raise AgentInterjectionError, "Assembly is not currently running" end if active.length > 1 raise AgentInterjectionError, "Assembly has multiple active participants; the interjection target is ambiguous" end active.first end child.interject(message, **options) end
Adds an interjection to the single active leaf Agent.
Source
# File lib/little_ghost/assembly.rb, line 385 def prompt_locals = {}
Additional prompt locals made available to child agents.
# File lib/little_ghost/assembly.rb, line 203 def start_execution(payload, &event_consumer) ensure_standalone! Execution.start(build_stream_run(payload), &event_consumer) end
Starts payload in the background and returns an Execution. The consumer receives source-tagged :agent_stream progress from every Agent in the Run, including nested participants. Filter sources for the intended destination. Set include_agent_events to false in payload to omit Agent progress while keeping lifecycle and terminal results.
# File lib/little_ghost/assembly.rb, line 238 def stream_ask(message, **options) if standalone? options[:deadline_at] = options.delete(:deadline) if options.key?(:deadline) payload = entrypoint_payload(message, options) stream = nil return Enumerator.new do |events| stream ||= build_stream_run(payload).each stream.each { |event| events << event } end end stream(message, **options) end
Lazily streams message through the standalone or run-scoped assembly.
A standalone stream returns its terminal Run after enumeration. A run-scoped stream finishes with an invocation_stop event containing its RunResult. Standalone streams include source-tagged :agent_stream events from every Agent in the Run by default. Applications filter sources before forwarding progress, or set include_agent_events: false to omit all Agent progress. This option does not change execution or final results.