cortex-knowledge-assistant

Desarrollo

Guía para desarrolladores que quieran contribuir a Cortex o entender su arquitectura interna.


Configuración del Entorno

Requisitos

Herramienta Versión Propósito
Python 3.11+ Backend
Node.js 18+ Frontend
Docker 24+ Servicios de desarrollo
Git 2.30+ Control de versiones

Setup Inicial

# 1. Clonar repositorio
git clone https://github.com/DeepRatAI/cortex-knowledge-assistant.git
cd cortex-knowledge-assistant

# 2. Crear entorno virtual
python -m venv .venv
source .venv/bin/activate

# 3. Instalar dependencias de desarrollo
pip install -e ".[dev]"

# 4. Verificar instalación
pytest tests/ -v

Variables de Desarrollo

# Configuración mínima para desarrollo
export DATABASE_URL=sqlite:///./dev_cortex.db
export CKA_LLM_PROVIDER=Fake
export JWT_SECRET_KEY=dev-secret-key-for-testing-only
export CKA_LOG_LEVEL=DEBUG

Iniciar Servicios Auxiliares

# Iniciar solo Qdrant y Redis para desarrollo
docker compose up -d qdrant redis postgres

# Verificar que están corriendo
docker compose ps

Iniciar API en Modo Desarrollo

# Con auto-reload
uvicorn src.cortex_ka.api.main:app --reload --host 0.0.0.0 --port 8088

Iniciar UI en Modo Desarrollo

cd ui
npm install
npm run dev
# Acceder a http://localhost:5173

Estructura del Proyecto

cortex-knowledge-assistant/
├── src/
│   └── cortex_ka/           # Código principal
│       ├── api/             # FastAPI endpoints
│       ├── application/     # Lógica de aplicación (RAG, DLP)
│       ├── domain/          # Modelos y puertos
│       ├── infrastructure/  # Adaptadores (Qdrant, HF, Redis)
│       ├── auth/            # Autenticación
│       ├── transactions/    # Dominio transaccional
│       ├── system/          # Setup, status, admin
│       ├── scripts/         # Ingesta, evaluación
│       ├── demos/           # Modo demo
│       ├── eval/            # Evaluación PII
│       └── maintenance/     # Mantenimiento
├── tests/                   # Tests unitarios e integración
├── ui/                      # Frontend React
├── docs/                    # Documentación
├── k8s/                     # Manifests Kubernetes
├── docker/                  # Dockerfiles
├── scripts/                 # Scripts de utilidad
├── pyproject.toml           # Configuración del proyecto
├── docker-compose.yml       # Compose principal
└── Makefile                 # Comandos útiles

Testing

Ejecutar Tests

# Ejecutar todos los tests
pytest tests/ -v

# Con coverage (requiere pytest-cov)
pytest tests/ -v --cov=src/cortex_ka --cov-report=html
open htmlcov/index.html

Ejecutar Tests Específicos

# Un archivo específico
pytest tests/test_pii.py -v

# Un test específico
pytest tests/test_pii.py::TestRedactPii::test_redacts_dni -v

# Tests que coincidan con un patrón
pytest tests/ -k "pii" -v

Estructura de Tests

Archivo Propósito
test_pii.py Redacción de datos sensibles (DNI, CUIT, etc.)
test_chunking.py Particionado semántico de documentos
test_models.py Modelos de dominio (DocumentChunk, Answer)

Tests con Docker

# Ejecutar tests en contenedor
docker compose -f docker/compose.dev.yml run --rm api pytest tests/ -v

Estilo de Código

Python (Backend)

Usamos las siguientes herramientas:

Herramienta Propósito Comando
Black Formateo black src/ tests/
isort Ordenar imports isort src/ tests/
Flake8 Linting flake8 src/ tests/
MyPy Type checking mypy src/
# Formatear todo
black src/ tests/
isort src/ tests/

# Verificar estilo
flake8 src/ tests/
mypy src/

# Todo junto (usando Makefile)
make lint

Configuración en pyproject.toml

[tool.black]
line-length = 120
target-version = ["py311", "py312"]

[tool.isort]
profile = "black"
line_length = 120

[tool.mypy]
python_version = "3.12"
strict = true

TypeScript (Frontend)

cd ui
npm run lint

Arquitectura Interna

Capas de la Aplicación

┌─────────────────────────────────────────────────────────────────┐
│                     API Layer (FastAPI)                         │
│ - Endpoints REST                                               │
│ - Middlewares (auth, rate limit, security headers)             │
│ - Request/Response models (Pydantic)                           │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                   Application Layer                              │
│ - RAGService (orquestación)                                    │
│ - PromptBuilder, Chunking, Reranking                           │
│ - DLP/PII enforcement                                          │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                     Domain Layer                                 │
│ - Value Objects: DocumentChunk, Answer, RetrievalResult        │
│ - Ports (interfaces): RetrieverPort, LLMPort, CachePort        │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                  Infrastructure Layer                            │
│ - Adapters: QdrantRetriever, HFLLM, RedisCache                 │
│ - External services: PostgreSQL, Qdrant, Redis, HF API         │
└─────────────────────────────────────────────────────────────────┘

Flujo de Datos

  1. Request llega a FastAPI
  2. Middleware valida auth, rate limit
  3. Endpoint delega a RAGService
  4. RAGService usa RetrieverPort (implementado por QdrantRetriever)
  5. Re-ranking y selección de chunks
  6. PromptBuilder construye prompt
  7. LLMPort (implementado por HFLLM) genera respuesta
  8. DLP aplica redacción de PII
  9. Response retornada al cliente

