Spaces:
Running
Running
# core/llm/llm_manager.py | |
import os | |
import base64 | |
from openai import OpenAI | |
from dotenv import load_dotenv | |
from loguru import logger | |
load_dotenv(dotenv_path="config/.env") | |
class LLMManager: | |
"""Gestor de interacción con modelos de lenguaje compatibles con la API de OpenAI.""" | |
def __init__(self): | |
self.api_key = os.getenv("LLM_API_KEY") | |
self.base_url = os.getenv("LLM_BASE_URL") | |
self.model = os.getenv("LLM_MODEL_NAME") | |
self.client = OpenAI(api_key=self.api_key, base_url=self.base_url) | |
self.prompt_system = self._load_system_prompt() | |
def _load_system_prompt(self) -> str: | |
"""Carga el prompt del sistema desde 'config/prompt_system.txt'.""" | |
path_system_prompt = os.getenv("PATH_SYSTEM_PROMPT") | |
try: | |
with open(path_system_prompt, "r", encoding="utf-8") as f: | |
logger.info("✅ Prompt del sistema cargado correctamente.") | |
return f.read().strip() | |
except FileNotFoundError: | |
logger.warning( | |
f"⚠️ No se encontró '{path_system_prompt}'. Se usará un prompt por defecto." | |
) | |
return "Eres un asistente educativo del MINEDU." | |
def _encode_image(self, image_bytes: bytes) -> str: | |
"""Convierte bytes de imagen a Base64.""" | |
logger.debug("🔄 Codificando imagen a Base64.") | |
return base64.b64encode(image_bytes).decode("utf-8") | |
def generate_response( | |
self, user_query: str, context: str = "", image: bytes = None | |
) -> str: | |
"""Genera respuesta multimodal (texto + imagen) o solo texto.""" | |
try: | |
logger.info("🔹 Generando respuesta para la consulta del usuario.") | |
messages = [] | |
# Añadir prompt del sistema | |
if self.prompt_system: | |
messages.append({"role": "system", "content": self.prompt_system}) | |
# Si es imagen (multimodal) | |
if image: | |
logger.debug("🖼️ Procesando entrada multimodal con imagen.") | |
base64_image = self._encode_image(image) | |
messages.append( | |
{ | |
"role": "user", | |
"content": [ | |
{ | |
"type": "text", | |
"text": user_query | |
if user_query | |
else "Describe esta imagen con enfoque educativo.", | |
}, | |
{ | |
"type": "image_url", | |
"image_url": { | |
"url": f"data:image/png;base64,{base64_image}" | |
}, | |
}, | |
], | |
} | |
) | |
else: | |
# Solo texto, con posible contexto | |
full_prompt = user_query | |
if context: | |
logger.debug("➕ Añadiendo contexto al mensaje.") | |
full_prompt = f"{context}\n\nPregunta: {user_query}" | |
messages.append({"role": "user", "content": full_prompt}) | |
# Llamada al modelo | |
response = self.client.chat.completions.create( | |
model=self.model, | |
messages=messages, | |
) | |
logger.success("✅ Respuesta generada correctamente.") | |
return response.choices[0].message.content | |
except Exception as e: | |
logger.error(f"❌ Error al generar respuesta: {str(e)}") | |
return f"Error al generar respuesta: {str(e)}" | |