11from __future__ import annotations
22
3+ import gc
34import json
45
56from openai .types .responses .response_computer_tool_call import (
@@ -57,16 +58,18 @@ def make_message(
5758
5859def test_extract_last_content_of_text_message () -> None :
5960 # Build a message containing two text segments.
60- content1 = ResponseOutputText (annotations = [], text = "Hello " , type = "output_text" )
61- content2 = ResponseOutputText (annotations = [], text = "world!" , type = "output_text" )
61+ content1 = ResponseOutputText (annotations = [], text = "Hello " , type = "output_text" , logprobs = [] )
62+ content2 = ResponseOutputText (annotations = [], text = "world!" , type = "output_text" , logprobs = [] )
6263 message = make_message ([content1 , content2 ])
6364 # Helpers should yield the last segment's text.
6465 assert ItemHelpers .extract_last_content (message ) == "world!"
6566
6667
6768def test_extract_last_content_of_refusal_message () -> None :
6869 # Build a message whose last content entry is a refusal.
69- content1 = ResponseOutputText (annotations = [], text = "Before refusal" , type = "output_text" )
70+ content1 = ResponseOutputText (
71+ annotations = [], text = "Before refusal" , type = "output_text" , logprobs = []
72+ )
7073 refusal = ResponseOutputRefusal (refusal = "I cannot do that" , type = "refusal" )
7174 message = make_message ([content1 , refusal ])
7275 # Helpers should extract the refusal string when last content is a refusal.
@@ -87,8 +90,8 @@ def test_extract_last_content_non_message_returns_empty() -> None:
8790
8891def test_extract_last_text_returns_text_only () -> None :
8992 # A message whose last segment is text yields the text.
90- first_text = ResponseOutputText (annotations = [], text = "part1" , type = "output_text" )
91- second_text = ResponseOutputText (annotations = [], text = "part2" , type = "output_text" )
93+ first_text = ResponseOutputText (annotations = [], text = "part1" , type = "output_text" , logprobs = [] )
94+ second_text = ResponseOutputText (annotations = [], text = "part2" , type = "output_text" , logprobs = [] )
9295 message = make_message ([first_text , second_text ])
9396 assert ItemHelpers .extract_last_text (message ) == "part2"
9497 # Whereas when last content is a refusal, extract_last_text returns None.
@@ -116,9 +119,9 @@ def test_input_to_new_input_list_deep_copies_lists() -> None:
116119def test_text_message_output_concatenates_text_segments () -> None :
117120 # Build a message with both text and refusal segments, only text segments are concatenated.
118121 pieces : list [ResponseOutputText | ResponseOutputRefusal ] = []
119- pieces .append (ResponseOutputText (annotations = [], text = "a" , type = "output_text" ))
122+ pieces .append (ResponseOutputText (annotations = [], text = "a" , type = "output_text" , logprobs = [] ))
120123 pieces .append (ResponseOutputRefusal (refusal = "denied" , type = "refusal" ))
121- pieces .append (ResponseOutputText (annotations = [], text = "b" , type = "output_text" ))
124+ pieces .append (ResponseOutputText (annotations = [], text = "b" , type = "output_text" , logprobs = [] ))
122125 message = make_message (pieces )
123126 # Wrap into MessageOutputItem to feed into text_message_output.
124127 item = MessageOutputItem (agent = Agent (name = "test" ), raw_item = message )
@@ -131,8 +134,12 @@ def test_text_message_outputs_across_list_of_runitems() -> None:
131134 that only MessageOutputItem instances contribute any text. The non-message
132135 (ReasoningItem) should be ignored by Helpers.text_message_outputs.
133136 """
134- message1 = make_message ([ResponseOutputText (annotations = [], text = "foo" , type = "output_text" )])
135- message2 = make_message ([ResponseOutputText (annotations = [], text = "bar" , type = "output_text" )])
137+ message1 = make_message (
138+ [ResponseOutputText (annotations = [], text = "foo" , type = "output_text" , logprobs = [])]
139+ )
140+ message2 = make_message (
141+ [ResponseOutputText (annotations = [], text = "bar" , type = "output_text" , logprobs = [])]
142+ )
136143 item1 : RunItem = MessageOutputItem (agent = Agent (name = "test" ), raw_item = message1 )
137144 item2 : RunItem = MessageOutputItem (agent = Agent (name = "test" ), raw_item = message2 )
138145 # Create a non-message run item of a different type, e.g., a reasoning trace.
@@ -142,6 +149,19 @@ def test_text_message_outputs_across_list_of_runitems() -> None:
142149 assert ItemHelpers .text_message_outputs ([item1 , non_message_item , item2 ]) == "foobar"
143150
144151
152+ def test_message_output_item_retains_agent_until_release () -> None :
153+ # Construct the run item with an inline agent to ensure the run item keeps a strong reference.
154+ message = make_message ([ResponseOutputText (annotations = [], text = "hello" , type = "output_text" )])
155+ item = MessageOutputItem (agent = Agent (name = "inline" ), raw_item = message )
156+ assert item .agent is not None
157+ assert item .agent .name == "inline"
158+
159+ # After explicitly releasing, the weak reference should drop once GC runs.
160+ item .release_agent ()
161+ gc .collect ()
162+ assert item .agent is None
163+
164+
145165def test_tool_call_output_item_constructs_function_call_output_dict ():
146166 # Build a simple ResponseFunctionToolCall.
147167 call = ResponseFunctionToolCall (
@@ -171,7 +191,9 @@ def test_tool_call_output_item_constructs_function_call_output_dict():
171191
172192def test_to_input_items_for_message () -> None :
173193 """An output message should convert into an input dict matching the message's own structure."""
174- content = ResponseOutputText (annotations = [], text = "hello world" , type = "output_text" )
194+ content = ResponseOutputText (
195+ annotations = [], text = "hello world" , type = "output_text" , logprobs = []
196+ )
175197 message = ResponseOutputMessage (
176198 id = "m1" , content = [content ], role = "assistant" , status = "completed" , type = "message"
177199 )
@@ -184,6 +206,7 @@ def test_to_input_items_for_message() -> None:
184206 "content" : [
185207 {
186208 "annotations" : [],
209+ "logprobs" : [],
187210 "text" : "hello world" ,
188211 "type" : "output_text" ,
189212 }
@@ -305,6 +328,7 @@ def test_input_to_new_input_list_copies_the_ones_produced_by_pydantic() -> None:
305328 type = "output_text" ,
306329 text = "Hey, what's up?" ,
307330 annotations = [],
331+ logprobs = [],
308332 )
309333 ],
310334 role = "assistant" ,
0 commit comments