Añadir un Nuevo LLM Provider

# src/cortex_ka/infrastructure/llm_openai.py

from ..domain.ports import LLMPort

class OpenAILLM(LLMPort):
    """OpenAI GPT provider."""

    def __init__(self, api_key: str, model: str = "gpt-4"):
        self.api_key = api_key
        self.model = model
        self._client = openai.OpenAI(api_key=api_key)

    def generate(self, prompt: str) -> str:
        response = self._client.chat.completions.create(
            model=self.model,
            messages=[{"role": "user", "content": prompt}]
        )
        return response.choices[0].message.content

    def generate_stream(self, prompt: str) -> Iterator[str]:
        stream = self._client.chat.completions.create(
            model=self.model,
            messages=[{"role": "user", "content": prompt}],
            stream=True
        )
        for chunk in stream:
            if chunk.choices[0].delta.content:
                yield chunk.choices[0].delta.content

Luego modificar _select_llm() en api/main.py:

def _select_llm():
    provider = settings.llm_provider.lower()
    if provider == "hf":
        return HFLLM(...)
    elif provider == "openai":
        return OpenAILLM(api_key=settings.openai_api_key)
    return _FakeLLM()

Añadir un Nuevo Endpoint

# En src/cortex_ka/api/main.py

from pydantic import BaseModel

class NewFeatureRequest(BaseModel):
    param1: str
    param2: int = 10

class NewFeatureResponse(BaseModel):
    result: str
    metadata: dict

@app.post("/api/new-feature", response_model=NewFeatureResponse)
def new_feature(
    payload: NewFeatureRequest,
    current_user: CurrentUser = Depends(get_current_user),
):
    """Descripción del nuevo endpoint.

    Solo accesible para usuarios autenticados.
    """
    # Verificar permisos si es necesario
    if getattr(current_user, "role", "") != "admin":
        raise HTTPException(403, "Forbidden")

    # Lógica del endpoint
    result = process_new_feature(payload.param1, payload.param2)

    # Auditar si es operación sensible
    _audit(login_db_session, operation="new_feature", outcome="success")

    return NewFeatureResponse(result=result, metadata={})

Flujo de Contribución

1. Fork y Clone

# Fork en GitHub, luego:
git clone https://github.com/TU_USUARIO/cortex-knowledge-assistant.git
cd cortex-knowledge-assistant
git remote add upstream https://github.com/DeepRatAI/cortex-knowledge-assistant.git

2. Crear Branch

git checkout -b feature/mi-nueva-funcionalidad

Convención de nombres:

3. Desarrollar

# Hacer cambios...
# Ejecutar tests
pytest tests/ -v

# Verificar estilo
make lint

4. Commit

Seguimos Conventional Commits:

git add .
git commit -m "feat: añadir soporte para OpenAI GPT-4"
# o
git commit -m "fix: corregir rate limiting para endpoints streaming"
# o
git commit -m "docs: actualizar guía de despliegue"

5. Push y PR

git push origin feature/mi-nueva-funcionalidad
# Crear Pull Request en GitHub

Checklist de PR


Documentación

Docstrings

Usamos formato Google:

def calculate_score(chunk: DocumentChunk, query: str) -> float:
    """Calculate relevance score for a chunk.

    Combines semantic similarity with keyword matching
    using a weighted average.

    Args:
        chunk: The document chunk to score.
        query: The user's search query.

    Returns:
        Relevance score between 0 and 1.

    Raises:
        ValueError: If chunk or query is empty.

    Example:
        >>> score = calculate_score(chunk, "requisitos cuenta")
        >>> print(f"Score: {score:.2f}")
        Score: 0.85
    """

Actualizar Documentación

# Documentación está en docs/
# Formato: Markdown

# Para preview local (si usas Docusaurus):
cd docs
npm run start

Debugging

Logs Detallados

export CKA_LOG_LEVEL=DEBUG
uvicorn src.cortex_ka.api.main:app --reload

Debugger (VS Code)

.vscode/launch.json:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Cortex API",
      "type": "debugpy",
      "request": "launch",
      "module": "uvicorn",
      "args": ["src.cortex_ka.api.main:app", "--reload"],
      "env": {
        "CKA_LLM_PROVIDER": "Fake",
        "DATABASE_URL": "sqlite:///./dev.db"
      }
    },
    {
      "name": "Tests",
      "type": "debugpy",
      "request": "launch",
      "module": "pytest",
      "args": ["tests/", "-v", "-s"]
    }
  ]
}

Profiling

# Para profiling de performance
import cProfile
import pstats

with cProfile.Profile() as pr:
    result = rag_service.answer("mi query")

stats = pstats.Stats(pr)
stats.sort_stats("cumulative")
stats.print_stats(20)

Versionado

Seguimos Semantic Versioning:

Crear Release

# 1. Actualizar versión en pyproject.toml
# 2. Actualizar CHANGELOG.md
# 3. Commit
git commit -am "chore: release v1.1.0"

# 4. Tag
git tag -a v1.1.0 -m "Release v1.1.0"
git push origin v1.1.0

# 5. GitHub Actions construye y publica

Recursos

Documentación de Dependencias

Papers de Referencia


FAQ de Desarrollo

¿Por qué Arquitectura Hexagonal?

¿Por qué SQLite en desarrollo?

¿Cómo añado un nuevo tipo de PII?

  1. Añadir regex en application/pii.py
  2. Actualizar classify_pii() en pii_classifier.py
  3. Añadir test en tests/test_pii_classifier.py
  4. Actualizar documentación de seguridad

← DespliegueÍndice →