mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-02 03:55:55 +00:00
feat(knowledge): validate required fields based on plugin schema
Add business-agnostic validation for knowledge base creation: - Backend: dynamically validate required fields from plugin's creation_schema and retrieval_schema, with support for show_if conditional fields - Frontend: expose validation function from DynamicFormComponent and validate before KBForm submission - Add i18n translations for validation error messages Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -195,6 +195,7 @@ export default function DynamicFormComponent({
|
||||
isEditing,
|
||||
externalDependentValues,
|
||||
systemContext,
|
||||
onValidate,
|
||||
}: {
|
||||
itemConfigList: IDynamicFormItemSchema[];
|
||||
onSubmit?: (val: object) => unknown;
|
||||
@@ -205,6 +206,9 @@ export default function DynamicFormComponent({
|
||||
/** Extra variables accessible via the `__system.*` namespace in show_if conditions.
|
||||
* e.g. `{ is_wizard: true }` makes `show_if: { field: "__system.is_wizard", ... }` work. */
|
||||
systemContext?: Record<string, unknown>;
|
||||
/** Callback to expose validation function to parent component.
|
||||
* Parent can call this function to trigger validation and get validity state. */
|
||||
onValidate?: (validateFn: () => Promise<boolean>) => void;
|
||||
}) {
|
||||
const isInitialMount = useRef(true);
|
||||
const previousInitialValues = useRef(initialValues);
|
||||
@@ -352,6 +356,17 @@ export default function DynamicFormComponent({
|
||||
}, {} as FormValues),
|
||||
});
|
||||
|
||||
// Expose validation function to parent component
|
||||
const validate = async (): Promise<boolean> => {
|
||||
// Trigger validation for all fields
|
||||
const result = await form.trigger();
|
||||
return result;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
onValidate?.(validate);
|
||||
}, [onValidate]);
|
||||
|
||||
// 当 initialValues 变化时更新表单值
|
||||
// 但要避免因为内部表单更新触发的 onSubmit 导致的 initialValues 变化而重新设置表单
|
||||
useEffect(() => {
|
||||
|
||||
@@ -57,7 +57,6 @@ const getFormSchema = (t: (key: string) => string) =>
|
||||
* Parse creation schema from Knowledge Engine to IDynamicFormItemSchema[]
|
||||
*/
|
||||
function parseCreationSchema(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
schemaItems: any | any[] | undefined,
|
||||
): IDynamicFormItemSchema[] {
|
||||
if (!schemaItems) return [];
|
||||
@@ -107,6 +106,10 @@ export default function KBForm({
|
||||
const savedSnapshotRef = useRef<string>('');
|
||||
const isInitializing = useRef(true);
|
||||
|
||||
// Refs to store validation functions from dynamic forms
|
||||
const configValidateRef = useRef<(() => Promise<boolean>) | null>(null);
|
||||
const retrievalValidateRef = useRef<(() => Promise<boolean>) | null>(null);
|
||||
|
||||
const formSchema = getFormSchema(t);
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
@@ -235,7 +238,24 @@ export default function KBForm({
|
||||
}
|
||||
};
|
||||
|
||||
const onSubmit = (data: z.infer<typeof formSchema>) => {
|
||||
const onSubmit = async (data: z.infer<typeof formSchema>) => {
|
||||
// Validate dynamic forms before submission
|
||||
if (configValidateRef.current) {
|
||||
const configValid = await configValidateRef.current();
|
||||
if (!configValid) {
|
||||
toast.error(t('knowledge.engineSettingsInvalid'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (retrievalValidateRef.current) {
|
||||
const retrievalValid = await retrievalValidateRef.current();
|
||||
if (!retrievalValid) {
|
||||
toast.error(t('knowledge.retrievalSettingsInvalid'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const kbData: KnowledgeBase = {
|
||||
name: data.name,
|
||||
description: data.description ?? '',
|
||||
@@ -490,6 +510,9 @@ export default function KBForm({
|
||||
}
|
||||
isEditing={isEditing}
|
||||
externalDependentValues={retrievalSettings}
|
||||
onValidate={(validateFn) =>
|
||||
(configValidateRef.current = validateFn)
|
||||
}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -512,6 +535,9 @@ export default function KBForm({
|
||||
setRetrievalSettings(val as Record<string, unknown>)
|
||||
}
|
||||
externalDependentValues={configSettings}
|
||||
onValidate={(validateFn) =>
|
||||
(retrievalValidateRef.current = validateFn)
|
||||
}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@@ -928,6 +928,10 @@ const enUS = {
|
||||
engineSettingsDescription:
|
||||
'Configuration for the selected knowledge engine',
|
||||
engineSettingsReadonly: 'read-only in edit mode',
|
||||
engineSettingsInvalid:
|
||||
'Engine settings validation failed, please check required fields',
|
||||
retrievalSettingsInvalid:
|
||||
'Retrieval settings validation failed, please check required fields',
|
||||
retrievalSettings: 'Retrieval Settings',
|
||||
retrievalSettingsDescription:
|
||||
'Configure how documents are retrieved from this knowledge base',
|
||||
|
||||
@@ -886,6 +886,8 @@ const zhHans = {
|
||||
engineSettings: '引擎设置',
|
||||
engineSettingsDescription: '所选知识引擎的配置',
|
||||
engineSettingsReadonly: '编辑模式下不可修改',
|
||||
engineSettingsInvalid: '引擎设置中存在无效项,请检查必填字段',
|
||||
retrievalSettingsInvalid: '检索设置中存在无效项,请检查必填字段',
|
||||
retrievalSettings: '检索设置',
|
||||
retrievalSettingsDescription: '配置从此知识库检索文档的方式',
|
||||
dangerZone: '危险区域',
|
||||
|
||||
Reference in New Issue
Block a user