From 6ae7e6fd1fcdf7d41d923c582f0d1a57a83937b8 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 19 Feb 2026 00:29:00 +0000 Subject: [PATCH] refactor(config): reuse legacy audio transcription migration path --- src/config/legacy-migrate.test.ts | 66 +++++++++++++++++++ src/config/legacy.migrations.part-2.ts | 89 ++++++++++++++------------ 2 files changed, 113 insertions(+), 42 deletions(-) create mode 100644 src/config/legacy-migrate.test.ts diff --git a/src/config/legacy-migrate.test.ts b/src/config/legacy-migrate.test.ts new file mode 100644 index 00000000000..e9ea4ac37bc --- /dev/null +++ b/src/config/legacy-migrate.test.ts @@ -0,0 +1,66 @@ +import { describe, expect, it } from "vitest"; +import { migrateLegacyConfig } from "./legacy-migrate.js"; + +describe("legacy migrate audio transcription", () => { + it("moves routing.transcribeAudio into tools.media.audio.models", () => { + const res = migrateLegacyConfig({ + routing: { + transcribeAudio: { + command: ["whisper", "--model", "base"], + timeoutSeconds: 2, + }, + }, + }); + + expect(res.changes).toContain("Moved routing.transcribeAudio → tools.media.audio.models."); + expect(res.config?.tools?.media?.audio).toEqual({ + enabled: true, + models: [ + { + command: "whisper", + type: "cli", + args: ["--model", "base"], + timeoutSeconds: 2, + }, + ], + }); + expect((res.config as { routing?: unknown } | null)?.routing).toBeUndefined(); + }); + + it("keeps existing tools media model and drops legacy routing value", () => { + const res = migrateLegacyConfig({ + routing: { + transcribeAudio: { + command: ["whisper", "--model", "tiny"], + }, + }, + tools: { + media: { + audio: { + models: [{ command: "existing", type: "cli" }], + }, + }, + }, + }); + + expect(res.changes).toContain( + "Removed routing.transcribeAudio (tools.media.audio.models already set).", + ); + expect(res.config?.tools?.media?.audio?.models).toEqual([{ command: "existing", type: "cli" }]); + expect((res.config as { routing?: unknown } | null)?.routing).toBeUndefined(); + }); + + it("drops invalid audio.transcription payloads", () => { + const res = migrateLegacyConfig({ + audio: { + transcription: { + command: [{}], + }, + }, + }); + + expect(res.changes).toContain("Removed audio.transcription (invalid or empty command)."); + expect(res.config?.audio).toBeUndefined(); + expect(res.config?.tools?.media?.audio).toBeUndefined(); + }); +}); diff --git a/src/config/legacy.migrations.part-2.ts b/src/config/legacy.migrations.part-2.ts index f9625856bb5..f8bee0da405 100644 --- a/src/config/legacy.migrations.part-2.ts +++ b/src/config/legacy.migrations.part-2.ts @@ -9,6 +9,32 @@ import { mergeMissing, } from "./legacy.shared.js"; +function applyLegacyAudioTranscriptionModel(params: { + raw: Record; + source: unknown; + changes: string[]; + movedMessage: string; + alreadySetMessage: string; + invalidMessage: string; +}) { + const mapped = mapLegacyAudioTranscription(params.source); + if (!mapped) { + params.changes.push(params.invalidMessage); + return; + } + const tools = ensureRecord(params.raw, "tools"); + const media = ensureRecord(tools, "media"); + const mediaAudio = ensureRecord(media, "audio"); + const models = Array.isArray(mediaAudio.models) ? (mediaAudio.models as unknown[]) : []; + if (models.length === 0) { + mediaAudio.enabled = true; + mediaAudio.models = [mapped]; + params.changes.push(params.movedMessage); + return; + } + params.changes.push(params.alreadySetMessage); +} + export const LEGACY_CONFIG_MIGRATIONS_PART_2: LegacyConfigMigration[] = [ { id: "agent.model-config-v2", @@ -355,22 +381,15 @@ export const LEGACY_CONFIG_MIGRATIONS_PART_2: LegacyConfigMigration[] = [ } if (routing.transcribeAudio !== undefined) { - const mapped = mapLegacyAudioTranscription(routing.transcribeAudio); - if (mapped) { - const tools = ensureRecord(raw, "tools"); - const media = ensureRecord(tools, "media"); - const mediaAudio = ensureRecord(media, "audio"); - const models = Array.isArray(mediaAudio.models) ? (mediaAudio.models as unknown[]) : []; - if (models.length === 0) { - mediaAudio.enabled = true; - mediaAudio.models = [mapped]; - changes.push("Moved routing.transcribeAudio → tools.media.audio.models."); - } else { - changes.push("Removed routing.transcribeAudio (tools.media.audio.models already set)."); - } - } else { - changes.push("Removed routing.transcribeAudio (invalid or empty command)."); - } + applyLegacyAudioTranscriptionModel({ + raw, + source: routing.transcribeAudio, + changes, + movedMessage: "Moved routing.transcribeAudio → tools.media.audio.models.", + alreadySetMessage: + "Removed routing.transcribeAudio (tools.media.audio.models already set).", + invalidMessage: "Removed routing.transcribeAudio (invalid or empty command).", + }); delete routing.transcribeAudio; } @@ -388,33 +407,19 @@ export const LEGACY_CONFIG_MIGRATIONS_PART_2: LegacyConfigMigration[] = [ return; } - const mapped = mapLegacyAudioTranscription(audio.transcription); - if (mapped) { - const tools = ensureRecord(raw, "tools"); - const media = ensureRecord(tools, "media"); - const mediaAudio = ensureRecord(media, "audio"); - const models = Array.isArray(mediaAudio.models) ? (mediaAudio.models as unknown[]) : []; - if (models.length === 0) { - mediaAudio.enabled = true; - mediaAudio.models = [mapped]; - changes.push("Moved audio.transcription → tools.media.audio.models."); - } else { - changes.push("Removed audio.transcription (tools.media.audio.models already set)."); - } - delete audio.transcription; - if (Object.keys(audio).length === 0) { - delete raw.audio; - } else { - raw.audio = audio; - } + applyLegacyAudioTranscriptionModel({ + raw, + source: audio.transcription, + changes, + movedMessage: "Moved audio.transcription → tools.media.audio.models.", + alreadySetMessage: "Removed audio.transcription (tools.media.audio.models already set).", + invalidMessage: "Removed audio.transcription (invalid or empty command).", + }); + delete audio.transcription; + if (Object.keys(audio).length === 0) { + delete raw.audio; } else { - delete audio.transcription; - changes.push("Removed audio.transcription (invalid or empty command)."); - if (Object.keys(audio).length === 0) { - delete raw.audio; - } else { - raw.audio = audio; - } + raw.audio = audio; } }, },