Module: Smith::Tool::ScopedContext

Included in:
Smith::Tool
Defined in:
lib/smith/tool/scoped_context.rb

Constant Summary collapse

CONTEXT_KEYS =
{
  current_guardrails: :smith_tool_guardrails,
  current_deadline: :smith_tool_deadline,
  current_ledger: :smith_tool_ledger,
  current_tool_call_allowance: :smith_tool_call_allowance,
  current_tool_execution_tracker: :smith_tool_execution_tracker,
  current_tool_dispatch_claim: :smith_tool_dispatch_claim,
  current_tool_dispatch_start_handler: :smith_tool_dispatch_start_handler,
  current_tool_result_collector: :smith_tool_result_collector,
  current_invocation_context: :smith_tool_invocation_context,
  current_invocation_batch_admitter: :smith_tool_invocation_batch_admitter,
  current_invocation_failure_handler: :smith_tool_invocation_failure_handler,
  current_invocation_sequence: :smith_tool_invocation_sequence,
  current_invocation: :smith_tool_invocation
}.freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.around(values, &block) ⇒ Object

Raises:

  • (ArgumentError)


43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/smith/tool/scoped_context.rb', line 43

def self.around(values, &block)
  raise ArgumentError, "block required" unless block

  validate!(values)
  previous = capture
  Thread.handle_interrupt(Object => :never) do
    install(values)
    begin
      Thread.handle_interrupt(Object => :immediate, &block)
    ensure
      install(previous)
    end
  end
end

.captureObject



39
40
41
# File 'lib/smith/tool/scoped_context.rb', line 39

def self.capture
  CONTEXT_KEYS.to_h { |reader, key| [reader, Thread.current[key]] }.freeze
end

Instance Method Details

#with_call_budget(budget, on_exhaustion: :raise, &block) ⇒ Object

Raises:

  • (ArgumentError)


81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/smith/tool/scoped_context.rb', line 81

def with_call_budget(budget, on_exhaustion: :raise, &block)
  raise ArgumentError, "block required" unless block

  previous = current_tool_call_allowance
  if previous && !previous.is_a?(CallAllowance)
    raise ArgumentError, "a scoped tool call budget cannot nest under a legacy Hash tool call allowance"
  end

  allowance = previous ? previous.scope(budget, on_exhaustion:) : CallAllowance.new(budget, on_exhaustion:)
  Thread.handle_interrupt(Object => :never) do
    self.current_tool_call_allowance = allowance
    begin
      Thread.handle_interrupt(Object => :immediate, &block)
    ensure
      self.current_tool_call_allowance = previous
    end
  end
end

#with_invocation_context(value, invocation_sequence: InvocationSequence.new, batch_admitter: nil, failure_handler: nil, &block) ⇒ Object

Raises:

  • (ArgumentError)


58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/smith/tool/scoped_context.rb', line 58

def with_invocation_context(value, invocation_sequence: InvocationSequence.new, batch_admitter: nil,
                            failure_handler: nil, &block)
  raise ArgumentError, "block required" unless block
  unless invocation_sequence.is_a?(InvocationSequence)
    raise ArgumentError, "invocation sequence must be a Smith::Tool::InvocationSequence"
  end

  validate_callback!(batch_admitter, "batch admitter")
  validate_callback!(failure_handler, "failure handler")
  if batch_admitter && !failure_handler
    raise ArgumentError, "invocation failure handler is required when a batch admitter is configured"
  end

  values = ScopedContext.capture.merge(
    current_invocation_context: value,
    current_invocation_batch_admitter: batch_admitter,
    current_invocation_failure_handler: failure_handler,
    current_invocation_sequence: invocation_sequence,
    current_invocation: nil
  ).freeze
  ScopedContext.around(values, &block)
end