{"openapi":"3.1.0","info":{"title":"RAG Normativa — API on-premise","description":"Consulta de normativa interna 100% on-premise. Envuelve el núcleo RAG.","version":"1.0.0"},"paths":{"/login":{"post":{"summary":"Login","description":"Valida usuario/contraseña contra el IdP interno y devuelve un access token.\n\nEl navegador NO toca Keycloak: la API hace el `password grant` por la red\ninterna (Keycloak queda privado). Conveniencia de demo/primer-party; para SSO\ncompleto, el flujo OIDC redirect es preferible.\n\nRate-limit por IP (anti fuerza bruta): un atacante no puede probar miles de\ncontraseñas. Se keyea por IP para no evadir variando el usuario.","operationId":"login_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Login Login Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/health":{"get":{"summary":"Health","description":"Liveness: el proceso responde. No toca dependencias (para el orquestador).","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Health Health Get"}}}}}}},"/ready":{"get":{"summary":"Ready","description":"Readiness: hay índice con documentos. 503 si está vacío o no disponible.\n\nDistinto de /health: un proceso vivo pero con índice vacío NO está listo para\nrecibir tráfico (se abstendría de todo). El orquestador no debe rutear acá.","operationId":"ready_ready_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Ready Ready Get"}}}}}}},"/version":{"get":{"summary":"Version","description":"Identidad del build desplegado: el SHA del commit que corre AHORA.\n\nAbierto como /health y /ready (es metadato de ops, no topología sensible): el\nvalor es auditar el deploy con un `curl`, sin token ni entrar al contenedor.\n\"unknown\" si la imagen se buildeó sin inyectar RAG_GIT_SHA (build local).","operationId":"version_version_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Version Version Get"}}}}}}},"/query":{"post":{"summary":"Query","description":"Pregunta → respuesta citada (o abstención). Envuelve rag.answer_question.\n\nRequiere rol RBAC válido (admin/consultor/readonly), aplica rate-limiting por\nidentidad y deja una fila de auditoría append-only por cada consulta.","operationId":"query_query_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/query/stream":{"post":{"summary":"Query Stream","description":"Igual que /query pero PRODUCE la respuesta en streaming (mejora la latencia\npercibida: el texto aparece a medida que se genera).\n\nMismo gate, auth (RBAC), rate-limit, aislamiento multi-tenant y auditoría que\n/query. Emite eventos SSE (`text/event-stream`): `{\"delta\": \"...\"}` por cada\nfragmento de texto y un `{\"done\": true, \"abstuvo\": ..., \"fuentes\": [...]}`\nfinal. La AUDITORÍA se escribe al terminar, cuando ya se conocen el texto\ncompleto y las fuentes (coherencia con la abstención).\n\nAsync a propósito: el generador del core es SÍNCRONO (cliente Ollama sync), así\nque cada fragmento se saca en un hilo (`anyio.to_thread`) y entre fragmentos se\nchequea si el cliente se DESCONECTÓ (cerró la pestaña). Si se fue, se corta y se\nCIERRA el generador (`finally: sync_gen.close()`) → eso cierra el stream de\nOllama y libera el slot de generación al instante. Sin esto, un stream abandonado\nseguía generando al vacío y terminaba colgando al resto (bug de orphans).","operationId":"query_stream_query_stream_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/search":{"post":{"summary":"Search","description":"Búsqueda EXHAUSTIVA: lista TODAS las menciones del término en el corpus —\nsin LLM ni gate (no genera, no inventa: es listado de ocurrencias). Responde\n\"todos los artículos que mencionan X\". Auditada como acceso a datos (Art. 9).","operationId":"search_search_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/summarize":{"post":{"summary":"Summarize","description":"Resumen citado de lo que la normativa dice sobre el tema. Mismo gate\nanti-alucinación que /query (se abstiene si no hay contexto). Auditado.","operationId":"summarize_summarize_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/documents":{"get":{"summary":"Documents","description":"Documentos disponibles en el corpus del tenant (para elegir qué comparar).","operationId":"documents_documents_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Documents Documents Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/documents/detailed":{"get":{"summary":"Documents Detailed","description":"Documentos con su jurisdicción [{documento, nivel}] para agrupar en la UI.","operationId":"documents_detailed_documents_detailed_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Documents Detailed Documents Detailed Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/documents/download":{"get":{"summary":"Documents Download","description":"Descarga el TEXTO COMPLETO de una norma para analizarla fuera del sistema.\nSe reconstruye desde los chunks (el corpus guarda texto, no el archivo original),\nquitando el solapamiento. `fmt=pdf` (default) da un PDF con formato tipo documento\nnormativo; `fmt=txt`, texto plano. Acotado al scope del tenant. Auditado.","operationId":"documents_download_documents_download_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"source","in":"query","required":true,"schema":{"type":"string","title":"Source"}},{"name":"fmt","in":"query","required":false,"schema":{"type":"string","default":"pdf","title":"Fmt"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/documents/nivel":{"post":{"summary":"Documents Set Nivel","description":"Reasigna la jurisdicción (nivel) de un documento. Solo admin.","operationId":"documents_set_nivel_documents_nivel_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NivelRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Documents Set Nivel Documents Nivel Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/documents/vigencia":{"post":{"summary":"Documents Set Vigencia","description":"Reasigna el estado de vigencia (vigente/derogada/modificada) de un documento.","operationId":"documents_set_vigencia_documents_vigencia_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VigenciaRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Documents Set Vigencia Documents Vigencia Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/documents/materia":{"post":{"summary":"Documents Set Materia","description":"Reasigna la materia/área temática de un documento. Solo admin. Texto libre.","operationId":"documents_set_materia_documents_materia_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MateriaRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Documents Set Materia Documents Materia Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/documents/provincia":{"post":{"summary":"Documents Set Provincia","description":"Reasigna la provincia de un documento (provincial/municipal). Solo admin.","operationId":"documents_set_provincia_documents_provincia_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeoRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Documents Set Provincia Documents Provincia Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/documents/municipio":{"post":{"summary":"Documents Set Municipio","description":"Reasigna el municipio de un documento (solo municipal). Solo admin.","operationId":"documents_set_municipio_documents_municipio_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeoRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Documents Set Municipio Documents Municipio Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/documents/fuente":{"post":{"summary":"Documents Set Fuente","description":"Reasigna el linaje (fuente oficial + URL) de un documento. Solo admin.\n\nAuditado: cambiar de qué fuente oficial proviene un documento es un acto\nsensible para un organismo (responde \"¿de dónde sacaron esta norma?\"), así que\nqueda registrado en el log append-only (quién, qué documento, qué fuente).","operationId":"documents_set_fuente_documents_fuente_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FuenteRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Documents Set Fuente Documents Fuente Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/documents/anio":{"post":{"summary":"Documents Set Anio","description":"Reasigna el año/versión de un documento (texto libre, para identificar la\nversión de la norma de un vistazo). Solo admin y solo si es propio.","operationId":"documents_set_anio_documents_anio_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnioRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Documents Set Anio Documents Anio Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/documents/relations/extract":{"post":{"summary":"Documents Relations Extract","description":"Corre el extractor de relaciones sobre un documento PROPIO y persiste las\npropuestas (estado `propuesta` / `pendiente_sin_resolver`). Solo admin, auditado.\n\nReconstruye el texto del documento desde el corpus, extrae fórmulas legales\n(deroga/modifica) y resuelve cada referencia contra las identidades del MISMO\ntenant (estricto). Nada se aplica acá: la cascada exige confirmación humana.","operationId":"documents_relations_extract_documents_relations_extract_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RelExtractRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Documents Relations Extract Documents Relations Extract Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/me":{"get":{"summary":"Me","description":"Identidad del principal autenticado. La UI la usa para adaptar lo que muestra\npor rol (p. ej. ocultarle al consultor los botones de curar relaciones — la\nautorización REAL la hace el backend con require_role; esto es solo cosmético).","operationId":"me_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Me Me Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/feedback":{"post":{"summary":"Feedback Endpoint","description":"Registra el feedback 👍/👎 del usuario sobre una respuesta. Señal de calidad y\ndataset para mejorar. Append-only por tenant, no re-consulta el LLM.","operationId":"feedback_endpoint_feedback_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Feedback Endpoint Feedback Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/novedades/feed":{"get":{"summary":"Novedades","description":"Feed de novedades para estar al día: (1) cambios normativos CONFIRMADOS\n(derogaciones/modificaciones) con su fecha, y (2) las normas del corpus que hoy\nestán NO vigentes. Todo acotado al scope del tenant.","operationId":"novedades_novedades_feed_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"limite","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Limite"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Novedades Novedades Feed Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/documents/relations":{"get":{"summary":"Documents Relations","description":"Lista las relaciones del tenant del principal (filtrable por `estado`).\nEstricto por tenant: un organismo no ve las relaciones de otro.","operationId":"documents_relations_documents_relations_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"estado","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Estado"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Documents Relations Documents Relations Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/documents/relations/apply":{"post":{"summary":"Documents Relations Apply","description":"Confirma relación(es) y dispara la cascada de vigencia sobre el destino. Solo\nadmin (un no-admin recibe 403 vía el Depends). Invalida el cache del tenant y\naudita. La cascada en sí está acotada al tenant del principal (aislamiento).","operationId":"documents_relations_apply_documents_relations_apply_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RelApplyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Documents Relations Apply Documents Relations Apply Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/documents/relations/reject":{"post":{"summary":"Documents Relations Reject","description":"Descarta relación(es) propuesta(s) marcándolas `rechazada` (el curador desestima\nla propuesta del extractor). Solo admin (no-admin → 403 vía el Depends). NO toca la\nvigencia ni el cache: rechazar no aplica cascada. Aislamiento por tenant SAGRADO —\nuna relación de otro tenant se saltea (no se toca), igual que apply. Auditado.","operationId":"documents_relations_reject_documents_relations_reject_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RelRejectRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Documents Relations Reject Documents Relations Reject Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/documents/upload":{"post":{"summary":"Documents Upload","description":"Sube e INDEXA un documento (PDF/Word/Excel/HTML/TXT) desde la UI. Solo admin.\n\nReusa el pipeline de ingesta (extraer → chunk → embed → store + corpus). El\narchivo NO se persiste: se procesa en un temporal y se descarta; lo que queda es\nel corpus (chunks) + los embeddings. Invalida el cache.","operationId":"documents_upload_documents_upload_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_documents_upload_documents_upload_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Documents Upload Documents Upload Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/documents/delete":{"post":{"summary":"Documents Delete","description":"Borra un documento del índice y del corpus. Solo admin y solo si es propio\n(la base es read-only para los organismos).","operationId":"documents_delete_documents_delete_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteDocRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Documents Delete Documents Delete Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/compare":{"post":{"summary":"Compare","description":"Diff DETERMINÍSTICO artículo por artículo entre dos documentos (sin LLM →\nsin alucinación). Para \"¿qué cambió entre la versión X y la Y?\". Auditado.","operationId":"compare_compare_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompareRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Compare Compare Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/metrics":{"get":{"summary":"Metrics","description":"Métricas Prometheus (scrapeable por Grafana/Prometheus on-prem).\n\nFail-closed: si NO hay `RAG_METRICS_TOKEN`, el endpoint queda CERRADO (403)\nsalvo opt-in explícito `RAG_METRICS_PUBLIC=1` (dev / detrás de firewall interno).\nCon token, exige `Authorization: Bearer <token>`. Evita exponer topología y\nvolúmenes de consulta sin querer (antes quedaba abierto por default).","operationId":"metrics_metrics_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/audit/tail":{"get":{"summary":"Audit Tail","description":"Últimas `n` entradas del audit log. Solo rol admin (demuestra el RBAC).","operationId":"audit_tail_audit_tail_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"n","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"N"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Audit Tail Audit Tail Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/audit/verify":{"get":{"summary":"Audit Verify","description":"Verifica la INTEGRIDAD del audit log (cadena de hashes). Solo admin.\n\nDemuestra tamper-evidence (Art. 9): si alguien alteró o borró una entrada\npasada, la cadena se rompe y `ok` es false con la línea afectada en `rota_en`.","operationId":"audit_verify_audit_verify_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Audit Verify Audit Verify Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/confiabilidad":{"get":{"summary":"Confiabilidad","description":"Prueba de Control de Calidad EN VIVO (reporte de confiabilidad). Admin y consultor\n(el consultor lo usa para mostrarle la calidad al organismo en una demo).\n\nCompone —barato, sin LLM— la postura de defensa activa (gate, guarda numérica,\nreranker, verificador, cita obligatoria), los últimos resultados de QA, la integridad\nde la cadena de auditoría y la cobertura del corpus. `formato=md` (default) descarga el\nMarkdown para compartir con el organismo; `formato=json` devuelve los datos crudos.","operationId":"confiabilidad_confiabilidad_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"org","in":"query","required":false,"schema":{"type":"string","default":"","title":"Org"}},{"name":"formato","in":"query","required":false,"schema":{"type":"string","default":"md","title":"Formato"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/stats":{"get":{"summary":"Stats","description":"Métricas de uso agregadas desde el audit log (para el dashboard). Solo admin.","operationId":"stats_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Stats Stats Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/cache":{"get":{"summary":"Cache Stats","description":"Estado del cache de respuestas (hits/misses/entradas). Solo admin.","operationId":"cache_stats_cache_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Cache Stats Cache Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/cache/clear":{"post":{"summary":"Cache Clear","description":"Vacía el cache de respuestas (usar tras re-ingestar documentos). Solo admin.","operationId":"cache_clear_cache_clear_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Cache Clear Cache Clear Post"}}}}},"security":[{"HTTPBearer":[]}]}},"/export":{"post":{"summary":"Export Docx","description":"Exporta la consulta + respuesta + fuentes citadas a un .docx (para el expediente).\n\nEl cliente envía la respuesta ya generada (no se re-consulta el LLM): el server\nsólo la formatea. Word es editable y universal; sin dependencia nueva (python-docx\nya está). Queda registrado en auditoría.","operationId":"export_docx_export_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}}},"components":{"schemas":{"AnioRequest":{"properties":{"documento":{"type":"string","maxLength":300,"minLength":1,"title":"Documento"},"anio":{"type":"string","maxLength":40,"title":"Anio","default":""}},"type":"object","required":["documento"],"title":"AnioRequest"},"Body_documents_upload_documents_upload_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"},"nivel":{"type":"string","title":"Nivel","default":"otro"},"vigencia":{"type":"string","title":"Vigencia","default":"vigente"},"materia":{"type":"string","title":"Materia","default":"general"},"provincia":{"type":"string","title":"Provincia","default":""},"municipio":{"type":"string","title":"Municipio","default":""},"fuente":{"type":"string","title":"Fuente","default":""},"fuente_url":{"type":"string","title":"Fuente Url","default":""},"anio":{"type":"string","title":"Anio","default":""},"tipo_norma":{"type":"string","title":"Tipo Norma","default":""},"numero":{"type":"string","title":"Numero","default":""},"visibilidad":{"type":"string","title":"Visibilidad","default":"privado"}},"type":"object","required":["file"],"title":"Body_documents_upload_documents_upload_post"},"CompareRequest":{"properties":{"documento_a":{"type":"string","minLength":1,"title":"Documento A"},"documento_b":{"type":"string","minLength":1,"title":"Documento B"}},"type":"object","required":["documento_a","documento_b"],"title":"CompareRequest"},"DeleteDocRequest":{"properties":{"documento":{"type":"string","maxLength":300,"minLength":1,"title":"Documento"}},"type":"object","required":["documento"],"title":"DeleteDocRequest"},"ExportRequest":{"properties":{"pregunta":{"type":"string","maxLength":4000,"minLength":1,"title":"Pregunta"},"respuesta":{"type":"string","maxLength":40000,"minLength":1,"title":"Respuesta"},"fuentes":{"items":{"$ref":"#/components/schemas/Fuente"},"type":"array","title":"Fuentes"}},"type":"object","required":["pregunta","respuesta"],"title":"ExportRequest"},"FeedbackRequest":{"properties":{"voto":{"type":"string","pattern":"^(up|down)$","title":"Voto","description":"👍 up o 👎 down."},"pregunta":{"type":"string","maxLength":4000,"minLength":1,"title":"Pregunta"},"respuesta":{"type":"string","maxLength":20000,"title":"Respuesta","default":""},"fuentes":{"items":{"type":"string"},"type":"array","maxItems":40,"title":"Fuentes"},"comentario":{"type":"string","maxLength":1000,"title":"Comentario","default":""}},"type":"object","required":["voto","pregunta"],"title":"FeedbackRequest"},"Fuente":{"properties":{"documento":{"type":"string","title":"Documento"},"pagina":{"type":"integer","title":"Pagina"},"texto":{"type":"string","title":"Texto"},"vigencia":{"type":"string","title":"Vigencia","default":"vigente"}},"type":"object","required":["documento","pagina","texto"],"title":"Fuente"},"FuenteRequest":{"properties":{"documento":{"type":"string","maxLength":300,"minLength":1,"title":"Documento"},"fuente":{"type":"string","maxLength":40,"title":"Fuente","default":""},"fuente_url":{"type":"string","maxLength":500,"title":"Fuente Url","default":""}},"type":"object","required":["documento"],"title":"FuenteRequest"},"GeoRequest":{"properties":{"documento":{"type":"string","maxLength":300,"minLength":1,"title":"Documento"},"valor":{"type":"string","maxLength":60,"title":"Valor"}},"type":"object","required":["documento","valor"],"title":"GeoRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"LoginRequest":{"properties":{"usuario":{"type":"string","minLength":1,"title":"Usuario"},"password":{"type":"string","minLength":1,"title":"Password"}},"type":"object","required":["usuario","password"],"title":"LoginRequest"},"MateriaRequest":{"properties":{"documento":{"type":"string","maxLength":300,"minLength":1,"title":"Documento"},"materia":{"type":"string","maxLength":60,"minLength":1,"title":"Materia"}},"type":"object","required":["documento","materia"],"title":"MateriaRequest"},"NivelRequest":{"properties":{"documento":{"type":"string","maxLength":300,"minLength":1,"title":"Documento"},"nivel":{"type":"string","maxLength":20,"minLength":1,"title":"Nivel"}},"type":"object","required":["documento","nivel"],"title":"NivelRequest"},"QueryRequest":{"properties":{"pregunta":{"type":"string","maxLength":4000,"minLength":1,"title":"Pregunta","description":"Consulta en lenguaje natural."},"historial":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":20},{"type":"null"}],"title":"Historial","description":"Turnos previos (modo conversacional) para resolver repreguntas."},"fuente":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}],"title":"Fuente","description":"Opcional: acota la consulta a un único documento (su 'source')."},"materia":{"anyOf":[{"type":"string","maxLength":60},{"type":"null"}],"title":"Materia","description":"Opcional: acota la consulta a un área temática (p. ej. 'ambiental')."},"excluir_derogadas":{"type":"boolean","title":"Excluir Derogadas","description":"Opcional: si es true, excluye del retrieval la normativa DEROGADA (solo considera vigente/modificada). Útil para 'qué rige hoy'.","default":false}},"type":"object","required":["pregunta"],"title":"QueryRequest"},"QueryResponse":{"properties":{"respuesta":{"type":"string","title":"Respuesta"},"abstuvo":{"type":"boolean","title":"Abstuvo"},"fuentes":{"items":{"$ref":"#/components/schemas/Fuente"},"type":"array","title":"Fuentes"}},"type":"object","required":["respuesta","abstuvo","fuentes"],"title":"QueryResponse"},"RelApplyRequest":{"properties":{"rel_id":{"anyOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"}],"minLength":1,"title":"Rel Id"}},"type":"object","required":["rel_id"],"title":"RelApplyRequest"},"RelExtractRequest":{"properties":{"source":{"type":"string","maxLength":300,"minLength":1,"title":"Source"}},"type":"object","required":["source"],"title":"RelExtractRequest"},"RelRejectRequest":{"properties":{"rel_id":{"anyOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"}],"minLength":1,"title":"Rel Id"}},"type":"object","required":["rel_id"],"title":"RelRejectRequest"},"SearchHit":{"properties":{"documento":{"type":"string","title":"Documento"},"pagina":{"type":"integer","title":"Pagina"},"texto":{"type":"string","title":"Texto"}},"type":"object","required":["documento","pagina","texto"],"title":"SearchHit"},"SearchRequest":{"properties":{"consulta":{"type":"string","maxLength":4000,"minLength":1,"title":"Consulta","description":"Término o frase a buscar."},"limite":{"type":"integer","maximum":200.0,"minimum":1.0,"title":"Limite","description":"Máximo de resultados.","default":50}},"type":"object","required":["consulta"],"title":"SearchRequest"},"SearchResponse":{"properties":{"total":{"type":"integer","title":"Total"},"por_documento":{"additionalProperties":{"type":"integer"},"type":"object","title":"Por Documento"},"por_articulo":{"additionalProperties":{"type":"integer"},"type":"object","title":"Por Articulo"},"nota":{"type":"string","title":"Nota","default":""},"resultados":{"items":{"$ref":"#/components/schemas/SearchHit"},"type":"array","title":"Resultados"}},"type":"object","required":["total","por_documento","por_articulo","resultados"],"title":"SearchResponse"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VigenciaRequest":{"properties":{"documento":{"type":"string","maxLength":300,"minLength":1,"title":"Documento"},"vigencia":{"type":"string","maxLength":20,"minLength":1,"title":"Vigencia"}},"type":"object","required":["documento","vigencia"],"title":"VigenciaRequest"}},"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}}}}