Custom Adapter
An adapter is an HTTP server that sits between the relay client and your AI system. It receives OpenAI-compatible requests, translates them into whatever your target understands, then returns the response in a standard format.
Write a custom adapter when your AI system doesn't expose an OpenAI-compatible endpoint, for example a proprietary HTTP API, a browser-based interface, or a system that requires session management.
Endpoints
Your adapter serves one or both of two HTTP endpoints, matching the protocol you picked for the Internal target:
| Endpoint | Protocol | Implement it when |
|---|---|---|
POST /v1/chat/completions | Chat Completions | Your target exchanges plain text and you don't need tool calls evaluated. |
POST /v1/responses | Responses API | You want Spectral to evaluate how your target invokes tools, not just what it says. This is the default for Internal targets. |
The relay client calls whichever path matches the target's protocol, so you only implement the endpoint you selected. Implementing both lets a single adapter serve targets of either protocol.
The POST /v1/responses endpoint requires spectral-bridge 0.2.0 or later.
The contract is language-agnostic: anything that responds correctly to the requests below qualifies. Your adapter must also handle concurrent requests, since spectral-bridge may dispatch several at once, one per active conversation turn in a running evaluation.
Chat Completions contract
POST /v1/chat/completions HTTP/1.1Content-Type: application/json{"messages": [{ "role": "user", "content": "What is the capital of France?" },{ "role": "assistant", "content": "The capital of France is Paris." },{ "role": "user", "content": "And of Germany?" }]}
messages is an ordered conversation history ending with the current user turn. Use the full history if your target is stateless, or only the last message if it maintains session state.
Responses API contract
POST /v1/responses HTTP/1.1Content-Type: application/json{"input": [{ "role": "user", "content": "What is the capital of France?" },{ "role": "assistant", "content": "The capital of France is Paris." },{ "role": "user", "content": "And of Germany?" }]}
input is the ordered conversation history ending with the current user turn, following the OpenAI Responses API. As with Chat Completions, use the full history if your target is stateless, or only the last turn if it keeps session state.
Tool calls
To have tool use evaluated, return the target's tool activity in output as function_call and function_call_output items alongside the final assistant message, following the Responses item types.
Example
The pass-through adapter source is a minimal working implementation of both contracts in a few lines of Python.