优化模型配置和节回显

This commit is contained in:
2026-06-05 17:33:18 +08:00
parent 56e1517743
commit ee8ba0a5d9
3 changed files with 99 additions and 125 deletions

View File

@@ -141,6 +141,15 @@ These rules capture long-term repository preferences confirmed by the user and s
- When code changes appear not to take effect, check in this order: compile/HMR status, render conditions, API data, then style overrides.
- Turn repeated low-level mistakes into explicit workflow checks and follow those checks in future edits.
### Environment and Tool Selection Rules
- Before running environment-dependent commands, first follow the known runtime context already provided by the IDE, such as OS and default shell.
- On Windows projects with PowerShell as the active shell, prefer PowerShell-native commands and scripts by default.
- Do not assume Python, bash, sed, awk, or other non-default tooling is installed locally unless the user or repository explicitly indicates that they are available.
- For reading and editing repository files, prefer dedicated file tools first; use shell-based text replacement only when file tools are not suitable for the change.
- When a replacement fails once, do not keep retrying the same method blindly. Re-read the exact target content and switch to a smaller or more reliable edit strategy that matches the current environment.
- Avoid trial-and-error probing for basic tooling when the available environment is already known from context.
### Componentization and Structure Rules
- If a view block has an independent responsibility, prefer extracting it into a component instead of keeping it inside a large page file.

View File

