Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"words": [
"OpenProse",
"openprose",
"aisdk",
"Fumadocs",
"fumadocs",
"Forme",
Expand Down
31 changes: 21 additions & 10 deletions content/docs/cli/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,18 @@ Each built-in provider resolves to an endpoint + a key env var:
| --- | --- | --- | --- |
| `openrouter` (default) | `https://openrouter.ai/api/v1` | `OPENROUTER_API_KEY` | `google/gemini-3.5-flash` |
| `openai` | `https://api.openai.com/v1` | `OPENAI_API_KEY` | `gpt-4o-mini` |
| `anthropic` | `https://api.anthropic.com/v1/` | `ANTHROPIC_API_KEY` | `claude-haiku-4-5` |
| `anthropic` | Anthropic **Messages API** (native, see below) | `ANTHROPIC_API_KEY` | `claude-haiku-4-5` |
| `google` | `https://generativelanguage.googleapis.com/v1beta/openai/` | `GEMINI_API_KEY` | `gemini-2.5-flash` |

`openrouter`, `openai`, and `google` are OpenAI-compatible Chat Completions
surfaces. `anthropic` is special: the CLI routes it through Anthropic's **native
Messages API** (via the bundled `@openai/agents` AI-SDK adapter over
`@ai-sdk/anthropic`), because Anthropic's OpenAI-compat endpoint ignores
`response_format` and can't drive Reactor's structured compile/render. You don't
install or wire anything -- `provider: anthropic` just works, structured outputs and
all. An optional `base_url` points the adapter at a proxy in front of the Messages
API.

For any other OpenAI-compatible vendor (or a self-hosted gateway), name it freely
and supply both `base_url` and `api_key_env`:

Expand All @@ -106,15 +115,17 @@ up from the project directory. A missing key fails `compile`/`run`/`serve` with
silently misdirects you to OpenRouter, and never exits 0 on an auth dead-end.
</Callout>

<Callout type="warn">
**Claude models: prefer `openrouter` over `anthropic` for now.** Compile + render
use a JSON-schema response format, and Anthropic's direct OpenAI-compatible
endpoint rejects it unless `strict: true` (`400 response_format.json_schema.strict`).
OpenRouter, OpenAI, and Google accept it. To run Claude today, use
`provider: openrouter` with a `render_model`/`compile_model` like
`anthropic/claude-haiku-4-5`. `provider: anthropic` (direct) is wired and
authenticates, but the structured compile/run path is blocked by that endpoint
requirement.
<Callout type="info">
**Claude, two ways.** `provider: anthropic` drives Claude directly through the
native Messages API and fully supports the structured compile/render path -- it is
the recommended route for Anthropic models. If you already aggregate models through
one gateway, `provider: openrouter` with a `render_model`/`compile_model` like
`anthropic/claude-haiku-4-5` also works. Avoid pointing a `base_url` at Anthropic's
OpenAI-compatible endpoint (`https://api.anthropic.com/v1/`): it ignores
`response_format` and rejects Reactor's JSON-schema with
`400 response_format.json_schema.strict`. See the
[SDK provider guide](/sdk/agents#claude-via-the-native-anthropic-messages-api)
for the underlying `@openai/agents` wiring.
</Callout>

## sandbox
Expand Down
73 changes: 61 additions & 12 deletions content/docs/sdk/agents.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ Any vendor with an OpenAI-compatible Chat Completions endpoint drops straight in
| --- | --- | --- | --- |
| OpenRouter (default) | `https://openrouter.ai/api/v1` | `OPENROUTER_API_KEY` | `google/gemini-3.5-flash` |
| OpenAI | `https://api.openai.com/v1` | `OPENAI_API_KEY` | `gpt-4o-mini` |
| Anthropic | `https://api.anthropic.com/v1/` | `ANTHROPIC_API_KEY` | `claude-haiku-4-5` |
| Anthropic (plain text only -- see below) | `https://api.anthropic.com/v1/` | `ANTHROPIC_API_KEY` | `claude-haiku-4-5` |
| Google Gemini | `https://generativelanguage.googleapis.com/v1beta/openai/` | `GEMINI_API_KEY` | `gemini-2.5-flash` |

<Callout type="info">
Expand All @@ -275,19 +275,68 @@ Anthropic) in
`packages/reactor/src/adapters/agent-render/__tests__/provider-byo.live.test.ts`.

<Callout type="warn">
**Anthropic's direct endpoint and structured outputs.** Compile sessions and the
render's done/failed signal use a JSON-schema `response_format`. Anthropic's
OpenAI-compatible endpoint requires `strict: true` on that schema and rejects the
default with `400 response_format.json_schema.strict`. OpenAI and Google (and
every model **via OpenRouter**, including Anthropic's) accept it as-is. So for
**Claude models today, route through OpenRouter** (`provider` pointed at
`https://openrouter.ai/api/v1`, model `anthropic/claude-...`); direct
`api.anthropic.com` works for plain-text use but not yet for the structured
compile/render path.
**Don't use Anthropic's OpenAI-compatible endpoint for Claude -- use the native
adapter below.** Compile sessions and the render's done/failed signal use a
JSON-schema `response_format`, and renders drive tools. Anthropic documents its
[OpenAI SDK compatibility layer](https://platform.claude.com/docs/en/api/openai-sdk)
as "primarily for testing" -- it **ignores `response_format`** and rejects our
structured schema with `400 response_format.json_schema.strict`. So
`baseURL: "https://api.anthropic.com/v1/"` (the row above) is fine for plain-text
probes but **not** the structured compile/render path. For Claude with structured
outputs you have two supported routes: the **native Messages API** via the AI-SDK
adapter (next), or **OpenRouter** (`provider` pointed at
`https://openrouter.ai/api/v1`, model `anthropic/claude-...`), where Anthropic's
own models accept the schema as-is.
</Callout>

For an API that is **not** OpenAI-compatible at all (a native Anthropic Messages
client, a local model, a bespoke gateway), drop one level to the
### Claude via the native Anthropic Messages API

The supported way to drive Claude directly is the official `@openai/agents` route
for non-OpenAI models: the [AI-SDK adapter](https://openai.github.io/openai-agents-js/extensions/ai-sdk/)
over [`@ai-sdk/anthropic`](https://platform.claude.com/docs/en/build-with-claude/structured-outputs),
which hits Anthropic's native Messages API where structured outputs and tools
work. This is **still plain `@openai/agents` configuration** -- `provider` accepts
any `ModelProvider`, so the harness is untouched; only the model session changes.
No SDK changes are needed.

```sh
npm add @openai/agents-extensions ai @ai-sdk/anthropic
```

```ts
import { reactor } from "@openprose/reactor";
import type { ModelProvider } from "@openai/agents";
import { aisdk } from "@openai/agents-extensions/ai-sdk";
import { createAnthropic } from "@ai-sdk/anthropic";

// A scoped ModelProvider backed by Anthropic's NATIVE Messages API.
function anthropicNativeProvider(apiKey: string): ModelProvider {
const anthropic = createAnthropic({ apiKey });
const cache = new Map<string, ReturnType<typeof aisdk>>();
return {
getModel(modelName = "claude-haiku-4-5") {
let model = cache.get(modelName);
if (!model) cache.set(modelName, (model = aisdk(anthropic(modelName))));
return model;
},
};
}

const provider = anthropicNativeProvider(process.env.ANTHROPIC_API_KEY!);
const { reactor: r } = await reactor("./my-project", {
directory: "./state",
render: { provider, model: "claude-haiku-4-5" }, // run-phase renders
compile: { options: { provider, model: "claude-haiku-4-5" } }, // compile sessions
});
await r.ingest("source", { wake: { source: "external", refs: [] } });
```

The [Reactor CLI](/cli/configuration#choosing-a-model-provider) builds exactly
this provider for you when you set `provider: anthropic` in `reactor.yml` -- you
don't write the adapter wiring yourself there.

For an API that is **not** OpenAI-compatible at all (a local model, a bespoke
gateway) and has no AI-SDK provider, drop one level to the
[`RenderBackend` injection seam](#the-renderbackend-injection-seam) below: you
own the whole session and the harness keeps its instruction composition, tools,
harvest, and cost capture.
Expand Down
Loading