mirror of
https://github.com/hwchase17/langchain.git
synced 2026-07-13 20:47:15 +00:00
chore: Use uuid7 in callback run IDs
This commit is contained in:
@@ -6,7 +6,6 @@ import asyncio
|
||||
import atexit
|
||||
import functools
|
||||
import logging
|
||||
import uuid
|
||||
from abc import ABC, abstractmethod
|
||||
from collections.abc import Callable
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
@@ -40,6 +39,7 @@ from langchain_core.tracers.context import (
|
||||
)
|
||||
from langchain_core.tracers.langchain import LangChainTracer
|
||||
from langchain_core.tracers.stdout import ConsoleCallbackHandler
|
||||
from langchain_core.utils import uuid as lc_uuid
|
||||
from langchain_core.utils.env import env_var_is_set
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -504,7 +504,7 @@ class BaseRunManager(RunManagerMixin):
|
||||
|
||||
"""
|
||||
return cls(
|
||||
run_id=uuid.uuid4(),
|
||||
run_id=lc_uuid.uuid7(),
|
||||
handlers=[],
|
||||
inheritable_handlers=[],
|
||||
tags=[],
|
||||
@@ -1330,7 +1330,7 @@ class CallbackManager(BaseCallbackManager):
|
||||
managers = []
|
||||
for i, prompt in enumerate(prompts):
|
||||
# Can't have duplicate runs with the same run ID (if provided)
|
||||
run_id_ = run_id if i == 0 and run_id is not None else uuid.uuid4()
|
||||
run_id_ = run_id if i == 0 and run_id is not None else lc_uuid.uuid7()
|
||||
handle_event(
|
||||
self.handlers,
|
||||
"on_llm_start",
|
||||
@@ -1384,7 +1384,7 @@ class CallbackManager(BaseCallbackManager):
|
||||
run_id_ = run_id
|
||||
run_id = None
|
||||
else:
|
||||
run_id_ = uuid.uuid4()
|
||||
run_id_ = lc_uuid.uuid7()
|
||||
handle_event(
|
||||
self.handlers,
|
||||
"on_chat_model_start",
|
||||
@@ -1433,7 +1433,7 @@ class CallbackManager(BaseCallbackManager):
|
||||
|
||||
"""
|
||||
if run_id is None:
|
||||
run_id = uuid.uuid4()
|
||||
run_id = lc_uuid.uuid7()
|
||||
handle_event(
|
||||
self.handlers,
|
||||
"on_chain_start",
|
||||
@@ -1488,7 +1488,7 @@ class CallbackManager(BaseCallbackManager):
|
||||
|
||||
"""
|
||||
if run_id is None:
|
||||
run_id = uuid.uuid4()
|
||||
run_id = lc_uuid.uuid7()
|
||||
|
||||
handle_event(
|
||||
self.handlers,
|
||||
@@ -1537,7 +1537,7 @@ class CallbackManager(BaseCallbackManager):
|
||||
The callback manager for the retriever run.
|
||||
"""
|
||||
if run_id is None:
|
||||
run_id = uuid.uuid4()
|
||||
run_id = lc_uuid.uuid7()
|
||||
|
||||
handle_event(
|
||||
self.handlers,
|
||||
@@ -1594,7 +1594,7 @@ class CallbackManager(BaseCallbackManager):
|
||||
)
|
||||
raise ValueError(msg)
|
||||
if run_id is None:
|
||||
run_id = uuid.uuid4()
|
||||
run_id = lc_uuid.uuid7()
|
||||
|
||||
handle_event(
|
||||
self.handlers,
|
||||
@@ -1816,7 +1816,7 @@ class AsyncCallbackManager(BaseCallbackManager):
|
||||
run_id_ = run_id
|
||||
run_id = None
|
||||
else:
|
||||
run_id_ = uuid.uuid4()
|
||||
run_id_ = lc_uuid.uuid7()
|
||||
|
||||
if inline_handlers:
|
||||
inline_tasks.append(
|
||||
@@ -1900,7 +1900,7 @@ class AsyncCallbackManager(BaseCallbackManager):
|
||||
run_id_ = run_id
|
||||
run_id = None
|
||||
else:
|
||||
run_id_ = uuid.uuid4()
|
||||
run_id_ = lc_uuid.uuid7()
|
||||
|
||||
for handler in self.handlers:
|
||||
task = ahandle_event(
|
||||
@@ -1962,7 +1962,7 @@ class AsyncCallbackManager(BaseCallbackManager):
|
||||
The async callback manager for the chain run.
|
||||
"""
|
||||
if run_id is None:
|
||||
run_id = uuid.uuid4()
|
||||
run_id = lc_uuid.uuid7()
|
||||
|
||||
await ahandle_event(
|
||||
self.handlers,
|
||||
@@ -2010,7 +2010,7 @@ class AsyncCallbackManager(BaseCallbackManager):
|
||||
The async callback manager for the tool run.
|
||||
"""
|
||||
if run_id is None:
|
||||
run_id = uuid.uuid4()
|
||||
run_id = lc_uuid.uuid7()
|
||||
|
||||
await ahandle_event(
|
||||
self.handlers,
|
||||
@@ -2060,7 +2060,7 @@ class AsyncCallbackManager(BaseCallbackManager):
|
||||
if not self.handlers:
|
||||
return
|
||||
if run_id is None:
|
||||
run_id = uuid.uuid4()
|
||||
run_id = lc_uuid.uuid7()
|
||||
|
||||
if kwargs:
|
||||
msg = (
|
||||
@@ -2102,7 +2102,7 @@ class AsyncCallbackManager(BaseCallbackManager):
|
||||
The async callback manager for the retriever run.
|
||||
"""
|
||||
if run_id is None:
|
||||
run_id = uuid.uuid4()
|
||||
run_id = lc_uuid.uuid7()
|
||||
|
||||
await ahandle_event(
|
||||
self.handlers,
|
||||
|
||||
@@ -12,7 +12,7 @@ from typing import (
|
||||
TypeVar,
|
||||
cast,
|
||||
)
|
||||
from uuid import UUID, uuid4
|
||||
from uuid import UUID
|
||||
|
||||
from typing_extensions import NotRequired, override
|
||||
|
||||
@@ -42,6 +42,7 @@ from langchain_core.tracers.log_stream import (
|
||||
_astream_log_implementation,
|
||||
)
|
||||
from langchain_core.tracers.memory_stream import _MemoryStream
|
||||
from langchain_core.utils import uuid as lc_uuid
|
||||
from langchain_core.utils.aiter import aclosing, py_anext
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -1006,7 +1007,7 @@ async def _astream_events_implementation_v2(
|
||||
|
||||
# Assign the stream handler to the config
|
||||
config = ensure_config(config)
|
||||
run_id = cast("UUID", config.setdefault("run_id", uuid4()))
|
||||
run_id = cast("UUID", config.setdefault("run_id", lc_uuid.uuid7()))
|
||||
callbacks = config.get("callbacks")
|
||||
if callbacks is None:
|
||||
config["callbacks"] = [event_streamer]
|
||||
|
||||
108
libs/core/langchain_core/utils/_internal/_uuid.py
Normal file
108
libs/core/langchain_core/utils/_internal/_uuid.py
Normal file
@@ -0,0 +1,108 @@
|
||||
"""Private UUID v7 implementation backported from Python 3.14+.
|
||||
|
||||
This module provides UUID v7 generation for Python versions < 3.14.
|
||||
The implementation is taken directly from CPython's uuid.py:
|
||||
https://github.com/python/cpython/blob/main/Lib/uuid.py
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
import uuid as uuid_std
|
||||
|
||||
__all__ = ["uuid7"]
|
||||
# RFC 4122 version 7 flags: version bits (7 << 76) | variant bits (0x8000 << 48)
|
||||
_RFC_4122_VERSION_7_FLAGS = (7 << 76) | (0x8000 << 48)
|
||||
|
||||
# Global state for monotonic counter
|
||||
_last_timestamp_v7: int | None = None
|
||||
_last_counter_v7: int = 0 # 42-bit counter
|
||||
|
||||
|
||||
def _uuid7_get_counter_and_tail() -> tuple[int, int]:
|
||||
"""Generate random counter and tail values for UUID v7."""
|
||||
rand = int.from_bytes(os.urandom(10), byteorder="big")
|
||||
# 42-bit counter with MSB set to 0
|
||||
counter = (rand >> 32) & 0x1FF_FFFF_FFFF
|
||||
# 32-bit random data
|
||||
tail = rand & 0xFFFF_FFFF
|
||||
return counter, tail
|
||||
|
||||
|
||||
def _uuid7(nanoseconds: int | None = None) -> uuid_std.UUID:
|
||||
"""Generate a UUID from a Unix timestamp in milliseconds and random bits.
|
||||
|
||||
UUIDv7 objects feature monotonicity within a millisecond.
|
||||
|
||||
Args:
|
||||
nanoseconds: Optional ns timestamp. If not provided, uses current time.
|
||||
"""
|
||||
# --- 48 --- -- 4 -- --- 12 --- -- 2 -- --- 30 --- - 32 -
|
||||
# unix_ts_ms | version | counter_hi | variant | counter_lo | random
|
||||
#
|
||||
# 'counter = counter_hi | counter_lo' is a 42-bit counter constructed
|
||||
# with Method 1 of RFC 9562, §6.2, and its MSB is set to 0.
|
||||
#
|
||||
# 'random' is a 32-bit random value regenerated for every new UUID.
|
||||
#
|
||||
# If multiple UUIDs are generated within the same millisecond, the LSB
|
||||
# of 'counter' is incremented by 1. When overflowing, the timestamp is
|
||||
# advanced and the counter is reset to a random 42-bit integer with MSB
|
||||
# set to 0.
|
||||
|
||||
global _last_timestamp_v7 # noqa: PLW0603
|
||||
global _last_counter_v7 # noqa: PLW0603
|
||||
|
||||
if nanoseconds is None:
|
||||
nanoseconds = time.time_ns()
|
||||
timestamp_ms = nanoseconds // 1_000_000
|
||||
|
||||
if _last_timestamp_v7 is None or timestamp_ms > _last_timestamp_v7:
|
||||
counter, tail = _uuid7_get_counter_and_tail()
|
||||
else:
|
||||
if timestamp_ms < _last_timestamp_v7:
|
||||
timestamp_ms = _last_timestamp_v7 + 1
|
||||
# advance the 42-bit counter
|
||||
counter = _last_counter_v7 + 1
|
||||
if counter > 0x3FF_FFFF_FFFF: # noqa: PLR2004
|
||||
# advance the 48-bit timestamp
|
||||
timestamp_ms += 1
|
||||
counter, tail = _uuid7_get_counter_and_tail()
|
||||
else:
|
||||
# 32-bit random data
|
||||
tail = int.from_bytes(os.urandom(4), byteorder="big")
|
||||
|
||||
unix_ts_ms = timestamp_ms & 0xFFFF_FFFF_FFFF
|
||||
counter_msbs = counter >> 30
|
||||
# keep 12 counter's MSBs and clear variant bits
|
||||
counter_hi = counter_msbs & 0x0FFF
|
||||
# keep 30 counter's LSBs and clear version bits
|
||||
counter_lo = counter & 0x3FFF_FFFF
|
||||
# ensure that the tail is always a 32-bit integer
|
||||
tail &= 0xFFFF_FFFF
|
||||
|
||||
int_uuid_7 = unix_ts_ms << 80
|
||||
int_uuid_7 |= counter_hi << 64
|
||||
int_uuid_7 |= counter_lo << 32
|
||||
int_uuid_7 |= tail
|
||||
# by construction, the variant and version bits are already cleared
|
||||
int_uuid_7 |= _RFC_4122_VERSION_7_FLAGS
|
||||
|
||||
# Use the public UUID constructor with int parameter
|
||||
res = uuid.UUID(int=int_uuid_7)
|
||||
|
||||
# defer global update until all computations are done
|
||||
_last_timestamp_v7 = timestamp_ms
|
||||
_last_counter_v7 = counter
|
||||
return res
|
||||
|
||||
|
||||
def uuid7() -> uuid_std.UUID:
|
||||
"""Generate a random UUID v7.
|
||||
|
||||
Returns:
|
||||
uuid_std.UUID: A random, RFC 9562-compliant UUID v7.
|
||||
"""
|
||||
return _uuid7()
|
||||
19
libs/core/langchain_core/utils/uuid.py
Normal file
19
libs/core/langchain_core/utils/uuid.py
Normal file
@@ -0,0 +1,19 @@
|
||||
"""UUID utility functions.
|
||||
|
||||
This module exports a uuid7 function to generate monotonic, time-ordered UUIDs
|
||||
for tracing and similar operations.
|
||||
"""
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langchain_core.utils._internal._uuid import uuid7
|
||||
try:
|
||||
# Exported after version 0.4.43.
|
||||
# When available, re-use to ensure monotonicity between IDs
|
||||
from langsmith import uuid7 # type: ignore[no-redef]
|
||||
except ImportError:
|
||||
from langchain_core.utils._internal._uuid import uuid7
|
||||
|
||||
|
||||
__all__ = ["uuid7"]
|
||||
Reference in New Issue
Block a user