API inferencji AI
API inferencji AI napędza Schema designer w dashboardzie. Wystawia ten sam trzykrokowy przepływ — profile, propose, render — jako endpointy, które możesz wywołać z własnego kodu. Przydatne, gdy chcesz zautomatyzować generowanie kostek na wielu hurtowniach, osadzić autorstwo kostek we własnym produkcie albo zintegrować Saiku Cloud z wyżej położonym przepływem onboardingu.
Wszystkie endpointy wymagają klucza API typu Bearer. Podstawy zobacz w Uwierzytelnianiu.
Przepływ
Schema designer w dashboardzie to kanoniczna implementacja referencyjna:
- Profile połączenie lub wgrany plik. Tanio próbkujemy jego
strukturę i zwracamy
SchemaProfile. - Propose kostkę. Wysyłamy profil + opcjonalną intencję po
ludzku do Claude’a i zwracamy strukturalny
CubeProposal. - Render propozycję jako XML schemy Mondrian. Wynik to string gotowy do zapisania jako schema w Twoim workspace.
Możesz wywoływać kroki niezależnie — profiluj raz i proponuj kilka
razy z różnymi intencjami, albo pomiń krok propose i ręcznie zlep
CubeProposal dla renderera.
POST /me/inference/profile/connection/{id}
Sprofiluj zapisane połączenie hurtowni. Czyta information_schema,
próbkuje kilka wierszy na kolumnę, zwraca strukturalny profil tabel
i kolumn hurtowni.
Parametry ścieżki:
id— ID połączenia zGET /me/connections.
Opcjonalne parametry query:
schema— ogranicz do konkretnej schemy bazy danych (np.public,analytics). Domyślnie schema, dla której połączenie zostało zapisane.maxTables— ogranicz liczbę profilowanych tabel. Domyślnie 20.tableTypes— lista oddzielona przecinkami (TABLE,VIEW,MATERIALIZED VIEW). Domyślnie tylkoTABLE.
Odpowiedź (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}Koszt: zazwyczaj poniżej $0.05 na profil względem hurtowni Postgres. Większość kosztu to zapytania o metadane, nie skany danych.
Tryby porażki:
404 not_found— ID połączenia nie istnieje lub nie jest widoczne dla Twojego tenanta.502 warehouse_unreachable— nie udało się połączyć z hurtownią (złe poświadczenia, host nie działa, problem sieciowy).
GET /me/inference/profile/connection/{id}/sample
Pobierz kilka przykładowych wierszy z jednej tabeli. Ten sam kształt auth i dostępności co endpoint profilowania, węższy zakres.
Parametry ścieżki + query:
id— ID połączenia.table— nazwa tabeli (wymagane).schema— schema bazy danych (domyślnie domyślna połączenia).rows— liczba wierszy (domyślnie 5, max 50).
Odpowiedź (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"] ]}Przydatne, by pokazać użytkownikowi, jak naprawdę wyglądają jego dane, zanim zatwierdzi projekt kostki.
POST /me/inference/profile/file
Sprofiluj wgrany plik zamiast tabeli hurtowni. Ten sam kształt co profiler połączeń, z ID pliku w miejscu ID połączenia.
Treść multipart:
file— część plikowa..parquet,.csvlub.json.tableTypes— domyślnieTABLE(ten sam kształt co profiler połączeń, dopuszcza przyszłe rozszerzenia).
Odpowiedź (200): ten sam kształt SchemaProfile co profiler
połączeń. Pliki wynurzają się jako pojedyncza pozycja tables[0].
httpfs DuckDB-a czyta plik kolumna po kolumnie, więc wielogigabajtowy
plik Parquet profiluje się w sekundach bez wciągania całości w pamięć.
POST /me/inference/propose
Wyślij profil do Claude’a i dostań propozycję kostki.
Treść żądania:
{ "profile": { /* SchemaProfile z kroku profile */ }, "intent": "Sales facts joined to customer and product dimensions, sum of revenue, count of orders.", "factTable": { "schema": "public", "name": "fact_sales" }}profile(wymagane) — JSON zwrócony przez którykolwiek endpoint profilowania.intent(opcjonalne, ale gorąco zalecane) — jednolinijkowy opis po ludzku, czego chcesz. Bez tego Claude ma znacznie mniej, na czym pracować.factTable(opcjonalne) — przypnij konkretną tabelę faktów. Bez tego Claude wybiera jedną na podstawie heurystyk nazewniczych i kształtów kolumn.
Odpowiedź (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": { /* skondensowana propozycja — miary + nazwy wymiarów + graf złączeń */ }, "detectorFindings": { /* heurystyczne wnioski o hierarchiach daty itd. */ }, "usage": { "model": "claude-sonnet-4-6", "inputTokens": 4321, "outputTokens": 892, "totalTokens": 5213, "estimatedCostUsd": 0.018, "isPricingExact": true }, "quota": { /* migawka miesięcznego budżetu LLM */ }}Co warto zauważyć: odpowiedź już zawiera wyrenderowany
mondrianXml dla ścieżki szczęśliwej. Nie musisz wywoływać osobno
/render, chyba że najpierw zmodyfikowałeś propozycję.
traceId pozwala później pobrać pełną konwersację LLM przez
GET /me/inference/trace/{traceId}
— przydatne do audytu lub debugowania nieoczekiwanych propozycji.
Tryby porażki:
400 invalid_proposal— Claude zwrócił strukturalnie nieprawidłową propozycję, którą nasz walidator odrzucił. Trace ID i tak zwracane.429 over_budget— Twój tenant przekroczył miesięczny budżet LLM. Wraca z nagłówkiemRetry-Afteri polemreset_atpokazującym, kiedy budżet się resetuje.502 upstream_error— Claude zwrócił błąd sieci lub odmówił żądania.
POST /me/inference/render
Konwertuj SchemaProposal na XML schemy Mondrian.
Treść żądania — JSON propozycji bezpośrednio (nie opakowany
w {proposal: …}). Użyj pola proposal z wcześniejszej odpowiedzi
/propose, z naniesionymi lokalnymi edycjami:
{ "schemaName": "Sales Analytics", "cubes": [ { "name": "Sales", "factTableSchema": "public", "factTableName": "fact_sales", "measures": [ /* … */ ], "dimensions": [ /* … */ ] } ]}Odpowiedź (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": { /* skondensowany widok — miary + nazwy wymiarów + graf złączeń */ }}Czysta transformacja — bez wywołania LLM, bez interakcji z hurtownią. Darmowe.
Render możesz wywoływać wielokrotnie na tej samej propozycji w trakcie jej edycji — dokładnie to robi Schema designer w pętli edycji dashboarda.
Tryby porażki:
400 invalid_proposal— propozycja nie spełnia ograniczeń Mondrian XML (brakujące wymagane pole, sprzeczne złączenia itd.). Treść odpowiedzi:{ error, kind, message }.400 empty_proposal— treść żądania była pusta.
POST /me/inference/try-query
Uruchom przykładowe zapytanie MDX względem szkicu kostki przed jej zapisaniem. Przydatne, by potwierdzić, że złączenia lądują tam, gdzie oczekujesz.
Treść żądania:
{ "proposal": { /* CubeProposal */ }, "connectionId": "uuid-of-saved-connection", "mdx": "SELECT { [Measures].[Revenue] } ON COLUMNS, { [Customer].[Name].MEMBERS } ON ROWS FROM [Sales]"}Odpowiedź (200):
{ "columns": ["Customer", "Revenue"], "rows": [ ["Acme Corp", "1234.56"], ["Beta Inc", "987.65"] ], "executionMillis": 234}Kostka jest kompilowana w pamięci; nic nie jest zapisywane. Używaj tego we własnej pętli iteracji tak, jak robi to Schema designer.
GET /me/inference/trace/{traceId}
Pobierz konwersację LLM dla wcześniejszego wywołania propose. Każda
odpowiedź propose zawiera traceId; przekaż go tutaj, by dostać
pełny prompt + odpowiedź.
Odpowiedź (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": "...pełny prompt systemowy + użytkownika (często 20+ KB)...", "responseText": "...pełna odpowiedź LLM, sparsowana i niesparsowana..."}Przydatne do:
- Debugowania nieoczekiwanych propozycji. Zobacz dokładnie, co wysłaliśmy do Claude’a i co wróciło.
- Ślad audytowy. Zespoły compliance czasem chcą rekordu tego, co AI wygenerowało, do przeglądu przez człowieka.
- Iteracji. Porównaj dwie kolejne propozycje, by zobaczyć, co Claude zrobił inaczej.
Trace’y są przechowywane przez 30 dni, potem czyszczone. RLS zakresuje je do Twojego tenanta — możesz pobierać tylko swoje trace’y.
Przykład end-to-end
Kompletny skrypt — sprofiluj hurtownię, zaproponuj kostkę, wyrenderuj XML, zapisz:
KEY="$SAIKU_API_KEY"CONN_ID="abc-123-def"
# 1. Profile (wyciągnij pole `profile` — propose chce obiektu profilu,# a nie całej odpowiedzi).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.\"}")
# Ścieżka szczęśliwa: odpowiedź propose już zawiera mondrianXml.echo "$PROPOSAL" | jq -r '.mondrianXml' > sales-cube.xml
# 3. Ponowny render (potrzebny tylko jeśli edytowałeś propozycję).# Ważne: endpoint render przyjmuje JSON propozycji# bezpośrednio — NIE opakowany w {proposal: ...}.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. Zapis (przez /me/schemas — nie pokryte jeszcze na tej stronie)echo "$XML" > sales-cube.xmlZastąp krok 4 tym, czego potrzebuje Twój workflow — zapisz do własnego repo, wrzuć do gita, podaj do recenzji człowiekowi.
Co dalej
- Schema designer — UI dashboarda zbudowany na tym API.
- Uwierzytelnianie — tokeny Bearer, limity szybkości.
- Serwer MCP — dla agentów LLM, którzy chcą odpytywać Twoje kostki (ta strona jest o ich autorstwie).