Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
eba2bae
feat(mistral): Add integration with Chat.complete and Chat.complete_a…
alexander-alderman-webb Sep 17, 2026
4b57f72
.
alexander-alderman-webb Sep 17, 2026
08b3eb0
.
alexander-alderman-webb Sep 17, 2026
e5c7c9a
.
alexander-alderman-webb Sep 17, 2026
e07fee8
feat(mistral): Record token usage
alexander-alderman-webb Sep 17, 2026
544f550
feat(mistral): Record request parameters
alexander-alderman-webb Sep 17, 2026
cb02dfa
merge master and address comments
alexander-alderman-webb Sep 21, 2026
6cc054a
merge
alexander-alderman-webb Sep 21, 2026
e9d366a
Merge branch 'webb/mistral/tokens' into webb/mistral/configuration-at…
alexander-alderman-webb Sep 21, 2026
4e2b954
.
alexander-alderman-webb Sep 21, 2026
4b631ad
.
alexander-alderman-webb Sep 21, 2026
0bca24b
Merge branch 'webb/mistral/chat-complete' into webb/mistral/tokens
alexander-alderman-webb Sep 21, 2026
38552dc
merge
alexander-alderman-webb Sep 21, 2026
9cc9bef
take out of auto-enabling
alexander-alderman-webb Sep 21, 2026
203b691
Merge branch 'webb/mistral/chat-complete' into webb/mistral/tokens
alexander-alderman-webb Sep 21, 2026
14a680f
Merge branch 'webb/mistral/tokens' into webb/mistral/configuration-at…
alexander-alderman-webb Sep 21, 2026
e21572c
handle None Model
alexander-alderman-webb Sep 21, 2026
ae22b23
generate GH workflows
alexander-alderman-webb Sep 21, 2026
eb04a75
Merge branch 'webb/mistral/chat-complete' into webb/mistral/tokens
alexander-alderman-webb Sep 21, 2026
6bb297f
Merge branch 'webb/mistral/tokens' into webb/mistral/configuration-at…
alexander-alderman-webb Sep 21, 2026
868ea47
raise min version to 2.0.5
alexander-alderman-webb Sep 21, 2026
e19d1f0
Merge branch 'webb/mistral/chat-complete' into webb/mistral/tokens
alexander-alderman-webb Sep 21, 2026
b20f5f0
Merge branch 'webb/mistral/tokens' into webb/mistral/configuration-at…
alexander-alderman-webb Sep 21, 2026
4e885fb
test: use high reasoning level
alexander-alderman-webb Sep 21, 2026
f46f2bf
update assertions
alexander-alderman-webb Sep 21, 2026
054997c
merge master
alexander-alderman-webb Sep 21, 2026
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
52 changes: 52 additions & 0 deletions sentry_sdk/integrations/mistral.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,32 @@ def wrap_complete(self: "Chat", *args: "Any", **kwargs: "Any") -> "Any":

set_on_span(SPANDATA.GEN_AI_RESPONSE_STREAMING, False)

max_tokens = kwargs.get("max_tokens")
if max_tokens is not None:
set_on_span(SPANDATA.GEN_AI_REQUEST_MAX_TOKENS, max_tokens)

frequency_penalty = kwargs.get("frequency_penalty")
if frequency_penalty is not None:
set_on_span(
SPANDATA.GEN_AI_REQUEST_FREQUENCY_PENALTY, frequency_penalty
)

presence_penalty = kwargs.get("presence_penalty")
if presence_penalty is not None:
set_on_span(SPANDATA.GEN_AI_REQUEST_PRESENCE_PENALTY, presence_penalty)

temperature = kwargs.get("temperature")
if temperature is not None:
set_on_span(SPANDATA.GEN_AI_REQUEST_TEMPERATURE, temperature)

top_p = kwargs.get("top_p")
if top_p is not None:
set_on_span(SPANDATA.GEN_AI_REQUEST_TOP_P, top_p)

reasoning_effort = kwargs.get("reasoning_effort")
if reasoning_effort is not None:
set_on_span(SPANDATA.GEN_AI_REQUEST_REASONING_LEVEL, reasoning_effort)

response = f(self, *args, **kwargs)

if not isinstance(response, ChatCompletionResponse):
Expand Down Expand Up @@ -136,6 +162,32 @@ async def wrap_complete_async(self: "Chat", *args: "Any", **kwargs: "Any") -> "A

set_on_span(SPANDATA.GEN_AI_RESPONSE_STREAMING, False)

max_tokens = kwargs.get("max_tokens")
if max_tokens is not None:
set_on_span(SPANDATA.GEN_AI_REQUEST_MAX_TOKENS, max_tokens)

frequency_penalty = kwargs.get("frequency_penalty")
if frequency_penalty is not None:
set_on_span(
SPANDATA.GEN_AI_REQUEST_FREQUENCY_PENALTY, frequency_penalty
)

presence_penalty = kwargs.get("presence_penalty")
if presence_penalty is not None:
set_on_span(SPANDATA.GEN_AI_REQUEST_PRESENCE_PENALTY, presence_penalty)

temperature = kwargs.get("temperature")
if temperature is not None:
set_on_span(SPANDATA.GEN_AI_REQUEST_TEMPERATURE, temperature)

top_p = kwargs.get("top_p")
if top_p is not None:
set_on_span(SPANDATA.GEN_AI_REQUEST_TOP_P, top_p)

reasoning_effort = kwargs.get("reasoning_effort")
if reasoning_effort is not None:
set_on_span(SPANDATA.GEN_AI_REQUEST_REASONING_LEVEL, reasoning_effort)

response = await f(self, *args, **kwargs)

