@odla-ai/ai 0.2.2 → 0.3.1
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 +71 -17
- package/dist/{anthropic-BIGCVQE4.js → anthropic-2NPMHJZT.js} +16 -8
- package/dist/anthropic-2NPMHJZT.js.map +1 -0
- package/dist/chunk-OX4TA4ZH.js +16 -0
- package/dist/chunk-OX4TA4ZH.js.map +1 -0
- package/dist/{chunk-QFT2DU2X.js → chunk-PXXCN2EU.js} +62 -5
- package/dist/chunk-PXXCN2EU.js.map +1 -0
- package/dist/{google-QY5B4T5O.js → google-3XV2VWVB.js} +18 -8
- package/dist/google-3XV2VWVB.js.map +1 -0
- package/dist/index.cjs +664 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +120 -10
- package/dist/index.d.ts +120 -10
- package/dist/index.js +545 -50
- package/dist/index.js.map +1 -1
- package/dist/{openai-PTZPJYTT.js → openai-NTQCF577.js} +20 -10
- package/dist/openai-NTQCF577.js.map +1 -0
- package/llms.txt +34 -15
- package/package.json +12 -5
- package/dist/anthropic-BIGCVQE4.js.map +0 -1
- package/dist/chunk-QFT2DU2X.js.map +0 -1
- package/dist/google-QY5B4T5O.js.map +0 -1
- package/dist/openai-PTZPJYTT.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,25 +1,28 @@
|
|
|
1
1
|
# @odla-ai/ai
|
|
2
2
|
|
|
3
|
-
> ⚠️ **
|
|
4
|
-
>
|
|
5
|
-
>
|
|
6
|
-
>
|
|
3
|
+
> ⚠️ **Early access — pre-1.0.** Agents work from bounded runbooks; humans
|
|
4
|
+
> approve credentials, production changes, releases, and merges. APIs and exact
|
|
5
|
+
> package availability can change. Review the documented guarantees and
|
|
6
|
+
> limitations; this software is MIT-licensed and provided without warranty.
|
|
7
7
|
|
|
8
8
|
One general-purpose interface for AI inference across **Anthropic (Claude)**,
|
|
9
9
|
**OpenAI (GPT)**, and **Google (Gemini)** — text, image, and audio — plus a
|
|
10
10
|
tool-use **agent engine** and an **eval harness**. It's the place every odla app
|
|
11
11
|
reaches for when it needs AI.
|
|
12
12
|
|
|
13
|
-
- **Library
|
|
14
|
-
gateway,
|
|
13
|
+
- **Library surface.** Import it in-process and bring your own keys; this
|
|
14
|
+
package exports no generic hosted gateway. Separately, odla's System AI
|
|
15
|
+
broker uses the same facade server-side for bounded o11y/security purposes
|
|
16
|
+
and returns purpose grants rather than provider credentials.
|
|
15
17
|
- **One canonical shape.** Anthropic-flavored content blocks, forced tool calls
|
|
16
18
|
with object inputs, a normalized streaming event vocabulary, cache-aware usage,
|
|
17
19
|
and a closed error taxonomy — the same regardless of which provider answers.
|
|
18
20
|
- **Capability-checked.** Every request is validated against the model before
|
|
19
21
|
dispatch, so "audio to Claude" fails fast with a clear error instead of an
|
|
20
22
|
opaque provider 400.
|
|
21
|
-
- **
|
|
22
|
-
lazy
|
|
23
|
+
- **Server runtimes.** Node 20+ and Cloudflare Workers. Provider adapters are
|
|
24
|
+
runtime-lazy, but all three provider SDKs are package dependencies. Do not put
|
|
25
|
+
long-lived provider keys in browser code; use your own authenticated proxy.
|
|
23
26
|
|
|
24
27
|
```bash
|
|
25
28
|
npm install @odla-ai/ai
|
|
@@ -77,9 +80,17 @@ const { value } = await ai.extract<{ sentiment: "positive" | "negative" | "neutr
|
|
|
77
80
|
},
|
|
78
81
|
},
|
|
79
82
|
});
|
|
80
|
-
// value.sentiment === "positive"
|
|
83
|
+
// value.sentiment === "positive" (parsed and validated before it is returned)
|
|
81
84
|
```
|
|
82
85
|
|
|
86
|
+
`extract` and local agent tools validate provider arguments before returning or
|
|
87
|
+
executing them. The dependency-free validator supports the common tool-schema
|
|
88
|
+
subset: local `$ref`/`$defs`, `type`, `enum`, `const`, composition, object
|
|
89
|
+
properties/required/additional properties, arrays, and string/number constraints.
|
|
90
|
+
Unknown or unsupported schema keywords outside that documented subset fail
|
|
91
|
+
closed. `extract()` throws `ToolInputError`; local agent tools return an error
|
|
92
|
+
`tool_result` and their handlers do not execute.
|
|
93
|
+
|
|
83
94
|
### Web search
|
|
84
95
|
|
|
85
96
|
```ts
|
|
@@ -146,6 +157,28 @@ console.log(run.toolCalls); // [{ toolUse, output }]
|
|
|
146
157
|
console.log(run.usage); // accumulated across turns
|
|
147
158
|
```
|
|
148
159
|
|
|
160
|
+
Provider calls and tool handlers receive a combined cancellation signal. Runs
|
|
161
|
+
can also stop before additional effects when a run-wide budget is exhausted:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
const run = await runAgent(ai, calculator, {
|
|
165
|
+
input: "Reconcile these records",
|
|
166
|
+
signal: request.signal,
|
|
167
|
+
deadline: Date.now() + 30_000,
|
|
168
|
+
budget: { maxInputTokens: 20_000, maxOutputTokens: 4_000, maxToolCalls: 8 },
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Token limits are post-turn ceilings because provider input usage is known only
|
|
173
|
+
after a response. `maxInputTokens` and `maxTotalTokens` therefore cannot
|
|
174
|
+
tokenizer-bound the first prompt; they stop later turns and tool effects once
|
|
175
|
+
reported usage reaches the ceiling. `maxOutputTokens` and the remaining total
|
|
176
|
+
budget cap each request's `maxTokens` before dispatch. Limits must be integers
|
|
177
|
+
(`maxToolCalls` may be zero; token limits must be positive). When a limit blocks
|
|
178
|
+
tools, the loop records a matching error `tool_result` for every requested tool
|
|
179
|
+
before persisting memory, so retries never inherit an unmatched effect request.
|
|
180
|
+
`stoppedReason` is `"budget_exhausted"` when a limit prevents further work.
|
|
181
|
+
|
|
149
182
|
Tools can declare taint constraints (`outputTaint` / `acceptsTaint`) for a
|
|
150
183
|
lightweight CaMeL-style prompt-injection gate, and personas can carry a pluggable
|
|
151
184
|
`MemoryScope`.
|
|
@@ -229,20 +262,23 @@ The agent handshake (an agent never takes a platform secret):
|
|
|
229
262
|
import { requestToken, init as odlaInit } from "@odla-ai/db";
|
|
230
263
|
import { provisionAgentApp, odlaDbKeyResolver, OdlaDbMemory, persistRun, runAgent, init } from "@odla-ai/ai";
|
|
231
264
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
265
|
+
const ODLA_PLATFORM = "https://odla.ai";
|
|
266
|
+
const ODLA_DB_URL = "https://db.odla.ai";
|
|
267
|
+
|
|
268
|
+
// 1. Device authorization — the human approves a short code at odla.ai;
|
|
269
|
+
// the agent receives a scoped, revocable platform token.
|
|
270
|
+
const { token } = await requestToken({ endpoint: ODLA_PLATFORM, onCode: ({ userCode }) => console.log(userCode) });
|
|
235
271
|
|
|
236
272
|
// 2. Provision: create app, mint an app key, push the agent schema, and store the
|
|
237
273
|
// user's BYO provider keys as odla-db secrets (encrypted at rest).
|
|
238
274
|
const { appId, appKey } = await provisionAgentApp({
|
|
239
|
-
endpoint:
|
|
275
|
+
endpoint: ODLA_PLATFORM,
|
|
240
276
|
token, // operator/dev token — needed to WRITE secrets
|
|
241
277
|
providerKeys: { openai: OPENAI_KEY, anthropic: ANTHROPIC_KEY },
|
|
242
278
|
});
|
|
243
279
|
|
|
244
280
|
// 3. Runtime: read keys from secrets with the app key, persist memory + run traces.
|
|
245
|
-
const db = odlaInit({ appId, adminToken: appKey, endpoint:
|
|
281
|
+
const db = odlaInit({ appId, adminToken: appKey, endpoint: ODLA_DB_URL });
|
|
246
282
|
const ai = init({ resolveKey: odlaDbKeyResolver(db) }); // keys come from odla-db secrets
|
|
247
283
|
|
|
248
284
|
const persona = { name: "assistant", model: "gpt-5", memory: new OdlaDbMemory({ db, sessionId: "user-42" }) };
|
|
@@ -279,11 +315,28 @@ const { ai, provider, model } = await initFromPlatform({
|
|
|
279
315
|
|
|
280
316
|
The config comes from the anonymous `public-config` endpoint and is cached
|
|
281
317
|
(~60s by default), so switching provider or model in Studio takes effect
|
|
282
|
-
without a redeploy
|
|
283
|
-
|
|
318
|
+
without a redeploy. The configured provider is enforced: its facade catalog is
|
|
319
|
+
filtered to that provider, and a mismatched configured/default/call model fails
|
|
320
|
+
with `ConfigError`. If Studio has no default model, each call must name a model
|
|
321
|
+
for that provider. Tenant-bound facades are never shared globally. Provider
|
|
322
|
+
keys and SDK clients have finite 60-second in-isolate caches; rotation replaces
|
|
323
|
+
the previous credential generation rather than retaining it. Set
|
|
324
|
+
`keyResolverOptions: { ttlMs, cacheVersion }` to shorten or immediately
|
|
325
|
+
invalidate it after rotation. **Never put provider keys in an app
|
|
284
326
|
Worker's wrangler vars/secrets** — the worker's only secret is its odla-db
|
|
285
327
|
app key.
|
|
286
328
|
|
|
329
|
+
### 0.3 migration notes
|
|
330
|
+
|
|
331
|
+
- Tool/extraction JSON Schemas now fail closed. `extract()` throws
|
|
332
|
+
`ToolInputError`; `runAgent()` records an error `tool_result` and does not run
|
|
333
|
+
a local handler when its arguments do not validate.
|
|
334
|
+
- Platform calls cannot cross the provider selected in Studio.
|
|
335
|
+
- `CancelledError` (`cancelled`) and `DeadlineExceededError`
|
|
336
|
+
(`deadline_exceeded`) distinguish caller limits from provider failures.
|
|
337
|
+
- Invalid run budgets now throw `ConfigError`; budget stops persist paired tool
|
|
338
|
+
results and may therefore add one final user/tool-result turn to memory.
|
|
339
|
+
|
|
287
340
|
## Models & capabilities
|
|
288
341
|
|
|
289
342
|
The catalog maps a canonical model id → provider, native id, and capabilities.
|
|
@@ -307,7 +360,8 @@ models; extend or override it via `buildCatalog` / `init({ catalog })`.
|
|
|
307
360
|
|
|
308
361
|
Every error is an `OdlaAIError` subclass carrying a stable `code` — branch on
|
|
309
362
|
`err.code`, not message text: `ConfigError`, `AuthError`, `RateLimitError`,
|
|
310
|
-
`CapabilityError`, `ContextWindowError`, `InvalidRequestError`, `
|
|
363
|
+
`CapabilityError`, `ContextWindowError`, `InvalidRequestError`, `ToolInputError`,
|
|
364
|
+
`CancelledError`, `DeadlineExceededError`, `ProviderError`.
|
|
311
365
|
|
|
312
366
|
## Develop
|
|
313
367
|
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
toolInput
|
|
3
|
+
} from "./chunk-OX4TA4ZH.js";
|
|
1
4
|
import {
|
|
2
5
|
addUsage,
|
|
3
6
|
emptyUsage,
|
|
4
7
|
mapProviderError,
|
|
5
|
-
normalizeError
|
|
6
|
-
|
|
8
|
+
normalizeError,
|
|
9
|
+
signalWithDeadline
|
|
10
|
+
} from "./chunk-PXXCN2EU.js";
|
|
7
11
|
|
|
8
12
|
// src/providers/anthropic.ts
|
|
9
13
|
import Anthropic from "@anthropic-ai/sdk";
|
|
@@ -103,7 +107,7 @@ function mapContent(blocks) {
|
|
|
103
107
|
for (const b of blocks) {
|
|
104
108
|
if (b.type === "text") out.push({ type: "text", text: b.text });
|
|
105
109
|
else if (b.type === "tool_use") {
|
|
106
|
-
out.push({ type: "tool_use", id: b.id, name: b.name, input: b.input ?? {} });
|
|
110
|
+
out.push({ type: "tool_use", id: b.id, name: b.name, input: toolInput(b.input ?? {}, "anthropic", b.name) });
|
|
107
111
|
} else if (b.type === "thinking") {
|
|
108
112
|
out.push({ type: "thinking", thinking: b.thinking, signature: b.signature });
|
|
109
113
|
}
|
|
@@ -182,6 +186,7 @@ var AnthropicProvider = class {
|
|
|
182
186
|
this.client = client ?? new Anthropic({ apiKey });
|
|
183
187
|
}
|
|
184
188
|
async create(spec, req) {
|
|
189
|
+
const signal = signalWithDeadline(req.signal, req.deadline);
|
|
185
190
|
try {
|
|
186
191
|
let messages = toAnthropicMessages(req);
|
|
187
192
|
const content = [];
|
|
@@ -190,7 +195,8 @@ var AnthropicProvider = class {
|
|
|
190
195
|
let id = "";
|
|
191
196
|
for (let i = 0; i < 8; i++) {
|
|
192
197
|
const res = await this.client.messages.create(
|
|
193
|
-
buildParams(spec, req, messages)
|
|
198
|
+
buildParams(spec, req, messages),
|
|
199
|
+
{ signal }
|
|
194
200
|
);
|
|
195
201
|
id = res.id;
|
|
196
202
|
addUsage(usage, mapUsage(res.usage));
|
|
@@ -204,24 +210,26 @@ var AnthropicProvider = class {
|
|
|
204
210
|
}
|
|
205
211
|
return { id, model: req.model, provider: "anthropic", role: "assistant", content, stopReason, usage };
|
|
206
212
|
} catch (err) {
|
|
207
|
-
mapProviderError(err, "anthropic");
|
|
213
|
+
mapProviderError(err, "anthropic", signal);
|
|
208
214
|
}
|
|
209
215
|
}
|
|
210
216
|
async *stream(spec, req) {
|
|
217
|
+
const signal = signalWithDeadline(req.signal, req.deadline);
|
|
211
218
|
try {
|
|
212
219
|
const events = this.client.messages.stream(
|
|
213
|
-
buildParams(spec, req, toAnthropicMessages(req))
|
|
220
|
+
buildParams(spec, req, toAnthropicMessages(req)),
|
|
221
|
+
{ signal }
|
|
214
222
|
);
|
|
215
223
|
for await (const raw of events) {
|
|
216
224
|
const ev = mapStreamEvent(raw, req);
|
|
217
225
|
if (ev) yield ev;
|
|
218
226
|
}
|
|
219
227
|
} catch (err) {
|
|
220
|
-
yield { type: "error", error: normalizeError(err, "anthropic").toShape() };
|
|
228
|
+
yield { type: "error", error: normalizeError(err, "anthropic", signal).toShape() };
|
|
221
229
|
}
|
|
222
230
|
}
|
|
223
231
|
};
|
|
224
232
|
export {
|
|
225
233
|
AnthropicProvider
|
|
226
234
|
};
|
|
227
|
-
//# sourceMappingURL=anthropic-
|
|
235
|
+
//# sourceMappingURL=anthropic-2NPMHJZT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/anthropic.ts","../src/providers/anthropic/request.ts","../src/providers/anthropic/response.ts","../src/providers/anthropic/stream.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 in ./anthropic/request and cast at the call site\n// (the wire accepts them), 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 { addUsage, emptyUsage, type OracleContentBlock, type OracleEvent, type OracleRequest, type OracleResponse, type OracleStopReason } from \"../shape/index\";\nimport { buildParams, toAnthropicMessages } from \"./anthropic/request\";\nimport { mapContent, mapStop, mapUsage } from \"./anthropic/response\";\nimport { mapStreamEvent } from \"./anthropic/stream\";\nimport { signalWithDeadline } from \"../client/abort\";\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 const signal = signalWithDeadline(req.signal, req.deadline);\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 { signal },\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\", signal);\n }\n }\n\n async *stream(spec: ModelSpec, req: OracleRequest): AsyncIterable<OracleEvent> {\n const signal = signalWithDeadline(req.signal, req.deadline);\n try {\n const events = this.client.messages.stream(\n buildParams(spec, req, toAnthropicMessages(req)) as unknown as Anthropic.MessageStreamParams,\n { signal },\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\", signal).toShape() };\n }\n }\n}\n","// Anthropic request mapping. The canonical shape is Anthropic-flavored, so this\n// is the lowest-translation adapter.\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport type { ModelSpec } from \"../../shape/capabilities\";\nimport type { OracleContentBlock, OracleRequest, TextBlock, ToolChoice } from \"../../shape/index\";\n\nexport function 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\nexport function 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","// Anthropic response mapping. mapUsage / mapStop are also used by the stream\n// mapper (kept here so the entry ↔ stream edge stays one-directional).\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport type { OracleContentBlock, OracleStopReason, OracleUsage } from \"../../shape/index\";\nimport { toolInput } from \"../tool-input\";\n\nexport function 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: toolInput(b.input ?? {}, \"anthropic\", b.name) });\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\nexport function 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\nexport function 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","// Anthropic stream mapping. The SDK's raw events pass through nearly 1:1.\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport type { OracleContentBlock, OracleEvent, OracleRequest } from \"../../shape/index\";\nimport { mapStop, mapUsage } from \"./response\";\n\nexport function 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":[]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ToolInputError
|
|
3
|
+
} from "./chunk-PXXCN2EU.js";
|
|
4
|
+
|
|
5
|
+
// src/providers/tool-input.ts
|
|
6
|
+
function toolInput(value, provider, name) {
|
|
7
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
throw new ToolInputError(`[${provider}] tool "${name}" returned arguments that are not a JSON object.`, { tool: name });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
toolInput
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=chunk-OX4TA4ZH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/tool-input.ts"],"sourcesContent":["import { ToolInputError, type ProviderId } from \"../shape/index\";\n\n/** Normalize a provider's decoded function arguments without manufacturing an\n * empty object from malformed data. Schema validation happens at the facade or\n * agent boundary, but structural corruption fails here. */\nexport function toolInput(value: unknown, provider: ProviderId, name: string): Record<string, unknown> {\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n throw new ToolInputError(`[${provider}] tool \"${name}\" returned arguments that are not a JSON object.`, { tool: name });\n}\n"],"mappings":";;;;;AAKO,SAAS,UAAU,OAAgB,UAAsB,MAAuC;AACrG,MAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AACxE,WAAO;AAAA,EACT;AACA,QAAM,IAAI,eAAe,IAAI,QAAQ,WAAW,IAAI,oDAAoD,EAAE,MAAM,KAAK,CAAC;AACxH;","names":[]}
|
|
@@ -64,6 +64,23 @@ var InvalidRequestError = class extends OdlaAIError {
|
|
|
64
64
|
super("invalid_request", message, opts);
|
|
65
65
|
}
|
|
66
66
|
};
|
|
67
|
+
var ToolInputError = class extends OdlaAIError {
|
|
68
|
+
tool;
|
|
69
|
+
constructor(message, opts) {
|
|
70
|
+
super("tool_input_invalid", message, { cause: opts?.cause });
|
|
71
|
+
this.tool = opts?.tool;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var CancelledError = class extends OdlaAIError {
|
|
75
|
+
constructor(message = "AI request was cancelled.", opts) {
|
|
76
|
+
super("cancelled", message, opts);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
var DeadlineExceededError = class extends OdlaAIError {
|
|
80
|
+
constructor(message = "AI request deadline was exceeded.", opts) {
|
|
81
|
+
super("deadline_exceeded", message, opts);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
67
84
|
var ProviderError = class extends OdlaAIError {
|
|
68
85
|
constructor(message, opts) {
|
|
69
86
|
super("provider_error", message, opts);
|
|
@@ -97,8 +114,10 @@ function messageOf(err) {
|
|
|
97
114
|
if (typeof err === "string") return err;
|
|
98
115
|
return "unknown provider error";
|
|
99
116
|
}
|
|
100
|
-
function normalizeError(err, provider) {
|
|
117
|
+
function normalizeError(err, provider, signal) {
|
|
101
118
|
if (err instanceof OdlaAIError) return err;
|
|
119
|
+
const cancellation = cancellationError(signal?.aborted ? signal.reason : err);
|
|
120
|
+
if (signal?.aborted || cancellation) return cancellation ?? new CancelledError(void 0, { cause: err });
|
|
102
121
|
const status = statusOf(err);
|
|
103
122
|
const message = messageOf(err);
|
|
104
123
|
const tagged = `[${provider}] ${message}`;
|
|
@@ -114,8 +133,41 @@ function normalizeError(err, provider) {
|
|
|
114
133
|
}
|
|
115
134
|
return new ProviderError(tagged, { providerStatus: status, cause: err });
|
|
116
135
|
}
|
|
117
|
-
function mapProviderError(err, provider) {
|
|
118
|
-
throw normalizeError(err, provider);
|
|
136
|
+
function mapProviderError(err, provider, signal) {
|
|
137
|
+
throw normalizeError(err, provider, signal);
|
|
138
|
+
}
|
|
139
|
+
function cancellationError(reason) {
|
|
140
|
+
if (reason instanceof DeadlineExceededError || reason instanceof CancelledError) return reason;
|
|
141
|
+
if (!reason || typeof reason !== "object") return void 0;
|
|
142
|
+
const rec = reason;
|
|
143
|
+
if (rec.name === "TimeoutError") return new DeadlineExceededError(void 0, { cause: reason });
|
|
144
|
+
if (rec.name === "AbortError" || rec.name === "APIUserAbortError" || rec.code === "ABORT_ERR") {
|
|
145
|
+
return new CancelledError(void 0, { cause: reason });
|
|
146
|
+
}
|
|
147
|
+
return void 0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/client/abort.ts
|
|
151
|
+
function signalWithDeadline(signal, deadline) {
|
|
152
|
+
if (deadline === void 0) return signal;
|
|
153
|
+
if (!Number.isSafeInteger(deadline)) throw new ConfigError("deadline must be a finite integer Unix timestamp in milliseconds.");
|
|
154
|
+
const remaining = deadline - Date.now();
|
|
155
|
+
if (remaining <= 0) {
|
|
156
|
+
const expired = new AbortController();
|
|
157
|
+
expired.abort(new DeadlineExceededError());
|
|
158
|
+
return signal ? AbortSignal.any([signal, expired.signal]) : expired.signal;
|
|
159
|
+
}
|
|
160
|
+
const timeout = AbortSignal.timeout(remaining);
|
|
161
|
+
return signal ? AbortSignal.any([signal, timeout]) : timeout;
|
|
162
|
+
}
|
|
163
|
+
function throwIfAborted(signal) {
|
|
164
|
+
if (!signal?.aborted) return;
|
|
165
|
+
const reason = signal.reason;
|
|
166
|
+
if (reason instanceof DeadlineExceededError || reason instanceof CancelledError) throw reason;
|
|
167
|
+
if (reason && typeof reason === "object" && reason.name === "TimeoutError") {
|
|
168
|
+
throw new DeadlineExceededError(void 0, { cause: reason });
|
|
169
|
+
}
|
|
170
|
+
throw new CancelledError(void 0, { cause: reason });
|
|
119
171
|
}
|
|
120
172
|
|
|
121
173
|
export {
|
|
@@ -128,8 +180,13 @@ export {
|
|
|
128
180
|
CapabilityError,
|
|
129
181
|
ContextWindowError,
|
|
130
182
|
InvalidRequestError,
|
|
183
|
+
ToolInputError,
|
|
184
|
+
CancelledError,
|
|
185
|
+
DeadlineExceededError,
|
|
131
186
|
ProviderError,
|
|
132
187
|
normalizeError,
|
|
133
|
-
mapProviderError
|
|
188
|
+
mapProviderError,
|
|
189
|
+
signalWithDeadline,
|
|
190
|
+
throwIfAborted
|
|
134
191
|
};
|
|
135
|
-
//# sourceMappingURL=chunk-
|
|
192
|
+
//# sourceMappingURL=chunk-PXXCN2EU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shape/usage.ts","../src/shape/errors.ts","../src/providers/errors.ts","../src/client/abort.ts"],"sourcesContent":["// Token usage accounting. The one runtime island with no dependencies.\n\nexport interface OracleUsage {\n inputTokens: number;\n outputTokens: number;\n cacheCreationTokens?: number;\n cacheReadTokens?: number;\n}\n\n/** A fresh zeroed usage tally (0 input/output tokens, no cache fields) — the seed for accumulation. */\nexport function emptyUsage(): OracleUsage {\n return { inputTokens: 0, outputTokens: 0 };\n}\n\n/** Accumulate `more` into `into`, in place. Cache fields sum when present. */\nexport function addUsage(into: OracleUsage, more: OracleUsage): void {\n into.inputTokens += more.inputTokens;\n into.outputTokens += more.outputTokens;\n if (more.cacheCreationTokens !== undefined) {\n into.cacheCreationTokens = (into.cacheCreationTokens ?? 0) + more.cacheCreationTokens;\n }\n if (more.cacheReadTokens !== undefined) {\n into.cacheReadTokens = (into.cacheReadTokens ?? 0) + more.cacheReadTokens;\n }\n}\n","// The normalized error taxonomy and the class hierarchy every adapter throws.\n\n/** The closed taxonomy of normalized error codes carried by every OdlaAIError\n * and by the streaming `error` event. */\nexport type OracleErrorType =\n | \"invalid_request\"\n | \"auth\"\n | \"rate_limit\"\n | \"context_window\"\n | \"cancelled\"\n | \"deadline_exceeded\"\n | \"tool_input_invalid\"\n | \"capability_unsupported\"\n | \"config\"\n | \"provider_error\";\n\n/** The plain-data error shape used inside the streaming `error` event. */\nexport interface OracleErrorShape {\n code: OracleErrorType;\n message: string;\n /** HTTP status from the upstream provider, when applicable. */\n providerStatus?: number;\n /** Seconds until a retry might succeed (rate limit / overload). */\n retryAfter?: number;\n}\n\n/** Base class for every error odla-ai throws. Carries a stable string `code`\n * (the odla ecosystem convention — see odla-db's AuthError/RuleError) so\n * callers branch on `err.code`, never on message text. */\nexport class OdlaAIError extends Error {\n readonly code: OracleErrorType;\n readonly providerStatus?: number;\n readonly retryAfter?: number;\n\n constructor(\n code: OracleErrorType,\n message: string,\n opts?: { providerStatus?: number; retryAfter?: number; cause?: unknown },\n ) {\n super(message, opts?.cause !== undefined ? { cause: opts.cause } : undefined);\n this.name = new.target.name;\n this.code = code;\n this.providerStatus = opts?.providerStatus;\n this.retryAfter = opts?.retryAfter;\n }\n\n toShape(): OracleErrorShape {\n return {\n code: this.code,\n message: this.message,\n providerStatus: this.providerStatus,\n retryAfter: this.retryAfter,\n };\n }\n}\n\n/** A provider/model is not configured — e.g. no API key for its provider, or an\n * unknown canonical model id. The library analog of odla-db's 501 gating. */\nexport class ConfigError extends OdlaAIError {\n constructor(message: string) {\n super(\"config\", message);\n }\n}\n\n/** Authentication with the provider failed (bad/missing key). */\nexport class AuthError extends OdlaAIError {\n constructor(message: string, opts?: { providerStatus?: number; cause?: unknown }) {\n super(\"auth\", message, opts);\n }\n}\n\n/** The provider rate-limited or is overloaded; check `retryAfter`. */\nexport class RateLimitError extends OdlaAIError {\n constructor(message: string, opts?: { providerStatus?: number; retryAfter?: number; cause?: unknown }) {\n super(\"rate_limit\", message, opts);\n }\n}\n\n/** The request asked a model to do something it cannot — audio to Claude, tools\n * to a text-only model, web search where unsupported, etc. Raised before any\n * network call by `validateRequest`. */\nexport class CapabilityError extends OdlaAIError {\n constructor(message: string) {\n super(\"capability_unsupported\", message);\n }\n}\n\n/** The request exceeded the model's context window. */\nexport class ContextWindowError extends OdlaAIError {\n constructor(message: string, opts?: { providerStatus?: number; cause?: unknown }) {\n super(\"context_window\", message, opts);\n }\n}\n\n/** The request was malformed / rejected as invalid by the provider. */\nexport class InvalidRequestError extends OdlaAIError {\n constructor(message: string, opts?: { providerStatus?: number; cause?: unknown }) {\n super(\"invalid_request\", message, opts);\n }\n}\n\n/** A provider returned tool arguments that are malformed or do not satisfy the\n * declared JSON Schema. Tool handlers are never invoked for this error. */\nexport class ToolInputError extends OdlaAIError {\n readonly tool?: string;\n\n constructor(message: string, opts?: { tool?: string; cause?: unknown }) {\n super(\"tool_input_invalid\", message, { cause: opts?.cause });\n this.tool = opts?.tool;\n }\n}\n\n/** The caller cancelled a request. This is not an upstream failure and should\n * not be retried unless the caller explicitly starts a new operation. */\nexport class CancelledError extends OdlaAIError {\n constructor(message = \"AI request was cancelled.\", opts?: { cause?: unknown }) {\n super(\"cancelled\", message, opts);\n }\n}\n\n/** The request's absolute deadline elapsed. Callers may choose a new deadline,\n * but should not classify this as a provider outage. */\nexport class DeadlineExceededError extends OdlaAIError {\n constructor(message = \"AI request deadline was exceeded.\", opts?: { cause?: unknown }) {\n super(\"deadline_exceeded\", message, opts);\n }\n}\n\n/** Any other upstream provider failure (5xx, transport, unexpected shape). */\nexport class ProviderError extends OdlaAIError {\n constructor(message: string, opts?: { providerStatus?: number; cause?: unknown }) {\n super(\"provider_error\", message, opts);\n }\n}\n","// Normalize a thrown provider-SDK error into the odla-ai error taxonomy. Kept\n// dependency-light: it duck-types the HTTP status / retry-after off the error\n// object rather than importing each SDK's error classes, so it works uniformly\n// across @anthropic-ai/sdk, openai, and @google/genai.\n\nimport {\n AuthError,\n CancelledError,\n ContextWindowError,\n DeadlineExceededError,\n InvalidRequestError,\n OdlaAIError,\n ProviderError,\n RateLimitError,\n type ProviderId,\n} from \"../shape/index\";\n\nfunction statusOf(err: unknown): number | undefined {\n if (err && typeof err === \"object\") {\n const rec = err as Record<string, unknown>;\n for (const k of [\"status\", \"statusCode\", \"code\"]) {\n const v = rec[k];\n if (typeof v === \"number\") return v;\n }\n }\n return undefined;\n}\n\nfunction retryAfterOf(err: unknown): number | undefined {\n if (err && typeof err === \"object\") {\n const headers = (err as { headers?: unknown }).headers;\n if (headers && typeof headers === \"object\") {\n const raw = (headers as Record<string, unknown>)[\"retry-after\"];\n const n = typeof raw === \"string\" ? Number(raw) : typeof raw === \"number\" ? raw : NaN;\n if (Number.isFinite(n)) return n;\n }\n }\n return undefined;\n}\n\nfunction messageOf(err: unknown): string {\n if (err instanceof Error) return err.message;\n if (typeof err === \"string\") return err;\n return \"unknown provider error\";\n}\n\n/** Map any provider error to an OdlaAIError, returning it (does not throw).\n * Re-returns an OdlaAIError unchanged (already normalized). */\nexport function normalizeError(err: unknown, provider: ProviderId, signal?: AbortSignal): OdlaAIError {\n if (err instanceof OdlaAIError) return err;\n const cancellation = cancellationError(signal?.aborted ? signal.reason : err);\n if (signal?.aborted || cancellation) return cancellation ?? new CancelledError(undefined, { cause: err });\n\n const status = statusOf(err);\n const message = messageOf(err);\n const tagged = `[${provider}] ${message}`;\n\n if (status === 401 || status === 403) return new AuthError(tagged, { providerStatus: status, cause: err });\n if (status === 429) {\n return new RateLimitError(tagged, { providerStatus: status, retryAfter: retryAfterOf(err), cause: err });\n }\n if (status === 400 || status === 422) {\n if (/context|token limit|too long|maximum context/i.test(message)) {\n return new ContextWindowError(tagged, { providerStatus: status, cause: err });\n }\n return new InvalidRequestError(tagged, { providerStatus: status, cause: err });\n }\n return new ProviderError(tagged, { providerStatus: status, cause: err });\n}\n\n/**\n * Normalize and throw. The `never` return lets call sites write\n * `catch (e) { mapProviderError(e, id); }` with no fallthrough.\n */\nexport function mapProviderError(err: unknown, provider: ProviderId, signal?: AbortSignal): never {\n throw normalizeError(err, provider, signal);\n}\n\nfunction cancellationError(reason: unknown): CancelledError | DeadlineExceededError | undefined {\n if (reason instanceof DeadlineExceededError || reason instanceof CancelledError) return reason;\n if (!reason || typeof reason !== \"object\") return undefined;\n const rec = reason as { name?: unknown; code?: unknown; message?: unknown };\n if (rec.name === \"TimeoutError\") return new DeadlineExceededError(undefined, { cause: reason });\n if (rec.name === \"AbortError\" || rec.name === \"APIUserAbortError\" || rec.code === \"ABORT_ERR\") {\n return new CancelledError(undefined, { cause: reason });\n }\n return undefined;\n}\n","import { CancelledError, ConfigError, DeadlineExceededError } from \"../shape/errors\";\n\n/** Combine a caller signal with an absolute deadline. Kept provider-neutral so\n * every adapter and the agent loop observes the same cancellation contract. */\nexport function signalWithDeadline(signal?: AbortSignal, deadline?: number): AbortSignal | undefined {\n if (deadline === undefined) return signal;\n if (!Number.isSafeInteger(deadline)) throw new ConfigError(\"deadline must be a finite integer Unix timestamp in milliseconds.\");\n const remaining = deadline - Date.now();\n if (remaining <= 0) {\n const expired = new AbortController();\n expired.abort(new DeadlineExceededError());\n return signal ? AbortSignal.any([signal, expired.signal]) : expired.signal;\n }\n const timeout = AbortSignal.timeout(remaining);\n return signal ? AbortSignal.any([signal, timeout]) : timeout;\n}\n\n/** Fail synchronously before beginning a local side effect when a run was\n * cancelled or its deadline elapsed. Handlers must still observe the signal\n * while in flight; this closes the already-aborted race at invocation time. */\nexport function throwIfAborted(signal?: AbortSignal): void {\n if (!signal?.aborted) return;\n const reason = signal.reason;\n if (reason instanceof DeadlineExceededError || reason instanceof CancelledError) throw reason;\n if (reason && typeof reason === \"object\" && (reason as { name?: unknown }).name === \"TimeoutError\") {\n throw new DeadlineExceededError(undefined, { cause: reason });\n }\n throw new CancelledError(undefined, { cause: reason });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
stringifyBlocks
|
|
3
3
|
} from "./chunk-U7F6VJCV.js";
|
|
4
|
+
import {
|
|
5
|
+
toolInput
|
|
6
|
+
} from "./chunk-OX4TA4ZH.js";
|
|
4
7
|
import {
|
|
5
8
|
CapabilityError,
|
|
6
9
|
emptyUsage,
|
|
7
10
|
mapProviderError,
|
|
8
|
-
normalizeError
|
|
9
|
-
|
|
11
|
+
normalizeError,
|
|
12
|
+
signalWithDeadline
|
|
13
|
+
} from "./chunk-PXXCN2EU.js";
|
|
10
14
|
|
|
11
15
|
// src/providers/google.ts
|
|
12
16
|
import { GoogleGenAI } from "@google/genai";
|
|
@@ -99,7 +103,7 @@ function mapResponse(res, req) {
|
|
|
99
103
|
else if (p.functionCall) {
|
|
100
104
|
sawToolUse = true;
|
|
101
105
|
const name = p.functionCall.name ?? "";
|
|
102
|
-
content.push({ type: "tool_use", id: p.functionCall.id ?? name, name, input: p.functionCall.args ?? {} });
|
|
106
|
+
content.push({ type: "tool_use", id: p.functionCall.id ?? name, name, input: toolInput(p.functionCall.args ?? {}, "google", name) });
|
|
103
107
|
}
|
|
104
108
|
}
|
|
105
109
|
return {
|
|
@@ -191,19 +195,25 @@ var GoogleProvider = class {
|
|
|
191
195
|
this.client = client ?? new GoogleGenAI({ apiKey });
|
|
192
196
|
}
|
|
193
197
|
async create(spec, req) {
|
|
198
|
+
const signal = signalWithDeadline(req.signal, req.deadline);
|
|
194
199
|
try {
|
|
195
|
-
const
|
|
200
|
+
const params = buildParams(spec, req);
|
|
201
|
+
params.config = { ...params.config, abortSignal: signal };
|
|
202
|
+
const res = await this.client.models.generateContent(params);
|
|
196
203
|
return mapResponse(res, req);
|
|
197
204
|
} catch (err) {
|
|
198
|
-
mapProviderError(err, "google");
|
|
205
|
+
mapProviderError(err, "google", signal);
|
|
199
206
|
}
|
|
200
207
|
}
|
|
201
208
|
async *stream(spec, req) {
|
|
209
|
+
const signal = signalWithDeadline(req.signal, req.deadline);
|
|
202
210
|
try {
|
|
203
|
-
const
|
|
211
|
+
const params = buildParams(spec, req);
|
|
212
|
+
params.config = { ...params.config, abortSignal: signal };
|
|
213
|
+
const iter = await this.client.models.generateContentStream(params);
|
|
204
214
|
yield* mapStream(iter, req);
|
|
205
215
|
} catch (err) {
|
|
206
|
-
yield { type: "error", error: normalizeError(err, "google").toShape() };
|
|
216
|
+
yield { type: "error", error: normalizeError(err, "google", signal).toShape() };
|
|
207
217
|
}
|
|
208
218
|
}
|
|
209
219
|
};
|
|
@@ -212,4 +222,4 @@ export {
|
|
|
212
222
|
mapResponse,
|
|
213
223
|
toContents
|
|
214
224
|
};
|
|
215
|
-
//# sourceMappingURL=google-
|
|
225
|
+
//# sourceMappingURL=google-3XV2VWVB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/google.ts","../src/providers/google/request.ts","../src/providers/google/response.ts","../src/providers/google/stream.ts"],"sourcesContent":["// Google (Gemini) adapter, on @google/genai's models.generateContent /\n// generateContentStream. Gemini is natively multimodal (text + image + audio +\n// PDF), so most translation is structural: canonical messages → Gemini\n// `contents` (role \"assistant\" → \"model\"), tools → functionDeclarations,\n// system → config.systemInstruction, tool_use/tool_result → functionCall/\n// functionResponse (name-keyed). The translations live in ./google/request.\n\nimport { GoogleGenAI } from \"@google/genai\";\nimport type { ModelSpec } from \"../shape/capabilities\";\nimport type { Provider } from \"./types\";\nimport { mapProviderError, normalizeError } from \"./errors\";\nimport type { OracleEvent, OracleRequest, OracleResponse } from \"../shape/index\";\nimport { buildParams, toContents } from \"./google/request\";\nimport { mapResponse } from \"./google/response\";\nimport { mapStream } from \"./google/stream\";\nimport { signalWithDeadline } from \"../client/abort\";\n\nexport class GoogleProvider implements Provider {\n readonly id = \"google\" as const;\n private client: GoogleGenAI;\n\n constructor(apiKey: string, client?: GoogleGenAI) {\n this.client = client ?? new GoogleGenAI({ apiKey });\n }\n\n async create(spec: ModelSpec, req: OracleRequest): Promise<OracleResponse> {\n const signal = signalWithDeadline(req.signal, req.deadline);\n try {\n const params = buildParams(spec, req);\n params.config = { ...params.config, abortSignal: signal };\n const res = await this.client.models.generateContent(params);\n return mapResponse(res, req);\n } catch (err) {\n mapProviderError(err, \"google\", signal);\n }\n }\n\n async *stream(spec: ModelSpec, req: OracleRequest): AsyncIterable<OracleEvent> {\n const signal = signalWithDeadline(req.signal, req.deadline);\n try {\n const params = buildParams(spec, req);\n params.config = { ...params.config, abortSignal: signal };\n const iter = await this.client.models.generateContentStream(params);\n yield* mapStream(iter, req);\n } catch (err) {\n yield { type: \"error\", error: normalizeError(err, \"google\", signal).toShape() };\n }\n }\n}\n\n// Re-exported for the test suite (test/adapters.test.ts) and callers that pinned\n// these helpers when they lived in this file.\nexport { toContents, mapResponse };\n","// Google (Gemini) request mapping: canonical shape → generateContent params.\nimport { FunctionCallingConfigMode } from \"@google/genai\";\nimport type { Content, GenerateContentParameters, Part } from \"@google/genai\";\nimport type { ModelSpec } from \"../../shape/capabilities\";\nimport { CapabilityError, type OracleContentBlock, type OracleRequest, type ToolChoice } from \"../../shape/index\";\nimport { stringifyBlocks } from \"../blocks\";\n\nexport function buildParams(spec: ModelSpec, req: OracleRequest): GenerateContentParameters {\n const config: Record<string, unknown> = { maxOutputTokens: req.maxTokens };\n\n if (req.system !== undefined) {\n config.systemInstruction = typeof req.system === \"string\" ? req.system : req.system.map((b) => b.text).join(\"\");\n }\n if (req.temperature !== undefined) config.temperature = req.temperature;\n if (req.stopSequences && req.stopSequences.length > 0) config.stopSequences = req.stopSequences;\n\n const tools = buildTools(req);\n if (tools) config.tools = tools;\n\n const toolConfig = mapToolChoice(req.toolChoice);\n if (toolConfig) config.toolConfig = toolConfig;\n\n if (req.thinking === \"adaptive\" && spec.capabilities.thinking) {\n config.thinkingConfig = { thinkingBudget: -1 }; // -1 = dynamic (\"let the model decide\")\n }\n\n if (req.responseFormat && spec.capabilities.structuredOutput) {\n config.responseMimeType = \"application/json\";\n config.responseSchema = req.responseFormat.schema;\n }\n\n if (req.providerExtras) Object.assign(config, req.providerExtras);\n\n return { model: spec.nativeId, contents: toContents(req), config } as unknown as GenerateContentParameters;\n}\n\nexport function toContents(req: OracleRequest): Content[] {\n return req.messages.map((m) => ({\n role: m.role === \"assistant\" ? \"model\" : \"user\",\n parts: (typeof m.content === \"string\" ? [{ type: \"text\" as const, text: m.content }] : m.content).map(toPart),\n }));\n}\n\nfunction toPart(b: OracleContentBlock): Part {\n switch (b.type) {\n case \"text\":\n return { text: b.text };\n case \"image\":\n if (b.source.type !== \"base64\") throw new CapabilityError(\"Google image input requires base64 data, not a URL.\");\n return { inlineData: { mimeType: b.source.mediaType, data: b.source.data } };\n case \"audio\":\n if (b.source.type !== \"base64\") throw new CapabilityError(\"Google audio input requires base64 data, not a URL.\");\n return { inlineData: { mimeType: b.source.mediaType, data: b.source.data } };\n case \"document\":\n if (b.source.type !== \"base64\") throw new CapabilityError(\"Google document input requires base64 data, not a URL.\");\n return { inlineData: { mimeType: \"application/pdf\", data: b.source.data } };\n case \"tool_use\":\n return { functionCall: { name: b.name, args: b.input } };\n case \"tool_result\":\n return {\n functionResponse: {\n // Gemini correlates by name; the canonical tool_use id is keyed to it.\n name: b.toolUseId,\n response: { result: typeof b.content === \"string\" ? b.content : stringifyBlocks(b.content) },\n },\n };\n case \"thinking\":\n return { text: \"\" }; // no thinking input channel; drop content\n }\n}\n\nfunction buildTools(req: OracleRequest): unknown[] | undefined {\n const tools: unknown[] = [];\n if (req.tools && req.tools.length > 0) {\n tools.push({\n functionDeclarations: req.tools.map((t) => ({\n name: t.name,\n description: t.description,\n parameters: t.inputSchema,\n })),\n });\n }\n if (req.webSearch) tools.push({ googleSearch: {} });\n return tools.length > 0 ? tools : undefined;\n}\n\nfunction mapToolChoice(tc: ToolChoice | undefined): unknown | undefined {\n if (tc === undefined || tc === \"auto\") return undefined;\n if (tc === \"none\") return { functionCallingConfig: { mode: FunctionCallingConfigMode.NONE } };\n if (tc === \"any\") return { functionCallingConfig: { mode: FunctionCallingConfigMode.ANY } };\n return { functionCallingConfig: { mode: FunctionCallingConfigMode.ANY, allowedFunctionNames: [tc.name] } };\n}\n","// Google (Gemini) response mapping. mapFinish / mapUsage are also used by the\n// stream mapper (kept here so the entry ↔ stream edge stays one-directional).\nimport type { GenerateContentResponse } from \"@google/genai\";\nimport type { OracleContentBlock, OracleRequest, OracleResponse, OracleStopReason, OracleUsage } from \"../../shape/index\";\nimport { toolInput } from \"../tool-input\";\n\nexport function mapResponse(res: GenerateContentResponse, req: OracleRequest): OracleResponse {\n const candidate = res.candidates?.[0];\n const parts = candidate?.content?.parts ?? [];\n const content: OracleContentBlock[] = [];\n let sawToolUse = false;\n\n for (const p of parts) {\n if (p.text) content.push({ type: \"text\", text: p.text });\n else if (p.functionCall) {\n sawToolUse = true;\n const name = p.functionCall.name ?? \"\";\n content.push({ type: \"tool_use\", id: p.functionCall.id ?? name, name, input: toolInput(p.functionCall.args ?? {}, \"google\", name) });\n }\n }\n\n return {\n id: res.responseId ?? \"\",\n model: req.model,\n provider: \"google\",\n role: \"assistant\",\n content,\n stopReason: sawToolUse ? \"tool_use\" : mapFinish(candidate?.finishReason),\n usage: mapUsage(res),\n };\n}\n\nexport function mapFinish(reason: string | null | undefined): OracleStopReason {\n switch (reason) {\n case \"STOP\":\n return \"end_turn\";\n case \"MAX_TOKENS\":\n return \"max_tokens\";\n case \"SAFETY\":\n case \"RECITATION\":\n case \"BLOCKLIST\":\n case \"PROHIBITED_CONTENT\":\n case \"SPII\":\n return \"refusal\";\n default:\n return \"end_turn\";\n }\n}\n\nexport function mapUsage(res: GenerateContentResponse): OracleUsage {\n const u = res.usageMetadata;\n const usage: OracleUsage = {\n inputTokens: u?.promptTokenCount ?? 0,\n outputTokens: u?.candidatesTokenCount ?? 0,\n };\n if (u?.cachedContentTokenCount != null) usage.cacheReadTokens = u.cachedContentTokenCount;\n return usage;\n}\n","// Google (Gemini) stream mapping onto the canonical event vocabulary.\nimport type { GenerateContentResponse } from \"@google/genai\";\nimport { emptyUsage, type OracleEvent, type OracleRequest, type OracleStopReason } from \"../../shape/index\";\nimport { mapFinish, mapUsage } from \"./response\";\n\nexport async function* mapStream(iter: AsyncIterable<GenerateContentResponse>, req: OracleRequest): AsyncIterable<OracleEvent> {\n let started = false;\n let textOpen = false;\n let nextBlockIndex = 1;\n let stopReason: OracleStopReason = \"end_turn\";\n const usage = emptyUsage();\n\n for await (const chunk of iter) {\n if (!started) {\n started = true;\n yield {\n type: \"message_start\",\n message: { id: chunk.responseId ?? \"\", model: req.model, provider: \"google\", role: \"assistant\", content: [], usage: emptyUsage() },\n };\n }\n\n const candidate = chunk.candidates?.[0];\n for (const p of candidate?.content?.parts ?? []) {\n if (p.text) {\n if (!textOpen) {\n textOpen = true;\n yield { type: \"content_block_start\", index: 0, contentBlock: { type: \"text\", text: \"\" } };\n }\n yield { type: \"content_block_delta\", index: 0, delta: { type: \"text_delta\", text: p.text } };\n } else if (p.functionCall) {\n const idx = nextBlockIndex++;\n const name = p.functionCall.name ?? \"\";\n yield { type: \"content_block_start\", index: idx, contentBlock: { type: \"tool_use\", id: p.functionCall.id ?? name, name, input: {} } };\n yield { type: \"content_block_delta\", index: idx, delta: { type: \"input_json_delta\", partialJson: JSON.stringify(p.functionCall.args ?? {}) } };\n yield { type: \"content_block_stop\", index: idx };\n stopReason = \"tool_use\";\n }\n }\n\n if (chunk.usageMetadata) {\n const u = mapUsage(chunk);\n usage.inputTokens = u.inputTokens; // Gemini reports cumulative counts\n usage.outputTokens = u.outputTokens;\n if (u.cacheReadTokens != null) usage.cacheReadTokens = u.cacheReadTokens;\n }\n if (candidate?.finishReason && stopReason !== \"tool_use\") stopReason = mapFinish(candidate.finishReason);\n }\n\n if (textOpen) yield { type: \"content_block_stop\", index: 0 };\n yield { type: \"message_delta\", delta: { stopReason }, usage };\n yield { type: \"message_stop\" };\n}\n"],"mappings":"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","names":[]}
|