Compare commits

...
Author SHA1 Message Date
Gerald LonlasandClaude Opus 4.8 837e1dadee fix(client): avoid duplicate text when output item has both content and text
In `extract_responses_text`, the loop over non-dict output items extracted
text from `item.content[].text` AND unconditionally also from `item.text`.
When an item carried both attributes (e.g. an OpenAI Responses API message
object that exposes `.content` as a list and also a convenience `.text`
shorthand), the same content was appended twice, producing a response like
`"the-answer\nthe-answer"` instead of `"the-answer"`.

The dict-item branch already used `continue` to prevent this; mirror that
guard in the object branch by making `item.text` extraction an `else` clause
that only runs when `item.content` is NOT a list.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 00:58:34 +08:00
2 changed files with 17 additions and 3 deletions
+4 -3
View File
@@ -233,9 +233,10 @@ def extract_responses_text(response: Any) -> str:
text = getattr(entry, "text", None)
if isinstance(text, str):
parts.append(text)
text = getattr(item, "text", None)
if isinstance(text, str):
parts.append(text)
else:
text = getattr(item, "text", None)
if isinstance(text, str):
parts.append(text)
if parts:
return "\n".join(parts)
+13
View File
@@ -183,6 +183,19 @@ def test_extract_responses_text_variants() -> None:
assert client.extract_responses_text(ns(output_text="", output=[])) == ""
def test_extract_responses_text_no_duplicate_when_item_has_content_and_text() -> None:
# An output item that has BOTH a content list and a top-level text attribute
# should only yield text from the content list; the item-level text field
# must NOT be appended a second time (regression guard for the duplicate-text bug).
item_with_both = ns(content=[ns(text="the-answer")], text="the-answer")
response = ns(output=[item_with_both])
result = client.extract_responses_text(response)
assert result == "the-answer", (
f"Expected 'the-answer' but got {result!r} — "
"item.text was appended even though content was already processed"
)
def test_dump_openai_response_branches() -> None:
class WithModelDump:
def model_dump(self):