Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ dependencies = [
# Note, grpcio>1.30.0 requires setting GRPC_POLL_STRATEGY=epoll1
# See https://github.com/grpc/grpc/issues/23796 and
# https://github.com/grpc/grpc/blob/v1.35.x/doc/core/grpc-polling-engines.md#polling-engine-implementations-in-grpc
"grpcio>=1.67.0",
"grpcio>=1.71.2",
# not directly used, but provides a speedup for redis
"hiredis>=2.3.2",
"httpx>=0.28.1",
Expand Down Expand Up @@ -93,7 +93,7 @@ dependencies = [
"sentry-ophio>=1.1.3",
# sentry-options is only used in getsentry for now
"sentry-options>=1.0.13",
"sentry-protos>=0.13.0",
"sentry-protos>=0.15.0",
"sentry-redis-tools>=0.5.0",
"sentry-relay>=0.9.27",
"sentry-scm==0.16.0",
Expand Down
8 changes: 4 additions & 4 deletions src/sentry/seer/autofix/coding_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ class StateReposNotFound(NotFound):
StoreCodingAgentStatesRequest,
extract_api_error_message,
get_autofix_state,
get_automation_handoff,
get_coding_agent_prompt,
make_store_coding_agent_states_request,
read_preference_from_sentry_db,
update_coding_agent_state,
)
from sentry.seer.models import SeerApiError
Expand Down Expand Up @@ -243,9 +243,9 @@ def _launch_agents_for_repos(
auto_create_pr = False
try:
project = Project.objects.get_from_cache(id=autofix_state.request.project_id)
preference = read_preference_from_sentry_db(project)
if preference.automation_handoff:
auto_create_pr = preference.automation_handoff.auto_create_pr
handoff = get_automation_handoff(project.get_option)
if handoff:
auto_create_pr = handoff.auto_create_pr
except Project.DoesNotExist:
logger.exception(
"coding_agent.project_not_found",
Expand Down
8 changes: 2 additions & 6 deletions src/sentry/seer/autofix/issue_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
is_seer_autotriggered_autofix_rate_limited,
is_seer_autotriggered_autofix_rate_limited_and_increment,
is_seer_seat_based_tier_enabled,
read_preference_from_sentry_db,
)
from sentry.seer.entrypoints.cache import SeerOperatorAutofixCache
from sentry.seer.entrypoints.operator import SeerAutofixOperator
Expand Down Expand Up @@ -466,18 +465,15 @@ def get_automation_stopping_point(group: Group) -> AutofixStoppingPoint | None:
"""
Get the automation stopping point for a group.
"""
user_preference = read_preference_from_sentry_db(group.project).automated_run_stopping_point
user_preference = group.project.get_option("sentry:seer_automated_run_stopping_point")

if is_seer_seat_based_tier_enabled(group.organization):
fixability_score = get_and_update_group_fixability_score(group)
fixability_stopping_point = _get_stopping_point_from_fixability(fixability_score)

return _apply_user_preference_upper_bound(fixability_stopping_point, user_preference)

if user_preference:
return AutofixStoppingPoint(user_preference)

return None
return AutofixStoppingPoint(user_preference)


def _generate_summary(
Expand Down
4 changes: 2 additions & 2 deletions src/sentry/seer/autofix/on_completion_hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
from sentry.seer.autofix.utils import (
AutofixStoppingPoint,
clear_preference_automation_handoff,
read_preference_from_sentry_db,
get_automation_handoff,
)
from sentry.seer.entrypoints.operator import SeerAutofixOperator, process_autofix_updates
from sentry.seer.models import (
Expand Down Expand Up @@ -536,7 +536,7 @@ def _get_handoff_config_if_applicable(
]:
return None

return read_preference_from_sentry_db(group.project).automation_handoff
return get_automation_handoff(group.project.get_option)

@classmethod
def _clear_handoff_preference(
Expand Down
6 changes: 3 additions & 3 deletions src/sentry/seer/autofix/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -628,7 +628,7 @@ def build_repo_definition_from_project_repo(
)


def build_automation_handoff(
def get_automation_handoff(
get_option: Callable[[str], Any],
) -> SeerAutomationHandoffConfiguration | None:
"""Build a SeerAutomationHandoffConfiguration from option key/value pairs, or None if incomplete."""
Expand Down Expand Up @@ -668,7 +668,7 @@ def read_preference_from_sentry_db(project: Project) -> SeerProjectPreference:
project_id=project.id,
repositories=repo_definitions,
automated_run_stopping_point=project.get_option("sentry:seer_automated_run_stopping_point"),
automation_handoff=build_automation_handoff(project.get_option),
automation_handoff=get_automation_handoff(project.get_option),
autofix_automation_tuning=project.get_option("sentry:autofix_automation_tuning"),
)

Expand Down Expand Up @@ -719,7 +719,7 @@ def _get_project_option(key: str) -> Any:
automated_run_stopping_point=_get_project_option(
"sentry:seer_automated_run_stopping_point"
),
automation_handoff=build_automation_handoff(_get_project_option),
automation_handoff=get_automation_handoff(_get_project_option),
autofix_automation_tuning=_get_project_option("sentry:autofix_automation_tuning"),
)

Expand Down
6 changes: 3 additions & 3 deletions src/sentry/seer/endpoints/project_seer_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@
from sentry.seer.autofix.utils import (
AutofixStoppingPoint,
AutomationCodingAgent,
build_automation_handoff,
bulk_update_seer_project_settings,
get_automation_handoff,
get_valid_automated_run_stopping_points,
update_seer_project_settings,
)
Expand Down Expand Up @@ -89,7 +89,7 @@ def _get_project_settings(project: Project) -> SeerProjectSettings:
automation_tuning=project.get_option("sentry:autofix_automation_tuning"),
scanner_automation=project.get_option("sentry:seer_scanner_automation"),
stopping_point=project.get_option("sentry:seer_automated_run_stopping_point"),
handoff=build_automation_handoff(project.get_option),
handoff=get_automation_handoff(project.get_option),
repos_count=SeerProjectRepository.objects.filter(
project_repository__project=project,
project_repository__repository__status=ObjectStatus.ACTIVE,
Expand Down Expand Up @@ -131,7 +131,7 @@ def _get_option(key: str):
automation_tuning=_get_option("sentry:autofix_automation_tuning"),
scanner_automation=_get_option("sentry:seer_scanner_automation"),
stopping_point=_get_option("sentry:seer_automated_run_stopping_point"),
handoff=build_automation_handoff(_get_option),
handoff=get_automation_handoff(_get_option),
repos_count=repo_counts.get(project.id, 0),
)

Expand Down
4 changes: 2 additions & 2 deletions src/sentry/seer/entrypoints/operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
AutofixState,
AutofixStoppingPoint,
get_autofix_state,
get_automation_handoff,
)
from sentry.seer.autofix.utils import CodingAgentState as LegacyCodingAgentState
from sentry.seer.entrypoints.cache import SeerOperatorAgentCache, SeerOperatorAutofixCache
Expand Down Expand Up @@ -316,7 +317,6 @@ def trigger_handoff(
from sentry.seer.autofix.utils import (
CodingAgentProviderType,
CodingAgentStatus,
read_preference_from_sentry_db,
)
from sentry.utils.locking import UnableToAcquireLock

Expand All @@ -328,7 +328,7 @@ def trigger_handoff(
with event_lifecycle.capture() as lifecycle:
lifecycle.add_extras({"group_id": str(group.id), "run_id": str(run_id)})

handoff_config = read_preference_from_sentry_db(group.project).automation_handoff
handoff_config = get_automation_handoff(group.project.get_option)
if handoff_config is None:
# Handoff was unset between message render and click.
lifecycle.record_halt(halt_reason="no_handoff_configured")
Expand Down
14 changes: 8 additions & 6 deletions tests/sentry/seer/autofix/test_autofix_on_completion_hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,8 +452,10 @@ def _make_handoff_config(
auto_create_pr=True,
)

@patch("sentry.seer.autofix.on_completion_hook.read_preference_from_sentry_db")
def test_get_handoff_config_returns_none_when_not_root_cause_step(self, mock_read_pref) -> None:
@patch("sentry.seer.autofix.on_completion_hook.get_automation_handoff")
def test_get_handoff_config_returns_none_when_not_root_cause_step(
self, mock_get_handoff
) -> None:
"""Returns None without reading preferences when current step is not ROOT_CAUSE."""
result = AutofixOnCompletionHook._get_handoff_config_if_applicable(
stopping_point=AutofixStoppingPoint.CODE_CHANGES,
Expand All @@ -462,11 +464,11 @@ def test_get_handoff_config_returns_none_when_not_root_cause_step(self, mock_rea
)

assert result is None
mock_read_pref.assert_not_called()
mock_get_handoff.assert_not_called()

@patch("sentry.seer.autofix.on_completion_hook.read_preference_from_sentry_db")
@patch("sentry.seer.autofix.on_completion_hook.get_automation_handoff")
def test_get_handoff_config_returns_none_when_stopping_at_root_cause(
self, mock_read_pref
self, mock_get_handoff
) -> None:
"""Returns None without reading preferences when stopping point is ROOT_CAUSE."""
result = AutofixOnCompletionHook._get_handoff_config_if_applicable(
Expand All @@ -476,7 +478,7 @@ def test_get_handoff_config_returns_none_when_stopping_at_root_cause(
)

assert result is None
mock_read_pref.assert_not_called()
mock_get_handoff.assert_not_called()

def test_get_handoff_config_returns_none_when_no_handoff_configured(self) -> None:
"""Returns None when project has no automation handoff configured."""
Expand Down
18 changes: 0 additions & 18 deletions tests/sentry/seer/autofix/test_issue_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -1304,21 +1304,3 @@ def test_low_fixability_returns_root_cause(self, mock_fixability, mock_seat_base
self.group.project.update_option("sentry:seer_automated_run_stopping_point", "open_pr")

assert get_automation_stopping_point(self.group) == AutofixStoppingPoint.ROOT_CAUSE

@patch("sentry.seer.autofix.issue_summary.read_preference_from_sentry_db")
@patch("sentry.seer.autofix.issue_summary.get_and_update_group_fixability_score")
def test_null_stopping_point_uses_fixability_only(
self, mock_fixability, mock_read_pref, mock_seat_based_tier
):
"""When preference.automated_run_stopping_point is None, fixability score alone drives the result."""
from sentry.seer.models.seer_api_models import SeerProjectPreference

mock_fixability.return_value = 0.80
mock_read_pref.return_value = SeerProjectPreference(
organization_id=self.group.project.organization_id,
project_id=self.group.project.id,
repositories=[],
automated_run_stopping_point=None,
)

assert get_automation_stopping_point(self.group) == AutofixStoppingPoint.OPEN_PR
Loading
Loading