API AI Query — cubes OLAP
L’API AI Query permet à un agent de requêter un cube OLAP Mondrian sans jamais voir ni écrire de MDX. L’agent récupère un schéma auto-descriptif, remplit une requête JSON à partir de celui-ci, le serveur valide chaque nom, exécute et retourne des records typés. Si un nom est incorrect, l’erreur dit à l’agent exactement quoi corriger — aucun prompt engineering requis.
C’est le pendant OLAP de l’
API AI Query pour les modèles Ossie (la surface
SQL/YAML-sémantique). Même discipline, mêmes garde-fous ; celle-ci travaille
contre des cubes Mondrian au lieu de YAML Ossie. Assise par-dessus, la couche
en langage naturel /ai/ask — l’endpoint ask traduit une
question en anglais simple en exactement le corps de requête documenté ici.
Orientation rapide
Trois endpoints couvrent ~90 % de l’usage des agents :
| Endpoint | Objectif |
|---|---|
GET /rest/saiku/api/ai/cubes | Liste chaque cube que l’appelant peut requêter. |
GET /rest/saiku/api/ai/schema/{cubeId} | Schéma auto-descriptif — mesures, dimensions, hiérarchies, niveaux, membres d’échantillon, synonymes, et le JSON Schema de la requête. |
POST /rest/saiku/api/ai/query | Exécute une requête typée. Réponse au format records par défaut ; matrix avec ?format=matrix. |
Endpoints de longue traîne (documentés sur les pages liées) :
| Endpoint | Objectif |
|---|---|
GET /rest/saiku/api/ai/members/search | Recherche par sous-chaîne de membres sur un niveau. |
POST /rest/saiku/api/ai/scenario/whatif | Simulation what-if en write-back. |
POST /rest/saiku/api/ai/query/execute-async | Soumet pour exécution en arrière-plan (interroge status / result). |
POST /rest/saiku/api/ai/anomaly | Exécute la requête puis signale les anomalies le long d’un axe temporel. |
POST /rest/saiku/api/ai/forecast | Projette des points futurs (ETS / ARIMA / Prophet). |
POST /rest/saiku/api/ai/ask | Question en langage naturel. Nécessite une clé LLM. |
Le cubeId partout est le quadruplet connection/catalog/schema/cubeName
joint par /. Tous les endpoints nécessitent une session authentifiée ; les
endpoints POST nécessitent la paire cookie/en-tête CSRF.
Étape 1 — lister les cubes
GET /rest/saiku/api/ai/cubes[ { "connectionName": "unknown_foodmart", "catalog": "FoodMart", "schema": "FoodMart", "cubeName": "Sales", "cubeCaption": "Sales", "defaultMeasure": "Unit Sales", "measureCount": 8 }]Le quadruplet connectionName/catalog/schema/cubeName est
l’identifiant de cube utilisé partout ailleurs.
Étape 2 — récupérer le schéma typé
GET /rest/saiku/api/ai/schema/unknown_foodmart/FoodMart/FoodMart/SalesN’encodez pas les slashes en URL — le template de chemin accepte directement
la forme multi-segment connection/catalog/schema/cubeName. La réponse est
dense — c’est ce qui rend l’API auto-descriptive :
{ "cubeId": "unknown_foodmart/FoodMart/FoodMart/Sales", "measures": { "store sales": { "name": "Store Sales", "uniqueName": "[Measures].[Store Sales]", "description": "Net retail revenue in USD across all transactions.", "synonyms": ["revenue", "turnover", "top-line"], // accepted as `name` on input "unit": "USD", "aggregationKind": "sum" } // …8 measures total… }, "measureAliases": { "revenue": "store sales", "turnover": "store sales" }, "dimensions": { "time": { "name": "Time", "uniqueName": "[Time]", "hierarchies": { "time by": { "name": "Time By", "levels": { "quarter": { "name": "Quarter", "synonyms": ["quarterly", "qtr"], // accepted as `level` on input "sampleMembers": [ { "caption": "Q1", "uniqueName": "[Time].[Time By].[Quarter].&[Q1]" } ] } } } } } }}Les synonymes et alias de nom d’affichage sont acceptés en entrée partout où
le name canonique l’est — un agent peut dire "revenue" et le serveur le
résout en Store Sales.
Étape 3 — exécuter une requête
« Montre Store Sales et Unit Sales par Product Family, top 3 par Store Sales. »
POST /rest/saiku/api/ai/queryContent-Type: application/json{ "cube": "unknown_foodmart/FoodMart/FoodMart/Sales", "measures": [{ "name": "Store Sales" }, { "name": "Unit Sales" }], "rows": [{ "dimension": "Product", "hierarchy": "Products", "level": "Product Family" }], "order": [{ "by": "Store Sales", "direction": "desc" }], "limit": 3}cube accepte soit la forme objet à 4 segments, soit la chaîne compacte
"connection/catalog/schema/cube".
Réponse (200) :
{ "status": "SUCCESS", "format": "records", "metadata": { "generatedMdx": "SELECT NON EMPTY {[Measures].[Store Sales], [Measures].[Unit Sales]} ON COLUMNS, NON EMPTY TopCount([Product].[Products].[Product Family].Members, 3, [Measures].[Store Sales]) ON ROWS FROM [Sales]", "freshness": { "computedAtMillis": 1715798421042, "cached": false } }, "data": [ { "Product Family": "Food", "Store Sales": { "value": 409035.59, "formatted": "409,035.59", "unit": null }, "Unit Sales": { "value": 191940.0, "formatted": "191,940", "unit": null } } ], "totalRows": 3, "runtimeMs": 421}Chaque ligne est un objet auto-descriptif clé par la légende de colonne lisible par l’humain. Chaque cellule numérique est une enveloppe typée :
value— nombre analysé (pour les maths / le tri / le graphique)formatted— la chaîne d’affichage pré-formatée de Mondrian (pour l’UI)unit— reniflée depuis la chaîne formatée (USD,GBP,EUR,JPY,%) ounull
generatedMdx est renvoyé en écho pour le débogage ; les agents l’ignorent
généralement.
Format matrix
Les clients indexés par position se désengagent des records avec
?format=matrix — la réponse porte matrix au lieu de data, chaque ligne
clée par l’index de colonne sous forme de chaîne, les cellules restant
l’enveloppe typée {value, formatted, unit}.
Confidentialité : k-anonymat
Quand ai.kAnonymity est défini (défaut 5 ; 0 désactive), le serveur
masque les valeurs de mesure des petites cellules avant que le résultat ne
franchisse la frontière AI — toute ligne dont la mesure de comptage dans le
résultat tombe sous k voit ses cellules de mesure masquées avec
suppressed: true. S’applique aux records et matrix, et aux endpoints dérivés
/ai/anomaly + /ai/forecast.
Étape 4 — la validation enseigne à l’agent
Fournissez un nom qui ne se résout pas et le serveur retourne 400 avec un corps à partir duquel l’agent peut s’auto-corriger — aucun prompting de réessai nécessaire :
{ "status": "VALIDATION_ERROR", "error": "Unknown measure 'Made Up Measure'", "field": "measures[].name", "available": ["Unit Sales", "Store Cost", "Store Sales", "Profit", "Customer Count"]}L’agent lit field (ce qui était incorrect), lit available[] (les valeurs
légales), corrige et réessaie. Deux couches s’exécutent : un validateur de
forme (JSON Schema — champs manquants, types incorrects, violations d’enum,
avec des chemins de champ indexés par tableau comme filters[0].op) et un
validateur sémantique (résolution de cube — noms valides en forme mais qui
n’existent pas). Le contrat est identique dans les deux cas : lire field, lire
available[], corriger, réessayer.
Où aller ensuite
- API AI Ask — la couche en langage naturel qui produit le
corps de requête ci-dessus, plus les endpoints compagnons
members/search,scenario/whatifetpii-suggestions. - Agent Spaces — des personas qui limitent cette surface à un ensemble de cubes en liste blanche.
- Agent Skills — des workflows créés par l’admin, découvrables depuis chaque ask.
- Serveur MCP — la même surface typée pour les hôtes d’agents
externes (
list_cubes,describe_cube,run_query, …). - API AI Query pour les modèles Ossie — le pendant SQL/YAML-sémantique.
- API d’inférence AI — une surface différente : le designer de cube au moment de la conception (profile → propose → render), pas l’exécution de requête.