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



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
109
110
111
112
113
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 73

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



61
62
63
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 61

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



61
62
63
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 61

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

#cache_creation_tokensObject

Prompt-cache usage. First-class accessors, not metadata keys: these feed both the persisted execution record and the input-cost split, so a generic bag entry would be silently droppable (it was).



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

def cache_creation_tokens
  @cache_creation_tokens
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

#cached_tokensObject

Prompt-cache usage. First-class accessors, not metadata keys: these feed both the persisted execution record and the input-cost split, so a generic bag entry would be silently droppable (it was).



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

def cached_tokens
  @cached_tokens
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



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

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



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

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



58
59
60
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 58

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



58
59
60
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 58

def stream_block
  @stream_block
end

#stream_eventsObject

Streaming support



58
59
60
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 58

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



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

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)



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

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



198
199
200
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 198

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



206
207
208
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 206

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")



137
138
139
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 137

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)


151
152
153
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 151

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



247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 247

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



118
119
120
121
122
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 118

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)


165
166
167
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 165

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



187
188
189
190
191
192
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 187

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



213
214
215
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 213

def 
  @metadata.dup
end

#stream_events?Boolean

Are stream events enabled?

Returns:

  • (Boolean)


144
145
146
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 144

def stream_events?
  @stream_events == true
end

#success?Boolean

Did execution succeed?

Returns:

  • (Boolean)


158
159
160
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 158

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

#to_hHash

Convert to hash for logging/recording

Returns:

  • (Hash)

    Hash representation of the context



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 220

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)


172
173
174
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 172

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

#trace_enabled?Boolean

Is debug tracing enabled?

Returns:

  • (Boolean)


127
128
129
# File 'lib/ruby_llm/agents/pipeline/context.rb', line 127

def trace_enabled?
  @trace_enabled
end