fix: expand the model after deploying from catalog

This commit is contained in:
jialin
2025-01-16 17:24:37 +08:00
parent 4a838e7bdb
commit b14c01f9f7
11 changed files with 151 additions and 67 deletions
+4
View File
@@ -0,0 +1,4 @@
import { atom } from 'jotai';
// models expand keys: create, update , delete,
export const modelsExpandKeysAtom = atom<string[]>([]);
+3
View File
@@ -71,6 +71,9 @@ const AutoImage: React.FC<
}, [props.onLoad]); }, [props.onLoad]);
const handleOnError = () => { const handleOnError = () => {
console.log('error===img=========', {
src: props.src
});
setIsError(true); setIsError(true);
}; };
+6 -5
View File
@@ -496,6 +496,11 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
overlayCtx!.resetTransform(); overlayCtx!.resetTransform();
}, []); }, []);
const updateCursorSize = () => {
cursorRef.current!.style.width = `${lineWidth * autoScale.current}px`;
cursorRef.current!.style.height = `${lineWidth * autoScale.current}px`;
};
const initializeImage = useCallback(async () => { const initializeImage = useCallback(async () => {
if (imguid === preImguid.current) { if (imguid === preImguid.current) {
return; return;
@@ -518,6 +523,7 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
if (strokesRef.current.length) { if (strokesRef.current.length) {
redrawStrokes(strokesRef.current); redrawStrokes(strokesRef.current);
} }
updateCursorSize();
}, [drawImage, onReset, redrawStrokes, imguid]); }, [drawImage, onReset, redrawStrokes, imguid]);
const updateZoom = (scaleChange: number, mouseX: number, mouseY: number) => { const updateZoom = (scaleChange: number, mouseX: number, mouseY: number) => {
@@ -557,11 +563,6 @@ const CanvasImageEditor: React.FC<CanvasImageEditorProps> = ({
canvasRef.current!.style.transform = `scale(${autoScale.current})`; canvasRef.current!.style.transform = `scale(${autoScale.current})`;
}; };
const updateCursorSize = () => {
cursorRef.current!.style.width = `${lineWidth * autoScale.current}px`;
cursorRef.current!.style.height = `${lineWidth * autoScale.current}px`;
};
const handleOnWheel = (event: any) => { const handleOnWheel = (event: any) => {
handleZoom(event); handleZoom(event);
updateCursorSize(); updateCursorSize();
+3 -2
View File
@@ -1,7 +1,8 @@
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
export default function useExpandedRowKeys() { export default function useExpandedRowKeys(defaultKeys: React.Key[] = []) {
const [expandedRowKeys, setExpandedRowKeys] = useState<React.Key[]>([]); const [expandedRowKeys, setExpandedRowKeys] =
useState<React.Key[]>(defaultKeys);
const handleExpandChange = useCallback( const handleExpandChange = useCallback(
(expanded: boolean, record: any, rowKey: any) => { (expanded: boolean, record: any, rowKey: any) => {
+1 -1
View File
@@ -5,7 +5,7 @@ export default {
'menu.playground.embedding': '文本嵌入', 'menu.playground.embedding': '文本嵌入',
'menu.playground.chat': '对话', 'menu.playground.chat': '对话',
'menu.playground.speech': '语音', 'menu.playground.speech': '语音',
'menu.playground.text2images': '文生图', 'menu.playground.text2images': '图',
'menu.compare': '多模型对比', 'menu.compare': '多模型对比',
'menu.models': '模型', 'menu.models': '模型',
'menu.models.modelList': '部署与管理', 'menu.models.modelList': '部署与管理',
+1 -1
View File
@@ -4,7 +4,7 @@ export default {
'playground.system': '系统', 'playground.system': '系统',
'playground.systemMessage': '系统消息', 'playground.systemMessage': '系统消息',
'playground.user': '用户', 'playground.user': '用户',
'playground.assistant': '助手', 'playground.assistant': 'AI助手',
'playground.newMessage': '新消息', 'playground.newMessage': '新消息',
'playground.viewcode': '查看代码', 'playground.viewcode': '查看代码',
'playground.model': '模型', 'playground.model': '模型',
+5 -1
View File
@@ -1,9 +1,11 @@
import { modelsExpandKeysAtom } from '@/atoms/models';
import PageTools from '@/components/page-tools'; import PageTools from '@/components/page-tools';
import { PageAction } from '@/config'; import { PageAction } from '@/config';
import { SyncOutlined } from '@ant-design/icons'; import { SyncOutlined } from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-components'; import { PageContainer } from '@ant-design/pro-components';
import { useIntl, useNavigate } from '@umijs/max'; import { useIntl, useNavigate } from '@umijs/max';
import { Button, Input, Pagination, Select, Space, message } from 'antd'; import { Button, Input, Pagination, Select, Space, message } from 'antd';
import { useAtom } from 'jotai';
import _ from 'lodash'; import _ from 'lodash';
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import { createModel, queryCatalogList } from './apis'; import { createModel, queryCatalogList } from './apis';
@@ -38,6 +40,7 @@ const Catalog: React.FC = () => {
current: {}, current: {},
source: modelSourceMap.huggingface_value source: modelSourceMap.huggingface_value
}); });
const [modelsExpandKeys, setModelsExpandKeys] = useAtom(modelsExpandKeysAtom);
const cacheData = React.useRef<CatalogItemType[]>([]); const cacheData = React.useRef<CatalogItemType[]>([]);
const categoryOptions = [...modelCategories.filter((item) => item.value)]; const categoryOptions = [...modelCategories.filter((item) => item.value)];
@@ -53,7 +56,7 @@ const Catalog: React.FC = () => {
); );
} }
if (search) { if (search) {
return _.toLower(item.name).includes(search); return _.toLower(item.name).includes(_.toLower(search));
} }
if (categories.length > 0) { if (categories.length > 0) {
return categories.some((category) => return categories.some((category) =>
@@ -134,6 +137,7 @@ const Catalog: React.FC = () => {
show: false show: false
}); });
message.success(intl.formatMessage({ id: 'common.message.success' })); message.success(intl.formatMessage({ id: 'common.message.success' }));
setModelsExpandKeys([modelData.id]);
navigate('/models/list'); navigate('/models/list');
} catch (error) {} } catch (error) {}
}, },
+6 -1
View File
@@ -1,3 +1,4 @@
import { modelsExpandKeysAtom } from '@/atoms/models';
import AutoTooltip from '@/components/auto-tooltip'; import AutoTooltip from '@/components/auto-tooltip';
import DeleteModal from '@/components/delete-modal'; import DeleteModal from '@/components/delete-modal';
import DropdownButtons from '@/components/drop-down-buttons'; import DropdownButtons from '@/components/drop-down-buttons';
@@ -30,6 +31,7 @@ import { PageContainer } from '@ant-design/pro-components';
import { Access, useAccess, useIntl, useNavigate } from '@umijs/max'; import { Access, useAccess, useIntl, useNavigate } from '@umijs/max';
import { Button, Dropdown, Input, Select, Space, Tag, message } from 'antd'; import { Button, Dropdown, Input, Select, Space, Tag, message } from 'antd';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useAtom } from 'jotai';
import _ from 'lodash'; import _ from 'lodash';
import { memo, useCallback, useEffect, useRef, useState } from 'react'; import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
@@ -125,6 +127,7 @@ const Models: React.FC<ModelsProps> = ({
loading, loading,
total total
}) => { }) => {
const [expandAtom] = useAtom(modelsExpandKeysAtom);
const access = useAccess(); const access = useAccess();
const intl = useIntl(); const intl = useIntl();
const navigate = useNavigate(); const navigate = useNavigate();
@@ -134,7 +137,7 @@ const Models: React.FC<ModelsProps> = ({
updateExpandedRowKeys, updateExpandedRowKeys,
removeExpandedRowKey, removeExpandedRowKey,
expandedRowKeys expandedRowKeys
} = useExpandedRowKeys(); } = useExpandedRowKeys(expandAtom);
const { sortOrder, setSortOrder } = useTableSort({ const { sortOrder, setSortOrder } = useTableSort({
defaultSortOrder: 'descend' defaultSortOrder: 'descend'
}); });
@@ -309,6 +312,7 @@ const Models: React.FC<ModelsProps> = ({
} }
}); });
message.success(intl.formatMessage({ id: 'common.message.success' })); message.success(intl.formatMessage({ id: 'common.message.success' }));
updateExpandedRowKeys([row.id, ...expandedRowKeys]);
} catch (error) { } catch (error) {
// ingore // ingore
} }
@@ -329,6 +333,7 @@ const Models: React.FC<ModelsProps> = ({
replicas: 0 replicas: 0
} }
}); });
removeExpandedRowKey([row.id]);
} catch (error) { } catch (error) {
// ingore // ingore
} }
@@ -352,6 +352,7 @@ const GroundImages: React.FC<MessageProps> = forwardRef((props, ref) => {
if (item.b64_json && stream_options.chunk_results) { if (item.b64_json && stream_options.chunk_results) {
imgItem.dataUrl += item.b64_json; imgItem.dataUrl += item.b64_json;
} else if (item.b64_json) { } else if (item.b64_json) {
console.log('item.b64_json:', item.b64_json, item.index);
imgItem.dataUrl = `data:image/png;base64,${item.b64_json}`; imgItem.dataUrl = `data:image/png;base64,${item.b64_json}`;
} }
const progress = _.round(item.progress, 0); const progress = _.round(item.progress, 0);
+89 -35
View File
@@ -199,20 +199,32 @@ export const readStreamData = async (
}; };
// Process the remainder of the buffer // Process the remainder of the buffer
const processBuffer = (buffer: string, callback: (data: any) => void) => { const processBuffer = async (buffer: string, callback: (data: any) => void) => {
if (!buffer) return;
const lines = buffer.split('\n'); const lines = buffer.split('\n');
for (const line of lines) { for (const line of lines) {
if (line.startsWith('data: ')) { const trimmedLine = line.trim();
const jsonStr = line.slice(6).trim();
if (trimmedLine.startsWith('data: ')) {
const jsonStr = trimmedLine.slice(6).trim();
try { try {
const jsonData = JSON.parse(jsonStr); if (jsonStr !== '[DONE]') {
callback(jsonData); console.log('jsonStr>>>>>>>>>>>>>done:', jsonStr);
const jsonData = JSON.parse(jsonStr);
callback(jsonData);
}
} catch (e) { } catch (e) {
console.error( console.error('Failed to parse JSON from line:', jsonStr, e);
'Failed to parse JSON from remaining buffer:', }
jsonStr, } else if (trimmedLine.startsWith('error:')) {
e const errorStr = trimmedLine.slice(7).trim();
); console.log('jsonStr>>>>>>>>>>>>>error:', errorStr);
try {
const jsonData = JSON.parse(errorStr);
callback({ error: jsonData });
} catch (e) {
console.error('Failed to parse error JSON from line:', errorStr, e);
} }
} }
} }
@@ -221,47 +233,89 @@ const processBuffer = (buffer: string, callback: (data: any) => void) => {
export const readLargeStreamData = async ( export const readLargeStreamData = async (
reader: any, reader: any,
decoder: TextDecoder, decoder: TextDecoder,
callback: (data: any) => void callback: (data: any) => void,
throttleDelay = 200
) => { ) => {
let buffer = ''; // cache incomplete line let buffer = ''; // cache incomplete line
class BufferManager {
private buffer: any[] = [];
private failed: boolean = false;
private isFlushing: boolean = false;
private callback: (data: any) => void;
constructor(callback: (data: any) => void) {
this.callback = callback;
}
public add(data: any) {
this.buffer.push(data);
}
public async flush() {
if (this.buffer.length === 0 || this.isFlushing) {
return;
}
this.failed = false;
this.isFlushing = true;
while (this.buffer.length > 0) {
const data = this.buffer.shift();
try {
processBuffer(data, this.callback);
} catch (error) {
console.error('Error processing buffer:', error);
this.failed = true;
this.buffer.unshift(data);
break;
}
}
this.isFlushing = false;
}
public getBuffer() {
return this.buffer;
}
}
const bufferManager = new BufferManager(callback);
const throttledCallback = throttle(async () => {
bufferManager.flush();
}, throttleDelay);
let isReading = true;
while (true) { while (true) {
const { done, value } = await reader?.read?.(); const { done, value } = await reader?.read?.();
if (done) { if (done) {
isReading = false;
// Process remaining buffered data // Process remaining buffered data
if (buffer.trim()) { if (buffer) {
processBuffer(buffer, callback); bufferManager.add(buffer);
} }
bufferManager.flush();
break; break;
} }
// Decode new chunk of data and append to buffer try {
buffer += decoder.decode(value, { stream: true }); // Decode new chunk of data and append to buffer
buffer += decoder.decode(value, { stream: true });
// Try to process the complete line in the buffer // Try to process the complete line in the buffer
const lines = buffer.split('\n'); const lines = buffer.split('\n');
buffer = lines.pop() || ''; // Keep last line (may be incomplete) buffer = lines.pop() || ''; // Keep last line (may be incomplete)
for (const line of lines) { for (const line of lines) {
if (line.startsWith('data: ')) { bufferManager.add(line);
const jsonStr = line.slice(6).trim();
try {
if (jsonStr !== '[DONE]') {
const jsonData = JSON.parse(jsonStr);
callback(jsonData);
}
} catch (e) {
console.error('Failed to parse JSON:', jsonStr, e);
}
} }
if (line.startsWith('error:')) { throttledCallback();
const errorStr = line.slice(7).trim(); } catch (error) {
const jsonData = JSON.parse(errorStr); console.log('Error reading stream data:', error);
callback({ error: jsonData }); // do nothing
}
} }
} }
}; };
+32 -21
View File
@@ -149,35 +149,46 @@ export const generateRandomNumber = () => {
}; };
function base64ToBlob(base64: string, contentType = '', sliceSize = 512) { function base64ToBlob(base64: string, contentType = '', sliceSize = 512) {
const base64Content = base64.replace(/^data:image\/(png|jpg);base64,/, ''); try {
const byteCharacters = atob(base64Content); const base64Content = base64.replace(/^data:image\/(png|jpg);base64,/, '');
const byteArrays = []; const byteCharacters = atob(base64Content);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) { for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
const slice = byteCharacters.slice(offset, offset + sliceSize); const slice = byteCharacters.slice(offset, offset + sliceSize);
const byteNumbers = new Array(slice.length); const byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) { for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i); byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
} }
const byteArray = new Uint8Array(byteNumbers); return new Blob(byteArrays, { type: contentType });
byteArrays.push(byteArray); } catch (error) {
return null;
} }
return new Blob(byteArrays, { type: contentType });
} }
export const base64ToFile = (base64String: string, fileName: string) => { export const base64ToFile = (base64String: string, fileName: string) => {
if (!base64String) { try {
if (!base64String) {
return null;
}
console.log('base64String:', base64String);
const match = base64String.match(/data:(.*?);base64,/);
if (!match) {
throw new Error('Invalid base64 string');
}
const contentType = match[1];
const blob = base64ToBlob(base64String, contentType);
if (!blob) {
throw new Error('Failed to convert base64 to blob');
}
return new File([blob], fileName || contentType, { type: contentType });
} catch (error) {
return null; return null;
} }
console.log('base64String:', base64String);
const match = base64String.match(/data:(.*?);base64,/);
if (!match) {
throw new Error('Invalid base64 string');
}
const contentType = match[1];
const blob = base64ToBlob(base64String, contentType);
return new File([blob], fileName || contentType, { type: contentType });
}; };