Class: Smith::Tool

Inherits:
RubyLLM::Tool
  • Object
show all
Includes:
BudgetEnforcement, Capture, Policy
Defined in:
lib/smith/tool.rb,
lib/smith/tool/policy.rb,
lib/smith/tool/capture.rb,
lib/smith/tool/compatibility.rb,
lib/smith/tool/budget_enforcement.rb,
lib/smith/tool/capability_builder.rb

Defined Under Namespace

Modules: BudgetEnforcement, Capture, Compatibility, Policy Classes: CapabilityBuilder

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Class Attribute Details

.compatible_with_specObject (readonly)

Returns the value of attribute compatible_with_spec.



41
42
43
# File 'lib/smith/tool.rb', line 41

def compatible_with_spec
  @compatible_with_spec
end

Class Method Details

.authorize(&block) ⇒ Object



97
98
99
100
101
# File 'lib/smith/tool.rb', line 97

def authorize(&block)
  return @authorize unless block_given?

  @authorize = block
end

.before_execute(&block) ⇒ Object



103
104
105
106
107
# File 'lib/smith/tool.rb', line 103

def before_execute(&block)
  return @before_execute unless block_given?

  @before_execute = block
end

.capabilitiesObject



89
90
91
92
93
94
95
# File 'lib/smith/tool.rb', line 89

def capabilities(&)
  return @capabilities unless block_given?

  builder = CapabilityBuilder.new
  builder.instance_eval(&)
  @capabilities = builder.to_h
end

.capture_result(&block) ⇒ Object



109
110
111
112
113
# File 'lib/smith/tool.rb', line 109

def capture_result(&block)
  return @capture_result unless block_given?

  @capture_result = block
end

.category(value = nil) ⇒ Object



83
84
85
86
87
# File 'lib/smith/tool.rb', line 83

def category(value = nil)
  return @category if value.nil?

  @category = value
end

.compatible_with(*providers, except: nil, **provider_endpoints) ⇒ Object

Declarative compatibility DSL. Examples:

compatible_with :anthropic, :gemini
compatible_with :anthropic, :gemini, openai: :responses
compatible_with except: { openai: :chat_completions }

Tools that NEVER declare compatible_with are universally compatible. Consumed by Smith::Models::Normalizer.drop_incompatible_tools when the resolved model rejects the (tools + thinking) combo and no routing fallback (e.g., openai_api_mode :auto) is available.



37
38
39
# File 'lib/smith/tool.rb', line 37

def compatible_with(*providers, except: nil, **provider_endpoints)
  @compatible_with_spec = Compatibility.parse(providers, except: except, **provider_endpoints)
end

.current_deadlineObject



51
52
53
# File 'lib/smith/tool.rb', line 51

def current_deadline
  Thread.current[:smith_tool_deadline]
end

.current_deadline=(value) ⇒ Object



55
56
57
# File 'lib/smith/tool.rb', line 55

def current_deadline=(value)
  Thread.current[:smith_tool_deadline] = value
end

.current_guardrailsObject



43
44
45
# File 'lib/smith/tool.rb', line 43

def current_guardrails
  Thread.current[:smith_tool_guardrails]
end

.current_guardrails=(value) ⇒ Object



47
48
49
# File 'lib/smith/tool.rb', line 47

def current_guardrails=(value)
  Thread.current[:smith_tool_guardrails] = value
end

.current_ledgerObject



59
60
61
# File 'lib/smith/tool.rb', line 59

def current_ledger
  Thread.current[:smith_tool_ledger]
end

.current_ledger=(value) ⇒ Object



63
64
65
# File 'lib/smith/tool.rb', line 63

def current_ledger=(value)
  Thread.current[:smith_tool_ledger] = value
end

.current_tool_call_allowanceObject



67
68
69
# File 'lib/smith/tool.rb', line 67

def current_tool_call_allowance
  Thread.current[:smith_tool_call_allowance]
end

.current_tool_call_allowance=(value) ⇒ Object



71
72
73
# File 'lib/smith/tool.rb', line 71

def current_tool_call_allowance=(value)
  Thread.current[:smith_tool_call_allowance] = value
end

.current_tool_result_collectorObject



75
76
77
# File 'lib/smith/tool.rb', line 75

def current_tool_result_collector
  Thread.current[:smith_tool_result_collector]
end

.current_tool_result_collector=(value) ⇒ Object



79
80
81
# File 'lib/smith/tool.rb', line 79

def current_tool_result_collector=(value)
  Thread.current[:smith_tool_result_collector] = value
end

.inherited(subclass) ⇒ Object

Tool subclasses inherit the parent’s compatible_with spec by reference (the spec is a frozen Hash; immutability makes shared references safe). Subclasses can override by calling ‘compatible_with` again — assigns a NEW frozen Hash to its own @compatible_with_spec, leaving the parent untouched.



23
24
25
26
# File 'lib/smith/tool.rb', line 23

def inherited(subclass)
  super
  subclass.instance_variable_set(:@compatible_with_spec, @compatible_with_spec)
end

Instance Method Details

#execute(**kwargs) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/smith/tool.rb', line 116

def execute(**kwargs)
  run_before_execute_hook!(kwargs)
  check_tool_deadline!
  check_privilege!(kwargs)
  check_authorization!(kwargs)
  run_tool_guardrails!(kwargs)
  check_tool_deadline!
  charge_tool_call!

  start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  result = perform(**kwargs)
  duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start

  emit_tool_trace(kwargs, result, duration)
  capture_result_if_configured(kwargs, result)
  result
end