Skip to content

Commit 06c6808

Browse files
Fix issue #7658: [Bug]: BadRequestError from ContentPolicyViolationError (#7660)
Co-authored-by: Engel Nyst <[email protected]>
1 parent 6cfaad8 commit 06c6808

File tree

3 files changed

+63
-2
lines changed

3 files changed

+63
-2
lines changed

frontend/src/i18n/translation.json

+15
Original file line numberDiff line numberDiff line change
@@ -4494,6 +4494,21 @@
44944494
"tr": "OpenHands kredileriniz tükendi",
44954495
"de": "Ihre OpenHands-Guthaben sind aufgebraucht"
44964496
},
4497+
"STATUS$ERROR_LLM_CONTENT_POLICY_VIOLATION": {
4498+
"en": "Content policy violation. The output was blocked by content filtering policy.",
4499+
"ja": "コンテンツポリシー違反。出力はコンテンツフィルタリングポリシーによってブロックされました。",
4500+
"zh-CN": "内容政策违规。输出被内容过滤策略阻止。",
4501+
"zh-TW": "內容政策違規。輸出被內容過濾政策阻止。",
4502+
"ko-KR": "콘텐츠 정책 위반. 출력이 콘텐츠 필터링 정책에 의해 차단되었습니다.",
4503+
"de": "Verstoß gegen Inhaltsrichtlinien. Die Ausgabe wurde durch die Inhaltsfilterrichtlinie blockiert.",
4504+
"no": "Brudd på innholdspolicy. Utdata ble blokkert av innholdsfiltrering.",
4505+
"it": "Violazione della policy sui contenuti. L'output è stato bloccato dalla policy di filtraggio dei contenuti.",
4506+
"pt": "Violação da política de conteúdo. A saída foi bloqueada pela política de filtragem de conteúdo.",
4507+
"es": "Violación de la política de contenido. La salida fue bloqueada por la política de filtrado de contenido.",
4508+
"ar": "انتهاك سياسة المحتوى. تم حظر المخرجات بواسطة سياسة تصفية المحتوى.",
4509+
"fr": "Violation de la politique de contenu. La sortie a été bloquée par la politique de filtrage de contenu.",
4510+
"tr": "İçerik politikası ihlali. Çıktı, içerik filtreleme politikası tarafından engellendi."
4511+
},
44974512
"STATUS$ERROR_RUNTIME_DISCONNECTED": {
44984513
"en": "There was an error while connecting to the runtime. Please refresh the page.",
44994514
"zh-CN": "运行时已断开连接",

openhands/controller/agent_controller.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
APIError,
1111
AuthenticationError,
1212
BadRequestError,
13+
ContentPolicyViolationError,
1314
ContextWindowExceededError,
1415
InternalServerError,
1516
NotFoundError,
@@ -250,7 +251,12 @@ async def _react_to_exception(
250251
self.state.last_error = err_id
251252
elif isinstance(e, BadRequestError) and 'ExceededBudget' in str(e):
252253
err_id = 'STATUS$ERROR_LLM_OUT_OF_CREDITS'
253-
# Set error reason for budget exceeded
254+
self.state.last_error = err_id
255+
elif isinstance(e, ContentPolicyViolationError) or (
256+
isinstance(e, BadRequestError)
257+
and 'ContentPolicyViolationError' in str(e)
258+
):
259+
err_id = 'STATUS$ERROR_LLM_CONTENT_POLICY_VIOLATION'
254260
self.state.last_error = err_id
255261
elif isinstance(e, RateLimitError):
256262
await self.set_agent_state_to(AgentState.RATE_LIMITED)
@@ -283,6 +289,7 @@ async def _step_with_exception_handling(self):
283289
or isinstance(e, InternalServerError)
284290
or isinstance(e, AuthenticationError)
285291
or isinstance(e, RateLimitError)
292+
or isinstance(e, ContentPolicyViolationError)
286293
or isinstance(e, LLMContextWindowExceedError)
287294
):
288295
reported = e

tests/unit/test_agent_controller.py

+40-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from uuid import uuid4
44

55
import pytest
6-
from litellm import ContextWindowExceededError
6+
from litellm import ContentPolicyViolationError, ContextWindowExceededError
77

88
from openhands.controller.agent import Agent
99
from openhands.controller.agent_controller import AgentController
@@ -166,6 +166,45 @@ async def test_react_to_exception(mock_agent, mock_event_stream, mock_status_cal
166166
await controller.close()
167167

168168

169+
@pytest.mark.asyncio
170+
async def test_react_to_content_policy_violation(
171+
mock_agent, mock_event_stream, mock_status_callback
172+
):
173+
"""Test that the controller properly handles content policy violations from the LLM."""
174+
controller = AgentController(
175+
agent=mock_agent,
176+
event_stream=mock_event_stream,
177+
status_callback=mock_status_callback,
178+
max_iterations=10,
179+
sid='test',
180+
confirmation_mode=False,
181+
headless_mode=True,
182+
)
183+
184+
controller.state.agent_state = AgentState.RUNNING
185+
186+
# Create and handle the content policy violation error
187+
error = ContentPolicyViolationError(
188+
message='Output blocked by content filtering policy',
189+
model='gpt-4',
190+
llm_provider='openai',
191+
)
192+
await controller._react_to_exception(error)
193+
194+
# Verify the status callback was called with correct parameters
195+
mock_status_callback.assert_called_once_with(
196+
'error',
197+
'STATUS$ERROR_LLM_CONTENT_POLICY_VIOLATION',
198+
'STATUS$ERROR_LLM_CONTENT_POLICY_VIOLATION',
199+
)
200+
201+
# Verify the state was updated correctly
202+
assert controller.state.last_error == 'STATUS$ERROR_LLM_CONTENT_POLICY_VIOLATION'
203+
assert controller.state.agent_state == AgentState.ERROR
204+
205+
await controller.close()
206+
207+
169208
@pytest.mark.asyncio
170209
async def test_run_controller_with_fatal_error(test_event_stream, mock_memory):
171210
config = AppConfig()

0 commit comments

Comments
 (0)