Expand description
§llm-message-hash
Stable canonical hash of LLM request/message structures.
Two semantically-identical Anthropic requests can produce different
sha256(serde_json::to_string(&req)) results because:
- JSON key order isn’t guaranteed.
cache_controlfields don’t change request semantics but change the bytes.- Optional metadata (
metadata.user_id, etc.) varies between callers.
This crate fixes that. It walks the value tree, sorts keys recursively, drops a configurable set of fields, and sha256s the canonical bytes.
§Quick example
use serde_json::json;
use llm_message_hash::{hash_canonical_hex, HashOpts};
let a = json!({
"model": "claude-sonnet-4-5",
"messages": [{"role": "user", "content": "hi"}],
});
let b = json!({
"messages": [{"content": "hi", "role": "user"}],
"model": "claude-sonnet-4-5",
});
assert_eq!(hash_canonical_hex(&a), hash_canonical_hex(&b));§With per-provider ignore lists
use serde_json::json;
use llm_message_hash::{hash_canonical_hex_with, HashOpts};
let req_a = json!({
"model": "claude-sonnet-4-5",
"messages": [{
"role": "user",
"content": [{"type": "text", "text": "hi", "cache_control": {"type": "ephemeral"}}],
}],
});
let req_b = json!({
"model": "claude-sonnet-4-5",
"messages": [{
"role": "user",
"content": [{"type": "text", "text": "hi"}],
}],
});
// `HashOpts::anthropic()` drops cache_control + metadata.user_id
let h1 = hash_canonical_hex_with(&req_a, &HashOpts::anthropic());
let h2 = hash_canonical_hex_with(&req_b, &HashOpts::anthropic());
assert_eq!(h1, h2);Structs§
- Hash
Opts - Tunables for
hash_canonical_with.
Functions§
- hash_
canonical - sha256 of the canonical JSON representation of
vwith default options (no fields ignored). - hash_
canonical_ hex - Hex-encoded sha256 of the canonical JSON representation of
vwith default options. - hash_
canonical_ hex_ with - Hex-encoded sha256 of the canonical JSON representation of
v, honoringopts. - hash_
canonical_ with - sha256 of the canonical JSON representation of
v, honoringopts.