Class: LLM::Context
- Inherits:
-
Object
- Object
- LLM::Context
- Includes:
- Deserializer
- Defined in:
- lib/llm/context.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
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.
-
#deserialize(path: nil, string: nil, data: nil) ⇒ LLM::Context
(also: #restore)
Restore a saved context state.
-
#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
Constructor Details
#initialize(llm, params = {}) ⇒ Context
Returns a new instance of Context.
65 66 67 68 69 70 71 |
# File 'lib/llm/context.rb', line 65 def initialize(llm, params = {}) @llm = llm @mode = params.delete(:mode) || :completions @params = {model: llm.default_model, schema: nil}.compact.merge!(params) @messages = LLM::Buffer.new(llm) @owner = Fiber.current end |
Instance Attribute Details
#llm ⇒ LLM::Provider (readonly)
Returns a provider
48 49 50 |
# File 'lib/llm/context.rb', line 48 def llm @llm end |
#messages ⇒ LLM::Buffer<LLM::Message> (readonly)
Returns the accumulated message history for this context
43 44 45 |
# File 'lib/llm/context.rb', line 43 def @messages end |
#mode ⇒ Symbol (readonly)
Returns the context mode
53 54 55 |
# File 'lib/llm/context.rb', line 53 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`.
154 155 156 157 158 159 |
# File 'lib/llm/context.rb', line 154 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.
351 352 353 354 355 356 357 358 |
# File 'lib/llm/context.rb', line 351 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
334 335 336 337 338 339 340 341 |
# File 'lib/llm/context.rb', line 334 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 |
#deserialize(path: nil, string: nil, data: nil) ⇒ LLM::Context Also known as: restore
Restore a saved context state
315 316 317 318 319 320 321 322 323 324 325 326 327 |
# File 'lib/llm/context.rb', line 315 def deserialize(path: nil, string: nil, data: nil) ctx = if data data elsif path.nil? and string.nil? raise ArgumentError, "a path, string, or data payload is required" elsif path LLM.json.load(::File.binread(path)) else LLM.json.load(string) end @messages.concat [*ctx["messages"]].map { (_1) } self end |
#functions ⇒ Array<LLM::Function>
Returns an array of functions that can be called
133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/llm/context.rb', line 133 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
239 240 241 |
# File 'lib/llm/context.rb', line 239 def image_url(url) LLM::Object.from(value: url, kind: :image_url) end |
#inspect ⇒ String
124 125 126 127 128 |
# File 'lib/llm/context.rb', line 124 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.
197 198 199 |
# File 'lib/llm/context.rb', line 197 def interrupt! llm.interrupt!(@owner) end |
#local_file(path) ⇒ LLM::Object
Recongize an object as a local file
249 250 251 |
# File 'lib/llm/context.rb', line 249 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
273 274 275 |
# File 'lib/llm/context.rb', line 273 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.
228 229 230 |
# File 'lib/llm/context.rb', line 228 def prompt(&b) LLM::Prompt.new(@llm, &b) end |
#remote_file(res) ⇒ LLM::Object
Reconginize an object as a remote file
259 260 261 |
# File 'lib/llm/context.rb', line 259 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.
111 112 113 114 115 116 117 118 119 120 |
# File 'lib/llm/context.rb', line 111 def respond(prompt, params = {}) params = @params.merge(params) 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
164 165 166 167 168 169 170 171 172 |
# File 'lib/llm/context.rb', line 164 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
299 300 301 |
# File 'lib/llm/context.rb', line 299 def serialize(path:) ::File.binwrite path, LLM.json.dump(self) 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.
85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/llm/context.rb', line 85 def talk(prompt, params = {}) return respond(prompt, params) if mode == :responses params = params.merge(messages: @messages.to_a) params = @params.merge(params) 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
279 280 281 |
# File 'lib/llm/context.rb', line 279 def to_h {schema_version: 1, model:, messages:} end |
#to_json ⇒ String
285 286 287 |
# File 'lib/llm/context.rb', line 285 def to_json(...) to_h.to_json(...) end |
#tracer ⇒ LLM::Tracer
Returns an LLM tracer
266 267 268 |
# File 'lib/llm/context.rb', line 266 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.
209 210 211 |
# File 'lib/llm/context.rb', line 209 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.
184 185 186 187 188 189 190 191 |
# File 'lib/llm/context.rb', line 184 def wait(strategy) stream = @params[:stream] if LLM::Stream === stream && !stream.queue.empty? stream.wait(strategy) else functions.wait(strategy) end end |