Class: RubyLLM::Agents::Pipeline::Builder

Inherits:
Object
  • Object
show all
Defined in:
lib/ruby_llm/agents/pipeline/builder.rb

Overview

Builds the middleware pipeline from agent DSL configuration.

The builder allows both manual pipeline construction and automatic construction based on agent DSL settings.

Examples:

Manual pipeline construction

builder = Builder.new(MyEmbedder)
builder.use(Middleware::Tenant)
builder.use(Middleware::Cache)
builder.use(Middleware::Instrumentation)
pipeline = builder.build(core_executor)

Automatic construction from DSL

pipeline = Builder.for(MyEmbedder).build(core_executor)

With custom middleware insertion

builder = Builder.for(MyEmbedder)
builder.insert_before(Middleware::Instrumentation, MyLoggingMiddleware)
pipeline = builder.build(core_executor)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(agent_class) ⇒ Builder

Creates a new builder for an agent class

Parameters:

  • agent_class (Class)

    The agent class



36
37
38
39
# File 'lib/ruby_llm/agents/pipeline/builder.rb', line 36

def initialize(agent_class)
  @agent_class = agent_class
  @stack = []
end

Instance Attribute Details

#agent_classClass (readonly)

Returns The agent class this builder is for.

Returns:

  • (Class)

    The agent class this builder is for



28
29
30
# File 'lib/ruby_llm/agents/pipeline/builder.rb', line 28

def agent_class
  @agent_class
end

#stackArray<Class> (readonly)

Returns The middleware stack (in execution order).

Returns:

  • (Array<Class>)

    The middleware stack (in execution order)



31
32
33
# File 'lib/ruby_llm/agents/pipeline/builder.rb', line 31

def stack
  @stack
end

Class Method Details

.empty(agent_class) ⇒ Builder

Returns an empty builder (no middleware)

Useful for testing or when you want full control.

Parameters:

  • agent_class (Class)

    The agent class

Returns:



162
163
164
# File 'lib/ruby_llm/agents/pipeline/builder.rb', line 162

def empty(agent_class)
  new(agent_class)
end

.for(agent_class) ⇒ Builder

Build default pipeline for an agent class

Reads DSL configuration to determine which middleware to include. The order is:

  1. Tenant (always - resolves tenant context)

  2. Budget (if enabled - checks budget before execution)

  3. Instrumentation (always - tracks execution, including cache hits)

  4. Cache (if enabled - returns cached results)

  5. Reliability (if enabled - retries and fallbacks)

Note: Instrumentation must come BEFORE Cache so it can track cache hits. When Cache returns early on a hit, Instrumentation still sees it.

Parameters:

  • agent_class (Class)

    The agent class

Returns:

  • (Builder)

    A configured builder



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/ruby_llm/agents/pipeline/builder.rb', line 131

def for(agent_class)
  new(agent_class).tap do |builder|
    # Always included - tenant resolution
    builder.use(Middleware::Tenant)

    # Budget checking (if enabled globally)
    builder.use(Middleware::Budget) if budgets_enabled?

    # Instrumentation (always - for tracking, must be before Cache)
    builder.use(Middleware::Instrumentation)

    # Caching (if enabled on the agent)
    builder.use(Middleware::Cache) if cache_enabled?(agent_class)

    # Reliability (if agent has retries or fallbacks configured)
    builder.use(Middleware::Reliability) if reliability_enabled?(agent_class)

    # Global custom middleware
    apply_custom_middleware!(builder, global_middleware_entries)

    # Per-agent custom middleware
    apply_custom_middleware!(builder, agent_middleware_entries(agent_class))
  end
end

Instance Method Details

#build(core) ⇒ #call

Build the pipeline, wrapping the core executor

Middleware is wrapped in reverse order so that the first middleware in the stack is the outermost wrapper.

Parameters:

  • core (#call)

    The core execution logic (usually an Executor)

Returns:

  • (#call)

    The complete pipeline



94
95
96
97
98
# File 'lib/ruby_llm/agents/pipeline/builder.rb', line 94

def build(core)
  @stack.reverse.reduce(core) do |app, middleware_class|
    middleware_class.new(app, @agent_class)
  end
end

#delete(middleware_class) ⇒ self

Remove a middleware from the stack

Parameters:

  • middleware_class (Class)

    The middleware to remove

Returns:

  • (self)

    For method chaining



82
83
84
85
# File 'lib/ruby_llm/agents/pipeline/builder.rb', line 82

def delete(middleware_class)
  @stack.delete(middleware_class)
  self
end

#include?(middleware_class) ⇒ Boolean

Returns whether the stack includes a middleware

Parameters:

  • middleware_class (Class)

    The middleware class to check

Returns:

  • (Boolean)


104
105
106
# File 'lib/ruby_llm/agents/pipeline/builder.rb', line 104

def include?(middleware_class)
  @stack.include?(middleware_class)
end

#insert_after(existing, new_middleware) ⇒ self

Insert middleware after another middleware

Parameters:

  • existing (Class)

    The middleware to insert after

  • new_middleware (Class)

    The middleware to insert

Returns:

  • (self)

    For method chaining

Raises:

  • (ArgumentError)

    If existing middleware not found



70
71
72
73
74
75
76
# File 'lib/ruby_llm/agents/pipeline/builder.rb', line 70

def insert_after(existing, new_middleware)
  index = @stack.index(existing)
  raise ArgumentError, "#{existing} not found in stack" unless index

  @stack.insert(index + 1, new_middleware)
  self
end

#insert_before(existing, new_middleware) ⇒ self

Insert middleware before another middleware

Parameters:

  • existing (Class)

    The middleware to insert before

  • new_middleware (Class)

    The middleware to insert

Returns:

  • (self)

    For method chaining

Raises:

  • (ArgumentError)

    If existing middleware not found



56
57
58
59
60
61
62
# File 'lib/ruby_llm/agents/pipeline/builder.rb', line 56

def insert_before(existing, new_middleware)
  index = @stack.index(existing)
  raise ArgumentError, "#{existing} not found in stack" unless index

  @stack.insert(index, new_middleware)
  self
end

#to_aArray<Class>

Returns the stack as an array (copy)

Returns:

  • (Array<Class>)


111
112
113
# File 'lib/ruby_llm/agents/pipeline/builder.rb', line 111

def to_a
  @stack.dup
end

#use(middleware_class) ⇒ self

Add middleware to the end of the stack

Parameters:

  • middleware_class (Class)

    Middleware class to add

Returns:

  • (self)

    For method chaining



45
46
47
48
# File 'lib/ruby_llm/agents/pipeline/builder.rb', line 45

def use(middleware_class)
  @stack << middleware_class
  self
end