You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
refactor!: remove Safety API and replace with moderation_endpoint (#5291)
## Summary
Remove the entire Safety API subsystem and replace it with a single
`moderation_endpoint` config field on the responses provider. Guardrails
are now a simple boolean toggle.
### Before
```
Client → POST /v1/moderations → SafetyRouter → ShieldsRoutingTable → Provider → external service
Client → Responses API (guardrails=["llama-guard"]) → safety_api.run_moderation() → same chain
```
### After
```
Client → Responses API (guardrails=true) → httpx.post(moderation_endpoint) → external service
```
No proxy endpoint, no routing table, no provider abstraction, no model
IDs. One config field, one boolean, one HTTP call.
### What's removed
- **`Api.safety` and `Api.shields`** — enum values, protocol
definitions, routing tables, routers
- **`/v1/moderations`** — standalone endpoint (users call external
moderation services directly)
- **`/v1/shields`** — shield introspection endpoints
- **`/v1/safety/run-shield`** — native shield endpoint
- **All safety providers** — `inline::llama-guard`,
`inline::code-scanner`, `inline::prompt-guard`, `remote::bedrock`,
`remote::nvidia`, `remote::sambanova`, `remote::passthrough`
- **Safety infrastructure** — `SafetyRouter`, `ShieldsRoutingTable`,
`SafetyConfig`, `ShieldStore`, `ShieldToModerationMixin`,
`ShieldRunnerMixin`, `ResponseGuardrailSpec`
- **Shield registration endpoints** — `POST /v1/shields`, `DELETE
/v1/shields/{identifier}`
### What's added
- **`moderation_endpoint`** on `BuiltinResponsesImplConfig` — URL of an
OpenAI-compatible `/v1/moderations` endpoint. Documented contract:
accepts `POST {"input": "text"}`, returns `{"results": [{"flagged":
bool, "categories": {...}}]}`
- **Response format validation** — logs a warning with a link to the
OpenAI moderations docs if the endpoint returns an unexpected format
- **`guardrails: bool`** — simplified from a list of model IDs to a
boolean toggle via `extra_body`
- **`x-extra-body-field`** — new OpenAPI generator mechanism to keep
`guardrails` typed server-side while hidden from the public schema
### Configuration
```yaml
providers:
responses:
- provider_id: builtin
provider_type: inline::builtin
config:
moderation_endpoint: "https://api.openai.com/v1/moderations"
```
```python
client.responses.create(
model="gpt-4o",
input="Hello",
extra_body={"guardrails": True},
)
```
### Rationale
- `/v1/moderations` as a passthrough adds a network hop for zero value —
clients can call any moderation service directly
- The only server-side value is guardrails during generation
(mid-generation moderation checks aren't composable from the client).
This is now a direct HTTP call
- 6 safety providers with different auth, config, and edge cases is too
much maintenance. Users point `moderation_endpoint` at OpenAI (free),
Azure, or any compatible service
- Letting clients pass URLs in requests would be an SSRF vector, so
`moderation_endpoint` is server-config only
## Test plan
- [x] `uv run pre-commit run --all-files` passes
- [x] Distribution codegen passes
- [x] Provider codegen passes
- [x] OpenAPI spec generation passes (46 paths, 65 operations)
- [x] Unit tests fixed and passing (streaming guardrail test, lazy
imports, env var tests)
- [x] OpenAI coverage baseline updated (92.9%)
- [ ] Full CI (integration tests)
BREAKING CHANGE: Safety API removed entirely. `/v1/moderations`,
`/v1/shields`, `/v1/safety/run-shield` endpoints removed. All safety
providers removed. `guardrails` field changed from list to boolean.
Configure `moderation_endpoint` on the responses provider config.
---------
Signed-off-by: Sébastien Han <seb@redhat.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: ARCHITECTURE.md
+14-23Lines changed: 14 additions & 23 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,7 @@ This document describes the internal architecture of OGX for contributors and AI
4
4
5
5
## System Overview
6
6
7
-
OGX is a server that exposes a unified API for AI capabilities: inference, agents, safety, vector storage, evaluation, and more. It is provider-agnostic: the same API works whether the backend is Ollama, OpenAI, vLLM, Fireworks, or dozens of other services.
7
+
OGX is a server that exposes a unified API for AI capabilities: inference, responses orchestration, vector storage, tool execution, evaluation, and more. It is provider-agnostic: the same API works whether the backend is Ollama, OpenAI, vLLM, Fireworks, or dozens of other services.
8
8
9
9
The codebase is split into two packages:
10
10
@@ -30,7 +30,7 @@ Route Dispatch
30
30
v
31
31
Router (src/ogx/core/routers/)
32
32
|
33
-
|-- Looks up the resource (model, shield, etc.) in the RoutingTable
33
+
|-- Looks up the resource (model, vector store, tool group, etc.) in the RoutingTable
34
34
|-- Resolves which provider handles this resource
35
35
|-- Enforces access control policies
36
36
|
@@ -80,7 +80,7 @@ Each provider spec declares:
80
80
81
81
### Provider Registry
82
82
83
-
`src/ogx/providers/registry/` contains one file per API (e.g., `inference.py`, `safety.py`). Each file defines an `available_providers()` function that returns all `ProviderSpec` objects for that API. The registry is loaded at startup by `get_provider_registry()` in `core/distribution.py`.
83
+
`src/ogx/providers/registry/` contains one file per API (e.g., `inference.py`, `responses.py`). Each file defines an `available_providers()` function that returns all `ProviderSpec` objects for that API. The registry is loaded at startup by `get_provider_registry()` in `core/distribution.py`.
The full list of auto-routed pairs is defined in `builtin_automatically_routed_apis()` in `core/distribution.py`:
107
107
108
-
| Routing Table API | Router API |
109
-
|-----------------------|------------------|
110
-
|`Api.models`|`Api.inference`|
111
-
|`Api.shields`|`Api.safety`|
112
-
|`Api.datasets`|`Api.datasetio`|
113
-
|`Api.scoring_functions`|`Api.scoring`|
114
-
|`Api.benchmarks`|`Api.eval`|
115
-
|`Api.tool_groups`|`Api.tool_runtime`|
116
-
|`Api.vector_stores`|`Api.vector_io`|
108
+
| Routing Table API | Router API |
109
+
|---------------------|--------------------|
110
+
|`Api.models`|`Api.inference`|
111
+
|`Api.tool_groups`|`Api.tool_runtime`|
112
+
|`Api.vector_stores`|`Api.vector_io`|
117
113
118
114
## The API Layer (`ogx_api`)
119
115
120
116
The `ogx_api` package defines all public-facing types and protocols:
121
117
122
-
-**Protocols** -- Python `Protocol` classes like `Inference`, `Safety` that define the API contract. HTTP routes are defined via FastAPI routers in `fastapi_routes.py` modules.
123
-
-**Data Types** -- Pydantic models for requests, responses, and resources (e.g., `Model`, `Shield`, `ChatCompletionRequest`).
118
+
-**Protocols** -- Python `Protocol` classes like `Inference`, `Responses` that define the API contract. HTTP routes are defined via FastAPI routers in `fastapi_routes.py` modules.
119
+
-**Data Types** -- Pydantic models for requests, responses, and resources (e.g., `Model`, `VectorStore`, `ChatCompletionRequest`).
124
120
-**Provider Specs** -- `InlineProviderSpec`, `RemoteProviderSpec`, and related types that define how providers are declared.
125
121
-**Internal utilities** -- KVStore and SqlStore abstract interfaces live here so third-party providers can use them without depending on the full server.
126
122
@@ -171,7 +167,7 @@ Used by: inference store (chat completion logs), conversations, prompts.
171
167
172
168
### Distribution Registry
173
169
174
-
`src/ogx/core/store/`implements `DistributionRegistry`, which tracks all registered resources (models, shields, datasets, etc.) across providers. It persists to the configured KVStore so resources survive server restarts.
170
+
`src/ogx/core/store/`implements `DistributionRegistry`, which tracks all registered resources (models, vector stores, tool groups, prompts, etc.) across providers. It persists to the configured KVStore so resources survive server restarts.
0 commit comments