From c54a082165ffad80ff85a845a29fbf9d016b3765 Mon Sep 17 00:00:00 2001 From: TomuHirata Date: Wed, 23 Apr 2025 13:15:53 +0900 Subject: [PATCH 1/2] allow overriding max_iter in ReAct --- dspy/predict/react.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/dspy/predict/react.py b/dspy/predict/react.py index c01a6875a..a91dab779 100644 --- a/dspy/predict/react.py +++ b/dspy/predict/react.py @@ -73,7 +73,8 @@ def _format_trajectory(self, trajectory: dict[str, Any]): def forward(self, **input_args): trajectory = {} - for idx in range(self.max_iters): + max_iters = input_args.pop("max_iters", self.max_iters) + for idx in range(max_iters): pred = self._call_with_potential_trajectory_truncation(self.react, trajectory, **input_args) trajectory[f"thought_{idx}"] = pred.next_thought @@ -173,8 +174,4 @@ def truncate_trajectory(self, trajectory): TOPIC 06: Idiomatically allowing tools that maintain state across iterations, but not across different `forward` calls. * So the tool would be newly initialized at the start of each `forward` call, but maintain state across iterations. * This is pretty useful for allowing the agent to keep notes or count certain things, etc. - -TOPIC 07: Make max_iters a bit more expressive. - * Allow passing `max_iters` in forward to overwrite the default. - * Get rid of `last_iteration: bool` in the format function. It's not necessary now. """ From fee8ecfa6d5b445e629efc2bfbf617aec126233b Mon Sep 17 00:00:00 2001 From: TomuHirata Date: Wed, 23 Apr 2025 13:34:16 +0900 Subject: [PATCH 2/2] add test for retrying --- tests/predict/test_react.py | 40 +++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/tests/predict/test_react.py b/tests/predict/test_react.py index a3f0ecdca..ba4bf0fe5 100644 --- a/tests/predict/test_react.py +++ b/tests/predict/test_react.py @@ -1,10 +1,7 @@ -from dataclasses import dataclass - from pydantic import BaseModel import dspy -from dspy.predict import react -from dspy.utils.dummies import DummyLM, dummy_rm +from dspy.utils.dummies import DummyLM import litellm # def test_example_no_tools(): @@ -276,3 +273,38 @@ def mock_react(**kwargs): assert "thought_0" not in result.trajectory assert "thought_2" in result.trajectory assert result.output_text == "Final output" + + +def test_error_retry(): + def foo(a, b): + raise Exception("tool error") + + react = dspy.ReAct("a, b -> c:int", tools=[foo]) + max_iters = 2 + lm = DummyLM( + [ + {"next_thought": "I need to add two numbers.", "next_tool_name": "foo", "next_tool_args": {"a": 1, "b": 2}}, + {"next_thought": "I need to add two numbers.", "next_tool_name": "foo", "next_tool_args": {"a": 1, "b": 2}}, + {"reasoning": "I added the numbers successfully", "c": 3}, + ] + ) + dspy.settings.configure(lm=lm) + + outputs = react(a=1, b=2, max_iters=max_iters) + expected_trajectory = { + "thought_0": "I need to add two numbers.", + "tool_name_0": "foo", + "tool_args_0": { + "a": 1, + "b": 2, + }, + 'observation_0': 'Failed to execute: tool error', + 'thought_1': 'I need to add two numbers.', + 'tool_name_1': 'foo', + "tool_args_1": { + "a": 1, + "b": 2, + }, + 'observation_1': 'Failed to execute: tool error', + } + assert outputs.trajectory == expected_trajectory \ No newline at end of file