1919from semantic_kernel .contents .streaming_chat_message_content import StreamingChatMessageContent
2020from semantic_kernel .contents .streaming_text_content import StreamingTextContent
2121from semantic_kernel .contents .utils .author_role import AuthorRole
22+ from semantic_kernel .functions import KernelArguments
2223
2324
2425@pytest .fixture
@@ -67,7 +68,6 @@ def mock_thread():
6768 return thread
6869
6970
70- @pytest .mark .asyncio
7171async def test_invoke_no_function_calls (mock_agent , mock_response , mock_chat_history , mock_thread ):
7272 async def mock_get_response (* args , ** kwargs ):
7373 return mock_response
@@ -89,7 +89,6 @@ async def mock_get_response(*args, **kwargs):
8989 assert final_msg .role == AuthorRole .ASSISTANT
9090
9191
92- @pytest .mark .asyncio
9392async def test_invoke_raises_on_failed_response (mock_agent , mock_chat_history , mock_thread ):
9493 mock_failed_response = MagicMock (spec = Response )
9594 mock_failed_response .status = "failed"
@@ -115,7 +114,6 @@ async def mock_get_response(*args, **kwargs):
115114 pass
116115
117116
118- @pytest .mark .asyncio
119117async def test_invoke_reaches_maximum_attempts (mock_agent , mock_chat_history , mock_thread ):
120118 call_counter = 0
121119
@@ -173,7 +171,6 @@ async def mock_get_response(*args, **kwargs):
173171 assert messages is not None
174172
175173
176- @pytest .mark .asyncio
177174async def test_invoke_with_function_calls (mock_agent , mock_chat_history , mock_thread ):
178175 initial_response = MagicMock (spec = Response )
179176 initial_response .status = "completed"
@@ -227,6 +224,138 @@ async def mock_get_response(*args, **kwargs):
227224 assert len (messages ) == 3 , f"Expected exactly 3 messages, got { len (messages )} "
228225
229226
227+ async def test_invoke_passes_kernel_arguments_to_kernel (mock_agent , mock_chat_history , mock_thread ):
228+ # Prepare a response that triggers a function call
229+ initial_response = MagicMock (spec = Response )
230+ initial_response .status = "completed"
231+ initial_response .id = "fake-response-id"
232+ initial_response .output = [
233+ ResponseFunctionToolCall (
234+ id = "tool_call_id" ,
235+ call_id = "call_id" ,
236+ name = "test_function" ,
237+ arguments = '{"some_arg": 123}' ,
238+ type = "function_call" ,
239+ )
240+ ]
241+ initial_response .error = None
242+ initial_response .incomplete_details = None
243+ initial_response .created_at = 123456
244+ initial_response .usage = None
245+ initial_response .role = "assistant"
246+
247+ final_response = MagicMock (spec = Response )
248+ final_response .status = "completed"
249+ final_response .id = "fake-final-response-id"
250+ final_response .output = []
251+ final_response .error = None
252+ final_response .incomplete_details = None
253+ final_response .created_at = 123456
254+ final_response .usage = None
255+ final_response .role = "assistant"
256+
257+ responses = [initial_response , final_response ]
258+
259+ async def mock_invoke_fc (* args , ** kwargs ):
260+ # Assert that KernelArguments were forwarded
261+ assert isinstance (kwargs .get ("arguments" ), KernelArguments )
262+ assert kwargs ["arguments" ].get ("foo" ) == "bar"
263+ return MagicMock (terminate = False )
264+
265+ mock_agent .kernel .invoke_function_call = MagicMock (side_effect = mock_invoke_fc )
266+
267+ async def mock_get_response (* args , ** kwargs ):
268+ return responses .pop (0 )
269+
270+ with patch .object (ResponsesAgentThreadActions , "_get_response" , new = mock_get_response ):
271+ args = KernelArguments (foo = "bar" )
272+ # Run invoke and ensure no assertion fails inside mock_invoke_fc
273+ collected = []
274+ async for _ , msg in ResponsesAgentThreadActions .invoke (
275+ agent = mock_agent ,
276+ chat_history = mock_chat_history ,
277+ thread = mock_thread ,
278+ store_enabled = True ,
279+ function_choice_behavior = MagicMock (maximum_auto_invoke_attempts = 1 ),
280+ arguments = args ,
281+ ):
282+ collected .append (msg )
283+ assert len (collected ) >= 2
284+
285+
286+ async def test_invoke_stream_passes_kernel_arguments_to_kernel (mock_agent , mock_chat_history , mock_thread ):
287+ class MockStream (AsyncStream [ResponseStreamEvent ]):
288+ def __init__ (self , events ):
289+ self ._events = events
290+
291+ async def __aenter__ (self ):
292+ return self
293+
294+ async def __aexit__ (self , exc_type , exc_val , exc_tb ):
295+ pass
296+
297+ def __aiter__ (self ):
298+ return self
299+
300+ async def __anext__ (self ):
301+ if not self ._events :
302+ raise StopAsyncIteration
303+ return self ._events .pop (0 )
304+
305+ # Event that includes a function call
306+ mock_tool_call_event = ResponseOutputItemAddedEvent (
307+ item = ResponseFunctionToolCall (
308+ id = "fake-tool-call-id" ,
309+ call_id = "fake-call-id" ,
310+ name = "test_function" ,
311+ arguments = '{"arg": 123}' ,
312+ type = "function_call" ,
313+ ),
314+ output_index = 0 ,
315+ type = "response.output_item.added" ,
316+ sequence_number = 0 ,
317+ )
318+
319+ mock_stream_event_end = ResponseOutputItemDoneEvent (
320+ item = ResponseOutputMessage (
321+ role = "assistant" ,
322+ status = "completed" ,
323+ id = "fake-item-id" ,
324+ content = [ResponseOutputText (text = "Final message after tool call" , type = "output_text" , annotations = [])],
325+ type = "message" ,
326+ ),
327+ output_index = 0 ,
328+ sequence_number = 0 ,
329+ type = "response.output_item.done" ,
330+ )
331+
332+ async def mock_get_response (* args , ** kwargs ):
333+ return MockStream ([mock_tool_call_event , mock_stream_event_end ])
334+
335+ async def mock_invoke_function_call (* args , ** kwargs ):
336+ assert isinstance (kwargs .get ("arguments" ), KernelArguments )
337+ assert kwargs ["arguments" ].get ("foo" ) == "bar"
338+ return MagicMock (terminate = False )
339+
340+ mock_agent .kernel .invoke_function_call = MagicMock (side_effect = mock_invoke_function_call )
341+
342+ with patch .object (ResponsesAgentThreadActions , "_get_response" , new = mock_get_response ):
343+ args = KernelArguments (foo = "bar" )
344+ collected_stream_messages = []
345+ async for _ in ResponsesAgentThreadActions .invoke_stream (
346+ agent = mock_agent ,
347+ chat_history = mock_chat_history ,
348+ thread = mock_thread ,
349+ store_enabled = True ,
350+ function_choice_behavior = MagicMock (maximum_auto_invoke_attempts = 1 ),
351+ output_messages = collected_stream_messages ,
352+ arguments = args ,
353+ ):
354+ pass
355+ # If assertions passed in mock, arguments were forwarded
356+ assert len (collected_stream_messages ) >= 1
357+
358+
230359async def test_invoke_stream_no_function_calls (mock_agent , mock_chat_history , mock_thread ):
231360 class MockStream (AsyncStream [ResponseStreamEvent ]):
232361 def __init__ (self , events ):
@@ -294,7 +423,6 @@ async def mock_get_response(*args, **kwargs):
294423 assert collected_stream_messages [0 ].role == AuthorRole .ASSISTANT
295424
296425
297- @pytest .mark .asyncio
298426async def test_invoke_stream_with_tool_calls (mock_agent , mock_chat_history , mock_thread ):
299427 class MockStream (AsyncStream [ResponseStreamEvent ]):
300428 def __init__ (self , events ):
0 commit comments