kumoai 2.15.0.dev202601121731__cp313-cp313-macosx_11_0_arm64.whl → 2.15.0.dev202601181732__cp313-cp313-macosx_11_0_arm64.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.
- kumoai/__init__.py +15 -4
- kumoai/_version.py +1 -1
- kumoai/experimental/rfm/backend/snow/sampler.py +53 -17
- kumoai/experimental/rfm/backend/snow/table.py +16 -13
- kumoai/experimental/rfm/backend/sqlite/sampler.py +4 -2
- kumoai/experimental/rfm/base/utils.py +16 -7
- kumoai/experimental/rfm/graph.py +19 -7
- kumoai/experimental/rfm/infer/dtype.py +3 -1
- kumoai/experimental/rfm/rfm.py +2 -2
- kumoai/utils/display.py +13 -5
- kumoai/utils/progress_logger.py +205 -129
- kumoai/utils/sql.py +2 -2
- {kumoai-2.15.0.dev202601121731.dist-info → kumoai-2.15.0.dev202601181732.dist-info}/METADATA +1 -1
- {kumoai-2.15.0.dev202601121731.dist-info → kumoai-2.15.0.dev202601181732.dist-info}/RECORD +17 -17
- {kumoai-2.15.0.dev202601121731.dist-info → kumoai-2.15.0.dev202601181732.dist-info}/WHEEL +0 -0
- {kumoai-2.15.0.dev202601121731.dist-info → kumoai-2.15.0.dev202601181732.dist-info}/licenses/LICENSE +0 -0
- {kumoai-2.15.0.dev202601121731.dist-info → kumoai-2.15.0.dev202601181732.dist-info}/top_level.txt +0 -0
kumoai/__init__.py
CHANGED
|
@@ -277,7 +277,7 @@ __all__ = [
|
|
|
277
277
|
]
|
|
278
278
|
|
|
279
279
|
|
|
280
|
-
def
|
|
280
|
+
def in_streamlit_notebook() -> bool:
|
|
281
281
|
try:
|
|
282
282
|
from snowflake.snowpark.context import get_active_session
|
|
283
283
|
import streamlit # noqa: F401
|
|
@@ -287,9 +287,7 @@ def in_snowflake_notebook() -> bool:
|
|
|
287
287
|
return False
|
|
288
288
|
|
|
289
289
|
|
|
290
|
-
def
|
|
291
|
-
if in_snowflake_notebook():
|
|
292
|
-
return True
|
|
290
|
+
def in_jupyter_notebook() -> bool:
|
|
293
291
|
try:
|
|
294
292
|
from IPython import get_ipython
|
|
295
293
|
shell = get_ipython()
|
|
@@ -298,3 +296,16 @@ def in_notebook() -> bool:
|
|
|
298
296
|
return shell.__class__.__name__ == 'ZMQInteractiveShell'
|
|
299
297
|
except Exception:
|
|
300
298
|
return False
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def in_vnext_notebook() -> bool:
|
|
302
|
+
try:
|
|
303
|
+
from snowflake.snowpark.context import get_active_session
|
|
304
|
+
get_active_session()
|
|
305
|
+
return in_jupyter_notebook()
|
|
306
|
+
except Exception:
|
|
307
|
+
return False
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def in_notebook() -> bool:
|
|
311
|
+
return in_streamlit_notebook() or in_jupyter_notebook()
|
kumoai/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '2.15.0.
|
|
1
|
+
__version__ = '2.15.0.dev202601181732'
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import json
|
|
2
|
+
import math
|
|
2
3
|
from collections.abc import Iterator
|
|
3
4
|
from contextlib import contextmanager
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
5
|
+
from typing import TYPE_CHECKING, cast
|
|
5
6
|
|
|
6
7
|
import numpy as np
|
|
7
8
|
import pandas as pd
|
|
@@ -11,7 +12,7 @@ from kumoapi.pquery import ValidatedPredictiveQuery
|
|
|
11
12
|
from kumoai.experimental.rfm.backend.snow import Connection, SnowTable
|
|
12
13
|
from kumoai.experimental.rfm.base import SQLSampler, Table
|
|
13
14
|
from kumoai.experimental.rfm.pquery import PQueryPandasExecutor
|
|
14
|
-
from kumoai.utils import ProgressLogger
|
|
15
|
+
from kumoai.utils import ProgressLogger, quote_ident
|
|
15
16
|
|
|
16
17
|
if TYPE_CHECKING:
|
|
17
18
|
from kumoai.experimental.rfm import Graph
|
|
@@ -37,6 +38,15 @@ class SnowSampler(SQLSampler):
|
|
|
37
38
|
assert isinstance(table, SnowTable)
|
|
38
39
|
self._connection = table._connection
|
|
39
40
|
|
|
41
|
+
self._num_rows_dict: dict[str, int] = {
|
|
42
|
+
table.name: cast(int, table._num_rows)
|
|
43
|
+
for table in graph.tables.values()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def num_rows_dict(self) -> dict[str, int]:
|
|
48
|
+
return self._num_rows_dict
|
|
49
|
+
|
|
40
50
|
def _get_min_max_time_dict(
|
|
41
51
|
self,
|
|
42
52
|
table_names: list[str],
|
|
@@ -45,8 +55,9 @@ class SnowSampler(SQLSampler):
|
|
|
45
55
|
for table_name in table_names:
|
|
46
56
|
column = self.time_column_dict[table_name]
|
|
47
57
|
column_ref = self.table_column_ref_dict[table_name][column]
|
|
58
|
+
ident = quote_ident(table_name, char="'")
|
|
48
59
|
select = (f"SELECT\n"
|
|
49
|
-
f"
|
|
60
|
+
f" {ident} as table_name,\n"
|
|
50
61
|
f" MIN({column_ref}) as min_date,\n"
|
|
51
62
|
f" MAX({column_ref}) as max_date\n"
|
|
52
63
|
f"FROM {self.source_name_dict[table_name]}")
|
|
@@ -54,14 +65,13 @@ class SnowSampler(SQLSampler):
|
|
|
54
65
|
sql = "\nUNION ALL\n".join(selects)
|
|
55
66
|
|
|
56
67
|
out_dict: dict[str, tuple[pd.Timestamp, pd.Timestamp]] = {}
|
|
57
|
-
with
|
|
58
|
-
cursor.execute(sql
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
)
|
|
68
|
+
with self._connection.cursor() as cursor:
|
|
69
|
+
cursor.execute(sql)
|
|
70
|
+
for table_name, _min, _max in cursor.fetchall():
|
|
71
|
+
out_dict[table_name] = (
|
|
72
|
+
pd.Timestamp.max if _min is None else pd.Timestamp(_min),
|
|
73
|
+
pd.Timestamp.min if _max is None else pd.Timestamp(_max),
|
|
74
|
+
)
|
|
65
75
|
|
|
66
76
|
return out_dict
|
|
67
77
|
|
|
@@ -239,9 +249,30 @@ class SnowSampler(SQLSampler):
|
|
|
239
249
|
) -> tuple[pd.DataFrame, np.ndarray]:
|
|
240
250
|
time_column = self.time_column_dict.get(table_name)
|
|
241
251
|
|
|
252
|
+
end_time: pd.Series | None = None
|
|
253
|
+
start_time: pd.Series | None = None
|
|
242
254
|
if time_column is not None and anchor_time is not None:
|
|
243
|
-
|
|
244
|
-
|
|
255
|
+
# In order to avoid a full table scan, we limit foreign key
|
|
256
|
+
# sampling to a certain time range, approximated by the number of
|
|
257
|
+
# rows, timestamp ranges and `num_neighbors` value.
|
|
258
|
+
# Downstream, this helps Snowflake to apply partition pruning:
|
|
259
|
+
dst_table_name = [
|
|
260
|
+
dst_table
|
|
261
|
+
for key, dst_table in self.foreign_key_dict[table_name]
|
|
262
|
+
if key == foreign_key
|
|
263
|
+
][0]
|
|
264
|
+
num_facts = self.num_rows_dict[table_name]
|
|
265
|
+
num_entities = self.num_rows_dict[dst_table_name]
|
|
266
|
+
min_time = self.get_min_time([table_name])
|
|
267
|
+
max_time = self.get_max_time([table_name])
|
|
268
|
+
freq = num_facts / num_entities
|
|
269
|
+
freq = freq / max((max_time - min_time).total_seconds(), 1)
|
|
270
|
+
offset = pd.Timedelta(seconds=math.ceil(5 * num_neighbors / freq))
|
|
271
|
+
|
|
272
|
+
end_time = anchor_time.dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
273
|
+
start_time = anchor_time - offset
|
|
274
|
+
start_time = start_time.dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
275
|
+
payload = json.dumps(list(zip(index, end_time, start_time)))
|
|
245
276
|
else:
|
|
246
277
|
payload = json.dumps(list(zip(index)))
|
|
247
278
|
|
|
@@ -260,9 +291,10 @@ class SnowSampler(SQLSampler):
|
|
|
260
291
|
sql += " f.value[0]::FLOAT as __KUMO_ID__"
|
|
261
292
|
else:
|
|
262
293
|
sql += " f.value[0]::VARCHAR as __KUMO_ID__"
|
|
263
|
-
if
|
|
294
|
+
if end_time is not None and start_time is not None:
|
|
264
295
|
sql += (",\n"
|
|
265
|
-
" f.value[1]::TIMESTAMP_NTZ as
|
|
296
|
+
" f.value[1]::TIMESTAMP_NTZ as __KUMO_END_TIME__,\n"
|
|
297
|
+
" f.value[2]::TIMESTAMP_NTZ as __KUMO_START_TIME__")
|
|
266
298
|
sql += (f"\n"
|
|
267
299
|
f" FROM TABLE(FLATTEN(INPUT => PARSE_JSON(?))) f\n"
|
|
268
300
|
f")\n"
|
|
@@ -272,9 +304,13 @@ class SnowSampler(SQLSampler):
|
|
|
272
304
|
f"FROM TMP\n"
|
|
273
305
|
f"JOIN {self.source_name_dict[table_name]}\n"
|
|
274
306
|
f" ON {key_ref} = TMP.__KUMO_ID__\n")
|
|
275
|
-
if
|
|
307
|
+
if end_time is not None and start_time is not None:
|
|
308
|
+
assert time_column is not None
|
|
276
309
|
time_ref = self.table_column_ref_dict[table_name][time_column]
|
|
277
|
-
sql += f" AND {time_ref} <= TMP.
|
|
310
|
+
sql += (f" AND {time_ref} <= TMP.__KUMO_END_TIME__\n"
|
|
311
|
+
f" AND {time_ref} > TMP.__KUMO_START_TIME__\n"
|
|
312
|
+
f"WHERE {time_ref} <= '{end_time.max()}'\n"
|
|
313
|
+
f" AND {time_ref} > '{start_time.min()}'\n")
|
|
278
314
|
sql += ("QUALIFY ROW_NUMBER() OVER (\n"
|
|
279
315
|
" PARTITION BY TMP.__KUMO_BATCH__\n")
|
|
280
316
|
if time_column is not None:
|
|
@@ -76,21 +76,13 @@ class SnowTable(Table):
|
|
|
76
76
|
|
|
77
77
|
@property
|
|
78
78
|
def source_name(self) -> str:
|
|
79
|
-
names
|
|
80
|
-
|
|
81
|
-
names.append(self._database)
|
|
82
|
-
if self._schema is not None:
|
|
83
|
-
names.append(self._schema)
|
|
84
|
-
return '.'.join(names + [self._source_name])
|
|
79
|
+
names = [self._database, self._schema, self._source_name]
|
|
80
|
+
return '.'.join(names)
|
|
85
81
|
|
|
86
82
|
@property
|
|
87
83
|
def _quoted_source_name(self) -> str:
|
|
88
|
-
names
|
|
89
|
-
|
|
90
|
-
names.append(quote_ident(self._database))
|
|
91
|
-
if self._schema is not None:
|
|
92
|
-
names.append(quote_ident(self._schema))
|
|
93
|
-
return '.'.join(names + [quote_ident(self._source_name)])
|
|
84
|
+
names = [self._database, self._schema, self._source_name]
|
|
85
|
+
return '.'.join([quote_ident(name) for name in names])
|
|
94
86
|
|
|
95
87
|
@property
|
|
96
88
|
def backend(self) -> DataBackend:
|
|
@@ -159,7 +151,18 @@ class SnowTable(Table):
|
|
|
159
151
|
)
|
|
160
152
|
|
|
161
153
|
def _get_num_rows(self) -> int | None:
|
|
162
|
-
|
|
154
|
+
with self._connection.cursor() as cursor:
|
|
155
|
+
quoted_source_name = quote_ident(self._source_name, char="'")
|
|
156
|
+
sql = (f"SHOW TABLES LIKE {quoted_source_name} "
|
|
157
|
+
f"IN SCHEMA {quote_ident(self._database)}."
|
|
158
|
+
f"{quote_ident(self._schema)}")
|
|
159
|
+
cursor.execute(sql)
|
|
160
|
+
num_rows = cursor.fetchone()[7]
|
|
161
|
+
|
|
162
|
+
if num_rows == 0:
|
|
163
|
+
raise RuntimeError("Table '{self.source_name}' is empty")
|
|
164
|
+
|
|
165
|
+
return num_rows
|
|
163
166
|
|
|
164
167
|
def _get_expr_sample_df(
|
|
165
168
|
self,
|
|
@@ -121,8 +121,9 @@ class SQLiteSampler(SQLSampler):
|
|
|
121
121
|
for table_name in table_names:
|
|
122
122
|
column = self.time_column_dict[table_name]
|
|
123
123
|
column_ref = self.table_column_ref_dict[table_name][column]
|
|
124
|
+
ident = quote_ident(table_name, char="'")
|
|
124
125
|
select = (f"SELECT\n"
|
|
125
|
-
f"
|
|
126
|
+
f" {ident} as table_name,\n"
|
|
126
127
|
f" MIN({column_ref}) as min_date,\n"
|
|
127
128
|
f" MAX({column_ref}) as max_date\n"
|
|
128
129
|
f"FROM {self.source_name_dict[table_name]}")
|
|
@@ -131,12 +132,13 @@ class SQLiteSampler(SQLSampler):
|
|
|
131
132
|
|
|
132
133
|
out_dict: dict[str, tuple[pd.Timestamp, pd.Timestamp]] = {}
|
|
133
134
|
with self._connection.cursor() as cursor:
|
|
134
|
-
cursor.execute(sql
|
|
135
|
+
cursor.execute(sql)
|
|
135
136
|
for table_name, _min, _max in cursor.fetchall():
|
|
136
137
|
out_dict[table_name] = (
|
|
137
138
|
pd.Timestamp.max if _min is None else pd.Timestamp(_min),
|
|
138
139
|
pd.Timestamp.min if _max is None else pd.Timestamp(_max),
|
|
139
140
|
)
|
|
141
|
+
|
|
140
142
|
return out_dict
|
|
141
143
|
|
|
142
144
|
def _sample_entity_table(
|
|
@@ -4,11 +4,22 @@ import pandas as pd
|
|
|
4
4
|
import pyarrow as pa
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
def is_datetime(ser: pd.Series) -> bool:
|
|
8
|
+
r"""Check whether a :class:`pandas.Series` holds datetime values."""
|
|
9
|
+
if isinstance(ser.dtype, pd.ArrowDtype):
|
|
10
|
+
dtype = ser.dtype.pyarrow_dtype
|
|
11
|
+
return (pa.types.is_timestamp(dtype) or pa.types.is_date(dtype)
|
|
12
|
+
or pa.types.is_time(dtype))
|
|
13
|
+
|
|
14
|
+
return pd.api.types.is_datetime64_any_dtype(ser)
|
|
15
|
+
|
|
16
|
+
|
|
7
17
|
def to_datetime(ser: pd.Series) -> pd.Series:
|
|
8
|
-
"""Converts a :class:`
|
|
9
|
-
if (
|
|
10
|
-
|
|
11
|
-
|
|
18
|
+
"""Converts a :class:`pandas.Series` to ``datetime64[ns]`` format."""
|
|
19
|
+
if isinstance(ser.dtype, pd.ArrowDtype):
|
|
20
|
+
ser = pd.Series(ser.to_numpy(), index=ser.index, name=ser.name)
|
|
21
|
+
|
|
22
|
+
if not pd.api.types.is_datetime64_any_dtype(ser):
|
|
12
23
|
with warnings.catch_warnings():
|
|
13
24
|
warnings.filterwarnings(
|
|
14
25
|
'ignore',
|
|
@@ -16,9 +27,7 @@ def to_datetime(ser: pd.Series) -> pd.Series:
|
|
|
16
27
|
)
|
|
17
28
|
ser = pd.to_datetime(ser, errors='coerce')
|
|
18
29
|
|
|
19
|
-
if
|
|
20
|
-
or (isinstance(ser.dtype, pd.ArrowDtype)
|
|
21
|
-
and ser.dtype.pyarrow_dtype.tz is not None)):
|
|
30
|
+
if isinstance(ser.dtype, pd.DatetimeTZDtype):
|
|
22
31
|
ser = ser.dt.tz_localize(None)
|
|
23
32
|
|
|
24
33
|
if ser.dtype != 'datetime64[ns]':
|
kumoai/experimental/rfm/graph.py
CHANGED
|
@@ -17,8 +17,9 @@ from kumoapi.table import TableDefinition
|
|
|
17
17
|
from kumoapi.typing import Stype
|
|
18
18
|
from typing_extensions import Self
|
|
19
19
|
|
|
20
|
-
from kumoai import
|
|
20
|
+
from kumoai import in_jupyter_notebook, in_streamlit_notebook
|
|
21
21
|
from kumoai.experimental.rfm.base import ColumnSpec, DataBackend, Table
|
|
22
|
+
from kumoai.experimental.rfm.infer import infer_time_column
|
|
22
23
|
from kumoai.graph import Edge
|
|
23
24
|
from kumoai.mixin import CastMixin
|
|
24
25
|
from kumoai.utils import display
|
|
@@ -415,8 +416,9 @@ class Graph:
|
|
|
415
416
|
assert isinstance(connection, Connection)
|
|
416
417
|
|
|
417
418
|
with connection.cursor() as cursor:
|
|
418
|
-
|
|
419
|
-
|
|
419
|
+
sql = (f"SELECT SYSTEM$READ_YAML_FROM_SEMANTIC_VIEW("
|
|
420
|
+
f"'{semantic_view_name}')")
|
|
421
|
+
cursor.execute(sql)
|
|
420
422
|
cfg = yaml.safe_load(cursor.fetchone()[0])
|
|
421
423
|
|
|
422
424
|
graph = cls(tables=[])
|
|
@@ -492,7 +494,17 @@ class Graph:
|
|
|
492
494
|
)
|
|
493
495
|
|
|
494
496
|
# TODO Add a way to register time columns without heuristic usage.
|
|
495
|
-
|
|
497
|
+
time_candidates = [
|
|
498
|
+
column_cfg['name']
|
|
499
|
+
for column_cfg in table_cfg.get('time_dimensions', [])
|
|
500
|
+
if table.has_column(column_cfg['name'])
|
|
501
|
+
and table[column_cfg['name']].stype == Stype.timestamp
|
|
502
|
+
]
|
|
503
|
+
if time_column := infer_time_column(
|
|
504
|
+
df=table._get_sample_df(),
|
|
505
|
+
candidates=time_candidates,
|
|
506
|
+
):
|
|
507
|
+
table.time_column = time_column
|
|
496
508
|
|
|
497
509
|
graph.add_table(table)
|
|
498
510
|
|
|
@@ -1071,7 +1083,7 @@ class Graph:
|
|
|
1071
1083
|
raise ImportError("The 'graphviz' package is required for "
|
|
1072
1084
|
"visualization") from e
|
|
1073
1085
|
|
|
1074
|
-
if not
|
|
1086
|
+
if not in_streamlit_notebook() and not has_graphviz_executables():
|
|
1075
1087
|
raise RuntimeError("Could not visualize graph as 'graphviz' "
|
|
1076
1088
|
"executables are not installed. These "
|
|
1077
1089
|
"dependencies are required in addition to the "
|
|
@@ -1161,10 +1173,10 @@ class Graph:
|
|
|
1161
1173
|
graph.render(path, cleanup=True)
|
|
1162
1174
|
elif isinstance(path, io.BytesIO):
|
|
1163
1175
|
path.write(graph.pipe())
|
|
1164
|
-
elif
|
|
1176
|
+
elif in_streamlit_notebook():
|
|
1165
1177
|
import streamlit as st
|
|
1166
1178
|
st.graphviz_chart(graph)
|
|
1167
|
-
elif
|
|
1179
|
+
elif in_jupyter_notebook():
|
|
1168
1180
|
from IPython.display import display
|
|
1169
1181
|
display(graph)
|
|
1170
1182
|
else:
|
|
@@ -3,6 +3,8 @@ import pandas as pd
|
|
|
3
3
|
import pyarrow as pa
|
|
4
4
|
from kumoapi.typing import Dtype
|
|
5
5
|
|
|
6
|
+
from kumoai.experimental.rfm.base.utils import is_datetime
|
|
7
|
+
|
|
6
8
|
PANDAS_TO_DTYPE: dict[str, Dtype] = {
|
|
7
9
|
'bool': Dtype.bool,
|
|
8
10
|
'boolean': Dtype.bool,
|
|
@@ -34,7 +36,7 @@ def infer_dtype(ser: pd.Series) -> Dtype:
|
|
|
34
36
|
Returns:
|
|
35
37
|
The data type.
|
|
36
38
|
"""
|
|
37
|
-
if
|
|
39
|
+
if is_datetime(ser):
|
|
38
40
|
return Dtype.date
|
|
39
41
|
if pd.api.types.is_timedelta64_dtype(ser.dtype):
|
|
40
42
|
return Dtype.timedelta
|
kumoai/experimental/rfm/rfm.py
CHANGED
|
@@ -610,7 +610,7 @@ class KumoRFM:
|
|
|
610
610
|
|
|
611
611
|
if start == 0 and task.num_prediction_examples > batch_size:
|
|
612
612
|
num = math.ceil(task.num_prediction_examples / batch_size)
|
|
613
|
-
verbose.init_progress(
|
|
613
|
+
verbose.init_progress(msg='Predicting', total=num)
|
|
614
614
|
|
|
615
615
|
for attempt in range(self._num_retries + 1):
|
|
616
616
|
try:
|
|
@@ -643,7 +643,7 @@ class KumoRFM:
|
|
|
643
643
|
df['ANCHOR_TIMESTAMP'] = pd.to_datetime(
|
|
644
644
|
ser, errors='coerce', unit=unit)
|
|
645
645
|
|
|
646
|
-
predictions.append(df)
|
|
646
|
+
predictions.append(df.reset_index(drop=True))
|
|
647
647
|
|
|
648
648
|
if task.num_prediction_examples > batch_size:
|
|
649
649
|
verbose.step()
|
kumoai/utils/display.py
CHANGED
|
@@ -6,14 +6,19 @@ from rich.console import Console
|
|
|
6
6
|
from rich.table import Table
|
|
7
7
|
from rich.text import Text
|
|
8
8
|
|
|
9
|
-
from kumoai import
|
|
9
|
+
from kumoai import (
|
|
10
|
+
in_jupyter_notebook,
|
|
11
|
+
in_notebook,
|
|
12
|
+
in_streamlit_notebook,
|
|
13
|
+
in_vnext_notebook,
|
|
14
|
+
)
|
|
10
15
|
|
|
11
16
|
|
|
12
17
|
def message(msg: str) -> None:
|
|
13
|
-
if
|
|
18
|
+
if in_streamlit_notebook():
|
|
14
19
|
import streamlit as st
|
|
15
20
|
st.markdown(msg)
|
|
16
|
-
elif
|
|
21
|
+
elif in_jupyter_notebook():
|
|
17
22
|
from IPython.display import Markdown, display
|
|
18
23
|
display(Markdown(msg))
|
|
19
24
|
else:
|
|
@@ -54,10 +59,13 @@ def unordered_list(items: Sequence[str]) -> None:
|
|
|
54
59
|
|
|
55
60
|
|
|
56
61
|
def dataframe(df: pd.DataFrame) -> None:
|
|
57
|
-
if
|
|
62
|
+
if in_streamlit_notebook():
|
|
58
63
|
import streamlit as st
|
|
59
64
|
st.dataframe(df, hide_index=True)
|
|
60
|
-
elif
|
|
65
|
+
elif in_vnext_notebook():
|
|
66
|
+
from IPython.display import display
|
|
67
|
+
display(df.reset_index(drop=True))
|
|
68
|
+
elif in_jupyter_notebook():
|
|
61
69
|
from IPython.display import display
|
|
62
70
|
try:
|
|
63
71
|
if hasattr(df.style, 'hide'):
|
kumoai/utils/progress_logger.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import re
|
|
2
2
|
import sys
|
|
3
3
|
import time
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
4
5
|
from typing import Any
|
|
5
6
|
|
|
6
7
|
from rich.console import Console, ConsoleOptions, RenderResult
|
|
@@ -20,52 +21,179 @@ from rich.text import Text
|
|
|
20
21
|
from typing_extensions import Self
|
|
21
22
|
|
|
22
23
|
|
|
23
|
-
class ProgressLogger:
|
|
24
|
+
class ProgressLogger(ABC):
|
|
25
|
+
r"""An abstract base class for logging progress updates."""
|
|
24
26
|
def __init__(self, msg: str, verbose: bool = True) -> None:
|
|
25
|
-
self.msg = msg
|
|
26
|
-
self.verbose = verbose
|
|
27
|
-
self.depth = 0
|
|
27
|
+
self.msg: str = msg
|
|
28
|
+
self.verbose: bool = verbose
|
|
28
29
|
|
|
29
30
|
self.logs: list[str] = []
|
|
30
31
|
|
|
31
32
|
self.start_time: float | None = None
|
|
32
33
|
self.end_time: float | None = None
|
|
33
34
|
|
|
35
|
+
# Handle nested loggers gracefully:
|
|
36
|
+
self._depth: int = 0
|
|
37
|
+
|
|
38
|
+
# Internal progress bar cache:
|
|
39
|
+
self._progress_bar_msg: str | None = None
|
|
40
|
+
self._total: int = 0
|
|
41
|
+
self._current: int = 0
|
|
42
|
+
|
|
43
|
+
def __repr__(self) -> str:
|
|
44
|
+
return f'{self.__class__.__name__}()'
|
|
45
|
+
|
|
34
46
|
@classmethod
|
|
35
47
|
def default(cls, msg: str, verbose: bool = True) -> 'ProgressLogger':
|
|
36
|
-
|
|
48
|
+
r"""The default progress logger for the current environment."""
|
|
49
|
+
from kumoai import in_streamlit_notebook, in_vnext_notebook
|
|
37
50
|
|
|
38
|
-
if
|
|
51
|
+
if in_streamlit_notebook():
|
|
39
52
|
return StreamlitProgressLogger(msg, verbose)
|
|
53
|
+
if in_vnext_notebook():
|
|
54
|
+
return PlainProgressLogger(msg, verbose)
|
|
40
55
|
return RichProgressLogger(msg, verbose)
|
|
41
56
|
|
|
42
57
|
@property
|
|
43
58
|
def duration(self) -> float:
|
|
59
|
+
r"""The current/final duration."""
|
|
44
60
|
assert self.start_time is not None
|
|
45
61
|
if self.end_time is not None:
|
|
46
62
|
return self.end_time - self.start_time
|
|
47
63
|
return time.perf_counter() - self.start_time
|
|
48
64
|
|
|
65
|
+
def __enter__(self) -> Self:
|
|
66
|
+
from kumoai import in_notebook
|
|
67
|
+
|
|
68
|
+
self._depth += 1
|
|
69
|
+
if self._depth == 1:
|
|
70
|
+
self.start_time = time.perf_counter()
|
|
71
|
+
if self._depth == 1 and not in_notebook(): # Show progress bar in TUI.
|
|
72
|
+
sys.stdout.write("\x1b]9;4;3\x07")
|
|
73
|
+
sys.stdout.flush()
|
|
74
|
+
if self._depth == 1 and self.verbose:
|
|
75
|
+
self.on_enter()
|
|
76
|
+
return self
|
|
77
|
+
|
|
78
|
+
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
79
|
+
from kumoai import in_notebook
|
|
80
|
+
|
|
81
|
+
self._depth -= 1
|
|
82
|
+
if self._depth == 0:
|
|
83
|
+
self.end_time = time.perf_counter()
|
|
84
|
+
if self._depth == 0 and self.verbose:
|
|
85
|
+
self.on_exit(error=exc_val is not None)
|
|
86
|
+
if self._depth == 0 and not in_notebook(): # Stop progress bar in TUI.
|
|
87
|
+
sys.stdout.write("\x1b]9;4;0\x07")
|
|
88
|
+
sys.stdout.flush()
|
|
89
|
+
|
|
49
90
|
def log(self, msg: str) -> None:
|
|
91
|
+
r"""Logs a new message."""
|
|
50
92
|
self.logs.append(msg)
|
|
93
|
+
if self.verbose:
|
|
94
|
+
self.on_log(msg)
|
|
51
95
|
|
|
52
|
-
def init_progress(self,
|
|
53
|
-
|
|
96
|
+
def init_progress(self, msg: str, total: int) -> None:
|
|
97
|
+
r"""Initializes a progress bar."""
|
|
98
|
+
if self._progress_bar_msg is not None:
|
|
99
|
+
raise RuntimeError("Current progress not yet finished")
|
|
100
|
+
self._progress_bar_msg = msg
|
|
101
|
+
self._current = 0
|
|
102
|
+
self._total = total
|
|
103
|
+
if self.verbose:
|
|
104
|
+
self.on_init_progress(msg, total)
|
|
54
105
|
|
|
55
106
|
def step(self) -> None:
|
|
107
|
+
r"""Increments an active progress bar."""
|
|
108
|
+
assert self._progress_bar_msg is not None
|
|
109
|
+
self._current += 1
|
|
110
|
+
if self.verbose:
|
|
111
|
+
self.on_step(self._progress_bar_msg, self._current, self._total)
|
|
112
|
+
if self._current >= self._total:
|
|
113
|
+
self._progress_bar_msg = None
|
|
114
|
+
self._current = self._total = 0
|
|
115
|
+
|
|
116
|
+
@abstractmethod
|
|
117
|
+
def on_enter(self) -> None:
|
|
56
118
|
pass
|
|
57
119
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return self
|
|
120
|
+
@abstractmethod
|
|
121
|
+
def on_exit(self, error: bool) -> None:
|
|
122
|
+
pass
|
|
62
123
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
124
|
+
@abstractmethod
|
|
125
|
+
def on_log(self, msg: str) -> None:
|
|
126
|
+
pass
|
|
66
127
|
|
|
67
|
-
|
|
68
|
-
|
|
128
|
+
@abstractmethod
|
|
129
|
+
def on_init_progress(self, msg: str, total: int) -> None:
|
|
130
|
+
pass
|
|
131
|
+
|
|
132
|
+
@abstractmethod
|
|
133
|
+
def on_step(self, msg: str, current: int, total: int) -> None:
|
|
134
|
+
pass
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class PlainProgressLogger(ProgressLogger):
|
|
138
|
+
RESET: str = '\x1b[0m'
|
|
139
|
+
BOLD: str = '\x1b[1m'
|
|
140
|
+
DIM: str = '\x1b[2m'
|
|
141
|
+
RED: str = '\x1b[31m'
|
|
142
|
+
GREEN: str = '\x1b[32m'
|
|
143
|
+
CYAN: str = '\x1b[36m'
|
|
144
|
+
|
|
145
|
+
def on_enter(self) -> None:
|
|
146
|
+
from kumoai import in_vnext_notebook
|
|
147
|
+
|
|
148
|
+
msg = self.msg.replace('[bold]', self.BOLD)
|
|
149
|
+
msg = msg.replace('[/bold]', self.RESET + self.CYAN)
|
|
150
|
+
msg = self.CYAN + msg + self.RESET
|
|
151
|
+
print(msg, end='\n' if in_vnext_notebook() else '', flush=True)
|
|
152
|
+
|
|
153
|
+
def on_exit(self, error: bool) -> None:
|
|
154
|
+
from kumoai import in_vnext_notebook
|
|
155
|
+
|
|
156
|
+
if error:
|
|
157
|
+
msg = f"❌ {self.RED}({self.duration:.2f}s){self.RESET}"
|
|
158
|
+
else:
|
|
159
|
+
msg = f"✅ {self.GREEN}({self.duration:.2f}s){self.RESET}"
|
|
160
|
+
|
|
161
|
+
if in_vnext_notebook():
|
|
162
|
+
print(f"{self.DIM}↳{self.RESET} {msg}", flush=True)
|
|
163
|
+
else:
|
|
164
|
+
print(f" {msg}", flush=True)
|
|
165
|
+
|
|
166
|
+
def on_log(self, msg: str) -> None:
|
|
167
|
+
from kumoai import in_vnext_notebook
|
|
168
|
+
|
|
169
|
+
msg = f"{self.DIM}↳ {msg}{self.RESET}"
|
|
170
|
+
|
|
171
|
+
if in_vnext_notebook():
|
|
172
|
+
print(msg, flush=True)
|
|
173
|
+
else:
|
|
174
|
+
print(f"\n{msg}", end='', flush=True)
|
|
175
|
+
|
|
176
|
+
def on_init_progress(self, msg: str, total: int) -> None:
|
|
177
|
+
from kumoai import in_vnext_notebook
|
|
178
|
+
|
|
179
|
+
msg = f"{self.DIM}↳ {msg}{self.RESET}"
|
|
180
|
+
|
|
181
|
+
if in_vnext_notebook():
|
|
182
|
+
print(msg, flush=True)
|
|
183
|
+
else:
|
|
184
|
+
print(f"\n{msg} {self.DIM}[{self.RESET}", end='', flush=True)
|
|
185
|
+
|
|
186
|
+
def on_step(self, msg: str, current: int, total: int) -> None:
|
|
187
|
+
from kumoai import in_vnext_notebook
|
|
188
|
+
|
|
189
|
+
if in_vnext_notebook():
|
|
190
|
+
return
|
|
191
|
+
|
|
192
|
+
msg = f"{self.DIM}#{self.RESET}"
|
|
193
|
+
if current == total:
|
|
194
|
+
msg += f"{self.DIM}]{self.RESET}"
|
|
195
|
+
|
|
196
|
+
print(msg, end='', flush=True)
|
|
69
197
|
|
|
70
198
|
|
|
71
199
|
class ColoredMofNCompleteColumn(MofNCompleteColumn):
|
|
@@ -103,71 +231,51 @@ class RichProgressLogger(ProgressLogger):
|
|
|
103
231
|
self._live: Live | None = None
|
|
104
232
|
self._exception: bool = False
|
|
105
233
|
|
|
106
|
-
def
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
self.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
TextColumn('•', style='dim'),
|
|
114
|
-
ColoredTimeRemainingColumn(style='dim'),
|
|
115
|
-
)
|
|
116
|
-
self._task = self._progress.add_task("Progress", total=total)
|
|
117
|
-
|
|
118
|
-
def step(self) -> None:
|
|
119
|
-
if self.verbose:
|
|
120
|
-
assert self._progress is not None
|
|
121
|
-
assert self._task is not None
|
|
122
|
-
self._progress.update(self._task, advance=1) # type: ignore
|
|
123
|
-
|
|
124
|
-
def __enter__(self) -> Self:
|
|
125
|
-
from kumoai import in_notebook
|
|
126
|
-
|
|
127
|
-
super().__enter__()
|
|
128
|
-
|
|
129
|
-
if self.depth > 1:
|
|
130
|
-
return self
|
|
234
|
+
def on_enter(self) -> None:
|
|
235
|
+
self._live = Live(
|
|
236
|
+
self,
|
|
237
|
+
refresh_per_second=self.refresh_per_second,
|
|
238
|
+
vertical_overflow='visible',
|
|
239
|
+
)
|
|
240
|
+
self._live.start()
|
|
131
241
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
sys.stdout.flush()
|
|
242
|
+
def on_exit(self, error: bool) -> None:
|
|
243
|
+
self._exception = error
|
|
135
244
|
|
|
136
|
-
if self.
|
|
137
|
-
self.
|
|
138
|
-
self,
|
|
139
|
-
refresh_per_second=self.refresh_per_second,
|
|
140
|
-
vertical_overflow='visible',
|
|
141
|
-
)
|
|
142
|
-
self._live.start()
|
|
245
|
+
if self._progress is not None:
|
|
246
|
+
self._progress.stop()
|
|
143
247
|
|
|
144
|
-
|
|
248
|
+
if self._live is not None:
|
|
249
|
+
self._live.update(self, refresh=True)
|
|
250
|
+
self._live.stop()
|
|
145
251
|
|
|
146
|
-
|
|
147
|
-
|
|
252
|
+
self._progress = None
|
|
253
|
+
self._task = None
|
|
254
|
+
self._live = None
|
|
148
255
|
|
|
149
|
-
|
|
256
|
+
def on_log(self, msg: str) -> None:
|
|
257
|
+
pass
|
|
150
258
|
|
|
151
|
-
|
|
152
|
-
|
|
259
|
+
def on_init_progress(self, msg: str, total: int) -> None:
|
|
260
|
+
self._progress = Progress(
|
|
261
|
+
TextColumn(f' ↳ {msg}', style='dim'),
|
|
262
|
+
BarColumn(bar_width=None),
|
|
263
|
+
ColoredMofNCompleteColumn(style='dim'),
|
|
264
|
+
TextColumn('•', style='dim'),
|
|
265
|
+
ColoredTimeRemainingColumn(style='dim'),
|
|
266
|
+
)
|
|
267
|
+
self._task = self._progress.add_task("Progress", total=total)
|
|
153
268
|
|
|
154
|
-
|
|
155
|
-
|
|
269
|
+
def on_step(self, msg: str, current: int, total: int) -> None:
|
|
270
|
+
assert self._progress is not None
|
|
271
|
+
assert self._task is not None
|
|
272
|
+
self._progress.update(self._task, advance=1) # type: ignore
|
|
156
273
|
|
|
157
|
-
if
|
|
274
|
+
if current == total:
|
|
158
275
|
self._progress.stop()
|
|
159
276
|
self._progress = None
|
|
160
277
|
self._task = None
|
|
161
278
|
|
|
162
|
-
if self._live is not None:
|
|
163
|
-
self._live.update(self, refresh=True)
|
|
164
|
-
self._live.stop()
|
|
165
|
-
self._live = None
|
|
166
|
-
|
|
167
|
-
if not in_notebook():
|
|
168
|
-
sys.stdout.write("\x1b]9;4;0\x07")
|
|
169
|
-
sys.stdout.flush()
|
|
170
|
-
|
|
171
279
|
def __rich_console__(
|
|
172
280
|
self,
|
|
173
281
|
console: Console,
|
|
@@ -198,7 +306,7 @@ class RichProgressLogger(ProgressLogger):
|
|
|
198
306
|
|
|
199
307
|
yield table
|
|
200
308
|
|
|
201
|
-
if self.
|
|
309
|
+
if self._progress is not None:
|
|
202
310
|
yield self._progress.get_renderable()
|
|
203
311
|
|
|
204
312
|
|
|
@@ -211,82 +319,50 @@ class StreamlitProgressLogger(ProgressLogger):
|
|
|
211
319
|
super().__init__(msg=msg, verbose=verbose)
|
|
212
320
|
|
|
213
321
|
self._status: Any = None
|
|
214
|
-
|
|
215
|
-
self._total = 0
|
|
216
|
-
self._current = 0
|
|
217
|
-
self._description: str = ''
|
|
218
322
|
self._progress: Any = None
|
|
219
323
|
|
|
220
|
-
|
|
221
|
-
|
|
324
|
+
@staticmethod
|
|
325
|
+
def _sanitize_text(msg: str) -> str:
|
|
326
|
+
return re.sub(r'\[/?bold\]', '**', msg)
|
|
222
327
|
|
|
328
|
+
def on_enter(self) -> None:
|
|
223
329
|
import streamlit as st
|
|
224
330
|
|
|
225
|
-
if self.depth > 1:
|
|
226
|
-
return self
|
|
227
|
-
|
|
228
331
|
# Adjust layout for prettier output:
|
|
229
332
|
st.markdown(STREAMLIT_CSS, unsafe_allow_html=True)
|
|
230
333
|
|
|
231
|
-
|
|
232
|
-
self.
|
|
233
|
-
|
|
334
|
+
self._status = st.status(
|
|
335
|
+
f':blue[{self._sanitize_text(self.msg)}]',
|
|
336
|
+
expanded=True,
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
def on_exit(self, error: bool) -> None:
|
|
340
|
+
if self._status is not None:
|
|
341
|
+
label = f'{self._sanitize_text(self.msg)} ({self.duration:.2f}s)'
|
|
342
|
+
self._status.update(
|
|
343
|
+
label=f':red[{label}]' if error else f':green[{label}]',
|
|
344
|
+
state='error' if error else 'complete',
|
|
234
345
|
expanded=True,
|
|
235
346
|
)
|
|
236
347
|
|
|
237
|
-
|
|
348
|
+
def on_log(self, msg: str) -> None:
|
|
349
|
+
if self._status is not None:
|
|
350
|
+
self._status.write(msg)
|
|
238
351
|
|
|
239
|
-
def
|
|
240
|
-
|
|
241
|
-
if self.verbose and self._status is not None:
|
|
242
|
-
self._status.write(self._sanitize_text(msg))
|
|
243
|
-
|
|
244
|
-
def init_progress(self, total: int, description: str) -> None:
|
|
245
|
-
if self.verbose and self._status is not None:
|
|
246
|
-
self._total = total
|
|
247
|
-
self._current = 0
|
|
248
|
-
self._description = self._sanitize_text(description)
|
|
249
|
-
percent = min(self._current / self._total, 1.0)
|
|
352
|
+
def on_init_progress(self, msg: str, total: int) -> None:
|
|
353
|
+
if self._status is not None:
|
|
250
354
|
self._progress = self._status.progress(
|
|
251
|
-
value=
|
|
252
|
-
text=f'{
|
|
355
|
+
value=0.0,
|
|
356
|
+
text=f'{msg} [{0}/{total}]',
|
|
253
357
|
)
|
|
254
358
|
|
|
255
|
-
def
|
|
256
|
-
self.
|
|
257
|
-
|
|
258
|
-
if self.verbose and self._progress is not None:
|
|
259
|
-
percent = min(self._current / self._total, 1.0)
|
|
359
|
+
def on_step(self, msg: str, current: int, total: int) -> None:
|
|
360
|
+
if self._progress is not None:
|
|
260
361
|
self._progress.progress(
|
|
261
|
-
value=
|
|
262
|
-
text=f'{
|
|
362
|
+
value=min(current / total, 1.0),
|
|
363
|
+
text=f'{msg} [{current}/{total}]',
|
|
263
364
|
)
|
|
264
365
|
|
|
265
|
-
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
266
|
-
super().__exit__(exc_type, exc_val, exc_tb)
|
|
267
|
-
|
|
268
|
-
if not self.verbose or self._status is None or self.depth > 1:
|
|
269
|
-
return
|
|
270
|
-
|
|
271
|
-
label = f'{self._sanitize_text(self.msg)} ({self.duration:.2f}s)'
|
|
272
|
-
|
|
273
|
-
if exc_type is not None:
|
|
274
|
-
self._status.update(
|
|
275
|
-
label=f':red[{label}]',
|
|
276
|
-
state='error',
|
|
277
|
-
expanded=True,
|
|
278
|
-
)
|
|
279
|
-
else:
|
|
280
|
-
self._status.update(
|
|
281
|
-
label=f':green[{label}]',
|
|
282
|
-
state='complete',
|
|
283
|
-
expanded=True,
|
|
284
|
-
)
|
|
285
|
-
|
|
286
|
-
@staticmethod
|
|
287
|
-
def _sanitize_text(msg: str) -> str:
|
|
288
|
-
return re.sub(r'\[/?bold\]', '**', msg)
|
|
289
|
-
|
|
290
366
|
|
|
291
367
|
STREAMLIT_CSS = """
|
|
292
368
|
<style>
|
kumoai/utils/sql.py
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
def quote_ident(
|
|
1
|
+
def quote_ident(ident: str, char: str = '"') -> str:
|
|
2
2
|
r"""Quotes a SQL identifier."""
|
|
3
|
-
return
|
|
3
|
+
return char + ident.replace(char, char + char) + char
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
kumoai/kumolib.cpython-313-darwin.so,sha256=waBv-DiZ3WcasxiCQ-OM9EbSTgTtCfBTZIibXAK-JiQ,232816
|
|
2
2
|
kumoai/_logging.py,sha256=U2_5ROdyk92P4xO4H2WJV8EC7dr6YxmmnM-b7QX9M7I,886
|
|
3
3
|
kumoai/mixin.py,sha256=MP413xzuCqWhxAPUHmloLA3j4ZyF1tEtfi516b_hOXQ,812
|
|
4
|
-
kumoai/_version.py,sha256=
|
|
5
|
-
kumoai/__init__.py,sha256=
|
|
4
|
+
kumoai/_version.py,sha256=2ksd2GuX-AZRJGtcuDxCIRV0etIpcKZFRxJlf6Of638,39
|
|
5
|
+
kumoai/__init__.py,sha256=n2Mi2n5S_WKpxpCInQKfGEmsIWVwrX86nGnYn5HwtIE,11171
|
|
6
6
|
kumoai/formatting.py,sha256=jA_rLDCGKZI8WWCha-vtuLenVKTZvli99Tqpurz1H84,953
|
|
7
7
|
kumoai/futures.py,sha256=oJFIfdCM_3nWIqQteBKYMY4fPhoYlYWE_JA2o6tx-ng,3737
|
|
8
8
|
kumoai/jobs.py,sha256=NrdLEFNo7oeCYSy-kj2nAvCFrz9BZ_xrhkqHFHk5ksY,2496
|
|
@@ -12,23 +12,23 @@ kumoai/spcs.py,sha256=N31d7rLa-bgYh8e2J4YzX1ScxGLqiVXrqJnCl1y4Mts,4139
|
|
|
12
12
|
kumoai/_singleton.py,sha256=UTwrbDkoZSGB8ZelorvprPDDv9uZkUi1q_SrmsyngpQ,836
|
|
13
13
|
kumoai/experimental/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
14
|
kumoai/experimental/rfm/relbench.py,sha256=cVsxxV3TIL3PLEoYb-8tAVW3GSef6NQAd3rxdHJL63I,2276
|
|
15
|
-
kumoai/experimental/rfm/graph.py,sha256=
|
|
15
|
+
kumoai/experimental/rfm/graph.py,sha256=4Jo17oYSoZouzvNQT2-Ai9GOX-bIdefPcxj_gcoM3dI,46873
|
|
16
16
|
kumoai/experimental/rfm/__init__.py,sha256=bW2XyYtkbdiu_iICYFF2Fu1Fx5fyGbqne6m_6c1P-fY,7016
|
|
17
17
|
kumoai/experimental/rfm/sagemaker.py,sha256=6fyXO1Jd_scq-DH7kcv6JcV8QPyTbh4ceqwQDPADlZ0,4963
|
|
18
|
-
kumoai/experimental/rfm/rfm.py,sha256=
|
|
18
|
+
kumoai/experimental/rfm/rfm.py,sha256=XsxwiDIvlZ_js7rvvffrOiXFsLX15-C7N0T9M-aptCw,60017
|
|
19
19
|
kumoai/experimental/rfm/authenticate.py,sha256=G2RkRWznMVQUzvhvbKhn0bMCY7VmoNYxluz3THRqSdE,18851
|
|
20
20
|
kumoai/experimental/rfm/task_table.py,sha256=n_gZNQlCqHOiAkbeaa18nnQ-amt1oWKA9riO2rkrZuw,9847
|
|
21
21
|
kumoai/experimental/rfm/backend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
22
|
kumoai/experimental/rfm/backend/sqlite/__init__.py,sha256=jl-DBbhsqQ-dUXyWhyQTM1AU2qNAtXCmi1mokdhtBTg,902
|
|
23
23
|
kumoai/experimental/rfm/backend/sqlite/table.py,sha256=WqYtd_rwlawItRMXZUfv14qdyU6huQmODuFjDo483dI,6683
|
|
24
|
-
kumoai/experimental/rfm/backend/sqlite/sampler.py,sha256=
|
|
24
|
+
kumoai/experimental/rfm/backend/sqlite/sampler.py,sha256=I-zaSMd5XLg0qaJOoCR8arFBauUfhW_ZMl7gI97ress,18699
|
|
25
25
|
kumoai/experimental/rfm/backend/local/__init__.py,sha256=2s9sSA-E-8pfkkzCH4XPuaSxSznEURMfMgwEIfYYPsg,1014
|
|
26
26
|
kumoai/experimental/rfm/backend/local/table.py,sha256=GKeYGcu52ztCU8EBMqp5UVj85E145Ug41xiCPiTCXq4,3489
|
|
27
27
|
kumoai/experimental/rfm/backend/local/graph_store.py,sha256=RHhkI13KpdPxqb4vXkwEwuFiX5DkrEsfZsOLywNnrvU,11294
|
|
28
28
|
kumoai/experimental/rfm/backend/local/sampler.py,sha256=UKxTjsYs00sYuV_LAlDuZOvQq0BZzPCzZK1Fki2Fd70,10726
|
|
29
29
|
kumoai/experimental/rfm/backend/snow/__init__.py,sha256=BYfsiuJ4Ee30GjG9EuUtitMHXnRfvVKi85zNlIwldV4,993
|
|
30
|
-
kumoai/experimental/rfm/backend/snow/table.py,sha256=
|
|
31
|
-
kumoai/experimental/rfm/backend/snow/sampler.py,sha256=
|
|
30
|
+
kumoai/experimental/rfm/backend/snow/table.py,sha256=1RXpPiTxawTTOFprXvu7jDLG0ZGio_vE9lSfB6wqbWM,9078
|
|
31
|
+
kumoai/experimental/rfm/backend/snow/sampler.py,sha256=tDOEiPTFFG6pWDcuuTvaOBAsMJLsxu4PzqryIgH1Kb4,16322
|
|
32
32
|
kumoai/experimental/rfm/pquery/__init__.py,sha256=X0O3EIq5SMfBEE-ii5Cq6iDhR3s3XMXB52Cx5htoePw,152
|
|
33
33
|
kumoai/experimental/rfm/pquery/pandas_executor.py,sha256=MwSvFRwLq-z19LEdF0G0AT7Gj9tCqu-XLEA7mNbqXwc,18454
|
|
34
34
|
kumoai/experimental/rfm/pquery/executor.py,sha256=gs5AVNaA50ci8zXOBD3qt5szdTReSwTs4BGuEyx4BEE,2728
|
|
@@ -37,14 +37,14 @@ kumoai/experimental/rfm/infer/categorical.py,sha256=VwNaKwKbRYkTxEJ1R6gziffC8dGs
|
|
|
37
37
|
kumoai/experimental/rfm/infer/time_col.py,sha256=iw_aUcHD2bHr7uRa3E7uDC30kU37aLIRTVAFdQEpt68,1818
|
|
38
38
|
kumoai/experimental/rfm/infer/pkey.py,sha256=IaJI5GHK8ds_a3AOr3YYVgUlSmYYEgr4Nu92s2RyBV4,4412
|
|
39
39
|
kumoai/experimental/rfm/infer/id.py,sha256=ZIO0DWIoiEoS_8MVc5lkqBfkTWWQ0yGCgjkwLdaYa_Q,908
|
|
40
|
-
kumoai/experimental/rfm/infer/dtype.py,sha256=
|
|
40
|
+
kumoai/experimental/rfm/infer/dtype.py,sha256=fbRRyyKSzO4riqX3RlhvBK7DhnjhwTgZVUjQ9inVPYI,2811
|
|
41
41
|
kumoai/experimental/rfm/infer/__init__.py,sha256=8GDxQKd0pxZULdk7mpwl3CsOpL4v2HPuPEsbi2t_vzc,519
|
|
42
42
|
kumoai/experimental/rfm/infer/timestamp.py,sha256=vM9--7eStzaGG13Y-oLYlpNJyhL6f9dp17HDXwtl_DM,1094
|
|
43
43
|
kumoai/experimental/rfm/infer/stype.py,sha256=fu4zsOB-C7jNeMnq6dsK4bOZSewe7PtZe_AkohSRLoM,894
|
|
44
44
|
kumoai/experimental/rfm/base/sql_sampler.py,sha256=_go8TnH7AHki-0gg_pB7xd228VYhogQh10OkxT7PEnI,15682
|
|
45
45
|
kumoai/experimental/rfm/base/mapper.py,sha256=WbWXSF8Vkdeud7UeQ2JgSX7z4d27b_b6o7nR4zET1aw,2420
|
|
46
46
|
kumoai/experimental/rfm/base/__init__.py,sha256=rjmMux5lG8srw1bjQGcFQFv6zET9e5riP81nPkw28Jg,724
|
|
47
|
-
kumoai/experimental/rfm/base/utils.py,sha256=
|
|
47
|
+
kumoai/experimental/rfm/base/utils.py,sha256=Easg1bvjPLR8oZIoxIQCtCyl92pp2dUskdnSv1eayxQ,1133
|
|
48
48
|
kumoai/experimental/rfm/base/table.py,sha256=eJuOUM64VWDkHaslNgeR5A_FZjlPF_4czC8OfFGR62E,26015
|
|
49
49
|
kumoai/experimental/rfm/base/sampler.py,sha256=2G6VmgAGV1mSQWHK4wUgf5Ngr8nnH8Hg6_D3sPZZx1A,31951
|
|
50
50
|
kumoai/experimental/rfm/base/expression.py,sha256=Y7NtLTnKlx6euG_N3fLTcrFKheB6P5KS_jhCfoXV9DE,1252
|
|
@@ -60,9 +60,9 @@ kumoai/artifact_export/job.py,sha256=GEisSwvcjK_35RgOfsLXGgxMTXIWm765B_BW_Kgs-V0
|
|
|
60
60
|
kumoai/artifact_export/__init__.py,sha256=BsfDrc3mCHpO9-BqvqKm8qrXDIwfdaoH5UIoG4eQkc4,238
|
|
61
61
|
kumoai/utils/datasets.py,sha256=ptKIUoBONVD55pTVNdRCkQT3NWdN_r9UAUu4xewPa3U,2928
|
|
62
62
|
kumoai/utils/__init__.py,sha256=6S-UtwjeLpnCYRCCIEWhkitPYGaqOGXC1ChE13DzXiU,256
|
|
63
|
-
kumoai/utils/display.py,sha256=
|
|
64
|
-
kumoai/utils/progress_logger.py,sha256=
|
|
65
|
-
kumoai/utils/sql.py,sha256=
|
|
63
|
+
kumoai/utils/display.py,sha256=QmgeQQT7SzoC1CK2A0ftWbfkEuVN4KQfrKoPCrCDaGc,2626
|
|
64
|
+
kumoai/utils/progress_logger.py,sha256=1PtXxfMteg2nyQAfTGx6qnljiZMZvhwDTndQ9_4_nCE,12161
|
|
65
|
+
kumoai/utils/sql.py,sha256=CNKa-M56QiWoCSe9WLuumahsu3_ugQGr2YoTbveFHq0,147
|
|
66
66
|
kumoai/utils/forecasting.py,sha256=-nDS6ucKNfQhTQOfebjefj0wwWH3-KYNslIomxwwMBM,7415
|
|
67
67
|
kumoai/codegen/generate.py,sha256=SvfWWa71xSAOjH9645yQvgoEM-o4BYjupM_EpUxqB_E,7331
|
|
68
68
|
kumoai/codegen/naming.py,sha256=_XVQGxHfuub4bhvyuBKjltD5Lm_oPpibvP_LZteCGk0,3021
|
|
@@ -117,8 +117,8 @@ kumoai/trainer/__init__.py,sha256=zUdFl-f-sBWmm2x8R-rdVzPBeU2FaMzUY5mkcgoTa1k,93
|
|
|
117
117
|
kumoai/trainer/online_serving.py,sha256=9cddb5paeZaCgbUeceQdAOxysCtV5XP-KcsgFz_XR5w,9566
|
|
118
118
|
kumoai/trainer/distilled_trainer.py,sha256=2pPs5clakNxkLfaak7uqPJOrpTWe1RVVM7ztDSqQZvU,6484
|
|
119
119
|
kumoai/trainer/trainer.py,sha256=hBXO7gwpo3t59zKFTeIkK65B8QRmWCwO33sbDuEAPlY,20133
|
|
120
|
-
kumoai-2.15.0.
|
|
121
|
-
kumoai-2.15.0.
|
|
122
|
-
kumoai-2.15.0.
|
|
123
|
-
kumoai-2.15.0.
|
|
124
|
-
kumoai-2.15.0.
|
|
120
|
+
kumoai-2.15.0.dev202601181732.dist-info/RECORD,,
|
|
121
|
+
kumoai-2.15.0.dev202601181732.dist-info/WHEEL,sha256=oqGJCpG61FZJmvyZ3C_0aCv-2mdfcY9e3fXvyUNmWfM,136
|
|
122
|
+
kumoai-2.15.0.dev202601181732.dist-info/top_level.txt,sha256=YjU6UcmomoDx30vEXLsOU784ED7VztQOsFApk1SFwvs,7
|
|
123
|
+
kumoai-2.15.0.dev202601181732.dist-info/METADATA,sha256=ETr8-9Zfq1pM_Smk8KOWZZm14cRdtNl9vcd_hqSQBKg,2564
|
|
124
|
+
kumoai-2.15.0.dev202601181732.dist-info/licenses/LICENSE,sha256=TbWlyqRmhq9PEzCaTI0H0nWLQCCOywQM8wYH8MbjfLo,1102
|
|
File without changes
|
{kumoai-2.15.0.dev202601121731.dist-info → kumoai-2.15.0.dev202601181732.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
{kumoai-2.15.0.dev202601121731.dist-info → kumoai-2.15.0.dev202601181732.dist-info}/top_level.txt
RENAMED
|
File without changes
|