|
28 | 28 | from ..telemetry.tracer import get_tracer |
29 | 29 | from ..tools.executor import run_tools, validate_and_prepare_tools |
30 | 30 | from ..types.content import Message |
31 | | -from ..types.exceptions import ContextWindowOverflowException, EventLoopException, ModelThrottledException |
| 31 | +from ..types.exceptions import ( |
| 32 | + ContextWindowOverflowException, |
| 33 | + EventLoopException, |
| 34 | + MaxTokensReachedException, |
| 35 | + ModelThrottledException, |
| 36 | +) |
32 | 37 | from ..types.streaming import Metrics, StopReason |
33 | 38 | from ..types.tools import ToolChoice, ToolChoiceAuto, ToolConfig, ToolGenerator, ToolResult, ToolUse |
34 | 39 | from .streaming import stream_messages |
@@ -187,6 +192,22 @@ async def event_loop_cycle(agent: "Agent", invocation_state: dict[str, Any]) -> |
187 | 192 | raise e |
188 | 193 |
|
189 | 194 | try: |
| 195 | + if stop_reason == "max_tokens": |
| 196 | + """ |
| 197 | + Handle max_tokens limit reached by the model. |
| 198 | + |
| 199 | + When the model reaches its maximum token limit, this represents a potentially unrecoverable |
| 200 | + state where the model's response was truncated. By default, Strands fails hard with an |
| 201 | + MaxTokensReachedException to maintain consistency with other failure types. |
| 202 | + """ |
| 203 | + raise MaxTokensReachedException( |
| 204 | + message=( |
| 205 | + "Agent has reached an unrecoverable state due to max_tokens limit. " |
| 206 | + "For more information see: " |
| 207 | + "https://strandsagents.com/latest/user-guide/concepts/agents/agent-loop/#maxtokensreachedexception" |
| 208 | + ), |
| 209 | + incomplete_message=message, |
| 210 | + ) |
190 | 211 | # Add message in trace and mark the end of the stream messages trace |
191 | 212 | stream_trace.add_message(message) |
192 | 213 | stream_trace.end() |
@@ -231,7 +252,8 @@ async def event_loop_cycle(agent: "Agent", invocation_state: dict[str, Any]) -> |
231 | 252 | # Don't yield or log the exception - we already did it when we |
232 | 253 | # raised the exception and we don't need that duplication. |
233 | 254 | raise |
234 | | - except ContextWindowOverflowException as e: |
| 255 | + except (ContextWindowOverflowException, MaxTokensReachedException) as e: |
| 256 | + # Special cased exceptions which we want to bubble up rather than get wrapped in an EventLoopException |
235 | 257 | if cycle_span: |
236 | 258 | tracer.end_span_with_error(cycle_span, str(e), e) |
237 | 259 | raise e |
|
0 commit comments