-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
feat(seer): Add trigger_explorer method to SeerOperator #109675
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f6220d6
a2fe63b
4bb7df5
e9dec47
fc24539
a96e8f6
099d6a0
c3eb9e3
0377ee7
97dec2c
cc231b0
a18ccba
8cb182e
827104e
bd36469
47ed128
08e9591
6138ec0
75382c1
b22a09f
e1fba91
7285d50
08c6f73
b50c851
76645d9
b5db5bd
b5ee290
1930e57
ca05f75
482a470
f121c49
9058c57
731b782
0645460
cd7b0bc
74f6ba9
6df0a3d
218beb7
2914861
e56416f
77932ab
41fa7f4
6776b44
5702801
ae33249
faab9c1
ce351a7
6b8006d
3aef4cb
e2af363
5cc7997
0b6cdc4
5944992
227ce61
d27aabc
1d99e53
d3e3a5f
b476812
721498d
9129a52
b8f46bb
6a0562f
59c1f1e
02cb3e6
6f4c144
2bbae48
0ccd443
3111940
ea6da55
e5b73c0
3551c53
bc3b4f0
5dd3099
27924a4
37a92a9
2498b8f
ebd0942
c2bb621
6483e6a
97e8202
cd36905
100117b
5f115b8
ef0e582
740f000
6f096f8
fc8e725
850d456
2c11301
159fadf
d624fc0
cd5d7f1
90cc7e4
0e1873f
3f89d14
8d13982
23627e4
992af50
c1c60fa
0f900b6
e85439b
4a34638
a651048
358e2e1
6aa6f70
0a6e20e
7b0469c
aa0c40e
af59ed7
e080061
400c71f
63b972d
6e93dbe
0c5bf93
96a243f
e3d8d75
a6aa4fd
ba4ba30
a19d89f
05fb353
b592755
6dd21cd
e55a2b2
4d75da4
549cce6
f40a857
c4e9da3
13610ba
aa98cc5
30fe216
e14f33c
2a7c95c
582c432
d9cfcc5
e46a408
47d079e
701b711
d678207
abb494b
74ed8a1
9d3fb32
bb97154
e1a3526
b010fc4
012c540
b4a784b
7951c83
7ce7517
4b69a3c
c6212cd
02961e2
a319864
b5043fc
2f2d31f
b9ee703
369cf4a
7e2ec0e
327c413
53d5f35
be606f4
8b8fbc5
1dfaa73
26042e3
493a22c
9fe7e13
cd9edd2
0a9b820
85ec546
7a89817
800d95a
468e64e
a20fa71
bd00e6a
305f162
b3d0522
730fc10
a4beefe
8a32832
054c8df
a7b499a
f672f8f
61309a6
d64597e
c577ef4
9d0c232
ee55f9d
bdfde1e
5417904
736ae2b
14d3bb2
b3f231b
3d0b80f
b6f4878
46b442f
a7e3ecc
f52c325
b7fdc36
9b095b9
489c1df
7a9e248
bd97c61
116fddb
1c931de
1f76d0b
b566ea3
1e3bdae
6b2112d
8196059
f8df17c
52f26f0
c2dd050
fdcf826
2c04a1d
0de4d1c
22fe20b
82e0124
e6b262b
417d5b6
c40b913
1827083
4d3f3b9
45a4d97
15d08b8
b577805
d09404c
099b744
80d62bc
de29a75
f4418cd
376fca2
804aa1e
95c9ae1
df812d8
375650f
1c563fb
c76aa39
1619a7a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -28,9 +28,15 @@ | |||||||||||||||||||||||||
| autofix_entrypoint_registry, | ||||||||||||||||||||||||||
| explorer_entrypoint_registry, | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
| from sentry.seer.entrypoints.types import SeerAutofixEntrypoint, SeerEntrypointKey | ||||||||||||||||||||||||||
| from sentry.seer.entrypoints.types import ( | ||||||||||||||||||||||||||
| SeerAutofixEntrypoint, | ||||||||||||||||||||||||||
| SeerEntrypointKey, | ||||||||||||||||||||||||||
| SeerExplorerEntrypoint, | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
| from sentry.seer.explorer.client import SeerExplorerClient | ||||||||||||||||||||||||||
| from sentry.seer.explorer.client_models import SeerRunState | ||||||||||||||||||||||||||
| from sentry.seer.explorer.on_completion_hook import ExplorerOnCompletionHook | ||||||||||||||||||||||||||
| from sentry.seer.models import SeerPermissionError | ||||||||||||||||||||||||||
| from sentry.seer.seer_setup import has_seer_access | ||||||||||||||||||||||||||
| from sentry.sentry_apps.metrics import SentryAppEventType | ||||||||||||||||||||||||||
| from sentry.tasks.base import instrumented_task | ||||||||||||||||||||||||||
|
|
@@ -438,6 +444,116 @@ def trigger_autofix_legacy( | |||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| class SeerExplorerOperator[CachePayloadT]: | ||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||
| A class that connects to entrypoint implementations and runs Explorer operations for Seer. | ||||||||||||||||||||||||||
| It does this to ensure all entrypoints have consistent behavior and responses. | ||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| def __init__(self, entrypoint: SeerExplorerEntrypoint[CachePayloadT]): | ||||||||||||||||||||||||||
| self.entrypoint = entrypoint | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| def trigger_explorer( | ||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||
| *, | ||||||||||||||||||||||||||
| organization: Organization, | ||||||||||||||||||||||||||
| user: User | RpcUser | None, | ||||||||||||||||||||||||||
| prompt: str, | ||||||||||||||||||||||||||
| on_page_context: str | None = None, | ||||||||||||||||||||||||||
| category_key: str, | ||||||||||||||||||||||||||
| category_value: str, | ||||||||||||||||||||||||||
| ) -> int | None: | ||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||
| Start or continue an Explorer run and return the run_id. | ||||||||||||||||||||||||||
| If a run exists for this category (e.g. slack thread), continues it; otherwise starts new. | ||||||||||||||||||||||||||
| Uses the entrypoint's Explorer callbacks for success/error handling. | ||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||
| event_lifecycle = SeerOperatorEventLifecycleMetric( | ||||||||||||||||||||||||||
| interaction_type=SeerOperatorInteractionType.OPERATOR_TRIGGER_EXPLORER, | ||||||||||||||||||||||||||
| entrypoint_key=self.entrypoint.key, | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| with event_lifecycle.capture() as lifecycle: | ||||||||||||||||||||||||||
| lifecycle.add_extras( | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| "category_key": category_key, | ||||||||||||||||||||||||||
| "category_value": category_value, | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||
| # RpcUser is not in SeerExplorerClient's type signature but works at runtime | ||||||||||||||||||||||||||
| client = SeerExplorerClient( | ||||||||||||||||||||||||||
| organization=organization, | ||||||||||||||||||||||||||
| user=user, | ||||||||||||||||||||||||||
| category_key=category_key, | ||||||||||||||||||||||||||
| category_value=category_value, | ||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
You can add this flag as a reminder to the agent there's a user to chat with/get feedback from Nit can also explicitly set
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you! I've changed to include both of your suggestions: sentry/src/sentry/seer/entrypoints/operator.py Lines 486 to 494 in 1c563fb
|
||||||||||||||||||||||||||
| on_completion_hook=SeerOperatorCompletionHook, | ||||||||||||||||||||||||||
| is_interactive=True, | ||||||||||||||||||||||||||
| enable_coding=False, | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
| except SeerPermissionError as e: | ||||||||||||||||||||||||||
| with SeerOperatorEventLifecycleMetric( | ||||||||||||||||||||||||||
| interaction_type=SeerOperatorInteractionType.ENTRYPOINT_ON_TRIGGER_EXPLORER, | ||||||||||||||||||||||||||
| entrypoint_key=self.entrypoint.key, | ||||||||||||||||||||||||||
| ).capture(assume_success=False): | ||||||||||||||||||||||||||
| self.entrypoint.on_trigger_explorer_error(error=str(e)) | ||||||||||||||||||||||||||
| lifecycle.record_failure(failure_reason=e) | ||||||||||||||||||||||||||
| return None | ||||||||||||||||||||||||||
|
Comment on lines
+496
to
+502
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh interesting. What is this entrypoint call doing on failure? I wonder if it needs to be contained to a task if we expect to propagate this to users reliably.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The entrypoint will send an error message to the thread, with a generic error message. Maybe we should make this into a task, but at the same time, spinning up a task will require a lot more overhead, so I don't know if it's worth it 🤔 |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||
| existing_runs = client.get_runs( | ||||||||||||||||||||||||||
| category_key=category_key, | ||||||||||||||||||||||||||
| category_value=category_value, | ||||||||||||||||||||||||||
| limit=1, | ||||||||||||||||||||||||||
| only_current_user=False, | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
|
sentry[bot] marked this conversation as resolved.
cursor[bot] marked this conversation as resolved.
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if existing_runs: | ||||||||||||||||||||||||||
| run_id = client.continue_run( | ||||||||||||||||||||||||||
| run_id=existing_runs[0].run_id, | ||||||||||||||||||||||||||
| prompt=prompt, | ||||||||||||||||||||||||||
| on_page_context=on_page_context, | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
|
alexsohn1126 marked this conversation as resolved.
|
||||||||||||||||||||||||||
| lifecycle.add_extra("continued", "true") | ||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||
| run_id = client.start_run( | ||||||||||||||||||||||||||
| prompt=prompt, | ||||||||||||||||||||||||||
| on_page_context=on_page_context, | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
| lifecycle.add_extra("continued", "false") | ||||||||||||||||||||||||||
| except Exception as e: | ||||||||||||||||||||||||||
| with SeerOperatorEventLifecycleMetric( | ||||||||||||||||||||||||||
| interaction_type=SeerOperatorInteractionType.ENTRYPOINT_ON_TRIGGER_EXPLORER, | ||||||||||||||||||||||||||
| entrypoint_key=self.entrypoint.key, | ||||||||||||||||||||||||||
| ).capture(assume_success=False): | ||||||||||||||||||||||||||
| self.entrypoint.on_trigger_explorer_error(error="An unexpected error occurred") | ||||||||||||||||||||||||||
| lifecycle.record_failure(failure_reason=e) | ||||||||||||||||||||||||||
| return None | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| lifecycle.add_extra("run_id", str(run_id)) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| with SeerOperatorEventLifecycleMetric( | ||||||||||||||||||||||||||
| interaction_type=SeerOperatorInteractionType.ENTRYPOINT_ON_TRIGGER_EXPLORER, | ||||||||||||||||||||||||||
| entrypoint_key=self.entrypoint.key, | ||||||||||||||||||||||||||
| ).capture(): | ||||||||||||||||||||||||||
| self.entrypoint.on_trigger_explorer_success(run_id=run_id) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| with SeerOperatorEventLifecycleMetric( | ||||||||||||||||||||||||||
| interaction_type=SeerOperatorInteractionType.ENTRYPOINT_CREATE_EXPLORER_CACHE_PAYLOAD, | ||||||||||||||||||||||||||
| entrypoint_key=self.entrypoint.key, | ||||||||||||||||||||||||||
| ).capture(): | ||||||||||||||||||||||||||
| cache_payload = self.entrypoint.create_explorer_cache_payload() | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| SeerOperatorExplorerCache.set( | ||||||||||||||||||||||||||
| entrypoint_key=str(self.entrypoint.key), | ||||||||||||||||||||||||||
| run_id=run_id, | ||||||||||||||||||||||||||
| cache_payload=cache_payload, | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
|
Comment on lines
+542
to
+552
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: In Suggested FixWrap the calls to Prompt for AI Agent |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| return run_id | ||||||||||||||||||||||||||
|
alexsohn1126 marked this conversation as resolved.
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| @instrumented_task( | ||||||||||||||||||||||||||
| name="sentry.seer.entrypoints.operator.process_autofix_updates", | ||||||||||||||||||||||||||
| namespace=seer_tasks, | ||||||||||||||||||||||||||
|
|
@@ -693,8 +809,22 @@ def execute(cls, organization: Organization, run_id: int) -> None: | |||||||||||||||||||||||||
| if not cache_payload: | ||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| entrypoint_cls.on_explorer_update( | ||||||||||||||||||||||||||
| cache_payload=cache_payload, | ||||||||||||||||||||||||||
| summary=summary, | ||||||||||||||||||||||||||
| run_id=run_id, | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
| if cache_payload.get("organization_id") != organization.id: | ||||||||||||||||||||||||||
| # run_id is globally unique in Seer, so only one entrypoint will | ||||||||||||||||||||||||||
| # have a cache entry per run. An org mismatch here is anomalous; | ||||||||||||||||||||||||||
| # return rather than continue to abort the entire method. | ||||||||||||||||||||||||||
| lifecycle.record_failure(failure_reason="org_mismatch") | ||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||
|
alexsohn1126 marked this conversation as resolved.
alexsohn1126 marked this conversation as resolved.
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| with SeerOperatorEventLifecycleMetric( | ||||||||||||||||||||||||||
| interaction_type=SeerOperatorInteractionType.ENTRYPOINT_ON_EXPLORER_UPDATE, | ||||||||||||||||||||||||||
| entrypoint_key=str(entrypoint_key), | ||||||||||||||||||||||||||
| ).capture() as ept_lifecycle: | ||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||
| entrypoint_cls.on_explorer_update( | ||||||||||||||||||||||||||
| cache_payload=cache_payload, | ||||||||||||||||||||||||||
| summary=summary, | ||||||||||||||||||||||||||
| run_id=run_id, | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
| except Exception as e: | ||||||||||||||||||||||||||
| ept_lifecycle.record_failure(failure_reason=e) | ||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We really should not be using type-ignores unless the spaghetti is really bad. Can we add RpcUser to the SeerExplorerClient signature?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in be606f4