feat(api): add OpenAPI discovery
This commit is contained in:
parent
d63a096bdc
commit
f1cd87c7a0
12 changed files with 1074 additions and 514 deletions
714
docs/API_RAG.md
714
docs/API_RAG.md
|
|
@ -2,188 +2,109 @@
|
||||||
|
|
||||||
**Proyecto:** Workspace de tools IA para empresas
|
**Proyecto:** Workspace de tools IA para empresas
|
||||||
**Modulo:** RAG
|
**Modulo:** RAG
|
||||||
**Ultima actualizacion:** 2026-04-05
|
**Ultima actualizacion:** 2026-09-08
|
||||||
**Ultima modificacion por:** Agente tools IA para potenciar servicios empresariales
|
**Ultima modificacion por:** Agente RAG 2
|
||||||
**Estado:** Operativa en VPS2
|
**Estado:** Operativa en produccion; endpoints de descubrimiento pendientes de despliegue
|
||||||
|
|
||||||
---
|
Esta guia explica como descubrir y consumir la API. El contrato tecnico completo y canonico se publica en OpenAPI 3.1.1; este documento prioriza el camino rapido, las decisiones de uso y los ejemplos habituales.
|
||||||
|
|
||||||
## Proposito
|
## Acceso rapido
|
||||||
|
|
||||||
Dejar una referencia rapida y practica de la API del servicio `RAG` para poder conectarlo facilmente desde modelos, agentes, flujos de `n8n` y otras aplicaciones.
|
Base URL de produccion:
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Base URL actual
|
|
||||||
|
|
||||||
Produccion actual en `VPS2`:
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
https://rag.por-correo.com
|
https://rag.por-correo.com
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
Cuando se despliegue esta mejora:
|
||||||
|
|
||||||
## Endpoints disponibles
|
| Recurso | URL | Uso |
|
||||||
|
|---|---|---|
|
||||||
|
| Ayuda resumida | `GET /help` | Descubrimiento rapido para personas y agentes |
|
||||||
|
| Contrato OpenAPI | `GET /openapi.json` | Esquemas, parametros, respuestas y errores |
|
||||||
|
| Playground | `GET /playground` | Pruebas manuales desde navegador |
|
||||||
|
|
||||||
### 1. `GET /health`
|
La API no requiere autenticacion actualmente. No debe asumirse que esta situacion es definitiva: la autenticacion y la proteccion de operaciones sensibles forman parte del pendiente 5.
|
||||||
|
|
||||||
Sirve para comprobar si el servicio esta levantado y si la conexion con `Qdrant` esta operativa.
|
## Elegir la operacion correcta
|
||||||
|
|
||||||
Ejemplo:
|
| Necesidad | Endpoint recomendado |
|
||||||
|
|---|---|
|
||||||
|
| Comprobar servicio y capacidades | `GET /health` |
|
||||||
|
| Ver scopes ingeridos | `GET /sources` |
|
||||||
|
| Recuperar contexto para otro agente | `POST /retrieve` |
|
||||||
|
| Obtener una respuesta final con citas | `POST /answer` |
|
||||||
|
| Comparar una respuesta sin RAG | `POST /answer/direct` |
|
||||||
|
| Conversar con bootstrap e historial | `POST /chat` |
|
||||||
|
| Ingerir una ruta accesible por el servidor | `POST /ingest` |
|
||||||
|
| Subir un archivo o carpeta ZIP | `POST /ingest/upload` |
|
||||||
|
| Eliminar una fuente antes de reingestar | `POST /cleanup` |
|
||||||
|
| Consultar o actualizar evaluaciones | `/logs/*` |
|
||||||
|
|
||||||
```bash
|
## Catalogo completo
|
||||||
curl -sS "https://rag.por-correo.com/health"
|
|
||||||
```
|
|
||||||
|
|
||||||
Respuesta esperada aproximada:
|
La API queda formada por 16 operaciones tras incorporar el descubrimiento:
|
||||||
|
|
||||||
|
| Metodo | Ruta | Descripcion |
|
||||||
|
|---|---|---|
|
||||||
|
| `GET` | `/help` | Catalogo resumido derivado de OpenAPI |
|
||||||
|
| `GET` | `/openapi.json` | Contrato OpenAPI 3.1.1 |
|
||||||
|
| `GET` | `/playground` | Interfaz web de prueba |
|
||||||
|
| `GET` | `/health` | Estado, proveedores, parsers y chunking |
|
||||||
|
| `GET` | `/sources` | Scopes disponibles en Qdrant |
|
||||||
|
| `GET` | `/models/answer` | Modelo por defecto y modelos seleccionables |
|
||||||
|
| `POST` | `/ingest` | Ingesta desde una ruta del servidor |
|
||||||
|
| `POST` | `/ingest/upload` | Upload `multipart/form-data` |
|
||||||
|
| `POST` | `/cleanup` | Borrado controlado por scope |
|
||||||
|
| `POST` | `/retrieve` | Recuperacion de contexto |
|
||||||
|
| `POST` | `/answer` | Respuesta apoyada en retrieval |
|
||||||
|
| `POST` | `/answer/direct` | Respuesta del modelo sin retrieval |
|
||||||
|
| `POST` | `/chat` | Conversacion con contexto opcional |
|
||||||
|
| `GET` | `/logs/recent` | Logs recientes de evaluacion |
|
||||||
|
| `POST` | `/logs/manual` | Registro manual de una evaluacion |
|
||||||
|
| `PATCH` | `/logs/{id}` | Seguimiento y resolucion de un log |
|
||||||
|
|
||||||
|
## Contratos comunes
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
|
||||||
|
Permite limitar una consulta o una operacion a fuentes concretas:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"ok": true,
|
"sourceId": "customer-a:manual:v1",
|
||||||
"service": "rag",
|
"sourceRef": "customer-a-errors.md",
|
||||||
"environment": "production",
|
"tags": ["customer-a", "support"]
|
||||||
"embeddings": {
|
|
||||||
"provider": "openrouter",
|
|
||||||
"model": "qwen/qwen3-embedding-8b"
|
|
||||||
},
|
|
||||||
"answer": {
|
|
||||||
"provider": "openrouter",
|
|
||||||
"model": "openai/gpt-4.1-mini"
|
|
||||||
},
|
|
||||||
"vectorStore": {
|
|
||||||
"ok": true,
|
|
||||||
"kind": "qdrant"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
Todos los campos son opcionales en consultas. Para `POST /cleanup` es obligatorio enviar al menos `sourceId` o `sourceRef`; las etiquetas solas no autorizan un borrado.
|
||||||
|
|
||||||
### 2. `POST /ingest`
|
### Modos e intents
|
||||||
|
|
||||||
Sirve para ingerir una fuente en el RAG.
|
| Campo | Valores | Default |
|
||||||
|
|---|---|---|
|
||||||
|
| `mode` en retrieval y answer | `documental`, `codigo`, `auto` | `auto` |
|
||||||
|
| `mode` en chat | `documental`, `codigo`, `auto` | `documental` |
|
||||||
|
| `intent` | `specific`, `bootstrap` | `specific` |
|
||||||
|
| `mode` en ingesta | `mechanical`, `interactive` | `mechanical` |
|
||||||
|
|
||||||
Puede usarse sobre:
|
### Errores
|
||||||
- un archivo
|
|
||||||
- una carpeta
|
|
||||||
|
|
||||||
Payload base:
|
Los errores JSON siguen esta forma:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"sourceType": "folder",
|
"ok": false,
|
||||||
"sourceRef": "/ruta/a/la/carpeta",
|
"error": "Description of the error"
|
||||||
"mode": "mechanical",
|
|
||||||
"tags": ["workspace", "global-docs"]
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Campos:
|
Actualmente la mayoria de fallos de validacion o de proveedores se devuelven como `500`. `POST /cleanup` y `POST /ingest/upload` tambien pueden devolver `400`, y `PATCH /logs/{id}` devuelve `404` si no encuentra el log. Mejorar la validacion queda fuera de este bloque para no cambiar silenciosamente el comportamiento existente.
|
||||||
|
|
||||||
- `sourceId`: opcional, permite aislar la fuente en un scope propio
|
## Recuperar contexto
|
||||||
- `sourceType`: `file` o `folder`
|
|
||||||
- `sourceRef`: ruta de la fuente
|
|
||||||
- `readPath`: uso interno cuando se sube un archivo y el backend lo procesa desde una ruta temporal
|
|
||||||
- `mode`: `mechanical` o `interactive`
|
|
||||||
- `tags`: etiquetas opcionales para clasificar la fuente
|
|
||||||
|
|
||||||
Ejemplo documental:
|
`POST /retrieve` es la operacion recomendada cuando otro agente o flujo generara la respuesta final.
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -sS -X POST "https://rag.por-correo.com/ingest" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"sourceType": "folder",
|
|
||||||
"sourceRef": "/home/pancho/Documentos/Empresa/Desarrollo/IA/docs",
|
|
||||||
"mode": "mechanical",
|
|
||||||
"tags": ["workspace", "global-docs"]
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
Ejemplo codigo:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -sS -X POST "https://rag.por-correo.com/ingest" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"sourceType": "folder",
|
|
||||||
"sourceRef": "/home/pancho/Documentos/Empresa/Desarrollo/IA/RAG/src",
|
|
||||||
"mode": "mechanical",
|
|
||||||
"tags": ["rag", "module-code"]
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
Respuesta esperada aproximada:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"accepted": true,
|
|
||||||
"filesDiscovered": 10,
|
|
||||||
"documentsProcessed": 10,
|
|
||||||
"chunksStored": 45,
|
|
||||||
"collectionName": "rag_chunks"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Ejemplo de aislamiento en un scope propio:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -sS -X POST "https://rag.por-correo.com/ingest" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"sourceId": "src:default:manual:pdf-tecnico-cliente-a",
|
|
||||||
"sourceType": "file",
|
|
||||||
"sourceRef": "/ruta/a/documento.pdf",
|
|
||||||
"mode": "mechanical",
|
|
||||||
"tags": ["cliente-a", "pdf-tecnico"]
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. `POST /retrieve`
|
|
||||||
|
|
||||||
Sirve para recuperar contexto del RAG sin pedir una respuesta final al modelo.
|
|
||||||
|
|
||||||
Es ideal para:
|
|
||||||
- agentes IA
|
|
||||||
- nodos tool de n8n
|
|
||||||
- flujos que quieran contexto rico y trazable
|
|
||||||
|
|
||||||
Payload base:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"mode": "documental",
|
|
||||||
"intent": "specific",
|
|
||||||
"model": "openai/gpt-4.1-mini",
|
|
||||||
"query": "que tenemos pendiente por hacer en este workspace",
|
|
||||||
"scope": {
|
|
||||||
"sourceRef": "/home/pancho/Documentos/Empresa/Desarrollo/IA/docs"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Campos:
|
|
||||||
|
|
||||||
- `mode`:
|
|
||||||
- `documental`
|
|
||||||
- `codigo`
|
|
||||||
- `auto`
|
|
||||||
- `intent`:
|
|
||||||
- `specific`
|
|
||||||
- `bootstrap`
|
|
||||||
- `query`: consulta del usuario o del flujo
|
|
||||||
- `scope` opcional:
|
|
||||||
- `sourceId`
|
|
||||||
- `sourceRef`
|
|
||||||
- `tags`
|
|
||||||
|
|
||||||
Nota util:
|
|
||||||
- si una ingesta se hizo con `sourceId` propio, despues puedes consultar solo ese material usando `scope.sourceId`
|
|
||||||
|
|
||||||
#### Ejemplo `retrieve` documental
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -sS -X POST "https://rag.por-correo.com/retrieve" \
|
curl -sS -X POST "https://rag.por-correo.com/retrieve" \
|
||||||
|
|
@ -191,329 +112,81 @@ curl -sS -X POST "https://rag.por-correo.com/retrieve" \
|
||||||
-d '{
|
-d '{
|
||||||
"mode": "documental",
|
"mode": "documental",
|
||||||
"intent": "specific",
|
"intent": "specific",
|
||||||
"query": "que tenemos pendiente por hacer en este workspace",
|
"query": "What does error FAT07 mean?",
|
||||||
"scope": {
|
"scope": {
|
||||||
"sourceRef": "/home/pancho/Documentos/Empresa/Desarrollo/IA/docs"
|
"sourceId": "customer-a:manual:v1"
|
||||||
}
|
},
|
||||||
|
"useModelInRetrieve": false
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Ejemplo `retrieve` bootstrap
|
Campos principales:
|
||||||
|
|
||||||
```bash
|
| Campo | Requerido | Funcion |
|
||||||
curl -sS -X POST "https://rag.por-correo.com/retrieve" \
|
|---|---|---|
|
||||||
-H "Content-Type: application/json" \
|
| `query` | Si | Consulta de recuperacion |
|
||||||
-d '{
|
| `mode` | No | Tipo de conocimiento |
|
||||||
"mode": "documental",
|
| `intent` | No | Consulta concreta o mapa bootstrap |
|
||||||
"intent": "bootstrap",
|
| `scope` | No | Limita las fuentes |
|
||||||
"query": "dame un mapa inicial del workspace y sus lineas de trabajo principales",
|
| `useModelInRetrieve` | No | Añade `modelSummary` generado por un modelo |
|
||||||
"scope": {
|
| `model` | No | Override usado solo para `modelSummary` |
|
||||||
"sourceRef": "/home/pancho/Documentos/Empresa/Desarrollo/IA/docs"
|
| `bootstrapMeta` | No | Trazabilidad del bootstrap en logs |
|
||||||
}
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Ejemplo `retrieve` codigo
|
Respuesta resumida:
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -sS -X POST "https://rag.por-correo.com/retrieve" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"mode": "codigo",
|
|
||||||
"intent": "specific",
|
|
||||||
"query": "como se construye source_id en el rag",
|
|
||||||
"scope": {
|
|
||||||
"sourceRef": "/home/pancho/Documentos/Empresa/Desarrollo/IA/RAG/src"
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
Respuesta resumida esperada:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"mode": "documental",
|
"mode": "documental",
|
||||||
"intent": "specific",
|
"intent": "specific",
|
||||||
"summary": "...",
|
"summary": "Retrieved 5 relevant chunks.",
|
||||||
"topics": ["PENDIENTES_GENERALES.md"],
|
"topics": ["support-errors.md"],
|
||||||
"criticalPoints": [],
|
"criticalPoints": [],
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"chunkId": "chk:...",
|
"chunkId": "chk:example",
|
||||||
"documentId": "doc:...",
|
"documentId": "doc:example",
|
||||||
"sourceId": "src:...",
|
"sourceId": "customer-a:manual:v1",
|
||||||
"title": "PENDIENTES_GENERALES.md",
|
"title": "support-errors.md",
|
||||||
"sectionTitle": "Resumen rapido",
|
|
||||||
"content": "...",
|
"content": "...",
|
||||||
"score": 0.87
|
"score": 0.87
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"followUpRefs": ["doc:..."],
|
"followUpRefs": ["doc:example"],
|
||||||
"scope": {
|
"scope": {
|
||||||
"sourceRef": "/home/pancho/Documentos/Empresa/Desarrollo/IA/docs"
|
"sourceId": "customer-a:manual:v1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
## Obtener una respuesta
|
||||||
|
|
||||||
### 4. `POST /answer`
|
`POST /answer` ejecuta retrieval y genera una respuesta final con citas.
|
||||||
|
|
||||||
Sirve para pedir al RAG una respuesta final apoyada en el contexto recuperado.
|
|
||||||
|
|
||||||
Es util para:
|
|
||||||
- agentes conversacionales
|
|
||||||
- respuestas listas para usuario
|
|
||||||
- pruebas rapidas desde n8n o aplicaciones
|
|
||||||
|
|
||||||
Tambien permite indicar el modelo de respuesta explicitamente mediante el campo opcional `model`.
|
|
||||||
|
|
||||||
Payload base:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"mode": "documental",
|
"mode": "documental",
|
||||||
"intent": "specific",
|
"intent": "specific",
|
||||||
"model": "openai/gpt-4.1-mini",
|
"query": "Explain the documented resolution for FAT07",
|
||||||
"query": "que tenemos pendiente por hacer en este workspace",
|
|
||||||
"scope": {
|
"scope": {
|
||||||
"sourceRef": "/home/pancho/Documentos/Empresa/Desarrollo/IA/docs"
|
"sourceId": "customer-a:manual:v1"
|
||||||
}
|
},
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Ejemplo `answer` documental
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -sS -X POST "https://rag.por-correo.com/answer" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"mode": "documental",
|
|
||||||
"intent": "specific",
|
|
||||||
"query": "que tenemos pendiente por hacer en este workspace",
|
|
||||||
"scope": {
|
|
||||||
"sourceRef": "/home/pancho/Documentos/Empresa/Desarrollo/IA/docs"
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Ejemplo `answer` codigo
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -sS -X POST "https://rag.por-correo.com/answer" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"mode": "codigo",
|
|
||||||
"intent": "specific",
|
|
||||||
"query": "como se construye source_id en el rag",
|
|
||||||
"scope": {
|
|
||||||
"sourceRef": "/home/pancho/Documentos/Empresa/Desarrollo/IA/RAG/src"
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
Respuesta resumida esperada:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"mode": "codigo",
|
|
||||||
"intent": "specific",
|
|
||||||
"model": "openai/gpt-4.1-mini",
|
|
||||||
"answer": "...",
|
|
||||||
"summary": "...",
|
|
||||||
"topics": ["ids.ts"],
|
|
||||||
"criticalPoints": [],
|
|
||||||
"citations": [
|
|
||||||
{
|
|
||||||
"chunkId": "chk:...",
|
|
||||||
"documentId": "doc:...",
|
|
||||||
"title": "ids.ts",
|
|
||||||
"sectionTitle": "export function buildSourceId(...)",
|
|
||||||
"startLine": 20,
|
|
||||||
"endLine": 25
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"scope": {
|
|
||||||
"sourceRef": "/home/pancho/Documentos/Empresa/Desarrollo/IA/RAG/src"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. `POST /answer/direct`
|
|
||||||
|
|
||||||
Sirve para pedir una respuesta directa al modelo sin usar contexto del RAG.
|
|
||||||
|
|
||||||
Es util para comparar:
|
|
||||||
- respuesta sin RAG
|
|
||||||
- respuesta con RAG
|
|
||||||
|
|
||||||
Payload base:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"query": "que es este servicio",
|
|
||||||
"model": "openai/gpt-4.1-mini"
|
"model": "openai/gpt-4.1-mini"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
Tambien acepta `preloadedContext` y `bootstrapMeta`. La respuesta incluye `model`, `answer`, resumen del retrieval, temas, puntos criticos, citas y scope.
|
||||||
|
|
||||||
### 6. `POST /chat`
|
`POST /answer/direct` no consulta el vector store:
|
||||||
|
|
||||||
Sirve para conversar con un modelo usando:
|
|
||||||
|
|
||||||
- contexto bootstrap precargado
|
|
||||||
- historial reciente de mensajes
|
|
||||||
- y opcionalmente una consulta adicional al RAG durante la propia conversacion
|
|
||||||
|
|
||||||
Payload base:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"message": "y ahora cuales son los siguientes pasos mas naturales?",
|
"query": "Explain what a RAG service is",
|
||||||
"history": [
|
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": "dame un mapa inicial del workspace"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"mode": "documental",
|
|
||||||
"model": "openai/gpt-4.1-mini",
|
"model": "openai/gpt-4.1-mini",
|
||||||
"preloadedContext": "<resumen del bootstrap>",
|
"preloadedContext": "Optional context supplied by the caller"
|
||||||
"allowAdditionalRetrieve": true,
|
|
||||||
"scope": {
|
|
||||||
"sourceRef": "/home/pancho/Documentos/Empresa/Desarrollo/IA/docs"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
Su respuesta real solo contiene:
|
||||||
|
|
||||||
### 7. `POST /ingest/upload`
|
|
||||||
|
|
||||||
Sirve para subir un archivo directamente al backend desde una interfaz web o cliente compatible con `multipart/form-data`.
|
|
||||||
|
|
||||||
Es util para:
|
|
||||||
- PDFs o documentos sueltos
|
|
||||||
- pruebas rapidas desde el playground
|
|
||||||
- crear un scope aislado para una carga concreta
|
|
||||||
|
|
||||||
Campos esperados del formulario:
|
|
||||||
|
|
||||||
- `file`
|
|
||||||
- `sourceId` opcional
|
|
||||||
- `mode`
|
|
||||||
- `tags`
|
|
||||||
|
|
||||||
Si se usa `sourceId`, el archivo subido no se mezcla con otros scopes salvo que elijas reutilizar ese mismo identificador.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 8. `POST /cleanup`
|
|
||||||
|
|
||||||
Sirve para eliminar vectores (chunks) ya ingeridos en el RAG segun un scope determinado, permitiendo limpiar contexto viejo antes de una reingesta.
|
|
||||||
|
|
||||||
Payload base (requiere `sourceId` o `sourceRef` por seguridad):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"scope": {
|
|
||||||
"sourceId": "src:default:folder:src_a9df8105"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Respuesta esperada:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"ok": true,
|
|
||||||
"deleted": 124
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 9. `GET /logs/recent`
|
|
||||||
|
|
||||||
Devuelve los logs recientes de evaluacion guardados por el sistema.
|
|
||||||
|
|
||||||
Sirve para revisar:
|
|
||||||
- consultas con contexto insuficiente
|
|
||||||
- respuestas problemáticas
|
|
||||||
- logs manuales marcados por el usuario
|
|
||||||
|
|
||||||
Ejemplo:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -sS "https://rag.por-correo.com/logs/recent"
|
|
||||||
```
|
|
||||||
|
|
||||||
Tambien admite actualizacion posterior del estado de revision mediante `PATCH /logs/:id`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 10. `POST /logs/manual`
|
|
||||||
|
|
||||||
Permite registrar manualmente una consulta o respuesta que quieras revisar despues.
|
|
||||||
|
|
||||||
Es util cuando:
|
|
||||||
- la respuesta no te convence
|
|
||||||
- detectas una carencia del RAG
|
|
||||||
- quieres dejar una nota humana asociada a una consulta
|
|
||||||
|
|
||||||
Payload base:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"operation": "answer",
|
|
||||||
"reason": "manual_review_requested",
|
|
||||||
"query": "prueba de log manual",
|
|
||||||
"mode": "documental",
|
|
||||||
"intent": "specific",
|
|
||||||
"model": "openai/gpt-4.1-mini",
|
|
||||||
"note": "la respuesta parece demasiado generica",
|
|
||||||
"scope": {
|
|
||||||
"sourceRef": "/home/pancho/Documentos/Empresa/Desarrollo/IA/docs"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 11. `PATCH /logs/:id`
|
|
||||||
|
|
||||||
Permite actualizar el seguimiento de un log ya creado.
|
|
||||||
|
|
||||||
Ejemplo:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -sS -X PATCH "https://rag.por-correo.com/logs/<id>" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"reviewStatus": "resolved",
|
|
||||||
"severity": "high",
|
|
||||||
"reviewedBy": "Paco POR-CORREO",
|
|
||||||
"resolutionNote": "Se ajusto el bootstrap para scopes aislados.",
|
|
||||||
"fixReference": "6557aea"
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
Respuesta esperada resumida:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"model": "openai/gpt-4.1-mini",
|
|
||||||
"answer": "...",
|
|
||||||
"usedBootstrapContext": true,
|
|
||||||
"usedAdditionalRetrieve": true,
|
|
||||||
"retrieved": {
|
|
||||||
"summary": "..."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Respuesta esperada:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -522,82 +195,165 @@ Respuesta esperada:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
## Chat con bootstrap
|
||||||
|
|
||||||
## Recomendacion practica para n8n
|
`POST /chat` reutiliza contexto precargado, historial reciente y, opcionalmente, una nueva consulta al RAG:
|
||||||
|
|
||||||
Si quieres probarlo rapido desde `n8n`:
|
|
||||||
|
|
||||||
1. Usa un nodo `HTTP Request`
|
|
||||||
2. Metodo `POST`
|
|
||||||
3. URL:
|
|
||||||
|
|
||||||
```text
|
|
||||||
https://rag.por-correo.com/retrieve
|
|
||||||
```
|
|
||||||
|
|
||||||
o
|
|
||||||
|
|
||||||
```text
|
|
||||||
https://rag.por-correo.com/answer
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Body en JSON
|
|
||||||
5. Empieza usando `answer` si quieres una respuesta directa
|
|
||||||
6. Usa `retrieve` si quieres dar el contexto al agente y que el modelo final responda por su cuenta
|
|
||||||
|
|
||||||
Payload recomendado para primera prueba en n8n:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"message": "What should I check next?",
|
||||||
|
"history": [
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "Give me an initial map of this project"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "assistant",
|
||||||
|
"content": "The project contains..."
|
||||||
|
}
|
||||||
|
],
|
||||||
"mode": "documental",
|
"mode": "documental",
|
||||||
"intent": "specific",
|
"preloadedContext": "Previously loaded bootstrap",
|
||||||
"query": "que tenemos pendiente por hacer en este workspace",
|
"allowAdditionalRetrieve": true,
|
||||||
"scope": {
|
"scope": {
|
||||||
"sourceRef": "/home/pancho/Documentos/Empresa/Desarrollo/IA/docs"
|
"sourceId": "project-docs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
Respuesta:
|
||||||
|
|
||||||
## Nota importante
|
|
||||||
|
|
||||||
El `scope` es clave para no mezclar fuentes distintas.
|
|
||||||
|
|
||||||
Ejemplos de `scope` utiles:
|
|
||||||
|
|
||||||
- documentacion global del workspace:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"sourceRef": "/home/pancho/Documentos/Empresa/Desarrollo/IA/docs"
|
"model": "openai/gpt-4.1-mini",
|
||||||
|
"answer": "...",
|
||||||
|
"usedBootstrapContext": true,
|
||||||
|
"usedAdditionalRetrieve": true,
|
||||||
|
"retrieved": {
|
||||||
|
"summary": "...",
|
||||||
|
"items": []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- documentacion del modulo RAG:
|
## Ingesta
|
||||||
|
|
||||||
|
### Ruta del servidor
|
||||||
|
|
||||||
|
`POST /ingest` requiere `sourceType` y `sourceRef`:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"sourceRef": "/home/pancho/Documentos/Empresa/Desarrollo/IA/RAG/docs"
|
"sourceId": "project-docs",
|
||||||
|
"sourceType": "folder",
|
||||||
|
"sourceRef": "/data/project-docs",
|
||||||
|
"mode": "mechanical",
|
||||||
|
"tags": ["project", "documentation"]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- codigo del modulo RAG:
|
`readPath` es un campo interno para separar la ruta fisica de lectura de la referencia logica. Los consumidores normales no deben enviarlo.
|
||||||
|
|
||||||
|
### Upload
|
||||||
|
|
||||||
|
`POST /ingest/upload` usa `multipart/form-data`:
|
||||||
|
|
||||||
|
| Campo | Requerido | Formato |
|
||||||
|
|---|---|---|
|
||||||
|
| `file` | Si | Archivo binario o ZIP |
|
||||||
|
| `sourceId` | No | Identificador logico |
|
||||||
|
| `mode` | No | `mechanical` o `interactive` |
|
||||||
|
| `tags` | No | Lista separada por comas |
|
||||||
|
| `isZipFolder` | No | Texto `true` o `false` |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -sS -X POST "https://rag.por-correo.com/ingest/upload" \
|
||||||
|
-F "file=@./support-errors.md" \
|
||||||
|
-F "sourceId=customer-a:manual:v1" \
|
||||||
|
-F "mode=mechanical" \
|
||||||
|
-F "tags=customer-a,support"
|
||||||
|
```
|
||||||
|
|
||||||
|
Ambas variantes responden `202` con el origen aceptado, archivos descubiertos, documentos procesados, chunks almacenados y coleccion. El upload añade `uploadedResource`.
|
||||||
|
|
||||||
|
## Limpieza controlada
|
||||||
|
|
||||||
|
`POST /cleanup` elimina los chunks que coinciden con un scope:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"sourceRef": "/home/pancho/Documentos/Empresa/Desarrollo/IA/RAG/src"
|
"scope": {
|
||||||
|
"sourceId": "customer-a:manual:v1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
Respuesta:
|
||||||
|
|
||||||
## Siguiente evolucion natural
|
```json
|
||||||
|
{
|
||||||
|
"ok": true,
|
||||||
|
"deleted": 36
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Mas adelante esta API podra exponerse tambien mediante un `MCP`, pero a dia de hoy ya puede consumirse directamente por HTTP desde:
|
Esta operacion es destructiva. Verifica primero el scope con `GET /sources`.
|
||||||
|
|
||||||
- modelos
|
## Evaluacion
|
||||||
- agentes
|
|
||||||
- flujos de `n8n`
|
`GET /logs/recent?limit=20` devuelve los logs recientes. Si no se indica `limit`, usa 20.
|
||||||
- aplicaciones propias
|
|
||||||
|
`POST /logs/manual` registra una observacion humana:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"operation": "retrieve",
|
||||||
|
"reason": "manual_review_requested",
|
||||||
|
"query": "What does error FAT07 mean?",
|
||||||
|
"mode": "documental",
|
||||||
|
"intent": "specific",
|
||||||
|
"scope": {
|
||||||
|
"sourceId": "customer-a:manual:v1"
|
||||||
|
},
|
||||||
|
"note": "The first result is too generic"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`PATCH /logs/{id}` actualiza su seguimiento:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"reviewStatus": "resolved",
|
||||||
|
"severity": "high",
|
||||||
|
"reviewedBy": "maintainer",
|
||||||
|
"resolutionNote": "Retrieval ranking adjusted and validated.",
|
||||||
|
"fixReference": "commit-or-ticket-reference"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Valores admitidos:
|
||||||
|
|
||||||
|
| Campo | Valores |
|
||||||
|
|---|---|
|
||||||
|
| `reviewStatus` | `pending`, `in_progress`, `resolved`, `ignored` |
|
||||||
|
| `severity` | `low`, `medium`, `high` |
|
||||||
|
|
||||||
|
## Integracion recomendada con n8n
|
||||||
|
|
||||||
|
Para una `HTTP Request Tool` conectada a un AI Agent:
|
||||||
|
|
||||||
|
1. Usa `POST /retrieve`.
|
||||||
|
2. Fija `mode`, `intent` y `scope` en el nodo.
|
||||||
|
3. Permite que el agente complete solo `query`.
|
||||||
|
4. Mantiene `useModelInRetrieve=false` para evitar procesar la consulta con dos modelos.
|
||||||
|
5. Entrega `items[].content` y sus metadatos al agente final.
|
||||||
|
|
||||||
|
Usa `POST /answer` solamente cuando el propio RAG deba generar la respuesta final.
|
||||||
|
|
||||||
|
## Fuente de verdad y mantenimiento
|
||||||
|
|
||||||
|
- `GET /openapi.json` es el contrato publico para integraciones y herramientas.
|
||||||
|
- `GET /help` es el indice rapido derivado del mismo contrato.
|
||||||
|
- Este documento explica flujos y decisiones de uso.
|
||||||
|
- `src/app.ts` contiene la implementacion efectiva.
|
||||||
|
- Cualquier cambio de endpoint debe actualizar OpenAPI y esta guia en el mismo bloque de trabajo.
|
||||||
|
|
|
||||||
|
|
@ -492,7 +492,9 @@ El soporte inicial se entiende para PDFs con texto extraible. PDFs escaneados o
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Pendientes inmediatos del diseño
|
## Decisiones de diseño que estaban pendientes
|
||||||
|
|
||||||
|
**Estado actual:** Resueltas e implementadas. Esta lista se conserva como registro historico del camino de diseño, no como backlog vigente.
|
||||||
|
|
||||||
1. Definir como se construiran exactamente `source_id`, `document_id` y `chunk_id`.
|
1. Definir como se construiran exactamente `source_id`, `document_id` y `chunk_id`.
|
||||||
2. Definir la estrategia concreta del chunking inicial.
|
2. Definir la estrategia concreta del chunking inicial.
|
||||||
|
|
@ -501,6 +503,8 @@ El soporte inicial se entiende para PDFs con texto extraible. PDFs escaneados o
|
||||||
5. Definir como se accedera a los servicios minimos del RAG desde la API.
|
5. Definir como se accedera a los servicios minimos del RAG desde la API.
|
||||||
6. Definir si la ingesta asistida por modelo estara presente desde la v1 o quedara para la siguiente fase.
|
6. Definir si la ingesta asistida por modelo estara presente desde la v1 o quedara para la siguiente fase.
|
||||||
|
|
||||||
|
Los pendientes actuales se mantienen en [`PENDIENTES_RAG.md`](./PENDIENTES_RAG.md).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Rol de esta bitacora dentro del modulo
|
## Rol de esta bitacora dentro del modulo
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
**Proyecto:** Workspace de tools IA para empresas
|
**Proyecto:** Workspace de tools IA para empresas
|
||||||
**Modulo:** RAG
|
**Modulo:** RAG
|
||||||
**Ultima actualizacion:** 2026-04-06
|
**Ultima actualizacion:** 2026-09-08
|
||||||
**Ultima modificacion por:** Agente RAG 2
|
**Ultima modificacion por:** Agente RAG 2
|
||||||
**Estado:** Activo
|
**Estado:** Activo
|
||||||
|
|
||||||
|
|
@ -64,3 +64,24 @@ Dar continuidad al RAG en `RAG/` a partir del estado actual documentado.
|
||||||
- Soporte explicito para flujos de `bootstrap` y `precarga` dirigida antes de revisar codigo.
|
- Soporte explicito para flujos de `bootstrap` y `precarga` dirigida antes de revisar codigo.
|
||||||
- Ajuste del paquete portable del agente para usar placeholder `__IA_WORKSPACE_ROOT__` y poder reinstalarlo correctamente en otros equipos sin depender de rutas locales de este PC.
|
- Ajuste del paquete portable del agente para usar placeholder `__IA_WORKSPACE_ROOT__` y poder reinstalarlo correctamente en otros equipos sin depender de rutas locales de este PC.
|
||||||
- Creacion de `RAG/agente_gstreamer/` como carpeta autocontenida para llevar el agente a otro PC con configuracion, scripts e instrucciones en un solo paquete.
|
- Creacion de `RAG/agente_gstreamer/` como carpeta autocontenida para llevar el agente a otro PC con configuracion, scripts e instrucciones en un solo paquete.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2026-09-08 - Agente RAG 2
|
||||||
|
|
||||||
|
**Modelo:** gpt-5.6-sol
|
||||||
|
**Session ID OpenCode:** `ses_29bdbd003ffeLrLjUlFgnp08Y7`
|
||||||
|
**Directorio:** `/home/pancho/Documentos/Empresa/Desarrollo/IA/RAG`
|
||||||
|
|
||||||
|
**Trabajo realizado:**
|
||||||
|
- Auditoria de las 14 operaciones HTTP existentes y de sus contratos reales.
|
||||||
|
- Implementacion de `GET /help` y `GET /openapi.json`, dejando 16 operaciones documentadas.
|
||||||
|
- Creacion de un contrato OpenAPI 3.1.1 con esquemas, parametros, respuestas, errores y ejemplos.
|
||||||
|
- Reescritura de `docs/API_RAG.md` como guia operativa enlazada al contrato OpenAPI.
|
||||||
|
- Correccion del estado obsoleto de playground, logs, cleanup, stack y documentos de diseño.
|
||||||
|
- Correccion de la metodologia: local se limita a compilacion y comprobaciones estaticas; los flujos HTTP se validan tras publicar en produccion.
|
||||||
|
- Validacion satisfactoria con `npm run check`, `npm run build`, comprobacion de referencias OpenAPI y contraste automatico entre rutas implementadas y documentadas.
|
||||||
|
|
||||||
|
**Estado final:**
|
||||||
|
- Pendiente 1 implementado en local.
|
||||||
|
- Pendiente commit, push, despliegue y validacion en `https://rag.por-correo.com` antes de marcarlo completado.
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
**Proyecto:** Workspace de tools IA para empresas
|
**Proyecto:** Workspace de tools IA para empresas
|
||||||
**Modulo:** RAG
|
**Modulo:** RAG
|
||||||
**Ultima actualizacion:** 2026-04-06
|
**Ultima actualizacion:** 2026-09-08
|
||||||
**Ultima modificacion por:** Agente tools IA para potenciar servicios empresariales
|
**Ultima modificacion por:** Agente RAG 2
|
||||||
**Estado:** Implementado en codigo, pendiente de redeploy
|
**Estado:** Implementado y operativo en produccion
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
**Proyecto:** Workspace de tools IA para empresas
|
**Proyecto:** Workspace de tools IA para empresas
|
||||||
**Modulo:** RAG
|
**Modulo:** RAG
|
||||||
**Ultima actualizacion:** 2026-04-06
|
**Ultima actualizacion:** 2026-09-08
|
||||||
**Ultima modificacion por:** Agente tools IA para potenciar servicios empresariales
|
**Ultima modificacion por:** Agente RAG 2
|
||||||
**Estado:** Activa
|
**Estado:** Activa
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
Dejar explicado de forma explicita el flujo de trabajo que se esta siguiendo para:
|
Dejar explicado de forma explicita el flujo de trabajo que se esta siguiendo para:
|
||||||
|
|
||||||
- implementar mejoras del RAG
|
- implementar mejoras del RAG
|
||||||
- validarlas localmente
|
- comprobar localmente que compilan y mantienen consistencia estatica
|
||||||
- subir solo los cambios adecuados al repo propio
|
- subir solo los cambios adecuados al repo propio
|
||||||
- avisar al usuario cuando ya solo queda hacer `Deploy` en EasyPanel
|
- avisar al usuario cuando ya solo queda hacer `Deploy` en EasyPanel
|
||||||
|
|
||||||
|
|
@ -26,7 +26,7 @@ La idea es que futuros agentes o nuevas sesiones no tengan que redescubrir este
|
||||||
El flujo correcto que ya esta funcionando bien es este:
|
El flujo correcto que ya esta funcionando bien es este:
|
||||||
|
|
||||||
1. implementar cambios en local
|
1. implementar cambios en local
|
||||||
2. validar localmente antes de tocar produccion
|
2. validar compilacion y consistencia estatica
|
||||||
3. documentar lo relevante
|
3. documentar lo relevante
|
||||||
4. hacer commit y push solo de lo que debe versionarse
|
4. hacer commit y push solo de lo que debe versionarse
|
||||||
5. avisar al usuario de que ya puede hacer `Deploy` en EasyPanel
|
5. avisar al usuario de que ya puede hacer `Deploy` en EasyPanel
|
||||||
|
|
@ -51,9 +51,9 @@ Esto incluye:
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 2. Validacion local obligatoria
|
### 2. Comprobacion local obligatoria
|
||||||
|
|
||||||
Antes de pedir redeploy, se valida en local.
|
Antes de pedir redeploy, se comprueba en local que el proyecto compila y que los artefactos estaticos son coherentes.
|
||||||
|
|
||||||
Minimo esperado:
|
Minimo esperado:
|
||||||
|
|
||||||
|
|
@ -62,16 +62,9 @@ cd /home/pancho/Documentos/Empresa/Desarrollo/IA/RAG
|
||||||
npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
Y si aplica, pruebas HTTP locales contra:
|
Tambien se pueden ejecutar comprobaciones estaticas que no levanten el servicio, como validar la estructura de un contrato OpenAPI o contrastar rutas documentadas.
|
||||||
|
|
||||||
- `/health`
|
No se levantan flujos ni servicios locales para validacion funcional. Las pruebas HTTP se realizan despues de publicar, contra el entorno real de produccion.
|
||||||
- `/ingest`
|
|
||||||
- `/retrieve`
|
|
||||||
- `/answer`
|
|
||||||
- `/chat`
|
|
||||||
- endpoints de logs
|
|
||||||
|
|
||||||
No se debe pedir deploy al usuario sin haber comprobado antes que la mejora compila y que el flujo principal funciona en local.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -130,7 +123,7 @@ Regla importante:
|
||||||
Cuando los cambios ya estan:
|
Cuando los cambios ya estan:
|
||||||
|
|
||||||
- implementados
|
- implementados
|
||||||
- validados en local
|
- compilados y comprobados estaticamente
|
||||||
- documentados
|
- documentados
|
||||||
- subidos al repo
|
- subidos al repo
|
||||||
|
|
||||||
|
|
@ -182,11 +175,13 @@ Probar segun aplique:
|
||||||
Un agente no deberia decir "haz deploy" si no se cumplen estas condiciones:
|
Un agente no deberia decir "haz deploy" si no se cumplen estas condiciones:
|
||||||
|
|
||||||
- compila en local
|
- compila en local
|
||||||
- el flujo nuevo ha sido probado localmente
|
- las comprobaciones estaticas aplicables son correctas
|
||||||
- la documentacion minima esta actualizada
|
- la documentacion minima esta actualizada
|
||||||
- el commit y push ya estan hechos
|
- el commit y push ya estan hechos
|
||||||
- no se han subido secretos por error
|
- no se han subido secretos por error
|
||||||
|
|
||||||
|
La validacion funcional del flujo se completa despues del deploy y exclusivamente en produccion.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Resultado esperado de esta metodologia
|
## Resultado esperado de esta metodologia
|
||||||
|
|
|
||||||
92
docs/PENDIENTES_RAG.md
Normal file
92
docs/PENDIENTES_RAG.md
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
# Pendientes priorizados del RAG
|
||||||
|
|
||||||
|
**Ultima actualizacion:** 2026-09-08
|
||||||
|
**Responsable de la priorizacion:** Usuario
|
||||||
|
**Estado:** Activo
|
||||||
|
|
||||||
|
Este documento es la fuente canonica del orden de trabajo pendiente del modulo RAG. La numeracion ya refleja la prioridad final indicada por el usuario.
|
||||||
|
|
||||||
|
## 1. Documentacion y descubrimiento de la API
|
||||||
|
|
||||||
|
**Estado:** Implementado; pendiente de publicacion y validacion en produccion.
|
||||||
|
|
||||||
|
- Implementar `/help` o una especificacion OpenAPI consultable.
|
||||||
|
- Actualizar documentos desfasados que todavia muestran como pendientes funciones ya desplegadas, como playground, logs y cleanup.
|
||||||
|
- Documentar los contratos, parametros, respuestas y errores reales de todos los endpoints.
|
||||||
|
- Mantener la documentacion alineada con produccion para que agentes e integradores no tengan que reconstruir el comportamiento desde el codigo.
|
||||||
|
|
||||||
|
## 2. Ciclo de vida del conocimiento
|
||||||
|
|
||||||
|
- Crear un catalogo de fuentes y versiones ingeridas.
|
||||||
|
- Saber que documento esta vigente, obsoleto, reemplazado o pendiente de reingesta.
|
||||||
|
- Evitar duplicados y permitir actualizaciones incrementales controladas.
|
||||||
|
- Registrar fecha de indexacion, modelo de embeddings, proveedor y version del contenido.
|
||||||
|
- Facilitar el reemplazo o rollback de una fuente sin depender de operaciones manuales dificiles de auditar.
|
||||||
|
|
||||||
|
## 3. OCR integrado en la ingesta
|
||||||
|
|
||||||
|
- Detectar PDFs con capturas, imagenes o una capa textual insuficiente.
|
||||||
|
- Ejecutar OCR automaticamente o bloquear la ingesta para revision cuando no pueda garantizarse la cobertura.
|
||||||
|
- Generar un documento intermedio auditable cuando sea necesario.
|
||||||
|
- Verificar el resultado antes de sustituir una fuente vigente.
|
||||||
|
- Evitar que se repita el problema detectado con el PDF de FacturaTech.
|
||||||
|
|
||||||
|
## 4. Mejora del retrieval
|
||||||
|
|
||||||
|
- Anadir busqueda hibrida semantica y textual para codigos exactos como `FAT07`, `504` o `SQLSTATE[23505]`.
|
||||||
|
- Mejorar el ranking usando coincidencias de codigo, regla, modulo y mensaje, no solo proximidad semantica.
|
||||||
|
- Crear un conjunto estable de consultas de evaluacion basado en casos reales.
|
||||||
|
- Medir precision, resultados irrelevantes y consultas ambiguas.
|
||||||
|
- Refinar retrieval y chunking con evidencia de uso, no mediante cambios generales sin medicion.
|
||||||
|
|
||||||
|
## 5. Seguridad de la API
|
||||||
|
|
||||||
|
- Anadir autenticacion para consumidores autorizados.
|
||||||
|
- Proteger especialmente uploads, cleanup y operaciones sobre logs.
|
||||||
|
- Aplicar rate limiting y limites de tamano o tipo de archivo.
|
||||||
|
- Revisar CORS, exposicion publica, registros sensibles y permisos por operacion.
|
||||||
|
- Sustituir la configuracion sin credenciales de n8n cuando exista el mecanismo de autenticacion definitivo.
|
||||||
|
|
||||||
|
## 6. Pruebas automatizadas
|
||||||
|
|
||||||
|
- Incorporar un framework y scripts de pruebas; actualmente el proyecto no tiene suite automatizada.
|
||||||
|
- Cubrir ingesta, parsing, chunking, scopes, cleanup y retrieval.
|
||||||
|
- Probar contratos y respuestas de error de la API.
|
||||||
|
- Anadir pruebas de regresion con consultas reales como las validadas para FacturaTech.
|
||||||
|
- Separar pruebas unitarias, de integracion y verificaciones opcionales contra servicios remotos.
|
||||||
|
|
||||||
|
## 7. Operacion y mantenimiento
|
||||||
|
|
||||||
|
- Resolver la colision de `sourceRef` al ingerir carpetas homonimas.
|
||||||
|
- Separar claramente configuracion versionable y secretos locales.
|
||||||
|
- Verificar y cerrar documentalmente la migracion del repositorio raiz de RAG y su despliegue en EasyPanel.
|
||||||
|
- Revisar copias temporales o backups asociados a la migracion antes de eliminarlos.
|
||||||
|
- Mantener un procedimiento fiable de despliegue, verificacion y rollback.
|
||||||
|
|
||||||
|
## 8. Sistema de evaluacion
|
||||||
|
|
||||||
|
- Revisar periodicamente los logs de evaluacion almacenados en Qdrant.
|
||||||
|
- Convertir incidencias reales en casos de prueba permanentes.
|
||||||
|
- Anadir metricas de recuperacion, calidad y contexto insuficiente.
|
||||||
|
- Mantener trazabilidad entre una incidencia, el cambio aplicado y la validacion posterior.
|
||||||
|
- Diferenciar claramente observacion manual, alerta automatica y regresion confirmada.
|
||||||
|
|
||||||
|
## 9. Capa MCP
|
||||||
|
|
||||||
|
- Exponer capacidades del RAG como tools MCP reutilizables.
|
||||||
|
- Definir inicialmente retrieval y consulta de fuentes; limitar operaciones destructivas.
|
||||||
|
- Mantener HTTP como API base y MCP como adaptador, evitando duplicar la logica del servicio.
|
||||||
|
- Definir autenticacion, contratos y permisos antes de exponer operaciones adicionales.
|
||||||
|
|
||||||
|
## 10. Modelo de `answer`
|
||||||
|
|
||||||
|
- Evaluar y sustituir `openai/gpt-4.1-mini` por el modelo definitivo de respuesta.
|
||||||
|
- Comparar calidad, coste, latencia y dependencia del proveedor.
|
||||||
|
- Mantener `answer` construido sobre `retrieve` y evitar un segundo camino de recuperacion.
|
||||||
|
- Considerar esta tarea despues de las anteriores; integraciones como WhatsApp ya pueden usar `/retrieve` y dejar la respuesta final a su propio agente.
|
||||||
|
|
||||||
|
## Regla de mantenimiento
|
||||||
|
|
||||||
|
- No cambiar este orden sin confirmacion del usuario.
|
||||||
|
- Al completar un punto, registrar evidencia y marcar su estado sin renumerar silenciosamente los demas.
|
||||||
|
- Las tareas detalladas pueden vivir en documentos independientes, pero este archivo conserva la prioridad global del modulo.
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
**Proyecto:** Workspace de tools IA para empresas
|
**Proyecto:** Workspace de tools IA para empresas
|
||||||
**Modulo:** RAG
|
**Modulo:** RAG
|
||||||
**Ultima actualizacion:** 2026-04-06
|
**Ultima actualizacion:** 2026-09-08
|
||||||
**Ultima modificacion por:** Agente tools IA para potenciar servicios empresariales
|
**Ultima modificacion por:** Agente RAG 2
|
||||||
**Estado:** Implementado en codigo, pendiente de redeploy
|
**Estado:** Implementado y operativo en produccion
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -96,16 +96,16 @@ Estado confirmado:
|
||||||
- dominio activo: `https://rag.por-correo.com`
|
- dominio activo: `https://rag.por-correo.com`
|
||||||
- conexion operativa con `Qdrant`
|
- conexion operativa con `Qdrant`
|
||||||
- soporte funcional para `documental` y `codigo`
|
- soporte funcional para `documental` y `codigo`
|
||||||
- endpoints operativos: `GET /health`, `POST /ingest`, `POST /retrieve`, `POST /answer`
|
- API HTTP operativa con ingesta, upload, cleanup, retrieval, answer, chat, catalogo de fuentes/modelos y logs de evaluacion
|
||||||
|
- playground web operativo en `/playground`
|
||||||
|
- contrato OpenAPI y ayuda de descubrimiento implementados, pendientes de despliegue
|
||||||
|
|
||||||
Pruebas funcionales ya superadas:
|
Pruebas funcionales ya superadas:
|
||||||
- consulta documental sobre pendientes del workspace
|
- consulta documental sobre pendientes del workspace
|
||||||
- consulta conceptual sobre caracteristicas del RAG
|
- consulta conceptual sobre caracteristicas del RAG
|
||||||
- consulta tecnica en modo codigo sobre la construccion de `source_id`
|
- consulta tecnica en modo codigo sobre la construccion de `source_id`
|
||||||
|
|
||||||
Pendientes de evolucion de esta base:
|
Los pendientes vigentes, su prioridad y su estado se mantienen en [`PENDIENTES_RAG.md`](./PENDIENTES_RAG.md).
|
||||||
- sustituir el modelo actual de `answer` por una alternativa alineada con la decision de no depender de OpenAI en esa capa
|
|
||||||
- seguir refinando retrieval y answer segun resultados de uso real
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -160,10 +160,24 @@ Extensiones de codigo ya soportadas:
|
||||||
- encaja bien con una futura capa MCP
|
- encaja bien con una futura capa MCP
|
||||||
|
|
||||||
Endpoints funcionales actuales:
|
Endpoints funcionales actuales:
|
||||||
|
- `GET /help`
|
||||||
|
- `GET /openapi.json`
|
||||||
|
- `GET /playground`
|
||||||
- `GET /health`
|
- `GET /health`
|
||||||
|
- `GET /sources`
|
||||||
|
- `GET /models/answer`
|
||||||
|
- `GET /logs/recent`
|
||||||
- `POST /ingest`
|
- `POST /ingest`
|
||||||
|
- `POST /ingest/upload`
|
||||||
|
- `POST /cleanup`
|
||||||
- `POST /retrieve`
|
- `POST /retrieve`
|
||||||
- `POST /answer`
|
- `POST /answer`
|
||||||
|
- `POST /answer/direct`
|
||||||
|
- `POST /chat`
|
||||||
|
- `POST /logs/manual`
|
||||||
|
- `PATCH /logs/:id`
|
||||||
|
|
||||||
|
`/help` y `/openapi.json` estan implementados en codigo y quedaran disponibles en produccion tras el siguiente despliegue.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -216,10 +230,9 @@ La v1 se construira con una idea clara:
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Pendiente inmediato
|
## Pendientes vigentes
|
||||||
|
|
||||||
- Revisar y sustituir mas adelante el modelo actual de `answer` por una opcion alineada con la estrategia de no depender de OpenAI para esa capa.
|
El orden de trabajo actual se mantiene exclusivamente en [`PENDIENTES_RAG.md`](./PENDIENTES_RAG.md) para evitar listas duplicadas y contradictorias.
|
||||||
- Documentar la metodologia reutilizable para desplegar futuros servicios correctamente en EasyPanel.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -231,7 +244,7 @@ Estado confirmado:
|
||||||
- servicio activo en `https://rag.por-correo.com`
|
- servicio activo en `https://rag.por-correo.com`
|
||||||
- `Qdrant` conectado correctamente por red interna
|
- `Qdrant` conectado correctamente por red interna
|
||||||
- variables de entorno cargadas en produccion
|
- variables de entorno cargadas en produccion
|
||||||
- `health`, `retrieve` y `answer` probados con exito
|
- endpoints principales, playground, cleanup y logs de evaluacion probados con exito
|
||||||
|
|
||||||
Observacion abierta:
|
Observacion abierta:
|
||||||
- aparece un warning de deprecacion relacionado con `punycode`, pero no esta bloqueando el funcionamiento del servicio
|
- aparece un warning de deprecacion relacionado con `punycode`, pero no esta bloqueando el funcionamiento del servicio
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
**Proyecto:** Workspace de tools IA para empresas
|
**Proyecto:** Workspace de tools IA para empresas
|
||||||
**Modulo:** RAG
|
**Modulo:** RAG
|
||||||
**Ultima actualizacion:** 2026-04-06
|
**Ultima actualizacion:** 2026-09-08
|
||||||
**Estado:** En implementacion
|
**Estado:** Completada y validada en produccion
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -29,3 +29,10 @@ Permitir borrar contenido ya ingerido del RAG de forma controlada antes de reing
|
||||||
- Borrar el codigo de `RAG/src` previamente ingerido.
|
- Borrar el codigo de `RAG/src` previamente ingerido.
|
||||||
- Reingestar `RAG/src`.
|
- Reingestar `RAG/src`.
|
||||||
- Confirmar que se mantiene la integridad sin duplicar fragmentos.
|
- Confirmar que se mantiene la integridad sin duplicar fragmentos.
|
||||||
|
|
||||||
|
## Resultado
|
||||||
|
|
||||||
|
- `POST /cleanup` esta implementado y exige `sourceId` o `sourceRef` para evitar borrados masivos accidentales.
|
||||||
|
- La pestaña `Limpieza` esta disponible en el playground.
|
||||||
|
- El flujo fue validado inicialmente limpiando y reingiriendo `RAG/src`.
|
||||||
|
- Posteriormente se volvio a validar en produccion al sustituir una ingesta incompleta de FacturaTech por 36 chunks revisados.
|
||||||
|
|
|
||||||
663
src/api/openapi.ts
Normal file
663
src/api/openapi.ts
Normal file
|
|
@ -0,0 +1,663 @@
|
||||||
|
const ref = (name: string) => ({ $ref: `#/components/schemas/${name}` });
|
||||||
|
|
||||||
|
const jsonContent = (schema: Record<string, unknown>, example?: unknown) => ({
|
||||||
|
"application/json": {
|
||||||
|
schema,
|
||||||
|
...(example === undefined ? {} : { example })
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const jsonResponse = (description: string, schema: Record<string, unknown>, example?: unknown) => ({
|
||||||
|
description,
|
||||||
|
content: jsonContent(schema, example)
|
||||||
|
});
|
||||||
|
|
||||||
|
const serverError = jsonResponse("Internal or upstream service error.", ref("Error"));
|
||||||
|
|
||||||
|
export const openApiDocument = {
|
||||||
|
openapi: "3.1.1",
|
||||||
|
info: {
|
||||||
|
title: "RAG Service API",
|
||||||
|
version: "0.1.0",
|
||||||
|
description: "HTTP API for ingesting, retrieving, answering from, and evaluating scoped RAG knowledge."
|
||||||
|
},
|
||||||
|
jsonSchemaDialect: "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
servers: [
|
||||||
|
{ url: "https://rag.por-correo.com", description: "Production" },
|
||||||
|
{ url: "http://localhost:3000", description: "Local development" }
|
||||||
|
],
|
||||||
|
tags: [
|
||||||
|
{ name: "Discovery", description: "API contract and browser playground." },
|
||||||
|
{ name: "Status", description: "Service capabilities and available resources." },
|
||||||
|
{ name: "Ingestion", description: "Knowledge ingestion and cleanup." },
|
||||||
|
{ name: "Retrieval", description: "Context retrieval and model-backed answers." },
|
||||||
|
{ name: "Evaluation", description: "Evaluation log capture and review." }
|
||||||
|
],
|
||||||
|
paths: {
|
||||||
|
"/help": {
|
||||||
|
get: {
|
||||||
|
tags: ["Discovery"],
|
||||||
|
summary: "Discover the API",
|
||||||
|
description: "Returns a concise endpoint catalog generated from this OpenAPI document.",
|
||||||
|
responses: {
|
||||||
|
"200": jsonResponse("API discovery catalog.", ref("HelpResponse"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/openapi.json": {
|
||||||
|
get: {
|
||||||
|
tags: ["Discovery"],
|
||||||
|
summary: "Get the OpenAPI contract",
|
||||||
|
responses: {
|
||||||
|
"200": jsonResponse("OpenAPI 3.1.1 document.", { type: "object" })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/playground": {
|
||||||
|
get: {
|
||||||
|
tags: ["Discovery"],
|
||||||
|
summary: "Open the browser playground",
|
||||||
|
responses: {
|
||||||
|
"200": {
|
||||||
|
description: "Playground HTML page.",
|
||||||
|
content: { "text/html": { schema: { type: "string" } } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/health": {
|
||||||
|
get: {
|
||||||
|
tags: ["Status"],
|
||||||
|
summary: "Check service health and capabilities",
|
||||||
|
responses: {
|
||||||
|
"200": jsonResponse("Current service status.", ref("HealthResponse"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/sources": {
|
||||||
|
get: {
|
||||||
|
tags: ["Status"],
|
||||||
|
summary: "List available knowledge scopes",
|
||||||
|
responses: {
|
||||||
|
"200": jsonResponse("Scopes currently stored in the vector database.", {
|
||||||
|
type: "array",
|
||||||
|
items: ref("AvailableScope")
|
||||||
|
}),
|
||||||
|
"500": serverError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/models/answer": {
|
||||||
|
get: {
|
||||||
|
tags: ["Status"],
|
||||||
|
summary: "List answer models",
|
||||||
|
description: "Returns the configured default and up to 80 selectable answer models.",
|
||||||
|
responses: {
|
||||||
|
"200": jsonResponse("Available answer models.", ref("AnswerModelsResponse")),
|
||||||
|
"500": serverError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/ingest": {
|
||||||
|
post: {
|
||||||
|
tags: ["Ingestion"],
|
||||||
|
summary: "Ingest a server-accessible file or folder",
|
||||||
|
requestBody: {
|
||||||
|
required: true,
|
||||||
|
content: jsonContent(ref("IngestRequest"), {
|
||||||
|
sourceType: "folder",
|
||||||
|
sourceRef: "/data/project-docs",
|
||||||
|
mode: "mechanical",
|
||||||
|
tags: ["project-docs"]
|
||||||
|
})
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
"202": jsonResponse("Ingestion completed and accepted.", ref("IngestResponse")),
|
||||||
|
"500": serverError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/ingest/upload": {
|
||||||
|
post: {
|
||||||
|
tags: ["Ingestion"],
|
||||||
|
summary: "Upload and ingest a file or ZIP folder",
|
||||||
|
requestBody: {
|
||||||
|
required: true,
|
||||||
|
content: {
|
||||||
|
"multipart/form-data": {
|
||||||
|
schema: ref("UploadIngestRequest")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
"202": jsonResponse("Upload ingested successfully.", ref("UploadIngestResponse")),
|
||||||
|
"400": jsonResponse("The file field is missing.", ref("Error"), { ok: false, error: "Missing file upload" }),
|
||||||
|
"500": serverError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/cleanup": {
|
||||||
|
post: {
|
||||||
|
tags: ["Ingestion"],
|
||||||
|
summary: "Delete chunks matching a source scope",
|
||||||
|
description: "Requires sourceId or sourceRef to prevent accidental mass deletion. Tags alone are not accepted.",
|
||||||
|
requestBody: {
|
||||||
|
required: true,
|
||||||
|
content: jsonContent(ref("CleanupRequest"), {
|
||||||
|
scope: { sourceId: "customer-a:manual:v1" }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
"200": jsonResponse("Matching chunks deleted.", ref("CleanupResponse")),
|
||||||
|
"400": jsonResponse("Scope is missing or unsafe for cleanup.", ref("Error")),
|
||||||
|
"500": serverError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/retrieve": {
|
||||||
|
post: {
|
||||||
|
tags: ["Retrieval"],
|
||||||
|
summary: "Retrieve scoped context",
|
||||||
|
description: "Returns retrieved chunks. Set useModelInRetrieve to true to also generate modelSummary.",
|
||||||
|
requestBody: {
|
||||||
|
required: true,
|
||||||
|
content: jsonContent(ref("RetrieveRequest"), {
|
||||||
|
mode: "documental",
|
||||||
|
intent: "specific",
|
||||||
|
query: "How is the service deployed?",
|
||||||
|
scope: { sourceId: "project-docs" },
|
||||||
|
useModelInRetrieve: false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
"200": jsonResponse("Retrieved context.", ref("RetrieveResponse")),
|
||||||
|
"500": serverError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/answer": {
|
||||||
|
post: {
|
||||||
|
tags: ["Retrieval"],
|
||||||
|
summary: "Answer using retrieved RAG context",
|
||||||
|
requestBody: {
|
||||||
|
required: true,
|
||||||
|
content: jsonContent(ref("AnswerRequest"))
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
"200": jsonResponse("Model answer with retrieval citations.", ref("AnswerResponse")),
|
||||||
|
"500": serverError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/answer/direct": {
|
||||||
|
post: {
|
||||||
|
tags: ["Retrieval"],
|
||||||
|
summary: "Answer without retrieving RAG context",
|
||||||
|
description: "Primarily used to compare direct model output with RAG-backed output.",
|
||||||
|
requestBody: {
|
||||||
|
required: true,
|
||||||
|
content: jsonContent(ref("DirectAnswerRequest"))
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
"200": jsonResponse("Direct model answer.", ref("DirectAnswerResponse")),
|
||||||
|
"500": serverError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/chat": {
|
||||||
|
post: {
|
||||||
|
tags: ["Retrieval"],
|
||||||
|
summary: "Chat with optional bootstrap and additional retrieval",
|
||||||
|
requestBody: {
|
||||||
|
required: true,
|
||||||
|
content: jsonContent(ref("ChatRequest"))
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
"200": jsonResponse("Chat response and retrieval usage metadata.", ref("ChatResponse")),
|
||||||
|
"500": serverError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/logs/recent": {
|
||||||
|
get: {
|
||||||
|
tags: ["Evaluation"],
|
||||||
|
summary: "List recent evaluation logs",
|
||||||
|
parameters: [{
|
||||||
|
name: "limit",
|
||||||
|
in: "query",
|
||||||
|
required: false,
|
||||||
|
description: "Maximum number of logs. Defaults to 20.",
|
||||||
|
schema: { type: "integer", default: 20 }
|
||||||
|
}],
|
||||||
|
responses: {
|
||||||
|
"200": jsonResponse("Recent evaluation logs.", { type: "array", items: ref("EvaluationLog") }),
|
||||||
|
"500": serverError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/logs/manual": {
|
||||||
|
post: {
|
||||||
|
tags: ["Evaluation"],
|
||||||
|
summary: "Create a manual evaluation log",
|
||||||
|
requestBody: {
|
||||||
|
required: true,
|
||||||
|
content: jsonContent(ref("ManualLogRequest"))
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
"201": jsonResponse("Evaluation log created.", ref("EvaluationLog")),
|
||||||
|
"500": serverError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/logs/{id}": {
|
||||||
|
patch: {
|
||||||
|
tags: ["Evaluation"],
|
||||||
|
summary: "Update evaluation log review status",
|
||||||
|
parameters: [{
|
||||||
|
name: "id",
|
||||||
|
in: "path",
|
||||||
|
required: true,
|
||||||
|
schema: { type: "string" }
|
||||||
|
}],
|
||||||
|
requestBody: {
|
||||||
|
required: true,
|
||||||
|
content: jsonContent(ref("UpdateLogRequest"))
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
"200": jsonResponse("Updated evaluation log.", ref("EvaluationLog")),
|
||||||
|
"404": jsonResponse("Evaluation log not found.", ref("Error"), { ok: false, error: "Log not found" }),
|
||||||
|
"500": serverError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
schemas: {
|
||||||
|
Error: {
|
||||||
|
type: "object",
|
||||||
|
required: ["ok", "error"],
|
||||||
|
properties: {
|
||||||
|
ok: { type: "boolean", const: false },
|
||||||
|
error: { type: "string" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Scope: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
sourceId: { type: "string" },
|
||||||
|
sourceRef: { type: "string" },
|
||||||
|
tags: { type: "array", items: { type: "string" } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
AvailableScope: {
|
||||||
|
type: "object",
|
||||||
|
required: ["sourceId", "sourceRef", "chunkModes", "tags"],
|
||||||
|
properties: {
|
||||||
|
sourceId: { type: "string" },
|
||||||
|
sourceRef: { type: "string" },
|
||||||
|
chunkModes: { type: "array", items: { $ref: "#/components/schemas/ChunkMode" } },
|
||||||
|
tags: { type: "array", items: { type: "string" } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ChunkMode: { type: "string", enum: ["documental", "codigo", "auto"] },
|
||||||
|
RetrieveIntent: { type: "string", enum: ["bootstrap", "specific"] },
|
||||||
|
IngestRequest: {
|
||||||
|
type: "object",
|
||||||
|
required: ["sourceType", "sourceRef"],
|
||||||
|
properties: {
|
||||||
|
sourceId: { type: "string" },
|
||||||
|
sourceType: { type: "string", enum: ["file", "folder"] },
|
||||||
|
sourceRef: { type: "string" },
|
||||||
|
readPath: { type: "string", description: "Internal filesystem path used when the logical sourceRef differs from the read location." },
|
||||||
|
mode: { type: "string", enum: ["mechanical", "interactive"], default: "mechanical" },
|
||||||
|
tags: { type: "array", items: { type: "string" } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
IngestResponse: {
|
||||||
|
type: "object",
|
||||||
|
required: ["accepted", "source", "filesDiscovered", "documentsProcessed", "chunksStored", "collectionName"],
|
||||||
|
properties: {
|
||||||
|
accepted: { type: "boolean", const: true },
|
||||||
|
source: ref("IngestRequest"),
|
||||||
|
filesDiscovered: { type: "integer" },
|
||||||
|
documentsProcessed: { type: "integer" },
|
||||||
|
chunksStored: { type: "integer" },
|
||||||
|
collectionName: { type: "string" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UploadIngestRequest: {
|
||||||
|
type: "object",
|
||||||
|
required: ["file"],
|
||||||
|
properties: {
|
||||||
|
file: { type: "string", format: "binary" },
|
||||||
|
sourceId: { type: "string" },
|
||||||
|
mode: { type: "string", enum: ["mechanical", "interactive"], default: "mechanical" },
|
||||||
|
tags: { type: "string", description: "Comma-separated tags." },
|
||||||
|
isZipFolder: { type: "string", enum: ["true", "false"], default: "false" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UploadIngestResponse: {
|
||||||
|
allOf: [
|
||||||
|
ref("IngestResponse"),
|
||||||
|
{
|
||||||
|
type: "object",
|
||||||
|
required: ["uploadedResource"],
|
||||||
|
properties: { uploadedResource: { type: "string" } }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
CleanupRequest: {
|
||||||
|
type: "object",
|
||||||
|
required: ["scope"],
|
||||||
|
properties: { scope: ref("Scope") }
|
||||||
|
},
|
||||||
|
CleanupResponse: {
|
||||||
|
type: "object",
|
||||||
|
required: ["ok", "deleted"],
|
||||||
|
properties: {
|
||||||
|
ok: { type: "boolean", const: true },
|
||||||
|
deleted: { type: "integer" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
BootstrapMeta: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
query: { type: "string" },
|
||||||
|
mode: ref("ChunkMode"),
|
||||||
|
scope: ref("Scope"),
|
||||||
|
model: { type: "string" },
|
||||||
|
usedModelSummary: { type: "boolean" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RetrieveRequest: {
|
||||||
|
type: "object",
|
||||||
|
required: ["query"],
|
||||||
|
properties: {
|
||||||
|
mode: { ...ref("ChunkMode"), default: "auto" },
|
||||||
|
intent: { ...ref("RetrieveIntent"), default: "specific" },
|
||||||
|
query: { type: "string" },
|
||||||
|
scope: ref("Scope"),
|
||||||
|
model: { type: "string", description: "Model override used only when useModelInRetrieve is true." },
|
||||||
|
useModelInRetrieve: { type: "boolean", default: false },
|
||||||
|
bootstrapMeta: ref("BootstrapMeta")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RetrievedItem: {
|
||||||
|
type: "object",
|
||||||
|
required: ["chunkId", "documentId", "sourceId", "title", "content", "score"],
|
||||||
|
properties: {
|
||||||
|
chunkId: { type: "string" },
|
||||||
|
documentId: { type: "string" },
|
||||||
|
sourceId: { type: "string" },
|
||||||
|
title: { type: "string" },
|
||||||
|
sectionTitle: { type: "string" },
|
||||||
|
content: { type: "string" },
|
||||||
|
score: { type: "number" },
|
||||||
|
chunkMode: ref("ChunkMode"),
|
||||||
|
startLine: { type: "integer" },
|
||||||
|
endLine: { type: "integer" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RetrieveResponse: {
|
||||||
|
type: "object",
|
||||||
|
required: ["mode", "intent", "summary", "topics", "criticalPoints", "items", "followUpRefs"],
|
||||||
|
properties: {
|
||||||
|
mode: ref("ChunkMode"),
|
||||||
|
intent: ref("RetrieveIntent"),
|
||||||
|
model: { type: "string" },
|
||||||
|
summary: { type: "string" },
|
||||||
|
modelSummary: { type: "string" },
|
||||||
|
topics: { type: "array", items: { type: "string" } },
|
||||||
|
criticalPoints: { type: "array", items: { type: "string" } },
|
||||||
|
items: { type: "array", items: ref("RetrievedItem") },
|
||||||
|
followUpRefs: { type: "array", items: { type: "string" } },
|
||||||
|
scope: ref("Scope")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
AnswerRequest: {
|
||||||
|
type: "object",
|
||||||
|
required: ["query"],
|
||||||
|
properties: {
|
||||||
|
mode: { ...ref("ChunkMode"), default: "auto" },
|
||||||
|
intent: { ...ref("RetrieveIntent"), default: "specific" },
|
||||||
|
query: { type: "string" },
|
||||||
|
scope: ref("Scope"),
|
||||||
|
model: { type: "string" },
|
||||||
|
preloadedContext: { type: "string" },
|
||||||
|
bootstrapMeta: ref("BootstrapMeta")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Citation: {
|
||||||
|
type: "object",
|
||||||
|
required: ["chunkId", "documentId", "title"],
|
||||||
|
properties: {
|
||||||
|
chunkId: { type: "string" },
|
||||||
|
documentId: { type: "string" },
|
||||||
|
title: { type: "string" },
|
||||||
|
sectionTitle: { type: "string" },
|
||||||
|
startLine: { type: "integer" },
|
||||||
|
endLine: { type: "integer" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
AnswerResponse: {
|
||||||
|
type: "object",
|
||||||
|
required: ["mode", "intent", "model", "answer", "summary", "topics", "criticalPoints", "citations"],
|
||||||
|
properties: {
|
||||||
|
mode: ref("ChunkMode"),
|
||||||
|
intent: ref("RetrieveIntent"),
|
||||||
|
model: { type: "string" },
|
||||||
|
answer: { type: "string" },
|
||||||
|
summary: { type: "string" },
|
||||||
|
topics: { type: "array", items: { type: "string" } },
|
||||||
|
criticalPoints: { type: "array", items: { type: "string" } },
|
||||||
|
citations: { type: "array", items: ref("Citation") },
|
||||||
|
scope: ref("Scope")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DirectAnswerRequest: {
|
||||||
|
type: "object",
|
||||||
|
required: ["query"],
|
||||||
|
properties: {
|
||||||
|
query: { type: "string" },
|
||||||
|
model: { type: "string" },
|
||||||
|
preloadedContext: { type: "string" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DirectAnswerResponse: {
|
||||||
|
type: "object",
|
||||||
|
required: ["model", "answer"],
|
||||||
|
properties: {
|
||||||
|
model: { type: "string" },
|
||||||
|
answer: { type: "string" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ChatMessage: {
|
||||||
|
type: "object",
|
||||||
|
required: ["role", "content"],
|
||||||
|
properties: {
|
||||||
|
role: { type: "string", enum: ["user", "assistant"] },
|
||||||
|
content: { type: "string" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ChatRequest: {
|
||||||
|
type: "object",
|
||||||
|
required: ["message"],
|
||||||
|
properties: {
|
||||||
|
message: { type: "string" },
|
||||||
|
history: { type: "array", items: ref("ChatMessage") },
|
||||||
|
mode: { ...ref("ChunkMode"), default: "documental" },
|
||||||
|
model: { type: "string" },
|
||||||
|
preloadedContext: { type: "string" },
|
||||||
|
allowAdditionalRetrieve: { type: "boolean", default: false },
|
||||||
|
scope: ref("Scope"),
|
||||||
|
bootstrapMeta: ref("BootstrapMeta")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ChatResponse: {
|
||||||
|
type: "object",
|
||||||
|
required: ["model", "answer", "usedBootstrapContext", "usedAdditionalRetrieve"],
|
||||||
|
properties: {
|
||||||
|
model: { type: "string" },
|
||||||
|
answer: { type: "string" },
|
||||||
|
usedBootstrapContext: { type: "boolean" },
|
||||||
|
usedAdditionalRetrieve: { type: "boolean" },
|
||||||
|
retrieved: ref("RetrieveResponse")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
EvaluationLog: {
|
||||||
|
type: "object",
|
||||||
|
required: ["id", "trigger", "operation", "reason", "query", "createdAt", "retrievedItemsCount", "chunkIds", "documentIds", "reviewStatus", "severity"],
|
||||||
|
properties: {
|
||||||
|
id: { type: "string" },
|
||||||
|
trigger: { type: "string", enum: ["automatic", "manual"] },
|
||||||
|
operation: { type: "string", enum: ["retrieve", "answer", "chat"] },
|
||||||
|
reason: { type: "string" },
|
||||||
|
query: { type: "string" },
|
||||||
|
mode: ref("ChunkMode"),
|
||||||
|
intent: ref("RetrieveIntent"),
|
||||||
|
scope: ref("Scope"),
|
||||||
|
model: { type: "string" },
|
||||||
|
note: { type: "string" },
|
||||||
|
createdAt: { type: "string", format: "date-time" },
|
||||||
|
usedBootstrapContext: { type: "boolean" },
|
||||||
|
bootstrapQuery: { type: "string" },
|
||||||
|
bootstrapMode: ref("ChunkMode"),
|
||||||
|
bootstrapScope: ref("Scope"),
|
||||||
|
bootstrapModel: { type: "string" },
|
||||||
|
bootstrapUsedModelSummary: { type: "boolean" },
|
||||||
|
usedAdditionalRetrieve: { type: "boolean" },
|
||||||
|
responseSummary: { type: "string" },
|
||||||
|
retrievedItemsCount: { type: "integer" },
|
||||||
|
chunkIds: { type: "array", items: { type: "string" } },
|
||||||
|
documentIds: { type: "array", items: { type: "string" } },
|
||||||
|
reviewStatus: { type: "string", enum: ["pending", "in_progress", "resolved", "ignored"] },
|
||||||
|
severity: { type: "string", enum: ["low", "medium", "high"] },
|
||||||
|
reviewedAt: { type: "string", format: "date-time" },
|
||||||
|
reviewedBy: { type: "string" },
|
||||||
|
resolutionNote: { type: "string" },
|
||||||
|
fixReference: { type: "string" },
|
||||||
|
supersedesLogId: { type: "string" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ManualLogRequest: {
|
||||||
|
type: "object",
|
||||||
|
required: ["query"],
|
||||||
|
properties: {
|
||||||
|
operation: { type: "string", enum: ["retrieve", "answer", "chat"], default: "answer" },
|
||||||
|
reason: { type: "string", default: "manual_review_requested" },
|
||||||
|
query: { type: "string" },
|
||||||
|
mode: ref("ChunkMode"),
|
||||||
|
intent: ref("RetrieveIntent"),
|
||||||
|
scope: ref("Scope"),
|
||||||
|
model: { type: "string" },
|
||||||
|
note: { type: "string" },
|
||||||
|
usedBootstrapContext: { type: "boolean" },
|
||||||
|
bootstrapMeta: ref("BootstrapMeta"),
|
||||||
|
usedAdditionalRetrieve: { type: "boolean" },
|
||||||
|
responseSummary: { type: "string" },
|
||||||
|
retrievedItems: { type: "array", items: ref("RetrievedItem") }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpdateLogRequest: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
reviewStatus: { type: "string", enum: ["pending", "in_progress", "resolved", "ignored"] },
|
||||||
|
severity: { type: "string", enum: ["low", "medium", "high"] },
|
||||||
|
reviewedBy: { type: "string" },
|
||||||
|
resolutionNote: { type: "string" },
|
||||||
|
fixReference: { type: "string" },
|
||||||
|
supersedesLogId: { type: "string" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
HealthResponse: {
|
||||||
|
type: "object",
|
||||||
|
required: ["ok", "service", "environment", "embeddings", "answer", "vectorStore", "parsers", "chunking"],
|
||||||
|
properties: {
|
||||||
|
ok: { type: "boolean", const: true },
|
||||||
|
service: { type: "string", const: "rag" },
|
||||||
|
environment: { type: "string" },
|
||||||
|
embeddings: ref("ProviderModel"),
|
||||||
|
answer: ref("ProviderModel"),
|
||||||
|
vectorStore: { type: "object", additionalProperties: true },
|
||||||
|
parsers: { type: "array", items: { type: "string" } },
|
||||||
|
chunking: { type: "object", additionalProperties: true }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ProviderModel: {
|
||||||
|
type: "object",
|
||||||
|
required: ["provider", "model"],
|
||||||
|
properties: {
|
||||||
|
provider: { type: "string" },
|
||||||
|
model: { type: "string" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
AnswerModelsResponse: {
|
||||||
|
type: "object",
|
||||||
|
required: ["defaultModel", "models"],
|
||||||
|
properties: {
|
||||||
|
defaultModel: { type: "string" },
|
||||||
|
models: { type: "array", items: { type: "string" }, maxItems: 80 }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
HelpEndpoint: {
|
||||||
|
type: "object",
|
||||||
|
required: ["method", "path", "summary", "tags"],
|
||||||
|
properties: {
|
||||||
|
method: { type: "string" },
|
||||||
|
path: { type: "string" },
|
||||||
|
summary: { type: "string" },
|
||||||
|
tags: { type: "array", items: { type: "string" } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
HelpResponse: {
|
||||||
|
type: "object",
|
||||||
|
required: ["service", "version", "description", "documentation", "authentication", "endpoints"],
|
||||||
|
properties: {
|
||||||
|
service: { type: "string" },
|
||||||
|
version: { type: "string" },
|
||||||
|
description: { type: "string" },
|
||||||
|
documentation: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
openapi: { type: "string" },
|
||||||
|
playground: { type: "string" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
authentication: { type: "string" },
|
||||||
|
endpoints: { type: "array", items: ref("HelpEndpoint") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
type OpenApiOperation = {
|
||||||
|
summary?: string;
|
||||||
|
tags?: readonly string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function buildApiHelp() {
|
||||||
|
const endpoints = Object.entries(openApiDocument.paths).flatMap(([routePath, pathItem]) =>
|
||||||
|
Object.entries(pathItem).map(([method, operation]) => {
|
||||||
|
const details = operation as OpenApiOperation;
|
||||||
|
return {
|
||||||
|
method: method.toUpperCase(),
|
||||||
|
path: routePath,
|
||||||
|
summary: details.summary ?? "",
|
||||||
|
tags: details.tags ?? []
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
service: openApiDocument.info.title,
|
||||||
|
version: openApiDocument.info.version,
|
||||||
|
description: openApiDocument.info.description,
|
||||||
|
documentation: {
|
||||||
|
openapi: "/openapi.json",
|
||||||
|
playground: "/playground"
|
||||||
|
},
|
||||||
|
authentication: "None. The current API is publicly accessible; authentication is planned separately.",
|
||||||
|
endpoints
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ import os from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import AdmZip from "adm-zip";
|
import AdmZip from "adm-zip";
|
||||||
|
import { buildApiHelp, openApiDocument } from "./api/openapi.js";
|
||||||
import { env } from "./config/env.js";
|
import { env } from "./config/env.js";
|
||||||
import { AnswerService } from "./modules/answer/service.js";
|
import { AnswerService } from "./modules/answer/service.js";
|
||||||
import { IngestService } from "./modules/ingest/service.js";
|
import { IngestService } from "./modules/ingest/service.js";
|
||||||
|
|
@ -61,6 +62,14 @@ export function createApp() {
|
||||||
app.use(express.json({ limit: "5mb" }));
|
app.use(express.json({ limit: "5mb" }));
|
||||||
app.use(express.static(publicDir));
|
app.use(express.static(publicDir));
|
||||||
|
|
||||||
|
app.get("/help", (_req, res) => {
|
||||||
|
res.json(buildApiHelp());
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/openapi.json", (_req, res) => {
|
||||||
|
res.json(openApiDocument);
|
||||||
|
});
|
||||||
|
|
||||||
app.get("/playground", (_req, res) => {
|
app.get("/playground", (_req, res) => {
|
||||||
res.sendFile(path.join(publicDir, "playground", "index.html"));
|
res.sendFile(path.join(publicDir, "playground", "index.html"));
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue