mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### What problem does this PR solve? Feat: Add invoke and github operators #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -1308,6 +1308,7 @@ This delimiter is used to split the input text into several text pieces echo of
|
||||
management: 'Management',
|
||||
import: 'Import',
|
||||
export: 'Export',
|
||||
seconds: 'Seconds',
|
||||
},
|
||||
llmTools: {
|
||||
bad_calculator: {
|
||||
|
||||
@ -109,6 +109,9 @@ function AccordionOperators() {
|
||||
Operator.Wikipedia,
|
||||
Operator.GoogleScholar,
|
||||
Operator.ArXiv,
|
||||
Operator.PubMed,
|
||||
Operator.GitHub,
|
||||
Operator.Invoke,
|
||||
]}
|
||||
></OperatorItemList>
|
||||
</AccordionContent>
|
||||
|
||||
@ -372,9 +372,15 @@ export const initialWikipediaValues = {
|
||||
};
|
||||
|
||||
export const initialPubMedValues = {
|
||||
top_n: 10,
|
||||
top_n: 12,
|
||||
email: '',
|
||||
...initialQueryBaseValues,
|
||||
query: AgentGlobals.SysQuery,
|
||||
outputs: {
|
||||
formalized_content: {
|
||||
value: '',
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const initialArXivValues = {
|
||||
@ -444,7 +450,17 @@ export const initialDeepLValues = {
|
||||
|
||||
export const initialGithubValues = {
|
||||
top_n: 5,
|
||||
...initialQueryBaseValues,
|
||||
query: AgentGlobals.SysQuery,
|
||||
outputs: {
|
||||
formalized_content: {
|
||||
value: '',
|
||||
type: 'string',
|
||||
},
|
||||
json: {
|
||||
value: [],
|
||||
type: 'Array<Object>',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const initialBaiduFanyiValues = {
|
||||
@ -540,7 +556,7 @@ export const initialCrawlerValues = {
|
||||
};
|
||||
|
||||
export const initialInvokeValues = {
|
||||
url: 'http://',
|
||||
url: '',
|
||||
method: 'GET',
|
||||
timeout: 60,
|
||||
headers: `{
|
||||
@ -548,8 +564,9 @@ export const initialInvokeValues = {
|
||||
"Cache-Control": "no-cache",
|
||||
"Connection": "keep-alive"
|
||||
}`,
|
||||
proxy: 'http://',
|
||||
proxy: '',
|
||||
clean_html: false,
|
||||
variables: [],
|
||||
};
|
||||
|
||||
export const initialTemplateValues = {
|
||||
@ -852,7 +869,7 @@ export const NodeMap = {
|
||||
[Operator.TuShare]: 'ragNode',
|
||||
[Operator.Note]: 'noteNode',
|
||||
[Operator.Crawler]: 'ragNode',
|
||||
[Operator.Invoke]: 'invokeNode',
|
||||
[Operator.Invoke]: 'ragNode',
|
||||
[Operator.Template]: 'templateNode',
|
||||
[Operator.Email]: 'ragNode',
|
||||
[Operator.Iteration]: 'group',
|
||||
|
||||
@ -126,7 +126,7 @@ function AgentForm({ node }: INextOperatorForm) {
|
||||
name={`sys_prompt`}
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormLabel>Prompt</FormLabel>
|
||||
<FormLabel>System Prompt</FormLabel>
|
||||
<FormControl>
|
||||
<PromptEditor
|
||||
{...field}
|
||||
|
||||
@ -39,13 +39,7 @@ const Menus = [
|
||||
},
|
||||
{
|
||||
label: 'Developer',
|
||||
list: [
|
||||
Operator.GitHub,
|
||||
Operator.ExeSQL,
|
||||
Operator.Invoke,
|
||||
Operator.Code,
|
||||
Operator.Retrieval,
|
||||
],
|
||||
list: [Operator.GitHub, Operator.ExeSQL, Operator.Code, Operator.Retrieval],
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@ -1,21 +1,52 @@
|
||||
import TopNItem from '@/components/top-n-item';
|
||||
import { Form } from 'antd';
|
||||
import { IOperatorForm } from '../../interface';
|
||||
import DynamicInputVariable from '../components/dynamic-input-variable';
|
||||
import { FormContainer } from '@/components/form-container';
|
||||
import { TopNFormField } from '@/components/top-n-item';
|
||||
import { Form } from '@/components/ui/form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { memo } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import { initialGithubValues } from '../../constant';
|
||||
import { useFormValues } from '../../hooks/use-form-values';
|
||||
import { useWatchFormChange } from '../../hooks/use-watch-form-change';
|
||||
import { INextOperatorForm } from '../../interface';
|
||||
import { buildOutputList } from '../../utils/build-output-list';
|
||||
import { FormWrapper } from '../components/form-wrapper';
|
||||
import { Output } from '../components/output';
|
||||
import { QueryVariable } from '../components/query-variable';
|
||||
|
||||
export const FormSchema = z.object({
|
||||
query: z.string(),
|
||||
top_n: z.number(),
|
||||
});
|
||||
|
||||
const outputList = buildOutputList(initialGithubValues.outputs);
|
||||
|
||||
function GithubForm({ node }: INextOperatorForm) {
|
||||
const defaultValues = useFormValues(initialGithubValues, node);
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
defaultValues,
|
||||
resolver: zodResolver(FormSchema),
|
||||
mode: 'onChange',
|
||||
});
|
||||
|
||||
useWatchFormChange(node?.id, form);
|
||||
|
||||
const GithubForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
||||
return (
|
||||
<Form
|
||||
name="basic"
|
||||
autoComplete="off"
|
||||
form={form}
|
||||
onValuesChange={onValuesChange}
|
||||
layout={'vertical'}
|
||||
>
|
||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
||||
<TopNItem initialValue={5}></TopNItem>
|
||||
<Form {...form}>
|
||||
<FormWrapper>
|
||||
<FormContainer>
|
||||
<QueryVariable></QueryVariable>
|
||||
</FormContainer>
|
||||
<FormContainer>
|
||||
<TopNFormField></TopNFormField>
|
||||
</FormContainer>
|
||||
</FormWrapper>
|
||||
<div className="p-5">
|
||||
<Output list={outputList}></Output>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default GithubForm;
|
||||
export default memo(GithubForm);
|
||||
|
||||
@ -1,131 +0,0 @@
|
||||
import { EditableCell, EditableRow } from '@/components/editable-cell';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { DeleteOutlined } from '@ant-design/icons';
|
||||
import { Button, Collapse, Flex, Input, Select, Table, TableProps } from 'antd';
|
||||
import { trim } from 'lodash';
|
||||
import { useBuildVariableOptions } from '../../hooks/use-get-begin-query';
|
||||
import { IInvokeVariable } from '../../interface';
|
||||
import { useHandleOperateParameters } from './hooks';
|
||||
|
||||
import { RAGFlowNodeType } from '@/interfaces/database/flow';
|
||||
import styles from './index.less';
|
||||
|
||||
interface IProps {
|
||||
node?: RAGFlowNodeType;
|
||||
}
|
||||
|
||||
const components = {
|
||||
body: {
|
||||
row: EditableRow,
|
||||
cell: EditableCell,
|
||||
},
|
||||
};
|
||||
|
||||
const DynamicVariablesForm = ({ node }: IProps) => {
|
||||
const nodeId = node?.id;
|
||||
const { t } = useTranslate('flow');
|
||||
|
||||
const options = useBuildVariableOptions(nodeId, node?.parentId);
|
||||
const {
|
||||
dataSource,
|
||||
handleAdd,
|
||||
handleRemove,
|
||||
handleSave,
|
||||
handleComponentIdChange,
|
||||
handleValueChange,
|
||||
} = useHandleOperateParameters(nodeId!);
|
||||
|
||||
const columns: TableProps<IInvokeVariable>['columns'] = [
|
||||
{
|
||||
title: t('key'),
|
||||
dataIndex: 'key',
|
||||
key: 'key',
|
||||
onCell: (record: IInvokeVariable) => ({
|
||||
record,
|
||||
editable: true,
|
||||
dataIndex: 'key',
|
||||
title: 'key',
|
||||
handleSave,
|
||||
}),
|
||||
},
|
||||
{
|
||||
title: t('componentId'),
|
||||
dataIndex: 'component_id',
|
||||
key: 'component_id',
|
||||
align: 'center',
|
||||
width: 140,
|
||||
render(text, record) {
|
||||
return (
|
||||
<Select
|
||||
style={{ width: '100%' }}
|
||||
allowClear
|
||||
options={options}
|
||||
value={text}
|
||||
disabled={trim(record.value) !== ''}
|
||||
onChange={handleComponentIdChange(record)}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('value'),
|
||||
dataIndex: 'value',
|
||||
key: 'value',
|
||||
align: 'center',
|
||||
width: 140,
|
||||
render(text, record) {
|
||||
return (
|
||||
<Input
|
||||
value={text}
|
||||
disabled={!!record.component_id}
|
||||
onChange={handleValueChange(record)}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('operation'),
|
||||
dataIndex: 'operation',
|
||||
width: 20,
|
||||
key: 'operation',
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
render(_, record) {
|
||||
return <DeleteOutlined onClick={handleRemove(record.id)} />;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Collapse
|
||||
className={styles.dynamicParameterVariable}
|
||||
defaultActiveKey={['1']}
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
label: (
|
||||
<Flex justify={'space-between'}>
|
||||
<span className={styles.title}>{t('parameter')}</span>
|
||||
<Button size="small" onClick={handleAdd}>
|
||||
{t('add')}
|
||||
</Button>
|
||||
</Flex>
|
||||
),
|
||||
children: (
|
||||
<Table
|
||||
dataSource={dataSource}
|
||||
columns={columns}
|
||||
rowKey={'id'}
|
||||
components={components}
|
||||
rowClassName={() => styles.editableRow}
|
||||
scroll={{ x: true }}
|
||||
bordered
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default DynamicVariablesForm;
|
||||
@ -1,8 +1,33 @@
|
||||
import { Collapse } from '@/components/collapse';
|
||||
import { FormContainer } from '@/components/form-container';
|
||||
import NumberInput from '@/components/originui/number-input';
|
||||
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import Editor, { loader } from '@monaco-editor/react';
|
||||
import { Form, Input, InputNumber, Select, Space, Switch } from 'antd';
|
||||
import { Plus } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { IOperatorForm } from '../../interface';
|
||||
import DynamicVariablesForm from './dynamic-variables';
|
||||
import { initialInvokeValues } from '../../constant';
|
||||
import { useFormValues } from '../../hooks/use-form-values';
|
||||
import { useWatchFormChange } from '../../hooks/use-watch-form-change';
|
||||
import { INextOperatorForm } from '../../interface';
|
||||
import { FormWrapper } from '../components/form-wrapper';
|
||||
import { FormSchema, FormSchemaType } from './schema';
|
||||
import { useEditVariableRecord } from './use-edit-variable';
|
||||
import { VariableDialog } from './variable-dialog';
|
||||
import { VariableTable } from './variable-table';
|
||||
|
||||
loader.config({ paths: { vs: '/vs' } });
|
||||
|
||||
@ -25,54 +50,170 @@ interface TimeoutInputProps {
|
||||
const TimeoutInput = ({ value, onChange }: TimeoutInputProps) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Space>
|
||||
<InputNumber value={value} onChange={onChange} /> {t('common.s')}
|
||||
</Space>
|
||||
<div className="flex gap-2 items-center">
|
||||
<NumberInput value={value} onChange={onChange} /> {t('flow.seconds')}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const InvokeForm = ({ onValuesChange, form, node }: IOperatorForm) => {
|
||||
function InvokeForm({ node }: INextOperatorForm) {
|
||||
const { t } = useTranslation();
|
||||
const defaultValues = useFormValues(initialInvokeValues, node);
|
||||
|
||||
const form = useForm<FormSchemaType>({
|
||||
defaultValues,
|
||||
resolver: zodResolver(FormSchema),
|
||||
mode: 'onChange',
|
||||
});
|
||||
|
||||
const {
|
||||
visible,
|
||||
hideModal,
|
||||
showModal,
|
||||
ok,
|
||||
currentRecord,
|
||||
otherThanCurrentQuery,
|
||||
handleDeleteRecord,
|
||||
} = useEditVariableRecord({
|
||||
form,
|
||||
node,
|
||||
});
|
||||
|
||||
const variables = useWatch({ control: form.control, name: 'variables' });
|
||||
|
||||
useWatchFormChange(node?.id, form);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form
|
||||
name="basic"
|
||||
autoComplete="off"
|
||||
form={form}
|
||||
onValuesChange={onValuesChange}
|
||||
layout={'vertical'}
|
||||
<Form {...form}>
|
||||
<FormWrapper>
|
||||
<FormContainer>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="url"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('flow.url')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="http://" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="method"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('flow.method')}</FormLabel>
|
||||
<FormControl>
|
||||
<SelectWithSearch {...field} options={MethodOptions} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="timeout"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('flow.timeout')}</FormLabel>
|
||||
<FormControl>
|
||||
<TimeoutInput {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="headers"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('flow.headers')}</FormLabel>
|
||||
<FormControl>
|
||||
<Editor
|
||||
height={200}
|
||||
defaultLanguage="json"
|
||||
theme="vs-dark"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="proxy"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('flow.proxy')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="clean_html"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel tooltip={t('flow.cleanHtmlTip')}>
|
||||
{t('flow.cleanHtml')}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Switch
|
||||
onCheckedChange={field.onChange}
|
||||
checked={field.value}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{/* Create a hidden field to make Form instance record this */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={'variables'}
|
||||
render={() => <div></div>}
|
||||
/>
|
||||
</FormContainer>
|
||||
<Collapse
|
||||
title={<div>{t('flow.parameter')}</div>}
|
||||
rightContent={
|
||||
<Button
|
||||
variant={'ghost'}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
showModal();
|
||||
}}
|
||||
>
|
||||
<Form.Item name={'url'} label={t('flow.url')}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={'method'}
|
||||
label={t('flow.method')}
|
||||
initialValue={Method.GET}
|
||||
<Plus />
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Select options={MethodOptions} />
|
||||
</Form.Item>
|
||||
<Form.Item name={'timeout'} label={t('flow.timeout')}>
|
||||
<TimeoutInput></TimeoutInput>
|
||||
</Form.Item>
|
||||
<Form.Item name={'headers'} label={t('flow.headers')}>
|
||||
<Editor height={200} defaultLanguage="json" theme="vs-dark" />
|
||||
</Form.Item>
|
||||
<Form.Item name={'proxy'} label={t('flow.proxy')}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={'clean_html'}
|
||||
label={t('flow.cleanHtml')}
|
||||
tooltip={t('flow.cleanHtmlTip')}
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<DynamicVariablesForm node={node}></DynamicVariablesForm>
|
||||
<VariableTable
|
||||
data={variables}
|
||||
showModal={showModal}
|
||||
deleteRecord={handleDeleteRecord}
|
||||
nodeId={node?.id}
|
||||
></VariableTable>
|
||||
</Collapse>
|
||||
{visible && (
|
||||
<VariableDialog
|
||||
hideModal={hideModal}
|
||||
initialValue={currentRecord}
|
||||
otherThanCurrentQuery={otherThanCurrentQuery}
|
||||
submit={ok}
|
||||
></VariableDialog>
|
||||
)}
|
||||
</FormWrapper>
|
||||
</Form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default InvokeForm;
|
||||
export default memo(InvokeForm);
|
||||
|
||||
21
web/src/pages/agent/form/invoke-form/schema.ts
Normal file
21
web/src/pages/agent/form/invoke-form/schema.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const VariableFormSchema = z.object({
|
||||
key: z.string(),
|
||||
ref: z.string(),
|
||||
value: z.string(),
|
||||
});
|
||||
|
||||
export const FormSchema = z.object({
|
||||
url: z.string().url(),
|
||||
method: z.string(),
|
||||
timeout: z.number(),
|
||||
headers: z.string(),
|
||||
proxy: z.string().url(),
|
||||
clean_html: z.boolean(),
|
||||
variables: z.array(VariableFormSchema),
|
||||
});
|
||||
|
||||
export type FormSchemaType = z.infer<typeof FormSchema>;
|
||||
|
||||
export type VariableFormSchemaType = z.infer<typeof VariableFormSchema>;
|
||||
70
web/src/pages/agent/form/invoke-form/use-edit-variable.ts
Normal file
70
web/src/pages/agent/form/invoke-form/use-edit-variable.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import { useSetSelectedRecord } from '@/hooks/logic-hooks';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { UseFormReturn, useWatch } from 'react-hook-form';
|
||||
import { INextOperatorForm } from '../../interface';
|
||||
import { FormSchemaType, VariableFormSchemaType } from './schema';
|
||||
|
||||
export const useEditVariableRecord = ({
|
||||
form,
|
||||
}: INextOperatorForm & { form: UseFormReturn<FormSchemaType> }) => {
|
||||
const { setRecord, currentRecord } =
|
||||
useSetSelectedRecord<VariableFormSchemaType>();
|
||||
|
||||
const { visible, hideModal, showModal } = useSetModalState();
|
||||
const [index, setIndex] = useState(-1);
|
||||
const variables = useWatch({
|
||||
control: form.control,
|
||||
name: 'variables',
|
||||
});
|
||||
|
||||
const otherThanCurrentQuery = useMemo(() => {
|
||||
return variables.filter((item, idx) => idx !== index);
|
||||
}, [index, variables]);
|
||||
|
||||
const handleEditRecord = useCallback(
|
||||
(record: VariableFormSchemaType) => {
|
||||
const variables = form?.getValues('variables') || [];
|
||||
|
||||
const nextVaribales =
|
||||
index > -1
|
||||
? variables.toSpliced(index, 1, record)
|
||||
: [...variables, record];
|
||||
|
||||
form.setValue('variables', nextVaribales);
|
||||
|
||||
hideModal();
|
||||
},
|
||||
[form, hideModal, index],
|
||||
);
|
||||
|
||||
const handleShowModal = useCallback(
|
||||
(idx?: number, record?: VariableFormSchemaType) => {
|
||||
setIndex(idx ?? -1);
|
||||
setRecord(record ?? ({} as VariableFormSchemaType));
|
||||
showModal();
|
||||
},
|
||||
[setRecord, showModal],
|
||||
);
|
||||
|
||||
const handleDeleteRecord = useCallback(
|
||||
(idx: number) => {
|
||||
const variables = form?.getValues('variables') || [];
|
||||
const nextVariables = variables.filter((item, index) => index !== idx);
|
||||
|
||||
form.setValue('variables', nextVariables);
|
||||
},
|
||||
[form],
|
||||
);
|
||||
|
||||
return {
|
||||
ok: handleEditRecord,
|
||||
currentRecord,
|
||||
setRecord,
|
||||
visible,
|
||||
hideModal,
|
||||
showModal: handleShowModal,
|
||||
otherThanCurrentQuery,
|
||||
handleDeleteRecord,
|
||||
};
|
||||
};
|
||||
143
web/src/pages/agent/form/invoke-form/variable-dialog.tsx
Normal file
143
web/src/pages/agent/form/invoke-form/variable-dialog.tsx
Normal file
@ -0,0 +1,143 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { useEffect } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { z } from 'zod';
|
||||
import { QueryVariable } from '../components/query-variable';
|
||||
import { VariableFormSchemaType } from './schema';
|
||||
|
||||
type ModalFormProps = {
|
||||
initialValue: VariableFormSchemaType;
|
||||
otherThanCurrentQuery: VariableFormSchemaType[];
|
||||
submit(values: any): void;
|
||||
};
|
||||
|
||||
const FormId = 'BeginParameterForm';
|
||||
|
||||
function VariableForm({
|
||||
initialValue,
|
||||
otherThanCurrentQuery,
|
||||
submit,
|
||||
}: ModalFormProps) {
|
||||
const { t } = useTranslation();
|
||||
const FormSchema = z.object({
|
||||
key: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1)
|
||||
.refine(
|
||||
(value) =>
|
||||
!value || !otherThanCurrentQuery.some((x) => x.key === value),
|
||||
{ message: 'The key cannot be repeated!' },
|
||||
),
|
||||
ref: z.string(),
|
||||
value: z.string(),
|
||||
});
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
mode: 'onChange',
|
||||
defaultValues: {
|
||||
key: '',
|
||||
value: '',
|
||||
ref: '',
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEmpty(initialValue)) {
|
||||
form.reset(initialValue);
|
||||
}
|
||||
}, [form, initialValue]);
|
||||
|
||||
function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||
submit(data);
|
||||
}
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
id={FormId}
|
||||
className="space-y-5"
|
||||
autoComplete="off"
|
||||
>
|
||||
<FormField
|
||||
name="key"
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('flow.key')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} autoComplete="off" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<QueryVariable name="ref" label={t('flow.ref')}></QueryVariable>
|
||||
<FormField
|
||||
name="value"
|
||||
control={form.control}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('flow.value')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
export function VariableDialog({
|
||||
initialValue,
|
||||
hideModal,
|
||||
otherThanCurrentQuery,
|
||||
submit,
|
||||
}: ModalFormProps & IModalProps<VariableFormSchemaType>) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Dialog open onOpenChange={hideModal}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t('flow.variableSettings')}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<VariableForm
|
||||
initialValue={initialValue}
|
||||
otherThanCurrentQuery={otherThanCurrentQuery}
|
||||
submit={submit}
|
||||
></VariableForm>
|
||||
<DialogFooter>
|
||||
<Button type="submit" form={FormId}>
|
||||
Confirm
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
199
web/src/pages/agent/form/invoke-form/variable-table.tsx
Normal file
199
web/src/pages/agent/form/invoke-form/variable-table.tsx
Normal file
@ -0,0 +1,199 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
SortingState,
|
||||
VisibilityState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from '@tanstack/react-table';
|
||||
import { Pencil, Trash2 } from 'lucide-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { TableEmpty } from '@/components/table-skeleton';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/components/ui/table';
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from '@/components/ui/tooltip';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useGetVariableLabelByValue } from '../../hooks/use-get-begin-query';
|
||||
import { VariableFormSchemaType } from './schema';
|
||||
|
||||
interface IProps {
|
||||
data: VariableFormSchemaType[];
|
||||
deleteRecord(index: number): void;
|
||||
showModal(index: number, record: VariableFormSchemaType): void;
|
||||
nodeId?: string;
|
||||
}
|
||||
|
||||
export function VariableTable({
|
||||
data = [],
|
||||
deleteRecord,
|
||||
showModal,
|
||||
nodeId,
|
||||
}: IProps) {
|
||||
const { t } = useTranslation();
|
||||
const getLabel = useGetVariableLabelByValue(nodeId!);
|
||||
|
||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||
[],
|
||||
);
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
React.useState<VisibilityState>({});
|
||||
|
||||
const columns: ColumnDef<VariableFormSchemaType>[] = [
|
||||
{
|
||||
accessorKey: 'key',
|
||||
header: 'key',
|
||||
meta: { cellClassName: 'max-w-30' },
|
||||
cell: ({ row }) => {
|
||||
const key: string = row.getValue('key');
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="truncate">{key}</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{key}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'ref',
|
||||
header: t('flow.ref'),
|
||||
meta: { cellClassName: 'max-w-30' },
|
||||
cell: ({ row }) => {
|
||||
const ref: string = row.getValue('ref');
|
||||
const label = getLabel(ref);
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="truncate">{label}</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{label}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'value',
|
||||
header: t('flow.value'),
|
||||
cell: ({ row }) => <div>{row.getValue('value')}</div>,
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
enableHiding: false,
|
||||
header: t('common.action'),
|
||||
cell: ({ row }) => {
|
||||
const record = row.original;
|
||||
const idx = row.index;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
className="bg-transparent text-foreground hover:bg-muted-foreground hover:text-foreground"
|
||||
onClick={() => showModal(idx, record)}
|
||||
>
|
||||
<Pencil />
|
||||
</Button>
|
||||
<Button
|
||||
className="bg-transparent text-foreground hover:bg-muted-foreground hover:text-foreground"
|
||||
onClick={() => deleteRecord(idx)}
|
||||
>
|
||||
<Trash2 />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
columnVisibility,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="rounded-md border">
|
||||
<Table rootClassName="rounded-md">
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext(),
|
||||
)}
|
||||
</TableHead>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && 'selected'}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell
|
||||
key={cell.id}
|
||||
className={cn(cell.column.columnDef.meta?.cellClassName)}
|
||||
>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext(),
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableEmpty columnsLength={columns.length}></TableEmpty>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
import { FormContainer } from '@/components/form-container';
|
||||
import { TopNFormField } from '@/components/top-n-item';
|
||||
import {
|
||||
Form,
|
||||
@ -9,21 +10,35 @@ import {
|
||||
} from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { memo } from 'react';
|
||||
import { useForm, useFormContext } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import { initialPubMedValues } from '../../constant';
|
||||
import { useFormValues } from '../../hooks/use-form-values';
|
||||
import { useWatchFormChange } from '../../hooks/use-watch-form-change';
|
||||
import { INextOperatorForm } from '../../interface';
|
||||
import { DynamicInputVariable } from '../components/next-dynamic-input-variable';
|
||||
import { buildOutputList } from '../../utils/build-output-list';
|
||||
import { FormWrapper } from '../components/form-wrapper';
|
||||
import { Output } from '../components/output';
|
||||
import { QueryVariable } from '../components/query-variable';
|
||||
|
||||
const PubMedForm = ({ form, node }: INextOperatorForm) => {
|
||||
export const PubMedFormPartialSchema = {
|
||||
top_n: z.number(),
|
||||
email: z.string().email(),
|
||||
};
|
||||
|
||||
export const FormSchema = z.object({
|
||||
...PubMedFormPartialSchema,
|
||||
query: z.string(),
|
||||
});
|
||||
|
||||
export function PubMedFormWidgets() {
|
||||
const form = useFormContext();
|
||||
const { t } = useTranslate('flow');
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form
|
||||
className="space-y-6"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
>
|
||||
<DynamicInputVariable node={node}></DynamicInputVariable>
|
||||
<>
|
||||
<TopNFormField></TopNFormField>
|
||||
<FormField
|
||||
control={form.control}
|
||||
@ -38,9 +53,38 @@ const PubMedForm = ({ form, node }: INextOperatorForm) => {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const outputList = buildOutputList(initialPubMedValues.outputs);
|
||||
|
||||
function PubMedForm({ node }: INextOperatorForm) {
|
||||
const defaultValues = useFormValues(initialPubMedValues, node);
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
defaultValues,
|
||||
resolver: zodResolver(FormSchema),
|
||||
mode: 'onChange',
|
||||
});
|
||||
|
||||
useWatchFormChange(node?.id, form);
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<FormWrapper>
|
||||
<FormContainer>
|
||||
<QueryVariable></QueryVariable>
|
||||
</FormContainer>
|
||||
<FormContainer>
|
||||
<PubMedFormWidgets></PubMedFormWidgets>
|
||||
</FormContainer>
|
||||
</FormWrapper>
|
||||
<div className="p-5">
|
||||
<Output list={outputList}></Output>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default PubMedForm;
|
||||
export default memo(PubMedForm);
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
import { Operator } from '../../constant';
|
||||
import AkShareForm from '../akshare-form';
|
||||
import DeepLForm from '../deepl-form';
|
||||
import GithubForm from '../github-form';
|
||||
import PubMedForm from '../pubmed-form';
|
||||
import ArXivForm from './arxiv-form';
|
||||
import BingForm from './bing-form';
|
||||
import CrawlerForm from './crawler-form';
|
||||
import DuckDuckGoForm from './duckduckgo-form';
|
||||
import EmailForm from './email-form';
|
||||
import ExeSQLForm from './exesql-form';
|
||||
import GithubForm from './github-form';
|
||||
import GoogleForm from './google-form';
|
||||
import GoogleScholarForm from './google-scholar-form';
|
||||
import PubMedForm from './pubmed-form';
|
||||
import RetrievalForm from './retrieval-form';
|
||||
import TavilyForm from './tavily-form';
|
||||
import WikipediaForm from './wikipedia-form';
|
||||
|
||||
36
web/src/pages/agent/form/tool-form/github-form/index.tsx
Normal file
36
web/src/pages/agent/form/tool-form/github-form/index.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import { FormContainer } from '@/components/form-container';
|
||||
import { TopNFormField } from '@/components/top-n-item';
|
||||
import { Form } from '@/components/ui/form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { memo } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import { FormWrapper } from '../../components/form-wrapper';
|
||||
import { useValues } from '../use-values';
|
||||
import { useWatchFormChange } from '../use-watch-change';
|
||||
|
||||
function GithubForm() {
|
||||
const values = useValues();
|
||||
|
||||
const FormSchema = z.object({ query: z.string() });
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
defaultValues: values,
|
||||
resolver: zodResolver(FormSchema),
|
||||
mode: 'onChange',
|
||||
});
|
||||
|
||||
useWatchFormChange(form);
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<FormWrapper>
|
||||
<FormContainer>
|
||||
<TopNFormField></TopNFormField>
|
||||
</FormContainer>
|
||||
</FormWrapper>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(GithubForm);
|
||||
36
web/src/pages/agent/form/tool-form/pubmed-form/index.tsx
Normal file
36
web/src/pages/agent/form/tool-form/pubmed-form/index.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import { FormContainer } from '@/components/form-container';
|
||||
import { Form } from '@/components/ui/form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { memo } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import { FormWrapper } from '../../components/form-wrapper';
|
||||
import { PubMedFormPartialSchema, PubMedFormWidgets } from '../../pubmed-form';
|
||||
import { useValues } from '../use-values';
|
||||
import { useWatchFormChange } from '../use-watch-change';
|
||||
|
||||
function PubMedForm() {
|
||||
const values = useValues();
|
||||
|
||||
const FormSchema = z.object(PubMedFormPartialSchema);
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
defaultValues: values,
|
||||
resolver: zodResolver(FormSchema),
|
||||
mode: 'onChange',
|
||||
});
|
||||
|
||||
useWatchFormChange(form);
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<FormWrapper>
|
||||
<FormContainer>
|
||||
<PubMedFormWidgets></PubMedFormWidgets>
|
||||
</FormContainer>
|
||||
</FormWrapper>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(PubMedForm);
|
||||
@ -7,7 +7,6 @@ import {
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
@ -21,6 +20,7 @@ import { INextOperatorForm } from '../../interface';
|
||||
import { buildOutputList } from '../../utils/build-output-list';
|
||||
import { FormWrapper } from '../components/form-wrapper';
|
||||
import { Output } from '../components/output';
|
||||
import { QueryVariable } from '../components/query-variable';
|
||||
|
||||
export const YahooFinanceFormPartialSchema = {
|
||||
info: z.boolean(),
|
||||
@ -106,19 +106,12 @@ const YahooFinanceForm = ({ node }: INextOperatorForm) => {
|
||||
<Form {...form}>
|
||||
<FormWrapper>
|
||||
<FormContainer>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`stock_code`}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('stockCode')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field}></Input>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<QueryVariable
|
||||
name="stock_code"
|
||||
label={t('stockCode')}
|
||||
></QueryVariable>
|
||||
</FormContainer>
|
||||
<FormContainer>
|
||||
<YahooFinanceFormWidgets></YahooFinanceFormWidgets>
|
||||
</FormContainer>
|
||||
</FormWrapper>
|
||||
|
||||
@ -48,6 +48,10 @@ export function useAgentToolInitialValues() {
|
||||
return omit(initialValues, 'query', 'outputs');
|
||||
case Operator.ArXiv:
|
||||
return pick(initialValues, 'top_n', 'sort_by');
|
||||
case Operator.PubMed:
|
||||
return pick(initialValues, 'top_n', 'email');
|
||||
case Operator.GitHub:
|
||||
return pick(initialValues, 'top_n');
|
||||
|
||||
default:
|
||||
return initialValues;
|
||||
|
||||
Reference in New Issue
Block a user