Provider Adapters

Normalizing disparate LLM APIs into a unified runtime streaming contract.

Every AI provider uses a distinct API schema, authorization header scheme, and streaming event layout. To handle this without filling our orchestration code with switch statements, Arbiter defines a strict interface contract.

The AgentAdapter Contract

All adapters under lib/agents/types.ts must satisfy the following TypeScript signature:

typescript
export interface AgentAdapter {
  provider: 'openai' | 'claude' | 'gemini' | 'deepseek' | 'mistral';
  streamWorker(input: WorkerInput): AsyncGenerator<AgentStreamEvent>;
  streamEvaluator(input: EvaluatorInput): AsyncGenerator<AgentStreamEvent>;
  runTitle(input: TitleInput): Promise<string>;
}

Adapter Example: OpenAI

The OpenAI adapter (located in lib/agents/openai.ts) intercepts SSE stream lines. Since OpenAI does not yield token counts on streaming chunks by default, we configure stream_options: { include_usage: true } and parse the final chunk. Here is a stripped-down look at how delta parsing is normalized:

typescript
// From openAiAdapter.streamWorker:
const response = await fetch('https://api.openai.com/v1/chat/completions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${input.apiKey}`
  },
  body: JSON.stringify({
    model: input.modelId,
    messages,
    stream: true,
    stream_options: { include_usage: true }
  })
});

const reader = response.body.getReader();
const lineIterator = makeLineIterator(reader);
for await (const line of lineIterator) {
  if (!line.startsWith('data: ')) continue;
  const dataStr = line.slice(6).trim();
  if (dataStr === '[DONE]') continue;
  
  const parsed = JSON.parse(dataStr);
  const delta = parsed.choices?.[0]?.delta?.content || '';
  if (delta) {
    yield { type: 'delta', text: delta };
  }
}

This design lets us swap or add providers (like Anthropic Claude or Google Gemini) easily by adding their respective files in lib/agents/ and registering them in lib/agents/registry.ts.