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