Orchestrate

Report an issue

Something wrong, out of date or unclear? Tell us. Corrections are prioritized over new topics. You don't need to sign in.

0 / 2000

Don't include personal or confidential company details. Reports are reviewed with AI assistance.

Tabular AI and SAP's predictive models

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.

Updated Oct 19, 2026Foundational 9 minDeep 40 min
Foundational layer · 9 min read

The 60-second version

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.

Why it matters to the business

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.

How SAP does it

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.

Questions to ask

Ask your team:

  • What decision will this prediction change, and who acts on it? A score nobody reads is a cost.
  • How will we measure accuracy on invoices the model has never seen, before anyone relies on it?
  • Which rows do we send as examples? Recent ones, similar ones, or a sample? Who decides?
  • What is our baseline today? If the collections team already prioritizes well, the bar is higher.

Ask SAP or your partner:

  • Which SAP-RPT and TabPFN versions are available in our region and service plan today, and which are being retired?
  • How is a call priced, and what does a typical request of our size cost?
  • Where is the data processed, and is any of it kept after the request?
  • What explanation does the service return for each prediction?

Ask your data protection officer:

  • Can customer payment history be sent to SAP AI Core for this purpose?
  • Can anyone upload real data to the public playground? (Usually the answer should be no.)

Common misconceptions

  • "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.

Key terms

All course terms, A to Z, in the glossary

  • Tabular foundation model: a model pretrained on many tables that predicts a column of a new table without being trained on it.
  • In-context learning: learning only from the examples included in the request, with no change to the model.
  • Context rows: example rows where the answer is known, sent with the request.
  • Query rows: rows where the answer is missing and should be predicted. SAP-RPT marks them with [PREDICT].
  • Classification: predicting a category, such as late or on time.
  • Regression: predicting a number, such as days late.
  • SAP-RPT: SAP's family of tabular foundation models ("relational pretrained transformer"), sold through the generative AI hub.
  • TabPFN: a tabular foundation model from Prior Labs, an SAP company since July 2026.
  • PAL and APL: SAP HANA's libraries for training classic machine learning models inside the database.
  • ISLM: Intelligent Scenario Lifecycle Management, the S/4HANA tool for managing machine learning scenarios.

Check yourself

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.
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:

  1. 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.
  2. Updating is cheap. Send newer rows and the model reflects them. No retraining job.
  3. 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.

How it works

From a table to a prediction

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.

Where the knowledge comes from

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.

What the output looks like

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.

Limits you design around

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.

When trees still win

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.

Before you start: complete Set up your computer for this course and Set up for Unit 12: local models. This walkthrough uses numpy, pandas and scikit-learn, which you installed in Set up for Unit 2. It needs no model download, no account and no key.

flowchart LR
  S1[Step 1<br/>open folder] --> S2[Step 2<br/>save script]
  S2 --> S3[Step 3<br/>run the race]
  S3 --> S4[Step 4<br/>look inside]
  S4 --> S5[Step 5<br/>playground CSV]
  S5 --> S6[Step 6<br/>save in Git]

What you need

  • Your course folder orchestrate-course with its .venv, from the setup topics.
  • 20 to 30 minutes.
  • Cost: free. Everything runs on your computer with made-up data.
  • Optional, for Step 5: a browser, to look at the SAP playground. Uploading is optional too.

Step 1: Open your course folder

  1. Open VS Code, choose File > Open Folder, and open orchestrate-course.

  2. Open a terminal with Terminal > New Terminal. Turn on the virtual environment if the prompt doesn't start with (.venv):

    Windows (PowerShell):

    .venv\Scripts\Activate.ps1

    macOS or Linux:

    source .venv/bin/activate
  3. Check that the unit12 folder exists. If it doesn't, create it:

    mkdir unit12

Run every command in this topic from the course folder, not from inside unit12.

Step 2: Save the script

  1. In VS Code, choose File > New File, and save it as unit12/tabular_icl.py.
  2. 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.

