Class: Silas::Tool
- Inherits:
-
Object
show all
- Defined in:
- lib/silas/tool.rb
Overview
Base class for agent tools. The keyword signature of #call IS the schema:
class Agent::Tools::IssueRefund < Silas::Tool
description "Refund an order."
param :amount, :integer, desc: "Pence" # optional type refinement
approval :always # :never | :once | :always | lambda
transactional! # or at_most_once! (default) / idempotent!
:once approves ONE (tool, arguments) pair per session — an identical repeat
call skips re-approval; different arguments park again. For graded gates
(e.g. auto-approve under a threshold) use a lambda:
approval ->(session:, input:) { input[:amount] > 5000 ? :user_approval : :approved }
def call(order_id:, amount:, note: nil)
...
end
end
Tool identity is the FILENAME (app/agent/tools/issue_refund.rb -> "issue_refund"),
eve's convention, enforced by the Registry.
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
Instance Attribute Details
#session ⇒ Object
Execution context, set by the Ledger before #call.
92
93
94
|
# File 'lib/silas/tool.rb', line 92
def session
@session
end
|
Class Method Details
.approval(policy = nil, &block) ⇒ Object
Also known as:
approval_policy
33
34
35
36
|
# File 'lib/silas/tool.rb', line 33
def approval(policy = nil, &block)
@approval_policy = block || policy unless policy.nil? && block.nil?
@approval_policy || :never
end
|
.at_most_once! ⇒ Object
41
|
# File 'lib/silas/tool.rb', line 41
def at_most_once! = @effect_mode = :at_most_once
|
.description(text = nil) ⇒ Object
24
25
26
27
|
# File 'lib/silas/tool.rb', line 24
def description(text = nil)
@description = text if text
@description || ""
end
|
.effect_mode ⇒ Object
43
|
# File 'lib/silas/tool.rb', line 43
def effect_mode = @effect_mode || :at_most_once
|
.idempotent! ⇒ Object
40
|
# File 'lib/silas/tool.rb', line 40
def idempotent! = @effect_mode = :idempotent
|
.param(name, type = :string, desc: nil) ⇒ Object
29
30
31
|
# File 'lib/silas/tool.rb', line 29
def param(name, type = :string, desc: nil)
param_refinements[name.to_sym] = { type: type.to_s, desc: desc }
end
|
.schema ⇒ Object
description:, input_schema: — provider-agnostic JSON schema.
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
# File 'lib/silas/tool.rb', line 50
def schema
validate_signature!
properties = {}
required = []
instance_method(:call).parameters.each do |kind, pname|
refinement = param_refinements[pname] || {}
properties[pname.to_s] = { "type" => refinement[:type] || "string" }
properties[pname.to_s]["description"] = refinement[:desc] if refinement[:desc]
required << pname.to_s if kind == :keyreq
end
{
"name" => tool_name,
"description" => description,
"input_schema" => {
"type" => "object",
"properties" => properties,
"required" => required
}
}
end
|
45
46
47
|
# File 'lib/silas/tool.rb', line 45
def tool_name
name.demodulize.underscore
end
|
.transactional! ⇒ Object
39
|
# File 'lib/silas/tool.rb', line 39
def transactional! = @effect_mode = :transactional
|
.validate_signature! ⇒ Object
71
72
73
74
75
76
77
78
|
# File 'lib/silas/tool.rb', line 71
def validate_signature!
raise Error, "#{name} must define #call" unless method_defined?(:call)
kinds = instance_method(:call).parameters.map(&:first)
if kinds.any? { |k| %i[req opt rest].include?(k) }
raise Error, "#{name}#call must accept keyword arguments only — the signature is the schema"
end
end
|
Instance Method Details
#approval_policy ⇒ Object
Instance-side delegation so a resolved instance answers the Ledger.
88
|
# File 'lib/silas/tool.rb', line 88
def approval_policy = self.class.approval_policy
|
#call ⇒ Object
94
95
96
|
# File 'lib/silas/tool.rb', line 94
def call(**)
raise NotImplementedError, "#{self.class}#call"
end
|
#effect_mode ⇒ Object
89
|
# File 'lib/silas/tool.rb', line 89
def effect_mode = self.class.effect_mode
|