Added model selection function, updated model list acquisition logic, and enhanced model information display

This commit is contained in:
wyx-hhhh 2025-05-21 14:33:36 +08:00
parent cb5d0cb4da
commit 5cc5cf39e6
2 changed files with 158 additions and 8 deletions

View file

@ -2,7 +2,7 @@ import { Status, statusRankMap, useTrainingStore } from '@/store/useTrainingStor
import { startService, stopService } from '@/service/train';
import { StatusBar } from '../StatusBar';
import { useRef, useEffect, useState, useMemo } from 'react';
import { message } from 'antd';
import { message, Select, Tooltip, Spin } from 'antd';
import {
CloudUploadOutlined,
CheckCircleOutlined,
@ -15,6 +15,7 @@ import RegisterUploadModal from '../upload/RegisterUploadModal';
import { useLoadInfoStore } from '@/store/useLoadInfoStore';
import TrainingTipModal from '../upload/TraingTipModal';
import { getMemoryList } from '@/service/memory';
import { getModelList, ModelInfo } from '@/service/model';
const StatusDot = ({ active }: { active: boolean }) => (
<div
@ -22,6 +23,41 @@ const StatusDot = ({ active }: { active: boolean }) => (
/>
);
// Helper component for option tooltips
const OptionTooltip = ({ model }: { model: ModelInfo }) => {
const fileName = model.model_path.split('/')[1];
const timeStamp = fileName?.replace('.gguf', '') || 'Unknown version';
const modelName = model.model_path.split('/')[0];
return (
<Tooltip
title={
<div className="text-xs">
<div className="font-bold mb-1">{modelName}</div>
<div className="font-bold mt-2 mb-1">Training Parameters:</div>
<ul>
{Object.entries(model.training_params).length > 0 ? (
Object.entries(model.training_params).map(([key, value]) => (
<li key={key}>
{key}: {typeof value === 'object' ? JSON.stringify(value) : String(value)}
</li>
))
) : (
<li>No training parameters available</li>
)}
</ul>
</div>
}
placement="left"
>
<div className="flex flex-col">
<span className="font-medium">{timeStamp}</span>
<span className="text-xs text-gray-500">{modelName}</span>
</div>
</Tooltip>
);
};
export function ModelStatus() {
const status = useTrainingStore((state) => state.status);
const setStatus = useTrainingStore((state) => state.setStatus);
@ -43,6 +79,11 @@ export function ModelStatus() {
const [showRegisterModal, setShowRegisterModal] = useState(false);
const [showtrainingModal, setShowtrainingModal] = useState(false);
// Model selection states
const [modelList, setModelList] = useState<ModelInfo[]>([]);
const [selectedModel, setSelectedModel] = useState<string>('');
const [loadingModels, setLoadingModels] = useState<boolean>(false);
const handleRegistryClick = () => {
if (!serviceStarted) {
messageApi.info({
@ -54,6 +95,16 @@ export function ModelStatus() {
}
};
const handleModelChange = (value: string) => {
setSelectedModel(value);
// Update configuration in localStorage
const config = JSON.parse(localStorage.getItem('trainingParams') || '{}');
// Store the complete model path
config.model_name = value;
localStorage.setItem('trainingParams', JSON.stringify(config));
};
const fetchMemories = async () => {
try {
const memoryRes = await getMemoryList();
@ -70,9 +121,51 @@ export function ModelStatus() {
}
};
const fetchModels = async () => {
setLoadingModels(true);
try {
const res = await getModelList();
if (res.data.code === 0) {
setModelList(res.data.data);
// Read saved configuration, initialize selected model
const config = JSON.parse(localStorage.getItem('trainingParams') || '{}');
if (config.model_name) {
// Check if the model exists in the list
const modelExists = res.data.data.some(
(model: ModelInfo) => model.model_path === config.model_name
);
if (modelExists) {
setSelectedModel(config.model_name);
} else if (res.data.data.length > 0) {
// Default to the first model in the list
setSelectedModel(res.data.data[0].model_path);
}
} else if (res.data.data.length > 0) {
// Default to the first model in the list
setSelectedModel(res.data.data[0].model_path);
}
} else {
messageApi.error({ content: res.data.message!, duration: 1 });
}
} catch (error) {
console.error('Error fetching model list:', error);
messageApi.error({
content: error.response?.data?.message || error.message,
duration: 1
});
} finally {
setLoadingModels(false);
}
};
useEffect(() => {
fetchMemories();
fetchServiceStatus();
fetchModels();
return () => {
clearPolling();
@ -130,19 +223,18 @@ export function ModelStatus() {
console.error('Error checking service status:', error);
});
}, 3000);
};
const handleStartService = () => {
}; const handleStartService = () => {
// Use selected model or get from local storage
const config = JSON.parse(localStorage.getItem('trainingParams') || '{}');
const modelPath = selectedModel || config.model_name;
if (!config.model_name) {
message.error('Please train a base model first');
if (!modelPath) {
message.error('Please select a model to start');
return;
}
setServiceStarting(true);
startService({ model_name: config.model_name })
startService({ model_name: modelPath })
.then((res) => {
if (res.data.code === 0) {
messageApi.success({ content: 'Service starting...', duration: 1 });
@ -204,6 +296,47 @@ export function ModelStatus() {
<StatusBar status={status} />
<div className="flex items-center gap-6">
{/* Model Selection - only show when service is not started */}
{!serviceStarted && (
<div className="relative mr-3">
<Spin spinning={loadingModels} size="small">
<Select
className="w-56"
disabled={serviceStarted || isServiceStarting}
loading={loadingModels}
onChange={handleModelChange}
optionLabelProp="label"
placeholder="Select Model"
value={selectedModel}
options={modelList.map((model) => {
const fileName = model.model_path.split('/')[1];
const timeStamp = fileName?.replace('.gguf', '') || 'Unknown version';
const modelName = model.model_path.split('/')[0];
return {
label: timeStamp,
value: model.model_path,
model: model
};
})}
optionRender={(option) => {
const model = option.data.model as ModelInfo;
return <OptionTooltip model={model} />;
}}
onDropdownVisibleChange={(open) => {
if (open) {
// Hide any currently visible tooltips when dropdown opens
const tooltipElements = document.querySelectorAll('.ant-tooltip');
tooltipElements.forEach(el => {
if (window.getComputedStyle(el).display !== 'none') {
(el as HTMLElement).style.display = 'none';
}
});
}
}}
/>
</Spin>
</div>
)}
{/* Control Buttons */}
<div className="flex items-center gap-3">
<div

View file

@ -44,6 +44,16 @@ export interface BioVersion {
version: number;
}
export interface ModelInfo {
model_path: string;
full_path: string;
file_size: number;
created_time: number;
training_params: {
[key: string]: any;
};
}
export const getGlobalBioVersion = () => {
return Request<CommonResponse<BioVersion[]>>({
method: 'get',
@ -64,3 +74,10 @@ export const getStatusBio = () => {
url: '/api/kernel/l1/status_bio/get'
});
};
export const getModelList = () => {
return Request<CommonResponse<ModelInfo[]>>({
method: 'get',
url: '/api/kernel2/list_gguf_models'
});
};