From 5f0a3208f5b38063f7da24a0eef7326073b42314 Mon Sep 17 00:00:00 2001 From: Kush Dubey Date: Mon, 10 Nov 2025 00:51:20 -0800 Subject: [PATCH] fix(bug-prediction): Make tool wrapper type more precise --- src/sentry/seer/fetch_issues/utils.py | 16 +++++++++------- .../seer/fetch_issues/test_by_error_type.py | 8 ++++++++ .../seer/fetch_issues/test_by_function_name.py | 1 + .../seer/fetch_issues/test_by_text_query.py | 4 ++++ 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/sentry/seer/fetch_issues/utils.py b/src/sentry/seer/fetch_issues/utils.py index 1b120db12a3150..28ec393dd45f90 100644 --- a/src/sentry/seer/fetch_issues/utils.py +++ b/src/sentry/seer/fetch_issues/utils.py @@ -21,14 +21,20 @@ MAX_NUM_DAYS_AGO_DEFAULT = 90 -def handle_fetch_issues_exceptions(func: Callable[..., Any]) -> Callable[..., Any]: +class SeerResponseError(TypedDict): + error: str + + +def handle_fetch_issues_exceptions[R]( + func: Callable[..., R], +) -> Callable[..., R | SeerResponseError]: @wraps(func) - def wrapper(*args: Any, **kwargs: Any) -> Any: + def wrapper(*args: Any, **kwargs: Any) -> R | SeerResponseError: try: return func(*args, **kwargs) except Exception as e: logger.warning("Exception in fetch_issues function", exc_info=True) - return {"error": str(e)} + return SeerResponseError(error=str(e)) return wrapper @@ -52,10 +58,6 @@ class SeerResponse(TypedDict): issues_full: list[dict[str, Any]] -class SeerResponseError(TypedDict): - error: str - - def get_repo_and_projects( organization_id: int, provider: str, diff --git a/tests/sentry/seer/fetch_issues/test_by_error_type.py b/tests/sentry/seer/fetch_issues/test_by_error_type.py index 4c05bb2bf5a9df..185dabda4e4666 100644 --- a/tests/sentry/seer/fetch_issues/test_by_error_type.py +++ b/tests/sentry/seer/fetch_issues/test_by_error_type.py @@ -48,6 +48,7 @@ def test_simple(self) -> None: external_id="1", exception_type="KeyError", ) + assert "error" not in seer_response assert seer_response["issues"][0] == group.id full_issues = seer_response["issues_full"][0] @@ -159,6 +160,7 @@ def test_multiple_projects(self) -> None: external_id="1", exception_type="KeyError", ) + assert "error" not in seer_response assert {group_1.id, group_2.id} == set(seer_response["issues"]) assert group_3.id not in seer_response["issues"] assert group_3.id not in [int(issue["id"]) for issue in seer_response["issues_full"]] @@ -208,6 +210,7 @@ def test_last_seen_filter(self) -> None: external_id="1", exception_type="KeyError", ) + assert "error" not in seer_response assert seer_response["issues"] == [group.id] assert seer_response["issues_full"][0]["id"] == str(group.id) assert seer_response["issues_full"][0]["title"] == "KeyError: This a bad error" @@ -283,6 +286,7 @@ def test_multiple_exception_types(self) -> None: external_id="1", exception_type="KeyError", ) + assert "error" not in seer_response assert seer_response["issues"] == [group_1.id] assert len(seer_response["issues_full"]) == 1 assert seer_response["issues_full"][0]["id"] == str(group_1.id) @@ -295,6 +299,7 @@ def test_multiple_exception_types(self) -> None: external_id="1", exception_type="ValueError", ) + assert "error" not in seer_response assert seer_response["issues"] == [group_2.id] assert len(seer_response["issues_full"]) == 1 assert seer_response["issues_full"][0]["id"] == str(group_2.id) @@ -409,6 +414,7 @@ def _assert_exception_type_matches( external_id="1", exception_type=search_exception_type, ) + assert "error" not in seer_response assert seer_response["issues"] == [expected_group.id] assert len(seer_response["issues_full"]) == 1 @@ -507,6 +513,7 @@ def test_normalized_matching_multiple_groups(self) -> None: external_id="1", exception_type="valueerror", ) + assert "error" not in seer_response assert seer_response["issues"] == [group1.id] assert len(seer_response["issues_full"]) == 1 @@ -517,6 +524,7 @@ def test_normalized_matching_multiple_groups(self) -> None: external_id="1", exception_type="type error", ) + assert "error" not in seer_response assert seer_response["issues"] == [group2.id] assert len(seer_response["issues_full"]) == 1 diff --git a/tests/sentry/seer/fetch_issues/test_by_function_name.py b/tests/sentry/seer/fetch_issues/test_by_function_name.py index df5de7e509f7b9..ca2fbfac580778 100644 --- a/tests/sentry/seer/fetch_issues/test_by_function_name.py +++ b/tests/sentry/seer/fetch_issues/test_by_function_name.py @@ -398,6 +398,7 @@ def test_fetch_issues_end_to_end_with_metadata_and_message(self): ) # Basic structure checks + assert "error" not in seer_response assert "issues" in seer_response assert "issues_full" in seer_response assert len(seer_response["issues"]) > 0 diff --git a/tests/sentry/seer/fetch_issues/test_by_text_query.py b/tests/sentry/seer/fetch_issues/test_by_text_query.py index 434c2383719a4c..d400932878e1de 100644 --- a/tests/sentry/seer/fetch_issues/test_by_text_query.py +++ b/tests/sentry/seer/fetch_issues/test_by_text_query.py @@ -45,6 +45,7 @@ def test_fetch_issues_message_substring_search(self): external_id=self.gh_repo.external_id, query="hello", ) + assert "error" not in seer_response assert len(seer_response["issues"]) > 0, "Should find issue with 'hello' substring" assert group.id in seer_response["issues"] @@ -56,6 +57,7 @@ def test_fetch_issues_message_substring_search(self): external_id=self.gh_repo.external_id, query="auth", ) + assert "error" not in seer_response assert len(seer_response["issues"]) > 0, "Should find issue with 'auth' substring" assert group.id in seer_response["issues"] @@ -115,6 +117,7 @@ def test_fetch_issues_culprit_search(self): query="database conn", ) + assert "error" not in seer_response assert len(seer_response["issues"]) > 0 assert group.id in seer_response["issues"] @@ -139,6 +142,7 @@ def test_fetch_issues_limit_parameter(self): limit=limit, ) + assert "error" not in seer_response assert len(seer_response["issues"]) <= limit def test_fetch_issues_from_repo_projects_returns_groups(self):