Class: Omnibot::Agent

Inherits:
Object
  • Object
show all
Defined in:
lib/omnibot/agent.rb

Defined Under Namespace

Classes: TurnLimit

Constant Summary collapse

FAST_REPLY =
:__omnibot_fast_reply

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(context = {}) ⇒ Agent

Returns a new instance of Agent.



51
52
53
# File 'lib/omnibot/agent.rb', line 51

def initialize(context = {})
  @context = context
end

Instance Attribute Details

#contextObject (readonly)

Returns the value of attribute context.



49
50
51
# File 'lib/omnibot/agent.rb', line 49

def context
  @context
end

Class Method Details

.extract(input, schema:, context: {}) ⇒ Object



44
45
46
# File 'lib/omnibot/agent.rb', line 44

def extract(input, schema:, context: {})
  new(context).extract(input, schema: schema)
end

.fast_path(&block) ⇒ Object



29
# File 'lib/omnibot/agent.rb', line 29

def fast_path(&block) = fast_paths << block

.fast_pathsObject



28
# File 'lib/omnibot/agent.rb', line 28

def fast_paths = @fast_paths ||= []

.inherited(subclass) ⇒ Object



31
32
33
34
35
36
37
38
# File 'lib/omnibot/agent.rb', line 31

def inherited(subclass)
  super
  subclass.instance_variable_set(:@model, @model)
  subclass.instance_variable_set(:@instructions, @instructions)
  subclass.instance_variable_set(:@max_turns, @max_turns)
  subclass.instance_variable_set(:@tools, tools.dup)
  subclass.instance_variable_set(:@fast_paths, fast_paths.dup)
end

.instructions(value = nil) ⇒ Object



12
13
14
# File 'lib/omnibot/agent.rb', line 12

def instructions(value = nil)
  value ? @instructions = value : @instructions
end

.max_turns(value = nil) ⇒ Object

Bounds the number of tool executions in a run, not conversation rounds — the before_tool_call hook counts each call, so parallel tool calls in a single round each count separately.



19
20
21
# File 'lib/omnibot/agent.rb', line 19

def max_turns(value = nil)
  value ? @max_turns = value : (@max_turns || 5)
end

.model(value = nil) ⇒ Object



8
9
10
# File 'lib/omnibot/agent.rb', line 8

def model(value = nil)
  value ? @model = value : (@model || Omnibot.config.default_model)
end

.run(message, history: [], context: {}, stream: nil) ⇒ Object



40
41
42
# File 'lib/omnibot/agent.rb', line 40

def run(message, history: [], context: {}, stream: nil)
  new(context).run(message, history: history, stream: stream)
end

.tool(name_or_class, description = nil, &block) ⇒ Object



23
24
25
# File 'lib/omnibot/agent.rb', line 23

def tool(name_or_class, description = nil, &block)
  tools << (block ? Tool.from_block(name_or_class, description, &block) : name_or_class)
end

.toolsObject



27
# File 'lib/omnibot/agent.rb', line 27

def tools = @tools ||= []

Instance Method Details

#extract(input, schema:) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/omnibot/agent.rb', line 71

def extract(input, schema:)
  chat = Omnibot.chat_factory.call(model: self.class.model, agent_class: self.class)
  chat.with_instructions(interpolated_instructions) if self.class.instructions
  chat.with_schema(schema)

  response = instrument_llm { chat.ask(input.to_s) }
  parse_extraction(response.content) do |error|
    repair = instrument_llm do
      chat.ask("Your previous output was not valid JSON (#{error.message}). " \
                "Respond again with ONLY valid JSON matching the schema.")
    end
    parse_extraction(repair.content) do |_error|
      raise ExtractionError, "extraction failed after repair: #{repair.content.to_s.truncate(200)}"
    end
  end
end

#reply(text) ⇒ Object

Called from within a fast_path block to short-circuit the run loop.



69
# File 'lib/omnibot/agent.rb', line 69

def reply(text) = throw(FAST_REPLY, text)

#run(message, history: [], stream: nil) ⇒ Object



55
56
57
58
59
60
61
62
63
# File 'lib/omnibot/agent.rb', line 55

def run(message, history: [], stream: nil)
  ActiveSupport::Notifications.instrument("omnibot.agent.run", agent: self.class) do |payload|
    payload[:fast_path] = false
    result = run_loop(message, history, stream)
    payload[:fast_path] = result.fast_path?
    payload[:usage] = result.usage
    result
  end
end

#tools_for(_context) ⇒ Object

Override in subclasses to gate tools per run (context-aware).



66
# File 'lib/omnibot/agent.rb', line 66

def tools_for(_context) = self.class.tools