Spaces:
Running
Running
File size: 10,874 Bytes
3e45881 30640f2 3e45881 30640f2 3e45881 e56e25e 3e45881 e56e25e 3e45881 30640f2 3e45881 b37f788 3e45881 b37f788 3e45881 de7af8e b37f788 3e45881 b37f788 3e45881 06050b2 3e45881 06050b2 3e45881 30640f2 b37f788 3e45881 de7af8e f1602bc 30640f2 3e45881 a15b26b 3e45881 b565510 3e45881 30640f2 3e45881 b565510 b37f788 3e45881 e56e25e 3e45881 215337f 4c9a302 3e45881 b565510 3e45881 b37f788 06050b2 3e45881 e56e25e 3e45881 b565510 3e45881 30640f2 3e45881 |
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 228 229 230 231 232 233 234 235 236 237 |
// 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" }
}
};
// CORRECCI脫N: Nombres de modelos de OpenAI actualizados con tus nuevas solicitudes.
export const llmProviders = [
{ name: "OpenAI", value: "openai", models: ["gpt-4o", "gpt-4o-mini", "gpt-5-mini", "gpt-5", "gpt-5-nano"], url: "https://api.openai.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) {
try {
localStorage.setItem("iaConfig", JSON.stringify(config));
} catch (e) {
console.error("[iaConfigModule] Error guardando la configuraci贸n:", e);
}
}
function clone(obj) {
// structuredClone es moderno y robusto, con un fallback seguro.
try {
return structuredClone(obj);
} catch (e) {
return JSON.parse(JSON.stringify(obj));
}
}
function loadConfig() {
let config;
try {
const storedConfig = localStorage.getItem("iaConfig");
config = storedConfig ?
JSON.parse(storedConfig) : clone(defaultConfig);
} catch (error) {
console.error("[iaConfigModule] Error al parsear config, usando defaults.", error);
config = clone(defaultConfig);
}
let needsSave = false;
if (config.transcription.apiKey !== undefined) {
console.log("[iaConfigModule] Migrando config antigua de transcripci贸n...");
const oldKey = config.transcription.apiKey;
const oldModel = config.transcription.model;
const oldProvider = config.transcription.provider;
// Se usa el spread operator para no perder las keys de otros proveedores.
config.transcription.apiKeys = { ...defaultConfig.transcription.apiKeys, [oldProvider]: oldKey };
config.transcription.models = { ...defaultConfig.transcription.models, [oldProvider]: oldModel };
delete config.transcription.apiKey;
delete config.transcription.model;
needsSave = true;
}
if (config.llm.apiKey !== undefined) {
console.log("[iaConfigModule] Migrando config antigua de LLM...");
const old = config.llm.apiKey;
config.llm.apiKeys = { ...defaultConfig.llm.apiKeys, [config.llm.provider]: old };
delete config.llm.apiKey;
needsSave = true;
}
if (config.llm.provider === 'deepseek' && (config.llm.model === 'deepseek-v3' || config.llm.model === 'deepseek-llm')) {
config.llm.model = 'deepseek-chat';
needsSave = true;
}
if (needsSave) {
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; padding: 1rem;'>[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">
<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">
<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>
`;
// Elementos del DOM
const llmProviderSelect = document.getElementById("llmProvider");
const llmModelSelect = document.getElementById("llmModel");
const llmApiKeyInput = document.getElementById("llmApiKey");
const transProviderSelect = document.getElementById("transProvider");
const transModelSelect = document.getElementById("transModel");
const transApiKeyInput = document.getElementById("transApiKey");
// Listeners
document.getElementById("btnCloseConfig")?.addEventListener("click", () => document.getElementById("configModal")?.classList.remove("active"));
document.getElementById("toggleLlmApiKey").addEventListener("click", () => { llmApiKeyInput.type = llmApiKeyInput.type === "password" ? "text" : "password"; });
document.getElementById("toggleTransApiKey").addEventListener("click", () => { transApiKeyInput.type = transApiKeyInput.type === "password" ? "text" : "password"; });
function updateLlmUI() {
const selectedProvider = llmProviderSelect.value;
const providerObj = llmProviders.find(p => p.value === selectedProvider);
if (!providerObj) return;
llmModelSelect.innerHTML = providerObj.models.map(m => `<option value="${m}">${m}</option>`).join("");
let modelToSelect = providerObj.models[0];
if (selectedProvider === config.llm.provider) {
if (providerObj.models.includes(config.llm.model)) {
modelToSelect = config.llm.model;
}
}
llmModelSelect.value = modelToSelect;
llmApiKeyInput.value = maskApiKey(config.llm.apiKeys[selectedProvider] || '');
}
function updateTransUI() {
const selectedProvider = transProviderSelect.value;
const providerObj = transcriptionProviders.find(p => p.value === selectedProvider);
if (!providerObj) return;
transModelSelect.innerHTML = providerObj.models.map(m => `<option value="${m}">${m}</option>`).join("");
transModelSelect.value = config.transcription.models[selectedProvider] || providerObj.models[0];
transApiKeyInput.value = maskApiKey(config.transcription.apiKeys[selectedProvider] || '');
}
llmProviderSelect.addEventListener("change", updateLlmUI);
transProviderSelect.addEventListener("change", updateTransUI);
// Estado inicial del formulario
llmProviderSelect.value = config.llm.provider;
transProviderSelect.value = config.transcription.provider;
updateLlmUI();
updateTransUI();
// Guardar configuraci贸n
document.getElementById("iaConfigForm").addEventListener("submit", e => {
e.preventDefault();
const prevConfig = config;
const newConfig = clone(prevConfig);
const llmProv = llmProviderSelect.value;
const rawLlmKey = llmApiKeyInput.value;
const oldLlmKey = prevConfig.llm.apiKeys[llmProv] || '';
const actualLlmKey = rawLlmKey === maskApiKey(oldLlmKey) ? oldLlmKey : rawLlmKey;
newConfig.llm = {
...prevConfig.llm,
provider: llmProv,
model: llmModelSelect.value,
apiKeys: { ...prevConfig.llm.apiKeys, [llmProv]: actualLlmKey }
};
const transProv = transProviderSelect.value;
const rawTransKey = transApiKeyInput.value;
const oldTransKey = prevConfig.transcription.apiKeys[transProv] || '';
const actualTransKey = rawTransKey === maskApiKey(oldTransKey) ? oldTransKey : rawTransKey;
newConfig.transcription = {
...prevConfig.transcription,
provider: transProv,
models: { ...prevConfig.transcription.models, [transProv]: transModelSelect.value },
apiKeys: { ...prevConfig.transcription.apiKeys, [transProv]: actualTransKey }
};
saveConfig(newConfig);
config = newConfig;
document.dispatchEvent(new CustomEvent('iaConfigChanged'));
llmApiKeyInput.value = maskApiKey(newConfig.llm.apiKeys[newConfig.llm.provider] || '');
transApiKeyInput.value = maskApiKey(newConfig.transcription.apiKeys[newConfig.transcription.provider] || '');
llmApiKeyInput.type = "password";
transApiKeyInput.type = "password";
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-lg text-lg z-50';
msg.innerHTML = '<i class="fas fa-check-circle mr-2"></i>隆Configuraci贸n guardada!';
document.body.appendChild(msg);
}
msg.style.display = 'block';
setTimeout(() => { msg.style.display = 'none'; }, 2000);
document.getElementById("configModal")?.classList.remove("active");
});
} |