provider-openrouter.test.ts 4.7 KB

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