import AlertInfo from '@/components/alert-info'; import IconFont from '@/components/icon-font'; import SealSelect from '@/components/seal-form/seal-select'; import SpeechContent from '@/components/speech-content'; import useOverlayScroller from '@/hooks/use-overlay-scroller'; import { SendOutlined } from '@ant-design/icons'; import { getLocale, useIntl, useSearchParams } from '@umijs/max'; import { Form, Spin } from 'antd'; import classNames from 'classnames'; import _ from 'lodash'; import 'overlayscrollbars/overlayscrollbars.css'; import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'; import { CHAT_API, queryModelVoices, textToSpeech } from '../apis'; import { TTSParamsConfig as paramsConfig } from '../config/params-config'; import { MessageItem, ParamsSchema } from '../config/types'; import '../style/ground-left.less'; import '../style/system-message-wrap.less'; import DynamicParams from './dynamic-params'; import MessageInput from './message-input'; import ViewCodeModal from './view-code-modal'; interface MessageProps { modelList: Global.BaseOption[]; loaded?: boolean; ref?: any; } const initialValues = { voice: '', response_format: 'mp3' }; const GroundLeft: React.FC = forwardRef((props, ref) => { const { modelList } = props; const messageId = useRef(0); const [messageList, setMessageList] = useState< { input: string; voice: string; format: string; speed: number; uid: number; autoplay: boolean; audioUrl: string; }[] >([ { input: '', voice: '', format: '', speed: 0, uid: 0, autoplay: false, audioUrl: '' } ]); const locale = getLocale(); const intl = useIntl(); const [searchParams] = useSearchParams(); const modelType = searchParams.get('type') || ''; const selectModel = searchParams.get('model') ? modelType === 'tts' && searchParams.get('model') : ''; const [parameters, setParams] = useState({}); const [show, setShow] = useState(false); const [loading, setLoading] = useState(false); const [tokenResult, setTokenResult] = useState(null); const [voiceError, setVoiceError] = useState(null); const [collapse, setCollapse] = useState(false); const controllerRef = useRef(null); const scroller = useRef(null); const paramsRef = useRef(null); const messageListLengthCache = useRef(0); const checkvalueRef = useRef(true); const [currentPrompt, setCurrentPrompt] = useState(''); const [voiceDataList, setVoiceList] = useState[]>( [] ); const formRef = useRef(null); const { initialize, updateScrollerPosition } = useOverlayScroller(); const { initialize: innitializeParams } = useOverlayScroller(); useImperativeHandle(ref, () => { return { viewCode() { setShow(true); }, setCollapse() { setCollapse(!collapse); }, collapse: collapse }; }); const sortVoiceList = useCallback( (locale: string, voiceDataList: Global.BaseOption[]) => { const lang = locale === 'en-US' ? 'english' : 'chinese'; const list = voiceDataList.sort((a, b) => { const aContains = a.value.toLowerCase().includes(lang) ? 1 : 0; const bContains = b.value.toLowerCase().includes(lang) ? 1 : 0; return bContains - aContains; }); return list; }, [] ); const voiceList = useMemo(() => { if (!voiceDataList.length) return []; const newList = sortVoiceList(locale, voiceDataList); return newList; }, [locale, voiceDataList, sortVoiceList]); useEffect(() => { const newList = sortVoiceList(locale, voiceDataList); setParams((pre: any) => { return { ...pre, voice: newList[0]?.value }; }); formRef.current?.form.setFieldValue('voice', newList[0]?.value); }, [locale, voiceDataList, sortVoiceList]); const setMessageId = () => { messageId.current = messageId.current + 1; }; const handleStopConversation = () => { controllerRef.current?.abort?.(); setLoading(false); }; const submitMessage = async (current?: { role: string; content: string }) => { await formRef.current?.form.validateFields(); if (!parameters.model) return; try { setLoading(true); setMessageId(); setTokenResult(null); setCurrentPrompt(current?.content || ''); setMessageList([]); controllerRef.current?.abort?.(); controllerRef.current = new AbortController(); const signal = controllerRef.current.signal; const params = { ...parameters, input: current?.content || currentPrompt }; const res: any = await textToSpeech({ data: params, url: CHAT_API, signal }); console.log('result:', res); if (res?.status_code && res?.status_code !== 200) { setTokenResult({ error: true, errorMessage: res?.data?.error?.message || res?.data?.error || res?.detail || '' }); setMessageList([]); return; } setMessageList([ { input: current?.content || currentPrompt, voice: parameters.voice, format: parameters.response_format, speed: parameters.speed, uid: messageId.current, autoplay: checkvalueRef.current, audioUrl: res.url } ]); } catch (error: any) { const res = error?.response?.data; if (res?.error) { setTokenResult({ error: true, errorMessage: res?.error?.message || res?.data?.error || res?.detail || '' }); } } finally { setLoading(false); } }; const handleClear = () => { setMessageId(); setMessageList([]); setTokenResult(null); }; const handleSendMessage = (message: Omit) => { submitMessage(message); }; const handleCloseViewCode = () => { setShow(false); }; const handleSelectModel = useCallback( async (value: string) => { if (!value) return; try { const res = await queryModelVoices({ model: value }); if (res?.status_code && res?.status_code !== 200) { setVoiceError({ error: true, errorMessage: res?.data?.error?.message || res?.data?.error || res?.detail || '' }); setVoiceList([]); return; } const list = _.map(res.voices || [], (item: any) => { return { label: item, value: item }; }); const newList = sortVoiceList(locale, list); setVoiceList(newList); setVoiceError(null); setParams((pre: any) => { return { ...pre, voice: newList[0]?.value }; }); formRef.current?.form.setFieldValue('voice', newList[0]?.value); } catch (error: any) { const res = error?.response?.data; if (res?.error) { setVoiceError({ error: true, errorMessage: res?.error?.message || res?.data?.error || res?.detail || '' }); } setVoiceList([]); formRef.current?.form.setFieldValue('voice', ''); setParams((pre: any) => { return { ...pre, voice: '' }; }); } }, [modelList] ); const handleOnCheckChange = (e: any) => { checkvalueRef.current = e.target.checked; }; const renderExtra = useMemo(() => { return paramsConfig.map((item: ParamsSchema) => { return ( ); }); }, [paramsConfig, intl, voiceList]); const renderVoiceError = useMemo(() => { if (!voiceError) return null; return ( <> {voiceError && (
)} ); }, [voiceError]); useEffect(() => { handleSelectModel(parameters.model); }, [parameters.model, handleSelectModel]); useEffect(() => { if (scroller.current) { initialize(scroller.current); } }, [scroller.current, initialize]); useEffect(() => { if (paramsRef.current) { innitializeParams(paramsRef.current); } }, [paramsRef.current, innitializeParams]); useEffect(() => { if (loading) { updateScrollerPosition(); } }, [messageList, loading]); useEffect(() => { if (messageList.length > messageListLengthCache.current) { updateScrollerPosition(); } messageListLengthCache.current = messageList.length; }, [messageList.length]); return (
{messageList.length ? ( ) : (
{intl.formatMessage({ id: 'playground.audio.texttospeech.tips' })}
)} {loading && (
)}
{tokenResult && (
)}
} />
); }); export default memo(GroundLeft);