mostlyai-engine 2.3.3__tar.gz → 2.5.0__tar.gz
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.
- mostlyai_engine-2.5.0/LICENSE_HEADER +13 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/PKG-INFO +9 -9
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/README.md +1 -1
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/__init__.py +1 -1
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_common.py +6 -3
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/tabular/character.py +3 -1
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/tabular/datetime.py +6 -5
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_language/engine/vllm_engine.py +10 -8
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_language/xgrammar_utils.py +11 -2
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_tabular/argn.py +5 -2
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_tabular/common.py +1 -1
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_tabular/generation.py +7 -5
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_tabular/probability.py +13 -9
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/pyproject.toml +11 -11
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/.gitignore +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/LICENSE +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_dtypes.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/__init__.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/language/__init__.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/language/categorical.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/language/datetime.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/language/numeric.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/language/text.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/tabular/__init__.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/tabular/categorical.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/tabular/itt.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/tabular/lat_long.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/tabular/numeric.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_language/__init__.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_language/common.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_language/encoding.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_language/engine/__init__.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_language/engine/base.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_language/engine/hf_engine.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_language/generation.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_language/interface.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_language/lstm.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_language/tokenizer_utils.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_language/training.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_memory.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_tabular/__init__.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_tabular/encoding.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_tabular/fairness.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_tabular/interface.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_tabular/training.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_training_utils.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_workspace.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/analysis.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/domain.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/encoding.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/generation.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/logging.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/random_state.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/splitting.py +0 -0
- {mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/training.py +0 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright 2025 MOSTLY AI
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mostlyai-engine
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.0
|
|
4
4
|
Summary: Synthetic Data Engine
|
|
5
5
|
Project-URL: homepage, https://github.com/mostly-ai/mostlyai-engine
|
|
6
6
|
Project-URL: repository, https://github.com/mostly-ai/mostlyai-engine
|
|
@@ -8,6 +8,7 @@ Project-URL: documentation, https://mostly-ai.github.io/mostlyai-engine/
|
|
|
8
8
|
Author-email: MOSTLY AI <dev@mostly.ai>
|
|
9
9
|
License-Expression: Apache-2.0
|
|
10
10
|
License-File: LICENSE
|
|
11
|
+
License-File: LICENSE_HEADER
|
|
11
12
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
13
|
Classifier: Intended Audience :: Developers
|
|
13
14
|
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
@@ -17,13 +18,12 @@ Classifier: Intended Audience :: Science/Research
|
|
|
17
18
|
Classifier: Intended Audience :: Telecommunications Industry
|
|
18
19
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
19
20
|
Classifier: Operating System :: OS Independent
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.11
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.12
|
|
23
23
|
Classifier: Programming Language :: Python :: 3.13
|
|
24
24
|
Classifier: Topic :: Software Development :: Libraries
|
|
25
25
|
Classifier: Typing :: Typed
|
|
26
|
-
Requires-Python:
|
|
26
|
+
Requires-Python: <3.14,>=3.11
|
|
27
27
|
Requires-Dist: accelerate>=1.5.0
|
|
28
28
|
Requires-Dist: datasets>=3.0.0
|
|
29
29
|
Requires-Dist: huggingface-hub[hf-xet]>=0.30.2
|
|
@@ -38,15 +38,15 @@ Requires-Dist: pyarrow>=16.0.0
|
|
|
38
38
|
Requires-Dist: scikit-learn>=1.4.0
|
|
39
39
|
Requires-Dist: setuptools>=77.0.3
|
|
40
40
|
Requires-Dist: tokenizers>=0.21.0
|
|
41
|
-
Requires-Dist: torch<2.
|
|
42
|
-
Requires-Dist: torchaudio<2.
|
|
43
|
-
Requires-Dist: torchvision<0.
|
|
44
|
-
Requires-Dist: transformers
|
|
41
|
+
Requires-Dist: torch<2.10.0,>=2.9.0
|
|
42
|
+
Requires-Dist: torchaudio<2.10.0,>=2.9.0
|
|
43
|
+
Requires-Dist: torchvision<0.25.0,>=0.24.0
|
|
44
|
+
Requires-Dist: transformers<5,>=4.55.0
|
|
45
45
|
Requires-Dist: xgrammar>=0.1.21
|
|
46
46
|
Provides-Extra: gpu
|
|
47
47
|
Requires-Dist: bitsandbytes==0.42.0; (sys_platform == 'darwin') and extra == 'gpu'
|
|
48
48
|
Requires-Dist: bitsandbytes>=0.45.5; (sys_platform == 'linux') and extra == 'gpu'
|
|
49
|
-
Requires-Dist: vllm==0.
|
|
49
|
+
Requires-Dist: vllm==0.12.0; (sys_platform == 'linux' or sys_platform == 'darwin') and extra == 'gpu'
|
|
50
50
|
Description-Content-Type: text/markdown
|
|
51
51
|
|
|
52
52
|
# Synthetic Data Engine 💎
|
|
@@ -116,7 +116,7 @@ uv pip install -U 'mostlyai-engine[gpu]'
|
|
|
116
116
|
On Linux, one can explicitly install the CPU-only variant of torch together with `mostlyai-engine`:
|
|
117
117
|
|
|
118
118
|
```bash
|
|
119
|
-
uv pip install -U torch==2.
|
|
119
|
+
uv pip install -U torch==2.9.1+cpu torchvision==0.24.1+cpu mostlyai-engine --extra-index-url https://download.pytorch.org/whl/cpu
|
|
120
120
|
```
|
|
121
121
|
|
|
122
122
|
## TabularARGN for Flat Data
|
|
@@ -65,7 +65,7 @@ uv pip install -U 'mostlyai-engine[gpu]'
|
|
|
65
65
|
On Linux, one can explicitly install the CPU-only variant of torch together with `mostlyai-engine`:
|
|
66
66
|
|
|
67
67
|
```bash
|
|
68
|
-
uv pip install -U torch==2.
|
|
68
|
+
uv pip install -U torch==2.9.1+cpu torchvision==0.24.1+cpu mostlyai-engine --extra-index-url https://download.pytorch.org/whl/cpu
|
|
69
69
|
```
|
|
70
70
|
|
|
71
71
|
## TabularARGN for Flat Data
|
|
@@ -34,7 +34,7 @@ __all__ = [
|
|
|
34
34
|
"TabularARGN",
|
|
35
35
|
"LanguageModel",
|
|
36
36
|
]
|
|
37
|
-
__version__ = "2.
|
|
37
|
+
__version__ = "2.5.0"
|
|
38
38
|
|
|
39
39
|
# suppress specific warning related to os.fork() in multi-threaded processes
|
|
40
40
|
warnings.filterwarnings("ignore", category=DeprecationWarning, message=".*multi-threaded.*fork.*")
|
|
@@ -273,8 +273,9 @@ def safe_convert_datetime(values: pd.Series, date_only: bool = False) -> pd.Seri
|
|
|
273
273
|
utc=True,
|
|
274
274
|
dayfirst=False, # assume 1/3/2020 is Jan 3
|
|
275
275
|
)
|
|
276
|
-
|
|
277
|
-
|
|
276
|
+
has_slash_dates = values.astype("string").str.contains(r"^\s*\d{1,2}/\d{1,2}/\d{4}(?:\s|$)", regex=True).any()
|
|
277
|
+
# some mixed-format slash dates are interpreted more reliably with dayfirst=True
|
|
278
|
+
if has_slash_dates and values_parsed_fixed.isna().sum() > values.isna().sum():
|
|
278
279
|
values_parsed_fixed_dayfirst = pd.to_datetime(
|
|
279
280
|
values,
|
|
280
281
|
errors="coerce", # silently map invalid dates to NA
|
|
@@ -937,6 +938,7 @@ def impute_from_non_nan_distribution(values: pd.Series, column_stats: dict) -> t
|
|
|
937
938
|
Returns:
|
|
938
939
|
tuple[pd.Series, pd.Series]: The series with imputed values and the mask of NaNs.
|
|
939
940
|
"""
|
|
941
|
+
values = values.copy()
|
|
940
942
|
nan_mask = values.isna()
|
|
941
943
|
vc = values.value_counts(normalize=True)
|
|
942
944
|
if vc.empty:
|
|
@@ -944,7 +946,8 @@ def impute_from_non_nan_distribution(values: pd.Series, column_stats: dict) -> t
|
|
|
944
946
|
probs = vc.values
|
|
945
947
|
categories = vc.index
|
|
946
948
|
# NOTE: an alternative will be to use the largest remainder method
|
|
947
|
-
|
|
949
|
+
if nan_mask.any():
|
|
950
|
+
values.loc[nan_mask] = np.random.choice(categories, size=nan_mask.sum(), p=probs)
|
|
948
951
|
return values, nan_mask.astype(int)
|
|
949
952
|
|
|
950
953
|
|
{mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/tabular/character.py
RENAMED
|
@@ -102,7 +102,9 @@ def encode_character(values: pd.Series, stats: dict, _: pd.Series | None = None)
|
|
|
102
102
|
df_split = split_sub_columns_character(values, max_string_length)
|
|
103
103
|
for idx in range(max_string_length):
|
|
104
104
|
sub_col = f"P{idx}"
|
|
105
|
-
|
|
105
|
+
categories = list(stats["codes"][sub_col].keys())
|
|
106
|
+
values_at_pos = df_split[sub_col].where(df_split[sub_col].isin(categories), UNKNOWN_TOKEN)
|
|
107
|
+
np_codes = np.array(pd.Categorical(values_at_pos, categories=categories).codes)
|
|
106
108
|
np.place(np_codes, np_codes == -1, 0)
|
|
107
109
|
df_split[sub_col] = np_codes
|
|
108
110
|
if stats["has_nan"]:
|
{mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/tabular/datetime.py
RENAMED
|
@@ -234,9 +234,10 @@ def decode_datetime(df_encoded: pd.DataFrame, stats: dict):
|
|
|
234
234
|
d = df_encoded["day"] + stats["min_values"]["day"]
|
|
235
235
|
# fix invalid dates by setting these to last day of month
|
|
236
236
|
is_leap = y.apply(lambda x: calendar.isleap(x))
|
|
237
|
-
d
|
|
238
|
-
d[
|
|
239
|
-
d[
|
|
237
|
+
d = d.copy()
|
|
238
|
+
d.loc[is_leap & (m == 2) & (d > 29)] = 29
|
|
239
|
+
d.loc[~is_leap & (m == 2) & (d > 28)] = 28
|
|
240
|
+
d.loc[((m == 4) | (m == 6) | (m == 9) | (m == 11)) & (d > 30)] = 30
|
|
240
241
|
# concatenate to datetime string
|
|
241
242
|
y = y.astype(str)
|
|
242
243
|
m = m.astype(str).str.zfill(2)
|
|
@@ -271,7 +272,7 @@ def decode_datetime(df_encoded: pd.DataFrame, stats: dict):
|
|
|
271
272
|
# set all values to NaN if no valid values were present
|
|
272
273
|
values[df_encoded["nan"] == 0] = pd.NA
|
|
273
274
|
# convert from string to datetime
|
|
274
|
-
values = pd.to_datetime(values)
|
|
275
|
+
values = pd.to_datetime(values).astype("datetime64[ns]")
|
|
275
276
|
if not stats["has_time"]:
|
|
276
|
-
values = pd.to_datetime(values.dt.date)
|
|
277
|
+
values = pd.to_datetime(values.dt.date).astype("datetime64[ns]")
|
|
277
278
|
return values
|
{mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_language/engine/vllm_engine.py
RENAMED
|
@@ -27,11 +27,10 @@ from peft import PeftConfig
|
|
|
27
27
|
from pydantic import BaseModel
|
|
28
28
|
from transformers import AutoConfig, AutoTokenizer
|
|
29
29
|
from vllm import LLM, SamplingParams
|
|
30
|
-
from vllm.config import _get_and_verify_max_len
|
|
31
30
|
from vllm.distributed import cleanup_dist_env_and_memory
|
|
32
31
|
from vllm.inputs.data import TokensPrompt
|
|
33
32
|
from vllm.lora.request import LoRARequest
|
|
34
|
-
from vllm.sampling_params import
|
|
33
|
+
from vllm.sampling_params import StructuredOutputsParams
|
|
35
34
|
|
|
36
35
|
from mostlyai.engine._language.common import is_bf16_supported
|
|
37
36
|
from mostlyai.engine._language.engine.base import EngineMetrics, LanguageEngine
|
|
@@ -80,8 +79,11 @@ class VLLMEngine(LanguageEngine):
|
|
|
80
79
|
|
|
81
80
|
model_path = str(model_path)
|
|
82
81
|
self._lora_request = LoRARequest("adapter", 1, model_path)
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
# Get max model length from config (different models use different attribute names)
|
|
83
|
+
config_max_model_len = getattr(
|
|
84
|
+
base_config,
|
|
85
|
+
"max_position_embeddings",
|
|
86
|
+
getattr(base_config, "n_positions", getattr(base_config, "max_sequence_length", 2048)),
|
|
85
87
|
)
|
|
86
88
|
|
|
87
89
|
self.llm = LLM(
|
|
@@ -136,18 +138,18 @@ class VLLMEngine(LanguageEngine):
|
|
|
136
138
|
|
|
137
139
|
sampling_params = []
|
|
138
140
|
for i in range(actual_batch_size):
|
|
139
|
-
|
|
141
|
+
structured_outputs = None
|
|
140
142
|
if effective_schemas and i < len(effective_schemas):
|
|
141
|
-
# Convert Pydantic model to JSON schema for
|
|
143
|
+
# Convert Pydantic model to JSON schema for structured output
|
|
142
144
|
schema_dict = effective_schemas[i].model_json_schema()
|
|
143
|
-
|
|
145
|
+
structured_outputs = StructuredOutputsParams(json=schema_dict)
|
|
144
146
|
|
|
145
147
|
sampling_params.append(
|
|
146
148
|
SamplingParams(
|
|
147
149
|
max_tokens=self.max_new_tokens,
|
|
148
150
|
temperature=sampling_temperature,
|
|
149
151
|
top_p=sampling_top_p,
|
|
150
|
-
|
|
152
|
+
structured_outputs=structured_outputs,
|
|
151
153
|
)
|
|
152
154
|
)
|
|
153
155
|
t_generate = time.time()
|
|
@@ -75,14 +75,23 @@ def create_schemas(
|
|
|
75
75
|
numeric_fields = field_types.get(ModelEncodingType.language_numeric, [])
|
|
76
76
|
datetime_fields = field_types.get(ModelEncodingType.language_datetime, [])
|
|
77
77
|
cache = {}
|
|
78
|
+
|
|
79
|
+
def _normalize_seed_value(seed_value):
|
|
80
|
+
return None if pd.isna(seed_value) else seed_value
|
|
81
|
+
|
|
78
82
|
for _, seed_row in seed_df.iterrows():
|
|
79
|
-
|
|
83
|
+
normalized_seed_items = [
|
|
84
|
+
(field_name, _normalize_seed_value(seed_value)) for field_name, seed_value in seed_row.items()
|
|
85
|
+
]
|
|
86
|
+
cache_key = hash(
|
|
87
|
+
tuple(sorted([(field_name, str(seed_value)) for field_name, seed_value in normalized_seed_items]))
|
|
88
|
+
)
|
|
80
89
|
if cache_key in cache:
|
|
81
90
|
yield cache[cache_key]
|
|
82
91
|
continue
|
|
83
92
|
model_dict = {}
|
|
84
93
|
if not seed_row.empty:
|
|
85
|
-
model_dict |= {field_name: (Literal[seed_value], ...) for field_name, seed_value in
|
|
94
|
+
model_dict |= {field_name: (Literal[seed_value], ...) for field_name, seed_value in normalized_seed_items} # type: ignore[valid-type]
|
|
86
95
|
for field_name in unseeded_fields:
|
|
87
96
|
if field_name in categorical_fields:
|
|
88
97
|
categories = stats["columns"][field_name]["categories"]
|
|
@@ -164,10 +164,11 @@ def _regressor_heuristic(id: str, model_size: ModelSizeOrUnits, dim_input: int,
|
|
|
164
164
|
def _flat_context_heuristic(id: str, model_size: ModelSizeOrUnits, dim_input: int) -> list[int]:
|
|
165
165
|
if isinstance(model_size, dict):
|
|
166
166
|
return model_size[id]
|
|
167
|
-
model_size_layers = dict(S=[
|
|
167
|
+
model_size_layers = dict(S=[2], M=[8], L=[32])
|
|
168
168
|
layers = model_size_layers[model_size]
|
|
169
169
|
coefficient = round(np.log(max(dim_input, np.e)))
|
|
170
170
|
dims = [unit * coefficient for unit in layers]
|
|
171
|
+
_LOG.info(f"[ARGN] flat context heuristic: {dim_input=} -> {dims}")
|
|
171
172
|
return dims
|
|
172
173
|
|
|
173
174
|
|
|
@@ -176,10 +177,11 @@ def _sequential_context_heuristic(
|
|
|
176
177
|
) -> list[int]:
|
|
177
178
|
if isinstance(model_size, dict):
|
|
178
179
|
return model_size[id]
|
|
179
|
-
model_size_layers = dict(S=[
|
|
180
|
+
model_size_layers = dict(S=[4], M=[16], L=[64, 64])
|
|
180
181
|
layers = model_size_layers[model_size]
|
|
181
182
|
coefficient = round(np.log(max(dim_input * seq_len_median, np.e)))
|
|
182
183
|
dims = [unit * coefficient for unit in layers]
|
|
184
|
+
_LOG.info(f"[ARGN] sequential context heuristic: {dim_input=} x {seq_len_median=} -> {dims}")
|
|
183
185
|
return dims
|
|
184
186
|
|
|
185
187
|
|
|
@@ -190,6 +192,7 @@ def _history_heuristic(id: str, model_size: ModelSizeOrUnits, dim_input: int, se
|
|
|
190
192
|
layers = model_size_layers[model_size]
|
|
191
193
|
coefficient = round(np.log(max(dim_input * seq_len_median, np.e)))
|
|
192
194
|
dims = [unit * coefficient for unit in layers]
|
|
195
|
+
_LOG.info(f"[ARGN] history heuristic: {dim_input=} x {seq_len_median=} -> {dims}")
|
|
193
196
|
return dims
|
|
194
197
|
|
|
195
198
|
|
|
@@ -197,7 +197,7 @@ def prepare_context_inputs(
|
|
|
197
197
|
# Build flat context inputs (CTXFLT/*)
|
|
198
198
|
ctxflt_inputs = {
|
|
199
199
|
col: torch.unsqueeze(
|
|
200
|
-
torch.as_tensor(ctx_encoded[col].to_numpy(), device=device).type(torch.int),
|
|
200
|
+
torch.as_tensor(ctx_encoded[col].to_numpy(copy=True), device=device).type(torch.int),
|
|
201
201
|
dim=-1,
|
|
202
202
|
)
|
|
203
203
|
for col in ctx_encoded.columns
|
|
@@ -640,10 +640,9 @@ def decode_buffered_samples(
|
|
|
640
640
|
keys=keys,
|
|
641
641
|
key_name=tgt_context_key,
|
|
642
642
|
)
|
|
643
|
-
df_syn = df_syn.drop(
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
).reset_index(drop=True)
|
|
643
|
+
df_syn = df_syn.drop(columns=[c for c in df_syn.columns if c.startswith(POSITIONAL_COLUMN)]).reset_index(
|
|
644
|
+
drop=True
|
|
645
|
+
)
|
|
647
646
|
else:
|
|
648
647
|
data, seed_data = zip(*buffer.buffer)
|
|
649
648
|
df_syn = pd.concat(data, axis=0).reset_index(drop=True)
|
|
@@ -820,6 +819,7 @@ def generate(
|
|
|
820
819
|
|
|
821
820
|
if not enable_flexible_generation:
|
|
822
821
|
check_column_order(gen_column_order, trn_column_order)
|
|
822
|
+
|
|
823
823
|
_LOG.info(f"{rare_category_replacement_method=}")
|
|
824
824
|
rare_token_fixed_probs = fix_rare_token_probs(tgt_stats, rare_category_replacement_method)
|
|
825
825
|
imputation_fixed_probs = _fix_imputation_probs(tgt_stats, imputation)
|
|
@@ -1223,7 +1223,9 @@ def generate(
|
|
|
1223
1223
|
# Use context inputs prepared earlier
|
|
1224
1224
|
x = ctx_inputs
|
|
1225
1225
|
fixed_values = {
|
|
1226
|
-
col: torch.as_tensor(seed_batch_encoded[col].to_numpy(), device=model.device).type(
|
|
1226
|
+
col: torch.as_tensor(seed_batch_encoded[col].to_numpy(copy=True), device=model.device).type(
|
|
1227
|
+
torch.int
|
|
1228
|
+
)
|
|
1227
1229
|
for col in seed_batch_encoded.columns
|
|
1228
1230
|
if col in tgt_sub_columns
|
|
1229
1231
|
}
|
|
@@ -245,6 +245,7 @@ def _generate_marginal_probs(
|
|
|
245
245
|
tgt_stats: dict,
|
|
246
246
|
seed_columns: list[str],
|
|
247
247
|
device: torch.device,
|
|
248
|
+
n_samples: int,
|
|
248
249
|
ctx_data: pd.DataFrame | None = None,
|
|
249
250
|
ctx_stats: dict | None = None,
|
|
250
251
|
fixed_probs: dict | None = None,
|
|
@@ -259,6 +260,7 @@ def _generate_marginal_probs(
|
|
|
259
260
|
tgt_stats: Target statistics
|
|
260
261
|
seed_columns: Seed column names in original format, in correct order
|
|
261
262
|
device: Device for computation
|
|
263
|
+
n_samples: Number of samples to generate probabilities for
|
|
262
264
|
ctx_data: Optional context data
|
|
263
265
|
ctx_stats: Optional context statistics (required if ctx_data provided)
|
|
264
266
|
fixed_probs: Optional fixed probabilities for rare token handling
|
|
@@ -266,7 +268,6 @@ def _generate_marginal_probs(
|
|
|
266
268
|
Returns:
|
|
267
269
|
DataFrame of shape (n_samples, cardinality) with probabilities and column names
|
|
268
270
|
"""
|
|
269
|
-
n_samples = len(seed_encoded)
|
|
270
271
|
target_stats = tgt_stats["columns"][target_column]
|
|
271
272
|
|
|
272
273
|
# Build fixed_values dict from seed_encoded
|
|
@@ -362,15 +363,14 @@ def predict_proba(
|
|
|
362
363
|
)
|
|
363
364
|
)
|
|
364
365
|
|
|
365
|
-
# Get seed column names (needed for column order check and _generate_marginal_probs)
|
|
366
366
|
seed_columns = list(seed_data.columns)
|
|
367
367
|
|
|
368
|
-
# Check column order when flexible generation is disabled
|
|
369
368
|
if not enable_flexible_generation:
|
|
370
369
|
seed_columns_argn = get_argn_column_names(tgt_stats["columns"], seed_columns)
|
|
371
370
|
target_columns_argn = get_argn_column_names(tgt_stats["columns"], target_columns)
|
|
372
|
-
|
|
373
|
-
|
|
371
|
+
columns_to_check = seed_columns_argn + target_columns_argn
|
|
372
|
+
expected_order = [col for col in all_columns if col in columns_to_check]
|
|
373
|
+
check_column_order(columns_to_check, expected_order)
|
|
374
374
|
|
|
375
375
|
# Encode seed data (features to condition on) - common for both single and multi-target
|
|
376
376
|
# seed_data should NOT include any target columns
|
|
@@ -407,6 +407,7 @@ def predict_proba(
|
|
|
407
407
|
tgt_stats=tgt_stats,
|
|
408
408
|
seed_columns=seed_columns,
|
|
409
409
|
device=device,
|
|
410
|
+
n_samples=n_samples,
|
|
410
411
|
ctx_data=ctx_data,
|
|
411
412
|
ctx_stats=ctx_stats,
|
|
412
413
|
fixed_probs=fixed_probs,
|
|
@@ -451,10 +452,10 @@ def predict_proba(
|
|
|
451
452
|
# Build DataFrames for each combo with actual values, then concatenate
|
|
452
453
|
combo_dfs = []
|
|
453
454
|
for combo_idx, prev_combo in enumerate(prev_combos):
|
|
454
|
-
#
|
|
455
|
-
|
|
455
|
+
# Build data dict starting with columns from extended_seed
|
|
456
|
+
data = {col: extended_seed[col].values for col in extended_seed.columns}
|
|
456
457
|
|
|
457
|
-
# Add previous target columns with actual values
|
|
458
|
+
# Add previous target columns with actual values
|
|
458
459
|
for i in range(target_idx):
|
|
459
460
|
prev_target_col = target_columns[i]
|
|
460
461
|
encoded_val = prev_combo[i]
|
|
@@ -466,8 +467,10 @@ def predict_proba(
|
|
|
466
467
|
argn_column=prev_target_stats[ARGN_COLUMN],
|
|
467
468
|
argn_sub_column=sub_col_key,
|
|
468
469
|
)
|
|
469
|
-
|
|
470
|
+
data[full_sub_col_name] = encoded_val
|
|
470
471
|
|
|
472
|
+
# Create DataFrame with explicit row count
|
|
473
|
+
df = pd.DataFrame(data, index=range(n_samples))
|
|
471
474
|
combo_dfs.append(df)
|
|
472
475
|
|
|
473
476
|
# Concatenate all combo DataFrames into single batch
|
|
@@ -490,6 +493,7 @@ def predict_proba(
|
|
|
490
493
|
tgt_stats=tgt_stats,
|
|
491
494
|
seed_columns=extended_seed_columns,
|
|
492
495
|
device=device,
|
|
496
|
+
n_samples=n_samples * num_prev_combos,
|
|
493
497
|
ctx_data=batched_ctx_data,
|
|
494
498
|
ctx_stats=ctx_stats,
|
|
495
499
|
fixed_probs=fixed_probs,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mostlyai-engine"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.5.0"
|
|
4
4
|
description = "Synthetic Data Engine"
|
|
5
5
|
authors = [{ name = "MOSTLY AI", email = "dev@mostly.ai" }]
|
|
6
|
-
requires-python = ">=3.
|
|
6
|
+
requires-python = ">=3.11,<3.14"
|
|
7
7
|
readme = "README.md"
|
|
8
8
|
license = "Apache-2.0"
|
|
9
9
|
classifiers = [
|
|
@@ -14,7 +14,6 @@ classifiers = [
|
|
|
14
14
|
"Intended Audience :: Financial and Insurance Industry",
|
|
15
15
|
"Intended Audience :: Healthcare Industry",
|
|
16
16
|
"Intended Audience :: Telecommunications Industry",
|
|
17
|
-
"Programming Language :: Python :: 3.10",
|
|
18
17
|
"Programming Language :: Python :: 3.11",
|
|
19
18
|
"Programming Language :: Python :: 3.12",
|
|
20
19
|
"Programming Language :: Python :: 3.13",
|
|
@@ -25,7 +24,7 @@ classifiers = [
|
|
|
25
24
|
]
|
|
26
25
|
|
|
27
26
|
dependencies = [
|
|
28
|
-
"setuptools>=77.0.3",
|
|
27
|
+
"setuptools>=77.0.3",
|
|
29
28
|
"numpy>=2.0.0",
|
|
30
29
|
"pandas>=2.2.0",
|
|
31
30
|
"pyarrow>=16.0.0",
|
|
@@ -33,24 +32,24 @@ dependencies = [
|
|
|
33
32
|
"scikit-learn>=1.4.0",
|
|
34
33
|
"psutil>=5.9.5,<6", # upgrade when colab psutil is updated
|
|
35
34
|
"tokenizers>=0.21.0",
|
|
36
|
-
"transformers>=4.55.0", #
|
|
35
|
+
"transformers>=4.55.0,<5", # keep <5 for vllm==0.12 compatibility
|
|
37
36
|
"datasets>=3.0.0",
|
|
38
37
|
"accelerate>=1.5.0",
|
|
39
38
|
"peft>=0.12.0",
|
|
40
39
|
"huggingface-hub[hf-xet]>=0.30.2",
|
|
41
40
|
"opacus>=1.5.4",
|
|
42
|
-
"xgrammar>=0.1.21",
|
|
41
|
+
"xgrammar>=0.1.21",
|
|
43
42
|
"json-repair>=0.47.0",
|
|
44
|
-
"torch>=2.
|
|
45
|
-
"torchaudio>=2.
|
|
46
|
-
"torchvision>=0.
|
|
43
|
+
"torch>=2.9.0,<2.10.0",
|
|
44
|
+
"torchaudio>=2.9.0,<2.10.0",
|
|
45
|
+
"torchvision>=0.24.0,<0.25.0"
|
|
47
46
|
]
|
|
48
47
|
|
|
49
48
|
[project.optional-dependencies]
|
|
50
49
|
gpu = [
|
|
51
50
|
"bitsandbytes==0.42.0; sys_platform == 'darwin'",
|
|
52
51
|
"bitsandbytes>=0.45.5; sys_platform == 'linux'",
|
|
53
|
-
"vllm==0.
|
|
52
|
+
"vllm==0.12.0; sys_platform == 'linux' or sys_platform == 'darwin'",
|
|
54
53
|
]
|
|
55
54
|
|
|
56
55
|
[dependency-groups]
|
|
@@ -95,8 +94,9 @@ requires = ["hatchling", "hatch-vcs"]
|
|
|
95
94
|
build-backend = "hatchling.build"
|
|
96
95
|
|
|
97
96
|
[tool.ruff]
|
|
98
|
-
target-version = "
|
|
97
|
+
target-version = "py311"
|
|
99
98
|
line-length = 120
|
|
99
|
+
extend-exclude = ["*.ipynb"]
|
|
100
100
|
[tool.ruff.format]
|
|
101
101
|
exclude = ["examples/*.ipynb"]
|
|
102
102
|
[tool.ruff.lint]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/language/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/language/datetime.py
RENAMED
|
File without changes
|
{mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/language/numeric.py
RENAMED
|
File without changes
|
{mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/language/text.py
RENAMED
|
File without changes
|
{mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/tabular/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/tabular/itt.py
RENAMED
|
File without changes
|
{mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/tabular/lat_long.py
RENAMED
|
File without changes
|
{mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_encoding_types/tabular/numeric.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_language/engine/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_language/engine/hf_engine.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mostlyai_engine-2.3.3 → mostlyai_engine-2.5.0}/mostlyai/engine/_language/tokenizer_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|