sentry-sdk 2.39.0__py2.py3-none-any.whl → 2.41.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sentry-sdk might be problematic. Click here for more details.
- sentry_sdk/_metrics.py +81 -0
- sentry_sdk/_metrics_batcher.py +156 -0
- sentry_sdk/_types.py +27 -22
- sentry_sdk/ai/__init__.py +7 -0
- sentry_sdk/ai/utils.py +48 -0
- sentry_sdk/client.py +87 -36
- sentry_sdk/consts.py +15 -9
- sentry_sdk/envelope.py +31 -17
- sentry_sdk/feature_flags.py +0 -1
- sentry_sdk/hub.py +17 -9
- sentry_sdk/integrations/__init__.py +1 -0
- sentry_sdk/integrations/anthropic.py +10 -2
- sentry_sdk/integrations/asgi.py +3 -2
- sentry_sdk/integrations/dramatiq.py +89 -31
- sentry_sdk/integrations/grpc/aio/client.py +2 -1
- sentry_sdk/integrations/grpc/client.py +3 -4
- sentry_sdk/integrations/langchain.py +29 -5
- sentry_sdk/integrations/langgraph.py +5 -3
- sentry_sdk/integrations/launchdarkly.py +0 -1
- sentry_sdk/integrations/litellm.py +251 -0
- sentry_sdk/integrations/litestar.py +4 -4
- sentry_sdk/integrations/logging.py +1 -1
- sentry_sdk/integrations/loguru.py +1 -1
- sentry_sdk/integrations/openai.py +3 -2
- sentry_sdk/integrations/openai_agents/spans/ai_client.py +4 -1
- sentry_sdk/integrations/openai_agents/spans/invoke_agent.py +10 -2
- sentry_sdk/integrations/openai_agents/utils.py +60 -19
- sentry_sdk/integrations/pure_eval.py +3 -1
- sentry_sdk/integrations/spark/spark_driver.py +2 -1
- sentry_sdk/integrations/sqlalchemy.py +2 -6
- sentry_sdk/integrations/starlette.py +1 -3
- sentry_sdk/integrations/starlite.py +4 -4
- sentry_sdk/integrations/threading.py +52 -8
- sentry_sdk/integrations/wsgi.py +3 -2
- sentry_sdk/logger.py +1 -1
- sentry_sdk/profiler/utils.py +2 -6
- sentry_sdk/scope.py +6 -3
- sentry_sdk/serializer.py +1 -3
- sentry_sdk/session.py +4 -2
- sentry_sdk/sessions.py +4 -2
- sentry_sdk/tracing.py +36 -33
- sentry_sdk/tracing_utils.py +1 -3
- sentry_sdk/transport.py +9 -26
- sentry_sdk/types.py +3 -0
- sentry_sdk/utils.py +22 -4
- {sentry_sdk-2.39.0.dist-info → sentry_sdk-2.41.0.dist-info}/METADATA +3 -1
- {sentry_sdk-2.39.0.dist-info → sentry_sdk-2.41.0.dist-info}/RECORD +51 -49
- sentry_sdk/metrics.py +0 -965
- {sentry_sdk-2.39.0.dist-info → sentry_sdk-2.41.0.dist-info}/WHEEL +0 -0
- {sentry_sdk-2.39.0.dist-info → sentry_sdk-2.41.0.dist-info}/entry_points.txt +0 -0
- {sentry_sdk-2.39.0.dist-info → sentry_sdk-2.41.0.dist-info}/licenses/LICENSE +0 -0
- {sentry_sdk-2.39.0.dist-info → sentry_sdk-2.41.0.dist-info}/top_level.txt +0 -0
sentry_sdk/envelope.py
CHANGED
|
@@ -57,25 +57,29 @@ class Envelope:
|
|
|
57
57
|
)
|
|
58
58
|
|
|
59
59
|
def add_event(
|
|
60
|
-
self,
|
|
60
|
+
self,
|
|
61
|
+
event, # type: Event
|
|
61
62
|
):
|
|
62
63
|
# type: (...) -> None
|
|
63
64
|
self.add_item(Item(payload=PayloadRef(json=event), type="event"))
|
|
64
65
|
|
|
65
66
|
def add_transaction(
|
|
66
|
-
self,
|
|
67
|
+
self,
|
|
68
|
+
transaction, # type: Event
|
|
67
69
|
):
|
|
68
70
|
# type: (...) -> None
|
|
69
71
|
self.add_item(Item(payload=PayloadRef(json=transaction), type="transaction"))
|
|
70
72
|
|
|
71
73
|
def add_profile(
|
|
72
|
-
self,
|
|
74
|
+
self,
|
|
75
|
+
profile, # type: Any
|
|
73
76
|
):
|
|
74
77
|
# type: (...) -> None
|
|
75
78
|
self.add_item(Item(payload=PayloadRef(json=profile), type="profile"))
|
|
76
79
|
|
|
77
80
|
def add_profile_chunk(
|
|
78
|
-
self,
|
|
81
|
+
self,
|
|
82
|
+
profile_chunk, # type: Any
|
|
79
83
|
):
|
|
80
84
|
# type: (...) -> None
|
|
81
85
|
self.add_item(
|
|
@@ -87,13 +91,15 @@ class Envelope:
|
|
|
87
91
|
)
|
|
88
92
|
|
|
89
93
|
def add_checkin(
|
|
90
|
-
self,
|
|
94
|
+
self,
|
|
95
|
+
checkin, # type: Any
|
|
91
96
|
):
|
|
92
97
|
# type: (...) -> None
|
|
93
98
|
self.add_item(Item(payload=PayloadRef(json=checkin), type="check_in"))
|
|
94
99
|
|
|
95
100
|
def add_session(
|
|
96
|
-
self,
|
|
101
|
+
self,
|
|
102
|
+
session, # type: Union[Session, Any]
|
|
97
103
|
):
|
|
98
104
|
# type: (...) -> None
|
|
99
105
|
if isinstance(session, Session):
|
|
@@ -101,13 +107,15 @@ class Envelope:
|
|
|
101
107
|
self.add_item(Item(payload=PayloadRef(json=session), type="session"))
|
|
102
108
|
|
|
103
109
|
def add_sessions(
|
|
104
|
-
self,
|
|
110
|
+
self,
|
|
111
|
+
sessions, # type: Any
|
|
105
112
|
):
|
|
106
113
|
# type: (...) -> None
|
|
107
114
|
self.add_item(Item(payload=PayloadRef(json=sessions), type="sessions"))
|
|
108
115
|
|
|
109
116
|
def add_item(
|
|
110
|
-
self,
|
|
117
|
+
self,
|
|
118
|
+
item, # type: Item
|
|
111
119
|
):
|
|
112
120
|
# type: (...) -> None
|
|
113
121
|
self.items.append(item)
|
|
@@ -133,7 +141,8 @@ class Envelope:
|
|
|
133
141
|
return iter(self.items)
|
|
134
142
|
|
|
135
143
|
def serialize_into(
|
|
136
|
-
self,
|
|
144
|
+
self,
|
|
145
|
+
f, # type: Any
|
|
137
146
|
):
|
|
138
147
|
# type: (...) -> None
|
|
139
148
|
f.write(json_dumps(self.headers))
|
|
@@ -149,7 +158,8 @@ class Envelope:
|
|
|
149
158
|
|
|
150
159
|
@classmethod
|
|
151
160
|
def deserialize_from(
|
|
152
|
-
cls,
|
|
161
|
+
cls,
|
|
162
|
+
f, # type: Any
|
|
153
163
|
):
|
|
154
164
|
# type: (...) -> Envelope
|
|
155
165
|
headers = parse_json(f.readline())
|
|
@@ -163,7 +173,8 @@ class Envelope:
|
|
|
163
173
|
|
|
164
174
|
@classmethod
|
|
165
175
|
def deserialize(
|
|
166
|
-
cls,
|
|
176
|
+
cls,
|
|
177
|
+
bytes, # type: bytes
|
|
167
178
|
):
|
|
168
179
|
# type: (...) -> Envelope
|
|
169
180
|
return cls.deserialize_from(io.BytesIO(bytes))
|
|
@@ -274,14 +285,14 @@ class Item:
|
|
|
274
285
|
return "error"
|
|
275
286
|
elif ty == "log":
|
|
276
287
|
return "log_item"
|
|
288
|
+
elif ty == "trace_metric":
|
|
289
|
+
return "trace_metric"
|
|
277
290
|
elif ty == "client_report":
|
|
278
291
|
return "internal"
|
|
279
292
|
elif ty == "profile":
|
|
280
293
|
return "profile"
|
|
281
294
|
elif ty == "profile_chunk":
|
|
282
295
|
return "profile_chunk"
|
|
283
|
-
elif ty == "statsd":
|
|
284
|
-
return "metric_bucket"
|
|
285
296
|
elif ty == "check_in":
|
|
286
297
|
return "monitor"
|
|
287
298
|
else:
|
|
@@ -307,7 +318,8 @@ class Item:
|
|
|
307
318
|
return None
|
|
308
319
|
|
|
309
320
|
def serialize_into(
|
|
310
|
-
self,
|
|
321
|
+
self,
|
|
322
|
+
f, # type: Any
|
|
311
323
|
):
|
|
312
324
|
# type: (...) -> None
|
|
313
325
|
headers = dict(self.headers)
|
|
@@ -326,7 +338,8 @@ class Item:
|
|
|
326
338
|
|
|
327
339
|
@classmethod
|
|
328
340
|
def deserialize_from(
|
|
329
|
-
cls,
|
|
341
|
+
cls,
|
|
342
|
+
f, # type: Any
|
|
330
343
|
):
|
|
331
344
|
# type: (...) -> Optional[Item]
|
|
332
345
|
line = f.readline().rstrip()
|
|
@@ -341,7 +354,7 @@ class Item:
|
|
|
341
354
|
# if no length was specified we need to read up to the end of line
|
|
342
355
|
# and remove it (if it is present, i.e. not the very last char in an eof terminated envelope)
|
|
343
356
|
payload = f.readline().rstrip(b"\n")
|
|
344
|
-
if headers.get("type") in ("event", "transaction"
|
|
357
|
+
if headers.get("type") in ("event", "transaction"):
|
|
345
358
|
rv = cls(headers=headers, payload=PayloadRef(json=parse_json(payload)))
|
|
346
359
|
else:
|
|
347
360
|
rv = cls(headers=headers, payload=payload)
|
|
@@ -349,7 +362,8 @@ class Item:
|
|
|
349
362
|
|
|
350
363
|
@classmethod
|
|
351
364
|
def deserialize(
|
|
352
|
-
cls,
|
|
365
|
+
cls,
|
|
366
|
+
bytes, # type: bytes
|
|
353
367
|
):
|
|
354
368
|
# type: (...) -> Optional[Item]
|
|
355
369
|
return cls.deserialize_from(io.BytesIO(bytes))
|
sentry_sdk/feature_flags.py
CHANGED
sentry_sdk/hub.py
CHANGED
|
@@ -205,7 +205,8 @@ class Hub(with_metaclass(HubMeta)): # type: ignore
|
|
|
205
205
|
scope._isolation_scope.set(old_isolation_scope)
|
|
206
206
|
|
|
207
207
|
def run(
|
|
208
|
-
self,
|
|
208
|
+
self,
|
|
209
|
+
callback, # type: Callable[[], T]
|
|
209
210
|
):
|
|
210
211
|
# type: (...) -> T
|
|
211
212
|
"""
|
|
@@ -219,7 +220,8 @@ class Hub(with_metaclass(HubMeta)): # type: ignore
|
|
|
219
220
|
return callback()
|
|
220
221
|
|
|
221
222
|
def get_integration(
|
|
222
|
-
self,
|
|
223
|
+
self,
|
|
224
|
+
name_or_class, # type: Union[str, Type[Integration]]
|
|
223
225
|
):
|
|
224
226
|
# type: (...) -> Any
|
|
225
227
|
"""
|
|
@@ -277,7 +279,8 @@ class Hub(with_metaclass(HubMeta)): # type: ignore
|
|
|
277
279
|
return self._last_event_id
|
|
278
280
|
|
|
279
281
|
def bind_client(
|
|
280
|
-
self,
|
|
282
|
+
self,
|
|
283
|
+
new, # type: Optional[BaseClient]
|
|
281
284
|
):
|
|
282
285
|
# type: (...) -> None
|
|
283
286
|
"""
|
|
@@ -430,7 +433,7 @@ class Hub(with_metaclass(HubMeta)): # type: ignore
|
|
|
430
433
|
transaction=None,
|
|
431
434
|
instrumenter=INSTRUMENTER.SENTRY,
|
|
432
435
|
custom_sampling_context=None,
|
|
433
|
-
**kwargs
|
|
436
|
+
**kwargs,
|
|
434
437
|
):
|
|
435
438
|
# type: (Optional[Transaction], str, Optional[SamplingContext], Unpack[TransactionKwargs]) -> Union[Transaction, NoOpSpan]
|
|
436
439
|
"""
|
|
@@ -487,14 +490,16 @@ class Hub(with_metaclass(HubMeta)): # type: ignore
|
|
|
487
490
|
|
|
488
491
|
@overload
|
|
489
492
|
def push_scope(
|
|
490
|
-
self,
|
|
493
|
+
self,
|
|
494
|
+
callback=None, # type: Optional[None]
|
|
491
495
|
):
|
|
492
496
|
# type: (...) -> ContextManager[Scope]
|
|
493
497
|
pass
|
|
494
498
|
|
|
495
499
|
@overload
|
|
496
500
|
def push_scope( # noqa: F811
|
|
497
|
-
self,
|
|
501
|
+
self,
|
|
502
|
+
callback, # type: Callable[[Scope], None]
|
|
498
503
|
):
|
|
499
504
|
# type: (...) -> None
|
|
500
505
|
pass
|
|
@@ -540,14 +545,16 @@ class Hub(with_metaclass(HubMeta)): # type: ignore
|
|
|
540
545
|
|
|
541
546
|
@overload
|
|
542
547
|
def configure_scope(
|
|
543
|
-
self,
|
|
548
|
+
self,
|
|
549
|
+
callback=None, # type: Optional[None]
|
|
544
550
|
):
|
|
545
551
|
# type: (...) -> ContextManager[Scope]
|
|
546
552
|
pass
|
|
547
553
|
|
|
548
554
|
@overload
|
|
549
555
|
def configure_scope( # noqa: F811
|
|
550
|
-
self,
|
|
556
|
+
self,
|
|
557
|
+
callback, # type: Callable[[Scope], None]
|
|
551
558
|
):
|
|
552
559
|
# type: (...) -> None
|
|
553
560
|
pass
|
|
@@ -587,7 +594,8 @@ class Hub(with_metaclass(HubMeta)): # type: ignore
|
|
|
587
594
|
return inner()
|
|
588
595
|
|
|
589
596
|
def start_session(
|
|
590
|
-
self,
|
|
597
|
+
self,
|
|
598
|
+
session_mode="application", # type: str
|
|
591
599
|
):
|
|
592
600
|
# type: (...) -> None
|
|
593
601
|
"""
|
|
@@ -3,7 +3,11 @@ from typing import TYPE_CHECKING
|
|
|
3
3
|
|
|
4
4
|
import sentry_sdk
|
|
5
5
|
from sentry_sdk.ai.monitoring import record_token_usage
|
|
6
|
-
from sentry_sdk.ai.utils import
|
|
6
|
+
from sentry_sdk.ai.utils import (
|
|
7
|
+
set_data_normalized,
|
|
8
|
+
normalize_message_roles,
|
|
9
|
+
get_start_span_function,
|
|
10
|
+
)
|
|
7
11
|
from sentry_sdk.consts import OP, SPANDATA, SPANSTATUS
|
|
8
12
|
from sentry_sdk.integrations import _check_minimum_version, DidNotEnable, Integration
|
|
9
13
|
from sentry_sdk.scope import should_send_default_pii
|
|
@@ -140,8 +144,12 @@ def _set_input_data(span, kwargs, integration):
|
|
|
140
144
|
else:
|
|
141
145
|
normalized_messages.append(message)
|
|
142
146
|
|
|
147
|
+
role_normalized_messages = normalize_message_roles(normalized_messages)
|
|
143
148
|
set_data_normalized(
|
|
144
|
-
span,
|
|
149
|
+
span,
|
|
150
|
+
SPANDATA.GEN_AI_REQUEST_MESSAGES,
|
|
151
|
+
role_normalized_messages,
|
|
152
|
+
unpack=False,
|
|
145
153
|
)
|
|
146
154
|
|
|
147
155
|
set_data_normalized(
|
sentry_sdk/integrations/asgi.py
CHANGED
|
@@ -233,14 +233,15 @@ class SentryAsgiMiddleware:
|
|
|
233
233
|
if transaction:
|
|
234
234
|
transaction.set_tag("asgi.type", ty)
|
|
235
235
|
|
|
236
|
-
|
|
236
|
+
transaction_context = (
|
|
237
237
|
sentry_sdk.start_transaction(
|
|
238
238
|
transaction,
|
|
239
239
|
custom_sampling_context={"asgi_scope": scope},
|
|
240
240
|
)
|
|
241
241
|
if transaction is not None
|
|
242
242
|
else nullcontext()
|
|
243
|
-
)
|
|
243
|
+
)
|
|
244
|
+
with transaction_context:
|
|
244
245
|
try:
|
|
245
246
|
|
|
246
247
|
async def _sentry_wrapped_send(event):
|
|
@@ -1,18 +1,31 @@
|
|
|
1
1
|
import json
|
|
2
2
|
|
|
3
3
|
import sentry_sdk
|
|
4
|
-
from sentry_sdk.
|
|
4
|
+
from sentry_sdk.consts import OP, SPANSTATUS
|
|
5
|
+
from sentry_sdk.api import continue_trace, get_baggage, get_traceparent
|
|
6
|
+
from sentry_sdk.integrations import Integration, DidNotEnable
|
|
5
7
|
from sentry_sdk.integrations._wsgi_common import request_body_within_bounds
|
|
8
|
+
from sentry_sdk.tracing import (
|
|
9
|
+
BAGGAGE_HEADER_NAME,
|
|
10
|
+
SENTRY_TRACE_HEADER_NAME,
|
|
11
|
+
TransactionSource,
|
|
12
|
+
)
|
|
6
13
|
from sentry_sdk.utils import (
|
|
7
14
|
AnnotatedValue,
|
|
8
15
|
capture_internal_exceptions,
|
|
9
16
|
event_from_exception,
|
|
10
17
|
)
|
|
18
|
+
from typing import TypeVar
|
|
19
|
+
|
|
20
|
+
R = TypeVar("R")
|
|
11
21
|
|
|
12
|
-
|
|
13
|
-
from dramatiq.
|
|
14
|
-
from dramatiq.middleware import Middleware, default_middleware
|
|
15
|
-
from dramatiq.errors import Retry
|
|
22
|
+
try:
|
|
23
|
+
from dramatiq.broker import Broker
|
|
24
|
+
from dramatiq.middleware import Middleware, default_middleware
|
|
25
|
+
from dramatiq.errors import Retry
|
|
26
|
+
from dramatiq.message import Message
|
|
27
|
+
except ImportError:
|
|
28
|
+
raise DidNotEnable("Dramatiq is not installed")
|
|
16
29
|
|
|
17
30
|
from typing import TYPE_CHECKING
|
|
18
31
|
|
|
@@ -34,10 +47,12 @@ class DramatiqIntegration(Integration):
|
|
|
34
47
|
"""
|
|
35
48
|
|
|
36
49
|
identifier = "dramatiq"
|
|
50
|
+
origin = f"auto.queue.{identifier}"
|
|
37
51
|
|
|
38
52
|
@staticmethod
|
|
39
53
|
def setup_once():
|
|
40
54
|
# type: () -> None
|
|
55
|
+
|
|
41
56
|
_patch_dramatiq_broker()
|
|
42
57
|
|
|
43
58
|
|
|
@@ -85,22 +100,54 @@ class SentryMiddleware(Middleware): # type: ignore[misc]
|
|
|
85
100
|
DramatiqIntegration.
|
|
86
101
|
"""
|
|
87
102
|
|
|
88
|
-
|
|
89
|
-
|
|
103
|
+
SENTRY_HEADERS_NAME = "_sentry_headers"
|
|
104
|
+
|
|
105
|
+
def before_enqueue(self, broker, message, delay):
|
|
106
|
+
# type: (Broker, Message[R], int) -> None
|
|
90
107
|
integration = sentry_sdk.get_client().get_integration(DramatiqIntegration)
|
|
91
108
|
if integration is None:
|
|
92
109
|
return
|
|
93
110
|
|
|
94
|
-
message.
|
|
95
|
-
|
|
111
|
+
message.options[self.SENTRY_HEADERS_NAME] = {
|
|
112
|
+
BAGGAGE_HEADER_NAME: get_baggage(),
|
|
113
|
+
SENTRY_TRACE_HEADER_NAME: get_traceparent(),
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
def before_process_message(self, broker, message):
|
|
117
|
+
# type: (Broker, Message[R]) -> None
|
|
118
|
+
integration = sentry_sdk.get_client().get_integration(DramatiqIntegration)
|
|
119
|
+
if integration is None:
|
|
120
|
+
return
|
|
96
121
|
|
|
97
|
-
|
|
98
|
-
scope.
|
|
122
|
+
message._scope_manager = sentry_sdk.isolation_scope()
|
|
123
|
+
scope = message._scope_manager.__enter__()
|
|
124
|
+
scope.clear_breadcrumbs()
|
|
99
125
|
scope.set_extra("dramatiq_message_id", message.message_id)
|
|
100
126
|
scope.add_event_processor(_make_message_event_processor(message, integration))
|
|
101
127
|
|
|
128
|
+
sentry_headers = message.options.get(self.SENTRY_HEADERS_NAME) or {}
|
|
129
|
+
if "retries" in message.options:
|
|
130
|
+
# start new trace in case of retrying
|
|
131
|
+
sentry_headers = {}
|
|
132
|
+
|
|
133
|
+
transaction = continue_trace(
|
|
134
|
+
sentry_headers,
|
|
135
|
+
name=message.actor_name,
|
|
136
|
+
op=OP.QUEUE_TASK_DRAMATIQ,
|
|
137
|
+
source=TransactionSource.TASK,
|
|
138
|
+
origin=DramatiqIntegration.origin,
|
|
139
|
+
)
|
|
140
|
+
transaction.set_status(SPANSTATUS.OK)
|
|
141
|
+
sentry_sdk.start_transaction(
|
|
142
|
+
transaction,
|
|
143
|
+
name=message.actor_name,
|
|
144
|
+
op=OP.QUEUE_TASK_DRAMATIQ,
|
|
145
|
+
source=TransactionSource.TASK,
|
|
146
|
+
)
|
|
147
|
+
transaction.__enter__()
|
|
148
|
+
|
|
102
149
|
def after_process_message(self, broker, message, *, result=None, exception=None):
|
|
103
|
-
# type: (Broker, Message,
|
|
150
|
+
# type: (Broker, Message[R], Optional[Any], Optional[Exception]) -> None
|
|
104
151
|
integration = sentry_sdk.get_client().get_integration(DramatiqIntegration)
|
|
105
152
|
if integration is None:
|
|
106
153
|
return
|
|
@@ -108,27 +155,38 @@ class SentryMiddleware(Middleware): # type: ignore[misc]
|
|
|
108
155
|
actor = broker.get_actor(message.actor_name)
|
|
109
156
|
throws = message.options.get("throws") or actor.options.get("throws")
|
|
110
157
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
158
|
+
scope_manager = message._scope_manager
|
|
159
|
+
transaction = sentry_sdk.get_current_scope().transaction
|
|
160
|
+
if not transaction:
|
|
161
|
+
return None
|
|
162
|
+
|
|
163
|
+
is_event_capture_required = (
|
|
164
|
+
exception is not None
|
|
165
|
+
and not (throws and isinstance(exception, throws))
|
|
166
|
+
and not isinstance(exception, Retry)
|
|
167
|
+
)
|
|
168
|
+
if not is_event_capture_required:
|
|
169
|
+
# normal transaction finish
|
|
170
|
+
transaction.__exit__(None, None, None)
|
|
171
|
+
scope_manager.__exit__(None, None, None)
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
event, hint = event_from_exception(
|
|
175
|
+
exception, # type: ignore[arg-type]
|
|
176
|
+
client_options=sentry_sdk.get_client().options,
|
|
177
|
+
mechanism={
|
|
178
|
+
"type": DramatiqIntegration.identifier,
|
|
179
|
+
"handled": False,
|
|
180
|
+
},
|
|
181
|
+
)
|
|
182
|
+
sentry_sdk.capture_event(event, hint=hint)
|
|
183
|
+
# transaction error
|
|
184
|
+
transaction.__exit__(type(exception), exception, None)
|
|
185
|
+
scope_manager.__exit__(type(exception), exception, None)
|
|
128
186
|
|
|
129
187
|
|
|
130
188
|
def _make_message_event_processor(message, integration):
|
|
131
|
-
# type: (Message, DramatiqIntegration) -> Callable[[Event, Hint], Optional[Event]]
|
|
189
|
+
# type: (Message[R], DramatiqIntegration) -> Callable[[Event, Hint], Optional[Event]]
|
|
132
190
|
|
|
133
191
|
def inner(event, hint):
|
|
134
192
|
# type: (Event, Hint) -> Optional[Event]
|
|
@@ -142,7 +200,7 @@ def _make_message_event_processor(message, integration):
|
|
|
142
200
|
|
|
143
201
|
class DramatiqMessageExtractor:
|
|
144
202
|
def __init__(self, message):
|
|
145
|
-
# type: (Message) -> None
|
|
203
|
+
# type: (Message[R]) -> None
|
|
146
204
|
self.message_data = dict(message.asdict())
|
|
147
205
|
|
|
148
206
|
def content_length(self):
|
|
@@ -65,7 +65,8 @@ class SentryUnaryUnaryClientInterceptor(ClientInterceptor, UnaryUnaryClientInter
|
|
|
65
65
|
|
|
66
66
|
|
|
67
67
|
class SentryUnaryStreamClientInterceptor(
|
|
68
|
-
ClientInterceptor,
|
|
68
|
+
ClientInterceptor,
|
|
69
|
+
UnaryStreamClientInterceptor, # type: ignore
|
|
69
70
|
):
|
|
70
71
|
async def intercept_unary_stream(
|
|
71
72
|
self,
|
|
@@ -19,7 +19,8 @@ except ImportError:
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class ClientInterceptor(
|
|
22
|
-
grpc.UnaryUnaryClientInterceptor,
|
|
22
|
+
grpc.UnaryUnaryClientInterceptor, # type: ignore
|
|
23
|
+
grpc.UnaryStreamClientInterceptor, # type: ignore
|
|
23
24
|
):
|
|
24
25
|
_is_intercepted = False
|
|
25
26
|
|
|
@@ -60,9 +61,7 @@ class ClientInterceptor(
|
|
|
60
61
|
client_call_details
|
|
61
62
|
)
|
|
62
63
|
|
|
63
|
-
response = continuation(
|
|
64
|
-
client_call_details, request
|
|
65
|
-
) # type: UnaryStreamCall
|
|
64
|
+
response = continuation(client_call_details, request) # type: UnaryStreamCall
|
|
66
65
|
# Setting code on unary-stream leads to execution getting stuck
|
|
67
66
|
# span.set_data("code", response.code().name)
|
|
68
67
|
|
|
@@ -4,7 +4,12 @@ from functools import wraps
|
|
|
4
4
|
|
|
5
5
|
import sentry_sdk
|
|
6
6
|
from sentry_sdk.ai.monitoring import set_ai_pipeline_name
|
|
7
|
-
from sentry_sdk.ai.utils import
|
|
7
|
+
from sentry_sdk.ai.utils import (
|
|
8
|
+
GEN_AI_ALLOWED_MESSAGE_ROLES,
|
|
9
|
+
normalize_message_roles,
|
|
10
|
+
set_data_normalized,
|
|
11
|
+
get_start_span_function,
|
|
12
|
+
)
|
|
8
13
|
from sentry_sdk.consts import OP, SPANDATA
|
|
9
14
|
from sentry_sdk.integrations import DidNotEnable, Integration
|
|
10
15
|
from sentry_sdk.scope import should_send_default_pii
|
|
@@ -209,8 +214,18 @@ class SentryLangchainCallback(BaseCallbackHandler): # type: ignore[misc]
|
|
|
209
214
|
_set_tools_on_span(span, all_params.get("tools"))
|
|
210
215
|
|
|
211
216
|
if should_send_default_pii() and self.include_prompts:
|
|
217
|
+
normalized_messages = [
|
|
218
|
+
{
|
|
219
|
+
"role": GEN_AI_ALLOWED_MESSAGE_ROLES.USER,
|
|
220
|
+
"content": {"type": "text", "text": prompt},
|
|
221
|
+
}
|
|
222
|
+
for prompt in prompts
|
|
223
|
+
]
|
|
212
224
|
set_data_normalized(
|
|
213
|
-
span,
|
|
225
|
+
span,
|
|
226
|
+
SPANDATA.GEN_AI_REQUEST_MESSAGES,
|
|
227
|
+
normalized_messages,
|
|
228
|
+
unpack=False,
|
|
214
229
|
)
|
|
215
230
|
|
|
216
231
|
def on_chat_model_start(self, serialized, messages, *, run_id, **kwargs):
|
|
@@ -262,6 +277,8 @@ class SentryLangchainCallback(BaseCallbackHandler): # type: ignore[misc]
|
|
|
262
277
|
normalized_messages.append(
|
|
263
278
|
self._normalize_langchain_message(message)
|
|
264
279
|
)
|
|
280
|
+
normalized_messages = normalize_message_roles(normalized_messages)
|
|
281
|
+
|
|
265
282
|
set_data_normalized(
|
|
266
283
|
span,
|
|
267
284
|
SPANDATA.GEN_AI_REQUEST_MESSAGES,
|
|
@@ -554,7 +571,6 @@ def _simplify_langchain_tools(tools):
|
|
|
554
571
|
for tool in tools:
|
|
555
572
|
try:
|
|
556
573
|
if isinstance(tool, dict):
|
|
557
|
-
|
|
558
574
|
if "function" in tool and isinstance(tool["function"], dict):
|
|
559
575
|
func = tool["function"]
|
|
560
576
|
simplified_tool = {
|
|
@@ -741,8 +757,12 @@ def _wrap_agent_executor_invoke(f):
|
|
|
741
757
|
and should_send_default_pii()
|
|
742
758
|
and integration.include_prompts
|
|
743
759
|
):
|
|
760
|
+
normalized_messages = normalize_message_roles([input])
|
|
744
761
|
set_data_normalized(
|
|
745
|
-
span,
|
|
762
|
+
span,
|
|
763
|
+
SPANDATA.GEN_AI_REQUEST_MESSAGES,
|
|
764
|
+
normalized_messages,
|
|
765
|
+
unpack=False,
|
|
746
766
|
)
|
|
747
767
|
|
|
748
768
|
output = result.get("output")
|
|
@@ -792,8 +812,12 @@ def _wrap_agent_executor_stream(f):
|
|
|
792
812
|
and should_send_default_pii()
|
|
793
813
|
and integration.include_prompts
|
|
794
814
|
):
|
|
815
|
+
normalized_messages = normalize_message_roles([input])
|
|
795
816
|
set_data_normalized(
|
|
796
|
-
span,
|
|
817
|
+
span,
|
|
818
|
+
SPANDATA.GEN_AI_REQUEST_MESSAGES,
|
|
819
|
+
normalized_messages,
|
|
820
|
+
unpack=False,
|
|
797
821
|
)
|
|
798
822
|
|
|
799
823
|
# Run the agent
|
|
@@ -2,7 +2,7 @@ from functools import wraps
|
|
|
2
2
|
from typing import Any, Callable, List, Optional
|
|
3
3
|
|
|
4
4
|
import sentry_sdk
|
|
5
|
-
from sentry_sdk.ai.utils import set_data_normalized
|
|
5
|
+
from sentry_sdk.ai.utils import set_data_normalized, normalize_message_roles
|
|
6
6
|
from sentry_sdk.consts import OP, SPANDATA
|
|
7
7
|
from sentry_sdk.integrations import DidNotEnable, Integration
|
|
8
8
|
from sentry_sdk.scope import should_send_default_pii
|
|
@@ -180,10 +180,11 @@ def _wrap_pregel_invoke(f):
|
|
|
180
180
|
):
|
|
181
181
|
input_messages = _parse_langgraph_messages(args[0])
|
|
182
182
|
if input_messages:
|
|
183
|
+
normalized_input_messages = normalize_message_roles(input_messages)
|
|
183
184
|
set_data_normalized(
|
|
184
185
|
span,
|
|
185
186
|
SPANDATA.GEN_AI_REQUEST_MESSAGES,
|
|
186
|
-
|
|
187
|
+
normalized_input_messages,
|
|
187
188
|
unpack=False,
|
|
188
189
|
)
|
|
189
190
|
|
|
@@ -230,10 +231,11 @@ def _wrap_pregel_ainvoke(f):
|
|
|
230
231
|
):
|
|
231
232
|
input_messages = _parse_langgraph_messages(args[0])
|
|
232
233
|
if input_messages:
|
|
234
|
+
normalized_input_messages = normalize_message_roles(input_messages)
|
|
233
235
|
set_data_normalized(
|
|
234
236
|
span,
|
|
235
237
|
SPANDATA.GEN_AI_REQUEST_MESSAGES,
|
|
236
|
-
|
|
238
|
+
normalized_input_messages,
|
|
237
239
|
unpack=False,
|
|
238
240
|
)
|
|
239
241
|
|