iagofp's picture
Añadido groq, pysentimiento en local
85885ab
"""
Este archivo se encarga de generar el texto que el chatbot mostrará al usuario
basado en su estado emocional, recomendaciones y eventos anteriores. Se usa la
Inference API de HuggingFace como generador principal, con plantilla de respaldo.
"""
import requests as http_requests
from config import GROQ_TOKEN, GROQ_TEXT_MODEL, GROQ_CHAT_URL
def construir_respuesta_manual(
emocion_dominante: str,
modo_recomendacion: str,
recomendaciones: list[dict],
emocion_previa: dict | None,
pelicula_transicion: dict | None,
) -> str:
"""
Construye la respuesta que el chatbot da en base al estado emocional del usuario
Args:
- emocion_dominante: El estado emocional actual del usuario (e.g. "alegria", "tristeza").
- modo_recomendacion: El modo de recomendacion basado en la emocion
- recomendaciones: Lista de peliculas recomendadas para el usuario.
- emocion_previa: El estado emocional anterior del usuario, si existe.
- pelicula_transicion: La pelicula vista entre el estado emocional anterior y el actual
Returns:
- Un texto que el chatbot puede mostrar al usuario explicando su estado emocional, el modo de recomendacion, y mencionando alguna pelicula recomendada.
"""
# Primero se construye la parte explicativa del estado y modo de recomendacion.
if modo_recomendacion == "diferente":
mode_text = "Como estas en un estado positivo, te propongo explorar peliculas distintas a tu historial."
else:
mode_text = "Como estas en un estado negativo, te propongo peliculas similares a tu historial para mantener una zona conocida."
# Una vez explicado el modo se menciona la pelicula con mejor puntuacion de las recomendaciones
if recomendaciones:
top_title = recomendaciones[0].get("title", "una pelicula")
reco_text = f"Primera sugerencia: {top_title}."
else:
reco_text = "No encontre recomendaciones para ese estado emocional en este momento."
# Se construye la parte de transicion de una emocion a otra
if not emocion_previa:
transition_text = "Este es tu primer punto de referencia emocional para analizar transiciones futuras."
else:
prev_emotion = emocion_previa.get("emotion", "neutral") # Si no se encuentra la emocion previa, se asume neutral para la explicacion.
if prev_emotion == emocion_dominante:
transition_text = f"Tu estado se mantiene en {emocion_dominante}." # Si la emocion previa es igual a la actual, se explica que no hay transicion.
else:
transition_text = f"Detecto una transicion de {prev_emotion} a {emocion_dominante}." # Si hay una emocion previa diferente a la actual, se explica la transicion.
if pelicula_transicion:
movie_title = pelicula_transicion.get("title") or pelicula_transicion.get("movie_id", "pelicula marcada")
transition_text += f" Ultima pelicula vista entre ambos estados: {movie_title}."
return f"Estado actual: {emocion_dominante}. {mode_text} {reco_text} {transition_text}"
def _construir_prompt(
emocion_dominante: str,
modo_recomendacion: str,
recomendaciones: list[dict],
emocion_previa: dict | None,
pelicula_transicion: dict | None,
) -> str:
"""
Funcion que construye el prompt para la generacion de texto con Ollama.
Se le da contexto sobre el estado emocional actual, el modo de recomendacion, las recomendaciones, la emocion previa y la pelicula de transicion para que genere una respuesta empatica y explicativa.
Args:
- emocion_dominante: El estado emocional actual del usuario.
- modo_recomendacion: El modo de recomendacion basado en la emocion.
- recomendaciones: Lista de peliculas recomendadas para el usuario.
- emocion_previa: El estado emocional anterior del usuario, si existe.
- pelicula_transicion: La pelicula vista entre el estado emocional anterior y el actual.
Returns:
- Un prompt de texto que se le puede pasar a Ollama para generar una respuesta.
"""
titulos_peliculas_top = [str(row.get("title", "")).strip() for row in recomendaciones[:3] if row.get("title")]
titulos_peliculas_top_text = ", ".join(titulos_peliculas_top) if titulos_peliculas_top else "sin recomendaciones"
prev_emotion = emocion_previa.get("emotion") if emocion_previa else "ninguna"
titulo_pelicula_transicion = ""
if pelicula_transicion:
titulo_pelicula_transicion = str(pelicula_transicion.get("title") or pelicula_transicion.get("movie_id") or "").strip()
return (
"Eres un asistente de recomendaciones de peliculas. "
"Redacta una respuesta breve en espanol (maximo 3 frases), clara y empatica. "
"Incluye estado emocional actual, explicacion de por que el modo de recomendacion es ese, "
"y menciona una pelicula sugerida si existe. "
f"Estado actual: {emocion_dominante}. "
f"Modo: {modo_recomendacion}. "
f"Emocion anterior: {prev_emotion}. "
f"Peliculas sugeridas: {titulos_peliculas_top_text}. "
f"Pelicula asociada a transicion: {titulo_pelicula_transicion or 'ninguna'}."
)
def generar_texto_chatbot(
emocion_dominante: str,
modo_recomendacion: str,
recomendaciones: list[dict],
emocion_previa: dict | None,
pelicula_transicion: dict | None,
) -> tuple[str, str]:
"""
Funcion que genera el texto final a mostrar. Intenta usar Ollama en primera instancia,
si falla utiliza la plantilla contruida en construir_respuesta_manual().
Args:
- emocion_dominante: El estado emocional actual del usuario.
- modo_recomendacion: El modo de recomendacion basado en la emocion.
- recomendaciones: Lista de peliculas recomendadas para el usuario.
- emocion_previa: El estado emocional anterior del usuario, si existe.
- pelicula_transicion: La pelicula vista entre el estado emocional anterior y el actual.
Returns:
- Un tuple con el texto generado y la fuente del texto ("ollama" o "template-fallback").
"""
respuesta_manual = construir_respuesta_manual(
emocion_dominante=emocion_dominante,
modo_recomendacion=modo_recomendacion,
recomendaciones=recomendaciones,
emocion_previa=emocion_previa,
pelicula_transicion=pelicula_transicion,
)
try:
prompt = _construir_prompt(
emocion_dominante=emocion_dominante,
modo_recomendacion=modo_recomendacion,
recomendaciones=recomendaciones,
emocion_previa=emocion_previa,
pelicula_transicion=pelicula_transicion,
)
url = GROQ_CHAT_URL
headers = {
"Authorization": f"Bearer {GROQ_TOKEN}",
"Content-Type": "application/json",
}
payload = {
"model": GROQ_TEXT_MODEL,
"messages": [{"role": "user", "content": prompt}],
"max_tokens": 120,
"temperature": 0.7,
"top_p": 0.9,
}
res = http_requests.post(url, headers=headers, json=payload, timeout=20)
res.raise_for_status()
data = res.json()
generated = str(data["choices"][0]["message"]["content"]).strip() if data.get("choices") else ""
if generated:
return generated, "groq"
print("Aviso: Groq devolvio respuesta vacia. Se usa plantilla.")
except Exception as exc:
print(f"Aviso: fallo generando texto con Groq ({exc}). Se usa plantilla.")
return respuesta_manual, "template-fallback"