mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-21 00:21:49 +00:00
fix(otel v2): keep Responses refusal text on the folded assistant message
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
364d897545
commit
7d93821e41
3 changed files with 30 additions and 9 deletions
|
|
@ -720,6 +720,7 @@ class _ToolCall(TypedDict):
|
|||
class _AssistantMessage(TypedDict):
|
||||
role: ReadOnly[str]
|
||||
content: ReadOnly[str | None]
|
||||
refusal: ReadOnly[str | None]
|
||||
tool_calls: ReadOnly[tuple[_ToolCall, ...] | None]
|
||||
|
||||
|
||||
|
|
@ -735,13 +736,7 @@ def _responses_choices(response: Mapping[str, object]) -> tuple[_Choice, ...]:
|
|||
"""A Responses API ``output`` folded into one chat-shaped assistant choice."""
|
||||
items: Final = _dicts(response.get("output"))
|
||||
messages: Final = tuple(item for item in items if item.get("type") == "message")
|
||||
content: Final = "".join(
|
||||
text
|
||||
for item in messages
|
||||
for part in _dicts(item.get("content"))
|
||||
if part.get("type") == "output_text"
|
||||
if (text := as_str(part.get("text"))) is not None
|
||||
)
|
||||
parts: Final = tuple(part for item in messages for part in _dicts(item.get("content")))
|
||||
tool_calls: Final = tuple(
|
||||
_responses_tool_call(item) for item in items if item.get("type") in _RESPONSES_TOOL_CALL_TYPES
|
||||
)
|
||||
|
|
@ -749,13 +744,21 @@ def _responses_choices(response: Mapping[str, object]) -> tuple[_Choice, ...]:
|
|||
return ()
|
||||
message: Final[_AssistantMessage] = {
|
||||
"role": next((role for item in messages if (role := as_str(item.get("role")))), "assistant"),
|
||||
"content": content if messages else None,
|
||||
"content": _responses_parts_text(parts, "output_text", "text"),
|
||||
"refusal": _responses_parts_text(parts, "refusal", "refusal"),
|
||||
"tool_calls": tool_calls or None,
|
||||
}
|
||||
choice: Final[_Choice] = {"message": message, "finish_reason": _responses_finish_reason(response, bool(tool_calls))}
|
||||
return (choice,)
|
||||
|
||||
|
||||
def _responses_parts_text(parts: tuple[Mapping[str, object], ...], part_type: str, field: str) -> str | None:
|
||||
texts: Final = tuple(
|
||||
text for part in parts if part.get("type") == part_type if (text := as_str(part.get(field))) is not None
|
||||
)
|
||||
return "".join(texts) if texts else None
|
||||
|
||||
|
||||
def _responses_tool_call(item: Mapping[str, object]) -> _ToolCall:
|
||||
custom: Final = item.get("type") == "custom_tool_call"
|
||||
function: Final[_ToolFunction] = {
|
||||
|
|
|
|||
|
|
@ -761,7 +761,7 @@ def test_responses_output_text_becomes_one_assistant_choice_with_stop():
|
|||
|
||||
assert json.loads(json.dumps(data.choices_out)) == [
|
||||
{
|
||||
"message": {"role": "assistant", "content": "pong", "tool_calls": None},
|
||||
"message": {"role": "assistant", "content": "pong", "refusal": None, "tool_calls": None},
|
||||
"finish_reason": "stop",
|
||||
}
|
||||
]
|
||||
|
|
@ -835,6 +835,23 @@ def test_responses_content_only_reads_output_text_parts():
|
|||
data = LLMCallSpanData.from_standard_logging_payload(_responses_payload([item]), capture_content=True)
|
||||
|
||||
assert data.choices_out[0]["message"]["content"] == "ok"
|
||||
assert data.choices_out[0]["message"]["refusal"] == "no"
|
||||
|
||||
|
||||
def test_responses_refusal_only_output_keeps_the_refusal_text():
|
||||
item = {
|
||||
"type": "message",
|
||||
"role": "assistant",
|
||||
"content": [{"type": "refusal", "refusal": "I can't "}, {"type": "refusal", "refusal": "help with that."}],
|
||||
}
|
||||
data = LLMCallSpanData.from_standard_logging_payload(_responses_payload([item]), capture_content=True)
|
||||
|
||||
assert json.loads(json.dumps(data.choices_out)) == [
|
||||
{
|
||||
"message": {"role": "assistant", "content": None, "refusal": "I can't help with that.", "tool_calls": None},
|
||||
"finish_reason": "stop",
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def test_responses_output_without_messages_or_tool_calls_stays_empty():
|
||||
|
|
|
|||
|
|
@ -218,6 +218,7 @@ def test_langfuse_mapper_renders_a_responses_api_call_from_the_standard_logging_
|
|||
{
|
||||
"role": "assistant",
|
||||
"content": "Checking.",
|
||||
"refusal": None,
|
||||
"tool_calls": [
|
||||
{"id": "call_1", "type": "function", "function": {"name": "get_weather", "arguments": '{"city": "sf"}'}}
|
||||
],
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue