| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- import { expect } from "bun:test"
- import { LLMClient, LLMEvent, LLMResponse, Model } from "@opencode-ai/ai"
- import { OpenAIChat } from "@opencode-ai/ai/protocols"
- import { AISDK } from "@opencode-ai/core/aisdk"
- import { Catalog } from "@opencode-ai/core/catalog"
- import { Generate } from "@opencode-ai/core/generate"
- import { Integration } from "@opencode-ai/core/integration"
- import { ModelResolver } from "@opencode-ai/core/model-resolver"
- import { ModelV2 } from "@opencode-ai/core/model"
- import { ProviderV2 } from "@opencode-ai/core/provider"
- import { Npm } from "@opencode-ai/util/npm"
- import { Effect, Layer, Stream } from "effect"
- import { testEffect } from "./lib/effect"
- const selected = ModelV2.Info.make({
- ...ModelV2.Info.empty(ProviderV2.ID.make("test-provider"), ModelV2.ID.make("gemini")),
- package: ProviderV2.aisdk("@ai-sdk/google"),
- })
- const runtime = Model.make({ id: "gemini", provider: "test-provider", route: OpenAIChat.route })
- const catalog = Layer.mock(Catalog.Service, {
- provider: {
- get: () => Effect.succeed(undefined),
- all: () => Effect.die("unused"),
- available: () => Effect.die("unused"),
- },
- model: {
- get: () => Effect.succeed(selected),
- all: () => Effect.die("unused"),
- available: () => Effect.die("unused"),
- default: () => Effect.die("unused"),
- small: () => Effect.die("unused"),
- },
- })
- const integrations = Layer.mock(Integration.Service, {
- connection: {
- active: () => Effect.succeed(undefined),
- resolve: () => Effect.die("unused"),
- key: () => Effect.die("unused"),
- update: () => Effect.die("unused"),
- remove: () => Effect.die("unused"),
- },
- oauth: {
- connect: () => Effect.die("unused"),
- status: () => Effect.die("unused"),
- complete: () => Effect.die("unused"),
- cancel: () => Effect.die("unused"),
- },
- command: {
- connect: () => Effect.die("unused"),
- status: () => Effect.die("unused"),
- cancel: () => Effect.die("unused"),
- },
- })
- const npm = Layer.mock(Npm.Service, {
- add: () => Effect.die("unused"),
- install: () => Effect.die("unused"),
- which: () => Effect.die("unused"),
- })
- const aisdk = Layer.mock(AISDK.Service, {
- hook: {
- sdk: () => Effect.die("unused"),
- language: () => Effect.die("unused"),
- },
- model: () => Effect.succeed(runtime),
- })
- const client = Layer.mock(LLMClient.Service)({
- prepare: () => Effect.die("unused"),
- stream: () => Stream.die("unused"),
- generate: () =>
- Effect.sync(() => {
- const response = LLMResponse.fromEvents([
- LLMEvent.textStart({ id: "generate" }),
- LLMEvent.textDelta({ id: "generate", text: "OK" }),
- LLMEvent.textEnd({ id: "generate" }),
- LLMEvent.finish({ reason: "stop" }),
- ])
- if (!response) throw new Error("Incomplete generate response")
- return response
- }),
- })
- const resolver = ModelResolver.layer.pipe(Layer.provide(Layer.mergeAll(catalog, integrations, npm, aisdk)))
- const it = testEffect(Generate.layer.pipe(Layer.provide(Layer.merge(resolver, client))))
- const resolverIt = testEffect(resolver)
- it.effect("loads dynamic AI SDK models", () =>
- Effect.gen(function* () {
- const generate = yield* Generate.Service
- const result = yield* generate.text({
- prompt: "Return exactly OK",
- model: ModelV2.Ref.make({ providerID: selected.providerID, id: selected.id }),
- })
- expect(result).toBe("OK")
- }),
- )
- resolverIt.effect("resolves dynamic models with their catalog metadata", () =>
- Effect.gen(function* () {
- const resolver = yield* ModelResolver.Service
- const result = yield* resolver.resolve(ModelV2.Ref.make({ providerID: selected.providerID, id: selected.id }))
- expect(result).toEqual({
- model: runtime,
- ref: ModelV2.Ref.make({ providerID: selected.providerID, id: selected.id }),
- capabilities: selected.capabilities,
- cost: selected.cost,
- })
- }),
- )
|