diff --git a/src/components/logs-viewer/parse-worker.ts b/src/components/logs-viewer/parse-worker.ts index db9caee8..ebe8da02 100644 --- a/src/components/logs-viewer/parse-worker.ts +++ b/src/components/logs-viewer/parse-worker.ts @@ -8,6 +8,15 @@ const removeBracketsFromLine = (row: string) => { return row.startsWith('(…)') ? row.slice(3) : row; }; +interface MessageProps { + inputStr: string; + reset?: boolean; + page?: number; + isComplete?: boolean; + chunked?: boolean; + progress?: number; + percent?: number; +} class AnsiParser { private cursorRow: number = 0; private cursorCol: number = 0; @@ -17,6 +26,8 @@ class AnsiParser { private isProcessing: boolean = false; private taskQueue: string[] = []; private page: number = 1; + private progress: number = 0; + private percent: number = 0; private isComplete: boolean = false; private chunked: boolean = true; // true: send data in chunks, false: send all data at once private pageSize: number = 500; @@ -44,8 +55,16 @@ class AnsiParser { this.page = 1; } - public setPage(page: number) { - this.page = page; + public setPage(page: number | undefined) { + this.page = page ?? 1; + } + + public setPercent(percent: number | undefined) { + this.percent = percent ?? 0; + } + + public setProgress(progress: number | undefined) { + this.progress = progress ?? 0; } public setIsCompelete(isComplete: boolean) { @@ -179,6 +198,12 @@ class AnsiParser { const result = this.processInput(input); if (this.chunked) { self.postMessage({ result: result.data, lines: result.lines }); + } else if (!this.isComplete) { + self.postMessage({ + result: '', + percent: this.percent, + isComplete: false + }); } } catch (error) { console.error('Error processing input:', error); @@ -194,7 +219,11 @@ class AnsiParser { if (this.taskQueue.length > 0) { this.processQueue(); } else if (this.isComplete && !this.chunked) { - self.postMessage({ result: this.getScreenText() }); + self.postMessage({ + result: this.getScreenText(), + percent: this.percent, + isComplete: true + }); this.reset(); } } @@ -208,18 +237,20 @@ class AnsiParser { } const parser = new AnsiParser(); -self.onmessage = function (event) { +self.onmessage = function (event: MessageEvent) { const { inputStr, reset, page, isComplete = false, - chunked = true + chunked = true, + percent = 0 } = event.data; parser.setPage(page); parser.setIsCompelete(isComplete); parser.setChunked(chunked); + parser.setPercent(percent); if (reset) { parser.reset(); diff --git a/src/components/markdown-viewer/index.less b/src/components/markdown-viewer/index.less index b796848b..15f17278 100644 --- a/src/components/markdown-viewer/index.less +++ b/src/components/markdown-viewer/index.less @@ -5,6 +5,7 @@ border: none; height: 1px; background-color: var(--ant-color-split); + border-color: var(--ant-color-split); } p { diff --git a/src/global.less b/src/global.less index 696178a7..b64772e5 100644 --- a/src/global.less +++ b/src/global.less @@ -907,3 +907,7 @@ body { width: 0; } } + +.ant-notification .ant-notification-notice-close { + width: fit-content !important; +} diff --git a/src/hooks/use-chunk-fetch.ts b/src/hooks/use-chunk-fetch.ts index 13e5aaae..b80ee3dc 100644 --- a/src/hooks/use-chunk-fetch.ts +++ b/src/hooks/use-chunk-fetch.ts @@ -89,7 +89,7 @@ const useSetChunkFetch = () => { let isReading = true; - while (true) { + while (isReading) { const { done, value } = await reader.read(); if (done) { diff --git a/src/hooks/use-download-stream.ts b/src/hooks/use-download-stream.ts index 2f1554ed..e10b19f2 100644 --- a/src/hooks/use-download-stream.ts +++ b/src/hooks/use-download-stream.ts @@ -3,7 +3,7 @@ import { message } from 'antd'; import { useEffect, useRef } from 'react'; export default function useDownloadStream() { - const chunkRequedtRef = useRef(null); + const chunkRequestRef = useRef(null); const logParseWorker = useRef(null); const clearScreen = useRef(false); const filename = useRef('log'); @@ -26,19 +26,13 @@ export default function useDownloadStream() { }; const updateContent = (data: string, options?: HandlerOptions) => { - const { isComplete } = options || {}; - - downloadNotificationRef.current?.({ - ...options, - duration: isComplete ? 1 : null, - filename: filename.current - }); + const { isComplete, percent } = options || {}; logParseWorker.current?.postMessage({ inputStr: data, - page: 1, reset: clearScreen.current, isComplete: isComplete, + percent: percent, chunked: false }); clearScreen.current = false; @@ -72,13 +66,9 @@ export default function useDownloadStream() { downloadNotificationRef.current = props.downloadNotification; const { params, url } = props; - downloadNotificationRef.current?.({ - filename: filename.current - }); + chunkRequestRef.current?.current?.abort?.(); - chunkRequedtRef.current?.current?.abort?.(); - - chunkRequedtRef.current = setChunkFetch({ + chunkRequestRef.current = setChunkFetch({ url, params, watch: false, @@ -86,6 +76,11 @@ export default function useDownloadStream() { errorHandler: handleError, handler: updateContent }); + downloadNotificationRef.current?.({ + filename: filename.current, + duration: null, + chunkRequestRef: chunkRequestRef.current + }); } catch (error) { // downloadNotificationRef.current?.({ @@ -108,8 +103,24 @@ export default function useDownloadStream() { ); logParseWorker.current.onmessage = (event: any) => { - const { result } = event.data; - downloadFile(result); + const { result, isComplete, percent } = event.data; + + const isAborted = chunkRequestRef.current?.current?.signal?.aborted; + if (!isComplete && !isAborted) { + downloadNotificationRef.current?.({ + percent: percent, + duration: null, + filename: filename.current, + chunkRequestRef: chunkRequestRef.current + }); + } else if (isComplete && !isAborted) { + downloadNotificationRef.current?.({ + duration: 1, + percent: 100, + filename: filename.current + }); + downloadFile(result); + } }; return () => { diff --git a/src/pages/llmodels/components/instance-item.tsx b/src/pages/llmodels/components/instance-item.tsx index 3c5045b4..7e18d499 100644 --- a/src/pages/llmodels/components/instance-item.tsx +++ b/src/pages/llmodels/components/instance-item.tsx @@ -115,10 +115,11 @@ const InstanceItem: React.FC = ({ modelData, handleChildSelect }) => { - const [api, contextHolder] = notification.useNotification(); + const [api, contextHolder] = notification.useNotification({ + stack: { threshold: 1 } + }); const { downloadStream } = useDownloadStream(); const intl = useIntl(); - const actionItems = useMemo(() => { return _.filter(childActionList, (action: any) => { if (action.key === 'viewlog' || action.key === 'download') { @@ -135,12 +136,25 @@ const InstanceItem: React.FC = ({ }; const downloadNotification = useCallback( - (data: HandlerOptions & { filename: string; duration?: number }) => { + ( + data: HandlerOptions & { + filename: string; + duration?: number; + chunkRequestRef: any; + } + ) => { api.open({ duration: data.duration, message: renderMessage(data.filename), key: data.filename, - description: + closeIcon: ( + {intl.formatMessage({ id: 'common.button.cancel' })} + ), + description: , + onClose() { + data.chunkRequestRef?.current?.abort(); + notification.destroy?.(data.filename); + } }); }, [] diff --git a/src/pages/playground/components/ground-embedding.tsx b/src/pages/playground/components/ground-embedding.tsx index 64ab48ac..629010e8 100644 --- a/src/pages/playground/components/ground-embedding.tsx +++ b/src/pages/playground/components/ground-embedding.tsx @@ -198,10 +198,9 @@ const GroundEmbedding: React.FC = forwardRef((props, ref) => { }; const submitMessage = async (current?: { role: string; content: string }) => { - await formRef.current?.form.validateFields(); - if (!parameters.model) return; - try { + await formRef.current?.form.validateFields(); + if (!parameters.model) return; const validTextList = textList.filter((item) => item.text); const validFileList = fileList.filter((item) => item.text); diff --git a/src/pages/playground/components/ground-images.tsx b/src/pages/playground/components/ground-images.tsx index 374bea5d..c9a653f3 100644 --- a/src/pages/playground/components/ground-images.tsx +++ b/src/pages/playground/components/ground-images.tsx @@ -229,7 +229,9 @@ const GroundImages: React.FC = forwardRef((props, ref) => { - {intl.formatMessage({ id: 'playground.params.empty.tips' })} + {intl.formatMessage({ + id: 'playground.params.empty.tips' + })} )} @@ -308,7 +310,7 @@ const GroundImages: React.FC = forwardRef((props, ref) => { })} > } diff --git a/src/pages/playground/components/ground-tts.tsx b/src/pages/playground/components/ground-tts.tsx index a3dece56..83cb6e4e 100644 --- a/src/pages/playground/components/ground-tts.tsx +++ b/src/pages/playground/components/ground-tts.tsx @@ -92,6 +92,10 @@ const GroundLeft: React.FC = forwardRef((props, ref) => { }; }); + const defaultModel = useMemo(() => { + return selectModel || modelList[0]?.value || ''; + }, [modelList]); + const viewCodeContent = useMemo(() => { return TextToSpeechCode({ api: AUDIO_TEXT_TO_SPEECH_API, @@ -147,9 +151,9 @@ const GroundLeft: React.FC = forwardRef((props, ref) => { }; const submitMessage = async (current?: { role: string; content: string }) => { - await formRef.current?.form.validateFields(); - if (!parameters.model) return; try { + await formRef.current?.form.validateFields(); + if (!parameters.model) return; setLoading(true); setMessageId(); setTokenResult(null); @@ -235,14 +239,6 @@ const GroundLeft: React.FC = forwardRef((props, ref) => { const handleSelectModel = useCallback( async (value: string) => { if (!value) { - setVoiceList([]); - setParams((pre: any) => { - return { - ...pre, - voice: '' - }; - }); - formRef.current?.form.setFieldValue('voice', ''); return; } const model = modelList.find((item) => item.value === value); @@ -259,10 +255,10 @@ const GroundLeft: React.FC = forwardRef((props, ref) => { setParams((pre: any) => { return { ...pre, + model: value, voice: newList[0]?.value }; }); - formRef.current?.form.setFieldValue('voice', newList[0]?.value); }, [modelList] ); @@ -307,11 +303,10 @@ const GroundLeft: React.FC = forwardRef((props, ref) => { }, [paramsConfig, intl, voiceList]); useEffect(() => { - if (!parameters.model && modelList.length) { - const model = modelList[0]?.value; - handleSelectModel(model); + if (defaultModel) { + handleSelectModel(defaultModel); } - }, [modelList, parameters.model, handleSelectModel]); + }, [defaultModel]); useEffect(() => { if (scroller.current) { diff --git a/src/pages/playground/components/image-edit.tsx b/src/pages/playground/components/image-edit.tsx index 28f33071..2e11854b 100644 --- a/src/pages/playground/components/image-edit.tsx +++ b/src/pages/playground/components/image-edit.tsx @@ -506,7 +506,7 @@ const GroundImages: React.FC = forwardRef((props, ref) => { })} >