Provider adapters
Pluggable adapters for OpenAI-compatible and other LLM providers.
Provider adapters translate normalized IBEX chat requests into upstream LLM API calls and stream responses back to clients. Phase 1 ships the adapter interface and registry skeleton only — no adapter is registered at runtime, so every valid chat request stops with 501 PROVIDER_NOT_CONFIGURED.
Why adapters exist
IBEX Harness must support multiple LLM vendors without leaking provider specifics into middleware. Adapters isolate:
- Upstream URL and authentication (API keys, Azure deployment IDs)
- Request/response dialect differences (tool calls, streaming chunk format)
- Retry and circuit-breaker policy per provider
The proxy critical path stays provider-agnostic: normalize once, delegate to the registry, stream the response. Target overhead remains under 20ms p99 excluding upstream LLM latency — see Architecture overview.
Planned adapter contract
Register at startup
Each adapter registers a name, supported model prefixes, and a Forward function with the proxy registry.
Receive normalized payload
Input is the validated OpenAI chat JSON plus org_id, agent_id, and request_id from middleware context.
Call upstream
Adapter applies provider credentials from org-scoped configuration (never from the client request body).
Stream response
Return OpenAI-compatible JSON or SSE chunks to the client; accumulate for async trace emission in later phases.
Adapters must not read org_id from the request body. Tenant context comes from verified auth middleware only — Tenant isolation.
Phase 1 behavior
Every authenticated, validated chat request hits the stub handler:
curl -s http://localhost:8080/v1/chat/completions \
-H "Authorization: Bearer ${IBEX_DEV_TOKEN}" \
-H "X-IBEX-Agent-ID: ${IBEX_DEV_AGENT_ID}" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-4o","messages":[{"role":"user","content":"hi"}]}' \
| jq '.error.code'Expected output: "PROVIDER_NOT_CONFIGURED".
Error envelope
{
"error": {
"code": "PROVIDER_NOT_CONFIGURED",
"message": "No LLM provider adapter is configured for this request",
"request_id": "0192a3b4-c5d6-7890-abcd-ef1234567890",
"timestamp": "2026-06-14T12:00:00Z"
}
}This is distinct from 503 dependency failures — the proxy itself is healthy; forwarding is simply not implemented yet.
What Phase 2 adds
Registry
Model-to-adapter resolution with org-level overrides.
OpenAI adapter
First production adapter with streaming SSE support.
Circuit breaker
Provider 5xx triggers breaker; clients receive 503 with Retry-After.
Context injection
Parallel memory and directive retrieval before upstream call (later milestones).
Provider API keys will be stored per org in Postgres and injected by the adapter — never accepted from client headers. Rotation guidance: Secrets and keys.
Security boundaries
| Control | Phase 1 | Phase 2+ |
|---|---|---|
| Client supplies provider API key | N/A (no forward) | Forbidden |
| Org from token | Enforced | Enforced |
| Audit log per upstream call | No | Yes (ClickHouse traces) |
| PII in upstream prompts | Validated locally | Redaction pipeline |
Verify adapter stub
make compose-dev-up
make db-migrate && make db-seed
make dev-smoke # asserts 501 on chat without LLM keyIntegration tests in services/proxy/ assert PROVIDER_NOT_CONFIGURED for valid authenticated chat bodies.
Related
- Request routing — normalization before adapter handoff
- Overview — middleware and failure modes
- Architecture services — proxy role in the system map
- Roadmap Phase 2 — adapter delivery timeline
Was this page helpful?
Last updated on