AI Inference API
Die AI Inference API treibt den Schema-Designer im Dashboard an. Sie stellt denselben dreistufigen Flow — profile, propose, render — als Endpunkte zur Verfügung, die Sie aus Ihrem eigenen Code aufrufen können. Nützlich, wenn Sie die Cube-Generierung über viele Warehouses hinweg automatisieren, die Cube-Erstellung in Ihr eigenes Produkt einbetten oder Saiku Cloud in einen vorgelagerten Onboarding-Flow integrieren möchten.
Alle Endpunkte erfordern einen Bearer-API-Key. Siehe Authentifizierung für die Grundlagen.
Der Flow
Der Schema-Designer im Dashboard ist die kanonische Referenzimplementierung:
- Profile Sie eine Verbindung oder eine hochgeladene Datei. Wir
sampeln ihre Struktur kostengünstig und geben ein
SchemaProfilezurück. - Propose Sie einen Cube. Wir senden das Profil + eine optionale
einfach-englische Absicht an Claude und geben einen strukturierten
CubeProposalzurück. - Render Sie den Vorschlag als Mondrian-Schema-XML. Das Ergebnis ist ein String, der bereit ist, als Schema in Ihrem Workspace gespeichert zu werden.
Sie können die Schritte unabhängig aufrufen — einmal profilen und
mehrmals mit unterschiedlichen Absichten vorschlagen oder den
Propose-Schritt überspringen und einen CubeProposal von Hand für
den Renderer bauen.
POST /me/inference/profile/connection/{id}
Profilieren Sie eine gespeicherte Warehouse-Verbindung. Liest
information_schema, sampelt einige Zeilen pro Spalte und gibt ein
strukturiertes Profil der Tabellen und Spalten des Warehouses zurück.
Pfadparameter:
id— die Connection-ID ausGET /me/connections.
Optionale Query-Parameter:
schema— auf ein bestimmtes Datenbankschema beschränken (z. B.public,analytics). Standard ist das Schema, gegen das die Verbindung gespeichert wurde.maxTables— Anzahl der profilierten Tabellen begrenzen. Standard 20.tableTypes— kommaseparierte Liste (TABLE,VIEW,MATERIALIZED VIEW). Standard nurTABLE.
Antwort (200):
{ "databaseProductName": "PostgreSQL", "databaseProductVersion": "16.6", "tables": [ { "schema": "public", "name": "fact_sales", "rowCount": 1245678, "columns": [ { "name": "sale_id", "type": "BIGINT", "nullable": false, "distinctApprox": 1245678, "sampleValues": [1, 2, 3, 4, 5] }, { "name": "customer_id", "type": "BIGINT", "nullable": false, "distinctApprox": 25000, "sampleValues": [101, 102, 103, 104, 105] } ] } ], "sampledAt": "2026-05-23T18:00:00Z", "sampleDurationMillis": 1832, "sampleCostUsd": 0.001}Kosten: typischerweise unter $0,05 pro Profil gegen ein Postgres-Warehouse. Die Kosten entfallen hauptsächlich auf Metadaten-Abfragen, nicht auf Daten-Scans.
Fehlermodi:
404 not_found— Connection-ID existiert nicht oder ist für Ihren Tenant nicht sichtbar.502 warehouse_unreachable— wir konnten das Warehouse nicht erreichen (Anmeldedaten falsch, Host down, Netzwerkproblem).
GET /me/inference/profile/connection/{id}/sample
Holen Sie einige Sample-Zeilen aus einer Tabelle. Gleiche Auth- und Erreichbarkeitsform wie der Profile-Endpunkt, engerer Scope.
Pfad- + Query-Parameter:
id— Connection-ID.table— Tabellenname (erforderlich).schema— Datenbankschema (Standard ist der Default der Verbindung).rows— Anzahl der Zeilen (Standard 5, Max. 50).
Antwort (200):
{ "schema": "public", "table": "fact_sales", "columns": ["sale_id", "customer_id", "amount", "sale_date"], "rows": [ [1, 101, "29.99", "2024-01-15"], [2, 102, "149.00", "2024-01-15"] ]}Nützlich, um dem Benutzer zu zeigen, wie seine Daten tatsächlich aussehen, bevor er sich auf ein Cube-Design festlegt.
POST /me/inference/profile/file
Profilieren Sie eine hochgeladene Datei anstelle einer Warehouse-Tabelle. Gleiche Form wie der Connection-Profiler, mit der Datei-ID anstelle der Connection-ID.
Multipart-Body:
file— der Datei-Teil..parquet,.csvoder.json.tableTypes— StandardTABLE(gleiche Form wie der Connection-Profiler, lässt zukünftige Erweiterung zu).
Antwort (200): gleiche SchemaProfile-Form wie der
Connection-Profiler. Dateien erscheinen als einzelner
tables[0]-Eintrag.
DuckDBs httpfs liest die Datei spaltenweise, sodass eine
Multi-GB-Parquet-Datei in Sekunden profiliert wird, ohne dass wir
das Ganze in den Speicher ziehen.
POST /me/inference/propose
Senden Sie ein Profil an Claude und erhalten Sie einen Cube-Vorschlag zurück.
Request-Body:
{ "profile": { /* SchemaProfile aus dem Profile-Schritt */ }, "intent": "Sales facts joined to customer and product dimensions, sum of revenue, count of orders.", "factTable": { "schema": "public", "name": "fact_sales" }}profile(erforderlich) — das JSON, das einer der Profile-Endpunkte zurückgegeben hat.intent(optional, aber dringend empfohlen) — einzeilige einfach-englische Beschreibung dessen, was Sie wollen. Ohne sie hat Claude viel weniger zu tun.factTable(optional) — eine bestimmte Faktentabelle festlegen. Ohne sie wählt Claude eine basierend auf Namens-Heuristiken und Spalten-Formen aus.
Antwort (200):
{ "traceId": "ac2ab729-7fc3-4c3a-8e36-7cd77be87267", "proposal": { "schemaName": "Sales Analytics", "cubes": [ { "name": "Sales", "factTableSchema": "public", "factTableName": "fact_sales", "measures": [ { "name": "Revenue", "column": "amount", "aggregator": "sum" }, { "name": "Orders", "column": "sale_id", "aggregator": "count" } ], "dimensions": [ { "name": "Customer", "foreignKey": "customer_id", "tableSchema": "public", "tableName": "dim_customer", "primaryKey": "customer_id", "levels": [ { "name": "Name", "column": "customer_name", "type": "String", "uniqueMembers": false } ] } ] } ] }, "mondrianXml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Schema name=\"Sales Analytics\">\n <Cube name=\"Sales\">...</Cube>\n</Schema>", "validation": { "valid": true, "cubeCount": 1, "warnings": [] }, "summary": { /* verdichteter Vorschlag — Measures + Dim-Namen + Join-Graph */ }, "detectorFindings": { /* heuristische Einsichten zu Datumshierarchien usw. */ }, "usage": { "model": "claude-sonnet-4-6", "inputTokens": 4321, "outputTokens": 892, "totalTokens": 5213, "estimatedCostUsd": 0.018, "isPricingExact": true }, "quota": { /* Snapshot des monatlichen LLM-Budgets */ }}Wichtig zu beachten: Die Antwort enthält für den Happy Path bereits
die gerenderte mondrianXml. Sie müssen /render nicht separat
aufrufen, es sei denn, Sie haben den Vorschlag zuvor bearbeitet.
Die traceId erlaubt es Ihnen, die vollständige LLM-Konversation
später über GET /me/inference/trace/{traceId}
abzurufen — nützlich für Auditing oder zum Debuggen unerwarteter
Vorschläge.
Fehlermodi:
400 invalid_proposal— Claude hat einen strukturell ungültigen Vorschlag zurückgegeben, den unser Validator abgelehnt hat. Die Trace-ID wird trotzdem zurückgegeben.429 over_budget— Ihr Tenant hat sein monatliches LLM-Budget überschritten. Kommt mit einemRetry-After-Header und einemreset_at-Feld, das angibt, wann das Budget zurückgesetzt wird.502 upstream_error— Claude hat einen Netzwerkfehler zurückgegeben oder die Anfrage abgelehnt.
POST /me/inference/render
Konvertieren Sie einen SchemaProposal in Mondrian-Schema-XML.
Request-Body — das Proposal-JSON direkt (nicht in
{proposal: …} eingewickelt). Verwenden Sie das proposal-Feld aus
einer vorherigen /propose-Antwort mit allen lokalen Änderungen:
{ "schemaName": "Sales Analytics", "cubes": [ { "name": "Sales", "factTableSchema": "public", "factTableName": "fact_sales", "measures": [ /* … */ ], "dimensions": [ /* … */ ] } ]}Antwort (200):
{ "mondrianXml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Schema name=\"Sales Analytics\">\n <Cube name=\"Sales\">...</Cube>\n</Schema>", "validation": { "valid": true, "cubeCount": 1, "warnings": [] }, "summary": { /* verdichtete Ansicht — Measures + Dim-Namen + Join-Graph */ }}Reine Transformation — kein LLM-Aufruf, keine Warehouse-Interaktion. Kostenlos.
Sie können Render mehrmals auf demselben Vorschlag aufrufen, während Sie ihn bearbeiten — genau das tut der Schema-Designer in der Bearbeitungsschleife des Dashboards.
Fehlermodi:
400 invalid_proposal— der Vorschlag erfüllt nicht die Mondrian-XML-Bedingungen (fehlendes Pflichtfeld, widersprüchliche Joins usw.). Antwort-Body:{ error, kind, message }.400 empty_proposal— Request-Body war leer.
POST /me/inference/try-query
Führen Sie eine MDX-Beispielabfrage gegen einen Entwurfs-Cube aus, bevor Sie ihn speichern. Nützlich, um zu bestätigen, dass die Joins dort landen, wo Sie es erwarten.
Request-Body:
{ "proposal": { /* CubeProposal */ }, "connectionId": "uuid-of-saved-connection", "mdx": "SELECT { [Measures].[Revenue] } ON COLUMNS, { [Customer].[Name].MEMBERS } ON ROWS FROM [Sales]"}Antwort (200):
{ "columns": ["Customer", "Revenue"], "rows": [ ["Acme Corp", "1234.56"], ["Beta Inc", "987.65"] ], "executionMillis": 234}Der Cube wird im Speicher kompiliert; nichts wird gespeichert. Verwenden Sie dies in Ihrer eigenen Iterationsschleife so, wie es der Schema-Designer des Dashboards tut.
GET /me/inference/trace/{traceId}
Rufen Sie die LLM-Konversation für einen vorherigen propose-Aufruf
ab. Jede Propose-Antwort enthält eine traceId; übergeben Sie sie
hier, um den vollständigen Prompt + die Antwort zu erhalten.
Antwort (200):
{ "traceId": "ac2ab729-7fc3-4c3a-8e36-7cd77be87267", "createdAt": "2026-05-23T18:00:00Z", "status": "success", "model": "claude-sonnet-4-6", "inputTokens": 4321, "outputTokens": 892, "estimatedCostUsd": 0.018, "promptText": "...full system + user prompt (often 20+ KB)...", "responseText": "...full LLM response, parsed and unparsed..."}Nützlich für:
- Debuggen unerwarteter Vorschläge. Sehen Sie genau, was wir Claude gesendet haben und was zurückkam.
- Audit-Trail. Compliance-Teams wollen manchmal eine Aufzeichnung dessen, was KI für die menschliche Prüfung generiert hat.
- Iteration. Vergleichen Sie zwei aufeinanderfolgende Vorschläge, um zu sehen, was Claude anders gemacht hat.
Traces werden 30 Tage aufbewahrt und dann gelöscht. RLS-gescopt auf Ihren Tenant — Sie können nur Ihre eigenen Traces abrufen.
End-to-End-Beispiel
Ein vollständiges Skript — ein Warehouse profilieren, einen Cube vorschlagen, die XML rendern, speichern:
KEY="$SAIKU_API_KEY"CONN_ID="abc-123-def"
# 1. Profile (das `profile`-Feld extrahieren — propose will das# profile-Objekt, nicht die ganze Antwort).PROFILE=$(curl -sS https://api.saiku.bi/me/inference/profile/connection/$CONN_ID \ -X POST -H "Authorization: Bearer $KEY" -d '{}' \ -H "Content-Type: application/json" | jq '.profile')
# 2. ProposePROPOSAL=$(curl -sS https://api.saiku.bi/me/inference/propose \ -X POST -H "Authorization: Bearer $KEY" \ -H "Content-Type: application/json" \ -d "{\"profile\": $PROFILE, \"intent\": \"Sales revenue and order count by customer and date.\"}")
# Happy path: die propose-Antwort enthält bereits mondrianXml.echo "$PROPOSAL" | jq -r '.mondrianXml' > sales-cube.xml
# 3. Re-render (nur erforderlich, wenn Sie den Vorschlag bearbeitet haben).# Wichtig: Der render-Endpunkt nimmt das proposal-JSON# direkt — NICHT in {proposal: ...} eingewickelt.XML=$(echo "$PROPOSAL" | jq '.proposal' | \ curl -sS https://api.saiku.bi/me/inference/render \ -X POST -H "Authorization: Bearer $KEY" \ -H "Content-Type: application/json" \ -d @- | jq -r '.mondrianXml')
# 4. Speichern (über /me/schemas — noch nicht auf dieser Seite behandelt)echo "$XML" > sales-cube.xmlErsetzen Sie Schritt 4 durch das, was Ihr Workflow benötigt — in Ihr eigenes Repo speichern, in Git committen, einem menschlichen Reviewer übergeben.
Wie geht es weiter?
- Schema-Designer — die Dashboard-UI, die auf dieser API aufbaut.
- Authentifizierung — Bearer-Tokens, Rate-Limits.
- MCP Server — für LLM-Agenten, die Ihre Cubes abfragen möchten (diese Seite befasst sich mit der Erstellung).