provider-anthropic.test.ts 3.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  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 { Model } from "@opencode-ai/core/model"
  6. import { Plugin } from "@opencode-ai/core/plugin"
  7. import { PluginHost } from "@opencode-ai/core/plugin/host"
  8. import { AnthropicPlugin } from "@opencode-ai/core/plugin/provider/anthropic"
  9. import { Provider } 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* Plugin.Service
  15. const aisdk = yield* AISDK.Service
  16. const host = yield* PluginHost.make(plugin)
  17. yield* AnthropicPlugin.effect(host)
  18. })
  19. function required<T>(value: T | undefined): T {
  20. if (value === undefined) throw new Error("Expected value")
  21. return value
  22. }
  23. describe("AnthropicPlugin", () => {
  24. it.effect("applies legacy beta headers", () =>
  25. Effect.gen(function* () {
  26. const catalog = yield* Catalog.Service
  27. yield* catalog.transform((catalog) => {
  28. const item = Provider.Info.make({
  29. ...Provider.Info.empty(Provider.ID.anthropic),
  30. package: Provider.aisdk("@ai-sdk/anthropic"),
  31. headers: { Existing: "1" },
  32. })
  33. catalog.provider.update(item.id, (draft) => {
  34. draft.package = item.package
  35. draft.headers = { Existing: "1" }
  36. })
  37. })
  38. yield* addPlugin()
  39. expect(required(yield* catalog.provider.get(Provider.ID.anthropic)).headers?.["anthropic-beta"]).toBe(
  40. "interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14",
  41. )
  42. expect(required(yield* catalog.provider.get(Provider.ID.anthropic)).headers?.Existing).toBe("1")
  43. }),
  44. )
  45. it.effect("ignores non-Anthropic providers", () =>
  46. Effect.gen(function* () {
  47. const catalog = yield* Catalog.Service
  48. yield* catalog.transform((catalog) => catalog.provider.update(Provider.ID.openai, () => {}))
  49. yield* addPlugin()
  50. expect(required(yield* catalog.provider.get(Provider.ID.openai)).headers?.["anthropic-beta"]).toBeUndefined()
  51. }),
  52. )
  53. it.effect("creates Anthropic SDKs with the model provider ID as the SDK name", () =>
  54. Effect.gen(function* () {
  55. const plugin = yield* Plugin.Service
  56. const aisdk = yield* AISDK.Service
  57. yield* addPlugin()
  58. const result = yield* aisdk.runSDK({
  59. model: Model.Info.make({
  60. ...Model.Info.default(Provider.ID.make("custom-anthropic"), Model.ID.make("claude-sonnet-4-5")),
  61. modelID: Model.ID.make("claude-sonnet-4-5"),
  62. package: Provider.aisdk("@ai-sdk/anthropic"),
  63. }),
  64. package: "@ai-sdk/anthropic",
  65. options: { name: "custom-anthropic", apiKey: "test" },
  66. })
  67. expect(result.sdk.languageModel("claude-sonnet-4-5").provider).toBe("custom-anthropic")
  68. }),
  69. )
  70. it.effect("uses the Anthropic provider ID as the SDK name for the bundled Anthropic provider", () =>
  71. Effect.gen(function* () {
  72. const plugin = yield* Plugin.Service
  73. const aisdk = yield* AISDK.Service
  74. yield* addPlugin()
  75. const result = yield* aisdk.runSDK({
  76. model: Model.Info.make({
  77. ...Model.Info.default(Provider.ID.anthropic, Model.ID.make("claude-sonnet-4-5")),
  78. modelID: Model.ID.make("claude-sonnet-4-5"),
  79. package: Provider.aisdk("@ai-sdk/anthropic"),
  80. }),
  81. package: "@ai-sdk/anthropic",
  82. options: { name: "anthropic", apiKey: "test" },
  83. })
  84. expect(result.sdk.languageModel("claude-sonnet-4-5").provider).toBe("anthropic")
  85. }),
  86. )
  87. })