Fix datetime comparison errors by normalizing to UTC (#988)

* Fix datetime comparison errors by normalizing to UTC

Applied ensure_utc() to all datetime comparisons in edge_operations.py to prevent TypeError when comparing timezone-naive and timezone-aware datetimes. Removed redundant tzinfo checks since ensure_utc() handles both None and naive datetimes.

Fixed comparisons at:
- Lines 419, 423: resolve_edge_contradictions function
- Line 430: resolve_edge_contradictions function
- Line 627: resolve_extracted_edge function (removed redundant tzinfo checks)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Update uv.lock

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Fix sorting with mixed timezone-aware/naive datetimes

Normalize datetime to UTC in sort key to prevent TypeError when comparing mixed timezone-aware and timezone-naive datetimes during sorting.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Daniel Chalef 2025-10-07 08:28:56 -07:00 committed by GitHub
parent 27d4f1097b
commit 73015e980e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 21 additions and 16 deletions

View file

@ -413,21 +413,26 @@ def resolve_edge_contradictions(
invalidated_edges: list[EntityEdge] = []
for edge in invalidation_candidates:
# (Edge invalid before new edge becomes valid) or (new edge invalid before edge becomes valid)
edge_invalid_at_utc = ensure_utc(edge.invalid_at)
resolved_edge_valid_at_utc = ensure_utc(resolved_edge.valid_at)
edge_valid_at_utc = ensure_utc(edge.valid_at)
resolved_edge_invalid_at_utc = ensure_utc(resolved_edge.invalid_at)
if (
edge.invalid_at is not None
and resolved_edge.valid_at is not None
and edge.invalid_at <= resolved_edge.valid_at
edge_invalid_at_utc is not None
and resolved_edge_valid_at_utc is not None
and edge_invalid_at_utc <= resolved_edge_valid_at_utc
) or (
edge.valid_at is not None
and resolved_edge.invalid_at is not None
and resolved_edge.invalid_at <= edge.valid_at
edge_valid_at_utc is not None
and resolved_edge_invalid_at_utc is not None
and resolved_edge_invalid_at_utc <= edge_valid_at_utc
):
continue
# New edge invalidates edge
elif (
edge.valid_at is not None
and resolved_edge.valid_at is not None
and edge.valid_at < resolved_edge.valid_at
edge_valid_at_utc is not None
and resolved_edge_valid_at_utc is not None
and edge_valid_at_utc < resolved_edge_valid_at_utc
):
edge.invalid_at = resolved_edge.valid_at
edge.expired_at = edge.expired_at if edge.expired_at is not None else utc_now()
@ -619,14 +624,14 @@ async def resolve_extracted_edge(
# Determine if the new_edge needs to be expired
if resolved_edge.expired_at is None:
invalidation_candidates.sort(key=lambda c: (c.valid_at is None, c.valid_at))
invalidation_candidates.sort(key=lambda c: (c.valid_at is None, ensure_utc(c.valid_at)))
for candidate in invalidation_candidates:
candidate_valid_at_utc = ensure_utc(candidate.valid_at)
resolved_edge_valid_at_utc = ensure_utc(resolved_edge.valid_at)
if (
candidate.valid_at
and resolved_edge.valid_at
and candidate.valid_at.tzinfo
and resolved_edge.valid_at.tzinfo
and candidate.valid_at > resolved_edge.valid_at
candidate_valid_at_utc is not None
and resolved_edge_valid_at_utc is not None
and candidate_valid_at_utc > resolved_edge_valid_at_utc
):
# Expire new edge since we have information about more recent events
resolved_edge.invalid_at = candidate.valid_at

2
uv.lock generated
View file

@ -783,7 +783,7 @@ wheels = [
[[package]]
name = "graphiti-core"
version = "0.22.0rc4"
version = "0.22.0rc5"
source = { editable = "." }
dependencies = [
{ name = "diskcache" },