Class: LLM::Agent
- Inherits:
-
Object
- Object
- LLM::Agent
- Defined in:
- lib/llm/agent.rb,
lib/llm/agent/registry.rb,
lib/llm/agent/interrupt.rb
Overview
Unlike LLM::Context the LLM::Agent class automatically manages the tool loop.
Defined Under Namespace
Modules: Interrupt Classes: Registry
Instance Attribute Summary collapse
-
#llm ⇒ LLM::Provider
readonly
Returns a provider.
Class Method Summary collapse
-
.set(properties)
Bulk-assign class-level defaults.
-
.name(name = UNDEFINED, &block) ⇒ String
Set or get an agent's name.
-
.description(desc = UNDEFINED, &block) ⇒ String?
Set or get an agent's description.
-
.concurrency(concurrency = UNDEFINED) ⇒ Symbol, ...
Set or get the tool execution concurrency.
-
.model(model = UNDEFINED, &block) ⇒ String?
Set or get the model an agent will use.
-
.schema(schema = UNDEFINED, &block) ⇒ LLM::Schema?
Set or get an agent's schema.
-
.instructions(instructions = UNDEFINED, &block) ⇒ String?
Set or get an agent's instructions.
-
.tracer(tracer = UNDEFINED, &block) ⇒ LLM::Tracer, ...
Set or get an agent's tracer.
-
.compactor(compactor = UNDEFINED, &block) ⇒ Object, ...
Set or get an agent's compactor.
-
.compactor_options(options = UNDEFINED, &block) ⇒ Hash, ...
Set or get an agent's compactor options.
-
.intercept(intercept = UNDEFINED, &block) ⇒ LLM::Intercept?
Set or get an agent's intercept.
-
.intercept_options(options = UNDEFINED, &block) ⇒ Hash?
Set or get an intercept's options.
-
.stream(stream = UNDEFINED, &block) ⇒ Object, ...
Set or get an agent's stream.
-
.path(path = UNDEFINED, &block) ⇒ String?
Set the file path where an agent's memory can be restored from, and written to.
-
.tool_budget(budget = UNDEFINED, &block) ⇒ Integer?
Set or get the maximum number of tool calls an agent can make in a single turn.
-
.retry_budget(budget = UNDEFINED, &block) ⇒ Integer?
Set or get the number of attempts an agent will make when given a failed request.
-
.tools(*tools, &block) ⇒ Array<LLM::Function>
Set or get the default tools.
-
.confirm(*tool_names, &block) ⇒ Array<String>, ...
Set or get the agent tools that require confirmation.
-
.registry ⇒ LLM::Agent::Registry
Returns an instance of LLM::Agent::Registry.
Instance Method Summary collapse
-
#on_tool_confirmation(fn, strategy) ⇒ LLM::Function::Return
This method is called when confirmation is required before a tool can run.
-
#initialize(llm, params = {}) ⇒ Agent
constructor
A new instance of Agent.
-
#name ⇒ String
Returns the agent's name.
-
#path ⇒ String?
Returns a file path where an agent's memory is restored from, and written to after each turn.
-
#description ⇒ String?
Returns the agent's description.
-
#record ⇒ Object?
Returns the ORM record this agent is bound to, or nil.
-
#talk(prompt, params = {}) ⇒ LLM::Response
Maintain a conversation via the chat completions API.
- #ask(prompt, params = {}) ⇒ Object
- #messages ⇒ LLM::Buffer<LLM::Message>
-
#id ⇒ String
Returns a stable id for this agent.
-
#created_at ⇒ Time?
Returns the time this agent was created.
- #retry_budget ⇒ Integer
- #pending_functions ⇒ Array<LLM::Function>
- #returns ⇒ Array<LLM::Function::Return>
- #wait ⇒ Array<LLM::Function::Return>
-
#token_usage ⇒ LLM::Usage
(also: #usage)
See also: Context#token_usage.
-
#context_used ⇒ Integer?
See also: Context#context_used.
-
#context_usage ⇒ Rational?
See also: Context#context_usage.
-
#interrupt! ⇒ nil
(also: #cancel!)
Interrupt the active request, if any.
- #prompt(&b) ⇒ LLM::Prompt (also: #build_prompt)
-
#image_url(url) ⇒ LLM::Object
Returns a tagged object.
-
#local_file(path) ⇒ LLM::Object
Returns a tagged object.
-
#remote_file(res) ⇒ LLM::Object
Returns a tagged object.
-
#tracer ⇒ LLM::Tracer
Returns an LLM tracer.
- #tracer=(other)
-
#stream ⇒ LLM::Stream, ...
Returns a stream object, or nil.
-
#model ⇒ String
Returns the model an Agent is actively using.
- #mode ⇒ Symbol
-
#concurrency ⇒ Symbol, ...
Returns the configured tool execution concurrency.
- #cost ⇒ LLM::Cost
- #context_window ⇒ Integer
- #registry ⇒ LLM::Registry
- #compacted? ⇒ Boolean
- #compactor ⇒ Class<LLM::Compactor>
- #compactor_options ⇒ Hash
-
#console(name: self.name, path: nil, tools: [], tracer: false)
(also: #repl)
Start an agent console.
- #params ⇒ Hash
- #to_h ⇒ Hash
- #to_json ⇒ String
- #inspect ⇒ String
- #serialize(**kw) (also: #save)
- #deserialize(**kw) ⇒ LLM::Agent (also: #restore)
Constructor Details
#initialize(llm, params = {}) ⇒ Agent
Returns a new instance of Agent.
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 |
# File 'lib/llm/agent.rb', line 460 def initialize(llm, params = {}) params = {}.merge!(params) @llm = llm fields, fields_ivar = FIELDS, IVARS fields.each do |field| resolvable = if params.key?(field) params.delete(field) else (self.class.respond_to?(field) ? self.class.public_send(field) : nil) end resolve_symbol = !%i[concurrency].include?(field) resolved = resolvable != nil ? resolve_option(self, resolvable, resolve_symbol:) : resolvable resolved = [*resolved].map(&:to_s) if field == :confirm && resolved if field == :model params[field] = resolved unless resolved.nil? || params.key?(field) elsif resolved && !fields_ivar.include?(field) params[field] ||= resolved elsif fields_ivar.include?(field) instance_variable_set(:"@#{field}", resolved) end end if params[:retry_budget].nil? params[:retry_budget] = llm.retry_budget end @ctx = LLM::Context.new(llm, params.merge(record: @record).compact) @path and File.readable?(@path) ? @ctx.restore(path:) : nil end |
Instance Attribute Details
#llm ⇒ LLM::Provider (readonly)
Returns a provider
90 91 92 |
# File 'lib/llm/agent.rb', line 90 def llm @llm end |
Class Method Details
.set(properties)
This method returns an undefined value.
Bulk-assign class-level defaults
132 133 134 135 136 137 138 139 140 |
# File 'lib/llm/agent.rb', line 132 def self.set(properties) properties.each do if respond_to?(_1) public_send(_1, _2) else raise KeyError, "key not found: #{_1}" end end end |
.name(name = UNDEFINED, &block) ⇒ String
This method serves as a self-documenting string and it is used by LLM::Console. It is optional but recommended.
Set or get an agent's name
152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/llm/agent.rb', line 152 def self.name(name = UNDEFINED, &block) if name.equal?(UNDEFINED) and block.nil? LLM.share!(@name || to_s.split("::").last.gsub(CASE_PATTERN, "-").downcase) else if Class === name name = name.to_s.split("::").last @name = LLM.share!(name.gsub(CASE_PATTERN, "-").downcase) else @name = LLM.share!(block || name) end end end |
.description(desc = UNDEFINED, &block) ⇒ String?
Reading this on the class returns what was configured - a Symbol or Proc included - because an instance is what resolves those. Read it on an instance for the description itself.
This method serves as a self-documenting string. It is optional but recommended.
Set or get an agent's description
179 180 181 182 183 184 185 |
# File 'lib/llm/agent.rb', line 179 def self.description(desc = UNDEFINED, &block) if desc.equal?(UNDEFINED) and block.nil? LLM.share!(@desc) else @desc = LLM.share!(block || desc) end end |
.concurrency(concurrency = UNDEFINED) ⇒ Symbol, ...
Set or get the tool execution concurrency.
198 199 200 201 202 203 204 |
# File 'lib/llm/agent.rb', line 198 def self.concurrency(concurrency = UNDEFINED) if concurrency.equal?(UNDEFINED) @concurrency else @concurrency = concurrency end end |
.model(model = UNDEFINED, &block) ⇒ String?
Set or get the model an agent will use
214 215 216 217 218 219 220 |
# File 'lib/llm/agent.rb', line 214 def self.model(model = UNDEFINED, &block) if model.equal?(UNDEFINED) and block.nil? LLM.share!(@model) else @model = LLM.share!(block || model) end end |
.schema(schema = UNDEFINED, &block) ⇒ LLM::Schema?
Set or get an agent's schema
230 231 232 233 234 235 236 |
# File 'lib/llm/agent.rb', line 230 def self.schema(schema = UNDEFINED, &block) if schema.equal?(UNDEFINED) and block.nil? LLM.share!(@schema) else @schema = LLM.share!(block || schema) end end |
.instructions(instructions = UNDEFINED, &block) ⇒ String?
Set or get an agent's instructions
246 247 248 249 250 251 252 |
# File 'lib/llm/agent.rb', line 246 def self.instructions(instructions = UNDEFINED, &block) if instructions.equal?(UNDEFINED) and block.nil? LLM.share!(@instructions) else @instructions = LLM.share!(block || instructions) end end |
.tracer(tracer = UNDEFINED, &block) ⇒ LLM::Tracer, ...
Set or get an agent's tracer.
265 266 267 268 269 270 271 |
# File 'lib/llm/agent.rb', line 265 def self.tracer(tracer = UNDEFINED, &block) if tracer.equal?(UNDEFINED) and block.nil? @tracer else @tracer = block || tracer end end |
.compactor(compactor = UNDEFINED, &block) ⇒ Object, ...
Set or get an agent's compactor.
278 279 280 281 282 283 284 |
# File 'lib/llm/agent.rb', line 278 def self.compactor(compactor = UNDEFINED, &block) if compactor.equal?(UNDEFINED) and block.nil? @compactor else @compactor = block || compactor end end |
.compactor_options(options = UNDEFINED, &block) ⇒ Hash, ...
Set or get an agent's compactor options.
291 292 293 294 295 296 297 |
# File 'lib/llm/agent.rb', line 291 def self.( = UNDEFINED, &block) if .equal?(UNDEFINED) and block.nil? LLM.share!(@compactor_options) else @compactor_options = LLM.share!(block || ) end end |
.intercept(intercept = UNDEFINED, &block) ⇒ LLM::Intercept?
Set or get an agent's intercept.
306 307 308 309 310 311 312 |
# File 'lib/llm/agent.rb', line 306 def self.intercept(intercept = UNDEFINED, &block) if intercept.equal?(UNDEFINED) and block.nil? @intercept else @intercept = block || intercept end end |
.intercept_options(options = UNDEFINED, &block) ⇒ Hash?
Set or get an intercept's options
321 322 323 324 325 326 327 |
# File 'lib/llm/agent.rb', line 321 def self.( = UNDEFINED, &block) if .equal?(UNDEFINED) and block.nil? @intercept_options else @intercept_options = block || end end |
.stream(stream = UNDEFINED, &block) ⇒ Object, ...
Set or get an agent's stream
338 339 340 341 342 343 344 |
# File 'lib/llm/agent.rb', line 338 def self.stream(stream = UNDEFINED, &block) if stream.equal?(UNDEFINED) and block.nil? @stream else @stream = block || stream end end |
.path(path = UNDEFINED, &block) ⇒ String?
Set the file path where an agent's memory can be restored from, and written to.
352 353 354 355 356 357 358 |
# File 'lib/llm/agent.rb', line 352 def self.path(path = UNDEFINED, &block) if path.equal?(UNDEFINED) and block.nil? LLM.share!(@path) else @path = LLM.share!(block || path) end end |
.tool_budget(budget = UNDEFINED, &block) ⇒ Integer?
This feature is disabled by default.
Set or get the maximum number of tool calls an agent can make in a single turn.
371 372 373 374 375 376 377 |
# File 'lib/llm/agent.rb', line 371 def self.tool_budget(budget = UNDEFINED, &block) if budget.equal?(UNDEFINED) and block.nil? @tool_budget else @tool_budget = block || budget end end |
.retry_budget(budget = UNDEFINED, &block) ⇒ Integer?
Set or get the number of attempts an agent will make when given a failed request.
387 388 389 390 391 392 393 |
# File 'lib/llm/agent.rb', line 387 def self.retry_budget(budget = UNDEFINED, &block) if budget.equal?(UNDEFINED) and block.nil? @retry_budget else @retry_budget = block || budget end end |
.tools(*tools, &block) ⇒ Array<LLM::Function>
Set or get the default tools
401 402 403 404 405 406 407 408 409 410 |
# File 'lib/llm/agent.rb', line 401 def self.tools(*tools, &block) return @tools || [] if tools.empty? and !block if block @tools = block elsif single_callable?(tools) @tools = LLM.share!(tools.first) else @tools = LLM.share!(tools.flatten) end end |
.confirm(*tool_names, &block) ⇒ Array<String>, ...
Set or get the agent tools that require confirmation.
429 430 431 432 433 434 435 436 437 438 |
# File 'lib/llm/agent.rb', line 429 def self.confirm(*tool_names, &block) return @confirm if tool_names.empty? and !block if block @confirm = LLM.share!(block) elsif single_callable?(tool_names) @confirm = LLM.share!(tool_names.first) else @confirm = LLM.share!(tool_names.flatten.map(&:to_s)) end end |
.registry ⇒ LLM::Agent::Registry
The registry holds all active instances of LLM::Agent and inactive agents are automatically removed from the registry. The registry is local to a process, and its children.
Returns an instance of LLM::Agent::Registry.
101 102 103 |
# File 'lib/llm/agent.rb', line 101 def self.registry REGISTRY end |
Instance Method Details
#on_tool_confirmation(fn, strategy) ⇒ LLM::Function::Return
This method is called when confirmation is required before a tool can run.
837 838 839 |
# File 'lib/llm/agent.rb', line 837 def on_tool_confirmation(fn, strategy) fn.cancel end |
#name ⇒ String
Returns the agent's name
491 492 493 |
# File 'lib/llm/agent.rb', line 491 def name @name end |
#path ⇒ String?
Returns a file path where an agent's memory is restored from, and written to after each turn.
499 500 501 |
# File 'lib/llm/agent.rb', line 499 def path @path end |
#description ⇒ String?
Returns the agent's description
506 507 508 |
# File 'lib/llm/agent.rb', line 506 def description @description end |
#record ⇒ Object?
Returns the ORM record this agent is bound to, or nil.
513 514 515 |
# File 'lib/llm/agent.rb', line 513 def record @record end |
#talk(prompt, params = {}) ⇒ LLM::Response
Maintain a conversation via the chat completions API. This method immediately sends a request to the LLM and returns the response.
534 535 536 537 538 |
# File 'lib/llm/agent.rb', line 534 def talk(prompt, params = {}) res = run_loop(prompt, params, :talk) path ? @ctx.save(path:) : nil res end |
#ask(prompt, params = {}) ⇒ Object
542 543 544 545 546 |
# File 'lib/llm/agent.rb', line 542 def ask(prompt, params = {}) res = run_loop(prompt, params, :ask) path ? @ctx.save(path:) : nil res end |
#id ⇒ String
Returns a stable id for this agent. The id is borrowed from the context it wraps, so it survives save and restore.
558 559 560 |
# File 'lib/llm/agent.rb', line 558 def id @ctx.id end |
#created_at ⇒ Time?
Returns the time this agent was created. It is derived from the context id, so it is not persisted separately.
566 567 568 |
# File 'lib/llm/agent.rb', line 566 def created_at @ctx.created_at end |
#retry_budget ⇒ Integer
572 573 574 |
# File 'lib/llm/agent.rb', line 572 def retry_budget @ctx.retry_budget end |
#pending_functions ⇒ Array<LLM::Function>
578 579 580 |
# File 'lib/llm/agent.rb', line 578 def pending_functions @tracer ? @llm.with_tracer(@tracer) { @ctx.pending_functions } : @ctx.pending_functions end |
#returns ⇒ Array<LLM::Function::Return>
585 586 587 |
# File 'lib/llm/agent.rb', line 585 def returns @ctx.returns end |
#wait ⇒ Array<LLM::Function::Return>
592 593 594 |
# File 'lib/llm/agent.rb', line 592 def wait(...) @tracer ? @llm.with_tracer(@tracer) { @ctx.wait(...) } : @ctx.wait(...) end |
#token_usage ⇒ LLM::Usage Also known as: usage
See also: Context#token_usage
599 600 601 |
# File 'lib/llm/agent.rb', line 599 def token_usage @ctx.token_usage end |
#context_used ⇒ Integer?
See also: Context#context_used
607 608 609 |
# File 'lib/llm/agent.rb', line 607 def context_used @ctx.context_used end |
#context_usage ⇒ Rational?
See also: Context#context_usage
614 615 616 |
# File 'lib/llm/agent.rb', line 614 def context_usage @ctx.context_usage end |
#interrupt! ⇒ nil Also known as: cancel!
Interrupt the active request, if any.
621 622 623 |
# File 'lib/llm/agent.rb', line 621 def interrupt! @ctx.interrupt! end |
#prompt(&b) ⇒ LLM::Prompt Also known as: build_prompt
630 631 632 |
# File 'lib/llm/agent.rb', line 630 def prompt(&b) @ctx.prompt(&b) end |
#image_url(url) ⇒ LLM::Object
Returns a tagged object
640 641 642 |
# File 'lib/llm/agent.rb', line 640 def image_url(url) @ctx.image_url(url) end |
#local_file(path) ⇒ LLM::Object
Returns a tagged object
649 650 651 |
# File 'lib/llm/agent.rb', line 649 def local_file(path) @ctx.local_file(path) end |
#remote_file(res) ⇒ LLM::Object
Returns a tagged object
658 659 660 |
# File 'lib/llm/agent.rb', line 658 def remote_file(res) @ctx.remote_file(res) end |
#tracer ⇒ LLM::Tracer
Returns an LLM tracer
665 666 667 |
# File 'lib/llm/agent.rb', line 665 def tracer @tracer || @ctx.tracer end |
#tracer=(other)
This method returns an undefined value.
673 674 675 676 |
# File 'lib/llm/agent.rb', line 673 def tracer=(other) @ctx.tracer = other @tracer = other end |
#stream ⇒ LLM::Stream, ...
Returns a stream object, or nil
681 682 683 |
# File 'lib/llm/agent.rb', line 681 def stream @ctx.stream end |
#model ⇒ String
Returns the model an Agent is actively using
688 689 690 |
# File 'lib/llm/agent.rb', line 688 def model @ctx.model end |
#mode ⇒ Symbol
694 695 696 |
# File 'lib/llm/agent.rb', line 694 def mode @ctx.mode end |
#concurrency ⇒ Symbol, ...
Returns the configured tool execution concurrency.
701 702 703 |
# File 'lib/llm/agent.rb', line 701 def concurrency @concurrency end |
#context_window ⇒ Integer
715 716 717 |
# File 'lib/llm/agent.rb', line 715 def context_window @ctx.context_window end |
#registry ⇒ LLM::Registry
722 723 724 |
# File 'lib/llm/agent.rb', line 722 def registry @ctx.registry end |
#compacted? ⇒ Boolean
729 730 731 |
# File 'lib/llm/agent.rb', line 729 def compacted? @ctx.compacted? end |
#compactor ⇒ Class<LLM::Compactor>
736 737 738 |
# File 'lib/llm/agent.rb', line 736 def compactor @ctx.compactor end |
#compactor_options ⇒ Hash
743 744 745 |
# File 'lib/llm/agent.rb', line 743 def @ctx. end |
#console(name: self.name, path: nil, tools: [], tracer: false) Also known as: repl
This method returns an undefined value.
Start an agent console.
This method requires the following gems to be installed and available to require: 'unicode-display_width', 'kramdown', 'curses', 'test-cmd.rb', and 'xchan.rb'.
768 769 770 771 772 773 774 775 776 777 778 779 |
# File 'lib/llm/agent.rb', line 768 def console(name: self.name, path: nil, tools: [], tracer: false) if !tracer previous = self.tracer self.tracer = nil end require_relative "console" unless defined?(::LLM::Console) LLM::Console.new(agent: self, name:, path:, tools:).start ensure if !tracer self.tracer = previous end end |
#params ⇒ Hash
785 786 787 |
# File 'lib/llm/agent.rb', line 785 def params @ctx.params end |
#to_h ⇒ Hash
792 793 794 |
# File 'lib/llm/agent.rb', line 792 def to_h @ctx.to_h end |
#to_json ⇒ String
798 799 800 |
# File 'lib/llm/agent.rb', line 798 def to_json(...) LLM.json.dump(to_h, ...) end |
#inspect ⇒ String
804 805 806 807 |
# File 'lib/llm/agent.rb', line 804 def inspect "#<#{LLM::Utils.object_id(self)} " \ "@llm=#{@llm.class}, @mode=#{mode.inspect}, @messages=#{.inspect}>" end |
#serialize(**kw) Also known as: save
This method returns an undefined value.
812 813 814 |
# File 'lib/llm/agent.rb', line 812 def serialize(**kw) @ctx.serialize(**kw) end |
#deserialize(**kw) ⇒ LLM::Agent Also known as: restore
820 821 822 823 |
# File 'lib/llm/agent.rb', line 820 def deserialize(**kw) @ctx.deserialize(**kw) self end |