Explorar el Código

Feat: In a dialog message, users can enter different types of data #3221 (#8583)

### What problem does this PR solve?

Feat: In a dialog message, users can enter different types of data #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
tags/v0.20.0
balibabu hace 4 meses
padre
commit
d620432e3b
No account linked to committer's email address

+ 43
- 3
web/src/pages/agent/chat/box.tsx Ver fichero

@@ -11,8 +11,14 @@ import PdfDrawer from '@/components/pdf-drawer';
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
import { useFetchAgent } from '@/hooks/use-agent-request';
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
import { Message } from '@/interfaces/database/chat';
import { buildMessageUuidWithRole } from '@/utils/chat';
import { InputForm } from './input-form';
import { get } from 'lodash';
import { useCallback } from 'react';
import { useParams } from 'umi';
import DebugContent from '../debug-content';
import { BeginQuery } from '../interface';
import { buildBeginQueryWithObject } from '../utils';

const AgentChatBox = () => {
const {
@@ -25,7 +31,7 @@ const AgentChatBox = () => {
derivedMessages,
reference,
stopOutputMessage,
send,
sendFormMessage,
} = useSendNextMessage();

const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
@@ -33,6 +39,35 @@ const AgentChatBox = () => {
useGetFileIcon();
const { data: userInfo } = useFetchUserInfo();
const { data: canvasInfo } = useFetchAgent();
const { id: canvasId } = useParams();

const getInputs = useCallback((message: Message) => {
return get(message, 'data.inputs', {}) as Record<string, BeginQuery>;
}, []);

const buildInputList = useCallback(
(message: Message) => {
return Object.entries(getInputs(message)).map(([key, val]) => {
return {
...val,
key,
};
});
},
[getInputs],
);

const handleOk = useCallback(
(message: Message) => (values: BeginQuery[]) => {
const inputs = getInputs(message);
const nextInputs = buildBeginQueryWithObject(inputs, values);
sendFormMessage({
inputs: nextInputs,
id: canvasId,
});
},
[canvasId, getInputs, sendFormMessage],
);

return (
<>
@@ -62,7 +97,12 @@ const AgentChatBox = () => {
showLikeButton={false}
sendLoading={sendLoading}
>
<InputForm send={send} message={message}></InputForm>
<DebugContent
parameters={buildInputList(message)}
ok={handleOk(message)}
isNext={false}
btnText={'Submit'}
></DebugContent>
</MessageItem>
);
})}

+ 15
- 0
web/src/pages/agent/chat/hooks.ts Ver fichero

@@ -22,6 +22,7 @@ import { useParams } from 'umi';
import { v4 as uuid } from 'uuid';
import { BeginId } from '../constant';
import { AgentChatLogContext } from '../context';
import { BeginQuery } from '../interface';
import useGraphStore from '../store';
import { receiveMessageError } from '../utils';

@@ -176,6 +177,19 @@ export const useSendNextMessage = () => {
});
}, [value, done, addNewestOneQuestion, setValue, handleSendMessage]);

const sendFormMessage = useCallback(
(body: { id?: string; inputs: Record<string, BeginQuery> }) => {
send(body);
addNewestOneQuestion({
content: Object.entries(body.inputs)
.map(([key, val]) => `${key}: ${val.value}`)
.join('<br/>'),
role: MessageType.User,
});
},
[addNewestOneQuestion, send],
);

