fix(doctor): stop repeating talk normalization

This commit is contained in:
Vincent Koc
2026-06-06 08:46:28 -07:00
parent f00e7af3e3
commit b77ef4d6df
4 changed files with 98 additions and 0 deletions
+13
View File
@@ -2916,6 +2916,19 @@ describe("doctor config flow", () => {
modelId: "eleven_v3",
},
},
realtime: {
provider: "openai",
providers: {
openai: {
model: "gpt-realtime",
},
},
model: "gpt-realtime",
voice: "cedar",
mode: "realtime",
transport: "gateway-relay",
brain: "agent-consult",
},
},
},
null,
@@ -1251,6 +1251,37 @@ describe("normalizeCompatibilityConfigValues", () => {
expect(res.changes).toStrictEqual([]);
});
it("does not report talk provider normalization for realtime voice aliases", () => {
const input = {
talk: {
provider: "elevenlabs",
providers: {
elevenlabs: {
voiceId: "voice-123",
},
},
realtime: {
provider: "openai",
providers: {
openai: {
model: "gpt-realtime",
},
},
model: "gpt-realtime",
voice: "cedar",
mode: "realtime",
transport: "gateway-relay",
brain: "agent-consult",
},
},
};
const res = normalizeCompatibilityConfigValues(input as OpenClawConfig);
expect(res.config).toEqual(input);
expect(res.changes).toStrictEqual([]);
});
it("migrates tools.message.allowCrossContextSend to canonical crossContext settings", () => {
const res = normalizeCompatibilityConfigValues({
tools: {
@@ -102,6 +102,38 @@ describe("legacy migrate provider-shaped config", () => {
});
});
it("does not treat an existing realtime voice alias as Talk provider repair", () => {
const input: OpenClawConfig = {
talk: {
provider: "elevenlabs",
providers: {
elevenlabs: {
voiceId: "voice-1",
},
},
realtime: {
provider: "openai",
providers: {
openai: {
model: "gpt-realtime",
},
},
model: "gpt-realtime",
voice: "cedar",
mode: "realtime",
transport: "gateway-relay",
brain: "agent-consult",
},
},
};
const changes: string[] = [];
const migrated = normalizeLegacyTalkConfig(input, changes);
expect(changes).toStrictEqual([]);
expect(migrated).toEqual(input);
});
it("moves messages.tts.<provider> keys into messages.tts.providers", () => {
const res = migrateLegacyConfig({
messages: {
@@ -41,6 +41,27 @@ function buildLegacyRealtimeTalkCompat(
return normalizeTalkSection({ realtime: compat } as OpenClawConfig["talk"])?.realtime;
}
function removeDerivedRealtimeSpeakerVoice(
rawTalk: Record<string, unknown>,
normalizedTalk: NonNullable<OpenClawConfig["talk"]>,
): void {
const rawRealtime = rawTalk.realtime;
const normalizedRealtime = normalizedTalk.realtime;
if (
!isRecord(rawRealtime) ||
!normalizedRealtime ||
rawRealtime.speakerVoice !== undefined ||
normalizedRealtime.speakerVoice === undefined ||
normalizedRealtime.speakerVoice !== normalizedRealtime.voice
) {
return;
}
// Runtime clients still get speakerVoice from the deprecated voice alias, but
// doctor should not persist that derived value or report it as provider repair.
delete normalizedRealtime.speakerVoice;
}
/** Normalize legacy Talk provider/realtime fields into current talk.providers and talk.realtime. */
export function normalizeLegacyTalkConfig(cfg: OpenClawConfig, changes: string[]): OpenClawConfig {
const rawTalk = cfg.talk;
@@ -66,6 +87,7 @@ export function normalizeLegacyTalkConfig(cfg: OpenClawConfig, changes: string[]
...normalizedTalk.realtime,
};
}
removeDerivedRealtimeSpeakerVoice(rawTalk, normalizedTalk);
if (Object.keys(normalizedTalk).length === 0 || isDeepStrictEqual(normalizedTalk, rawTalk)) {
return cfg;
}