mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### What problem does this PR solve? Feat: Allow agent operators to select speech-to-text models #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -38,9 +38,12 @@ export const LargeModelFilterFormSchema = {
|
|||||||
llm_filter: z.string().optional(),
|
llm_filter: z.string().optional(),
|
||||||
};
|
};
|
||||||
|
|
||||||
type LargeModelFormFieldProps = Pick<NextInnerLLMSelectProps, 'showTTSModel'>;
|
type LargeModelFormFieldProps = Pick<
|
||||||
|
NextInnerLLMSelectProps,
|
||||||
|
'showSpeech2TextModel'
|
||||||
|
>;
|
||||||
export function LargeModelFormField({
|
export function LargeModelFormField({
|
||||||
showTTSModel,
|
showSpeech2TextModel: showTTSModel,
|
||||||
}: LargeModelFormFieldProps) {
|
}: LargeModelFormFieldProps) {
|
||||||
const form = useFormContext();
|
const form = useFormContext();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -91,7 +94,7 @@ export function LargeModelFormField({
|
|||||||
<NextLLMSelect
|
<NextLLMSelect
|
||||||
{...field}
|
{...field}
|
||||||
filter={filter}
|
filter={filter}
|
||||||
showTTSModel={showTTSModel}
|
showSpeech2TextModel={showTTSModel}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -13,18 +13,18 @@ export interface NextInnerLLMSelectProps {
|
|||||||
onChange?: (value: string) => void;
|
onChange?: (value: string) => void;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
filter?: string;
|
filter?: string;
|
||||||
showTTSModel?: boolean;
|
showSpeech2TextModel?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NextInnerLLMSelect = forwardRef<
|
const NextInnerLLMSelect = forwardRef<
|
||||||
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
||||||
NextInnerLLMSelectProps
|
NextInnerLLMSelectProps
|
||||||
>(({ value, disabled, filter, showTTSModel = false }, ref) => {
|
>(({ value, disabled, filter, showSpeech2TextModel = false }, ref) => {
|
||||||
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
||||||
|
|
||||||
const ttsModel = useMemo(() => {
|
const ttsModel = useMemo(() => {
|
||||||
return showTTSModel ? [LlmModelType.TTS] : [];
|
return showSpeech2TextModel ? [LlmModelType.Speech2text] : [];
|
||||||
}, [showTTSModel]);
|
}, [showSpeech2TextModel]);
|
||||||
|
|
||||||
const modelTypes = useMemo(() => {
|
const modelTypes = useMemo(() => {
|
||||||
if (filter === LlmModelType.Chat) {
|
if (filter === LlmModelType.Chat) {
|
||||||
|
|||||||
@ -24,7 +24,7 @@
|
|||||||
.messageText {
|
.messageText {
|
||||||
.chunkText();
|
.chunkText();
|
||||||
.messageTextBase();
|
.messageTextBase();
|
||||||
background-color: #e6f4ff;
|
// background-color: #e6f4ff;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
.messageTextDark {
|
.messageTextDark {
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import {
|
|||||||
useFetchDocumentThumbnailsByIds,
|
useFetchDocumentThumbnailsByIds,
|
||||||
} from '@/hooks/document-hooks';
|
} from '@/hooks/document-hooks';
|
||||||
import { IRegenerateMessage, IRemoveMessageById } from '@/hooks/logic-hooks';
|
import { IRegenerateMessage, IRemoveMessageById } from '@/hooks/logic-hooks';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
import { IMessage } from '@/pages/chat/interface';
|
import { IMessage } from '@/pages/chat/interface';
|
||||||
import MarkdownContent from '@/pages/chat/markdown-content';
|
import MarkdownContent from '@/pages/chat/markdown-content';
|
||||||
import { Avatar, Flex, Space } from 'antd';
|
import { Avatar, Flex, Space } from 'antd';
|
||||||
@ -129,13 +130,14 @@ const MessageItem = ({
|
|||||||
{/* <b>{isAssistant ? '' : nickname}</b> */}
|
{/* <b>{isAssistant ? '' : nickname}</b> */}
|
||||||
</Space>
|
</Space>
|
||||||
<div
|
<div
|
||||||
className={
|
className={cn(
|
||||||
isAssistant
|
isAssistant
|
||||||
? theme === 'dark'
|
? theme === 'dark'
|
||||||
? styles.messageTextDark
|
? styles.messageTextDark
|
||||||
: styles.messageText
|
: styles.messageText
|
||||||
: styles.messageUserText
|
: styles.messageUserText,
|
||||||
}
|
{ '!bg-bg-card': !isAssistant },
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<MarkdownContent
|
<MarkdownContent
|
||||||
loading={loading}
|
loading={loading}
|
||||||
|
|||||||
@ -369,22 +369,28 @@ export const useScrollToBottom = (
|
|||||||
return () => container.removeEventListener('scroll', handleScroll);
|
return () => container.removeEventListener('scroll', handleScroll);
|
||||||
}, [containerRef, checkIfUserAtBottom]);
|
}, [containerRef, checkIfUserAtBottom]);
|
||||||
|
|
||||||
|
// Imperative scroll function
|
||||||
|
const scrollToBottom = useCallback(() => {
|
||||||
|
if (containerRef?.current) {
|
||||||
|
const container = containerRef.current;
|
||||||
|
container.scrollTo({
|
||||||
|
top: container.scrollHeight - container.clientHeight,
|
||||||
|
behavior: 'smooth',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [containerRef]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!messages) return;
|
if (!messages) return;
|
||||||
if (!containerRef?.current) return;
|
if (!containerRef?.current) return;
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (isAtBottomRef.current) {
|
if (isAtBottomRef.current) {
|
||||||
ref.current?.scrollIntoView({ behavior: 'smooth' });
|
scrollToBottom();
|
||||||
}
|
}
|
||||||
}, 30);
|
}, 100);
|
||||||
});
|
});
|
||||||
}, [messages, containerRef]);
|
}, [messages, containerRef, scrollToBottom]);
|
||||||
|
|
||||||
// Imperative scroll function
|
|
||||||
const scrollToBottom = useCallback(() => {
|
|
||||||
ref.current?.scrollIntoView({ behavior: 'smooth' });
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return { scrollRef: ref, isAtBottom, scrollToBottom };
|
return { scrollRef: ref, isAtBottom, scrollToBottom };
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,6 +5,7 @@ export default {
|
|||||||
deleteModalTitle: 'Are you sure to delete this item?',
|
deleteModalTitle: 'Are you sure to delete this item?',
|
||||||
ok: 'Yes',
|
ok: 'Yes',
|
||||||
cancel: 'No',
|
cancel: 'No',
|
||||||
|
no: 'No',
|
||||||
total: 'Total',
|
total: 'Total',
|
||||||
rename: 'Rename',
|
rename: 'Rename',
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
@ -575,6 +576,8 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
|
|||||||
automatic: 'Automatic',
|
automatic: 'Automatic',
|
||||||
manual: 'Manual',
|
manual: 'Manual',
|
||||||
},
|
},
|
||||||
|
cancel: 'Cancel',
|
||||||
|
chatSetting: 'Chat setting',
|
||||||
},
|
},
|
||||||
setting: {
|
setting: {
|
||||||
profile: 'Profile',
|
profile: 'Profile',
|
||||||
|
|||||||
@ -569,6 +569,8 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
|
|||||||
automatic: '自动',
|
automatic: '自动',
|
||||||
manual: '手动',
|
manual: '手动',
|
||||||
},
|
},
|
||||||
|
cancel: '取消',
|
||||||
|
chatSetting: '聊天设置',
|
||||||
},
|
},
|
||||||
setting: {
|
setting: {
|
||||||
profile: '概要',
|
profile: '概要',
|
||||||
|
|||||||
@ -128,7 +128,7 @@ function AgentForm({ node }: INextOperatorForm) {
|
|||||||
<FormWrapper>
|
<FormWrapper>
|
||||||
<FormContainer>
|
<FormContainer>
|
||||||
{isSubAgent && <DescriptionField></DescriptionField>}
|
{isSubAgent && <DescriptionField></DescriptionField>}
|
||||||
<LargeModelFormField showTTSModel></LargeModelFormField>
|
<LargeModelFormField showSpeech2TextModel></LargeModelFormField>
|
||||||
{findLlmByUuid(llmId)?.model_type === LlmModelType.Image2text && (
|
{findLlmByUuid(llmId)?.model_type === LlmModelType.Image2text && (
|
||||||
<QueryVariable
|
<QueryVariable
|
||||||
name="visual_files_var"
|
name="visual_files_var"
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { ButtonLoading } from '@/components/ui/button';
|
import { Button, ButtonLoading } from '@/components/ui/button';
|
||||||
import { Form } from '@/components/ui/form';
|
import { Form } from '@/components/ui/form';
|
||||||
import { Separator } from '@/components/ui/separator';
|
import { Separator } from '@/components/ui/separator';
|
||||||
import { useFetchDialog, useSetDialog } from '@/hooks/use-chat-request';
|
import { useFetchDialog, useSetDialog } from '@/hooks/use-chat-request';
|
||||||
@ -11,6 +11,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
|||||||
import { X } from 'lucide-react';
|
import { X } from 'lucide-react';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useParams } from 'umi';
|
import { useParams } from 'umi';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { DatasetMetadata } from '../../constants';
|
import { DatasetMetadata } from '../../constants';
|
||||||
@ -25,6 +26,7 @@ export function ChatSettings({ switchSettingVisible }: ChatSettingsProps) {
|
|||||||
const { data } = useFetchDialog();
|
const { data } = useFetchDialog();
|
||||||
const { setDialog, loading } = useSetDialog();
|
const { setDialog, loading } = useSetDialog();
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
type FormSchemaType = z.infer<typeof formSchema>;
|
type FormSchemaType = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
@ -89,25 +91,26 @@ export function ChatSettings({ switchSettingVisible }: ChatSettingsProps) {
|
|||||||
return (
|
return (
|
||||||
<section className="p-5 w-[440px] border-l">
|
<section className="p-5 w-[440px] border-l">
|
||||||
<div className="flex justify-between items-center text-base pb-2">
|
<div className="flex justify-between items-center text-base pb-2">
|
||||||
Chat Settings
|
{t('chat.chatSetting')}
|
||||||
<X className="size-4 cursor-pointer" onClick={switchSettingVisible} />
|
<X className="size-4 cursor-pointer" onClick={switchSettingVisible} />
|
||||||
</div>
|
</div>
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit, onInvalid)}>
|
<form onSubmit={form.handleSubmit(onSubmit, onInvalid)}>
|
||||||
<section className="space-y-6 overflow-auto max-h-[85vh] pr-4">
|
<section className="space-y-6 overflow-auto max-h-[82vh] pr-4">
|
||||||
<ChatBasicSetting></ChatBasicSetting>
|
<ChatBasicSetting></ChatBasicSetting>
|
||||||
<Separator />
|
<Separator />
|
||||||
<ChatPromptEngine></ChatPromptEngine>
|
<ChatPromptEngine></ChatPromptEngine>
|
||||||
<Separator />
|
<Separator />
|
||||||
<ChatModelSettings></ChatModelSettings>
|
<ChatModelSettings></ChatModelSettings>
|
||||||
</section>
|
</section>
|
||||||
<ButtonLoading
|
<div className="space-x-5 text-right">
|
||||||
className="w-full my-4"
|
<Button variant={'outline'} onClick={switchSettingVisible}>
|
||||||
type="submit"
|
{t('chat.cancel')}
|
||||||
loading={loading}
|
</Button>
|
||||||
>
|
<ButtonLoading className=" my-4" type="submit" loading={loading}>
|
||||||
Update
|
{t('common.save')}
|
||||||
</ButtonLoading>
|
</ButtonLoading>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -23,7 +23,7 @@ interface IProps {
|
|||||||
export function SingleChatBox({ controller }: IProps) {
|
export function SingleChatBox({ controller }: IProps) {
|
||||||
const {
|
const {
|
||||||
value,
|
value,
|
||||||
// scrollRef,
|
scrollRef,
|
||||||
messageContainerRef,
|
messageContainerRef,
|
||||||
sendLoading,
|
sendLoading,
|
||||||
derivedMessages,
|
derivedMessages,
|
||||||
@ -47,7 +47,7 @@ export function SingleChatBox({ controller }: IProps) {
|
|||||||
return (
|
return (
|
||||||
<section className="flex flex-col p-5 h-full">
|
<section className="flex flex-col p-5 h-full">
|
||||||
<div ref={messageContainerRef} className="flex-1 overflow-auto min-h-0">
|
<div ref={messageContainerRef} className="flex-1 overflow-auto min-h-0">
|
||||||
<div className="w-full">
|
<div className="w-full pr-5">
|
||||||
{derivedMessages?.map((message, i) => {
|
{derivedMessages?.map((message, i) => {
|
||||||
return (
|
return (
|
||||||
<MessageItem
|
<MessageItem
|
||||||
@ -77,7 +77,7 @@ export function SingleChatBox({ controller }: IProps) {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
{/* <div ref={scrollRef} /> */}
|
<div ref={scrollRef} />
|
||||||
</div>
|
</div>
|
||||||
<NextMessageInput
|
<NextMessageInput
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
|||||||
@ -100,7 +100,7 @@ export default function Chat() {
|
|||||||
{t('common.embedIntoSite')}
|
{t('common.embedIntoSite')}
|
||||||
</Button>
|
</Button>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<div className="flex flex-1 min-h-0">
|
<div className="flex flex-1 min-h-0 pb-9">
|
||||||
<Sessions
|
<Sessions
|
||||||
hasSingleChatBox={hasSingleChatBox}
|
hasSingleChatBox={hasSingleChatBox}
|
||||||
handleConversationCardClick={handleConversationCardClick}
|
handleConversationCardClick={handleConversationCardClick}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import {
|
|||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { PanelLeftClose, PanelRightClose, Plus } from 'lucide-react';
|
import { PanelLeftClose, PanelRightClose, Plus } from 'lucide-react';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useHandleClickConversationCard } from '../hooks/use-click-card';
|
import { useHandleClickConversationCard } from '../hooks/use-click-card';
|
||||||
import { useSelectDerivedConversationList } from '../hooks/use-select-conversation-list';
|
import { useSelectDerivedConversationList } from '../hooks/use-select-conversation-list';
|
||||||
import { ConversationDropdown } from './conversation-dropdown';
|
import { ConversationDropdown } from './conversation-dropdown';
|
||||||
@ -24,6 +25,7 @@ export function Sessions({
|
|||||||
handleConversationCardClick,
|
handleConversationCardClick,
|
||||||
switchSettingVisible,
|
switchSettingVisible,
|
||||||
}: SessionProps) {
|
}: SessionProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
list: conversationList,
|
list: conversationList,
|
||||||
addTemporaryConversation,
|
addTemporaryConversation,
|
||||||
@ -102,8 +104,9 @@ export function Sessions({
|
|||||||
className="w-full"
|
className="w-full"
|
||||||
onClick={switchSettingVisible}
|
onClick={switchSettingVisible}
|
||||||
disabled={!hasSingleChatBox}
|
disabled={!hasSingleChatBox}
|
||||||
|
variant={'outline'}
|
||||||
>
|
>
|
||||||
Chat Settings
|
{t('chat.chatSetting')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
Reference in New Issue
Block a user