Implementación del cliente Ollama y su credencial, integración de logging en base de datos, y mejoras en la gestión de herramientas MCP.

This commit is contained in:
2025-05-19 22:57:01 +02:00
parent 9db2f70009
commit 6b491a9a41
10 changed files with 544 additions and 167 deletions
+51 -30
View File
@@ -11,29 +11,43 @@ from fastmcp.client.transports import StreamableHttpTransport
from fastmcp.client import Client
from src.Llms.MCPs.McpClient import MCPClient # ya tienes esta clase
from src.Llms.MCPs.McpClient_Registry import ClientRegistry # o ajusta según tu estructura
from src.Credenciales.ollama_credencial import OllamaCredencial
from src.ConexionApis.Ollama_cliente import OllamaCliente
from src.Llms.Modelos.Ollama_model import ModeloOllama
import asyncio
async def main():
# Usar Credencial openai
# # Usar Credencial openai
conexion_admin = PostgresConexion(db_credencial)
repo = OpenAICredencialRepo(conexion_admin)
credencial_openai = repo.get_by_id("OPAK20250513-61b29978b7604031014")
cliente = OpenAICliente(credencial_openai)
# conexion_admin = PostgresConexion(db_credencial)
# repo = OpenAICredencialRepo(conexion_admin)
# credencial_openai = repo.get_by_id("OPAK20250513-61b29978b7604031014")
# if credencial_openai is None:
# raise ValueError("No se encontró la credencial OpenAI con el ID proporcionado.")
# cliente = OpenAICliente(credencial_openai)
# Usar Credencial ollama
credencial_ollama = OllamaCredencial(titulo="Ollama")
# crea el modelo (openai)
cliente = OllamaCliente(credencial_ollama)
modelo = ModeloOpenAI(
modelo = ModeloOllama(
cliente=cliente,
model="gpt-4o",
temperature=1,
top_p=1.0
)
model="llama3.1:8b")
# # crea el modelo (openai)
# modelo = ModeloOpenAI(
# cliente=cliente,
# model="gpt-4o",
# temperature=1
# )
# Le otorga memoria
@@ -70,20 +84,20 @@ async def main():
agente2 = AgenteAI(
modelo=modelo,
nombre="Asistente Inteligente",
descripcion="Un asistente conversacional versátil, capaz de resolver problemas, acceder a herramientas y proporcionar respuestas útiles.",
system_prompt=(
"Eres un asistente inteligente que ayuda al usuario a resolver tareas, responder preguntas y usar herramientas disponibles si es necesario. "
"Debes razonar paso a paso, y si se detecta que una herramienta MCP es útil, actúa generando el bloque MCP apropiado sin dar más explicaciones. "
"Siempre estructura tus respuestas con claridad, y termina con <END> cuando creas haber completado la tarea."
),
descripcion="",
system_prompt="",
# system_prompt = """
# Eres un asistente general. No tienes acceso a herramientas externas ni herramientas MCP. No debes mencionar herramientas MCP, servidores ni bloques de código.
# Responde de forma clara y amigable a cualquier pregunta general del usuario.
# """.strip(),
rol="asistente",
objetivos=[
"Resolver tareas del usuario",
"Usar herramientas MCP si es útil",
"Responder de forma clara y útil"
# "Resolver tareas del usuario",
# "Usar herramientas MCP si es útil",
# "Responder de forma clara y útil"
],
# max_iterations=3,
max_iterations=0,
# memoria=memoria,
mcp=registry # ← ✅ Integración del cliente MCP
@@ -92,22 +106,29 @@ async def main():
# --- FUNCIÓN DE EJECUCIÓN ---
async def probar_interaccion_stream():
# # 🔌 Conectar a los servidores MCP registrados
# await mcp_client.connect_all()
print("🧠 Agente iniciado. Escribe 'salir' para terminar.\n")
print("Respuesta en streaming:\n")
respuesta_gen = await agente2.interactuar_en_bucle(
"¿Cuál es mi nombre de usuario en este sistema?",
stream=True
)
while True:
prompt = input("\n📝 Escribe tu pregunta: ")
if prompt.strip().lower() in ("salir", "exit", "quit"):
print("\n👋 Hasta pronto.")
break
async for token in respuesta_gen:
print(token, end="", flush=True)
print("\n💬 Respuesta en streaming:\n")
respuesta_gen = await agente2.interactuar_en_bucle(
prompt=prompt,
stream=True
)
async for token in respuesta_gen:
print(token, end="", flush=True)
await probar_interaccion_stream()
# Ejecutar
if __name__ == "__main__":
asyncio.run(main())
+8
View File
@@ -0,0 +1,8 @@
from entrypoint.init_db import db_credencial
from src.Logger.logger_db import LoggerDB, logger
LoggerDB(db_credencial, "logger_eventos", created_by="sistema_agente")
logger.info("Esto solo se verá en la base de datos")
logger.error("No aparecerá en la terminal")
+62
View File
@@ -0,0 +1,62 @@
import requests
from src.Credenciales.ollama_credencial import OllamaCredencial
class OllamaCliente:
def __init__(self, credencial: OllamaCredencial):
"""
Inicializa el cliente de Ollama con una instancia de OllamaCredencial.
"""
self.credencial = credencial
self.base_url = self.credencial.base_url
# --- Chat Completions ---
def chat_completion(self, model: str, messages: list, stream: bool = False, **kwargs):
url = f"{self.base_url}/api/chat"
payload = {
"model": model,
"messages": messages,
"stream": stream,
**kwargs
}
response = requests.post(url, json=payload, stream=stream)
response.raise_for_status()
return self._handle_stream(response) if stream else response.json()
def _handle_stream(self, response):
for line in response.iter_lines():
if line:
try:
parsed = line.decode("utf-8")
# Extraer contenido si está en JSON como {"message":{"content":"..."},...}
if parsed.startswith("{"):
import json
data = json.loads(parsed)
if "message" in data and "content" in data["message"]:
yield data["message"]["content"]
except Exception:
continue
# --- Text Completion (legacy) ---
def completion(self, model: str, prompt: str, **kwargs):
url = f"{self.base_url}/api/generate"
payload = {
"model": model,
"prompt": prompt,
**kwargs
}
response = requests.post(url, json=payload)
response.raise_for_status()
return response.json()
# --- Embeddings ---
def embedding(self, model: str, input: str | list[str], **kwargs):
url = f"{self.base_url}/api/embeddings"
payload = {
"model": model,
"prompt": input,
**kwargs
}
response = requests.post(url, json=payload)
response.raise_for_status()
return response.json()
+20
View File
@@ -0,0 +1,20 @@
from src.Security.GenerarIDs import GeneradorIDUnico
class OllamaCredencial:
def __init__(self, titulo: str, base_url: str = "http://localhost:11434", id: str = None):
"""
:param titulo: Nombre descriptivo para esta credencial de Ollama.
:param base_url: URL base donde está corriendo el servidor de Ollama (por defecto: localhost).
"""
self.id = id if id is not None else GeneradorIDUnico("OLLA").generar()
self.titulo = titulo
self.base_url = base_url.rstrip("/")
def get_headers(self) -> dict:
"""
Retorna encabezados para autenticación si se requiere en el futuro.
Por defecto, Ollama local no usa headers especiales.
"""
return {
"Content-Type": "application/json"
}
+258 -120
View File
@@ -3,6 +3,14 @@ from src.Llms.Memory.Base_MemoryConv import MemoryConvABC
from src.Llms.MCPs.McpClient_Registry import ClientRegistry
from datetime import datetime
from typing import Optional, List, Union, AsyncGenerator
import re
import json
from entrypoint.init_db import db_credencial
from src.Logger.logger_db import LoggerDB, logger
LoggerDB(db_credencial, "logger_agentes", created_by="sistema")
class AgenteAI:
@@ -17,7 +25,7 @@ class AgenteAI:
max_iterations: int = 1,
memoria: Optional[MemoryConvABC] = None,
version: str = "1.0.0",
mcp: ClientRegistry = None,
mcp: Optional[ClientRegistry] = None,
output_schema: Optional[dict] = None,
):
self.modelo = modelo
@@ -48,219 +56,349 @@ class AgenteAI:
@property
async def full_system_prompt(self) -> str:
tools_str = await self._obtener_herramientas_disponibles_str()
return f"""
Eres un agente conversacional con acceso a herramientas MCP (Model Context Protocol).
async def generar_system_prompt(self) -> str:
Tu comportamiento sigue este flujo:
info = f"""Eres un agente de texto y te llamas {self.nombre}
1. **Piensa** para razonar tu decisión.
2. **Decide** si:
- puedes responder tú mismo,
- necesitas más información del usuario,
- o necesitas una herramienta MCP.
3. **Actúa**:
- Cuando uses MCP, termina **solo** con un bloque de código MCP y **nada más**.
- Ten en cuenta EXACTAMENTE los parámetros especificados.
- **No expliques, no hables después del bloque. Termina tu turno.**
### Descripción:
{self.descripcion}
---
### Rol:
{self.rol}
# Formato MCP
### Objetivos:
{chr(10).join(f"- {o}" for o in self.objetivos)}
```mcp
{{
"tool": "<nombre_de_la_herramienta>",
"input": {{
"clave": "valor"
}}
}}
Reglas clave:
### System Prompt:
{self.system_prompt}
Razonas antes de actuar.
Siempre estructura tus respuestas con claridad, y termina con <END> cuando hayas completado la tarea principal del usuario.
""".strip()
Nunca hables después de un bloque MCP.
No combines respuestas y herramientas.
Piensa. Decide. Actúa.
Herramientas disponibles para usar con MCP:
{tools_str}
""".strip()
return info
async def construir_prompt_usuario(self, prompt_usuario: str) -> str:
bloques = []
# Conseguir las herramientas disponibles
if self.mcp:
tools_str = await self._obtener_herramientas_disponibles_str()
bloques.append(f"### Herramientas disponibles (MCP):\n{tools_str}")
bloques.append("""### Instrucciones para actuar con herramientas MCP:
Eres un agente conversacional con acceso a herramientas MCP. Cuando el usuario te haga una solicitud, sigue este proceso paso a paso:
---
🧠 **Piensa**:
Reflexiona en voz alta. Explica claramente qué crees que se necesita hacer y por qué.
🎯 **Decide**:
Elige si puedes resolverlo tú solo, si necesitas más información del usuario, o si una herramienta MCP sería útil.
⚙️ **Actúa**:
Si decides usar una herramienta, **escribe el bloque MCP justo después**, sin ningún texto extra después del bloque.
---
### Formato MCP:
```mcp
{
"server": "tools",
"tool": "get_current_user",
"input": {}
}
```
---
### ❗ REGLAS IMPORTANTES:
- **Puedes pensar y decidir con texto normal**, pero:
- El **bloque MCP debe ser lo último** que aparece en tu mensaje.
- **NO escribas nada después del bloque MCP.**
- **NO pongas `<END>` justo después de usar MCP.**
- Solo usa `<END>` cuando:
- hayas terminado completamente la tarea del usuario,
- e interpretado la salida de las herramientas que usaste.
- Puedes hacer múltiples pasos si es necesario: usar una herramienta, esperar su salida, analizarla, usar otra, etc.
- Si decides no usar herramientas, simplemente responde como lo harías normalmente.
- Si no estás seguro de algo, **pide aclaraciones al usuario** antes de actuar.
---
### ⚠️ Ejemplos comunes de errores y cómo evitarlos:
❌ Incorrecto:
```
{
"server": "tools",
"tool": "generate_uuid",
"input": {}
}
````
🔴 Este bloque no funcionará. Le falta indicar que es un bloque MCP.
✅ Correcto:
```mcp
{
"server": "tools",
"tool": "generate_uuid",
"input": {}
}
````
🔵 Siempre usa ` ```mcp ` (con triple backtick y la palabra `mcp`) antes del JSON. No escribas nada después del bloque.
````
---
### ✅ Ejemplo correcto:
Necesito generar un identificador único para el usuario.
Para eso usaré la herramienta `generate_uuid` disponible.
```mcp
{
"server": "tools",
"tool": "generate_uuid",
"input": {}
}
""")
if self.memoria:
historial = self.memoria.cargar_historial_chat()
if historial:
memoria_str = "\n".join(
[f"{msg['role']}: {msg['content']}" for msg in historial]
)
bloques.append(f"### Memoria del chat:\n{memoria_str}")
if self.output_schema:
schema_str = str(self.output_schema)
bloques.append(f"### Salida esperada:\n{schema_str}")
bloques.append(f"### Prompt del usuario:\n{prompt_usuario}")
return "\n\n".join(bloques)
### Conseguir las herramientas disponibles
async def _obtener_herramientas_disponibles_str(self) -> str:
logger.info("Inicio de obtención de herramientas disponibles")
if not self.mcp:
logger.warning("No se ha definido el cliente MCP.")
return "No se han definido herramientas disponibles."
herramientas = []
tools_por_cliente = await self.mcp.listar_tools_por_cliente()
try:
resultado = await self.mcp.listar_tools_por_cliente()
tools_por_cliente = resultado.get("tools", {})
errores = resultado.get("errores", {})
for name, tools in tools_por_cliente.items():
if not tools:
continue
herramientas.append(f"\n🔌 Cliente: {name}")
for tool in tools:
props = tool.inputSchema.get("properties", {})
parametros = "\n ".join(f"- {k} ({v.get('type', '?')})" for k, v in props.items())
herramientas.append(f"""Nombre: {tool.name}
Descripción: {tool.description}
Parámetros:
{parametros}
""")
return "\n".join(herramientas) or "No hay herramientas disponibles actualmente."
logger.debug(f"Tools obtenidas: {list(tools_por_cliente.keys())}")
logger.debug(f"Errores detectados: {list(errores.keys())}")
herramientas = []
for name, tools in tools_por_cliente.items():
if not tools:
logger.info(f"Servidor {name} no tiene herramientas disponibles.")
continue
herramientas.append(f"\n🔌 Server: {name}")
for tool in tools:
props = tool.inputSchema.get("properties", {})
parametros = "\n ".join(f"- {k} ({v.get('type', '?')})" for k, v in props.items())
herramientas.append(f"""Nombre: {tool.name}
Descripción: {tool.description}
Parámetros:
{parametros}
""")
logger.debug(f"Herramienta agregada: {tool.name} del servidor {name}")
if errores:
herramientas.append("\n⚠️ Los siguientes servidores no están disponibles:")
for name, error in errores.items():
herramientas.append(f"- {name}: {error}")
logger.warning(f"Servidor con error: {name} -> {error}")
logger.info("Finalización de obtención de herramientas exitosamente.")
return "\n".join(herramientas) or "No hay herramientas disponibles actualmente."
except Exception as e:
logger.error(f"Error inesperado al obtener herramientas: {str(e)}", exc_info=True)
return "Se produjo un error al obtener las herramientas disponibles."
# Formatear prompt para agentes
### Formatear prompt para agentes
def _formatear_prompt(self, mensajes: List[dict]) -> str:
return "\n".join([f"{msg['role']}: {msg['content']}" for msg in mensajes])
### Ejecutar codigo MCP
async def ejecutar_bloque_mcp(self, respuesta: str) -> Optional[str]:
logger.info("Iniciando ejecución de bloque MCP.")
patron = r"```mcp\s*(\{.*?\})\s*```"
match = re.search(patron, respuesta, re.DOTALL)
if not match:
patron_incorrecto = r"```[\s]*\{.*?\}[\s]*```"
if re.search(patron_incorrecto, respuesta, re.DOTALL):
logger.warning("Bloque detectado sin especificador `mcp`.")
return "Advertencia: Usaste un bloque de herramienta MCP pero olvidaste indicar el lenguaje `mcp`. Corrige el bloque a: ```mcp { ... } ```"
logger.info("No se encontró ningún bloque MCP en la respuesta.")
return None
try:
bloque_json_str = match.group(1)
logger.debug(f"Bloque MCP detectado: {bloque_json_str}")
bloque = json.loads(bloque_json_str)
server_name = bloque["server"]
tool_name = bloque["tool"]
input_args = bloque.get("input", {})
logger.info(f"Bloque MCP válido. Servidor: {server_name}, Herramienta: {tool_name}")
logger.debug(f"Parámetros de entrada: {input_args}")
except Exception as e:
logger.error(f"Error al interpretar el bloque MCP: {e}", exc_info=True)
return f"Error al interpretar el bloque MCP: {e}"
try:
cliente_mcp = self.mcp.get(server_name)
except KeyError:
logger.warning(f"No se encontró el cliente MCP para el servidor '{server_name}'.")
return f"No se encontró el cliente MCP para el servidor '{server_name}'"
try:
logger.info(f"Ejecutando herramienta '{tool_name}' en servidor '{server_name}' con argumentos: {json.dumps(input_args, ensure_ascii=False)}")
async with cliente_mcp:
resultado = await cliente_mcp.call_tool(tool_name, input_args)
logger.info(f"Ejecución completada exitosamente. Resultado: {resultado}")
return str(resultado)
except Exception as e:
logger.error(f"Error al ejecutar herramienta '{tool_name}' en servidor '{server_name}': {e}", exc_info=True)
return f"Error al ejecutar herramienta '{tool_name}' en servidor '{server_name}': {e}"
###----------- Funcion para interactuar
###----------- Funcion para interactuar
async def interactuar(self, prompt: str, stream: bool = False) -> Union[str, AsyncGenerator[str, None]]:
historial = self.memoria.cargar_historial_chat() if self.memoria else []
contexto = historial + [{"role": "user", "content": prompt}]
mensaje_usuario = await self.construir_prompt_usuario(prompt)
contexto = [{"role": "user", "content": mensaje_usuario}]
prompt_final = self._formatear_prompt(contexto)
respuesta = await self.modelo.responder(
prompt=prompt_final,
system_prompt=await self.full_system_prompt, # ✅ correcto
system_prompt=await self.generar_system_prompt(),
stream=stream
)
if stream:
async def wrapper():
buffer_respuesta = ""
async for token in respuesta:
buffer_respuesta += token
yield token
if self.memoria:
self.memoria.guardar_turno("user", prompt)
self.memoria.guardar_turno("assistant", buffer_respuesta)
self.numero_interacciones += 1
self.updated_at = datetime.now()
return wrapper()
else:
if self.memoria:
self.memoria.guardar_turno("user", prompt)
self.memoria.guardar_turno("assistant", respuesta)
self.numero_interacciones += 1
self.updated_at = datetime.now()
return respuesta
return respuesta
###----------- Funcion para interactuar en bucle
###----------- Funcion para interactuar en bucle
async def interactuar_en_bucle(self, prompt: str, stream: bool = False) -> Union[List[str], AsyncGenerator[str, None]]:
print("🚀 [interactuar_en_bucle] Iniciando interacción")
historial = self.memoria.cargar_historial_chat() if self.memoria else []
print(f"📜 [interactuar_en_bucle] Historial cargado: {historial}")
respuestas = [] if not stream else None
respuesta_anterior = None
respuesta_anterior = ""
resultado_mcp_anterior = None # <-- Guarda último resultado del MCP
iteration = 0
prompt_original = prompt.strip()
print(f"✏️ [interactuar_en_bucle] Prompt original: {prompt_original}")
async def generador():
nonlocal iteration, respuesta_anterior
prompt_actual = prompt_original
nonlocal iteration, respuesta_anterior, resultado_mcp_anterior
while self.max_iterations == 0 or iteration < self.max_iterations:
print(f"\n🔁 [generador] Iteración: {iteration}")
instruccion_fin = (
"\n\nIMPORTANTE: Cuando hayas respondido completamente a la pregunta original del usuario y no requieras más pasos, "
"escribe <END> para indicar que has terminado."
)
if iteration == 0:
prompt_actual += (
"\n\nIMPORTANTE:\n"
"Si al revisar tu última respuesta y mi pregunta inicial consideras que has terminado, "
"di alguna de estas frases: <END>"
)
prompt_actual = prompt_original + instruccion_fin
else:
prompt_actual = (
f"Esta es la pregunta original:\n{prompt_original}\n\n"
f"Esto fue lo último que dijiste:\n{respuesta_anterior}\n"
"\n\nIMPORTANTE:\n"
"Si al revisar tu última respuesta y mi pregunta inicial consideras que has terminado, "
"di alguna de estas frases: <END>"
"En caso contrario, responde a la pregunta original "
"y añade información relevante que no hayas mencionado antes.\n\n"
f"Esto fue lo último que dijiste:\n{respuesta_anterior}\n\n"
f"{instruccion_fin}"
)
contexto = historial + [{"role": "user", "content": prompt_actual}]
if resultado_mcp_anterior:
prompt_actual += (
"\n\nEsta fue la salida de la herramienta que usaste:\n"
f"{resultado_mcp_anterior}\n\n"
"Úsala para seguir resolviendo el problema o tomar una nueva decisión."
)
mensaje_usuario = await self.construir_prompt_usuario(prompt_actual)
contexto = [{"role": "user", "content": mensaje_usuario}]
prompt_final = self._formatear_prompt(contexto)
print(f"📨 [generador] Prompt final enviado al modelo:\n{prompt_final}")
print("🤖 [generador] Esperando respuesta del modelo...")
respuesta = await self.modelo.responder(
prompt=prompt_final,
system_prompt=await self.full_system_prompt,
system_prompt=await self.generar_system_prompt(),
stream=stream
)
print("✅ [generador] Respuesta recibida")
if stream:
buffer_respuesta = ""
async for token in respuesta:
buffer_respuesta += token
# print(f"🔹 [stream] Token: {token}")
yield token
respuesta_anterior = buffer_respuesta
# print(f"📦 [stream] Respuesta completa:\n{respuesta_anterior}")
else:
respuestas.append(respuesta)
respuesta_anterior = respuesta
# print(f"📦 [generador] Respuesta completa:\n{respuesta_anterior}")
# Revisar y ejecutar bloque MCP si existe
resultado_mcp_anterior = None
if "```mcp" in respuesta_anterior:
resultado_mcp = await self.ejecutar_bloque_mcp(respuesta_anterior)
if resultado_mcp:
resultado_mcp_anterior = resultado_mcp
if stream:
yield "\n" + resultado_mcp
else:
respuestas.append(resultado_mcp)
# Guardar historial si hay memoria
if self.memoria:
print("💾 [memoria] Guardando turno en la memoria...")
self.memoria.guardar_turno("user", prompt_actual)
self.memoria.guardar_turno("assistant", respuesta_anterior)
self.numero_interacciones += 1
self.updated_at = datetime.now()
print(f"📊 [generador] Interacción #{self.numero_interacciones} registrada")
if "<end>" in respuesta_anterior.lower():
print("🛑 [generador] Detectado <end>. Terminando bucle.")
if "<end>" in respuesta_anterior.lower() and "```mcp" not in respuesta_anterior.lower():
break
iteration += 1
prompt_actual = ""
return generador() if stream else await generador_to_list(generador)
return generador() if stream else await generador_to_list(generador())
+15 -15
View File
@@ -22,35 +22,35 @@ class ClientRegistry:
def __contains__(self, name: str) -> bool:
return name in self._clients
async def listar_tools_por_cliente(self) -> dict[str, list[Any]]:
resultado = {}
async def listar_tools_por_cliente(self) -> dict[str, Any]:
resultado = {"tools": {}, "errores": {}}
for name, wrapper in self._clients.items():
try:
async with wrapper:
resultado[name] = await wrapper.list_tools()
resultado["tools"][name] = await wrapper.list_tools()
except Exception as e:
print(f"[TOOLS] ❌ Error en '{name}': {e}")
resultado[name] = []
resultado["errores"][name] = str(e)
resultado["tools"][name] = []
return resultado
async def listar_prompts_por_cliente(self) -> dict[str, list[Any]]:
resultado = {}
async def listar_prompts_por_cliente(self) -> dict[str, Any]:
resultado = {"prompts": {}, "errores": {}}
for name, wrapper in self._clients.items():
try:
async with wrapper:
resultado[name] = await wrapper.list_prompts()
resultado["prompts"][name] = await wrapper.list_prompts()
except Exception as e:
print(f"[PROMPTS] ❌ Error en '{name}': {e}")
resultado[name] = []
resultado["errores"][name] = str(e)
resultado["prompts"][name] = []
return resultado
async def listar_resources_por_cliente(self) -> dict[str, list[Any]]:
resultado = {}
async def listar_resources_por_cliente(self) -> dict[str, Any]:
resultado = {"resources": {}, "errores": {}}
for name, wrapper in self._clients.items():
try:
async with wrapper:
resultado[name] = await wrapper.list_resources()
resultado["resources"][name] = await wrapper.list_resources()
except Exception as e:
print(f"[RESOURCES] ❌ Error en '{name}': {e}")
resultado[name] = []
resultado["errores"][name] = str(e)
resultado["resources"][name] = []
return resultado
+2 -2
View File
@@ -87,6 +87,6 @@ def is_prime(n: int) -> bool:
if __name__ == "__main__":
# mcp.run(transport="streamable-http", host="127.0.0.1", port=4200, path="/math")
mcp.run(transport="streamable-http", host="127.0.0.1", port=4200, path="/math")
mcp.run(transport="stdio")
# mcp.run(transport="stdio")
+1
View File
@@ -66,3 +66,4 @@ def current_timestamp() -> float:
if __name__ == "__main__":
mcp.run(transport="streamable-http", host="127.0.0.1", port=4300, path="/tools")
+67
View File
@@ -0,0 +1,67 @@
from src.Llms.Modelos.Base_model import ModeloABC
from src.Security.GenerarIDs import GeneradorIDUnico
from typing import AsyncGenerator, Union
from src.ConexionApis.Ollama_cliente import OllamaCliente # Asegúrate de importar correctamente
import asyncio
class ModeloOllama(ModeloABC):
def __init__(
self,
cliente: OllamaCliente,
model: str = "llama3",
id: str = None,
temperature: float = 0.7,
top_p: float = 1.0,
top_k: int = None,
frecuencia_penalizacion: float = 0.0,
num_tokens_maximos: int = 512
):
if not isinstance(cliente, OllamaCliente):
raise TypeError("El parámetro 'cliente' debe ser una instancia de OllamaCliente")
self.id = id if id else GeneradorIDUnico("MOOL").generar()
super().__init__(
model=model,
temperature=temperature,
top_p=top_p,
top_k=top_k,
frecuencia_penalizacion=frecuencia_penalizacion,
num_tokens_maximos=num_tokens_maximos
)
self.cliente = cliente
async def responder(
self,
prompt: str,
system_prompt: str = "",
stream: bool = False,
**kwargs
) -> Union[str, AsyncGenerator[str, None]]:
messages = []
if system_prompt:
messages.append({"role": "system", "content": system_prompt})
messages.append({"role": "user", "content": prompt})
def sync_call():
return self.cliente.chat_completion(
model=self.model,
messages=messages,
temperature=self.temperature,
top_p=self.top_p,
max_tokens=self.num_tokens_maximos,
frequency_penalty=self.frecuencia_penalizacion,
stream=stream,
**kwargs
)
loop = asyncio.get_event_loop()
resultado = await loop.run_in_executor(None, sync_call)
if stream:
async def generador():
for token in resultado:
yield token
return generador()
else:
return resultado.choices[0].message.content
+60
View File
@@ -0,0 +1,60 @@
from loguru import logger
from sqlalchemy import Column, Integer, String, Text, TIMESTAMP
from sqlalchemy.orm import sessionmaker
from sqlalchemy.exc import SQLAlchemyError
from src.ArquitectureLayer.Model import Model_base
from src.ConexionSql.Postgres_conexion import PostgresConexion
from src.Credenciales.postgres_credencial import PostgresCredencial
class LoggerDB:
_sink_removido = False # ← evita múltiples remove() si se crean varias instancias
def __init__(self, credencial: PostgresCredencial, nombre_tabla: str, created_by: str = None):
if not LoggerDB._sink_removido:
logger.remove() # 🧹 elimina impresión en terminal
LoggerDB._sink_removido = True
self.conexion = PostgresConexion(credencial)
self.engine = self.conexion.get_engine()
self.Session = sessionmaker(bind=self.engine)
self.nombre_tabla = nombre_tabla
self.created_by = created_by
self.modelo_logger = self._generar_modelo_logger()
self._crear_tabla_si_no_existe()
logger.add(self._sink, level="DEBUG")
def _generar_modelo_logger(self):
class LoggerTable(Model_base):
__tablename__ = self.nombre_tabla
id = Column(Integer, primary_key=True)
nivel = Column(String, nullable=False)
mensaje = Column(Text, nullable=False)
fecha = Column(TIMESTAMP(timezone=True), nullable=False)
modulo = Column(String, nullable=True)
funcion = Column(String, nullable=True)
linea = Column(Integer, nullable=True)
return LoggerTable
def _crear_tabla_si_no_existe(self):
self.modelo_logger.__table__.create(self.engine, checkfirst=True)
def _sink(self, message):
record = message.record
try:
session = self.Session()
log_entry = self.modelo_logger(
nivel=record["level"].name,
mensaje=record["message"],
fecha=record["time"],
modulo=record["module"],
funcion=record["function"],
linea=record["line"],
sys_created_by=self.created_by
)
session.add(log_entry)
session.commit()
session.close()
except SQLAlchemyError as e:
print(f"[LoggerDB] Error guardando log en BD: {e}")