langsmith 0.4.11 → 0.4.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.cjs CHANGED
@@ -2949,7 +2949,7 @@ class Client {
2949
2949
  return res;
2950
2950
  });
2951
2951
  }
2952
- async createFeedback(runId, key, { score, value, correction, comment, sourceInfo, feedbackSourceType = "api", sourceRunId, feedbackId, feedbackConfig, projectId, comparativeExperimentId, }) {
2952
+ async createFeedback(runId, key, { score, value, correction, comment, sourceInfo, feedbackSourceType = "api", sourceRunId, feedbackId, feedbackConfig, projectId, comparativeExperimentId, sessionId, startTime, }) {
2953
2953
  if (!runId && !projectId) {
2954
2954
  throw new Error("One of runId or projectId must be provided");
2955
2955
  }
@@ -2980,7 +2980,8 @@ class Client {
2980
2980
  feedback_source: feedback_source,
2981
2981
  comparative_experiment_id: comparativeExperimentId,
2982
2982
  feedbackConfig,
2983
- session_id: projectId,
2983
+ session_id: sessionId ?? projectId,
2984
+ start_time: startTime,
2984
2985
  };
2985
2986
  const body = JSON.stringify(feedback);
2986
2987
  const url = `${this.apiUrl}/feedback`;
@@ -3201,6 +3202,8 @@ class Client {
3201
3202
  sourceRunId: res.sourceRunId,
3202
3203
  feedbackConfig: res.feedbackConfig,
3203
3204
  feedbackSourceType: "model",
3205
+ sessionId: run?.session_id,
3206
+ startTime: run?.start_time,
3204
3207
  }));
3205
3208
  }
3206
3209
  return [evalResults, feedbacks];
package/dist/client.d.ts CHANGED
@@ -822,7 +822,7 @@ export declare class Client implements LangSmithTracingClientInterface {
822
822
  exampleIds: string[];
823
823
  remove?: boolean;
824
824
  }): Promise<void>;
825
- createFeedback(runId: string | null, key: string, { score, value, correction, comment, sourceInfo, feedbackSourceType, sourceRunId, feedbackId, feedbackConfig, projectId, comparativeExperimentId, }: {
825
+ createFeedback(runId: string | null, key: string, { score, value, correction, comment, sourceInfo, feedbackSourceType, sourceRunId, feedbackId, feedbackConfig, projectId, comparativeExperimentId, sessionId, startTime, }: {
826
826
  score?: ScoreType;
827
827
  value?: ValueType;
828
828
  correction?: object;
@@ -835,6 +835,10 @@ export declare class Client implements LangSmithTracingClientInterface {
835
835
  eager?: boolean;
836
836
  projectId?: string;
837
837
  comparativeExperimentId?: string;
838
+ /** The session (project) ID of the run this feedback is for. */
839
+ sessionId?: string;
840
+ /** The start time of the run this feedback is for. Accepts ISO string or epoch ms. */
841
+ startTime?: number | string;
838
842
  }): Promise<Feedback>;
839
843
  updateFeedback(feedbackId: string, { score, value, correction, comment, }: {
840
844
  score?: number | boolean | null;
package/dist/client.js CHANGED
@@ -2911,7 +2911,7 @@ export class Client {
2911
2911
  return res;
2912
2912
  });
2913
2913
  }
2914
- async createFeedback(runId, key, { score, value, correction, comment, sourceInfo, feedbackSourceType = "api", sourceRunId, feedbackId, feedbackConfig, projectId, comparativeExperimentId, }) {
2914
+ async createFeedback(runId, key, { score, value, correction, comment, sourceInfo, feedbackSourceType = "api", sourceRunId, feedbackId, feedbackConfig, projectId, comparativeExperimentId, sessionId, startTime, }) {
2915
2915
  if (!runId && !projectId) {
2916
2916
  throw new Error("One of runId or projectId must be provided");
2917
2917
  }
@@ -2942,7 +2942,8 @@ export class Client {
2942
2942
  feedback_source: feedback_source,
2943
2943
  comparative_experiment_id: comparativeExperimentId,
2944
2944
  feedbackConfig,
2945
- session_id: projectId,
2945
+ session_id: sessionId ?? projectId,
2946
+ start_time: startTime,
2946
2947
  };
2947
2948
  const body = JSON.stringify(feedback);
2948
2949
  const url = `${this.apiUrl}/feedback`;
@@ -3163,6 +3164,8 @@ export class Client {
3163
3164
  sourceRunId: res.sourceRunId,
3164
3165
  feedbackConfig: res.feedbackConfig,
3165
3166
  feedbackSourceType: "model",
3167
+ sessionId: run?.session_id,
3168
+ startTime: run?.start_time,
3166
3169
  }));
3167
3170
  }
3168
3171
  return [evalResults, feedbacks];
@@ -176,15 +176,20 @@ async function evaluateComparative(experiments, options) {
176
176
  referenceOutputs: example.outputs || {},
177
177
  })
178
178
  : await evaluator(runs, example);
179
+ // Build a lookup for run metadata
180
+ const runsById = new Map(runs.map((r) => [r.id, r]));
179
181
  for (const [runId, score] of Object.entries(result.scores)) {
180
182
  // validate if the run id
181
183
  if (!expectedRunIds.has(runId)) {
182
184
  throw new Error(`Returning an invalid run id ${runId} from evaluator.`);
183
185
  }
186
+ const run = runsById.get(runId);
184
187
  await client.createFeedback(runId, result.key, {
185
188
  score,
186
189
  sourceRunId: result.source_run_id,
187
190
  comparativeExperimentId: comparativeExperiment.id,
191
+ sessionId: run?.session_id,
192
+ startTime: run?.start_time,
188
193
  });
189
194
  }
190
195
  return result;
@@ -170,15 +170,20 @@ export async function evaluateComparative(experiments, options) {
170
170
  referenceOutputs: example.outputs || {},
171
171
  })
