generate.test.ts 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import { expect } from "bun:test"
  2. import { LanguageModel } from "@opencode-ai/ai"
  3. import { OpenAIChat } from "@opencode-ai/ai/protocols"
  4. import { TestLLM } from "@opencode-ai/ai/testing"
  5. import { AISDK } from "@opencode-ai/core/aisdk"
  6. import { Catalog } from "@opencode-ai/core/catalog"
  7. import { Generate } from "@opencode-ai/core/generate"
  8. import { Integration } from "@opencode-ai/core/integration"
  9. import { ModelResolver } from "@opencode-ai/core/model-resolver"
  10. import { PluginHooks } from "@opencode-ai/core/plugin/hooks"
  11. import { ID, Info, Ref } from "@opencode-ai/core/model"
  12. import { Provider } from "@opencode-ai/core/provider"
  13. import { Npm } from "@opencode-ai/util/npm"
  14. import { Effect, Layer } from "effect"
  15. import { testEffect } from "./lib/effect"
  16. const selected = Info.make({
  17. ...Info.default(Provider.ID.make("test-provider"), ID.make("gemini")),
  18. package: Provider.aisdk("@ai-sdk/mistral"),
  19. })
  20. const runtime = LanguageModel.make({ id: "gemini", provider: "test-provider", route: OpenAIChat.route })
  21. const catalog = Layer.mock(Catalog.Service, {
  22. provider: {
  23. get: () => Effect.succeed(undefined),
  24. all: () => Effect.die("unused"),
  25. available: () => Effect.die("unused"),
  26. },
  27. model: {
  28. get: () => Effect.succeed(selected),
  29. all: () => Effect.die("unused"),
  30. available: () => Effect.die("unused"),
  31. default: () => Effect.die("unused"),
  32. small: () => Effect.die("unused"),
  33. },
  34. })
  35. const integrations = Layer.mock(Integration.Service, {
  36. connection: {
  37. active: () => Effect.succeed(undefined),
  38. resolve: () => Effect.die("unused"),
  39. key: () => Effect.die("unused"),
  40. update: () => Effect.die("unused"),
  41. remove: () => Effect.die("unused"),
  42. },
  43. oauth: {
  44. connect: () => Effect.die("unused"),
  45. status: () => Effect.die("unused"),
  46. complete: () => Effect.die("unused"),
  47. cancel: () => Effect.die("unused"),
  48. },
  49. command: {
  50. connect: () => Effect.die("unused"),
  51. status: () => Effect.die("unused"),
  52. cancel: () => Effect.die("unused"),
  53. },
  54. })
  55. const npm = Layer.mock(Npm.Service, {
  56. add: () => Effect.die("unused"),
  57. install: () => Effect.die("unused"),
  58. which: () => Effect.die("unused"),
  59. })
  60. const aisdk = Layer.mock(AISDK.Service, {
  61. hook: {
  62. sdk: () => Effect.die("unused"),
  63. language: () => Effect.die("unused"),
  64. },
  65. model: () => Effect.succeed(runtime),
  66. })
  67. const hooks = Layer.mock(PluginHooks.Service, {
  68. register: () => Effect.die("unused"),
  69. trigger: (_domain, _name, event) => Effect.succeed(event),
  70. })
  71. const client = TestLLM.clientLayer.pipe(Layer.provide(TestLLM.layer({ fallback: TestLLM.text("OK", "generate") })))
  72. const resolver = ModelResolver.layer.pipe(Layer.provide(Layer.mergeAll(catalog, integrations, npm, aisdk, hooks)))
  73. const it = testEffect(Generate.layer.pipe(Layer.provide(Layer.merge(resolver, client))))
  74. const resolverIt = testEffect(resolver)
  75. it.effect("loads dynamic AI SDK models", () =>
  76. Effect.gen(function* () {
  77. const generate = yield* Generate.Service
  78. const result = yield* generate.text({
  79. prompt: "Return exactly OK",
  80. model: Ref.make({ providerID: selected.providerID, id: selected.id }),
  81. })
  82. expect(result).toBe("OK")
  83. }),
  84. )
  85. resolverIt.effect("resolves dynamic models with their catalog metadata", () =>
  86. Effect.gen(function* () {
  87. const resolver = yield* ModelResolver.Service
  88. const result = yield* resolver.resolve(Ref.make({ providerID: selected.providerID, id: selected.id }))
  89. expect(result).toEqual({
  90. model: runtime,
  91. ref: Ref.make({ providerID: selected.providerID, id: selected.id }),
  92. capabilities: selected.capabilities,
  93. cost: selected.cost,
  94. })
  95. }),
  96. )