364 lines
15 KiB
JavaScript
364 lines
15 KiB
JavaScript
let selectedProfileId = localStorage.getItem('selectedProfileId') || 'default';
|
|
let editingProfile = null;
|
|
|
|
// Profile functions
|
|
async function loadProfiles() {
|
|
try {
|
|
const response = await apiRequest('/profiles');
|
|
if (!response || !Object.keys(response).length) {
|
|
throw new Error('No profiles available');
|
|
}
|
|
|
|
const profiles = Object.values(response);
|
|
const select = document.getElementById('profileSelect');
|
|
|
|
// Actualizar el selector
|
|
select.innerHTML = profiles.map(profile => `
|
|
<option value="${profile.id}" ${profile.id === selectedProfileId ? 'selected' : ''}>
|
|
${profile.name || profile.id}
|
|
</option>
|
|
`).join('');
|
|
|
|
// Intentar seleccionar el perfil guardado o el predeterminado
|
|
const savedProfile = profiles.find(p => p.id === selectedProfileId);
|
|
if (savedProfile) {
|
|
await selectProfile(savedProfile.id);
|
|
} else {
|
|
// Si no se encuentra el perfil guardado, usar el primero disponible
|
|
selectedProfileId = profiles[0].id;
|
|
select.value = selectedProfileId;
|
|
await selectProfile(selectedProfileId);
|
|
}
|
|
|
|
localStorage.setItem('selectedProfileId', selectedProfileId);
|
|
|
|
} catch (error) {
|
|
console.error('Error loading profiles:', error);
|
|
showError('Error loading profiles. Using default profile.');
|
|
await loadDefaultProfile();
|
|
}
|
|
}
|
|
|
|
async function loadDefaultProfile() {
|
|
try {
|
|
currentProfile = await apiRequest('/profiles/default');
|
|
selectedProfileId = 'default';
|
|
localStorage.setItem('selectedProfileId', 'default');
|
|
|
|
const select = document.getElementById('profileSelect');
|
|
select.innerHTML = `<option value="default">Default Profile</option>`;
|
|
select.value = 'default';
|
|
|
|
// Actualizar la visualización del perfil
|
|
updateProfileDisplay();
|
|
|
|
} catch (error) {
|
|
console.error('Error loading default profile:', error);
|
|
showError('Failed to load default profile');
|
|
}
|
|
}
|
|
|
|
async function selectProfile(profileId) {
|
|
try {
|
|
const response = await apiRequest(`/profiles/${profileId}`);
|
|
if (!response || response.error) {
|
|
throw new Error(response?.error || 'Profile not found');
|
|
}
|
|
|
|
currentProfile = response;
|
|
selectedProfileId = profileId;
|
|
localStorage.setItem('selectedProfileId', profileId);
|
|
|
|
// Actualizar la visualización del perfil
|
|
updateProfileDisplay();
|
|
|
|
} catch (error) {
|
|
console.error('Failed to load profile:', error);
|
|
showError(`Failed to load profile: ${error.message}`);
|
|
// Intentar cargar el perfil por defecto si falla
|
|
if (profileId !== 'default') {
|
|
await loadDefaultProfile();
|
|
}
|
|
}
|
|
}
|
|
|
|
function updateProfileDisplay() {
|
|
const profileConfig = document.getElementById('profileConfig');
|
|
if (!profileConfig || !currentProfile) return;
|
|
|
|
profileConfig.innerHTML = `
|
|
<div class="space-y-4">
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-500">Profile ID</label>
|
|
<div class="mt-1 text-sm">${currentProfile.id}</div>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-500">Name</label>
|
|
<div class="mt-1 text-sm">${currentProfile.name}</div>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-500">LLM Model</label>
|
|
<div class="mt-1 text-sm">${currentProfile.llm_settings?.model || 'Not set'}</div>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-500">Temperature</label>
|
|
<div class="mt-1 text-sm">${currentProfile.llm_settings?.temperature || 'Not set'}</div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-500">API Key</label>
|
|
<div class="mt-1 text-sm">
|
|
${currentProfile.llm_settings?.api_key ? '********' : 'Not set'}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
async function changeProfile() {
|
|
const select = document.getElementById('profileSelect');
|
|
await selectProfile(select.value);
|
|
}
|
|
|
|
// Eliminar la función updateWorkDirDisplay y selectWorkDir
|
|
|
|
// Profile editor modal
|
|
|
|
function showProfileEditor(profile = null) {
|
|
editingProfile = profile;
|
|
const modal = document.createElement('div');
|
|
modal.className = 'modal active';
|
|
|
|
const editableInputClass = "mt-1 block w-full rounded-md border-2 border-gray-300 bg-green-50 px-3 py-2 shadow-sm focus:border-indigo-500 focus:ring-indigo-500";
|
|
const readonlyInputClass = "mt-1 block w-full rounded-md border-2 border-gray-200 bg-gray-100 px-3 py-2 shadow-sm";
|
|
|
|
modal.innerHTML = `
|
|
<div class="modal-content">
|
|
<h2 class="text-xl font-bold mb-4">${profile ? 'Editar Perfil' : 'Nuevo Perfil'}</h2>
|
|
<form id="profileForm" onsubmit="saveProfile(event)">
|
|
<div class="form-group">
|
|
<label for="profileId" class="block text-sm font-medium text-gray-700">ID del Perfil</label>
|
|
<input type="text" id="profileId" name="id"
|
|
class="${profile ? readonlyInputClass : editableInputClass}"
|
|
value="${profile?.id || ''}"
|
|
${profile ? 'readonly' : ''}
|
|
required pattern="[a-zA-Z0-9_-]+"
|
|
title="Solo se permiten letras, números, guión bajo y guión">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="profileName" class="block text-sm font-medium text-gray-700">Nombre</label>
|
|
<input type="text" id="profileName" name="name"
|
|
class="${editableInputClass}"
|
|
value="${profile?.name || ''}" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="llmModel" class="block text-sm font-medium text-gray-700">LLM Model</label>
|
|
<select id="llmModel" name="llm_model"
|
|
class="${editableInputClass}">
|
|
<option value="gpt-4" ${profile?.llm_settings?.model === 'gpt-4' ? 'selected' : ''}>GPT-4</option>
|
|
<option value="gpt-3.5-turbo" ${profile?.llm_settings?.model === 'gpt-3.5-turbo' ? 'selected' : ''}>GPT-3.5 Turbo</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="apiKey" class="block text-sm font-medium text-gray-700">API Key</label>
|
|
<input type="password" id="apiKey" name="api_key"
|
|
class="${editableInputClass}"
|
|
value="${profile?.llm_settings?.api_key || ''}">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="temperature" class="block text-sm font-medium text-gray-700">Temperature</label>
|
|
<input type="number" id="temperature" name="temperature"
|
|
class="${editableInputClass}"
|
|
value="${profile?.llm_settings?.temperature || 0.7}"
|
|
min="0" max="2" step="0.1">
|
|
</div>
|
|
<div class="mt-4 flex justify-end space-x-3">
|
|
<button type="button" onclick="closeModal(this)"
|
|
class="px-4 py-2 bg-gray-200 text-gray-800 rounded-md hover:bg-gray-300">Cancelar</button>
|
|
<button type="submit"
|
|
class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600">Guardar</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
`;
|
|
|
|
document.body.appendChild(modal);
|
|
}
|
|
|
|
async function saveProfile(event) {
|
|
event.preventDefault();
|
|
const form = event.target;
|
|
const formData = new FormData(form);
|
|
|
|
const profileData = {
|
|
id: formData.get('id'),
|
|
name: formData.get('name'),
|
|
llm_settings: {
|
|
model: formData.get('llm_model'),
|
|
api_key: formData.get('api_key'),
|
|
temperature: parseFloat(formData.get('temperature'))
|
|
}
|
|
};
|
|
|
|
try {
|
|
if (editingProfile) {
|
|
await apiRequest(`/profiles/${editingProfile.id}`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify(profileData)
|
|
});
|
|
} else {
|
|
await apiRequest('/profiles', {
|
|
method: 'POST',
|
|
body: JSON.stringify(profileData)
|
|
});
|
|
}
|
|
|
|
await loadProfiles();
|
|
closeModal(event.target);
|
|
showSuccess(`Perfil ${editingProfile ? 'actualizado' : 'creado'} correctamente`);
|
|
} catch (error) {
|
|
showError(`Error al ${editingProfile ? 'actualizar' : 'crear'} el perfil`);
|
|
}
|
|
}
|
|
|
|
// static/js/profile.js
|
|
async function editProfile() {
|
|
if (!currentProfile) {
|
|
showError('No profile selected');
|
|
return;
|
|
}
|
|
|
|
const content = `
|
|
<form id="profileForm" class="space-y-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700">Profile ID</label>
|
|
<input type="text" name="id" value="${currentProfile.id}"
|
|
class="mt-1 block w-full rounded-md border-2 border-gray-300 bg-green-50 px-3 py-2 shadow-sm focus:border-indigo-500 focus:ring-indigo-500"
|
|
${currentProfile.id === 'default' ? 'readonly' : ''}>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700">Name</label>
|
|
<input type="text" name="name" value="${currentProfile.name}"
|
|
class="mt-1 block w-full rounded-md border-2 border-gray-300 bg-green-50 px-3 py-2 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700">LLM Model</label>
|
|
<select name="llm_model"
|
|
class="mt-1 block w-full rounded-md border-2 border-gray-300 bg-green-50 px-3 py-2 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
|
|
<option value="gpt-4" ${currentProfile.llm_settings?.model === 'gpt-4' ? 'selected' : ''}>GPT-4</option>
|
|
<option value="gpt-3.5-turbo" ${currentProfile.llm_settings?.model === 'gpt-3.5-turbo' ? 'selected' : ''}>GPT-3.5 Turbo</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700">API Key</label>
|
|
<input type="password" name="api_key" value="${currentProfile.llm_settings?.api_key || ''}"
|
|
class="mt-1 block w-full rounded-md border-2 border-gray-300 bg-green-50 px-3 py-2 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700">Temperature</label>
|
|
<input type="number" name="temperature" value="${currentProfile.llm_settings?.temperature || 0.7}"
|
|
min="0" max="2" step="0.1"
|
|
class="mt-1 block w-full rounded-md border-2 border-gray-300 bg-green-50 px-3 py-2 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
|
|
</div>
|
|
</form>
|
|
`;
|
|
|
|
const modal = createModal('Edit Profile', content, true);
|
|
modal.querySelector('[onclick="saveModal(this)"]').onclick = async () => {
|
|
await saveProfile(modal);
|
|
};
|
|
}
|
|
|
|
async function saveProfile(modal) {
|
|
const form = modal.querySelector('#profileForm');
|
|
const formData = new FormData(form);
|
|
|
|
const profileData = {
|
|
id: formData.get('id'),
|
|
name: formData.get('name'),
|
|
llm_settings: {
|
|
model: formData.get('llm_model'),
|
|
api_key: formData.get('api_key'),
|
|
temperature: parseFloat(formData.get('temperature'))
|
|
}
|
|
};
|
|
|
|
try {
|
|
if (editingProfile) {
|
|
await apiRequest(`/profiles/${editingProfile.id}`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify(profileData)
|
|
});
|
|
} else {
|
|
await apiRequest('/profiles', {
|
|
method: 'POST',
|
|
body: JSON.stringify(profileData)
|
|
});
|
|
}
|
|
|
|
await loadProfiles();
|
|
closeModal(modal);
|
|
showSuccess(`Perfil ${editingProfile ? 'actualizado' : 'creado'} correctamente`);
|
|
} catch (error) {
|
|
showError(`Error al ${editingProfile ? 'actualizar' : 'crear'} el perfil`);
|
|
}
|
|
}
|
|
|
|
function newProfile() {
|
|
const editableInputClass = "mt-1 block w-full rounded-md border-2 border-gray-300 bg-green-50 px-3 py-2 shadow-sm focus:border-indigo-500 focus:ring-indigo-500";
|
|
const readonlyInputClass = "mt-1 block w-full rounded-md border-2 border-gray-200 bg-gray-100 px-3 py-2 shadow-sm";
|
|
|
|
const content = `
|
|
<form id="profileForm" class="space-y-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700">Profile ID</label>
|
|
<input type="text" name="id" required pattern="[a-zA-Z0-9_-]+"
|
|
class="${editableInputClass}"
|
|
title="Only letters, numbers, underscore and dash allowed">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700">Name</label>
|
|
<input type="text" name="name" required
|
|
class="${editableInputClass}">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700">LLM Model</label>
|
|
<select name="llm_model"
|
|
class="${editableInputClass}">
|
|
<option value="gpt-4">GPT-4</option>
|
|
<option value="gpt-3.5-turbo">GPT-3.5 Turbo</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700">API Key</label>
|
|
<input type="password" name="api_key"
|
|
class="${editableInputClass}">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700">Temperature</label>
|
|
<input type="number" name="temperature" value="0.7"
|
|
min="0" max="2" step="0.1"
|
|
class="${editableInputClass}">
|
|
</div>
|
|
</form>
|
|
`;
|
|
|
|
const modal = createModal('New Profile', content, true);
|
|
editingProfile = null;
|
|
|
|
modal.querySelector('[onclick="saveModal(this)"]').onclick = async () => {
|
|
await saveProfile(modal);
|
|
};
|
|
}
|
|
|
|
async function onProfileChange(event) {
|
|
const newProfileId = event.target.value;
|
|
if (newProfileId !== selectedProfileId) {
|
|
selectedProfileId = newProfileId;
|
|
localStorage.setItem('selectedProfileId', selectedProfileId);
|
|
await selectProfile(selectedProfileId);
|
|
}
|
|
} |