1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
//! Canonical [`SaltHash`] derivation from a [`crate::Request`].
//!
//! The salt is the *prefix-cache isolation key*. Two requests that should not share
//! cache prefixes must produce different salt hashes; two requests that should share
//! prefixes must produce identical salt hashes.
//!
//! # Router parity
//!
//! For requests with no extra `salt`, this function reproduces the seed used by
//! `dynamo_kv_router::protocols::compute_block_hash_for_seq`
//! (`lib/kv-router/src/protocols.rs:79-82`):
//!
//! ```text
//! (salt=None, lora=None) → CHAIN_XXH3_SEED
//! (salt=None, lora=Some(name)) → CHAIN_XXH3_SEED.wrapping_add(xxh3_64(name))
//! ```
//!
//! Producer events whose `block_hash` is `compute_block_hash(tokens, salt_hash)` therefore
//! match the router's `compute_block_hash_for_seq(tokens, _, BlockHashOptions { lora_name })`
//! byte-for-byte on the no-salt path — required for kv-router's indexers (which key on
//! both `tokens_hash` and the `seq_hash` chain) to find matches against consolidator-emitted
//! events.
//!
//! Multimodal data is **not** part of the salt — it is folded into per-block hashing
//! so that requests with the same image at the same global token position can still
//! share the prefix blocks before the image.
use ;
use crateKvHashingError;
/// Computes the canonical [`SaltHash`] from `(salt, lora_name)`.
///
/// Empty strings (`Some("")`) are normalized to `None` for both `salt` and `lora_name`
/// before hashing. This matches the router's existing behavior at
/// `lib/kv-router/src/protocols.rs:79` (`options.lora_name.filter(|n| !n.is_empty())`)
/// so a client that sends `lora_name = ""` shares the cache with a client that sends
/// `lora_name = None`.
pub