AppConsulta360 / js /iaConfigModule.js
aarnal80's picture
Upload 6 files
2be59fa verified
raw
history blame
11.4 kB
// js/iaConfigModule.js
const defaultConfig = {
llm: {
provider: "deepseek",
apiKeys: { deepseek: "", openai: "" },
model: "deepseek-chat"
},
transcription: {
provider: "openai",
apiKeys: { openai: "", deepgram: "" },
models: { openai: "whisper-1", deepgram: "nova-2" }
}
};
// Lista de modelos actualizada (2024)
export const llmProviders = [
{ name: "OpenAI", value: "openai", models: ["gpt-4o-mini-2024-07-18","chatgpt-4o-latest","o1-mini-2024-09-12","o4-mini-2025-04-16"], url: "https://api.openai.com" },
// { name: "Gemini", value: "gemini", models: [
// "gemini-2.5-flash-preview-04-17", // Versi贸n preliminar de Gemini 2.5 Flash 04-17
// "gemini-2.0-flash", // Gemini 2.0 Flash
// "gemini-2.0-flash-lite", // Gemini 2.0 Flash-Lite
// "gemini-1.5-flash" // Gemini 1.5 Flash
// ], url: "https://generativelanguage.googleapis.com" },
{ name: "DeepSeek", value: "deepseek", models: ["deepseek-chat", "deepseek-reasoner"], url: "https://api.deepseek.com" }
];
export const transcriptionProviders = [
{ name: "OpenAI Whisper", value: "openai", models: ["whisper-1"], url: "https://api.openai.com" },
{ name: "Deepgram", value: "deepgram", models: ["nova-2", "whisper-large"], url: "https://api.deepgram.com" }
];
function saveConfig(config) {
localStorage.setItem("iaConfig", JSON.stringify(config));
}
function loadConfig() {
const config = JSON.parse(localStorage.getItem("iaConfig")) || defaultConfig;
// Migrar configuraci贸n antigua de transcription
if (config.transcription.apiKey !== undefined) {
const oldKey = config.transcription.apiKey;
const oldModel = config.transcription.model;
config.transcription.apiKeys = { [config.transcription.provider]: oldKey, deepgram: "" };
config.transcription.models = { [config.transcription.provider]: oldModel, deepgram: "nova-2" };
delete config.transcription.apiKey;
delete config.transcription.model;
saveConfig(config);
}
// Migrar configuraci贸n antigua de LLM apiKey a apiKeys
if (config.llm.apiKey !== undefined) {
const old = config.llm.apiKey;
config.llm.apiKeys = { ...defaultConfig.llm.apiKeys, [config.llm.provider]: old };
delete config.llm.apiKey;
saveConfig(config);
}
// Migrar modelos obsoletos de DeepSeek a 'deepseek-chat'
if (config.llm.provider === 'deepseek' && (config.llm.model === 'deepseek-v3' || config.llm.model === 'deepseek-llm')) {
config.llm.model = 'deepseek-chat';
console.log('[iaConfigModule] Migrado modelo DeepSeek a deepseek-chat');
saveConfig(config);
}
return config;
}
export function getIaConfig() {
return loadConfig();
}
export function renderIaConfigForm(containerId) {
let config = loadConfig();
const container = document.getElementById(containerId);
if (!container) {
console.error(`[iaConfigModule] No se encontr贸 el contenedor '${containerId}'`);
document.body.insertAdjacentHTML('beforeend', `<div style='color:red'>[Error] No se encontr贸 el contenedor '${containerId}' para la configuraci贸n IA.</div>`);
return;
}
function maskApiKey(key) {
if (!key) return '';
if (key.length <= 8) return '*'.repeat(key.length);
return key.substring(0, 3) + '-****' + key.slice(-4);
}
container.innerHTML = `
<div class="flex justify-between items-center mb-6 border-b pb-2 border-blue-100">
<h2 class="text-xl font-bold text-blue-700 flex items-center">
<i class='fas fa-cogs mr-2'></i>Configurar Proveedores IA
</h2>
<button id="btnCloseConfig" type="button" class="text-gray-500 hover:text-blue-600 text-2xl focus:outline-none" aria-label="Cerrar">
<i class="fas fa-times"></i>
</button>
</div>
<form id="iaConfigForm" class="space-y-6">
<div class="bg-blue-50 p-4 rounded-lg border border-blue-100 mb-2">
<label class="block font-semibold text-blue-800 mb-2">Proveedor LLM</label>
<select id="llmProvider" class="w-full mb-3 p-2 rounded border border-gray-300 focus:ring-2 focus:ring-blue-300">
${llmProviders.map(p => `<option value="${p.value}">${p.name}</option>`).join("")}
</select>
<div class="flex items-center mb-3">
<input type="password" id="llmApiKey" class="flex-1 p-2 rounded border border-gray-300 mr-2 bg-gray-100" placeholder="API Key LLM" autocomplete="off">
<button class="text-blue-700 hover:text-blue-900 px-3 py-2 rounded focus:outline-none border border-blue-200 bg-white" type="button" id="toggleLlmApiKey">
<i class="fas fa-eye"></i>
</button>
</div>
<select id="llmModel" class="w-full p-2 rounded border border-gray-300 focus:ring-2 focus:ring-blue-300"></select>
</div>
<div class="bg-purple-50 p-4 rounded-lg border border-purple-100 mb-2">
<label class="block font-semibold text-purple-800 mb-2">Proveedor Transcripci贸n</label>
<select id="transProvider" class="w-full mb-3 p-2 rounded border border-gray-300 focus:ring-2 focus:ring-purple-300">
${transcriptionProviders.map(p => `<option value="${p.value}">${p.name}</option>`).join("")}
</select>
<div class="flex items-center mb-3">
<input type="password" id="transApiKey" class="flex-1 p-2 rounded border border-gray-300 mr-2 bg-gray-100" placeholder="API Key Transcripci贸n" autocomplete="off">
<button class="text-purple-700 hover:text-purple-900 px-3 py-2 rounded focus:outline-none border border-purple-200 bg-white" type="button" id="toggleTransApiKey">
<i class="fas fa-eye"></i>
</button>
</div>
<select id="transModel" class="w-full p-2 rounded border border-gray-300 focus:ring-2 focus:ring-purple-300"></select>
</div>
<button type="submit" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-semibold py-3 rounded-lg shadow transition-colors flex items-center justify-center text-lg">
<i class="fas fa-save mr-2"></i>Guardar configuraci贸n
</button>
</form>
`;
// Bot贸n de cerrar modal
const closeBtn = document.getElementById("btnCloseConfig");
if (closeBtn) {
closeBtn.addEventListener("click", () => {
const modal = document.getElementById("configModal");
if (modal) modal.classList.remove("active");
});
}
// Set initial values
document.getElementById("llmProvider").value = config.llm.provider;
document.getElementById("llmApiKey").value = maskApiKey(config.llm.apiKeys[config.llm.provider] || '');
document.getElementById("transProvider").value = config.transcription.provider;
document.getElementById("transApiKey").value = maskApiKey(config.transcription.apiKeys[config.transcription.provider] || '');
// API Key toggle (ver/ocultar)
document.getElementById("toggleLlmApiKey").addEventListener("click", () => {
const input = document.getElementById("llmApiKey");
input.type = input.type === "password" ? "text" : "password";
});
document.getElementById("toggleTransApiKey").addEventListener("click", () => {
const input = document.getElementById("transApiKey");
input.type = input.type === "password" ? "text" : "password";
});
// Populate models
function updateLlmModels() {
const prov = document.getElementById("llmProvider").value;
const providerObj = llmProviders.find(p => p.value === prov);
const models = providerObj.models;
const sel = document.getElementById("llmModel");
sel.innerHTML = models.map(m => `<option value="${m}">${m}</option>`).join("");
sel.value = config.llm.model;
}
function updateTransModels() {
const prov = document.getElementById("transProvider").value;
const providerObj = transcriptionProviders.find(p => p.value === prov);
const models = providerObj.models;
const sel = document.getElementById("transModel");
sel.innerHTML = models.map(m => `<option value="${m}">${m}</option>`).join("");
sel.value = config.transcription.models[prov] || models[0];
// Actualizar API Key input al cambiar proveedor
document.getElementById("transApiKey").value = maskApiKey(config.transcription.apiKeys[prov] || '');
}
document.getElementById("llmProvider").addEventListener("change", () => {
const p = document.getElementById("llmProvider").value;
updateLlmModels();
// Refrescar config con todas las llaves y mostrar la del proveedor seleccionado
const fresh = loadConfig();
const keyEl = document.getElementById("llmApiKey");
if (keyEl) keyEl.value = maskApiKey(fresh.llm.apiKeys[p] || '');
});
document.getElementById("transProvider").addEventListener("change", updateTransModels);
updateLlmModels();
updateTransModels();
// Save on submit
document.getElementById("iaConfigForm").addEventListener("submit", e => {
e.preventDefault();
// Persistir configuraci贸n por proveedor
const prev = config;
const newConfig = { ...prev };
// LLM: provider, apiKeys y model
const llProv = document.getElementById("llmProvider").value;
const rawKey = document.getElementById("llmApiKey").value;
const oldKey = prev.llm.apiKeys[llProv] || '';
const newKey = rawKey === maskApiKey(oldKey) ? oldKey : rawKey;
newConfig.llm = { ...prev.llm, provider: llProv, model: document.getElementById("llmModel").value };
newConfig.llm.apiKeys = { ...prev.llm.apiKeys, [llProv]: newKey };
// Transcription: provider, apiKeys y models por proveedor
const tp = document.getElementById("transProvider").value;
const rawKeyTrans = document.getElementById("transApiKey").value;
const existingKeyTrans = prev.transcription.apiKeys[tp] || '';
const actualKeyTrans = rawKeyTrans === maskApiKey(existingKeyTrans) ? existingKeyTrans : rawKeyTrans;
newConfig.transcription.provider = tp;
newConfig.transcription.apiKeys = { ...prev.transcription.apiKeys, [tp]: actualKeyTrans };
newConfig.transcription.models = { ...prev.transcription.models, [tp]: document.getElementById("transModel").value };
console.log('[iaConfigModule] Configuraci贸n guardada:', newConfig);
saveConfig(newConfig);
// Actualizar config local despu茅s de guardar
config = newConfig;
// Actualizar labels de modelo en la UI
document.dispatchEvent(new CustomEvent('iaConfigChanged'));
// Ofusca los campos API Key
document.getElementById("llmApiKey").value = maskApiKey(newConfig.llm.apiKeys[newConfig.llm.provider] || '');
document.getElementById("transApiKey").value = maskApiKey(newConfig.transcription.apiKeys[tp] || '');
document.getElementById("llmApiKey").type = "password";
document.getElementById("transApiKey").type = "password";
// Mensaje de 茅xito discreto
let msg = document.getElementById('iaConfigSavedMsg');
if (!msg) {
msg = document.createElement('div');
msg.id = 'iaConfigSavedMsg';
msg.className = 'fixed left-1/2 top-6 -translate-x-1/2 bg-green-500 text-white px-6 py-3 rounded shadow text-lg z-50';
msg.innerHTML = '<i class="fas fa-check-circle mr-2"></i>隆Configuraci贸n guardada!';
document.body.appendChild(msg);
} else {
msg.style.display = 'block';
}
setTimeout(() => { msg.style.display = 'none'; }, 2000);
// Cierra el modal
const modal = document.getElementById("configModal");
if (modal) modal.classList.remove("active");
});
}