diff --git a/src/components/seal-table/components/table-row.tsx b/src/components/seal-table/components/table-row.tsx index 0e84401d..67f05c01 100644 --- a/src/components/seal-table/components/table-row.tsx +++ b/src/components/seal-table/components/table-row.tsx @@ -47,6 +47,7 @@ const TableRow: React.FC< const childrenDataRef = useRef([]); childrenDataRef.current = childrenData; const axiosToken = useRef(null); + const [updateChild, setUpdateChild] = useState(true); const { updateChunkedList, cacheDataListRef } = useUpdateChunkedList({ dataList: childrenData, @@ -111,6 +112,9 @@ const TableRow: React.FC< }; const filterUpdateChildrenHandler = () => { + if (!expanded) { + return; + } const dataList = _.filter(tableContext.allChildren, (data: any) => { return _.get(data, [childParentKey]) === _.get(record, [rowKey]); }); @@ -161,6 +165,8 @@ const TableRow: React.FC< pollTimer.current = setInterval(() => { handlePolling(); }, 1000); + } else { + handleLoadChildren(); } }; @@ -186,17 +192,13 @@ const TableRow: React.FC< } }, [expandedRowKeys]); - useEffect(() => { - if (expanded) { - handleLoadChildren(); - } - }, [expanded]); - useEffect(() => { const handleVisibilityChange = async () => { if (document.visibilityState === 'hidden') { cacheDataListRef.current = []; - // setChildrenData([]); + setUpdateChild(false); + } else { + setUpdateChild(true); } }; @@ -208,15 +210,15 @@ const TableRow: React.FC< }, []); useEffect(() => { - if (!firstLoad && expanded) { - cacheDataListRef.current = childrenData; + if (updateChild) { + // for update watch data filterUpdateChildrenHandler(); } return () => { chunkRequestRef.current?.current?.cancel?.(); cacheDataListRef.current = []; }; - }, [firstLoad, expanded, tableContext.allChildren]); + }, [updateChild, tableContext.allChildren]); const renderRowPrefix = () => { if (expandable && rowSelection) { @@ -296,7 +298,8 @@ const TableRow: React.FC< renderChildrenData() ) : ( { + const validJSONStrings = []; + let startIndex = 0; + + while (startIndex < inputStr.length) { + const openingBraceIndex = inputStr.indexOf('{', startIndex); + if (openingBraceIndex === -1) break; // No more opening braces + + let closingBraceIndex = openingBraceIndex; + let braceCount = 0; + + // find couple of braces + while (closingBraceIndex < inputStr.length) { + if (inputStr[closingBraceIndex] === '{') { + braceCount++; + } else if (inputStr[closingBraceIndex] === '}') { + braceCount--; + } + if (braceCount === 0) { + break; + } + closingBraceIndex++; + } + + if (braceCount !== 0) { + // no matching closing brace + break; + } + + const jsonString = inputStr.substring( + openingBraceIndex, + closingBraceIndex + 1 + ); + + try { + const parsedData = JSON.parse(jsonString); + validJSONStrings.push(parsedData); + startIndex = closingBraceIndex + 1; + } catch (error) { + // mabye invalid JSON + break; + } + } + + return { + parsedJSON: validJSONStrings, + remainingData: inputStr.slice(startIndex) // it is the not parsed data + }; +}; + +// Web Worker +self.onmessage = function (event) { + bufferedData += event.data; + + const { parsedJSON, remainingData } = findValidJSONStrings(bufferedData); + + if (parsedJSON.length > 0) { + postMessage(parsedJSON); + } + + // save the remaining data for the next message + bufferedData = remainingData; +}; diff --git a/src/hooks/use-chunk-request.ts b/src/hooks/use-chunk-request.ts index a6d0325b..2bae2531 100644 --- a/src/hooks/use-chunk-request.ts +++ b/src/hooks/use-chunk-request.ts @@ -36,69 +36,6 @@ export const sliceJsonStr = (text: string) => { return result; }; -export const parseJsonStr = (list: string[]) => { - return _.map(list, (str: string) => { - return JSON.parse(str); - }); -}; - -const findMatchingClosingBracket = (inputStr: string, startIndex: number) => { - let count = 0; - for (let i = startIndex; i < inputStr.length; i += 1) { - if (inputStr[i] === '{') { - count += 1; - } else if (inputStr[i] === '}') { - count -= 1; - } - - if (count === 0) { - return i; - } - } - return -1; -}; - -const findValidJSONStrings = (inputStr: string) => { - const validJSONStrings: any[] = []; - let startIndex = 0; - - while (startIndex < inputStr.length) { - const openingBraceIndex = inputStr.indexOf('{', startIndex); - if (openingBraceIndex === -1) { - break; // No more opening braces - } - - const closingBraceIndex = findMatchingClosingBracket( - inputStr, - openingBraceIndex - ); - if (closingBraceIndex === -1) { - // If no matching closing brace, set startIndex to next character - startIndex = openingBraceIndex + 1; - } else { - const jsonString = inputStr.substring( - openingBraceIndex, - closingBraceIndex + 1 - ); - try { - const data = JSON.parse(jsonString); - validJSONStrings.push(data); - } catch (error) { - // Ignore invalid JSON - } - - startIndex = closingBraceIndex + 1; - } - } - - return validJSONStrings; -}; - -const parseData = (data: string) => { - const res = findValidJSONStrings(data); - return res; -}; - export const createAxiosToken = () => { const { CancelToken } = axios; const source = CancelToken.source(); @@ -123,7 +60,7 @@ const useSetChunkRequest = () => { const particalConfig = { params: {}, contentType: 'json' }; const timer = useRef(null); const loadedSize = useRef(0); - const bufferedDataRef = useRef(''); + const workerRef = useRef(null); const reset = () => { loaded.current = 0; @@ -172,6 +109,23 @@ const useSetChunkRequest = () => { cancelWatchRequest(watchRequestList.length - 4 || 1); } + if (contentType === 'json') { + workerRef.current?.terminate(); + + workerRef.current = new Worker( + // @ts-ignore + new URL('./json-parser-worker.ts', import.meta.url) + ); + + workerRef.current.onmessage = function (event: any) { + const validJSON = event.data; + if (validJSON.length > 0) { + const result = resetResultSchema(validJSON); + handler(result); + } + }; + } + try { const { request: requestData } = await request(url, { params: { @@ -188,38 +142,17 @@ const useSetChunkRequest = () => { loaded.current = e.loaded || 0; total.current = e.total || 0; - let currentRes = sliceData(response, e.loaded, loadedSize); - let result: any[] = []; - let cres = currentRes; - if (contentType === 'json') { - // Append the new data to the buffered data - bufferedDataRef.current += currentRes; - - // Find valid JSON strings in the buffered data - let validJSON = findValidJSONStrings(bufferedDataRef.current); - if (validJSON.length > 0) { - result = resetResultSchema(validJSON); - - // Calculate the position of the last complete JSON fragment, keeping the unfinished part - const lastValidJSON = validJSON[validJSON.length - 1]; - const lastJSONIndex = bufferedDataRef.current.lastIndexOf( - JSON.stringify(lastValidJSON) - ); - bufferedDataRef.current = bufferedDataRef.current.slice( - lastJSONIndex + JSON.stringify(lastValidJSON).length - ); - } - handler(result); + let currentRes = sliceData(response, e.loaded, loadedSize); + workerRef.current.postMessage(currentRes); } else { - handler(currentRes); + handler(response); } console.log('chunkrequest===', { - result, + result: response, url, - params, - raw: cres + params }); } }); diff --git a/src/pages/llmodels/index.tsx b/src/pages/llmodels/index.tsx index 0334dca6..2002d486 100644 --- a/src/pages/llmodels/index.tsx +++ b/src/pages/llmodels/index.tsx @@ -9,7 +9,12 @@ import { import _ from 'lodash'; import qs from 'query-string'; import { memo, useCallback, useEffect, useRef, useState } from 'react'; -import { MODELS_API, MODEL_INSTANCE_API, queryModelsList } from './apis'; +import { + MODELS_API, + MODEL_INSTANCE_API, + queryModelsInstances, + queryModelsList +} from './apis'; import TableList from './components/table-list'; import { ListItem } from './config/types'; @@ -81,39 +86,60 @@ const Models: React.FC = () => { } }; - const fetchData = useCallback(async () => { - axiosToken?.cancel?.(); - axiosToken = createAxiosToken(); - setDataSource((pre) => { - pre.loading = true; - return { ...pre }; - }); + const getAllModelInstances = useCallback(async () => { try { + instancesToken.current?.cancel?.(); + instancesToken.current = createAxiosToken(); const params = { - ..._.pickBy(queryParams, (val: any) => !!val) + page: 1, + perPage: 100 }; - const res: any = await queryModelsList(params, { - cancelToken: axiosToken.token - }); - setDataSource({ - dataList: res.items || [], - loading: false, - loadend: true, - total: res.pagination.total, - deletedIds: [] + const res: any = await queryModelsInstances(params, { + token: instancesToken.current.token }); + cacheInsDataListRef.current = res.items || []; + setModelInstances(res.items || []); } catch (error) { - if (!isPageHidden.current) { + // ignore + } + }, []); + + const fetchData = useCallback( + async (loadingVal?: boolean) => { + axiosToken?.cancel?.(); + axiosToken = createAxiosToken(); + setDataSource((pre) => { + pre.loading = loadingVal ?? true; + return { ...pre }; + }); + try { + const params = { + ..._.pickBy(queryParams, (val: any) => !!val) + }; + const res: any = await queryModelsList(params, { + cancelToken: axiosToken.token + }); setDataSource({ - dataList: [], + dataList: res.items || [], loading: false, loadend: true, - total: dataSource.total, + total: res.pagination.total, deletedIds: [] }); + } catch (error) { + if (!isPageHidden.current) { + setDataSource({ + dataList: [], + loading: false, + loadend: true, + total: dataSource.total, + deletedIds: [] + }); + } } - } - }, [queryParams]); + }, + [queryParams] + ); const handlePageChange = useCallback( (page: number, pageSize: number | undefined) => { @@ -135,6 +161,7 @@ const Models: React.FC = () => { const updateInstanceHandler = (list: any) => { // filter the data _.each(list, (data: any) => { + console.log('updateInstanceHandler====', data); updateInstanceChunkedList(data); }); }; @@ -166,7 +193,7 @@ const Models: React.FC = () => { } catch (error) { // ignore } - }, []); + }, [updateInstanceHandler]); const handleOnViewLogs = useCallback(() => { isPageHidden.current = true; @@ -174,13 +201,15 @@ const Models: React.FC = () => { cacheDataListRef.current = []; cacheInsDataListRef.current = []; chunkInstanceRequedtRef.current?.current?.cancel?.(); + instancesToken.current?.cancel?.(); }, []); const handleOnCancelViewLogs = useCallback(async () => { isPageHidden.current = false; + await getAllModelInstances(); await createModelsInstanceChunkRequest(); await createModelsChunkRequest(); - fetchData(); + fetchData(false); }, [fetchData, createModelsChunkRequest, createModelsInstanceChunkRequest]); const handleSearch = useCallback(async () => { @@ -232,19 +261,25 @@ const Models: React.FC = () => { }; }, []); + useEffect(() => { + console.log('modelInstances====', modelInstances); + }, [modelInstances]); + useEffect(() => { const handleVisibilityChange = async () => { if (document.visibilityState === 'visible') { isPageHidden.current = false; + await getAllModelInstances(); await createModelsInstanceChunkRequest(); await createModelsChunkRequest(); - fetchData(); + fetchData(false); } else { isPageHidden.current = true; chunkRequedtRef.current?.current?.cancel?.(); cacheDataListRef.current = []; cacheInsDataListRef.current = []; chunkInstanceRequedtRef.current?.current?.cancel?.(); + instancesToken.current?.cancel?.(); } };