Spaces:
Running
Running
File size: 11,424 Bytes
f1602bc 2be59fa f1602bc |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
// 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");
});
}
|