> This page is for Structured Data Models Client.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/sdgm/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/sdgm/_mcp/server.

# Migrate to structured-data-models-client

> Update package names, imports, and API calls to move from kumo-relational-client to structured-data-models-client

**structured-data-models-client** (imported as `sdm_client`) is NVIDIA's open-source client for structured-data foundation model NIMs, serving both Kumo Relational and Kumo Tabular from one package. This page covers moving Kumo Relational usage from `kumo-relational-client` 1.0 to `structured-data-models-client` 1.0.

## Update package names

| Earlier package          | New package                                | New import              |
| ------------------------ | ------------------------------------------ | ----------------------- |
| `kumo-relational-client` | `structured-data-models-client`            | `sdm_client`            |
| `kumo-relational-engine` | `structured-data-models-relational-engine` | `sdm_relational_engine` |
| `kumo-connectors`        | `structured-data-models-connectors`        | `sdm_connectors`        |

```bash
pip install "structured-data-models-client[relational]"
```

**What you install is not what you import.** The five distributions are `structured-data-models-client`, `-api`, `-pql`, `-connectors`, and `-relational-engine`; the modules are `sdm_client`, `sdm_api`, `predictive_query`, `sdm_connectors`, and `sdm_relational_engine`. They are released together on one version and pin each other exactly.

## Update imports and the client class

```python
# Before
from kumo_relational_client import RelationalClient, relational

graph = relational.Graph.from_data(tables)

with RelationalClient(url="http://localhost:8000") as client:
    result = client.relational(graph).predict(query, run_mode="fast", indices=entity_ids)
```

```python
# After
from sdm_client import SDMClient, Schema, PredictiveQuery, KumoRelational

schema = Schema.from_data(tables)
q = PredictiveQuery(query, schema)
request = q.materialize(indices=entity_ids)

with SDMClient(url="http://localhost:8000") as client:
    result = client.predict(request, model=KumoRelational())
```

The model is now an argument to one `predict()` method, not a method name — `client.relational(graph)` is gone. Gathering the request (`materialize()`) is now a separate, explicit step from sending it (`predict()`).

## `Graph` becomes `Schema`

`relational.Graph` is renamed `Schema`, and it is the same type Kumo Tabular uses — one table is the degenerate case, not a second system.

| Before                                                 | After                                                                                                     |
| ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |
| `Graph.from_data(tables)`                              | `Schema.from_data(tables)`                                                                                |
| `Graph.from_snowflake(...)`, `.from_sqlite(...)`, etc. | `Schema.from_source(SnowflakeSource(...), ...)` and similar — **restored**, see below                     |
| `graph["table"]`                                       | `schema["table"]`                                                                                         |
| `graph.link(src_table=, fkey=, dst_table=)`            | `schema.relationships.append(Relationship(source_table=, source_column=, target_table=, target_column=))` |
| `graph.unlink(...)`                                    | Filter `schema.relationships` and reassign it                                                             |
| `graph.print_metadata()`, `graph.print_links()`        | `print(schema)`, `schema.tables`, `schema.relationships`                                                  |
| `graph.validate()`                                     | `schema.check_consistent()`                                                                               |
| `graph.visualize(...)`                                 | Not available                                                                                             |

## Connector-backed (in-place) sampling is back, under `Schema.from_source`

Earlier releases of `structured-data-models-client` temporarily dropped warehouse-sampled graphs; **`Schema.from_source` restores them** as of 1.0.0. If you built against a pre-release snapshot that only had `Schema.from_data`, revisit any workaround that read whole tables into memory — you likely don't need it anymore:

```python
# Before (kumo-relational-client)
graph = relational.Graph.from_snowflake(database="MY_DATABASE", schema="MY_SCHEMA")
```

```python
# After
import snowflake.connector
from sdm_client import Schema
from sdm_connectors import SnowflakeSource

connection = snowflake.connector.connect(account="...", user="...", password="...", warehouse="...")
source = SnowflakeSource(connection, database="MY_DATABASE", schema="MY_SCHEMA")
schema = Schema.from_source(source)   # tables={} means "every table the source holds"
```

Only `materialize()`'s sampled rows leave the source. `DatabricksSource`, `SnowflakeSource`, `PostgresSource`, `DuckDBSource`, and `SQLiteSource` cover the connector pages under **Connect Data Sources**, each with its own backend-specific connection. `DatabricksSource` and `SnowflakeSource` also convert a metric view or semantic view directly with `source.semantic_model(name)`.

If you still only have DataFrames, `Schema.from_data()` is unchanged — read a table first with `sdm_connectors.read()`, then build the schema from the result.

## `run_mode` is gone

Set `num_context_samples` on `materialize()` instead.

```python
# Before
client.relational(graph).predict(query, run_mode="best", indices=ids)
```

```python
# After
request = query_obj.materialize(indices=ids, num_context_samples=5000)
client.predict(request, model=KumoRelational())
```

## Batching is now automatic — `batch_size` and `num_retries` are gone