172
172
  : await evaluator(runs, example);
173
+ // Build a lookup for run metadata
174
+ const runsById = new Map(runs.map((r) => [r.id, r]));
173
175
  for (const [runId, score] of Object.entries(result.scores)) {
174
176
  // validate if the run id
175
177
  if (!expectedRunIds.has(runId)) {
176
178
  throw new Error(`Returning an invalid run id ${runId} from evaluator.`);
177
179
  }
180
+ const run = runsById.get(runId);
178
181
  await client.createFeedback(runId, result.key, {
179
182
  score,
180
183
  sourceRunId: result.source_run_id,
181
184
  comparativeExperimentId: comparativeExperiment.id,
185
+ sessionId: run?.session_id,
186
+ startTime: run?.start_time,
182
187
  });
183
188
  }
184
189
  return result;
package/dist/index.cjs CHANGED
@@ -15,4 +15,4 @@ Object.defineProperty(exports, "uuid7FromTime", { enumerable: true, get: functio
15
15
  var prompts_cache_js_1 = require("./utils/prompts_cache.cjs");
16
16
  Object.defineProperty(exports, "Cache", { enumerable: true, get: function () { return prompts_cache_js_1.Cache; } });
17
17
  // Update using yarn bump-version
18
- exports.__version__ = "0.4.11";
18
+ exports.__version__ = "0.4.12";
package/dist/index.d.ts CHANGED
@@ -5,4 +5,4 @@ export { overrideFetchImplementation } from "./singletons/fetch.js";
5
5
  export { getDefaultProjectName } from "./utils/project.js";
6
6
  export { uuid7, uuid7FromTime } from "./uuid.js";
7
7
  export { Cache, type CacheConfig, type CacheMetrics, } from "./utils/prompts_cache.js";
8
- export declare const __version__ = "0.4.11";
8
+ export declare const __version__ = "0.4.12";
package/dist/index.js CHANGED
@@ -5,4 +5,4 @@ export { getDefaultProjectName } from "./utils/project.js";
5
5
  export { uuid7, uuid7FromTime } from "./uuid.js";
6
6
  export { Cache, } from "./utils/prompts_cache.js";
7
7
  // Update using yarn bump-version
8
- export const __version__ = "0.4.11";
8
+ export const __version__ = "0.4.12";
@@ -636,14 +636,15 @@ class RunTree {
636
636
  }
637
637
  }
638
638
  }
