@@ -290,6 +290,40 @@ async def another_regular_tool(x: str) -> str:
290
290
assert result_output .usage () == snapshot (RunUsage (requests = 2 , input_tokens = 103 , output_tokens = 15 , tool_calls = 1 ))
291
291
292
292
293
+ async def test_output_tool_allowed_at_limit () -> None :
294
+ """Test that output tools can be called even when at the tool_calls_limit."""
295
+
296
+ class MyOutput (BaseModel ):
297
+ result : str
298
+
299
+ def call_output_after_regular (messages : list [ModelMessage ], info : AgentInfo ) -> ModelResponse :
300
+ if len (messages ) == 1 :
301
+ return ModelResponse (
302
+ parts = [
303
+ ToolCallPart ('regular_tool' , {'x' : 'test' }, 'call_1' ),
304
+ ],
305
+ usage = RequestUsage (input_tokens = 10 , output_tokens = 5 ),
306
+ )
307
+ else :
308
+ return ModelResponse (
309
+ parts = [
310
+ ToolCallPart ('final_result' , {'result' : 'success' }, 'call_2' ),
311
+ ],
312
+ usage = RequestUsage (input_tokens = 10 , output_tokens = 5 ),
313
+ )
314
+
315
+ test_agent = Agent (FunctionModel (call_output_after_regular ), output_type = ToolOutput (MyOutput ))
316
+
317
+ @test_agent .tool_plain
318
+ async def regular_tool (x : str ) -> str :
319
+ return f'{ x } -processed'
320
+
321
+ result = await test_agent .run ('test' , usage_limits = UsageLimits (tool_calls_limit = 1 ))
322
+
323
+ assert result .output .result == 'success'
324
+ assert result .usage () == snapshot (RunUsage (requests = 2 , input_tokens = 20 , output_tokens = 10 , tool_calls = 1 ))
325
+
326
+
293
327
async def test_failed_tool_calls_not_counted () -> None :
294
328
"""Test that failed tool calls (raising ModelRetry) are not counted."""
295
329
test_agent = Agent (TestModel ())
0 commit comments