Skip to navigation

Configuration

View as Markdown

Configure predictions by passing settings directly to PredictiveQuery.materialize(...) and to client.predict(...). The following sections describe the available settings, their defaults, and how to use them.

There is no run_mode in sdm-client. Set num_context_samples directly on materialize() — the client derives what the endpoint needs from that number instead of a preset.

Neighborhood sampling

Kumo Relational samples a relational neighborhood around each prediction entity. By default, it samples two hops.

num_neighbors controls both the number of hops and the maximum neighbor fan-out at each hop:

  • The number of entries determines the number of hops.
  • Each entry specifies the maximum number of neighbors sampled at that hop. For example, num_neighbors=[16, 8, 4] requests three hops, with fan-outs of 16, 8, and 4.
  • Kumo Relational supports at most six hops.
request = query.materialize(
indices=[870],
num_neighbors=[16, 8, 4],
)

num_hops accepts values from 1 through 6. When you provide num_neighbors, its length determines the sampling depth and takes precedence over num_hops.

Larger fan-outs and deeper neighborhoods can substantially increase sampling time, memory use, and request size.

Context size

num_context_samples sets the number of labeled context rows to gather. Omit it on a relational request and you get the smaller budget, up to 1,000 examples. Ask for more, up to 10,000, and the client uses the wider budget the endpoint offers.

request = query.materialize(
indices=[870],
num_context_samples=500,
)

The number is honored, not rounded up to a bucket. Ask for more than exists and you get what exists; ask for more than 10,000 and you’re told the ceiling.

When narrowing applies to a temporal question, it keeps the newest anchors, not a random sample across time. The engine gathers a block of entities per anchor date, newest first, and a narrowed count slices from the front: the result is full coverage of the most recent anchors and no coverage of older ones, not a thinned sample across the whole span. Entities within one anchor block are sampled, and that part does follow random_seed.

Materialize options

The following are keyword arguments to PredictiveQuery.materialize(...).

SettingMeaning
indicesEntity primary keys for which to generate predictions. Up to 100,000 per call; gathered as several NIM calls automatically when there are more than one call scores.
candidatesThe rows a ranking is over. Required for RANK TOP K; one call per candidate.
taskThe kind of question being asked, for the ambiguous single-table case. Refused on a relational query, which the engine already resolves.
num_context_samplesMaximum labeled context rows to gather.
modelWhich model to gather the request for. Defaults to the schema’s shape.
num_neighborsMaximum neighbor fan-out for each hop.
num_hopsSampling depth, 1 through 6. num_neighbors takes precedence.
anchor_timePrediction reference time.
context_anchor_timeMaximum anchor time for context examples, independent of anchor_time.
use_prediction_timeIncludes the prediction anchor timestamp as an input feature.
lag_timestepsNumber of previous target windows to include as lagged features.
random_seedSeed used for pseudo-random sampling and request materialization.
max_pq_iterationsMaximum iterations used to collect valid labels for context examples.
exclude_cols_dictColumns to leave out of the sampled context, per table.
return_embeddingsIncludes the model embedding for each prediction example, where the deployment produces one.
top_kOverrides the RANK TOP K written in the query, when the query does not set it.
prediction_statisticWhat a regression’s prediction reports of its predicted distribution: "median" (default) or "mean".
rows_per_callHow many entities one call scores. Defaults to the endpoint’s own cap (10,000, or 200 for temporal link prediction) and may only be lowered.
inference_configTask-specific model settings, described below.
import pandas as pd
request = query.materialize(
indices=[870],
num_context_samples=500,
num_neighbors=[16, 8, 4],
anchor_time=pd.Timestamp("2025-06-01T00:00:00Z"),
context_anchor_time=pd.Timestamp("2025-03-01T00:00:00Z"),
use_prediction_time=False,
lag_timesteps=3,
random_seed=7,
)

Unknown materialize options are rejected during materialization, naming the accepted set.

Inference configuration

Pass inference_config as a dictionary. These fields are forwarded to the deployment, and acceptance is deployment dependent — a deployment may reject any value that differs from its own configured default, so check what a deployment accepts before setting any of them. The relational engine defaults to num_estimators=1.

request = query.materialize(indices=[870])
# Only set a field your deployment is known to accept:
request = query.materialize(
indices=[870],
inference_config={
"num_estimators": 1, # must match the deployment's own configured value
},
)

Accepted fields

FieldMeaning
num_estimatorsNumber of estimators in the ensemble.
column_shuffleVaries column order across estimators.
category_shuffleVaries category order across estimators.
hop_shuffleVaries subgraph depth across estimators.
class_shuffleVaries class order across estimators, for classification.

Some deployments accept only their own configured num_estimators value and reject every shuffle flag. Check a deployment’s accepted values before setting these, or omit inference_config to use the deployment’s defaults.

Some deployments also do not accept target_transforms or output_type in inference_config, even though earlier clients exposed them for regression and forecasting quantile control. Ask for outputs=[..., "quantiles"] on client.predict() instead to receive quantile predictions as quantile_<level> columns — there is no client-side control over which quantile levels come back.

Output controls

SettingMeaning
outputsOn client.predict(request, model=..., outputs=[...]). Which output fields to ask for. Defaults to a task-appropriate set.
return_embeddingsOn materialize(). Includes the model embedding associated with each prediction example, where the deployment produces one.
result = client.predict(
request,
model=KumoRelational(),
outputs=["prediction", "probabilities"],
)

Limits

The endpoint enforces several size limits. materialize() checks them before sending and refuses or samples while naming the limit.

LimitModelValueChecked
Context rowsrelational1,000 by default, up to 10,000 with num_context_samplesAt materialize()
Context rowstabular10,000Sampled at materialize(); refused on a direct Request
Rows scored per callrelational10,000, or 200 for temporal link predictionMore are gathered as several calls automatically
Entities one materialize() gathersrelational100,000, in at most 100 callsThis client’s own bound — the endpoint is never asked for more than one call’s worth
Calls a question asked in parts makesrelational200 — one per candidate (ranking) or step (forecast)sdm_client.decompose.MOST_PARTS
Request bodyrelational30 MiB per callAt materialize()
Request body, through Databricks Model Servingrelational16 MB per call, the gateway’s own limitAt send time, on the wrapped envelope
ExplanationrelationalEvery scored entity within ordinary request limits, on a compatible client and deployment; see ExplainabilityBefore sending
Hopsrelational6At materialize()
Tablesrelational15Against the graph the request carries

A request for more entities than one call scores is not refused: materialize() gathers it in batches of at most that many, predict() sends one call per batch, and the results come back as one PredictionResult. A ranking or a forecast is a different kind of “several calls” — one per candidate or step — and is bounded separately by MOST_PARTS (200); it cannot additionally be batched, so a ranking over 200 candidates and 50,000 entities is refused rather than silently sent as 200 × 50 calls.

Pass rows_per_call to ask in smaller batches than the default; it only ever goes down. Reach for it when a graph is wide enough that a full batch outruns the 30 MiB body, or your deployment’s cap is lower than this client’s default.

A call that fails ends the request, but the answers already paid for are not discarded: they’re carried on the error as details['answered'] and details['rows_answered'] (or details['parts_answered'] for a forecast), so you can ask again for only the rest.

Choose settings conservatively

Start with the defaults. Change one dimension at a time and measure the effect on latency, request size, and prediction quality with representative data.

If a request becomes too large, first reduce num_neighbors or num_context_samples, then rows_per_call. You can also remove unused columns or tables. See Batch Prediction for scoring jobs beyond what one materialize() call handles.