1
0

provider-helper.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import { Npm } from "@opencode-ai/core/npm"
  2. import type { LanguageModelV3 } from "@ai-sdk/provider"
  3. import { expect } from "bun:test"
  4. import { Effect, Layer, Option } from "effect"
  5. import { ModelV2 } from "@opencode-ai/core/model"
  6. import { PluginV2 } from "@opencode-ai/core/plugin"
  7. import { ProviderV2 } from "@opencode-ai/core/provider"
  8. import { testEffect } from "../lib/effect"
  9. export const fixtureProvider = new URL("./fixtures/provider-factory.ts", import.meta.url).href
  10. export const npmLayer = Layer.succeed(
  11. Npm.Service,
  12. Npm.Service.of({
  13. add: () => Effect.succeed({ directory: "", entrypoint: Option.none<string>() }),
  14. install: () => Effect.void,
  15. which: () => Effect.succeed(Option.none<string>()),
  16. }),
  17. )
  18. export const it = testEffect(Layer.mergeAll(PluginV2.defaultLayer, npmLayer))
  19. export function provider(providerID: string, options?: Partial<ProviderV2.Info>) {
  20. return new ProviderV2.Info({
  21. ...ProviderV2.Info.empty(ProviderV2.ID.make(providerID)),
  22. endpoint: {
  23. type: "aisdk",
  24. package: "test-provider",
  25. },
  26. ...options,
  27. options: {
  28. headers: {},
  29. body: {},
  30. aisdk: {
  31. provider: {},
  32. request: {},
  33. },
  34. ...options?.options,
  35. },
  36. })
  37. }
  38. export function model(providerID: string, modelID: string, options?: Partial<ModelV2.Info>) {
  39. return new ModelV2.Info({
  40. ...ModelV2.Info.empty(ProviderV2.ID.make(providerID), ModelV2.ID.make(modelID)),
  41. apiID: ModelV2.ID.make(modelID),
  42. endpoint: {
  43. type: "aisdk",
  44. package: "test-provider",
  45. },
  46. ...options,
  47. options: {
  48. headers: {},
  49. body: {},
  50. aisdk: {
  51. provider: {},
  52. request: {},
  53. },
  54. ...options?.options,
  55. },
  56. })
  57. }
  58. export function withEnv<A, E, R>(vars: Record<string, string | undefined>, fx: () => Effect.Effect<A, E, R>) {
  59. return Effect.acquireUseRelease(
  60. Effect.sync(() => {
  61. const previous = Object.fromEntries(Object.keys(vars).map((key) => [key, process.env[key]]))
  62. for (const [key, value] of Object.entries(vars)) {
  63. if (value === undefined) delete process.env[key]
  64. else process.env[key] = value
  65. }
  66. return previous
  67. }),
  68. () => fx(),
  69. (previous) =>
  70. Effect.sync(() => {
  71. for (const [key, value] of Object.entries(previous)) {
  72. if (value === undefined) delete process.env[key]
  73. else process.env[key] = value
  74. }
  75. }),
  76. )
  77. }
  78. export function fakeSelectorSdk(calls: string[]) {
  79. const make = (method: string) => (id: string) => {
  80. calls.push(`${method}:${id}`)
  81. return { modelId: id, provider: method, specificationVersion: "v3" } as unknown as LanguageModelV3
  82. }
  83. return {
  84. responses: make("responses"),
  85. messages: make("messages"),
  86. chat: make("chat"),
  87. languageModel: make("languageModel"),
  88. }
  89. }
  90. export function expectPluginRegistered(ids: string[], id: string) {
  91. expect(ids).toContain(PluginV2.ID.make(id))
  92. }