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
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,16 @@ Install the SDK next to the Restate SDKs it builds on, or clone the
with one agent and one handler:

```sh
npm install @restatedev/agentlib @restatedev/restate-sdk @restatedev/restate-sdk-gen
npm install @restatedev/agentlib @restatedev/restate-sdk @restatedev/restate-sdk-gen @ai-sdk/openai
```

```ts
import * as restate from "@restatedev/restate-sdk-gen";
import {agent} from "@restatedev/agentlib";
import {openAIModel} from "@restatedev/agentlib/openai";
import {openai} from "@ai-sdk/openai";
import {aiModel} from "@restatedev/agentlib/provider";

const assistant = agent({model: openAIModel({model: "gpt-5.6-luna"})});
const assistant = agent({model: aiModel({model: openai("gpt-5.6-luna")})});

export const hello = restate.service({
name: "Hello",
Expand Down Expand Up @@ -76,9 +77,8 @@ with live controls. Start a Restate server, for example with
```sh
pnpm install --frozen-lockfile
export OPENAI_API_KEY=...
# Optional: export AGENT_MODEL=... An OpenAI model (with OPENAI_BASE_URL, one on
# any Chat Completions server), or anthropic:<model> or bedrock:<model> with that
# provider's credentials. Lessons that need other keys say so.
# Each lesson defines its model locally; edit that definition to change providers.
# Lessons that need other keys say so.
pnpm dev
```

Expand Down Expand Up @@ -163,7 +163,7 @@ repeat a completed model call.
| Long runs | `agent({compaction, contextWindow})`, `ContextOverflowError` | — |
| Limits | `maxSteps`, `maxToolCalls`, `maxToolDepth`, `maxOutputChars`, `finalize` | — |
| Custom model adapters | `journaledModel` | — |
| Models | `openAIModel` (`/openai`), `aiModel`, `modelGateway` (`/provider`) | [shared.ts](packages/examples/src/shared.ts) |
| Models | `aiModel`, `modelGateway` (`/provider`) | [01_hello.ts](packages/examples/src/lessons/01_hello.ts) |

Subpaths are imports from `@restatedev/agentlib`, such as
`@restatedev/agentlib/mcp`. `aiModel` accepts an OpenTelemetry `tracer`, and
Expand Down
22 changes: 11 additions & 11 deletions packages/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ code. Model calls, tool results, and approvals are journaled, so a crash,
deploy, or long wait does not mean starting the work over.

```sh
npm install @restatedev/agentlib @restatedev/restate-sdk @restatedev/restate-sdk-gen
npm install @restatedev/agentlib @restatedev/restate-sdk @restatedev/restate-sdk-gen @ai-sdk/openai
```

Or start from the [project template](https://github.com/restatedev/agentlib-template),
Expand All @@ -15,9 +15,10 @@ a ready-to-run Restate service with one agent.
```ts
import * as restate from "@restatedev/restate-sdk-gen";
import {agent} from "@restatedev/agentlib";
import {openAIModel} from "@restatedev/agentlib/openai";
import {openai} from "@ai-sdk/openai";
import {aiModel} from "@restatedev/agentlib/provider";

const assistant = agent({model: openAIModel({model: "gpt-5.6-luna"})});
const assistant = agent({model: aiModel({model: openai("gpt-5.6-luna")})});

