Spaces:
Sleeping
Sleeping
| # 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 "" | |