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.
@@ -0,0 +1,363 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES.
2
+ # All rights reserved.
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ r"""Which rows an encoded request is about.
6
+
7
+ The keys are in the envelope rather than in anything the caller still holds,
8
+ and the two models write them differently: a relational envelope carries an
9
+ opaque entity reference, a tabular one the caller's own key column. Reading
10
+ that is a question about the wire, so it is answered here rather than by the
11
+ client, which should have to ask only whether two sets of rows overlap.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import itertools
17
+ from collections.abc import Iterable, Mapping, Sequence
18
+ from decimal import Decimal
19
+ from numbers import Real
20
+ from typing import TYPE_CHECKING, Any
21
+
22
+ import pandas as pd
23
+
24
+ from sdm_client.schema import _as_key_tuple
25
+
26
+ if TYPE_CHECKING:
27
+ from sdm_client.request import Request
28
+
29
+ #: What a relational envelope calls the moment, when nothing of the caller's
30
+ #: is called that already.
31
+ _ANCHOR = '__kumo_anchor_time'
32
+
33
+ #: How a relational envelope names the entity a row is about.
34
+ _ENTITY_REF = '__kumo_entity_ref'
35
+
36
+ #: What ties a related row to the instance it hangs off.
37
+ INSTANCE_ID = 'instance_id'
38
+
39
+ #: Counts the rows that name no entity, so that no two of them are one.
40
+ _UNIDENTIFIED = itertools.count()
41
+
42
+
43
+ def _anchor_column(payload: dict[str, Any] | None) -> str:
44
+ r"""What this envelope calls the moment.
45
+
46
+ Read from the task rather than assumed. The engine suffixes the name when
47
+ a column of the caller's own is called that, so looking only for the bare
48
+ name goes blind on exactly the graphs that have one.
49
+ """
50
+ task = (payload or {}).get('task')
51
+ named = (
52
+ task.get('anchor_time_column') if isinstance(task, Mapping) else None
53
+ )
54
+ return str(named) if named else _ANCHOR
55
+
56
+
57
+ def rows_in(
58
+ payload: dict[str, Any] | None, half: str, *, with_anchor: bool = False
59
+ ) -> list[Any]:
60
+ r"""The key of every row one half of a wire envelope holds, one per row.
61
+
62
+ The row-wise reading. :func:`keys_in` is this without the duplicates, and
63
+ is what a question about overlap wants; a sentence that counts rows wants
64
+ this, because two rows about one entity are two rows.
65
+ """
66
+ if not isinstance(payload, dict):
67
+ return []
68
+ table = (payload.get(half) or {}).get('instance_table') or {}
69
+ columns = table.get('columns')
70
+ rows = table.get('rows')
71
+ if not isinstance(columns, list) or not isinstance(rows, list):
72
+ return []
73
+ named: Any = next(
74
+ (name for name in columns if str(name).startswith(_ENTITY_REF)),
75
+ None,
76
+ )
77
+ if named is None:
78
+ named = ((payload.get('schema') or {}).get('instance_table') or {}).get(
79
+ 'primary_key'
80
+ )
81
+ if named is None:
82
+ return []
83
+ wanted = named if isinstance(named, list) else [named]
84
+ if any(name not in columns for name in wanted):
85
+ return []
86
+ at = [columns.index(name) for name in wanted]
87
+ if with_anchor:
88
+ anchor = _anchor_column(payload)
89
+ if anchor in columns:
90
+ at = [*at, columns.index(anchor)]
91
+ return [
92
+ _named_by(row[position] for position in at)
93
+ for row in rows
94
+ if isinstance(row, list) and len(row) > max(at)
95
+ ]
96
+
97
+
98
+ def keys_in(
99
+ payload: dict[str, Any] | None, half: str, *, with_anchor: bool = False
100
+ ) -> frozenset[Any]:
101
+ r"""The entity keys one half of a wire envelope holds.
102
+
103
+ Kept when a session is opened, and only the keys: the context itself is
104
+ deliberately not kept, since holding a second encoded copy of a whole
105
+ dataset for the life of the handle is what `pinned` exists to avoid. A set
106
+ of keys is small enough to keep and is the only thing needed to notice that
107
+ a later call scores rows the context already answers.
108
+
109
+ The key is named in the schema rather than in the rows, and the two models
110
+ name it differently: a relational envelope carries an opaque entity
111
+ reference, a tabular one carries the caller's own key column.
112
+
113
+ `with_anchor` puts the moment into the key. An entity asked about over a
114
+ window is a different question at a different moment, so the context has
115
+ only answered the one it was asked at; an entity whose answer is a
116
+ standing value has been given away at any moment at all.
117
+ """
118
+ return frozenset(rows_in(payload, half, with_anchor=with_anchor))
119
+
120
+
121
+ def _named_by(values: Iterable[Any]) -> str:
122
+ r"""One way of writing a key, whichever side of the wire it came from.
123
+
124
+ A pinned key is read from the encoded payload and a scored key from the
125
+ frame, and the two disagree about the same value: an integer past the
126
+ JS-safe range travels as a base-10 string by contract, so
127
+ `9007199254740993` on one side and `'9007199254740993'` on the other would
128
+ never meet, and the warning would go quiet on precisely the warehouse keys
129
+ it matters most for.
130
+
131
+ A whole number written as a float is the same entity as the integer: a
132
+ context typed `int64` pins entity `1` and a scored frame of floats sends
133
+ `1.0`, and the endpoint answers the second with the first's id. Comparing
134
+ the spellings called that two entities, and a session scored rows its
135
+ context answers without saying so.
136
+
137
+ A composite key becomes a tuple of its parts rather than nothing at all,
138
+ which is what reading only single-column keys amounted to.
139
+ """
140
+ named = tuple(values)
141
+ if any(_is_missing(value) for value in named):
142
+ # A row with no identifier is not the same row as another with no
143
+ # identifier. Named alike, two unidentified rows were reported as one
144
+ # entity leaking into the other, and `holdout=[None]` took every
145
+ # unidentified row out of the context at once.
146
+ return f'<unidentified {next(_UNIDENTIFIED)}>'
147
+ return repr(tuple(_one_spelling(value) for value in named))
148
+
149
+
150
+ def _is_missing(value: Any) -> bool:
151
+ r"""Whether this value names no entity at all."""
152
+ if value is None:
153
+ return True
154
+ try:
155
+ return bool(pd.isna(value))
156
+ except (ArithmeticError, TypeError, ValueError):
157
+ # A signalling NaN raises rather than comparing. It names no entity
158
+ # either, and asking whether it does must not be the thing that fails.
159
+ return True
160
+
161
+
162
+ def _one_spelling(value: Any) -> str:
163
+ r"""A value as the name of an entity, not as the type that carried it.
164
+
165
+ Every real number is read as the number it is, rather than a list of the
166
+ types that can hold one: ``1``, ``1.0``, a numpy float of any width and
167
+ ``Decimal("1.00")`` all name entity 1, and ``Decimal("1.50")`` names the
168
+ same entity as ``1.5``. Written by type instead, a holdout given as a
169
+ ``float32`` removed nothing and a trailing zero made two names for one row.
170
+
171
+ Anything that is not a number is left as it is written. A key column of
172
+ text has ``"1.50"`` and ``"1.5"`` as two different entities, and reading
173
+ them as numbers would merge two rows the schema says are distinct.
174
+ """
175
+ if isinstance(value, bool) or not isinstance(value, (Real, Decimal)):
176
+ return str(value)
177
+ try:
178
+ exact = Decimal(str(value))
179
+ # A name is not arithmetic. An infinity or a NaN is not an entity and
180
+ # cannot be read as one, and asking anyway raised `OverflowError` and
181
+ # `InvalidOperation` out of a function whose whole job is to write a
182
+ # name. Thus, a holdout naming one failed the call before it was sent.
183
+ if not exact.is_finite():
184
+ return str(value)
185
+ if exact == exact.to_integral_value():
186
+ return str(int(exact))
187
+ return str(exact.normalize())
188
+ except (ArithmeticError, ValueError):
189
+ return str(value)
190
+
191
+
192
+ def key_dtypes(
193
+ table: Any, key: Sequence[str], widened_by: Any = None
194
+ ) -> dict[str, str] | None:
195
+ r"""One wire dtype per key column, settled the way the adapter settles it.
196
+
197
+ One per column, not one per half. Settled separately for each frame, the
198
+ same entity came out ``1.0`` on the side read from the context and ``1``
199
+ on the side read from the rows being scored, and the comparison between
200
+ them reported overlaps the encoded request does not have.
201
+
202
+ ``None`` where no dtype can be settled. Every caller is a warning, and a
203
+ warning is never worth a failure.
204
+ """
205
+ from sdm_client.adapters.tabular import _raw_dtype_of
206
+ from sdm_client.wire import UNTYPED, widen_tfm_dtype
207
+
208
+ try:
209
+ dtypes = {}
210
+ for name in key:
211
+ column = table.columns[name]
212
+ dtype = UNTYPED if column.untyped else column.dtype
213
+ if widened_by is not None and name in widened_by.columns:
214
+ dtype = widen_tfm_dtype(
215
+ (dtype, _raw_dtype_of(widened_by[name], dtype))
216
+ )
217
+ dtypes[name] = dtype or 'string'
218
+ return dtypes
219
+ except Exception: # noqa: BLE001 - a warning is never worth a failure
220
+ return None
221
+
222
+
223
+ def keys_from_a_frame(
224
+ frame: Any, key: Sequence[str], dtypes: Mapping[str, str] | None
225
+ ) -> list[Any]:
226
+ r"""``frame``'s keys, written the way the wire will write them.
227
+
228
+ The other side of every comparison here is read from an encoded envelope,
229
+ so a frame read raw does not meet it: an entity held as ``1.0`` in a frame
230
+ is ``1`` on the wire, and the two would never match. This is how a
231
+ session came to score rows its pinned context answers without saying so.
232
+
233
+ Through the encoder that writes the request, given the dtypes
234
+ :func:`key_dtypes` settled for the request as a whole. Empty where there
235
+ are none or the encoding fails: every caller is a warning, and a warning
236
+ is never worth a failure.
237
+ """
238
+ from sdm_client.wire.tables import encode_table
239
+
240
+ if dtypes is None:
241
+ return []
242
+ try:
243
+ encoded = encode_table(
244
+ frame[list(key)], {name: dtypes[name] for name in key}
245
+ )
246
+ except Exception: # noqa: BLE001 - a warning is never worth a failure
247
+ return []
248
+ return [_named_by(row) for row in encoded['rows']]
249
+
250
+
251
+ def scored_rows(
252
+ request: Request,
253
+ *,
254
+ with_anchor: bool = False,
255
+ dtypes: Mapping[str, str] | None = None,
256
+ ) -> list[Any]:
257
+ r"""The key of every row a request asks about, one per row.
258
+
259
+ A sentence that counts rows has to be given rows: two rows asking about
260
+ one entity are two rows, and a set of keys had already lost one of them.
261
+
262
+ ``dtypes`` come from the other side of the comparison, namely a session's
263
+ pinned schema. Settled here instead, the same entity has
264
+ one name on each side and neither meets the other.
265
+ """
266
+ if request.payloads:
267
+ return [
268
+ named
269
+ for payload in request.payloads
270
+ for named in rows_in(payload, 'predict', with_anchor=with_anchor)
271
+ ]
272
+ frame = getattr(request.predict, 'instance_table', None)
273
+ table = request.schema[request.entity_table] if request.schema else None
274
+ names = _as_key_tuple(getattr(table, 'primary_key', None))
275
+ if frame is None or not names or any(n not in frame for n in names):
276
+ return []
277
+ if dtypes is None:
278
+ dtypes = key_dtypes(table, names, frame)
279
+ return keys_from_a_frame(frame, names, dtypes)
280
+
281
+
282
+ def scored_keys(
283
+ request: Request, *, with_anchor: bool = False
284
+ ) -> frozenset[Any]:
285
+ r"""The keys a request is asking about.
286
+
287
+ Read from the envelopes where there are any, and from the frame where
288
+ there are not. Only the first can carry a moment: a request holding
289
+ frames is a single-table one, and a single-table model is not asked for a
290
+ window over time. The query is refused before it reaches here, so there
291
+ is no moment in the frame to put in the key and none to look for.
292
+ """
293
+ return frozenset(scored_rows(request, with_anchor=with_anchor))
294
+
295
+
296
+ def without_the_keys(payload: dict[str, Any], keys: frozenset[Any]) -> int:
297
+ r"""Take out of ``payload``'s context every row naming one of ``keys``.
298
+
299
+ Returns how many instance rows went. Related rows that hang off them go
300
+ too, by the ``instance_id`` they were tied with, because a related row
301
+ whose instance has gone describes nothing.
302
+
303
+ The key is found the way `keys_in` finds it, so what is removed here is
304
+ exactly what would have been reported as an overlap there.
305
+ """
306
+ context = payload.get('context')
307
+ if not isinstance(context, Mapping) or not keys:
308
+ return 0
309
+ table = context.get('instance_table')
310
+ columns = (table or {}).get('columns')
311
+ rows = (table or {}).get('rows')
312
+ if not isinstance(columns, list) or not isinstance(rows, list):
313
+ return 0
314
+
315
+ named = next(
316
+ (name for name in columns if str(name).startswith(_ENTITY_REF)), None
317
+ )
318
+ if named is None:
319
+ named = ((payload.get('schema') or {}).get('instance_table') or {}).get(
320
+ 'primary_key'
321
+ )
322
+ wanted = named if isinstance(named, list) else [named]
323
+ if named is None or any(name not in columns for name in wanted):
324
+ return 0
325
+
326
+ at = [columns.index(name) for name in wanted]
327
+ instance = columns.index(INSTANCE_ID) if INSTANCE_ID in columns else None
328
+ going, leaving = [], set()
329
+ for row in rows:
330
+ if not isinstance(row, list) or len(row) <= max(at):
331
+ going.append(row)
332
+ continue
333
+ if _named_by(row[position] for position in at) in keys:
334
+ if instance is not None and len(row) > instance:
335
+ leaving.add(row[instance])
336
+ continue
337
+ going.append(row)
338
+
339
+ removed = len(rows) - len(going)
340
+ if not removed:
341
+ return 0
342
+ table['rows'] = going
343
+ for related in (context.get('related_tables') or {}).values():
344
+ _drop_instances(related, leaving)
345
+ return removed
346
+
347
+
348
+ def _drop_instances(table: Any, instances: set[Any]) -> None:
349
+ r"""Remove related rows tied to removed instances."""
350
+ columns = (table or {}).get('columns')
351
+ rows = (table or {}).get('rows')
352
+ if not isinstance(columns, list) or not isinstance(rows, list):
353
+ return
354
+ if INSTANCE_ID not in columns or not instances:
355
+ return
356
+ at = columns.index(INSTANCE_ID)
357
+ table['rows'] = [
358
+ row
359
+ for row in rows
360
+ if not (
361
+ isinstance(row, list) and len(row) > at and row[at] in instances
362
+ )
363
+ ]
@@ -0,0 +1,50 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES.
2
+ # All rights reserved.
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ r"""Turning a DataFrame into the wire's ``arrays`` table."""
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any
10
+
11
+ import pandas as pd
12
+
13
+ from sdm_client.wire.dtypes import serialize_column
14
+
15
+
16
+ def encode_table(frame: pd.DataFrame, dtypes: dict[str, str]) -> dict[str, Any]:
17
+ r"""Encode ``frame`` as an ``arrays`` table.
18
+
19
+ ``arrays`` is the only table format the contract defines today: an ordered
20
+ list of column names and a list of row arrays positionally matching it.
21
+ Arrow IPC is a reserved extension point everywhere in this system and is
22
+ not implemented.
23
+
24
+ Serialization is per column rather than per cell, because a column has one
25
+ dtype and the rule for writing a value follows from it. The rows are then
26
+ transposed out of the serialized columns, which is what keeps a column's
27
+ values consistently written even where a row-major walk would see mixed
28
+ types.
29
+
30
+ A frame with no columns still carries its row count, so an empty-column
31
+ table is a list of empty rows rather than an empty table. The two mean
32
+ different things to the server.
33
+
34
+ Args:
35
+ frame: The rows to encode.
36
+ dtypes: Wire dtype per column name. Must cover every column of
37
+ ``frame``.
38
+ """
39
+ columns = list(frame.columns)
40
+ if not columns:
41
+ return {
42
+ 'format': 'arrays',
43
+ 'columns': [],
44
+ 'rows': [[] for _ in range(len(frame))],
45
+ }
46
+ serialized_columns = [
47
+ serialize_column(frame[column], dtypes[column]) for column in columns
48
+ ]
49
+ rows = [list(row) for row in zip(*serialized_columns, strict=True)]
50
+ return {'format': 'arrays', 'columns': columns, 'rows': rows}
@@ -0,0 +1,99 @@
1
+ Metadata-Version: 2.4
2
+ Name: structured-data-models-client
3
+ Version: 1.0.0
4
+ Summary: One Python client for NVIDIA's structured-data foundation model NIMs
5
+ Author: NVIDIA
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/NVIDIA/structured-data-models-client
8
+ Project-URL: Source, https://github.com/NVIDIA/structured-data-models-client
9
+ Project-URL: Issues, https://github.com/NVIDIA/structured-data-models-client/issues
10
+ Project-URL: Changelog, https://github.com/NVIDIA/structured-data-models-client/blob/main/CHANGELOG.md
11
+ Keywords: structured-data,foundation-model,tabular,relational
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Programming Language :: Python :: 3 :: Only
22
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ License-File: NOTICE
28
+ Requires-Dist: structured-data-models-api==1.0.0
29
+ Requires-Dist: structured-data-models-pql==1.0.0
30
+ Requires-Dist: pandas>=2.2.2
31
+ Requires-Dist: numpy>=1.23
32
+ Requires-Dist: requests>=2.28
33
+ Requires-Dist: urllib3>=1.26
34
+ Provides-Extra: relational
35
+ Requires-Dist: structured-data-models-relational-engine==1.0.0; extra == "relational"
36
+ Provides-Extra: sqlite
37
+ Requires-Dist: structured-data-models-connectors[sqlite]==1.0.0; extra == "sqlite"
38
+ Provides-Extra: duckdb
39
+ Requires-Dist: structured-data-models-connectors[duckdb]==1.0.0; extra == "duckdb"
40
+ Provides-Extra: snowflake
41
+ Requires-Dist: structured-data-models-connectors[snowflake]==1.0.0; extra == "snowflake"
42
+ Provides-Extra: databricks
43
+ Requires-Dist: structured-data-models-connectors[databricks]==1.0.0; extra == "databricks"
44
+ Provides-Extra: postgres
45
+ Requires-Dist: structured-data-models-connectors[postgres]==1.0.0; extra == "postgres"
46
+ Provides-Extra: lakebase
47
+ Requires-Dist: structured-data-models-connectors[lakebase]==1.0.0; extra == "lakebase"
48
+ Provides-Extra: s3
49
+ Requires-Dist: structured-data-models-connectors[s3]==1.0.0; extra == "s3"
50
+ Provides-Extra: databricks-serving
51
+ Requires-Dist: databricks-sdk<1.0,>=0.68.0; extra == "databricks-serving"
52
+ Provides-Extra: all
53
+ Requires-Dist: structured-data-models-client[databricks,duckdb,lakebase,postgres,relational,s3,snowflake,sqlite]; extra == "all"
54
+ Provides-Extra: test
55
+ Requires-Dist: pytest; extra == "test"
56
+ Requires-Dist: requests-mock; extra == "test"
57
+ Dynamic: license-file
58
+
59
+ # structured-data-models-client
60
+
61
+ One Python client for NVIDIA's structured-data foundation models. Ask a
62
+ predictive question of one table, or of a whole relational schema, without
63
+ training a model for each dataset.
64
+
65
+ ```bash
66
+ pip install structured-data-models-client
67
+ ```
68
+
69
+ Inference runs in a NIM, so this package downloads no weights and needs no GPU.
70
+
71
+ ```python
72
+ import pandas as pd
73
+ from sdm_client import KumoTabular, Request, SDMClient
74
+
75
+ labelled = pd.DataFrame(
76
+ {
77
+ 'age': [31.0, 44.5, 28.0, 51.0, 39.0],
78
+ 'spend': [120.0, 430.0, 90.0, 510.0, 260.0],
79
+ 'churn': [0, 1, 0, 1, 0],
80
+ }
81
+ )
82
+ to_score = pd.DataFrame({'age': [33.0, 47.0], 'spend': [150.0, 480.0]})
83
+
84
+ request = Request(
85
+ context=labelled, predict=to_score, target='churn', task='classification'
86
+ )
87
+
88
+ with SDMClient(url='http://localhost:8000') as client:
89
+ print(client.predict(request, model=KumoTabular()).to_pandas())
90
+ ```
91
+
92
+ The model is an argument, not a method name. For several related tables, ask in
93
+ the Predictive Query Language and pass `KumoRelational()` instead; that needs
94
+ `pip install 'structured-data-models-client[relational]'`.
95
+
96
+ Documentation, the other four packages and the examples are in the repository:
97
+ <https://github.com/NVIDIA/structured-data-models-client>
98
+
99
+ Apache-2.0. Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
@@ -0,0 +1,36 @@
1
+ sdm_client/__init__.py,sha256=80jgFI3qqu2vM6cPg2D_1_4wjGiOodQpMI1Lqh20ygA,2067
2
+ sdm_client/_version.py,sha256=poqaNUKu0_Ul5ZFqMGv8evj9j4qgAD5x8P_Il9KxWOg,163
3
+ sdm_client/base.py,sha256=YDrTAoLgxQQG4WrsdI08tt40fx7wkuqhEWT52X6BKgU,10959
4
+ sdm_client/client.py,sha256=B4tVNTXOlrfBZVx1X814jt_BKymRP3ajEkapRZDvM-A,57548
5
+ sdm_client/decompose.py,sha256=EfHHgQ9VEqkI1HHJD-je7kKFPpGQ9C8ZRdpLyoaZKOY,16976
6
+ sdm_client/errors.py,sha256=-XsFUAnVuhNuG5vr21JNU72Esmn5SQSOU6H7WkAobJU,6474
7
+ sdm_client/models.py,sha256=BbCi04Cw4f8uo2_CXAs93enaHnIxAFVQF_BZrFvisoA,9708
8
+ sdm_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ sdm_client/query.py,sha256=8ArlBRQkN-HVodnZSkl7LFbgC6mtK5JNPt_zo_aqbKk,28438
10
+ sdm_client/request.py,sha256=R5C5OUnpYeLN0aQu6xdy4JyJ6xXDeaggiLezCYjuv1Y,47310
11
+ sdm_client/result.py,sha256=UQKeUQitF7Xql02CC_DRp-Y1SRgOfZO9j-R9FecAUkM,13917
12
+ sdm_client/schema.py,sha256=v6b2YOMTrXJdnYpX2ofXUnXMZwuYKBykW8bckIbHDWM,134074
13
+ sdm_client/adapters/__init__.py,sha256=2fRta-sa2gyep6ZWzsa56SczPXwusBKSzLBZBIgu1ro,275
14
+ sdm_client/adapters/relational.py,sha256=aHW0DjhNzOk1NekRcoSNmGKTw8hgJ_3n1xIwDp_FMZc,35381
15
+ sdm_client/adapters/tabular.py,sha256=KhqW7OOoik39-urT3jMWNkcJMKXR-IT5Jf_7ORqoDLs,23460
16
+ sdm_client/core/__init__.py,sha256=dzFONzK2amcBi2mjeOZLTtsGl9v9zm5BlbbdiGFYWQI,201
17
+ sdm_client/core/databricks_serving.py,sha256=rb7xxudzMZ-iAdNrSm7JXwTki-k-Ri9Z4xRoClf8BoA,23884
18
+ sdm_client/core/databricks_user_agent.py,sha256=L2hfkeZKydT8aN9zYWzx17cIbwWcTpvjU4DkqKyxwfA,2969
19
+ sdm_client/core/dates.py,sha256=0lrCfXS4PLQGi-rV7LoMXx9Ljg7zVGcInnCofZsFgIk,11393
20
+ sdm_client/core/transport.py,sha256=JMnOvEOjjUNDmAUwxi1UvB0z455gVq-MbmltbcnNu7s,34658
21
+ sdm_client/core/warnings.py,sha256=vWfjbTI3H7BsGJZCwKI3sVpgbfyROi8rVes6B_kiUx8,1693
22
+ sdm_client/materializers/__init__.py,sha256=0LRVeZyFxJGvN71w01NKOo4IQ5PvU08P_APD9XlQR4Y,407
23
+ sdm_client/materializers/relational.py,sha256=lJzFDBzFnUflZQWpdGhI3nwHFWMIIZ3giUgff8H93Jg,75385
24
+ sdm_client/materializers/sources.py,sha256=oRcExP2eL4cNUTn2a8hMYpsP3CZUGrfPAOUCMwmWt2M,11492
25
+ sdm_client/materializers/tabular.py,sha256=ExhK4HR6xdcTU4j1ysSY4RN_1h07IsYDcPL1CxpIItE,12463
26
+ sdm_client/wire/__init__.py,sha256=6eIZ3kuYq-XKMvKiQWFgIhFXxMOWkQrMQaJusWayXyE,1333
27
+ sdm_client/wire/answers.py,sha256=6v_91HuHB7OaAuHrbz77_Xmk94N83t-l1azp3SQ2rjU,3156
28
+ sdm_client/wire/dtypes.py,sha256=hFhhjmlY-UDRoXgNiNHf-8xlAk0Tm_gSPiaxxveqX2I,28791
29
+ sdm_client/wire/keys.py,sha256=mkuHX-Hfm6wiilwDTfxmjRjrN8x-8Dc7nLYqNkHK95o,14315
30
+ sdm_client/wire/tables.py,sha256=YFBURW3v00X2rzmyeG5ebTTEnwwEv8WKKwjayWO1jEU,1822
31
+ structured_data_models_client-1.0.0.dist-info/licenses/LICENSE,sha256=fM-2HkOnqT3X8X_O17f2oQGYvlXIdaMvdUYepurE32I,11432
32
+ structured_data_models_client-1.0.0.dist-info/licenses/NOTICE,sha256=n-tTBUaxjlPv7qKQfp4g1JFKcB7nYalVQv4NjirI8b0,405
33
+ structured_data_models_client-1.0.0.dist-info/METADATA,sha256=EPxCwaTW4zwAOGCxxvPt59puzYXck10T7TYQhlIWxIA,4111
34
+ structured_data_models_client-1.0.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
35
+ structured_data_models_client-1.0.0.dist-info/top_level.txt,sha256=adIMKX4LMOb2GTCVOMWoHptnvE2vsHMY2glsW1zCphM,11
36
+ structured_data_models_client-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+