Compare commits

...

2 Commits

Author SHA1 Message Date
claude[bot] f7c88846db cleanup: Remove temporary test file
The datetime fix test was only for development verification.
2025-08-22 15:41:21 +00:00
claude[bot] 8d954870a5 fix: Ensure datetime serialization includes timezone info for RFC 3339 compliance
Add field_serializer methods to all datetime fields in memory schemas to ensure
proper timezone-aware datetime serialization. This fixes validation errors in
MCP tools like build_context where datetime strings were missing timezone
information.

- Added ensure_timezone_aware utility function
- Updated EntitySummary, RelationSummary, ObservationSummary, MemoryMetadata
- All datetime fields now serialize with timezone suffix (Z or +00:00)
- Handles both timezone-aware and naive datetimes by converting to UTC

Fixes #253

Co-authored-by: jope-bm <jope-bm@users.noreply.github.com>
2025-08-22 15:41:10 +00:00
+29 -2
View File
@@ -1,14 +1,21 @@
"""Schemas for memory context."""
from datetime import datetime
from datetime import datetime, timezone
from typing import List, Optional, Annotated, Sequence, Literal, Union
from annotated_types import MinLen, MaxLen
from pydantic import BaseModel, Field, BeforeValidator, TypeAdapter
from pydantic import BaseModel, Field, BeforeValidator, TypeAdapter, field_serializer
from basic_memory.schemas.search import SearchItemType
def ensure_timezone_aware(dt: datetime) -> datetime:
"""Ensure datetime is timezone-aware by converting to UTC if naive."""
if dt.tzinfo is None:
return dt.replace(tzinfo=timezone.utc)
return dt.astimezone(timezone.utc)
def validate_memory_url_path(path: str) -> bool:
"""Validate that a memory URL path is well-formed.
@@ -125,6 +132,11 @@ class EntitySummary(BaseModel):
file_path: str
created_at: datetime
@field_serializer('created_at')
def serialize_created_at(self, dt: datetime) -> str:
"""Serialize datetime as RFC 3339 compliant string with timezone."""
return ensure_timezone_aware(dt).isoformat()
class RelationSummary(BaseModel):
"""Simplified relation representation."""
@@ -138,6 +150,11 @@ class RelationSummary(BaseModel):
to_entity: Optional[str] = None
created_at: datetime
@field_serializer('created_at')
def serialize_created_at(self, dt: datetime) -> str:
"""Serialize datetime as RFC 3339 compliant string with timezone."""
return ensure_timezone_aware(dt).isoformat()
class ObservationSummary(BaseModel):
"""Simplified observation representation."""
@@ -150,6 +167,11 @@ class ObservationSummary(BaseModel):
content: str
created_at: datetime
@field_serializer('created_at')
def serialize_created_at(self, dt: datetime) -> str:
"""Serialize datetime as RFC 3339 compliant string with timezone."""
return ensure_timezone_aware(dt).isoformat()
class MemoryMetadata(BaseModel):
"""Simplified response metadata."""
@@ -165,6 +187,11 @@ class MemoryMetadata(BaseModel):
total_relations: Optional[int] = None
total_observations: Optional[int] = None
@field_serializer('generated_at')
def serialize_generated_at(self, dt: datetime) -> str:
"""Serialize datetime as RFC 3339 compliant string with timezone."""
return ensure_timezone_aware(dt).isoformat()
class ContextResult(BaseModel):
"""Context result containing a primary item with its observations and related items."""