Spaces:
Sleeping
Sleeping
| import { STATE, getMaxDimensions } from './State.js'; | |
| /** Builds and downloads a dimension analysis markdown export for LLM ingestion. */ | |
| export function exportDimensionAnalysis(dimIndex, customSource = null) { | |
| const data = customSource || (STATE.reliefPinned ? STATE.reliefPinned.source : null) || STATE.reliefData || (STATE.reliefDocs && STATE.reliefDocs[0]); | |
| if (!data || !data.matrix || data.matrix.length === 0) { | |
| console.warn('[Relieve] No hay matriz cargada para exportar.'); | |
| return; | |
| } | |
| const isSAE = STATE.featureSpace === 'SAE'; | |
| const dims = getMaxDimensions(); | |
| if (dimIndex < 0 || dimIndex >= dims) { | |
| console.warn(`[Relieve] Índice de dimensión ${dimIndex} fuera de rango (0-${dims - 1}).`); | |
| return; | |
| } | |
| const rows = data.matrix.map((row, chunkIndex) => ({ | |
| chunkIndex, | |
| value: typeof row[dimIndex] === 'number' && !Number.isNaN(row[dimIndex]) ? row[dimIndex] : 0, | |
| text: (data.text_metadata && data.text_metadata[chunkIndex] ? data.text_metadata[chunkIndex] : '').trim(), | |
| })); | |
| const sorted = [...rows].sort((a, b) => b.value - a.value); | |
| const values = rows.map(r => r.value); | |
| const maxV = Math.max(...values); | |
| const minV = Math.min(...values); | |
| const meanV = values.reduce((s, v) => s + v, 0) / (values.length || 1); | |
| const fmt = (v) => (v >= 0 ? '+' : '') + v.toFixed(4); | |
| const docName = data.filename || 'documento'; | |
| const stamp = new Date().toISOString(); | |
| const spaceLabel = isSAE ? 'SAE (Sparse Autoencoder 8,192 features)' : `RAW Embedding (${dims}D base)`; | |
| const lines = []; | |
| lines.push(`# Análisis de Dimensión #${dimIndex} — ${docName}`); | |
| lines.push(''); | |
| lines.push('## 🤖 Instrucciones para el modelo (Gemini / ChatGPT / LLM)'); | |
| lines.push(''); | |
| if (isSAE) { | |
| lines.push(`Eres un experto en interpretabilidad mecanicista de redes neuronales. Abajo están los fragmentos (chunks) del documento "${docName}" ordenados por la activación de la **característica latente SAE #${dimIndex}** (Sparse Autoencoder de 8,192 características).`); | |
| } else { | |
| lines.push(`Eres un analista de representaciones semánticas en modelos de embeddings. Abajo están los fragmentos (chunks) del documento "${docName}" ordenados por la activación de la **dimensión #${dimIndex}** (espacio denso base).`); | |
| } | |
| lines.push('Los fragmentos están ordenados de mayor a menor activación: los primeros (valores positivos altos) corresponden al concepto que MÁS estimula esta dimensión; los últimos (valores negativos) corresponden al polo opuesto.'); | |
| lines.push(''); | |
| lines.push('### Tu tarea de análisis:'); | |
| lines.push(`1. **Concepto Principal**: Determina qué concepto, tema, patrón gramatical o entidad semántica específica captura la dimensión #${dimIndex}.`); | |
| lines.push('2. **Comparación de Polos**: Contrasta los fragmentos con mayor activación positiva con los de menor o negativa activación.'); | |
| lines.push('3. **Etiqueta Sugerida**: Propón un nombre corto en MAYÚSCULAS (ej. "SEGURIDAD_VIAL", "TÉRMINOS_LEGALES") y una descripción resumida en 1-2 frases.'); | |
| lines.push(''); | |
| lines.push('## 📊 Metadatos'); | |
| lines.push(''); | |
| lines.push(`- **Documento**: ${docName}`); | |
| lines.push(`- **Espacio de Características**: ${spaceLabel}`); | |
| lines.push(`- **Dimensión / Feature ID**: #${dimIndex} (de ${dims})`); | |
| lines.push(`- **Total Fragmentos**: ${rows.length}`); | |
| lines.push(`- **Estadísticas de Activación**: Máx: ${fmt(maxV)} · Mín: ${fmt(minV)} · Media: ${fmt(meanV)}`); | |
| lines.push(`- **Fecha de Exportación**: ${stamp}`); | |
| lines.push(''); | |
| lines.push('## 📝 Fragmentos Ordenados por Activación'); | |
| lines.push(''); | |
| for (const r of sorted) { | |
| lines.push(`### Chunk ${r.chunkIndex} · activación: \`${fmt(r.value)}\``); | |
| lines.push(''); | |
| lines.push(r.text ? `> ${r.text.replace(/\n/g, '\n> ')}` : '*(fragmento sin texto)*'); | |
| lines.push(''); | |
| } | |
| const content = lines.join('\n'); | |
| if (navigator.clipboard && navigator.clipboard.writeText) { | |
| navigator.clipboard.writeText(content).catch(() => {}); | |
| } | |
| const blob = new Blob([content], { type: 'text/markdown;charset=utf-8' }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| const safeDoc = docName.replace(/[^\w.-]+/g, '_').slice(0, 40); | |
| a.href = url; | |
| a.download = `dimension_${dimIndex}_${safeDoc}.md`; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| setTimeout(() => URL.revokeObjectURL(url), 1000); | |
| const exportBtn = document.getElementById('relief-hud-export'); | |
| if (exportBtn) { | |
| const originalText = exportBtn.innerHTML; | |
| exportBtn.innerHTML = '✅ ¡Copiado al portapapeles y descargado!'; | |
| exportBtn.style.background = 'var(--color-semantic-success)'; | |
| exportBtn.style.color = 'var(--color-neutral-950)'; | |
| setTimeout(() => { | |
| exportBtn.innerHTML = originalText; | |
| exportBtn.style.background = 'var(--color-brand-secondary)'; | |
| exportBtn.style.color = 'var(--color-neutral-950)'; | |
| }, 2500); | |
| } | |
| } | |
| /** Wires the relief HUD export button to exportDimensionAnalysis. */ | |
| export function wireReliefExportButton() { | |
| const exportBtn = document.getElementById('relief-hud-export'); | |
| if (!exportBtn) return; | |
| exportBtn.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| const dim = STATE.reliefPinned | |
| ? STATE.reliefPinned.dimIndex | |
| : parseInt(exportBtn.dataset.dim || '-1', 10); | |
| const source = STATE.reliefPinned ? STATE.reliefPinned.source : null; | |
| if (dim >= 0) exportDimensionAnalysis(dim, source); | |
| }); | |
| } | |