muryshev's picture
update
19eca0c
import React from 'react';
import Modal from 'react-modal';
import { GoX } from 'react-icons/go';
import { LLMConfig } from '@/api/llmConfigs/types';
import { useQuery } from '@tanstack/react-query';
import { fetchLLMConfigById, fetchDefaultLLMConfig } from '@/api/llmConfigs/llmConfigApi';
import './LlmConfigModal.scss';
interface LLMConfigModalProps {
isOpen: boolean;
onRequestClose: () => void;
config: LLMConfig | null;
onSave: (config: LLMConfig | Omit<LLMConfig, 'id' | 'created_at'>) => Promise<void>;
isEditMode: boolean;
}
const customStyles = {
content: {
top: '40%',
left: '50%',
right: 'auto',
bottom: 'auto',
transform: 'translate(-50%, -50%)',
borderRadius: '15px',
maxWidth: '100%',
overflow: 'hidden',
},
};
const LLMConfigModal: React.FC<LLMConfigModalProps> = ({
isOpen,
onRequestClose,
config,
onSave,
isEditMode,
}) => {
const [formData, setFormData] = React.useState<LLMConfig | Omit<LLMConfig, 'id' | 'created_at'>>(() => ({
is_default: false,
model: '',
temperature: 0.7,
top_p: 0.95,
min_p: 0.05,
frequency_penalty: 0,
presence_penalty: 0,
seed: 42,
n_predict: 1000
}));
// Запрос конфига для редактирования
const { data: serverConfig, isLoading: isConfigLoading } = useQuery({
queryKey: ['llmConfig', config?.id],
queryFn: () => fetchLLMConfigById(config!.id),
enabled: isOpen && isEditMode && !!config?.id,
});
// Запрос дефолтной конфигурации для создания
const { data: defaultConfig, isLoading: isDefaultLoading } = useQuery({
queryKey: ['defaultLLMConfig'],
queryFn: fetchDefaultLLMConfig,
enabled: isOpen && !isEditMode, // Запрашиваем только при создании
});
React.useEffect(() => {
if (isOpen) {
if (isEditMode && serverConfig) {
setFormData(serverConfig); // Данные с сервера для редактирования
} else if (!isEditMode && defaultConfig) {
setFormData(defaultConfig); // Дефолтная запись с сервера для создания
} else if (!isEditMode && !defaultConfig && !isDefaultLoading) {
setFormData({ // Если дефолтной записи нет и загрузка завершена
is_default: true,
model: 'meta-llama/Llama-3.3-70B-Instruct-Turbo',
temperature: 0.14,
top_p: 0.95,
min_p: 0.05,
frequency_penalty: -0.001,
presence_penalty: 1.3,
n_predict: 1000,
seed: 42,
});
}
}
}, [isOpen, isEditMode, serverConfig, defaultConfig, isDefaultLoading]);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]:
name === 'is_default' ? e.target.checked :
name === 'model' ? value :
name === 'seed' ? parseInt(value, 10) || 0 :
parseFloat(value) || 0,
}));
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
await onSave(formData);
onRequestClose();
} catch (err) {
console.error('Save failed:', err);
}
};
if (isConfigLoading && isEditMode) {
return <div>Loading config...</div>;
}
if (isDefaultLoading && !isEditMode) {
return <div>Loading default config...</div>;
}
return (
<Modal
isOpen={isOpen}
onRequestClose={onRequestClose}
style={customStyles}
overlayClassName="modal-overlay"
>
<div className="modal-content">
<div className="label">
<h3 className="name">{isEditMode ? 'Редактирование настроек' : 'Новые настройки'}</h3>
<button className="close-button" onClick={onRequestClose}>
<GoX style={{ height: '25px', width: '25px' }} />
</button>
</div>
<form onSubmit={handleSubmit}>
{isEditMode && 'id' in formData && (
<label>
<span className='title'>ID:</span>
<input type="text" value={formData.id} disabled />
</label>
)}
<label>
<span className='title'>Задать по-умолчанию:</span>
<input
type="checkbox"
name="is_default"
checked={formData.is_default}
onChange={handleChange}
/>
</label>
<label>
<span className='title'>Модель:</span>
<input
type="text"
name="model"
value={formData.model}
onChange={handleChange}
/>
</label>
<label>
<span className='title'>Temperature:</span>
<input
type="number"
name="temperature"
value={formData.temperature}
onChange={handleChange}
step="0.01"
/>
</label>
<label>
<span className='title'>Top P:</span>
<input
type="number"
name="top_p"
value={formData.top_p}
onChange={handleChange}
step="0.01"
/>
</label>
<label>
<span className='title'>Min P:</span>
<input
type="number"
name="min_p"
value={formData.min_p}
onChange={handleChange}
step="0.01"
/>
</label>
<label>
<span className='title'>Frequency Penalty:</span>
<input
type="number"
name="frequency_penalty"
value={formData.frequency_penalty}
onChange={handleChange}
step="0.01"
/>
</label>
<label>
<span className='title'>Presence Penalty:</span>
<input
type="number"
name="presence_penalty"
value={formData.presence_penalty}
onChange={handleChange}
step="0.01"
/>
</label>
<label>
<span className='title'>N predict:</span>
<input
type="number"
name="n_predict"
value={formData.n_predict}
onChange={handleChange}
step="1"
/>
</label>
<label>
<span className='title'>Seed:</span>
<input
type="number"
name="seed"
value={formData.seed}
onChange={handleChange}
step="1"
/>
</label>
<div className="button-group">
<button type="submit">{isEditMode ? 'Сохранить' : 'Создать'}</button>
</div>
</form>
</div>
</Modal>
);
};
export default LLMConfigModal;