fix(omo): preserve custom fields and align otherFields import/validation

This commit is contained in:
YoVinchen
2026-02-12 01:14:30 +08:00
parent a301a81f7a
commit 651496eabb
6 changed files with 312 additions and 35 deletions
@@ -77,7 +77,11 @@ interface OmoFormFieldsProps {
onOtherFieldsStrChange: (value: string) => void;
}
type CustomModelItem = { key: string; model: string };
export type CustomModelItem = {
key: string;
model: string;
sourceKey?: string;
};
type BuiltinModelDef = Pick<
OmoAgentDef | OmoCategoryDef,
"key" | "display" | "descKey" | "recommended" | "tooltipKey"
@@ -236,45 +240,58 @@ function collectCustomModels(
customs.push({
key: k,
model: ((v as Record<string, unknown>).model as string) || "",
sourceKey: k,
});
}
}
return customs;
}
function mergeCustomModelsIntoStore(
export function mergeCustomModelsIntoStore(
store: Record<string, Record<string, unknown>>,
builtinKeys: Set<string>,
customs: CustomModelItem[],
modelVariantsMap: Record<string, string[]>,
): Record<string, Record<string, unknown>> {
const updated = { ...store };
for (const key of Object.keys(updated)) {
if (!builtinKeys.has(key)) delete updated[key];
const updated: Record<string, Record<string, unknown>> = {};
for (const [key, value] of Object.entries(store)) {
if (builtinKeys.has(key)) {
updated[key] = { ...value };
}
}
for (const custom of customs) {
if (custom.key.trim()) {
const nextEntry = { ...(updated[custom.key] || {}) };
if (custom.model.trim()) {
nextEntry.model = custom.model;
const currentVariant =
typeof nextEntry.variant === "string" ? nextEntry.variant : "";
if (currentVariant) {
const validVariants = modelVariantsMap[custom.model] || [];
if (!validVariants.includes(currentVariant)) {
delete nextEntry.variant;
}
}
updated[custom.key] = nextEntry;
} else {
delete nextEntry.model;
delete nextEntry.variant;
if (Object.keys(nextEntry).length > 0) {
updated[custom.key] = nextEntry;
} else {
delete updated[custom.key];
const targetKey = custom.key.trim();
if (!targetKey) continue;
const sourceKey = (custom.sourceKey || targetKey).trim();
const sourceEntry = store[sourceKey] ?? store[targetKey];
const nextEntry = {
...(updated[targetKey] || {}),
...(sourceEntry || {}),
};
if (custom.model.trim()) {
nextEntry.model = custom.model;
const currentVariant =
typeof nextEntry.variant === "string" ? nextEntry.variant : "";
if (currentVariant) {
const validVariants = modelVariantsMap[custom.model] || [];
if (!validVariants.includes(currentVariant)) {
delete nextEntry.variant;
}
}
updated[targetKey] = nextEntry;
continue;
}
delete nextEntry.model;
delete nextEntry.variant;
if (Object.keys(nextEntry).length > 0) {
updated[targetKey] = nextEntry;
} else {
delete updated[targetKey];
}
}
return updated;
@@ -1032,11 +1049,17 @@ export function OmoFormFields({
const addCustomModel = (scope: AdvancedScope) => {
if (scope === "agent") {
setCustomAgents((prev) => [...prev, { key: "", model: "" }]);
setCustomAgents((prev) => [
...prev,
{ key: "", model: "", sourceKey: "" },
]);
setSubAgentsOpen(true);
return;
}
setCustomCategories((prev) => [...prev, { key: "", model: "" }]);
setCustomCategories((prev) => [
...prev,
{ key: "", model: "", sourceKey: "" },
]);
setCategoriesOpen(true);
};
@@ -54,7 +54,7 @@ import { type OmoGlobalConfigFieldsRef } from "./OmoGlobalConfigFields";
import { OmoCommonConfigEditor } from "./OmoCommonConfigEditor";
import * as configApi from "@/lib/api/config";
import type { OmoGlobalConfig } from "@/types/omo";
import { mergeOmoConfigPreview } from "@/types/omo";
import { mergeOmoConfigPreview, parseOmoOtherFieldsObject } from "@/types/omo";
import {
ProviderAdvancedConfig,
type PricingModelSourceOption,
@@ -219,8 +219,10 @@ function buildOmoProfilePreview(
}
if (otherFieldsStr.trim()) {
try {
const other = JSON.parse(otherFieldsStr);
Object.assign(profileOnly, other);
const other = parseOmoOtherFieldsObject(otherFieldsStr);
if (other) {
Object.assign(profileOnly, other);
}
} catch {}
}
return profileOnly;
@@ -1203,7 +1205,19 @@ export function ProviderForm({
}
if (omoOtherFieldsStr.trim()) {
try {
omoConfig.otherFields = JSON.parse(omoOtherFieldsStr);
const otherFields = parseOmoOtherFieldsObject(omoOtherFieldsStr);
if (!otherFields) {
toast.error(
t("omo.jsonMustBeObject", {
field: t("omo.otherFields", {
defaultValue: "Other Config",
}),
defaultValue: "{{field}} must be a JSON object",
}),
);
return;
}
omoConfig.otherFields = otherFields;
} catch {
toast.error(
t("omo.invalidJson", {