export const hello = restate.service({
name: "Hello",
Expand All @@ -37,14 +38,13 @@ go from a first run to multi-agent workflows.

## Subpaths

| Import | Purpose |
| -------------------------------- | --------------------------------------------- |
| `@restatedev/agentlib` | Agents, tools, conversations, guardrails |
| `@restatedev/agentlib/openai` | OpenAI and Chat Completions-compatible models |
| `@restatedev/agentlib/provider` | Models through the Vercel AI SDK providers |
| `@restatedev/agentlib/mcp` | Tools from MCP servers |
| `@restatedev/agentlib/program` | Programmatic tool calling in a sandbox |
| `@restatedev/agentlib/discovery` | Expose agents and tools for discovery |
| Import | Purpose |
| -------------------------------- | ------------------------------------------ |
| `@restatedev/agentlib` | Agents, tools, conversations, guardrails |
| `@restatedev/agentlib/provider` | Models through the Vercel AI SDK providers |
| `@restatedev/agentlib/mcp` | Tools from MCP servers |
| `@restatedev/agentlib/program` | Programmatic tool calling in a sandbox |
| `@restatedev/agentlib/discovery` | Expose agents and tools for discovery |

## License

Expand Down
5 changes: 0 additions & 5 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./openai": {
"types": "./dist/openai.d.ts",
"import": "./dist/openai.js"
},
"./program": {
"types": "./dist/program.d.ts",
"import": "./dist/program.js"
Expand Down Expand Up @@ -64,7 +60,6 @@
"@opentelemetry/api": "1.9.0",
"ai": "7.0.26",
"minisearch": "7.2.0",
"openai": "^7.17.0",
"quickjs-emscripten-core": "0.32.0",
"zod": "^4.6.5"
},
Expand Down
165 changes: 0 additions & 165 deletions packages/core/src/openai.ts

This file was deleted.

19 changes: 16 additions & 3 deletions packages/core/src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,19 @@ function finished(reason: string | undefined) {
if (reason !== "stop" && reason !== "tool-calls")
throw new ModelProtocolError(`Incomplete model response: ${reason ?? "unknown"}`);
}

function contextOverflow(error: APICallError): boolean {
if (error.statusCode === 413) return true;
const data: unknown = error.data;
if (!data || typeof data !== "object" || !("error" in data)) return false;
const detail = data.error;
return (
!!detail &&
typeof detail === "object" &&
"code" in detail &&
detail.code === "context_length_exceeded"
);
}
// Namespaced: each provider reads its own key, so no provider detection is needed.
const CACHE_POINT = {
anthropic: {cacheControl: {type: "ephemeral"}},
Expand Down Expand Up @@ -253,9 +266,9 @@ export function aiModel(options: ProviderOptions): Model {
// throttling and transient provider failures stay retryable.
if (LoadAPIKeyError.isInstance(error) || UnsupportedFunctionalityError.isInstance(error))
throw new TerminalError(error.message);
// Providers word an overflow differently and give it no code of its own,
// so only a 413 counts here; contextWindow avoids the rest up front.
if (APICallError.isInstance(error) && error.statusCode === 413)
// Use the SDK's parsed error code or HTTP 413, never provider-specific
// message text. contextWindow avoids uncoded overflows up front.
if (APICallError.isInstance(error) && contextOverflow(error))
throw new ContextOverflowError(error.message);
if (APICallError.isInstance(error) && !error.isRetryable)
throw new TerminalError(error.message, {errorCode: error.statusCode});
Expand Down
51 changes: 33 additions & 18 deletions packages/examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,7 @@ from the workspace root:
```sh
pnpm install --frozen-lockfile
export OPENAI_API_KEY=...
# Optional: export AGENT_MODEL=... For example anthropic:claude-sonnet-5-5,
# with ANTHROPIC_API_KEY; see "Model configuration" below.
# To change providers, edit the model in the lesson; see "Model configuration" below.
pnpm dev
```

Expand Down Expand Up @@ -516,28 +515,44 @@ agent with a sandbox, as in lesson 11, can copy a skill in and run them.

## Model configuration

[shared.ts](src/shared.ts) configures the example model from `AGENT_MODEL`:
`anthropic:<model>` (reads `ANTHROPIC_API_KEY`), `bedrock:<model>` (reads the
AWS credentials), or an OpenAI model, optionally as `openai:<model>`. The
OpenAI adapter speaks Chat Completions, so with `OPENAI_BASE_URL` it reaches
compatible servers such as DeepSeek, xAI, OpenRouter, Ollama or vLLM.
`openAIModel` defaults
GPT-5.6 function-tool requests to `reasoning_effort: "none"` to avoid the default
Luna model's Chat Completions tool/reasoning compatibility error. Explicit model
settings are preserved. Change an agent's `model` field to use another adapter
or gateway.
Each lesson defines its own model with `aiModel`, so the provider and retry
policy are visible alongside the agent. The examples use OpenAI Responses
and read `OPENAI_API_KEY` when the first call is made; importing a lesson
requires no credentials.

To choose another model or provider, edit that lesson's definition. For
example, use Anthropic with `ANTHROPIC_API_KEY`:

```ts
import {anthropic} from "@ai-sdk/anthropic";
import {aiModel} from "@restatedev/agentlib/provider";

const model = aiModel({
model: anthropic("claude-sonnet-5-5"),
retry: {maxAttempts: 3},
});
```

For Bedrock, use `createAmazonBedrock({region: "us-east-1"})("<model id>")`
from `@ai-sdk/amazon-bedrock` with AWS credentials. For a Chat Completions-compatible
server, use `createOpenAI({baseURL: "https://your-server/v1", apiKey: "..."}).chat("<model id>")`
from `@ai-sdk/openai`. Pass provider-specific settings through
`aiModel({providerOptions, ...})`.

There is no shared `AGENT_MODEL` selector; each example's model configuration
is independent.

## Retry policies

Configure retries at the operation that can fail; there is no agent-wide retry
default. `maxSteps` limits the model/tool loop, not retries.

| Operation | Where to configure it | Example |
| ------------------- | ------------------------------------------------------ | -------------------------------------- |
| Ordinary async tool | `asyncTool({retry, ...})` | [02_tools.ts](src/lessons/02_tools.ts) |
| Model call | `openAIModel({retry, ...})` or `aiModel({retry, ...})` | [shared.ts](src/shared.ts) |
| Individual action | `restate.run(action, {name, retry})` | Below |
| Handler-backed tool | `retryPolicy` on the called service or handler | — |
| Operation | Where to configure it | Example |
| ------------------- | ---------------------------------------------- | -------------------------------------- |
| Ordinary async tool | `asyncTool({retry, ...})` | [02_tools.ts](src/lessons/02_tools.ts) |
| Model call | `aiModel({retry, ...})` | [01_hello.ts](src/lessons/01_hello.ts) |
| Individual action | `restate.run(action, {name, retry})` | Below |
| Handler-backed tool | `retryPolicy` on the called service or handler | — |

Lesson 02 uses this policy; the same shape works for model adapters and
individual actions:
Expand Down
1 change: 1 addition & 0 deletions packages/examples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
"@ag-ui/core": "1.0.0",
"@ai-sdk/amazon-bedrock": "5.0.20",
"@ai-sdk/anthropic": "4.0.14",
"@ai-sdk/openai": "4.0.13",
"@restatedev/agentlib": "workspace:*",
"@restatedev/restate-sdk": "^1.18.2",
"@restatedev/restate-sdk-clients": "^1.18.2",
Expand Down
Loading
Loading