if not isinstance(response, ChatCompletionResponse):
Expand Down
53 changes: 52 additions & 1 deletion tests/integrations/mistral/test_mistral.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,13 @@ def test_nonstreaming_chat(
messages=[
{"role": "user", "content": "What is the best French cheese?"}
],
max_tokens=1024,
presence_penalty=0.1,
frequency_penalty=0.2,
temperature=0.7,
top_p=0.9,
reasoning_effort="high",
Comment thread
cursor[bot] marked this conversation as resolved.
)

sentry_sdk.flush()
spans = [item.payload for item in items]
(span,) = (
Expand All @@ -93,6 +98,13 @@ def test_nonstreaming_chat(
)
assert span["attributes"][SPANDATA.GEN_AI_RESPONSE_STREAMING] is False

assert span["attributes"][SPANDATA.GEN_AI_REQUEST_TEMPERATURE] == 0.7
assert span["attributes"][SPANDATA.GEN_AI_REQUEST_TOP_P] == 0.9
assert span["attributes"][SPANDATA.GEN_AI_REQUEST_FREQUENCY_PENALTY] == 0.2
assert span["attributes"][SPANDATA.GEN_AI_REQUEST_PRESENCE_PENALTY] == 0.1
assert span["attributes"][SPANDATA.GEN_AI_REQUEST_MAX_TOKENS] == 1024
assert span["attributes"][SPANDATA.GEN_AI_REQUEST_REASONING_LEVEL] == "high"

assert span["attributes"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS] == 10
assert span["attributes"][SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS] == 20
assert span["attributes"][SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS] == 30
Expand All @@ -107,6 +119,12 @@ def test_nonstreaming_chat(
client.chat.complete(
model="open-mistral",
messages=[{"role": "user", "content": "Hello, Mistral"}],
max_tokens=1024,
presence_penalty=0.1,
frequency_penalty=0.2,
temperature=0.7,
top_p=0.9,
reasoning_effort="high",
)

(transaction,) = [item.payload for item in items]
Expand All @@ -119,6 +137,13 @@ def test_nonstreaming_chat(
assert span["data"][SPANDATA.GEN_AI_REQUEST_MODEL] == "open-mistral"
assert span["data"][SPANDATA.GEN_AI_RESPONSE_STREAMING] is False

assert span["data"][SPANDATA.GEN_AI_REQUEST_TEMPERATURE] == 0.7
assert span["data"][SPANDATA.GEN_AI_REQUEST_TOP_P] == 0.9
assert span["data"][SPANDATA.GEN_AI_REQUEST_FREQUENCY_PENALTY] == 0.2
assert span["data"][SPANDATA.GEN_AI_REQUEST_PRESENCE_PENALTY] == 0.1
assert span["data"][SPANDATA.GEN_AI_REQUEST_MAX_TOKENS] == 1024
assert span["data"][SPANDATA.GEN_AI_REQUEST_REASONING_LEVEL] == "high"

assert span["data"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS] == 10
assert span["data"][SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS] == 20
assert span["data"][SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS] == 30
Expand Down Expand Up @@ -162,6 +187,12 @@ async def test_nonstreaming_chat_async(
messages=[
{"role": "user", "content": "What is the best French cheese?"}
],
max_tokens=1024,
presence_penalty=0.1,
frequency_penalty=0.2,
temperature=0.7,
top_p=0.9,
reasoning_effort="high",
)

sentry_sdk.flush()
Expand All @@ -181,6 +212,13 @@ async def test_nonstreaming_chat_async(
)
assert span["attributes"][SPANDATA.GEN_AI_RESPONSE_STREAMING] is False

assert span["attributes"][SPANDATA.GEN_AI_REQUEST_TEMPERATURE] == 0.7
assert span["attributes"][SPANDATA.GEN_AI_REQUEST_TOP_P] == 0.9
assert span["attributes"][SPANDATA.GEN_AI_REQUEST_FREQUENCY_PENALTY] == 0.2
assert span["attributes"][SPANDATA.GEN_AI_REQUEST_PRESENCE_PENALTY] == 0.1
assert span["attributes"][SPANDATA.GEN_AI_REQUEST_MAX_TOKENS] == 1024
assert span["attributes"][SPANDATA.GEN_AI_REQUEST_REASONING_LEVEL] == "high"

assert span["attributes"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS] == 10
assert span["attributes"][SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS] == 20
assert span["attributes"][SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS] == 30
Expand All @@ -195,6 +233,12 @@ async def test_nonstreaming_chat_async(
await client.chat.complete_async(
model="mistral-medium-latest",
messages=[{"role": "user", "content": "Hello, Mistral"}],
max_tokens=1024,
presence_penalty=0.1,
frequency_penalty=0.2,
temperature=0.7,
top_p=0.9,
reasoning_effort="high",
)

(transaction,) = [item.payload for item in items]
Expand All @@ -207,6 +251,13 @@ async def test_nonstreaming_chat_async(
assert span["data"][SPANDATA.GEN_AI_REQUEST_MODEL] == "mistral-medium-latest"
assert span["data"][SPANDATA.GEN_AI_RESPONSE_STREAMING] is False

assert span["data"][SPANDATA.GEN_AI_REQUEST_TEMPERATURE] == 0.7
assert span["data"][SPANDATA.GEN_AI_REQUEST_TOP_P] == 0.9
assert span["data"][SPANDATA.GEN_AI_REQUEST_FREQUENCY_PENALTY] == 0.2
assert span["data"][SPANDATA.GEN_AI_REQUEST_PRESENCE_PENALTY] == 0.1
assert span["data"][SPANDATA.GEN_AI_REQUEST_MAX_TOKENS] == 1024
assert span["data"][SPANDATA.GEN_AI_REQUEST_REASONING_LEVEL] == "high"

assert span["data"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS] == 10
assert span["data"][SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS] == 20
assert span["data"][SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS] == 30
Loading