@@ -118,15 +118,22 @@ interface ModelItem {
apiKey?: string;
isPrivate?: number;
isChatModel?: number;
headMsg?: string;
headMsg?: string | Record<string, string>;
operatorName?: string;
responseBody?: Record<string, unknown>;
responseTokenField?: string;
tokenConfig?: Record<string, unknown> | string;
extendMapping?: Record<string, unknown> | string;
queryConfig?: Record<string, unknown>;
streamConfig?: Record<string, unknown>;
form?: ModelFormEntry[] | Record<string, unknown>;
requestMapping?: Record<string, unknown>;
requiredFields?: string[];
firstFrame?: string;
lastFrame?: string;
responseMapping?: Record<string, unknown>;
callMode?: number;
isAsync?: number;
maxConcurrency?: number;
queueLimit?: number;
timeoutSeconds?: number;
@@ -271,74 +278,71 @@ const fieldsToUnknownObject = (fields: Array<{ key: string; value: string }>) =>
return obj;
};
const flattenNestedObject = (obj: Record<string, unknown>, prefix = ''): Array<{ key: string; value: string }> => {
const rows: Array<{ key: string; value: string }> = [];
Object.entries(obj || {}).forEach(([k, v]) => {
const fk = prefix ? `${prefix}.${k}` : k;
if (v && typeof v === 'object' && !Array.isArray(v)) {
rows.push(...flattenNestedObject(v as Record<string, unknown>, fk));
return;
const parseFormEntries = (raw: unknown): ModelFormEntry[] => {
if (Array.isArray(raw)) {
return (raw as ModelFormEntry[])
.filter((item) => item && item.key !== undefined)
.map((item) => ({
key: String(item.key ?? '').trim(),
value: String(item.value ?? ''),
}))
.filter((item) => item.key !== '');
}
rows.push({ key: fk, value: String(v ?? '') });
if (raw && typeof raw === 'object') {
return Object.entries(raw as Record<string, unknown>).map(([key, value]) => {
let nextValue = value;
if (nextValue && typeof nextValue === 'object' && !Array.isArray(nextValue) && 'value' in nextValue) {
nextValue = (nextValue as { value: unknown }).value;
}
return {
key: String(key).trim(),
value: String(nextValue ?? ''),
};
});
return rows;
}
return [];
};
const nestFieldsToObject = (fields: Array<{ key: string; value: string }>) => {
const root: Record<string, unknown> = {};
fields.forEach((f) => {
const path = String(f.key || '').trim();
if (!path) return;
const parts = path
.split('.')
.map((p) => p.trim())
.filter(Boolean);
if (!parts.length) return;
let cur: Record<string, unknown> = root;
parts.forEach((part, idx) => {
if (idx === parts.length - 1) {
cur[part] = String(f.value ?? '');
return;
const normalizeQueryConfig = (raw: unknown): Record<string, unknown> => {
if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
return raw as Record<string, unknown>;
}
if (!cur[part] || typeof cur[part] !== 'object' || Array.isArray(cur[part])) {
cur[part] = {};
}
cur = cur[part] as Record<string, unknown>;
});
});
return root;
return {};
};
const buildQueryConfigFromRaw = (rawQc: Record<string, unknown> | null): Record<string, unknown> => {
if (!rawQc) return { responseType: 'sync', callbackUrl: '' };
const rt = String(rawQc.responseType || 'sync');
if (rt === 'callback') return { responseType: 'callback', callbackUrl: String(rawQc.callbackUrl || '') };
if (rt === 'pull') {
const hFields = Object.entries((rawQc.headers as Record<string, unknown>) || {}).map(([k, v]) => ({ key: k, value: String(v ?? '') }));
const bFields = flattenNestedObject((rawQc.body as Record<string, unknown>) || {});
return {
responseType: 'pull',
callbackUrl: '',
method: String(rawQc.method || 'GET'),
url: String(rawQc.url || ''),
headers: fieldsToUnknownObject(hFields),
body: nestFieldsToObject(bFields),
response: ((rawQc.response as unknown[]) || [])
.map((item) => {
if (typeof item === 'string') {
return { value: item, isTokenField: false, isMainBody: false };
const parseHeadMsgRecord = (raw: ModelItem['headMsg']) => {
const headMsgRecord: Record<string, string> = {};
if (typeof raw === 'string') {
try {
const parsed = JSON.parse(raw);
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
Object.entries(parsed).forEach(([k, v]) => {
headMsgRecord[k] = String(v);
});
return headMsgRecord;
}
const row = item as Record<string, unknown>;
return {
value: String(row.value ?? ''),
isTokenField: Boolean(row.isTokenField),
isMainBody: Boolean(row.isMainBody),
};
})
.filter((row) => row.value !== ''),
};
} catch {
const pairs = raw.split(',');
pairs.forEach((pair) => {
const idx = pair.indexOf(':');
if (idx === -1) return;
const key = pair.slice(0, idx).trim();
const value = pair.slice(idx + 1).trim();
if (key) {
headMsgRecord[key] = value;
}
return { responseType: 'sync', callbackUrl: '' };
});
return headMsgRecord;
}
}
if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
Object.entries(raw).forEach(([k, v]) => {
headMsgRecord[k] = String(v);
});
}
return headMsgRecord;
};
const fetchModelList = async () => {
@@ -396,43 +400,7 @@ const handleCreatePrivateModel = async () => {
creatingModel.value = true;
const builtInModel = builtInModelToClone.value;
const formList: ModelFormEntry[] = Array.isArray(builtInModel.form)
? (builtInModel.form as ModelFormEntry[])
: Object.entries((builtInModel.form as Record<string, unknown>) || {}).map(([key, value]) => ({
key: String(key),
value: String(value ?? ''),
}));
// Parse headMsg to Record<string, string> - it might be stored as string or already as object
let headMsgRecord: Record<string, string> = {};
if (builtInModel.headMsg && typeof builtInModel.headMsg === 'string') {
// Try to parse as JSON first (new format stored as string)
try {
const parsed = JSON.parse(builtInModel.headMsg);
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
Object.entries(parsed).forEach(([k, v]) => {
headMsgRecord[k] = String(v);
});
}
} catch {
// If JSON parse fails, parse as old format "key1:value1,key2:value2"
const pairs = builtInModel.headMsg.split(',');
pairs.forEach((pair) => {
const idx = pair.indexOf(':');
if (idx === -1) return;
const key = pair.slice(0, idx).trim();
const value = pair.slice(idx + 1).trim();
if (key) {
headMsgRecord[key] = value;
}
});
}
} else if (builtInModel.headMsg && typeof builtInModel.headMsg === 'object' && !Array.isArray(builtInModel.headMsg)) {
// Already an object
Object.entries(builtInModel.headMsg).forEach(([k, v]) => {
headMsgRecord[k] = String(v);
});
}
const formList = parseFormEntries(builtInModel.form);
const createParams: CreateModelParams = {
modelName: apiKeyForm.modelName,
@@ -440,7 +408,7 @@ const handleCreatePrivateModel = async () => {
operatorName: builtInModel.operatorName || '',
baseUrl: builtInModel.baseUrl,
httpMethod: builtInModel.httpMethod || 'POST',
headMsg: headMsgRecord,
headMsg: parseHeadMsgRecord(builtInModel.headMsg),
isPrivate: builtInModel.isPrivate ?? 1,
enabled: builtInModel.enabled ?? 1,
isChatModel: builtInModel.isChatModel || 0,
@@ -466,14 +434,13 @@ const handleCreatePrivateModel = async () => {
tokenConfig: fieldsToUnknownObject(
Object.entries(parseJsonObjectField(builtInModel.tokenConfig)).map(([k, v]) => ({ key: k, value: String(v ?? '') }))
),
queryConfig: buildQueryConfigFromRaw(
builtInModel.queryConfig && typeof builtInModel.queryConfig === 'object' && !Array.isArray(builtInModel.queryConfig)
? (builtInModel.queryConfig as Record<string, unknown>)
: null
),
queryConfig: normalizeQueryConfig(builtInModel.queryConfig),
responseTokenField: String(builtInModel.responseTokenField || ''),
streamConfig:
builtInModel.streamConfig && typeof builtInModel.streamConfig === 'object' && !Array.isArray(builtInModel.streamConfig)
Number(builtInModel.callMode ?? builtInModel.isAsync ?? 0) === 2
? builtInModel.streamConfig && typeof builtInModel.streamConfig === 'object' && !Array.isArray(builtInModel.streamConfig)
? (builtInModel.streamConfig as Record<string, unknown>)
: {}
: undefined,
};

View File

@@ -1116,6 +1116,11 @@ const availableParentParams = computed(() => {
const allParentIds = findAllParentNodes(selectedElement.value.id);
const params: Array<{ label: string; value: string }> = [];
const pushParentParam = (label: string, value: string) => {
if (!label || !value) return;
if (params.some((item) => item.value === value)) return;
params.push({ label, value });
};
// 遍历所有上级节点
allParentIds.forEach((parentId) => {
@@ -1133,15 +1138,14 @@ const availableParentParams = computed(() => {
// 如果是判断节点,跳过不添加其字段
if (isJudge) return;
pushParentParam(`${parentNodeName}.输出结果`, `\${${parentId}.nodeOutputResult}`);
const modelOutputFields = Array.isArray(parentProps.modelOutputFields) ? parentProps.modelOutputFields : [];
if (modelOutputFields.length > 0) {
modelOutputFields.forEach((field: any) => {
const fieldName = String(field || '').trim();
if (!fieldName) return;
params.push({
label: `${parentNodeName}.${fieldName}`,
value: `\${${parentId}.${fieldName}}`,
});
pushParentParam(`${parentNodeName}.${fieldName}`, `\${${parentId}.${fieldName}}`);
});
}
@@ -1163,10 +1167,7 @@ const availableParentParams = computed(() => {
if (responseValue && typeof responseValue === 'object' && !Array.isArray(responseValue)) {
Object.keys(responseValue).forEach((key) => {
if (!key || key.startsWith('_temp_')) return;
params.push({
label: `${parentNodeName}.${key}`,
value: `\${${parentId}.${key}}`,
});
pushParentParam(`${parentNodeName}.${key}`, `\${${parentId}.${key}}`);
});
}
return;
@@ -1174,10 +1175,7 @@ const availableParentParams = computed(() => {
parentProps.formConfig.forEach((field: any) => {
if (field.label) {
params.push({
label: `${parentNodeName}.${field.label}`,
value: `\${${parentId}.${field.label}}`,
});
pushParentParam(`${parentNodeName}.${field.label}`, `\${${parentId}.${field.label}}`);
}
});
}