Skip to content

Fix issue #7658: [Bug]: BadRequestError from ContentPolicyViolationError #7660

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

Merged
merged 12 commits into from
Apr 3, 2025
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions frontend/src/i18n/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -4494,6 +4494,21 @@
"tr": "OpenHands kredileriniz tükendi",
"de": "Ihre OpenHands-Guthaben sind aufgebraucht"
},
"STATUS$ERROR_LLM_CONTENT_POLICY_VIOLATION": {
"en": "Content policy violation. The output was blocked by content filtering policy.",
"ja": "コンテンツポリシー違反。出力はコンテンツフィルタリングポリシーによってブロックされました。",
"zh-CN": "内容政策违规。输出被内容过滤策略阻止。",
"zh-TW": "內容政策違規。輸出被內容過濾政策阻止。",
"ko-KR": "콘텐츠 정책 위반. 출력이 콘텐츠 필터링 정책에 의해 차단되었습니다.",
"de": "Verstoß gegen Inhaltsrichtlinien. Die Ausgabe wurde durch die Inhaltsfilterrichtlinie blockiert.",
"no": "Brudd på innholdspolicy. Utdata ble blokkert av innholdsfiltrering.",
"it": "Violazione della policy sui contenuti. L'output è stato bloccato dalla policy di filtraggio dei contenuti.",
"pt": "Violação da política de conteúdo. A saída foi bloqueada pela política de filtragem de conteúdo.",
"es": "Violación de la política de contenido. La salida fue bloqueada por la política de filtrado de contenido.",
"ar": "انتهاك سياسة المحتوى. تم حظر المخرجات بواسطة سياسة تصفية المحتوى.",
"fr": "Violation de la politique de contenu. La sortie a été bloquée par la politique de filtrage de contenu.",
"tr": "İçerik politikası ihlali. Çıktı, içerik filtreleme politikası tarafından engellendi."
},
"STATUS$ERROR_RUNTIME_DISCONNECTED": {
"en": "There was an error while connecting to the runtime. Please refresh the page.",
"zh-CN": "运行时已断开连接",
Expand Down
11 changes: 9 additions & 2 deletions openhands/controller/agent_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
APIError,
AuthenticationError,
BadRequestError,
ContentPolicyViolationError,
ContextWindowExceededError,
InternalServerError,
NotFoundError,
Expand Down Expand Up @@ -250,12 +251,17 @@ async def _react_to_exception(
self.state.last_error = err_id
elif isinstance(e, BadRequestError) and 'ExceededBudget' in str(e):
err_id = 'STATUS$ERROR_LLM_OUT_OF_CREDITS'
# Set error reason for budget exceeded
self.state.last_error = err_id
elif isinstance(e, ContentPolicyViolationError) or (
isinstance(e, BadRequestError)
and 'ContentPolicyViolationError' in str(e)
):
err_id = 'STATUS$ERROR_LLM_CONTENT_POLICY_VIOLATION'
self.state.last_error = err_id
elif isinstance(e, RateLimitError):
await self.set_agent_state_to(AgentState.RATE_LIMITED)
return
self.status_callback('error', err_id, self.state.last_error)
await self.status_callback('error', err_id, self.state.last_error)

# Set the agent state to ERROR after storing the reason
await self.set_agent_state_to(AgentState.ERROR)
Expand Down Expand Up @@ -283,6 +289,7 @@ async def _step_with_exception_handling(self):
or isinstance(e, InternalServerError)
or isinstance(e, AuthenticationError)
or isinstance(e, RateLimitError)
or isinstance(e, ContentPolicyViolationError)
or isinstance(e, LLMContextWindowExceedError)
):
reported = e
Expand Down
41 changes: 40 additions & 1 deletion tests/unit/test_agent_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from uuid import uuid4

import pytest
from litellm import ContextWindowExceededError
from litellm import ContentPolicyViolationError, ContextWindowExceededError

from openhands.controller.agent import Agent
from openhands.controller.agent_controller import AgentController
Expand Down Expand Up @@ -166,6 +166,45 @@ async def test_react_to_exception(mock_agent, mock_event_stream, mock_status_cal
await controller.close()


@pytest.mark.asyncio
async def test_react_to_content_policy_violation(
mock_agent, mock_event_stream, mock_status_callback
):
"""Test that the controller properly handles content policy violations from the LLM."""
controller = AgentController(
agent=mock_agent,
event_stream=mock_event_stream,
status_callback=mock_status_callback,
max_iterations=10,
sid='test',
confirmation_mode=False,
headless_mode=True,
)

controller.state.agent_state = AgentState.RUNNING

# Create and handle the content policy violation error
error = ContentPolicyViolationError(
message='Output blocked by content filtering policy',
model='gpt-4',
llm_provider='openai',
)
await controller._react_to_exception(error)

# Verify the status callback was called with correct parameters
mock_status_callback.assert_called_once_with(
'error',
'STATUS$ERROR_LLM_CONTENT_POLICY_VIOLATION',
'STATUS$ERROR_LLM_CONTENT_POLICY_VIOLATION',
)

# Verify the state was updated correctly
assert controller.state.last_error == 'STATUS$ERROR_LLM_CONTENT_POLICY_VIOLATION'
assert controller.state.agent_state == AgentState.ERROR

await controller.close()


@pytest.mark.asyncio
async def test_run_controller_with_fatal_error(test_event_stream, mock_memory):
config = AppConfig()
Expand Down
Loading