feat(ai): move API implementations to src/api with lazy wrappers (phase 2)
Stream implementations move from src/providers/ to src/api/, renamed by API id (anthropic.ts -> anthropic-messages.ts, google.ts -> google-generative-ai.ts, mistral.ts -> mistral-conversations.ts, amazon-bedrock.ts -> bedrock-converse-stream.ts). Every module now exports exactly stream/streamSimple; shared helpers move alongside. New ProviderStreams dispatch contract in types.ts, lazyApi() wrapper in api/lazy.ts, and one .lazy.ts wrapper per API. Bedrock's wrapper keeps the node-only variable-specifier import and setBedrockProviderModule() (now taking ProviderStreams). providers/register-builtins.ts deleted; interim until the compat entrypoint lands, builtin api-registry registration lives in stream.ts and lazy wrappers are exported from the root barrel. Old per-API lazy exports (streamAnthropic, ...) are gone; package.json subpaths retarget to dist/api/.
This commit is contained in:
@@ -2,7 +2,7 @@ import { createServer, type IncomingMessage, type ServerResponse } from "node:ht
|
||||
import type { AddressInfo } from "node:net";
|
||||
import { Type } from "typebox";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { streamAnthropic } from "../src/providers/anthropic.ts";
|
||||
import { stream as streamAnthropic } from "../src/api/anthropic-messages.ts";
|
||||
import type { Context, Model, Tool } from "../src/types.ts";
|
||||
|
||||
interface CapturedRequest {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type Anthropic from "@anthropic-ai/sdk";
|
||||
import { Type } from "typebox";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { stream as streamAnthropic } from "../src/api/anthropic-messages.ts";
|
||||
import { getModel } from "../src/models.ts";
|
||||
import { streamAnthropic } from "../src/providers/anthropic.ts";
|
||||
import type { Context, ToolCall } from "../src/types.ts";
|
||||
|
||||
function createSseResponse(events: Array<{ event: string; data: string }>): Response {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { stream as streamAzureOpenAIResponses } from "../src/api/azure-openai-responses.ts";
|
||||
import { getModel } from "../src/models.ts";
|
||||
import { streamAzureOpenAIResponses } from "../src/providers/azure-openai-responses.ts";
|
||||
import type { Context } from "../src/types.ts";
|
||||
|
||||
interface CapturedAzureClientOptions {
|
||||
|
||||
@@ -44,8 +44,8 @@ vi.mock("@aws-sdk/client-bedrock-runtime", () => {
|
||||
};
|
||||
});
|
||||
|
||||
import { stream as streamBedrock } from "../src/api/bedrock-converse-stream.ts";
|
||||
import { getModel } from "../src/models.ts";
|
||||
import { streamBedrock } from "../src/providers/amazon-bedrock.ts";
|
||||
import type { Context, Message } from "../src/types.ts";
|
||||
|
||||
const baseModel = getModel("amazon-bedrock", "us.anthropic.claude-sonnet-4-5-20250929-v1:0");
|
||||
|
||||
@@ -51,9 +51,9 @@ vi.mock("@aws-sdk/client-bedrock-runtime", () => {
|
||||
};
|
||||
});
|
||||
|
||||
import type { BedrockOptions } from "../src/api/bedrock-converse-stream.ts";
|
||||
import { stream as streamBedrock, streamSimple as streamSimpleBedrock } from "../src/api/bedrock-converse-stream.ts";
|
||||
import { getModel } from "../src/models.ts";
|
||||
import type { BedrockOptions } from "../src/providers/amazon-bedrock.ts";
|
||||
import { streamBedrock, streamSimpleBedrock } from "../src/providers/amazon-bedrock.ts";
|
||||
import type { Context, Model } from "../src/types.ts";
|
||||
|
||||
const context: Context = {
|
||||
|
||||
@@ -44,8 +44,8 @@ vi.mock("@aws-sdk/client-bedrock-runtime", () => {
|
||||
};
|
||||
});
|
||||
|
||||
import { stream as streamBedrock } from "../src/api/bedrock-converse-stream.ts";
|
||||
import { getModel } from "../src/models.ts";
|
||||
import { streamBedrock } from "../src/providers/amazon-bedrock.ts";
|
||||
import type { Context, Model } from "../src/types.ts";
|
||||
|
||||
const context: Context = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { type BedrockOptions, stream as streamBedrock } from "../src/api/bedrock-converse-stream.ts";
|
||||
import { getModel } from "../src/models.ts";
|
||||
import { type BedrockOptions, streamBedrock } from "../src/providers/amazon-bedrock.ts";
|
||||
import type { Context, Model } from "../src/types.ts";
|
||||
import { hasBedrockCredentials } from "./bedrock-utils.ts";
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { stream as streamAnthropic } from "../src/api/anthropic-messages.ts";
|
||||
import { stream as streamOpenAICompletions } from "../src/api/openai-completions.ts";
|
||||
import { stream as streamOpenAIResponses } from "../src/api/openai-responses.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";
|
||||
|
||||
|
||||
@@ -10,13 +10,13 @@ import { tmpdir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
import { Type } from "typebox";
|
||||
import { AuthStorage } from "../../coding-agent/src/core/auth-storage.ts";
|
||||
import { getModel } from "../src/models.ts";
|
||||
import {
|
||||
closeOpenAICodexWebSocketSessions,
|
||||
getOpenAICodexWebSocketDebugStats,
|
||||
resetOpenAICodexWebSocketDebugStats,
|
||||
streamOpenAICodexResponses,
|
||||
} from "../src/providers/openai-codex-responses.ts";
|
||||
stream as streamOpenAICodexResponses,
|
||||
} from "../src/api/openai-codex-responses.ts";
|
||||
import { getModel } from "../src/models.ts";
|
||||
import type { AssistantMessage, Context, Message, Model, Tool, ToolResultMessage, Transport } from "../src/types.ts";
|
||||
|
||||
type ThinkingLevel = "minimal" | "low" | "medium" | "high" | "xhigh";
|
||||
|
||||
@@ -2,9 +2,9 @@ import { createServer, type IncomingMessage, type ServerResponse } from "node:ht
|
||||
import type { AddressInfo } from "node:net";
|
||||
import { Type } from "typebox";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { stream as streamAnthropic } from "../src/api/anthropic-messages.ts";
|
||||
import { findEnvKeys, getEnvApiKey } from "../src/env-api-keys.ts";
|
||||
import { getModel, getModels } from "../src/models.ts";
|
||||
import { streamAnthropic } from "../src/providers/anthropic.ts";
|
||||
import type { Context, Model, Tool } from "../src/types.ts";
|
||||
|
||||
const originalFireworksApiKey = process.env.FIREWORKS_API_KEY;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { stream as streamAnthropic } from "../src/api/anthropic-messages.ts";
|
||||
import { getModel } from "../src/models.ts";
|
||||
import { streamAnthropic } from "../src/providers/anthropic.ts";
|
||||
import type { Context } from "../src/types.ts";
|
||||
|
||||
const mockState = vi.hoisted(() => ({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { convertTools } from "../src/providers/google-shared.ts";
|
||||
import { convertTools } from "../src/api/google-shared.ts";
|
||||
import type { Tool } from "../src/types.ts";
|
||||
|
||||
function makeTool(parameters: Record<string, unknown>): Tool {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { convertMessages } from "../src/providers/google-shared.ts";
|
||||
import { convertMessages } from "../src/api/google-shared.ts";
|
||||
import type { Context, Model } from "../src/types.ts";
|
||||
|
||||
function makeGemini3Model<TApi extends "google-generative-ai" | "google-vertex">(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { convertMessages } from "../src/providers/google-shared.ts";
|
||||
import { convertMessages } from "../src/api/google-shared.ts";
|
||||
import type { Context, Model } from "../src/types.ts";
|
||||
|
||||
function makeModel<TApi extends "google-generative-ai">(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { isThinkingPart, retainThoughtSignature } from "../src/providers/google-shared.ts";
|
||||
import { isThinkingPart, retainThoughtSignature } from "../src/api/google-shared.ts";
|
||||
|
||||
describe("Google thinking detection (thoughtSignature)", () => {
|
||||
it("treats part.thought === true as thinking", () => {
|
||||
|
||||
@@ -45,8 +45,8 @@ vi.mock("@google/genai", () => {
|
||||
};
|
||||
});
|
||||
|
||||
import { stream as streamGoogleVertex } from "../src/api/google-vertex.ts";
|
||||
import { getModel } from "../src/models.ts";
|
||||
import { streamGoogleVertex } from "../src/providers/google-vertex.ts";
|
||||
import type { Context, Model } from "../src/types.ts";
|
||||
|
||||
const model = getModel("google-vertex", "gemini-3-flash-preview");
|
||||
|
||||
@@ -66,7 +66,7 @@ describe("lazy provider module loading", () => {
|
||||
expect(result.loadedSpecifiers).toEqual([]);
|
||||
});
|
||||
|
||||
it("loads only the Anthropic SDK when calling the root lazy wrapper", () => {
|
||||
it("loads only the Anthropic SDK when streaming through the lazy API wrapper", () => {
|
||||
const result = runProbe(`
|
||||
const model = {
|
||||
id: "claude-sonnet-4-6",
|
||||
@@ -81,7 +81,7 @@ describe("lazy provider module loading", () => {
|
||||
maxTokens: 8192,
|
||||
};
|
||||
const context = { messages: [{ role: "user", content: "hi" }] };
|
||||
await mod.streamSimpleAnthropic(model, context).result();
|
||||
await mod.anthropicMessagesApi().streamSimple(model, context).result();
|
||||
`);
|
||||
|
||||
expect(result.loadedSpecifiers).toEqual(["@anthropic-ai/sdk"]);
|
||||
|
||||
@@ -5,9 +5,9 @@ import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
getOpenAICodexWebSocketDebugStats,
|
||||
resetOpenAICodexWebSocketDebugStats,
|
||||
streamOpenAICodexResponses,
|
||||
streamSimpleOpenAICodexResponses,
|
||||
} from "../src/providers/openai-codex-responses.ts";
|
||||
stream as streamOpenAICodexResponses,
|
||||
streamSimple as streamSimpleOpenAICodexResponses,
|
||||
} from "../src/api/openai-codex-responses.ts";
|
||||
import type { Context, Model } from "../src/types.ts";
|
||||
|
||||
const originalAgentDir = process.env.PI_CODING_AGENT_DIR;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Type } from "typebox";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { stream as streamOpenAICompletions } from "../src/api/openai-completions.ts";
|
||||
import { getModel } from "../src/models.ts";
|
||||
import { streamOpenAICompletions } from "../src/providers/openai-completions.ts";
|
||||
import type { Model } from "../src/types.ts";
|
||||
|
||||
interface CacheControl {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { stream as streamOpenAICompletions } from "../src/api/openai-completions.ts";
|
||||
import { getModel } from "../src/models.ts";
|
||||
import { streamOpenAICompletions } from "../src/providers/openai-completions.ts";
|
||||
import type { Model } from "../src/types.ts";
|
||||
|
||||
interface FakeOpenAIClientOptions {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { streamOpenAICompletions } from "../src/providers/openai-completions.ts";
|
||||
import { stream as streamOpenAICompletions } from "../src/api/openai-completions.ts";
|
||||
import type { Context, Model } from "../src/types.ts";
|
||||
|
||||
const mockState = vi.hoisted(() => ({
|
||||
|
||||
@@ -2,7 +2,7 @@ import { once } from "node:events";
|
||||
import http from "node:http";
|
||||
import type { AddressInfo } from "node:net";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { convertMessages, streamOpenAICompletions } from "../src/providers/openai-completions.ts";
|
||||
import { convertMessages, stream as streamOpenAICompletions } from "../src/api/openai-completions.ts";
|
||||
import type {
|
||||
AssistantMessage,
|
||||
AssistantMessageEvent,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { convertMessages } from "../src/api/openai-completions.ts";
|
||||
import { getModel } from "../src/models.ts";
|
||||
import { convertMessages } from "../src/providers/openai-completions.ts";
|
||||
import type {
|
||||
AssistantMessage,
|
||||
Context,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { stream as streamOpenAIResponses } from "../src/api/openai-responses.ts";
|
||||
import { getModel } from "../src/models.ts";
|
||||
import { streamOpenAIResponses } from "../src/providers/openai-responses.ts";
|
||||
import type { Model } from "../src/types.ts";
|
||||
|
||||
type CapturedHeaders = Headers | string[][] | Record<string, string | readonly string[]> | undefined;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { convertResponsesMessages } from "../src/api/openai-responses-shared.ts";
|
||||
import { getModel } from "../src/models.ts";
|
||||
import { convertResponsesMessages } from "../src/providers/openai-responses-shared.ts";
|
||||
import type { AssistantMessage, Context, ToolResultMessage, Usage } from "../src/types.ts";
|
||||
import { shortHash } from "../src/utils/hash.ts";
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ResponseOutputMessage } from "openai/resources/responses/responses.js";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { convertResponsesMessages } from "../src/api/openai-responses-shared.ts";
|
||||
import { getModel } from "../src/models.ts";
|
||||
import { convertResponsesMessages } from "../src/providers/openai-responses-shared.ts";
|
||||
import type { AssistantMessage, Context, Usage } from "../src/types.ts";
|
||||
|
||||
const usage: Usage = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ResponseStreamEvent } from "openai/resources/responses/responses.js";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { processResponsesStream } from "../src/providers/openai-responses-shared.ts";
|
||||
import { processResponsesStream } from "../src/api/openai-responses-shared.ts";
|
||||
import type { AssistantMessage, AssistantMessageEvent, Model } from "../src/types.ts";
|
||||
import { AssistantMessageEventStream } from "../src/utils/event-stream.ts";
|
||||
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
// Run from packages/ai: node test/scratch.ts
|
||||
// Requires ANTHROPIC_API_KEY.
|
||||
|
||||
import { anthropicMessagesApi } from "../src/api/anthropic-messages.lazy.ts";
|
||||
import { createModels, getModels, type Provider } from "../src/models.ts";
|
||||
import { streamAnthropic, streamSimpleAnthropic } from "../src/providers/register-builtins.ts";
|
||||
import type { Context } from "../src/types.ts";
|
||||
|
||||
const anthropicApi = anthropicMessagesApi();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 1. Define a provider. In the final design this comes from
|
||||
// `@earendil-works/pi-ai/providers/anthropic` as `anthropicProvider()`;
|
||||
@@ -33,8 +35,8 @@ const anthropic: Provider<"anthropic-messages"> = {
|
||||
getModels: async () => getModels("anthropic"),
|
||||
|
||||
// shared lazy API implementation (loads the SDK on first request)
|
||||
stream: streamAnthropic,
|
||||
streamSimple: streamSimpleAnthropic,
|
||||
stream: anthropicApi.stream,
|
||||
streamSimple: anthropicApi.streamSimple,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { transformMessages } from "../src/providers/transform-messages.ts";
|
||||
import { transformMessages } from "../src/api/transform-messages.ts";
|
||||
import type { AssistantMessage, Message, Model, ToolCall } from "../src/types.ts";
|
||||
|
||||
// Normalize function matching what anthropic.ts uses
|
||||
|
||||
Reference in New Issue
Block a user