Class: Spurline::Tools::Idempotency::KeyComputer

Inherits:
Object
  • Object
show all
Defined in:
lib/spurline/tools/idempotency.rb

Overview

Computes idempotency keys from tool name and arguments.

Class Method Summary collapse

Class Method Details

.canonical_hash(args) ⇒ Object

Produces a deterministic hash of arguments. Sorts keys recursively for canonical representation.



39
40
41
42
# File 'lib/spurline/tools/idempotency.rb', line 39

def self.canonical_hash(args)
  json = JSON.generate(canonicalize(args))
  Digest::SHA256.hexdigest(json)
end

.canonicalize(obj) ⇒ Object

Recursively sorts hash keys for deterministic serialization.



45
46
47
48
49
50
51
52
53
54
# File 'lib/spurline/tools/idempotency.rb', line 45

def self.canonicalize(obj)
  case obj
  when Hash
    obj.sort_by { |k, _| k.to_s }.map { |k, v| [k.to_s, canonicalize(v)] }.to_h
  when Array
    obj.map { |v| canonicalize(v) }
  else
    obj
  end
end

.compute(tool_name:, args:, key_params: nil, key_fn: nil) ⇒ String

Computes a deterministic key for a tool call.

Key computation:

1. If key_fn provided: "#{tool_name}:#{key_fn.call(args)}"
2. If key_params provided: SHA256 of only those params
3. Default: SHA256 of all args (canonical JSON with sorted keys)

Parameters:

  • tool_name (Symbol)

    Tool identifier

  • args (Hash)

    Tool call arguments

  • key_params (Array<Symbol>, nil) (defaults to: nil)

    Specific params to include (nil = all)

  • key_fn (Proc, nil) (defaults to: nil)

    Custom key computation lambda

Returns:

  • (String)

    Deterministic key string “tool_name:hash”



23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/spurline/tools/idempotency.rb', line 23

def self.compute(tool_name:, args:, key_params: nil, key_fn: nil)
  prefix = tool_name.to_s

  hash = if key_fn
    key_fn.call(args).to_s
  elsif key_params
    canonical_hash(args.slice(*key_params))
  else
    canonical_hash(args)
  end

  "#{prefix}:#{hash}"
end