Extender DiagramaHub¶
Recetas para las extensiones más comunes. En todas: textos en español e inglés, pruebas para cada endpoint nuevo y nada de lógica de negocio en las rutas.
Crear un módulo del backend¶
Una entidad = una carpeta. Ejemplo con una entidad templates:
- Crea la carpeta
backend/app/api/v1/templates/con__init__.py,interfaces.py,repository.py,services.py,schemas.pyyroutes.py. - Esquemas (
schemas.py): modelos Pydantic v2TemplateBase,TemplateCreate,TemplateUpdate,TemplateResponsey el documentoTemplateInDB(Document)de Beanie. - Interfaz (
interfaces.py):class ITemplateRepository(ABC)con métodos@abstractmethod(create,get_by_id,list_by_user, …). - Repositorio (
repository.py):TemplateRepository(ITemplateRepository)implementado con Beanie. - Servicio (
services.py):TemplateServicerecibe unITemplateRepositoryen el constructor; aquí van las reglas (propiedad del recurso, límites, validaciones). - Rutas (
routes.py): unAPIRouter, una funciónget_template_service()que arma el servicio con el repositorio concreto, y endpoints que usanDepends(get_current_user_id)y delegan al servicio. - Registra en
main.pyel documento eninit_beanie(document_models=[...])y el router conapp.include_router(router, prefix=settings.API_V1_PREFIX). - Pruebas en
backend/tests/api/v1/templates/usandoauthenticated_client.
from fastapi import APIRouter, Depends
from app.api.deps import get_current_user_id
from app.api.v1.templates.repository import TemplateRepository
from app.api.v1.templates.schemas import TemplateCreate, TemplateResponse
from app.api.v1.templates.services import TemplateService
router = APIRouter(prefix="/templates", tags=["templates"])
def get_template_service() -> TemplateService:
"""Build the service with its concrete repository."""
return TemplateService(repository=TemplateRepository())
@router.post("", response_model=TemplateResponse, status_code=201)
async def create_template(
body: TemplateCreate,
user_id: str = Depends(get_current_user_id),
service: TemplateService = Depends(get_template_service),
) -> TemplateResponse:
"""Create a template owned by the current user."""
return await service.create(body, user_id)
Toda ruta privada debe identificar al usuario
Cada endpoint que lee o modifica datos de un usuario necesita Depends(get_current_user_id) (o get_current_user) y el servicio debe comprobar que el recurso le pertenece. Sin eso, cualquiera que conozca un id podría leerlo.
Agregar un proveedor de IA¶
- Cliente: crea
backend/app/api/v1/ai_providers/clients/<proveedor>_client.pycon una clase que extiendaBaseAICliente implemente sus métodos abstractos (complete,generate_description,fix_diagram,validate_api_key,chat_with_context,summarize_conversationy la propiedadprovider_name). La generación y mejora de diagramas ya viven enBaseAIClientcon el mismo prompt para todos los proveedores. - Enum: agrega el valor a
AIProviderTypeenai_providers/schemas.pyy enfrontend/src/types/ai.ts. - Fábrica: regístralo en
_clients_mapdeai_providers/clients/factory.py. -
Catálogo de modelos: añade sus modelos a
model_catalog.json, la fuente única de verdad:{"provider": "nuevo", "id": "nuevo-model-1", "context_window": 128000, "listed": true, "recommended": true, "supports_temperature": true}listed: el modelo aparece en la interfaz (los no listados se conservan para que configuraciones guardadas sigan funcionando).recommended: exactamente uno por proveedor; es el modelo por defecto.context_window: lo usa la compactación del chat.supports_temperature:falsesi el modelo rechaza ese parámetro.
Verifica los modelos contra la documentación del proveedor y con una llamada real.
-
Frontend: agrega los mismos modelos
listedaAI_PROVIDER_MODELSenfrontend/src/types/ai.ts. El testaiModelCatalog.test.tsfalla si no coinciden (ids, orden y recomendado). - Textos: nombre y ayuda del proveedor en
ai.providers.<id>yai.help.<id>dees.jsonyen.json. El modal de proveedores recorreAIProviderType, así que aparece solo. - Mide: corre el benchmark de IA con el modelo nuevo.
Agregar una página del frontend¶
- Crea el componente en
frontend/src/pages/MiPaginaPage.tsx. - Agrega la ruta en
App.tsx: dentro dePrivateRouteySidebarLayoutsi requiere sesión, o junto a las públicas si no. - Agrega los métodos de API a
ApiServiceenservices/api.tsy los tipos entypes/. - Sigue
DESIGN.md: Tailwind, modo oscuro, accesibilidad, sin librerías de UI. - Todos los textos con
t().
Agregar textos (i18n)¶
- Añade la clave en ambos archivos,
frontend/src/i18n/locales/es.jsonyen.json, en la sección que corresponda (common,nav,auth,diagram,editor,errors, …). - Usa los mismos placeholders
{{nombre}}en los dos idiomas. Para plurales usa las formas de i18next (_one/_other), no el sufijo antiguo_plural. - Corre
pnpm test:i18nParity.test.tscomprueba claves, placeholders y plurales.