fix: add a button to last page on logs

This commit is contained in:
jialin
2025-01-08 15:26:03 +08:00
parent 2a9c501ee2
commit decb3ef2f1
21 changed files with 245 additions and 127 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ export default {
contentPadding: '12px 16px' contentPadding: '12px 16px'
}, },
Tooltip: { Tooltip: {
colorBgSpotlight: 'rgba(110,110,110,1)' colorBgSpotlight: '#3e3e3e'
// sizePopupArrow: 0 // sizePopupArrow: 0
}, },
Slider: { Slider: {
+4 -4
View File
@@ -1,5 +1,5 @@
import { CloseCircleOutlined } from '@ant-design/icons'; import { CloseCircleOutlined } from '@ant-design/icons';
import { Progress } from 'antd'; import { Progress, ProgressProps } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import ResizeObserver from 'rc-resize-observer'; import ResizeObserver from 'rc-resize-observer';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
@@ -21,6 +21,7 @@ interface SingleImageProps {
autoBgColor?: boolean; autoBgColor?: boolean;
editable?: boolean; editable?: boolean;
style?: React.CSSProperties; style?: React.CSSProperties;
loadingSize?: ProgressProps['size'];
progressType?: 'line' | 'circle' | 'dashboard'; progressType?: 'line' | 'circle' | 'dashboard';
progressColor?: string; progressColor?: string;
progressWidth?: number; progressWidth?: number;
@@ -42,10 +43,8 @@ const SingleImage: React.FC<SingleImageProps> = (props) => {
dataUrl, dataUrl,
style, style,
autoBgColor, autoBgColor,
progressColor = 'var(--ant-color-primary)',
progressWidth = 2,
preview = true, preview = true,
progressType = 'dashboard' loadingSize = 'default'
} = props; } = props;
const imgWrapper = React.useRef<HTMLSpanElement>(null); const imgWrapper = React.useRef<HTMLSpanElement>(null);
@@ -137,6 +136,7 @@ const SingleImage: React.FC<SingleImageProps> = (props) => {
<Progress <Progress
percent={progress} percent={progress}
type="dashboard" type="dashboard"
size={loadingSize}
steps={{ count: 50, gap: 2 }} steps={{ count: 50, gap: 2 }}
format={() => ( format={() => (
<span className="font-size-20">{progress}%</span> <span className="font-size-20">{progress}%</span>
+97 -53
View File
@@ -34,6 +34,9 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
onSave, onSave,
uploadButton uploadButton
}) => { }) => {
const MIN_SCALE = 0.4;
const MAX_SCALE = 10;
const zoomFactor = 1.1;
const intl = useIntl(); const intl = useIntl();
const canvasRef = useRef<HTMLCanvasElement>(null); const canvasRef = useRef<HTMLCanvasElement>(null);
const overlayCanvasRef = useRef<HTMLCanvasElement>(null); const overlayCanvasRef = useRef<HTMLCanvasElement>(null);
@@ -47,25 +50,20 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
const autoScale = useRef<number>(1); const autoScale = useRef<number>(1);
const cursorRef = useRef<HTMLDivElement>(null); const cursorRef = useRef<HTMLDivElement>(null);
const [imgLoaded, setImgLoaded] = useState(false); const [imgLoaded, setImgLoaded] = useState(false);
const offsetRef = useRef({ x: 0, y: 0 });
let scale = 1; const originPosition = useRef({ x: 0, y: 0 });
let offsetX = 0;
let offsetY = 0;
const MIN_SCALE = 0.5;
const MAX_SCALE = 5;
const getTransformedPoint = (event: React.MouseEvent<HTMLCanvasElement>) => { const getTransformedPoint = (event: React.MouseEvent<HTMLCanvasElement>) => {
const overlayCanvas = overlayCanvasRef.current!; const overlayCanvas = overlayCanvasRef.current!;
const rect = overlayCanvas.getBoundingClientRect(); const rect = overlayCanvas.getBoundingClientRect();
// 获取鼠标在画布上的原始坐标
const x = event.clientX - rect.left; const x = event.clientX - rect.left;
const y = event.clientY - rect.top; const y = event.clientY - rect.top;
const transformedX = x - overlayCanvas.width / 2; // 考虑缩放比例和偏移量
const transformedY = y - overlayCanvas.height / 2; const transformedX = (x - offsetRef.current.x) / autoScale.current;
const transformedY = (y - offsetRef.current.y) / autoScale.current;
console.log('Mouse Coordinates (Transformed):', transformedX, transformedY);
return { x: transformedX, y: transformedY }; return { x: transformedX, y: transformedY };
}; };
@@ -113,6 +111,10 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
overlayCtx!.translate(ctx!.canvas.width / 2, ctx!.canvas.height / 2); overlayCtx!.translate(ctx!.canvas.width / 2, ctx!.canvas.height / 2);
ctx!.translate(ctx!.canvas.width / 2, ctx!.canvas.height / 2); ctx!.translate(ctx!.canvas.width / 2, ctx!.canvas.height / 2);
offscreenCtx!.translate(ctx!.canvas.width / 2, ctx!.canvas.height / 2); offscreenCtx!.translate(ctx!.canvas.width / 2, ctx!.canvas.height / 2);
originPosition.current = {
x: ctx!.canvas.width / 2,
y: ctx!.canvas.height / 2
};
}, [canvasRef.current, overlayCanvasRef.current]); }, [canvasRef.current, overlayCanvasRef.current]);
const scaleCanvasSize = useCallback(() => { const scaleCanvasSize = useCallback(() => {
@@ -439,8 +441,8 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
1 1
); );
canvas!.width = img.width * scale; canvas!.width = img.width;
canvas!.height = img.height * scale; canvas!.height = img.height;
autoScale.current = scale / autoScale.current; autoScale.current = scale / autoScale.current;
@@ -457,6 +459,7 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
canvas!.width, canvas!.width,
canvas!.height canvas!.height
); );
canvas!.style.transform = `scale(${scale})`;
resolve(); resolve();
}; };
}); });
@@ -502,61 +505,102 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
const y = event.clientY - rect.top; const y = event.clientY - rect.top;
// 考虑缩放比例和偏移量 // 考虑缩放比例和偏移量
const transformedX = (x - offsetX) / scale; const transformedX = (x - offsetRef.current.x) / autoScale.current;
const transformedY = (y - offsetY) / scale; const transformedY = (y - offsetRef.current.y) / autoScale.current;
return { x: transformedX, y: transformedY }; return { x: transformedX, y: transformedY };
}; };
const getMousePosition = (event: any) => {
const canvas = overlayCanvasRef.current!;
const rect = canvas.getBoundingClientRect();
const mouseX = event.clientX - rect.left;
const mouseY = event.clientY - rect.top;
const { x: originX, y: originY } = originPosition.current;
const canvasX = (mouseX - originX) / autoScale.current;
const canvasY = (mouseY - originY) / autoScale.current;
return { x: canvasX, y: canvasY };
};
const updateCavasTransform = (event: any) => {
const canvas = overlayCanvasRef.current!;
const rect = canvas.getBoundingClientRect();
const mouseX = event.clientX - rect.left;
const mouseY = event.clientY - rect.top;
// 鼠标在中心坐标系中的位置
const { x: originX, y: originY } = originPosition.current;
const canvasX = (mouseX - originX) / autoScale.current;
const canvasY = (mouseY - originY) / autoScale.current;
// 更新缩放比例
// max scale: 2, min scale: 0.3
const zoomIn = event.deltaY < 0;
let newScale = autoScale.current;
if (zoomIn) {
newScale = Math.min(MAX_SCALE, autoScale.current * zoomFactor);
} else {
newScale = Math.max(MIN_SCALE, autoScale.current / zoomFactor);
}
console.log('New Scale:', newScale);
autoScale.current = newScale;
// 更新原点
const neworiginX = mouseX - canvasX * newScale;
const neworiginY = mouseY - canvasY * newScale;
originPosition.current = { x: neworiginX, y: neworiginY };
const overlayCtx = overlayCanvasRef.current!.getContext('2d')!;
const canvasCtx = canvasRef.current!.getContext('2d')!;
// 更新 Canvas 的变换
overlayCtx.setTransform(
autoScale.current,
0,
0,
autoScale.current,
neworiginX,
neworiginY
);
canvasCtx.setTransform(
autoScale.current,
0,
0,
autoScale.current,
neworiginX,
neworiginY
);
};
const handleOnWheel = (event: WheelEvent) => { const handleOnWheel = (event: WheelEvent) => {
event.preventDefault(); event.preventDefault();
const zoomFactor = event.deltaY < 0 ? 1.1 : 0.9; updateCavasTransform(event);
const newScale = Math.min(
MAX_SCALE,
Math.max(MIN_SCALE, scale * zoomFactor)
);
const rect = canvasRef.current!.getBoundingClientRect(); overlayCanvasRef.current!.style.transform = `scale(${autoScale.current})`;
const mouseX = event.clientX - rect.left; canvasRef.current!.style.transform = `scale(${autoScale.current})`;
const mouseY = event.clientY - rect.top;
// 计算新的偏移量
offsetX = mouseX - (mouseX - offsetX) * (newScale / scale);
offsetY = mouseY - (mouseY - offsetY) * (newScale / scale);
// 更新缩放比例
scale = newScale;
// 设置画布的变换
const overlayCtx = overlayCanvasRef.current!.getContext('2d')!;
overlayCtx.setTransform(scale, 0, 0, scale, offsetX, offsetY);
const canvasCtx = canvasRef.current!.getContext('2d')!;
canvasCtx.setTransform(scale, 0, 0, scale, offsetX, offsetY);
overlayCanvasRef.current!.style.transform = `scale(${scale})`;
canvasRef.current!.style.transform = `scale(${scale})`;
console.log('Zoom:', scale, offsetX, offsetY);
}; };
useEffect(() => { useEffect(() => {
initializeImage(); initializeImage();
}, [initializeImage]); }, [initializeImage]);
// useEffect(() => { useEffect(() => {
// const container = containerRef.current; const container = containerRef.current;
// if (!container) return; if (!container) return;
// if (container) { if (container) {
// resizeObserver.current = new ResizeObserver( resizeObserver.current = new ResizeObserver(
// _.throttle(handleResize, 100) _.throttle(handleResize, 100)
// ); );
// resizeObserver.current.observe(container); resizeObserver.current.observe(container);
// } }
// return () => { return () => {
// resizeObserver.current?.disconnect(); resizeObserver.current?.disconnect();
// }; };
// }, [handleResize, containerRef.current]); }, [handleResize, containerRef.current]);
useEffect(() => { useEffect(() => {
createOffscreenCanvas(); createOffscreenCanvas();
+38 -16
View File
@@ -1,4 +1,8 @@
import { DownOutlined, UpOutlined } from '@ant-design/icons'; import {
DownOutlined,
UpOutlined,
VerticalLeftOutlined
} from '@ant-design/icons';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Button, Tooltip } from 'antd'; import { Button, Tooltip } from 'antd';
import React from 'react'; import React from 'react';
@@ -10,10 +14,11 @@ interface LogsPaginationProps {
pageSize?: number; pageSize?: number;
onPrev?: () => void; onPrev?: () => void;
onNext?: () => void; onNext?: () => void;
onBackend?: () => void;
} }
const LogsPagination: React.FC<LogsPaginationProps> = (props) => { const LogsPagination: React.FC<LogsPaginationProps> = (props) => {
const { page, total, pageSize, onNext, onPrev } = props; const { page, total, pageSize, onNext, onPrev, onBackend } = props;
const intl = useIntl(); const intl = useIntl();
const handleOnPrev = () => { const handleOnPrev = () => {
@@ -27,6 +32,7 @@ const LogsPagination: React.FC<LogsPaginationProps> = (props) => {
return ( return (
<div className="pagination"> <div className="pagination">
<Tooltip <Tooltip
placement="left"
title={intl.formatMessage( title={intl.formatMessage(
{ id: 'models.logs.pagination.prev' }, { id: 'models.logs.pagination.prev' },
{ lines: pageSize } { lines: pageSize }
@@ -46,21 +52,37 @@ const LogsPagination: React.FC<LogsPaginationProps> = (props) => {
<span className="total">{total}</span> <span className="total">{total}</span>
</span> </span>
{page < total && ( {page < total && (
<Tooltip <>
title={intl.formatMessage( <Tooltip
{ id: 'models.logs.pagination.next' }, placement="left"
{ lines: pageSize } title={intl.formatMessage(
)} { id: 'models.logs.pagination.next' },
> { lines: pageSize }
<Button )}
onClick={handleOnNext}
type="text"
shape="circle"
style={{ color: 'rgba(255,255,255,.7)' }}
> >
<DownOutlined /> <Button
</Button> onClick={handleOnNext}
</Tooltip> type="text"
shape="circle"
style={{ color: 'rgba(255,255,255,.7)' }}
>
<DownOutlined />
</Button>
</Tooltip>
<Tooltip
title={intl.formatMessage({ id: 'models.logs.pagination.last' })}
placement="left"
>
<Button
onClick={onBackend}
type="text"
shape="circle"
style={{ color: 'rgba(255,255,255,.7)', marginTop: 10 }}
>
<VerticalLeftOutlined rotate={90} />
</Button>
</Tooltip>
</>
)} )}
</div> </div>
); );
@@ -150,5 +150,10 @@ self.onmessage = function (event) {
content: item.content, content: item.content,
uid: item.uid uid: item.uid
})); }));
console.log('result===', result);
self.postMessage(result); self.postMessage(result);
}; };
self.onerror = function (event) {
console.error('parse logs error===', event);
};
@@ -11,6 +11,10 @@
background-color: rgba(71, 71, 71, 100%) !important; background-color: rgba(71, 71, 71, 100%) !important;
} }
.ant-btn {
background-color: rgba(71, 71, 71, 50%) !important;
}
.pages { .pages {
display: flex; display: flex;
justify-content: center; justify-content: center;
+44 -23
View File
@@ -49,6 +49,7 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
abort() { abort() {
chunkRequedtRef.current?.current?.abort?.(); chunkRequedtRef.current?.current?.abort?.();
logParseWorker.current?.terminate?.();
} }
})); }));
@@ -65,6 +66,7 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
logParseWorker.current.onmessage = (event: any) => { logParseWorker.current.onmessage = (event: any) => {
const res = event.data; const res = event.data;
console.log('res===', res);
setLogs(res); setLogs(res);
}; };
@@ -73,7 +75,7 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
logParseWorker.current.terminate(); logParseWorker.current.terminate();
} }
}; };
}, [setLogs, logParseWorker.current]); }, []);
const debounceLoading = _.debounce(() => { const debounceLoading = _.debounce(() => {
setLoading(false); setLoading(false);
@@ -88,26 +90,26 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
const getLastPage = (data: string) => { const getLastPage = (data: string) => {
const list = _.split(data.trim(), '\n'); const list = _.split(data.trim(), '\n');
let result = '';
if (!enableScorllLoad) { console.log('getlastPage===', list.length, enableScorllLoad, pageSize);
return list.join('\n');
}
if (list.length <= pageSize) {
setTotalPage(1);
return data;
}
setLoading(true); setLoading(true);
const totalPage = Math.ceil(list.length / pageSize); if (!enableScorllLoad) {
setTotalPage(totalPage); result = list.join('\n');
setPage(() => totalPage); } else if (list.length <= pageSize) {
pageRef.current = totalPage; setTotalPage(1);
totalPageRef.current = totalPage; result = data;
const lastPage = list.slice(-pageSize).join('\n'); } else {
console.log('list.length===', list.length, totalPage, page); const totalPage = Math.ceil(list.length / pageSize);
setTotalPage(totalPage);
setPage(() => totalPage);
pageRef.current = totalPage;
totalPageRef.current = totalPage;
const lastPage = list.slice(-pageSize).join('\n');
result = lastPage;
}
debounceLoading(); debounceLoading();
return lastPage; return result;
}; };
const getCurrentPage = () => { const getCurrentPage = () => {
@@ -116,7 +118,6 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
if (newPage < 1) { if (newPage < 1) {
newPage = 1; newPage = 1;
} }
console.log('currentpage===', newPage);
const start = (newPage - 1) * pageSize; const start = (newPage - 1) * pageSize;
const end = newPage * pageSize; const end = newPage * pageSize;
const currentPage = list.slice(start, end).join('\n'); const currentPage = list.slice(start, end).join('\n');
@@ -164,6 +165,20 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
}); });
}, [totalPage, page, pageSize]); }, [totalPage, page, pageSize]);
const handleonBackend = useCallback(() => {
const list = _.split(cacheDataRef.current.trim(), '\n');
let newPage = totalPage;
const start = (newPage - 1) * pageSize;
const end = newPage * pageSize;
const nextPage = list.slice(start, end).join('\n');
setPage(() => newPage);
setScrollPos(['bottom', newPage]);
pageRef.current = newPage;
logParseWorker.current.postMessage({
inputStr: nextPage
});
}, [totalPage, page, pageSize]);
const debounceParseData = _.debounce(() => { const debounceParseData = _.debounce(() => {
if (pageRef.current === totalPageRef.current) { if (pageRef.current === totalPageRef.current) {
logParseWorker.current.postMessage({ logParseWorker.current.postMessage({
@@ -175,20 +190,25 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
}, 100); }, 100);
const updateContent = (inputStr: string) => { const updateContent = (inputStr: string) => {
console.log('inputStr===', inputStr);
const data = inputStr.replace(replaceLineRegex, '\n'); const data = inputStr.replace(replaceLineRegex, '\n');
if (isClean(data)) { if (isClean(data)) {
cacheDataRef.current = data; cacheDataRef.current = data;
} else { } else {
cacheDataRef.current += data; cacheDataRef.current += data;
} }
console.log('page===', pageRef.current, totalPageRef.current); if (pageRef.current === totalPageRef.current) {
debounceParseData(); logParseWorker.current.postMessage({
inputStr: getLastPage(cacheDataRef.current)
});
} else {
getCurrentPage();
}
}; };
const createChunkConnection = async () => { const createChunkConnection = async () => {
cacheDataRef.current = ''; cacheDataRef.current = '';
chunkRequedtRef.current?.current?.abort?.(); chunkRequedtRef.current?.current?.abort?.();
chunkRequedtRef.current = setChunkFetch({ chunkRequedtRef.current = setChunkFetch({
url, url,
params: { params: {
@@ -287,6 +307,7 @@ const LogsViewer: React.FC<LogsViewerProps> = forwardRef((props, ref) => {
pageSize={pageSize} pageSize={pageSize}
onNext={getNextPage} onNext={getNextPage}
onPrev={getPrePage} onPrev={getPrePage}
onBackend={handleonBackend}
></LogsPagination> ></LogsPagination>
</div> </div>
</div> </div>
+1
View File
@@ -74,6 +74,7 @@ export default {
'More {backend} parameter details', 'More {backend} parameter details',
'models.logs.pagination.prev': 'Previous {lines} Lines', 'models.logs.pagination.prev': 'Previous {lines} Lines',
'models.logs.pagination.next': 'Next {lines} Lines', 'models.logs.pagination.next': 'Next {lines} Lines',
'models.logs.pagination.last': 'Last Page',
'models.form.localPath': 'Local Path', 'models.form.localPath': 'Local Path',
'models.form.filePath': 'Model Path', 'models.form.filePath': 'Model Path',
'models.form.backendVersion': 'Backend Version', 'models.form.backendVersion': 'Backend Version',
+1 -1
View File
@@ -85,7 +85,7 @@ export default {
'Upload an audio file or start recording', 'Upload an audio file or start recording',
'playground.audio.enablemic': 'playground.audio.enablemic':
"Enable microphone access in your browser's settings.", "Enable microphone access in your browser's settings.",
'playground.audio.enablemic.doc': 'Refer to', 'playground.audio.enablemic.doc': 'Refer to this link',
'playground.audio.startrecord': 'Start Recording', 'playground.audio.startrecord': 'Start Recording',
'playground.audio.stoprecord': 'Stop Recording', 'playground.audio.stoprecord': 'Stop Recording',
'playground.audio.generating.tips': 'Generated text will appear here.', 'playground.audio.generating.tips': 'Generated text will appear here.',
+1 -2
View File
@@ -55,6 +55,5 @@ export default {
'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(Linux Only)', '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 the required GPU indices. For GPU0 to GPU3, use <span style='color: #000;font-weight: 600'>ASCEND_VISIBLE_DEVICES=0,1,2,3</span> or <span style='color: #000;font-weight: 600'>ASCEND_VISIBLE_DEVICES=0-3</span>.`
'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
View File
@@ -71,6 +71,7 @@ export default {
'models.form.backend_parameters.vllm.tips': '更多 {backend} 参数说明查看', 'models.form.backend_parameters.vllm.tips': '更多 {backend} 参数说明查看',
'models.logs.pagination.prev': '上一 {lines} 行', 'models.logs.pagination.prev': '上一 {lines} 行',
'models.logs.pagination.next': '下一 {lines} 行', 'models.logs.pagination.next': '下一 {lines} 行',
'models.logs.pagination.last': '最后一页',
'models.form.localPath': '本地路径', 'models.form.localPath': '本地路径',
'models.form.filePath': '模型路径', 'models.form.filePath': '模型路径',
'models.form.backendVersion': '后端版本', 'models.form.backendVersion': '后端版本',
+1 -1
View File
@@ -54,5 +54,5 @@ export default {
'resources.worker.script.install': '脚本安装', 'resources.worker.script.install': '脚本安装',
'resources.worker.container.install': '容器安装(仅支持 Linux)', 'resources.worker.container.install': '容器安装(仅支持 Linux)',
'resources.worker.cann.tips': 'resources.worker.cann.tips':
'若 GPU 数量为 1,则设 ASCEND_VISIBLE_DEVICES=0;若大于 1,则设为索引范围(如 8 张为 0-7)' '按需要挂载的 GPU index 设置 <span style="color: #000;font-weight: 600">ASCEND_VISIBLE_DEVICES</span>,如需挂载 GPU0 - GPU3,则设为 <span style="color: #000;font-weight: 600">ASCEND_VISIBLE_DEVICES=0,1,2,3</span> 或 <span style="color: #000;font-weight: 600">ASCEND_VISIBLE_DEVICES=0-3</span>'
}; };
@@ -50,7 +50,7 @@ const ActiveTable = () => {
render: (text: any, record: any) => { render: (text: any, record: any) => {
return ( return (
<AutoTooltip ghost> <AutoTooltip ghost>
<span>{text}</span> <span>{text || 'N/A'}</span>
</AutoTooltip> </AutoTooltip>
); );
} }
+1 -1
View File
@@ -151,7 +151,7 @@ export async function queryModelScopeModels(
config?: any config?: any
) { ) {
const tagsCriterion = params.tags?.map((tag: string) => { const tagsCriterion = params.tags?.map((tag: string) => {
return { category: 'tags', predicate: 'contains', values: [tag] }; return { category: 'libraries', predicate: 'contains', values: [tag] };
}); });
const tasksCriterion = params.tasks?.map((task: string) => { const tasksCriterion = params.tasks?.map((task: string) => {
return { category: 'tasks', predicate: 'contains', values: [task] }; return { category: 'tasks', predicate: 'contains', values: [task] };
@@ -303,7 +303,6 @@ const AdvanceConfig: React.FC<AdvanceConfigProps> = (props) => {
{ backend: backendParamsTips.backend || '' } { backend: backendParamsTips.backend || '' }
)}{' '} )}{' '}
<Typography.Link <Typography.Link
style={{ color: 'var(--ant-blue-4)' }}
href={backendParamsTips.link} href={backendParamsTips.link}
target="_blank" target="_blank"
> >
@@ -125,6 +125,10 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
return [...shardFileListResult, ...newGeneralFileList]; return [...shardFileListResult, ...newGeneralFileList];
}, []); }, []);
const hfFileFilter = (file: any) => {
return filterRegGGUF.test(file.path) || _.includes(file.path, '.gguf');
};
// hugging face files // hugging face files
const getHuggingfaceFiles = async () => { const getHuggingfaceFiles = async () => {
try { try {
@@ -139,7 +143,7 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
}); });
const list = _.filter(fileList, (file: any) => { const list = _.filter(fileList, (file: any) => {
return filterRegGGUF.test(file.path) || _.includes(file.path, '.gguf'); return hfFileFilter(file);
}); });
return list; return list;
@@ -148,6 +152,10 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
} }
}; };
const modelscopeFileFilter = (file: any) => {
return filterRegGGUF.test(file.Path) && file.Type === 'blob';
};
// modelscope files // modelscope files
const getModelScopeFiles = async () => { const getModelScopeFiles = async () => {
try { try {
@@ -161,7 +169,7 @@ const HFModelFile: React.FC<HFModelFileProps> = forwardRef((props, ref) => {
} }
); );
const fileList = _.filter(_.get(data, ['Data', 'Files']), (file: any) => { const fileList = _.filter(_.get(data, ['Data', 'Files']), (file: any) => {
return filterRegGGUF.test(file.Path) && file.Type === 'blob'; return modelscopeFileFilter(file);
}); });
const list = _.map(fileList, (item: any) => { const list = _.map(fileList, (item: any) => {
@@ -51,6 +51,8 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
url: `${MODELS_API}/${props.modelId}/instances`, url: `${MODELS_API}/${props.modelId}/instances`,
handler: updateHandler handler: updateHandler
}); });
} else {
logsViewerRef.current?.abort();
} }
return () => { return () => {
requestRef.current?.current?.cancel?.(); requestRef.current?.current?.cancel?.();
@@ -302,7 +302,7 @@ const AudioInput: React.FC<AudioInputProps> = (props) => {
href={`${externalRefer.audioPermission}`} href={`${externalRefer.audioPermission}`}
target="_blank" target="_blank"
style={{ style={{
color: 'var(--ant-blue-5)' paddingInline: 0
}} }}
> >
{intl.formatMessage({ id: 'playground.audio.enablemic.doc' })} {intl.formatMessage({ id: 'playground.audio.enablemic.doc' })}
@@ -6,7 +6,7 @@ import { InfoCircleOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Form, InputNumber, Slider, Tooltip } from 'antd'; import { Form, InputNumber, Slider, Tooltip } from 'antd';
import _ from 'lodash'; import _ from 'lodash';
import { memo, useCallback, useEffect, useId } from 'react'; import { memo, useCallback, useEffect, useId, useState } from 'react';
import CustomLabelStyles from '../style/custom-label.less'; import CustomLabelStyles from '../style/custom-label.less';
type ParamsSettingsFormProps = { type ParamsSettingsFormProps = {
@@ -29,12 +29,13 @@ type ParamsSettingsProps = {
globalParams?: ParamsSettingsFormProps; globalParams?: ParamsSettingsFormProps;
}; };
const METAKEYS: Record<string, string> = { const METAKEYS: Record<string, any> = {
seed: 'seed', seed: 'seed',
stop: 'stop', stop: 'stop',
temperature: 'temperature', temperature: 'temperature',
top_p: 'top_p', top_p: 'top_p',
max_tokens: 'n_ctx' n_slot_ctx: 'max_tokens',
max_model_len: 'max_tokens'
}; };
const ParamsSettings: React.FC<ParamsSettingsProps> = ({ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
@@ -56,6 +57,8 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
}; };
const [form] = Form.useForm(); const [form] = Form.useForm();
const formId = useId(); const formId = useId();
const [metaData, setMetaData] = useState<Record<string, any>>({});
const [firstLoad, setFirstLoad] = useState(true);
const handleOnFinish = (values: any) => { const handleOnFinish = (values: any) => {
console.log('handleOnFinish', values); console.log('handleOnFinish', values);
@@ -97,18 +100,16 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
const handleModelChange = (val: string) => { const handleModelChange = (val: string) => {
const model = _.find(modelList, { value: val }); const model = _.find(modelList, { value: val });
const modelMeta = model?.meta || {}; const modelMeta = model?.meta || {};
const keys = Object.keys(METAKEYS).map((k: string) => { const modelMetaValue = _.pick(modelMeta, _.keys(METAKEYS));
return METAKEYS[k]; const obj = Object.entries(METAKEYS).reduce((acc: any, [key, value]) => {
}); const val = modelMetaValue[key];
const modelMetaKeys = _.pick(modelMeta, keys); if (val && _.hasIn(modelMetaValue, key)) {
const obj = _.reduce( acc[value] = val;
METAKEYS, }
(result: any, value: any, key: string) => { return acc;
result[key] = modelMetaKeys[value]; }, {});
return result; form.setFieldsValue(obj);
}, setMetaData(obj);
{}
);
return obj; return obj;
}; };
@@ -127,11 +128,14 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
...mergeData, ...mergeData,
model: model model: model
}); });
setFirstLoad(false);
}, [modelList, showModelSelector, selectedModel]); }, [modelList, showModelSelector, selectedModel]);
useEffect(() => { useEffect(() => {
form.setFieldsValue(globalParams); if (!firstLoad) {
}, [globalParams]); form.setFieldsValue(globalParams);
}
}, [globalParams, firstLoad]);
const renderLabel = (args: { const renderLabel = (args: {
field: string; field: string;
@@ -196,7 +200,11 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
} }
]} ]}
> >
<SealSelect showSearch={true} options={modelList}></SealSelect> <SealSelect
showSearch={true}
options={modelList}
onChange={handleModelChange}
></SealSelect>
</Form.Item> </Form.Item>
</> </>
)} )}
@@ -246,7 +254,7 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
> >
<Slider <Slider
defaultValue={2048} defaultValue={2048}
max={16 * 1024} max={metaData.max_tokens || 16 * 1024}
step={1} step={1}
style={{ marginBottom: 0, marginTop: 16, marginInline: 0 }} style={{ marginBottom: 0, marginTop: 16, marginInline: 0 }}
tooltip={{ open: false }} tooltip={{ open: false }}
@@ -80,7 +80,7 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
</ul> </ul>
<h3>1. {intl.formatMessage({ id: 'resources.worker.add.step1' })}</h3> <h3>1. {intl.formatMessage({ id: 'resources.worker.add.step1' })}</h3>
<HighlightCode <HighlightCode
code={addWorkerGuide.mac.getToken} code={addWorkerGuide.container.getToken}
theme="dark" theme="dark"
></HighlightCode> ></HighlightCode>
<h3> <h3>
+4
View File
@@ -56,6 +56,10 @@ export const addWorkerGuide: Record<string, any> = {
registerWorker(params: { server: string; tag: string; token: string }) { registerWorker(params: { server: string; tag: string; token: string }) {
return `docker run -d --ipc=host --network=host gpustack/gpustack:${params.tag} --server-url ${params.server} --token ${params.token}`; return `docker run -d --ipc=host --network=host gpustack/gpustack:${params.tag} --server-url ${params.server} --token ${params.token}`;
} }
},
container: {
getToken:
'docker run -it ${gpustack_container_id} cat /var/lib/gpustack/token'
} }
}; };