Spaces:
Running
Running
| // 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"); | |
| }); | |
| } | |