mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-10 07:11:25 +03:00
212 lines
9.6 KiB
TypeScript
212 lines
9.6 KiB
TypeScript
import { describe, expect, test } from "bun:test"
|
|
import { Effect, Layer } from "effect"
|
|
import {
|
|
ai,
|
|
AIClient,
|
|
AIError,
|
|
DecisionClient,
|
|
Generation,
|
|
ImageClient,
|
|
LanguageModel,
|
|
LLMClient,
|
|
Media,
|
|
} from "@opencode/ai/effect"
|
|
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()
|
|
})
|
|
|
|
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* 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.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(OpenCode.provider.chat).toBe(OpenCode.chat)
|
|
expect(OpenCode.configure({ apiKey: "fixture" }).model("glm-5.3").route.id).toBe("opencode-chat")
|
|
expect(OpenCode.responses("gpt-5-nano").route.id).toBe("opencode-responses")
|
|
expect(OpenCode.messages("claude-haiku-5-5").route.id).toBe("opencode-messages")
|
|
expect(OpenCode.gemini("gemini-3.5-flash-lite").route.id).toBe("opencode-gemini")
|
|
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")
|
|
})
|
|
})
|