fix(memory): jsonify metadata before Prisma writes on /v1/memory#26536
Conversation
The POST/PUT memory endpoints handed bare dicts (and bare `None`) to prisma-client-python for the `Json?` `metadata` column, which the client rejects with `MissingRequiredValueError` / `DataError: metadata should be of any of the following types: NullableJsonNullValueInput, Json`. Both the create and upsert paths now route writes through the existing `jsonify_object` helper used elsewhere in the proxy for `Json?` columns (e.g. `LiteLLM_VerificationToken.budget_limits`), and omit metadata when None so the column defaults to SQL NULL via the schema. Explicit `metadata: null` on PUT is now a no-op for the column to match how the rest of the proxy handles nullable JSON fields (no `JsonNull`/`DbNull` sentinel exists in prisma-client-python — see RobertCraigie/prisma-client-py#714). A payload with only `metadata: null` returns 400 instead of a misleading 200. Made-with: Cursor
|
|
Greptile SummaryThis PR fixes a Confidence Score: 4/5Safe to merge with the caveat that The core serialisation fix is correct and well-tested across all metadata types (dict, list, scalar, string). No new P0/P1 issues were found in this review pass. The backwards-incompatible No files require special attention beyond the stale
|
| Filename | Overview |
|---|---|
| litellm/proxy/memory/memory_endpoints.py | Introduces _serialize_metadata_for_prisma (always json.dumps) to fix Prisma DataError on Json? columns; applied consistently across POST create, PUT update, and PUT-create paths. The metadata: null no-op behaviour is a documented backwards-incompatible change (flagged separately in previous review threads). |
| tests/test_litellm/proxy/memory/test_memory_endpoints.py | Good test coverage added for dict/list/string/scalar metadata serialization and null-only 400 path. Mock create/update helpers now round-trip JSON correctly. Stale jsonify_object references in mock comments should be updated to _serialize_metadata_for_prisma. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["POST /v1/memory or PUT /v1/memory/{key}"] --> B{body.metadata is None?}
B -- Yes --> C[Omit metadata field\ncolumn defaults to SQL NULL]
B -- No --> D["_serialize_metadata_for_prisma(metadata)\njson.dumps → str"]
D --> E{PUT: existing row?}
C --> E
E -- Yes/Update --> F[prisma .update with data dict]
E -- No/Create --> G[prisma .create with create_data dict]
F --> H[Return LiteLLM_MemoryRow]
G --> H
B2{metadata: null AND\nonly field sent?} -- Yes --> I[HTTP 400]
Reviews (3): Last reviewed commit: "fix(memory): always json.dumps metadata,..." | Re-trigger Greptile
`jsonify_object` only stringifies dict values, so list-shaped metadata still hit Prisma as raw Python objects and triggered the same DataError this PR is meant to fix. `metadata` is typed `Optional[Any]` so list payloads are valid input. Replace `jsonify_object` with a local `_serialize_metadata_for_prisma` helper that always `json.dumps` non-string values, applied at all three write sites (POST create, PUT update, PUT-create). Adds regression tests for list metadata on each path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The str-passthrough in `_serialize_metadata_for_prisma` left plain Python strings (e.g. `metadata: "hello"`) unencoded — Postgres `jsonb` rejects bare-word strings as invalid JSON, reproducing the same DataError this PR is meant to fix. Always `json.dumps` regardless of input type so all `Optional[Any]` shapes (dict, list, scalar, str) become valid JSON. Adds a regression test for plain-string metadata. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
740bb44
into
litellm_internal_staging
…rge-after-nits, BerriAI/litellm#26531 needs-discussion
…riAI#26536) * fix(memory): jsonify metadata before Prisma writes on /v1/memory The POST/PUT memory endpoints handed bare dicts (and bare `None`) to prisma-client-python for the `Json?` `metadata` column, which the client rejects with `MissingRequiredValueError` / `DataError: metadata should be of any of the following types: NullableJsonNullValueInput, Json`. Both the create and upsert paths now route writes through the existing `jsonify_object` helper used elsewhere in the proxy for `Json?` columns (e.g. `LiteLLM_VerificationToken.budget_limits`), and omit metadata when None so the column defaults to SQL NULL via the schema. Explicit `metadata: null` on PUT is now a no-op for the column to match how the rest of the proxy handles nullable JSON fields (no `JsonNull`/`DbNull` sentinel exists in prisma-client-python — see RobertCraigie/prisma-client-py#714). A payload with only `metadata: null` returns 400 instead of a misleading 200. Made-with: Cursor * fix(memory): JSON-encode non-dict metadata before Prisma writes `jsonify_object` only stringifies dict values, so list-shaped metadata still hit Prisma as raw Python objects and triggered the same DataError this PR is meant to fix. `metadata` is typed `Optional[Any]` so list payloads are valid input. Replace `jsonify_object` with a local `_serialize_metadata_for_prisma` helper that always `json.dumps` non-string values, applied at all three write sites (POST create, PUT update, PUT-create). Adds regression tests for list metadata on each path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(memory): always json.dumps metadata, not just non-strings The str-passthrough in `_serialize_metadata_for_prisma` left plain Python strings (e.g. `metadata: "hello"`) unencoded — Postgres `jsonb` rejects bare-word strings as invalid JSON, reproducing the same DataError this PR is meant to fix. Always `json.dumps` regardless of input type so all `Optional[Any]` shapes (dict, list, scalar, str) become valid JSON. Adds a regression test for plain-string metadata. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The POST/PUT memory endpoints handed bare dicts (and bare
None) to prisma-client-python for theJson?metadatacolumn, which the client rejects withMissingRequiredValueError/DataError: metadata should be of any of the following types: NullableJsonNullValueInput, Json. All write paths (POST, PUT-update, PUT-create) now route metadata through a dedicated_serialize_metadata_for_prismahelper that alwaysjson.dumpsthe payload — covering everyOptional[Any]shape (dict, list, scalar, plain string), all of which Postgresjsonbrequires to be valid JSON.Explicit
metadata: nullon PUT is encoded as JSONnull(Postgresjsonb 'null') — prisma-client-python has noJsonNull/DbNullsentinel for writing a true SQL NULL (RobertCraigie/prisma-client-py#714), but the typed client deserializesjsonb 'null'back to PythonNoneon read, so from a caller's perspectivePUT {"metadata": null}clears the field. Callers wanting a strict SQL NULL must use raw SQL — there is no typed-client path.