Learn how tabular foundation models such as SAP-RPT and TabPFN predict from example rows without training, and when they beat or lose to a trained model.
Most business data lives in tables: invoices, orders, deliveries, suppliers. For years, predicting a column of such a table meant a machine learning project. Someone collected history, trained a model, tested it and deployed it. That took weeks per question.
Tabular foundation models change the order of work. The model has already been trained on millions of tables. You send it a table of examples where the answer is known, plus the rows where it isn't, in one request. It fills in the missing answers in seconds. Nothing is trained for your question. This is called in-context learning: the examples in the request are the only thing the model learns from.
SAP sells two families of these models through SAP AI Core: its own SAP-RPT models and TabPFN from Prior Labs, which SAP bought in July 2026. Both promise "predict without a training project". Neither removes the need to check whether the predictions are good enough.
Take order-to-cash. A collections team wants to know which open invoices will be paid late, so it can call those customers first. The classic route is a project: extract a year of paid invoices, train a model, validate, deploy, retrain each quarter. Many such ideas never start because the project costs more than the question seems worth.
With a tabular foundation model, the first answer costs an afternoon. You send a few thousand recent paid invoices with a "late: yes or no" column, plus this week's open invoices, and get a probability for each. If the ranking helps, you build from there. If it doesn't, you've lost a day, not a quarter.
That changes three things:
Cost of trying. More prediction ideas get tested: payment delays, supplier risk, which blocked sales orders get released, which MRP exceptions matter.
Freshness. The model reads the examples you send each time. Send last month's invoices and it reflects last month. There is no retraining cycle to fall behind.
Fewer models to run. One hosted model serves many questions, instead of one trained model per question to monitor.
The risks are just as concrete. The predictions can be wrong, and a hosted model's confidence can look precise when it isn't. Each request carries real business data to the service. Each call has a price. And on large, stable problems, a well-built trained model can still be more accurate. SAP's own research paper on this model family says tuned decision trees remain strong on large datasets.
As of October 2026, SAP offers tabular AI in three layers.
Tabular foundation models in SAP AI Core. SAP's developer tutorial shows two SAP-RPT-1 models in the generative AI hub, sap-rpt-1-small for prototyping and high volume and sap-rpt-1-large for the highest accuracy. SAP's product page now names SAP-RPT-1.6 as the latest version in the AI Core model library and links model cards for 1.6 and 1.5. SAP published the 1.5 model card in July 2026. In September 2026 SAP announced that TabPFN-3.5 Plus, from its company Prior Labs, is available in SAP AI Core too. SAP's tutorial calls SAP-RPT through SAP AI Core on the extended service plan.
A free playground. At rpt.cloud.sap anyone can upload a CSV file of up to 2,048 rows and 100 columns and try SAP-RPT. SAP calls it a demonstration platform, not a production service.
Classic trained models. SAP HANA ships its own machine learning libraries: the Predictive Analysis Library (PAL) for algorithms you choose and tune, and the Automated Predictive Library (APL) for automated model building. Inside S/4HANA, Intelligent Scenario Lifecycle Management (ISLM) manages shipped and custom machine learning scenarios. These remain the right tools when a prediction runs millions of times a day inside a business process.
There is also an open model, sap-rpt-1-oss, on GitHub and Hugging Face. SAP licenses it for research and non-commercial use only, so it is for learning, not for customer work.
#A decision guide: foundation model or trained model
Situation
Better first choice
Why
A new question, and you want to know within a day if it is predictable
Tabular foundation model
No training project; one request gives a first answer
A few hundred to a few thousand labelled rows
Tabular foundation model
Trained models struggle on small data; these models are built for it
Behaviour changes month to month
Tabular foundation model
Send recent examples each time instead of retraining
Millions of labelled rows and a stable process
Trained model (PAL, APL, or your own)
Trees trained on large data are hard to beat, and per-call costs add up
Prediction runs inside S/4HANA on every document
Embedded scenario through ISLM
Stays in the system, no external call per document
Data may not leave the system
Trained model in HANA
PAL and APL compute where the data already is
You need to explain each prediction to an auditor
Either, but check the explanation features
SAP's 1.5 model card has explainability sections; trees offer their own explanations
The honest answer for many teams is both. Use the foundation model to find out quickly whether a question is worth pursuing. Train a dedicated model only for the predictions that prove their value and run at high volume.
"No training means no data work." You still pick the columns, clean the values, choose the example rows and remove anything that leaks the answer. That is most of the work in any prediction project.
"It's a language model reading a spreadsheet." It isn't. These models are built for tables, and SAP's product page claims clearly better predictions than language models on this job. Test that claim on your own data.
"The confidence score is a guarantee." A confidence of 0.9 is the model's own estimate. Only a test on held-back data tells you how often 0.9 is right.
"It replaces data scientists." It removes the training step, not the judgement. Someone still has to design the test, read the results and decide whether to act.
"The open model is free for our project." sap-rpt-1-oss is licensed for research and non-commercial use. Newer TabPFN weights carry non-commercial licences too.
Pick one answer for each question. The explanation appears after you choose.
1What is the main difference between a tabular foundation model and a classic trained model?
Answer: D. A tabular foundation model was pretrained on many tables, so you send labelled examples and the rows to predict in one request. Nothing is trained for your question. A classic model needs its own training project first.
2A collections lead wants to know quickly whether late payments can be predicted at all. What is a sensible first step?
Answer: D. The point of these models is a cheap first answer. One request with recent paid invoices as examples, tested on invoices the model hasn't seen, shows within a day whether the question is predictable.
3Your company has millions of labelled rows for a stable process that runs on every document. Which option usually fits best?
Answer: D. With lots of stable data, tuned trained models are hard to beat, and per-call costs add up at high volume. SAP's own research says tuned trees remain strong on large datasets.
4Which statement about SAP's offering is accurate as of October 2026?
Answer: A. SAP offers SAP-RPT through the generative AI hub and announced TabPFN-3.5 Plus in SAP AI Core in September 2026. The playground is a demonstration, and the open model is for research and non-commercial use.
5Someone wants to upload a real customer payment history to the public playground for a quick demo. What should you do?
Answer: B. The playground is a demonstration service. Even if uploads are deleted after processing, sending real customer data needs approval first. Made-up or approved data is the safe default.
6The model returns a confidence of 0.9 for an invoice being late. What does that tell you?
Answer: C. Confidence is the model's estimate, not a measured accuracy. Only a test on invoices it never saw tells you how often its 0.9 is right.
7Which question matters most to ask a vendor before committing to a tabular foundation model?
Answer: C. Availability, price per call and data processing decide whether a plan survives contact with procurement and data protection. Pretraining size is interesting but doesn't change your decision.
Ask a question
Testing: only staff see this
Stuck on something in this layer? Ask it here. Questions are answered in the order they arrive, and the answer appears under My questions.
Sign in (free) to ask a question. You can ask anonymously.
Deep layer · 40 min read
#Mental model: the context is the training set, sent every time
A classic model learns once and then forgets its training data. Its knowledge sits in fitted parameters. A tabular foundation model works the other way round. Its parameters hold general knowledge about how tables behave, learned from millions of other tables. What it knows about your question arrives with each request, as labelled example rows.
So the rows you send are your training set. Choose them as carefully as you would choose training data. Too few, too old, one class only, or leaking the answer, and the prediction is bad no matter how good the model is.
Three consequences follow, and the rest of this topic builds on them:
There is no training step to get wrong, but there is a context step. Picking which rows to send is now the main design decision.
Updating is cheap. Send newer rows and the model reflects them. No retraining job.
Size limits are hard limits. A request can carry only so many rows and columns. Beyond that, a trained model sees more data than the foundation model ever can in one call.
Every request has the same shape. Context rows carry the answer. Query rows carry [PREDICT] where the answer should go. The model reads them together and returns a value for each [PREDICT] cell.
flowchart LR
A[Paid invoices<br/>with late yes/no] --> C[One request]
B[Open invoices<br/>late = PREDICT] --> C
C --> M[Pretrained<br/>tabular model]
M --> P[Late probability<br/>per open invoice]
Inside, the model turns every cell into a vector of numbers. Then it alternates two kinds of attention:
Across columns, so each cell can take its row's other values into account. An amount means something different on NET60 terms than on 2/10NET30 terms.
Across rows, so each row can look at the context rows. SAP's ConTextTab paper says this row attention is masked so that each row attends only to the provided context. A query row compares itself with the labelled examples and borrows from the ones that matter.
The second kind is the in-context learning. It is the same idea as a language model reading examples in a prompt, but the "prompt" is a table.
The model learned how to borrow from context rows during pretraining. It saw millions of prediction tasks, each with context rows and hidden answers, and learned to fill them in.
The two families differ in what they practised on:
TabPFN
SAP-RPT and ConTextTab
Pretraining data
Synthetically generated tasks, per the ConTextTab paper
About 2.18 million real tables from the T4 dataset
Column names and text
Treated as plain values
Embedded with a text model, so "PaymentTerms" and "NET60" carry meaning
Results in the ConTextTab paper
Mostly on par on plain numeric benchmarks
Better where names and text carry meaning, as in ERP data
The ConTextTab paper, written by SAP researchers, reports that adding meaning ("semantics") helps most on benchmarks where column names and text values matter. On plain numeric benchmarks the differences to TabPFN were mostly not statistically significant. The paper links its code to the repository SAP-samples/sap-rpt-1-oss, and SAP's model card for SAP-RPT-1.5 describes semantic embeddings and a table-native 2D attention scheme.
For classification you get a predicted class and a confidence between 0 and 1, per SAP's tutorial. For regression you get a number. SAP's 1.5 model card has column-wise and row-wise explainability sections, and SAP's product page says the playground lets you explore the rows and columns behind a prediction. Check your own model version for what the API returns.
SAP's developer tutorial gives these per-call limits for SAP-RPT-1:
Limit
sap-rpt-1-small
sap-rpt-1-large
Context rows (maximum)
2,048
65,536
Context rows (recommended)
500 to 2,000
4,000 to 8,000
Columns
100
256
Classes per target
256
1,023
Rows to predict per call
128
128
Target columns per call
10
10
Limits for SAP-RPT-1.5 weren't in the parts of the model card we could read; check the generative AI hub documentation in your system. TabPFN-3.5 is documented for up to 1,000,000 rows and 20,000 features, but on a CPU the open package limits TabPFN-3 and 3.5 to 5,000 samples unless you override it.
ConTextTab's authors say tuned, ensembled boosted trees remain strong, especially on large datasets. That matches the intuition from the mental model. A trained model can learn from every row you have. A foundation model sees only what fits in one request, and it learned its "how to compare rows" on other people's tables.
The practical rule: foundation models are strongest when labelled data is small, changing or messy. Trees are strongest when it is large and stable. You find out which case you are in by testing, which is what you will do next.
#Build it yourself: in-context learning with your own hands
You will build the smallest possible in-context learner, a few lines of Python with no training at all, and race it against a trained model on made-up invoices. Then you will look inside one prediction to see which example rows it borrowed from. Last, you will write a CSV in the context-plus-[PREDICT] shape, ready for the SAP playground.
The hand-built learner is much simpler than SAP-RPT or TabPFN. It compares rows with a fixed rule; the real models learned how to compare rows from millions of tables. But the shape of the work is the same, and it shows you why the context matters.
In VS Code, choose File > New File, and save it as unit12/tabular_icl.py.
Paste this code and save again.
"""Tabular in-context learning vs. a trained model, on made-up invoices.
Question: will this customer invoice be paid late?
Three predictors see the same labelled "context" rows (the most recent
paid invoices) and score the same open invoices:
1. base rate - predicts the share of late invoices in the context
2. in-context - no training: compares each open invoice with every
context row and lets similar rows vote (attention)
3. trained model - gradient boosted trees fitted on the context rows
Run: python unit12/tabular_icl.py
Options:
--context 50 200 1000 2000 context sizes to compare
--show 5 explain the votes for N open invoices
--rpt-csv FILE also write a CSV with [PREDICT] rows
--tabpfn also try TabPFN (needs extra setup)
"""
import argparse
import time
import numpy as np
import pandas as pd
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.metrics import roc_auc_score
CATEGORICAL = ["customer", "country", "payment_terms", "sales_org"]
NUMERIC = ["amount_eur", "dunning_level", "days_to_month_end"]
TARGET = "paid_late"
N_OPEN = 500 # the newest invoices play the role of open items
def make_invoices(n=4000, seed=7):
"""Made-up, SAP-shaped invoice history. Not real SAP field names."""
rng = np.random.default_rng(seed)
customers = [f"C{100 + i}" for i in range(60)]
reliability = dict(zip(customers, rng.normal(0, 1.0, len(customers))))
home = dict(zip(customers, rng.choice(["DE", "FR", "US", "IN", "BR"],
len(customers),
p=[0.35, 0.2, 0.2, 0.15, 0.1])))
start = pd.Timestamp("2025-01-01")
days = np.sort(rng.integers(0, 540, n))
rows = []
for i, d in enumerate(days):
cust = rng.choice(customers)
date = start + pd.Timedelta(days=int(d))
terms = rng.choice(["NET30", "NET45", "NET60", "2/10NET30"],
p=[0.4, 0.25, 0.2, 0.15])
amount = float(np.round(rng.lognormal(8.5, 1.0), 2))
dunning = int(rng.choice([0, 0, 0, 1, 2]))
month_end = (date + pd.offsets.MonthEnd(0) - date).days
score = (-1.2 - 1.1 * reliability[cust]
+ 0.5 * (terms == "NET60") - 0.6 * (terms == "2/10NET30")
+ 0.35 * np.log(amount / 5000) + 0.7 * dunning
+ 0.4 * (home[cust] in ("BR", "IN"))
+ 0.02 * (d - 270) / 27) # slow drift: more late later
late = rng.random() < 1 / (1 + np.exp(-score))
rows.append({
"invoice": f"9000{i:05d}",
"posting_date": date.date().isoformat(),
"customer": cust,
"country": home[cust],
"payment_terms": terms,
"sales_org": rng.choice(["1010", "1710"]),
"amount_eur": amount,
"dunning_level": dunning,
"days_to_month_end": int(month_end),
TARGET: "yes" if late else "no",
})
return pd.DataFrame(rows)
def encode(context, query):
"""One-hot categories and scaled numbers, using context statistics only."""
both = pd.concat([context, query], keys=["c", "q"])
cats = pd.get_dummies(both[CATEGORICAL].astype(str), dtype=float)
nums = both[NUMERIC].astype(float).copy()
nums["amount_eur"] = np.log(nums["amount_eur"])
mean = nums.loc["c"].mean()
std = nums.loc["c"].std().replace(0, 1)
nums = (nums - mean) / std
x = pd.concat([cats, nums], axis=1)
return x.loc["c"].to_numpy(), x.loc["q"].to_numpy()
def in_context_predict(context, query, temperature=1.0):
"""Attention over context rows: no fitting, the context is the model."""
xc, xq = encode(context, query)
yc = (context[TARGET] == "yes").to_numpy(dtype=float)
# squared distance between every open row and every context row
dist = ((xq ** 2).sum(1)[:, None] + (xc ** 2).sum(1)[None, :]
- 2 * xq @ xc.T)
logits = -dist / temperature
logits -= logits.max(axis=1, keepdims=True)
weights = np.exp(logits)
weights /= weights.sum(axis=1, keepdims=True)
return weights @ yc, weights
def trained_predict(context, query):
"""Gradient boosted trees, fitted on the same context rows."""
xc, xq = context.copy(), query.copy()
for col in CATEGORICAL:
codes = {v: i for i, v in enumerate(sorted(xc[col].unique()))}
xc[col] = xc[col].map(codes)
xq[col] = xq[col].map(codes).fillna(-1) # unseen value
cols = CATEGORICAL + NUMERIC
model = HistGradientBoostingClassifier(
categorical_features=[c in CATEGORICAL for c in cols],
random_state=0)
model.fit(xc[cols], context[TARGET])
return model.predict_proba(xq[cols])[:, list(model.classes_).index("yes")]
def tabpfn_predict(context, query):
from tabpfn import TabPFNClassifier # optional; see the topic
cols = CATEGORICAL + NUMERIC
clf = TabPFNClassifier()
clf.fit(context[cols], context[TARGET])
return clf.predict_proba(query[cols])[:, list(clf.classes_).index("yes")]
def score(name, y, p, seconds):
auc = roc_auc_score(y, p) if len(set(p.round(6))) > 1 else 0.5
acc = ((p >= 0.5) == y).mean()
return {"method": name, "auc": round(auc, 3),
"accuracy": round(acc, 3), "seconds": round(seconds, 3)}
def timed(fn, *args):
t0 = time.perf_counter()
out = fn(*args)
return out, time.perf_counter() - t0
def main():
ap = argparse.ArgumentParser(description=__doc__,
formatter_class=argparse.RawTextHelpFormatter)
ap.add_argument("--context", type=int, nargs="+",
default=[50, 200, 1000, 2000])
ap.add_argument("--show", type=int, default=0)
ap.add_argument("--rpt-csv")
ap.add_argument("--tabpfn", action="store_true")
args = ap.parse_args()
data = make_invoices()
history, open_items = data.iloc[:-N_OPEN], data.iloc[-N_OPEN:]
y = (open_items[TARGET] == "yes").to_numpy()
print(f"{len(history)} paid invoices, {len(open_items)} open invoices "
f"(posted {open_items.posting_date.min()} to "
f"{open_items.posting_date.max()}), "
f"{y.mean():.0%} of open ones end up late\n")
results = []
for n in args.context:
context = history.iloc[-n:] # the n most recent paid invoices
base = np.full(len(open_items), (context[TARGET] == "yes").mean())
results.append({"context_rows": n, **score("base rate", y, base, 0)})
(p_icl, _), s = timed(in_context_predict, context, open_items)
results.append({"context_rows": n, **score("in-context", y, p_icl, s)})
p_gbt, s = timed(trained_predict, context, open_items)
results.append({"context_rows": n,
**score("trained trees", y, p_gbt, s)})
if args.tabpfn:
try:
p_pfn, s = timed(tabpfn_predict, context, open_items)
results.append({"context_rows": n,
**score("TabPFN", y, p_pfn, s)})
except Exception as err: # missing library, licence, token
print(f"TabPFN skipped: {type(err).__name__}: {err}")
args.tabpfn = False
table = pd.DataFrame(results)
print(table.to_string(index=False))
table.to_csv("unit12/tabular_results.csv", index=False)
print("\nSaved unit12/tabular_results.csv")
if args.show:
context = history.iloc[-max(args.context):]
p, weights = in_context_predict(context, open_items.iloc[:args.show])
for i in range(args.show):
row = open_items.iloc[i]
top = np.argsort(weights[i])[::-1][:3]
print(f"\nInvoice {row.invoice} {row.customer} {row.payment_terms}"
f" {row.amount_eur:,.0f} EUR -> late p={p[i]:.2f}"
f" (actual: {row[TARGET]})")
for j in top:
c = context.iloc[j]
print(f" weight {weights[i, j]:.2f} {c.invoice} {c.customer}"
f" {c.payment_terms} {c.amount_eur:,.0f} EUR"
f" late={c[TARGET]}")
if args.rpt_csv:
context = history.iloc[-1000:]
query = open_items.iloc[:50].copy()
query[TARGET] = "[PREDICT]"
out = pd.concat([context, query])
out.to_csv(args.rpt_csv, index=False)
print(f"\nWrote {args.rpt_csv}: {len(context)} context rows, "
f"{len(query)} rows to predict, {out.shape[1]} columns")
if __name__ == "__main__":
main()
The script makes 4,000 invoices over 18 months. The newest 500 play the open items. For each context size it scores the open invoices three ways: the base rate, the in-context learner and gradient boosted trees trained on the same rows.
Wait a few seconds. You should see a table like this. The made-up data is the same each run, so your numbers should match; the seconds will differ.
3500 paid invoices, 500 open invoices (posted 2026-04-15 to 2026-06-24), 43% of open ones end up late
context_rows method auc accuracy seconds
50 base rate 0.500 0.574 0.000
50 in-context 0.523 0.548 0.014
50 trained trees 0.583 0.578 0.097
200 base rate 0.500 0.574 0.000
200 in-context 0.616 0.576 0.012
200 trained trees 0.570 0.546 0.148
1000 base rate 0.500 0.574 0.000
1000 in-context 0.658 0.630 0.022
1000 trained trees 0.709 0.664 0.229
2000 base rate 0.500 0.574 0.000
2000 in-context 0.681 0.646 0.031
2000 trained trees 0.722 0.670 0.274
Saved unit12/tabular_results.csv
Read it row by row:
Base rate: AUC 0.5, as expected. Its accuracy of 0.574 comes from always guessing "not late", because fewer than half are late. That is why accuracy alone misleads.
50 rows: nothing works well. There isn't enough to learn from.
200 rows: the in-context learner ranks better than the trees (0.616 vs 0.570). Trees need more data to find their splits.
1,000 and 2,000 rows: the trees pull ahead (0.722 vs 0.681). With enough data, training wins over our fixed comparison rule.
That is the pattern from "When trees still win", on your own machine. The real foundation models move the crossover point much further out, because they learned a far better comparison rule than ours. Where it falls on your data is something only a test tells you.
Read it: the open invoice from customer C139 on NET60 terms found one very similar paid invoice (same customer, same terms, similar amount) that was late. That single row carries 27% of the vote and pushes the prediction to 0.58. It was wrong this time.
This is in-context learning at its most visible: the prediction is a weighted vote of the examples you sent. It also shows a risk. One unusual example row can swing a prediction, which is why the choice of context rows matters so much.
Wrote unit12/rpt_upload.csv: 1000 context rows, 50 rows to predict, 10 columns
Open unit12/rpt_upload.csv in VS Code. The last 50 rows have [PREDICT] in the paid_late column. Context rows come first, as SAP's tutorial asks. The file stays within the playground's documented limits: under 5 MB, under 2,048 rows and under 100 columns.
Optional: open https://rpt.cloud.sap in your browser. If you choose to sign in and upload the file, follow the screens to pick paid_late as the column to predict and invoice as the identifier. The screens may differ from this description, since SAP updates the playground. Compare its predictions for the 50 rows with the actual values the script printed in Step 4.
On first use, the package opens a browser to log in with Prior Labs and accept the licence for the default TabPFN-3.5 weights. Those weights are licensed for non-commercial use, which covers this exercise. On a computer without a browser, accept the licence at ux.priorlabs.ai and set the TABPFN_TOKEN environment variable.
If anything fails, the script prints TabPFN skipped: with the reason and carries on with the other methods. On a CPU, the package allows up to 5,000 rows for TabPFN-3.5, so stay at or below that.
Makes 4,000 SAP-shaped invoices with a hidden rule: each customer's reliability, the payment terms, the amount, the dunning level and the country push the chance of a late payment. The field names are plain, not real SAP fields.
history and open_items
Splits by time: the newest 500 invoices are the "open" ones, everything before is paid history. Context rows come only from history, so nothing from the future leaks in.
encode
One-hot encodes categories and scales numbers, using statistics from the context rows only.
in_context_predict
The in-context learner. Measures how far each open invoice is from each context row, turns distance into weights (softmax, as in attention), and averages the late flags with those weights. No fitting at all.
trained_predict
Fits a HistGradientBoostingClassifier on the same context rows, then predicts. Unknown customers get the code -1.
tabpfn_predict
Optional: the same race with a real tabular foundation model.
score
AUC and accuracy at a 0.5 threshold against the true outcome of the open invoices.
--show
Prints the three heaviest-weighted context rows behind a prediction.
--rpt-csv
Writes context rows followed by rows with [PREDICT], the shape SAP-RPT expects.
SAP-RPT runs as a model in the generative AI hub on SAP AI Core. SAP's developer tutorial, as of October 2026, describes this path:
Access. An SAP BTP subaccount with SAP AI Core on the extended service plan, and a service key for it.
Deploy. Create a deployment of an SAP-RPT model through SAP AI Launchpad, the Python SDK or a REST client. The tutorial names sap-rpt-1-small and sap-rpt-1-large; the product page names SAP-RPT-1.6, with model cards for 1.6 and 1.5.
Prepare the table. Labelled context rows first, then rows with [PREDICT] in each target column. Pick an index column (the tutorial uses Transaction ID) so each prediction maps back to its row. Up to 10 target columns, classification or regression.
Call. The tutorial uses the Python package sap-ai-sdk-gen, a beta JavaScript package @sap-ai-sdk/rpt with methods such as predictWithSchema() and predictParquet(), and a REST collection for the Bruno client. It can send the table as JSON or as a Parquet file.
Read. Classification returns a label and a confidence from 0 to 1; regression returns a number.
The tutorial's troubleshooting notes are worth keeping: 401 means a bad or expired token, 404 a wrong deployment ID or a stopped deployment, confidence below 0.5 usually means too few or unrepresentative context rows, and the same answer for every row means the context held only one class.
SAP's model card for SAP-RPT-1.5 (version 1.5.0.3, dated 24 July 2026) describes a table-native relational foundation model with classification and regression heads. It lists a large variant and a variant "with targeted context retrieval". The name points at the context step: retrieving relevant rows rather than sending a fixed set. SAP's product page also lists "tabular orchestration workflows" for automating tabular and predictive capabilities. Treat both as features to confirm in your own system before you design around them.
On 16 September 2026, SAP announced TabPFN-3.5 Plus as available in SAP AI Core. Prior Labs, which makes TabPFN, has been an SAP company since July 2026 and continues to operate independently. SAP's announcement stresses handling missing values, mixed data types and columns with thousands of distinct values, such as product codes or customer numbers, without preprocessing. Its examples are cash flow forecasting, payment delays, supplier risk, upsell and churn.
SAP now offers two tabular model families side by side. Which one wins on your table is an empirical question. Run both on the same held-back rows, as in Step 3.
For trained models, SAP HANA has the Predictive Analysis Library (PAL) and the Automated Predictive Library (APL). The hana-ml Python client wraps both and works on HANA DataFrames, so data stays in the database during training and scoring. In S/4HANA, ISLM manages shipped and custom intelligent scenarios through two Fiori apps, Intelligent Scenarios and Intelligent Scenario Management. The late deliveries topic built such a trained model by hand.
Data and authorizations. Every call sends business rows to SAP AI Core. Send only the columns the prediction needs. The user who triggers a prediction should only see predictions for documents they may see in S/4HANA, so filter context and query rows by the same authorization objects the source app uses. See agent permissions and SAP authorizations.
Leakage. A context row must contain only what was known at prediction time. If clearing_date or dunning_level_final sneaks into the context, the model will look brilliant in testing and useless in production. The rules from the late deliveries topic apply unchanged.
Evaluation. Hold back the newest rows, as the script does, and score with AUC plus the business measure: how many late invoices are in the top 50 the team will call? Re-run the evaluation when you change the context selection, the model version or the columns. Version changes matter here; SAP moved from SAP-RPT-1 to 1.5 and then 1.6 within months, and a new version can change every prediction.
Cost. Cost grows with rows per call times calls. At 128 rows to predict per call, scoring 10,000 open items is about 79 calls, each resending its context. Cache predictions for documents that haven't changed and score only new or changed ones.
Operations. Log every request: model version, context selection rule, row count, and the predictions returned. Without that log you can't explain a prediction from last month, and you can't compute whether the collections team's results improved.
Clean core. Call the model from a side-by-side app on BTP, reading S/4HANA through released APIs. Write results back only through released APIs, and only after a person approves any change to SAP data.
You'll try two other rules for choosing context rows and see whether either beats "the most recent rows". Your result goes into your Unit 12 decision log and feeds the buy-vs-build topic later in this unit.
Copy the script: unit12/tabular_icl.py to unit12/tabular_icl_v2.py.
In main, after context = history.iloc[-n:], build two more contexts of the same size n. A random one, drawn from all paid invoices, and a balanced one, with the newest late and the newest on-time invoices in equal numbers:
Score both new contexts. Copy the five lines that start with (p_icl, _), s = timed( and end with the trained trees result. Paste them twice below the originals. In the first copy, replace context with context_random in the two timed(...) calls and rename the methods to in-context (random) and trees (random). In the second copy, do the same with context_balanced.
Run it for --context 200 1000.
Add three sentences to unit12/decision-log.md (create it if it doesn't exist): which context rule won, by how much AUC, and what that means for how you'd select rows for SAP-RPT.
Commit both files with Git.
Done when your results table shows all three context rules for both sizes, and your decision log states which rule you'd use with SAP-RPT and why.
Pick one answer for each question. The explanation appears after you choose.
1In a tabular foundation model, where does the knowledge about your specific question come from?
Answer: B. The pretrained parameters hold general knowledge about tables. Everything specific to your question arrives as labelled context rows in the request, so those rows act as your training set.
2How does a query row use the context during prediction in models like ConTextTab?
Answer: B. The model alternates attention across columns and across rows. The row attention is masked so each row attends only to the provided context, which is how a query row borrows from similar labelled rows.
3In Step 3, the in-context learner beat the trees at 200 rows but lost at 2,000. What is the best explanation?
Answer: A. With little data, trees can't find reliable splits, so a fixed similarity rule does better. With enough data, training adapts to the problem and pulls ahead. The real foundation models move this crossover further out because they learned a better comparison rule.
4Your SAP-RPT call returns "not late" for every open invoice. What is the most likely cause?
Answer: A. SAP's tutorial lists the same prediction for every row as the symptom of a context with only one class. Check that late and on-time examples are both present before suspecting anything else.
5You need to score 1,000 open invoices with sap-rpt-1-small. What does the per-call limit mean for your design?
Answer: B. The tutorial allows 128 prediction rows per call for both sizes. The 2,048 figure is the context limit, and the 10 is the number of target columns, not rows.
6A context row for a paid invoice includes its final clearing date. What happens?
Answer: B. That's leakage: information not available at prediction time. It inflates test results and collapses in production, just as in the late deliveries topic.
7A team wants to ship a customer demo built on sap-rpt-1-oss and the default TabPFN-3.5 weights. What's the issue?
Answer: C. SAP licenses sap-rpt-1-oss for research and non-commercial use, and TabPFN-2.5 and later weights are non-commercial. For customer work, use the commercial models in SAP AI Core.
8You plan to roll out predictions to a collections team. Which control matters most for clean core and safety?
Answer: D. A side-by-side app reads through released APIs, respects the user's authorizations and changes SAP data only after a person approves. Logging is what lets you explain and measure results later.
Ask a question
Testing: only staff see this
Stuck on something in this layer? Ask it here. Questions are answered in the order they arrive, and the answer appears under My questions.
Sign in (free) to ask a question. You can ask anonymously.
Related topics
Picked from the terms, links and tags this topic shares with others.
Using SAP-RPT-1 for predictions in generative AI hub (SAP Developers tutorial)— sap-rpt-1-small and sap-rpt-1-large; context rows and [PREDICT] rows, context first; index column; limits per call (context rows 2,048 vs 65,536, columns 100 vs 256, classes 256 vs 1,023, recommended context 500-2,000 vs 4,000-8,000, 128 prediction rows and 10 target columns); confidence 0 to 1 for classification; extended SAP AI Core service plan; sap-ai-sdk-gen (Python), @sap-ai-sdk/rpt (JavaScript, beta), Bruno; troubleshooting for 401, 404, low confidence and one-class context
Transform structured data with SAP-RPT-1.5 (sap.com product page, Bulgaria site)— As re-read on 2026-11-01: SAP-RPT-1.6 described as the latest version, available through the model library in AI Core, with model cards for 1.6 and 1.5; tabular orchestration workflows; playground at rpt.cloud.sap free to test; open versions for research and non-commercial use. The earlier 2026 dates for 1.5 general availability, playground upgrade, observability and orchestration no longer appear on this page
SAP-RPT-1.5 Model Card (SAP, version 1.5.0.3, 24 July 2026)— table-native relational foundation model; context rows and query rows with [PREDICT]; classification and regression heads; semantic embeddings and 2D attention; variants SAP-RPT-1.5-large and SAP-RPT-1.5 with targeted context retrieval; column-wise and row-wise explainability sections; productive use through generative AI hub on SAP AI Core
SAP-RPT playground documentation (rpt.cloud.sap)— CSV upload limits: 5 MB, 2,048 rows, 100 columns, 1,000 characters per cell, 100 characters per column name; API at https://rpt.cloud.sap/api/predict with a Bearer token created in Settings; token hygiene
ConTextTab: A Semantics-Aware Tabular In-Context Learner (Spinaci et al., SAP, arXiv 2506.10707v4)— SAP authors; headers and text cells embedded with a text model; alternating cross-column and cross-row attention, rows attend only to the context; pretrained on about 2.18 million real tables (T4) while TabPFN used synthetic tasks; tuned trees still strong on large data; code at SAP-samples/sap-rpt-1-oss
tabpfn (PyPI, version 9.2.0)— Python 3.10+; code Apache 2.0; TabPFN-2.5 and later weights non-commercial, PriorLabs login or TABPFN_TOKEN; TabPFN-2 weights under the Prior Labs License; CPU up to 5,000 samples for TabPFN-3 and 3.5; TabPFNClassifier fit and predict; hosted TabPFN Client
hana-ml (PyPI, version 2.30.26091800)— Python machine learning client for SAP HANA: HANA DataFrame without moving data, PAL algorithm wrappers, APL automated predictive models; SAP Developer License