refactor(config): split legacy handling
This commit is contained in:
413
src/config/legacy.migrations.part-2.ts
Normal file
413
src/config/legacy.migrations.part-2.ts
Normal file
@@ -0,0 +1,413 @@
|
||||
import {
|
||||
ensureAgentEntry,
|
||||
ensureRecord,
|
||||
getAgentsList,
|
||||
getRecord,
|
||||
isRecord,
|
||||
type LegacyConfigMigration,
|
||||
mapLegacyAudioTranscription,
|
||||
mergeMissing,
|
||||
} from "./legacy.shared.js";
|
||||
|
||||
export const LEGACY_CONFIG_MIGRATIONS_PART_2: LegacyConfigMigration[] = [
|
||||
{
|
||||
id: "agent.model-config-v2",
|
||||
describe:
|
||||
"Migrate legacy agent.model/allowedModels/modelAliases/modelFallbacks/imageModelFallbacks to agent.models + model lists",
|
||||
apply: (raw, changes) => {
|
||||
const agentRoot = getRecord(raw.agent);
|
||||
const defaults = getRecord(getRecord(raw.agents)?.defaults);
|
||||
const agent = agentRoot ?? defaults;
|
||||
if (!agent) return;
|
||||
const label = agentRoot ? "agent" : "agents.defaults";
|
||||
|
||||
const legacyModel =
|
||||
typeof agent.model === "string" ? String(agent.model) : undefined;
|
||||
const legacyImageModel =
|
||||
typeof agent.imageModel === "string"
|
||||
? String(agent.imageModel)
|
||||
: undefined;
|
||||
const legacyAllowed = Array.isArray(agent.allowedModels)
|
||||
? (agent.allowedModels as unknown[]).map(String)
|
||||
: [];
|
||||
const legacyModelFallbacks = Array.isArray(agent.modelFallbacks)
|
||||
? (agent.modelFallbacks as unknown[]).map(String)
|
||||
: [];
|
||||
const legacyImageModelFallbacks = Array.isArray(agent.imageModelFallbacks)
|
||||
? (agent.imageModelFallbacks as unknown[]).map(String)
|
||||
: [];
|
||||
const legacyAliases =
|
||||
agent.modelAliases && typeof agent.modelAliases === "object"
|
||||
? (agent.modelAliases as Record<string, unknown>)
|
||||
: {};
|
||||
|
||||
const hasLegacy =
|
||||
legacyModel ||
|
||||
legacyImageModel ||
|
||||
legacyAllowed.length > 0 ||
|
||||
legacyModelFallbacks.length > 0 ||
|
||||
legacyImageModelFallbacks.length > 0 ||
|
||||
Object.keys(legacyAliases).length > 0;
|
||||
if (!hasLegacy) return;
|
||||
|
||||
const models =
|
||||
agent.models && typeof agent.models === "object"
|
||||
? (agent.models as Record<string, unknown>)
|
||||
: {};
|
||||
|
||||
const ensureModel = (rawKey?: string) => {
|
||||
if (typeof rawKey !== "string") return;
|
||||
const key = rawKey.trim();
|
||||
if (!key) return;
|
||||
if (!models[key]) models[key] = {};
|
||||
};
|
||||
|
||||
ensureModel(legacyModel);
|
||||
ensureModel(legacyImageModel);
|
||||
for (const key of legacyAllowed) ensureModel(key);
|
||||
for (const key of legacyModelFallbacks) ensureModel(key);
|
||||
for (const key of legacyImageModelFallbacks) ensureModel(key);
|
||||
for (const target of Object.values(legacyAliases)) {
|
||||
if (typeof target !== "string") continue;
|
||||
ensureModel(target);
|
||||
}
|
||||
|
||||
for (const [alias, targetRaw] of Object.entries(legacyAliases)) {
|
||||
if (typeof targetRaw !== "string") continue;
|
||||
const target = targetRaw.trim();
|
||||
if (!target) continue;
|
||||
const entry =
|
||||
models[target] && typeof models[target] === "object"
|
||||
? (models[target] as Record<string, unknown>)
|
||||
: {};
|
||||
if (!("alias" in entry)) {
|
||||
entry.alias = alias;
|
||||
models[target] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
const currentModel =
|
||||
agent.model && typeof agent.model === "object"
|
||||
? (agent.model as Record<string, unknown>)
|
||||
: null;
|
||||
if (currentModel) {
|
||||
if (!currentModel.primary && legacyModel) {
|
||||
currentModel.primary = legacyModel;
|
||||
}
|
||||
if (
|
||||
legacyModelFallbacks.length > 0 &&
|
||||
(!Array.isArray(currentModel.fallbacks) ||
|
||||
currentModel.fallbacks.length === 0)
|
||||
) {
|
||||
currentModel.fallbacks = legacyModelFallbacks;
|
||||
}
|
||||
agent.model = currentModel;
|
||||
} else if (legacyModel || legacyModelFallbacks.length > 0) {
|
||||
agent.model = {
|
||||
primary: legacyModel,
|
||||
fallbacks: legacyModelFallbacks.length ? legacyModelFallbacks : [],
|
||||
};
|
||||
}
|
||||
|
||||
const currentImageModel =
|
||||
agent.imageModel && typeof agent.imageModel === "object"
|
||||
? (agent.imageModel as Record<string, unknown>)
|
||||
: null;
|
||||
if (currentImageModel) {
|
||||
if (!currentImageModel.primary && legacyImageModel) {
|
||||
currentImageModel.primary = legacyImageModel;
|
||||
}
|
||||
if (
|
||||
legacyImageModelFallbacks.length > 0 &&
|
||||
(!Array.isArray(currentImageModel.fallbacks) ||
|
||||
currentImageModel.fallbacks.length === 0)
|
||||
) {
|
||||
currentImageModel.fallbacks = legacyImageModelFallbacks;
|
||||
}
|
||||
agent.imageModel = currentImageModel;
|
||||
} else if (legacyImageModel || legacyImageModelFallbacks.length > 0) {
|
||||
agent.imageModel = {
|
||||
primary: legacyImageModel,
|
||||
fallbacks: legacyImageModelFallbacks.length
|
||||
? legacyImageModelFallbacks
|
||||
: [],
|
||||
};
|
||||
}
|
||||
|
||||
agent.models = models;
|
||||
|
||||
if (legacyModel !== undefined) {
|
||||
changes.push(
|
||||
`Migrated ${label}.model string → ${label}.model.primary.`,
|
||||
);
|
||||
}
|
||||
if (legacyModelFallbacks.length > 0) {
|
||||
changes.push(
|
||||
`Migrated ${label}.modelFallbacks → ${label}.model.fallbacks.`,
|
||||
);
|
||||
}
|
||||
if (legacyImageModel !== undefined) {
|
||||
changes.push(
|
||||
`Migrated ${label}.imageModel string → ${label}.imageModel.primary.`,
|
||||
);
|
||||
}
|
||||
if (legacyImageModelFallbacks.length > 0) {
|
||||
changes.push(
|
||||
`Migrated ${label}.imageModelFallbacks → ${label}.imageModel.fallbacks.`,
|
||||
);
|
||||
}
|
||||
if (legacyAllowed.length > 0) {
|
||||
changes.push(`Migrated ${label}.allowedModels → ${label}.models.`);
|
||||
}
|
||||
if (Object.keys(legacyAliases).length > 0) {
|
||||
changes.push(
|
||||
`Migrated ${label}.modelAliases → ${label}.models.*.alias.`,
|
||||
);
|
||||
}
|
||||
|
||||
delete agent.allowedModels;
|
||||
delete agent.modelAliases;
|
||||
delete agent.modelFallbacks;
|
||||
delete agent.imageModelFallbacks;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "routing.agents-v2",
|
||||
describe: "Move routing.agents/defaultAgentId to agents.list",
|
||||
apply: (raw, changes) => {
|
||||
const routing = getRecord(raw.routing);
|
||||
if (!routing) return;
|
||||
|
||||
const routingAgents = getRecord(routing.agents);
|
||||
const agents = ensureRecord(raw, "agents");
|
||||
const list = getAgentsList(agents);
|
||||
|
||||
if (routingAgents) {
|
||||
for (const [rawId, entryRaw] of Object.entries(routingAgents)) {
|
||||
const agentId = String(rawId ?? "").trim();
|
||||
const entry = getRecord(entryRaw);
|
||||
if (!agentId || !entry) continue;
|
||||
|
||||
const target = ensureAgentEntry(list, agentId);
|
||||
const entryCopy: Record<string, unknown> = { ...entry };
|
||||
|
||||
if ("mentionPatterns" in entryCopy) {
|
||||
const mentionPatterns = entryCopy.mentionPatterns;
|
||||
const groupChat = ensureRecord(target, "groupChat");
|
||||
if (groupChat.mentionPatterns === undefined) {
|
||||
groupChat.mentionPatterns = mentionPatterns;
|
||||
changes.push(
|
||||
`Moved routing.agents.${agentId}.mentionPatterns → agents.list (id "${agentId}").groupChat.mentionPatterns.`,
|
||||
);
|
||||
} else {
|
||||
changes.push(
|
||||
`Removed routing.agents.${agentId}.mentionPatterns (agents.list groupChat mentionPatterns already set).`,
|
||||
);
|
||||
}
|
||||
delete entryCopy.mentionPatterns;
|
||||
}
|
||||
|
||||
const legacyGroupChat = getRecord(entryCopy.groupChat);
|
||||
if (legacyGroupChat) {
|
||||
const groupChat = ensureRecord(target, "groupChat");
|
||||
mergeMissing(groupChat, legacyGroupChat);
|
||||
delete entryCopy.groupChat;
|
||||
}
|
||||
|
||||
const legacySandbox = getRecord(entryCopy.sandbox);
|
||||
if (legacySandbox) {
|
||||
const sandboxTools = getRecord(legacySandbox.tools);
|
||||
if (sandboxTools) {
|
||||
const tools = ensureRecord(target, "tools");
|
||||
const sandbox = ensureRecord(tools, "sandbox");
|
||||
const toolPolicy = ensureRecord(sandbox, "tools");
|
||||
mergeMissing(toolPolicy, sandboxTools);
|
||||
delete legacySandbox.tools;
|
||||
changes.push(
|
||||
`Moved routing.agents.${agentId}.sandbox.tools → agents.list (id "${agentId}").tools.sandbox.tools.`,
|
||||
);
|
||||
}
|
||||
entryCopy.sandbox = legacySandbox;
|
||||
}
|
||||
|
||||
mergeMissing(target, entryCopy);
|
||||
}
|
||||
delete routing.agents;
|
||||
changes.push("Moved routing.agents → agents.list.");
|
||||
}
|
||||
|
||||
const defaultAgentId =
|
||||
typeof routing.defaultAgentId === "string"
|
||||
? routing.defaultAgentId.trim()
|
||||
: "";
|
||||
if (defaultAgentId) {
|
||||
const hasDefault = list.some(
|
||||
(entry): entry is Record<string, unknown> =>
|
||||
isRecord(entry) && entry.default === true,
|
||||
);
|
||||
if (!hasDefault) {
|
||||
const entry = ensureAgentEntry(list, defaultAgentId);
|
||||
entry.default = true;
|
||||
changes.push(
|
||||
`Moved routing.defaultAgentId → agents.list (id "${defaultAgentId}").default.`,
|
||||
);
|
||||
} else {
|
||||
changes.push(
|
||||
"Removed routing.defaultAgentId (agents.list default already set).",
|
||||
);
|
||||
}
|
||||
delete routing.defaultAgentId;
|
||||
}
|
||||
|
||||
if (list.length > 0) {
|
||||
agents.list = list;
|
||||
}
|
||||
|
||||
if (Object.keys(routing).length === 0) {
|
||||
delete raw.routing;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "routing.config-v2",
|
||||
describe:
|
||||
"Move routing bindings/groupChat/queue/agentToAgent/transcribeAudio",
|
||||
apply: (raw, changes) => {
|
||||
const routing = getRecord(raw.routing);
|
||||
if (!routing) return;
|
||||
|
||||
if (routing.bindings !== undefined) {
|
||||
if (raw.bindings === undefined) {
|
||||
raw.bindings = routing.bindings;
|
||||
changes.push("Moved routing.bindings → bindings.");
|
||||
} else {
|
||||
changes.push("Removed routing.bindings (bindings already set).");
|
||||
}
|
||||
delete routing.bindings;
|
||||
}
|
||||
|
||||
if (routing.agentToAgent !== undefined) {
|
||||
const tools = ensureRecord(raw, "tools");
|
||||
if (tools.agentToAgent === undefined) {
|
||||
tools.agentToAgent = routing.agentToAgent;
|
||||
changes.push("Moved routing.agentToAgent → tools.agentToAgent.");
|
||||
} else {
|
||||
changes.push(
|
||||
"Removed routing.agentToAgent (tools.agentToAgent already set).",
|
||||
);
|
||||
}
|
||||
delete routing.agentToAgent;
|
||||
}
|
||||
|
||||
if (routing.queue !== undefined) {
|
||||
const messages = ensureRecord(raw, "messages");
|
||||
if (messages.queue === undefined) {
|
||||
messages.queue = routing.queue;
|
||||
changes.push("Moved routing.queue → messages.queue.");
|
||||
} else {
|
||||
changes.push("Removed routing.queue (messages.queue already set).");
|
||||
}
|
||||
delete routing.queue;
|
||||
}
|
||||
|
||||
const groupChat = getRecord(routing.groupChat);
|
||||
if (groupChat) {
|
||||
const historyLimit = groupChat.historyLimit;
|
||||
if (historyLimit !== undefined) {
|
||||
const messages = ensureRecord(raw, "messages");
|
||||
const messagesGroup = ensureRecord(messages, "groupChat");
|
||||
if (messagesGroup.historyLimit === undefined) {
|
||||
messagesGroup.historyLimit = historyLimit;
|
||||
changes.push(
|
||||
"Moved routing.groupChat.historyLimit → messages.groupChat.historyLimit.",
|
||||
);
|
||||
} else {
|
||||
changes.push(
|
||||
"Removed routing.groupChat.historyLimit (messages.groupChat.historyLimit already set).",
|
||||
);
|
||||
}
|
||||
delete groupChat.historyLimit;
|
||||
}
|
||||
|
||||
const mentionPatterns = groupChat.mentionPatterns;
|
||||
if (mentionPatterns !== undefined) {
|
||||
const messages = ensureRecord(raw, "messages");
|
||||
const messagesGroup = ensureRecord(messages, "groupChat");
|
||||
if (messagesGroup.mentionPatterns === undefined) {
|
||||
messagesGroup.mentionPatterns = mentionPatterns;
|
||||
changes.push(
|
||||
"Moved routing.groupChat.mentionPatterns → messages.groupChat.mentionPatterns.",
|
||||
);
|
||||
} else {
|
||||
changes.push(
|
||||
"Removed routing.groupChat.mentionPatterns (messages.groupChat.mentionPatterns already set).",
|
||||
);
|
||||
}
|
||||
delete groupChat.mentionPatterns;
|
||||
}
|
||||
|
||||
if (Object.keys(groupChat).length === 0) {
|
||||
delete routing.groupChat;
|
||||
} else {
|
||||
routing.groupChat = groupChat;
|
||||
}
|
||||
}
|
||||
|
||||
if (routing.transcribeAudio !== undefined) {
|
||||
const mapped = mapLegacyAudioTranscription(routing.transcribeAudio);
|
||||
if (mapped) {
|
||||
const tools = ensureRecord(raw, "tools");
|
||||
const toolsAudio = ensureRecord(tools, "audio");
|
||||
if (toolsAudio.transcription === undefined) {
|
||||
toolsAudio.transcription = mapped;
|
||||
changes.push(
|
||||
"Moved routing.transcribeAudio → tools.audio.transcription.",
|
||||
);
|
||||
} else {
|
||||
changes.push(
|
||||
"Removed routing.transcribeAudio (tools.audio.transcription already set).",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
changes.push(
|
||||
"Removed routing.transcribeAudio (unsupported transcription CLI).",
|
||||
);
|
||||
}
|
||||
delete routing.transcribeAudio;
|
||||
}
|
||||
|
||||
const audio = getRecord(raw.audio);
|
||||
if (audio?.transcription !== undefined) {
|
||||
const mapped = mapLegacyAudioTranscription(audio.transcription);
|
||||
if (mapped) {
|
||||
const tools = ensureRecord(raw, "tools");
|
||||
const toolsAudio = ensureRecord(tools, "audio");
|
||||
if (toolsAudio.transcription === undefined) {
|
||||
toolsAudio.transcription = mapped;
|
||||
changes.push(
|
||||
"Moved audio.transcription → tools.audio.transcription.",
|
||||
);
|
||||
} else {
|
||||
changes.push(
|
||||
"Removed audio.transcription (tools.audio.transcription already set).",
|
||||
);
|
||||
}
|
||||
delete audio.transcription;
|
||||
if (Object.keys(audio).length === 0) delete raw.audio;
|
||||
else raw.audio = audio;
|
||||
} else {
|
||||
delete audio.transcription;
|
||||
changes.push(
|
||||
"Removed audio.transcription (unsupported transcription CLI).",
|
||||
);
|
||||
if (Object.keys(audio).length === 0) delete raw.audio;
|
||||
else raw.audio = audio;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(routing).length === 0) {
|
||||
delete raw.routing;
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user