File size: 3,783 Bytes
f6cb78b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# 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)}"