fix: do not update logs when viewing pre page

This commit is contained in:
jialin
2025-01-03 20:38:44 +08:00
parent 472ef1a231
commit 95058f7890
13 changed files with 164 additions and 64 deletions
+79 -41
View File
@@ -43,6 +43,8 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
const [scrollPos, setScrollPos] = useState<any[]>([]); const [scrollPos, setScrollPos] = useState<any[]>([]);
const logListRef = useRef<any>(null); const logListRef = useRef<any>(null);
const loadMoreDone = useRef(false); const loadMoreDone = useRef(false);
const pageRef = useRef<any>(page);
const totalPageRef = useRef<any>(totalPage);
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
abort() { abort() {
@@ -84,28 +86,47 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
return command === 'J' && n === 2; return command === 'J' && n === 2;
}, []); }, []);
const getLastPage = useCallback( const getLastPage = (data: string) => {
(data: string) => { const list = _.split(data.trim(), '\n');
const list = _.split(data.trim(), '\n');
if (!enableScorllLoad) {
return list.join('\n');
}
if (list.length <= pageSize) { if (!enableScorllLoad) {
setTotalPage(1); return list.join('\n');
return data; }
}
setLoading(true); if (list.length <= pageSize) {
const totalPage = Math.ceil(list.length / pageSize); setTotalPage(1);
setTotalPage(totalPage); return data;
setPage(totalPage); }
const lastPage = list.slice(-pageSize).join('\n');
debounceLoading(); setLoading(true);
return lastPage; const totalPage = Math.ceil(list.length / pageSize);
}, setTotalPage(totalPage);
[pageSize, setTotalPage, setPage, debounceLoading, enableScorllLoad] setPage(() => totalPage);
); pageRef.current = totalPage;
totalPageRef.current = totalPage;
const lastPage = list.slice(-pageSize).join('\n');
console.log('list.length===', list.length, totalPage, page);
debounceLoading();
return lastPage;
};
const getCurrentPage = () => {
const list = _.split(cacheDataRef.current.trim(), '\n');
let newPage = page;
if (newPage < 1) {
newPage = 1;
}
console.log('currentpage===', newPage);
const start = (newPage - 1) * pageSize;
const end = newPage * pageSize;
const currentPage = list.slice(start, end).join('\n');
setPage(() => newPage);
setScrollPos(['bottom', newPage]);
pageRef.current = newPage;
logParseWorker.current.postMessage({
inputStr: currentPage
});
};
const getPrePage = useCallback(() => { const getPrePage = useCallback(() => {
const list = _.split(cacheDataRef.current.trim(), '\n'); const list = _.split(cacheDataRef.current.trim(), '\n');
@@ -116,8 +137,10 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
const start = (newPage - 1) * pageSize; const start = (newPage - 1) * pageSize;
const end = newPage * pageSize; const end = newPage * pageSize;
const prePage = list.slice(start, end).join('\n'); const prePage = list.slice(start, end).join('\n');
setPage(newPage); console.log('prePage===', newPage);
setPage(() => newPage);
setScrollPos(['bottom', newPage]); setScrollPos(['bottom', newPage]);
pageRef.current = newPage;
logParseWorker.current.postMessage({ logParseWorker.current.postMessage({
inputStr: prePage inputStr: prePage
}); });
@@ -132,29 +155,36 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
const start = (newPage - 1) * pageSize; const start = (newPage - 1) * pageSize;
const end = newPage * pageSize; const end = newPage * pageSize;
const nextPage = list.slice(start, end).join('\n'); const nextPage = list.slice(start, end).join('\n');
setPage(newPage); console.log('nextPage===', newPage);
setPage(() => newPage);
setScrollPos(['top', newPage]); setScrollPos(['top', newPage]);
pageRef.current = newPage;
logParseWorker.current.postMessage({ logParseWorker.current.postMessage({
inputStr: nextPage inputStr: nextPage
}); });
}, [totalPage, page, pageSize]); }, [totalPage, page, pageSize]);
const updateContent = useCallback( const debounceParseData = _.debounce(() => {
(inputStr: string) => { if (pageRef.current === totalPageRef.current) {
console.log('inputStr===', inputStr);
const data = inputStr.replace(replaceLineRegex, '\n');
if (isClean(data)) {
cacheDataRef.current = data;
} else {
cacheDataRef.current += data;
}
logParseWorker.current.postMessage({ logParseWorker.current.postMessage({
inputStr: getLastPage(cacheDataRef.current) inputStr: getLastPage(cacheDataRef.current)
}); });
}, } else {
[getLastPage] getCurrentPage();
); }
}, 100);
const updateContent = (inputStr: string) => {
console.log('inputStr===', inputStr);
const data = inputStr.replace(replaceLineRegex, '\n');
if (isClean(data)) {
cacheDataRef.current = data;
} else {
cacheDataRef.current += data;
}
console.log('page===', pageRef.current, totalPageRef.current);
debounceParseData();
};
const createChunkConnection = async () => { const createChunkConnection = async () => {
cacheDataRef.current = ''; cacheDataRef.current = '';
@@ -188,12 +218,20 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
createChunkConnection(); createChunkConnection();
loadMoreDone.current = true; loadMoreDone.current = true;
} else if (isTop && page <= totalPage && page > 1) { } else if (isTop && page <= totalPage && page > 1) {
getPrePage(); // getPrePage();
} else if (isBottom && page < totalPage) { } else if (isBottom && page < totalPage) {
getNextPage(); // getNextPage();
} }
}, },
[loading, logs.length, pageSize, enableScorllLoad, page, totalPage] [
loading,
logs.length,
pageSize,
enableScorllLoad,
page,
totalPage,
createChunkConnection
]
); );
const debouncedScroll = useCallback( const debouncedScroll = useCallback(
@@ -215,9 +253,9 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
}; };
}, [url, props.params]); }, [url, props.params]);
// useEffect(() => { useEffect(() => {
// debouncedScroll(); debouncedScroll();
// }, [scrollPos]); }, [scrollPos]);
return ( return (
<div className="logs-viewer-wrap-w2"> <div className="logs-viewer-wrap-w2">
+1 -1
View File
@@ -54,7 +54,7 @@ export default {
'resources.worker.select.command': 'resources.worker.select.command':
'Select a label to generate the command and copy it using the copy button.', 'Select a label to generate the command and copy it using the copy button.',
'resources.worker.script.install': 'Script Installation', 'resources.worker.script.install': 'Script Installation',
'resources.worker.container.install': 'Container Installation', 'resources.worker.container.install': 'Container Installation(Linux Only)',
'resources.worker.cann.tips': 'resources.worker.cann.tips':
'Set <span style="color: #000;font-weight: 600">ASCEND_VISIBLE_DEVICES</span> to 0 for a single GPU or to the index range (e.g., 0-7 for 8 GPUs) for multiple GPUs.' 'Set <span style="color: #000;font-weight: 600">ASCEND_VISIBLE_DEVICES</span> to 0 for a single GPU or to the index range (e.g., 0-7 for 8 GPUs) for multiple GPUs.'
}; };
+1 -1
View File
@@ -52,7 +52,7 @@ export default {
'resources.worker.driver.install': 'resources.worker.driver.install':
'在安装 GPUStack 之前,确保系统上安装了所有必需的驱动程序和库', '在安装 GPUStack 之前,确保系统上安装了所有必需的驱动程序和库',
'resources.worker.script.install': '脚本安装', 'resources.worker.script.install': '脚本安装',
'resources.worker.container.install': '容器安装', 'resources.worker.container.install': '容器安装(仅支持 Linux)',
'resources.worker.cann.tips': 'resources.worker.cann.tips':
'若 GPU 数量为 1,则设 ASCEND_VISIBLE_DEVICES=0;若大于 1,则设为索引范围(如 8 张为 0-7)' '若 GPU 数量为 1,则设 ASCEND_VISIBLE_DEVICES=0;若大于 1,则设为索引范围(如 8 张为 0-7)'
}; };
+19 -1
View File
@@ -23,7 +23,8 @@ import {
EditOutlined, EditOutlined,
ExperimentOutlined, ExperimentOutlined,
PictureOutlined, PictureOutlined,
SyncOutlined SyncOutlined,
WechatWorkOutlined
} from '@ant-design/icons'; } from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-components'; import { PageContainer } from '@ant-design/pro-components';
import { Access, useAccess, useIntl, useNavigate } from '@umijs/max'; import { Access, useAccess, useIntl, useNavigate } from '@umijs/max';
@@ -651,6 +652,23 @@ const Models: React.FC<ModelsProps> = ({
</Tag> </Tag>
); );
} }
if (record.categories?.includes(modelCategoriesMap.llm)) {
return (
<Tag
icon={<WechatWorkOutlined />}
style={{
margin: 0,
opacity: 1,
paddingInline: 8,
borderRadius: 12,
transform: 'scale(0.9)'
}}
color="green"
>
LLM
</Tag>
);
}
return null; return null;
}, },
[intl] [intl]
+3 -1
View File
@@ -248,11 +248,13 @@ export const modelCategoriesMap = {
text_to_speech: 'text_to_speech', text_to_speech: 'text_to_speech',
speech_to_text: 'speech_to_text', speech_to_text: 'speech_to_text',
embedding: 'embedding', embedding: 'embedding',
reranker: 'reranker' reranker: 'reranker',
llm: 'llm'
}; };
export const modelCategories = [ export const modelCategories = [
{ label: 'common.options.auto', value: null, locale: true }, { label: 'common.options.auto', value: null, locale: true },
{ label: 'LLM', value: modelCategoriesMap.llm },
{ label: 'Image', value: 'image' }, { label: 'Image', value: 'image' },
{ label: 'Text-to-speech', value: 'text_to_speech' }, { label: 'Text-to-speech', value: 'text_to_speech' },
{ label: 'Speech-to-text', value: 'speech_to_text' }, { label: 'Speech-to-text', value: 'speech_to_text' },
+2 -1
View File
@@ -1,5 +1,6 @@
import IconFont from '@/components/icon-font'; import IconFont from '@/components/icon-font';
import HotKeys from '@/config/hotkeys'; import HotKeys from '@/config/hotkeys';
import { modelCategoriesMap } from '@/pages/llmodels/config';
import { PageContainer } from '@ant-design/pro-components'; import { PageContainer } from '@ant-design/pro-components';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Button, Space } from 'antd'; import { Button, Space } from 'antd';
@@ -30,7 +31,7 @@ const PlaygroundEmbedding: React.FC = () => {
const getModelListByEmbedding = async () => { const getModelListByEmbedding = async () => {
try { try {
const params = { const params = {
categories: 'embedding' categories: modelCategoriesMap.embedding
}; };
const res = await queryModelsList(params); const res = await queryModelsList(params);
const list = _.map(res.data || [], (item: any) => { const list = _.map(res.data || [], (item: any) => {
+2 -1
View File
@@ -2,6 +2,7 @@ import IconFont from '@/components/icon-font';
import breakpoints from '@/config/breakpoints'; import breakpoints from '@/config/breakpoints';
import HotKeys from '@/config/hotkeys'; import HotKeys from '@/config/hotkeys';
import useWindowResize from '@/hooks/use-window-resize'; import useWindowResize from '@/hooks/use-window-resize';
import { modelCategoriesMap } from '@/pages/llmodels/config';
import { DiffOutlined, HighlightOutlined } from '@ant-design/icons'; import { DiffOutlined, HighlightOutlined } from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-components'; import { PageContainer } from '@ant-design/pro-components';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
@@ -85,7 +86,7 @@ const TextToImages: React.FC = () => {
const getModelList = async () => { const getModelList = async () => {
try { try {
const params = { const params = {
categories: 'image', categories: modelCategoriesMap.image,
with_meta: true with_meta: true
}; };
const res = await queryModelsList(params); const res = await queryModelsList(params);
+2 -1
View File
@@ -2,6 +2,7 @@ import IconFont from '@/components/icon-font';
import breakpoints from '@/config/breakpoints'; import breakpoints from '@/config/breakpoints';
import HotKeys from '@/config/hotkeys'; import HotKeys from '@/config/hotkeys';
import useWindowResize from '@/hooks/use-window-resize'; import useWindowResize from '@/hooks/use-window-resize';
import { modelCategoriesMap } from '@/pages/llmodels/config';
import { MessageOutlined, OneToOneOutlined } from '@ant-design/icons'; import { MessageOutlined, OneToOneOutlined } from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-components'; import { PageContainer } from '@ant-design/pro-components';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
@@ -79,7 +80,7 @@ const Playground: React.FC = () => {
const getModelList = async () => { const getModelList = async () => {
try { try {
const params = { const params = {
categories: '', categories: modelCategoriesMap.llm,
with_meta: true with_meta: true
}; };
const res = await queryModelsList(params); const res = await queryModelsList(params);
+2 -1
View File
@@ -1,5 +1,6 @@
import IconFont from '@/components/icon-font'; import IconFont from '@/components/icon-font';
import HotKeys from '@/config/hotkeys'; import HotKeys from '@/config/hotkeys';
import { modelCategoriesMap } from '@/pages/llmodels/config';
import { PageContainer } from '@ant-design/pro-components'; import { PageContainer } from '@ant-design/pro-components';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Button, Space } from 'antd'; import { Button, Space } from 'antd';
@@ -32,7 +33,7 @@ const PlaygroundRerank: React.FC = () => {
const getModelListByReranker = async () => { const getModelListByReranker = async () => {
try { try {
const params = { const params = {
categories: 'reranker' categories: modelCategoriesMap.reranker
}; };
const res = await queryModelsList(params); const res = await queryModelsList(params);
const list = _.map(res.data || [], (item: any) => { const list = _.map(res.data || [], (item: any) => {
+3 -2
View File
@@ -2,6 +2,7 @@ import IconFont from '@/components/icon-font';
import breakpoints from '@/config/breakpoints'; import breakpoints from '@/config/breakpoints';
import HotKeys from '@/config/hotkeys'; import HotKeys from '@/config/hotkeys';
import useWindowResize from '@/hooks/use-window-resize'; import useWindowResize from '@/hooks/use-window-resize';
import { modelCategoriesMap } from '@/pages/llmodels/config';
import { AudioOutlined } from '@ant-design/icons'; import { AudioOutlined } from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-components'; import { PageContainer } from '@ant-design/pro-components';
import { useIntl, useSearchParams } from '@umijs/max'; import { useIntl, useSearchParams } from '@umijs/max';
@@ -102,7 +103,7 @@ const Playground: React.FC = () => {
const getTextToSpeechModels = async () => { const getTextToSpeechModels = async () => {
try { try {
const params = { const params = {
categories: 'text_to_speech', categories: modelCategoriesMap.text_to_speech,
with_meta: true with_meta: true
}; };
const res = await queryModelsList(params); const res = await queryModelsList(params);
@@ -122,7 +123,7 @@ const Playground: React.FC = () => {
const getSpeechToText = async () => { const getSpeechToText = async () => {
try { try {
const params = { const params = {
categories: 'speech_to_text', categories: modelCategoriesMap.speech_to_text,
with_meta: true with_meta: true
}; };
const res = await queryModelsList(params); const res = await queryModelsList(params);
@@ -41,7 +41,7 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
width={600} width={600}
styles={{ styles={{
body: { body: {
height: 420 height: 550
} }
}} }}
footer={null} footer={null}
@@ -1,7 +1,7 @@
import { GPUStackVersionAtom } from '@/atoms/user'; import { GPUStackVersionAtom } from '@/atoms/user';
import { getAtomStorage } from '@/atoms/utils'; import { getAtomStorage } from '@/atoms/utils';
import HighlightCode from '@/components/highlight-code'; import HighlightCode from '@/components/highlight-code';
import { BulbOutlined, WarningOutlined } from '@ant-design/icons'; import { WarningOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Button, Radio } from 'antd'; import { Button, Radio } from 'antd';
import React from 'react'; import React from 'react';
@@ -31,13 +31,13 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
return commandCode?.registerWorker({ return commandCode?.registerWorker({
server: origin, server: origin,
tag: version, tag: version,
token: props.token token: '${mytoken}'
}); });
} }
return commandCode?.registerWorker({ return commandCode?.registerWorker({
server: origin, server: origin,
tag: `${version}-${activeKey}`, tag: `${version}-${activeKey}`,
token: props.token token: '${mytoken}'
}); });
}, [versionInfo, activeKey, props.token, origin]); }, [versionInfo, activeKey, props.token, origin]);
@@ -78,6 +78,23 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
</Button> </Button>
</li> </li>
</ul> </ul>
<h3>1. {intl.formatMessage({ id: 'resources.worker.add.step1' })}</h3>
<HighlightCode
code={addWorkerGuide.mac.getToken}
theme="dark"
></HighlightCode>
<h3>
2. {intl.formatMessage({ id: 'resources.worker.add.step2' })}{' '}
<span
className="font-size-12"
style={{ color: 'var(--ant-color-text-tertiary)' }}
dangerouslySetInnerHTML={{
__html: `(${intl.formatMessage({
id: 'resources.worker.add.step2.tips'
})})`
}}
></span>
</h3>
<div className="m-b-20"> <div className="m-b-20">
<Radio.Group <Radio.Group
block block
@@ -99,9 +116,8 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
></div> ></div>
)} )}
<HighlightCode theme="dark" code={code}></HighlightCode> <HighlightCode theme="dark" code={code}></HighlightCode>
<h3> <h3 className="m-b-0">
<BulbOutlined className="m-r-5"></BulbOutlined> 3. {intl.formatMessage({ id: 'resources.worker.add.step3' })}
{intl.formatMessage({ id: 'resources.worker.add.step3' })}
</h3> </h3>
</div> </div>
); );
@@ -1,5 +1,4 @@
import HighlightCode from '@/components/highlight-code'; import HighlightCode from '@/components/highlight-code';
import { BulbOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import React from 'react'; import React from 'react';
import { addWorkerGuide } from '../config'; import { addWorkerGuide } from '../config';
@@ -13,11 +12,34 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
return ( return (
<div className="script-install"> <div className="script-install">
<h3>1. {intl.formatMessage({ id: 'resources.worker.add.step1' })}</h3>
<h4>{intl.formatMessage({ id: 'resources.worker.linuxormaxos' })}</h4>
<HighlightCode
code={addWorkerGuide.mac.getToken}
theme="dark"
></HighlightCode>
<h4>Windows </h4>
<HighlightCode
code={addWorkerGuide.win.getToken}
theme="dark"
></HighlightCode>
<h3>
2. {intl.formatMessage({ id: 'resources.worker.add.step2' })}{' '}
<span
className="font-size-12"
style={{ color: 'var(--ant-color-text-tertiary)' }}
dangerouslySetInnerHTML={{
__html: `(${intl.formatMessage({
id: 'resources.worker.add.step2.tips'
})})`
}}
></span>
</h3>
<h4>{intl.formatMessage({ id: 'resources.worker.linuxormaxos' })}</h4> <h4>{intl.formatMessage({ id: 'resources.worker.linuxormaxos' })}</h4>
<HighlightCode <HighlightCode
code={addWorkerGuide.mac.registerWorker({ code={addWorkerGuide.mac.registerWorker({
server: origin, server: origin,
token: props.token token: '${mytoken}'
})} })}
theme="dark" theme="dark"
></HighlightCode> ></HighlightCode>
@@ -26,12 +48,11 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
theme="dark" theme="dark"
code={addWorkerGuide.win.registerWorker({ code={addWorkerGuide.win.registerWorker({
server: origin, server: origin,
token: props.token token: '${mytoken}'
})} })}
></HighlightCode> ></HighlightCode>
<h3> <h3 className="m-b-0">
<BulbOutlined className="m-r-5"></BulbOutlined> 3. {intl.formatMessage({ id: 'resources.worker.add.step3' })}
{intl.formatMessage({ id: 'resources.worker.add.step3' })}
</h3> </h3>
</div> </div>
); );