Skip to content

Commit

Permalink
fix(autofix): Correctly format chained exceptions (#915)
Browse files Browse the repository at this point in the history
The new exception chaining introduced in #899 caused error messages not
to be returned correctly to the agent:

<img width="1063" alt="Screenshot 2024-07-17 at 2 07 57 AM"
src="https://github.com/user-attachments/assets/6fc96bf0-1949-4fd6-8c1e-4c072adf3f7f">

These changes now result in the correct response being sent to the
agent:

<img width="1123" alt="Screenshot 2024-07-17 at 1 56 14 AM"
src="https://github.com/user-attachments/assets/d932b503-0ad4-42a2-a300-b7a8bda65ca4">
  • Loading branch information
jennmueng committed Jul 16, 2024
1 parent 662071a commit 5fd276d
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 1 deletion.
12 changes: 11 additions & 1 deletion src/seer/automation/agent/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@
logger = logging.getLogger(__name__)


def get_full_exception_string(exc):
result = str(exc)
if exc.__cause__:
if result:
result += f"\n\nThe above exception was the direct cause of the following exception:\n\n{str(exc.__cause__)}"
else:
result = str(exc.__cause__)
return result


class FunctionTool(BaseModel):
name: str
description: str
Expand All @@ -18,7 +28,7 @@ def call(self, **kwargs):
return self.fn(**kwargs)
except Exception as e:
logger.exception(e)
return f"Error: {e}"
return f"Error: {get_full_exception_string(e)}"

def to_dict(self):
return {
Expand Down
87 changes: 87 additions & 0 deletions tests/automation/agent/test_agent_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from unittest.mock import Mock, patch

import pytest

from seer.automation.agent.tools import FunctionTool, get_full_exception_string


class TestGetFullExceptionString:
def test_simple_exception(self):
exc = ValueError("Simple error")
assert get_full_exception_string(exc) == "Simple error"

def test_chained_exception(self):
try:
raise RuntimeError("Main error") from ValueError("Root cause")
except RuntimeError as exc:
assert (
get_full_exception_string(exc)
== "Main error\n\nThe above exception was the direct cause of the following exception:\n\nRoot cause"
)

def test_empty_main_exception(self):
try:
raise RuntimeError() from ValueError("Root cause")
except RuntimeError as exc:
assert get_full_exception_string(exc) == "Root cause"


class TestFunctionTool:
@pytest.fixture
def mock_function(self):
return Mock(return_value="Success")

@pytest.fixture
def function_tool(self, mock_function):
return FunctionTool(
name="test_tool",
description="A test tool",
fn=mock_function,
parameters=[{"name": "param1", "type": "string"}],
)

def test_successful_call(self, function_tool):
result = function_tool.call(param1="test")
assert result == "Success"

def test_exception_handling(self, function_tool):
function_tool.fn.side_effect = ValueError("Test error")

with patch("seer.automation.agent.tools.logger") as mock_logger:
result = function_tool.call(param1="test")

assert result.startswith("Error: Test error")
mock_logger.exception.assert_called_once()

def test_chained_exception_handling(self, function_tool):
cause = ValueError("Root cause")
main_error = RuntimeError("Main error")
main_error.__cause__ = cause
function_tool.fn.side_effect = main_error

with patch("seer.automation.agent.tools.logger") as mock_logger:
result = function_tool.call(param1="test")

expected = "Error: Main error\n\nThe above exception was the direct cause of the following exception:\n\nRoot cause"
assert result == expected
mock_logger.exception.assert_called_once()

def test_to_dict(self, function_tool):
expected = {
"type": "function",
"function": {
"name": "test_tool",
"description": "A test tool",
"parameters": {
"type": "object",
"properties": {
"param1": {
"type": "string",
"description": "",
}
},
"required": [],
},
},
}
assert function_tool.to_dict() == expected

0 comments on commit 5fd276d

Please sign in to comment.