Skip to content

Commit 7df9bc6

Browse files
lesebclaude
andcommitted
fix: validate moderation endpoint response format and document contract
Log a warning with a link to the OpenAI moderations docs when the endpoint returns an unexpected format. Document the expected request and response shape in the config field description. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Sébastien Han <seb@redhat.com>
1 parent f9e67d4 commit 7df9bc6

3 files changed

Lines changed: 23 additions & 4 deletions

File tree

docs/docs/providers/responses/inline_builtin.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ Be concise, structured, and focused on helping the next LLM seamlessly continue
9393
| `compaction_config.summarization_model` | `str \| None` | No | | Model to use for generating compaction summaries. If not set, uses the same model as the conversation. |
9494
| `compaction_config.default_compact_threshold` | `int \| None` | No | | Default token threshold for auto-compaction via context_management. If set, conversations exceeding this token count will be automatically compacted. |
9595
| `compaction_config.tokenizer_encoding` | `str \| None` | No | | Tiktoken encoding name for token counting (e.g. 'o200k_base', 'cl100k_base'). If not set, the encoding is resolved from the model name via tiktoken.encoding_for_model(). |
96-
| `moderation_endpoint` | `str \| None` | No | | URL of an OpenAI-compatible /v1/moderations endpoint for guardrails. |
96+
| `moderation_endpoint` | `str \| None` | No | | URL of an OpenAI-compatible /v1/moderations endpoint for guardrails. The endpoint must accept POST &#123;"input": "text"&#125; and return &#123;"results": [&#123;"flagged": bool, "categories": &#123;...&#125;&#125;]&#125;. |
9797

9898
## Sample Configuration
9999

src/ogx/providers/inline/responses/builtin/config.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ class BuiltinResponsesImplConfig(BaseModel):
8080

8181
moderation_endpoint: str | None = Field(
8282
default=None,
83-
description="URL of an OpenAI-compatible /v1/moderations endpoint for guardrails.",
83+
description="URL of an OpenAI-compatible /v1/moderations endpoint for guardrails. "
84+
'The endpoint must accept POST {"input": "text"} and return '
85+
'{"results": [{"flagged": bool, "categories": {...}}]}.',
8486
)
8587

8688
@classmethod

src/ogx/providers/inline/responses/builtin/responses/utils.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,11 @@ async def run_guardrails(
548548
moderation_endpoint: str | None,
549549
messages: str,
550550
) -> str | None:
551-
"""Run content moderation by calling an external OpenAI-compatible moderation endpoint."""
551+
"""Run content moderation by calling an external OpenAI-compatible moderation endpoint.
552+
553+
The endpoint must conform to the OpenAI Moderations API response format:
554+
{"id": "...", "model": "...", "results": [{"flagged": bool, "categories": {...}, ...}]}
555+
"""
552556
if not messages or not moderation_endpoint:
553557
return None
554558

@@ -562,7 +566,20 @@ async def run_guardrails(
562566
logger.warning("Failed to call moderation endpoint", endpoint=moderation_endpoint)
563567
return None
564568

565-
for result in resp.json().get("results", []):
569+
data = resp.json()
570+
results = data.get("results")
571+
if not isinstance(results, list):
572+
logger.warning(
573+
"Moderation endpoint returned unexpected format (expected OpenAI-compatible "
574+
"response with 'results' array, see https://platform.openai.com/docs/api-reference/moderations)",
575+
endpoint=moderation_endpoint,
576+
response_keys=list(data.keys()) if isinstance(data, dict) else type(data).__name__,
577+
)
578+
return None
579+
580+
for result in results:
581+
if not isinstance(result, dict):
582+
continue
566583
if result.get("flagged", False):
567584
categories = result.get("categories", {})
568585
flagged_cats = [c for c, f in categories.items() if f]

0 commit comments

Comments
 (0)