Skip to content

Commit 280fbd8

Browse files
committed
wip
1 parent b17b79d commit 280fbd8

4 files changed

Lines changed: 288 additions & 75 deletions

File tree

sentry_sdk/ai/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class GEN_AI_ALLOWED_MESSAGE_ROLES:
3030
GEN_AI_MESSAGE_ROLE_REVERSE_MAPPING = {
3131
GEN_AI_ALLOWED_MESSAGE_ROLES.SYSTEM: ["system"],
3232
GEN_AI_ALLOWED_MESSAGE_ROLES.USER: ["user", "human"],
33-
GEN_AI_ALLOWED_MESSAGE_ROLES.ASSISTANT: ["assistant", "ai"],
33+
GEN_AI_ALLOWED_MESSAGE_ROLES.ASSISTANT: ["assistant", "ai", "chatbot"],
3434
GEN_AI_ALLOWED_MESSAGE_ROLES.TOOL: ["tool", "tool_call"],
3535
}
3636

sentry_sdk/integrations/cohere.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from sentry_sdk.ai.utils import (
77
set_data_normalized,
88
normalize_message_roles,
9+
truncate_and_annotate_messages,
910
)
1011

1112
from typing import TYPE_CHECKING
@@ -166,21 +167,23 @@ def new_chat(*args: "Any", **kwargs: "Any") -> "Any":
166167
if should_send_default_pii() and integration.include_prompts:
167168
messages = []
168169
for x in kwargs.get("chat_history", []):
169-
role = getattr(x, "role", "").lower()
170-
if role == "chatbot":
171-
role = "assistant"
172170
messages.append({
173-
"role": role,
171+
"role": getattr(x, "role", "").lower(),
174172
"content": getattr(x, "message", ""),
175173
})
176174
messages.append({"role": "user", "content": message})
177175
messages = normalize_message_roles(messages)
178-
set_data_normalized(
179-
span,
180-
SPANDATA.GEN_AI_REQUEST_MESSAGES,
181-
messages,
182-
unpack=False,
176+
scope = sentry_sdk.get_current_scope()
177+
messages_data = truncate_and_annotate_messages(
178+
messages, span, scope
183179
)
180+
if messages_data is not None:
181+
set_data_normalized(
182+
span,
183+
SPANDATA.GEN_AI_REQUEST_MESSAGES,
184+
messages_data,
185+
unpack=False,
186+
)
184187
for k, v in COLLECTED_PII_CHAT_PARAMS.items():
185188
if k in kwargs:
186189
set_data_normalized(span, v, kwargs[k])

sentry_sdk/integrations/cohere_v2.py

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from sentry_sdk.ai.utils import (
77
set_data_normalized,
88
normalize_message_roles,
9+
truncate_and_annotate_messages,
910
)
1011

1112
from typing import TYPE_CHECKING
@@ -16,7 +17,7 @@
1617

1718
import sentry_sdk
1819
from sentry_sdk.scope import should_send_default_pii
19-
from sentry_sdk.utils import capture_internal_exceptions, event_from_exception, reraise
20+
from sentry_sdk.utils import capture_internal_exceptions, reraise
2021

2122
from sentry_sdk.integrations.cohere import (
2223
CohereIntegration,
@@ -26,11 +27,24 @@
2627

2728
try:
2829
from cohere.v2.client import V2Client as CohereV2Client
29-
from cohere.v2.types import V2ChatResponse
30-
from cohere.v2.types.v2chat_stream_response import MessageEndV2ChatStreamResponse
3130

32-
if TYPE_CHECKING:
33-
from cohere.v2.types import V2ChatStreamResponse
31+
# Type locations changed between cohere versions:
32+
# 5.13.x: cohere.types (ChatResponse, MessageEndStreamedChatResponseV2)
33+
# 5.20+: cohere.v2.types (V2ChatResponse, MessageEndV2ChatStreamResponse)
34+
try:
35+
from cohere.v2.types import V2ChatResponse
36+
from cohere.v2.types import MessageEndV2ChatStreamResponse
37+
38+
if TYPE_CHECKING:
39+
from cohere.v2.types import V2ChatStreamResponse
40+
except ImportError:
41+
from cohere.types import ChatResponse as V2ChatResponse
42+
from cohere.types import (
43+
MessageEndStreamedChatResponseV2 as MessageEndV2ChatStreamResponse,
44+
)
45+
46+
if TYPE_CHECKING:
47+
from cohere.types import StreamedChatResponseV2 as V2ChatStreamResponse
3448

3549
_has_v2 = True
3650
except ImportError:
@@ -39,7 +53,12 @@
3953

4054
def setup_v2(wrap_embed_fn):
4155
# type: (Callable[..., Any]) -> None
42-
"""Called from CohereIntegration.setup_once() to patch V2Client methods."""
56+
"""Called from CohereIntegration.setup_once() to patch V2Client methods.
57+
58+
The embed wrapper is passed in from cohere.py to reuse the same _wrap_embed
59+
for both V1 and V2, since the embed response format (.meta.billed_units)
60+
is identical across both API versions.
61+
"""
4362
if not _has_v2:
4463
return
4564

@@ -52,16 +71,25 @@ def setup_v2(wrap_embed_fn):
5271

5372
def _extract_messages_v2(messages):
5473
# type: (Any) -> list[dict[str, str]]
55-
"""Extract role/content dicts from V2-style message objects."""
74+
"""Extract role/content dicts from V2-style message objects.
75+
76+
Handles both plain dicts and Pydantic model instances.
77+
"""
5678
result = []
5779
for msg in messages:
58-
role = getattr(msg, "role", "unknown")
59-
content = getattr(msg, "content", "")
80+
if isinstance(msg, dict):
81+
role = msg.get("role", "unknown")
82+
content = msg.get("content", "")
83+
else:
84+
role = getattr(msg, "role", "unknown")
85+
content = getattr(msg, "content", "")
6086
if isinstance(content, str):
6187
text = content
6288
elif isinstance(content, list):
6389
text = " ".join(
64-
getattr(item, "text", "") for item in content if hasattr(item, "text")
90+
(item.get("text", "") if isinstance(item, dict) else getattr(item, "text", ""))
91+
for item in content
92+
if (isinstance(item, dict) and "text" in item) or hasattr(item, "text")
6593
)
6694
else:
6795
text = str(content) if content else ""
@@ -138,7 +166,7 @@ def new_chat(*args, **kwargs):
138166

139167
span = sentry_sdk.start_span(
140168
op=OP.GEN_AI_CHAT,
141-
name="chat {}".format(model).strip(),
169+
name=f"chat {model}".strip(),
142170
origin=CohereIntegration.origin,
143171
)
144172
span.__enter__()
@@ -160,12 +188,17 @@ def new_chat(*args, **kwargs):
160188
if should_send_default_pii() and integration.include_prompts:
161189
messages = _extract_messages_v2(kwargs.get("messages", []))
162190
messages = normalize_message_roles(messages)
163-
set_data_normalized(
164-
span,
165-
SPANDATA.GEN_AI_REQUEST_MESSAGES,
166-
messages,
167-
unpack=False,
191+
scope = sentry_sdk.get_current_scope()
192+
messages_data = truncate_and_annotate_messages(
193+
messages, span, scope
168194
)
195+
if messages_data is not None:
196+
set_data_normalized(
197+
span,
198+
SPANDATA.GEN_AI_REQUEST_MESSAGES,
199+
messages_data,
200+
unpack=False,
201+
)
169202
if "tools" in kwargs:
170203
set_data_normalized(
171204
span,

0 commit comments

Comments
 (0)