Skip to content

Fix non-conformant UUIDv7 timestamp encoding in the native Opik integration#5

Open
alexkuzmik wants to merge 1 commit into
mainfrom
fix/opik-uuid7-ms-timestamp
Open

Fix non-conformant UUIDv7 timestamp encoding in the native Opik integration#5
alexkuzmik wants to merge 1 commit into
mainfrom
fix/opik-uuid7-ms-timestamp

Conversation

@alexkuzmik

Copy link
Copy Markdown

Title

Fix non-conformant UUIDv7 timestamp encoding in the native Opik integration

Relevant issues

OPIK-7067

Pre-Submission checklist

  • I have added testing in the tests/test_litellm/ directory — tests/test_litellm/integrations/test_opik_utils.py (2 tests)
  • I have added the output of my new tests passing locally (below)
  • My PR passes the relevant unit tests via make test-unit (tests/test_litellm)
  • My PR's scope is as isolated as possible — it only fixes the UUIDv7 timestamp encoding

Type

🐛 Bug Fix

Changes

The bug: the timestamp encoded into the UUID was wrong

The native Opik callback (the opik callback bundled in litellm, not opik.integrations.litellm) mints trace/span ids with create_uuid7() in litellm/integrations/opik/utils.py.

A UUIDv7 must encode, in its top 48 bits, the Unix timestamp in milliseconds (RFC 9562). The old implementation instead encoded time in units of 16 seconds:

ns = time.time_ns()
sixteen_secs = 16_000_000_000
t1, rest1 = divmod(ns, sixteen_secs)   # t1 = ns // 16e9, NOT unix-ms

This made the embedded timestamp come out at roughly 4096× the real unix-ms. Decoding the top 48 bits of a generated id (the way a consumer reads a UUIDv7 timestamp) yields a date ~175 years in the future (e.g. year 2201) instead of "now".

This matters because Opik's backend validates the timestamp embedded in incoming UUIDv7 ids and rejects ids whose timestamp is implausibly far from the present — so every trace/span batch from the native integration was failing ingestion with HTTP 400.

The fix

Rewrite create_uuid7() to be RFC 9562 conformant: the top 48 bits now hold the real Unix-millisecond timestamp, followed by the version (7) and variant (0b10) bits and random data for the remainder.

  • Correct timestamp encoding — decoding the top 48 bits of a generated id now returns the actual current time.
  • Standard library only — implemented with time, os.urandom, and uuid; no new dependency is added to litellm. (opik.id_helpers was used only as a reference.)
  • The function name and signature are unchanged, so the two call sites (trace id and span id) are untouched.

Tests

tests/test_litellm/integrations/test_opik_utils.py:

  • test_create_uuid7_is_valid_version_7_uuid — asserts the result is a valid UUID with version 7 and the RFC 4122 variant.
  • test_create_uuid7_encodes_timestamp_in_milliseconds — pins the clock to a fixed instant and asserts the top 48 bits decode to that instant in milliseconds (the exact behavior the old code got wrong).
$ poetry run pytest tests/test_litellm/integrations/test_opik_utils.py -v
tests/test_litellm/integrations/test_opik_utils.py::test_create_uuid7_encodes_timestamp_in_milliseconds PASSED
tests/test_litellm/integrations/test_opik_utils.py::test_create_uuid7_is_valid_version_7_uuid PASSED
============================== 2 passed in 0.21s ===============================

create_uuid7() encoded the timestamp in units of 16 seconds instead of
milliseconds, so the top 48 bits came out ~4096x the real unix-ms. Opik's
backend validates the embedded UUIDv7 timestamp on ingestion (OPIK-7067);
the bad encoding decoded to ~year 2201 and every trace/span batch was
rejected with HTTP 400.

Rewrite create_uuid7() to be RFC 9562 conformant (top 48 bits = unix-ms),
using the standard library only so no new dependency is added. Add unit
tests covering UUIDv7 validity and millisecond timestamp encoding.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant