Skip to content
Merged
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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ requires-python = ">=3.9"
license = "MIT"
authors = [{ name = "OpenAI", email = "[email protected]" }]
dependencies = [
"openai>=2.6.1,<3",
"openai>=2.7.1,<3",
"pydantic>=2.12.3, <3",
"griffe>=1.5.6, <2",
"typing-extensions>=4.12.2, <5",
Expand Down
12 changes: 8 additions & 4 deletions src/agents/models/chatcmpl_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
from ..tool import FunctionTool, Tool
from .fake_id import FAKE_RESPONSES_ID

ResponseInputContentWithAudioParam = Union[ResponseInputContentParam, ResponseInputAudioParam]


class Converter:
@classmethod
Expand Down Expand Up @@ -136,7 +138,9 @@ def message_to_output_items(cls, message: ChatCompletionMessage) -> list[TRespon
)
if message.content:
message_item.content.append(
ResponseOutputText(text=message.content, type="output_text", annotations=[])
ResponseOutputText(
text=message.content, type="output_text", annotations=[], logprobs=[]
)
)
if message.refusal:
message_item.content.append(
Expand Down Expand Up @@ -246,7 +250,7 @@ def maybe_reasoning_message(cls, item: Any) -> ResponseReasoningItemParam | None

@classmethod
def extract_text_content(
cls, content: str | Iterable[ResponseInputContentParam]
cls, content: str | Iterable[ResponseInputContentWithAudioParam]
) -> str | list[ChatCompletionContentPartTextParam]:
all_content = cls.extract_all_content(content)
if isinstance(all_content, str):
Expand All @@ -259,7 +263,7 @@ def extract_text_content(

@classmethod
def extract_all_content(
cls, content: str | Iterable[ResponseInputContentParam]
cls, content: str | Iterable[ResponseInputContentWithAudioParam]
) -> str | list[ChatCompletionContentPartParam]:
if isinstance(content, str):
return content
Expand Down Expand Up @@ -535,7 +539,7 @@ def ensure_assistant_message() -> ChatCompletionAssistantMessageParam:
elif func_output := cls.maybe_function_tool_call_output(item):
flush_assistant_message()
output_content = cast(
Union[str, Iterable[ResponseInputContentParam]], func_output["output"]
Union[str, Iterable[ResponseInputContentWithAudioParam]], func_output["output"]
)
msg: ChatCompletionToolMessageParam = {
"role": "tool",
Expand Down
2 changes: 2 additions & 0 deletions src/agents/models/chatcmpl_stream_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ async def handle_stream(
text="",
type="output_text",
annotations=[],
logprobs=[],
),
)
# Start a new assistant message stream
Expand Down Expand Up @@ -258,6 +259,7 @@ async def handle_stream(
text="",
type="output_text",
annotations=[],
logprobs=[],
),
type="response.content_part.added",
sequence_number=sequence_number.get_and_increment(),
Expand Down
1 change: 1 addition & 0 deletions tests/extensions/memory/test_sqlalchemy_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def _make_message_item(item_id: str, text_value: str) -> TResponseInputItem:
"type": "output_text",
"text": text_value,
"annotations": [],
"logprobs": [],
}
message: ResponseOutputMessageParam = {
"id": item_id,
Expand Down
4 changes: 2 additions & 2 deletions tests/test_agent_as_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ async def test_agent_as_tool_returns_concatenated_text(monkeypatch: pytest.Monke
annotations=[],
text="Hello world",
type="output_text",
logprobs=None,
logprobs=[],
)
],
)
Expand Down Expand Up @@ -304,7 +304,7 @@ async def test_agent_as_tool_custom_output_extractor(monkeypatch: pytest.MonkeyP
annotations=[],
text="Original text",
type="output_text",
logprobs=None,
logprobs=[],
)
],
)
Expand Down
1 change: 1 addition & 0 deletions tests/test_agent_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -1226,6 +1226,7 @@ async def echo_tool(text: str) -> str:
"content": [
{
"annotations": [],
"logprobs": [],
"text": "Summary: Echoed foo and bar",
"type": "output_text",
}
Expand Down
8 changes: 6 additions & 2 deletions tests/test_call_model_input_filter_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ async def test_call_model_input_filter_sync_non_streamed_unit() -> None:
id="1",
type="message",
role="assistant",
content=[ResponseOutputText(text="ok", type="output_text", annotations=[])],
content=[
ResponseOutputText(text="ok", type="output_text", annotations=[], logprobs=[])
],
status="completed",
)
]
Expand Down Expand Up @@ -64,7 +66,9 @@ async def test_call_model_input_filter_async_streamed_unit() -> None:
id="1",
type="message",
role="assistant",
content=[ResponseOutputText(text="ok", type="output_text", annotations=[])],
content=[
ResponseOutputText(text="ok", type="output_text", annotations=[], logprobs=[])
],
status="completed",
)
]
Expand Down
4 changes: 3 additions & 1 deletion tests/test_extension_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ def _get_message_output_run_item(content: str) -> MessageOutputItem:
agent=fake_agent(),
raw_item=ResponseOutputMessage(
id="1",
content=[ResponseOutputText(text=content, annotations=[], type="output_text")],
content=[
ResponseOutputText(text=content, annotations=[], type="output_text", logprobs=[])
],
role="assistant",
status="completed",
type="message",
Expand Down
4 changes: 3 additions & 1 deletion tests/test_handoff_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ def message_item(content: str, agent: Agent[Any]) -> MessageOutputItem:
status="completed",
role="assistant",
type="message",
content=[ResponseOutputText(text=content, type="output_text", annotations=[])],
content=[
ResponseOutputText(text=content, type="output_text", annotations=[], logprobs=[])
],
),
)

Expand Down
30 changes: 20 additions & 10 deletions tests/test_items_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,18 @@ def make_message(

def test_extract_last_content_of_text_message() -> None:
# Build a message containing two text segments.
content1 = ResponseOutputText(annotations=[], text="Hello ", type="output_text")
content2 = ResponseOutputText(annotations=[], text="world!", type="output_text")
content1 = ResponseOutputText(annotations=[], text="Hello ", type="output_text", logprobs=[])
content2 = ResponseOutputText(annotations=[], text="world!", type="output_text", logprobs=[])
message = make_message([content1, content2])
# Helpers should yield the last segment's text.
assert ItemHelpers.extract_last_content(message) == "world!"


def test_extract_last_content_of_refusal_message() -> None:
# Build a message whose last content entry is a refusal.
content1 = ResponseOutputText(annotations=[], text="Before refusal", type="output_text")
content1 = ResponseOutputText(
annotations=[], text="Before refusal", type="output_text", logprobs=[]
)
refusal = ResponseOutputRefusal(refusal="I cannot do that", type="refusal")
message = make_message([content1, refusal])
# Helpers should extract the refusal string when last content is a refusal.
Expand All @@ -87,8 +89,8 @@ def test_extract_last_content_non_message_returns_empty() -> None:

def test_extract_last_text_returns_text_only() -> None:
# A message whose last segment is text yields the text.
first_text = ResponseOutputText(annotations=[], text="part1", type="output_text")
second_text = ResponseOutputText(annotations=[], text="part2", type="output_text")
first_text = ResponseOutputText(annotations=[], text="part1", type="output_text", logprobs=[])
second_text = ResponseOutputText(annotations=[], text="part2", type="output_text", logprobs=[])
message = make_message([first_text, second_text])
assert ItemHelpers.extract_last_text(message) == "part2"
# Whereas when last content is a refusal, extract_last_text returns None.
Expand Down Expand Up @@ -116,9 +118,9 @@ def test_input_to_new_input_list_deep_copies_lists() -> None:
def test_text_message_output_concatenates_text_segments() -> None:
# Build a message with both text and refusal segments, only text segments are concatenated.
pieces: list[ResponseOutputText | ResponseOutputRefusal] = []
pieces.append(ResponseOutputText(annotations=[], text="a", type="output_text"))
pieces.append(ResponseOutputText(annotations=[], text="a", type="output_text", logprobs=[]))
pieces.append(ResponseOutputRefusal(refusal="denied", type="refusal"))
pieces.append(ResponseOutputText(annotations=[], text="b", type="output_text"))
pieces.append(ResponseOutputText(annotations=[], text="b", type="output_text", logprobs=[]))
message = make_message(pieces)
# Wrap into MessageOutputItem to feed into text_message_output.
item = MessageOutputItem(agent=Agent(name="test"), raw_item=message)
Expand All @@ -131,8 +133,12 @@ def test_text_message_outputs_across_list_of_runitems() -> None:
that only MessageOutputItem instances contribute any text. The non-message
(ReasoningItem) should be ignored by Helpers.text_message_outputs.
"""
message1 = make_message([ResponseOutputText(annotations=[], text="foo", type="output_text")])
message2 = make_message([ResponseOutputText(annotations=[], text="bar", type="output_text")])
message1 = make_message(
[ResponseOutputText(annotations=[], text="foo", type="output_text", logprobs=[])]
)
message2 = make_message(
[ResponseOutputText(annotations=[], text="bar", type="output_text", logprobs=[])]
)
item1: RunItem = MessageOutputItem(agent=Agent(name="test"), raw_item=message1)
item2: RunItem = MessageOutputItem(agent=Agent(name="test"), raw_item=message2)
# Create a non-message run item of a different type, e.g., a reasoning trace.
Expand Down Expand Up @@ -171,7 +177,9 @@ def test_tool_call_output_item_constructs_function_call_output_dict():

def test_to_input_items_for_message() -> None:
"""An output message should convert into an input dict matching the message's own structure."""
content = ResponseOutputText(annotations=[], text="hello world", type="output_text")
content = ResponseOutputText(
annotations=[], text="hello world", type="output_text", logprobs=[]
)
message = ResponseOutputMessage(
id="m1", content=[content], role="assistant", status="completed", type="message"
)
Expand All @@ -184,6 +192,7 @@ def test_to_input_items_for_message() -> None:
"content": [
{
"annotations": [],
"logprobs": [],
"text": "hello world",
"type": "output_text",
}
Expand Down Expand Up @@ -305,6 +314,7 @@ def test_input_to_new_input_list_copies_the_ones_produced_by_pydantic() -> None:
type="output_text",
text="Hey, what's up?",
annotations=[],
logprobs=[],
)
],
role="assistant",
Expand Down
1 change: 1 addition & 0 deletions tests/test_openai_chatcompletions_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ def test_items_to_messages_with_output_message_and_function_call():
text="Part 1",
type="output_text",
annotations=[],
logprobs=[],
)
refusal: ResponseOutputRefusal = ResponseOutputRefusal(
refusal="won't do that",
Expand Down
4 changes: 2 additions & 2 deletions tests/test_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def get_text_message(content: str) -> ResponseOutputItem:
id="1",
type="message",
role="assistant",
content=[ResponseOutputText(text=content, type="output_text", annotations=[])],
content=[ResponseOutputText(text=content, type="output_text", annotations=[], logprobs=[])],
status="completed",
)

