module RubyLLM::AgUI::Canonical

Deterministic serialization and hashing.

Run identity depends on comparing an input with the one a retry sends. JSON object member order is not significant, so a naive hash of the raw bytes treats the same request as two. Canonical.dump sorts object keys recursively (like RubyLLM’s ToolCall#arguments, so a call reloaded from a database hashes the same), producing a stable string to digest.

Public Instance Methods

digest(value) click to toggle source

The SHA-256 hex digest of the canonical form.

# File lib/ruby_llm/ag_ui/canonical.rb, line 22
def digest(value)
  Digest::SHA256.hexdigest(dump(value))
end
dump(value) click to toggle source

A deterministic JSON string: object keys sorted by their byte length then value, arrays kept in order.

# File lib/ruby_llm/ag_ui/canonical.rb, line 17
def dump(value)
  JSON.generate(sort(value))
end
sort(value) click to toggle source

A recursively key-sorted copy of value.

# File lib/ruby_llm/ag_ui/canonical.rb, line 27
def sort(value)
  case value
  when Hash
    value.sort_by { |key, _| [key.to_s.bytesize, key.to_s] }
         .to_h { |key, nested| [key.to_s, sort(nested)] }
  when Array
    value.map { |item| sort(item) }
  else
    value
  end
end