From 717a8f958a1ac6ae245091a905672acebd2f3fe4 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Mon, 22 Jun 2026 12:54:12 +0200 Subject: [PATCH] fix(ai): revert selective pi-ai base entrypoints --- packages/agent/README.md | 18 - packages/agent/package.json | 4 - packages/agent/src/agent-loop.ts | 2 +- packages/agent/src/agent.ts | 2 +- packages/agent/src/base.ts | 39 -- packages/agent/src/harness/agent-harness.ts | 2 +- .../compaction/branch-summarization.ts | 3 +- .../src/harness/compaction/compaction.ts | 10 +- .../agent/src/harness/compaction/utils.ts | 2 +- packages/agent/src/harness/messages.ts | 2 +- packages/agent/src/harness/session/session.ts | 2 +- packages/agent/src/harness/types.ts | 4 +- packages/agent/src/index.ts | 49 +- packages/agent/src/proxy.ts | 2 +- packages/agent/src/types.ts | 2 +- packages/agent/vitest.config.ts | 6 - packages/agent/vitest.harness.config.ts | 6 - packages/ai/README.md | 32 +- packages/ai/package.json | 12 - packages/ai/src/base.ts | 45 -- packages/ai/src/bedrock-provider.ts | 4 +- packages/ai/src/images-api-registry.ts | 4 - packages/ai/src/images.ts | 2 + packages/ai/src/index.ts | 49 +- packages/ai/src/providers/amazon-bedrock.ts | 9 - packages/ai/src/providers/anthropic.ts | 9 - .../src/providers/azure-openai-responses.ts | 9 - packages/ai/src/providers/google-vertex.ts | 9 - packages/ai/src/providers/google.ts | 9 - .../ai/src/providers/images/openrouter.ts | 8 - .../src/providers/images/register-builtins.ts | 50 ++ packages/ai/src/providers/mistral.ts | 9 - .../src/providers/openai-codex-responses.ts | 9 - .../ai/src/providers/openai-completions.ts | 9 - packages/ai/src/providers/openai-responses.ts | 9 - .../ai/src/providers/register-builtins.ts | 458 +++++++++++------- packages/ai/src/stream.ts | 4 + packages/ai/test/abort.test.ts | 2 +- .../anthropic-eager-tool-input-e2e.test.ts | 2 +- ...ic-empty-thinking-signature-compat.test.ts | 2 +- .../anthropic-force-adaptive-thinking.test.ts | 2 +- ...anthropic-long-cache-retention-e2e.test.ts | 2 +- .../ai/test/anthropic-opus-4-8-smoke.test.ts | 2 +- .../test/anthropic-temperature-compat.test.ts | 2 +- .../test/anthropic-thinking-disable.test.ts | 2 +- .../anthropic-tool-name-normalization.test.ts | 2 +- packages/ai/test/base-entrypoint.test.ts | 68 --- packages/ai/test/bedrock-models.test.ts | 2 +- packages/ai/test/cache-retention.test.ts | 2 +- packages/ai/test/context-overflow.test.ts | 2 +- .../ai/test/cross-provider-handoff.test.ts | 2 +- packages/ai/test/empty.test.ts | 2 +- .../ai/test/google-thinking-disable.test.ts | 2 +- packages/ai/test/interleaved-thinking.test.ts | 2 +- packages/ai/test/lazy-module-load.test.ts | 52 +- .../ai/test/mistral-reasoning-mode.test.ts | 2 +- packages/ai/test/mistral-tool-schema.test.ts | 2 +- .../openai-codex-cache-affinity-e2e.test.ts | 2 +- .../openai-completions-empty-tools.test.ts | 2 +- .../openai-completions-response-model.test.ts | 2 +- .../openai-completions-tool-choice.test.ts | 2 +- ...penai-responses-cache-affinity-e2e.test.ts | 2 +- ...nai-responses-reasoning-replay-e2e.test.ts | 2 +- .../test/openrouter-cache-write-repro.test.ts | 2 +- packages/ai/test/openrouter-images.test.ts | 12 - packages/ai/test/responseid.test.ts | 2 +- packages/ai/test/stream.test.ts | 2 +- packages/ai/test/tokens.test.ts | 2 +- .../test/tool-call-id-normalization.test.ts | 2 +- .../ai/test/tool-call-without-result.test.ts | 2 +- packages/ai/test/total-tokens.test.ts | 2 +- packages/ai/test/unicode-surrogate.test.ts | 2 +- packages/ai/test/xhigh.test.ts | 2 +- ...ms-anthropic-empty-signature-smoke.test.ts | 2 +- packages/ai/test/zen.test.ts | 2 +- packages/coding-agent/vitest.config.ts | 9 - scripts/check-browser-smoke.mjs | 44 -- tsconfig.json | 1 - 78 files changed, 492 insertions(+), 668 deletions(-) delete mode 100644 packages/agent/src/base.ts delete mode 100644 packages/ai/src/base.ts create mode 100644 packages/ai/src/providers/images/register-builtins.ts delete mode 100644 packages/ai/test/base-entrypoint.test.ts diff --git a/packages/agent/README.md b/packages/agent/README.md index ca4c1080f..f0fedb810 100644 --- a/packages/agent/README.md +++ b/packages/agent/README.md @@ -31,24 +31,6 @@ agent.subscribe((event) => { await agent.prompt("Hello!"); ``` -## Base Entry Point - -Use `@earendil-works/pi-agent-core/base` with `@earendil-works/pi-ai/base` when bundling applications that should include only selected provider transports: - -```typescript -import { Agent } from "@earendil-works/pi-agent-core/base"; -import { getModel } from "@earendil-works/pi-ai/base"; -import { register } from "@earendil-works/pi-ai/anthropic"; - -register(); - -const agent = new Agent({ - initialState: { model: getModel("anthropic", "claude-sonnet-4-6") }, -}); -``` - -The default `@earendil-works/pi-agent-core` entry point remains batteries-included and registers pi-ai's lazy built-in transports for backward compatibility. - ## Core Concepts ### AgentMessage vs LLM Message diff --git a/packages/agent/package.json b/packages/agent/package.json index a3f236a44..9322d7c68 100644 --- a/packages/agent/package.json +++ b/packages/agent/package.json @@ -10,10 +10,6 @@ "types": "./dist/index.d.ts", "import": "./dist/index.js" }, - "./base": { - "types": "./dist/base.d.ts", - "import": "./dist/base.js" - }, "./node": { "types": "./dist/node.d.ts", "import": "./dist/node.js" diff --git a/packages/agent/src/agent-loop.ts b/packages/agent/src/agent-loop.ts index 8a8f20f17..77bd9568a 100644 --- a/packages/agent/src/agent-loop.ts +++ b/packages/agent/src/agent-loop.ts @@ -10,7 +10,7 @@ import { streamSimple, type ToolResultMessage, validateToolArguments, -} from "@earendil-works/pi-ai/base"; +} from "@earendil-works/pi-ai"; import type { AgentContext, AgentEvent, diff --git a/packages/agent/src/agent.ts b/packages/agent/src/agent.ts index ef26e2137..db6684a8a 100644 --- a/packages/agent/src/agent.ts +++ b/packages/agent/src/agent.ts @@ -7,7 +7,7 @@ import { type TextContent, type ThinkingBudgets, type Transport, -} from "@earendil-works/pi-ai/base"; +} from "@earendil-works/pi-ai"; import { runAgentLoop, runAgentLoopContinue } from "./agent-loop.ts"; import type { AfterToolCallContext, diff --git a/packages/agent/src/base.ts b/packages/agent/src/base.ts deleted file mode 100644 index ad1abafaf..000000000 --- a/packages/agent/src/base.ts +++ /dev/null @@ -1,39 +0,0 @@ -export * from "./agent.ts"; -export * from "./agent-loop.ts"; -export * from "./harness/agent-harness.ts"; -export { - type BranchPreparation, - type BranchSummaryDetails, - type CollectEntriesResult, - collectEntriesForBranchSummary, - generateBranchSummary, - prepareBranchEntries, -} from "./harness/compaction/branch-summarization.ts"; -export { - calculateContextTokens, - compact, - DEFAULT_COMPACTION_SETTINGS, - estimateContextTokens, - estimateTokens, - findCutPoint, - findTurnStartIndex, - generateSummary, - getLastAssistantUsage, - prepareCompaction, - serializeConversation, - shouldCompact, -} from "./harness/compaction/compaction.ts"; -export * from "./harness/messages.ts"; -export * from "./harness/prompt-templates.ts"; -export * from "./harness/session/jsonl-repo.ts"; -export * from "./harness/session/memory-repo.ts"; -export * from "./harness/session/repo-utils.ts"; -export * from "./harness/session/session.ts"; -export { uuidv7 } from "./harness/session/uuid.ts"; -export * from "./harness/skills.ts"; -export * from "./harness/system-prompt.ts"; -export * from "./harness/types.ts"; -export * from "./harness/utils/shell-output.ts"; -export * from "./harness/utils/truncate.ts"; -export * from "./proxy.ts"; -export * from "./types.ts"; diff --git a/packages/agent/src/harness/agent-harness.ts b/packages/agent/src/harness/agent-harness.ts index f097ee759..965634653 100644 --- a/packages/agent/src/harness/agent-harness.ts +++ b/packages/agent/src/harness/agent-harness.ts @@ -4,7 +4,7 @@ import { type Model, streamSimple, type UserMessage, -} from "@earendil-works/pi-ai/base"; +} from "@earendil-works/pi-ai"; import { runAgentLoop } from "../agent-loop.ts"; import type { AgentContext, diff --git a/packages/agent/src/harness/compaction/branch-summarization.ts b/packages/agent/src/harness/compaction/branch-summarization.ts index 60ffe7718..c1824ebf8 100644 --- a/packages/agent/src/harness/compaction/branch-summarization.ts +++ b/packages/agent/src/harness/compaction/branch-summarization.ts @@ -1,4 +1,5 @@ -import { completeSimple, type Model } from "@earendil-works/pi-ai/base"; +import type { Model } from "@earendil-works/pi-ai"; +import { completeSimple } from "@earendil-works/pi-ai"; import type { AgentMessage } from "../../types.ts"; import { convertToLlm, diff --git a/packages/agent/src/harness/compaction/compaction.ts b/packages/agent/src/harness/compaction/compaction.ts index d619b34a8..dba753d78 100644 --- a/packages/agent/src/harness/compaction/compaction.ts +++ b/packages/agent/src/harness/compaction/compaction.ts @@ -1,11 +1,5 @@ -import { - type AssistantMessage, - completeSimple, - type ImageContent, - type Model, - type TextContent, - type Usage, -} from "@earendil-works/pi-ai/base"; +import type { AssistantMessage, ImageContent, Model, TextContent, Usage } from "@earendil-works/pi-ai"; +import { completeSimple } from "@earendil-works/pi-ai"; import type { AgentMessage, ThinkingLevel } from "../../types.ts"; import { convertToLlm, diff --git a/packages/agent/src/harness/compaction/utils.ts b/packages/agent/src/harness/compaction/utils.ts index 388dd84a8..07535b79b 100644 --- a/packages/agent/src/harness/compaction/utils.ts +++ b/packages/agent/src/harness/compaction/utils.ts @@ -1,4 +1,4 @@ -import type { Message } from "@earendil-works/pi-ai/base"; +import type { Message } from "@earendil-works/pi-ai"; import type { AgentMessage } from "../../types.ts"; /** File paths touched by a session branch or compaction range. */ diff --git a/packages/agent/src/harness/messages.ts b/packages/agent/src/harness/messages.ts index 8368dd343..36ce96a1e 100644 --- a/packages/agent/src/harness/messages.ts +++ b/packages/agent/src/harness/messages.ts @@ -1,4 +1,4 @@ -import type { ImageContent, Message, TextContent } from "@earendil-works/pi-ai/base"; +import type { ImageContent, Message, TextContent } from "@earendil-works/pi-ai"; import type { AgentMessage } from "../types.ts"; export const COMPACTION_SUMMARY_PREFIX = `The conversation history before this point was compacted into the following summary: diff --git a/packages/agent/src/harness/session/session.ts b/packages/agent/src/harness/session/session.ts index 3fd76c6b5..6f136208a 100644 --- a/packages/agent/src/harness/session/session.ts +++ b/packages/agent/src/harness/session/session.ts @@ -1,4 +1,4 @@ -import type { ImageContent, TextContent } from "@earendil-works/pi-ai/base"; +import type { ImageContent, TextContent } from "@earendil-works/pi-ai"; import type { AgentMessage } from "../../types.ts"; import { createBranchSummaryMessage, createCompactionSummaryMessage, createCustomMessage } from "../messages.ts"; import type { diff --git a/packages/agent/src/harness/types.ts b/packages/agent/src/harness/types.ts index 8f281570f..4756ca841 100644 --- a/packages/agent/src/harness/types.ts +++ b/packages/agent/src/harness/types.ts @@ -1,5 +1,5 @@ -import type { ImageContent, Model, SimpleStreamOptions, TextContent, Transport } from "@earendil-works/pi-ai/base"; -import type { AgentEvent, AgentMessage, AgentTool, QueueMode, ThinkingLevel } from "../types.ts"; +import type { ImageContent, Model, SimpleStreamOptions, TextContent, Transport } from "@earendil-works/pi-ai"; +import type { AgentEvent, AgentMessage, AgentTool, QueueMode, ThinkingLevel } from "../index.ts"; import type { Session } from "./session/session.ts"; /** Result of a fallible operation. Expected failures are returned as `ok: false` instead of thrown. */ diff --git a/packages/agent/src/index.ts b/packages/agent/src/index.ts index 08b7b9cbe..fd3c2b9fc 100644 --- a/packages/agent/src/index.ts +++ b/packages/agent/src/index.ts @@ -1,5 +1,44 @@ -// Import the default pi-ai entrypoint so that all built-in providers register -// automatically. Unlike "@earendil-works/pi-ai/base" which does not. -import "@earendil-works/pi-ai"; - -export * from "./base.ts"; +// Core Agent +export * from "./agent.ts"; +// Loop functions +export * from "./agent-loop.ts"; +export * from "./harness/agent-harness.ts"; +export { + type BranchPreparation, + type BranchSummaryDetails, + type CollectEntriesResult, + collectEntriesForBranchSummary, + generateBranchSummary, + prepareBranchEntries, +} from "./harness/compaction/branch-summarization.ts"; +export { + calculateContextTokens, + compact, + DEFAULT_COMPACTION_SETTINGS, + estimateContextTokens, + estimateTokens, + findCutPoint, + findTurnStartIndex, + generateSummary, + getLastAssistantUsage, + prepareCompaction, + serializeConversation, + shouldCompact, +} from "./harness/compaction/compaction.ts"; +export * from "./harness/messages.ts"; +export * from "./harness/prompt-templates.ts"; +export * from "./harness/session/jsonl-repo.ts"; +export * from "./harness/session/memory-repo.ts"; +export * from "./harness/session/repo-utils.ts"; +export * from "./harness/session/session.ts"; +export { uuidv7 } from "./harness/session/uuid.ts"; +export * from "./harness/skills.ts"; +export * from "./harness/system-prompt.ts"; +// Harness +export * from "./harness/types.ts"; +export * from "./harness/utils/shell-output.ts"; +export * from "./harness/utils/truncate.ts"; +// Proxy utilities +export * from "./proxy.ts"; +// Types +export * from "./types.ts"; diff --git a/packages/agent/src/proxy.ts b/packages/agent/src/proxy.ts index 8e5cb2c2a..5f0925c91 100644 --- a/packages/agent/src/proxy.ts +++ b/packages/agent/src/proxy.ts @@ -14,7 +14,7 @@ import { type SimpleStreamOptions, type StopReason, type ToolCall, -} from "@earendil-works/pi-ai/base"; +} from "@earendil-works/pi-ai"; // Create stream class matching ProxyMessageEventStream class ProxyMessageEventStream extends EventStream { diff --git a/packages/agent/src/types.ts b/packages/agent/src/types.ts index 541d8d63b..cb99a79a8 100644 --- a/packages/agent/src/types.ts +++ b/packages/agent/src/types.ts @@ -9,7 +9,7 @@ import type { TextContent, Tool, ToolResultMessage, -} from "@earendil-works/pi-ai/base"; +} from "@earendil-works/pi-ai"; import type { Static, TSchema } from "typebox"; /** diff --git a/packages/agent/vitest.config.ts b/packages/agent/vitest.config.ts index 297fbd502..bcc497faa 100644 --- a/packages/agent/vitest.config.ts +++ b/packages/agent/vitest.config.ts @@ -1,12 +1,6 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ - resolve: { - alias: { - "@earendil-works/pi-ai/base": new URL("../ai/src/base.ts", import.meta.url).pathname, - "@earendil-works/pi-ai": new URL("../ai/src/index.ts", import.meta.url).pathname, - }, - }, test: { globals: true, environment: "node", diff --git a/packages/agent/vitest.harness.config.ts b/packages/agent/vitest.harness.config.ts index 37acc34d9..9421e5a99 100644 --- a/packages/agent/vitest.harness.config.ts +++ b/packages/agent/vitest.harness.config.ts @@ -1,12 +1,6 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ - resolve: { - alias: { - "@earendil-works/pi-ai/base": new URL("../ai/src/base.ts", import.meta.url).pathname, - "@earendil-works/pi-ai": new URL("../ai/src/index.ts", import.meta.url).pathname, - }, - }, test: { globals: true, environment: "node", diff --git a/packages/ai/README.md b/packages/ai/README.md index bec437aa5..428ef259b 100644 --- a/packages/ai/README.md +++ b/packages/ai/README.md @@ -8,7 +8,6 @@ Unified LLM API with automatic model discovery, provider configuration, token an - [Supported Providers](#supported-providers) - [Installation](#installation) -- [Base Entry Point](#base-entry-point) - [Quick Start](#quick-start) - [Tools](#tools) - [Defining Tools](#defining-tools) @@ -89,30 +88,6 @@ npm install @earendil-works/pi-ai TypeBox exports are re-exported from `@earendil-works/pi-ai`: `Type`, `Static`, and `TSchema`. -## Base Entry Point - -Use `@earendil-works/pi-ai/base` when a bundler should include only explicitly selected transport implementations. The base entry point exposes model discovery, registries, generic dispatch, environment-key helpers, and provider-neutral utilities without registering built-in transports during module initialization. Generic dispatch still resolves configured environment keys when called. - -Import each selected transport directly and call its `register()` function: - -```typescript -import { getModel, streamSimple } from '@earendil-works/pi-ai/base'; -import { register as registerAnthropic } from '@earendil-works/pi-ai/anthropic'; - -registerAnthropic(); - -const model = getModel('anthropic', 'claude-sonnet-4-6'); -const response = await streamSimple(model, { - messages: [{ role: 'user', content: 'Hello!' }] -}, { - apiKey: process.env.ANTHROPIC_API_KEY -}).result(); -``` - -Direct transport imports bundle their implementation and SDK code. Register only the transports used by the application. For example, OpenRouter, Groq, xAI, and DeepSeek chat models share the `@earendil-works/pi-ai/openai-completions` transport. - -Use `@earendil-works/pi-ai` for the batteries-included behavior. The root entry point remains backward-compatible: it registers lazy wrappers for all built-in transports and resolves environment API keys automatically during dispatch. - ## Quick Start ```typescript @@ -463,7 +438,7 @@ Do not use `stream()` or `complete()` for image generation. Image generation is ### Basic Image Generation ```typescript -import { getImageModel, generateImages } from '@earendil-works/pi-ai'; +import { getImageModel, generateImages } from '@mariozechner/pi-ai'; const model = getImageModel('openrouter', 'google/gemini-2.5-flash-image'); @@ -1123,7 +1098,6 @@ const response = await complete(model, { - OAuth login flows are not supported in browser environments. Use the `@earendil-works/pi-ai/oauth` entry point in Node.js. - In browser builds, Bedrock can still appear in model lists. Calls to Bedrock models fail at runtime. - Use a server-side proxy or backend service if you need Bedrock or OAuth-based auth from a web app. -- Use `@earendil-works/pi-ai/base` plus explicit direct transport registration when a browser bundle should exclude unused provider SDK implementations. ### Environment Variables (Node.js only) @@ -1360,7 +1334,6 @@ Create a new provider file (for example `amazon-bedrock.ts`) that exports: - `stream()` function returning `AssistantMessageEventStream` - `streamSimple()` for `SimpleStreamOptions` mapping -- `register()` for explicit direct transport registration from `@earendil-works/pi-ai/base` - Provider-specific options interface - Message conversion functions to transform `Context` to provider format - Tool conversion if the provider supports tools @@ -1371,8 +1344,7 @@ Create a new provider file (for example `amazon-bedrock.ts`) that exports: - Register the API with `registerApiProvider()` - Add a package subpath export in `package.json` for the provider module (`./dist/providers/.js`) - Add lazy loader wrappers in `src/providers/register-builtins.ts`, do not statically import provider implementation modules there -- Add any root-level `export type` re-exports in `src/index.ts` and `src/base.ts` that should remain available from `@earendil-works/pi-ai` and `@earendil-works/pi-ai/base` -- Keep `src/base.ts` free of built-in registration imports +- Add any root-level `export type` re-exports in `src/index.ts` that should remain available from `@earendil-works/pi-ai` - Add credential detection in `env-api-keys.ts` for the new provider - Ensure `streamSimple` handles auth lookup via `getEnvApiKey()` or provider-specific auth diff --git a/packages/ai/package.json b/packages/ai/package.json index 240209aea..5ba971333 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -10,14 +10,6 @@ "types": "./dist/index.d.ts", "import": "./dist/index.js" }, - "./base": { - "types": "./dist/base.d.ts", - "import": "./dist/base.js" - }, - "./amazon-bedrock": { - "types": "./dist/providers/amazon-bedrock.d.ts", - "import": "./dist/providers/amazon-bedrock.js" - }, "./anthropic": { "types": "./dist/providers/anthropic.d.ts", "import": "./dist/providers/anthropic.js" @@ -50,10 +42,6 @@ "types": "./dist/providers/openai-responses.d.ts", "import": "./dist/providers/openai-responses.js" }, - "./openrouter-images": { - "types": "./dist/providers/images/openrouter.d.ts", - "import": "./dist/providers/images/openrouter.js" - }, "./oauth": { "types": "./dist/oauth.d.ts", "import": "./dist/oauth.js" diff --git a/packages/ai/src/base.ts b/packages/ai/src/base.ts deleted file mode 100644 index f1a8a1101..000000000 --- a/packages/ai/src/base.ts +++ /dev/null @@ -1,45 +0,0 @@ -export type { Static, TSchema } from "typebox"; -export { Type } from "typebox"; - -export * from "./api-registry.ts"; -export * from "./env-api-keys.ts"; -export * from "./image-models.ts"; -export * from "./images.ts"; -export * from "./images-api-registry.ts"; -export * from "./models.ts"; -export type { BedrockOptions, BedrockThinkingDisplay } from "./providers/amazon-bedrock.ts"; -export type { AnthropicEffort, AnthropicOptions, AnthropicThinkingDisplay } from "./providers/anthropic.ts"; -export type { AzureOpenAIResponsesOptions } from "./providers/azure-openai-responses.ts"; -export * from "./providers/faux.ts"; -export type { GoogleOptions } from "./providers/google.ts"; -export type { GoogleThinkingLevel } from "./providers/google-shared.ts"; -export type { GoogleVertexOptions } from "./providers/google-vertex.ts"; -export type { MistralOptions } from "./providers/mistral.ts"; -export type { - OpenAICodexResponsesOptions, - OpenAICodexWebSocketDebugStats, -} from "./providers/openai-codex-responses.ts"; -export type { OpenAICompletionsOptions } from "./providers/openai-completions.ts"; -export type { OpenAIResponsesOptions } from "./providers/openai-responses.ts"; -export * from "./session-resources.ts"; -export * from "./stream.ts"; -export * from "./types.ts"; -export * from "./utils/diagnostics.ts"; -export * from "./utils/event-stream.ts"; -export * from "./utils/json-parse.ts"; -export type { - OAuthAuthInfo, - OAuthCredentials, - OAuthDeviceCodeInfo, - OAuthLoginCallbacks, - OAuthPrompt, - OAuthProvider, - OAuthProviderId, - OAuthProviderInfo, - OAuthProviderInterface, - OAuthSelectOption, - OAuthSelectPrompt, -} from "./utils/oauth/types.ts"; -export * from "./utils/overflow.ts"; -export * from "./utils/typebox-helpers.ts"; -export * from "./utils/validation.ts"; diff --git a/packages/ai/src/bedrock-provider.ts b/packages/ai/src/bedrock-provider.ts index f5aa3998c..cf08b33ef 100644 --- a/packages/ai/src/bedrock-provider.ts +++ b/packages/ai/src/bedrock-provider.ts @@ -1,6 +1,4 @@ -import { register, streamBedrock, streamSimpleBedrock } from "./providers/amazon-bedrock.ts"; - -export { register }; +import { streamBedrock, streamSimpleBedrock } from "./providers/amazon-bedrock.ts"; export const bedrockProviderModule = { streamBedrock, diff --git a/packages/ai/src/images-api-registry.ts b/packages/ai/src/images-api-registry.ts index 5335a9267..4758439bf 100644 --- a/packages/ai/src/images-api-registry.ts +++ b/packages/ai/src/images-api-registry.ts @@ -51,7 +51,3 @@ export function registerImagesApiProvider, project: string, diff --git a/packages/ai/src/providers/google.ts b/packages/ai/src/providers/google.ts index 62ad9cfd3..a270792af 100644 --- a/packages/ai/src/providers/google.ts +++ b/packages/ai/src/providers/google.ts @@ -4,7 +4,6 @@ import { GoogleGenAI, type ThinkingConfig, } from "@google/genai"; -import { registerApiProvider } from "../api-registry.ts"; import { calculateCost, clampThinkingLevel } from "../models.ts"; import type { Api, @@ -316,14 +315,6 @@ export const streamSimpleGoogle: StreamFunction<"google-generative-ai", SimpleSt } satisfies GoogleOptions); }; -export function register(): void { - registerApiProvider({ - api: "google-generative-ai", - stream: streamGoogle, - streamSimple: streamSimpleGoogle, - }); -} - function createClient( model: Model<"google-generative-ai">, apiKey?: string, diff --git a/packages/ai/src/providers/images/openrouter.ts b/packages/ai/src/providers/images/openrouter.ts index d1b2c7e8c..54caeaf05 100644 --- a/packages/ai/src/providers/images/openrouter.ts +++ b/packages/ai/src/providers/images/openrouter.ts @@ -6,7 +6,6 @@ import type { ChatCompletionContentPartText, ChatCompletionCreateParamsNonStreaming, } from "openai/resources/chat/completions.js"; -import { registerImagesApiProvider } from "../../images-api-registry.ts"; import type { AssistantImages, ImageContent, @@ -104,13 +103,6 @@ export const generateImagesOpenRouter: ImagesFunction<"openrouter-images", Image } }; -export function register(): void { - registerImagesApiProvider({ - api: "openrouter-images", - generateImages: generateImagesOpenRouter, - }); -} - function createClient( model: ImagesModel<"openrouter-images">, apiKey: string, diff --git a/packages/ai/src/providers/images/register-builtins.ts b/packages/ai/src/providers/images/register-builtins.ts new file mode 100644 index 000000000..e3decbb99 --- /dev/null +++ b/packages/ai/src/providers/images/register-builtins.ts @@ -0,0 +1,50 @@ +import { registerImagesApiProvider } from "../../images-api-registry.ts"; +import type { AssistantImages, ImagesContext, ImagesFunction, ImagesModel, ImagesOptions } from "../../types.ts"; +import type { generateImagesOpenRouter as generateImagesOpenRouterFunction } from "./openrouter.ts"; + +interface OpenRouterImagesProviderModule { + generateImagesOpenRouter: typeof generateImagesOpenRouterFunction; +} + +let openRouterImagesProviderModulePromise: Promise | undefined; + +function createLazyLoadErrorImages(model: ImagesModel<"openrouter-images">, error: unknown): AssistantImages { + return { + api: model.api, + provider: model.provider, + model: model.id, + output: [], + stopReason: "error", + errorMessage: error instanceof Error ? error.message : String(error), + timestamp: Date.now(), + }; +} + +function loadOpenRouterImagesProviderModule(): Promise { + openRouterImagesProviderModulePromise ||= import("./openrouter.ts").then( + (module) => module as OpenRouterImagesProviderModule, + ); + return openRouterImagesProviderModulePromise; +} + +export const generateImagesOpenRouter: ImagesFunction<"openrouter-images", ImagesOptions> = async ( + model: ImagesModel<"openrouter-images">, + context: ImagesContext, + options?: ImagesOptions, +) => { + try { + const module = await loadOpenRouterImagesProviderModule(); + return await module.generateImagesOpenRouter(model, context, options); + } catch (error) { + return createLazyLoadErrorImages(model, error); + } +}; + +export function registerBuiltInImagesApiProviders(): void { + registerImagesApiProvider({ + api: "openrouter-images", + generateImages: generateImagesOpenRouter, + }); +} + +registerBuiltInImagesApiProviders(); diff --git a/packages/ai/src/providers/mistral.ts b/packages/ai/src/providers/mistral.ts index ba331af4d..6a1322368 100644 --- a/packages/ai/src/providers/mistral.ts +++ b/packages/ai/src/providers/mistral.ts @@ -6,7 +6,6 @@ import type { ContentChunk, FunctionTool, } from "@mistralai/mistralai/models/components"; -import { registerApiProvider } from "../api-registry.ts"; import { calculateCost, clampThinkingLevel } from "../models.ts"; import type { AssistantMessage, @@ -131,14 +130,6 @@ export const streamSimpleMistral: StreamFunction<"mistral-conversations", Simple } satisfies MistralOptions); }; -export function register(): void { - registerApiProvider({ - api: "mistral-conversations", - stream: streamMistral, - streamSimple: streamSimpleMistral, - }); -} - function createOutput(model: Model<"mistral-conversations">): AssistantMessage { return { role: "assistant", diff --git a/packages/ai/src/providers/openai-codex-responses.ts b/packages/ai/src/providers/openai-codex-responses.ts index 1bc2879ae..19a2f5d77 100644 --- a/packages/ai/src/providers/openai-codex-responses.ts +++ b/packages/ai/src/providers/openai-codex-responses.ts @@ -20,7 +20,6 @@ if (typeof process !== "undefined" && (process.versions?.node || process.version }); } -import { registerApiProvider } from "../api-registry.ts"; import { clampThinkingLevel } from "../models.ts"; import { registerSessionResourceCleanup } from "../session-resources.ts"; import type { @@ -429,14 +428,6 @@ export const streamSimpleOpenAICodexResponses: StreamFunction<"openai-codex-resp } satisfies OpenAICodexResponsesOptions); }; -export function register(): void { - registerApiProvider({ - api: "openai-codex-responses", - stream: streamOpenAICodexResponses, - streamSimple: streamSimpleOpenAICodexResponses, - }); -} - // ============================================================================ // Request Building // ============================================================================ diff --git a/packages/ai/src/providers/openai-completions.ts b/packages/ai/src/providers/openai-completions.ts index b257f96e2..2909678c8 100644 --- a/packages/ai/src/providers/openai-completions.ts +++ b/packages/ai/src/providers/openai-completions.ts @@ -10,7 +10,6 @@ import type { ChatCompletionSystemMessageParam, ChatCompletionToolMessageParam, } from "openai/resources/chat/completions.js"; -import { registerApiProvider } from "../api-registry.ts"; import { calculateCost, clampThinkingLevel } from "../models.ts"; import type { AssistantMessage, @@ -486,14 +485,6 @@ export const streamSimpleOpenAICompletions: StreamFunction<"openai-completions", } satisfies OpenAICompletionsOptions); }; -export function register(): void { - registerApiProvider({ - api: "openai-completions", - stream: streamOpenAICompletions, - streamSimple: streamSimpleOpenAICompletions, - }); -} - function createClient( model: Model<"openai-completions">, context: Context, diff --git a/packages/ai/src/providers/openai-responses.ts b/packages/ai/src/providers/openai-responses.ts index b1692978e..014233d5c 100644 --- a/packages/ai/src/providers/openai-responses.ts +++ b/packages/ai/src/providers/openai-responses.ts @@ -1,6 +1,5 @@ import OpenAI from "openai"; import type { ResponseCreateParamsStreaming } from "openai/resources/responses/responses.js"; -import { registerApiProvider } from "../api-registry.ts"; import { clampThinkingLevel } from "../models.ts"; import type { Api, @@ -182,14 +181,6 @@ export const streamSimpleOpenAIResponses: StreamFunction<"openai-responses", Sim } satisfies OpenAIResponsesOptions); }; -export function register(): void { - registerApiProvider({ - api: "openai-responses", - stream: streamOpenAIResponses, - streamSimple: streamSimpleOpenAIResponses, - }); -} - function createClient( model: Model<"openai-responses">, context: Context, diff --git a/packages/ai/src/providers/register-builtins.ts b/packages/ai/src/providers/register-builtins.ts index 6192f8a2f..8fdcaaf0f 100644 --- a/packages/ai/src/providers/register-builtins.ts +++ b/packages/ai/src/providers/register-builtins.ts @@ -1,14 +1,9 @@ -import { type ApiProvider, clearApiProviders, getApiProvider, registerApiProvider } from "../api-registry.ts"; -import { getImagesApiProvider, type ImagesApiProvider, registerImagesApiProvider } from "../images-api-registry.ts"; +import { clearApiProviders, registerApiProvider } from "../api-registry.ts"; import type { Api, - AssistantImages, AssistantMessage, AssistantMessageEvent, - ImagesApi, - ImagesContext, - ImagesModel, - ImagesOptions, + Context, Model, SimpleStreamOptions, StreamFunction, @@ -25,47 +20,70 @@ import type { OpenAICodexResponsesOptions } from "./openai-codex-responses.ts"; import type { OpenAICompletionsOptions } from "./openai-completions.ts"; import type { OpenAIResponsesOptions } from "./openai-responses.ts"; -interface RegisteringProviderModule { - register(): void; +interface LazyProviderModule< + TApi extends Api, + TOptions extends StreamOptions, + TSimpleOptions extends SimpleStreamOptions, +> { + stream: (model: Model, context: Context, options?: TOptions) => AsyncIterable; + streamSimple: ( + model: Model, + context: Context, + options?: TSimpleOptions, + ) => AsyncIterable; } -function createLazyLoadErrorImages(model: ImagesModel<"openrouter-images">, error: unknown): AssistantImages { - return { - api: model.api, - provider: model.provider, - model: model.id, - output: [], - stopReason: "error", - errorMessage: error instanceof Error ? error.message : String(error), - timestamp: Date.now(), - }; +interface AnthropicProviderModule { + streamAnthropic: StreamFunction<"anthropic-messages", AnthropicOptions>; + streamSimpleAnthropic: StreamFunction<"anthropic-messages", SimpleStreamOptions>; } -function createLazyImagesApiProvider( - api: TApi, - loadModule: () => Promise, -): ImagesApiProvider { - return { - api, - generateImages: async (model: ImagesModel, context: ImagesContext, options?: TOptions) => { - try { - const module = await loadModule(); - module.register(); - const provider = getImagesApiProvider(api); - if (!provider) { - throw new Error(`No API provider registered for api: ${api}`); - } - return await provider.generateImages(model, context, options); - } catch (error) { - return createLazyLoadErrorImages(model as ImagesModel<"openrouter-images">, error); - } - }, - }; +interface AzureOpenAIResponsesProviderModule { + streamAzureOpenAIResponses: StreamFunction<"azure-openai-responses", AzureOpenAIResponsesOptions>; + streamSimpleAzureOpenAIResponses: StreamFunction<"azure-openai-responses", SimpleStreamOptions>; +} + +interface GoogleProviderModule { + streamGoogle: StreamFunction<"google-generative-ai", GoogleOptions>; + streamSimpleGoogle: StreamFunction<"google-generative-ai", SimpleStreamOptions>; +} + +interface GoogleVertexProviderModule { + streamGoogleVertex: StreamFunction<"google-vertex", GoogleVertexOptions>; + streamSimpleGoogleVertex: StreamFunction<"google-vertex", SimpleStreamOptions>; +} + +interface MistralProviderModule { + streamMistral: StreamFunction<"mistral-conversations", MistralOptions>; + streamSimpleMistral: StreamFunction<"mistral-conversations", SimpleStreamOptions>; +} + +interface OpenAICodexResponsesProviderModule { + streamOpenAICodexResponses: StreamFunction<"openai-codex-responses", OpenAICodexResponsesOptions>; + streamSimpleOpenAICodexResponses: StreamFunction<"openai-codex-responses", SimpleStreamOptions>; +} + +interface OpenAICompletionsProviderModule { + streamOpenAICompletions: StreamFunction<"openai-completions", OpenAICompletionsOptions>; + streamSimpleOpenAICompletions: StreamFunction<"openai-completions", SimpleStreamOptions>; +} + +interface OpenAIResponsesProviderModule { + streamOpenAIResponses: StreamFunction<"openai-responses", OpenAIResponsesOptions>; + streamSimpleOpenAIResponses: StreamFunction<"openai-responses", SimpleStreamOptions>; } interface BedrockProviderModule { - streamBedrock: StreamFunction<"bedrock-converse-stream", BedrockOptions>; - streamSimpleBedrock: StreamFunction<"bedrock-converse-stream", SimpleStreamOptions>; + streamBedrock: ( + model: Model<"bedrock-converse-stream">, + context: Context, + options?: BedrockOptions, + ) => AsyncIterable; + streamSimpleBedrock: ( + model: Model<"bedrock-converse-stream">, + context: Context, + options?: SimpleStreamOptions, + ) => AsyncIterable; } const importNodeOnlyProvider = (specifier: string): Promise => { @@ -73,10 +91,42 @@ const importNodeOnlyProvider = (specifier: string): Promise => { return import(runtimeSpecifier); }; -let bedrockProviderModuleOverride: BedrockProviderModule | undefined; +let anthropicProviderModulePromise: + | Promise> + | undefined; +let azureOpenAIResponsesProviderModulePromise: + | Promise> + | undefined; +let googleProviderModulePromise: + | Promise> + | undefined; +let googleVertexProviderModulePromise: + | Promise> + | undefined; +let mistralProviderModulePromise: + | Promise> + | undefined; +let openAICodexResponsesProviderModulePromise: + | Promise> + | undefined; +let openAICompletionsProviderModulePromise: + | Promise> + | undefined; +let openAIResponsesProviderModulePromise: + | Promise> + | undefined; +let bedrockProviderModuleOverride: + | LazyProviderModule<"bedrock-converse-stream", BedrockOptions, SimpleStreamOptions> + | undefined; +let bedrockProviderModulePromise: + | Promise> + | undefined; export function setBedrockProviderModule(module: BedrockProviderModule): void { - bedrockProviderModuleOverride = module; + bedrockProviderModuleOverride = { + stream: module.streamBedrock, + streamSimple: module.streamSimpleBedrock, + }; } function forwardStream(target: AssistantMessageEventStream, source: AsyncIterable): void { @@ -109,29 +159,15 @@ function createLazyLoadErrorMessage(model: Model, error: }; } -async function loadAndRegisterProvider( - api: TApi, - loadModule: () => Promise, -) { - const module = await loadModule(); - module.register(); - const provider = getApiProvider(api); - if (!provider) { - throw new Error(`No API provider registered for api: ${api}`); - } - return provider; -} - -function createLazyStream( - api: TApi, - loadModule: () => Promise, +function createLazyStream( + loadModule: () => Promise>, ): StreamFunction { return (model, context, options) => { const outer = new AssistantMessageEventStream(); - loadAndRegisterProvider(api, loadModule) - .then((provider) => { - const inner = provider.stream(model, context, options); + loadModule() + .then((module) => { + const inner = module.stream(model, context, options); forwardStream(outer, inner); }) .catch((error) => { @@ -144,16 +180,17 @@ function createLazyStream( }; } -function createLazySimpleStream( - api: TApi, - loadModule: () => Promise, -): StreamFunction { +function createLazySimpleStream< + TApi extends Api, + TOptions extends StreamOptions, + TSimpleOptions extends SimpleStreamOptions, +>(loadModule: () => Promise>): StreamFunction { return (model, context, options) => { const outer = new AssistantMessageEventStream(); - loadAndRegisterProvider(api, loadModule) - .then((provider) => { - const inner = provider.streamSimple(model, context, options); + loadModule() + .then((module) => { + const inner = module.streamSimple(model, context, options); forwardStream(outer, inner); }) .catch((error) => { @@ -166,112 +203,199 @@ function createLazySimpleStream( }; } -function createLazyApiProvider( - api: TApi, - loadModule: () => Promise, -): ApiProvider { - return { - api, - stream: createLazyStream(api, loadModule), - streamSimple: createLazySimpleStream(api, loadModule), - }; -} - -function registerBedrockProviderModule(module: BedrockProviderModule): void { - registerApiProvider({ - api: "bedrock-converse-stream", - stream: module.streamBedrock, - streamSimple: module.streamSimpleBedrock, +function loadAnthropicProviderModule(): Promise< + LazyProviderModule<"anthropic-messages", AnthropicOptions, SimpleStreamOptions> +> { + anthropicProviderModulePromise ||= import("./anthropic.ts").then((module) => { + const provider = module as AnthropicProviderModule; + return { + stream: provider.streamAnthropic, + streamSimple: provider.streamSimpleAnthropic, + }; }); + return anthropicProviderModulePromise; } -function loadBedrockProviderModule(): Promise { - const module = bedrockProviderModuleOverride; - if (module) { - return Promise.resolve({ register: () => registerBedrockProviderModule(module) }); +function loadAzureOpenAIResponsesProviderModule(): Promise< + LazyProviderModule<"azure-openai-responses", AzureOpenAIResponsesOptions, SimpleStreamOptions> +> { + azureOpenAIResponsesProviderModulePromise ||= import("./azure-openai-responses.ts").then((module) => { + const provider = module as AzureOpenAIResponsesProviderModule; + return { + stream: provider.streamAzureOpenAIResponses, + streamSimple: provider.streamSimpleAzureOpenAIResponses, + }; + }); + return azureOpenAIResponsesProviderModulePromise; +} + +function loadGoogleProviderModule(): Promise< + LazyProviderModule<"google-generative-ai", GoogleOptions, SimpleStreamOptions> +> { + googleProviderModulePromise ||= import("./google.ts").then((module) => { + const provider = module as GoogleProviderModule; + return { + stream: provider.streamGoogle, + streamSimple: provider.streamSimpleGoogle, + }; + }); + return googleProviderModulePromise; +} + +function loadGoogleVertexProviderModule(): Promise< + LazyProviderModule<"google-vertex", GoogleVertexOptions, SimpleStreamOptions> +> { + googleVertexProviderModulePromise ||= import("./google-vertex.ts").then((module) => { + const provider = module as GoogleVertexProviderModule; + return { + stream: provider.streamGoogleVertex, + streamSimple: provider.streamSimpleGoogleVertex, + }; + }); + return googleVertexProviderModulePromise; +} + +function loadMistralProviderModule(): Promise< + LazyProviderModule<"mistral-conversations", MistralOptions, SimpleStreamOptions> +> { + mistralProviderModulePromise ||= import("./mistral.ts").then((module) => { + const provider = module as MistralProviderModule; + return { + stream: provider.streamMistral, + streamSimple: provider.streamSimpleMistral, + }; + }); + return mistralProviderModulePromise; +} + +function loadOpenAICodexResponsesProviderModule(): Promise< + LazyProviderModule<"openai-codex-responses", OpenAICodexResponsesOptions, SimpleStreamOptions> +> { + openAICodexResponsesProviderModulePromise ||= import("./openai-codex-responses.ts").then((module) => { + const provider = module as OpenAICodexResponsesProviderModule; + return { + stream: provider.streamOpenAICodexResponses, + streamSimple: provider.streamSimpleOpenAICodexResponses, + }; + }); + return openAICodexResponsesProviderModulePromise; +} + +function loadOpenAICompletionsProviderModule(): Promise< + LazyProviderModule<"openai-completions", OpenAICompletionsOptions, SimpleStreamOptions> +> { + openAICompletionsProviderModulePromise ||= import("./openai-completions.ts").then((module) => { + const provider = module as OpenAICompletionsProviderModule; + return { + stream: provider.streamOpenAICompletions, + streamSimple: provider.streamSimpleOpenAICompletions, + }; + }); + return openAICompletionsProviderModulePromise; +} + +function loadOpenAIResponsesProviderModule(): Promise< + LazyProviderModule<"openai-responses", OpenAIResponsesOptions, SimpleStreamOptions> +> { + openAIResponsesProviderModulePromise ||= import("./openai-responses.ts").then((module) => { + const provider = module as OpenAIResponsesProviderModule; + return { + stream: provider.streamOpenAIResponses, + streamSimple: provider.streamSimpleOpenAIResponses, + }; + }); + return openAIResponsesProviderModulePromise; +} + +function loadBedrockProviderModule(): Promise< + LazyProviderModule<"bedrock-converse-stream", BedrockOptions, SimpleStreamOptions> +> { + if (bedrockProviderModuleOverride) { + return Promise.resolve(bedrockProviderModuleOverride); } - return importNodeOnlyProvider("./amazon-bedrock.ts").then((provider) => provider as RegisteringProviderModule); + bedrockProviderModulePromise ||= importNodeOnlyProvider("./amazon-bedrock.ts").then((module) => { + const provider = module as BedrockProviderModule; + return { + stream: provider.streamBedrock, + streamSimple: provider.streamSimpleBedrock, + }; + }); + return bedrockProviderModulePromise; } -const anthropicProvider = createLazyApiProvider<"anthropic-messages", AnthropicOptions>( - "anthropic-messages", - () => import("./anthropic.ts"), -); -const azureOpenAIResponsesProvider = createLazyApiProvider<"azure-openai-responses", AzureOpenAIResponsesOptions>( - "azure-openai-responses", - () => import("./azure-openai-responses.ts"), -); -const googleProvider = createLazyApiProvider<"google-generative-ai", GoogleOptions>( - "google-generative-ai", - () => import("./google.ts"), -); -const googleVertexProvider = createLazyApiProvider<"google-vertex", GoogleVertexOptions>( - "google-vertex", - () => import("./google-vertex.ts"), -); -const mistralProvider = createLazyApiProvider<"mistral-conversations", MistralOptions>( - "mistral-conversations", - () => import("./mistral.ts"), -); -const openAICodexResponsesProvider = createLazyApiProvider<"openai-codex-responses", OpenAICodexResponsesOptions>( - "openai-codex-responses", - () => import("./openai-codex-responses.ts"), -); -const openAICompletionsProvider = createLazyApiProvider<"openai-completions", OpenAICompletionsOptions>( - "openai-completions", - () => import("./openai-completions.ts"), -); -const openAIResponsesProvider = createLazyApiProvider<"openai-responses", OpenAIResponsesOptions>( - "openai-responses", - () => import("./openai-responses.ts"), -); -const bedrockProvider = createLazyApiProvider<"bedrock-converse-stream", BedrockOptions>( - "bedrock-converse-stream", - loadBedrockProviderModule, -); -const openRouterImagesProvider = createLazyImagesApiProvider( - "openrouter-images", - () => import("./images/openrouter.ts"), -); - -export const generateImagesOpenRouter = openRouterImagesProvider.generateImages; -export const streamAnthropic = anthropicProvider.stream; -export const streamSimpleAnthropic = anthropicProvider.streamSimple; -export const streamAzureOpenAIResponses = azureOpenAIResponsesProvider.stream; -export const streamSimpleAzureOpenAIResponses = azureOpenAIResponsesProvider.streamSimple; -export const streamGoogle = googleProvider.stream; -export const streamSimpleGoogle = googleProvider.streamSimple; -export const streamGoogleVertex = googleVertexProvider.stream; -export const streamSimpleGoogleVertex = googleVertexProvider.streamSimple; -export const streamMistral = mistralProvider.stream; -export const streamSimpleMistral = mistralProvider.streamSimple; -export const streamOpenAICodexResponses = openAICodexResponsesProvider.stream; -export const streamSimpleOpenAICodexResponses = openAICodexResponsesProvider.streamSimple; -export const streamOpenAICompletions = openAICompletionsProvider.stream; -export const streamSimpleOpenAICompletions = openAICompletionsProvider.streamSimple; -export const streamOpenAIResponses = openAIResponsesProvider.stream; -export const streamSimpleOpenAIResponses = openAIResponsesProvider.streamSimple; - -const registerBuiltInApiProviderFunctions = [ - () => registerApiProvider(anthropicProvider), - () => registerApiProvider(openAICompletionsProvider), - () => registerApiProvider(mistralProvider), - () => registerApiProvider(openAIResponsesProvider), - () => registerApiProvider(azureOpenAIResponsesProvider), - () => registerApiProvider(openAICodexResponsesProvider), - () => registerApiProvider(googleProvider), - () => registerApiProvider(googleVertexProvider), - () => registerApiProvider(bedrockProvider), -]; - -export function registerBuiltInImagesApiProviders(): void { - registerImagesApiProvider(openRouterImagesProvider); -} +export const streamAnthropic = createLazyStream(loadAnthropicProviderModule); +export const streamSimpleAnthropic = createLazySimpleStream(loadAnthropicProviderModule); +export const streamAzureOpenAIResponses = createLazyStream(loadAzureOpenAIResponsesProviderModule); +export const streamSimpleAzureOpenAIResponses = createLazySimpleStream(loadAzureOpenAIResponsesProviderModule); +export const streamGoogle = createLazyStream(loadGoogleProviderModule); +export const streamSimpleGoogle = createLazySimpleStream(loadGoogleProviderModule); +export const streamGoogleVertex = createLazyStream(loadGoogleVertexProviderModule); +export const streamSimpleGoogleVertex = createLazySimpleStream(loadGoogleVertexProviderModule); +export const streamMistral = createLazyStream(loadMistralProviderModule); +export const streamSimpleMistral = createLazySimpleStream(loadMistralProviderModule); +export const streamOpenAICodexResponses = createLazyStream(loadOpenAICodexResponsesProviderModule); +export const streamSimpleOpenAICodexResponses = createLazySimpleStream(loadOpenAICodexResponsesProviderModule); +export const streamOpenAICompletions = createLazyStream(loadOpenAICompletionsProviderModule); +export const streamSimpleOpenAICompletions = createLazySimpleStream(loadOpenAICompletionsProviderModule); +export const streamOpenAIResponses = createLazyStream(loadOpenAIResponsesProviderModule); +export const streamSimpleOpenAIResponses = createLazySimpleStream(loadOpenAIResponsesProviderModule); +const streamBedrockLazy = createLazyStream(loadBedrockProviderModule); +const streamSimpleBedrockLazy = createLazySimpleStream(loadBedrockProviderModule); export function registerBuiltInApiProviders(): void { - for (const register of registerBuiltInApiProviderFunctions) { - register(); - } + registerApiProvider({ + api: "anthropic-messages", + stream: streamAnthropic, + streamSimple: streamSimpleAnthropic, + }); + + registerApiProvider({ + api: "openai-completions", + stream: streamOpenAICompletions, + streamSimple: streamSimpleOpenAICompletions, + }); + + registerApiProvider({ + api: "mistral-conversations", + stream: streamMistral, + streamSimple: streamSimpleMistral, + }); + + registerApiProvider({ + api: "openai-responses", + stream: streamOpenAIResponses, + streamSimple: streamSimpleOpenAIResponses, + }); + + registerApiProvider({ + api: "azure-openai-responses", + stream: streamAzureOpenAIResponses, + streamSimple: streamSimpleAzureOpenAIResponses, + }); + + registerApiProvider({ + api: "openai-codex-responses", + stream: streamOpenAICodexResponses, + streamSimple: streamSimpleOpenAICodexResponses, + }); + + registerApiProvider({ + api: "google-generative-ai", + stream: streamGoogle, + streamSimple: streamSimpleGoogle, + }); + + registerApiProvider({ + api: "google-vertex", + stream: streamGoogleVertex, + streamSimple: streamSimpleGoogleVertex, + }); + + registerApiProvider({ + api: "bedrock-converse-stream", + stream: streamBedrockLazy, + streamSimple: streamSimpleBedrockLazy, + }); } export function resetApiProviders(): void { diff --git a/packages/ai/src/stream.ts b/packages/ai/src/stream.ts index 7b6b28c62..3f333d9e1 100644 --- a/packages/ai/src/stream.ts +++ b/packages/ai/src/stream.ts @@ -1,3 +1,5 @@ +import "./providers/register-builtins.ts"; + import { getApiProvider } from "./api-registry.ts"; import { getEnvApiKey } from "./env-api-keys.ts"; import type { @@ -11,6 +13,8 @@ import type { StreamOptions, } from "./types.ts"; +export { getEnvApiKey } from "./env-api-keys.ts"; + function hasExplicitApiKey(apiKey: string | undefined): apiKey is string { return typeof apiKey === "string" && apiKey.trim().length > 0; } diff --git a/packages/ai/test/abort.test.ts b/packages/ai/test/abort.test.ts index 303044ad8..27c274aac 100644 --- a/packages/ai/test/abort.test.ts +++ b/packages/ai/test/abort.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { complete, stream } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { complete, stream } from "../src/stream.ts"; import type { Api, Context, Model, StreamOptions } from "../src/types.ts"; type StreamOptionsWithExtras = StreamOptions & Record; diff --git a/packages/ai/test/anthropic-eager-tool-input-e2e.test.ts b/packages/ai/test/anthropic-eager-tool-input-e2e.test.ts index a9862485f..2483e1a4b 100644 --- a/packages/ai/test/anthropic-eager-tool-input-e2e.test.ts +++ b/packages/ai/test/anthropic-eager-tool-input-e2e.test.ts @@ -1,8 +1,8 @@ import { Type } from "typebox"; import { describe, expect, it } from "vitest"; import { getEnvApiKey } from "../src/env-api-keys.ts"; -import { complete } from "../src/index.ts"; import { getModels, getProviders } from "../src/models.ts"; +import { complete } from "../src/stream.ts"; import type { Api, KnownProvider, Model, ProviderStreamOptions, Tool } from "../src/types.ts"; import { resolveApiKey } from "./oauth.ts"; diff --git a/packages/ai/test/anthropic-empty-thinking-signature-compat.test.ts b/packages/ai/test/anthropic-empty-thinking-signature-compat.test.ts index 5663e2a8b..69e58e27b 100644 --- a/packages/ai/test/anthropic-empty-thinking-signature-compat.test.ts +++ b/packages/ai/test/anthropic-empty-thinking-signature-compat.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { streamSimple } from "../src/index.ts"; +import { streamSimple } from "../src/stream.ts"; import type { AssistantMessage, Context, Model } from "../src/types.ts"; interface AnthropicPayload { diff --git a/packages/ai/test/anthropic-force-adaptive-thinking.test.ts b/packages/ai/test/anthropic-force-adaptive-thinking.test.ts index cef2ca480..e797c3a94 100644 --- a/packages/ai/test/anthropic-force-adaptive-thinking.test.ts +++ b/packages/ai/test/anthropic-force-adaptive-thinking.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { streamSimple } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { streamSimple } from "../src/stream.ts"; import type { Context, Model, SimpleStreamOptions } from "../src/types.ts"; interface AnthropicThinkingPayload { diff --git a/packages/ai/test/anthropic-long-cache-retention-e2e.test.ts b/packages/ai/test/anthropic-long-cache-retention-e2e.test.ts index 0c9d590f6..2b7667d7e 100644 --- a/packages/ai/test/anthropic-long-cache-retention-e2e.test.ts +++ b/packages/ai/test/anthropic-long-cache-retention-e2e.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from "vitest"; import { getEnvApiKey } from "../src/env-api-keys.ts"; -import { complete } from "../src/index.ts"; import { getModels, getProviders } from "../src/models.ts"; +import { complete } from "../src/stream.ts"; import type { Api, KnownProvider, Model, ProviderStreamOptions } from "../src/types.ts"; import { resolveApiKey } from "./oauth.ts"; diff --git a/packages/ai/test/anthropic-opus-4-8-smoke.test.ts b/packages/ai/test/anthropic-opus-4-8-smoke.test.ts index 0c1288b39..bb4b739bb 100644 --- a/packages/ai/test/anthropic-opus-4-8-smoke.test.ts +++ b/packages/ai/test/anthropic-opus-4-8-smoke.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { streamSimple } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { streamSimple } from "../src/stream.ts"; import type { Context } from "../src/types.ts"; interface AnthropicThinkingPayload { diff --git a/packages/ai/test/anthropic-temperature-compat.test.ts b/packages/ai/test/anthropic-temperature-compat.test.ts index 06a056987..00161ab84 100644 --- a/packages/ai/test/anthropic-temperature-compat.test.ts +++ b/packages/ai/test/anthropic-temperature-compat.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { streamSimple } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { streamSimple } from "../src/stream.ts"; import type { Context, Model, SimpleStreamOptions } from "../src/types.ts"; interface AnthropicTemperaturePayload { diff --git a/packages/ai/test/anthropic-thinking-disable.test.ts b/packages/ai/test/anthropic-thinking-disable.test.ts index 581d3a347..13d333e57 100644 --- a/packages/ai/test/anthropic-thinking-disable.test.ts +++ b/packages/ai/test/anthropic-thinking-disable.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { streamSimple } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { streamSimple } from "../src/stream.ts"; import type { Context, Model, SimpleStreamOptions } from "../src/types.ts"; interface AnthropicThinkingPayload { diff --git a/packages/ai/test/anthropic-tool-name-normalization.test.ts b/packages/ai/test/anthropic-tool-name-normalization.test.ts index d52ff50e6..bb4540811 100644 --- a/packages/ai/test/anthropic-tool-name-normalization.test.ts +++ b/packages/ai/test/anthropic-tool-name-normalization.test.ts @@ -1,7 +1,7 @@ import { Type } from "typebox"; import { describe, expect, it } from "vitest"; -import { stream } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { stream } from "../src/stream.ts"; import type { Context, Tool } from "../src/types.ts"; import { resolveApiKey } from "./oauth.ts"; diff --git a/packages/ai/test/base-entrypoint.test.ts b/packages/ai/test/base-entrypoint.test.ts deleted file mode 100644 index d54fa4921..000000000 --- a/packages/ai/test/base-entrypoint.test.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { afterEach, describe, expect, it } from "vitest"; -import { clearApiProviders, complete, getApiProvider, getApiProviders } from "../src/base.ts"; -import { register as registerAmazonBedrock } from "../src/providers/amazon-bedrock.ts"; -import { register as registerAnthropic } from "../src/providers/anthropic.ts"; -import { register as registerAzureOpenAIResponses } from "../src/providers/azure-openai-responses.ts"; -import { fauxAssistantMessage, registerFauxProvider } from "../src/providers/faux.ts"; -import { register as registerGoogle } from "../src/providers/google.ts"; -import { register as registerGoogleVertex } from "../src/providers/google-vertex.ts"; -import { register as registerMistral } from "../src/providers/mistral.ts"; -import { register as registerOpenAICodexResponses } from "../src/providers/openai-codex-responses.ts"; -import { register as registerOpenAICompletions } from "../src/providers/openai-completions.ts"; -import { register as registerOpenAIResponses } from "../src/providers/openai-responses.ts"; - -const registrations = [ - ["bedrock-converse-stream", registerAmazonBedrock], - ["anthropic-messages", registerAnthropic], - ["azure-openai-responses", registerAzureOpenAIResponses], - ["google-generative-ai", registerGoogle], - ["google-vertex", registerGoogleVertex], - ["mistral-conversations", registerMistral], - ["openai-codex-responses", registerOpenAICodexResponses], - ["openai-completions", registerOpenAICompletions], - ["openai-responses", registerOpenAIResponses], -] as const; - -afterEach(() => { - clearApiProviders(); -}); - -describe("base entrypoint", () => { - it("starts without built-in provider registrations", () => { - expect(getApiProviders()).toEqual([]); - }); - - it.each(registrations)("registers the %s transport explicitly", (api, register) => { - register(); - expect(getApiProvider(api)?.api).toBe(api); - }); - - it("dispatches custom providers", async () => { - const registration = registerFauxProvider(); - registration.setResponses([fauxAssistantMessage("hello")]); - const response = await complete(registration.getModel(), { - messages: [{ role: "user", content: "hi", timestamp: Date.now() }], - }); - expect(response.content).toEqual([{ type: "text", text: "hello" }]); - }); - - it("fails clearly when no transport is registered", async () => { - await expect( - complete( - { - id: "missing-model", - name: "Missing Model", - api: "missing-api", - provider: "missing-provider", - baseUrl: "https://example.com", - reasoning: false, - input: ["text"], - cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, - contextWindow: 1, - maxTokens: 1, - }, - { messages: [] }, - ), - ).rejects.toThrow("No API provider registered for api: missing-api"); - }); -}); diff --git a/packages/ai/test/bedrock-models.test.ts b/packages/ai/test/bedrock-models.test.ts index 5bd5a1f31..2cfd8fb99 100644 --- a/packages/ai/test/bedrock-models.test.ts +++ b/packages/ai/test/bedrock-models.test.ts @@ -17,8 +17,8 @@ */ import { describe, expect, it } from "vitest"; -import { complete } from "../src/index.ts"; import { getModels } from "../src/models.ts"; +import { complete } from "../src/stream.ts"; import type { Context } from "../src/types.ts"; import { hasBedrockCredentials } from "./bedrock-utils.ts"; diff --git a/packages/ai/test/cache-retention.test.ts b/packages/ai/test/cache-retention.test.ts index b5c551985..6e2c1a5bc 100644 --- a/packages/ai/test/cache-retention.test.ts +++ b/packages/ai/test/cache-retention.test.ts @@ -1,10 +1,10 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest"; -import { stream } from "../src/index.ts"; import { MODELS } from "../src/models.generated.ts"; import { getModel } from "../src/models.ts"; import { streamAnthropic } from "../src/providers/anthropic.ts"; import { streamOpenAICompletions } from "../src/providers/openai-completions.ts"; import { streamOpenAIResponses } from "../src/providers/openai-responses.ts"; +import { stream } from "../src/stream.ts"; import type { Context, Model } from "../src/types.ts"; class PayloadCaptured extends Error { diff --git a/packages/ai/test/context-overflow.test.ts b/packages/ai/test/context-overflow.test.ts index 54087a29e..9f021a413 100644 --- a/packages/ai/test/context-overflow.test.ts +++ b/packages/ai/test/context-overflow.test.ts @@ -14,8 +14,8 @@ import type { ChildProcess } from "child_process"; import { execSync, spawn } from "child_process"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { complete } from "../src/index.ts"; import { getModel, getModels } from "../src/models.ts"; +import { complete } from "../src/stream.ts"; import type { AssistantMessage, Context, Model, Usage } from "../src/types.ts"; import { isContextOverflow } from "../src/utils/overflow.ts"; import { hasAzureOpenAICredentials } from "./azure-utils.ts"; diff --git a/packages/ai/test/cross-provider-handoff.test.ts b/packages/ai/test/cross-provider-handoff.test.ts index 73fac229b..23593394f 100644 --- a/packages/ai/test/cross-provider-handoff.test.ts +++ b/packages/ai/test/cross-provider-handoff.test.ts @@ -25,8 +25,8 @@ import { writeFileSync } from "fs"; import { Type } from "typebox"; import { beforeAll, describe, expect, it } from "vitest"; -import { completeSimple, getEnvApiKey } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { completeSimple, getEnvApiKey } from "../src/stream.ts"; import type { Api, AssistantMessage, Message, Model, Tool, ToolResultMessage } from "../src/types.ts"; import { hasAzureOpenAICredentials } from "./azure-utils.ts"; import { hasCloudflareAiGatewayCredentials, hasCloudflareWorkersAICredentials } from "./cloudflare-utils.ts"; diff --git a/packages/ai/test/empty.test.ts b/packages/ai/test/empty.test.ts index fadd67a9c..a8453dcc3 100644 --- a/packages/ai/test/empty.test.ts +++ b/packages/ai/test/empty.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { complete } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { complete } from "../src/stream.ts"; import type { Api, AssistantMessage, Context, Model, StreamOptions, UserMessage } from "../src/types.ts"; type StreamOptionsWithExtras = StreamOptions & Record; diff --git a/packages/ai/test/google-thinking-disable.test.ts b/packages/ai/test/google-thinking-disable.test.ts index 3920f4c80..30df3638b 100644 --- a/packages/ai/test/google-thinking-disable.test.ts +++ b/packages/ai/test/google-thinking-disable.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { streamSimple } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { streamSimple } from "../src/stream.ts"; import type { Api, Context, Model, SimpleStreamOptions } from "../src/types.ts"; type SimpleOptionsWithExtras = SimpleStreamOptions & Record; diff --git a/packages/ai/test/interleaved-thinking.test.ts b/packages/ai/test/interleaved-thinking.test.ts index a2d3f5a68..4cf387ce0 100644 --- a/packages/ai/test/interleaved-thinking.test.ts +++ b/packages/ai/test/interleaved-thinking.test.ts @@ -1,8 +1,8 @@ import { Type } from "typebox"; import { describe, expect, it } from "vitest"; import { getEnvApiKey } from "../src/env-api-keys.ts"; -import { completeSimple } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { completeSimple } from "../src/stream.ts"; import type { Api, Context, Model, StopReason, Tool, ToolCall, ToolResultMessage } from "../src/types.ts"; import { StringEnum } from "../src/utils/typebox-helpers.ts"; import { hasBedrockCredentials } from "./bedrock-utils.ts"; diff --git a/packages/ai/test/lazy-module-load.test.ts b/packages/ai/test/lazy-module-load.test.ts index 14bccff0f..e21f0d12d 100644 --- a/packages/ai/test/lazy-module-load.test.ts +++ b/packages/ai/test/lazy-module-load.test.ts @@ -5,7 +5,6 @@ import { describe, expect, it } from "vitest"; const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const aiEntryUrl = new URL("../src/index.ts", import.meta.url).href; -const baseEntryUrl = new URL("../src/base.ts", import.meta.url).href; const SDK_SPECIFIERS = [ "@anthropic-ai/sdk", @@ -17,10 +16,9 @@ const SDK_SPECIFIERS = [ type ProbeResult = { loadedSpecifiers: string[]; - value?: unknown; }; -function runProbe(action: string, entryUrl = aiEntryUrl): ProbeResult { +function runProbe(action: string): ProbeResult { const script = ` import { registerHooks } from "node:module"; @@ -36,11 +34,9 @@ function runProbe(action: string, entryUrl = aiEntryUrl): ProbeResult { }, }); - const mod = await import(${JSON.stringify(entryUrl)}); - const value = await (async () => { - ${action} - })(); - console.log(JSON.stringify({ loadedSpecifiers: [...new Set(loaded)], value })); + const mod = await import(${JSON.stringify(aiEntryUrl)}); + ${action} + console.log(JSON.stringify({ loadedSpecifiers: [...new Set(loaded)] })); `; const result = spawnSync(process.execPath, ["--input-type=module", "--eval", script], { @@ -70,33 +66,6 @@ describe("lazy provider module loading", () => { expect(result.loadedSpecifiers).toEqual([]); }); - it("registers built-in transports when importing the root barrel", () => { - const result = runProbe(`return mod.getApiProviders().map((provider) => provider.api).sort();`); - expect(result.value).toEqual([ - "anthropic-messages", - "azure-openai-responses", - "bedrock-converse-stream", - "google-generative-ai", - "google-vertex", - "mistral-conversations", - "openai-codex-responses", - "openai-completions", - "openai-responses", - ]); - }); - - it("registers built-in image transports when importing the root barrel", () => { - const result = runProbe(`return mod.getImagesApiProvider("openrouter-images")?.api;`); - expect(result.loadedSpecifiers).toEqual([]); - expect(result.value).toBe("openrouter-images"); - }); - - it("does not load provider SDKs or register transports when importing the base barrel", () => { - const result = runProbe(`return mod.getApiProviders().map((provider) => provider.api);`, baseEntryUrl); - expect(result.loadedSpecifiers).toEqual([]); - expect(result.value).toEqual([]); - }); - it("loads only the Anthropic SDK when calling the root lazy wrapper", () => { const result = runProbe(` const model = { @@ -127,17 +96,4 @@ describe("lazy provider module loading", () => { expect(result.loadedSpecifiers).toEqual(["@anthropic-ai/sdk"]); }); - - it("dispatches through a lazy wrapper again after resetting providers", () => { - const result = runProbe(` - const model = mod.getModel("anthropic", "claude-sonnet-4-6"); - const context = { messages: [{ role: "user", content: "hi" }] }; - await mod.streamSimple(model, context).result(); - mod.resetApiProviders(); - return (await mod.streamSimple(model, context).result()).stopReason; - `); - - expect(result.loadedSpecifiers).toEqual(["@anthropic-ai/sdk"]); - expect(result.value).toBe("error"); - }); }); diff --git a/packages/ai/test/mistral-reasoning-mode.test.ts b/packages/ai/test/mistral-reasoning-mode.test.ts index 0e2f45c58..d197a56a5 100644 --- a/packages/ai/test/mistral-reasoning-mode.test.ts +++ b/packages/ai/test/mistral-reasoning-mode.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { streamSimple } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { streamSimple } from "../src/stream.ts"; import type { Context, Model, SimpleStreamOptions } from "../src/types.ts"; interface MistralPayload { diff --git a/packages/ai/test/mistral-tool-schema.test.ts b/packages/ai/test/mistral-tool-schema.test.ts index 3c490c6da..c6898fc64 100644 --- a/packages/ai/test/mistral-tool-schema.test.ts +++ b/packages/ai/test/mistral-tool-schema.test.ts @@ -1,7 +1,7 @@ import { Type } from "typebox"; import { describe, expect, it } from "vitest"; -import { complete } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { complete } from "../src/stream.ts"; import type { Context, Model } from "../src/types.ts"; interface MistralToolPayload { diff --git a/packages/ai/test/openai-codex-cache-affinity-e2e.test.ts b/packages/ai/test/openai-codex-cache-affinity-e2e.test.ts index 62245dcf2..0abb46f63 100644 --- a/packages/ai/test/openai-codex-cache-affinity-e2e.test.ts +++ b/packages/ai/test/openai-codex-cache-affinity-e2e.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { complete } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { complete } from "../src/stream.ts"; import type { Context } from "../src/types.ts"; import { resolveApiKey } from "./oauth.ts"; diff --git a/packages/ai/test/openai-completions-empty-tools.test.ts b/packages/ai/test/openai-completions-empty-tools.test.ts index 0ecf8e139..be2336706 100644 --- a/packages/ai/test/openai-completions-empty-tools.test.ts +++ b/packages/ai/test/openai-completions-empty-tools.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -import { streamSimple } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { streamSimple } from "../src/stream.ts"; // Empty tools arrays must NOT be serialized as `tools: []` — some OpenAI-compatible // backends (e.g. DashScope / Aliyun Qwen via compatible-mode) reject the request with diff --git a/packages/ai/test/openai-completions-response-model.test.ts b/packages/ai/test/openai-completions-response-model.test.ts index ec2abea1d..d8e4d2f9a 100644 --- a/packages/ai/test/openai-completions-response-model.test.ts +++ b/packages/ai/test/openai-completions-response-model.test.ts @@ -1,5 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -import { complete } from "../src/index.ts"; +import { complete } from "../src/stream.ts"; import type { Model } from "../src/types.ts"; // Router/virtual ids (e.g. OpenRouter `auto`) keep `model` pinned to the diff --git a/packages/ai/test/openai-completions-tool-choice.test.ts b/packages/ai/test/openai-completions-tool-choice.test.ts index 416012818..e3b346817 100644 --- a/packages/ai/test/openai-completions-tool-choice.test.ts +++ b/packages/ai/test/openai-completions-tool-choice.test.ts @@ -1,8 +1,8 @@ import { Type } from "typebox"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import { stream, streamSimple } from "../src/index.ts"; import { getModel } from "../src/models.ts"; import { convertMessages } from "../src/providers/openai-completions.ts"; +import { stream, streamSimple } from "../src/stream.ts"; import type { AssistantMessage, Model, SimpleStreamOptions, Tool, ToolResultMessage } from "../src/types.ts"; const mockState = vi.hoisted(() => ({ diff --git a/packages/ai/test/openai-responses-cache-affinity-e2e.test.ts b/packages/ai/test/openai-responses-cache-affinity-e2e.test.ts index 0d247b0b1..d173694f9 100644 --- a/packages/ai/test/openai-responses-cache-affinity-e2e.test.ts +++ b/packages/ai/test/openai-responses-cache-affinity-e2e.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { complete } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { complete } from "../src/stream.ts"; import type { Context } from "../src/types.ts"; describe.skipIf(!process.env.OPENAI_API_KEY)("openai responses cache affinity e2e", () => { diff --git a/packages/ai/test/openai-responses-reasoning-replay-e2e.test.ts b/packages/ai/test/openai-responses-reasoning-replay-e2e.test.ts index 87945bc23..aa6f2e239 100644 --- a/packages/ai/test/openai-responses-reasoning-replay-e2e.test.ts +++ b/packages/ai/test/openai-responses-reasoning-replay-e2e.test.ts @@ -1,7 +1,7 @@ import { Type } from "typebox"; import { describe, expect, it } from "vitest"; -import { complete, getEnvApiKey } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { complete, getEnvApiKey } from "../src/stream.ts"; import type { AssistantMessage, Context, Message, Tool, ToolCall } from "../src/types.ts"; const testToolSchema = Type.Object({ diff --git a/packages/ai/test/openrouter-cache-write-repro.test.ts b/packages/ai/test/openrouter-cache-write-repro.test.ts index 2cf2a791d..4bdeb2868 100644 --- a/packages/ai/test/openrouter-cache-write-repro.test.ts +++ b/packages/ai/test/openrouter-cache-write-repro.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { completeSimple } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { completeSimple } from "../src/stream.ts"; function createLongSystemPrompt(): string { const nonce = `${Date.now()}-${Math.random()}`; diff --git a/packages/ai/test/openrouter-images.test.ts b/packages/ai/test/openrouter-images.test.ts index 5811e6f48..31882bec3 100644 --- a/packages/ai/test/openrouter-images.test.ts +++ b/packages/ai/test/openrouter-images.test.ts @@ -1,8 +1,5 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { generateImages } from "../src/images.ts"; -import { clearImagesApiProviders, getImagesApiProvider } from "../src/images-api-registry.ts"; -import { register as registerOpenRouterImages } from "../src/providers/images/openrouter.ts"; -import { registerBuiltInImagesApiProviders } from "../src/providers/register-builtins.ts"; import type { ImagesContext, ImagesModel } from "../src/types.ts"; const mockState = vi.hoisted(() => ({ @@ -65,15 +62,6 @@ describe("openrouter images", () => { beforeEach(() => { mockState.lastParams = undefined; mockState.lastRequestOptions = undefined; - clearImagesApiProviders(); - registerBuiltInImagesApiProviders(); - }); - - it("registers the direct OpenRouter images transport explicitly", () => { - clearImagesApiProviders(); - expect(getImagesApiProvider("openrouter-images")).toBeUndefined(); - registerOpenRouterImages(); - expect(getImagesApiProvider("openrouter-images")?.api).toBe("openrouter-images"); }); it("returns text plus images in final output", async () => { diff --git a/packages/ai/test/responseid.test.ts b/packages/ai/test/responseid.test.ts index bc5d6c54e..d250f5f41 100644 --- a/packages/ai/test/responseid.test.ts +++ b/packages/ai/test/responseid.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { complete } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { complete } from "../src/stream.ts"; import type { Api, Context, Model, StreamOptions } from "../src/types.ts"; import { hasAzureOpenAICredentials, resolveAzureDeploymentName } from "./azure-utils.ts"; import { resolveApiKey } from "./oauth.ts"; diff --git a/packages/ai/test/stream.test.ts b/packages/ai/test/stream.test.ts index 3a3819668..9a70b6c28 100644 --- a/packages/ai/test/stream.test.ts +++ b/packages/ai/test/stream.test.ts @@ -4,8 +4,8 @@ import { dirname, join } from "path"; import { Type } from "typebox"; import { fileURLToPath } from "url"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; -import { complete, stream } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { complete, stream } from "../src/stream.ts"; import type { Api, Context, ImageContent, Model, StreamOptions, Tool, ToolResultMessage } from "../src/types.ts"; type StreamOptionsWithExtras = StreamOptions & Record; diff --git a/packages/ai/test/tokens.test.ts b/packages/ai/test/tokens.test.ts index fdf45b94b..676f5a29c 100644 --- a/packages/ai/test/tokens.test.ts +++ b/packages/ai/test/tokens.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { stream } from "../src/index.ts"; import { getModel, getModels } from "../src/models.ts"; +import { stream } from "../src/stream.ts"; import type { Api, Context, Model, StreamOptions } from "../src/types.ts"; type StreamOptionsWithExtras = StreamOptions & Record; diff --git a/packages/ai/test/tool-call-id-normalization.test.ts b/packages/ai/test/tool-call-id-normalization.test.ts index 622e5127c..0672181b2 100644 --- a/packages/ai/test/tool-call-id-normalization.test.ts +++ b/packages/ai/test/tool-call-id-normalization.test.ts @@ -12,8 +12,8 @@ import { Type } from "typebox"; import { describe, expect, it } from "vitest"; -import { completeSimple, getEnvApiKey } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { completeSimple, getEnvApiKey } from "../src/stream.ts"; import type { AssistantMessage, Message, Tool, ToolResultMessage } from "../src/types.ts"; import { resolveApiKey } from "./oauth.ts"; diff --git a/packages/ai/test/tool-call-without-result.test.ts b/packages/ai/test/tool-call-without-result.test.ts index b21c1ce89..11b832f3b 100644 --- a/packages/ai/test/tool-call-without-result.test.ts +++ b/packages/ai/test/tool-call-without-result.test.ts @@ -1,7 +1,7 @@ import { Type } from "typebox"; import { describe, expect, it } from "vitest"; -import { complete } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { complete } from "../src/stream.ts"; import type { Api, Context, Model, StreamOptions, Tool } from "../src/types.ts"; type StreamOptionsWithExtras = StreamOptions & Record; diff --git a/packages/ai/test/total-tokens.test.ts b/packages/ai/test/total-tokens.test.ts index 4df4b2e1c..972913a7c 100644 --- a/packages/ai/test/total-tokens.test.ts +++ b/packages/ai/test/total-tokens.test.ts @@ -13,8 +13,8 @@ */ import { describe, expect, it } from "vitest"; -import { complete } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { complete } from "../src/stream.ts"; import type { Api, Context, Model, StreamOptions, Usage } from "../src/types.ts"; type StreamOptionsWithExtras = StreamOptions & Record; diff --git a/packages/ai/test/unicode-surrogate.test.ts b/packages/ai/test/unicode-surrogate.test.ts index d8b02392a..9cdddefa8 100644 --- a/packages/ai/test/unicode-surrogate.test.ts +++ b/packages/ai/test/unicode-surrogate.test.ts @@ -1,7 +1,7 @@ import { Type } from "typebox"; import { describe, expect, it } from "vitest"; -import { complete } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { complete } from "../src/stream.ts"; import type { Api, Context, Model, StreamOptions, ToolResultMessage } from "../src/types.ts"; type StreamOptionsWithExtras = StreamOptions & Record; diff --git a/packages/ai/test/xhigh.test.ts b/packages/ai/test/xhigh.test.ts index 1bda5c14b..3c2798635 100644 --- a/packages/ai/test/xhigh.test.ts +++ b/packages/ai/test/xhigh.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { stream } from "../src/index.ts"; import { getModel } from "../src/models.ts"; +import { stream } from "../src/stream.ts"; import type { Context, Model } from "../src/types.ts"; function makeContext(): Context { diff --git a/packages/ai/test/xiaomi-token-plan-ams-anthropic-empty-signature-smoke.test.ts b/packages/ai/test/xiaomi-token-plan-ams-anthropic-empty-signature-smoke.test.ts index 9a371b60d..ab2fec30a 100644 --- a/packages/ai/test/xiaomi-token-plan-ams-anthropic-empty-signature-smoke.test.ts +++ b/packages/ai/test/xiaomi-token-plan-ams-anthropic-empty-signature-smoke.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { completeSimple, getEnvApiKey, streamSimple } from "../src/index.ts"; +import { completeSimple, getEnvApiKey, streamSimple } from "../src/stream.ts"; import type { AssistantMessage, Context, Model } from "../src/types.ts"; const provider = "xiaomi-token-plan-ams"; diff --git a/packages/ai/test/zen.test.ts b/packages/ai/test/zen.test.ts index 82a2079fe..8ca80014e 100644 --- a/packages/ai/test/zen.test.ts +++ b/packages/ai/test/zen.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { complete } from "../src/index.ts"; import { MODELS } from "../src/models.generated.ts"; +import { complete } from "../src/stream.ts"; import type { Model } from "../src/types.ts"; describe.skipIf(!process.env.OPENCODE_API_KEY)("OpenCode Models Smoke Test", () => { diff --git a/packages/coding-agent/vitest.config.ts b/packages/coding-agent/vitest.config.ts index 5759e3c4b..67ce0fca8 100644 --- a/packages/coding-agent/vitest.config.ts +++ b/packages/coding-agent/vitest.config.ts @@ -2,11 +2,8 @@ import { fileURLToPath } from "node:url"; import { defineConfig } from "vitest/config"; const aiSrcIndex = fileURLToPath(new URL("../ai/src/index.ts", import.meta.url)); -const aiSrcBase = fileURLToPath(new URL("../ai/src/base.ts", import.meta.url)); const aiSrcOAuth = fileURLToPath(new URL("../ai/src/oauth.ts", import.meta.url)); -const aiOpenRouterImages = fileURLToPath(new URL("../ai/src/providers/images/openrouter.ts", import.meta.url)); const agentSrcIndex = fileURLToPath(new URL("../agent/src/index.ts", import.meta.url)); -const agentSrcBase = fileURLToPath(new URL("../agent/src/base.ts", import.meta.url)); const tuiSrcIndex = fileURLToPath(new URL("../tui/src/index.ts", import.meta.url)); export default defineConfig({ @@ -23,18 +20,12 @@ export default defineConfig({ resolve: { alias: [ { find: /^@earendil-works\/pi-ai$/, replacement: aiSrcIndex }, - { find: /^@earendil-works\/pi-ai\/base$/, replacement: aiSrcBase }, { find: /^@earendil-works\/pi-ai\/oauth$/, replacement: aiSrcOAuth }, - { find: /^@earendil-works\/pi-ai\/openrouter-images$/, replacement: aiOpenRouterImages }, { find: /^@earendil-works\/pi-agent-core$/, replacement: agentSrcIndex }, - { find: /^@earendil-works\/pi-agent-core\/base$/, replacement: agentSrcBase }, { find: /^@earendil-works\/pi-tui$/, replacement: tuiSrcIndex }, { find: /^@mariozechner\/pi-ai$/, replacement: aiSrcIndex }, - { find: /^@mariozechner\/pi-ai\/base$/, replacement: aiSrcBase }, { find: /^@mariozechner\/pi-ai\/oauth$/, replacement: aiSrcOAuth }, - { find: /^@mariozechner\/pi-ai\/openrouter-images$/, replacement: aiOpenRouterImages }, { find: /^@mariozechner\/pi-agent-core$/, replacement: agentSrcIndex }, - { find: /^@mariozechner\/pi-agent-core\/base$/, replacement: agentSrcBase }, { find: /^@mariozechner\/pi-tui$/, replacement: tuiSrcIndex }, ], }, diff --git a/scripts/check-browser-smoke.mjs b/scripts/check-browser-smoke.mjs index 906eccd32..d590988d6 100644 --- a/scripts/check-browser-smoke.mjs +++ b/scripts/check-browser-smoke.mjs @@ -4,21 +4,7 @@ import { join } from "node:path"; import { build } from "esbuild"; const outputPath = join(tmpdir(), "pi-browser-smoke.js"); -const baseOutputPath = join(tmpdir(), "pi-browser-base-smoke.js"); -const selectiveOutputPath = join(tmpdir(), "pi-browser-selective-smoke.js"); const errorLogPath = join(tmpdir(), "pi-browser-smoke-errors.log"); -const providerImplementationInputs = [ - "packages/ai/src/providers/amazon-bedrock.ts", - "packages/ai/src/providers/anthropic.ts", - "packages/ai/src/providers/azure-openai-responses.ts", - "packages/ai/src/providers/google.ts", - "packages/ai/src/providers/google-vertex.ts", - "packages/ai/src/providers/images/openrouter.ts", - "packages/ai/src/providers/mistral.ts", - "packages/ai/src/providers/openai-codex-responses.ts", - "packages/ai/src/providers/openai-completions.ts", - "packages/ai/src/providers/openai-responses.ts", -]; try { await build({ @@ -29,36 +15,6 @@ try { logLevel: "silent", outfile: outputPath, }); - const baseBuild = await build({ - stdin: { - contents: `import { complete } from "@earendil-works/pi-ai/base";\nimport { Agent } from "@earendil-works/pi-agent-core/base";\nconsole.log(typeof complete, typeof Agent);\n`, - resolveDir: process.cwd(), - sourcefile: "pi-browser-base-smoke-entry.ts", - }, - bundle: true, - platform: "browser", - format: "esm", - logLevel: "silent", - metafile: true, - outfile: baseOutputPath, - }); - const bundledInputs = new Set(Object.keys(baseBuild.metafile.inputs)); - const reachableProviderImplementations = providerImplementationInputs.filter((input) => bundledInputs.has(input)); - if (reachableProviderImplementations.length > 0) { - throw new Error(`Base browser bundle reached provider implementations:\n${reachableProviderImplementations.join("\n")}`); - } - await build({ - stdin: { - contents: `import { register as registerAnthropic } from "@earendil-works/pi-ai/anthropic";\nimport { register as registerOpenAICompletions } from "@earendil-works/pi-ai/openai-completions";\nimport { register as registerOpenRouterImages } from "@earendil-works/pi-ai/openrouter-images";\nconsole.log(typeof registerAnthropic, typeof registerOpenAICompletions, typeof registerOpenRouterImages);\n`, - resolveDir: process.cwd(), - sourcefile: "pi-browser-selective-smoke-entry.ts", - }, - bundle: true, - platform: "browser", - format: "esm", - logLevel: "silent", - outfile: selectiveOutputPath, - }); process.exit(0); } catch (error) { let detailedErrors = ""; diff --git a/tsconfig.json b/tsconfig.json index bcb12eeec..903f80f3c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,6 @@ "*": ["./*"], "@earendil-works/pi-ai": ["./packages/ai/src/index.ts"], "@earendil-works/pi-ai/oauth": ["./packages/ai/src/oauth.ts"], - "@earendil-works/pi-ai/openrouter-images": ["./packages/ai/src/providers/images/openrouter.ts"], "@earendil-works/pi-ai/*": ["./packages/ai/src/*.ts", "./packages/ai/src/providers/*.ts"], "@earendil-works/pi-ai/dist/*": ["./packages/ai/src/*"], "@earendil-works/pi-agent-core": ["./packages/agent/src/index.ts"],