Expand Down Expand Up @@ -73,6 +73,6 @@ def get_final_output_message(args: str) -> ResponseOutputItem:
id="1",
type="message",
role="assistant",
content=[ResponseOutputText(text=args, type="output_text", annotations=[])],
content=[ResponseOutputText(text=args, type="output_text", annotations=[], logprobs=[])],
status="completed",
)
3 changes: 2 additions & 1 deletion tests/utils/test_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def test_to_dump_compatible():
type="output_text",
text="Hey, what's up?",
annotations=[],
logprobs=[],
)
].__iter__(),
role="assistant",
Expand All @@ -28,5 +29,5 @@ def test_to_dump_compatible():
result = json.dumps(_to_dump_compatible(input_iter))
assert (
result
== """[{"id": "a75654dc-7492-4d1c-bce0-89e8312fbdd7", "content": [{"type": "output_text", "text": "Hey, what's up?", "annotations": []}], "role": "assistant", "status": "completed", "type": "message"}]""" # noqa: E501
== """[{"id": "a75654dc-7492-4d1c-bce0-89e8312fbdd7", "content": [{"type": "output_text", "text": "Hey, what's up?", "annotations": [], "logprobs": []}], "role": "assistant", "status": "completed", "type": "message"}]""" # noqa: E501
)
20 changes: 15 additions & 5 deletions tests/voice/test_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,9 @@ async def test_single_agent_workflow(monkeypatch) -> None:
},
{
"id": "1",
"content": [{"annotations": [], "text": "a_message", "type": "output_text"}],
"content": [
{"annotations": [], "logprobs": [], "text": "a_message", "type": "output_text"}
],
"role": "assistant",
"status": "completed",
"type": "message",
Expand All @@ -151,7 +153,9 @@ async def test_single_agent_workflow(monkeypatch) -> None:
},
{
"id": "1",
"content": [{"annotations": [], "text": "done", "type": "output_text"}],
"content": [
{"annotations": [], "logprobs": [], "text": "done", "type": "output_text"}
],
"role": "assistant",
"status": "completed",
"type": "message",
Expand Down Expand Up @@ -179,7 +183,9 @@ async def test_single_agent_workflow(monkeypatch) -> None:
},
{
"id": "1",
"content": [{"annotations": [], "text": "a_message", "type": "output_text"}],
"content": [
{"annotations": [], "logprobs": [], "text": "a_message", "type": "output_text"}
],
"role": "assistant",
"status": "completed",
"type": "message",
Expand All @@ -191,15 +197,19 @@ async def test_single_agent_workflow(monkeypatch) -> None:
},
{
"id": "1",
"content": [{"annotations": [], "text": "done", "type": "output_text"}],
"content": [
{"annotations": [], "logprobs": [], "text": "done", "type": "output_text"}
],
"role": "assistant",
"status": "completed",
"type": "message",
},
{"role": "user", "content": "transcription_2"},
{
"id": "1",
"content": [{"annotations": [], "text": "done_2", "type": "output_text"}],
"content": [
{"annotations": [], "logprobs": [], "text": "done_2", "type": "output_text"}
],
"role": "assistant",
"status": "completed",
"type": "message",
Expand Down
Loading