volcengine-python-sdk-aicc 0.0.41__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bytedance/__init__.py +4 -0
- bytedance/jeddak_secure_channel/__init__.py +28 -0
- bytedance/jeddak_secure_channel/client.py +305 -0
- bytedance/jeddak_secure_channel/config.py +171 -0
- bytedance/jeddak_secure_channel/consts.py +5 -0
- bytedance/jeddak_secure_channel/crypto/__init__.py +14 -0
- bytedance/jeddak_secure_channel/crypto/aes.py +174 -0
- bytedance/jeddak_secure_channel/crypto/envelope.py +395 -0
- bytedance/jeddak_secure_channel/crypto/rsa.py +164 -0
- bytedance/jeddak_secure_channel/error.py +131 -0
- bytedance/jeddak_secure_channel/log/__init__.py +23 -0
- bytedance/jeddak_secure_channel/log/config.py +31 -0
- bytedance/jeddak_secure_channel/log/logger.py +88 -0
- bytedance/jeddak_secure_channel/logger_factory.py +134 -0
- bytedance/jeddak_secure_channel/ra.py +772 -0
- bytedance/jeddak_secure_channel/server.py +745 -0
- bytedance/jeddak_secure_channel/service/__init__.py +16 -0
- bytedance/jeddak_secure_channel/service/eps.py +133 -0
- bytedance/jeddak_secure_channel/service/eps_models.py +47 -0
- bytedance/jeddak_secure_channel/service/ras.py +88 -0
- bytedance/jeddak_secure_channel/service/tks.py +51 -0
- bytedance/jeddak_secure_channel/service/tks_models.py +334 -0
- bytedance/jeddak_secure_channel/utils.py +469 -0
- bytedance/jeddak_secure_model/model_automation_tool.py +181 -0
- bytedance/jeddak_secure_model/model_encryption.py +416 -0
- bytedance/jeddak_secure_model/model_hub_admin.py +116 -0
- bytedance/jeddak_secure_model/pcc_utils.py +279 -0
- bytedance/jeddak_secure_model/rds_utils.py +63 -0
- bytedance/tks/attester.py +41 -0
- bytedance/tks/client.py +629 -0
- bytedance/tks/consts.py +122 -0
- bytedance/tks/crypto/aes.py +47 -0
- bytedance/tks/crypto/ec.py +39 -0
- bytedance/tks/exceptions.py +22 -0
- bytedance/volcengine_aicc_sdk/__init__.py +14 -0
- bytedance/volcengine_aicc_sdk/aicc_client.py +277 -0
- bytedance/volcengine_aicc_sdk/aicc_inference.py +64 -0
- bytedance/volcengine_aicc_sdk/ark_inference_client.py +343 -0
- bytedance/volcengine_aicc_sdk/asr_inference_client.py +158 -0
- bytedance/volcengine_aicc_sdk/asr_websocket_client.py +705 -0
- bytedance/volcengine_aicc_sdk/auc_inference_client.py +305 -0
- bytedance/volcengine_aicc_sdk/common/__init__.py +0 -0
- bytedance/volcengine_aicc_sdk/common/aes.py +136 -0
- bytedance/volcengine_aicc_sdk/common/constant.py +4 -0
- bytedance/volcengine_aicc_sdk/common/error.py +21 -0
- bytedance/volcengine_aicc_sdk/common/log/__init__.py +24 -0
- bytedance/volcengine_aicc_sdk/common/log/config.py +31 -0
- bytedance/volcengine_aicc_sdk/common/log/logger.py +88 -0
- bytedance/volcengine_aicc_sdk/common/tos_utils.py +341 -0
- bytedance/volcengine_aicc_sdk/common/utils.py +45 -0
- bytedance/volcengine_aicc_sdk/cu_inference_client.py +292 -0
- bytedance/volcengine_aicc_sdk/deepseek_inference_client.py +501 -0
- bytedance/volcengine_aicc_sdk/secure_http_client.py +406 -0
- bytedance/volcengine_aicc_sdk/seedance_inference_client.py +500 -0
- bytedance/volcengine_aicc_sdk/seedream_inference_client.py +213 -0
- volcengine_python_sdk_aicc-0.0.41.dist-info/METADATA +79 -0
- volcengine_python_sdk_aicc-0.0.41.dist-info/RECORD +60 -0
- volcengine_python_sdk_aicc-0.0.41.dist-info/WHEEL +5 -0
- volcengine_python_sdk_aicc-0.0.41.dist-info/licenses/LICENSE +202 -0
- volcengine_python_sdk_aicc-0.0.41.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import os
|
|
3
|
+
import tarfile
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from time import time
|
|
7
|
+
from multiprocessing import Pool
|
|
8
|
+
import concurrent.futures
|
|
9
|
+
from functools import reduce
|
|
10
|
+
|
|
11
|
+
import shutil
|
|
12
|
+
import requests
|
|
13
|
+
import tos
|
|
14
|
+
from tos.utils import SizeAdapter, MergeProcess
|
|
15
|
+
from tos import DataTransferType
|
|
16
|
+
from typing_extensions import TYPE_CHECKING, Dict, List, Optional
|
|
17
|
+
from bytedance.tks.client import TKSClient, TKSConfig, utc8_timestamp
|
|
18
|
+
from bytedance.tks.crypto.aes import gcm_decrypt, gcm_encrypt
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from _typeshed import GenericPath
|
|
22
|
+
else:
|
|
23
|
+
GenericPath = str
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def upload_files(args):
|
|
27
|
+
bucket_name = args["bucket_name"]
|
|
28
|
+
path = args["path"]
|
|
29
|
+
volc_ak = args["volc_ak"]
|
|
30
|
+
volc_sk = args["volc_sk"]
|
|
31
|
+
region = args["region"]
|
|
32
|
+
endpoint = args["endpoint"]
|
|
33
|
+
|
|
34
|
+
tos_client = tos.TosClientV2(volc_ak, volc_sk, endpoint, region)
|
|
35
|
+
|
|
36
|
+
total_size = os.path.getsize(path)
|
|
37
|
+
part_size = 5 * 1024 * 1024
|
|
38
|
+
|
|
39
|
+
def percentage(consumed_bytes: int, total_bytes: int, rw_once_bytes: int, type: DataTransferType):
|
|
40
|
+
if total_bytes:
|
|
41
|
+
rate = int(100 * float(consumed_bytes) / float(total_bytes))
|
|
42
|
+
if rw_once_bytes != 0 and ((consumed_bytes // rw_once_bytes) % 1000 == 0):
|
|
43
|
+
print(
|
|
44
|
+
" [{}] rate:{}, consumed_bytes:{},total_bytes{}, rw_once_bytes:{}, type:{}".format(
|
|
45
|
+
path, rate, consumed_bytes, total_bytes, rw_once_bytes, type))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# 配置进度条,与普通上传不同的是需将分片上传的进度聚合
|
|
49
|
+
data_transfer_listener = MergeProcess(percentage, total_size, (total_size + part_size - 1) // part_size, 0)
|
|
50
|
+
# 初始化上传任务
|
|
51
|
+
# 若需在初始化分片时设置对象的存储类型,可通过storage_class字段设置
|
|
52
|
+
# 若需在初始化分片时设置对象ACL,可通过acl、grant_full_control等字段设置
|
|
53
|
+
multi_result = tos_client.create_multipart_upload(bucket_name, path, acl=tos.ACLType.ACL_Public_Read,
|
|
54
|
+
storage_class=tos.StorageClassType.Storage_Class_Standard)
|
|
55
|
+
|
|
56
|
+
upload_id = multi_result.upload_id
|
|
57
|
+
parts = []
|
|
58
|
+
|
|
59
|
+
# 上传分片数据
|
|
60
|
+
with open(path, 'rb') as f:
|
|
61
|
+
part_number = 1
|
|
62
|
+
offset = 0
|
|
63
|
+
while offset < total_size:
|
|
64
|
+
num_to_upload = min(part_size, total_size - offset)
|
|
65
|
+
out = tos_client.upload_part(bucket_name, path, upload_id, part_number,
|
|
66
|
+
content=SizeAdapter(f, num_to_upload, init_offset=offset),
|
|
67
|
+
data_transfer_listener=data_transfer_listener)
|
|
68
|
+
parts.append(out)
|
|
69
|
+
offset += num_to_upload
|
|
70
|
+
part_number += 1
|
|
71
|
+
|
|
72
|
+
# 完成分片上传任务
|
|
73
|
+
tos_client.complete_multipart_upload(bucket_name, path, upload_id, parts)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _compress_model_dir(model_dir: GenericPath) -> Path:
|
|
77
|
+
compressed_file_name = os.path.basename(model_dir) + ".tar"
|
|
78
|
+
compressed_file_path = Path(compressed_file_name)
|
|
79
|
+
with tarfile.open(compressed_file_path, "w") as tar:
|
|
80
|
+
tar.add(model_dir, arcname=os.path.basename(model_dir))
|
|
81
|
+
return compressed_file_path
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _decompress_model_dir(compressed_file: GenericPath, output_path: GenericPath) -> None:
|
|
85
|
+
with tarfile.open(compressed_file, "r") as tar:
|
|
86
|
+
tar.extractall(path=output_path)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
DEFAULT_PARTITION_SIZE = 2 * 1024 * 1024 * 1024
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _partition(model_file: GenericPath, partition_size=DEFAULT_PARTITION_SIZE) -> List[bytes]:
|
|
93
|
+
with open(model_file, "rb") as fin:
|
|
94
|
+
plaintext_bytes = fin.read()
|
|
95
|
+
chunks: List[bytes] = []
|
|
96
|
+
offset = 0
|
|
97
|
+
while offset < len(plaintext_bytes):
|
|
98
|
+
chunks.append(plaintext_bytes[offset : offset + partition_size]) # noqa: E203
|
|
99
|
+
offset += partition_size
|
|
100
|
+
return chunks
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _decrypt_chunk(params):
|
|
104
|
+
enc_chunk_path = params["enc_chunk_path"]
|
|
105
|
+
key = params["key"]
|
|
106
|
+
t0 = time()
|
|
107
|
+
with open(enc_chunk_path, "rb") as fin:
|
|
108
|
+
enc_chunk = fin.read()
|
|
109
|
+
chunk = gcm_decrypt(key, enc_chunk)
|
|
110
|
+
print(f"Decrypt chunk: {enc_chunk_path} done, costs {time() - t0} seconds.")
|
|
111
|
+
return chunk
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def md5_hash(data: bytes, salt: str = "") -> str:
|
|
115
|
+
return hashlib.md5(data + salt.encode()).hexdigest()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def copy_folder(source_folder, destination_folder):
|
|
119
|
+
"""
|
|
120
|
+
Copy the entire contents of a folder to a new location.
|
|
121
|
+
|
|
122
|
+
:param source_folder: Path to the folder to copy.
|
|
123
|
+
:param destination_folder: Path to the destination folder.
|
|
124
|
+
"""
|
|
125
|
+
try:
|
|
126
|
+
# Check if source folder exists
|
|
127
|
+
if not os.path.exists(source_folder):
|
|
128
|
+
print(f"Source folder '{source_folder}' does not exist.")
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
# Copy folder
|
|
132
|
+
shutil.copytree(source_folder, destination_folder)
|
|
133
|
+
print(f" Folder copied from '{source_folder}' to '{destination_folder}'.")
|
|
134
|
+
|
|
135
|
+
except FileExistsError:
|
|
136
|
+
print(f" Destination folder '{destination_folder}' already exists.")
|
|
137
|
+
except PermissionError:
|
|
138
|
+
print(" Permission denied: Unable to copy folder.")
|
|
139
|
+
except Exception as e:
|
|
140
|
+
print(f" An error occurred: {e}")
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@dataclass
|
|
144
|
+
class EncryptionConfig:
|
|
145
|
+
app_id: str
|
|
146
|
+
|
|
147
|
+
password: str
|
|
148
|
+
|
|
149
|
+
ring_id: Optional[str] = None
|
|
150
|
+
|
|
151
|
+
key_id: Optional[str] = None
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class JeddakModelEncrypter:
|
|
155
|
+
def __init__(self, encryption_config: EncryptionConfig = None):
|
|
156
|
+
if encryption_config is not None:
|
|
157
|
+
self.app_id = encryption_config.app_id
|
|
158
|
+
self.password = encryption_config.password
|
|
159
|
+
self.ring_id = encryption_config.ring_id
|
|
160
|
+
self.key_id = encryption_config.key_id
|
|
161
|
+
|
|
162
|
+
tks_config = TKSConfig(enable_tls=True)
|
|
163
|
+
self.tks_client = TKSClient(self.app_id, tks_config)
|
|
164
|
+
|
|
165
|
+
self.login_url = "https://jeddakchain.bytedance.com/api/user/login"
|
|
166
|
+
|
|
167
|
+
def encrypt_model(self, model_path: GenericPath, output_path: GenericPath) -> bytes:
|
|
168
|
+
os.makedirs(output_path, exist_ok=True)
|
|
169
|
+
|
|
170
|
+
data_key = os.urandom(32)
|
|
171
|
+
|
|
172
|
+
print(" Start compressing model files.")
|
|
173
|
+
# compress if the `model_path` is directory
|
|
174
|
+
t0 = time()
|
|
175
|
+
if os.path.isdir(model_path):
|
|
176
|
+
model_file = _compress_model_dir(model_path)
|
|
177
|
+
else:
|
|
178
|
+
model_file = model_path
|
|
179
|
+
print(f" Compress done, costs {time() - t0} seconds.")
|
|
180
|
+
|
|
181
|
+
chunks = _partition(model_file)
|
|
182
|
+
print(f" Total {len(chunks)} chunks.")
|
|
183
|
+
|
|
184
|
+
t0 = time()
|
|
185
|
+
for i, chunk in enumerate(chunks):
|
|
186
|
+
enc_chunk = gcm_encrypt(data_key, chunk)
|
|
187
|
+
with open(os.path.join(output_path, f"enc-part-{i:05}"), "wb") as fout:
|
|
188
|
+
fout.write(enc_chunk)
|
|
189
|
+
print(f" Encrypt done, costs {time() - t0} seconds.")
|
|
190
|
+
|
|
191
|
+
return data_key
|
|
192
|
+
|
|
193
|
+
def decrypt_model(
|
|
194
|
+
self, enc_model_path: GenericPath, key: bytes, output_path: GenericPath, processes=8
|
|
195
|
+
) -> None:
|
|
196
|
+
os.makedirs(output_path, exist_ok=True)
|
|
197
|
+
pool = Pool(processes)
|
|
198
|
+
t0 = time()
|
|
199
|
+
plaintext_bytes = bytearray()
|
|
200
|
+
if os.path.isdir(enc_model_path):
|
|
201
|
+
print(f"Start decrypt enc_model_path: {enc_model_path}")
|
|
202
|
+
params_list = []
|
|
203
|
+
for filename in sorted(os.listdir(enc_model_path)):
|
|
204
|
+
params_list.append({"enc_chunk_path": os.path.join(enc_model_path, filename), "key": key})
|
|
205
|
+
chunks = pool.map(_decrypt_chunk, params_list)
|
|
206
|
+
print(f"Pool map done, costs {time() - t0} seconds.")
|
|
207
|
+
for chunk in chunks:
|
|
208
|
+
plaintext_bytes += chunk
|
|
209
|
+
else:
|
|
210
|
+
with open(enc_model_path, "rb") as fin:
|
|
211
|
+
enc_chunk = fin.read()
|
|
212
|
+
chunk = gcm_decrypt(key, enc_chunk)
|
|
213
|
+
plaintext_bytes += chunk
|
|
214
|
+
plaintext_bytes = bytes(plaintext_bytes)
|
|
215
|
+
print(f"Decrypt done, costs {time() - t0} seconds.")
|
|
216
|
+
|
|
217
|
+
plaintext_compressed_file = "./decrypted_model.tar"
|
|
218
|
+
with open(plaintext_compressed_file, "wb") as fout:
|
|
219
|
+
fout.write(plaintext_bytes)
|
|
220
|
+
|
|
221
|
+
t0 = time()
|
|
222
|
+
_decompress_model_dir(plaintext_compressed_file, output_path)
|
|
223
|
+
print(f"Decompress done, costs {time() - t0} seconds.")
|
|
224
|
+
|
|
225
|
+
# def compute_model_baseline(self, model_dir: GenericPath) -> str:
|
|
226
|
+
# baseline = b""
|
|
227
|
+
# file_path_list = []
|
|
228
|
+
# for dirpath, _, filenames in os.walk(model_dir):
|
|
229
|
+
# for filename in filenames:
|
|
230
|
+
# file_path = os.path.join(dirpath, filename)
|
|
231
|
+
# file_path_list.append(file_path)
|
|
232
|
+
|
|
233
|
+
# for file_path in sorted(file_path_list):
|
|
234
|
+
# with open(file_path, "rb") as fin:
|
|
235
|
+
# file_bytes = fin.read()
|
|
236
|
+
# baseline = hashlib.sha256(baseline + file_bytes).digest()
|
|
237
|
+
|
|
238
|
+
# return baseline.hex()
|
|
239
|
+
|
|
240
|
+
def compute_file_hash(self, file_path: str) -> bytes:
|
|
241
|
+
with open(file_path, "rb") as fin:
|
|
242
|
+
return hashlib.sha256(fin.read()).digest()
|
|
243
|
+
|
|
244
|
+
def compute_model_baseline(self, model_dir: str) -> str:
|
|
245
|
+
file_path_list: List[str] = []
|
|
246
|
+
|
|
247
|
+
for dirpath, _, filenames in os.walk(model_dir):
|
|
248
|
+
for filename in filenames:
|
|
249
|
+
file_path_list.append(os.path.join(dirpath, filename))
|
|
250
|
+
|
|
251
|
+
file_path_list.sort()
|
|
252
|
+
|
|
253
|
+
num_threads=os.cpu_count()
|
|
254
|
+
print(f" Using {num_threads} threads")
|
|
255
|
+
|
|
256
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=num_threads) as executor:
|
|
257
|
+
file_hashes = list(executor.map(self.compute_file_hash, file_path_list))
|
|
258
|
+
baseline = reduce(lambda acc, x: hashlib.sha256(acc + x).digest(), file_hashes, b"")
|
|
259
|
+
|
|
260
|
+
return baseline.hex()
|
|
261
|
+
|
|
262
|
+
def import_key(self, ring_id: str, key: bytes, key_name: str, desc="") -> None:
|
|
263
|
+
# login
|
|
264
|
+
ts = str(utc8_timestamp())
|
|
265
|
+
req_body = {"Username": self.app_id, "Password": md5_hash(self.password.encode(), ts)}
|
|
266
|
+
|
|
267
|
+
response = requests.post(self.login_url, headers={"TimeStamp": ts}, json=req_body)
|
|
268
|
+
cookies = response.cookies
|
|
269
|
+
jeddak_pcc_session = cookies.get("jeddak_pcc_session")
|
|
270
|
+
cookies = {"jeddak_pcc_session": jeddak_pcc_session,
|
|
271
|
+
"usage_scenario": "ModelEncryption"}
|
|
272
|
+
|
|
273
|
+
self.tks_client.import_key(
|
|
274
|
+
ring_id=ring_id,
|
|
275
|
+
algo="SYMMETRIC_256",
|
|
276
|
+
key=key,
|
|
277
|
+
key_name=key_name,
|
|
278
|
+
desc=desc,
|
|
279
|
+
cookies=cookies,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
def get_key(self, ring_id: str, key_id: str) -> bytes:
|
|
283
|
+
cookies = self._pcc_login()
|
|
284
|
+
key = self.tks_client.get_key(ring_id, key_id, cookies=cookies)
|
|
285
|
+
return key
|
|
286
|
+
|
|
287
|
+
def upload_model_to_tos(self, model_path: str, bucket_name: str, volc_ak: str, volc_sk: str, region: str, endpoint: str, processes: int = 10):
|
|
288
|
+
try:
|
|
289
|
+
pool = Pool(processes)
|
|
290
|
+
|
|
291
|
+
def upload_dir(root_dir):
|
|
292
|
+
files = [str(file) for file in Path(root_dir).rglob('*') if file.is_file()]
|
|
293
|
+
param_list = []
|
|
294
|
+
for file in files:
|
|
295
|
+
params = {
|
|
296
|
+
"bucket_name": bucket_name,
|
|
297
|
+
"path": file,
|
|
298
|
+
"volc_ak": volc_ak,
|
|
299
|
+
"volc_sk": volc_sk,
|
|
300
|
+
"region": region,
|
|
301
|
+
"endpoint": endpoint
|
|
302
|
+
}
|
|
303
|
+
param_list.append(params)
|
|
304
|
+
pool.map(upload_files, param_list)
|
|
305
|
+
pool.close()
|
|
306
|
+
pool.join()
|
|
307
|
+
|
|
308
|
+
upload_dir(model_path)
|
|
309
|
+
except tos.exceptions.TosClientError as e:
|
|
310
|
+
# 操作失败,捕获客户端异常,一般情况为非法请求参数或网络异常
|
|
311
|
+
print('fail with client error, message:{}, cause: {}'.format(e.message, e.cause))
|
|
312
|
+
except tos.exceptions.TosServerError as e:
|
|
313
|
+
# 操作失败,捕获服务端异常,可从返回信息中获取详细错误信息
|
|
314
|
+
print('fail with server error, code: {}'.format(e.code))
|
|
315
|
+
# request id 可定位具体问题,强烈建议日志中保存
|
|
316
|
+
print('error with request id: {}'.format(e.request_id))
|
|
317
|
+
print('error with message: {}'.format(e.message))
|
|
318
|
+
print('error with http code: {}'.format(e.status_code))
|
|
319
|
+
print('error with ec: {}'.format(e.ec))
|
|
320
|
+
print('error with request url: {}'.format(e.request_url))
|
|
321
|
+
except Exception as e:
|
|
322
|
+
print('fail with unknown error: {}'.format(e))
|
|
323
|
+
|
|
324
|
+
def encrypt_model_and_upload(self,
|
|
325
|
+
model_path: str, bucket_name: str, volc_ak: str, volc_sk, region: str, endpoint: str, app_id: str, top_ak: str = "", top_sk: str = "",
|
|
326
|
+
processes: int = 10, ring_id: str = "", ring_name: str = "", ring_desc: str = "", key_name: str = "", key_desc: str = "", service: str = "pcc",
|
|
327
|
+
tks_addr: str = "pcc.volcengineapi.com", encrypt_flag: bool = True
|
|
328
|
+
) -> dict:
|
|
329
|
+
# Example usage
|
|
330
|
+
if encrypt_flag:
|
|
331
|
+
print(" Step1.1: model needs encryption")
|
|
332
|
+
top_conf = {
|
|
333
|
+
"ak": volc_ak,
|
|
334
|
+
"sk": volc_sk,
|
|
335
|
+
"service": service,
|
|
336
|
+
"region": region,
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
tks_config = TKSConfig(addr=tks_addr, enable_tls=True, top_config=top_conf)
|
|
340
|
+
tks_client = TKSClient(app_id=app_id, config=tks_config)
|
|
341
|
+
|
|
342
|
+
step = 1
|
|
343
|
+
if not ring_id:
|
|
344
|
+
print(" Step1.2: ring_id is not provided, start creating.")
|
|
345
|
+
assert ring_name, "ring_name should provided when create"
|
|
346
|
+
ring_id = tks_client.create_ring(ring_name=ring_name, desc=ring_desc)["RingID"]
|
|
347
|
+
print(" Step1.3: create ring successfully.")
|
|
348
|
+
step = 3
|
|
349
|
+
|
|
350
|
+
model_name = os.path.basename(model_path)
|
|
351
|
+
enc_output_path = "enc-" + model_name
|
|
352
|
+
data_key = self.encrypt_model(model_path, output_path=enc_output_path)
|
|
353
|
+
|
|
354
|
+
print(" Step1.{}: start import key to tks...".format(1+step))
|
|
355
|
+
assert key_name, "key_name should provided"
|
|
356
|
+
key_id = tks_client.import_key(ring_id=ring_id, algo="SYMMETRIC_256", key=data_key, key_name=key_name, desc=key_desc, usage_scenario="ModelEncryption")["KeyID"]
|
|
357
|
+
print(" Step1.{}: import key to tks successfully.".format(2+step))
|
|
358
|
+
|
|
359
|
+
print(" Step1.{}: start compute model baseline".format(3+step))
|
|
360
|
+
baseline = self.compute_model_baseline(model_path)
|
|
361
|
+
print(" Step1.{}: compute model baseline done.".format(4+step))
|
|
362
|
+
|
|
363
|
+
print(" Step1.{}: start upload enc model to tos...".format(5+step))
|
|
364
|
+
t0 = time()
|
|
365
|
+
self.upload_model_to_tos(model_path=enc_output_path + "/", bucket_name=bucket_name, volc_ak=volc_ak, volc_sk=volc_sk, region=region, endpoint=endpoint, processes=processes)
|
|
366
|
+
print(" Step1.{}: upload to tos done, costs: {} seconds.".format(6+step, time() - t0))
|
|
367
|
+
|
|
368
|
+
if region == "e28-env":
|
|
369
|
+
tos_url = f"https://{bucket_name}.tos-vpc-{region}.e28.inspirecloud.io/{enc_output_path}/"
|
|
370
|
+
elif region == "neimeng-1":
|
|
371
|
+
tos_url = f"https://{bucket_name}.tos-vpc-neimeng-1.pcc.lenovo.com/{enc_output_path}/"
|
|
372
|
+
else:
|
|
373
|
+
tos_url = f"https://{bucket_name}.tos-{region}.ivolces.com/{enc_output_path}/"
|
|
374
|
+
return {
|
|
375
|
+
"ring_id": ring_id,
|
|
376
|
+
"key_id": key_id,
|
|
377
|
+
"baseline": baseline,
|
|
378
|
+
"model_name": model_name,
|
|
379
|
+
"tos_url": tos_url
|
|
380
|
+
}
|
|
381
|
+
else:
|
|
382
|
+
print(" Step1.1: model doesn't need encryption")
|
|
383
|
+
source = model_path
|
|
384
|
+
model_name = os.path.basename(model_path)
|
|
385
|
+
destination = "plain-" + model_name
|
|
386
|
+
copy_folder(source, destination)
|
|
387
|
+
model_name = os.path.basename(model_path)
|
|
388
|
+
print(" Step1.2: start compute model baseline")
|
|
389
|
+
baseline = self.compute_model_baseline(model_path)
|
|
390
|
+
print(" Step1.3: compute model baseline done.")
|
|
391
|
+
print(" Step1.4: start upload model to tos...")
|
|
392
|
+
t0 = time()
|
|
393
|
+
|
|
394
|
+
self.upload_model_to_tos(model_path=destination + "/", bucket_name=bucket_name, volc_ak=volc_ak, volc_sk=volc_sk, region=region, endpoint=endpoint, processes=processes)
|
|
395
|
+
print(f" Step1.5: upload to tos done, costs: {time() - t0} seconds.")
|
|
396
|
+
if region == "e28-env":
|
|
397
|
+
tos_url = f"https://{bucket_name}.tos-vpc-{region}.e28.inspirecloud.io/{destination}/"
|
|
398
|
+
elif region == "neimeng-1":
|
|
399
|
+
tos_url = f"https://{bucket_name}.tos-vpc-neimeng-1.pcc.lenovo.com/{destination}/"
|
|
400
|
+
else:
|
|
401
|
+
tos_url = f"https://{bucket_name}.tos-{region}.ivolces.com/{destination}/"
|
|
402
|
+
return {
|
|
403
|
+
"baseline": baseline,
|
|
404
|
+
"model_name": model_name,
|
|
405
|
+
"tos_url": tos_url
|
|
406
|
+
}
|
|
407
|
+
def _pcc_login(self) -> Dict[str, str]:
|
|
408
|
+
ts = str(utc8_timestamp())
|
|
409
|
+
req_body = {"Username": self.app_id, "Password": md5_hash(self.password.encode(), ts)}
|
|
410
|
+
|
|
411
|
+
response = requests.post(self.login_url, headers={"TimeStamp": ts}, json=req_body)
|
|
412
|
+
cookies = response.cookies
|
|
413
|
+
jeddak_pcc_session = cookies.get("jeddak_pcc_session")
|
|
414
|
+
assert jeddak_pcc_session is not None
|
|
415
|
+
cookies = {"jeddak_pcc_session": jeddak_pcc_session}
|
|
416
|
+
return cookies
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
from bytedance.jeddak_secure_model.model_encryption import EncryptionConfig, JeddakModelEncrypter
|
|
2
|
+
from bytedance.jeddak_secure_model.rds_utils import register_to_rds
|
|
3
|
+
from huggingface_hub import snapshot_download
|
|
4
|
+
import argparse
|
|
5
|
+
import os
|
|
6
|
+
import shutil
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
parser = argparse.ArgumentParser(description="An useful tool to host a new model for adminstrator.")
|
|
10
|
+
parser.add_argument("--huggingface_model", type=str, default="Qwen/Qwen2.5-0.5B", help="the huggingface modelname")
|
|
11
|
+
parser.add_argument("--rds_url", type=str, default="", help="the rds url used for storing model table")
|
|
12
|
+
parser.add_argument("--bucket_name", type=str, default="jeddakpcc-platform-hosted-models", help="the bucket_name of tos")
|
|
13
|
+
parser.add_argument("--volc_ak", type=str, default="", help="the bucket_name of tos")
|
|
14
|
+
parser.add_argument("--volc_sk", type=str, default="", help="the bucket_name of tos")
|
|
15
|
+
parser.add_argument("--region", type=str, default="cn-beijing", help="the bucket_name of tos")
|
|
16
|
+
parser.add_argument("--endpoint", type=str, default="tos-cn-beijing.volces.com", help="the bucket_name of tos")
|
|
17
|
+
args = parser.parse_args()
|
|
18
|
+
|
|
19
|
+
model_name = args.huggingface_model
|
|
20
|
+
rds_url = args.rds_url
|
|
21
|
+
bucket_name = args.bucket_name
|
|
22
|
+
volc_ak = args.volc_ak
|
|
23
|
+
volc_sk = args.volc_sk
|
|
24
|
+
region = args.region
|
|
25
|
+
endpoint = args.endpoint
|
|
26
|
+
assert rds_url != "", "rds_url is missing"
|
|
27
|
+
assert bucket_name != "", "bucket_name is missing"
|
|
28
|
+
assert volc_ak != "", "volc_ak is missing"
|
|
29
|
+
assert volc_sk != "", "volc_sk is missing"
|
|
30
|
+
assert region != "", "region is missing"
|
|
31
|
+
assert endpoint != "", "endpoint is missing"
|
|
32
|
+
app_id = ""
|
|
33
|
+
top_ak = ""
|
|
34
|
+
top_sk = ""
|
|
35
|
+
ring_id = ""
|
|
36
|
+
ring_name = ""
|
|
37
|
+
ring_desc = ""
|
|
38
|
+
key_name = ""
|
|
39
|
+
key_desc = ""
|
|
40
|
+
service = ""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def replace_symlinks_with_copies(model_name, directory):
|
|
44
|
+
folder_path = Path(model_name)
|
|
45
|
+
folder_path.mkdir(parents=True, exist_ok=True)
|
|
46
|
+
print(f"making dir for {model_name}")
|
|
47
|
+
for root, _, files in os.walk(directory):
|
|
48
|
+
for file in files:
|
|
49
|
+
file_path = os.path.join(root, file)
|
|
50
|
+
if os.path.islink(file_path): # 检测是否是符号链接
|
|
51
|
+
real_path = os.readlink(file_path)
|
|
52
|
+
abs_file_path = Path(file_path)
|
|
53
|
+
real_relative_path = Path(real_path)
|
|
54
|
+
abs_real_path = (abs_file_path.parent / real_relative_path).resolve()
|
|
55
|
+
model_file_path = folder_path / file_path.split("/")[-1]
|
|
56
|
+
print(f"copying {abs_real_path} to {model_file_path}")
|
|
57
|
+
shutil.copy(abs_real_path, model_file_path) # 复制实际文件
|
|
58
|
+
os.unlink(file_path) # 删除符号链接
|
|
59
|
+
# tmp_root_path = Path(directory).parts[0]
|
|
60
|
+
# print(f"removing {tmp_root_path}")
|
|
61
|
+
# shutil.rmtree(tmp_root_path)
|
|
62
|
+
|
|
63
|
+
# Safely remove only the intended cache directory itself.
|
|
64
|
+
tmp_root_path = Path(directory).resolve()
|
|
65
|
+
if tmp_root_path in (Path('/'), Path('.').resolve()) or str(tmp_root_path) in ('/', '.'):
|
|
66
|
+
raise ValueError(f"Refusing to remove dangerous path: {tmp_root_path}")
|
|
67
|
+
print(f"removing {tmp_root_path}")
|
|
68
|
+
shutil.rmtree(tmp_root_path)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
local_dir = snapshot_download(repo_id=model_name, cache_dir="./")
|
|
72
|
+
# replace_symlinks_with_copies(model_name, local_dir)
|
|
73
|
+
|
|
74
|
+
old=Path(local_dir)
|
|
75
|
+
new=old.parent / model_name.split("/")[-1]
|
|
76
|
+
try:
|
|
77
|
+
shutil.rmtree(new)
|
|
78
|
+
except Exception as e:
|
|
79
|
+
print(e)
|
|
80
|
+
old.rename(new)
|
|
81
|
+
|
|
82
|
+
model_path = str(new) # 模型的文件夹路径
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
config = EncryptionConfig("", "")
|
|
86
|
+
encrypter = JeddakModelEncrypter(config)
|
|
87
|
+
|
|
88
|
+
res = encrypter.encrypt_model_and_upload(
|
|
89
|
+
model_path=model_path,
|
|
90
|
+
bucket_name=bucket_name,
|
|
91
|
+
volc_ak=volc_ak,
|
|
92
|
+
volc_sk=volc_sk,
|
|
93
|
+
region=region,
|
|
94
|
+
endpoint=endpoint,
|
|
95
|
+
top_ak=top_ak,
|
|
96
|
+
top_sk=top_sk,
|
|
97
|
+
ring_id=ring_id,
|
|
98
|
+
ring_name=ring_name,
|
|
99
|
+
ring_desc=ring_desc,
|
|
100
|
+
key_name=key_name,
|
|
101
|
+
key_desc=key_desc,
|
|
102
|
+
app_id=app_id,
|
|
103
|
+
service=service,
|
|
104
|
+
encrypt_flag=False
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
model = model_name.split("/")[-1]
|
|
108
|
+
tos_url = f"https://{bucket_name}.{endpoint}/plain-{model}/".replace("volces", "ivolces")
|
|
109
|
+
baseline = res["baseline"]
|
|
110
|
+
model_meta_data = {
|
|
111
|
+
"model_name": model_name,
|
|
112
|
+
"tos_url": tos_url,
|
|
113
|
+
"baseline": baseline
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
register_to_rds(rds_url, model_meta_data)
|