@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
|
@@ -1,13 +1,18 @@
|
|
|
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,
|
|
9
|
+
ToolInputError,
|
|
6
10
|
addUsage,
|
|
7
11
|
emptyUsage,
|
|
8
12
|
mapProviderError,
|
|
9
|
-
normalizeError
|
|
10
|
-
|
|
13
|
+
normalizeError,
|
|
14
|
+
signalWithDeadline
|
|
15
|
+
} from "./chunk-PXXCN2EU.js";
|
|
11
16
|
|
|
12
17
|
// src/providers/openai.ts
|
|
13
18
|
import OpenAI from "openai";
|
|
@@ -153,9 +158,10 @@ function parseArgs(raw) {
|
|
|
153
158
|
if (!raw) return {};
|
|
154
159
|
try {
|
|
155
160
|
const parsed = JSON.parse(raw);
|
|
156
|
-
return parsed
|
|
157
|
-
} catch {
|
|
158
|
-
|
|
161
|
+
return toolInput(parsed, "openai", "function");
|
|
162
|
+
} catch (error) {
|
|
163
|
+
if (error instanceof ToolInputError) throw error;
|
|
164
|
+
throw new ToolInputError("[openai] tool returned malformed JSON arguments.", { cause: error });
|
|
159
165
|
}
|
|
160
166
|
}
|
|
161
167
|
function mapFinish(reason) {
|
|
@@ -237,23 +243,27 @@ var OpenAIProvider = class {
|
|
|
237
243
|
this.client = client ?? new OpenAI({ apiKey });
|
|
238
244
|
}
|
|
239
245
|
async create(spec, req) {
|
|
246
|
+
const signal = signalWithDeadline(req.signal, req.deadline);
|
|
240
247
|
try {
|
|
241
248
|
const res = await this.client.chat.completions.create(
|
|
242
|
-
buildParams(spec, req, false)
|
|
249
|
+
buildParams(spec, req, false),
|
|
250
|
+
{ signal }
|
|
243
251
|
);
|
|
244
252
|
return mapResponse(res, req);
|
|
245
253
|
} catch (err) {
|
|
246
|
-
mapProviderError(err, "openai");
|
|
254
|
+
mapProviderError(err, "openai", signal);
|
|
247
255
|
}
|
|
248
256
|
}
|
|
249
257
|
async *stream(spec, req) {
|
|
258
|
+
const signal = signalWithDeadline(req.signal, req.deadline);
|
|
250
259
|
try {
|
|
251
260
|
const stream = await this.client.chat.completions.create(
|
|
252
|
-
buildParams(spec, req, true)
|
|
261
|
+
buildParams(spec, req, true),
|
|
262
|
+
{ signal }
|
|
253
263
|
);
|
|
254
264
|
yield* mapStream(stream, req);
|
|
255
265
|
} catch (err) {
|
|
256
|
-
yield { type: "error", error: normalizeError(err, "openai").toShape() };
|
|
266
|
+
yield { type: "error", error: normalizeError(err, "openai", signal).toShape() };
|
|
257
267
|
}
|
|
258
268
|
}
|
|
259
269
|
};
|
|
@@ -263,4 +273,4 @@ export {
|
|
|
263
273
|
parseArgs,
|
|
264
274
|
toOpenAIMessages
|
|
265
275
|
};
|
|
266
|
-
//# sourceMappingURL=openai-
|
|
276
|
+
//# sourceMappingURL=openai-NTQCF577.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/openai.ts","../src/providers/openai/request.ts","../src/providers/openai/response.ts","../src/providers/openai/stream.ts"],"sourcesContent":["// OpenAI adapter, on the Chat Completions API (stable across the current model\n// lineup, multimodal, tool calls, streaming). The interesting translations —\n// system folded into a leading message, JSON-string tool `arguments` parsed to an\n// object, and tool_result blocks re-emitted as role:\"tool\" messages — happen in\n// ./openai/request so the rest of odla-ai only ever sees the canonical shape.\n\nimport OpenAI from \"openai\";\nimport type { ModelSpec } from \"../shape/capabilities\";\nimport type { Provider } from \"./types\";\nimport { normalizeError, mapProviderError } from \"./errors\";\nimport type { OracleEvent, OracleRequest, OracleResponse } from \"../shape/index\";\nimport { buildParams, toOpenAIMessages } from \"./openai/request\";\nimport { mapResponse, parseArgs } from \"./openai/response\";\nimport { mapStream } from \"./openai/stream\";\nimport { signalWithDeadline } from \"../client/abort\";\n\ntype ChatParams = OpenAI.Chat.Completions.ChatCompletionCreateParams;\n\nexport class OpenAIProvider implements Provider {\n readonly id = \"openai\" as const;\n private client: OpenAI;\n\n constructor(apiKey: string, client?: OpenAI) {\n this.client = client ?? new OpenAI({ apiKey });\n }\n\n async create(spec: ModelSpec, req: OracleRequest): Promise<OracleResponse> {\n const signal = signalWithDeadline(req.signal, req.deadline);\n try {\n const res = (await this.client.chat.completions.create(\n buildParams(spec, req, false) as unknown as ChatParams,\n { signal },\n )) as OpenAI.Chat.Completions.ChatCompletion;\n return mapResponse(res, req);\n } catch (err) {\n mapProviderError(err, \"openai\", 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 stream = (await this.client.chat.completions.create(\n buildParams(spec, req, true) as unknown as ChatParams,\n { signal },\n )) as unknown as AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>;\n yield* mapStream(stream, req);\n } catch (err) {\n yield { type: \"error\", error: normalizeError(err, \"openai\", 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 { toOpenAIMessages, mapResponse, parseArgs };\n","// OpenAI request mapping: canonical shape → Chat Completions params.\nimport type OpenAI from \"openai\";\nimport type { ModelSpec } from \"../../shape/capabilities\";\nimport {\n CapabilityError,\n type AudioMediaType,\n type Effort,\n type OracleContentBlock,\n type OracleRequest,\n type ToolChoice,\n} from \"../../shape/index\";\nimport { stringifyBlocks } from \"../blocks\";\n\ntype ChatMessage = OpenAI.Chat.Completions.ChatCompletionMessageParam;\n\nexport function buildParams(spec: ModelSpec, req: OracleRequest, stream: boolean): Record<string, unknown> {\n const params: Record<string, unknown> = {\n model: spec.nativeId,\n messages: toOpenAIMessages(req),\n max_completion_tokens: req.maxTokens,\n };\n\n const tools = buildTools(req);\n if (tools) params.tools = tools;\n\n const toolChoice = mapToolChoice(req.toolChoice);\n if (toolChoice !== undefined) params.tool_choice = toolChoice;\n\n if (req.stopSequences && req.stopSequences.length > 0) params.stop = req.stopSequences;\n\n // Reasoning (effort) models reject a custom temperature; only send it elsewhere.\n if (req.temperature !== undefined && !spec.capabilities.effort) params.temperature = req.temperature;\n\n if (req.effort && spec.capabilities.effort) params.reasoning_effort = reasoningEffort(req.effort);\n\n if (req.responseFormat && spec.capabilities.structuredOutput) {\n params.response_format = {\n type: \"json_schema\",\n json_schema: { name: req.responseFormat.name ?? \"response\", schema: req.responseFormat.schema },\n };\n }\n\n // Search-capable chat models (e.g. gpt-5-search-api) take web_search_options;\n // the capability gate keeps this off models without webSearch.\n if (req.webSearch) params.web_search_options = { search_context_size: \"medium\" };\n\n if (stream) {\n params.stream = true;\n params.stream_options = { include_usage: true };\n }\n\n if (req.providerExtras) Object.assign(params, req.providerExtras);\n return params;\n}\n\n/** Expand canonical messages into OpenAI's flat message list: system folded to a\n * leading message, tool_result blocks lifted to role:\"tool\" messages, tool_use\n * blocks emitted as assistant tool_calls. */\nexport function toOpenAIMessages(req: OracleRequest): ChatMessage[] {\n const out: ChatMessage[] = [];\n\n if (req.system !== undefined) {\n const text = typeof req.system === \"string\" ? req.system : req.system.map((b) => b.text).join(\"\");\n out.push({ role: \"system\", content: text });\n }\n\n for (const msg of req.messages) {\n const blocks = typeof msg.content === \"string\" ? [{ type: \"text\" as const, text: msg.content }] : msg.content;\n\n if (msg.role === \"assistant\") {\n const textParts: string[] = [];\n const toolCalls: OpenAI.Chat.Completions.ChatCompletionMessageToolCall[] = [];\n for (const b of blocks) {\n if (b.type === \"text\") textParts.push(b.text);\n else if (b.type === \"tool_use\") {\n toolCalls.push({\n id: b.id,\n type: \"function\",\n function: { name: b.name, arguments: JSON.stringify(b.input) },\n } as OpenAI.Chat.Completions.ChatCompletionMessageToolCall);\n }\n // thinking blocks are dropped — OpenAI has no thinking input channel.\n }\n const assistant: Record<string, unknown> = { role: \"assistant\", content: textParts.join(\"\") || null };\n if (toolCalls.length > 0) assistant.tool_calls = toolCalls;\n out.push(assistant as unknown as ChatMessage);\n continue;\n }\n\n // user turn: tool_result blocks become tool messages; the rest becomes one\n // user message with multimodal content parts.\n const toolResults = blocks.filter((b) => b.type === \"tool_result\");\n for (const b of toolResults) {\n if (b.type !== \"tool_result\") continue;\n out.push({\n role: \"tool\",\n tool_call_id: b.toolUseId,\n content: typeof b.content === \"string\" ? b.content : stringifyBlocks(b.content),\n });\n }\n const rest = blocks.filter((b) => b.type !== \"tool_result\");\n if (rest.length > 0) out.push({ role: \"user\", content: rest.map(userPart) });\n }\n\n return out;\n}\n\ntype UserPart = OpenAI.Chat.Completions.ChatCompletionContentPart;\n\nfunction userPart(b: OracleContentBlock): UserPart {\n switch (b.type) {\n case \"text\":\n return { type: \"text\", text: b.text };\n case \"image\":\n return {\n type: \"image_url\",\n image_url: {\n url: b.source.type === \"url\" ? b.source.url : `data:${b.source.mediaType};base64,${b.source.data}`,\n },\n };\n case \"audio\": {\n if (b.source.type !== \"base64\") {\n throw new CapabilityError(\"OpenAI audio input requires base64 data, not a URL.\");\n }\n return {\n type: \"input_audio\",\n input_audio: { data: b.source.data, format: audioFormat(b.source.mediaType) },\n } as UserPart;\n }\n default:\n // document/tool_* are excluded from OpenAI user content (documents are\n // gated off by capabilities; tool_result handled separately).\n return { type: \"text\", text: stringifyBlocks([b]) };\n }\n}\n\nfunction audioFormat(m: AudioMediaType): \"wav\" | \"mp3\" {\n if (m === \"audio/wav\") return \"wav\";\n if (m === \"audio/mp3\" || m === \"audio/mpeg\") return \"mp3\";\n throw new CapabilityError(`OpenAI audio input supports wav and mp3, not \"${m}\".`);\n}\n\nfunction buildTools(req: OracleRequest): unknown[] | undefined {\n if (!req.tools || req.tools.length === 0) return undefined;\n return req.tools.map((t) => ({\n type: \"function\",\n function: { name: t.name, description: t.description, parameters: t.inputSchema },\n }));\n}\n\nfunction mapToolChoice(tc: ToolChoice | undefined): unknown | undefined {\n if (tc === undefined) return undefined;\n if (tc === \"auto\") return \"auto\";\n if (tc === \"none\") return \"none\";\n if (tc === \"any\") return \"required\";\n return { type: \"function\", function: { name: tc.name } };\n}\n\nfunction reasoningEffort(e: Effort): \"low\" | \"medium\" | \"high\" {\n if (e === \"low\") return \"low\";\n if (e === \"medium\") return \"medium\";\n return \"high\"; // high / xhigh / max → high\n}\n","// OpenAI response mapping: Chat Completion → canonical response. mapFinish /\n// mapUsage are also consumed by the stream mapper (kept here, not in the entry,\n// so the entry ↔ stream dependency stays one-directional — no import cycle).\nimport type OpenAI from \"openai\";\nimport {\n type OracleContentBlock,\n type OracleRequest,\n type OracleResponse,\n type OracleStopReason,\n type OracleUsage,\n} from \"../../shape/index\";\nimport { ToolInputError } from \"../../shape/index\";\nimport { toolInput } from \"../tool-input\";\n\nexport function mapResponse(res: OpenAI.Chat.Completions.ChatCompletion, req: OracleRequest): OracleResponse {\n const choice = res.choices[0];\n const content: OracleContentBlock[] = [];\n const message = choice?.message;\n\n if (message?.content) content.push({ type: \"text\", text: message.content });\n for (const call of message?.tool_calls ?? []) {\n if (call.type !== \"function\") continue;\n content.push({ type: \"tool_use\", id: call.id, name: call.function.name, input: parseArgs(call.function.arguments) });\n }\n\n return {\n id: res.id,\n model: req.model,\n provider: \"openai\",\n role: \"assistant\",\n content,\n stopReason: mapFinish(choice?.finish_reason),\n usage: mapUsage(res.usage),\n };\n}\n\n/** OpenAI hands tool arguments back as a JSON *string*; malformed JSON fails\n * closed so a tool handler never receives invented empty arguments. */\nexport function parseArgs(raw: string | undefined): Record<string, unknown> {\n if (!raw) return {};\n try {\n const parsed = JSON.parse(raw);\n return toolInput(parsed, \"openai\", \"function\");\n } catch (error) {\n if (error instanceof ToolInputError) throw error;\n throw new ToolInputError(\"[openai] tool returned malformed JSON arguments.\", { cause: error });\n }\n}\n\nexport function mapFinish(reason: string | null | undefined): OracleStopReason {\n switch (reason) {\n case \"stop\":\n return \"end_turn\";\n case \"length\":\n return \"max_tokens\";\n case \"tool_calls\":\n case \"function_call\":\n return \"tool_use\";\n case \"content_filter\":\n return \"refusal\";\n default:\n return \"end_turn\";\n }\n}\n\nexport function mapUsage(u: OpenAI.Completions.CompletionUsage | undefined): OracleUsage {\n const usage: OracleUsage = { inputTokens: u?.prompt_tokens ?? 0, outputTokens: u?.completion_tokens ?? 0 };\n const cached = u?.prompt_tokens_details?.cached_tokens;\n if (cached != null) usage.cacheReadTokens = cached;\n return usage;\n}\n","// OpenAI stream mapping onto the canonical event vocabulary.\nimport type OpenAI from \"openai\";\nimport { addUsage, emptyUsage, type OracleEvent, type OracleRequest, type OracleStopReason } from \"../../shape/index\";\nimport { mapFinish, mapUsage } from \"./response\";\n\n/** Map an OpenAI chat stream onto the canonical event vocabulary. OpenAI has no\n * explicit block start/stop, so we synthesize them: a text channel (index 0) and\n * one channel per tool_call index, opened lazily and closed at finish. */\nexport async function* mapStream(\n stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>,\n req: OracleRequest,\n): AsyncIterable<OracleEvent> {\n let started = false;\n let textOpen = false;\n const toolIndex = new Map<number, number>(); // openai tool_call index → our block index\n let nextBlockIndex = 1; // 0 reserved for text\n let stopReason: OracleStopReason = \"end_turn\";\n const usage = emptyUsage();\n\n for await (const chunk of stream) {\n if (!started) {\n started = true;\n yield {\n type: \"message_start\",\n message: { id: chunk.id, model: req.model, provider: \"openai\", role: \"assistant\", content: [], usage: emptyUsage() },\n };\n }\n\n if (chunk.usage) addUsage(usage, mapUsage(chunk.usage));\n\n const choice = chunk.choices[0];\n const delta = choice?.delta;\n\n if (delta?.content) {\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: delta.content } };\n }\n\n for (const tc of delta?.tool_calls ?? []) {\n let blockIndex = toolIndex.get(tc.index);\n if (blockIndex === undefined) {\n blockIndex = nextBlockIndex++;\n toolIndex.set(tc.index, blockIndex);\n yield {\n type: \"content_block_start\",\n index: blockIndex,\n contentBlock: { type: \"tool_use\", id: tc.id ?? \"\", name: tc.function?.name ?? \"\", input: {} },\n };\n }\n if (tc.function?.arguments) {\n yield { type: \"content_block_delta\", index: blockIndex, delta: { type: \"input_json_delta\", partialJson: tc.function.arguments } };\n }\n }\n\n if (choice?.finish_reason) stopReason = mapFinish(choice.finish_reason);\n }\n\n if (textOpen) yield { type: \"content_block_stop\", index: 0 };\n for (const blockIndex of toolIndex.values()) yield { type: \"content_block_stop\", index: blockIndex };\n yield { type: \"message_delta\", delta: { stopReason }, usage };\n yield { type: \"message_stop\" };\n}\n"],"mappings":"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","names":[]}
|
package/llms.txt
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
# @odla-ai/ai — LLM context
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
>
|
|
5
|
-
>
|
|
3
|
+
> EARLY ACCESS — pre-1.0. Agents work from bounded runbooks; humans approve
|
|
4
|
+
> credentials, production changes, releases, and merges. APIs and exact package
|
|
5
|
+
> availability can change. Review documented guarantees and limitations; this
|
|
6
|
+
> software is MIT-licensed and provided without warranty.
|
|
6
7
|
|
|
7
8
|
One general-purpose interface for AI inference across Anthropic (Claude), OpenAI
|
|
8
9
|
(GPT), and Google (Gemini) — text, image, and audio — plus a tool-use agent
|
|
9
10
|
engine and an eval harness. Library-only: import in-process, bring your own keys.
|
|
10
|
-
|
|
11
|
+
Targets Node 20+ and Cloudflare Workers. Provider adapters are runtime-lazy, but
|
|
12
|
+
all provider SDKs are package dependencies. Never expose long-lived keys in a browser.
|
|
11
13
|
|
|
12
14
|
## Install
|
|
13
15
|
|
|
14
16
|
npm i @odla-ai/ai
|
|
15
|
-
# provider SDKs are peer-lazy-loaded; install those you use:
|
|
16
|
-
# @anthropic-ai/sdk openai @google/genai
|
|
17
17
|
# optional, for odla-db-backed storage + secrets:
|
|
18
18
|
# @odla-ai/db
|
|
19
19
|
|
|
@@ -27,7 +27,8 @@ Isomorphic (Node 20+, Cloudflare Workers, browser). Provider SDKs are lazy-loade
|
|
|
27
27
|
- `ai.stream(input)` → async iterable of normalized events: message_start →
|
|
28
28
|
content_block_start → content_block_delta {text_delta|thinking_delta|input_json_delta}
|
|
29
29
|
→ content_block_stop → message_delta → message_stop (plus `error`).
|
|
30
|
-
- `ai.extract<T>({ model, user, tool })` → forced tool call; returns
|
|
30
|
+
- `ai.extract<T>({ model, user, tool })` → forced tool call; returns an object
|
|
31
|
+
validated against the declared JSON Schema. Unsupported assertions fail closed.
|
|
31
32
|
- `ai.search({ model, user })` → web-search-grounded prose.
|
|
32
33
|
|
|
33
34
|
## Content blocks (multimodal)
|
|
@@ -46,7 +47,15 @@ audio block to a Claude model throws CapabilityError before any network call.
|
|
|
46
47
|
// run.finalText, run.toolCalls, run.usage, run.steps, run.stoppedReason
|
|
47
48
|
|
|
48
49
|
Tools may carry taint labels (outputTaint / acceptsTaint) for a CaMeL-style
|
|
49
|
-
prompt-injection gate. Personas may carry a MemoryScope.
|
|
50
|
+
prompt-injection gate. Personas may carry a MemoryScope. Tool inputs are schema-
|
|
51
|
+
validated before handlers run. Pass signal/deadline and a run budget
|
|
52
|
+
({maxInputTokens,maxOutputTokens,maxTotalTokens,maxToolCalls}) to bound follow-up
|
|
53
|
+
effects and requested output. Input and total limits are post-turn ceilings:
|
|
54
|
+
provider usage is unavailable before the first response, so they cannot
|
|
55
|
+
tokenizer-bound the first prompt. Output and remaining-total limits cap the next
|
|
56
|
+
request's maxTokens. Token limits must be positive integers; maxToolCalls may be
|
|
57
|
+
zero. Budget exits pair every requested tool_use with an error tool_result before
|
|
58
|
+
memory is persisted.
|
|
50
59
|
|
|
51
60
|
## Skills
|
|
52
61
|
|
|
@@ -73,14 +82,17 @@ device-authorization handshake for a scoped, revocable token, then provisions.
|
|
|
73
82
|
import { requestToken, init as odlaInit } from "@odla-ai/db";
|
|
74
83
|
import { provisionAgentApp, odlaDbKeyResolver, OdlaDbMemory, persistRun, init } from "@odla-ai/ai";
|
|
75
84
|
|
|
76
|
-
|
|
77
|
-
const
|
|
85
|
+
const ODLA_PLATFORM = "https://odla.ai";
|
|
86
|
+
const ODLA_DB_URL = "https://db.odla.ai";
|
|
87
|
+
|
|
88
|
+
// 1. Handshake: the human approves a short code at odla.ai.
|
|
89
|
+
const { token } = await requestToken({ endpoint: ODLA_PLATFORM, onCode: ({ userCode }) => show(userCode) });
|
|
78
90
|
|
|
79
91
|
// 2. Provision: create app, mint app key, push agent schema, store BYO provider keys as secrets.
|
|
80
|
-
const { appId, appKey } = await provisionAgentApp({ endpoint:
|
|
92
|
+
const { appId, appKey } = await provisionAgentApp({ endpoint: ODLA_PLATFORM, token, providerKeys: { openai: OPENAI_KEY } });
|
|
81
93
|
|
|
82
94
|
// 3. Runtime: read keys from odla-db secrets; persist memory + run traces.
|
|
83
|
-
const db = odlaInit({ appId, adminToken: appKey, endpoint:
|
|
95
|
+
const db = odlaInit({ appId, adminToken: appKey, endpoint: ODLA_DB_URL });
|
|
84
96
|
const ai = init({ resolveKey: odlaDbKeyResolver(db) });
|
|
85
97
|
const persona = { name: "assistant", model: "gpt-5", memory: new OdlaDbMemory({ db, sessionId: "user-42" }) };
|
|
86
98
|
const run = await runAgent(ai, persona, { input: "hello" });
|
|
@@ -106,16 +118,22 @@ env's odla-db tenant secrets, write-only for operators). One call reads both:
|
|
|
106
118
|
platform: "https://odla.ai", appId: APP_ID, env: "prod", db });
|
|
107
119
|
// ai.chat / ai.stream / ai.extract — provider/model from public-config
|
|
108
120
|
// (cached ~60s, so Studio changes apply without redeploys); the key is
|
|
109
|
-
// fetched from the vault
|
|
121
|
+
// fetched from the vault via a finite-TTL odlaDbKeyResolver.
|
|
110
122
|
|
|
111
123
|
The only secret the Worker carries is its odla-db app key. Rotating the LLM
|
|
112
|
-
key = writing the secret again;
|
|
124
|
+
key = writing the secret again; the default warm-isolate key TTL is 60 seconds
|
|
125
|
+
and cacheVersion provides immediate invalidation. Provider SDK clients are
|
|
126
|
+
fingerprinted, bounded to one generation per provider, and expire after 60
|
|
127
|
+
seconds. The platform provider is enforced: models from other providers are
|
|
128
|
+
absent from the facade catalog. If no default model is configured, each call
|
|
129
|
+
must name a model for that provider. Only public config is shared; tenant-bound
|
|
130
|
+
Ai facades are not cached globally.
|
|
113
131
|
|
|
114
132
|
## Errors
|
|
115
133
|
|
|
116
134
|
Every error is an OdlaAIError subclass with a stable `code` — branch on err.code:
|
|
117
135
|
config, auth, rate_limit, capability_unsupported, context_window, invalid_request,
|
|
118
|
-
tool_input_invalid, provider_error.
|
|
136
|
+
tool_input_invalid, cancelled, deadline_exceeded, provider_error.
|
|
119
137
|
|
|
120
138
|
## Models
|
|
121
139
|
|
|
@@ -124,6 +142,7 @@ tool_input_invalid, provider_error.
|
|
|
124
142
|
- `claude-fable-5` (anthropic) — text, image, tools, thinking, effort, web-search, structured
|
|
125
143
|
- `claude-haiku-4-5` (anthropic) — text, image, tools, structured
|
|
126
144
|
- `gpt-5.5` (openai) — text, image, tools, effort, structured
|
|
145
|
+
- `gpt-5.4-mini` (openai) — text, image, tools, effort, structured
|
|
127
146
|
- `gpt-5` (openai) — text, image, tools, effort, structured
|
|
128
147
|
- `gpt-5-mini` (openai) — text, image, tools, effort, structured
|
|
129
148
|
- `gpt-5-nano` (openai) — text, image, tools, effort, structured
|
package/package.json
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@odla-ai/ai",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "One general-purpose interface for AI inference across Anthropic (Claude), OpenAI, and Google (Gemini) \u2014 text, image, and audio \u2014 plus a tool-use agent engine and eval harness
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "One general-purpose interface for AI inference across Anthropic (Claude), OpenAI, and Google (Gemini) \u2014 text, image, and audio \u2014 plus a tool-use agent engine and eval harness for Node 20+ and Cloudflare Workers.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"homepage": "https://odla.ai/docs/packages/ai",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/cory/odla-ai.git",
|
|
10
|
+
"directory": "packages/ai"
|
|
11
|
+
},
|
|
6
12
|
"type": "module",
|
|
7
13
|
"main": "./dist/index.cjs",
|
|
8
14
|
"module": "./dist/index.js",
|
|
@@ -24,7 +30,8 @@
|
|
|
24
30
|
"node": ">=20"
|
|
25
31
|
},
|
|
26
32
|
"publishConfig": {
|
|
27
|
-
"access": "public"
|
|
33
|
+
"access": "public",
|
|
34
|
+
"provenance": false
|
|
28
35
|
},
|
|
29
36
|
"keywords": [
|
|
30
37
|
"odla",
|
|
@@ -50,7 +57,7 @@
|
|
|
50
57
|
"smoke": "tsx scripts/smoke.ts",
|
|
51
58
|
"gen:llms": "tsx -e \"import('./src/index.ts').then(m => process.stdout.write(m.generateLlmsTxt()))\" > llms.txt",
|
|
52
59
|
"check:publish": "tsx scripts/check-publish-safety.ts",
|
|
53
|
-
"prepublishOnly": "npm run build && npm run check:publish"
|
|
60
|
+
"prepublishOnly": "npm run build && npm run gen:llms && npm run check:publish"
|
|
54
61
|
},
|
|
55
62
|
"dependencies": {
|
|
56
63
|
"@anthropic-ai/sdk": "^0.70.0",
|
|
@@ -58,7 +65,7 @@
|
|
|
58
65
|
"openai": "^6.0.0"
|
|
59
66
|
},
|
|
60
67
|
"peerDependencies": {
|
|
61
|
-
"@odla-ai/db": ">=0.2.0 <0.
|
|
68
|
+
"@odla-ai/db": ">=0.2.0 <0.7.0"
|
|
62
69
|
},
|
|
63
70
|
"peerDependenciesMeta": {
|
|
64
71
|
"@odla-ai/db": {
|
|
@@ -1 +0,0 @@
|
|
|
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\";\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","// 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\";\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: (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\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":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shape/usage.ts","../src/shape/errors.ts","../src/providers/errors.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\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 | \"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/** 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 ContextWindowError,\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): OdlaAIError {\n if (err instanceof OdlaAIError) return 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): never {\n throw normalizeError(err, provider);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
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\";\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 try {\n const res = await this.client.models.generateContent(buildParams(spec, req));\n return mapResponse(res, req);\n } catch (err) {\n mapProviderError(err, \"google\");\n }\n }\n\n async *stream(spec: ModelSpec, req: OracleRequest): AsyncIterable<OracleEvent> {\n try {\n const iter = await this.client.models.generateContentStream(buildParams(spec, req));\n yield* mapStream(iter, req);\n } catch (err) {\n yield { type: \"error\", error: normalizeError(err, \"google\").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\";\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: (p.functionCall.args ?? {}) as Record<string, unknown> });\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":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/providers/openai.ts","../src/providers/openai/request.ts","../src/providers/openai/response.ts","../src/providers/openai/stream.ts"],"sourcesContent":["// OpenAI adapter, on the Chat Completions API (stable across the current model\n// lineup, multimodal, tool calls, streaming). The interesting translations —\n// system folded into a leading message, JSON-string tool `arguments` parsed to an\n// object, and tool_result blocks re-emitted as role:\"tool\" messages — happen in\n// ./openai/request so the rest of odla-ai only ever sees the canonical shape.\n\nimport OpenAI from \"openai\";\nimport type { ModelSpec } from \"../shape/capabilities\";\nimport type { Provider } from \"./types\";\nimport { normalizeError, mapProviderError } from \"./errors\";\nimport type { OracleEvent, OracleRequest, OracleResponse } from \"../shape/index\";\nimport { buildParams, toOpenAIMessages } from \"./openai/request\";\nimport { mapResponse, parseArgs } from \"./openai/response\";\nimport { mapStream } from \"./openai/stream\";\n\ntype ChatParams = OpenAI.Chat.Completions.ChatCompletionCreateParams;\n\nexport class OpenAIProvider implements Provider {\n readonly id = \"openai\" as const;\n private client: OpenAI;\n\n constructor(apiKey: string, client?: OpenAI) {\n this.client = client ?? new OpenAI({ apiKey });\n }\n\n async create(spec: ModelSpec, req: OracleRequest): Promise<OracleResponse> {\n try {\n const res = (await this.client.chat.completions.create(\n buildParams(spec, req, false) as unknown as ChatParams,\n )) as OpenAI.Chat.Completions.ChatCompletion;\n return mapResponse(res, req);\n } catch (err) {\n mapProviderError(err, \"openai\");\n }\n }\n\n async *stream(spec: ModelSpec, req: OracleRequest): AsyncIterable<OracleEvent> {\n try {\n const stream = (await this.client.chat.completions.create(\n buildParams(spec, req, true) as unknown as ChatParams,\n )) as unknown as AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>;\n yield* mapStream(stream, req);\n } catch (err) {\n yield { type: \"error\", error: normalizeError(err, \"openai\").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 { toOpenAIMessages, mapResponse, parseArgs };\n","// OpenAI request mapping: canonical shape → Chat Completions params.\nimport type OpenAI from \"openai\";\nimport type { ModelSpec } from \"../../shape/capabilities\";\nimport {\n CapabilityError,\n type AudioMediaType,\n type Effort,\n type OracleContentBlock,\n type OracleRequest,\n type ToolChoice,\n} from \"../../shape/index\";\nimport { stringifyBlocks } from \"../blocks\";\n\ntype ChatMessage = OpenAI.Chat.Completions.ChatCompletionMessageParam;\n\nexport function buildParams(spec: ModelSpec, req: OracleRequest, stream: boolean): Record<string, unknown> {\n const params: Record<string, unknown> = {\n model: spec.nativeId,\n messages: toOpenAIMessages(req),\n max_completion_tokens: req.maxTokens,\n };\n\n const tools = buildTools(req);\n if (tools) params.tools = tools;\n\n const toolChoice = mapToolChoice(req.toolChoice);\n if (toolChoice !== undefined) params.tool_choice = toolChoice;\n\n if (req.stopSequences && req.stopSequences.length > 0) params.stop = req.stopSequences;\n\n // Reasoning (effort) models reject a custom temperature; only send it elsewhere.\n if (req.temperature !== undefined && !spec.capabilities.effort) params.temperature = req.temperature;\n\n if (req.effort && spec.capabilities.effort) params.reasoning_effort = reasoningEffort(req.effort);\n\n if (req.responseFormat && spec.capabilities.structuredOutput) {\n params.response_format = {\n type: \"json_schema\",\n json_schema: { name: req.responseFormat.name ?? \"response\", schema: req.responseFormat.schema },\n };\n }\n\n // Search-capable chat models (e.g. gpt-5-search-api) take web_search_options;\n // the capability gate keeps this off models without webSearch.\n if (req.webSearch) params.web_search_options = { search_context_size: \"medium\" };\n\n if (stream) {\n params.stream = true;\n params.stream_options = { include_usage: true };\n }\n\n if (req.providerExtras) Object.assign(params, req.providerExtras);\n return params;\n}\n\n/** Expand canonical messages into OpenAI's flat message list: system folded to a\n * leading message, tool_result blocks lifted to role:\"tool\" messages, tool_use\n * blocks emitted as assistant tool_calls. */\nexport function toOpenAIMessages(req: OracleRequest): ChatMessage[] {\n const out: ChatMessage[] = [];\n\n if (req.system !== undefined) {\n const text = typeof req.system === \"string\" ? req.system : req.system.map((b) => b.text).join(\"\");\n out.push({ role: \"system\", content: text });\n }\n\n for (const msg of req.messages) {\n const blocks = typeof msg.content === \"string\" ? [{ type: \"text\" as const, text: msg.content }] : msg.content;\n\n if (msg.role === \"assistant\") {\n const textParts: string[] = [];\n const toolCalls: OpenAI.Chat.Completions.ChatCompletionMessageToolCall[] = [];\n for (const b of blocks) {\n if (b.type === \"text\") textParts.push(b.text);\n else if (b.type === \"tool_use\") {\n toolCalls.push({\n id: b.id,\n type: \"function\",\n function: { name: b.name, arguments: JSON.stringify(b.input) },\n } as OpenAI.Chat.Completions.ChatCompletionMessageToolCall);\n }\n // thinking blocks are dropped — OpenAI has no thinking input channel.\n }\n const assistant: Record<string, unknown> = { role: \"assistant\", content: textParts.join(\"\") || null };\n if (toolCalls.length > 0) assistant.tool_calls = toolCalls;\n out.push(assistant as unknown as ChatMessage);\n continue;\n }\n\n // user turn: tool_result blocks become tool messages; the rest becomes one\n // user message with multimodal content parts.\n const toolResults = blocks.filter((b) => b.type === \"tool_result\");\n for (const b of toolResults) {\n if (b.type !== \"tool_result\") continue;\n out.push({\n role: \"tool\",\n tool_call_id: b.toolUseId,\n content: typeof b.content === \"string\" ? b.content : stringifyBlocks(b.content),\n });\n }\n const rest = blocks.filter((b) => b.type !== \"tool_result\");\n if (rest.length > 0) out.push({ role: \"user\", content: rest.map(userPart) });\n }\n\n return out;\n}\n\ntype UserPart = OpenAI.Chat.Completions.ChatCompletionContentPart;\n\nfunction userPart(b: OracleContentBlock): UserPart {\n switch (b.type) {\n case \"text\":\n return { type: \"text\", text: b.text };\n case \"image\":\n return {\n type: \"image_url\",\n image_url: {\n url: b.source.type === \"url\" ? b.source.url : `data:${b.source.mediaType};base64,${b.source.data}`,\n },\n };\n case \"audio\": {\n if (b.source.type !== \"base64\") {\n throw new CapabilityError(\"OpenAI audio input requires base64 data, not a URL.\");\n }\n return {\n type: \"input_audio\",\n input_audio: { data: b.source.data, format: audioFormat(b.source.mediaType) },\n } as UserPart;\n }\n default:\n // document/tool_* are excluded from OpenAI user content (documents are\n // gated off by capabilities; tool_result handled separately).\n return { type: \"text\", text: stringifyBlocks([b]) };\n }\n}\n\nfunction audioFormat(m: AudioMediaType): \"wav\" | \"mp3\" {\n if (m === \"audio/wav\") return \"wav\";\n if (m === \"audio/mp3\" || m === \"audio/mpeg\") return \"mp3\";\n throw new CapabilityError(`OpenAI audio input supports wav and mp3, not \"${m}\".`);\n}\n\nfunction buildTools(req: OracleRequest): unknown[] | undefined {\n if (!req.tools || req.tools.length === 0) return undefined;\n return req.tools.map((t) => ({\n type: \"function\",\n function: { name: t.name, description: t.description, parameters: t.inputSchema },\n }));\n}\n\nfunction mapToolChoice(tc: ToolChoice | undefined): unknown | undefined {\n if (tc === undefined) return undefined;\n if (tc === \"auto\") return \"auto\";\n if (tc === \"none\") return \"none\";\n if (tc === \"any\") return \"required\";\n return { type: \"function\", function: { name: tc.name } };\n}\n\nfunction reasoningEffort(e: Effort): \"low\" | \"medium\" | \"high\" {\n if (e === \"low\") return \"low\";\n if (e === \"medium\") return \"medium\";\n return \"high\"; // high / xhigh / max → high\n}\n","// OpenAI response mapping: Chat Completion → canonical response. mapFinish /\n// mapUsage are also consumed by the stream mapper (kept here, not in the entry,\n// so the entry ↔ stream dependency stays one-directional — no import cycle).\nimport type OpenAI from \"openai\";\nimport {\n type OracleContentBlock,\n type OracleRequest,\n type OracleResponse,\n type OracleStopReason,\n type OracleUsage,\n} from \"../../shape/index\";\n\nexport function mapResponse(res: OpenAI.Chat.Completions.ChatCompletion, req: OracleRequest): OracleResponse {\n const choice = res.choices[0];\n const content: OracleContentBlock[] = [];\n const message = choice?.message;\n\n if (message?.content) content.push({ type: \"text\", text: message.content });\n for (const call of message?.tool_calls ?? []) {\n if (call.type !== \"function\") continue;\n content.push({ type: \"tool_use\", id: call.id, name: call.function.name, input: parseArgs(call.function.arguments) });\n }\n\n return {\n id: res.id,\n model: req.model,\n provider: \"openai\",\n role: \"assistant\",\n content,\n stopReason: mapFinish(choice?.finish_reason),\n usage: mapUsage(res.usage),\n };\n}\n\n/** OpenAI hands tool arguments back as a JSON *string*; the canonical shape wants\n * a parsed object. Tolerant: malformed JSON becomes an empty object. */\nexport function parseArgs(raw: string | undefined): Record<string, unknown> {\n if (!raw) return {};\n try {\n const parsed = JSON.parse(raw);\n return parsed && typeof parsed === \"object\" ? (parsed as Record<string, unknown>) : {};\n } catch {\n return {};\n }\n}\n\nexport function mapFinish(reason: string | null | undefined): OracleStopReason {\n switch (reason) {\n case \"stop\":\n return \"end_turn\";\n case \"length\":\n return \"max_tokens\";\n case \"tool_calls\":\n case \"function_call\":\n return \"tool_use\";\n case \"content_filter\":\n return \"refusal\";\n default:\n return \"end_turn\";\n }\n}\n\nexport function mapUsage(u: OpenAI.Completions.CompletionUsage | undefined): OracleUsage {\n const usage: OracleUsage = { inputTokens: u?.prompt_tokens ?? 0, outputTokens: u?.completion_tokens ?? 0 };\n const cached = u?.prompt_tokens_details?.cached_tokens;\n if (cached != null) usage.cacheReadTokens = cached;\n return usage;\n}\n","// OpenAI stream mapping onto the canonical event vocabulary.\nimport type OpenAI from \"openai\";\nimport { addUsage, emptyUsage, type OracleEvent, type OracleRequest, type OracleStopReason } from \"../../shape/index\";\nimport { mapFinish, mapUsage } from \"./response\";\n\n/** Map an OpenAI chat stream onto the canonical event vocabulary. OpenAI has no\n * explicit block start/stop, so we synthesize them: a text channel (index 0) and\n * one channel per tool_call index, opened lazily and closed at finish. */\nexport async function* mapStream(\n stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>,\n req: OracleRequest,\n): AsyncIterable<OracleEvent> {\n let started = false;\n let textOpen = false;\n const toolIndex = new Map<number, number>(); // openai tool_call index → our block index\n let nextBlockIndex = 1; // 0 reserved for text\n let stopReason: OracleStopReason = \"end_turn\";\n const usage = emptyUsage();\n\n for await (const chunk of stream) {\n if (!started) {\n started = true;\n yield {\n type: \"message_start\",\n message: { id: chunk.id, model: req.model, provider: \"openai\", role: \"assistant\", content: [], usage: emptyUsage() },\n };\n }\n\n if (chunk.usage) addUsage(usage, mapUsage(chunk.usage));\n\n const choice = chunk.choices[0];\n const delta = choice?.delta;\n\n if (delta?.content) {\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: delta.content } };\n }\n\n for (const tc of delta?.tool_calls ?? []) {\n let blockIndex = toolIndex.get(tc.index);\n if (blockIndex === undefined) {\n blockIndex = nextBlockIndex++;\n toolIndex.set(tc.index, blockIndex);\n yield {\n type: \"content_block_start\",\n index: blockIndex,\n contentBlock: { type: \"tool_use\", id: tc.id ?? \"\", name: tc.function?.name ?? \"\", input: {} },\n };\n }\n if (tc.function?.arguments) {\n yield { type: \"content_block_delta\", index: blockIndex, delta: { type: \"input_json_delta\", partialJson: tc.function.arguments } };\n }\n }\n\n if (choice?.finish_reason) stopReason = mapFinish(choice.finish_reason);\n }\n\n if (textOpen) yield { type: \"content_block_stop\", index: 0 };\n for (const blockIndex of toolIndex.values()) yield { type: \"content_block_stop\", index: blockIndex };\n yield { type: \"message_delta\", delta: { stopReason }, usage };\n yield { type: \"message_stop\" };\n}\n"],"mappings":"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","names":[]}
|