Skip to content

Commit 537d6ce

Browse files
committed
Put error in increment_retries to allow attempts, check lists for elements, check if args actually are incomplete or not
1 parent 78fb707 commit 537d6ce

File tree

4 files changed

+54
-0
lines changed

4 files changed

+54
-0
lines changed

pydantic_ai_slim/pydantic_ai/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
FallbackExceptionGroup,
2525
ModelHTTPError,
2626
ModelRetry,
27+
ToolExceedsTokenLimitError,
2728
UnexpectedModelBehavior,
2829
UsageLimitExceeded,
2930
UserError,
@@ -124,6 +125,7 @@
124125
'ModelRetry',
125126
'ModelHTTPError',
126127
'FallbackExceptionGroup',
128+
'ToolExceedsTokenLimitError',
127129
'UnexpectedModelBehavior',
128130
'UsageLimitExceeded',
129131
'UserError',

pydantic_ai_slim/pydantic_ai/_agent_graph.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,19 @@ class GraphAgentState:
9595
def increment_retries(self, max_result_retries: int, error: BaseException | None = None) -> None:
9696
self.retries += 1
9797
if self.retries > max_result_retries:
98+
if (
99+
self.message_history
100+
and isinstance(model_response := self.message_history[-1], _messages.ModelResponse)
101+
and model_response.finish_reason == 'length'
102+
and model_response.parts
103+
and isinstance(tool_call := model_response.parts[-1], _messages.ToolCallPart)
104+
):
105+
try:
106+
tool_call.args_as_dict()
107+
except Exception:
108+
raise exceptions.ToolExceedsTokenLimitError(
109+
'Model token limit exceeded while emitting a tool call. Increase max tokens or simplify tool call arguments.'
110+
)
98111
message = f'Exceeded maximum retries ({max_result_retries}) for output validation'
99112
if error:
100113
if isinstance(error, exceptions.UnexpectedModelBehavior) and error.__cause__ is not None:

pydantic_ai_slim/pydantic_ai/exceptions.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
'UnexpectedModelBehavior',
2424
'UsageLimitExceeded',
2525
'ModelHTTPError',
26+
'ToolExceedsTokenLimitError',
2627
'FallbackExceptionGroup',
2728
)
2829

@@ -168,3 +169,7 @@ class ToolRetryError(Exception):
168169
def __init__(self, tool_retry: RetryPromptPart):
169170
self.tool_retry = tool_retry
170171
super().__init__()
172+
173+
174+
class ToolExceedsTokenLimitError(AgentRunError):
175+
"""Error raised when a model stops due to token limit while emitting a tool call."""

tests/test_agent.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
SystemPromptPart,
4141
TextPart,
4242
ToolCallPart,
43+
ToolExceedsTokenLimitError,
4344
ToolReturn,
4445
ToolReturnPart,
4546
UnexpectedModelBehavior,
@@ -2448,6 +2449,39 @@ def empty(m: list[ModelMessage], _info: AgentInfo) -> ModelResponse:
24482449
)
24492450

24502451

2452+
def test_tool_exceeds_token_limit_error():
2453+
def return_incomplete_tool(_: list[ModelMessage], info: AgentInfo) -> ModelResponse:
2454+
resp = ModelResponse(parts=[ToolCallPart('dummy_tool', args='{"foo": "bar",')])
2455+
resp.finish_reason = 'length'
2456+
return resp
2457+
2458+
agent = Agent(FunctionModel(return_incomplete_tool), output_type=str)
2459+
2460+
with pytest.raises(
2461+
ToolExceedsTokenLimitError,
2462+
match='Model token limit exceeded while emitting a tool call. Increase max tokens or simplify tool call arguments.',
2463+
):
2464+
agent.run_sync('Hello')
2465+
2466+
2467+
def test_tool_exceeds_token_limit_but_complete_args():
2468+
def return_complete_tool_but_hit_limit(messages: list[ModelMessage], info: AgentInfo) -> ModelResponse:
2469+
if len(messages) == 1:
2470+
resp = ModelResponse(parts=[ToolCallPart('dummy_tool', args='{"foo": "bar"}')])
2471+
resp.finish_reason = 'length'
2472+
return resp
2473+
return ModelResponse(parts=[TextPart('done')])
2474+
2475+
agent = Agent(FunctionModel(return_complete_tool_but_hit_limit), output_type=str)
2476+
2477+
@agent.tool_plain
2478+
def dummy_tool(foo: str) -> str:
2479+
return 'tool-ok'
2480+
2481+
result = agent.run_sync('Hello')
2482+
assert result.output == 'done'
2483+
2484+
24512485
def test_model_requests_blocked(env: TestEnv):
24522486
try:
24532487
env.set('GEMINI_API_KEY', 'foobar')

0 commit comments

Comments
 (0)