Skip to contents

A request hash identifies the question asked. It keys on provider, model, the canonical role/content turns, and the generation parameters (model_params minus transport and internal knobs), plus any explicit tool, response-format, or schema signature. It is the key the archive layer uses for deduplication and replay, and the value to record when reproducibility of a specific call matters.

Usage

llm_request_hash(
  config = NULL,
  messages = NULL,
  provider = NULL,
  model = NULL,
  tools = NULL,
  response_format = NULL,
  schema = NULL,
  extra = NULL
)

Arguments

config

An llm_config() (its provider, model, and the answer-relevant entries of model_params are used), or NULL to supply provider/model directly and the generation parameters through extra$params.

messages

The messages or prompt sent. A character scalar, a named character vector of roles, or a list of list(role=, content=) turns – whatever was passed to call_llm(). Canonicalized to a provider-neutral list of role/content turns before hashing, so the message shape (a bare string vs a named vector vs a turn list) does not change the hash; only the roles and text do.

provider, model

Provider and model ids. Taken from config when it is given; supply them directly (with extra$params) when hashing a call read from an audit log, where there is no config.

tools

Optional tool definitions (as passed to call_llm() / bind_tools()). Their signature is included when present.

response_format

Optional response-format directive (e.g. a JSON-mode flag or object) that constrains the output.

schema

Optional JSON Schema used for structured output.

extra

Optional named list folded into the hashed object. The log side passes the call's generation parameters here as extra$params (a named list), which are consumed into the param key exactly as a config's model_params would be; a config, when given, takes precedence over extra$params. Any other entries are hashed as-is, to add identity-relevant fields the standard arguments do not cover.

Value

A 64-character lowercase SHA-256 hex string (see llm_hash()).

Details

The hash is built over a canonical object and passed to llm_hash(), so it inherits that convention: construction order and S3 class do not matter, and the value does not depend on R's serialization format. API keys and volatile fields (timestamps, request ids) are never part of the input. Transport and internal knobs in model_params (see the source for the full list – e.g. req_builder, response_modifier, timeout, api_url, max_tries, request_modifier, cache, use_responses_api, vertex) are excluded, since they change how a call is issued, not what is asked.

Scope

The hash keys on the canonical turns and generation parameters. It is designed so a call described by an llm_config() and the same call read from a logged provider request body agree for the common chat/completions path. It does not attempt to reconstruct every provider's exact translated body: calls that differ only in features outside this key – the OpenAI Responses API (input/instructions), full tool-call transcripts, or provider-injected defaults – may hash the same across the config and log sides. The archive layer's collision detection is the backstop for those cases: it flags when one replay key gathers records whose stored request hashes disagree.

Examples

cfg <- llm_config("openai", "gpt-4.1-mini", temperature = 0)
h1 <- llm_request_hash(cfg, "Hello")
# message order/content matters; temperature matters:
cfg2 <- llm_config("openai", "gpt-4.1-mini", temperature = 1)
identical(h1, llm_request_hash(cfg2, "Hello"))