From af37e67ec6ad4e6f1459d524e10dd329b4b220de Mon Sep 17 00:00:00 2001 From: chenmoneygithub Date: Tue, 25 Nov 2025 13:04:33 -0800 Subject: [PATCH 1/2] Upgrade json_repair to fix parsing issue --- pyproject.toml | 2 +- tests/adapters/test_chat_adapter.py | 35 +++++++++++++++++++++++++++-- uv.lock | 8 +++---- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e70a255f37..541022581d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ dependencies = [ "pydantic>=2.0", "litellm>=1.64.0", "diskcache>=5.6.0", - "json-repair>=0.30.0", + "json-repair>=0.54.2", "tenacity>=8.2.3", "anyio", "asyncer==0.0.8", diff --git a/tests/adapters/test_chat_adapter.py b/tests/adapters/test_chat_adapter.py index bbb7287572..5d8b62e98f 100644 --- a/tests/adapters/test_chat_adapter.py +++ b/tests/adapters/test_chat_adapter.py @@ -649,6 +649,37 @@ class MySignature(dspy.Signature): assert result[0]["reasoning"] == dspy.Reasoning(content="Step-by-step thinking about the capital of France") +def test_chat_adapter_parses_float_with_underscores(): + """ + This test verifies that ChatAdapter can parse float numbers with underscores. + After json-repair version 0.54.1, floats like "123_456.789" are treated as normal float numbers. + """ + + class Score(pydantic.BaseModel): + score: float + + class MySignature(dspy.Signature): + question: str = dspy.InputField() + score: Score = dspy.OutputField() + + adapter = dspy.ChatAdapter() + + # Simulate a response with a float number containing underscores + with mock.patch("litellm.completion") as mock_completion: + mock_completion.return_value = ModelResponse( + choices=[ + Choices(message=Message(content="[[ ## score ## ]]\n{'score': 123_456.789}\n[[ ## completed ## ]]")) + ], + model="openai/gpt-4o-mini", + ) + + lm = dspy.LM("openai/gpt-4o-mini", cache=False) + result = adapter(lm, {}, MySignature, [], {"question": "What is the score?"}) + + # The underscore-separated float should be parsed as a normal float + assert result[0]["score"].score == 123456.789 + + def test_format_system_message(): class MySignature(dspy.Signature): """Answer the question with multiple answers and scores""" @@ -662,7 +693,7 @@ class MySignature(dspy.Signature): expected_system_message = """Your input fields are: 1. `question` (str): Your output fields are: -1. `answers` (list[str]): +1. `answers` (list[str]): 2. `scores` (list[float]): All interactions will be structured in the following way, with the appropriate values filled in. @@ -676,6 +707,6 @@ class MySignature(dspy.Signature): {scores} # note: the value you produce must adhere to the JSON schema: {"type": "array", "items": {"type": "number"}} [[ ## completed ## ]] -In adhering to this structure, your objective is: +In adhering to this structure, your objective is: Answer the question with multiple answers and scores""" assert system_message == expected_system_message diff --git a/uv.lock b/uv.lock index 02ebc63c1c..bbfca92eb4 100644 --- a/uv.lock +++ b/uv.lock @@ -800,7 +800,7 @@ requires-dist = [ { name = "datasets", marker = "extra == 'test-extras'", specifier = ">=2.14.6" }, { name = "diskcache", specifier = ">=5.6.0" }, { name = "gepa", extras = ["dspy"], specifier = "==0.0.22" }, - { name = "json-repair", specifier = ">=0.30.0" }, + { name = "json-repair", specifier = ">=0.54.2" }, { name = "langchain-core", marker = "extra == 'langchain'" }, { name = "langchain-core", marker = "extra == 'test-extras'" }, { name = "litellm", specifier = ">=1.64.0" }, @@ -1433,11 +1433,11 @@ wheels = [ [[package]] name = "json-repair" -version = "0.46.2" +version = "0.54.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/74/f8e4eb4ce31be034c08fd3da37328c9ab7a7503831cf6f41d2121699cc88/json_repair-0.46.2.tar.gz", hash = "sha256:4c81154d61c028ca3750b451472dbb33978f2ee6f44be84c42b444b03d9f4b16", size = 33605, upload-time = "2025-06-06T08:05:48.46Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/05/9fbcd5ffab9c41455e7d80af65a90876718b8ea2fb4525e187ab11836dd4/json_repair-0.54.2.tar.gz", hash = "sha256:4b6b62ce17f1a505b220fa4aadba1fc37dc9c221544f158471efe3775620bad6", size = 38575, upload-time = "2025-11-25T19:31:22.768Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/d7/5f31df5ad00474f3005bbbac5f3a1e8d36535b40f1d352e6a5bd9880bf1f/json_repair-0.46.2-py3-none-any.whl", hash = "sha256:21fb339de583ab68db4272f984ec6fca9cc453d8117d9870e83c28b6b56c20e6", size = 22326, upload-time = "2025-06-06T08:05:47.064Z" }, + { url = "https://files.pythonhosted.org/packages/53/3a/1b4df9adcd69fee9c9e4b439c13e8c866f2fae520054aede7030b2278be9/json_repair-0.54.2-py3-none-any.whl", hash = "sha256:be51cce5dca97e0c24ebdf61a1ede2449a8a7666012de99467bb7b0afb35179b", size = 29322, upload-time = "2025-11-25T19:31:21.492Z" }, ] [[package]] From 955527206595b409f24e8be46077a9ef4b8842de Mon Sep 17 00:00:00 2001 From: chenmoneygithub Date: Tue, 25 Nov 2025 13:10:11 -0800 Subject: [PATCH 2/2] fix tests --- tests/adapters/test_chat_adapter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/adapters/test_chat_adapter.py b/tests/adapters/test_chat_adapter.py index 5d8b62e98f..adb0dfed22 100644 --- a/tests/adapters/test_chat_adapter.py +++ b/tests/adapters/test_chat_adapter.py @@ -693,7 +693,7 @@ class MySignature(dspy.Signature): expected_system_message = """Your input fields are: 1. `question` (str): Your output fields are: -1. `answers` (list[str]): +1. `answers` (list[str]): 2. `scores` (list[float]): All interactions will be structured in the following way, with the appropriate values filled in. @@ -707,6 +707,6 @@ class MySignature(dspy.Signature): {scores} # note: the value you produce must adhere to the JSON schema: {"type": "array", "items": {"type": "number"}} [[ ## completed ## ]] -In adhering to this structure, your objective is: +In adhering to this structure, your objective is: Answer the question with multiple answers and scores""" assert system_message == expected_system_message