Step 3: Run the race

  1. Run the script:

    Windows (PowerShell):

    python unit12\tabular_icl.py

    macOS or Linux:

    python unit12/tabular_icl.py
  2. 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
  3. 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.

Step 4: Look inside one prediction

  1. Ask the script to explain the first three open invoices:

    Windows (PowerShell):

    python unit12\tabular_icl.py --context 2000 --show 3

    macOS or Linux:

    python unit12/tabular_icl.py --context 2000 --show 3
  2. Below the table you should see, for each invoice, the three context rows with the most weight:

    Invoice 900003502 C139 NET60 8,216 EUR -> late p=0.58 (actual: no)
      weight 0.27  900002805 C139 NET60 7,226 EUR late=yes
      weight 0.04  900003277 C107 NET60 7,090 EUR late=no
      weight 0.03  900001671 C139 NET45 7,169 EUR late=no
  3. 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.

Step 5: Write a CSV in the playground shape

  1. Ask the script to write a file of 1,000 context rows plus 50 rows to predict:

    Windows (PowerShell):

    python unit12\tabular_icl.py --context 1000 --rpt-csv unit12\rpt_upload.csv

    macOS or Linux:

    python unit12/tabular_icl.py --context 1000 --rpt-csv unit12/rpt_upload.csv
  2. You should see this line at the end:

    Wrote unit12/rpt_upload.csv: 1000 context rows, 50 rows to predict, 10 columns
  3. 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.

  4. 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.

Step 6: Save your work in Git

  1. Save the script and results:

    git add unit12/tabular_icl.py unit12/tabular_results.csv unit12/rpt_upload.csv
    git commit -m "Unit 12: tabular in-context learning vs trained trees"

Optional: try TabPFN on your own computer

The open tabpfn package runs a real tabular foundation model locally. It needs extra setup, so it's optional:

  1. Install it. It pulls in PyTorch, which is a large download:

    pip install tabpfn
  2. Run the race with the TabPFN column:

    python unit12/tabular_icl.py --context 200 1000 --tabpfn
  3. 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.

  4. 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.

What each part of the script does

Part What it does
make_invoices 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.

If something goes wrong

What you see What it means What to do
python: command not found or 'python' is not recognized Python isn't on your path, or the environment is off Turn on .venv as in Step 1. On macOS or Linux try python3. See the Unit 1 setup.
ModuleNotFoundError: No module named 'sklearn' (or pandas, numpy) The Unit 2 libraries aren't installed in this environment Run pip install numpy pandas scikit-learn with .venv on.
can't open file ... tabular_icl.py You're in the wrong folder, or the file has another name Run from orchestrate-course; check the file is unit12/tabular_icl.py.
OSError: Cannot save file into a non-existent directory: 'unit12' The unit12 folder is missing Create it with mkdir unit12.
TabPFN skipped: ModuleNotFoundError The optional package isn't installed Ignore it, or install with pip install tabpfn.
TabPFN skipped: with a licence, login or token message The TabPFN weights need you to accept their licence Log in when the browser opens, or set TABPFN_TOKEN as described above.
pip install tabpfn times out or reports a proxy or SSL error A company network blocks the download Try another network, or ask IT to allow PyPI. The main race doesn't need TabPFN.
Playground upload rejected The file breaks a playground limit Keep it under 2,048 rows, 100 columns and 5 MB; the script's file already does.

The SAP way

SAP-RPT in the generative AI hub

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:

  1. Access. An SAP BTP subaccount with SAP AI Core on the extended service plan, and a service key for it.
  2. 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.
  3. 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.
  4. 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.
  5. 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-RPT-1.5

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.

TabPFN-3.5 Plus in SAP AI Core

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.

Classic models: PAL, APL and ISLM

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.

Licensing in one place

