Class: RubyLLM::Agents::Pipeline::Context

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

Overview

Carries request/response data through the middleware pipeline.

All data flows explicitly through this object - no hidden instance variables or implicit state. This makes the data flow visible and testable.

Examples:

Creating a context

context = Context.new(
  input: "Hello world",
  agent_class: MyEmbedder,
  model: "text-embedding-3-small"
)

Accessing data set by middleware

context.tenant_id          # Set by Tenant middleware
context.cached?            # Set by Cache middleware
context.duration_ms        # Computed from timestamps

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(input:, agent_class:, agent_instance: nil, model: nil, tenant: nil, skip_cache: false, stream_block: nil, stream_events: false, parent_execution_id: nil, root_execution_id: nil, **options) ⇒ Context

Creates a new pipeline context

Parameters:

  • input (Object)

    The input data for the agent

  • agent_class (Class)

    The agent class being executed

  • agent_instance (Object, nil) (defaults to: nil)

    The agent instance

  • model (String, nil) (defaults to: nil)

    Override model (defaults to agent_class.model)

  • tenant (Hash, Object, nil) (defaults to: nil)

    Raw tenant (resolved by Tenant middleware)

  • skip_cache (Boolean) (defaults to: false)

    Whether to skip caching

  • stream_block (Proc, nil) (defaults to: nil)

    Block for streaming

  • options (Hash)

    Additional options passed to the agent



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
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 68

def initialize(input:, agent_class:, agent_instance: nil, model: nil, tenant: nil, skip_cache: false, stream_block: nil, stream_events: false, parent_execution_id: nil, root_execution_id: nil, **options)
  @input = input
  @agent_class = agent_class
  @agent_instance = agent_instance
  @agent_type = extract_agent_type(agent_class)
  @model = model || extract_model(agent_class)

  # Execution hierarchy
  @parent_execution_id = parent_execution_id
  @root_execution_id = root_execution_id

  # Store tenant in options for middleware to resolve
  @options = options.merge(tenant: tenant).compact

  # Tenant fields (set by Tenant middleware)
  @tenant_id = nil
  @tenant_object = nil
  @tenant_config = nil

  # Execution options
  @skip_cache = skip_cache
  @stream_block = stream_block
  @stream_events = stream_events

  # Debug trace
  @trace = []
  @trace_enabled = options[:debug] == true

  # Initialize tracking fields
  @attempt = 0
  @attempts_made = 0
  @cached = false
  @metadata = {}

  # Initialize cost fields
  @input_tokens = 0
  @output_tokens = 0
  @input_cost = 0.0
  @output_cost = 0.0
  @total_cost = 0.0
end

Instance Attribute Details

#agent_classObject (readonly)

Agent metadata



56
57
58
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 56

def agent_class
  @agent_class
end

#agent_instanceObject

Agent reference (for execution)



29
30
31
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 29

def agent_instance
  @agent_instance
end

#agent_typeObject (readonly)

Agent metadata



56
57
58
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 56

def agent_type
  @agent_type
end

#attemptObject

Execution tracking (set by Instrumentation middleware)



35
36
37
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 35

def attempt
  @attempt
end

#attempts_madeObject

Execution tracking (set by Instrumentation middleware)



35
36
37
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 35

def attempts_made
  @attempts_made
end

#cachedObject

Result data (set by core execute method)



41
42
43
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 41

def cached
  @cached
end

#completed_atObject

Execution tracking (set by Instrumentation middleware)



35
36
37
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 35

def completed_at
  @completed_at
end

#errorObject

Result data (set by core execute method)



41
42
43
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 41

def error
  @error
end

#execution_idObject

Execution tracking (set by Instrumentation middleware)



35
36
37
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 35

def execution_id
  @execution_id
end

#finish_reasonObject

Response metadata



47
48
49
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 47

def finish_reason
  @finish_reason
end

#inputObject

Request data



26
27
28
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 26

def input
  @input
end

#input_costObject

Cost tracking



44
45
46
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 44

def input_cost
  @input_cost
end

#input_tokensObject

Cost tracking



44
45
46
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 44

def input_tokens
  @input_tokens
end

#modelObject

Request data



26
27
28
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 26

def model
  @model
end

#model_usedObject

Response metadata



47
48
49
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 47

def model_used
  @model_used
end

#optionsObject

Request data



26
27
28
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 26

def options
  @options
end

#outputObject

Result data (set by core execute method)



41
42
43
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 41

def output
  @output
end

#output_costObject

Cost tracking



44
45
46
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 44

def output_cost
  @output_cost
end

#output_tokensObject

Cost tracking



44
45
46
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 44

def output_tokens
  @output_tokens
end

#parent_execution_idObject

Execution hierarchy (agent-as-tool)



38
39
40
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 38

def parent_execution_id
  @parent_execution_id
end

#root_execution_idObject

Execution hierarchy (agent-as-tool)



38
39
40
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 38

def root_execution_id
  @root_execution_id
end

#skip_cacheObject

Streaming support



53
54
55
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 53

def skip_cache
  @skip_cache
end

#started_atObject

Execution tracking (set by Instrumentation middleware)



35
36
37
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 35

def started_at
  @started_at
end

#stream_blockObject

Streaming support



53
54
55
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 53

def stream_block
  @stream_block
end

#stream_eventsObject

Streaming support



53
54
55
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 53

def stream_events
  @stream_events
end

#tenant_configObject

Tenant data (set by Tenant middleware, or passed in)



32
33
34
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 32

def tenant_config
  @tenant_config
end

#tenant_idObject

Tenant data (set by Tenant middleware, or passed in)



32
33
34
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 32

def tenant_id
  @tenant_id
end

#tenant_objectObject

