import { describe, expect, test } from "bun:test" import { Effect, Layer } from "effect" import { ai, AIClient, AIError, DecisionClient, EmbeddingClient, Generation, ImageClient, LanguageModel, LLMClient, LLMEvent, Media, RawEvent, } from "@opencode/ai/effect" import { AI } from "@opencode/ai" import { Speech, SpeechClient, SpeechEvent, TranscriptionClient, Video, VideoClient } from "@opencode/ai/experimental" import { Route, Protocol, RequestExecutor, WebSocketTransport } from "@opencode/ai/route" import { Alibaba, AssemblyAI, Baseten, Cartesia, CloudflareAIGateway, CloudflareWorkersAI, Deepgram, DeepSeek, DigitalOcean, ElevenLabs, Fireworks, Google, Meta, MiniMax, OpenCode, OpenAI, OpenAICompatible, OpenRouter, Runway, TypeSafeAI, VercelAIGateway, XAI, ZAI, } from "@opencode/ai/providers" import { AnthropicMessages, OpenAIChat, OpenAICompatibleChat, OpenAICompatibleResponses, OpenAIResponses, OpenResponses, OpenResponsesChannel, } from "@opencode/ai/protocols" import { TestLLM } from "@opencode/ai/testing" describe("public exports", () => { test("modality, provider, and protocol entrypoints load first in a fresh process", async () => { const results = await Promise.all( ["image", "video", "speech", "transcription", "providers", "protocols"].map(async (entry) => { const child = Bun.spawn( [process.execPath, "-e", `await import(${JSON.stringify(`${import.meta.dir}/../src/${entry}.ts`)})`], { stderr: "pipe" }, ) return { entry, exitCode: await child.exited, stderr: await new Response(child.stderr).text() } }), ) expect(results.filter((result) => result.exitCode !== 0)).toEqual([]) }) test("root exposes app-facing runtime APIs", () => { expect(ai.request).toBeFunction() expect(LLMClient.Service).toBeFunction() expect(LLMClient.layer).toBeDefined() expect(AIError).toBeFunction() expect(LanguageModel.make).toBeFunction() expect(Media.bytes).toBeFunction() expect(ai.image.generate).toBeFunction() expect(Video.start).toBeFunction() expect(Video.resume).toBeFunction() expect(VideoClient.layer).toBeDefined() expect(Speech.generate).toBeFunction() expect(Speech.stream).toBeFunction() expect(SpeechClient.layer).toBeDefined() expect(SpeechEvent.is.audioDelta).toBeFunction() expect(Generation).toBeFunction() expect(TestLLM.testLayer).toBeFunction() expect(TestLLM.Test.of).toBeFunction() expect(ai.decision.generate).toBeFunction() expect(DecisionClient.layer).toBeDefined() expect(DecisionClient.fetchLayer).toBeDefined() expect(ai.embedding.generate).toBeFunction() expect(EmbeddingClient.layer).toBeDefined() expect(AI.ai.embedding.generate).toBeFunction() }) test("both entry points export the raw event type", () => { const event: RawEvent = LLMEvent.raw({ data: { type: "ping" } }) const promiseEvent: AI.RawEvent = event expect(RawEvent).toBe(AI.RawEvent) expect(LLMEvent.is.raw(promiseEvent)).toBe(true) }) test("AIClient.layerWith shares one executor across every client", async () => { let built = 0 const counting = Layer.effect( RequestExecutor.Service, Effect.sync(() => { built++ return RequestExecutor.Service.of({ execute: () => Effect.die("unexpected request") }) }), ) await Effect.runPromise( Effect.gen(function* () { yield* LLMClient.Service yield* ImageClient.Service yield* VideoClient.Service yield* SpeechClient.Service yield* TranscriptionClient.Service yield* DecisionClient.Service yield* EmbeddingClient.Service yield* RequestExecutor.Service }).pipe(Effect.provide(AIClient.layerWith(counting))), ) expect(built).toBe(1) }) test("route barrel exposes route-authoring APIs", () => { expect(Route.make).toBeFunction() expect(Protocol.make).toBeFunction() expect(WebSocketTransport.makeDirect).toBeFunction() }) test("provider barrels expose user-facing facades", async () => { const { OpenAICompatibleResponses } = await import("@opencode/ai/providers") expect(OpenAI.model).toBeFunction() expect(OpenAI.embedding).toBeFunction() expect(OpenAI.provider.responses).toBe(OpenAI.responses) expect(OpenAI.configure({ apiKey: "fixture" }).responses).toBeFunction() for (const provider of [Baseten, DeepSeek, Fireworks]) { expect(provider.configure).toBeFunction() expect(provider.model).toBeFunction() } for (const name of ["baseten", "cerebras", "deepinfra", "deepseek", "fireworks", "groq", "togetherai"]) { expect(OpenAICompatible).not.toHaveProperty(name) } expect( OpenAICompatibleResponses.configure({ baseURL: "https://responses.test/v1" }).model("fixture").route.id, ).toBe("openai-compatible-responses") expect(CloudflareAIGateway.configure).toBeFunction() expect(CloudflareAIGateway.configure({ accountId: "fixture", gatewayApiKey: "fixture" }).model).toBeFunction() expect(CloudflareWorkersAI.configure).toBeFunction() expect(CloudflareWorkersAI.configure({ accountId: "fixture", apiKey: "fixture" }).model).toBeFunction() expect(OpenRouter.model).toBeFunction() expect(OpenRouter.decision).toBeFunction() expect(TypeSafeAI.decision).toBeFunction() expect(OpenCode.decision).toBeFunction() expect(VercelAIGateway.decision("typesafe-ai/jev").route.id).toBe("openai-decisions") expect(Baseten.decision).toBeFunction() expect(DigitalOcean.decision).toBeFunction() expect(OpenAI.decision("gpt-6-luna").route.id).toBe("openai-decisions") expect(XAI.model).toBeFunction() expect(XAI.provider.responses).toBe(XAI.responses) expect(XAI.provider.chat).toBe(XAI.chat) expect(XAI.configure({ apiKey: "fixture" }).responses("grok-4.3").route.id).toBe("xai-responses") expect(XAI.configure({ apiKey: "fixture" }).chat("grok-4.3").route.id).toBe("xai-chat") expect(OpenAI.configure({ apiKey: "fixture" }).image("gpt-image-2").route.id).toBe("openai-images") expect(OpenAI.provider.image).toBe(OpenAI.image) expect(Google.configure({ apiKey: "fixture" }).image("imagen-4.0-generate-001").route.id).toBe("google-images") expect(Google.provider.image).toBe(Google.image) expect(XAI.configure({ apiKey: "fixture" }).image("grok-imagine-image").route.id).toBe("xai-images") expect(XAI.provider.image).toBe(XAI.image) expect(Meta.configure({ apiKey: "fixture" }).image("muse-image").route.id).toBe("meta-images") expect(Meta.provider.image).toBe(Meta.image) expect(ZAI.configure({ apiKey: "fixture" }).image("glm-image").route.id).toBe("zai-images") expect(ZAI.provider.image).toBe(ZAI.image) expect(MiniMax.configure({ apiKey: "fixture" }).image("image-01").route.id).toBe("minimax-images") expect(MiniMax.provider.image).toBe(MiniMax.image) expect(Alibaba.configure({ region: "ap-southeast-1", apiKey: "fixture" }).image("qwen-image-2.0").route.id).toBe( "alibaba-images", ) expect(VercelAIGateway.configure({ apiKey: "fixture" }).image("meta/muse-image-1.0").route.id).toBe("openai-images") expect(VercelAIGateway.provider.image).toBe(VercelAIGateway.image) expect( CloudflareWorkersAI.configure({ accountId: "fixture", apiKey: "fixture" }).image( "@cf/black-forest-labs/flux-1-schnell", ).route.id, ).toBe("cloudflare-workers-ai-images") expect(XAI.configure({ apiKey: "fixture" }).video("grok-imagine-video-1.5").route.id).toBe("xai-video") expect(XAI.provider.video).toBe(XAI.video) expect(Google.configure({ apiKey: "fixture" }).video("veo-3.1-generate-preview").route.id).toBe("google-video") expect(Google.provider.video).toBe(Google.video) expect(Runway.configure({ apiKey: "fixture" }).video("gen4.5").route.id).toBe("runway-video") expect(Runway.provider.video).toBe(Runway.video) expect(OpenAI.configure({ apiKey: "fixture" }).speech("gpt-4o-mini-tts").route.id).toBe("openai-speech") expect(Google.configure({ apiKey: "fixture" }).speech("gemini-2.5-flash-preview-tts").route.id).toBe( "google-speech", ) expect(ElevenLabs.configure({ apiKey: "fixture" }).speech("eleven_flash_v2_5").route.id).toBe("elevenlabs-speech") expect(Cartesia.configure({ apiKey: "fixture" }).speech("sonic-3").route.id).toBe("cartesia-speech") expect(Deepgram.configure({ apiKey: "fixture" }).speech("aura-2-thalia-en").route.id).toBe("deepgram-speech") expect(OpenAI.configure({ apiKey: "fixture" }).transcription("gpt-transcribe").route.kind).toBe("stream") expect(Google.configure({ apiKey: "fixture" }).transcription("gemini-3.5-transcribe").route.kind).toBe("stream") expect(Deepgram.configure({ apiKey: "fixture" }).transcription("nova-3").route.kind).toBe("inline") expect(AssemblyAI.configure({ apiKey: "fixture" }).transcription("universal-3-5-pro").route.kind).toBe("queued") expect(ElevenLabs.configure({ apiKey: "fixture" }).transcription("scribe_v2").route.id).toBe( "elevenlabs-transcription", ) expect(ElevenLabs.configure({ apiKey: "fixture" }).transcription("scribe_v2").route.kind).toBe("inline") expect(ElevenLabs.provider.transcription).toBe(ElevenLabs.transcription) }) test("protocol barrels expose supported low-level routes", () => { expect(OpenAIChat.route.id).toBe("openai-chat") expect(OpenAICompatibleChat.route.id).toBe("openai-compatible-chat") expect(OpenResponses.protocol.id).toBe("open-responses") expect(OpenResponsesChannel.transport).toBeFunction() expect(OpenAICompatibleResponses.route.id).toBe("openai-compatible-responses") expect(OpenAICompatibleResponses.route.protocol).toBe("open-responses") expect(OpenAIResponses.route.id).toBe("openai-responses") expect(AnthropicMessages.route.id).toBe("anthropic-messages") }) })