Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ def _map_finish_reason(anthropic_reason):
return _FINISH_REASON_MAP.get(anthropic_reason, anthropic_reason)


def _normalize_stop_sequences(stop_sequences):
"""Normalize stop_sequences to a list of strings, or None if nothing usable was passed."""
if isinstance(stop_sequences, str):
return [stop_sequences]
if isinstance(stop_sequences, (list, tuple)):
return [s for s in stop_sequences if isinstance(s, str)] or None
return None


async def _process_image_item(item, trace_id, span_id, message_index, content_index):
source = item.get("source", {})
media_type = source.get("media_type", "image/unknown")
Expand Down Expand Up @@ -138,6 +147,11 @@ async def aset_input_attributes(span, kwargs):
set_span_attribute(
span, GenAIAttributes.GEN_AI_REQUEST_PRESENCE_PENALTY, kwargs.get("presence_penalty")
)
set_span_attribute(
span,
GenAIAttributes.GEN_AI_REQUEST_STOP_SEQUENCES,
_normalize_stop_sequences(kwargs.get("stop_sequences")),
)
set_span_attribute(span, SpanAttributes.GEN_AI_IS_STREAMING, kwargs.get("stream"))

if should_send_prompts():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,75 @@ def test_input_messages_multi_turn():
assert messages[2] == {"role": "user", "parts": [{"type": "text", "content": "How are you?"}]}


def test_request_stop_sequences_attribute():
"""gen_ai.request.stop_sequences must be recorded as a list of strings."""
span = make_span()
kwargs = {
"model": "claude-3-opus-20240229",
"messages": [{"role": "user", "content": "Hello"}],
"max_tokens": 1024,
"stop_sequences": ["END", "###"],
}
asyncio.run(aset_input_attributes(span, kwargs))

assert list(span.attributes[GenAIAttributes.GEN_AI_REQUEST_STOP_SEQUENCES]) == ["END", "###"]


def test_request_stop_sequences_bare_string_is_one_item():
"""A bare-string stop_sequences must be recorded as a single item, not split into characters."""
span = make_span()
kwargs = {
"model": "claude-3-opus-20240229",
"messages": [{"role": "user", "content": "Hello"}],
"max_tokens": 1024,
"stop_sequences": "END",
}
asyncio.run(aset_input_attributes(span, kwargs))

assert list(span.attributes[GenAIAttributes.GEN_AI_REQUEST_STOP_SEQUENCES]) == ["END"]


def test_request_stop_sequences_tuple_filters_non_strings():
"""Only str items from a tuple stop_sequences must be recorded."""
span = make_span()
kwargs = {
"model": "claude-3-opus-20240229",
"messages": [{"role": "user", "content": "Hello"}],
"max_tokens": 1024,
"stop_sequences": ("END", 123, None, "###"),
}
asyncio.run(aset_input_attributes(span, kwargs))

assert list(span.attributes[GenAIAttributes.GEN_AI_REQUEST_STOP_SEQUENCES]) == ["END", "###"]


def test_request_stop_sequences_not_set_when_absent():
"""gen_ai.request.stop_sequences must not be set when stop_sequences is not passed."""
span = make_span()
kwargs = {
"model": "claude-3-opus-20240229",
"messages": [{"role": "user", "content": "Hello"}],
"max_tokens": 1024,
}
asyncio.run(aset_input_attributes(span, kwargs))

assert GenAIAttributes.GEN_AI_REQUEST_STOP_SEQUENCES not in span.attributes


def test_request_stop_sequences_not_set_when_no_string_items():
"""gen_ai.request.stop_sequences must not be set when stop_sequences has no str items."""
span = make_span()
kwargs = {
"model": "claude-3-opus-20240229",
"messages": [{"role": "user", "content": "Hello"}],
"max_tokens": 1024,
"stop_sequences": [1, None],
}
asyncio.run(aset_input_attributes(span, kwargs))

assert GenAIAttributes.GEN_AI_REQUEST_STOP_SEQUENCES not in span.attributes


def test_system_instructions_attribute():
"""gen_ai.system_instructions must be a JSON array of typed parts, NOT a plain string.

Expand Down