From 80a70ff62f36ae1a9a73fbc674e13ed9640d690f Mon Sep 17 00:00:00 2001 From: Mason Daugherty Date: Thu, 27 Aug 2026 10:57:26 -0400 Subject: [PATCH] fix(checkpoint): normalize mappings for indexed text --- .gitignore | 1 + libs/checkpoint/langgraph/store/base/embed.py | 5 ++++- libs/checkpoint/tests/test_store.py | 16 +++++++++++++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 7cf97c0d0..cc960f9fa 100644 --- a/.gitignore +++ b/.gitignore @@ -76,6 +76,7 @@ __pypackages__/ # Environments .env +.env.* .envrc *.crt *.key diff --git a/libs/checkpoint/langgraph/store/base/embed.py b/libs/checkpoint/langgraph/store/base/embed.py index 4255886e2..98baac865 100644 --- a/libs/checkpoint/langgraph/store/base/embed.py +++ b/libs/checkpoint/langgraph/store/base/embed.py @@ -11,7 +11,7 @@ from __future__ import annotations import asyncio import functools import json -from collections.abc import Awaitable, Callable, Sequence +from collections.abc import Awaitable, Callable, Mapping, Sequence from typing import Any from langchain_core.embeddings import Embeddings @@ -244,6 +244,9 @@ def get_text_at_path(obj: Any, path: str | list[str]) -> list[str]: - Multi-field selection: "{field1,field2}" - Nested paths in multi-field: "{field1,nested.field2}" """ + if isinstance(obj, Mapping) and not isinstance(obj, dict): + obj = dict(obj) + if not path or path == "$": return [json.dumps(obj, sort_keys=True, ensure_ascii=False)] diff --git a/libs/checkpoint/tests/test_store.py b/libs/checkpoint/tests/test_store.py index 42e7a7697..9079aeecc 100644 --- a/libs/checkpoint/tests/test_store.py +++ b/libs/checkpoint/tests/test_store.py @@ -1,7 +1,9 @@ import asyncio import json -from collections.abc import Iterable +from collections import UserDict +from collections.abc import Iterable, Mapping from datetime import datetime +from types import MappingProxyType from typing import Any import pytest @@ -137,6 +139,18 @@ def test_get_text_at_path() -> None: assert get_text_at_path(nested_data, "nested[{invalid}]") == [] +@pytest.mark.parametrize( + "mapping", + [ + UserDict({"text": "searchable"}), + MappingProxyType({"text": "searchable"}), + ], +) +def test_get_text_at_path_with_non_dict_mapping(mapping: Mapping[str, str]) -> None: + assert get_text_at_path(mapping, "$") == ['{"text": "searchable"}'] + assert get_text_at_path(mapping, "text") == ["searchable"] + + async def test_async_batch_store(mocker: MockerFixture) -> None: abatch = mocker.stub()