Class: Protege::Orchestrator::ResolverChain

Inherits:
Object
  • Object
show all
Defined in:
lib/protege/orchestrator/resolver_chain.rb

Overview

Ordered list of resolver classes that build inference context for one persona. Configured at persona declaration time (via Persona::Config#resolvers) and run once per Harness#build_initial_request call.

Each resolver runs independently and returns Array<ModelMessage> or nil. The chain concatenates all contributions in order — there is no wrapping or short-circuit (unlike middleware).

Entries are stored as [klass, args, kwargs, block] tuples rather than instances so that remove(Klass) can locate and remove a specific resolver class without needing to match on constructor arguments. Each run call creates fresh instances. A block passed to +use+/+prepend+ is captured and forwarded to klass.new, so resolvers can take a loader block (e.g. chain.use(Protege::LoadRecordResolver) { |ctx| Invoice.where(...) }).

Examples:

chain = ResolverChain.new
chain.use Protege::LoadFileResolver, 'app/personas/agent/PERSONA.md'
chain.use(Protege::LoadRecordResolver) { |ctx| Account.find_by(email: ctx.message.from_address) }
chain.use Protege::ThreadHistoryResolver

messages = chain.run(resolve_ctx)   # => Array<ModelMessage>

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializevoid

Initialize an empty chain.



33
34
35
# File 'lib/protege/orchestrator/resolver_chain.rb', line 33

def initialize
  @entries = []
end

Instance Attribute Details

#entriesArray<Array> (readonly)

Returns read-only view of the registered [klass, args, kwargs, block] entries.

Returns:

  • (Array<Array>)

    read-only view of the registered [klass, args, kwargs, block] entries



28
29
30
# File 'lib/protege/orchestrator/resolver_chain.rb', line 28

def entries
  @entries
end

Instance Method Details

#clearself

Empty the chain.

Returns:

  • (self)

    the now-empty chain



74
75
76
77
# File 'lib/protege/orchestrator/resolver_chain.rb', line 74

def clear
  @entries.clear
  self
end

#prepend(klass, *args, **kwargs) { ... } ⇒ self

Insert a resolver class at the front of the chain.

Parameters:

  • klass (Class)

    the resolver class to register

  • args (Array)

    positional constructor arguments forwarded at run time

  • kwargs (Hash)

    keyword constructor arguments forwarded at run time

Yields:

  • optional block forwarded to klass.new (e.g. a loader for LoadRecordResolver)

Returns:

  • (self)

    the chain, for fluent chaining



57
58
59
60
# File 'lib/protege/orchestrator/resolver_chain.rb', line 57

def prepend(klass, *args, **kwargs, &block)
  @entries.unshift([klass, args, kwargs, block])
  self
end

#remove(klass) ⇒ self

Remove every entry whose registered class equals klass.

Parameters:

  • klass (Class)

    the resolver class to remove

Returns:

  • (self)

    the chain, for fluent chaining



66
67
68
69
# File 'lib/protege/orchestrator/resolver_chain.rb', line 66

def remove(klass)
  @entries.reject! { |entry| entry.first == klass }
  self
end

#run(context) ⇒ Array<Object>

Run every resolver in order, passing context to each.

nil returns are silently skipped (coerced through Array(...)). Fresh resolver instances are constructed per call from the stored [klass, args, kwargs, block] tuples.

Parameters:

Returns:

  • (Array<Object>)

    a flat array of ModelMessage objects, empty when none contribute



86
87
88
89
90
# File 'lib/protege/orchestrator/resolver_chain.rb', line 86

def run(context)
  @entries.flat_map do |klass, args, kwargs, block|
    Array(klass.new(*args, **kwargs, &block).resolve(context:))
  end
end

#use(klass, *args, **kwargs) { ... } ⇒ self

Append a resolver class to the end of the chain. Constructor args are stored alongside and forwarded to klass.new at run time.

Parameters:

  • klass (Class)

    the resolver class to register

  • args (Array)

    positional constructor arguments forwarded at run time

  • kwargs (Hash)

    keyword constructor arguments forwarded at run time

Yields:

  • optional block forwarded to klass.new (e.g. a loader for LoadRecordResolver)

Returns:

  • (self)

    the chain, for fluent chaining



45
46
47
48
# File 'lib/protege/orchestrator/resolver_chain.rb', line 45

def use(klass, *args, **kwargs, &block)
  @entries << [klass, args, kwargs, block]
  self
end