Class: Brute::Pipeline

Inherits:
Object
  • Object
show all
Defined in:
lib/brute/pipeline.rb

Overview

Rack-style middleware pipeline for LLM calls.

Each middleware wraps the next, forming an onion model:

Tracing → Retry → DoomLoop → Reasoning → [LLM Call] → Reasoning → DoomLoop → Retry → Tracing

The innermost “app” is the actual LLM call. Each middleware can:

- Modify the env (context, params) BEFORE the call   (pre-processing)
- Modify or inspect the response AFTER the call       (post-processing)
- Short-circuit (return without calling inner app)
- Retry (call inner app multiple times)

## The env hash

{
  context:   LLM::Context,     # conversation state
  provider:  LLM::Provider,    # the LLM provider
  input:     <prompt/results>,  # what to pass to context.talk()
  tools:     [Tool, ...],       # tool classes
  params:    {},                # extra LLM call params (reasoning config, etc.)
  metadata:  {},                # shared scratchpad for middleware state
  callbacks: {},                # :on_content, :on_tool_call_start, :on_tool_result
}

## The response

The return value of call(env) is the LLM::Message from context.talk().

## Building a pipeline

pipeline = Brute::Pipeline.new do
  use Brute::Middleware::Tracing, logger: logger
  use Brute::Middleware::Retry, max_attempts: 3
  use Brute::Middleware::SessionPersistence, session: session
  run Brute::Middleware::LLMCall.new
end

response = pipeline.call(env)

Instance Method Summary collapse

Constructor Details

#initialize(&block) ⇒ Pipeline

Returns a new instance of Pipeline.



44
45
46
47
48
# File 'lib/brute/pipeline.rb', line 44

def initialize(&block)
  @middlewares = []
  @app = nil
  instance_eval(&block) if block
end

Instance Method Details

#buildObject

Build the chain without calling it. Useful for inspection or caching.



69
70
71
72
73
74
75
76
77
78
79
# File 'lib/brute/pipeline.rb', line 69

def build
  raise "Pipeline has no terminal app — call `run` first" unless @app

  @middlewares.reverse.inject(@app) do |inner, (klass, args, kwargs, block)|
    if block
      klass.new(inner, *args, **kwargs, &block)
    else
      klass.new(inner, *args, **kwargs)
    end
  end
end

#call(env) ⇒ Object

Build the full middleware chain and call it.



64
65
66
# File 'lib/brute/pipeline.rb', line 64

def call(env)
  build.call(env)
end

#run(app) ⇒ Object

Set the terminal app (innermost handler).



58
59
60
61
# File 'lib/brute/pipeline.rb', line 58

def run(app)
  @app = app
  self
end

#use(klass, *args, **kwargs, &block) ⇒ Object

Register a middleware class. The class must implement ‘initialize(app, *args, **kwargs)` and `call(env)`.



52
53
54
55
# File 'lib/brute/pipeline.rb', line 52

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