From a14cf87576ef269346874cb9365fe5c2dac05066 Mon Sep 17 00:00:00 2001 From: Jeremy Naccache <63456504+jeremynac@users.noreply.github.com> Date: Thu, 30 Nov 2023 17:52:24 +0100 Subject: [PATCH] =?UTF-8?q?core[patch]:=20Add=20**kwargs=20to=20Langchain'?= =?UTF-8?q?s=20dumps()=20to=20allow=20passing=20of=20json.dumps()=20?= =?UTF-8?q?=E2=80=A6=20(#10628)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …parameters. In Langchain's `dumps()` function, I've added a `**kwargs` parameter. This allows users to pass additional parameters to the underlying `json.dumps()` function, providing greater flexibility and control over JSON serialization. Many parameters available in `json.dumps()` can be useful or even necessary in specific situations. For example, when using an Agent with return_intermediate_steps set to true, the output is a list of AgentAction objects. These objects can't be serialized without using Langchain's `dumps()` function. The issue arises when using the Agent with a language other than English, which may contain non-ASCII characters like 'é'. The default behavior of `json.dumps()` sets ensure_ascii to true, converting `{"name": "José"}` into `{"name": "Jos\u00e9"}`. This can make the output hard to read, especially in the case of intermediate steps in agent logs. By allowing users to pass additional parameters to `json.dumps()` via Langchain's dumps(), we can solve this problem. For instance, users can set `ensure_ascii=False` to maintain the original characters. This update also enables users to pass other useful `json.dumps()` parameters like `sort_keys`, providing even more flexibility. The implementation takes into account edge cases where a user might pass a "default" parameter, which is already defined by `dumps()`, or an "indent" parameter, which is also predefined if `pretty=True` is set. --------- Co-authored-by: Erick Friis --- libs/core/langchain_core/load/dump.py | 9 ++++++--- .../unit_tests/load/__snapshots__/test_dump.ambr | 3 +++ libs/langchain/tests/unit_tests/load/test_dump.py | 11 +++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/libs/core/langchain_core/load/dump.py b/libs/core/langchain_core/load/dump.py index 2e2293f99e7..07c956a8400 100644 --- a/libs/core/langchain_core/load/dump.py +++ b/libs/core/langchain_core/load/dump.py @@ -13,12 +13,15 @@ def default(obj: Any) -> Any: return to_json_not_implemented(obj) -def dumps(obj: Any, *, pretty: bool = False) -> str: +def dumps(obj: Any, *, pretty: bool = False, **kwargs: Any) -> str: """Return a json string representation of an object.""" + if "default" in kwargs: + raise ValueError("`default` should not be passed to dumps") if pretty: - return json.dumps(obj, default=default, indent=2) + indent = kwargs.pop("indent", 2) + return json.dumps(obj, default=default, indent=indent, **kwargs) else: - return json.dumps(obj, default=default) + return json.dumps(obj, default=default, **kwargs) def dumpd(obj: Any) -> Dict[str, Any]: diff --git a/libs/langchain/tests/unit_tests/load/__snapshots__/test_dump.ambr b/libs/langchain/tests/unit_tests/load/__snapshots__/test_dump.ambr index 5b6b0e0778f..92427020e31 100644 --- a/libs/langchain/tests/unit_tests/load/__snapshots__/test_dump.ambr +++ b/libs/langchain/tests/unit_tests/load/__snapshots__/test_dump.ambr @@ -57,6 +57,9 @@ } ''' # --- +# name: test_person_with_kwargs + '{"lc":1,"type":"constructor","id":["tests","unit_tests","load","test_dump","Person"],"kwargs":{"secret":{"lc":1,"type":"secret","id":["SECRET"]},"you_can_see_me":"hello"}}' +# --- # name: test_serialize_llmchain ''' { diff --git a/libs/langchain/tests/unit_tests/load/test_dump.py b/libs/langchain/tests/unit_tests/load/test_dump.py index 8e0dcd25fdc..6eef608e101 100644 --- a/libs/langchain/tests/unit_tests/load/test_dump.py +++ b/libs/langchain/tests/unit_tests/load/test_dump.py @@ -141,3 +141,14 @@ def test_serialize_llmchain_with_non_serializable_arg(snapshot: Any) -> None: prompt = PromptTemplate.from_template("hello {name}!") chain = LLMChain(llm=llm, prompt=prompt) assert dumps(chain, pretty=True) == snapshot + + +def test_person_with_kwargs(snapshot: Any) -> None: + person = Person(secret="hello") + assert dumps(person, separators=(",", ":")) == snapshot + + +def test_person_with_invalid_kwargs() -> None: + person = Person(secret="hello") + with pytest.raises(TypeError): + dumps(person, invalid_kwarg="hello")