recorded-golden.ts 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. import type { HttpRecorder } from "@opencode-ai/http-recorder"
  2. import { describe } from "bun:test"
  3. import { Effect } from "effect"
  4. import type { LanguageModel } from "../src"
  5. import { goldenScenarioTags, goldenScenarioTitle, runGoldenScenario, type GoldenScenarioID } from "./recorded-scenarios"
  6. import { recordedTests } from "./recorded-test"
  7. import { kebab } from "./recorded-utils"
  8. type Transport = "http" | "websocket"
  9. type ScenarioInput =
  10. | GoldenScenarioID
  11. | {
  12. readonly id: GoldenScenarioID
  13. readonly name?: string
  14. readonly cassette?: string
  15. readonly tags?: ReadonlyArray<string>
  16. readonly maxTokens?: number
  17. readonly temperature?: number | false
  18. readonly timeout?: number
  19. }
  20. type TargetInput = {
  21. readonly name: string
  22. readonly model: LanguageModel
  23. readonly protocol?: string
  24. readonly requires?: ReadonlyArray<string>
  25. readonly transport?: Transport
  26. readonly prefix?: string
  27. readonly tags?: ReadonlyArray<string>
  28. readonly metadata?: HttpRecorder.CassetteMetadata
  29. readonly options?: HttpRecorder.RecorderOptions
  30. readonly scenarios: ReadonlyArray<ScenarioInput>
  31. }
  32. const scenarioInput = (input: ScenarioInput) => (typeof input === "string" ? { id: input } : input)
  33. const defaultPrefix = (target: TargetInput) => {
  34. if (target.prefix) return target.prefix
  35. const transport = target.transport === "websocket" ? "-websocket" : ""
  36. return `${target.model.provider}-${target.protocol ?? target.model.route.id}${transport}`
  37. }
  38. const metadata = (target: TargetInput) => ({
  39. provider: target.model.provider,
  40. ...(target.protocol ? { protocol: target.protocol } : {}),
  41. route: target.model.route.id,
  42. transport: target.transport ?? "http",
  43. model: target.model.id,
  44. ...target.metadata,
  45. })
  46. const tags = (target: TargetInput) => [
  47. ...(target.transport === "websocket" ? ["transport:websocket"] : []),
  48. ...(target.tags ?? []),
  49. ]
  50. const runTarget = (target: TargetInput) => {
  51. const recorded = recordedTests({
  52. prefix: defaultPrefix(target),
  53. provider: target.model.provider,
  54. protocol: target.protocol,
  55. requires: target.requires,
  56. tags: tags(target),
  57. metadata: metadata(target),
  58. options: target.options,
  59. })
  60. describe(`${target.name} recorded`, () => {
  61. target.scenarios.forEach((raw) => {
  62. const input = scenarioInput(raw)
  63. const name = input.name ?? goldenScenarioTitle(input.id)
  64. recorded.effect.with(
  65. name,
  66. {
  67. cassette: input.cassette,
  68. id: `${kebab(target.name)}-${input.id}`,
  69. tags: [...goldenScenarioTags(input.id), ...(input.tags ?? [])],
  70. },
  71. () =>
  72. Effect.gen(function* () {
  73. yield* runGoldenScenario(input.id, {
  74. id: `recorded_${kebab(target.name).replaceAll("-", "_")}_${input.id.replaceAll("-", "_")}`,
  75. model: target.model,
  76. maxTokens: input.maxTokens,
  77. temperature: input.temperature,
  78. })
  79. }),
  80. input.timeout,
  81. )
  82. })
  83. })
  84. }
  85. export const describeRecordedGoldenScenarios = (targets: ReadonlyArray<TargetInput>) => {
  86. targets.forEach(runTarget)
  87. }