1import * as Config from "@effect/ai-codegen/Config"
2import * as Generator from "@effect/ai-codegen/Generator"
3import * as NodeServices from "@effect/platform-node/NodeServices"
4import { assert, describe, it } from "@effect/vitest"
5import * as Effect from "effect/Effect"
6import * as Yaml from "effect/encoding/Yaml"
7import * as FileSystem from "effect/FileSystem"
8import * as Layer from "effect/Layer"
9import * as Schema from "effect/Schema"
10
11const operation = (operationId: string) => ({
12 operationId,
13 requestBody: {
14 required: true,
15 content: {
16 "application/json": {
17 schema: { type: "object", properties: { input_audio: { type: "string" } }, required: ["input_audio"] }
18 },
19 "multipart/form-data": {
20 schema: { type: "object", properties: { file: { type: "string", format: "binary" } }, required: ["file"] }
21 }
22 }
23 },
24 responses: { "200": { description: "Success", content: { "application/json": { schema: { type: "string" } } } } }
25})
26
27describe("OpenRouter transcription spec patch", () => {
28 it.effect("selects JSON only for transcriptions while preserving multipart elsewhere", () =>
29 Effect.gen(function*() {
30 const fs = yield* FileSystem.FileSystem
31 const generator = yield* Generator.CodeGenerator
32 const config = Schema.decodeUnknownSync(Config.CodegenConfig)(Yaml.parse(
33 yield* fs.readFileString(new URL("../../../ai/openrouter/codegen.yml", import.meta.url).pathname)
34 ))
35 const patches = config.patchList.filter((patch) => patch.includes("/paths/~1audio~1transcriptions/"))
36 assert.strictEqual(patches.length, 1)
37 const generate = (patches: ReadonlyArray<string>) =>
38 generator.generate({
39 name: "openrouter",
40 packagePath: ".",
41 outputPath: "Generated.ts",
42 specSource: Config.SpecSource.File("fixture.json"),
43 config: new Config.CodegenConfig({ ...config, patches })
44 }, {
45 openapi: "3.1.0",
46 info: { title: "Transcriptions", version: "1.0.0" },
47 paths: {
48 "/audio/transcriptions": { post: operation("createAudioTranscriptions") },
49 "/uploads": { post: operation("upload") }
50 }
51 })
52 const before = yield* generate([])
53 const after = yield* generate(patches)
54 assert.include(before, "CreateAudioTranscriptionsRequestFormData.Encoded")
55 assert.notInclude(after, "CreateAudioTranscriptionsRequestFormData")
56 assert.include(after, "CreateAudioTranscriptionsRequestJson.Encoded")
57 assert.include(after, "UploadRequestFormData.Encoded")
58 assert.strictEqual(before.match(/HttpClientRequest.bodyFormDataRecord/g)?.length, 2)
59 assert.strictEqual(after.match(/HttpClientRequest.bodyFormDataRecord/g)?.length, 1)
60 assert.strictEqual(after.match(/HttpClientRequest.bodyJsonUnsafe/g)?.length, 1)
61 }).pipe(Effect.provide(Generator.layerSchema.pipe(Layer.provideMerge(NodeServices.layer)))))
62})