Model
Connect Harness to an OpenAI-compatible, OpenAI Responses, Anthropic Messages, or custom provider.
Harness is provider-neutral. A model adapter maps Harness's canonical prompt and tool contracts to
the provider API you choose, then maps its response back to a candidate. Your host code still owns
the endpoint, SDK or fetch, credentials, headers, model name, retries, and provider-specific
features.
Import the built-in translators from the public subpath:
import {
anthropicAdapter,
chatCompletionsAdapter,
responsesAdapter,
} from "@nylorun/harness/model/adapters";OpenAI-compatible endpoints
chatCompletionsAdapter is the simplest choice for any provider exposing the Chat Completions
shape. It works equally well with a provider's compatible endpoint, a gateway, or a local server.
import { chatCompletionsAdapter } from "@nylorun/harness/model/adapters";
const adapter = chatCompletionsAdapter(async (body, call, { signal }) => {
const response = await fetch(`${baseUrl}/chat/completions`, {
method: "POST",
headers: {
authorization: `Bearer ${apiKey}`,
"content-type": "application/json",
...providerHeaders,
},
body: JSON.stringify({
model: modelName,
...call.model?.config,
...body,
}),
signal,
});
if (!response.ok) throw new Error(await response.text());
return response.json();
});Keep baseUrl, apiKey, modelName, and providerHeaders in environment-backed host
configuration. The public example uses
this boundary and can target OpenAI-compatible providers, gateways, or local endpoints.
Direct OpenAI Responses
Use responsesAdapter when your application calls the OpenAI Responses API directly. The send
function receives the translated request, canonical call, and abort signal.
const adapter = responsesAdapter((body, call, { signal }) =>
openai.responses.create(
{ model: "your-model", ...call.model?.config, ...body },
{ signal },
),
);Direct Anthropic Messages
Use anthropicAdapter for the Messages API. It requires a positive defaultMaxOutputTokens; a
per-step maxOutputTokens model control overrides that fallback.
const adapter = anthropicAdapter({
defaultMaxOutputTokens: 1_024,
send: (body, call, { signal }) =>
anthropic.messages.create(
{ model: "your-model", ...call.model?.config, ...body },
{ signal },
),
});Any other provider
Implement ModelAdapter directly when the provider has a different request or response format.
The adapter receives a provider-neutral ModelCall (model, prompt, tools, and sessionId)
and must resolve to a ModelCandidate, string, or deferred outcome.
import type { ModelAdapter } from "@nylorun/harness";
const adapter: ModelAdapter = async (call, { signal }) => {
const response = await provider.complete({
prompt: call.prompt,
tools: call.tools,
signal,
});
return {
output: [{ type: "text", text: response.text }],
finishReason: "stop",
};
};Translator boundary
The built-in translators cover text output and JSON-object tool loops. Provider-native continuation state, streaming, images, cache controls, and other provider-specific features remain application integration concerns. Use your provider SDK or a custom adapter when those features are required.
See Model adapters reference for request and response shapes.