Option Licence or cost, as of October 2026
SAP-RPT in the generative AI hub Commercial; needs the SAP AI Core extended plan; check your SAP price list per call
TabPFN-3.5 Plus in SAP AI Core Through SAP AI Core; check the terms for your contract
SAP-RPT playground Free demonstration platform; not for production
sap-rpt-1-oss Research and non-commercial use only
tabpfn package Code under Apache 2.0; TabPFN-2.5 and later weights non-commercial; TabPFN-2 weights under the Prior Labs License
PAL, APL, hana-ml Part of SAP HANA; hana-ml under the SAP Developer License Agreement

Build vs. SAP

Need Build (open tools) SAP Choose SAP when
Quick test of whether a column is predictable Your own trees, or local TabPFN for non-commercial tests SAP-RPT or TabPFN in AI Core, or the playground with made-up data The data may be processed in SAP AI Core and you want a commercial licence
Small or changing labelled data Local TabPFN (licence permitting) SAP-RPT or TabPFN-3.5 Plus Almost always, since open weights are non-commercial
Large, stable data at high volume scikit-learn, LightGBM, CatBoost PAL or APL in HANA, ISLM scenarios Data already lives in HANA and must not leave
Explanations for each prediction SHAP on trees, or nearest examples as in Step 4 Explainability sections in the SAP-RPT-1.5 model card Auditors accept SAP's explanation format
Full control over the model Your own pipeline Not offered Never; build it

Production concerns

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.

Pitfalls

  • One-class context. If every context row says "not late", every prediction says "not late". SAP's tutorial lists this symptom.
  • Stale context. Sending the same context file for months throws away the freshness advantage.
  • Too-similar duplicates. Fifty copies of one customer's invoices dominate the vote for that customer, as Step 4 showed with a single row.
  • Order mistakes. SAP's tutorial expects context rows before [PREDICT] rows. Mixed order is a quiet way to get wrong answers.
  • Comparing on accuracy only. The base rate scored 0.574 accuracy in Step 3 while knowing nothing. Use AUC and a business measure.
  • Licence surprises. Prototyping on open weights, then discovering they're non-commercial. Check the licence before the demo, not after.
  • Assuming version stability. The model name moved from SAP-RPT-1 to 1.5 and 1.6 within a year. Pin the version and re-test on upgrade.

Exercise: choose your context on purpose

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.

  1. Copy the script: unit12/tabular_icl.py to unit12/tabular_icl_v2.py.

  2. 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:

    context_random = history.sample(n, random_state=0)
    late = history[history[TARGET] == "yes"].iloc[-(n // 2):]
    on_time = history[history[TARGET] == "no"].iloc[-(n // 2):]
    context_balanced = pd.concat([late, on_time])
  3. 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.

  4. Run it for --context 200 1000.

  5. 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.

  6. 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.

Check yourself

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.

Sources

  • 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
  • Transform structured business data into actionable insights with SAP-RPT-1 (sap.com product page) — classification and regression from example records in the API call; small for low latency and throughput, large for accuracy; playground is a demonstration platform; uploaded playground data deleted after processing
  • 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-3.5 Plus now available in SAP AI Core (SAP announcement, republished by iTWire, 16 September 2026) — TabPFN-3.5 Plus from Prior Labs, an SAP company since July 2026, available in SAP AI Core; in-context learning on raw tables; missing values, mixed types and high-cardinality columns; example use cases cash flow, payment delays, supplier risk, churn
  • 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
  • TabPFN README (PriorLabs/TabPFN on GitHub, via jsDelivr) — TabPFN-3.5 default; create_default_for_version for other versions; up to 1,000,000 rows and 20,000 features for TabPFN-3.5; GPU recommended
  • 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

Sign in to track your progress

We'll email you a one-time sign-in link. No password needed.

or

Tell us a little about you

Optional, every field. It helps us pitch answers to your questions at the right level and decide which topics to write next. It is never shown publicly, and you can change or clear it anytime from the account menu.

SAP areas you work in