Class: TurnKit::Agent

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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name:, description: "", model: nil, instructions: "", tools: [], skills: [], available_skills: [], sub_agents: [], system_prompt: nil, prompt_sections: nil, prompt_mode: nil, client: nil, store: nil, max_iterations: nil, timeout: nil, cost_limit: nil, max_depth: nil, max_tool_executions: nil, thinking: nil) ⇒ Agent

Returns a new instance of Agent.

Raises:

  • (ArgumentError)


9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/turnkit/agent.rb', line 9

def initialize(name:, description: "", model: nil, instructions: "", tools: [], skills: [], available_skills: [], sub_agents: [],
  system_prompt: nil, prompt_sections: nil, prompt_mode: nil, client: nil, store: nil,
  max_iterations: nil, timeout: nil, cost_limit: nil, max_depth: nil, max_tool_executions: nil, thinking: nil)
  @name = name.to_s
  @description = description.to_s
  @model = model
  @instructions = instructions.to_s
  @tools = Array(tools)
  @skills = Array(skills)
  @available_skills = Array(available_skills)
  @sub_agents = Array(sub_agents)
  @system_prompt = system_prompt
  @prompt_sections = prompt_sections
  @prompt_mode = prompt_mode&.to_sym
  @client = client
  @store = store
  @max_iterations = max_iterations
  @timeout = timeout
  @cost_limit = cost_limit
  @max_depth = max_depth
  @max_tool_executions = max_tool_executions
  @thinking = self.class.normalize_thinking(thinking)
  raise ArgumentError, "name is required" if @name.empty?
end

Instance Attribute Details

#available_skillsObject (readonly)

Returns the value of attribute available_skills.



5
6
7
# File 'lib/turnkit/agent.rb', line 5

def available_skills
  @available_skills
end

#clientObject (readonly)

Returns the value of attribute client.



6
7
8
# File 'lib/turnkit/agent.rb', line 6

def client
  @client
end

#cost_limitObject (readonly)

Returns the value of attribute cost_limit.



6
7
8
# File 'lib/turnkit/agent.rb', line 6

def cost_limit
  @cost_limit
end

#descriptionObject (readonly)

Returns the value of attribute description.



5
6
7
# File 'lib/turnkit/agent.rb', line 5

def description
  @description
end

#instructionsObject (readonly)

Returns the value of attribute instructions.



5
6
7
# File 'lib/turnkit/agent.rb', line 5

def instructions
  @instructions
end

#max_depthObject (readonly)

Returns the value of attribute max_depth.



6
7
8
# File 'lib/turnkit/agent.rb', line 6

def max_depth
  @max_depth
end

#max_iterationsObject (readonly)

Returns the value of attribute max_iterations.



6
7
8
# File 'lib/turnkit/agent.rb', line 6

def max_iterations
  @max_iterations
end

#max_tool_executionsObject (readonly)

Returns the value of attribute max_tool_executions.



6
7
8
# File 'lib/turnkit/agent.rb', line 6

def max_tool_executions
  @max_tool_executions
end

#modelObject (readonly)

Returns the value of attribute model.



5
6
7
# File 'lib/turnkit/agent.rb', line 5

def model
  @model
end

#nameObject (readonly)

Returns the value of attribute name.



5
6
7
# File 'lib/turnkit/agent.rb', line 5

def name
  @name
end

#prompt_modeObject (readonly)

Returns the value of attribute prompt_mode.



7
8
9
# File 'lib/turnkit/agent.rb', line 7

def prompt_mode
  @prompt_mode
end

#prompt_sectionsObject (readonly)

Returns the value of attribute prompt_sections.



7
8
9
# File 'lib/turnkit/agent.rb', line 7

def prompt_sections
  @prompt_sections
end

#skillsObject (readonly)

Returns the value of attribute skills.



5
6
7
# File 'lib/turnkit/agent.rb', line 5

def skills
  @skills
end

#storeObject (readonly)

Returns the value of attribute store.



6
7
8
# File 'lib/turnkit/agent.rb', line 6

def store
  @store
end

#sub_agentsObject (readonly)

Returns the value of attribute sub_agents.



5
6
7
# File 'lib/turnkit/agent.rb', line 5

def sub_agents
  @sub_agents
end

#system_promptObject (readonly)

Returns the value of attribute system_prompt.



7
8
9
# File 'lib/turnkit/agent.rb', line 7

def system_prompt
  @system_prompt
end

#thinkingObject (readonly)

Returns the value of attribute thinking.



7
8
9
# File 'lib/turnkit/agent.rb', line 7

def thinking
  @thinking
end

#timeoutObject (readonly)

Returns the value of attribute timeout.



6
7
8
# File 'lib/turnkit/agent.rb', line 6