useEffect(() => {
if (prologue) {
addNewestOneAnswer({
@@ -200,5 +214,6 @@ export const useSendNextMessage = () => {
removeMessageById,
stopOutputMessage,
send,
sendFormMessage,
};
};

+ 31
- 69
web/src/pages/agent/debug-content/index.tsx Ver fichero

@@ -12,11 +12,8 @@ import { Input } from '@/components/ui/input';
import { RAGFlowSelect } from '@/components/ui/select';
import { Switch } from '@/components/ui/switch';
import { Textarea } from '@/components/ui/textarea';
import { useSetModalState } from '@/hooks/common-hooks';
import { useSetSelectedRecord } from '@/hooks/logic-hooks';
import { zodResolver } from '@hookform/resolvers/zod';
import { UploadChangeParam, UploadFile } from 'antd/es/upload';
import React, { useCallback, useMemo, useState } from 'react';
import React, { ReactNode, useCallback, useMemo } from 'react';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
@@ -44,6 +41,7 @@ interface IProps {
isNext?: boolean;
loading?: boolean;
submitButtonDisabled?: boolean;
btnText?: ReactNode;
}

const values = {};
@@ -54,76 +52,45 @@ const DebugContent = ({
isNext = true,
loading = false,
submitButtonDisabled = false,
btnText,
}: IProps) => {
const { t } = useTranslation();

const FormSchema = useMemo(() => {
const obj = parameters.reduce((pre, cur, idx) => {
const type = cur.type;
let fieldSchema;
if (StringFields.some((x) => x === type)) {
fieldSchema = z.string();
} else if (type === BeginQueryType.Boolean) {
fieldSchema = z.boolean();
} else if (type === BeginQueryType.Integer) {
fieldSchema = z.coerce.number();
} else {
fieldSchema = z.instanceof(File);
}
const obj = parameters.reduce<Record<string, z.ZodType>>(
(pre, cur, idx) => {
const type = cur.type;
let fieldSchema;
if (StringFields.some((x) => x === type)) {
fieldSchema = z.string();
} else if (type === BeginQueryType.Boolean) {
fieldSchema = z.boolean();
} else if (type === BeginQueryType.Integer) {
fieldSchema = z.coerce.number();
} else {
fieldSchema = z.instanceof(File);
}

if (cur.optional) {
fieldSchema.optional();
}
if (cur.optional) {
fieldSchema.optional();
}

pre[idx.toString()] = fieldSchema;
pre[idx.toString()] = fieldSchema;

return pre;
}, {});
return pre;
},
{},
);

return z.object(obj);
}, [parameters]);

const form = useForm({
const form = useForm<z.infer<typeof FormSchema>>({
defaultValues: values,
resolver: zodResolver(FormSchema),
});

const {
visible,
hideModal: hidePopover,
switchVisible,
showModal: showPopover,
} = useSetModalState();

const { setRecord, currentRecord } = useSetSelectedRecord<number>();
// const { submittable } = useHandleSubmittable(form);
const submittable = true;
const [isUploading, setIsUploading] = useState(false);

const handleShowPopover = useCallback(
(idx: number) => () => {
setRecord(idx);
showPopover();
},
[setRecord, showPopover],
);

const normFile = (e: any) => {
if (Array.isArray(e)) {
return e;
}
return e?.fileList;
};

const onChange = useCallback(
(optional: boolean) =>
({ fileList }: UploadChangeParam<UploadFile>) => {
if (!optional) {
setIsUploading(fileList.some((x) => x.status === 'uploading'));
}
},
[],
);

const renderWidget = useCallback(
(q: BeginQuery, idx: string) => {
@@ -132,14 +99,6 @@ const DebugContent = ({
label: q.name ?? q.key,
name: idx,
};
if (q.optional === false) {
props.rules = [{ required: true }];
}

// const urlList: { url: string; result: string }[] =
// form.getFieldValue(idx) || [];

const urlList: { url: string; result: string }[] = [];

const BeginQueryTypeMap = {
[BeginQueryType.Line]: (
@@ -183,7 +142,10 @@ const DebugContent = ({
<RAGFlowSelect
allowClear
options={
q.options?.map((x) => ({ label: x, value: x })) ?? []
q.options?.map((x) => ({
label: x,
value: x as string,
})) ?? []
}
{...field}
></RAGFlowSelect>
@@ -295,10 +257,10 @@ const DebugContent = ({
<ButtonLoading
type="submit"
loading={loading}
disabled={!submittable || isUploading || submitButtonDisabled}
disabled={!submittable || submitButtonDisabled}
className="w-full"
>
{t(isNext ? 'common.next' : 'flow.run')}
{btnText || t(isNext ? 'common.next' : 'flow.run')}
</ButtonLoading>
</form>
</Form>

+ 2
- 10
web/src/pages/agent/run-sheet/index.tsx Ver fichero

@@ -14,6 +14,7 @@ import { useGetBeginNodeDataQuery } from '../hooks/use-get-begin-query';
import { useSaveGraphBeforeOpeningDebugDrawer } from '../hooks/use-save-graph';
import { BeginQuery } from '../interface';
import useGraphStore from '../store';
import { buildBeginQueryWithObject } from '../utils';

const RunSheet = ({
hideModal,
@@ -34,16 +35,7 @@ const RunSheet = ({
const beginNode = getNode(BeginId);
const inputs: Record<string, BeginQuery> = beginNode?.data.form.inputs;

const nextInputs = Object.keys(inputs).reduce<Record<string, BeginQuery>>(
(pre, key) => {
const item = nextValues.find((x) => x.key === key);
if (item) {
pre[key] = { ...item };
}
return pre;
},
{},
);
const nextInputs = buildBeginQueryWithObject(inputs, nextValues);

const currentNodes = updateNodeForm(BeginId, nextInputs, ['inputs']);
handleRun(currentNodes);

+ 19
- 1
web/src/pages/agent/utils.ts Ver fichero

@@ -19,7 +19,7 @@ import {
NodeMap,
Operator,
} from './constant';
import { IPosition } from './interface';
import { BeginQuery, IPosition } from './interface';

const buildEdges = (
operatorIds: string[],
@@ -537,3 +537,21 @@ export function mapEdgeMouseEvent(

return nextEdges;
}

export function buildBeginQueryWithObject(
inputs: Record<string, BeginQuery>,
values: BeginQuery[],
) {
const nextInputs = Object.keys(inputs).reduce<Record<string, BeginQuery>>(
(pre, key) => {
const item = values.find((x) => x.key === key);
if (item) {
pre[key] = { ...item };
}
return pre;
},
{},
);

return nextInputs;
}

Cargando…
Cancelar
Guardar