@odla-ai/ai 0.2.1 → 0.2.2

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.
@@ -1,40 +1,18 @@
1
+ import {
2
+ stringifyBlocks
3
+ } from "./chunk-U7F6VJCV.js";
1
4
  import {
2
5
  CapabilityError,
3
6
  addUsage,
4
7
  emptyUsage,
5
8
  mapProviderError,
6
9
  normalizeError
7
- } from "./chunk-LANGYP65.js";
10
+ } from "./chunk-QFT2DU2X.js";
8
11
 
9
12
  // src/providers/openai.ts
10
13
  import OpenAI from "openai";
11
- var OpenAIProvider = class {
12
- id = "openai";
13
- client;
14
- constructor(apiKey, client) {
15
- this.client = client ?? new OpenAI({ apiKey });
16
- }
17
- async create(spec, req) {
18
- try {
19
- const res = await this.client.chat.completions.create(
20
- buildParams(spec, req, false)
21
- );
22
- return mapResponse(res, req);
23
- } catch (err) {
24
- mapProviderError(err, "openai");
25
- }
26
- }
27
- async *stream(spec, req) {
28
- try {
29
- const stream = await this.client.chat.completions.create(
30
- buildParams(spec, req, true)
31
- );
32
- yield* mapStream(stream, req);
33
- } catch (err) {
34
- yield { type: "error", error: normalizeError(err, "openai").toShape() };
35
- }
36
- }
37
- };
14
+
15
+ // src/providers/openai/request.ts
38
16
  function buildParams(spec, req, stream) {
39
17
  const params = {
40
18
  model: spec.nativeId,
@@ -131,9 +109,6 @@ function audioFormat(m) {
131
109
  if (m === "audio/mp3" || m === "audio/mpeg") return "mp3";
132
110
  throw new CapabilityError(`OpenAI audio input supports wav and mp3, not "${m}".`);
133
111
  }
134
- function stringifyBlocks(blocks) {
135
- return blocks.map((b) => b.type === "text" ? b.text : `[${b.type}]`).join("");
136
- }
137
112
  function buildTools(req) {
138
113
  if (!req.tools || req.tools.length === 0) return void 0;
139
114
  return req.tools.map((t) => ({
@@ -153,6 +128,8 @@ function reasoningEffort(e) {
153
128
  if (e === "medium") return "medium";
154
129
  return "high";
155
130
  }
131
+
132
+ // src/providers/openai/response.ts
156
133
  function mapResponse(res, req) {
157
134
  const choice = res.choices[0];
158
135
  const content = [];
@@ -202,6 +179,8 @@ function mapUsage(u) {
202
179
  if (cached != null) usage.cacheReadTokens = cached;
203
180
  return usage;
204
181
  }
182
+
183
+ // src/providers/openai/stream.ts
205
184
  async function* mapStream(stream, req) {
206
185
  let started = false;
207
186
  let textOpen = false;
@@ -249,10 +228,39 @@ async function* mapStream(stream, req) {
249
228
  yield { type: "message_delta", delta: { stopReason }, usage };
250
229
  yield { type: "message_stop" };
251
230
  }
231
+
232
+ // src/providers/openai.ts
233
+ var OpenAIProvider = class {
234
+ id = "openai";
235
+ client;
236
+ constructor(apiKey, client) {
237
+ this.client = client ?? new OpenAI({ apiKey });
238
+ }
239
+ async create(spec, req) {
240
+ try {
241
+ const res = await this.client.chat.completions.create(
242
+ buildParams(spec, req, false)
243
+ );
244
+ return mapResponse(res, req);
245
+ } catch (err) {
246
+ mapProviderError(err, "openai");
247
+ }
248
+ }
249
+ async *stream(spec, req) {
250
+ try {
251
+ const stream = await this.client.chat.completions.create(
252
+ buildParams(spec, req, true)
253
+ );
254
+ yield* mapStream(stream, req);
255
+ } catch (err) {
256
+ yield { type: "error", error: normalizeError(err, "openai").toShape() };
257
+ }
258
+ }
259
+ };
252
260
  export {
253
261
  OpenAIProvider,
254
262
  mapResponse,
255
263
  parseArgs,
256
264
  toOpenAIMessages
257
265
  };
258
- //# sourceMappingURL=openai-XZRXMKCC.js.map
266
+ //# sourceMappingURL=openai-PTZPJYTT.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\";\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":[]}
package/llms.txt CHANGED
@@ -1,5 +1,9 @@
1
1
  # @odla-ai/ai — LLM context
2
2
 
3
+ > EXPERIMENTAL — an agentic-coding experiment. This package is built and operated
4
+ > by autonomous coding agents as an experiment in agentic loops. APIs change
5
+ > without notice and nothing here is production-hardened. Use at your own risk.
6
+
3
7
  One general-purpose interface for AI inference across Anthropic (Claude), OpenAI
4
8
  (GPT), and Google (Gemini) — text, image, and audio — plus a tool-use agent
5
9
  engine and an eval harness. Library-only: import in-process, bring your own keys.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@odla-ai/ai",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
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. The place every odla app reaches for when it needs AI. Isomorphic: Node 20+, Cloudflare Workers, the browser.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/providers/anthropic.ts"],"sourcesContent":["// Anthropic (Claude) adapter. The canonical shape is Anthropic-flavored, so this\n// is the lowest-translation adapter. Ported from odla-kg's claude.ts patterns:\n// forced tool-call via tool_choice, web_search_20260209 with pause_turn\n// continuation, adaptive thinking + output_config.effort (never budget_tokens).\n//\n// The installed @anthropic-ai/sdk (0.70.x) doesn't yet type adaptive thinking,\n// output_config, or web_search_20260209 — those newer request fields are built\n// into a loose params object and cast at the call site (the wire accepts them),\n// exactly as odla-kg does for the web-search tool literal.\n\nimport Anthropic from \"@anthropic-ai/sdk\";\nimport type { ModelSpec } from \"../shape/capabilities\";\nimport type { Provider } from \"./types\";\nimport { mapProviderError, normalizeError } from \"./errors\";\nimport {\n addUsage,\n emptyUsage,\n type OracleContentBlock,\n type OracleEvent,\n type OracleRequest,\n type OracleResponse,\n type OracleStopReason,\n type OracleUsage,\n type TextBlock,\n type ToolChoice,\n} from \"../shape/index\";\n\nexport class AnthropicProvider implements Provider {\n readonly id = \"anthropic\" as const;\n private client: Anthropic;\n\n /** `client` may be injected (tests, custom transport); otherwise built from key. */\n constructor(apiKey: string, client?: Anthropic) {\n this.client = client ?? new Anthropic({ apiKey });\n }\n\n async create(spec: ModelSpec, req: OracleRequest): Promise<OracleResponse> {\n try {\n let messages = toAnthropicMessages(req);\n const content: OracleContentBlock[] = [];\n const usage = emptyUsage();\n let stopReason: OracleStopReason = \"end_turn\";\n let id = \"\";\n\n // Loop only to resolve server-side tools (pause_turn), max 8 hops. Client\n // tool_use / end_turn / etc. return immediately.\n for (let i = 0; i < 8; i++) {\n const res = await this.client.messages.create(\n buildParams(spec, req, messages) as unknown as Anthropic.MessageCreateParamsNonStreaming,\n );\n id = res.id;\n addUsage(usage, mapUsage(res.usage));\n content.push(...mapContent(res.content));\n stopReason = mapStop(res.stop_reason);\n if (res.stop_reason === \"pause_turn\") {\n messages = [...messages, { role: \"assistant\", content: res.content }];\n continue;\n }\n break;\n }\n\n return { id, model: req.model, provider: \"anthropic\", role: \"assistant\", content, stopReason, usage };\n } catch (err) {\n mapProviderError(err, \"anthropic\");\n }\n }\n\n async *stream(spec: ModelSpec, req: OracleRequest): AsyncIterable<OracleEvent> {\n try {\n const events = this.client.messages.stream(\n buildParams(spec, req, toAnthropicMessages(req)) as unknown as Anthropic.MessageStreamParams,\n );\n for await (const raw of events) {\n const ev = mapStreamEvent(raw, req);\n if (ev) yield ev;\n }\n } catch (err) {\n yield { type: \"error\", error: normalizeError(err, \"anthropic\").toShape() };\n }\n }\n}\n\n// ----- request mapping -----\n\nfunction buildParams(\n spec: ModelSpec,\n req: OracleRequest,\n messages: Anthropic.MessageParam[],\n): Record<string, unknown> {\n const params: Record<string, unknown> = {\n model: spec.nativeId,\n max_tokens: req.maxTokens,\n messages,\n };\n\n const system = toAnthropicSystem(req.system);\n if (system !== undefined) params.system = system;\n\n const tools = buildTools(req);\n if (tools) params.tools = tools;\n\n const toolChoice = mapToolChoice(req.toolChoice);\n if (toolChoice) params.tool_choice = toolChoice;\n\n if (req.stopSequences && req.stopSequences.length > 0) params.stop_sequences = req.stopSequences;\n\n // Sampling params are rejected on the frontier models (they carry effort);\n // only the non-effort tier (Haiku) accepts temperature.\n if (req.temperature !== undefined && !spec.capabilities.effort) params.temperature = req.temperature;\n\n if (req.thinking === \"adaptive\" && spec.capabilities.thinking) params.thinking = { type: \"adaptive\" };\n\n const outputConfig: Record<string, unknown> = {};\n if (req.effort && spec.capabilities.effort) outputConfig.effort = req.effort;\n if (req.responseFormat && spec.capabilities.structuredOutput) {\n outputConfig.format = { type: \"json_schema\", schema: req.responseFormat.schema };\n }\n if (Object.keys(outputConfig).length > 0) params.output_config = outputConfig;\n\n if (req.providerExtras) Object.assign(params, req.providerExtras);\n return params;\n}\n\nfunction toAnthropicSystem(system: OracleRequest[\"system\"]): string | Anthropic.TextBlockParam[] | undefined {\n if (system === undefined) return undefined;\n if (typeof system === \"string\") return system;\n return system.map((b: TextBlock) => ({\n type: \"text\" as const,\n text: b.text,\n ...(b.cacheControl ? { cache_control: { type: \"ephemeral\" as const } } : {}),\n }));\n}\n\nfunction toAnthropicMessages(req: OracleRequest): Anthropic.MessageParam[] {\n return req.messages.map((m) => ({\n role: m.role,\n content: typeof m.content === \"string\" ? m.content : m.content.map(anthropicBlock),\n }));\n}\n\nfunction anthropicBlock(b: OracleContentBlock): Anthropic.ContentBlockParam {\n switch (b.type) {\n case \"text\":\n return {\n type: \"text\",\n text: b.text,\n ...(b.cacheControl ? { cache_control: { type: \"ephemeral\" } } : {}),\n };\n case \"image\":\n return {\n type: \"image\",\n source:\n b.source.type === \"base64\"\n ? { type: \"base64\", media_type: b.source.mediaType, data: b.source.data }\n : { type: \"url\", url: b.source.url },\n } as Anthropic.ContentBlockParam;\n case \"document\":\n return {\n type: \"document\",\n source:\n b.source.type === \"base64\"\n ? { type: \"base64\", media_type: \"application/pdf\", data: b.source.data }\n : { type: \"url\", url: b.source.url },\n } as Anthropic.ContentBlockParam;\n case \"tool_use\":\n return { type: \"tool_use\", id: b.id, name: b.name, input: b.input };\n case \"tool_result\":\n return {\n type: \"tool_result\",\n tool_use_id: b.toolUseId,\n content:\n typeof b.content === \"string\"\n ? b.content\n : (b.content.map(anthropicBlock) as Anthropic.ToolResultBlockParam[\"content\"]),\n ...(b.isError ? { is_error: true } : {}),\n };\n case \"thinking\":\n return { type: \"thinking\", thinking: b.thinking, signature: b.signature ?? \"\" };\n case \"audio\":\n // Guarded by validateRequest; defensive in case validation is bypassed.\n throw new Error(\"Anthropic does not support audio input\");\n }\n}\n\nfunction buildTools(req: OracleRequest): unknown[] | undefined {\n const tools: unknown[] = [];\n for (const t of req.tools ?? []) {\n tools.push({ name: t.name, description: t.description, input_schema: t.inputSchema });\n }\n if (req.webSearch) tools.push({ type: \"web_search_20260209\", name: \"web_search\" });\n return tools.length > 0 ? tools : undefined;\n}\n\nfunction mapToolChoice(tc: ToolChoice | undefined): unknown | undefined {\n if (tc === undefined) return undefined;\n if (tc === \"auto\") return { type: \"auto\" };\n if (tc === \"any\") return { type: \"any\" };\n if (tc === \"none\") return { type: \"none\" };\n return { type: \"tool\", name: tc.name };\n}\n\n// ----- response mapping -----\n\nfunction mapContent(blocks: Anthropic.ContentBlock[]): OracleContentBlock[] {\n const out: OracleContentBlock[] = [];\n for (const b of blocks) {\n if (b.type === \"text\") out.push({ type: \"text\", text: b.text });\n else if (b.type === \"tool_use\") {\n out.push({ type: \"tool_use\", id: b.id, name: b.name, input: (b.input ?? {}) as Record<string, unknown> });\n } else if (b.type === \"thinking\") {\n out.push({ type: \"thinking\", thinking: b.thinking, signature: b.signature });\n }\n // server_tool_use / web_search_tool_result blocks are dropped — the answer\n // text lives in text blocks.\n }\n return out;\n}\n\nfunction mapUsage(u: Anthropic.Usage): OracleUsage {\n const usage: OracleUsage = { inputTokens: u.input_tokens ?? 0, outputTokens: u.output_tokens ?? 0 };\n if (u.cache_creation_input_tokens != null) usage.cacheCreationTokens = u.cache_creation_input_tokens;\n if (u.cache_read_input_tokens != null) usage.cacheReadTokens = u.cache_read_input_tokens;\n return usage;\n}\n\nfunction mapStop(reason: string | null | undefined): OracleStopReason {\n switch (reason) {\n case \"end_turn\":\n case \"max_tokens\":\n case \"stop_sequence\":\n case \"tool_use\":\n case \"pause_turn\":\n case \"refusal\":\n return reason;\n default:\n return \"end_turn\";\n }\n}\n\n// ----- stream mapping -----\n\nfunction mapStreamEvent(raw: Anthropic.RawMessageStreamEvent, req: OracleRequest): OracleEvent | undefined {\n switch (raw.type) {\n case \"message_start\":\n return {\n type: \"message_start\",\n message: {\n id: raw.message.id,\n model: req.model,\n provider: \"anthropic\",\n role: \"assistant\",\n content: [],\n usage: mapUsage(raw.message.usage),\n },\n };\n case \"content_block_start\": {\n const block = mapStartBlock(raw.content_block);\n return block ? { type: \"content_block_start\", index: raw.index, contentBlock: block } : undefined;\n }\n case \"content_block_delta\": {\n const d = raw.delta;\n if (d.type === \"text_delta\") return { type: \"content_block_delta\", index: raw.index, delta: { type: \"text_delta\", text: d.text } };\n if (d.type === \"thinking_delta\") return { type: \"content_block_delta\", index: raw.index, delta: { type: \"thinking_delta\", thinking: d.thinking } };\n if (d.type === \"input_json_delta\") return { type: \"content_block_delta\", index: raw.index, delta: { type: \"input_json_delta\", partialJson: d.partial_json } };\n return undefined;\n }\n case \"content_block_stop\":\n return { type: \"content_block_stop\", index: raw.index };\n case \"message_delta\":\n return { type: \"message_delta\", delta: { stopReason: mapStop(raw.delta.stop_reason) }, usage: mapUsage(raw.usage as Anthropic.Usage) };\n case \"message_stop\":\n return { type: \"message_stop\" };\n default:\n return undefined;\n }\n}\n\nfunction mapStartBlock(cb: Anthropic.ContentBlock): OracleContentBlock | undefined {\n if (cb.type === \"text\") return { type: \"text\", text: cb.text };\n if (cb.type === \"tool_use\") return { type: \"tool_use\", id: cb.id, name: cb.name, input: {} };\n if (cb.type === \"thinking\") return { type: \"thinking\", thinking: cb.thinking, signature: cb.signature };\n return undefined;\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/shape/index.ts","../src/providers/errors.ts"],"sourcesContent":["// The canonical request / response / event types for talking to any LLM\n// provider through odla-ai. One shape regardless of which vendor answers.\n//\n// Modeled after Anthropic's content-block shape because: (a) odla is\n// Claude-native in spirit, (b) Anthropic's tool-use is cleaner (object `input`\n// instead of a JSON-string `arguments`, structured content blocks, tool_result\n// as a content block), and (c) translating OpenAI/Google → Anthropic loses less\n// fidelity than the reverse. Forked and generalized from odla-ai-old's\n// `@odla-ai/oracle-shape`: this version adds Google as a first-class provider,\n// adds audio (and PDF document) input, replaces two-provider hardcoding with an\n// open provider union, and generalizes reasoning controls.\n//\n// This module has ZERO runtime dependencies. It is the load-bearing contract the\n// adapters, the client facade, the agent loop, and the eval harness all speak;\n// provider SDK types never leak past the adapters.\n\n// ----- Providers -----\n\n/** Every provider odla-ai can route to. Apps should not branch on this for\n * portability, but it is carried on responses/events for observability. */\nexport type ProviderId = \"anthropic\" | \"openai\" | \"google\";\n\n// ----- Content blocks -----\n\nexport interface TextBlock {\n type: \"text\";\n text: string;\n /** Anthropic prompt-caching marker. Other providers ignore it. */\n cacheControl?: { type: \"ephemeral\" };\n}\n\nexport type ImageMediaType = \"image/jpeg\" | \"image/png\" | \"image/gif\" | \"image/webp\";\n\nexport type ImageSource =\n | { type: \"base64\"; mediaType: ImageMediaType; data: string }\n | { type: \"url\"; url: string };\n\nexport interface ImageBlock {\n type: \"image\";\n source: ImageSource;\n}\n\nexport type AudioMediaType =\n | \"audio/wav\"\n | \"audio/mp3\"\n | \"audio/mpeg\"\n | \"audio/ogg\"\n | \"audio/flac\"\n | \"audio/aac\"\n | \"audio/webm\";\n\nexport type AudioSource =\n | { type: \"base64\"; mediaType: AudioMediaType; data: string }\n | { type: \"url\"; url: string };\n\n/** Audio input. Supported by OpenAI (audio models) and Google (Gemini); NOT by\n * Anthropic — a request carrying an AudioBlock to a Claude model is rejected up\n * front by capability validation, never sent. */\nexport interface AudioBlock {\n type: \"audio\";\n source: AudioSource;\n}\n\nexport type DocumentSource =\n | { type: \"base64\"; mediaType: \"application/pdf\"; data: string }\n | { type: \"url\"; url: string };\n\n/** A document (PDF) input. Supported by Anthropic and Google. */\nexport interface DocumentBlock {\n type: \"document\";\n source: DocumentSource;\n}\n\nexport interface ToolUseBlock {\n type: \"tool_use\";\n /** Correlates a `tool_result` with this call. Translated from OpenAI's\n * `tool_calls[].id` by the openai adapter. */\n id: string;\n name: string;\n /** Already-parsed object. OpenAI's JSON-string `arguments` is parsed before it\n * reaches this shape — callers never JSON.parse a tool's arguments. */\n input: Record<string, unknown>;\n}\n\nexport interface ToolResultBlock {\n type: \"tool_result\";\n toolUseId: string;\n /** String for plain-text results (most tools); a content-block array for tools\n * that return structured / multi-modal content. */\n content: string | OracleContentBlock[];\n isError?: boolean;\n}\n\nexport interface ThinkingBlock {\n type: \"thinking\";\n /** Extended-thinking text (Anthropic). Empty when `display` is omitted. */\n thinking: string;\n signature?: string;\n}\n\nexport type OracleContentBlock =\n | TextBlock\n | ImageBlock\n | AudioBlock\n | DocumentBlock\n | ToolUseBlock\n | ToolResultBlock\n | ThinkingBlock;\n\n// ----- Messages -----\n\nexport type OracleRole = \"user\" | \"assistant\";\n\nexport interface OracleMessage {\n role: OracleRole;\n /** A plain string (shorthand for a single TextBlock) or an explicit array for\n * multi-modal / tool turns. */\n content: string | OracleContentBlock[];\n}\n\n// ----- Tools -----\n\nexport interface OracleTool {\n name: string;\n description: string;\n /** JSON Schema for the tool's arguments. */\n inputSchema: Record<string, unknown>;\n}\n\n/** How the model may use tools this turn. `{ type: \"tool\", name }` forces one\n * specific tool — the mechanism behind `extract<T>()`. */\nexport type ToolChoice = \"auto\" | \"any\" | \"none\" | { type: \"tool\"; name: string };\n\n// ----- Reasoning / output controls -----\n\n/** Whether the model reasons before answering. Maps to Anthropic adaptive\n * thinking; emulated or ignored on providers without a native equivalent. */\nexport type ThinkingMode = \"off\" | \"adaptive\";\n\n/** Reasoning/spend depth. Maps to Anthropic `output_config.effort`; the openai\n * and google adapters map it to their nearest reasoning-effort knob. */\nexport type Effort = \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\";\n\n/** Constrain the response to valid JSON matching a schema (structured output). */\nexport interface ResponseFormat {\n type: \"json_schema\";\n name?: string;\n schema: Record<string, unknown>;\n}\n\n// ----- Request -----\n\nexport interface OracleRequest {\n /** Canonical model id from the catalog; resolved to a provider + native id. */\n model: string;\n /** System prompt. Top-level for Anthropic/Google; the openai adapter folds it\n * into a leading system message. */\n system?: string | TextBlock[];\n messages: OracleMessage[];\n tools?: OracleTool[];\n toolChoice?: ToolChoice;\n maxTokens: number;\n temperature?: number;\n stopSequences?: string[];\n /** Reasoning mode. Only applied on models whose capabilities allow it. */\n thinking?: ThinkingMode;\n /** Reasoning/spend depth. Only applied on models whose capabilities allow it. */\n effort?: Effort;\n responseFormat?: ResponseFormat;\n /** Enable the provider's server-side web-search tool for this request. */\n webSearch?: boolean;\n /** Provider-specific escape hatch, passed through opaquely. Using it gives up\n * portability across providers. */\n providerExtras?: Record<string, unknown>;\n}\n\n// ----- Usage -----\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\n// ----- Response (non-streaming) -----\n\nexport type OracleStopReason =\n | \"end_turn\"\n | \"max_tokens\"\n | \"stop_sequence\"\n | \"tool_use\"\n | \"pause_turn\"\n | \"refusal\"\n | \"error\";\n\nexport interface OracleResponse {\n id: string;\n model: string;\n /** The provider that actually answered — for observability, not branching. */\n provider: ProviderId;\n role: \"assistant\";\n content: OracleContentBlock[];\n stopReason: OracleStopReason;\n usage: OracleUsage;\n}\n\n// ----- Streaming events -----\n\n/** A discriminated union of SSE events with the same vocabulary regardless of\n * provider. The anthropic adapter passes these through nearly 1:1; the openai\n * and google adapters map their native streams onto this shape. */\nexport type OracleEvent =\n | MessageStartEvent\n | ContentBlockStartEvent\n | ContentBlockDeltaEvent\n | ContentBlockStopEvent\n | MessageDeltaEvent\n | MessageStopEvent\n | OracleErrorEvent;\n\nexport interface MessageStartEvent {\n type: \"message_start\";\n message: {\n id: string;\n model: string;\n provider: ProviderId;\n role: \"assistant\";\n content: [];\n usage: OracleUsage;\n };\n}\n\nexport interface ContentBlockStartEvent {\n type: \"content_block_start\";\n index: number;\n contentBlock: OracleContentBlock;\n}\n\nexport type ContentBlockDelta =\n | { type: \"text_delta\"; text: string }\n | { type: \"thinking_delta\"; thinking: string }\n | { type: \"input_json_delta\"; partialJson: string };\n\nexport interface ContentBlockDeltaEvent {\n type: \"content_block_delta\";\n index: number;\n delta: ContentBlockDelta;\n}\n\nexport interface ContentBlockStopEvent {\n type: \"content_block_stop\";\n index: number;\n}\n\nexport interface MessageDeltaEvent {\n type: \"message_delta\";\n delta: { stopReason?: OracleStopReason };\n usage: OracleUsage;\n}\n\nexport interface MessageStopEvent {\n type: \"message_stop\";\n}\n\nexport interface OracleErrorEvent {\n type: \"error\";\n error: OracleErrorShape;\n}\n\n// ----- Errors -----\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\n// ----- Helpers (cheap, dependency-free) -----\n\nexport function isTextBlock(b: OracleContentBlock): b is TextBlock {\n return b.type === \"text\";\n}\nexport function isImageBlock(b: OracleContentBlock): b is ImageBlock {\n return b.type === \"image\";\n}\nexport function isAudioBlock(b: OracleContentBlock): b is AudioBlock {\n return b.type === \"audio\";\n}\nexport function isDocumentBlock(b: OracleContentBlock): b is DocumentBlock {\n return b.type === \"document\";\n}\nexport function isToolUseBlock(b: OracleContentBlock): b is ToolUseBlock {\n return b.type === \"tool_use\";\n}\nexport function isToolResultBlock(b: OracleContentBlock): b is ToolResultBlock {\n return b.type === \"tool_result\";\n}\nexport function isThinkingBlock(b: OracleContentBlock): b is ThinkingBlock {\n return b.type === \"thinking\";\n}\n\n/** Normalize a message's `content` (string shorthand or block array) to blocks. */\nexport function blocksOf(content: string | OracleContentBlock[]): OracleContentBlock[] {\n return typeof content === \"string\" ? [{ type: \"text\", text: content }] : content;\n}\n\n/** Concatenate the text of every TextBlock. Image/audio/tool/thinking ignored. */\nexport function extractText(content: OracleContentBlock[]): string {\n return content.filter(isTextBlock).map((b) => b.text).join(\"\");\n}\n\n/** Collect every tool_use block from response or message content. */\nexport function extractToolUses(content: OracleContentBlock[]): ToolUseBlock[] {\n return content.filter(isToolUseBlock);\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"],"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 — Gemini correlates by function name, so we key\n// the canonical tool_use id to the name).\n\nimport { GoogleGenAI, FunctionCallingConfigMode } from \"@google/genai\";\nimport type { Content, GenerateContentResponse, GenerateContentParameters, Part } from \"@google/genai\";\nimport type { ModelSpec } from \"../shape/capabilities\";\nimport type { Provider } from \"./types\";\nimport { mapProviderError, normalizeError } from \"./errors\";\nimport {\n CapabilityError,\n emptyUsage,\n type OracleContentBlock,\n type OracleEvent,\n type OracleRequest,\n type OracleResponse,\n type OracleStopReason,\n type OracleUsage,\n type ToolChoice,\n} from \"../shape/index\";\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// ----- request mapping -----\n\nfunction 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 stringifyBlocks(blocks: OracleContentBlock[]): string {\n return blocks.map((b) => (b.type === \"text\" ? b.text : `[${b.type}]`)).join(\"\");\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\n// ----- response mapping -----\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\nfunction 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\nfunction 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\n// ----- stream mapping -----\n\nasync 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"],"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 here\n// so the rest of odla-ai only ever sees the canonical (Anthropic-flavored) shape.\n\nimport OpenAI from \"openai\";\nimport type { ModelSpec } from \"../shape/capabilities\";\nimport type { Provider } from \"./types\";\nimport { normalizeError, mapProviderError } from \"./errors\";\nimport {\n CapabilityError,\n addUsage,\n emptyUsage,\n type AudioMediaType,\n type Effort,\n type OracleContentBlock,\n type OracleEvent,\n type OracleRequest,\n type OracleResponse,\n type OracleStopReason,\n type OracleUsage,\n type ToolChoice,\n} from \"../shape/index\";\n\ntype ChatParams = OpenAI.Chat.Completions.ChatCompletionCreateParams;\ntype ChatMessage = OpenAI.Chat.Completions.ChatCompletionMessageParam;\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// ----- request mapping -----\n\nfunction 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 stringifyBlocks(blocks: OracleContentBlock[]): string {\n return blocks\n .map((b) => (b.type === \"text\" ? b.text : `[${b.type}]`))\n .join(\"\");\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\n// ----- response mapping -----\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\nfunction 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\nfunction 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\n// ----- stream mapping -----\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. */\nasync 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":[]}