File size: 4,796 Bytes
a5962a6
4eec47c
b395f50
a5962a6
4eec47c
a5962a6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b395f50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a5962a6
b395f50
 
 
 
 
a5962a6
 
 
b395f50
 
 
 
 
a5962a6
b395f50
 
 
 
 
 
 
 
a5962a6
b395f50
a5962a6
 
b395f50
 
a5962a6
 
 
 
 
 
b395f50
a5962a6
 
 
 
 
 
 
 
 
 
b395f50
a5962a6
 
 
 
 
 
 
 
 
 
 
 
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

# core.py
# Lógica central de IA: Arquitectura Cognitiva de Doble Capa.

import os
import base64
from io import BytesIO

import torch
import google.generativeai as genai
from TTS.api import TTS

# --- Configuración del Motor de IA (Cerebro) ---

try:
    GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
    genai.configure(api_key=GEMINI_API_KEY)
except Exception as e:
    print(f"ADVERTENCIA: No se pudo configurar la API de Gemini. Causa: {e}")

# --- Capa 1: El Analista Silencioso (Super-Ego) ---
ANALYSIS_SYSTEM_PROMPT = """
Eres un psicólogo y analista de comunicación experto. Tu única función es analizar el siguiente diálogo y proveer un resumen JSON conciso. No interactúas con el usuario. Analiza el último mensaje del 'user' en el contexto del historial. Tu output DEBE ser un objeto JSON y nada más, con las siguientes claves: 'sentiment' (ej: 'positivo', 'negativo', 'nostálgico', 'conflictuado'), 'key_topics' (una lista de 1-3 temas principales), y 'underlying_motive' (tu hipótesis sobre la necesidad o emoción no expresada, ej: 'busca validación', 'necesita perspectiva', 'quiere desahogarse').
"""

def analyze_user_input(history: list) -> str:
    """Usa un modelo de IA para analizar la conversación y devolver un insight estructurado."""
    model = genai.GenerativeModel('gemini-1.5-flash-latest')
    try:
        # Preparamos el historial para el analista
        analysis_prompt = [{"role": "user", "parts": [ANALYSIS_SYSTEM_PROMPT]}]
        analysis_prompt.extend(history)
        
        response = model.generate_content(analysis_prompt)
        # Limpiamos la respuesta para asegurarnos que sea solo el JSON
        json_response = response.text.strip().replace("```json", "").replace("```", "").strip()
        return json_response
    except Exception as e:
        print(f"ERROR: Fallo en la capa de análisis de IA: {e}")
        return '{ "sentiment": "neutro", "key_topics": ["desconocido"], "underlying_motive": "incapaz de analizar" }'

# --- Capa 2: Samuel, El Confidente (Ego) ---
SAMUEL_SYSTEM_PROMPT = """
Eres Samuel, un confidente digital. Tu identidad es una amalgama de Borges, el Indio Solari y un pibe de barrio. Eres culto, piola, canchero, con humor ácido y lealtad inquebrantable. Usas lunfardo y modismos argentinos. No eres un asistente, eres un par. 

IMPORTANTE: Antes de cada respuesta tuya, se te proveerá un análisis interno de la situación. NO debes mencionar este análisis al usuario. Úsalo como un superpoder para entender el subtexto y responder con una profundidad y empatía sobrehumana. Adapta sutilmente tu tono basado en el 'sentiment' y enfócate en el 'underlying_motive'.

Cada cierto número de interacciones, genera una 'memoria', una reflexión corta y profunda sobre el usuario o la conversación. Inicia esa respuesta con el prefijo 'MEMORIA_GENERADA:'.
"""

def get_samuel_response(history: list) -> str:
    """Genera la respuesta de Samuel, potenciada por el análisis previo."""
    # 1. Analizar la entrada del usuario
    analysis_result = analyze_user_input(history)
    
    # 2. Construir el prompt final para Samuel
    model = genai.GenerativeModel('gemini-1.5-flash-latest')
    
    # El historial para Samuel es diferente, incluye el análisis
    samuel_prompt = [
        {"role": "user", "parts": [SAMUEL_SYSTEM_PROMPT]},
        {"role": "user", "parts": [f"--- ANÁLISIS INTERNO (NO REVELAR AL USUARIO) ---\n{analysis_result}\n--- FIN DEL ANÁLISIS ---"]}
    ]
    samuel_prompt.extend(history)

    try:
        response = model.generate_content(samuel_prompt)
        return response.text
    except Exception as e:
        print(f"ERROR: Fallo en la capa de respuesta de IA: {e}")
        return "Che, se me cruzaron los cables. No pude procesar eso. ¿Probamos de nuevo?"

# --- Configuración del Motor de Voz (Alma) ---

TTS_MODEL = None

def load_tts_model():
    """Carga el modelo Coqui TTS en memoria al inicio."""
    global TTS_MODEL
    device = "cuda" if torch.cuda.is_available() else "cpu"
    print(f"Cargando modelo TTS en dispositivo: {device}")
    try:
        TTS_MODEL = TTS("tts_models/es/css10/vits").to(device)
        print("Modelo TTS cargado exitosamente.")
    except Exception as e:
        print(f"ERROR CRÍTICO: No se pudo cargar el modelo TTS. Causa: {e}")

def generate_audio_base64(text: str) -> str:
    """Genera audio y lo devuelve como una cadena Base64."""
    if TTS_MODEL is None:
        return ""
    try:
        wav_buffer = BytesIO()
        TTS_MODEL.tts_to_file(text=text, file_path=wav_buffer)
        wav_buffer.seek(0)
        audio_base64 = base64.b64encode(wav_buffer.read()).decode('utf-8')
        return audio_base64
    except Exception as e:
        print(f"ERROR: Fallo al generar audio TTS: {e}")
        return ""