feat: Implement main application shell with navigation and color scheme toggle
- Added Appshell component with responsive navbar and main content area - Integrated ColorSchemeToggle for light/dark mode switching - Created Welcome component with styled title and introductory text - Developed ChatPage for LLM interaction with WebSocket support - Implemented Biblioteca for managing notes with rich text editor - Added LoginPage for user authentication with error handling - Introduced MessageList and MessageBubble components for chat messages - Styled components with CSS modules for consistent design
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
class ModeloABC(ABC):
|
||||
"""
|
||||
Clase base para definir la interfaz de un modelo conversacional.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
model: str,
|
||||
temperature: float = 0.7,
|
||||
top_p: float = 1.0,
|
||||
top_k: int = None,
|
||||
frecuencia_penalizacion: float = 0.0,
|
||||
num_tokens_maximos: int = 512
|
||||
):
|
||||
self.model = model
|
||||
self.temperature = temperature
|
||||
self.top_p = top_p
|
||||
self.top_k = top_k
|
||||
self.frecuencia_penalizacion = frecuencia_penalizacion
|
||||
self.num_tokens_maximos = num_tokens_maximos
|
||||
|
||||
@abstractmethod
|
||||
async def responder(self, prompt: str, system_prompt: str = "", stream: bool = False, **kwargs) -> str:
|
||||
"""
|
||||
Devuelve una respuesta a partir de un prompt y configuración del modelo.
|
||||
Este método debe implementarse de forma asíncrona en las subclases.
|
||||
"""
|
||||
pass
|
||||
@@ -0,0 +1,67 @@
|
||||
from domains.Llms.Modelos.Base_model import ModeloABC
|
||||
from domains.Security.GenerarIDs import GeneradorIDUnico
|
||||
from typing import AsyncGenerator, Union
|
||||
from domains.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
|
||||
@@ -0,0 +1,90 @@
|
||||
from domains.Llms.Modelos.Base_model import ModeloABC
|
||||
from domains.ConexionApis.OpenAi_conexion import OpenAICliente
|
||||
from domains.Security.GenerarIDs import GeneradorIDUnico
|
||||
import asyncio
|
||||
from typing import AsyncGenerator, Union
|
||||
|
||||
class ModeloOpenAI(ModeloABC):
|
||||
def __init__(
|
||||
self,
|
||||
cliente: OpenAICliente,
|
||||
model: str = "gpt-4o",
|
||||
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,
|
||||
use_legacy: bool = False
|
||||
):
|
||||
# Generar ID con prefijo MOPA si no fue proporcionado
|
||||
self.id = id if id is not None else GeneradorIDUnico("MOPA").generar()
|
||||
|
||||
# Inicializar resto del modelo base
|
||||
super().__init__(
|
||||
model=model,
|
||||
temperature=temperature,
|
||||
top_p=top_p,
|
||||
top_k=top_k,
|
||||
frecuencia_penalizacion=frecuencia_penalizacion,
|
||||
num_tokens_maximos=num_tokens_maximos
|
||||
)
|
||||
|
||||
# Asignar cliente e indicadores adicionales
|
||||
self.cliente = cliente
|
||||
self.use_legacy = use_legacy
|
||||
|
||||
async def responder(
|
||||
self,
|
||||
prompt: str,
|
||||
system_prompt: str = "",
|
||||
stream: bool = False,
|
||||
**kwargs
|
||||
) -> Union[str, AsyncGenerator[str, None]]:
|
||||
if self.use_legacy:
|
||||
if stream:
|
||||
raise NotImplementedError("El modo legacy no soporta streaming.")
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
respuesta = await loop.run_in_executor(
|
||||
None,
|
||||
lambda: self.cliente.completion(
|
||||
model=self.model,
|
||||
prompt=prompt,
|
||||
temperature=self.temperature,
|
||||
top_p=self.top_p,
|
||||
max_tokens=self.num_tokens_maximos,
|
||||
frequency_penalty=self.frecuencia_penalizacion,
|
||||
**kwargs
|
||||
)
|
||||
)
|
||||
return respuesta.choices[0].text.strip()
|
||||
|
||||
# Construcción de mensajes estilo Chat
|
||||
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
|
||||
@@ -0,0 +1,122 @@
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
from sqlalchemy import Column, Integer, String, Float, Boolean
|
||||
|
||||
from domains.ArquitectureLayer.Mapper import Mapper_base
|
||||
from domains.ArquitectureLayer.Model import Model_base
|
||||
from domains.ArquitectureLayer.Repo import Repo_base
|
||||
from typing import Optional
|
||||
|
||||
|
||||
from domains.ConexionSql.Base_conexion import ConexionBase
|
||||
from domains.base import Base
|
||||
from domains.Llms.Modelos.Openai_model import ModeloOpenAI # Clase real de lógica
|
||||
|
||||
# ----------------------
|
||||
# Cargar clave maestra
|
||||
# ----------------------
|
||||
from entrypoint import ENV_PATH
|
||||
load_dotenv(ENV_PATH)
|
||||
pssword = os.getenv('MASTER_PASSWORD')
|
||||
if pssword is None:
|
||||
raise ValueError("MASTER_PASSWORD no está definida en el archivo .env")
|
||||
|
||||
# ----------------------
|
||||
# MODELO (SQLAlchemy)
|
||||
# ----------------------
|
||||
|
||||
class ModeloOpenAIConfigModel(Base, Model_base):
|
||||
__tablename__ = 'modelo_openai_configs'
|
||||
|
||||
id = Column(String, primary_key=True)
|
||||
|
||||
model = Column(String, nullable=False)
|
||||
temperature = Column(Float, default=0.7, nullable=False)
|
||||
top_p = Column(Float, default=1.0, nullable=False)
|
||||
top_k = Column(Integer, nullable=True)
|
||||
|
||||
frecuencia_penalizacion = Column(Float, default=0.0, nullable=False)
|
||||
num_tokens_maximos = Column(Integer, default=512, nullable=False)
|
||||
|
||||
use_legacy = Column(Boolean, default=False, nullable=False)
|
||||
|
||||
# ----------------------
|
||||
# MAPPER
|
||||
# ----------------------
|
||||
|
||||
class ModeloOpenAIConfigMapper(Mapper_base[ModeloOpenAI, ModeloOpenAIConfigModel]):
|
||||
|
||||
@staticmethod
|
||||
def to_model(obj: ModeloOpenAI) -> ModeloOpenAIConfigModel:
|
||||
return ModeloOpenAIConfigModel(
|
||||
id=obj.id,
|
||||
model=obj.model,
|
||||
temperature=obj.temperature,
|
||||
top_p=obj.top_p,
|
||||
top_k=obj.top_k,
|
||||
frecuencia_penalizacion=obj.frecuencia_penalizacion,
|
||||
num_tokens_maximos=obj.num_tokens_maximos,
|
||||
use_legacy=obj.use_legacy
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def from_model(model: ModeloOpenAIConfigModel, cliente: Optional[object] = None) -> ModeloOpenAI:
|
||||
return ModeloOpenAI(
|
||||
id=model.id,
|
||||
cliente=cliente,
|
||||
model=model.model,
|
||||
temperature=model.temperature,
|
||||
top_p=model.top_p,
|
||||
top_k=model.top_k,
|
||||
frecuencia_penalizacion=model.frecuencia_penalizacion,
|
||||
num_tokens_maximos=model.num_tokens_maximos,
|
||||
use_legacy=model.use_legacy
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def to_dict(obj: ModeloOpenAI) -> dict:
|
||||
return {
|
||||
"id": obj.id,
|
||||
"model": obj.model,
|
||||
"temperature": obj.temperature,
|
||||
"top_p": obj.top_p,
|
||||
"top_k": obj.top_k,
|
||||
"frecuencia_penalizacion": obj.frecuencia_penalizacion,
|
||||
"num_tokens_maximos": obj.num_tokens_maximos,
|
||||
"use_legacy": obj.use_legacy
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def from_dict(data: dict, cliente: Optional[object] = None) -> ModeloOpenAI:
|
||||
return ModeloOpenAI(
|
||||
id=data["id"],
|
||||
cliente=cliente,
|
||||
model=data["model"],
|
||||
temperature=data["temperature"],
|
||||
top_p=data["top_p"],
|
||||
top_k=data["top_k"],
|
||||
frecuencia_penalizacion=data["frecuencia_penalizacion"],
|
||||
num_tokens_maximos=data["num_tokens_maximos"],
|
||||
use_legacy=data["use_legacy"]
|
||||
)
|
||||
|
||||
# ----------------------
|
||||
# REPO
|
||||
# ----------------------
|
||||
|
||||
class ModeloOpenAIConfigRepo(Repo_base[ModeloOpenAIConfigModel, ModeloOpenAI]):
|
||||
def __init__(self, conexion: ConexionBase, cliente: object):
|
||||
super().__init__(
|
||||
session=conexion.get_session(),
|
||||
modelo=ModeloOpenAIConfigModel,
|
||||
mapper=ModeloOpenAIConfigMapper
|
||||
)
|
||||
self.cliente = cliente # Necesario para construir el dominio con lógica
|
||||
|
||||
def get_by_id(self, id_: str) -> ModeloOpenAI | None:
|
||||
model = self.session.get(self.Modelo, id_)
|
||||
return self.Mapper.from_model(model, self.cliente) if model else None
|
||||
|
||||
def get_all(self) -> list[ModeloOpenAI]:
|
||||
models = self.session.query(self.Modelo).all()
|
||||
return [self.Mapper.from_model(m, self.cliente) for m in models]
|
||||
Reference in New Issue
Block a user