Enhance ToolLibrary by adding sourceKey to ToolParameterDraft and updating related functions for improved schema management. Introduce normalization functions for object schemas and defaults, and refactor buildToolParameterConfig to utilize these enhancements. Update state management in ToolLibraryPage to accommodate new schema handling and defaults integration.
This commit is contained in:
@@ -24,6 +24,7 @@ type ToolParameterType = 'string' | 'number' | 'integer' | 'boolean' | 'object'
|
||||
type ToolParameterDraft = {
|
||||
id: string;
|
||||
key: string;
|
||||
sourceKey: string;
|
||||
type: ToolParameterType;
|
||||
required: boolean;
|
||||
description: string;
|
||||
@@ -36,6 +37,7 @@ const TOOL_PARAMETER_TYPES: ToolParameterType[] = ['string', 'number', 'integer'
|
||||
const newParameterDraft = (): ToolParameterDraft => ({
|
||||
id: `param_${Date.now()}_${Math.random().toString(16).slice(2, 8)}`,
|
||||
key: '',
|
||||
sourceKey: '',
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: '',
|
||||
@@ -106,6 +108,7 @@ const draftsFromSchema = (schema: Record<string, any> | undefined, defaults: Rec
|
||||
return {
|
||||
id: `param_${key}_${Math.random().toString(16).slice(2, 8)}`,
|
||||
key,
|
||||
sourceKey: key,
|
||||
type: schemaTypeOrDefault(typedSpec.type),
|
||||
required: requiredSet.has(key),
|
||||
description: String(typedSpec.description || ''),
|
||||
@@ -119,6 +122,7 @@ const draftsFromSchema = (schema: Record<string, any> | undefined, defaults: Rec
|
||||
drafts.push({
|
||||
id: `param_${key}_${Math.random().toString(16).slice(2, 8)}`,
|
||||
key,
|
||||
sourceKey: key,
|
||||
type: typeof value === 'number' && Number.isInteger(value) ? 'integer' : (typeof value as ToolParameterType) || 'string',
|
||||
required: false,
|
||||
description: '',
|
||||
@@ -129,7 +133,31 @@ const draftsFromSchema = (schema: Record<string, any> | undefined, defaults: Rec
|
||||
return drafts;
|
||||
};
|
||||
|
||||
const buildToolParameterConfig = (drafts: ToolParameterDraft[]): { schema: Record<string, any>; defaults: Record<string, any>; error?: string } => {
|
||||
const normalizeObjectSchema = (value: any): Record<string, any> => {
|
||||
const schema = value && typeof value === 'object' && !Array.isArray(value) ? { ...value } : {};
|
||||
const properties = schema.properties && typeof schema.properties === 'object' && !Array.isArray(schema.properties)
|
||||
? schema.properties
|
||||
: {};
|
||||
const required = Array.isArray(schema.required) ? schema.required.map((item: any) => String(item)) : [];
|
||||
return {
|
||||
...schema,
|
||||
type: 'object',
|
||||
properties,
|
||||
required,
|
||||
};
|
||||
};
|
||||
|
||||
const normalizeDefaultsObject = (value: any): Record<string, any> =>
|
||||
value && typeof value === 'object' && !Array.isArray(value) ? { ...value } : {};
|
||||
|
||||
const buildToolParameterConfig = (
|
||||
drafts: ToolParameterDraft[],
|
||||
baseSchemaInput?: Record<string, any>
|
||||
): { schema: Record<string, any>; defaults: Record<string, any>; error?: string } => {
|
||||
const baseSchema = normalizeObjectSchema(baseSchemaInput);
|
||||
const baseProperties = baseSchema.properties && typeof baseSchema.properties === 'object' && !Array.isArray(baseSchema.properties)
|
||||
? (baseSchema.properties as Record<string, any>)
|
||||
: {};
|
||||
const properties: Record<string, any> = {};
|
||||
const required: string[] = [];
|
||||
const defaults: Record<string, any> = {};
|
||||
@@ -138,18 +166,26 @@ const buildToolParameterConfig = (drafts: ToolParameterDraft[]): { schema: Recor
|
||||
for (const draft of drafts) {
|
||||
const key = draft.key.trim();
|
||||
if (!key) continue;
|
||||
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) {
|
||||
return { schema: {}, defaults: {}, error: `参数标识不合法: ${key}` };
|
||||
}
|
||||
if (seen.has(key)) {
|
||||
return { schema: {}, defaults: {}, error: `参数标识重复: ${key}` };
|
||||
}
|
||||
seen.add(key);
|
||||
|
||||
properties[key] = {
|
||||
type: draft.type,
|
||||
...(draft.description.trim() ? { description: draft.description.trim() } : {}),
|
||||
};
|
||||
const sourceKey = String(draft.sourceKey || '').trim();
|
||||
const preservedRaw = (
|
||||
(sourceKey && Object.prototype.hasOwnProperty.call(baseProperties, sourceKey) ? baseProperties[sourceKey] : undefined)
|
||||
?? baseProperties[key]
|
||||
);
|
||||
const preservedSpec = preservedRaw && typeof preservedRaw === 'object' && !Array.isArray(preservedRaw)
|
||||
? { ...(preservedRaw as Record<string, any>) }
|
||||
: {};
|
||||
properties[key] = preservedSpec;
|
||||
properties[key].type = draft.type;
|
||||
if (draft.description.trim()) {
|
||||
properties[key].description = draft.description.trim();
|
||||
} else {
|
||||
delete properties[key].description;
|
||||
}
|
||||
if (draft.required) required.push(key);
|
||||
if (draft.hasDefault) {
|
||||
const parsed = parseDefaultValueByType(draft.type, draft.defaultValueText);
|
||||
@@ -157,11 +193,15 @@ const buildToolParameterConfig = (drafts: ToolParameterDraft[]): { schema: Recor
|
||||
return { schema: {}, defaults: {}, error: parsed.error };
|
||||
}
|
||||
defaults[key] = parsed.value;
|
||||
properties[key].default = parsed.value;
|
||||
} else {
|
||||
delete properties[key].default;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
schema: {
|
||||
...baseSchema,
|
||||
type: 'object',
|
||||
properties,
|
||||
required,
|
||||
@@ -202,6 +242,8 @@ export const ToolLibraryPage: React.FC = () => {
|
||||
const [toolHttpUrl, setToolHttpUrl] = useState('');
|
||||
const [toolHttpHeadersText, setToolHttpHeadersText] = useState('{}');
|
||||
const [toolHttpTimeoutMs, setToolHttpTimeoutMs] = useState(10000);
|
||||
const [toolSchema, setToolSchema] = useState<Record<string, any>>({ type: 'object', properties: {}, required: [] });
|
||||
const [toolParameterDefaults, setToolParameterDefaults] = useState<Record<string, any>>({});
|
||||
const [toolParameters, setToolParameters] = useState<ToolParameterDraft[]>([]);
|
||||
const [schemaDrawerOpen, setSchemaDrawerOpen] = useState(false);
|
||||
const [schemaEditorText, setSchemaEditorText] = useState('');
|
||||
@@ -237,9 +279,12 @@ export const ToolLibraryPage: React.FC = () => {
|
||||
setToolHttpUrl('');
|
||||
setToolHttpHeadersText('{}');
|
||||
setToolHttpTimeoutMs(10000);
|
||||
const emptySchema = { type: 'object', properties: {}, required: [] };
|
||||
setToolSchema(emptySchema);
|
||||
setToolParameterDefaults({});
|
||||
setToolParameters([]);
|
||||
setSchemaEditorError('');
|
||||
setSchemaEditorText(JSON.stringify({ type: 'object', properties: {}, required: [] }, null, 2));
|
||||
setSchemaEditorText(JSON.stringify(emptySchema, null, 2));
|
||||
setSchemaDrawerOpen(false);
|
||||
setIsToolModalOpen(true);
|
||||
};
|
||||
@@ -257,13 +302,18 @@ export const ToolLibraryPage: React.FC = () => {
|
||||
setToolHttpUrl(tool.httpUrl || '');
|
||||
setToolHttpHeadersText(JSON.stringify(tool.httpHeaders || {}, null, 2));
|
||||
setToolHttpTimeoutMs(tool.httpTimeoutMs || 10000);
|
||||
setToolParameters(draftsFromSchema(tool.parameterSchema, tool.parameterDefaults));
|
||||
const normalizedSchema = normalizeObjectSchema(tool.parameterSchema);
|
||||
const mergedDefaults = {
|
||||
...defaultsFromSchema(normalizedSchema),
|
||||
...normalizeDefaultsObject(tool.parameterDefaults),
|
||||
};
|
||||
setToolSchema(normalizedSchema);
|
||||
setToolParameterDefaults(mergedDefaults);
|
||||
setToolParameters(draftsFromSchema(normalizedSchema, mergedDefaults));
|
||||
setSchemaEditorError('');
|
||||
setSchemaEditorText(
|
||||
JSON.stringify(
|
||||
(tool.parameterSchema && typeof tool.parameterSchema === 'object')
|
||||
? tool.parameterSchema
|
||||
: { type: 'object', properties: {}, required: [] },
|
||||
normalizedSchema,
|
||||
null,
|
||||
2
|
||||
)
|
||||
@@ -273,11 +323,14 @@ export const ToolLibraryPage: React.FC = () => {
|
||||
};
|
||||
|
||||
const openSchemaDrawer = () => {
|
||||
const parameterConfig = buildToolParameterConfig(toolParameters);
|
||||
const parameterConfig = buildToolParameterConfig(toolParameters, toolSchema);
|
||||
if (!parameterConfig.error) {
|
||||
setToolSchema(parameterConfig.schema);
|
||||
setToolParameterDefaults(parameterConfig.defaults);
|
||||
setToolParameters((prev) => prev.map((item) => ({ ...item, sourceKey: item.key.trim() || item.sourceKey })));
|
||||
setSchemaEditorText(JSON.stringify(parameterConfig.schema, null, 2));
|
||||
} else if (editingTool?.parameterSchema && typeof editingTool.parameterSchema === 'object') {
|
||||
setSchemaEditorText(JSON.stringify(editingTool.parameterSchema, null, 2));
|
||||
} else if (toolSchema && typeof toolSchema === 'object') {
|
||||
setSchemaEditorText(JSON.stringify(toolSchema, null, 2));
|
||||
} else if (!schemaEditorText.trim()) {
|
||||
setSchemaEditorText(JSON.stringify({ type: 'object', properties: {}, required: [] }, null, 2));
|
||||
}
|
||||
@@ -325,19 +378,11 @@ export const ToolLibraryPage: React.FC = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const normalizedSchema: Record<string, any> = {
|
||||
...parsedSchema,
|
||||
type: 'object',
|
||||
properties: parsedSchema.properties && typeof parsedSchema.properties === 'object' && !Array.isArray(parsedSchema.properties)
|
||||
? parsedSchema.properties
|
||||
: {},
|
||||
required: Array.isArray(parsedSchema.required) ? parsedSchema.required : [],
|
||||
};
|
||||
|
||||
const currentParameterConfig = buildToolParameterConfig(toolParameters);
|
||||
const currentDefaults = currentParameterConfig.error ? {} : currentParameterConfig.defaults;
|
||||
const mergedDefaults = { ...currentDefaults, ...defaultsFromSchema(normalizedSchema) };
|
||||
const normalizedSchema = normalizeObjectSchema(parsedSchema);
|
||||
const mergedDefaults = { ...normalizeDefaultsObject(toolParameterDefaults), ...defaultsFromSchema(normalizedSchema) };
|
||||
|
||||
setToolSchema(normalizedSchema);
|
||||
setToolParameterDefaults(mergedDefaults);
|
||||
setToolParameters(draftsFromSchema(normalizedSchema, mergedDefaults));
|
||||
setSchemaEditorError('');
|
||||
setSchemaEditorText(JSON.stringify(normalizedSchema, null, 2));
|
||||
@@ -439,7 +484,7 @@ export const ToolLibraryPage: React.FC = () => {
|
||||
try {
|
||||
setSaving(true);
|
||||
let parsedHeaders: Record<string, string> = {};
|
||||
const parameterConfig = buildToolParameterConfig(toolParameters);
|
||||
const parameterConfig = buildToolParameterConfig(toolParameters, toolSchema);
|
||||
if (parameterConfig.error) {
|
||||
alert(parameterConfig.error);
|
||||
setSaving(false);
|
||||
@@ -447,6 +492,10 @@ export const ToolLibraryPage: React.FC = () => {
|
||||
}
|
||||
const parsedParameterSchema = parameterConfig.schema;
|
||||
const parsedParameterDefaults = parameterConfig.defaults;
|
||||
setToolSchema(parsedParameterSchema);
|
||||
setToolParameterDefaults(parsedParameterDefaults);
|
||||
setToolParameters((prev) => prev.map((item) => ({ ...item, sourceKey: item.key.trim() || item.sourceKey })));
|
||||
setSchemaEditorText(JSON.stringify(parsedParameterSchema, null, 2));
|
||||
|
||||
if (toolCategory === 'query') {
|
||||
if (
|
||||
@@ -807,7 +856,7 @@ export const ToolLibraryPage: React.FC = () => {
|
||||
</div>
|
||||
)}
|
||||
<p className="text-[11px] text-muted-foreground">
|
||||
支持系统指令和信息查询两类工具。Default Args 会在模型未传值时自动补齐。
|
||||
支持系统指令和信息查询两类工具。复杂 JSON Schema(如 items/anyOf/minItems)请在 Schema 抽屉编辑,参数表单修改时会保留这些扩展字段。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user