Class: LLM::Context
- Inherits:
-
Object
- Object
- LLM::Context
- Includes:
- Deserializer, Serializer
- Defined in:
- lib/llm/context.rb,
lib/llm/context/serializer.rb,
lib/llm/context/deserializer.rb
Overview
LLM::Context is the stateful execution boundary in llm.rb.
It holds the evolving runtime state for an LLM workflow: conversation history, tool calls and returns, schema and streaming configuration, accumulated usage, and request ownership for interruption.
This is broader than prompt context alone. A context is the object that lets one-off prompts, streaming turns, tool execution, persistence, retries, and serialized long-lived workflows all run through the same model.
A context can drive the chat completions API that all providers support or the Responses API on providers that expose it.
Defined Under Namespace
Modules: Deserializer, Serializer
Instance Attribute Summary collapse
-
#llm ⇒ LLM::Provider
readonly
Returns a provider.
-
#messages ⇒ LLM::Buffer<LLM::Message>
readonly
Returns the accumulated message history for this context.
-
#mode ⇒ Symbol
readonly
Returns the context mode.
Instance Method Summary collapse
-
#call(target) ⇒ Array<LLM::Function::Return>
Calls a named collection of work through the context.
-
#context_window ⇒ Integer
Returns the model’s context window.
-
#cost ⇒ LLM::Cost
Returns an approximate cost for a given context based on both the provider, and model.
-
#functions ⇒ Array<LLM::Function>
Returns an array of functions that can be called.
-
#image_url(url) ⇒ LLM::Object
Recongize an object as a URL to an image.
-
#initialize(llm, params = {}) ⇒ Context
constructor
A new instance of Context.
- #inspect ⇒ String
-
#interrupt! ⇒ nil
(also: #cancel!)
Interrupt the active request, if any.
-
#local_file(path) ⇒ LLM::Object
Recongize an object as a local file.
-
#model ⇒ String
Returns the model a Context is actively using.
-
#prompt(&b) ⇒ LLM::Prompt
(also: #build_prompt)
Build a role-aware prompt for a single request.
-
#remote_file(res) ⇒ LLM::Object
Reconginize an object as a remote file.
-
#respond(prompt, params = {}) ⇒ LLM::Response
Interact with the context via the responses API.
-
#returns ⇒ Array<LLM::Function::Return>
Returns tool returns accumulated in this context.
-
#serialize(path:) ⇒ void
(also: #save)
Save the current context state.
-
#talk(prompt, params = {}) ⇒ LLM::Response
(also: #chat)
Interact with the context via the chat completions API.
- #to_h ⇒ Hash
- #to_json ⇒ String
-
#tracer ⇒ LLM::Tracer
Returns an LLM tracer.
-
#usage ⇒ LLM::Object?
Returns token usage accumulated in this context This method returns token usage for the latest assistant message, and it returns nil for non-assistant messages.
-
#wait(strategy) ⇒ Array<LLM::Function::Return>
Waits for queued tool work to finish.
Methods included from Deserializer
#deserialize, #deserialize_message
Constructor Details
#initialize(llm, params = {}) ⇒ Context
Returns a new instance of Context.
68 69 70 71 72 73 74 75 |
# File 'lib/llm/context.rb', line 68 def initialize(llm, params = {}) @llm = llm @mode = params.delete(:mode) || :completions tools = [*params.delete(:tools), *load_skills(params.delete(:skills))] @params = {model: llm.default_model, schema: nil}.compact.merge!(params) @params[:tools] = tools unless tools.empty? @messages = LLM::Buffer.new(llm) end |
Instance Attribute Details
#llm ⇒ LLM::Provider (readonly)
Returns a provider
50 51 52 |
# File 'lib/llm/context.rb', line 50 def llm @llm end |
#messages ⇒ LLM::Buffer<LLM::Message> (readonly)
Returns the accumulated message history for this context
45 46 47 |
# File 'lib/llm/context.rb', line 45 def @messages end |
#mode ⇒ Symbol (readonly)
Returns the context mode
55 56 57 |
# File 'lib/llm/context.rb', line 55 def mode @mode end |
Instance Method Details
#call(target) ⇒ Array<LLM::Function::Return>
Calls a named collection of work through the context.
This currently supports ‘:functions`, forwarding to `functions.call`.
162 163 164 165 166 167 |
# File 'lib/llm/context.rb', line 162 def call(target) case target when :functions then functions.call else raise ArgumentError, "Unknown target: #{target.inspect}. Expected :functions" end end |
#context_window ⇒ Integer
This method returns 0 when the provider or model can’t be found within Registry.
Returns the model’s context window. The context window is the maximum amount of input and output tokens a model can consider in a single request.
335 336 337 338 339 340 341 342 |
# File 'lib/llm/context.rb', line 335 def context_window LLM .registry_for(llm) .limit(model:) .context rescue LLM::NoSuchModelError, LLM::NoSuchRegistryError 0 end |
#cost ⇒ LLM::Cost
Returns an approximate cost for a given context based on both the provider, and model
318 319 320 321 322 323 324 325 |
# File 'lib/llm/context.rb', line 318 def cost return LLM::Cost.new(0, 0) unless usage cost = LLM.registry_for(llm).cost(model:) LLM::Cost.new( (cost.input.to_f / 1_000_000.0) * usage.input_tokens, (cost.output.to_f / 1_000_000.0) * usage.output_tokens ) end |
#functions ⇒ Array<LLM::Function>
Returns an array of functions that can be called
141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/llm/context.rb', line 141 def functions return_ids = returns.map(&:id) @messages .select(&:assistant?) .flat_map do |msg| fns = msg.functions.select { _1.pending? && !return_ids.include?(_1.id) } fns.each do |fn| fn.tracer = tracer fn.model = msg.model end end.extend(LLM::Function::Array) end |
#image_url(url) ⇒ LLM::Object
Recongize an object as a URL to an image
249 250 251 |
# File 'lib/llm/context.rb', line 249 def image_url(url) LLM::Object.from(value: url, kind: :image_url) end |
#inspect ⇒ String
132 133 134 135 136 |
# File 'lib/llm/context.rb', line 132 def inspect "#<#{self.class.name}:0x#{object_id.to_s(16)} " \ "@llm=#{@llm.class}, @mode=#{@mode.inspect}, @params=#{@params.inspect}, " \ "@messages=#{@messages.inspect}>" end |
#interrupt! ⇒ nil Also known as: cancel!
Interrupt the active request, if any. This is inspired by Go’s context cancellation model.
207 208 209 |
# File 'lib/llm/context.rb', line 207 def interrupt! llm.interrupt!(@owner) end |
#local_file(path) ⇒ LLM::Object
Recongize an object as a local file
259 260 261 |
# File 'lib/llm/context.rb', line 259 def local_file(path) LLM::Object.from(value: LLM.File(path), kind: :local_file) end |
#model ⇒ String
Returns the model a Context is actively using
283 284 285 |
# File 'lib/llm/context.rb', line 283 def model .find(&:assistant?)&.model || @params[:model] end |
#prompt(&b) ⇒ LLM::Prompt Also known as: build_prompt
Build a role-aware prompt for a single request.
Prefer this method over #build_prompt. The older method name is kept for backward compatibility.
238 239 240 |
# File 'lib/llm/context.rb', line 238 def prompt(&b) LLM::Prompt.new(@llm, &b) end |
#remote_file(res) ⇒ LLM::Object
Reconginize an object as a remote file
269 270 271 |
# File 'lib/llm/context.rb', line 269 def remote_file(res) LLM::Object.from(value: res, kind: :remote_file) end |
#respond(prompt, params = {}) ⇒ LLM::Response
Not all LLM providers support this API
Interact with the context via the responses API. This method immediately sends a request to the LLM and returns the response.
117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/llm/context.rb', line 117 def respond(prompt, params = {}) @owner = Fiber.current params = @params.merge(params) bind!(params[:stream], params[:model]) res_id = params[:store] == false ? nil : @messages.find(&:assistant?)&.response&.response_id params = params.merge(previous_response_id: res_id, input: @messages.to_a).compact res = @llm.responses.create(prompt, params) role = params[:role] || @llm.user_role @messages.concat LLM::Prompt === prompt ? prompt.to_a : [LLM::Message.new(role, prompt)] @messages.concat [res.choices[-1]] res end |
#returns ⇒ Array<LLM::Function::Return>
Returns tool returns accumulated in this context
172 173 174 175 176 177 178 179 180 |
# File 'lib/llm/context.rb', line 172 def returns @messages .select(&:tool_return?) .flat_map do |msg| LLM::Function::Return === msg.content ? [msg.content] : [*msg.content].grep(LLM::Function::Return) end end |
#serialize(path:) ⇒ void Also known as: save
This method returns an undefined value.
Save the current context state
309 310 311 |
# File 'lib/llm/context.rb', line 309 def serialize(path:) ::File.binwrite path, LLM.json.dump(to_h) end |
#talk(prompt, params = {}) ⇒ LLM::Response Also known as: chat
Interact with the context via the chat completions API. This method immediately sends a request to the LLM and returns the response.
89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/llm/context.rb', line 89 def talk(prompt, params = {}) return respond(prompt, params) if mode == :responses @owner = Fiber.current params = params.merge(messages: @messages.to_a) params = @params.merge(params) bind!(params[:stream], params[:model]) res = @llm.complete(prompt, params) role = params[:role] || @llm.user_role role = @llm.tool_role if params[:role].nil? && [*prompt].grep(LLM::Function::Return).any? @messages.concat LLM::Prompt === prompt ? prompt.to_a : [LLM::Message.new(role, prompt)] @messages.concat [res.choices[-1]] res end |
#to_h ⇒ Hash
289 290 291 |
# File 'lib/llm/context.rb', line 289 def to_h {schema_version: 1, model:, messages: @messages.map { (_1) }} end |
#to_json ⇒ String
295 296 297 |
# File 'lib/llm/context.rb', line 295 def to_json(...) to_h.to_json(...) end |
#tracer ⇒ LLM::Tracer
Returns an LLM tracer
276 277 278 |
# File 'lib/llm/context.rb', line 276 def tracer @llm.tracer end |
#usage ⇒ LLM::Object?
Returns token usage accumulated in this context This method returns token usage for the latest assistant message, and it returns nil for non-assistant messages.
219 220 221 |
# File 'lib/llm/context.rb', line 219 def usage @messages.find(&:assistant?)&.usage end |
#wait(strategy) ⇒ Array<LLM::Function::Return>
Waits for queued tool work to finish.
This prefers queued streamed tool work when the configured stream exposes a non-empty queue. Otherwise it falls back to waiting on the context’s pending functions directly.
194 195 196 197 198 199 200 201 |
# File 'lib/llm/context.rb', line 194 def wait(strategy) stream = @params[:stream] if LLM::Stream === stream && !stream.queue.empty? stream.wait(strategy) else functions.wait(strategy) end end |