structured-data-models-client 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sdm_client/__init__.py +70 -0
- sdm_client/_version.py +5 -0
- sdm_client/adapters/__init__.py +9 -0
- sdm_client/adapters/relational.py +870 -0
- sdm_client/adapters/tabular.py +580 -0
- sdm_client/base.py +300 -0
- sdm_client/client.py +1416 -0
- sdm_client/core/__init__.py +5 -0
- sdm_client/core/databricks_serving.py +611 -0
- sdm_client/core/databricks_user_agent.py +76 -0
- sdm_client/core/dates.py +278 -0
- sdm_client/core/transport.py +898 -0
- sdm_client/core/warnings.py +51 -0
- sdm_client/decompose.py +421 -0
- sdm_client/errors.py +177 -0
- sdm_client/materializers/__init__.py +10 -0
- sdm_client/materializers/relational.py +1786 -0
- sdm_client/materializers/sources.py +331 -0
- sdm_client/materializers/tabular.py +339 -0
- sdm_client/models.py +251 -0
- sdm_client/py.typed +0 -0
- sdm_client/query.py +647 -0
- sdm_client/request.py +1127 -0
- sdm_client/result.py +328 -0
- sdm_client/schema.py +3187 -0
- sdm_client/wire/__init__.py +46 -0
- sdm_client/wire/answers.py +80 -0
- sdm_client/wire/dtypes.py +701 -0
- sdm_client/wire/keys.py +363 -0
- sdm_client/wire/tables.py +50 -0
- structured_data_models_client-1.0.0.dist-info/METADATA +99 -0
- structured_data_models_client-1.0.0.dist-info/RECORD +36 -0
- structured_data_models_client-1.0.0.dist-info/WHEEL +5 -0
- structured_data_models_client-1.0.0.dist-info/licenses/LICENSE +204 -0
- structured_data_models_client-1.0.0.dist-info/licenses/NOTICE +10 -0
- structured_data_models_client-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
r"""The Universal TFM wire format, in one place.
|
|
6
|
+
|
|
7
|
+
Every model posts the same envelope to ``POST /v1/predictions``; the server
|
|
8
|
+
tells them apart by the ``model`` field alone. These modules own the parts of
|
|
9
|
+
that envelope that are the same whatever the model is: how a column's dtype is
|
|
10
|
+
named, how a cell is written, and how a frame becomes an ``arrays`` table.
|
|
11
|
+
|
|
12
|
+
The boundary is DataFrames plus plain metadata. Nothing here knows what a
|
|
13
|
+
graph, a subgraph or a predictive query is, which is what lets the relational
|
|
14
|
+
path reach the same code: its adapter samples first and arrives with frames
|
|
15
|
+
like any other.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from sdm_client.wire.dtypes import (
|
|
19
|
+
JSON_SAFE_INT_MAX,
|
|
20
|
+
JSON_SAFE_INT_MIN,
|
|
21
|
+
UNTYPED,
|
|
22
|
+
as_declared,
|
|
23
|
+
infer_tfm_dtype,
|
|
24
|
+
is_container,
|
|
25
|
+
json_ready,
|
|
26
|
+
require_scalar_cells,
|
|
27
|
+
serialize_cell,
|
|
28
|
+
serialize_column,
|
|
29
|
+
widen_tfm_dtype,
|
|
30
|
+
)
|
|
31
|
+
from sdm_client.wire.tables import encode_table
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
'JSON_SAFE_INT_MAX',
|
|
35
|
+
'JSON_SAFE_INT_MIN',
|
|
36
|
+
'UNTYPED',
|
|
37
|
+
'as_declared',
|
|
38
|
+
'encode_table',
|
|
39
|
+
'infer_tfm_dtype',
|
|
40
|
+
'is_container',
|
|
41
|
+
'json_ready',
|
|
42
|
+
'require_scalar_cells',
|
|
43
|
+
'serialize_cell',
|
|
44
|
+
'serialize_column',
|
|
45
|
+
'widen_tfm_dtype',
|
|
46
|
+
]
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
r"""Giving an answer back in the type the question was asked in.
|
|
6
|
+
|
|
7
|
+
Both models answer with a class label the column already held, and both
|
|
8
|
+
spell it on the wire as text. Restoring the column's own type is therefore
|
|
9
|
+
about the response rather than about either model, and lives here rather
|
|
10
|
+
than with one of them.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import dataclasses
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from sdm_client.wire.dtypes import as_declared
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def as_declared_target(items: list[Any], payload: dict[str, Any] | None):
|
|
22
|
+
r"""Give `prediction` back as the type the target column holds.
|
|
23
|
+
|
|
24
|
+
One endpoint renders a class label as a string and the other sends the
|
|
25
|
+
number, for the same question on the same integer-coded target. Both are
|
|
26
|
+
legal and the values agree; only their types differ, so
|
|
27
|
+
|
|
28
|
+
sum(1 for row in result if row.prediction == 0)
|
|
29
|
+
|
|
30
|
+
This counted one of five on one model and none of five on the other. There
|
|
31
|
+
was no exception, only a plausible-looking answer wrong in the direction
|
|
32
|
+
of "this class never occurs". The target's dtype is on the wire, so it is
|
|
33
|
+
read back with it.
|
|
34
|
+
|
|
35
|
+
Class labels only, and a `> 0` target's labels are a bool, which one
|
|
36
|
+
endpoint spells `'false'`. They are values the column held, so giving them
|
|
37
|
+
back as the column's own type is restoring something. A regression's answer
|
|
38
|
+
is a number the model computed. Rounding it to the target's dtype would
|
|
39
|
+
make this client decide how precise the answer is.
|
|
40
|
+
|
|
41
|
+
The same labels key `probabilities` and are restored there too.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
task = (payload or {}).get('task', {})
|
|
45
|
+
if 'classification' not in str(task.get('kind', '')):
|
|
46
|
+
return items
|
|
47
|
+
dtype = task.get('target', {}).get('dtype')
|
|
48
|
+
if not dtype or not dtype.startswith(('int', 'float', 'bool')):
|
|
49
|
+
return items
|
|
50
|
+
read = []
|
|
51
|
+
for item in items:
|
|
52
|
+
changed: dict[str, Any] = {}
|
|
53
|
+
value = getattr(item, 'prediction', None)
|
|
54
|
+
restored = as_declared(value, dtype)
|
|
55
|
+
if restored is not value:
|
|
56
|
+
changed['prediction'] = restored
|
|
57
|
+
given = getattr(item, 'probabilities', None)
|
|
58
|
+
if given:
|
|
59
|
+
keyed = _as_the_column_spells_them(given, dtype)
|
|
60
|
+
if keyed != given:
|
|
61
|
+
changed['probabilities'] = keyed
|
|
62
|
+
read.append(dataclasses.replace(item, **changed) if changed else item)
|
|
63
|
+
return read
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _as_the_column_spells_them(
|
|
67
|
+
probabilities: dict[str, Any], dtype: str
|
|
68
|
+
) -> dict[str, Any]:
|
|
69
|
+
r"""The probability mapping, keyed by each class as the column spells it.
|
|
70
|
+
|
|
71
|
+
One endpoint keys a bool target `'false'` and the other `'False'`, so
|
|
72
|
+
`frame['probability_true']` raises depending on which answered.
|
|
73
|
+
|
|
74
|
+
Left alone where restoring would collapse two classes into one.
|
|
75
|
+
"""
|
|
76
|
+
renamed = {}
|
|
77
|
+
for key, value in probabilities.items():
|
|
78
|
+
restored = as_declared(key, dtype)
|
|
79
|
+
renamed[key if restored is key else str(restored)] = value
|
|
80
|
+
return renamed if len(renamed) == len(probabilities) else probabilities
|