refactor(config): reuse legacy audio transcription migration path

This commit is contained in:
Peter Steinberger
2026-02-19 00:29:00 +00:00
parent 2dd361c071
commit 6ae7e6fd1f
2 changed files with 113 additions and 42 deletions

View File

@@ -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();
});
});

View File

@@ -9,6 +9,32 @@ import {
mergeMissing,
} from "./legacy.shared.js";
function applyLegacyAudioTranscriptionModel(params: {
raw: Record<string, unknown>;
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;
}
},
},