@odla-ai/ai 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +290 -0
- package/dist/anthropic-JXDXR7O7.js +219 -0
- package/dist/anthropic-JXDXR7O7.js.map +1 -0
- package/dist/chunk-LANGYP65.js +173 -0
- package/dist/chunk-LANGYP65.js.map +1 -0
- package/dist/google-3TFOFIQO.js +206 -0
- package/dist/google-3TFOFIQO.js.map +1 -0
- package/dist/index.cjs +2002 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +991 -0
- package/dist/index.d.ts +991 -0
- package/dist/index.js +1122 -0
- package/dist/index.js.map +1 -0
- package/dist/openai-Y3OAONAU.js +257 -0
- package/dist/openai-Y3OAONAU.js.map +1 -0
- package/llms.txt +111 -0
- package/package.json +76 -0
package/README.md
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# @odla-ai/ai
|
|
2
|
+
|
|
3
|
+
One general-purpose interface for AI inference across **Anthropic (Claude)**,
|
|
4
|
+
**OpenAI (GPT)**, and **Google (Gemini)** — text, image, and audio — plus a
|
|
5
|
+
tool-use **agent engine** and an **eval harness**. It's the place every odla app
|
|
6
|
+
reaches for when it needs AI.
|
|
7
|
+
|
|
8
|
+
- **Library-only.** Import it in-process and bring your own keys. No hosted
|
|
9
|
+
gateway, no network hop.
|
|
10
|
+
- **One canonical shape.** Anthropic-flavored content blocks, forced tool calls
|
|
11
|
+
with object inputs, a normalized streaming event vocabulary, cache-aware usage,
|
|
12
|
+
and a closed error taxonomy — the same regardless of which provider answers.
|
|
13
|
+
- **Capability-checked.** Every request is validated against the model before
|
|
14
|
+
dispatch, so "audio to Claude" fails fast with a clear error instead of an
|
|
15
|
+
opaque provider 400.
|
|
16
|
+
- **Isomorphic.** Node 20+, Cloudflare Workers, the browser. Provider SDKs are
|
|
17
|
+
lazy-loaded — a Claude-only consumer never pulls in `openai` or `@google/genai`.
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @odla-ai/ai
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { init } from "@odla-ai/ai";
|
|
27
|
+
|
|
28
|
+
const ai = init({
|
|
29
|
+
keys: {
|
|
30
|
+
anthropic: process.env.ANTHROPIC_API_KEY,
|
|
31
|
+
openai: process.env.OPENAI_API_KEY,
|
|
32
|
+
google: process.env.GOOGLE_API_KEY,
|
|
33
|
+
},
|
|
34
|
+
defaultModel: "claude-opus-4-8",
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// One call, any provider — pick the model, the provider is resolved for you.
|
|
38
|
+
const res = await ai.chat({
|
|
39
|
+
model: "gpt-5",
|
|
40
|
+
messages: [{ role: "user", content: "Say hello in one word." }],
|
|
41
|
+
maxTokens: 64,
|
|
42
|
+
});
|
|
43
|
+
console.log(res.content); // [{ type: "text", text: "Hello" }]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Streaming
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
for await (const ev of ai.stream({ model: "gemini-2.5-flash", messages: [...], maxTokens: 512 })) {
|
|
50
|
+
if (ev.type === "content_block_delta" && ev.delta.type === "text_delta") {
|
|
51
|
+
process.stdout.write(ev.delta.text);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The event union (`message_start → content_block_start → content_block_delta →
|
|
57
|
+
content_block_stop → message_delta → message_stop`, plus `error`) is identical
|
|
58
|
+
across providers.
|
|
59
|
+
|
|
60
|
+
### Structured extraction (forced tool call)
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
const { value } = await ai.extract<{ sentiment: "positive" | "negative" | "neutral" }>({
|
|
64
|
+
model: "claude-haiku-4-5",
|
|
65
|
+
user: "Classify: 'I love this!'",
|
|
66
|
+
tool: {
|
|
67
|
+
name: "classify",
|
|
68
|
+
parameters: {
|
|
69
|
+
type: "object",
|
|
70
|
+
properties: { sentiment: { type: "string", enum: ["positive", "negative", "neutral"] } },
|
|
71
|
+
required: ["sentiment"],
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
// value.sentiment === "positive" (already a parsed object — never a JSON string)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Web search
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
const { text } = await ai.search({ model: "claude-sonnet-5", user: "Who won the 2026 World Cup?" });
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Multimodal: text, image, audio
|
|
85
|
+
|
|
86
|
+
Content blocks carry the modality. Image is broadly supported; **audio is
|
|
87
|
+
accepted by OpenAI and Google, but not Anthropic** — sending an `audio` block to
|
|
88
|
+
a Claude model throws a `CapabilityError` before any network call.
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
// Image (works on Claude, GPT, Gemini)
|
|
92
|
+
await ai.chat({
|
|
93
|
+
model: "gpt-4o",
|
|
94
|
+
messages: [{ role: "user", content: [
|
|
95
|
+
{ type: "image", source: { type: "base64", mediaType: "image/png", data: pngB64 } },
|
|
96
|
+
{ type: "text", text: "What's in this image?" },
|
|
97
|
+
] }],
|
|
98
|
+
maxTokens: 128,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Audio (OpenAI: gpt-audio; Google: gemini-2.5-*)
|
|
102
|
+
await ai.chat({
|
|
103
|
+
model: "gpt-audio",
|
|
104
|
+
messages: [{ role: "user", content: [
|
|
105
|
+
{ type: "audio", source: { type: "base64", mediaType: "audio/wav", data: wavB64 } },
|
|
106
|
+
{ type: "text", text: "Transcribe this." },
|
|
107
|
+
] }],
|
|
108
|
+
maxTokens: 256,
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Agents: build, run, and test
|
|
113
|
+
|
|
114
|
+
A **persona** is a role as config-as-data: a model, a system prompt, its tools,
|
|
115
|
+
and reasoning knobs. `runAgent` drives the tool-use loop (model turn → run tool
|
|
116
|
+
handlers → feed results back → repeat) until the model stops or a step limit hits.
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { init, runAgent, type Persona, type ToolDef } from "@odla-ai/ai";
|
|
120
|
+
|
|
121
|
+
const ai = init({ keys: { anthropic: process.env.ANTHROPIC_API_KEY } });
|
|
122
|
+
|
|
123
|
+
const add: ToolDef = {
|
|
124
|
+
name: "add",
|
|
125
|
+
description: "Add two integers.",
|
|
126
|
+
inputSchema: { type: "object", properties: { a: { type: "number" }, b: { type: "number" } }, required: ["a", "b"] },
|
|
127
|
+
handler: (input) => ({ content: String((input.a as number) + (input.b as number)) }),
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const calculator: Persona = {
|
|
131
|
+
name: "calculator",
|
|
132
|
+
model: "claude-opus-4-8",
|
|
133
|
+
system: "Use the add tool for arithmetic, then state the result.",
|
|
134
|
+
tools: [add],
|
|
135
|
+
maxSteps: 4,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const run = await runAgent(ai, calculator, { input: "What is 21 + 21?" });
|
|
139
|
+
console.log(run.finalText); // "…42"
|
|
140
|
+
console.log(run.toolCalls); // [{ toolUse, output }]
|
|
141
|
+
console.log(run.usage); // accumulated across turns
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Tools can declare taint constraints (`outputTaint` / `acceptsTaint`) for a
|
|
145
|
+
lightweight CaMeL-style prompt-injection gate, and personas can carry a pluggable
|
|
146
|
+
`MemoryScope`.
|
|
147
|
+
|
|
148
|
+
### Skills
|
|
149
|
+
|
|
150
|
+
A **skill** is a named bundle of tools + instructions. Attach skills to a persona
|
|
151
|
+
and their tools are merged into the model's tool list and their instructions into
|
|
152
|
+
the system prompt — the primitive for "add a skill and call it as tools."
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
import { entityCrudSkill, runAgent } from "@odla-ai/ai";
|
|
156
|
+
import { init as odlaInit } from "@odla-ai/db";
|
|
157
|
+
|
|
158
|
+
const db = odlaInit({ appId, adminToken: appKey, endpoint: ODLA_URL });
|
|
159
|
+
|
|
160
|
+
// Turn an odla-db entity into list/create/update/delete tools that change data.
|
|
161
|
+
const todos = entityCrudSkill({
|
|
162
|
+
db,
|
|
163
|
+
entity: "todos",
|
|
164
|
+
fields: { text: { type: "string" }, done: { type: "boolean" } },
|
|
165
|
+
required: ["text"],
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const assistant = { name: "todo-bot", model: "claude-opus-4-8", skills: [todos] };
|
|
169
|
+
const run = await runAgent(ai, assistant, { input: "Add 'buy milk' and show my open todos." });
|
|
170
|
+
// the agent calls create_todo then list_todos, writing/reading via odla-db
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
`entityCrudSkill` generates `list_<plural>`, `create_<singular>`, `update_<singular>`
|
|
174
|
+
(partial — pass only the fields that change, e.g. toggle `done`), and
|
|
175
|
+
`delete_<singular>` tools whose handlers call `db.transact`/`db.query`. Or hand-roll
|
|
176
|
+
a `Skill` = `{ name, instructions?, tools: ToolDef[] }` for anything else.
|
|
177
|
+
|
|
178
|
+
### Evals
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
import { evaluate, exactMatch, llmJudge } from "@odla-ai/ai";
|
|
182
|
+
|
|
183
|
+
const report = await evaluate({
|
|
184
|
+
inference: ai,
|
|
185
|
+
model: "claude-opus-4-8",
|
|
186
|
+
system: "Answer with only the number.",
|
|
187
|
+
grader: exactMatch(),
|
|
188
|
+
cases: [
|
|
189
|
+
{ input: "What is 2+2?", expected: "4" },
|
|
190
|
+
{ input: "What is 10-3?", expected: "7" },
|
|
191
|
+
],
|
|
192
|
+
});
|
|
193
|
+
console.log(`${report.passed}/${report.total} passed`);
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Graders: `exactMatch`, `includes`, `structuredMatch`, and `llmJudge` (which
|
|
197
|
+
dogfoods the inference core to grade open-ended output against a rubric). Pass a
|
|
198
|
+
`persona` instead of a `model` to evaluate a full agent.
|
|
199
|
+
|
|
200
|
+
## Keys & roles
|
|
201
|
+
|
|
202
|
+
Keys are **BYO** — a static per-provider map and/or a dynamic `resolveKey`
|
|
203
|
+
(multi-tenant / per-call). A provider with no resolvable key is simply
|
|
204
|
+
unavailable: requesting one of its models throws a `ConfigError`.
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
const ai = init({
|
|
208
|
+
resolveKey: (provider) => lookupTenantKey(currentTenant, provider),
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
"Roles" are personas (above) — config-as-data, not RBAC.
|
|
213
|
+
|
|
214
|
+
## Storage, memory & BYO keys via odla-db
|
|
215
|
+
|
|
216
|
+
odla-ai follows odla-db's model: it's a self-contained npm package, ships an
|
|
217
|
+
`llms.txt` for machine context, and integrates with `@odla-ai/db` for durable
|
|
218
|
+
storage — **without taking a runtime dependency on it**. You install `@odla-ai/db`,
|
|
219
|
+
build a client, and inject it; odla-ai talks to it through a structural interface.
|
|
220
|
+
|
|
221
|
+
The agent handshake (an agent never takes a platform secret):
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
import { requestToken, init as odlaInit } from "@odla-ai/db";
|
|
225
|
+
import { provisionAgentApp, odlaDbKeyResolver, OdlaDbMemory, persistRun, runAgent, init } from "@odla-ai/ai";
|
|
226
|
+
|
|
227
|
+
// 1. Device-authorization handshake — the human supplies the endpoint and
|
|
228
|
+
// approves a short code in odla-db Studio; you get a scoped, revocable token.
|
|
229
|
+
const { token } = await requestToken({ endpoint: ODLA_URL, onCode: ({ userCode }) => console.log(userCode) });
|
|
230
|
+
|
|
231
|
+
// 2. Provision: create app, mint an app key, push the agent schema, and store the
|
|
232
|
+
// user's BYO provider keys as odla-db secrets (encrypted at rest).
|
|
233
|
+
const { appId, appKey } = await provisionAgentApp({
|
|
234
|
+
endpoint: ODLA_URL,
|
|
235
|
+
token, // operator/dev token — needed to WRITE secrets
|
|
236
|
+
providerKeys: { openai: OPENAI_KEY, anthropic: ANTHROPIC_KEY },
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// 3. Runtime: read keys from secrets with the app key, persist memory + run traces.
|
|
240
|
+
const db = odlaInit({ appId, adminToken: appKey, endpoint: ODLA_URL });
|
|
241
|
+
const ai = init({ resolveKey: odlaDbKeyResolver(db) }); // keys come from odla-db secrets
|
|
242
|
+
|
|
243
|
+
const persona = { name: "assistant", model: "gpt-5", memory: new OdlaDbMemory({ db, sessionId: "user-42" }) };
|
|
244
|
+
const run = await runAgent(ai, persona, { input: "Pick up where we left off." });
|
|
245
|
+
await persistRun(run, { db, sessionId: "user-42" });
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**BYO keys as secrets** — the write/read split mirrors odla-db: storing a secret
|
|
249
|
+
is a *privileged* action (operator or `odla_dev_` token, via `provisionAgentApp` /
|
|
250
|
+
`putSecret`); reading it at request time uses the ordinary **read-only** app key
|
|
251
|
+
(`odlaDbKeyResolver` → `db.secrets.get`). Keys are AES-GCM-encrypted at rest and
|
|
252
|
+
never returned to browser clients. `OdlaDbMemory` persists conversation turns and
|
|
253
|
+
`persistRun` writes run traces, both as natural-key upserts (idempotent on retry).
|
|
254
|
+
|
|
255
|
+
## Models & capabilities
|
|
256
|
+
|
|
257
|
+
The catalog maps a canonical model id → provider, native id, and capabilities.
|
|
258
|
+
The built-in `DEFAULT_CATALOG` covers current Anthropic, OpenAI, and Google
|
|
259
|
+
models; extend or override it via `buildCatalog` / `init({ catalog })`.
|
|
260
|
+
|
|
261
|
+
| | image in | audio in | tools | thinking | effort | web search | structured |
|
|
262
|
+
|---|---|---|---|---|---|---|---|
|
|
263
|
+
| Claude (opus/sonnet/fable) | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
264
|
+
| Claude Haiku | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ |
|
|
265
|
+
| GPT-5 line | ✅ | ❌ | ✅ | — | ✅ | — | ✅ |
|
|
266
|
+
| GPT-4o | ✅ | ❌ | ✅ | — | ❌ | — | ✅ |
|
|
267
|
+
| gpt-audio | ❌ | ✅ | ✅ | — | ❌ | — | ✅ |
|
|
268
|
+
| Gemini 2.5 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
269
|
+
|
|
270
|
+
> **Model ids drift.** The catalog is data — verify ids against each provider's
|
|
271
|
+
> current docs (the `claude-api` skill for Anthropic; `/v1/models` for OpenAI;
|
|
272
|
+
> Google GenAI docs for Gemini) rather than trusting them from memory.
|
|
273
|
+
|
|
274
|
+
## Errors
|
|
275
|
+
|
|
276
|
+
Every error is an `OdlaAIError` subclass carrying a stable `code` — branch on
|
|
277
|
+
`err.code`, not message text: `ConfigError`, `AuthError`, `RateLimitError`,
|
|
278
|
+
`CapabilityError`, `ContextWindowError`, `InvalidRequestError`, `ProviderError`.
|
|
279
|
+
|
|
280
|
+
## Develop
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
npm run typecheck # tsc --noEmit (strict)
|
|
284
|
+
npm test # vitest — offline, mock-provider based
|
|
285
|
+
npm run build # tsup → dual ESM + CJS + d.ts
|
|
286
|
+
npm run smoke # live check against real providers (needs .dev.vars)
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
For the live smoke, copy `.dev.vars.example` → `.dev.vars` and fill in whatever
|
|
290
|
+
keys you have; providers without a key are skipped.
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import {
|
|
2
|
+
addUsage,
|
|
3
|
+
emptyUsage,
|
|
4
|
+
mapProviderError,
|
|
5
|
+
normalizeError
|
|
6
|
+
} from "./chunk-LANGYP65.js";
|
|
7
|
+
|
|
8
|
+
// src/providers/anthropic.ts
|
|
9
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
10
|
+
var AnthropicProvider = class {
|
|
11
|
+
id = "anthropic";
|
|
12
|
+
client;
|
|
13
|
+
/** `client` may be injected (tests, custom transport); otherwise built from key. */
|
|
14
|
+
constructor(apiKey, client) {
|
|
15
|
+
this.client = client ?? new Anthropic({ apiKey });
|
|
16
|
+
}
|
|
17
|
+
async create(spec, req) {
|
|
18
|
+
try {
|
|
19
|
+
let messages = toAnthropicMessages(req);
|
|
20
|
+
const content = [];
|
|
21
|
+
const usage = emptyUsage();
|
|
22
|
+
let stopReason = "end_turn";
|
|
23
|
+
let id = "";
|
|
24
|
+
for (let i = 0; i < 8; i++) {
|
|
25
|
+
const res = await this.client.messages.create(
|
|
26
|
+
buildParams(spec, req, messages)
|
|
27
|
+
);
|
|
28
|
+
id = res.id;
|
|
29
|
+
addUsage(usage, mapUsage(res.usage));
|
|
30
|
+
content.push(...mapContent(res.content));
|
|
31
|
+
stopReason = mapStop(res.stop_reason);
|
|
32
|
+
if (res.stop_reason === "pause_turn") {
|
|
33
|
+
messages = [...messages, { role: "assistant", content: res.content }];
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
return { id, model: req.model, provider: "anthropic", role: "assistant", content, stopReason, usage };
|
|
39
|
+
} catch (err) {
|
|
40
|
+
mapProviderError(err, "anthropic");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async *stream(spec, req) {
|
|
44
|
+
try {
|
|
45
|
+
const events = this.client.messages.stream(
|
|
46
|
+
buildParams(spec, req, toAnthropicMessages(req))
|
|
47
|
+
);
|
|
48
|
+
for await (const raw of events) {
|
|
49
|
+
const ev = mapStreamEvent(raw, req);
|
|
50
|
+
if (ev) yield ev;
|
|
51
|
+
}
|
|
52
|
+
} catch (err) {
|
|
53
|
+
yield { type: "error", error: normalizeError(err, "anthropic").toShape() };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
function buildParams(spec, req, messages) {
|
|
58
|
+
const params = {
|
|
59
|
+
model: spec.nativeId,
|
|
60
|
+
max_tokens: req.maxTokens,
|
|
61
|
+
messages
|
|
62
|
+
};
|
|
63
|
+
const system = toAnthropicSystem(req.system);
|
|
64
|
+
if (system !== void 0) params.system = system;
|
|
65
|
+
const tools = buildTools(req);
|
|
66
|
+
if (tools) params.tools = tools;
|
|
67
|
+
const toolChoice = mapToolChoice(req.toolChoice);
|
|
68
|
+
if (toolChoice) params.tool_choice = toolChoice;
|
|
69
|
+
if (req.stopSequences && req.stopSequences.length > 0) params.stop_sequences = req.stopSequences;
|
|
70
|
+
if (req.temperature !== void 0 && !spec.capabilities.effort) params.temperature = req.temperature;
|
|
71
|
+
if (req.thinking === "adaptive" && spec.capabilities.thinking) params.thinking = { type: "adaptive" };
|
|
72
|
+
const outputConfig = {};
|
|
73
|
+
if (req.effort && spec.capabilities.effort) outputConfig.effort = req.effort;
|
|
74
|
+
if (req.responseFormat && spec.capabilities.structuredOutput) {
|
|
75
|
+
outputConfig.format = { type: "json_schema", schema: req.responseFormat.schema };
|
|
76
|
+
}
|
|
77
|
+
if (Object.keys(outputConfig).length > 0) params.output_config = outputConfig;
|
|
78
|
+
if (req.providerExtras) Object.assign(params, req.providerExtras);
|
|
79
|
+
return params;
|
|
80
|
+
}
|
|
81
|
+
function toAnthropicSystem(system) {
|
|
82
|
+
if (system === void 0) return void 0;
|
|
83
|
+
if (typeof system === "string") return system;
|
|
84
|
+
return system.map((b) => ({
|
|
85
|
+
type: "text",
|
|
86
|
+
text: b.text,
|
|
87
|
+
...b.cacheControl ? { cache_control: { type: "ephemeral" } } : {}
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
function toAnthropicMessages(req) {
|
|
91
|
+
return req.messages.map((m) => ({
|
|
92
|
+
role: m.role,
|
|
93
|
+
content: typeof m.content === "string" ? m.content : m.content.map(anthropicBlock)
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
function anthropicBlock(b) {
|
|
97
|
+
switch (b.type) {
|
|
98
|
+
case "text":
|
|
99
|
+
return {
|
|
100
|
+
type: "text",
|
|
101
|
+
text: b.text,
|
|
102
|
+
...b.cacheControl ? { cache_control: { type: "ephemeral" } } : {}
|
|
103
|
+
};
|
|
104
|
+
case "image":
|
|
105
|
+
return {
|
|
106
|
+
type: "image",
|
|
107
|
+
source: b.source.type === "base64" ? { type: "base64", media_type: b.source.mediaType, data: b.source.data } : { type: "url", url: b.source.url }
|
|
108
|
+
};
|
|
109
|
+
case "document":
|
|
110
|
+
return {
|
|
111
|
+
type: "document",
|
|
112
|
+
source: b.source.type === "base64" ? { type: "base64", media_type: "application/pdf", data: b.source.data } : { type: "url", url: b.source.url }
|
|
113
|
+
};
|
|
114
|
+
case "tool_use":
|
|
115
|
+
return { type: "tool_use", id: b.id, name: b.name, input: b.input };
|
|
116
|
+
case "tool_result":
|
|
117
|
+
return {
|
|
118
|
+
type: "tool_result",
|
|
119
|
+
tool_use_id: b.toolUseId,
|
|
120
|
+
content: typeof b.content === "string" ? b.content : b.content.map(anthropicBlock),
|
|
121
|
+
...b.isError ? { is_error: true } : {}
|
|
122
|
+
};
|
|
123
|
+
case "thinking":
|
|
124
|
+
return { type: "thinking", thinking: b.thinking, signature: b.signature ?? "" };
|
|
125
|
+
case "audio":
|
|
126
|
+
throw new Error("Anthropic does not support audio input");
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function buildTools(req) {
|
|
130
|
+
const tools = [];
|
|
131
|
+
for (const t of req.tools ?? []) {
|
|
132
|
+
tools.push({ name: t.name, description: t.description, input_schema: t.inputSchema });
|
|
133
|
+
}
|
|
134
|
+
if (req.webSearch) tools.push({ type: "web_search_20260209", name: "web_search" });
|
|
135
|
+
return tools.length > 0 ? tools : void 0;
|
|
136
|
+
}
|
|
137
|
+
function mapToolChoice(tc) {
|
|
138
|
+
if (tc === void 0) return void 0;
|
|
139
|
+
if (tc === "auto") return { type: "auto" };
|
|
140
|
+
if (tc === "any") return { type: "any" };
|
|
141
|
+
if (tc === "none") return { type: "none" };
|
|
142
|
+
return { type: "tool", name: tc.name };
|
|
143
|
+
}
|
|
144
|
+
function mapContent(blocks) {
|
|
145
|
+
const out = [];
|
|
146
|
+
for (const b of blocks) {
|
|
147
|
+
if (b.type === "text") out.push({ type: "text", text: b.text });
|
|
148
|
+
else if (b.type === "tool_use") {
|
|
149
|
+
out.push({ type: "tool_use", id: b.id, name: b.name, input: b.input ?? {} });
|
|
150
|
+
} else if (b.type === "thinking") {
|
|
151
|
+
out.push({ type: "thinking", thinking: b.thinking, signature: b.signature });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return out;
|
|
155
|
+
}
|
|
156
|
+
function mapUsage(u) {
|
|
157
|
+
const usage = { inputTokens: u.input_tokens ?? 0, outputTokens: u.output_tokens ?? 0 };
|
|
158
|
+
if (u.cache_creation_input_tokens != null) usage.cacheCreationTokens = u.cache_creation_input_tokens;
|
|
159
|
+
if (u.cache_read_input_tokens != null) usage.cacheReadTokens = u.cache_read_input_tokens;
|
|
160
|
+
return usage;
|
|
161
|
+
}
|
|
162
|
+
function mapStop(reason) {
|
|
163
|
+
switch (reason) {
|
|
164
|
+
case "end_turn":
|
|
165
|
+
case "max_tokens":
|
|
166
|
+
case "stop_sequence":
|
|
167
|
+
case "tool_use":
|
|
168
|
+
case "pause_turn":
|
|
169
|
+
case "refusal":
|
|
170
|
+
return reason;
|
|
171
|
+
default:
|
|
172
|
+
return "end_turn";
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function mapStreamEvent(raw, req) {
|
|
176
|
+
switch (raw.type) {
|
|
177
|
+
case "message_start":
|
|
178
|
+
return {
|
|
179
|
+
type: "message_start",
|
|
180
|
+
message: {
|
|
181
|
+
id: raw.message.id,
|
|
182
|
+
model: req.model,
|
|
183
|
+
provider: "anthropic",
|
|
184
|
+
role: "assistant",
|
|
185
|
+
content: [],
|
|
186
|
+
usage: mapUsage(raw.message.usage)
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
case "content_block_start": {
|
|
190
|
+
const block = mapStartBlock(raw.content_block);
|
|
191
|
+
return block ? { type: "content_block_start", index: raw.index, contentBlock: block } : void 0;
|
|
192
|
+
}
|
|
193
|
+
case "content_block_delta": {
|
|
194
|
+
const d = raw.delta;
|
|
195
|
+
if (d.type === "text_delta") return { type: "content_block_delta", index: raw.index, delta: { type: "text_delta", text: d.text } };
|
|
196
|
+
if (d.type === "thinking_delta") return { type: "content_block_delta", index: raw.index, delta: { type: "thinking_delta", thinking: d.thinking } };
|
|
197
|
+
if (d.type === "input_json_delta") return { type: "content_block_delta", index: raw.index, delta: { type: "input_json_delta", partialJson: d.partial_json } };
|
|
198
|
+
return void 0;
|
|
199
|
+
}
|
|
200
|
+
case "content_block_stop":
|
|
201
|
+
return { type: "content_block_stop", index: raw.index };
|
|
202
|
+
case "message_delta":
|
|
203
|
+
return { type: "message_delta", delta: { stopReason: mapStop(raw.delta.stop_reason) }, usage: mapUsage(raw.usage) };
|
|
204
|
+
case "message_stop":
|
|
205
|
+
return { type: "message_stop" };
|
|
206
|
+
default:
|
|
207
|
+
return void 0;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function mapStartBlock(cb) {
|
|
211
|
+
if (cb.type === "text") return { type: "text", text: cb.text };
|
|
212
|
+
if (cb.type === "tool_use") return { type: "tool_use", id: cb.id, name: cb.name, input: {} };
|
|
213
|
+
if (cb.type === "thinking") return { type: "thinking", thinking: cb.thinking, signature: cb.signature };
|
|
214
|
+
return void 0;
|
|
215
|
+
}
|
|
216
|
+
export {
|
|
217
|
+
AnthropicProvider
|
|
218
|
+
};
|
|
219
|
+
//# sourceMappingURL=anthropic-JXDXR7O7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/anthropic.ts"],"sourcesContent":["// Anthropic (Claude) adapter. The canonical shape is Anthropic-flavored, so this\n// is the lowest-translation adapter. Ported from odla-kg's claude.ts patterns:\n// forced tool-call via tool_choice, web_search_20260209 with pause_turn\n// continuation, adaptive thinking + output_config.effort (never budget_tokens).\n//\n// The installed @anthropic-ai/sdk (0.70.x) doesn't yet type adaptive thinking,\n// output_config, or web_search_20260209 — those newer request fields are built\n// into a loose params object and cast at the call site (the wire accepts them),\n// exactly as odla-kg does for the web-search tool literal.\n\nimport Anthropic from \"@anthropic-ai/sdk\";\nimport type { ModelSpec } from \"../shape/capabilities\";\nimport type { Provider } from \"./types\";\nimport { mapProviderError, normalizeError } from \"./errors\";\nimport {\n addUsage,\n emptyUsage,\n type OracleContentBlock,\n type OracleEvent,\n type OracleRequest,\n type OracleResponse,\n type OracleStopReason,\n type OracleUsage,\n type TextBlock,\n type ToolChoice,\n} from \"../shape/index\";\n\nexport class AnthropicProvider implements Provider {\n readonly id = \"anthropic\" as const;\n private client: Anthropic;\n\n /** `client` may be injected (tests, custom transport); otherwise built from key. */\n constructor(apiKey: string, client?: Anthropic) {\n this.client = client ?? new Anthropic({ apiKey });\n }\n\n async create(spec: ModelSpec, req: OracleRequest): Promise<OracleResponse> {\n try {\n let messages = toAnthropicMessages(req);\n const content: OracleContentBlock[] = [];\n const usage = emptyUsage();\n let stopReason: OracleStopReason = \"end_turn\";\n let id = \"\";\n\n // Loop only to resolve server-side tools (pause_turn), max 8 hops. Client\n // tool_use / end_turn / etc. return immediately.\n for (let i = 0; i < 8; i++) {\n const res = await this.client.messages.create(\n buildParams(spec, req, messages) as unknown as Anthropic.MessageCreateParamsNonStreaming,\n );\n id = res.id;\n addUsage(usage, mapUsage(res.usage));\n content.push(...mapContent(res.content));\n stopReason = mapStop(res.stop_reason);\n if (res.stop_reason === \"pause_turn\") {\n messages = [...messages, { role: \"assistant\", content: res.content }];\n continue;\n }\n break;\n }\n\n return { id, model: req.model, provider: \"anthropic\", role: \"assistant\", content, stopReason, usage };\n } catch (err) {\n mapProviderError(err, \"anthropic\");\n }\n }\n\n async *stream(spec: ModelSpec, req: OracleRequest): AsyncIterable<OracleEvent> {\n try {\n const events = this.client.messages.stream(\n buildParams(spec, req, toAnthropicMessages(req)) as unknown as Anthropic.MessageStreamParams,\n );\n for await (const raw of events) {\n const ev = mapStreamEvent(raw, req);\n if (ev) yield ev;\n }\n } catch (err) {\n yield { type: \"error\", error: normalizeError(err, \"anthropic\").toShape() };\n }\n }\n}\n\n// ----- request mapping -----\n\nfunction buildParams(\n spec: ModelSpec,\n req: OracleRequest,\n messages: Anthropic.MessageParam[],\n): Record<string, unknown> {\n const params: Record<string, unknown> = {\n model: spec.nativeId,\n max_tokens: req.maxTokens,\n messages,\n };\n\n const system = toAnthropicSystem(req.system);\n if (system !== undefined) params.system = system;\n\n const tools = buildTools(req);\n if (tools) params.tools = tools;\n\n const toolChoice = mapToolChoice(req.toolChoice);\n if (toolChoice) params.tool_choice = toolChoice;\n\n if (req.stopSequences && req.stopSequences.length > 0) params.stop_sequences = req.stopSequences;\n\n // Sampling params are rejected on the frontier models (they carry effort);\n // only the non-effort tier (Haiku) accepts temperature.\n if (req.temperature !== undefined && !spec.capabilities.effort) params.temperature = req.temperature;\n\n if (req.thinking === \"adaptive\" && spec.capabilities.thinking) params.thinking = { type: \"adaptive\" };\n\n const outputConfig: Record<string, unknown> = {};\n if (req.effort && spec.capabilities.effort) outputConfig.effort = req.effort;\n if (req.responseFormat && spec.capabilities.structuredOutput) {\n outputConfig.format = { type: \"json_schema\", schema: req.responseFormat.schema };\n }\n if (Object.keys(outputConfig).length > 0) params.output_config = outputConfig;\n\n if (req.providerExtras) Object.assign(params, req.providerExtras);\n return params;\n}\n\nfunction toAnthropicSystem(system: OracleRequest[\"system\"]): string | Anthropic.TextBlockParam[] | undefined {\n if (system === undefined) return undefined;\n if (typeof system === \"string\") return system;\n return system.map((b: TextBlock) => ({\n type: \"text\" as const,\n text: b.text,\n ...(b.cacheControl ? { cache_control: { type: \"ephemeral\" as const } } : {}),\n }));\n}\n\nfunction toAnthropicMessages(req: OracleRequest): Anthropic.MessageParam[] {\n return req.messages.map((m) => ({\n role: m.role,\n content: typeof m.content === \"string\" ? m.content : m.content.map(anthropicBlock),\n }));\n}\n\nfunction anthropicBlock(b: OracleContentBlock): Anthropic.ContentBlockParam {\n switch (b.type) {\n case \"text\":\n return {\n type: \"text\",\n text: b.text,\n ...(b.cacheControl ? { cache_control: { type: \"ephemeral\" } } : {}),\n };\n case \"image\":\n return {\n type: \"image\",\n source:\n b.source.type === \"base64\"\n ? { type: \"base64\", media_type: b.source.mediaType, data: b.source.data }\n : { type: \"url\", url: b.source.url },\n } as Anthropic.ContentBlockParam;\n case \"document\":\n return {\n type: \"document\",\n source:\n b.source.type === \"base64\"\n ? { type: \"base64\", media_type: \"application/pdf\", data: b.source.data }\n : { type: \"url\", url: b.source.url },\n } as Anthropic.ContentBlockParam;\n case \"tool_use\":\n return { type: \"tool_use\", id: b.id, name: b.name, input: b.input };\n case \"tool_result\":\n return {\n type: \"tool_result\",\n tool_use_id: b.toolUseId,\n content:\n typeof b.content === \"string\"\n ? b.content\n : (b.content.map(anthropicBlock) as Anthropic.ToolResultBlockParam[\"content\"]),\n ...(b.isError ? { is_error: true } : {}),\n };\n case \"thinking\":\n return { type: \"thinking\", thinking: b.thinking, signature: b.signature ?? \"\" };\n case \"audio\":\n // Guarded by validateRequest; defensive in case validation is bypassed.\n throw new Error(\"Anthropic does not support audio input\");\n }\n}\n\nfunction buildTools(req: OracleRequest): unknown[] | undefined {\n const tools: unknown[] = [];\n for (const t of req.tools ?? []) {\n tools.push({ name: t.name, description: t.description, input_schema: t.inputSchema });\n }\n if (req.webSearch) tools.push({ type: \"web_search_20260209\", name: \"web_search\" });\n return tools.length > 0 ? tools : undefined;\n}\n\nfunction mapToolChoice(tc: ToolChoice | undefined): unknown | undefined {\n if (tc === undefined) return undefined;\n if (tc === \"auto\") return { type: \"auto\" };\n if (tc === \"any\") return { type: \"any\" };\n if (tc === \"none\") return { type: \"none\" };\n return { type: \"tool\", name: tc.name };\n}\n\n// ----- response mapping -----\n\nfunction mapContent(blocks: Anthropic.ContentBlock[]): OracleContentBlock[] {\n const out: OracleContentBlock[] = [];\n for (const b of blocks) {\n if (b.type === \"text\") out.push({ type: \"text\", text: b.text });\n else if (b.type === \"tool_use\") {\n out.push({ type: \"tool_use\", id: b.id, name: b.name, input: (b.input ?? {}) as Record<string, unknown> });\n } else if (b.type === \"thinking\") {\n out.push({ type: \"thinking\", thinking: b.thinking, signature: b.signature });\n }\n // server_tool_use / web_search_tool_result blocks are dropped — the answer\n // text lives in text blocks.\n }\n return out;\n}\n\nfunction mapUsage(u: Anthropic.Usage): OracleUsage {\n const usage: OracleUsage = { inputTokens: u.input_tokens ?? 0, outputTokens: u.output_tokens ?? 0 };\n if (u.cache_creation_input_tokens != null) usage.cacheCreationTokens = u.cache_creation_input_tokens;\n if (u.cache_read_input_tokens != null) usage.cacheReadTokens = u.cache_read_input_tokens;\n return usage;\n}\n\nfunction mapStop(reason: string | null | undefined): OracleStopReason {\n switch (reason) {\n case \"end_turn\":\n case \"max_tokens\":\n case \"stop_sequence\":\n case \"tool_use\":\n case \"pause_turn\":\n case \"refusal\":\n return reason;\n default:\n return \"end_turn\";\n }\n}\n\n// ----- stream mapping -----\n\nfunction mapStreamEvent(raw: Anthropic.RawMessageStreamEvent, req: OracleRequest): OracleEvent | undefined {\n switch (raw.type) {\n case \"message_start\":\n return {\n type: \"message_start\",\n message: {\n id: raw.message.id,\n model: req.model,\n provider: \"anthropic\",\n role: \"assistant\",\n content: [],\n usage: mapUsage(raw.message.usage),\n },\n };\n case \"content_block_start\": {\n const block = mapStartBlock(raw.content_block);\n return block ? { type: \"content_block_start\", index: raw.index, contentBlock: block } : undefined;\n }\n case \"content_block_delta\": {\n const d = raw.delta;\n if (d.type === \"text_delta\") return { type: \"content_block_delta\", index: raw.index, delta: { type: \"text_delta\", text: d.text } };\n if (d.type === \"thinking_delta\") return { type: \"content_block_delta\", index: raw.index, delta: { type: \"thinking_delta\", thinking: d.thinking } };\n if (d.type === \"input_json_delta\") return { type: \"content_block_delta\", index: raw.index, delta: { type: \"input_json_delta\", partialJson: d.partial_json } };\n return undefined;\n }\n case \"content_block_stop\":\n return { type: \"content_block_stop\", index: raw.index };\n case \"message_delta\":\n return { type: \"message_delta\", delta: { stopReason: mapStop(raw.delta.stop_reason) }, usage: mapUsage(raw.usage as Anthropic.Usage) };\n case \"message_stop\":\n return { type: \"message_stop\" };\n default:\n return undefined;\n }\n}\n\nfunction mapStartBlock(cb: Anthropic.ContentBlock): OracleContentBlock | undefined {\n if (cb.type === \"text\") return { type: \"text\", text: cb.text };\n if (cb.type === \"tool_use\") return { type: \"tool_use\", id: cb.id, name: cb.name, input: {} };\n if (cb.type === \"thinking\") return { type: \"thinking\", thinking: cb.thinking, signature: cb.signature };\n return undefined;\n}\n"],"mappings":"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","names":[]}
|