provider-openrouter.test.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import { AISDK } from "@opencode-ai/core/aisdk"
  2. import { describe, expect } from "bun:test"
  3. import { Effect } from "effect"
  4. import { Catalog } from "@opencode-ai/core/catalog"
  5. import { ModelV2 } from "@opencode-ai/core/model"
  6. import { PluginV2 } from "@opencode-ai/core/plugin"
  7. import { PluginHost } from "@opencode-ai/core/plugin/host"
  8. import { ProviderPlugins } from "@opencode-ai/core/plugin/provider"
  9. import { OpenRouterPlugin } from "@opencode-ai/core/plugin/provider/openrouter"
  10. import { ProviderV2 } from "@opencode-ai/core/provider"
  11. import { testEffect } from "../lib/effect"
  12. import { PluginTestLayer } from "./fixture"
  13. const it = testEffect(PluginTestLayer)
  14. const addPlugin = Effect.fn(function* () {
  15. const plugin = yield* PluginV2.Service
  16. const aisdk = yield* AISDK.Service
  17. const host = yield* PluginHost.make(plugin)
  18. yield* OpenRouterPlugin.effect(host)
  19. })
  20. describe("OpenRouterPlugin", () => {
  21. it.effect("is registered so legacy OpenRouter behavior can be applied", () =>
  22. Effect.sync(() => expect(ProviderPlugins.map((item) => item.id)).toContain(PluginV2.ID.make("openrouter"))),
  23. )
  24. it.effect("applies legacy referer headers only to openrouter", () =>
  25. Effect.gen(function* () {
  26. const catalog = yield* Catalog.Service
  27. yield* catalog.transform((catalog) => {
  28. catalog.provider.update(ProviderV2.ID.openrouter, (provider) => {
  29. provider.api = { type: "aisdk", package: "@openrouter/ai-sdk-provider" }
  30. provider.request = { headers: { Existing: "value" }, body: {} }
  31. })
  32. catalog.provider.update(ProviderV2.ID.make("nvidia"), () => {})
  33. })
  34. yield* addPlugin()
  35. expect((yield* catalog.provider.get(ProviderV2.ID.openrouter))?.request.headers).toEqual({
  36. Existing: "value",
  37. "HTTP-Referer": "https://opencode.ai/",
  38. "X-Title": "opencode",
  39. })
  40. expect((yield* catalog.provider.get(ProviderV2.ID.make("nvidia")))?.request.headers).toEqual({})
  41. }),
  42. )
  43. it.effect("creates an SDK only for the OpenRouter package", () =>
  44. Effect.gen(function* () {
  45. const plugin = yield* PluginV2.Service
  46. const aisdk = yield* AISDK.Service
  47. yield* addPlugin()
  48. const ignored = yield* aisdk.runSDK({
  49. model: ModelV2.Info.make({
  50. ...ModelV2.Info.empty(ProviderV2.ID.openrouter, ModelV2.ID.make("openai/gpt-5")),
  51. api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
  52. }),
  53. package: "@ai-sdk/openai-compatible",
  54. options: { name: "openrouter" },
  55. })
  56. expect(ignored.sdk).toBeUndefined()
  57. const result = yield* aisdk.runSDK({
  58. model: ModelV2.Info.make({
  59. ...ModelV2.Info.empty(ProviderV2.ID.make("custom"), ModelV2.ID.make("openai/gpt-5")),
  60. api: { id: ModelV2.ID.make("openai/gpt-5"), type: "aisdk", package: "test-provider" },
  61. }),
  62. package: "@openrouter/ai-sdk-provider",
  63. options: { name: "custom" },
  64. })
  65. expect(result.sdk).toBeDefined()
  66. }),
  67. )
  68. it.effect("filters OpenRouter's gpt-5 chat alias", () =>
  69. Effect.gen(function* () {
  70. const catalog = yield* Catalog.Service
  71. yield* catalog.transform((catalog) => {
  72. catalog.provider.update(ProviderV2.ID.openrouter, (provider) => {
  73. provider.api = { type: "aisdk", package: "@openrouter/ai-sdk-provider" }
  74. })
  75. catalog.provider.update(ProviderV2.ID.openai, () => {})
  76. catalog.model.update(ProviderV2.ID.openrouter, ModelV2.ID.make("openai/gpt-5-chat"), () => {})
  77. catalog.model.update(ProviderV2.ID.openrouter, ModelV2.ID.make("openai/gpt-5"), () => {})
  78. catalog.model.update(ProviderV2.ID.openai, ModelV2.ID.make("openai/gpt-5-chat"), () => {})
  79. })
  80. yield* addPlugin()
  81. expect((yield* catalog.model.get(ProviderV2.ID.openrouter, ModelV2.ID.make("openai/gpt-5-chat")))?.enabled).toBe(
  82. false,
  83. )
  84. expect((yield* catalog.model.get(ProviderV2.ID.openrouter, ModelV2.ID.make("openai/gpt-5")))?.enabled).toBe(true)
  85. expect((yield* catalog.model.get(ProviderV2.ID.openai, ModelV2.ID.make("openai/gpt-5-chat")))?.enabled).toBe(true)
  86. }),
  87. )
  88. it.effect("does not disable gpt-5-chat-latest for non-OpenRouter providers", () =>
  89. Effect.gen(function* () {
  90. const catalog = yield* Catalog.Service
  91. yield* catalog.transform((catalog) => {
  92. catalog.provider.update(ProviderV2.ID.make("custom-openrouter"), () => {})
  93. catalog.model.update(ProviderV2.ID.make("custom-openrouter"), ModelV2.ID.make("gpt-5-chat-latest"), () => {})
  94. })
  95. yield* addPlugin()
  96. expect(
  97. (yield* catalog.model.get(ProviderV2.ID.make("custom-openrouter"), ModelV2.ID.make("gpt-5-chat-latest")))
  98. ?.enabled,
  99. ).toBe(true)
  100. }),
  101. )
  102. })