fix: schema parse,default config

This commit is contained in:
archer
2026-05-14 18:14:05 +08:00
parent b8f81e9839
commit fafaa28fc8
7 changed files with 96 additions and 59 deletions
+1
View File
@@ -0,0 +1 @@
export const stripUrlTrailingSlash = (value?: string) => value?.replace(/\/+$/, '') || '';
+22
View File
@@ -0,0 +1,22 @@
import z from 'zod';
import { stripUrlTrailingSlash } from '../string/url';
const truthyBoolStrs = ['true', '1', 'yes', 'y', 'on'];
export const BoolSchema = z.preprocess((val) => {
if (typeof val === 'boolean') return val;
if (typeof val === 'string') {
return truthyBoolStrs.includes(val.trim().toLowerCase());
}
if (typeof val === 'number') {
if (val === 1) return true;
if (val === 0) return false;
}
return val;
}, z.boolean());
export const NumSchema = z.coerce.number<number>();
export const IntSchema = NumSchema.int().nonnegative();
export const UrlSchema = z.string().url().transform(stripUrlTrailingSlash);
+42 -31
View File
@@ -3,6 +3,7 @@ import { WorkflowIOValueTypeEnum, NodeInputKeyEnum, NodeOutputKeyEnum } from '..
import { FlowNodeInputTypeEnum, FlowNodeOutputTypeEnum } from '../node/constant';
import { SecretValueTypeSchema } from '../../../common/secret/type';
import z from 'zod';
import { BoolSchema, IntSchema, NumSchema } from '../../../common/zod';
/* Dataset node */
export const SelectedDatasetSchema = z.object({
@@ -19,8 +20,9 @@ export type SelectedDatasetType = z.infer<typeof SelectedDatasetSchema>;
export const CustomFieldConfigTypeSchema = z.object({
// reference
selectValueTypeList: z.array(z.enum(WorkflowIOValueTypeEnum)).optional(), // 可以选哪个数据类型, 只有1个的话,则默认选择
showDefaultValue: z.boolean().optional(),
showDescription: z.boolean().optional()
showDefaultValue: BoolSchema.optional(),
showDescription: BoolSchema.optional(),
hideBottomDivider: BoolSchema.optional()
});
export type CustomFieldConfigType = z.infer<typeof CustomFieldConfigTypeSchema>;
@@ -29,31 +31,40 @@ export const InputComponentPropsTypeSchema = z.object({
label: z.string(),
valueType: z.enum(WorkflowIOValueTypeEnum).optional(),
required: z.boolean().optional(),
required: BoolSchema.optional(),
defaultValue: z.any().optional(),
// 不同组件的配置嘻嘻
referencePlaceholder: z.string().optional(),
isRichText: z.boolean().optional(), // Prompt editor
isRichText: BoolSchema.optional(), // Prompt editor
placeholder: z.string().optional(), // input,textarea
maxLength: z.number().optional(), // input,textarea
minLength: z.number().optional(), // password
list: z.array(z.object({ label: z.string(), value: z.string() })).optional(), // select
markList: z.array(z.object({ label: z.string(), value: z.number() })).optional(), // slider
step: z.number().optional(), // slider
max: z.number().optional(), // slider, number input
min: z.number().optional(), // slider, number input
precision: z.number().optional(), // number input
maxLength: IntSchema.optional(), // input,textarea
minLength: IntSchema.optional(), // password
list: z
.array(
z.object({
label: z.string(),
value: z.string(),
icon: z.string().optional(),
description: z.string().optional()
})
)
.optional(), // select
markList: z.array(z.object({ label: z.string(), value: NumSchema })).optional(), // slider
step: NumSchema.optional(), // slider
max: NumSchema.optional(), // slider, number input
min: NumSchema.optional(), // slider, number input
precision: NumSchema.optional(), // number input
canSelectFile: z.boolean().optional(), // file select
canSelectImg: z.boolean().optional(), // file select
canSelectVideo: z.boolean().optional(), // file select
canSelectAudio: z.boolean().optional(), // file select
canSelectCustomFileExtension: z.boolean().optional(), // file select
canSelectFile: BoolSchema.optional(), // file select
canSelectImg: BoolSchema.optional(), // file select
canSelectVideo: BoolSchema.optional(), // file select
canSelectAudio: BoolSchema.optional(), // file select
canSelectCustomFileExtension: BoolSchema.optional(), // file select
customFileExtensionList: z.array(z.string()).optional(), // file select
canLocalUpload: z.boolean().optional(), // file select
canUrlUpload: z.boolean().optional(), // file select
maxFiles: z.number().optional(), // file select
canLocalUpload: BoolSchema.optional(), // file select
canUrlUpload: BoolSchema.optional(), // file select
maxFiles: IntSchema.optional(), // file select
// Time
timeGranularity: z.enum(['day', 'hour', 'minute', 'second']).optional(), // time point select, time range select
@@ -76,7 +87,7 @@ export const InputConfigTypeSchema = z.object({
key: z.string(),
label: z.string(),
description: z.string().optional(),
required: z.boolean().optional(),
required: BoolSchema.optional(),
inputType: z.enum(['input', 'numberInput', 'secret', 'switch', 'select']),
value: SecretValueTypeSchema.optional(),
@@ -87,7 +98,7 @@ export type InputConfigType = z.infer<typeof InputConfigTypeSchema>;
// Workflow node input
export const FlowNodeInputItemTypeSchema = InputComponentPropsTypeSchema.extend({
selectedTypeIndex: z.number().optional(),
selectedTypeIndex: IntSchema.optional(),
renderTypeList: z.array(z.enum(FlowNodeInputTypeEnum)), // Node Type. Decide on a render style
valueDesc: z.string().optional(), // data desc
value: z.any().optional(),
@@ -101,11 +112,11 @@ export const FlowNodeInputItemTypeSchema = InputComponentPropsTypeSchema.extend(
inputList: z.array(InputConfigTypeSchema).optional(), // when key === 'system_input_config', this field is used
// render components params
canEdit: z.boolean().optional(), // dynamic inputs
isPro: z.boolean().optional(), // Pro version field
isToolOutput: z.boolean().optional(),
canEdit: BoolSchema.optional(), // dynamic inputs
isPro: BoolSchema.optional(), // Pro version field
isToolOutput: BoolSchema.optional(),
deprecated: z.boolean().optional() // node deprecated
deprecated: BoolSchema.optional() // node deprecated
});
export type FlowNodeInputItemType = z.infer<typeof FlowNodeInputItemTypeSchema>;
@@ -121,18 +132,18 @@ export const FlowNodeOutputItemTypeSchema = z.object({
label: z.string().optional(),
description: z.string().optional(),
defaultValue: z.any().optional(),
required: z.boolean().optional(),
required: BoolSchema.optional(),
invalid: z.boolean().optional(),
invalid: BoolSchema.optional(),
invalidCondition: z
.function({
input: z.tuple([
z.object({
inputs: z.array(FlowNodeInputItemTypeSchema),
inputs: z.custom<FlowNodeInputItemType[]>(),
llmModelMap: z.record(z.string(), LLMModelItemSchema)
})
]),
output: z.boolean()
output: BoolSchema
})
.optional()
.meta({
@@ -143,7 +154,7 @@ export const FlowNodeOutputItemTypeSchema = z.object({
}),
customFieldConfig: CustomFieldConfigTypeSchema.optional(),
deprecated: z.boolean().optional()
deprecated: BoolSchema.optional()
});
export type FlowNodeOutputItemType = z.infer<typeof FlowNodeOutputItemTypeSchema>;
+16 -10
View File
@@ -35,14 +35,14 @@ export const loadSystemModels = async (init = false, language = 'en') => {
return Promise.reject(error);
}
let _systemModelList: SystemModelItemType[] = [];
let _systemActiveModelList: SystemModelItemType[] = [];
let _llmModelMap = new Map<string, LLMModelItemType>();
let _embeddingModelMap = new Map<string, EmbeddingModelItemType>();
let _ttsModelMap = new Map<string, TTSModelType>();
let _sttModelMap = new Map<string, STTModelType>();
let _reRankModelMap = new Map<string, RerankModelItemType>();
let _systemDefaultModel: SystemDefaultModelType = {};
const _systemModelList: SystemModelItemType[] = [];
const _systemActiveModelList: SystemModelItemType[] = [];
const _llmModelMap = new Map<string, LLMModelItemType>();
const _embeddingModelMap = new Map<string, EmbeddingModelItemType>();
const _ttsModelMap = new Map<string, TTSModelType>();
const _sttModelMap = new Map<string, STTModelType>();
const _reRankModelMap = new Map<string, RerankModelItemType>();
const _systemDefaultModel: SystemDefaultModelType = {};
if (!global.systemModelList) {
global.systemModelList = [];
@@ -144,8 +144,14 @@ export const loadSystemModels = async (init = false, language = 'en') => {
...(model.type === ModelTypeEnum.llm && dbModel?.metadata?.type === ModelTypeEnum.llm
? {
maxResponse: dbModel?.metadata?.maxResponse ?? model.maxTokens ?? 8000,
defaultConfig: mergeObject(model.defaultConfig, dbModel?.metadata?.defaultConfig),
fieldMap: mergeObject(model.fieldMap, dbModel?.metadata?.fieldMap),
defaultConfig:
typeof dbModel?.metadata?.defaultConfig === 'object'
? dbModel?.metadata?.defaultConfig
: model.defaultConfig,
fieldMap:
typeof dbModel?.metadata?.fieldMap === 'object'
? dbModel?.metadata?.fieldMap
: model.fieldMap,
/** @deprecated */
maxTokens: undefined
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@fastgpt/app",
"version": "4.14.19",
"version": "4.14.20",
"private": false,
"browserslist": [
"Chrome >= 80",
@@ -6,22 +6,16 @@ import { findModelFromAlldata } from '@fastgpt/service/core/ai/model';
import { updatedReloadSystemModel } from '@fastgpt/service/core/ai/config/utils';
import { ModelTypeEnum } from '@fastgpt/global/core/ai/constants';
export type updateQuery = {};
export type updateBody = {
model: string;
metadata?: Record<string, any>;
};
export type updateResponse = {};
async function handler(
req: ApiRequestProps<updateBody, updateQuery>,
res: ApiResponseType<any>
): Promise<updateResponse> {
async function handler(req: ApiRequestProps<updateBody>, res: ApiResponseType<any>) {
await authSystemAdmin({ req });
let { model, metadata } = req.body;
const metadata = req.body.metadata;
let { model } = req.body;
if (!model) return Promise.reject(new Error('model is required'));
model = model.trim();
@@ -60,6 +54,11 @@ async function handler(
}
});
// 强制更新 defaultConfig 数据类型
if ('defaultConfig' in metadataConcat && typeof metadataConcat.defaultConfig !== 'object') {
metadataConcat.defaultConfig = {};
}
await MongoSystemModel.updateOne(
{ model },
{
@@ -6,18 +6,11 @@ import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { MongoSystemModel } from '@fastgpt/service/core/ai/config/schema';
import { updatedReloadSystemModel } from '@fastgpt/service/core/ai/config/utils';
export type updateWithJsonQuery = {};
export type updateWithJsonBody = {
config: string;
};
export type updateWithJsonResponse = {};
async function handler(
req: ApiRequestProps<updateWithJsonBody, updateWithJsonQuery>,
res: ApiResponseType<any>
): Promise<updateWithJsonResponse> {
async function handler(req: ApiRequestProps<updateWithJsonBody>, res: ApiResponseType<any>) {
await authSystemAdmin({ req });
const { config } = req.body;
@@ -41,6 +34,11 @@ async function handler(
if (!item.metadata.name) {
item.metadata.name = item.model;
}
if ('defaultConfig' in item.metadata && typeof item.metadata.defaultConfig !== 'object') {
{
item.metadata.defaultConfig = {};
}
}
}
await mongoSessionRun(async (session) => {