Tenant data (set by Tenant middleware, or passed in)



32
33
34
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 32

def tenant_object
  @tenant_object
end

#time_to_first_token_msObject

Response metadata



47
48
49
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 47

def time_to_first_token_ms
  @time_to_first_token_ms
end

#total_costObject

Cost tracking



44
45
46
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 44

def total_cost
  @total_cost
end

#traceObject

Debug trace (set when debug: true is passed)



50
51
52
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 50

def trace
  @trace
end

Instance Method Details

#[](key) ⇒ Object

Custom metadata storage - read

Parameters:

  • key (Symbol, String)

    The metadata key

Returns:

  • (Object)

    The stored value



193
194
195
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 193

def [](key)
  @metadata[key]
end

#[]=(key, value) ⇒ Object

Custom metadata storage - write

Parameters:

  • key (Symbol, String)

    The metadata key

  • value (Object)

    The value to store



201
202
203
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 201

def []=(key, value)
  @metadata[key] = value
end

#add_trace(middleware_name, started_at:, duration_ms:, action: nil) ⇒ Object

Adds a trace entry for a middleware execution

Parameters:

  • middleware_name (String)

    Name of the middleware

  • started_at (Time)

    When the middleware started

  • duration_ms (Float)

    How long the middleware took in ms

  • action (String, nil) (defaults to: nil)

    Optional action description (e.g., “cache hit”)



132
133
134
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 132

def add_trace(middleware_name, started_at:, duration_ms:, action: nil)
  @trace << {middleware: middleware_name, started_at: started_at, duration_ms: duration_ms, action: action}.compact
end

#cached?Boolean

Was the result served from cache?

Returns:

  • (Boolean)


146
147
148
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 146

def cached?
  @cached == true
end

#dup_for_retryContext

Creates a duplicate context for retry attempts

Returns:

  • (Context)

    A new context with the same input but reset state



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 242

def dup_for_retry
  # Extract tenant from options since dup_for_retry is called after middleware
  # has already resolved it - we want to preserve the resolved state
  opts_without_tenant = @options.except(:tenant)

  new_ctx = self.class.new(
    input: @input,
    agent_class: @agent_class,
    agent_instance: @agent_instance,
    model: @model,
    skip_cache: @skip_cache,
    stream_block: @stream_block,
    stream_events: @stream_events,
    **opts_without_tenant
  )
  # Preserve resolved tenant state
  new_ctx.tenant_id = @tenant_id
  new_ctx.tenant_object = @tenant_object
  new_ctx.tenant_config = @tenant_config
  new_ctx.started_at = @started_at
  new_ctx.attempts_made = @attempts_made
  # Preserve execution hierarchy
  new_ctx.parent_execution_id = @parent_execution_id
  new_ctx.root_execution_id = @root_execution_id
  # Preserve trace across retries
  new_ctx.trace = @trace
  new_ctx
end

#duration_msInteger?

Duration in milliseconds

Returns:

  • (Integer, nil)

    Duration in ms, or nil if not yet completed



113
114
115
116
117
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 113

def duration_ms
  return nil unless @started_at && @completed_at

  ((@completed_at - @started_at) * 1000).to_i
end

#failed?Boolean

Did execution fail?

Returns:

  • (Boolean)


160
161
162
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 160

def failed?
  !@error.nil?
end

#llmRubyLLM::Context, RubyLLM

Returns a RubyLLM interface scoped to tenant API keys when present.

When tenant API keys are stored on this context (by the Tenant middleware), returns a RubyLLM::Context with a cloned config that has tenant-specific keys applied. This avoids mutating global RubyLLM configuration, making multi-tenant LLM calls thread-safe.

When no tenant API keys are present, returns the RubyLLM module directly (which uses the global configuration).

Returns:

  • (RubyLLM::Context, RubyLLM)

    Scoped context or global module



182
183
184
185
186
187
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 182

def llm
  api_keys = self[:tenant_api_keys]
  return RubyLLM if api_keys.nil? || api_keys.empty?

  @llm_context ||= build_llm_context(api_keys)
end

#metadataHash

Returns all custom metadata

Returns:

  • (Hash)

    The metadata hash



208
209
210
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 208

def 
  @metadata.dup
end

#stream_events?Boolean

Are stream events enabled?

Returns:

  • (Boolean)


139
140
141
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 139

def stream_events?
  @stream_events == true
end

#success?Boolean

Did execution succeed?

Returns:

  • (Boolean)


153
154
155
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 153

def success?
  @error.nil? && !@output.nil?
end

#to_hHash

Convert to hash for logging/recording

Returns:

  • (Hash)

    Hash representation of the context



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 215

def to_h
  {
    agent_class: @agent_class&.name,
    agent_type: @agent_type,
    model: @model,
    model_used: @model_used,
    tenant_id: @tenant_id,
    duration_ms: duration_ms,
    cached: cached?,
    success: success?,
    input_tokens: @input_tokens,
    output_tokens: @output_tokens,
    total_tokens: total_tokens,
    input_cost: @input_cost,
    output_cost: @output_cost,
    total_cost: @total_cost,
    finish_reason: @finish_reason,
    time_to_first_token_ms: @time_to_first_token_ms,
    attempts_made: @attempts_made,
    error_class: @error&.class&.name,
    error_message: @error&.message
  }.compact
end

#total_tokensInteger

Total tokens used (input + output)

Returns:

  • (Integer)


167
168
169
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 167

def total_tokens
  (@input_tokens || 0) + (@output_tokens || 0)
end

#trace_enabled?Boolean

Is debug tracing enabled?

Returns:

  • (Boolean)


122
123
124
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 122

def trace_enabled?
  @trace_enabled
end