`predict(..., batch_size="max", num_retries=3)` used to auto-split a large job into batches. `materialize()` now does this for you: pass up to 100,000 entities and it gathers them as up to 100 calls, each sent by `predict()` and merged into one result. See [Batch Prediction](/sdgm/kumo-relational/user-guide/configure-and-scale/batch-prediction). You only chunk `indices` yourself past the 100,000-entity ceiling, which is a different, much higher threshold than the earlier 1,000-per-call limit.

`SDMClient(max_retries=...)` still retries a transient transport failure per call.

## `predict_task()` has no relational equivalent yet

Supplying your own labeled context directly, bypassing PQL, is not currently supported for the relational model — the direct `Request()` builder is refused for `model=KumoRelational()` even with `related_tables=`. See [Predict with Custom Context](/sdgm/kumo-relational/user-guide/make-predictions/custom-context).

## Ranking now requires `candidates=`, and runs as one call per candidate

```python
request = query.materialize(indices=[1, 2, 3], candidates=items["item_id"])
result = client.predict(request, model=KumoRelational())
result.unscored_candidates  # candidates no entity's context could score
```

A candidate your sampled context can't answer is left out of every row rather than failing the whole ranking — see [Prediction Results](/sdgm/kumo-relational/user-guide/work-with-prediction-results/prediction-results#a-ranking-can-hold-fewer-than-k-candidates). Consider the "rank over a table of pairs" pattern instead, which runs as one classification request, batched if needed, regardless of candidate count.

## Result field names changed

| Before (uppercase)                         | After                                                                                                                   |
| ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
| `ENTITY`                                   | `id` (present when the model has an entity key)                                                                         |
| —                                          | `row_index` (always present; join on this when there is no key)                                                         |
| `PREDICTION`                               | `prediction` — the **median** of the predicted distribution by default; pass `prediction_statistic="mean"` for the mean |
| `ANCHOR_TIMESTAMP`                         | Not a DataFrame column; pass `anchor_time` to `materialize()` if you need it recorded downstream                        |
| `<CLASS>_PROB`                             | `probability_<class>`                                                                                                   |
| `CLASS`, `SCORE`, `PREDICTED` (multiclass) | One row per entity, with `probability_<class>` columns instead of one row per class                                     |
| `TARGET_PRED` (legacy)                     | `prediction`                                                                                                            |

See [Prediction Results](/sdgm/kumo-relational/user-guide/work-with-prediction-results/prediction-results) for the current field-by-field mapping.

## `Dtype` / `Stype` are strings now

```python
# Before
from kumo_relational_client import relational
table["customer_id"].stype = relational.Stype.ID

# After
schema["customers"]["customer_id"].stype = "ID"
```

`ViewConversionWarning` and `graph.conversion_messages` are replaced by `schema.conversion_notes`, populated when the schema came from `source.semantic_model(...)`.

## `end_time_column` is gone

`Table` has only `time_column`. If your workflow relied on `end_time_column` to represent row validity intervals, that is not currently supported.

## Explanation support depends on your deployment

The upcoming explainability API uses `result.explanations.gradients` for per-entity gradients and `result.explanations.columns` for labelled-context cohorts.
The singular `result.explanation` property is deprecated.
The client does not generate a natural-language summary.
See [Prediction Explainability](/sdgm/kumo-relational/user-guide/explain-predictions/prediction-explainability) for the compatible-deployment requirement and examples.

## Databricks Model Serving has a new entry point

```python
# Before
RelationalClient.for_databricks_serving(endpoint="<endpoint-name>", workspace_client=workspace_client)

# After
SDMClient.for_databricks_serving("kumo-relational", workspace_client=workspace_client)
```

There is no `for_snowflake_serving` in `structured-data-models-client`.

## Update the model identifier

The only accepted model identifier is still `kumo-relational`:

```python
client.capabilities(KumoRelational())
```

## Update error handling

```python
from sdm_client import StructuredDataError

try:
    result = client.predict(request, model=KumoRelational())
except StructuredDataError as error:
    print(error.code)
```

`RelationalError` becomes `StructuredDataError`. `UnknownModelError`, `UnsupportedRequestError`, and `MissingExtraError` are subclasses with the same intent as before. See [Python SDK API](/sdgm/kumo-relational/reference/python-api-reference#errors).

## What is unchanged

* The PQL grammar, including `WHERE`, `ASSUMING`, `FORECAST N TIMEFRAMES`, and `RANK TOP K` (still capped at 20 candidates ranked).
* Task kinds: classification, binary/multiclass classification, regression, forecasting, and temporal link prediction are all still supported.
* The underlying Universal TFM REST contract — see [NIM REST API](/sdgm/kumo-relational/reference/nim-rest-api).
* Automatic removal of context rows that would answer the question being scored.

After updating, run a representative prediction for each task type and verify the task-dependent result shapes described in [Prediction Results](/sdgm/kumo-relational/user-guide/work-with-prediction-results/prediction-results). The client's own [changelog](https://github.com/NVIDIA/structured-data-models-client/blob/main/CHANGELOG.md) and [stability contract](https://github.com/NVIDIA/structured-data-models-client/blob/main/docs/reference/stability.md) are the canonical source for anything this page doesn't cover.