provider-openai-compatible.test.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import { AISDK } from "@opencode-ai/core/aisdk"
  2. import { describe, expect } from "bun:test"
  3. import { Effect } from "effect"
  4. import { ModelV2 } from "@opencode-ai/core/model"
  5. import { PluginV2 } from "@opencode-ai/core/plugin"
  6. import { PluginHost } from "@opencode-ai/core/plugin/host"
  7. import { OpenAICompatiblePlugin } from "@opencode-ai/core/plugin/provider/openai-compatible"
  8. import { ProviderV2 } from "@opencode-ai/core/provider"
  9. import { testEffect } from "../lib/effect"
  10. import { PluginTestLayer } from "./fixture"
  11. const it = testEffect(PluginTestLayer)
  12. const addPlugin = Effect.fn(function* () {
  13. const plugin = yield* PluginV2.Service
  14. const aisdk = yield* AISDK.Service
  15. const host = yield* PluginHost.make(plugin)
  16. yield* OpenAICompatiblePlugin.effect(host)
  17. })
  18. describe("OpenAICompatiblePlugin", () => {
  19. it.effect("preserves explicit includeUsage false and defaults it to true", () =>
  20. Effect.gen(function* () {
  21. const plugin = yield* PluginV2.Service
  22. const aisdk = yield* AISDK.Service
  23. yield* addPlugin()
  24. const defaulted = yield* aisdk.runSDK({
  25. model: ModelV2.Info.make({
  26. ...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("model")),
  27. api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
  28. }),
  29. package: "@ai-sdk/openai-compatible",
  30. options: { name: "custom" },
  31. })
  32. const disabled = yield* aisdk.runSDK({
  33. model: ModelV2.Info.make({
  34. ...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("model")),
  35. api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
  36. }),
  37. package: "@ai-sdk/openai-compatible",
  38. options: { name: "custom", includeUsage: false },
  39. })
  40. expect(defaulted.options.includeUsage).toBe(true)
  41. expect(disabled.options.includeUsage).toBe(false)
  42. }),
  43. )
  44. it.effect("defaults includeUsage for OpenAI-compatible package matches", () =>
  45. Effect.gen(function* () {
  46. const plugin = yield* PluginV2.Service
  47. const aisdk = yield* AISDK.Service
  48. yield* addPlugin()
  49. const result = yield* aisdk.runSDK({
  50. model: ModelV2.Info.make({
  51. ...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("model")),
  52. api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
  53. }),
  54. package: "file:///tmp/@ai-sdk/openai-compatible-provider.js",
  55. options: { name: "custom" },
  56. })
  57. expect(result.options.includeUsage).toBe(true)
  58. }),
  59. )
  60. it.effect("uses the provider ID as the OpenAI-compatible provider name", () =>
  61. Effect.gen(function* () {
  62. const plugin = yield* PluginV2.Service
  63. const aisdk = yield* AISDK.Service
  64. const observed: string[] = []
  65. yield* addPlugin()
  66. yield* aisdk.hook.sdk((event) =>
  67. Effect.sync(() => {
  68. observed.push(event.sdk.languageModel("model").provider)
  69. }),
  70. )
  71. yield* aisdk.runSDK({
  72. model: ModelV2.Info.make({
  73. ...ModelV2.Info.empty(ProviderV2.ID.make("custom-provider"), ModelV2.ID.make("model")),
  74. api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
  75. }),
  76. package: "@ai-sdk/openai-compatible",
  77. options: { name: "custom-provider", baseURL: "https://example.com/v1" },
  78. })
  79. expect(observed).toEqual(["custom-provider.chat"])
  80. }),
  81. )
  82. it.effect("does not overwrite an SDK created by an earlier provider-specific plugin", () =>
  83. Effect.gen(function* () {
  84. const aisdk = yield* AISDK.Service
  85. const sentinel = { languageModel: (modelID: string) => ({ modelID }) }
  86. yield* aisdk.hook.sdk((event) => {
  87. event.sdk = sentinel
  88. })
  89. yield* addPlugin()
  90. const result = yield* aisdk.runSDK({
  91. model: ModelV2.Info.make({
  92. ...ModelV2.Info.empty(ProviderV2.ID.make("cloudflare-workers-ai"), ModelV2.ID.make("model")),
  93. api: { id: ModelV2.ID.make("model"), type: "aisdk", package: "test-provider" },
  94. }),
  95. package: "@ai-sdk/openai-compatible",
  96. options: { name: "cloudflare-workers-ai" },
  97. })
  98. expect(result.sdk).toBe(sentinel)
  99. }),
  100. )
  101. })