Repository navigation
fix(agent): propagate context compression failures to runner #10645
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,7 +28,7 @@ | |
| from astrbot.core.agent.message import ImageURLPart, TextPart, ThinkPart | ||
| from astrbot.core.agent.tool import FunctionTool, ToolSet | ||
| from astrbot.core.agent.tool_image_cache import tool_image_cache | ||
| from astrbot.core.exceptions import EmptyModelOutputError | ||
| from astrbot.core.exceptions import ContextCompressionError, EmptyModelOutputError | ||
| from astrbot.core.message.components import Json | ||
| from astrbot.core.message.message_event_result import ( | ||
| MessageChain, | ||
|
|
@@ -827,12 +827,29 @@ async def step(self): | |
| # Process request-time context before sending it to the provider. | ||
| token_usage = self.req.conversation.token_usage if self.req.conversation else 0 | ||
| self._simple_print_message_role("[BefCompact]", self.run_context.messages) | ||
| processed_messages = await self._await_or_stop( | ||
| self.request_context_manager.process( | ||
| self.run_context.messages, | ||
| trusted_token_usage=token_usage, | ||
| try: | ||
| processed_messages = await self._await_or_stop( | ||
| self.request_context_manager.process( | ||
| self.run_context.messages, | ||
| trusted_token_usage=token_usage, | ||
| ) | ||
| ) | ||
| ) | ||
| except ContextCompressionError as e: | ||
| max_tokens = self.provider.provider_config.get("max_context_tokens", 0) | ||
| # Keep the count from before compression; a fresh local estimate may | ||
| # undercount the unchanged history and conceal a context overflow. | ||
| if max_tokens > 0 and e.token_count > max_tokens: | ||
| self._transition_state(AgentState.ERROR) | ||
| self.stats.end_time = time.time() | ||
| raise RuntimeError( | ||
| f"Context compression failed and the context exceeds the model's " | ||
| f"maximum context length ({e.token_count} > {max_tokens} tokens): {e}" | ||
| ) from e | ||
|
Comment on lines
+842
to
+847
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Medium · Overflow leaves no final response When a caller relies on the runner’s final response after Store the overflow error as the runner’s final response before raising, or route it through the runner’s normal error-response finalization path. Prompt for AI agentsIn `astrbot/core/agent/runners/tool_loop_agent_runner.py` at lines 842-847:
**Overflow leaves no final response**
When a caller relies on the runner’s final response after `run_agent()` handles a context-overflow exception, the overflow branch raises without assigning `self.final_llm_resp`, unlike the runner’s ordinary provider-error path. Callers that inspect `get_final_llm_resp()` after `run_agent()` handles the exception receive `None` instead of the terminal error response, so they cannot recognize or process the failure through the runner result.
Store the overflow error as the runner’s final response before raising, or route it through the runner’s normal error-response finalization path. |
||
| logger.warning( | ||
| f"{e}. Attempting the uncompressed request " | ||
| f"({e.token_count} tokens, context limit: {max_tokens})." | ||
| ) | ||
| processed_messages = e.messages | ||
| if processed_messages is None: | ||
| yield await self._finalize_aborted_step() | ||
| return | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Medium · Original conversation history is lost
When a custom compressor mutates its input list in place and then raises,
_run_compression()stores the same mutatedmessageslist inContextCompressionError, so the runner uses altered history instead of the pre-compression context and the caller loses history.Preserve a copy of the messages before calling the compressor and store that copy in
ContextCompressionErrorif compression fails.Prompt for AI agents