Skip to main content

Stable canonical sha256 hash of LLM request/message structures. Recursive key-sorted JSON canonicalization with per-provider presets that drop noise fields. For cache keys and idempotency. Zero runtime deps.

Project description

llm-message-hash-py

PyPI Python License: MIT

Stable canonical sha256 hash of LLM request/message structures.

Two semantically identical Anthropic requests can produce different sha256(json.dumps(req)) results because Python dict iteration order is not part of the value, and fields like cache_control change the bytes without changing what gets sent to the model. This library walks the value tree, sorts dict keys recursively, drops a configurable set of fields, and sha256s the canonical bytes.

Useful for prompt-cache lookups, idempotency keys, and dedupe.

Sibling to the Rust crate llm-message-hash.

Install

pip install llm-message-hash-py

Use

Default (no fields dropped):

from llm_message_hash import hash_request

a = {"model": "claude", "messages": [{"role": "user", "content": "hi"}]}
b = {"messages": [{"content": "hi", "role": "user"}], "model": "claude"}

assert hash_request(a) == hash_request(b)

Per-provider preset (drops cache_control, response-only fields, etc.):

from llm_message_hash import HashOpts, hash_request

with_cc = {
    "messages": [{
        "role": "user",
        "content": [{"type": "text", "text": "hi", "cache_control": {"type": "ephemeral"}}],
    }],
}
without_cc = {
    "messages": [{
        "role": "user",
        "content": [{"type": "text", "text": "hi"}],
    }],
}

h1 = hash_request(with_cc, HashOpts.for_anthropic())
h2 = hash_request(without_cc, HashOpts.for_anthropic())
assert h1 == h2

You can also get the canonical bytes directly:

from llm_message_hash import canonical_json

s = canonical_json({"b": 1, "a": 2})
assert s == '{"a":2,"b":1}'

Presets

Each preset drops the response-side metadata that varies per call plus provider-specific request fields that do not change semantics:

Preset Drops
HashOpts.for_anthropic() cache_control, id, usage, stop_reason, stop_sequence
HashOpts.for_openai() created, id, object, system_fingerprint, usage, finish_reason
HashOpts.for_bedrock() cache_control, usage, stopReason, metrics
HashOpts.for_gemini() usageMetadata, safetyRatings, finishReason

Extend any preset:

opts = HashOpts.for_anthropic()
opts.drop_keys.add("metadata")

Or build your own:

opts = HashOpts(drop_keys={"trace_id", "request_id"})

Drop key behavior

drop_keys matches exact key names at any depth. A key named in drop_keys is removed from every dict it appears in, no matter how deeply nested. List order is preserved (a list is structurally significant). Strings are case sensitive: "hi" and "Hi" hash differently. Numbers compare by their JSON representation: 42 and 42.0 are different strings and so hash differently.

What it does NOT do

  • No tokenization. The hash is over structure, not token count.
  • No semantic equivalence beyond key-order normalization and the drop list.
  • No streaming. Pass a complete Python object.
  • No HTTP. Does not talk to any LLM provider.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

llm_message_hash_py-0.1.0.tar.gz (7.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

llm_message_hash_py-0.1.0-py3-none-any.whl (6.2 kB view details)

Uploaded Python 3

File details

Details for the file llm_message_hash_py-0.1.0.tar.gz.

File metadata

  • Download URL: llm_message_hash_py-0.1.0.tar.gz
  • Upload date:
  • Size: 7.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for llm_message_hash_py-0.1.0.tar.gz
Algorithm Hash digest
SHA256 14a585668783c8fd818e3b447ffe8a5d55c4d40ac44e6fc1037abf2eef55cfe5
MD5 74587f1691b23f8862aac4d1755873ae
BLAKE2b-256 b61b6d9828f00f9ae8872f3217d11557bd87fb77b539b5096ffa6aae4cf0679a

See more details on using hashes here.

File details

Details for the file llm_message_hash_py-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for llm_message_hash_py-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a07dabd2e458d467609f57ddc75a9261e595751864ea18aff7fe58c91d52a51c
MD5 ab687f2080293bbdb6d2858a93157578
BLAKE2b-256 c6717acce332c81d2fcfaeede3698f03cbe84c78dad13fa2e4b6ff286494b390

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page