066047f3d1
- db.ts: v3 con tablas sessions, session_summaries, project_state - transcriptions-db.ts: CRUD para sesiones, summaries, project state - project-doc.ts: generateMasterDoc() desde BD (no desde datos en memoria) - session-analyzer.ts: +sessionDate en prompt y extraction - TranscriptionsView: flujo parse -> guardar BD -> IA -> project_state -> MD - docs/arquitectura_transcripciones.md: documentacion oficial del patron
105 lines
3.7 KiB
TypeScript
105 lines
3.7 KiB
TypeScript
import { callAI } from '@/services/ai'
|
|
|
|
export interface SessionExtraction {
|
|
sessionDate: string // YYYY-MM-DD
|
|
sessionTitle: string
|
|
summary: string
|
|
objectives: { text: string; isNew: boolean }[]
|
|
pendingTasks: { description: string; origin: string; priority: string }[]
|
|
commitments: { description: string; responsible: string; dueDate: string; status: string }[]
|
|
decisions: string[]
|
|
completedTasks: string[]
|
|
keyPoints: string[]
|
|
}
|
|
|
|
const SESSION_SYSTEM_PROMPT = `Eres un asistente de gestión de proyectos. Analizás transcripciones de reuniones y extraés información estructurada.
|
|
|
|
Reglas:
|
|
1. Identificá la fecha de la sesión (si no está explícita, usá la fecha actual)
|
|
2. Identificá el título de la sesión basado en el contenido
|
|
3. Extraé un resumen ejecutivo de 2-3 oraciones
|
|
4. Listá objetivos mencionados, marcando cuáles son NUEVOS vs existentes
|
|
5. Extraé tareas pendientes con su origen y prioridad (Alta/Media/Baja)
|
|
6. Identificá compromisos con responsable, fecha límite y estado
|
|
7. Listá decisiones tomadas durante la sesión
|
|
8. Detectá tareas completadas (si hay evidencia)
|
|
9. Incluí puntos clave, bloqueos o descubrimientos
|
|
10. No inventes información que no esté en la transcripción
|
|
11. Respondé SOLO con JSON válido
|
|
|
|
Formato de respuesta JSON:
|
|
{
|
|
"sessionDate": "YYYY-MM-DD",
|
|
"sessionTitle": "Título descriptivo de la sesión",
|
|
"summary": "Resumen ejecutivo de 2-3 oraciones",
|
|
"objectives": [
|
|
{ "text": "Descripción del objetivo", "isNew": true }
|
|
],
|
|
"pendingTasks": [
|
|
{ "description": "Descripción de la tarea", "origin": "Sesión o contexto", "priority": "Alta|Media|Baja" }
|
|
],
|
|
"commitments": [
|
|
{ "description": "Compromiso", "responsible": "Nombre", "dueDate": "YYYY-MM-DD", "status": "Pendiente|Cumplido|Vencido" }
|
|
],
|
|
"decisions": ["Decisión 1", "Decisión 2"],
|
|
"completedTasks": ["Tarea completada 1"],
|
|
"keyPoints": ["Punto clave 1"]
|
|
}`
|
|
|
|
export async function analyzeSession(
|
|
transcription: string,
|
|
projectName: string,
|
|
signal?: AbortSignal
|
|
): Promise<SessionExtraction> {
|
|
const userContent = `Proyecto: ${projectName}\n\nTranscripción:\n${transcription}`
|
|
|
|
console.log(`[Alpha] Session analyze — project: ${projectName}, text: ${transcription.length} chars`)
|
|
|
|
const content = await callAI(
|
|
[
|
|
{ role: 'system', content: SESSION_SYSTEM_PROMPT },
|
|
{ role: 'user', content: userContent },
|
|
],
|
|
0.3,
|
|
4096,
|
|
signal,
|
|
)
|
|
|
|
try {
|
|
const jsonStr = extractJSON(content)
|
|
const result: SessionExtraction = JSON.parse(jsonStr)
|
|
console.log(`[Alpha] Session analysis complete — ${result.pendingTasks.length} tasks, ${result.decisions.length} decisions`)
|
|
return result
|
|
} catch (e) {
|
|
console.error('[Alpha] Failed to parse session analysis. Raw response:', content)
|
|
throw new Error('No se pudo parsear el análisis de la sesión')
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extrae el primer objeto JSON válido del texto.
|
|
* Maneja markdown code blocks, texto antes/después, etc.
|
|
*/
|
|
function extractJSON(text: string): string {
|
|
// 1. Intentar parse directo
|
|
try { JSON.parse(text); return text } catch {}
|
|
|
|
// 2. Extraer entre ```json y ```
|
|
const jsonBlock = text.match(/```(?:json)?\s*([\s\S]*?)```/)
|
|
if (jsonBlock) {
|
|
const candidate = jsonBlock[1].trim()
|
|
try { JSON.parse(candidate); return candidate } catch {}
|
|
}
|
|
|
|
// 3. Extraer entre { y } (first { to last })
|
|
const firstBrace = text.indexOf('{')
|
|
const lastBrace = text.lastIndexOf('}')
|
|
if (firstBrace !== -1 && lastBrace > firstBrace) {
|
|
const candidate = text.slice(firstBrace, lastBrace + 1)
|
|
try { JSON.parse(candidate); return candidate } catch {}
|
|
}
|
|
|
|
// 4. Fallback: devolver el texto original (lanzará error en el caller)
|
|
return text
|
|
}
|