fix: handle datetime/date in to_prompt_json#1527
Open
jalaliamirreza wants to merge 1 commit into
Open
Conversation
to_prompt_json called json.dumps without a default= handler, so any datetime/date value in the serialized data raised 'TypeError: Object of type datetime is not JSON serializable'. This surfaces during entity-summary regeneration (extract_attributes_from_nodes -> _extract_entity_summaries_batch -> to_prompt_json) when an entity attribute holds a date, crashing the second ingest of such an entity. Add a default= fallback: date/datetime serialize as ISO-8601, anything else falls back to str(). Output is unchanged for already-serializable data. Adds unit tests in tests/test_prompt_helpers.py.
jalaliamirreza
added a commit
to Mayia-App/mayia
that referenced
this pull request
Jun 3, 2026
…as shim retirement gate (#169) Filed the upstream root-cause fix (option C): getzep/graphiti#1527 adds a default= handler to to_prompt_json (issue getzep/graphiti#1526). Wire the concrete reference into the shim docstring + journey so the retirement gate is unambiguous when the fix lands in a release.
jalaliamirreza
added a commit
to Mayia-App/mayia
that referenced
this pull request
Jun 3, 2026
…line identity check (#169) claude-review round 2: - _json_default now renders date/datetime via .isoformat() (RFC-3339) instead of default=str (space separator). Matches the upstream fix (getzep/graphiti#1527) + the rest of apps/memory (search.py/episodes.py), so the shim emits the same datetime form graphiti-core will once the upstream lands. Decimal/UUID/etc. still fall back to str(). - WARN test uses record.getMessage() (not .message — only set by Formatter.format(); raw LogRecords may lack it). - Inlined _is_original into the sweep to mirror _reset_for_testing's inverse identity check (symmetry). - Added test_patched_falls_back_to_str_for_other_unserializable (Decimal + UUID) — covers the str() fallback branch, back to 100%. Declined again: generator-fixture `-> None` annotation (round-1 #2) — matches the file convention, no type-checker enforces it, Ruff green.
Merged
4 tasks
jalaliamirreza
added a commit
to Mayia-App/mayia
that referenced
this pull request
Jun 3, 2026
…502 (#169) (#172) * fix(memory): datetime-safe to_prompt_json shim (#169) graphiti-core 0.29.0's to_prompt_json calls json.dumps with no default= handler, so entity-summary regen on the second ingest of any entity carrying a LAW datetime attribute (Person.date_of_birth, Matter.engagement_letter_date, Signed.signed_at, ...) raises TypeError: Object of type datetime is not JSON serializable -> 502. Option B (short-term shim): install_datetime_safe_to_prompt_json() rebinds every loaded graphiti_core to_prompt_json reference (a sys.modules sweep, since `from ... import` copies the reference into ~7 modules) onto a default=str wrapper. Identity-idempotent and re-runnable. Wired into _build_graphiti() before any add_episode. Also docs: cloudbuild-deploy.md now describes tag-triggered (server-v* / memory-v*) deploys instead of push-to-main. TDD: graphiti_patches.journey.md + test_graphiti_patches.py (8 tests: bug characterization, full import-site sweep, summarize_nodes crash site, unicode/indent preservation, idempotency) + J5 wiring test. 100% coverage on the new module; memory suite 225 passed, 92.9% total. * fix(memory): WARN on rebound-0 sweep + explicit shim upgrade gate (#169) Pre-PR architect review (2 MEDIUM, both applied): - _build_graphiti now WARNs when the to_prompt_json sweep rebinds 0 references. Production builds Graphiti once, so a 0 means graphiti-core moved the bind site on an in-range bump — the #169 502 would otherwise return silently. Converts a silent prod regression into an alertable log. - pyproject.toml graphiti-core pin documents that bumping requires re-running test_install_rebinds_summarize_nodes_crash_site as the go/no-go that the shim still covers the crash path. Security review: APPROVE, no findings. * test(memory): bot round 1 — happy-path wiring mock + explicit rebound-0 WARN test (#169) claude-review round 1: - test_build_graphiti_installs_datetime_patch mocked install to return 0, incidentally firing the rebound-0 WARN branch. Use return_value=7 (happy path) and add test_build_graphiti_warns_when_sweep_rebinds_nothing to cover the WARN branch deterministically via caplog (no longer order- dependent on real-build tests). - journey.md: corrected stale "guard flag" wording — idempotency is identity comparison against the stock function, not a flag. Declined: generator-fixture `-> None` annotation — matches the file's existing fixture convention (_reset_graphiti_singleton, _set_default_env) and Ruff is clean; changing only the new fixture breaks consistency. * docs(memory): cite upstream graphiti-core fix (getzep/graphiti#1527) as shim retirement gate (#169) Filed the upstream root-cause fix (option C): getzep/graphiti#1527 adds a default= handler to to_prompt_json (issue getzep/graphiti#1526). Wire the concrete reference into the shim docstring + journey so the retirement gate is unambiguous when the fix lands in a release. * fix(memory): bot round 2 — isoformat default + caplog getMessage + inline identity check (#169) claude-review round 2: - _json_default now renders date/datetime via .isoformat() (RFC-3339) instead of default=str (space separator). Matches the upstream fix (getzep/graphiti#1527) + the rest of apps/memory (search.py/episodes.py), so the shim emits the same datetime form graphiti-core will once the upstream lands. Decimal/UUID/etc. still fall back to str(). - WARN test uses record.getMessage() (not .message — only set by Formatter.format(); raw LogRecords may lack it). - Inlined _is_original into the sweep to mirror _reset_for_testing's inverse identity check (symmetry). - Added test_patched_falls_back_to_str_for_other_unserializable (Decimal + UUID) — covers the str() fallback branch, back to 100%. Declined again: generator-fixture `-> None` annotation (round-1 #2) — matches the file convention, no type-checker enforces it, Ruff green. * test(memory): bot round 3 — drop unused monkeypatch fixture param (#169) claude-review round 3: test_build_graphiti_installs_datetime_patch declared monkeypatch but patches exclusively via patch.object context managers — dead dependency. Removed. * fix(memory): bot round 4 — no false WARN on idempotent patch re-run + assert install ordering (#169) claude-review round 4: - MEDIUM: install runs before env validation, so a failed cold-start (missing env, or build_indices_and_constraints raising) left the patch installed but the singleton uncached; the retry's sweep rebound 0 and fired the "bind site moved / 502 risk" WARN falsely, burying the real error. Added is_datetime_safe_to_prompt_json_installed() predicate and a 3-way branch: rebound>0 -> INFO; rebound==0 & active -> DEBUG (benign idempotent re-run); rebound==0 & not active -> WARN (genuine regression). - LOW: J5 wiring test now asserts install ran BEFORE Graphiti() via a Graphiti side-effect, not just call_count. - Added unit test for the predicate + an idempotent-reinstall-is-quiet test. 229 passed, 100% on graphiti_patches.py, 92.98% total.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Fixes #1526.
to_prompt_json(graphiti_core/prompts/prompt_helpers.py) callsjson.dumps(data, ...)with nodefault=handler, so anydatetime/datevalue in the serialized data raises:This surfaces during entity-summary regeneration —
extract_attributes_from_nodes → _extract_entity_summaries_batch → extract_summaries_batch → to_prompt_json(context['attributes'])— whenever an entity attribute holds a date (common with custom entity types that declaredatetime/datefields, e.g. a contractsigned_ator a persondate_of_birth). The first ingest of a brand-new entity succeeds (no summary regen, so the dump never runs); the second ingest touching the same entity triggers the regen and crashes.Repro
Fix
Add a
default=fallback toto_prompt_json:date/datetime→ ISO-8601 (.isoformat())str()Output is unchanged for already-serializable data (the
default=callable only fires for values the stock encoder rejects). Since this serializes prompt content for an LLM, ISO-8601 dates are also more readable than the raw error path.Tests
Adds
tests/test_prompt_helpers.py(pure unit, no Neo4j/LLM, runs undermake test):ensure_ascii=Falseunicode +indentpreservedNotes
datetimeonly).