From f519b0d31c2b78bdb477dd6dc741e0d88521d0de Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sun, 16 Feb 2025 05:37:29 +0000 Subject: [PATCH] feat: improve error handling in _interpolate_only method - Add comprehensive docstring with examples - Add error handling for edge cases - Add test cases for error scenarios Co-Authored-By: Joe Moura --- src/crewai/agents/agent_builder/base_agent.py | 41 ++++++++++++++++--- tests/agent_test.py | 19 +++++++++ 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/crewai/agents/agent_builder/base_agent.py b/src/crewai/agents/agent_builder/base_agent.py index 0ecf83b82..d0a953114 100644 --- a/src/crewai/agents/agent_builder/base_agent.py +++ b/src/crewai/agents/agent_builder/base_agent.py @@ -315,13 +315,44 @@ class BaseAgent(ABC, BaseModel): return copied_agent def _interpolate_only(self, input_string: str, inputs: Dict[str, Any]) -> str: - """Interpolate placeholders (e.g., {key}) in a string while leaving JSON untouched.""" - escaped_string = input_string.replace("{", "{{").replace("}", "}}") + """Interpolate placeholders in a string while preserving JSON-like structures. - for key in inputs.keys(): - escaped_string = escaped_string.replace(f"{{{{{key}}}}}", f"{{{key}}}") + Args: + input_string (str): The string containing placeholders to interpolate. + inputs (Dict[str, Any]): Dictionary of values for interpolation. - return escaped_string.format(**inputs) + Returns: + str: The interpolated string with JSON structures preserved. + + Example: + >>> _interpolate_only("Name: {name}, Config: {'key': 'value'}", {"name": "John"}) + "Name: John, Config: {'key': 'value'}" + + Raises: + ValueError: If input_string is None or empty, or if inputs is empty + KeyError: If a required template variable is missing from inputs + """ + if not input_string: + raise ValueError("Input string cannot be None or empty") + if not inputs: + raise ValueError("Inputs dictionary cannot be empty") + + try: + # First check if all required variables are present + required_vars = [ + var.split("}")[0] for var in input_string.split("{")[1:] + if "}" in var + ] + for var in required_vars: + if var not in inputs: + raise KeyError(f"Missing required template variable: {var}") + + escaped_string = input_string.replace("{", "{{").replace("}", "}}") + for key in inputs.keys(): + escaped_string = escaped_string.replace(f"{{{{{key}}}}}", f"{{{key}}}") + return escaped_string.format(**inputs) + except ValueError as e: + raise ValueError(f"Error during string interpolation: {str(e)}") from e def interpolate_inputs(self, inputs: Dict[str, Any]) -> None: """Interpolate inputs into the agent description and backstory.""" diff --git a/tests/agent_test.py b/tests/agent_test.py index 4874c2b30..5b54148b8 100644 --- a/tests/agent_test.py +++ b/tests/agent_test.py @@ -1383,6 +1383,25 @@ def test_interpolate_inputs_with_tool_description(): }) assert "Tool Arguments: {'arg': {'description': 'test arg', 'type': 'str'}}" in agent.backstory +def test_interpolate_only_error_handling(): + agent = Agent( + role="{topic} specialist", + goal="Figure {goal} out", + backstory="I am the master of {role}", + ) + + # Test empty input string + with pytest.raises(ValueError, match="Input string cannot be None or empty"): + agent._interpolate_only("", {"topic": "AI"}) + + # Test empty inputs dictionary + with pytest.raises(ValueError, match="Inputs dictionary cannot be empty"): + agent._interpolate_only("test {topic}", {}) + + # Test missing template variable + with pytest.raises(KeyError, match="Missing required template variable"): + agent._interpolate_only("test {missing}", {"topic": "AI"}) + def test_agent_with_all_llm_attributes(): agent = Agent( role="test role",