Fixes: Added session variable types and modified configuration (#11269)

### What problem does this PR solve?

Fixes: Added session variable types and modified configuration

- Added more types of session variables
- Modified the embedding model switching logic in the knowledge base
configuration

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
chanx
2025-11-14 13:56:56 +08:00
committed by GitHub
parent 72c20022f6
commit 87e69868c0
18 changed files with 712 additions and 213 deletions

View File

@ -61,6 +61,12 @@ export interface FormFieldConfig {
horizontal?: boolean;
onChange?: (value: any) => void;
tooltip?: React.ReactNode;
customValidate?: (
value: any,
formValues: any,
) => string | boolean | Promise<string | boolean>;
dependencies?: string[];
schema?: ZodSchema;
}
// Component props interface
@ -94,36 +100,40 @@ const generateSchema = (fields: FormFieldConfig[]): ZodSchema<any> => {
let fieldSchema: ZodSchema;
// Create base validation schema based on field type
switch (field.type) {
case FormFieldType.Email:
fieldSchema = z.string().email('Please enter a valid email address');
break;
case FormFieldType.Number:
fieldSchema = z.coerce.number();
if (field.validation?.min !== undefined) {
fieldSchema = (fieldSchema as z.ZodNumber).min(
field.validation.min,
field.validation.message ||
`Value cannot be less than ${field.validation.min}`,
);
}
if (field.validation?.max !== undefined) {
fieldSchema = (fieldSchema as z.ZodNumber).max(
field.validation.max,
field.validation.message ||
`Value cannot be greater than ${field.validation.max}`,
);
}
break;
case FormFieldType.Checkbox:
fieldSchema = z.boolean();
break;
case FormFieldType.Tag:
fieldSchema = z.array(z.string());
break;
default:
fieldSchema = z.string();
break;
if (field.schema) {
fieldSchema = field.schema;
} else {
switch (field.type) {
case FormFieldType.Email:
fieldSchema = z.string().email('Please enter a valid email address');
break;
case FormFieldType.Number:
fieldSchema = z.coerce.number();
if (field.validation?.min !== undefined) {
fieldSchema = (fieldSchema as z.ZodNumber).min(
field.validation.min,
field.validation.message ||
`Value cannot be less than ${field.validation.min}`,
);
}
if (field.validation?.max !== undefined) {
fieldSchema = (fieldSchema as z.ZodNumber).max(
field.validation.max,
field.validation.message ||
`Value cannot be greater than ${field.validation.max}`,
);
}
break;
case FormFieldType.Checkbox:
fieldSchema = z.boolean();
break;
case FormFieldType.Tag:
fieldSchema = z.array(z.string());
break;
default:
fieldSchema = z.string();
break;
}
}
// Handle required fields
@ -300,10 +310,90 @@ const DynamicForm = {
// Initialize form
const form = useForm<T>({
resolver: zodResolver(schema),
resolver: async (data, context, options) => {
const zodResult = await zodResolver(schema)(data, context, options);
let combinedErrors = { ...zodResult.errors };
const fieldErrors: Record<string, { type: string; message: string }> =
{};
for (const field of fields) {
if (field.customValidate && data[field.name] !== undefined) {
try {
const result = await field.customValidate(
data[field.name],
data,
);
if (typeof result === 'string') {
fieldErrors[field.name] = {
type: 'custom',
message: result,
};
} else if (result === false) {
fieldErrors[field.name] = {
type: 'custom',
message:
field.validation?.message || `${field.label} is invalid`,
};
}
} catch (error) {
fieldErrors[field.name] = {
type: 'custom',
message:
error instanceof Error
? error.message
: 'Validation failed',
};
}
}
}
combinedErrors = {
...combinedErrors,
...fieldErrors,
} as any;
console.log('combinedErrors', combinedErrors);
return {
values: Object.keys(combinedErrors).length ? {} : data,
errors: combinedErrors,
} as any;
},
defaultValues,
});
useEffect(() => {
const dependencyMap: Record<string, string[]> = {};
fields.forEach((field) => {
if (field.dependencies && field.dependencies.length > 0) {
field.dependencies.forEach((dep) => {
if (!dependencyMap[dep]) {
dependencyMap[dep] = [];
}
dependencyMap[dep].push(field.name);
});
}
});
const subscriptions = Object.keys(dependencyMap).map((depField) => {
return form.watch((values: any, { name }) => {
if (name === depField && dependencyMap[depField]) {
dependencyMap[depField].forEach((dependentField) => {
form.trigger(dependentField as any);
});
}
});
});
return () => {
subscriptions.forEach((sub) => {
if (sub.unsubscribe) {
sub.unsubscribe();
}
});
};
}, [fields, form]);
// Expose form methods via ref
useImperativeHandle(ref, () => ({
submit: () => form.handleSubmit(onSubmit)(),

View File

@ -51,6 +51,7 @@ export interface SegmentedProps
direction?: 'ltr' | 'rtl';
motionName?: string;
activeClassName?: string;
itemClassName?: string;
rounded?: keyof typeof segmentedVariants.round;
sizeType?: keyof typeof segmentedVariants.size;
buttonSize?: keyof typeof segmentedVariants.buttonSize;
@ -62,6 +63,7 @@ export function Segmented({
onChange,
className,
activeClassName,
itemClassName,
rounded = 'default',
sizeType = 'default',
buttonSize = 'default',
@ -92,12 +94,13 @@ export function Segmented({
<div
key={actualValue}
className={cn(
'inline-flex items-center text-base font-normal cursor-pointer',
'inline-flex items-center text-base font-normal cursor-pointer',
segmentedVariants.round[rounded],
segmentedVariants.buttonSize[buttonSize],
{
'text-text-primary bg-bg-base': selectedValue === actualValue,
},
itemClassName,
activeClassName && selectedValue === actualValue
? activeClassName
: '',

View File

@ -1009,6 +1009,7 @@ Example: general/v2/`,
pleaseUploadAtLeastOneFile: 'Please upload at least one file',
},
flow: {
formatTypeError: 'Format or type error',
variableNameMessage:
'Variable name can only contain letters and underscores',
variableDescription: 'Variable Description',

View File

@ -956,6 +956,7 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
pleaseUploadAtLeastOneFile: '请上传至少一个文件',
},
flow: {
formatTypeError: '格式或类型错误',
variableNameMessage: '名称只能包含字母和下划线',
variableDescription: '变量的描述',
defaultValue: '默认值',

View File

@ -0,0 +1,134 @@
import {
DynamicForm,
DynamicFormRef,
FormFieldConfig,
} from '@/components/dynamic-form';
import { Modal } from '@/components/ui/modal/modal';
import { t } from 'i18next';
import { useEffect, useRef } from 'react';
import { FieldValues } from 'react-hook-form';
import { TypeMaps, TypesWithArray } from '../constant';
import { useHandleForm } from '../hooks/use-form';
import { useObjectFields } from '../hooks/use-object-fields';
export const AddVariableModal = (props: {
fields?: FormFieldConfig[];
setFields: (value: any) => void;
visible?: boolean;
hideModal: () => void;
defaultValues?: FieldValues;
setDefaultValues?: (value: FieldValues) => void;
}) => {
const {
fields,
setFields,
visible,
hideModal,
defaultValues,
setDefaultValues,
} = props;
const { handleSubmit: submitForm, loading } = useHandleForm();
const { handleCustomValidate, handleCustomSchema, handleRender } =
useObjectFields();
const formRef = useRef<DynamicFormRef>(null);
const handleFieldUpdate = (
fieldName: string,
updatedField: Partial<FormFieldConfig>,
) => {
setFields((prevFields: any) =>
prevFields.map((field: any) =>
field.name === fieldName ? { ...field, ...updatedField } : field,
),
);
};
useEffect(() => {
const typeField = fields?.find((item) => item.name === 'type');
if (typeField) {
typeField.onChange = (value) => {
handleFieldUpdate('value', {
type: TypeMaps[value as keyof typeof TypeMaps],
render: handleRender(value),
customValidate: handleCustomValidate(value),
schema: handleCustomSchema(value),
});
const values = formRef.current?.getValues();
// setTimeout(() => {
switch (value) {
case TypesWithArray.Boolean:
setDefaultValues?.({ ...values, value: false });
break;
case TypesWithArray.Number:
setDefaultValues?.({ ...values, value: 0 });
break;
case TypesWithArray.Object:
setDefaultValues?.({ ...values, value: {} });
break;
case TypesWithArray.ArrayString:
setDefaultValues?.({ ...values, value: [''] });
break;
case TypesWithArray.ArrayNumber:
setDefaultValues?.({ ...values, value: [''] });
break;
case TypesWithArray.ArrayBoolean:
setDefaultValues?.({ ...values, value: [false] });
break;
case TypesWithArray.ArrayObject:
setDefaultValues?.({ ...values, value: [] });
break;
default:
setDefaultValues?.({ ...values, value: '' });
break;
}
// }, 0);
};
}
}, [fields]);
const handleSubmit = async (fieldValue: FieldValues) => {
await submitForm(fieldValue);
hideModal();
};
return (
<Modal
title={t('flow.add') + t('flow.conversationVariable')}
open={visible || false}
onCancel={hideModal}
showfooter={false}
>
<DynamicForm.Root
ref={formRef}
fields={fields || []}
onSubmit={(data) => {
console.log(data);
}}
defaultValues={defaultValues}
onFieldUpdate={handleFieldUpdate}
>
<div className="flex items-center justify-end w-full gap-2">
<DynamicForm.CancelButton
handleCancel={() => {
hideModal?.();
}}
/>
<DynamicForm.SavingButton
submitLoading={loading || false}
buttonText={t('common.ok')}
submitFunc={(values: FieldValues) => {
handleSubmit(values);
// console.log(values);
// console.log(nodes, edges);
// handleOk(values);
}}
/>
</div>
</DynamicForm.Root>
</Modal>
);
};

View File

@ -13,14 +13,14 @@ export enum TypesWithArray {
String = 'string',
Number = 'number',
Boolean = 'boolean',
// Object = 'object',
// ArrayString = 'array<string>',
// ArrayNumber = 'array<number>',
// ArrayBoolean = 'array<boolean>',
// ArrayObject = 'array<object>',
Object = 'object',
ArrayString = 'array<string>',
ArrayNumber = 'array<number>',
ArrayBoolean = 'array<boolean>',
ArrayObject = 'array<object>',
}
export const GobalFormFields = [
export const GlobalFormFields = [
{
label: t('flow.name'),
name: 'name',
@ -50,11 +50,11 @@ export const GobalFormFields = [
label: t('flow.description'),
name: 'description',
placeholder: t('flow.variableDescription'),
type: 'textarea',
type: FormFieldType.Textarea,
},
] as FormFieldConfig[];
export const GobalVariableFormDefaultValues = {
export const GlobalVariableFormDefaultValues = {
name: '',
type: TypesWithArray.String,
value: '',
@ -65,9 +65,9 @@ export const TypeMaps = {
[TypesWithArray.String]: FormFieldType.Textarea,
[TypesWithArray.Number]: FormFieldType.Number,
[TypesWithArray.Boolean]: FormFieldType.Checkbox,
// [TypesWithArray.Object]: FormFieldType.Textarea,
// [TypesWithArray.ArrayString]: FormFieldType.Textarea,
// [TypesWithArray.ArrayNumber]: FormFieldType.Textarea,
// [TypesWithArray.ArrayBoolean]: FormFieldType.Textarea,
// [TypesWithArray.ArrayObject]: FormFieldType.Textarea,
[TypesWithArray.Object]: FormFieldType.Textarea,
[TypesWithArray.ArrayString]: FormFieldType.Textarea,
[TypesWithArray.ArrayNumber]: FormFieldType.Textarea,
[TypesWithArray.ArrayBoolean]: FormFieldType.Textarea,
[TypesWithArray.ArrayObject]: FormFieldType.Textarea,
};

View File

@ -0,0 +1,41 @@
import { useFetchAgent } from '@/hooks/use-agent-request';
import { GlobalVariableType } from '@/interfaces/database/agent';
import { useCallback } from 'react';
import { FieldValues } from 'react-hook-form';
import { useSaveGraph } from '../../hooks/use-save-graph';
import { TypesWithArray } from '../constant';
export const useHandleForm = () => {
const { data, refetch } = useFetchAgent();
const { saveGraph, loading } = useSaveGraph();
const handleObjectData = (value: any) => {
try {
return JSON.parse(value);
} catch (error) {
return value;
}
};
const handleSubmit = useCallback(async (fieldValue: FieldValues) => {
const param = {
...(data.dsl?.variables || {}),
[fieldValue.name]: {
...fieldValue,
value:
fieldValue.type === TypesWithArray.Object ||
fieldValue.type === TypesWithArray.ArrayObject
? handleObjectData(fieldValue.value)
: fieldValue.value,
},
} as Record<string, GlobalVariableType>;
const res = await saveGraph(undefined, {
globalVariables: param,
});
if (res.code === 0) {
refetch();
}
}, []);
return { handleSubmit, loading };
};

View File

@ -0,0 +1,246 @@
import { BlockButton, Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Segmented } from '@/components/ui/segmented';
import { Editor } from '@monaco-editor/react';
import { t } from 'i18next';
import { Trash2, X } from 'lucide-react';
import { useCallback } from 'react';
import { FieldValues } from 'react-hook-form';
import { z } from 'zod';
import { TypesWithArray } from '../constant';
export const useObjectFields = () => {
const booleanRender = useCallback(
(field: FieldValues, className?: string) => {
const fieldValue = field.value ? true : false;
return (
<Segmented
options={
[
{ value: true, label: 'True' },
{ value: false, label: 'False' },
] as any
}
sizeType="sm"
value={fieldValue}
onChange={field.onChange}
className={className}
itemClassName="justify-center flex-1"
></Segmented>
);
},
[],
);
const objectRender = useCallback((field: FieldValues) => {
const fieldValue =
typeof field.value === 'object'
? JSON.stringify(field.value, null, 2)
: JSON.stringify({}, null, 2);
console.log('object-render-field', field, fieldValue);
return (
<Editor
height={200}
defaultLanguage="json"
theme="vs-dark"
value={fieldValue}
onChange={field.onChange}
/>
);
}, []);
const objectValidate = useCallback((value: any) => {
try {
if (!JSON.parse(value)) {
throw new Error(t('knowledgeDetails.formatTypeError'));
}
return true;
} catch (e) {
throw new Error(t('knowledgeDetails.formatTypeError'));
}
}, []);
const arrayStringRender = useCallback((field: FieldValues, type = 'text') => {
const values = Array.isArray(field.value)
? field.value
: [type === 'number' ? 0 : ''];
return (
<>
{values?.map((item: any, index: number) => (
<div key={index} className="flex gap-1 items-center">
<Input
type={type}
value={item}
onChange={(e) => {
const newValues = [...values];
newValues[index] = e.target.value;
field.onChange(newValues);
}}
/>
<Button
variant={'secondary'}
onClick={() => {
const newValues = [...values];
newValues.splice(index, 1);
field.onChange(newValues);
}}
>
<Trash2 />
</Button>
</div>
))}
<BlockButton
type="button"
onClick={() => {
field.onChange([...field.value, '']);
}}
>
{t('flow.add')}
</BlockButton>
</>
);
}, []);
const arrayBooleanRender = useCallback(
(field: FieldValues) => {
// const values = field.value || [false];
const values = Array.isArray(field.value) ? field.value : [false];
return (
<div className="flex items-center gap-1 flex-wrap ">
{values?.map((item: any, index: number) => (
<div
key={index}
className="flex gap-1 items-center bg-bg-card rounded-lg border-[0.5px] border-border-button"
>
{booleanRender(
{
value: item,
onChange: (value) => {
values[index] = !!value;
field.onChange(values);
},
},
'bg-transparent',
)}
<Button
variant={'transparent'}
className="border-none py-0 px-1"
onClick={() => {
const newValues = [...values];
newValues.splice(index, 1);
field.onChange(newValues);
}}
>
<X />
</Button>
</div>
))}
<BlockButton
className="w-auto"
type="button"
onClick={() => {
field.onChange([...field.value, false]);
}}
>
{t('flow.add')}
</BlockButton>
</div>
);
},
[booleanRender],
);
const arrayNumberRender = useCallback(
(field: FieldValues) => {
return arrayStringRender(field, 'number');
},
[arrayStringRender],
);
const arrayValidate = useCallback((value: any, type: string = 'string') => {
if (!Array.isArray(value) || !value.every((item) => typeof item === type)) {
throw new Error(t('flow.formatTypeError'));
}
return true;
}, []);
const arrayStringValidate = useCallback(
(value: any) => {
return arrayValidate(value, 'string');
},
[arrayValidate],
);
const arrayNumberValidate = useCallback(
(value: any) => {
return arrayValidate(value, 'number');
},
[arrayValidate],
);
const arrayBooleanValidate = useCallback(
(value: any) => {
return arrayValidate(value, 'boolean');
},
[arrayValidate],
);
const handleRender = (value: TypesWithArray) => {
switch (value) {
case TypesWithArray.Boolean:
return booleanRender;
case TypesWithArray.Object:
case TypesWithArray.ArrayObject:
return objectRender;
case TypesWithArray.ArrayString:
return arrayStringRender;
case TypesWithArray.ArrayNumber:
return arrayNumberRender;
case TypesWithArray.ArrayBoolean:
return arrayBooleanRender;
default:
return undefined;
}
};
const handleCustomValidate = (value: TypesWithArray) => {
switch (value) {
case TypesWithArray.Object:
case TypesWithArray.ArrayObject:
return objectValidate;
case TypesWithArray.ArrayString:
return arrayStringValidate;
case TypesWithArray.ArrayNumber:
return arrayNumberValidate;
case TypesWithArray.ArrayBoolean:
return arrayBooleanValidate;
default:
return undefined;
}
};
const handleCustomSchema = (value: TypesWithArray) => {
switch (value) {
case TypesWithArray.ArrayString:
return z.array(z.string());
case TypesWithArray.ArrayNumber:
return z.array(z.number());
case TypesWithArray.ArrayBoolean:
return z.array(z.boolean());
default:
return undefined;
}
};
return {
objectRender,
objectValidate,
arrayStringRender,
arrayStringValidate,
arrayNumberRender,
booleanRender,
arrayBooleanRender,
arrayNumberValidate,
arrayBooleanValidate,
handleRender,
handleCustomValidate,
handleCustomSchema,
};
};

View File

@ -1,12 +1,6 @@
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
import {
DynamicForm,
DynamicFormRef,
FormFieldConfig,
FormFieldType,
} from '@/components/dynamic-form';
import { FormFieldConfig } from '@/components/dynamic-form';
import { BlockButton, Button } from '@/components/ui/button';
import { Modal } from '@/components/ui/modal/modal';
import {
Sheet,
SheetContent,
@ -19,117 +13,65 @@ import { GlobalVariableType } from '@/interfaces/database/agent';
import { cn } from '@/lib/utils';
import { t } from 'i18next';
import { Trash2 } from 'lucide-react';
import { useEffect, useRef, useState } from 'react';
import { useState } from 'react';
import { FieldValues } from 'react-hook-form';
import { useSaveGraph } from '../hooks/use-save-graph';
import { AddVariableModal } from './component/add-variable-modal';
import {
GobalFormFields,
GobalVariableFormDefaultValues,
GlobalFormFields,
GlobalVariableFormDefaultValues,
TypeMaps,
TypesWithArray,
} from './contant';
} from './constant';
import { useObjectFields } from './hooks/use-object-fields';
export type IGobalParamModalProps = {
export type IGlobalParamModalProps = {
data: any;
hideModal: (open: boolean) => void;
};
export const GobalParamSheet = (props: IGobalParamModalProps) => {
export const GlobalParamSheet = (props: IGlobalParamModalProps) => {
const { hideModal } = props;
const { data, refetch } = useFetchAgent();
const [fields, setFields] = useState<FormFieldConfig[]>(GobalFormFields);
const { visible, showModal, hideModal: hideAddModal } = useSetModalState();
const [fields, setFields] = useState<FormFieldConfig[]>(GlobalFormFields);
const [defaultValues, setDefaultValues] = useState<FieldValues>(
GobalVariableFormDefaultValues,
GlobalVariableFormDefaultValues,
);
const formRef = useRef<DynamicFormRef>(null);
const { handleCustomValidate, handleCustomSchema, handleRender } =
useObjectFields();
const { saveGraph } = useSaveGraph();
const handleFieldUpdate = (
fieldName: string,
updatedField: Partial<FormFieldConfig>,
) => {
setFields((prevFields) =>
prevFields.map((field) =>
field.name === fieldName ? { ...field, ...updatedField } : field,
),
);
};
useEffect(() => {
const typefileld = fields.find((item) => item.name === 'type');
if (typefileld) {
typefileld.onChange = (value) => {
// setWatchType(value);
handleFieldUpdate('value', {
type: TypeMaps[value as keyof typeof TypeMaps],
});
const values = formRef.current?.getValues();
setTimeout(() => {
switch (value) {
case TypesWithArray.Boolean:
setDefaultValues({ ...values, value: false });
break;
case TypesWithArray.Number:
setDefaultValues({ ...values, value: 0 });
break;
default:
setDefaultValues({ ...values, value: '' });
}
}, 0);
};
}
}, [fields]);
const { saveGraph, loading } = useSaveGraph();
const handleSubmit = async (value: FieldValues) => {
const param = {
...(data.dsl?.variables || {}),
[value.name]: value,
} as Record<string, GlobalVariableType>;
const res = await saveGraph(undefined, {
gobalVariables: param,
});
if (res.code === 0) {
refetch();
}
hideAddModal();
};
const handleDeleteGobalVariable = async (key: string) => {
const handleDeleteGlobalVariable = async (key: string) => {
const param = {
...(data.dsl?.variables || {}),
} as Record<string, GlobalVariableType>;
delete param[key];
const res = await saveGraph(undefined, {
gobalVariables: param,
globalVariables: param,
});
console.log('delete gobal variable-->', res);
if (res.code === 0) {
refetch();
}
};
const handleEditGobalVariable = (item: FieldValues) => {
fields.forEach((field) => {
if (field.name === 'value') {
switch (item.type) {
// [TypesWithArray.String]: FormFieldType.Textarea,
// [TypesWithArray.Number]: FormFieldType.Number,
// [TypesWithArray.Boolean]: FormFieldType.Checkbox,
case TypesWithArray.Boolean:
field.type = FormFieldType.Checkbox;
break;
case TypesWithArray.Number:
field.type = FormFieldType.Number;
break;
default:
field.type = FormFieldType.Textarea;
}
const handleEditGlobalVariable = (item: FieldValues) => {
const newFields = fields.map((field) => {
let newField = field;
newField.render = undefined;
newField.schema = undefined;
newField.customValidate = undefined;
if (newField.name === 'value') {
newField = {
...newField,
type: TypeMaps[item.type as keyof typeof TypeMaps],
render: handleRender(item.type),
customValidate: handleCustomValidate(item.type),
schema: handleCustomSchema(item.type),
};
}
return newField;
});
setFields(newFields);
setDefaultValues(item);
showModal();
};
@ -149,8 +91,8 @@ export const GobalParamSheet = (props: IGobalParamModalProps) => {
<div className="px-5 pb-5">
<BlockButton
onClick={() => {
setFields(GobalFormFields);
setDefaultValues(GobalVariableFormDefaultValues);
setFields(GlobalFormFields);
setDefaultValues(GlobalVariableFormDefaultValues);
showModal();
}}
>
@ -167,7 +109,7 @@ export const GobalParamSheet = (props: IGobalParamModalProps) => {
key={key}
className="flex items-center gap-3 min-h-14 justify-between px-5 py-3 border border-border-default rounded-lg hover:bg-bg-card group"
onClick={() => {
handleEditGobalVariable(item);
handleEditGlobalVariable(item);
}}
>
<div className="flex flex-col">
@ -177,13 +119,23 @@ export const GobalParamSheet = (props: IGobalParamModalProps) => {
{item.type}
</span>
</div>
<div>
<span className="text-text-primary">{item.value}</span>
</div>
{![
TypesWithArray.Object,
TypesWithArray.ArrayObject,
TypesWithArray.ArrayString,
TypesWithArray.ArrayNumber,
TypesWithArray.ArrayBoolean,
].includes(item.type as TypesWithArray) && (
<div>
<span className="text-text-primary">
{item.value}
</span>
</div>
)}
</div>
<div>
<ConfirmDeleteDialog
onOk={() => handleDeleteGobalVariable(key)}
onOk={() => handleDeleteGlobalVariable(key)}
>
<Button
variant={'secondary'}
@ -201,40 +153,14 @@ export const GobalParamSheet = (props: IGobalParamModalProps) => {
})}
</div>
</SheetContent>
<Modal
title={t('flow.add') + t('flow.conversationVariable')}
open={visible}
onCancel={hideAddModal}
showfooter={false}
>
<DynamicForm.Root
ref={formRef}
fields={fields}
onSubmit={(data) => {
console.log(data);
}}
defaultValues={defaultValues}
onFieldUpdate={handleFieldUpdate}
>
<div className="flex items-center justify-end w-full gap-2">
<DynamicForm.CancelButton
handleCancel={() => {
hideAddModal?.();
}}
/>
<DynamicForm.SavingButton
submitLoading={loading || false}
buttonText={t('common.ok')}
submitFunc={(values: FieldValues) => {
handleSubmit(values);
// console.log(values);
// console.log(nodes, edges);
// handleOk(values);
}}
/>
</div>
</DynamicForm.Root>
</Modal>
<AddVariableModal
visible={visible}
hideModal={hideAddModal}
fields={fields}
setFields={setFields}
defaultValues={defaultValues}
setDefaultValues={setDefaultValues}
/>
</Sheet>
</>
);

View File

@ -4,7 +4,7 @@ import { RAGFlowNodeType } from '@/interfaces/database/flow';
import { useCallback } from 'react';
import { Operator } from '../constant';
import useGraphStore from '../store';
import { buildDslComponentsByGraph, buildDslGobalVariables } from '../utils';
import { buildDslComponentsByGraph, buildDslGlobalVariables } from '../utils';
export const useBuildDslData = () => {
const { data } = useFetchAgent();
@ -13,7 +13,7 @@ export const useBuildDslData = () => {
const buildDslData = useCallback(
(
currentNodes?: RAGFlowNodeType[],
otherParam?: { gobalVariables: Record<string, GlobalVariableType> },
otherParam?: { globalVariables: Record<string, GlobalVariableType> },
) => {
const nodesToProcess = currentNodes ?? nodes;
@ -41,13 +41,13 @@ export const useBuildDslData = () => {
data.dsl.components,
);
const gobalVariables = buildDslGobalVariables(
const globalVariables = buildDslGlobalVariables(
data.dsl,
otherParam?.gobalVariables,
otherParam?.globalVariables,
);
return {
...data.dsl,
...gobalVariables,
...globalVariables,
graph: { nodes: filteredNodes, edges: filteredEdges },
components: dslComponents,
};

View File

@ -21,7 +21,7 @@ export const useSaveGraph = (showMessage: boolean = true) => {
const saveGraph = useCallback(
async (
currentNodes?: RAGFlowNodeType[],
otherParam?: { gobalVariables: Record<string, GlobalVariableType> },
otherParam?: { globalVariables: Record<string, GlobalVariableType> },
) => {
return setAgent({
id,

View File

@ -39,7 +39,7 @@ import { useParams } from 'umi';
import AgentCanvas from './canvas';
import { DropdownProvider } from './canvas/context';
import { Operator } from './constant';
import { GobalParamSheet } from './gobal-variable-sheet';
import { GlobalParamSheet } from './gobal-variable-sheet';
import { useCancelCurrentDataflow } from './hooks/use-cancel-dataflow';
import { useHandleExportJsonFile } from './hooks/use-export-json';
import { useFetchDataOnMount } from './hooks/use-fetch-data';
@ -126,9 +126,9 @@ export default function Agent() {
} = useSetModalState();
const {
visible: gobalParamSheetVisible,
showModal: showGobalParamSheet,
hideModal: hideGobalParamSheet,
visible: globalParamSheetVisible,
showModal: showGlobalParamSheet,
hideModal: hideGlobalParamSheet,
} = useSetModalState();
const {
@ -216,7 +216,7 @@ export default function Agent() {
</ButtonLoading>
<ButtonLoading
variant={'secondary'}
onClick={() => showGobalParamSheet()}
onClick={() => showGlobalParamSheet()}
loading={loading}
>
<MessageSquareCode /> {t('flow.conversationVariable')}
@ -314,11 +314,11 @@ export default function Agent() {
loading={pipelineRunning}
></PipelineRunSheet>
)}
{gobalParamSheetVisible && (
<GobalParamSheet
{globalParamSheetVisible && (
<GlobalParamSheet
data={{}}
hideModal={hideGobalParamSheet}
></GobalParamSheet>
hideModal={hideGlobalParamSheet}
></GlobalParamSheet>
)}
</section>
);

View File

@ -348,30 +348,30 @@ export const buildDslComponentsByGraph = (
return components;
};
export const buildDslGobalVariables = (
export const buildDslGlobalVariables = (
dsl: DSL,
gobalVariables?: Record<string, GlobalVariableType>,
globalVariables?: Record<string, GlobalVariableType>,
) => {
if (!gobalVariables) {
if (!globalVariables) {
return { globals: dsl.globals, variables: dsl.variables || {} };
}
let gobalVariablesTemp: Record<string, any> = {};
let gobalSystem: Record<string, any> = {};
let globalVariablesTemp: Record<string, any> = {};
let globalSystem: Record<string, any> = {};
Object.keys(dsl.globals)?.forEach((key) => {
if (key.indexOf('sys') > -1) {
gobalSystem[key] = dsl.globals[key];
globalSystem[key] = dsl.globals[key];
}
});
Object.keys(gobalVariables).forEach((key) => {
gobalVariablesTemp['env.' + key] = gobalVariables[key].value;
Object.keys(globalVariables).forEach((key) => {
globalVariablesTemp['env.' + key] = globalVariables[key].value;
});
const gobalVariablesResult = {
...gobalSystem,
...gobalVariablesTemp,
const globalVariablesResult = {
...globalSystem,
...globalVariablesTemp,
};
return { globals: gobalVariablesResult, variables: gobalVariables };
return { globals: globalVariablesResult, variables: globalVariables };
};
export const receiveMessageError = (res: any) =>

View File

@ -7,11 +7,14 @@ import {
FormMessage,
} from '@/components/ui/form';
import { Radio } from '@/components/ui/radio';
import { Spin } from '@/components/ui/spin';
import { Switch } from '@/components/ui/switch';
import { useTranslate } from '@/hooks/common-hooks';
import { cn } from '@/lib/utils';
import { useMemo, useState } from 'react';
import { useFormContext } from 'react-hook-form';
import {
useHandleKbEmbedding,
useHasParsedDocument,
useSelectChunkMethodList,
useSelectEmbeddingModelOptions,
@ -62,11 +65,17 @@ export function ChunkMethodItem(props: IProps) {
/>
);
}
export function EmbeddingModelItem({ line = 1, isEdit = true }: IProps) {
export function EmbeddingModelItem({ line = 1, isEdit }: IProps) {
const { t } = useTranslate('knowledgeConfiguration');
const form = useFormContext();
const embeddingModelOptions = useSelectEmbeddingModelOptions();
const { handleChange } = useHandleKbEmbedding();
const disabled = useHasParsedDocument(isEdit);
const oldValue = useMemo(() => {
const embdStr = form.getValues('embd_id');
return embdStr || '';
}, [form]);
const [loading, setLoading] = useState(false);
return (
<>
<FormField
@ -93,14 +102,33 @@ export function EmbeddingModelItem({ line = 1, isEdit = true }: IProps) {
className={cn('text-muted-foreground', { 'w-3/4': line === 1 })}
>
<FormControl>
<SelectWithSearch
onChange={field.onChange}
value={field.value}
options={embeddingModelOptions}
disabled={isEdit ? disabled : false}
placeholder={t('embeddingModelPlaceholder')}
triggerClassName="!bg-bg-base"
/>
<Spin
spinning={loading}
className={cn(' rounded-lg after:bg-bg-base', {
'opacity-20': loading,
})}
>
<SelectWithSearch
onChange={async (value) => {
field.onChange(value);
if (isEdit && disabled) {
setLoading(true);
const res = await handleChange({
embed_id: value,
callback: field.onChange,
});
if (res.code !== 0) {
field.onChange(oldValue);
}
setLoading(false);
}
}}
value={field.value}
options={embeddingModelOptions}
placeholder={t('embeddingModelPlaceholder')}
triggerClassName="!bg-bg-base"
/>
</Spin>
</FormControl>
</div>
</div>

View File

@ -88,7 +88,7 @@ export function GeneralForm() {
}}
/>
<PermissionFormField></PermissionFormField>
<EmbeddingModelItem></EmbeddingModelItem>
<EmbeddingModelItem isEdit={true}></EmbeddingModelItem>
<PageRankFormField></PageRankFormField>
<TagItems></TagItems>

View File

@ -4,10 +4,12 @@ import { useSetModalState } from '@/hooks/common-hooks';
import { useSelectLlmOptionsByModelType } from '@/hooks/llm-hooks';
import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request';
import { useSelectParserList } from '@/hooks/user-setting-hooks';
import kbService from '@/services/knowledge-service';
import { useIsFetching } from '@tanstack/react-query';
import { pick } from 'lodash';
import { useCallback, useEffect, useState } from 'react';
import { UseFormReturn } from 'react-hook-form';
import { useParams, useSearchParams } from 'umi';
import { z } from 'zod';
import { formSchema } from './form-schema';
@ -98,3 +100,22 @@ export const useRenameKnowledgeTag = () => {
showTagRenameModal: handleShowTagRenameModal,
};
};
export const useHandleKbEmbedding = () => {
const { id } = useParams();
const [searchParams] = useSearchParams();
const knowledgeBaseId = searchParams.get('id') || id;
const handleChange = useCallback(
async ({ embed_id }: { embed_id: string }) => {
const res = await kbService.checkEmbedding({
kb_id: knowledgeBaseId,
embd_id: embed_id,
});
return res.data;
},
[knowledgeBaseId],
);
return {
handleChange,
};
};

View File

@ -47,6 +47,7 @@ const {
traceGraphRag,
runRaptor,
traceRaptor,
check_embedding,
} = api;
const methods = {
@ -214,6 +215,11 @@ const methods = {
url: api.pipelineRerun,
method: 'post',
},
checkEmbedding: {
url: check_embedding,
method: 'post',
},
};
const kbService = registerServer<keyof typeof methods>(methods, request);

View File

@ -49,6 +49,8 @@ export default {
llm_tools: `${api_host}/plugin/llm_tools`,
// knowledge base
check_embedding: `${api_host}/kb/check_embedding`,
kb_list: `${api_host}/kb/list`,
create_kb: `${api_host}/kb/create`,
update_kb: `${api_host}/kb/update`,