@odla-ai/ai 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/llms.txt CHANGED
@@ -86,6 +86,27 @@ Secrets are read-only from the app key (odlaDbKeyResolver → db.secrets.get); t
86
86
  are WRITTEN only with the operator/dev token via provisionAgentApp/putSecret. Keys
87
87
  are AES-GCM-encrypted at rest in odla-db and never returned to browser clients.
88
88
 
89
+ ## Platform wiring (odla.ai apps registry)
90
+
91
+ Apps registered on the odla platform get LLM access as an APP SETTING —
92
+ never put provider keys in your Worker's wrangler vars/secrets. Provider +
93
+ default model are per-environment registry config (set in Studio's AI card
94
+ or `@odla-ai/apps` setAi); the API key lives in the platform vault (the
95
+ env's odla-db tenant secrets, write-only for operators). One call reads both:
96
+
97
+ import { initFromPlatform } from "@odla-ai/ai";
98
+ import { init as odlaInit } from "@odla-ai/db";
99
+
100
+ const db = odlaInit({ appId: TENANT, adminToken: env.ODLA_API_KEY, endpoint: "https://db.odla.ai" });
101
+ const { ai, provider, model } = await initFromPlatform({
102
+ platform: "https://odla.ai", appId: APP_ID, env: "prod", db });
103
+ // ai.chat / ai.stream / ai.extract — provider/model from public-config
104
+ // (cached ~60s, so Studio changes apply without redeploys); the key is
105
+ // fetched from the vault at call time via odlaDbKeyResolver.
106
+
107
+ The only secret the Worker carries is its odla-db app key. Rotating the LLM
108
+ key = writing the secret again; switching provider/model = a Studio edit.
109
+
89
110
  ## Errors
90
111
 
91
112
  Every error is an OdlaAIError subclass with a stable `code` — branch on err.code:
@@ -101,6 +122,8 @@ tool_input_invalid, provider_error.
101
122
  - `gpt-5.5` (openai) — text, image, tools, effort, structured
102
123
  - `gpt-5` (openai) — text, image, tools, effort, structured
103
124
  - `gpt-5-mini` (openai) — text, image, tools, effort, structured
125
+ - `gpt-5-nano` (openai) — text, image, tools, effort, structured
126
+ - `gpt-5-search-api` (openai) — text, image, tools, web-search, structured
104
127
  - `gpt-4o` (openai) — text, image, tools, structured
105
128
  - `gpt-audio` (openai) — text, audio, tools, structured
106
129
  - `gemini-2.5-pro` (google) — text, image, audio, tools, thinking, effort, web-search, structured
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@odla-ai/ai",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "One general-purpose interface for AI inference across Anthropic (Claude), OpenAI, and Google (Gemini) — text, image, and audio — 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",
@@ -71,6 +71,6 @@
71
71
  "tsup": "^8.3.0",
72
72
  "tsx": "^4.19.0",
73
73
  "typescript": "^6.0.3",
74
- "vitest": "^3.0.0"
74
+ "vitest": "^4.1.9"
75
75
  }
76
76
  }
@@ -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 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":[]}