def timeout
  @timeout
end

#toolsObject (readonly)

Returns the value of attribute tools.



5
6
7
# File 'lib/turnkit/agent.rb', line 5

def tools
  @tools
end

Class Method Details

.normalize_thinking(value) ⇒ Object

Raises:

  • (ArgumentError)


34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/turnkit/agent.rb', line 34

def self.normalize_thinking(value)
  return nil if value.nil?

  attrs = value.respond_to?(:to_h) ? value.to_h : value
  raise ArgumentError, "thinking must be a hash" unless attrs.is_a?(Hash)

  attrs = attrs.transform_keys(&:to_sym)
  unknown = attrs.keys - %i[effort budget]
  raise ArgumentError, "unknown thinking attributes: #{unknown.join(", ")}" if unknown.any?
  raise ArgumentError, "thinking requires :effort or :budget" if attrs[:effort].nil? && attrs[:budget].nil?
  raise ArgumentError, "thinking budget must be an Integer" if attrs[:budget] && !attrs[:budget].is_a?(Integer)

  attrs.slice(:effort, :budget).compact
end

Instance Method Details

#build_budget(root_started_at: Clock.now) ⇒ Object



115
116
117
118
119
120
121
122
123
124
# File 'lib/turnkit/agent.rb', line 115

def build_budget(root_started_at: Clock.now)
  Budget.new(
    max_iterations: max_iterations || TurnKit.max_iterations,
    timeout: timeout || TurnKit.timeout,
    max_depth: max_depth || TurnKit.max_depth,
    max_tool_executions: max_tool_executions || TurnKit.max_tool_executions,
    cost_limit: cost_limit || TurnKit.cost_limit,
    root_started_at: root_started_at
  )
end

#conversation(model: nil, subject: nil, metadata: {}) ⇒ Object



49
50
51
52
53
54
55
56
57
58
# File 'lib/turnkit/agent.rb', line 49

def conversation(model: nil, subject: nil, metadata: {})
  store = effective_store
  record = store.create_conversation(
    "agent_name" => name,
    "model" => model || effective_model,
    "subject" => subject,
    "metadata" => 
  )
  Conversation.new(agent: self, record: record, store: store, model: model || effective_model, subject: subject, metadata: )
end

#costObject



60
61
62
# File 'lib/turnkit/agent.rb', line 60

def cost
  Cost.from_records(effective_store.list_turns(agent_name: name))
end

#effective_available_skillsObject



88
89
90
# File 'lib/turnkit/agent.rb', line 88

def effective_available_skills
  (Array(TurnKit.available_skills) + available_skills).uniq { |skill| skill.key }
end

#effective_clientObject



76
77
78
# File 'lib/turnkit/agent.rb', line 76

def effective_client
  client || TurnKit.client
end

#effective_modelObject



68
69
70
# File 'lib/turnkit/agent.rb', line 68

def effective_model
  model || TurnKit.default_model
end

#effective_prompt_mode(turn: nil) ⇒ Object



96
97
98
99
100
# File 'lib/turnkit/agent.rb', line 96

def effective_prompt_mode(turn: nil)
  return prompt_mode if prompt_mode

  turn&.depth.to_i.positive? ? :minimal : :full
end

#effective_prompt_sectionsObject



92
93
94
# File 'lib/turnkit/agent.rb', line 92

def effective_prompt_sections
  prompt_sections || TurnKit.prompt_sections
end

#effective_storeObject



80
81
82
# File 'lib/turnkit/agent.rb', line 80

def effective_store
  store || TurnKit.store
end

#effective_thinkingObject



72
73
74
# File 'lib/turnkit/agent.rb', line 72

def effective_thinking
  thinking
end

#effective_toolsObject



84
85
86
# File 'lib/turnkit/agent.rb', line 84

def effective_tools
  tools + sub_agents.map { |agent| SubAgentTool.for(agent) }
end

#instructions_with_skillsObject



126
127
128
129
130
# File 'lib/turnkit/agent.rb', line 126

def instructions_with_skills
  parts = [ instructions ]
  parts << SystemPrompt.loaded_skills_text(skills)
  parts.reject(&:empty?).join("\n\n")
end

#system_prompt_for(turn:, conversation:) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/turnkit/agent.rb', line 102

def system_prompt_for(turn:, conversation:)
  prompt = SystemPrompt.new(agent: self, turn: turn, conversation: conversation, mode: effective_prompt_mode(turn: turn))

  case system_prompt
  when nil
    prompt.to_s
  when String
    system_prompt
  else
    system_prompt.call(prompt).to_s
  end
end

#usageObject



64
65
66
# File 'lib/turnkit/agent.rb', line 64

def usage
  Usage.from_records(effective_store.list_turns(agent_name: name))
end