maxframe 2.2.0__cp312-cp312-macosx_10_9_universal2.whl → 2.3.0rc1__cp312-cp312-macosx_10_9_universal2.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.cpython-312-darwin.so +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/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.cpython-312-darwin.so +0 -0
- maxframe/opcodes.py +9 -1
- maxframe/remote/core.py +4 -0
- maxframe/serialization/core.cpython-312-darwin.so +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 +112 -89
- 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,359 @@
|
|
|
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 json
|
|
16
|
+
import os.path
|
|
17
|
+
from typing import List
|
|
18
|
+
|
|
19
|
+
import pytest
|
|
20
|
+
|
|
21
|
+
from ..core import (
|
|
22
|
+
ResourceNameTemplate,
|
|
23
|
+
_registered_llm_models,
|
|
24
|
+
clean_registered_model,
|
|
25
|
+
get_registered_model_config,
|
|
26
|
+
register_model_deployments,
|
|
27
|
+
)
|
|
28
|
+
from ..framework import InferenceFrameworkEnum
|
|
29
|
+
from ..loader import (
|
|
30
|
+
DeploymentConfigBase,
|
|
31
|
+
DeploymentConfigResourceRequirement,
|
|
32
|
+
FrameworkDeploymentConfig,
|
|
33
|
+
ModelDeploymentConfig,
|
|
34
|
+
_model_config_to_json,
|
|
35
|
+
register_models_from_json_file,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@pytest.fixture
|
|
42
|
+
def qwen2_5_0_5b_model():
|
|
43
|
+
model_name = "Qwen2.5-0.5B-instruct"
|
|
44
|
+
model_file_resource = ResourceNameTemplate(
|
|
45
|
+
project="bigdata_public_dataset",
|
|
46
|
+
schema="data_plus_ai_{region_id}",
|
|
47
|
+
name="Qwen2.5-0.5B-Instruct.Q8_0.gguf",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
common_load_params = {
|
|
51
|
+
"chat_format": "qwen",
|
|
52
|
+
"flash_attn": True,
|
|
53
|
+
"cache-type-k": "q8_0",
|
|
54
|
+
"cache-type-v": "q8_0",
|
|
55
|
+
"defrag-thold": "1",
|
|
56
|
+
}
|
|
57
|
+
common_config = DeploymentConfigBase.from_json(
|
|
58
|
+
{
|
|
59
|
+
"properties": {"family": "Qwen2.5", "type": "instruct"},
|
|
60
|
+
"load_params": common_load_params,
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
load_params = {"n_threads": 2, "n_batch": 1024, "n_ctx": 32768, "verbose": False}
|
|
65
|
+
calc_resources = DeploymentConfigResourceRequirement(cpu=2, memory=8)
|
|
66
|
+
deployment_llama_cpp_python = FrameworkDeploymentConfig(
|
|
67
|
+
model_file="Qwen2.5-0.5B-Instruct.Q8_0.gguf",
|
|
68
|
+
framework=InferenceFrameworkEnum.LLAMA_CPP_PYTHON_TEXT.value,
|
|
69
|
+
device="cpu",
|
|
70
|
+
model_file_resources=[model_file_resource],
|
|
71
|
+
load_params=load_params,
|
|
72
|
+
resource_requirements=calc_resources,
|
|
73
|
+
)
|
|
74
|
+
deployment_llama_server = FrameworkDeploymentConfig(
|
|
75
|
+
model_file="Qwen2.5-0.5B-Instruct.Q8_0.gguf",
|
|
76
|
+
framework=InferenceFrameworkEnum.LLAMA_CPP_SERVE_TEXT.value,
|
|
77
|
+
device="cpu",
|
|
78
|
+
model_file_resources=[model_file_resource],
|
|
79
|
+
load_params=load_params,
|
|
80
|
+
resource_requirements=calc_resources,
|
|
81
|
+
)
|
|
82
|
+
deployments = [deployment_llama_cpp_python, deployment_llama_server]
|
|
83
|
+
|
|
84
|
+
return ModelDeploymentConfig(
|
|
85
|
+
model_name=model_name,
|
|
86
|
+
tags=["tag"],
|
|
87
|
+
common_config=common_config,
|
|
88
|
+
deployments=deployments,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@pytest.fixture
|
|
93
|
+
def qwen3_8b_model():
|
|
94
|
+
json_config = """
|
|
95
|
+
{
|
|
96
|
+
"model_name": "Qwen3-0.6B",
|
|
97
|
+
"model_identifier": "qwen3-0.6b",
|
|
98
|
+
"common_config": {
|
|
99
|
+
"properties": {
|
|
100
|
+
"family": "Qwen3",
|
|
101
|
+
"reasoning_model": true
|
|
102
|
+
},
|
|
103
|
+
"load_params": {
|
|
104
|
+
"flash_attn": true,
|
|
105
|
+
"cache-type-k": "q8_0",
|
|
106
|
+
"cache-type-v": "q8_0",
|
|
107
|
+
"defrag-thold": "1",
|
|
108
|
+
"n_threads": 2,
|
|
109
|
+
"n_batch": 1024,
|
|
110
|
+
"n_ctx": 32768,
|
|
111
|
+
"verbose": false
|
|
112
|
+
},
|
|
113
|
+
"model_file": "Qwen3-0.6B-Q8_0.gguf",
|
|
114
|
+
"model_file_resources": [
|
|
115
|
+
{
|
|
116
|
+
"project": "bigdata_public_dataset",
|
|
117
|
+
"schema": "data_plus_ai_{region_id}",
|
|
118
|
+
"name": "Qwen3-0.6B-Q8_0.gguf"
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
"resource_requirements": {
|
|
122
|
+
"cpu": 2,
|
|
123
|
+
"memory": 8
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"deployments": [
|
|
127
|
+
{
|
|
128
|
+
"framework": "LLAMA_CPP_SERVE:TEXT",
|
|
129
|
+
"device": "cpu",
|
|
130
|
+
"load_params": {},
|
|
131
|
+
"resource_requirements": {},
|
|
132
|
+
"properties": {}
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"framework": "LLAMA_CPP_PYTHON:TEXT",
|
|
136
|
+
"device": "cpu",
|
|
137
|
+
"load_params": {},
|
|
138
|
+
"resource_requirements": {
|
|
139
|
+
"cpu": 2,
|
|
140
|
+
"memory": 8
|
|
141
|
+
},
|
|
142
|
+
"properties": {}
|
|
143
|
+
}
|
|
144
|
+
],
|
|
145
|
+
"tags": []
|
|
146
|
+
}
|
|
147
|
+
"""
|
|
148
|
+
model = ModelDeploymentConfig.from_json(json.loads(json_config))
|
|
149
|
+
return model
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@pytest.fixture
|
|
153
|
+
def models(qwen2_5_0_5b_model, qwen3_8b_model):
|
|
154
|
+
model_list = [qwen2_5_0_5b_model, qwen3_8b_model]
|
|
155
|
+
import uuid
|
|
156
|
+
|
|
157
|
+
test_json_path = os.path.join(current_dir, f"./test_models_{uuid.uuid4()}.json")
|
|
158
|
+
try:
|
|
159
|
+
with open(test_json_path, "w") as f:
|
|
160
|
+
f.write(_model_config_to_json(model_list))
|
|
161
|
+
|
|
162
|
+
yield test_json_path, model_list
|
|
163
|
+
finally:
|
|
164
|
+
clean_registered_model()
|
|
165
|
+
if os.path.exists(test_json_path):
|
|
166
|
+
os.remove(test_json_path)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def test_serialize_and_deserialize(qwen2_5_0_5b_model):
|
|
170
|
+
serialized = qwen2_5_0_5b_model.to_json()
|
|
171
|
+
deserialized = ModelDeploymentConfig.from_json(serialized)
|
|
172
|
+
assert serialized == deserialized.to_json()
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def test_register_models():
|
|
176
|
+
clean_registered_model()
|
|
177
|
+
register_model_deployments(
|
|
178
|
+
model_name="mock_model",
|
|
179
|
+
model_file="DeepSeek-R1-Distill-Qwen-1.5B-Q8_0.gguf",
|
|
180
|
+
inference_frameworks=InferenceFrameworkEnum.LLAMA_CPP_PYTHON_TEXT,
|
|
181
|
+
required_resources=[
|
|
182
|
+
ResourceNameTemplate(
|
|
183
|
+
project="test",
|
|
184
|
+
schema=None,
|
|
185
|
+
name="DeepSeek-R1-Distill-Qwen-1.5B-Q8_0.gguf",
|
|
186
|
+
),
|
|
187
|
+
],
|
|
188
|
+
default_load_params={
|
|
189
|
+
"n_threads": 3,
|
|
190
|
+
"n_threads_batch": 512,
|
|
191
|
+
"n_batch": 512,
|
|
192
|
+
"flash_attn": True,
|
|
193
|
+
"verbose": True,
|
|
194
|
+
"n_ctx": 32 * 1024,
|
|
195
|
+
},
|
|
196
|
+
required_cpu=2,
|
|
197
|
+
required_memory=8,
|
|
198
|
+
device="cpu",
|
|
199
|
+
properties={"reasoning_model": True},
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
cpu_model_config = get_registered_model_config(
|
|
203
|
+
"mock_model",
|
|
204
|
+
expected_device="cpu",
|
|
205
|
+
expected_inference_framework=InferenceFrameworkEnum.LLAMA_CPP_PYTHON_TEXT,
|
|
206
|
+
)
|
|
207
|
+
assert cpu_model_config is not None
|
|
208
|
+
assert cpu_model_config is get_registered_model_config(
|
|
209
|
+
"mock_model", expected_device="cpu"
|
|
210
|
+
)
|
|
211
|
+
general_config = get_registered_model_config("mock_model")
|
|
212
|
+
assert cpu_model_config.model_name == general_config.model_name
|
|
213
|
+
assert cpu_model_config.device == general_config.device
|
|
214
|
+
assert cpu_model_config.device == "cpu"
|
|
215
|
+
assert cpu_model_config.required_memory == 8
|
|
216
|
+
assert cpu_model_config.required_cpu == 2
|
|
217
|
+
assert cpu_model_config.required_resource_files == [
|
|
218
|
+
ResourceNameTemplate(
|
|
219
|
+
project="test", schema=None, name="DeepSeek-R1-Distill-Qwen-1.5B-Q8_0.gguf"
|
|
220
|
+
)
|
|
221
|
+
]
|
|
222
|
+
assert cpu_model_config.load_params.get("flash_attn") is True
|
|
223
|
+
|
|
224
|
+
register_model_deployments(
|
|
225
|
+
model_name="mock_model",
|
|
226
|
+
model_file="DeepSeek-R1-Distill-Qwen-1.5B-Q8_0.gguf",
|
|
227
|
+
inference_frameworks=InferenceFrameworkEnum.LLAMA_CPP_PYTHON_TEXT,
|
|
228
|
+
required_resources=[],
|
|
229
|
+
default_load_params={},
|
|
230
|
+
required_cpu=2,
|
|
231
|
+
required_memory=8,
|
|
232
|
+
device="gpu",
|
|
233
|
+
properties={"reasoning_model": True},
|
|
234
|
+
)
|
|
235
|
+
gpu_model_config = get_registered_model_config("mock_model", expected_device="gpu")
|
|
236
|
+
cpu_model_config_again = get_registered_model_config(
|
|
237
|
+
"mock_model", expected_device="cpu"
|
|
238
|
+
)
|
|
239
|
+
assert cpu_model_config.model_name == cpu_model_config_again.model_name
|
|
240
|
+
assert cpu_model_config.device == cpu_model_config_again.device
|
|
241
|
+
assert cpu_model_config.device != gpu_model_config.device
|
|
242
|
+
|
|
243
|
+
with pytest.raises(ValueError):
|
|
244
|
+
register_model_deployments(
|
|
245
|
+
model_name="mock_model",
|
|
246
|
+
model_file="DeepSeek-R1-Distill-Qwen-1.5B-Q8_0.gguf",
|
|
247
|
+
inference_frameworks=InferenceFrameworkEnum.LLAMA_CPP_PYTHON_TEXT,
|
|
248
|
+
required_resources=[],
|
|
249
|
+
default_load_params={},
|
|
250
|
+
required_cpu=2,
|
|
251
|
+
required_memory=8,
|
|
252
|
+
device="gpu",
|
|
253
|
+
properties={"reasoning_model": True},
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def test_model_config_register(models):
|
|
258
|
+
clean_registered_model()
|
|
259
|
+
import os
|
|
260
|
+
|
|
261
|
+
test_json_path, model_list = models
|
|
262
|
+
model_list: List[ModelDeploymentConfig]
|
|
263
|
+
|
|
264
|
+
with pytest.raises(FileNotFoundError):
|
|
265
|
+
register_models_from_json_file(
|
|
266
|
+
os.path.join(current_dir, "test_models_not_exist.json")
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
test_config_path = os.path.join(current_dir, test_json_path)
|
|
270
|
+
register_models_from_json_file(test_config_path)
|
|
271
|
+
|
|
272
|
+
deployments = []
|
|
273
|
+
for model in model_list:
|
|
274
|
+
deployments.extend(model.deployments)
|
|
275
|
+
|
|
276
|
+
assert len(_registered_llm_models) == len(model_list)
|
|
277
|
+
|
|
278
|
+
for model in model_list:
|
|
279
|
+
model_name = model.model_name.lower()
|
|
280
|
+
assert model_name in _registered_llm_models.keys()
|
|
281
|
+
|
|
282
|
+
for deployments in model.deployments:
|
|
283
|
+
assert deployments.device in _registered_llm_models[model_name]
|
|
284
|
+
assert (
|
|
285
|
+
InferenceFrameworkEnum.from_string(deployments.framework)
|
|
286
|
+
in _registered_llm_models[model_name][deployments.device]
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def test_resource_name_template():
|
|
291
|
+
"""Test ResourceNameTemplate functionality."""
|
|
292
|
+
# Test with single variable
|
|
293
|
+
template = ResourceNameTemplate(
|
|
294
|
+
project="test_project", schema="schema_{region_id}", name="resource_{region_id}"
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
result = template.build_full_resource_name({"region_id": "cn-hangzhou"})
|
|
298
|
+
assert "cn_hangzhou" in result
|
|
299
|
+
|
|
300
|
+
# Test with multiple variables and hyphen replacement
|
|
301
|
+
template2 = ResourceNameTemplate(
|
|
302
|
+
project="my_project",
|
|
303
|
+
schema="schema_{region_id}_{env}",
|
|
304
|
+
name="resource_{region_id}_{version}",
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
result2 = template2.build_full_resource_name(
|
|
308
|
+
{"region_id": "us-west-1", "env": "prod-test", "version": "v1-2-3"}
|
|
309
|
+
)
|
|
310
|
+
assert "us_west_1" in result2
|
|
311
|
+
assert "prod_test" in result2
|
|
312
|
+
assert "v1_2_3" in result2
|
|
313
|
+
|
|
314
|
+
# Test equality and hash
|
|
315
|
+
template3 = ResourceNameTemplate("proj", "schema", "name")
|
|
316
|
+
template4 = ResourceNameTemplate("proj", "schema", "name")
|
|
317
|
+
template5 = ResourceNameTemplate("proj2", "schema", "name")
|
|
318
|
+
|
|
319
|
+
assert template3 == template4
|
|
320
|
+
assert template3 != template5
|
|
321
|
+
assert hash(template3) == hash(template4)
|
|
322
|
+
assert hash(template3) != hash(template5)
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
@pytest.mark.parametrize(
|
|
326
|
+
"input_string,expected_enum",
|
|
327
|
+
[
|
|
328
|
+
("LLAMA_CPP_PYTHON:TEXT", InferenceFrameworkEnum.LLAMA_CPP_PYTHON_TEXT),
|
|
329
|
+
("LLAMA_CPP_SERVE:TEXT", InferenceFrameworkEnum.LLAMA_CPP_SERVE_TEXT),
|
|
330
|
+
("DASH_SCOPE:TEXT", InferenceFrameworkEnum.DASH_SCOPE_TEXT),
|
|
331
|
+
("DASH_SCOPE:MULTIMODAL", InferenceFrameworkEnum.DASH_SCOPE_MULTIMODAL),
|
|
332
|
+
("VLLM_SERVE:TEXT", InferenceFrameworkEnum.VLLM_SERVE_TEXT),
|
|
333
|
+
("OPENAI_REMOTE:TEXT", InferenceFrameworkEnum.OPENAI_REMOTE_TEXT),
|
|
334
|
+
("OTHER", InferenceFrameworkEnum.OTHER),
|
|
335
|
+
(
|
|
336
|
+
InferenceFrameworkEnum.DASH_SCOPE_TEXT,
|
|
337
|
+
InferenceFrameworkEnum.DASH_SCOPE_TEXT,
|
|
338
|
+
),
|
|
339
|
+
],
|
|
340
|
+
)
|
|
341
|
+
def test_inference_framework_enum_success(input_string, expected_enum):
|
|
342
|
+
result = InferenceFrameworkEnum.from_string(input_string)
|
|
343
|
+
assert result == expected_enum
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
@pytest.mark.parametrize(
|
|
347
|
+
"input_string",
|
|
348
|
+
[
|
|
349
|
+
"INVALID_FRAMEWORK",
|
|
350
|
+
"",
|
|
351
|
+
"llama_cpp_python:text",
|
|
352
|
+
"LLAMA_CPP_PYTHON:Text",
|
|
353
|
+
"UNKNOWN:FRAMEWORK",
|
|
354
|
+
],
|
|
355
|
+
)
|
|
356
|
+
def test_inference_framework_enum_failure(input_string):
|
|
357
|
+
"""Test that invalid inputs raise appropriate exceptions."""
|
|
358
|
+
with pytest.raises(ValueError):
|
|
359
|
+
InferenceFrameworkEnum.from_string(input_string)
|
|
@@ -19,7 +19,7 @@ from .....serialization.serializables.core import Serializable
|
|
|
19
19
|
from .....serialization.serializables.field import StringField
|
|
20
20
|
from ..core import LLMTextGenOperator
|
|
21
21
|
from ..multi_modal import MultiModalLLM
|
|
22
|
-
from ..text import
|
|
22
|
+
from ..text import TextGenLLM
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class DashScopeLLMMixin(Serializable):
|
|
@@ -33,7 +33,7 @@ class DashScopeLLMMixin(Serializable):
|
|
|
33
33
|
raise ValueError(f"{k} is not supported")
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
class DashScopeTextLLM(
|
|
36
|
+
class DashScopeTextLLM(TextGenLLM, DashScopeLLMMixin):
|
|
37
37
|
"""
|
|
38
38
|
DashScope text LLM.
|
|
39
39
|
"""
|
|
@@ -59,7 +59,7 @@ class DashScopeTextLLM(TextLLM, DashScopeLLMMixin):
|
|
|
59
59
|
prompt_template: Dict[str, Any],
|
|
60
60
|
params: Dict[str, Any] = None,
|
|
61
61
|
):
|
|
62
|
-
return
|
|
62
|
+
return DashScopeTextGenerationOp(
|
|
63
63
|
model=self,
|
|
64
64
|
prompt_template=prompt_template,
|
|
65
65
|
params=params,
|
|
@@ -93,16 +93,22 @@ class DashScopeMultiModalLLM(MultiModalLLM, DashScopeLLMMixin):
|
|
|
93
93
|
params: Dict[str, Any] = None,
|
|
94
94
|
):
|
|
95
95
|
# TODO add precheck here
|
|
96
|
-
return
|
|
96
|
+
return DashScopeMultiModalGenerationOp(
|
|
97
97
|
model=self,
|
|
98
98
|
prompt_template=prompt_template,
|
|
99
99
|
params=params,
|
|
100
100
|
)(data)
|
|
101
101
|
|
|
102
102
|
|
|
103
|
-
class
|
|
103
|
+
class DashScopeTextGenerationOp(LLMTextGenOperator):
|
|
104
104
|
_op_type_ = opcodes.DASHSCOPE_TEXT_GENERATION
|
|
105
|
+
_legacy_name = "DashScopeTextGenerationOperator"
|
|
105
106
|
|
|
106
107
|
|
|
107
|
-
class
|
|
108
|
+
class DashScopeMultiModalGenerationOp(LLMTextGenOperator):
|
|
108
109
|
_op_type_ = opcodes.DASHSCOPE_MULTI_MODAL_GENERATION
|
|
110
|
+
_legacy_name = "DashScopeMultiModalGenerationOperator"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
DashScopeTextGenerationOperator = DashScopeTextGenerationOp
|
|
114
|
+
DashScopeMultiModalGenerationOperator = DashScopeMultiModalGenerationOp
|
|
@@ -12,26 +12,34 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
from typing import Any, Dict, List
|
|
15
|
+
from typing import Any, Dict, List, Union
|
|
16
16
|
|
|
17
17
|
from ..... import opcodes
|
|
18
|
-
from .....serialization.serializables import StringField
|
|
19
|
-
from ..core import LLMTextGenOperator
|
|
20
|
-
from ..
|
|
18
|
+
from .....serialization.serializables import BoolField, ReferenceField, StringField
|
|
19
|
+
from ..core import LLMTextEmbeddingOp, LLMTextGenOperator
|
|
20
|
+
from ..deploy.config import ModelDeploymentConfig
|
|
21
|
+
from ..text import TextEmbeddingModel, TextGenLLM
|
|
21
22
|
|
|
22
23
|
|
|
23
|
-
class
|
|
24
|
+
class ManagedLLMTextGenOp(LLMTextGenOperator):
|
|
24
25
|
_op_type_ = opcodes.MANAGED_TEXT_MODAL_GENERATION
|
|
26
|
+
_legacy_name = "ManagedLLMTextGenOperator"
|
|
25
27
|
|
|
26
28
|
inference_framework: str = StringField("inference_framework", default=None)
|
|
29
|
+
simple_output: bool = BoolField("simple_output", default=False)
|
|
27
30
|
|
|
28
31
|
|
|
29
|
-
class
|
|
32
|
+
class ManagedTextGenLLM(TextGenLLM):
|
|
30
33
|
"""
|
|
31
34
|
Managed text LLM by MaxFrame.
|
|
32
35
|
"""
|
|
33
36
|
|
|
34
|
-
|
|
37
|
+
_legacy_name = "ManagedTextLLM" # since v2.3.0
|
|
38
|
+
deploy_config: ModelDeploymentConfig = ReferenceField(
|
|
39
|
+
"deploy_config", reference_type=ModelDeploymentConfig, default=None
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def __init__(self, name: str, deploy_config: ModelDeploymentConfig = None):
|
|
35
43
|
"""
|
|
36
44
|
Initialize a managed text LLM.
|
|
37
45
|
|
|
@@ -39,16 +47,73 @@ class ManagedTextLLM(TextLLM):
|
|
|
39
47
|
----------
|
|
40
48
|
name : str
|
|
41
49
|
The managed text LLM name to use.
|
|
50
|
+
deploy_config : ModelDeploymentConfig
|
|
51
|
+
The model deployment config to use.
|
|
42
52
|
"""
|
|
43
|
-
|
|
53
|
+
if deploy_config:
|
|
54
|
+
deploy_config.model_name = name
|
|
55
|
+
deploy_config.check_validity()
|
|
56
|
+
super().__init__(name=name, deploy_config=deploy_config)
|
|
44
57
|
|
|
45
58
|
def generate(
|
|
46
59
|
self,
|
|
47
60
|
data,
|
|
48
|
-
prompt_template: List[Dict[str, Any]],
|
|
61
|
+
prompt_template: Union[str, List[Dict[str, Any]]],
|
|
62
|
+
simple_output: bool = False,
|
|
49
63
|
params: Dict[str, Any] = None,
|
|
50
64
|
**kw
|
|
51
65
|
):
|
|
52
|
-
return
|
|
53
|
-
model=self,
|
|
66
|
+
return ManagedLLMTextGenOp(
|
|
67
|
+
model=self,
|
|
68
|
+
prompt_template=prompt_template,
|
|
69
|
+
simple_output=simple_output,
|
|
70
|
+
params=params,
|
|
71
|
+
**kw,
|
|
54
72
|
)(data)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# since v2.3.0, text llm has more types
|
|
76
|
+
ManagedTextLLM = ManagedTextGenLLM # for old client compatibility
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class ManagedLLMTextEmbeddingOp(LLMTextEmbeddingOp):
|
|
80
|
+
_op_type_ = opcodes.LLM_TEXT_EMBEDDING_TASK
|
|
81
|
+
inference_framework: str = StringField("inference_framework", default=None)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class ManagedTextEmbeddingModel(TextEmbeddingModel):
|
|
85
|
+
"""
|
|
86
|
+
Managed text embedder by MaxFrame.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
def __init__(self, name: str):
|
|
90
|
+
"""
|
|
91
|
+
Initialize a managed text embedder.
|
|
92
|
+
Parameters
|
|
93
|
+
----------
|
|
94
|
+
name : str
|
|
95
|
+
The managed text embedder name to use.
|
|
96
|
+
"""
|
|
97
|
+
super().__init__(name=name)
|
|
98
|
+
|
|
99
|
+
def embed(
|
|
100
|
+
self,
|
|
101
|
+
series,
|
|
102
|
+
dimensions: int = None,
|
|
103
|
+
encoding_format: str = None,
|
|
104
|
+
simple_output: bool = False,
|
|
105
|
+
params: Dict[str, Any] = None,
|
|
106
|
+
**kw
|
|
107
|
+
):
|
|
108
|
+
return ManagedLLMTextEmbeddingOp(
|
|
109
|
+
model=self,
|
|
110
|
+
dimensions=dimensions,
|
|
111
|
+
encoding_format=encoding_format,
|
|
112
|
+
simple_output=simple_output,
|
|
113
|
+
params=params,
|
|
114
|
+
task="text-embedding",
|
|
115
|
+
**kw,
|
|
116
|
+
)(series)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
ManagedLLMTextGenOperator = ManagedLLMTextGenOp
|
|
@@ -0,0 +1,72 @@
|
|
|
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 typing import Any, Dict, List
|
|
16
|
+
|
|
17
|
+
from ..... import opcodes
|
|
18
|
+
from .....serialization.serializables.field import BoolField, Int32Field, StringField
|
|
19
|
+
from ..core import LLM, LLMTextGenOperator
|
|
20
|
+
from ..text import TextGenLLM
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class OpenAICompatibleLLM(LLM):
|
|
24
|
+
"""
|
|
25
|
+
Remote OpenAI-compatible text LLM.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
base_url: str = StringField("base_url")
|
|
29
|
+
api_key: str = StringField("api_key")
|
|
30
|
+
batch_size: int = Int32Field("batch_size", default=None)
|
|
31
|
+
batch_timeout: int = Int32Field("batch_timeout", default=None)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class OpenAICompatibleTextLLM(TextGenLLM, OpenAICompatibleLLM):
|
|
35
|
+
def __init__(self, name: str, base_url: str, api_key: str):
|
|
36
|
+
"""
|
|
37
|
+
Initialize a remote OpenAI-compatible text LLM.
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
name : str
|
|
42
|
+
The remote model name to use.
|
|
43
|
+
base_url : str
|
|
44
|
+
The base URL of the OpenAI-compatible API service.
|
|
45
|
+
api_key : str
|
|
46
|
+
The API key for authentication.
|
|
47
|
+
"""
|
|
48
|
+
super().__init__(name=name)
|
|
49
|
+
self.base_url = base_url
|
|
50
|
+
self.api_key = api_key
|
|
51
|
+
|
|
52
|
+
def generate(
|
|
53
|
+
self,
|
|
54
|
+
data,
|
|
55
|
+
prompt_template: List[Dict[str, Any]],
|
|
56
|
+
simple_output: bool = False,
|
|
57
|
+
params: Dict[str, Any] = None,
|
|
58
|
+
**kw
|
|
59
|
+
):
|
|
60
|
+
return OpenAICompatibleTextGenOp(
|
|
61
|
+
model=self,
|
|
62
|
+
prompt_template=prompt_template,
|
|
63
|
+
simple_output=simple_output,
|
|
64
|
+
params=params,
|
|
65
|
+
**kw,
|
|
66
|
+
)(data)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class OpenAICompatibleTextGenOp(LLMTextGenOperator):
|
|
70
|
+
_op_type_ = opcodes.OPENAI_COMPATIBLE_TEXT_GENERATION
|
|
71
|
+
|
|
72
|
+
simple_output = BoolField("simple_output", default=False)
|
|
@@ -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,34 @@
|
|
|
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 core as llm_core
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_text_gen_operator_output_dtypes():
|
|
21
|
+
op = llm_core.LLMTextGenOperator()
|
|
22
|
+
dtypes = op.get_output_dtypes()
|
|
23
|
+
assert dtypes["response"] == np.dtype("O")
|
|
24
|
+
assert dtypes["success"] == np.dtype("bool")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_text_embedding_operator_output_dtypes_and_defaults():
|
|
28
|
+
op = llm_core.LLMTextEmbeddingOp()
|
|
29
|
+
dtypes = op.get_output_dtypes()
|
|
30
|
+
assert dtypes["response"] == np.dtype("O")
|
|
31
|
+
assert dtypes["success"] == np.dtype("bool")
|
|
32
|
+
assert op.simple_output is False
|
|
33
|
+
assert op.dimensions is None
|
|
34
|
+
assert op.encoding_format is None
|