ADR-0012: Proxy request normalization (OpenAI chat)
Architecture decision record 0012.
ADR-0012: Proxy request normalization (OpenAI chat)
- Status: Accepted
- Date: 2026-06-04
- Authors: IBEX Harness team
Context
Milestone 1.2.1 added auth middleware and a 501 stub on POST /v1/chat/completions without reading the body. Goal 1.2 requires OpenAI-shaped JSON to parse into an internal type before provider forwarding (Phase 2). Milestone 1.2.3 owns semantic validation, body size limits, and full error envelope extensions.
Decision
1) Route and path
- Service route:
POST /v1/chat/completions(implemented in proxy router) - API documentation:
/proxy/v1/chat/completionsis the external/gateway prefix alias (same handler behind ingress)
2) Package layout
services/proxy/internal/llm/— request DTOs and JSON parse onlyservices/proxy/internal/upstream/— reserved for Phase 2 provider HTTP client (FILE_STRUCTURE.md)
3) Parse scope (1.2.2)
- Decode JSON object; ignore unknown top-level fields (OpenAI compatibility)
- Require
messages(when present) to be a JSON array of objects withroleandcontentstrings - Extract
model,messages,stream, optionaltemperature,max_tokens - Do not reject empty
modelor emptymessages(1.2.3 validator) - Do not enforce
Content-Type, body size, or IBEX headers (1.2.3)
4) Errors
| Condition | HTTP | code |
|---|---|---|
| Malformed JSON / wrong JSON shape | 400 | INVALID_JSON |
| Valid parse, no provider | 501 | PROVIDER_NOT_CONFIGURED |
Uses existing services/proxy/internal/errors/ envelope (ADR-0011).
5) Logging
Per SECURITY.md §10.1: after successful parse, log metadata only — org_id, request_id, model, message_count, stream. Never log message content.
6) Middleware order
metrics → logging → auth → handler in 1.2.2 only. 1.2.3+ (ADR-0013): metrics → requestContext → responseHeaders → logging → [bodyLimit → contentType → auth] → handler
7) Deferred to 1.2.3
MaxRequestBodyBytes, 413/415VALIDATION_ERRORwithfield_errors- Required
model, role enum, message bounds X-IBEX-Agent-IDUUID validation- Response headers
X-Request-ID,X-Trace-ID,X-Response-Timeon all responses
Consequences
Positive
- Goal 1.2 parse criterion met without provider HTTP
- Clear boundary with validation milestone
- Parsed request on context for 1.2.3 and Phase 2 upstream
Negative
- Unbounded body read until 1.2.3 (documented risk)
- Empty model still returns 501 until semantic validation lands
References
- Milestone 1.2.2
- Milestone 1.2.3
- API_DOCUMENTATION.md — LLM Proxy API
Was this page helpful?
Last updated on