Skip to content

Commit

Permalink
Merge pull request #615 from lixinghua123/3.8.5-lxh
Browse files Browse the repository at this point in the history
feat: LLM supports audio playback
  • Loading branch information
lihqi authored Dec 5, 2024
2 parents cc1e00f + bb79a35 commit bf285e5
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 49 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getTextControlByConfig, RenderAnswer } from '@/components/LLMToolView/questionView';
import AudioView from '@/components/LLMToolView/questionView/components/audioView';
import { RenderQuestion } from '@/components/LLMToolView/questionView/components/header';
import ImgView from '@/components/LLMToolView/questionView/components/imgView';
import { ILLMMultiWheelToolConfig } from '@/components/LLMToolView/types';
Expand All @@ -18,6 +19,8 @@ interface IDialogViewProps {
answerIsImg: boolean;
questionIsImg: boolean;
LLMConfig?: ILLMMultiWheelToolConfig;
answerIsAudio?: boolean;
questionIsAudio?: boolean;
}

const DialogView = (props: IDialogViewProps) => {
Expand All @@ -29,6 +32,8 @@ const DialogView = (props: IDialogViewProps) => {
name = '',
answerIsImg,
questionIsImg,
questionIsAudio,
answerIsAudio,
LLMConfig,
} = props;
const { t } = useTranslation();
Expand All @@ -37,6 +42,39 @@ const DialogView = (props: IDialogViewProps) => {
const order = index + 1;
const showName = name || `${t('Dialog')}${order}`;

const dialogAnswer = () => {
if (answerIsImg) {
return (
<>
<Button type='primary'>{t('Answer')}</Button>
<ImgView answerList={answerList} />
</>
);
}
if (answerIsAudio) {
return (
<>
<Button type='primary'>{t('Answer')}</Button>
<AudioView answerList={answerList} />
</>
);
}
return answerList?.map((item: any, index: number) => {
const order = index + 1;
const answer = { ...item, order };
const isTextControl = getTextControlByConfig(answer, LLMConfig);
return (
<div key={index}>
<Button type='primary'>
{t('Answer')}
{order}
</Button>
<RenderAnswer i={answer} isTextControl={isTextControl} dataFormatType={dataFormatType} />
</div>
);
});
};

return (
<div
key={id}
Expand All @@ -63,36 +101,11 @@ const DialogView = (props: IDialogViewProps) => {
question={question}
dataFormatType={dataFormatType}
isImg={questionIsImg}
isAudio={questionIsAudio}
/>
</div>
</div>
<div className={`dialog-answer`}>
{answerIsImg ? (
<>
<Button type='primary'>{t('Answer')}</Button>
<ImgView answerList={answerList} />
</>
) : (
answerList?.map((item: any, index: number) => {
const order = index + 1;
const answer = { ...item, order };
const isTextControl = getTextControlByConfig(answer, LLMConfig);
return (
<div key={index}>
<Button type='primary'>
{t('Answer')}
{order}
</Button>
<RenderAnswer
i={answer}
isTextControl={isTextControl}
dataFormatType={dataFormatType}
/>
</div>
);
})
)}
</div>
<div className={`dialog-answer`}>{dialogAnswer()}</div>
</div>
);
};
Expand Down
51 changes: 35 additions & 16 deletions packages/lb-components/src/components/LLMMultiWheelView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,20 @@ interface ILLMMultiWheelSourceViewProps {
LLMConfig?: ILLMMultiWheelToolConfig;
dialogList: any[];
lang?: string;
questionIsAudio?: boolean;
answerIsAudio?: boolean;
}

export const LLMMultiWheelSourceView: React.FC<ILLMMultiWheelSourceViewProps> = (props) => {
const { questionIsImg, answerIsImg, LLMConfig, dialogList, lang = 'cn' } = props;
const {
questionIsImg,
answerIsImg,
questionIsAudio,
answerIsAudio,
LLMConfig,
dialogList,
lang = 'cn',
} = props;
const { dataFormatType, setDataFormatType } = useLLMMultiWheelStore();

useEffect(() => {
Expand Down Expand Up @@ -66,6 +76,8 @@ export const LLMMultiWheelSourceView: React.FC<ILLMMultiWheelSourceViewProps> =
index={index}
questionIsImg={questionIsImg}
answerIsImg={answerIsImg}
questionIsAudio={questionIsAudio}
answerIsAudio={answerIsAudio}
LLMConfig={LLMConfig}
/>
))}
Expand Down Expand Up @@ -109,6 +121,9 @@ const LLMMultiWheelView: React.FC<IProps> = (props) => {
const [dialogList, setDialogList] = useState<any[]>([]);
const questionIsImg = LLMConfig?.dataType?.prompt === ELLMDataType.Picture;
const answerIsImg = LLMConfig?.dataType?.response === ELLMDataType.Picture;
const questionIsAudio = LLMConfig?.dataType?.prompt === ELLMDataType.Audio;
const answerIsAudio = LLMConfig?.dataType?.response === ELLMDataType.Audio;

const { t } = useTranslation();
const [, forceRender] = useState(0);

Expand Down Expand Up @@ -136,23 +151,25 @@ const LLMMultiWheelView: React.FC<IProps> = (props) => {
const newDialogList = textList?.map((item: any, questionIndex: number) => {
return {
...item,
question: questionIsImg
? getInfoFromLLMFile({
type: 'question',
questionIndex,
llmFile,
}) || item?.question
: item?.question,
answerList: item?.answerList?.map((i: any, answerIndex: number) => {
const mapId = `${item?.id ?? ''}-${i?.id ?? ''}`;
const info = answerIsImg
question:
questionIsImg || questionIsAudio
? getInfoFromLLMFile({
type: 'answer',
type: 'question',
questionIndex,
answerIndex,
llmFile,
}) || {}
: {};
}) || item?.question
: item?.question,
answerList: item?.answerList?.map((i: any, answerIndex: number) => {
const mapId = `${item?.id ?? ''}-${i?.id ?? ''}`;
const info =
answerIsImg || answerIsAudio
? getInfoFromLLMFile({
type: 'answer',
questionIndex,
answerIndex,
llmFile,
}) || {}
: {};
return {
...i,
answer: i.answer,
Expand All @@ -164,7 +181,7 @@ const LLMMultiWheelView: React.FC<IProps> = (props) => {
};
});
setDialogList(newDialogList);
}, [newAnswerListMap, questionIsImg, answerIsImg]);
}, [newAnswerListMap, questionIsImg, questionIsAudio, answerIsImg]);

useEffect(() => {
if (stepList && step) {
Expand Down Expand Up @@ -192,6 +209,8 @@ const LLMMultiWheelView: React.FC<IProps> = (props) => {
<LLMMultiWheelSourceView
questionIsImg={questionIsImg}
answerIsImg={answerIsImg}
questionIsAudio={questionIsAudio}
answerIsAudio={answerIsAudio}
LLMConfig={LLMConfig}
dialogList={dialogList}
/>
Expand Down
6 changes: 4 additions & 2 deletions packages/lb-components/src/components/LLMToolView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,17 @@ const LLMToolView: React.FC<IProps> = (props) => {
return;
}
const questionIsImg = LLMConfig?.dataType?.prompt === ELLMDataType.Picture;
const questionIsAudio = LLMConfig?.dataType?.prompt === ELLMDataType.Audio;
const answerIsImg = LLMConfig?.dataType?.response === ELLMDataType.Picture;
const answerIsAudio = LLMConfig?.dataType?.response === ELLMDataType.Audio;

const qaData = imgList[imgIndex]?.questionList;

const llmFile = imgList[imgIndex]?.llmFile;
const titleQuestion = questionIsImg ? llmFile?.question : qaData?.question;
const titleQuestion = questionIsImg || questionIsAudio ? llmFile?.question : qaData?.question;
setQuestion(titleQuestion);
let list = qaData?.answerList || [];
if (answerIsImg) {
if (answerIsImg || answerIsAudio) {
list = llmFile?.answerList || [];
}
if (LLMConfig?.dataType?.response === ELLMDataType.None) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* @file LLM tool audio view
* @Author: lixinghua [email protected]
* @Date: 2023-11-13
*/
import React from 'react';
import { Tag } from 'antd';
import { prefix } from '@/constant';
import classNames from 'classnames';
import { IAnswerList } from '@/components/LLMToolView/types';

interface IProps {
hoverKey?: number;
answerList: IAnswerList[];
}

const LLMViewCls = `${prefix}-LLMView`;

const AudioPlayer = React.memo(({ url }: { url?: string }) => (
<audio controls>
<source src={url} type='audio/mpeg' />
</audio>
));

const AudioView = (props: IProps) => {
const { answerList, hoverKey } = props;

return (
<div>
{answerList?.length > 0 &&
answerList.map((i: IAnswerList, index: number) => {
return (
<div
key={index}
className={classNames({
[`${LLMViewCls}__content`]: true,
[`${LLMViewCls}__contentActive`]: hoverKey === i?.order,
})}
>
<Tag className={`${LLMViewCls}-tag`}>{i?.order}</Tag>
<div>
<AudioPlayer key={i?.url} url={i?.url} />
</div>
</div>
);
})}
</div>
);
};

export default AudioView;
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Resizable } from 're-resizable';
import { Radio, Image } from 'antd';
import { Radio, Image, Empty } from 'antd';
import { EDataFormatType, prefix } from '@/constant';
import { FileTextOutlined } from '@ant-design/icons';
import MarkdownView from '@/components/markdownView';
Expand All @@ -28,6 +28,7 @@ interface IProps {
dataFormatType: EDataFormatType;
setDataFormatType: (v: EDataFormatType) => void;
isImg?: boolean;
isAudio?: boolean;
}

const LLMViewCls = `${prefix}-LLMView`;
Expand All @@ -36,6 +37,7 @@ export const RenderQuestion = ({
question,
dataFormatType,
isImg,
isAudio,
}: {
question:
| string
Expand All @@ -48,6 +50,7 @@ export const RenderQuestion = ({
};
dataFormatType: EDataFormatType;
isImg?: boolean;
isAudio?: boolean;
}) => {
const textValue = isString(question) ? question : '';
const ImgFail = i18n.language === 'en' ? ImgFailEn : ImgFailCn;
Expand All @@ -56,14 +59,27 @@ export const RenderQuestion = ({
const url = isObject(question) ? question?.url : '';
return <Image src={url || ImgFail} fallback={ImgFail} />;
}

if (isAudio) {
const url = isObject(question) ? question?.url : '';
if (url) {
return (
<audio controls>
<source src={url} type='audio/mpeg' />
</audio>
);
}
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />;
}

return (
<div style={{ whiteSpace: 'pre-wrap' }}>
{dataFormatType === EDataFormatType.Markdown ? <MarkdownView value={textValue} /> : textValue}
</div>
);
};
const Header = (props: IProps) => {
const { question, dataFormatType, setDataFormatType, isImg } = props;
const { question, dataFormatType, setDataFormatType, isImg, isAudio } = props;
const DEFAULT_HEIGHT = 300;
const { t } = useTranslation();

Expand Down Expand Up @@ -91,7 +107,12 @@ const Header = (props: IProps) => {
/>
</div>
<div className={`${LLMViewCls}__headerContent`}>
<RenderQuestion question={question} dataFormatType={dataFormatType} isImg={isImg} />
<RenderQuestion
question={question}
dataFormatType={dataFormatType}
isImg={isImg}
isAudio={isAudio}
/>
</div>
</Resizable>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import DiffMatchPatchComponent from '@/components/diffMatchPatchComponent';
import Header from './components/header';
import ImgView from './components/imgView';
import { isString } from 'lodash';
import AudioView from './components/audioView';

interface IProps {
hoverKey?: number;
Expand Down Expand Up @@ -95,6 +96,8 @@ const QuestionView: React.FC<IProps> = (props) => {
const [dataFormatType, setDataFormatType] = useState(EDataFormatType.Default);
const questionIsImg = LLMConfig?.dataType?.prompt === ELLMDataType.Picture;
const answerIsImg = LLMConfig?.dataType?.response === ELLMDataType.Picture;
const questionIsAudio = LLMConfig?.dataType?.prompt === ELLMDataType.Audio;
const answerIsAudio = LLMConfig?.dataType?.response === ELLMDataType.Audio;
const { t } = useTranslation();

useEffect(() => {
Expand Down Expand Up @@ -132,19 +135,31 @@ const QuestionView: React.FC<IProps> = (props) => {
)}
</div>
);

const answerContent = () => {
if (answerIsAudio) {
return <AudioView hoverKey={hoverKey} answerList={answerList} />;
}
if (answerIsImg) {
return <ImgView hoverKey={hoverKey} answerList={answerList} />;
}
return textAnswer;
};

return (
<div className={LLMViewCls}>
<Header
question={question}
dataFormatType={dataFormatType}
setDataFormatType={setDataFormatType}
isImg={questionIsImg}
isAudio={questionIsAudio}
/>
<div className={`${LLMViewCls}__textBox`}>
<div className={`${LLMViewCls}__title`}>
{t('Answer')} {answerHeaderSlot}
</div>
{answerIsImg ? <ImgView hoverKey={hoverKey} answerList={answerList} /> : textAnswer}
{answerContent()}
</div>
</div>
);
Expand Down
Loading

0 comments on commit bf285e5

Please sign in to comment.