639
- // Remap IDs for the replica using uuid5 (deterministic)
640
- // This ensures consistency across runs in the same replica
639
+ // Remap IDs for the replica using nonCryptographicUuid7Deterministic
640
+ // This ensures consistency across runs in the same replica while
641
+ // preserving UUID7 properties (time-ordering, monotonicity)
641
642
  const oldId = baseRun.id;
642
- const newId = (0, uuid_1.v5)(`${oldId}:${projectName}`, UUID_NAMESPACE_DNS);
643
+ const newId = (0, _uuid_js_1.nonCryptographicUuid7Deterministic)(oldId, projectName);
643
644
  // Remap trace_id
644
645
  let newTraceId;
645
646
  if (baseRun.trace_id) {
646
- newTraceId = (0, uuid_1.v5)(`${baseRun.trace_id}:${projectName}`, UUID_NAMESPACE_DNS);
647
+ newTraceId = (0, _uuid_js_1.nonCryptographicUuid7Deterministic)(baseRun.trace_id, projectName);
647
648
  }
648
649
  else {
649
650
  newTraceId = newId;
@@ -651,7 +652,7 @@ class RunTree {
651
652
  // Remap parent_run_id
652
653
  let newParentId;
653
654
  if (baseRun.parent_run_id) {
654
- newParentId = (0, uuid_1.v5)(`${baseRun.parent_run_id}:${projectName}`, UUID_NAMESPACE_DNS);
655
+ newParentId = (0, _uuid_js_1.nonCryptographicUuid7Deterministic)(baseRun.parent_run_id, projectName);
655
656
  }
656
657
  // Remap dotted_order segments
657
658
  let newDottedOrder;
@@ -660,7 +661,7 @@ class RunTree {
660
661
  const remappedSegs = segs.map((seg) => {
661
662
  // Extract the UUID from the segment (last TIMESTAMP_LENGTH characters)
662
663
  const segId = seg.slice(-TIMESTAMP_LENGTH);
663
- const remappedId = (0, uuid_1.v5)(`${segId}:${projectName}`, UUID_NAMESPACE_DNS);
664
+ const remappedId = (0, _uuid_js_1.nonCryptographicUuid7Deterministic)(segId, projectName);
664
665
  // Replace the UUID part while keeping the timestamp prefix
665
666
  return seg.slice(0, -TIMESTAMP_LENGTH) + remappedId;
666
667
  });
package/dist/run_trees.js CHANGED
@@ -7,7 +7,7 @@ import { getEnvironmentVariable, getRuntimeEnvironment, } from "./utils/env.js";
7
7
  import { getDefaultProjectName } from "./utils/project.js";
8
8
  import { getLangSmithEnvironmentVariable } from "./utils/env.js";
9
9
  import { warnOnce } from "./utils/warn.js";
10
- import { uuid7FromTime } from "./utils/_uuid.js";
10
+ import { uuid7FromTime, nonCryptographicUuid7Deterministic, } from "./utils/_uuid.js";
11
11
  import { v5 as uuidv5 } from "uuid";
12
12
  const TIMESTAMP_LENGTH = 36;
13
13
  // DNS namespace for UUID v5 (same as Python's uuid.NAMESPACE_DNS)
@@ -630,14 +630,15 @@ export class RunTree {
630
630
  }
631
631
  }
632
632
  }
633
- // Remap IDs for the replica using uuid5 (deterministic)
634
- // This ensures consistency across runs in the same replica
633
+ // Remap IDs for the replica using nonCryptographicUuid7Deterministic
634
+ // This ensures consistency across runs in the same replica while
635
+ // preserving UUID7 properties (time-ordering, monotonicity)
635
636
  const oldId = baseRun.id;
636
- const newId = uuidv5(`${oldId}:${projectName}`, UUID_NAMESPACE_DNS);
637
+ const newId = nonCryptographicUuid7Deterministic(oldId, projectName);
637
638
  // Remap trace_id
638
639
  let newTraceId;
639
640
  if (baseRun.trace_id) {
640
- newTraceId = uuidv5(`${baseRun.trace_id}:${projectName}`, UUID_NAMESPACE_DNS);
641
+ newTraceId = nonCryptographicUuid7Deterministic(baseRun.trace_id, projectName);
641
642
  }
642
643
  else {
643
644
  newTraceId = newId;
@@ -645,7 +646,7 @@ export class RunTree {
645
646
  // Remap parent_run_id
646
647
  let newParentId;
647
648
  if (baseRun.parent_run_id) {
648
- newParentId = uuidv5(`${baseRun.parent_run_id}:${projectName}`, UUID_NAMESPACE_DNS);
649
+ newParentId = nonCryptographicUuid7Deterministic(baseRun.parent_run_id, projectName);
649
650
  }
650
651
  // Remap dotted_order segments
651
652
  let newDottedOrder;
@@ -654,7 +655,7 @@ export class RunTree {
654
655
  const remappedSegs = segs.map((seg) => {
655
656
  // Extract the UUID from the segment (last TIMESTAMP_LENGTH characters)
656
657
  const segId = seg.slice(-TIMESTAMP_LENGTH);
657
- const remappedId = uuidv5(`${segId}:${projectName}`, UUID_NAMESPACE_DNS);
658
+ const remappedId = nonCryptographicUuid7Deterministic(segId, projectName);
658
659
  // Replace the UUID part while keeping the timestamp prefix
659
660
  return seg.slice(0, -TIMESTAMP_LENGTH) + remappedId;
660
661
  });
package/dist/schemas.d.ts CHANGED
@@ -392,6 +392,7 @@ export interface InvocationParamsSchema {
392
392
  ls_temperature?: number;
393
393
  ls_max_tokens?: number;
394
394
  ls_stop?: string[];
395
+ ls_invocation_params?: Record<string, unknown>;
395
396
  }
396
397
  export interface PromptCommit {
397
398
  owner: string;
@@ -4,10 +4,12 @@ exports.assertUuid = assertUuid;
4
4
  exports.uuid7FromTime = uuid7FromTime;
5
5
  exports.getUuidVersion = getUuidVersion;
6
6
  exports.warnIfNotUuidV7 = warnIfNotUuidV7;
7
+ exports.nonCryptographicUuid7Deterministic = nonCryptographicUuid7Deterministic;
7
8
  // Relaxed UUID validation regex (allows any valid UUID format including nil UUIDs)
8
9
  const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
9
10
  const uuid_1 = require("uuid");
10
11
  const warn_js_1 = require("./warn.cjs");
12
+ const xxhash_js_1 = require("./xxhash/xxhash.cjs");
11
13
  let UUID7_WARNING_EMITTED = false;
12
14
  function assertUuid(str, which) {
13
15
  // Use relaxed regex validation instead of strict uuid.validate()
@@ -64,3 +66,114 @@ function warnIfNotUuidV7(uuidStr, _idType) {
64
66
  `Future versions will require UUID v7.`);
65
67
  }
66
68
  }
69
+ /**
70
+ * Convert a UUID string to its 16-byte representation.
71
+ * @param uuidStr - The UUID string (with or without dashes)
72
+ * @returns A Uint8Array containing the 16 bytes of the UUID
73
+ */
74
+ function uuidToBytes(uuidStr) {
75
+ const hex = uuidStr.replace(/-/g, "");
76
+ const bytes = new Uint8Array(16);
77
+ for (let i = 0; i < 16; i++) {
78
+ bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
79
+ }
80
+ return bytes;
81
+ }
82
+ /**
83
+ * Convert 16 bytes to a UUID string.
84
+ * @param bytes - A Uint8Array containing 16 bytes
85
+ * @returns A UUID string in standard format
86
+ */
87
+ function bytesToUuid(bytes) {
88
+ const hex = Array.from(bytes)
89
+ .map((b) => b.toString(16).padStart(2, "0"))
90
+ .join("");
91
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
92
+ }
93
+ // Reuse TextEncoder instance for performance
94
+ const _textEncoder = new TextEncoder();
95
+ /**
96
+ * Generates a 16-byte fingerprint for deterministic UUID generation using XXH3-128.
97
+ *
98
+ * XXH3 is an extremely fast, non-cryptographic hash function that provides excellent
99
+ * collision resistance. It's widely used in production systems and compatible with
100
+ * xxHash implementations in other languages.
101
+ *
102
+ * See: https://github.com/Cyan4973/xxHash
103
+ *
104
+ * @param str - The input string to hash
105
+ * @returns A Uint8Array containing 16 bytes of hash output
106
+ */
107
+ function _fastHash128(str) {
108
+ const data = _textEncoder.encode(str);
109
+ // Compute XXH3-128 hash and convert to bytes
110
+ const hash128 = (0, xxhash_js_1.XXH3_128)(data);
111
+ return (0, xxhash_js_1.xxh128ToBytes)(hash128);
112
+ }
113
+ /**
114
+ * Generate a deterministic UUID v7 derived from an original UUID and a key.
115
+ *
116
+ * This function creates a new UUID that:
117
+ * - Preserves the timestamp from the original UUID if it's UUID v7
118
+ * - Uses current time if the original is not UUID v7
119
+ * - Uses deterministic "random" bits derived from hashing the original + key
120
+ * - Is valid UUID v7 format
121
+ *
122
+ * This is used for creating replica IDs that maintain time-ordering properties
123
+ * while being deterministic across distributed systems.
124
+ *
125
+ * @param originalId - The source UUID string (ideally UUID v7 to preserve timestamp)
126
+ * @param key - A string key used for deterministic derivation (e.g., project name)
127
+ * @returns A new UUID v7 string with preserved timestamp (if original is v7) and
128
+ * deterministic random bits
129
+ *
130
+ * @example
131
+ * ```typescript
132
+ * const original = uuidv7();
133
+ * const replicaId = nonCryptographicUuid7Deterministic(original, "replica-project");
134
+ * // Same inputs always produce same output
135
+ * assert(nonCryptographicUuid7Deterministic(original, "replica-project") === replicaId);
136
+ * ```
137
+ */
138
+ function nonCryptographicUuid7Deterministic(originalId, key) {
139
+ // Generate deterministic bytes from hash of original + key
140
+ const hashInput = `${originalId}:${key}`;
141
+ const h = _fastHash128(hashInput);
142
+ // Build new UUID7:
143
+ // UUID7 structure (RFC 9562):
144
+ // [0-5] 48 bits: unix_ts_ms (timestamp in milliseconds)
145
+ // [6] 4 bits: version (0111 = 7) + 4 bits rand_a
146
+ // [7] 8 bits: rand_a (continued)
147
+ // [8] 2 bits: variant (10) + 6 bits rand_b
148
+ // [9-15] 56 bits: rand_b (continued)
149
+ const b = new Uint8Array(16);
150
+ // Check if original is UUID v7 - if so, preserve its timestamp
151
+ // If not, use current time to ensure the derived UUID has a valid timestamp
152
+ const version = getUuidVersion(originalId);
153
+ if (version === 7) {
154
+ // Preserve timestamp from original UUID7 (bytes 0-5)
155
+ const originalBytes = uuidToBytes(originalId);
156
+ b.set(originalBytes.slice(0, 6), 0);
157
+ }
158
+ else {
159
+ // Generate fresh timestamp for non-UUID7 inputs
160
+ // This matches the uuid npm package's v7 implementation:
161
+ // https://github.com/uuidjs/uuid/blob/main/src/v7.ts
162
+ const msecs = Date.now();
163
+ b[0] = (msecs / 0x10000000000) & 0xff;
164
+ b[1] = (msecs / 0x100000000) & 0xff;
165
+ b[2] = (msecs / 0x1000000) & 0xff;
166
+ b[3] = (msecs / 0x10000) & 0xff;
167
+ b[4] = (msecs / 0x100) & 0xff;
168
+ b[5] = msecs & 0xff;
169
+ }
170
+ // Set version 7 (0111) in high nibble + 4 bits from hash
171
+ b[6] = 0x70 | (h[0] & 0x0f);
172
+ // rand_a continued (8 bits from hash)
173
+ b[7] = h[1];
174
+ // Set variant (10) in high 2 bits + 6 bits from hash
175
+ b[8] = 0x80 | (h[2] & 0x3f);
176
+ // rand_b (56 bits = 7 bytes from hash)
177
+ b.set(h.slice(3, 10), 9);
178
+ return bytesToUuid(b);
179
+ }
@@ -19,3 +19,29 @@ export declare function getUuidVersion(uuidStr: string): number | null;
19
19
  * @param idType - The type of ID (e.g., "run_id", "trace_id") for the warning message
20
20
  */
21
21
  export declare function warnIfNotUuidV7(uuidStr: string, _idType: string): void;
22
+ /**
23
+ * Generate a deterministic UUID v7 derived from an original UUID and a key.
24
+ *
25
+ * This function creates a new UUID that:
26
+ * - Preserves the timestamp from the original UUID if it's UUID v7
27
+ * - Uses current time if the original is not UUID v7
28
+ * - Uses deterministic "random" bits derived from hashing the original + key
29
+ * - Is valid UUID v7 format
30
+ *
31
+ * This is used for creating replica IDs that maintain time-ordering properties
32
+ * while being deterministic across distributed systems.
33
+ *
34
+ * @param originalId - The source UUID string (ideally UUID v7 to preserve timestamp)
35
+ * @param key - A string key used for deterministic derivation (e.g., project name)
36
+ * @returns A new UUID v7 string with preserved timestamp (if original is v7) and
37
+ * deterministic random bits
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * const original = uuidv7();
42
+ * const replicaId = nonCryptographicUuid7Deterministic(original, "replica-project");
43
+ * // Same inputs always produce same output
44
+ * assert(nonCryptographicUuid7Deterministic(original, "replica-project") === replicaId);
45
+ * ```
46
+ */
47
+ export declare function nonCryptographicUuid7Deterministic(originalId: string, key: string): string;
@@ -2,6 +2,7 @@
2
2
  const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
3
3
  import { v7 as uuidv7 } from "uuid";
4
4
  import { warnOnce } from "./warn.js";
5
+ import { XXH3_128, xxh128ToBytes } from "./xxhash/xxhash.js";
5
6
  let UUID7_WARNING_EMITTED = false;
6
7
  export function assertUuid(str, which) {
7
8
  // Use relaxed regex validation instead of strict uuid.validate()
@@ -58,3 +59,114 @@ export function warnIfNotUuidV7(uuidStr, _idType) {
58
59
  `Future versions will require UUID v7.`);
59
60
  }
60
61
  }
62
+ /**
63
+ * Convert a UUID string to its 16-byte representation.
64
+ * @param uuidStr - The UUID string (with or without dashes)
65
+ * @returns A Uint8Array containing the 16 bytes of the UUID
66
+ */
67
+ function uuidToBytes(uuidStr) {
68
+ const hex = uuidStr.replace(/-/g, "");
69
+ const bytes = new Uint8Array(16);
70
+ for (let i = 0; i < 16; i++) {
71
+ bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
72
+ }
73
+ return bytes;
74
+ }
75
+ /**
76
+ * Convert 16 bytes to a UUID string.
77
+ * @param bytes - A Uint8Array containing 16 bytes
78
+ * @returns A UUID string in standard format
79
+ */
80
+ function bytesToUuid(bytes) {
81
+ const hex = Array.from(bytes)
82
+ .map((b) => b.toString(16).padStart(2, "0"))
83
+ .join("");
84
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
85
+ }
86
+ // Reuse TextEncoder instance for performance
87
+ const _textEncoder = new TextEncoder();
88
+ /**
89
+ * Generates a 16-byte fingerprint for deterministic UUID generation using XXH3-128.
90
+ *
91
+ * XXH3 is an extremely fast, non-cryptographic hash function that provides excellent
92
+ * collision resistance. It's widely used in production systems and compatible with
93
+ * xxHash implementations in other languages.
94
+ *
95
+ * See: https://github.com/Cyan4973/xxHash
96
+ *
97
+ * @param str - The input string to hash
98
+ * @returns A Uint8Array containing 16 bytes of hash output
99
+ */
100
+ function _fastHash128(str) {
101
+ const data = _textEncoder.encode(str);
102
+ // Compute XXH3-128 hash and convert to bytes
103
+ const hash128 = XXH3_128(data);
104
+ return xxh128ToBytes(hash128);
105
+ }
106
+ /**
107
+ * Generate a deterministic UUID v7 derived from an original UUID and a key.
108
+ *
109
+ * This function creates a new UUID that:
110
+ * - Preserves the timestamp from the original UUID if it's UUID v7
111
+ * - Uses current time if the original is not UUID v7
112
+ * - Uses deterministic "random" bits derived from hashing the original + key
113
+ * - Is valid UUID v7 format
114
+ *
115
+ * This is used for creating replica IDs that maintain time-ordering properties
116
+ * while being deterministic across distributed systems.
117
+ *
118
+ * @param originalId - The source UUID string (ideally UUID v7 to preserve timestamp)
119
+ * @param key - A string key used for deterministic derivation (e.g., project name)
120
+ * @returns A new UUID v7 string with preserved timestamp (if original is v7) and
121
+ * deterministic random bits
122
+ *
123
+ * @example
124
+ * ```typescript
125
+ * const original = uuidv7();
126
+ * const replicaId = nonCryptographicUuid7Deterministic(original, "replica-project");
127
+ * // Same inputs always produce same output
128
+ * assert(nonCryptographicUuid7Deterministic(original, "replica-project") === replicaId);
129
+ * ```
130
+ */
131
+ export function nonCryptographicUuid7Deterministic(originalId, key) {
132
+ // Generate deterministic bytes from hash of original + key
133
+ const hashInput = `${originalId}:${key}`;
134
+ const h = _fastHash128(hashInput);
135
+ // Build new UUID7:
136
+ // UUID7 structure (RFC 9562):
137
+ // [0-5] 48 bits: unix_ts_ms (timestamp in milliseconds)
138
+ // [6] 4 bits: version (0111 = 7) + 4 bits rand_a
139
+ // [7] 8 bits: rand_a (continued)
140
+ // [8] 2 bits: variant (10) + 6 bits rand_b
141
+ // [9-15] 56 bits: rand_b (continued)
142
+ const b = new Uint8Array(16);
143
+ // Check if original is UUID v7 - if so, preserve its timestamp
144
+ // If not, use current time to ensure the derived UUID has a valid timestamp
145
+ const version = getUuidVersion(originalId);
146
+ if (version === 7) {
147
+ // Preserve timestamp from original UUID7 (bytes 0-5)
148
+ const originalBytes = uuidToBytes(originalId);
149
+ b.set(originalBytes.slice(0, 6), 0);
150
+ }
151
+ else {
152
+ // Generate fresh timestamp for non-UUID7 inputs
153
+ // This matches the uuid npm package's v7 implementation:
154
+ // https://github.com/uuidjs/uuid/blob/main/src/v7.ts
155
+ const msecs = Date.now();
156
+ b[0] = (msecs / 0x10000000000) & 0xff;
157
+ b[1] = (msecs / 0x100000000) & 0xff;
158
+ b[2] = (msecs / 0x1000000) & 0xff;
159
+ b[3] = (msecs / 0x10000) & 0xff;
160
+ b[4] = (msecs / 0x100) & 0xff;
161
+ b[5] = msecs & 0xff;
162
+ }
163
+ // Set version 7 (0111) in high nibble + 4 bits from hash
164
+ b[6] = 0x70 | (h[0] & 0x0f);
165
+ // rand_a continued (8 bits from hash)
166
+ b[7] = h[1];
167
+ // Set variant (10) in high 2 bits + 6 bits from hash
168
+ b[8] = 0x80 | (h[2] & 0x3f);
169
+ // rand_b (56 bits = 7 bytes from hash)
170
+ b.set(h.slice(3, 10), 9);
171
+ return bytesToUuid(b);
172
+ }