La superficie de consultas (OSL-SQL)
POST /osl/query es el único endpoint de consumo. La analítica estructurada,
las métricas, el retrieval vectorial gobernado y la travesía de grafos se expresan
todos aquí — no aprendes cinco superficies para leer un dominio.
POST /osl/queryAuthorization: Bearer <jwt> # attested delegation chain, see AuthenticationContent-Type: application/jsonEl body es una de tres formas mutuamente excluyentes (mezclarlas es 400 E0513);
las tres pasan por la misma costura gobernada y comparten un mismo request_id:
| Body | Qué es |
|---|---|
{ "osql": "…" } |
OSL-SQL — el dialecto de SQL semántico (la superficie canónica) |
{ "sql": "…" } |
Un SELECT crudo de solo lectura sobre el allowlist del tenant (el slice de SQL) |
{ "metrics": […], "group_by": […], … } |
Consulta de métricas al estilo MetricFlow |
OSL-SQL
Sección titulada «OSL-SQL»osql es un dialecto de SQL semántico. Nombra solo objetos semánticos —
osl.entities.<model> y columnas declaradas, o las funciones de tabla
RETRIEVE(...) / TRAVERSE(...). Nunca nombra una tabla física ni una columna
cruda.
El lowering es la frontera de seguridad
Sección titulada «El lowering es la frontera de seguridad»El motor parsea osql (vía sqlglot, dialecto trino) y lo reduce a un IR de
operadores cerrado — la escalera determinista sobre la que aplica el
modelo de gobernanza. El lowering es total y
default-deny: cualquier construcción que no reduzca a un nodo IR conocido se
rechaza.
Las cinco formas
Sección titulada «Las cinco formas»lower() clasifica cada consulta en exactamente una forma y valida su gramática:
SELECT segment, COUNT(*) AS nFROM osl.entities.CustomerWHERE country IN ('ES','FR','DE')GROUP BY segmentORDER BY n DESCLIMIT 100Reduce a to_trino() → la costura de SQL gobernada. Los joins entre entidades
estructuradas solo se permiten como un equijoin de clave sellada (ver abajo).
SELECT SUMMARIZE(text) AS summary, scoreFROM RETRIEVE(facet => 'meeting_transcripts', query => 'european equity exposure', top_k => 5)Reduce al store Lance gobernado + retrieval. Una columna desnuda pasa tal cual; un
<op>(col) en el SELECT es un operador de proyección.
SELECT concepto, amount_eurFROM TRAVERSE(anchor => 'customer', target => 'orders', keys => ('c-anna', 'c-bruno'))Reduce a la travesía L2 gobernada desde las claves ancla (salto a salto, cardinalidad acotada + dedup).
SELECT name, segment, recent_meeting_topicsFROM osl.entities.CustomerWHERE customer_id IN ('9c1e…f04a', 'e4f8…2a91')La forma “registro” orientada a entidad: columnas estructuradas + evidencia Lance
- atributos derivados para las claves dadas, en una sola resolución.
SELECT x.segment, r.text, r.scoreFROM osl.entities.Customer xJOIN RETRIEVE(facet => 'meeting_transcripts', query => 'churn risk') r ON x.customer_id = r.customer_id“La relación es un filtro”: RETRIEVE corre primero, sus claves prefiltran la pierna estructural, luego un hash join en la celda sobre la clave sellada. Ver abajo.
R3 es inexpresable, no bloqueada
Sección titulada «R3 es inexpresable, no bloqueada»El único nodo cross-relacional es un equijoin cuyo ON es una igualdad escalar
sobre una clave de entidad sellada (las entity_key_columns del modelo, o el
campo scalar_indexed de una faceta) en ambas piernas. Cualquier otro
JOIN … ON → 400 E0514.
No hay sintaxis que pase un segundo argumento relacional a un operador de similitud o de LLM — un join semántico relación×relación en query time (el no-objetivo R3) es una propiedad estructural de la gramática, no una blocklist que mantener.
El join cross-modal, ejecutado
Sección titulada «El join cross-modal, ejecutado»X JOIN RETRIEVE(...) ON X.k = R.k no es un join relacional de dos tablas
físicas. Corre así:
- RETRIEVE (gobernado) → passages, cada uno con su clave desnormalizada.
- Compuerta en claro → los valores de la clave, o rechazo (
403 E0515). - Prefiltro estructurado:
… WHERE k IN (<keys>)→to_trino→ SQL gobernado (la listaINse construye con conversión de literales de sqlglot — a prueba de inyección). - Hash join en la celda sobre la clave sellada, proyectando las columnas de cada pierna.
Operadores de proyección
Sección titulada «Operadores de proyección»En la forma RETRIEVE, un <op>(col) en el SELECT es un operador de proyección
(RFC 0004). Son mapas unarios — nunca un join.
| Clase | Operadores | Se ejecuta en | Si falta |
|---|---|---|---|
| Encoder (determinista) | SIMILARITY, CLASSIFY, TAG, RANK, DEDUP, OUTLIER, CLUSTER |
Sobre los embeddings ya presentes en el chunk | — |
| Decoder (inferencia) necesita LLM | EXTRACT, SUMMARIZE, SCORE, GENERATE, COMPLETE |
Un backend LLM | 503 E0521 |
Bodies de SQL crudo y MetricFlow
Sección titulada «Bodies de SQL crudo y MetricFlow»{ "sql": "<SELECT>" } es un SELECT de solo lectura validado contra el allowlist
del tenant con el punto de aplicación de política aplicado (filtros de filas +
proyección del snapshot compuesto). { "metrics": …, "group_by": … } sigue la
semántica de MetricFlow. Ambos comparten la misma costura gobernada que osql.
Códigos de estado
Sección titulada «Códigos de estado»| Código | Significado |
|---|---|
200 |
Éxito |
400 |
Malformada / fuera de gramática (E0511), objeto no declarado (E0512), el body mezcla formas (E0513), JOIN sin clave sellada (E0514), sql vacío (E0246) |
403 |
Deny del PDP (E2101/E2102), clave cross-modal no en claro (E0515), objeto fuera del allowlist (E0512) |
422 |
Válida pero no resoluble — p. ej. MetricFlow aún no implementado (E1001) |
503 |
Falta el snapshot del PDP (E2001, fail-closed), op decoder sin backend LLM (E0521) |
502 |
Fallo de ejecución de Trino (E0510) |
Ver Errores y códigos de estado para el envelope completo.