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, max_spend: nil, max_depth: nil, max_tool_executions: nil, max_tool_executions_by_name: nil, thinking: nil, compaction: nil, output_schema: nil, input_schema: nil, output_policy: nil, output_policy_mode: nil, output_policy_model: nil, output_policy_thinking: nil, output_retries: 0, on_event: nil) ⇒ Agent

Returns a new instance of Agent.

Raises:

  • (ArgumentError)


10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/turnkit/agent.rb', line 10

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, max_spend: nil, max_depth: nil, max_tool_executions: nil, max_tool_executions_by_name: nil, thinking: nil, compaction: nil,
  output_schema: nil, input_schema: nil, output_policy: nil, output_policy_mode: nil, output_policy_model: nil, output_policy_thinking: nil, output_retries: 0, on_event: 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
  @max_spend = max_spend
  @max_depth = max_depth
  @max_tool_executions = max_tool_executions
  @max_tool_executions_by_name = max_tool_executions_by_name
  @thinking = self.class.normalize_thinking(thinking)
  @compaction = compaction
  @output_schema = output_schema
  @input_schema = input_schema
  @output_policy_model = output_policy_model
  @output_policy = normalize_output_policy(output_policy, model: output_policy_model, thinking: output_policy_thinking)
  @output_policy_mode = normalize_output_policy_mode(output_policy_mode)
  @output_retries = Integer(output_retries || 0)
  @on_event = on_event
  raise ArgumentError, "name is required" if @name.empty?
  validate_tools!
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

#compactionObject (readonly)

Returns the value of attribute compaction.



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

def compaction
  @compaction
end

#descriptionObject (readonly)

Returns the value of attribute description.



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

def description
  @description
end

#input_schemaObject (readonly)

Returns the value of attribute input_schema.



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

def input_schema
  @input_schema
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_spendObject (readonly)

Returns the value of attribute max_spend.



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

def max_spend
  @max_spend
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

#max_tool_executions_by_nameObject (readonly)

Returns the value of attribute max_tool_executions_by_name.



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

def max_tool_executions_by_name
  @max_tool_executions_by_name
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

#on_eventObject (readonly)

Returns the value of attribute on_event.



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

def on_event
  @on_event
end

#output_policyObject (readonly)

Returns the value of attribute output_policy.



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

def output_policy
  @output_policy
end

#output_policy_modeObject (readonly)

Returns the value of attribute output_policy_mode.



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

def output_policy_mode
  @output_policy_mode
end

#output_policy_modelObject (readonly)

Returns the value of attribute output_policy_model.



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

def output_policy_model
  @output_policy_model
end

#output_retriesObject (readonly)

Returns the value of attribute output_retries.



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

def output_retries
  @output_retries
end

#output_schemaObject (readonly)

Returns the value of attribute output_schema.



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

def output_schema
  @output_schema
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)


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

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



154
155
156
157
158
159
160
161
162
163
164
# File 'lib/turnkit/agent.rb', line 154

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,
    max_tool_executions_by_name: max_tool_executions_by_name || TurnKit.max_tool_executions_by_name,
    max_spend: max_spend || TurnKit.max_spend,
    root_started_at: root_started_at
  )
end

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



61
62
63
64
65
66
67
68
69
70
# File 'lib/turnkit/agent.rb', line 61

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



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

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

#effective_available_skillsObject



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

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

#effective_clientObject



109
110
111
# File 'lib/turnkit/agent.rb', line 109

def effective_client
  client || TurnKit.client
end

#effective_modelObject



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

def effective_model
  model || TurnKit.default_model
end

#effective_on_eventObject



123
124
125
# File 'lib/turnkit/agent.rb', line 123

def effective_on_event
  on_event || TurnKit.on_event
end

#effective_output_policyObject



105
106
107
# File 'lib/turnkit/agent.rb', line 105

def effective_output_policy
  Array(output_policy).compact
end

#effective_prompt_mode(turn: nil) ⇒ Object



135
136
137
138
139
# File 'lib/turnkit/agent.rb', line 135

def effective_prompt_mode(turn: nil)
  return prompt_mode if prompt_mode

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

#effective_prompt_sectionsObject



131
132
133
# File 'lib/turnkit/agent.rb', line 131

def effective_prompt_sections
  prompt_sections || TurnKit.prompt_sections
end

#effective_storeObject



113
114
115
# File 'lib/turnkit/agent.rb', line 113

def effective_store
  store || TurnKit.store
end

#effective_thinkingObject



101
102
103
# File 'lib/turnkit/agent.rb', line 101

def effective_thinking
  thinking
end

#effective_toolsObject



117
118
119
120
121
# File 'lib/turnkit/agent.rb', line 117

def effective_tools
  configured = tools + sub_agents.map { |agent| SubAgentTool.for(agent) }
  skills = effective_available_skills
  skills.empty? ? configured : configured + [ LoadSkillTool.for(skills) ]
end

#instructions_with_skillsObject



166
167
168
169
170
# File 'lib/turnkit/agent.rb', line 166

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

#run(prompt = nil, task: nil, input: nil, async: false, subject: nil, metadata: {}, parent_run: nil, root_turn_id: nil, prompt_mode: :task, **options) ⇒ Object

Raises:

  • (ArgumentError)


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

def run(prompt = nil, task: nil, input: nil, async: false, subject: nil, metadata: {}, parent_run: nil, root_turn_id: nil, prompt_mode: :task, **options)
  task = task || prompt
  raise ArgumentError, "task is required" if task.to_s.empty?
  SchemaCheck.validate!(input, input_schema, error_class: InputError, label: "input") if input_schema

  conversation = self.conversation(subject: subject, metadata: )
  message = conversation.say(task_message(task, input), metadata: { "source" => "application", "task" => true })
  turn = conversation.build_turn(
    trigger_message_id: message.id,
    root_turn_id: root_turn_id || parent_run_root_turn_id(parent_run),
    prompt_mode: prompt_mode,
    **options
  )
  run = Run.new(turn)
  async ? run : run.run!
end

#system_prompt_for(turn:, conversation:) ⇒ Object



141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/turnkit/agent.rb', line 141

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



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

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