Fix SambaNova context length exception handling (#9252)

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
Graham Neubig 2025-06-23 07:06:31 -04:00 committed by GitHub
parent 1e33624951
commit 9b4ad4e6e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 140 additions and 0 deletions

View File

@ -821,6 +821,11 @@ class AgentController:
or 'input length and `max_tokens` exceed context limit' in error_str
or 'please reduce the length of either one'
in error_str # For OpenRouter context window errors
or (
'sambanovaexception' in error_str
and 'maximum context length' in error_str
)
# For SambaNova context window errors - only match when both patterns are present
or isinstance(e, ContextWindowExceededError)
):
if self.agent.config.enable_history_truncation:

View File

@ -1682,3 +1682,138 @@ async def test_openrouter_context_window_exceeded_error(
)
await controller.close()
@pytest.mark.asyncio
async def test_sambanova_context_window_exceeded_error(
mock_agent, test_event_stream, mock_status_callback
):
"""Test that SambaNova context window exceeded errors are properly detected and handled."""
max_iterations = 5
error_after = 2
class StepState:
def __init__(self):
self.has_errored = False
self.index = 0
self.views = []
def step(self, state: State):
# Store the view for later inspection
self.views.append(state.view)
# only throw it once.
if self.index < error_after or self.has_errored:
self.index += 1
return MessageAction(content=f'Test message {self.index}')
# Create a BadRequestError with the SambaNova context window exceeded message pattern
error = BadRequestError(
message='litellm.BadRequestError: SambanovaException - The maximum context length of DeepSeek-V3-0324 is 32768. However, answering your request will take 39732 tokens. Please reduce the length of the messages or the specified max_completion_tokens value.',
model='sambanova/deepseek-v3-0324',
llm_provider='sambanova',
)
self.has_errored = True
raise error
step_state = StepState()
mock_agent.step = step_state.step
mock_agent.config = AgentConfig(enable_history_truncation=True)
controller = AgentController(
agent=mock_agent,
event_stream=test_event_stream,
iteration_delta=max_iterations,
sid='test',
confirmation_mode=False,
headless_mode=True,
status_callback=mock_status_callback,
)
# Set the agent state to RUNNING
controller.state.agent_state = AgentState.RUNNING
# Run the controller until it hits the error
for _ in range(error_after + 2): # +2 to ensure we go past the error
await controller._step()
if step_state.has_errored:
break
# Verify that the error was handled as a context window exceeded error
# by checking that _handle_long_context_error was called (which adds a CondensationAction)
events = list(test_event_stream.get_events())
condensation_actions = [e for e in events if isinstance(e, CondensationAction)]
# There should be at least one CondensationAction if the error was handled correctly
assert len(condensation_actions) > 0, (
'SambaNova context window exceeded error was not handled correctly'
)
await controller.close()
@pytest.mark.asyncio
async def test_sambanova_generic_exception_not_handled_as_context_error(
mock_agent, test_event_stream, mock_status_callback
):
"""Test that generic SambaNova exceptions (without context length pattern) are NOT handled as context window errors."""
max_iterations = 5
error_after = 2
class StepState:
def __init__(self):
self.has_errored = False
self.index = 0
self.views = []
def step(self, state: State):
# Store the view for later inspection
self.views.append(state.view)
# only throw it once.
if self.index < error_after or self.has_errored:
self.index += 1
return MessageAction(content=f'Test message {self.index}')
# Create a BadRequestError with a generic SambaNova error (no context length pattern)
error = BadRequestError(
message='litellm.BadRequestError: SambanovaException - Some other error occurred',
model='sambanova/deepseek-v3-0324',
llm_provider='sambanova',
)
self.has_errored = True
raise error
step_state = StepState()
mock_agent.step = step_state.step
mock_agent.config = AgentConfig(enable_history_truncation=True)
controller = AgentController(
agent=mock_agent,
event_stream=test_event_stream,
iteration_delta=max_iterations,
sid='test',
confirmation_mode=False,
headless_mode=True,
status_callback=mock_status_callback,
)
# Set the agent state to RUNNING
controller.state.agent_state = AgentState.RUNNING
# Run the controller until it hits the error
with pytest.raises(BadRequestError):
for _ in range(error_after + 2): # +2 to ensure we go past the error
await controller._step()
if step_state.has_errored:
break
# Verify that the error was NOT handled as a context window exceeded error
# by checking that _handle_long_context_error was NOT called (no CondensationAction should be added)
events = list(test_event_stream.get_events())
condensation_actions = [e for e in events if isinstance(e, CondensationAction)]
# There should be NO CondensationAction if the error was correctly NOT handled as context window error
assert len(condensation_actions) == 0, (
'Generic SambaNova exception was incorrectly handled as context window error'
)
await controller.close()