maxframe 2.2.0__cp38-cp38-win32.whl → 2.3.0rc1__cp38-cp38-win32.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.
Potentially problematic release.
This version of maxframe might be problematic. Click here for more details.
- maxframe/_utils.cp38-win32.pyd +0 -0
- maxframe/codegen/core.py +3 -2
- maxframe/codegen/spe/dataframe/merge.py +4 -0
- maxframe/codegen/spe/dataframe/misc.py +2 -0
- maxframe/codegen/spe/dataframe/reduction.py +18 -0
- maxframe/codegen/spe/dataframe/sort.py +9 -1
- maxframe/codegen/spe/dataframe/tests/test_reduction.py +13 -0
- maxframe/codegen/spe/dataframe/tseries.py +9 -0
- maxframe/codegen/spe/learn/contrib/lightgbm.py +4 -3
- maxframe/codegen/spe/tensor/datasource.py +1 -0
- maxframe/config/config.py +3 -0
- maxframe/conftest.py +10 -0
- maxframe/core/base.py +2 -1
- maxframe/core/entity/tileables.py +2 -0
- maxframe/core/graph/core.cp38-win32.pyd +0 -0
- maxframe/core/graph/entity.py +7 -1
- maxframe/core/mode.py +6 -1
- maxframe/dataframe/__init__.py +2 -2
- maxframe/dataframe/arithmetic/__init__.py +4 -0
- maxframe/dataframe/arithmetic/maximum.py +33 -0
- maxframe/dataframe/arithmetic/minimum.py +33 -0
- maxframe/dataframe/core.py +98 -106
- maxframe/dataframe/datasource/core.py +6 -0
- maxframe/dataframe/datasource/direct.py +57 -0
- maxframe/dataframe/datasource/read_csv.py +19 -11
- maxframe/dataframe/datasource/read_odps_query.py +29 -6
- maxframe/dataframe/datasource/read_odps_table.py +32 -10
- maxframe/dataframe/datasource/read_parquet.py +38 -39
- maxframe/dataframe/datastore/__init__.py +6 -0
- maxframe/dataframe/datastore/direct.py +268 -0
- maxframe/dataframe/datastore/to_odps.py +6 -0
- maxframe/dataframe/extensions/flatjson.py +2 -1
- maxframe/dataframe/groupby/__init__.py +5 -1
- maxframe/dataframe/groupby/aggregation.py +10 -6
- maxframe/dataframe/groupby/apply_chunk.py +1 -3
- maxframe/dataframe/groupby/core.py +20 -4
- maxframe/dataframe/indexing/__init__.py +2 -1
- maxframe/dataframe/indexing/insert.py +45 -17
- maxframe/dataframe/merge/__init__.py +3 -0
- maxframe/dataframe/merge/combine.py +244 -0
- maxframe/dataframe/misc/__init__.py +14 -3
- maxframe/dataframe/misc/check_unique.py +41 -10
- maxframe/dataframe/misc/drop.py +31 -0
- maxframe/dataframe/misc/infer_dtypes.py +251 -0
- maxframe/dataframe/misc/map.py +31 -18
- maxframe/dataframe/misc/repeat.py +159 -0
- maxframe/dataframe/misc/tests/test_misc.py +35 -1
- maxframe/dataframe/missing/checkna.py +3 -2
- maxframe/dataframe/reduction/__init__.py +10 -5
- maxframe/dataframe/reduction/aggregation.py +6 -6
- maxframe/dataframe/reduction/argmax.py +7 -4
- maxframe/dataframe/reduction/argmin.py +7 -4
- maxframe/dataframe/reduction/core.py +18 -9
- maxframe/dataframe/reduction/mode.py +144 -0
- maxframe/dataframe/reduction/nunique.py +10 -3
- maxframe/dataframe/reduction/tests/test_reduction.py +12 -0
- maxframe/dataframe/sort/__init__.py +9 -2
- maxframe/dataframe/sort/argsort.py +7 -1
- maxframe/dataframe/sort/core.py +1 -1
- maxframe/dataframe/sort/rank.py +147 -0
- maxframe/dataframe/tseries/__init__.py +19 -0
- maxframe/dataframe/tseries/at_time.py +61 -0
- maxframe/dataframe/tseries/between_time.py +122 -0
- maxframe/dataframe/utils.py +30 -26
- maxframe/learn/contrib/llm/core.py +16 -7
- maxframe/learn/contrib/llm/deploy/__init__.py +13 -0
- maxframe/learn/contrib/llm/deploy/config.py +221 -0
- maxframe/learn/contrib/llm/deploy/core.py +247 -0
- maxframe/learn/contrib/llm/deploy/framework.py +35 -0
- maxframe/learn/contrib/llm/deploy/loader.py +360 -0
- maxframe/learn/contrib/llm/deploy/tests/__init__.py +13 -0
- maxframe/learn/contrib/llm/deploy/tests/test_register_models.py +359 -0
- maxframe/learn/contrib/llm/models/__init__.py +1 -0
- maxframe/learn/contrib/llm/models/dashscope.py +12 -6
- maxframe/learn/contrib/llm/models/managed.py +76 -11
- maxframe/learn/contrib/llm/models/openai.py +72 -0
- maxframe/learn/contrib/llm/tests/__init__.py +13 -0
- maxframe/learn/contrib/llm/tests/test_core.py +34 -0
- maxframe/learn/contrib/llm/tests/test_openai.py +187 -0
- maxframe/learn/contrib/llm/tests/test_text_gen.py +155 -0
- maxframe/learn/contrib/llm/text.py +348 -42
- maxframe/learn/contrib/models.py +4 -1
- maxframe/learn/contrib/xgboost/classifier.py +2 -0
- maxframe/learn/contrib/xgboost/core.py +31 -7
- maxframe/learn/contrib/xgboost/predict.py +4 -2
- maxframe/learn/contrib/xgboost/regressor.py +5 -0
- maxframe/learn/contrib/xgboost/train.py +2 -0
- maxframe/learn/preprocessing/_data/min_max_scaler.py +34 -23
- maxframe/learn/preprocessing/_data/standard_scaler.py +34 -25
- maxframe/learn/utils/__init__.py +1 -0
- maxframe/learn/utils/extmath.py +42 -9
- maxframe/learn/utils/odpsio.py +80 -11
- maxframe/lib/filesystem/_oss_lib/common.py +2 -0
- maxframe/lib/mmh3.cp38-win32.pyd +0 -0
- maxframe/opcodes.py +9 -1
- maxframe/remote/core.py +4 -0
- maxframe/serialization/core.cp38-win32.pyd +0 -0
- maxframe/serialization/tests/test_serial.py +2 -2
- maxframe/tensor/arithmetic/__init__.py +1 -1
- maxframe/tensor/arithmetic/core.py +2 -2
- maxframe/tensor/arithmetic/tests/test_arithmetic.py +0 -9
- maxframe/tensor/core.py +3 -0
- maxframe/tensor/misc/copyto.py +1 -1
- maxframe/tests/test_udf.py +61 -0
- maxframe/tests/test_utils.py +8 -5
- maxframe/udf.py +103 -7
- maxframe/utils.py +61 -8
- {maxframe-2.2.0.dist-info → maxframe-2.3.0rc1.dist-info}/METADATA +1 -2
- {maxframe-2.2.0.dist-info → maxframe-2.3.0rc1.dist-info}/RECORD +113 -90
- maxframe_client/session/task.py +8 -1
- maxframe_client/tests/test_session.py +24 -0
- maxframe/dataframe/arrays.py +0 -864
- {maxframe-2.2.0.dist-info → maxframe-2.3.0rc1.dist-info}/WHEEL +0 -0
- {maxframe-2.2.0.dist-info → maxframe-2.3.0rc1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Copyright 1999-2025 Alibaba Group Holding Ltd.
|
|
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.
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
|
|
17
|
+
from ... import opcodes
|
|
18
|
+
from ...core import get_output_types
|
|
19
|
+
from ...serialization.serializables import AnyField, Int32Field, StringField
|
|
20
|
+
from ..operators import DataFrameOperator, DataFrameOperatorMixin
|
|
21
|
+
from ..utils import parse_index, validate_axis
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class DataFrameBetweenTime(DataFrameOperator, DataFrameOperatorMixin):
|
|
25
|
+
_op_type_ = opcodes.BETWEEN_TIME
|
|
26
|
+
|
|
27
|
+
start_time = AnyField("start_time")
|
|
28
|
+
end_time = AnyField("end_time")
|
|
29
|
+
inclusive = StringField("inclusive")
|
|
30
|
+
axis = Int32Field("axis")
|
|
31
|
+
|
|
32
|
+
def __call__(self, df_or_series):
|
|
33
|
+
self._output_types = get_output_types(df_or_series)
|
|
34
|
+
out_params = df_or_series.params
|
|
35
|
+
|
|
36
|
+
new_shape = list(df_or_series.shape)
|
|
37
|
+
new_shape[self.axis] = np.nan
|
|
38
|
+
out_params["shape"] = tuple(new_shape)
|
|
39
|
+
|
|
40
|
+
idx_key_params = (df_or_series, self.start_time, self.end_time, self.inclusive)
|
|
41
|
+
if self.axis == 0:
|
|
42
|
+
out_params["index_value"] = parse_index(
|
|
43
|
+
df_or_series.index_value.to_pandas()[:0], idx_key_params
|
|
44
|
+
)
|
|
45
|
+
else:
|
|
46
|
+
out_params["columns_value"] = parse_index(
|
|
47
|
+
df_or_series.columns_value.to_pandas()[:0], idx_key_params
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
return self.new_tileable([df_or_series], **out_params)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def between_time(df_or_series, start_time, end_time, inclusive="both", axis=0):
|
|
54
|
+
"""
|
|
55
|
+
Select values between particular times of the day (e.g., 9:00-9:30 AM).
|
|
56
|
+
|
|
57
|
+
By setting ``start_time`` to be later than ``end_time``,
|
|
58
|
+
you can get the times that are *not* between the two times.
|
|
59
|
+
|
|
60
|
+
Parameters
|
|
61
|
+
----------
|
|
62
|
+
start_time : datetime.time or str
|
|
63
|
+
Initial time as a time filter limit.
|
|
64
|
+
end_time : datetime.time or str
|
|
65
|
+
End time as a time filter limit.
|
|
66
|
+
inclusive : {"both", "neither", "left", "right"}, default "both"
|
|
67
|
+
Include boundaries; whether to set each bound as closed or open.
|
|
68
|
+
axis : {0 or 'index', 1 or 'columns'}, default 0
|
|
69
|
+
Determine range time on index or columns value.
|
|
70
|
+
For `Series` this parameter is unused and defaults to 0.
|
|
71
|
+
|
|
72
|
+
Returns
|
|
73
|
+
-------
|
|
74
|
+
Series or DataFrame
|
|
75
|
+
Data from the original object filtered to the specified dates range.
|
|
76
|
+
|
|
77
|
+
Raises
|
|
78
|
+
------
|
|
79
|
+
TypeError
|
|
80
|
+
If the index is not a :class:`DatetimeIndex`
|
|
81
|
+
|
|
82
|
+
See Also
|
|
83
|
+
--------
|
|
84
|
+
at_time : Select values at a particular time of the day.
|
|
85
|
+
first : Select initial periods of time series based on a date offset.
|
|
86
|
+
last : Select final periods of time series based on a date offset.
|
|
87
|
+
DatetimeIndex.indexer_between_time : Get just the index locations for
|
|
88
|
+
values between particular times of the day.
|
|
89
|
+
|
|
90
|
+
Examples
|
|
91
|
+
--------
|
|
92
|
+
>>> import maxframe.dataframe as md
|
|
93
|
+
>>> i = md.date_range('2018-04-09', periods=4, freq='1D20min')
|
|
94
|
+
>>> ts = md.DataFrame({'A': [1, 2, 3, 4]}, index=i)
|
|
95
|
+
>>> ts.execute()
|
|
96
|
+
A
|
|
97
|
+
2018-04-09 00:00:00 1
|
|
98
|
+
2018-04-10 00:20:00 2
|
|
99
|
+
2018-04-11 00:40:00 3
|
|
100
|
+
2018-04-12 01:00:00 4
|
|
101
|
+
|
|
102
|
+
>>> ts.between_time('0:15', '0:45').execute()
|
|
103
|
+
A
|
|
104
|
+
2018-04-10 00:20:00 2
|
|
105
|
+
2018-04-11 00:40:00 3
|
|
106
|
+
|
|
107
|
+
You get the times that are *not* between two times by setting
|
|
108
|
+
``start_time`` later than ``end_time``:
|
|
109
|
+
|
|
110
|
+
>>> ts.between_time('0:45', '0:15').execute()
|
|
111
|
+
A
|
|
112
|
+
2018-04-09 00:00:00 1
|
|
113
|
+
2018-04-12 01:00:00 4
|
|
114
|
+
"""
|
|
115
|
+
axis = validate_axis(axis, df_or_series)
|
|
116
|
+
op = DataFrameBetweenTime(
|
|
117
|
+
start_time=start_time,
|
|
118
|
+
end_time=end_time,
|
|
119
|
+
inclusive=inclusive,
|
|
120
|
+
axis=axis,
|
|
121
|
+
)
|
|
122
|
+
return op(df_or_series)
|
maxframe/dataframe/utils.py
CHANGED
|
@@ -25,9 +25,9 @@ from typing import TYPE_CHECKING, Any, Callable, List, Optional
|
|
|
25
25
|
|
|
26
26
|
import numpy as np
|
|
27
27
|
import pandas as pd
|
|
28
|
-
from pandas.api.types import is_string_dtype
|
|
29
28
|
from pandas.core.dtypes.inference import is_dict_like, is_list_like
|
|
30
29
|
|
|
30
|
+
from ..config.validators import dtype_backend_validator
|
|
31
31
|
from ..core import ENTITY_TYPE, Entity, ExecutableTuple, OutputType, get_output_types
|
|
32
32
|
from ..lib.dtypes_extension import ExternalBlobDtype, SolidBlob
|
|
33
33
|
from ..lib.mmh3 import hash as mmh_hash
|
|
@@ -36,7 +36,6 @@ from ..utils import (
|
|
|
36
36
|
ModulePlaceholder,
|
|
37
37
|
is_full_slice,
|
|
38
38
|
lazy_import,
|
|
39
|
-
make_dtype,
|
|
40
39
|
make_dtypes,
|
|
41
40
|
quiet_stdio,
|
|
42
41
|
sbytes,
|
|
@@ -105,9 +104,9 @@ def hash_dtypes(dtypes, size):
|
|
|
105
104
|
return [dtypes[index] for index in hashed_indexes]
|
|
106
105
|
|
|
107
106
|
|
|
108
|
-
def sort_dataframe_inplace(df, *axis):
|
|
107
|
+
def sort_dataframe_inplace(df, *axis, **kw):
|
|
109
108
|
for ax in axis:
|
|
110
|
-
df.sort_index(axis=ax, inplace=True)
|
|
109
|
+
df.sort_index(axis=ax, inplace=True, **kw)
|
|
111
110
|
return df
|
|
112
111
|
|
|
113
112
|
|
|
@@ -1024,27 +1023,21 @@ def create_sa_connection(con, **kwargs):
|
|
|
1024
1023
|
engine.dispose()
|
|
1025
1024
|
|
|
1026
1025
|
|
|
1027
|
-
def to_arrow_dtypes(dtypes
|
|
1028
|
-
from .
|
|
1026
|
+
def to_arrow_dtypes(dtypes):
|
|
1027
|
+
from ..io.odpsio.schema import pandas_dtypes_to_arrow_schema
|
|
1029
1028
|
|
|
1029
|
+
arrow_schema = pandas_dtypes_to_arrow_schema(dtypes)
|
|
1030
1030
|
new_dtypes = dtypes.copy()
|
|
1031
1031
|
for i in range(len(dtypes)):
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
new_dtypes.iloc[i] = ArrowStringDtype()
|
|
1042
|
-
else: # pragma: no cover
|
|
1043
|
-
# empty, set arrow string dtype
|
|
1044
|
-
new_dtypes.iloc[i] = ArrowStringDtype()
|
|
1045
|
-
else:
|
|
1046
|
-
# empty, set arrow string dtype
|
|
1047
|
-
new_dtypes.iloc[i] = ArrowStringDtype()
|
|
1032
|
+
arrow_type = arrow_schema.types[i]
|
|
1033
|
+
dt = dtypes.iloc[i]
|
|
1034
|
+
if isinstance(dt, pd.api.extensions.ExtensionDtype):
|
|
1035
|
+
# make existing extension dtype consistent
|
|
1036
|
+
new_dtypes.iloc[i] = dt
|
|
1037
|
+
elif arrow_type == pa.string():
|
|
1038
|
+
new_dtypes.iloc[i] = pd.StringDtype("pyarrow")
|
|
1039
|
+
else:
|
|
1040
|
+
new_dtypes.iloc[i] = ArrowDtype(arrow_type)
|
|
1048
1041
|
return new_dtypes
|
|
1049
1042
|
|
|
1050
1043
|
|
|
@@ -1482,7 +1475,8 @@ def infer_dataframe_return_value(
|
|
|
1482
1475
|
elementwise=elementwise or False,
|
|
1483
1476
|
)
|
|
1484
1477
|
|
|
1485
|
-
ret_output_type =
|
|
1478
|
+
ret_output_type = None
|
|
1479
|
+
ret_dtypes = dtypes
|
|
1486
1480
|
maybe_agg = False
|
|
1487
1481
|
build_kw = build_kw or {}
|
|
1488
1482
|
obj_key = df_obj.key
|
|
@@ -1529,7 +1523,8 @@ def infer_dataframe_return_value(
|
|
|
1529
1523
|
f'please specify `output_type` as "dataframe"'
|
|
1530
1524
|
)
|
|
1531
1525
|
ret_output_type = ret_output_type or OutputType.dataframe
|
|
1532
|
-
|
|
1526
|
+
if ret_dtypes is None:
|
|
1527
|
+
ret_dtypes = infer_df_obj.dtypes
|
|
1533
1528
|
else:
|
|
1534
1529
|
if output_type is not None and output_type == OutputType.dataframe:
|
|
1535
1530
|
raise TypeError(
|
|
@@ -1549,7 +1544,7 @@ def infer_dataframe_return_value(
|
|
|
1549
1544
|
return InferredDataFrameMeta(
|
|
1550
1545
|
ret_output_type,
|
|
1551
1546
|
make_dtypes(ret_dtypes),
|
|
1552
|
-
|
|
1547
|
+
make_dtypes(dtype),
|
|
1553
1548
|
name,
|
|
1554
1549
|
ret_index_value,
|
|
1555
1550
|
maybe_agg,
|
|
@@ -1562,7 +1557,7 @@ def infer_dataframe_return_value(
|
|
|
1562
1557
|
return InferredDataFrameMeta(
|
|
1563
1558
|
output_type,
|
|
1564
1559
|
make_dtypes(dtypes),
|
|
1565
|
-
|
|
1560
|
+
make_dtypes(dtype),
|
|
1566
1561
|
name,
|
|
1567
1562
|
ret_index_value,
|
|
1568
1563
|
maybe_agg,
|
|
@@ -1645,3 +1640,12 @@ def call_groupby_with_params(df_or_series, groupby_params: dict):
|
|
|
1645
1640
|
if selection:
|
|
1646
1641
|
res = res[selection]
|
|
1647
1642
|
return res
|
|
1643
|
+
|
|
1644
|
+
|
|
1645
|
+
def validate_dtype_backend(value):
|
|
1646
|
+
if isinstance(value, bool):
|
|
1647
|
+
# compatibility for legacy use_arrow_dtype property
|
|
1648
|
+
value = "pyarrow" if value else "numpy"
|
|
1649
|
+
if not dtype_backend_validator(value):
|
|
1650
|
+
raise ValueError(f"Invalid dtype_backend: {value}")
|
|
1651
|
+
return value
|
|
@@ -20,12 +20,16 @@ import pandas as pd
|
|
|
20
20
|
from ....core.entity.output_types import OutputType
|
|
21
21
|
from ....core.operator.base import Operator
|
|
22
22
|
from ....core.operator.core import TileableOperatorMixin
|
|
23
|
-
from ....dataframe.core import SERIES_TYPE
|
|
24
23
|
from ....dataframe.operators import DataFrameOperatorMixin
|
|
25
24
|
from ....dataframe.utils import parse_index
|
|
26
25
|
from ....serialization.serializables import Int32Field
|
|
27
26
|
from ....serialization.serializables.core import Serializable
|
|
28
|
-
from ....serialization.serializables.field import
|
|
27
|
+
from ....serialization.serializables.field import (
|
|
28
|
+
AnyField,
|
|
29
|
+
BoolField,
|
|
30
|
+
DictField,
|
|
31
|
+
StringField,
|
|
32
|
+
)
|
|
29
33
|
|
|
30
34
|
|
|
31
35
|
class LLM(Serializable):
|
|
@@ -55,11 +59,7 @@ class LLMTaskOperator(Operator, DataFrameOperatorMixin):
|
|
|
55
59
|
col_name = list(outputs.keys())
|
|
56
60
|
columns = parse_index(pd.Index(col_name), store_data=True)
|
|
57
61
|
out_dtypes = pd.Series(list(outputs.values()), index=col_name)
|
|
58
|
-
index_value = index or
|
|
59
|
-
parse_index(pd.RangeIndex(-1), data)
|
|
60
|
-
if isinstance(data, SERIES_TYPE)
|
|
61
|
-
else data.index_value
|
|
62
|
-
)
|
|
62
|
+
index_value = index or data.index_value
|
|
63
63
|
|
|
64
64
|
return self.new_dataframe(
|
|
65
65
|
inputs=[data],
|
|
@@ -75,3 +75,12 @@ class LLMTextGenOperator(LLMTaskOperator, TileableOperatorMixin):
|
|
|
75
75
|
|
|
76
76
|
def get_output_dtypes(self) -> Dict[str, np.dtype]:
|
|
77
77
|
return {"response": np.dtype("O"), "success": np.dtype("bool")}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class LLMTextEmbeddingOp(LLMTaskOperator, TileableOperatorMixin):
|
|
81
|
+
dimensions = Int32Field("dimensions", default=None)
|
|
82
|
+
encoding_format = StringField("encoding_format", default=None)
|
|
83
|
+
simple_output = BoolField("simple_output", default=False)
|
|
84
|
+
|
|
85
|
+
def get_output_dtypes(self) -> Dict[str, np.dtype]:
|
|
86
|
+
return {"response": np.dtype("O"), "success": np.dtype("bool")}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Copyright 1999-2025 Alibaba Group Holding Ltd.
|
|
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.
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Copyright 1999-2025 Alibaba Group Holding Ltd.
|
|
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.
|
|
14
|
+
|
|
15
|
+
from copy import deepcopy
|
|
16
|
+
from typing import Any, Dict, List, Optional, Union
|
|
17
|
+
|
|
18
|
+
from .....protocol import Serializable
|
|
19
|
+
from .....serialization.serializables import StringField
|
|
20
|
+
from .....serialization.serializables.field import (
|
|
21
|
+
DictField,
|
|
22
|
+
EnumField,
|
|
23
|
+
Int32Field,
|
|
24
|
+
ListField,
|
|
25
|
+
)
|
|
26
|
+
from .....serialization.serializables.field_type import FieldTypes
|
|
27
|
+
from .framework import InferenceFrameworkEnum
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ModelDeploymentConfig(Serializable):
|
|
31
|
+
"""
|
|
32
|
+
Model deployment configuration for extending MaxFrame with custom models.
|
|
33
|
+
|
|
34
|
+
This configuration is designed for users who need to deploy models that are not
|
|
35
|
+
available within MaxFrame's built-in model offerings. It provides a way to specify
|
|
36
|
+
custom deployment solutions by informing each MaxFrame worker which framework to use,
|
|
37
|
+
which model path to load, and how to load it.
|
|
38
|
+
|
|
39
|
+
The configuration assumes that models are already set up in the container image or
|
|
40
|
+
mounted paths, and uses the current deploy_config to load them. Users are responsible
|
|
41
|
+
for ensuring the runtime environment state and compatibility.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
model_name: str
|
|
46
|
+
The name of the model.
|
|
47
|
+
model_file: str
|
|
48
|
+
The file path of the model.
|
|
49
|
+
inference_framework_type: InferenceFrameworkEnum
|
|
50
|
+
The inference framework of the model.
|
|
51
|
+
required_resource_files: List[Union[str, Any]]
|
|
52
|
+
The required resource files of the model.
|
|
53
|
+
load_params: Dict[str, Any]
|
|
54
|
+
The load params of the model.
|
|
55
|
+
required_cpu: int
|
|
56
|
+
The required cpu of the model.
|
|
57
|
+
required_memory: int
|
|
58
|
+
The required memory of the model.
|
|
59
|
+
required_gu: int
|
|
60
|
+
The required gu of the model.
|
|
61
|
+
required_gpu_memory: int
|
|
62
|
+
The required gpu memory of the model.
|
|
63
|
+
device: str
|
|
64
|
+
The device of the model. One of "cpu" or "cuda".
|
|
65
|
+
properties: Dict[str, Any]
|
|
66
|
+
The properties of the model.
|
|
67
|
+
tags: List[str]
|
|
68
|
+
The tags of the model.
|
|
69
|
+
|
|
70
|
+
Notes
|
|
71
|
+
-----
|
|
72
|
+
- Preview version for model deployments, all fields could be changed in the future.
|
|
73
|
+
|
|
74
|
+
**User Responsibility Notice**: Users must have a complete understanding of what
|
|
75
|
+
they are computing and ensure they fully comprehend the implications of their
|
|
76
|
+
configuration choices. You are responsible for:
|
|
77
|
+
|
|
78
|
+
* Ensuring model compatibility with the specified inference framework
|
|
79
|
+
* Verifying that model files exist and are accessible in the runtime environment
|
|
80
|
+
* Confirming that resource requirements (CPU, memory, GPU) are adequate
|
|
81
|
+
* Validating that all dependencies and libraries are properly installed
|
|
82
|
+
* Understanding the computational behavior and characteristics of your chosen model
|
|
83
|
+
|
|
84
|
+
Examples
|
|
85
|
+
--------
|
|
86
|
+
>>> from maxframe.learn.contrib.llm.deploy.config import ModelDeploymentConfig
|
|
87
|
+
>>> from maxframe.learn.contrib.llm.deploy.framework import InferenceFrameworkEnum
|
|
88
|
+
>>> from maxframe.learn.contrib.llm.models.managed import ManagedTextGenLLM
|
|
89
|
+
|
|
90
|
+
>>> # Configure model deployment with VLLM framework
|
|
91
|
+
>>> model_config = ModelDeploymentConfig(
|
|
92
|
+
... # Path to the model files (assumed to be available in container/mount)
|
|
93
|
+
... model_file="/models/Qwen3-4B-Instruct-2507-FP8",
|
|
94
|
+
... # Use VLLM serving framework for text generation
|
|
95
|
+
... inference_framework_type=InferenceFrameworkEnum.VLLM_SERVE_TEXT,
|
|
96
|
+
... # Framework-specific loading parameters
|
|
97
|
+
... load_params={
|
|
98
|
+
... "max_context_tokens": 4096, # Maximum context length for the model
|
|
99
|
+
... "max_startup_wait_seconds": 600 # Max wait time for model startup
|
|
100
|
+
... },
|
|
101
|
+
... # Target device for inference
|
|
102
|
+
... device="cuda",
|
|
103
|
+
... # Resource requirements (2 GPU units)
|
|
104
|
+
... required_gu=2,
|
|
105
|
+
... # Model tags for capabilities
|
|
106
|
+
... tags=["text-generation"],
|
|
107
|
+
... )
|
|
108
|
+
|
|
109
|
+
>>> # Create managed LLM instance with the deployment configuration
|
|
110
|
+
>>> llm = ManagedTextGenLLM(name="my-model", deploy_config=model_config)
|
|
111
|
+
>>> # Generate text using the deployed model.
|
|
112
|
+
>>> result_df = llm.generate(
|
|
113
|
+
... df, # Input DataFrame containing prompts
|
|
114
|
+
... prompt_template=messages, # Template for formatting prompts
|
|
115
|
+
... running_options={
|
|
116
|
+
... "max_context_tokens": 4096, # Runtime context limit
|
|
117
|
+
... },
|
|
118
|
+
... params={
|
|
119
|
+
... "temperature": 0.7, # Sampling temperature
|
|
120
|
+
... "max_tokens": 2048 # Maximum tokens to generate
|
|
121
|
+
... },
|
|
122
|
+
... )
|
|
123
|
+
|
|
124
|
+
To make this model config working with the ManagedTextGenLLM,
|
|
125
|
+
you need to provide a custom image with the required dependencies and model files.
|
|
126
|
+
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
model_name: str = StringField("model_name")
|
|
130
|
+
model_file: str = StringField("model_file")
|
|
131
|
+
inference_framework_type: InferenceFrameworkEnum = EnumField(
|
|
132
|
+
"inference_framework_type", enum_type=InferenceFrameworkEnum
|
|
133
|
+
)
|
|
134
|
+
required_resource_files: List[Union[str, Any]] = ListField(
|
|
135
|
+
"required_resource_files", field_type=FieldTypes.any, default_factory=list
|
|
136
|
+
)
|
|
137
|
+
load_params: Dict[str, Any] = DictField(
|
|
138
|
+
"load_params",
|
|
139
|
+
key_type=FieldTypes.string,
|
|
140
|
+
value_type=FieldTypes.any,
|
|
141
|
+
default_factory=dict,
|
|
142
|
+
)
|
|
143
|
+
required_cpu: Optional[int] = Int32Field("required_cpu", default=None)
|
|
144
|
+
required_memory: Optional[int] = Int32Field("required_memory", default=None)
|
|
145
|
+
required_gu: Optional[int] = Int32Field("required_gu", default=None)
|
|
146
|
+
required_gpu_memory: Optional[int] = Int32Field("required_gpu_memory", default=None)
|
|
147
|
+
device: str = StringField("device")
|
|
148
|
+
properties: Dict[str, Any] = DictField(
|
|
149
|
+
"properties",
|
|
150
|
+
key_type=FieldTypes.string,
|
|
151
|
+
value_type=FieldTypes.any,
|
|
152
|
+
default_factory=dict,
|
|
153
|
+
)
|
|
154
|
+
tags: List[str] = ListField(
|
|
155
|
+
"tags",
|
|
156
|
+
field_type=FieldTypes.string,
|
|
157
|
+
default_factory=list,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
def is_reasoning_model(self):
|
|
161
|
+
if self.properties is None:
|
|
162
|
+
return False
|
|
163
|
+
return self.properties.get("reasoning_model", False)
|
|
164
|
+
|
|
165
|
+
def copy(self) -> "ModelDeploymentConfig":
|
|
166
|
+
return deepcopy(self)
|
|
167
|
+
|
|
168
|
+
def __eq__(self, other):
|
|
169
|
+
if not isinstance(other, ModelDeploymentConfig):
|
|
170
|
+
return False
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
self.model_name == other.model_name
|
|
174
|
+
and self.model_file == other.model_file
|
|
175
|
+
and self.inference_framework_type == other.inference_framework_type
|
|
176
|
+
and self.required_resource_files == other.required_resource_files
|
|
177
|
+
and self.load_params == other.load_params
|
|
178
|
+
and self.required_cpu == other.required_cpu
|
|
179
|
+
and self.required_memory == other.required_memory
|
|
180
|
+
and self.required_gu == other.required_gu
|
|
181
|
+
and self.required_gpu_memory == other.required_gpu_memory
|
|
182
|
+
and self.device == other.device
|
|
183
|
+
and self.properties == other.properties
|
|
184
|
+
and self.tags == other.tags
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
def __hash__(self):
|
|
188
|
+
return hash(
|
|
189
|
+
(
|
|
190
|
+
self.model_name,
|
|
191
|
+
self.model_file,
|
|
192
|
+
self.inference_framework_type,
|
|
193
|
+
self.required_resource_files,
|
|
194
|
+
self.load_params,
|
|
195
|
+
self.required_cpu,
|
|
196
|
+
self.required_memory,
|
|
197
|
+
self.required_gu,
|
|
198
|
+
self.required_gpu_memory,
|
|
199
|
+
self.device,
|
|
200
|
+
self.properties,
|
|
201
|
+
self.tags,
|
|
202
|
+
)
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
def check_validity(self):
|
|
206
|
+
required_fields = [
|
|
207
|
+
"model_name",
|
|
208
|
+
"model_file",
|
|
209
|
+
"inference_framework_type",
|
|
210
|
+
"device",
|
|
211
|
+
]
|
|
212
|
+
for field in required_fields:
|
|
213
|
+
if getattr(self, field) is None:
|
|
214
|
+
raise ValueError(f"{field} is required")
|
|
215
|
+
|
|
216
|
+
one_of_fields = ["required_cpu", "required_gu"]
|
|
217
|
+
if not any(getattr(self, field) is not None for field in one_of_fields):
|
|
218
|
+
raise ValueError(f"At least one of {one_of_fields} is required")
|
|
219
|
+
|
|
220
|
+
if not self.tags:
|
|
221
|
+
raise ValueError("tags is required")
|