mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-22 06:06:40 +08:00
### What problem does this PR solve? Feat: Allow chat to use meta data #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -8,10 +8,25 @@ import classNames from 'classnames';
|
||||
import { useCallback } from 'react';
|
||||
import { ISegmentedContentProps } from '../interface';
|
||||
|
||||
import { DatasetMetadata } from '../constants';
|
||||
import styles from './index.less';
|
||||
import { MetadataFilterConditions } from './metadata-filter-conditions';
|
||||
|
||||
const emptyResponseField = ['prompt_config', 'empty_response'];
|
||||
|
||||
const MetadataOptions = Object.values(DatasetMetadata).map((x) => {
|
||||
let value: DatasetMetadata | boolean = x;
|
||||
if (x === DatasetMetadata.Disabled) {
|
||||
value = false;
|
||||
} else if (x === DatasetMetadata.Automatic) {
|
||||
value = true;
|
||||
}
|
||||
return {
|
||||
value,
|
||||
label: x,
|
||||
};
|
||||
});
|
||||
|
||||
const AssistantSetting = ({
|
||||
show,
|
||||
form,
|
||||
@ -20,6 +35,11 @@ const AssistantSetting = ({
|
||||
const { t } = useTranslate('chat');
|
||||
const { data } = useFetchTenantInfo(true);
|
||||
|
||||
const metadata = Form.useWatch(['meta_data_filter', 'auto'], form);
|
||||
const kbIds = Form.useWatch(['kb_ids'], form);
|
||||
|
||||
const hasKnowledge = Array.isArray(kbIds) && kbIds.length > 0;
|
||||
|
||||
const handleChange = useCallback(() => {
|
||||
const kbIds = form.getFieldValue('kb_ids');
|
||||
const emptyResponse = form.getFieldValue(emptyResponseField);
|
||||
@ -153,6 +173,24 @@ const AssistantSetting = ({
|
||||
required={false}
|
||||
onChange={handleChange}
|
||||
></KnowledgeBaseItem>
|
||||
{hasKnowledge && (
|
||||
<Form.Item
|
||||
label={t('metadata')}
|
||||
name={['meta_data_filter', 'auto']}
|
||||
tooltip={t('metadataTip')}
|
||||
>
|
||||
<Select options={MetadataOptions} />
|
||||
</Form.Item>
|
||||
)}
|
||||
{hasKnowledge && metadata === DatasetMetadata.Manual && (
|
||||
<Form.Item
|
||||
label={t('conditions')}
|
||||
tooltip={t('ttsTip')}
|
||||
initialValue={false}
|
||||
>
|
||||
<MetadataFilterConditions kbIds={kbIds}></MetadataFilterConditions>
|
||||
</Form.Item>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
@ -0,0 +1,84 @@
|
||||
import { useFetchKnowledgeMetadata } from '@/hooks/use-knowledge-request';
|
||||
import { SwitchOperatorOptions } from '@/pages/agent/constant';
|
||||
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
Button,
|
||||
Dropdown,
|
||||
Empty,
|
||||
Form,
|
||||
FormListOperation,
|
||||
Input,
|
||||
Select,
|
||||
Space,
|
||||
} from 'antd';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export function MetadataFilterConditions({ kbIds }: { kbIds: string[] }) {
|
||||
const metadata = useFetchKnowledgeMetadata(kbIds);
|
||||
|
||||
const renderItems = useCallback(
|
||||
(add: FormListOperation['add']) => {
|
||||
if (Object.keys(metadata.data).length === 0) {
|
||||
return [{ key: 'noData', label: <Empty></Empty> }];
|
||||
}
|
||||
return Object.keys(metadata.data).map((key) => {
|
||||
return {
|
||||
key,
|
||||
onClick: () => {
|
||||
add({
|
||||
key,
|
||||
value: '',
|
||||
op: SwitchOperatorOptions[0].value,
|
||||
});
|
||||
},
|
||||
label: key,
|
||||
};
|
||||
});
|
||||
},
|
||||
[metadata],
|
||||
);
|
||||
return (
|
||||
<Form.List name={['meta_data_filter', 'manual']}>
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
{fields.map(({ key, name, ...restField }) => (
|
||||
<Space
|
||||
key={key}
|
||||
style={{ display: 'flex', marginBottom: 8 }}
|
||||
align="baseline"
|
||||
>
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'key']}
|
||||
rules={[{ required: true, message: 'Missing first name' }]}
|
||||
>
|
||||
<Input placeholder="First Name" />
|
||||
</Form.Item>
|
||||
<Form.Item {...restField} name={[name, 'op']} className="w-20">
|
||||
<Select
|
||||
options={SwitchOperatorOptions}
|
||||
popupMatchSelectWidth={false}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'value']}
|
||||
rules={[{ required: true, message: 'Missing last name' }]}
|
||||
>
|
||||
<Input placeholder="Last Name" />
|
||||
</Form.Item>
|
||||
<MinusCircleOutlined onClick={() => remove(name)} />
|
||||
</Space>
|
||||
))}
|
||||
<Form.Item>
|
||||
<Dropdown trigger={['click']} menu={{ items: renderItems(add) }}>
|
||||
<Button type="dashed" block icon={<PlusOutlined />}>
|
||||
Add Condition
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
);
|
||||
}
|
||||
@ -1 +1,7 @@
|
||||
export const EmptyConversationId = 'empty';
|
||||
|
||||
export enum DatasetMetadata {
|
||||
Disabled = 'disabled',
|
||||
Automatic = 'automatic',
|
||||
Manual = 'manual',
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { PanelRightClose } from 'lucide-react';
|
||||
import { FormProvider, useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import ChatBasicSetting from './chat-basic-settings';
|
||||
@ -7,7 +8,8 @@ import { ChatModelSettings } from './chat-model-settings';
|
||||
import { ChatPromptEngine } from './chat-prompt-engine';
|
||||
import { useChatSettingSchema } from './use-chat-setting-schema';
|
||||
|
||||
export function ChatSettings() {
|
||||
type ChatSettingsProps = { switchSettingVisible(): void };
|
||||
export function ChatSettings({ switchSettingVisible }: ChatSettingsProps) {
|
||||
const formSchema = useChatSettingSchema();
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
@ -33,11 +35,18 @@ export function ChatSettings() {
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="py-6">
|
||||
<section className="p-5 w-[400px] max-w-[20%]">
|
||||
<div className="flex justify-between items-center text-base">
|
||||
Chat Settings
|
||||
<PanelRightClose
|
||||
className="size-4 cursor-pointer"
|
||||
onClick={switchSettingVisible}
|
||||
/>
|
||||
</div>
|
||||
<FormProvider {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-6 overflow-auto max-h-[88vh] pr-4"
|
||||
className="space-y-6 overflow-auto max-h-[87vh] pr-4"
|
||||
>
|
||||
<ChatBasicSetting></ChatBasicSetting>
|
||||
<ChatPromptEngine></ChatPromptEngine>
|
||||
|
||||
@ -23,7 +23,7 @@ interface IProps {
|
||||
export function ChatBox({ controller }: IProps) {
|
||||
const {
|
||||
value,
|
||||
scrollRef,
|
||||
// scrollRef,
|
||||
messageContainerRef,
|
||||
sendLoading,
|
||||
derivedMessages,
|
||||
@ -43,8 +43,8 @@ export function ChatBox({ controller }: IProps) {
|
||||
const sendDisabled = useSendButtonDisabled(value);
|
||||
|
||||
return (
|
||||
<section className="border-x flex flex-col p-5 w-full">
|
||||
<div ref={messageContainerRef} className="flex-1 overflow-auto">
|
||||
<section className="border-x flex flex-col p-5 flex-1 min-w-0">
|
||||
<div ref={messageContainerRef} className="flex-1 overflow-auto min-h-0">
|
||||
<div className="w-full">
|
||||
{derivedMessages?.map((message, i) => {
|
||||
return (
|
||||
@ -75,7 +75,7 @@ export function ChatBox({ controller }: IProps) {
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div ref={scrollRef} />
|
||||
{/* <div ref={scrollRef} /> */}
|
||||
</div>
|
||||
<NextMessageInput
|
||||
disabled={disabled}
|
||||
|
||||
@ -7,10 +7,12 @@ import {
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from '@/components/ui/breadcrumb';
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import { useFetchDialog } from '@/hooks/use-chat-request';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useHandleClickConversationCard } from '../hooks/use-click-card';
|
||||
import { ChatSettings } from './app-settings/chat-settings';
|
||||
import { ChatBox } from './chat-box';
|
||||
import { Sessions } from './sessions';
|
||||
|
||||
@ -20,6 +22,8 @@ export default function Chat() {
|
||||
const { t } = useTranslation();
|
||||
const { handleConversationCardClick, controller } =
|
||||
useHandleClickConversationCard();
|
||||
const { visible: settingVisible, switchVisible: switchSettingVisible } =
|
||||
useSetModalState(true);
|
||||
|
||||
return (
|
||||
<section className="h-full flex flex-col">
|
||||
@ -39,10 +43,18 @@ export default function Chat() {
|
||||
</Breadcrumb>
|
||||
</PageHeader>
|
||||
<div className="flex flex-1 min-h-0">
|
||||
<Sessions
|
||||
handleConversationCardClick={handleConversationCardClick}
|
||||
></Sessions>
|
||||
<ChatBox controller={controller}></ChatBox>
|
||||
<div className="flex flex-1 min-w-0">
|
||||
<Sessions
|
||||
handleConversationCardClick={handleConversationCardClick}
|
||||
switchSettingVisible={switchSettingVisible}
|
||||
></Sessions>
|
||||
<ChatBox controller={controller}></ChatBox>
|
||||
</div>
|
||||
{settingVisible && (
|
||||
<ChatSettings
|
||||
switchSettingVisible={switchSettingVisible}
|
||||
></ChatSettings>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
@ -1,21 +1,30 @@
|
||||
import { MoreButton } from '@/components/more-button';
|
||||
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { useGetChatSearchParams } from '@/hooks/use-chat-request';
|
||||
import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import {
|
||||
useFetchDialog,
|
||||
useGetChatSearchParams,
|
||||
} from '@/hooks/use-chat-request';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Plus } from 'lucide-react';
|
||||
import { PanelLeftClose, PanelRightClose, Plus } from 'lucide-react';
|
||||
import { useCallback } from 'react';
|
||||
import { useHandleClickConversationCard } from '../hooks/use-click-card';
|
||||
import { useSelectDerivedConversationList } from '../hooks/use-select-conversation-list';
|
||||
import { ChatSettingSheet } from './app-settings/chat-settings-sheet';
|
||||
|
||||
type SessionProps = Pick<
|
||||
ReturnType<typeof useHandleClickConversationCard>,
|
||||
'handleConversationCardClick'
|
||||
>;
|
||||
export function Sessions({ handleConversationCardClick }: SessionProps) {
|
||||
> & { switchSettingVisible(): void };
|
||||
export function Sessions({
|
||||
handleConversationCardClick,
|
||||
switchSettingVisible,
|
||||
}: SessionProps) {
|
||||
const { list: conversationList, addTemporaryConversation } =
|
||||
useSelectDerivedConversationList();
|
||||
const { data } = useFetchDialog();
|
||||
const { visible, switchVisible } = useSetModalState(true);
|
||||
|
||||
const handleCardClick = useCallback(
|
||||
(conversationId: string, isNew: boolean) => () => {
|
||||
@ -26,9 +35,32 @@ export function Sessions({ handleConversationCardClick }: SessionProps) {
|
||||
|
||||
const { conversationId } = useGetChatSearchParams();
|
||||
|
||||
if (!visible) {
|
||||
return (
|
||||
<PanelRightClose
|
||||
className="cursor-pointer size-4 mt-8"
|
||||
onClick={switchVisible}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="p-6 w-[400px] max-w-[20%] flex flex-col">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<section className="flex items-center text-base justify-between gap-2">
|
||||
<div className="flex gap-3 items-center min-w-0">
|
||||
<RAGFlowAvatar
|
||||
avatar={data.icon}
|
||||
name={data.name}
|
||||
className="size-8"
|
||||
></RAGFlowAvatar>
|
||||
<span className="flex-1 truncate">{data.name}</span>
|
||||
</div>
|
||||
<PanelLeftClose
|
||||
className="cursor-pointer size-4"
|
||||
onClick={switchVisible}
|
||||
/>
|
||||
</section>
|
||||
<div className="flex justify-between items-center mb-4 pt-10">
|
||||
<span className="text-xl font-bold">Conversations</span>
|
||||
<Button variant={'ghost'} onClick={addTemporaryConversation}>
|
||||
<Plus></Plus>
|
||||
@ -51,9 +83,9 @@ export function Sessions({ handleConversationCardClick }: SessionProps) {
|
||||
))}
|
||||
</div>
|
||||
<div className="py-2">
|
||||
<ChatSettingSheet>
|
||||
<Button className="w-full">Chat Settings</Button>
|
||||
</ChatSettingSheet>
|
||||
<Button className="w-full" onClick={switchSettingVisible}>
|
||||
Chat Settings
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user