Skip to content

Commit bf0393b

Browse files
committed
Merge branch 'master' into potel-base
2 parents 2770c4c + cb82483 commit bf0393b

File tree

11 files changed

+151
-82
lines changed

11 files changed

+151
-82
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
_Bad software is everywhere, and we're tired of it. Sentry is on a mission to help developers write better software faster, so we can get back to enjoying technology. If you want to join us
77
[<kbd>**Check out our open positions**</kbd>](https://sentry.io/careers/)_.
88

9-
[![Discord](https://img.shields.io/discord/621778831602221064?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb)](https://discord.gg/wdNEHETs87)
9+
[![Discord](https://img.shields.io/discord/621778831602221064?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb)](https://discord.com/invite/Ww9hbqr)
1010
[![Twitter Follow](https://img.shields.io/twitter/follow/getsentry?label=@getsentry&style=social)](https://twitter.com/intent/follow?screen_name=getsentry)
1111
[![PyPi page link -- version](https://img.shields.io/pypi/v/sentry-sdk.svg)](https://pypi.python.org/pypi/sentry-sdk)
1212
<img src="https://img.shields.io/badge/python-3.7 | 3.8 | 3.9 | 3.10 | 3.11 | 3.12 | 3.13-blue.svg" alt="python">
@@ -106,7 +106,7 @@ If you encounter issues or need help setting up or configuring the SDK, don't he
106106
Here are all resources to help you make the most of Sentry:
107107

108108
- [Documentation](https://docs.sentry.io/platforms/python/) - Official documentation to get started.
109-
- [Discord](https://img.shields.io/discord/621778831602221064) - Join our Discord community.
109+
- [Discord](https://discord.com/invite/Ww9hbqr) - Join our Discord community.
110110
- [X/Twitter](https://twitter.com/intent/follow?screen_name=getsentry) - Follow us on X (Twitter) for updates.
111111
- [Stack Overflow](https://stackoverflow.com/questions/tagged/sentry) - Questions and answers related to Sentry.
112112

sentry_sdk/ai/monitoring.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import inspect
22
from functools import wraps
33

4+
from sentry_sdk.consts import SPANDATA
45
import sentry_sdk.utils
56
from sentry_sdk import start_span
67
from sentry_sdk.tracing import Span
@@ -41,7 +42,7 @@ def sync_wrapped(*args, **kwargs):
4142
for k, v in kwargs.pop("sentry_data", {}).items():
4243
span.set_attribute(k, v)
4344
if curr_pipeline:
44-
span.set_attribute("ai.pipeline.name", curr_pipeline)
45+
span.set_attribute(SPANDATA.AI_PIPELINE_NAME, curr_pipeline)
4546
return f(*args, **kwargs)
4647
else:
4748
_ai_pipeline_name.set(description)
@@ -72,7 +73,7 @@ async def async_wrapped(*args, **kwargs):
7273
for k, v in kwargs.pop("sentry_data", {}).items():
7374
span.set_attribute(k, v)
7475
if curr_pipeline:
75-
span.set_attribute("ai.pipeline.name", curr_pipeline)
76+
span.set_attribute(SPANDATA.AI_PIPELINE_NAME, curr_pipeline)
7677
return await f(*args, **kwargs)
7778
else:
7879
_ai_pipeline_name.set(description)
@@ -104,7 +105,7 @@ def record_token_usage(
104105
# type: (Span, Optional[int], Optional[int], Optional[int]) -> None
105106
ai_pipeline_name = get_ai_pipeline_name()
106107
if ai_pipeline_name:
107-
span.set_attribute("ai.pipeline.name", ai_pipeline_name)
108+
span.set_attribute(SPANDATA.AI_PIPELINE_NAME, ai_pipeline_name)
108109
if prompt_tokens is not None:
109110
span.set_attribute("ai.prompt_tokens.used", prompt_tokens)
110111
if completion_tokens is not None:

sentry_sdk/consts.py

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ class SPANDATA:
174174
For an AI model call, the format of the response
175175
"""
176176

177-
AI_LOGIT_BIAS = "ai.response_format"
177+
AI_LOGIT_BIAS = "ai.logit_bias"
178178
"""
179179
For an AI model call, the logit bias
180180
"""
@@ -191,7 +191,6 @@ class SPANDATA:
191191
Minimize pre-processing done to the prompt sent to the LLM.
192192
Example: true
193193
"""
194-
195194
AI_RESPONSES = "ai.responses"
196195
"""
197196
The responses to an AI model call. Always as a list.
@@ -204,6 +203,66 @@ class SPANDATA:
204203
Example: 123.45
205204
"""
206205

206+
AI_CITATIONS = "ai.citations"
207+
"""
208+
References or sources cited by the AI model in its response.
209+
Example: ["Smith et al. 2020", "Jones 2019"]
210+
"""
211+
212+
AI_DOCUMENTS = "ai.documents"
213+
"""
214+
Documents or content chunks used as context for the AI model.
215+
Example: ["doc1.txt", "doc2.pdf"]
216+
"""
217+
218+
AI_SEARCH_QUERIES = "ai.search_queries"
219+
"""
220+
Queries used to search for relevant context or documents.
221+
Example: ["climate change effects", "renewable energy"]
222+
"""
223+
224+
AI_SEARCH_RESULTS = "ai.search_results"
225+
"""
226+
Results returned from search queries for context.
227+
Example: ["Result 1", "Result 2"]
228+
"""
229+
230+
AI_GENERATION_ID = "ai.generation_id"
231+
"""
232+
Unique identifier for the completion.
233+
Example: "gen_123abc"
234+
"""
235+
236+
AI_SEARCH_REQUIRED = "ai.is_search_required"
237+
"""
238+
Boolean indicating if the model needs to perform a search.
239+
Example: true
240+
"""
241+
242+
AI_FINISH_REASON = "ai.finish_reason"
243+
"""
244+
The reason why the model stopped generating.
245+
Example: "length"
246+
"""
247+
248+
AI_PIPELINE_NAME = "ai.pipeline.name"
249+
"""
250+
Name of the AI pipeline or chain being executed.
251+
Example: "qa-pipeline"
252+
"""
253+
254+
AI_TEXTS = "ai.texts"
255+
"""
256+
Raw text inputs provided to the model.
257+
Example: ["What is machine learning?"]
258+
"""
259+
260+
AI_WARNINGS = "ai.warnings"
261+
"""
262+
Warning messages generated during model execution.
263+
Example: ["Token limit exceeded"]
264+
"""
265+
207266
DB_NAME = "db.name"
208267
"""
209268
The name of the database being accessed. For commands that switch the database, this should be set to the target database (even if the command fails).

sentry_sdk/integrations/cohere.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,17 @@
5252
}
5353

5454
COLLECTED_CHAT_RESP_ATTRS = {
55-
"generation_id": "ai.generation_id",
56-
"is_search_required": "ai.is_search_required",
57-
"finish_reason": "ai.finish_reason",
55+
"generation_id": SPANDATA.AI_GENERATION_ID,
56+
"is_search_required": SPANDATA.AI_SEARCH_REQUIRED,
57+
"finish_reason": SPANDATA.AI_FINISH_REASON,
5858
}
5959

6060
COLLECTED_PII_CHAT_RESP_ATTRS = {
61-
"citations": "ai.citations",
62-
"documents": "ai.documents",
63-
"search_queries": "ai.search_queries",
64-
"search_results": "ai.search_results",
65-
"tool_calls": "ai.tool_calls",
61+
"citations": SPANDATA.AI_CITATIONS,
62+
"documents": SPANDATA.AI_DOCUMENTS,
63+
"search_queries": SPANDATA.AI_SEARCH_QUERIES,
64+
"search_results": SPANDATA.AI_SEARCH_RESULTS,
65+
"tool_calls": SPANDATA.AI_TOOL_CALLS,
6666
}
6767

6868

@@ -127,7 +127,7 @@ def collect_chat_response_fields(span, res, include_pii):
127127
)
128128

129129
if hasattr(res.meta, "warnings"):
130-
set_data_normalized(span, "ai.warnings", res.meta.warnings)
130+
set_data_normalized(span, SPANDATA.AI_WARNINGS, res.meta.warnings)
131131

132132
@wraps(f)
133133
def new_chat(*args, **kwargs):
@@ -240,7 +240,7 @@ def new_embed(*args, **kwargs):
240240
should_send_default_pii() and integration.include_prompts
241241
):
242242
if isinstance(kwargs["texts"], str):
243-
set_data_normalized(span, "ai.texts", [kwargs["texts"]])
243+
set_data_normalized(span, SPANDATA.AI_TEXTS, [kwargs["texts"]])
244244
elif (
245245
isinstance(kwargs["texts"], list)
246246
and len(kwargs["texts"]) > 0

sentry_sdk/integrations/huggingface_hub.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ def new_text_generation(*args, **kwargs):
9898
if should_send_default_pii() and integration.include_prompts:
9999
set_data_normalized(
100100
span,
101-
"ai.responses",
101+
SPANDATA.AI_RESPONSES,
102102
[res],
103103
)
104104
span.__exit__(None, None, None)
@@ -108,7 +108,7 @@ def new_text_generation(*args, **kwargs):
108108
if should_send_default_pii() and integration.include_prompts:
109109
set_data_normalized(
110110
span,
111-
"ai.responses",
111+
SPANDATA.AI_RESPONSES,
112112
[res.generated_text],
113113
)
114114
if res.details is not None and res.details.generated_tokens > 0:

sentry_sdk/integrations/openai.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def _new_chat_completion_common(f, *args, **kwargs):
156156
if should_send_default_pii() and integration.include_prompts:
157157
set_data_normalized(
158158
span,
159-
"ai.responses",
159+
SPANDATA.AI_RESPONSES,
160160
list(map(lambda x: x.message, res.choices)),
161161
)
162162
_calculate_chat_completion_usage(
@@ -331,15 +331,15 @@ def _new_embeddings_create_common(f, *args, **kwargs):
331331
should_send_default_pii() and integration.include_prompts
332332
):
333333
if isinstance(kwargs["input"], str):
334-
set_data_normalized(span, "ai.input_messages", [kwargs["input"]])
334+
set_data_normalized(span, SPANDATA.AI_INPUT_MESSAGES, [kwargs["input"]])
335335
elif (
336336
isinstance(kwargs["input"], list)
337337
and len(kwargs["input"]) > 0
338338
and isinstance(kwargs["input"][0], str)
339339
):
340-
set_data_normalized(span, "ai.input_messages", kwargs["input"])
340+
set_data_normalized(span, SPANDATA.AI_INPUT_MESSAGES, kwargs["input"])
341341
if "model" in kwargs:
342-
set_data_normalized(span, "ai.model_id", kwargs["model"])
342+
set_data_normalized(span, SPANDATA.AI_MODEL_ID, kwargs["model"])
343343

344344
response = yield f, args, kwargs
345345

tests/integrations/anthropic/test_anthropic.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ def test_nonstreaming_create_message(
130130
assert span["data"]["ai.prompt_tokens.used"] == 10
131131
assert span["data"]["ai.completion_tokens.used"] == 20
132132
assert span["data"]["ai.total_tokens.used"] == 30
133-
assert span["data"]["ai.streaming"] is False
133+
assert span["data"][SPANDATA.AI_STREAMING] is False
134134

135135

136136
@pytest.mark.asyncio
@@ -200,7 +200,7 @@ async def test_nonstreaming_create_message_async(
200200
assert span["data"]["ai.prompt_tokens.used"] == 10
201201
assert span["data"]["ai.completion_tokens.used"] == 20
202202
assert span["data"]["ai.total_tokens.used"] == 30
203-
assert span["data"]["ai.streaming"] is False
203+
assert span["data"][SPANDATA.AI_STREAMING] is False
204204

205205

206206
@pytest.mark.parametrize(
@@ -302,7 +302,7 @@ def test_streaming_create_message(
302302
assert span["data"]["ai.prompt_tokens.used"] == 10
303303
assert span["data"]["ai.completion_tokens.used"] == 30
304304
assert span["data"]["ai.total_tokens.used"] == 40
305-
assert span["data"]["ai.streaming"] is True
305+
assert span["data"][SPANDATA.AI_STREAMING] is True
306306

307307

308308
@pytest.mark.asyncio
@@ -407,7 +407,7 @@ async def test_streaming_create_message_async(
407407
assert span["data"]["ai.prompt_tokens.used"] == 10
408408
assert span["data"]["ai.completion_tokens.used"] == 30
409409
assert span["data"]["ai.total_tokens.used"] == 40
410-
assert span["data"]["ai.streaming"] is True
410+
assert span["data"][SPANDATA.AI_STREAMING] is True
411411

412412

413413
@pytest.mark.skipif(
@@ -539,7 +539,7 @@ def test_streaming_create_message_with_input_json_delta(
539539
assert span["data"]["ai.prompt_tokens.used"] == 366
540540
assert span["data"]["ai.completion_tokens.used"] == 51
541541
assert span["data"]["ai.total_tokens.used"] == 417
542-
assert span["data"]["ai.streaming"] is True
542+
assert span["data"][SPANDATA.AI_STREAMING] is True
543543

544544

545545
@pytest.mark.asyncio
@@ -678,7 +678,7 @@ async def test_streaming_create_message_with_input_json_delta_async(
678678
assert span["data"]["ai.prompt_tokens.used"] == 366
679679
assert span["data"]["ai.completion_tokens.used"] == 51
680680
assert span["data"]["ai.total_tokens.used"] == 417
681-
assert span["data"]["ai.streaming"] is True
681+
assert span["data"][SPANDATA.AI_STREAMING] is True
682682

683683

684684
@pytest.mark.forked
@@ -830,7 +830,7 @@ def test_add_ai_data_to_span_with_input_json_delta(sentry_init, capture_events):
830830
assert span["data"][SPANDATA.AI_RESPONSES] == _serialize_span_attribute(
831831
[{"type": "text", "text": "{'test': 'data','more': 'json'}"}]
832832
)
833-
assert span["data"]["ai.streaming"] is True
833+
assert span["data"][SPANDATA.AI_STREAMING] is True
834834
assert span["data"]["ai.prompt_tokens.used"] == 10
835835
assert span["data"]["ai.completion_tokens.used"] == 20
836836
assert span["data"]["ai.total_tokens.used"] == 30

tests/integrations/cohere/test_cohere.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from cohere import Client, ChatMessage
66

77
from sentry_sdk import start_span
8+
from sentry_sdk.consts import SPANDATA
89
from sentry_sdk.integrations.cohere import CohereIntegration
910

1011
from unittest import mock # python 3.3 and above
@@ -53,16 +54,16 @@ def test_nonstreaming_chat(
5354
assert tx["type"] == "transaction"
5455
span = tx["spans"][0]
5556
assert span["op"] == "ai.chat_completions.create.cohere"
56-
assert span["data"]["ai.model_id"] == "some-model"
57+
assert span["data"][SPANDATA.AI_MODEL_ID] == "some-model"
5758

5859
if send_default_pii and include_prompts:
59-
input_messages = json.loads(span["data"]["ai.input_messages"])
60+
input_messages = json.loads(span["data"][SPANDATA.AI_INPUT_MESSAGES])
6061
assert "some context" in input_messages[0]["content"]
6162
assert "hello" in input_messages[1]["content"]
62-
assert "the model response" in span["data"]["ai.responses"]
63+
assert "the model response" in span["data"][SPANDATA.AI_RESPONSES]
6364
else:
64-
assert "ai.input_messages" not in span["data"]
65-
assert "ai.responses" not in span["data"]
65+
assert SPANDATA.AI_INPUT_MESSAGES not in span["data"]
66+
assert SPANDATA.AI_RESPONSES not in span["data"]
6667

6768
assert span["data"]["ai.completion_tokens.used"] == 10
6869
assert span["data"]["ai.prompt_tokens.used"] == 20
@@ -125,16 +126,16 @@ def test_streaming_chat(sentry_init, capture_events, send_default_pii, include_p
125126
assert tx["type"] == "transaction"
126127
span = tx["spans"][0]
127128
assert span["op"] == "ai.chat_completions.create.cohere"
128-
assert span["data"]["ai.model_id"] == "some-model"
129+
assert span["data"][SPANDATA.AI_MODEL_ID] == "some-model"
129130

130131
if send_default_pii and include_prompts:
131-
input_messages = json.loads(span["data"]["ai.input_messages"])
132+
input_messages = json.loads(span["data"][SPANDATA.AI_INPUT_MESSAGES])
132133
assert "some context" in input_messages[0]["content"]
133134
assert "hello" in input_messages[1]["content"]
134-
assert "the model response" in span["data"]["ai.responses"]
135+
assert "the model response" in span["data"][SPANDATA.AI_RESPONSES]
135136
else:
136-
assert "ai.input_messages" not in span["data"]
137-
assert "ai.responses" not in span["data"]
137+
assert SPANDATA.AI_INPUT_MESSAGES not in span["data"]
138+
assert SPANDATA.AI_RESPONSES not in span["data"]
138139

139140
assert span["data"]["ai.completion_tokens.used"] == 10
140141
assert span["data"]["ai.prompt_tokens.used"] == 20
@@ -196,9 +197,9 @@ def test_embed(sentry_init, capture_events, send_default_pii, include_prompts):
196197
span = tx["spans"][0]
197198
assert span["op"] == "ai.embeddings.create.cohere"
198199
if send_default_pii and include_prompts:
199-
assert "hello" in span["data"]["ai.input_messages"]
200+
assert "hello" in span["data"][SPANDATA.AI_INPUT_MESSAGES]
200201
else:
201-
assert "ai.input_messages" not in span["data"]
202+
assert SPANDATA.AI_INPUT_MESSAGES not in span["data"]
202203

203204
assert span["data"]["ai.prompt_tokens.used"] == 10
204205
assert span["data"]["ai.total_tokens.used"] == 10

tests/integrations/huggingface_hub/test_huggingface_hub.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from huggingface_hub.errors import OverloadedError
99

1010
from sentry_sdk import start_span
11+
from sentry_sdk.consts import SPANDATA
1112
from sentry_sdk.integrations.huggingface_hub import HuggingfaceHubIntegration
1213

1314

@@ -67,11 +68,11 @@ def test_nonstreaming_chat_completion(
6768
assert span["op"] == "ai.chat_completions.create.huggingface_hub"
6869

6970
if send_default_pii and include_prompts:
70-
assert "hello" in span["data"]["ai.input_messages"]
71-
assert "the model response" in span["data"]["ai.responses"]
71+
assert "hello" in span["data"][SPANDATA.AI_INPUT_MESSAGES]
72+
assert "the model response" in span["data"][SPANDATA.AI_RESPONSES]
7273
else:
73-
assert "ai.input_messages" not in span["data"]
74-
assert "ai.responses" not in span["data"]
74+
assert SPANDATA.AI_INPUT_MESSAGES not in span["data"]
75+
assert SPANDATA.AI_RESPONSES not in span["data"]
7576

7677
if details_arg:
7778
assert span["data"]["ai.total_tokens.used"] == 10
@@ -126,11 +127,11 @@ def test_streaming_chat_completion(
126127
assert span["op"] == "ai.chat_completions.create.huggingface_hub"
127128

128129
if send_default_pii and include_prompts:
129-
assert "hello" in span["data"]["ai.input_messages"]
130-
assert "the model response" in span["data"]["ai.responses"]
130+
assert "hello" in span["data"][SPANDATA.AI_INPUT_MESSAGES]
131+
assert "the model response" in span["data"][SPANDATA.AI_RESPONSES]
131132
else:
132-
assert "ai.input_messages" not in span["data"]
133-
assert "ai.responses" not in span["data"]
133+
assert SPANDATA.AI_INPUT_MESSAGES not in span["data"]
134+
assert SPANDATA.AI_RESPONSES not in span["data"]
134135

135136
if details_arg:
136137
assert span["data"]["ai.total_tokens.used"] == 10

0 commit comments

Comments
 (0)