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,406 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
from functools import wraps
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
from typing import Optional, Tuple
|
|
6
|
+
from urllib.parse import urlparse
|
|
7
|
+
from bytedance.jeddak_secure_channel import ResponseKey
|
|
8
|
+
import bytedance.jeddak_secure_channel as jsc
|
|
9
|
+
from .common.error import AICCException
|
|
10
|
+
from .common.log import logger
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _merge_unknown_kwargs_into_extra_body(kwargs: dict, accepted_params: set) -> None:
|
|
14
|
+
moved = {key: kwargs.pop(key) for key in list(kwargs) if key not in accepted_params}
|
|
15
|
+
if not moved:
|
|
16
|
+
return
|
|
17
|
+
|
|
18
|
+
extra_body = dict(kwargs.get("extra_body") or {})
|
|
19
|
+
for key, value in moved.items():
|
|
20
|
+
extra_body.setdefault(key, value)
|
|
21
|
+
kwargs["extra_body"] = extra_body
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _wrap_create_with_extra_kwargs(create_func):
|
|
25
|
+
signature = inspect.signature(create_func)
|
|
26
|
+
accepted_params = set(signature.parameters)
|
|
27
|
+
accepts_var_keyword = any(
|
|
28
|
+
param.kind == inspect.Parameter.VAR_KEYWORD
|
|
29
|
+
for param in signature.parameters.values()
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
def _merge(kwargs: dict) -> None:
|
|
33
|
+
# 底层函数本身带 **kwargs 时任何关键字都能接收,无需改写(也不会抛 TypeError)。
|
|
34
|
+
if accepts_var_keyword:
|
|
35
|
+
return
|
|
36
|
+
_merge_unknown_kwargs_into_extra_body(kwargs, accepted_params)
|
|
37
|
+
|
|
38
|
+
if inspect.iscoroutinefunction(create_func):
|
|
39
|
+
@wraps(create_func)
|
|
40
|
+
async def _async_wrapper(*args, **kwargs):
|
|
41
|
+
_merge(kwargs)
|
|
42
|
+
return await create_func(*args, **kwargs)
|
|
43
|
+
|
|
44
|
+
return _async_wrapper
|
|
45
|
+
|
|
46
|
+
@wraps(create_func)
|
|
47
|
+
def _wrapper(*args, **kwargs):
|
|
48
|
+
_merge(kwargs)
|
|
49
|
+
return create_func(*args, **kwargs)
|
|
50
|
+
|
|
51
|
+
return _wrapper
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def enable_aicc_extra_kwargs(client):
|
|
55
|
+
"""让 openai SDK 的 create() 支持直接传 AICC/方舟扩展参数(如 top_k)。
|
|
56
|
+
|
|
57
|
+
openai Python SDK 的 create() 形参表不含 top_k 等扩展字段,裸关键字会在本地抛
|
|
58
|
+
TypeError;此函数把这些非标准关键字并入 extra_body 后透传给网关。需在构造客户端后
|
|
59
|
+
调用一次,同步与异步客户端均适用。
|
|
60
|
+
"""
|
|
61
|
+
chat_completions = getattr(getattr(client, "chat", None), "completions", None)
|
|
62
|
+
if chat_completions is not None and getattr(chat_completions, "create", None) is not None:
|
|
63
|
+
chat_completions.create = _wrap_create_with_extra_kwargs(chat_completions.create)
|
|
64
|
+
|
|
65
|
+
responses = getattr(client, "responses", None)
|
|
66
|
+
if responses is not None and getattr(responses, "create", None) is not None:
|
|
67
|
+
responses.create = _wrap_create_with_extra_kwargs(responses.create)
|
|
68
|
+
|
|
69
|
+
return client
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class AICCConfig:
|
|
74
|
+
"""AICC 安全通信配置类"""
|
|
75
|
+
|
|
76
|
+
def __init__(self,
|
|
77
|
+
ak: str = "",
|
|
78
|
+
sk: str = "",
|
|
79
|
+
aicc_llm_service_name: str = "AICC.ConfidentialInference",
|
|
80
|
+
policy_id: str = "router_policy",
|
|
81
|
+
region: str = "cn-beijing",
|
|
82
|
+
top_url: str = "pcc.volcengineapi.com",
|
|
83
|
+
top_service: str = "pcc",
|
|
84
|
+
ra_url: str = "",
|
|
85
|
+
auth_token: str = "",
|
|
86
|
+
custom_headers: Optional[dict[str, str]] = None,
|
|
87
|
+
**kwargs):
|
|
88
|
+
"""
|
|
89
|
+
:param ak: 火山引擎用户的ak
|
|
90
|
+
:param sk: 火山引擎用户的sk
|
|
91
|
+
:param aicc_llm_service_name: aicc推理服务的服务名称
|
|
92
|
+
:param policy_id: aicc推理服务做远程证明锁对应的policy
|
|
93
|
+
:param region: aicc推理服务所在的区域
|
|
94
|
+
:param service: aicc推理服务TOP网关的service
|
|
95
|
+
:param ra_url: 非TCA接口时的RA接口地址
|
|
96
|
+
:param auth_token: 非TCA接口时的鉴权凭据
|
|
97
|
+
:param kwargs:
|
|
98
|
+
"""
|
|
99
|
+
self.ak = ak
|
|
100
|
+
self.sk = sk
|
|
101
|
+
self.aicc_llm_service_name = aicc_llm_service_name
|
|
102
|
+
self.policy_id = policy_id
|
|
103
|
+
self.region = region
|
|
104
|
+
self.top_url = top_url
|
|
105
|
+
self.top_service = top_service
|
|
106
|
+
self.ra_url = ra_url
|
|
107
|
+
self.auth_token = auth_token
|
|
108
|
+
self.custom_headers = custom_headers
|
|
109
|
+
|
|
110
|
+
self.kwargs = kwargs
|
|
111
|
+
|
|
112
|
+
self.HTTP_RA_PATH = "v1/security/token"
|
|
113
|
+
if self.ra_url:
|
|
114
|
+
if self.ra_url.startswith("wss://"):
|
|
115
|
+
self.ra_url = self.ra_url.replace("wss", "https")
|
|
116
|
+
|
|
117
|
+
if self.ra_url.endswith("/api/v3") or self.ra_url.endswith("/anthropic"):
|
|
118
|
+
parsed = urlparse(self.ra_url)
|
|
119
|
+
base_domain = f"{parsed.scheme}://{parsed.netloc}"
|
|
120
|
+
self.ra_url = f"{base_domain}/{self.HTTP_RA_PATH}"
|
|
121
|
+
else:
|
|
122
|
+
self.ra_url = f"{self.ra_url}/{self.HTTP_RA_PATH}"
|
|
123
|
+
|
|
124
|
+
def to_dict(self) -> dict:
|
|
125
|
+
"""将配置对象转换为字典格式
|
|
126
|
+
|
|
127
|
+
:return: 包含所有配置项的字典
|
|
128
|
+
"""
|
|
129
|
+
config_dict = {
|
|
130
|
+
"ak": self.ak,
|
|
131
|
+
"sk": self.sk,
|
|
132
|
+
"aicc_llm_service_name": self.aicc_llm_service_name,
|
|
133
|
+
"policy_id": self.policy_id,
|
|
134
|
+
"region": self.region,
|
|
135
|
+
"top_url": self.top_url,
|
|
136
|
+
"top_service": self.top_service,
|
|
137
|
+
"ra_url": self.ra_url,
|
|
138
|
+
"auth_token": self.auth_token,
|
|
139
|
+
"custom_headers": self.custom_headers
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
# 添加额外的 kwargs
|
|
143
|
+
if self.kwargs:
|
|
144
|
+
config_dict.update(self.kwargs)
|
|
145
|
+
|
|
146
|
+
return config_dict
|
|
147
|
+
|
|
148
|
+
def get_jsc_config(self) -> dict:
|
|
149
|
+
"""生成aicc端云互信配置
|
|
150
|
+
"""
|
|
151
|
+
jsc_config = dict()
|
|
152
|
+
jsc_config["ra_url"] = self.ra_url
|
|
153
|
+
jsc_config["auth_token"] = self.auth_token
|
|
154
|
+
jsc_config["ra_service_name"] = self.aicc_llm_service_name
|
|
155
|
+
jsc_config["ra_policy_id"] = self.policy_id
|
|
156
|
+
jsc_config["custom_headers"] = self.custom_headers
|
|
157
|
+
|
|
158
|
+
if self.ak and self.sk:
|
|
159
|
+
jsc_config["bytedance_top_info"] = dict()
|
|
160
|
+
jsc_config["bytedance_top_info"]["ak"] = self.ak
|
|
161
|
+
jsc_config["bytedance_top_info"]["sk"] = self.sk
|
|
162
|
+
jsc_config["bytedance_top_info"]["region"] = self.region
|
|
163
|
+
jsc_config["bytedance_top_info"]["url"] = self.top_url
|
|
164
|
+
jsc_config["bytedance_top_info"]["service"] = self.top_service
|
|
165
|
+
|
|
166
|
+
# 添加额外的 kwargs
|
|
167
|
+
if self.kwargs:
|
|
168
|
+
jsc_config.update(self.kwargs)
|
|
169
|
+
|
|
170
|
+
return jsc_config
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
AICC_SDK_VERSION = "0.1.0"
|
|
174
|
+
jsc_clients = dict()
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class DemoSyncByteStream(httpx.SyncByteStream):
|
|
178
|
+
def __init__(self, raw_stream, encrypt_key: ResponseKey = None) -> None:
|
|
179
|
+
self._raw_stream = raw_stream
|
|
180
|
+
self.encrypt_key = encrypt_key
|
|
181
|
+
self.buffer = b""
|
|
182
|
+
|
|
183
|
+
def _decrypt_or_passthrough(self, payload: bytes) -> bytes:
|
|
184
|
+
if not payload or self.encrypt_key is None:
|
|
185
|
+
return payload
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
return self.encrypt_key.decrypt(payload)
|
|
189
|
+
except Exception:
|
|
190
|
+
return payload
|
|
191
|
+
|
|
192
|
+
def __iter__(self):
|
|
193
|
+
for part in self._raw_stream:
|
|
194
|
+
if not isinstance(part, bytes):
|
|
195
|
+
part = part.encode("utf-8")
|
|
196
|
+
|
|
197
|
+
for line in part.splitlines(keepends=True):
|
|
198
|
+
self.buffer += line
|
|
199
|
+
if self.buffer.endswith((b"\r", b"\n")):
|
|
200
|
+
payload = self.buffer.strip()
|
|
201
|
+
cipher_events = self._decrypt_or_passthrough(payload)
|
|
202
|
+
cipher_events += b"\n" if self.buffer[-1] == 10 else b"" # 10 == "\n"
|
|
203
|
+
self.buffer = b""
|
|
204
|
+
yield cipher_events
|
|
205
|
+
|
|
206
|
+
if self.buffer:
|
|
207
|
+
payload = self.buffer
|
|
208
|
+
self.buffer = b""
|
|
209
|
+
yield self._decrypt_or_passthrough(payload)
|
|
210
|
+
|
|
211
|
+
def close(self) -> None:
|
|
212
|
+
if hasattr(self._raw_stream, "close"):
|
|
213
|
+
self._raw_stream.close()
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class DemoAsyncByteStream(httpx.AsyncByteStream):
|
|
217
|
+
def __init__(self, raw_stream, encrypt_key: ResponseKey = None) -> None:
|
|
218
|
+
self._raw_stream = raw_stream
|
|
219
|
+
self.encrypt_key = encrypt_key
|
|
220
|
+
self.buffer = b""
|
|
221
|
+
|
|
222
|
+
def _decrypt_or_passthrough(self, payload: bytes) -> bytes:
|
|
223
|
+
if not payload or self.encrypt_key is None:
|
|
224
|
+
return payload
|
|
225
|
+
|
|
226
|
+
try:
|
|
227
|
+
return self.encrypt_key.decrypt(payload)
|
|
228
|
+
except Exception:
|
|
229
|
+
return payload
|
|
230
|
+
|
|
231
|
+
async def __aiter__(self):
|
|
232
|
+
async for part in self._raw_stream:
|
|
233
|
+
if not isinstance(part, bytes):
|
|
234
|
+
part = part.encode("utf-8")
|
|
235
|
+
|
|
236
|
+
for line in part.splitlines(keepends=True):
|
|
237
|
+
self.buffer += line
|
|
238
|
+
if self.buffer.endswith((b"\r", b"\n")):
|
|
239
|
+
payload = self.buffer.strip()
|
|
240
|
+
cipher_events = self._decrypt_or_passthrough(payload)
|
|
241
|
+
cipher_events += b"\n" if self.buffer[-1] == 10 else b"" # 10 == "\n"
|
|
242
|
+
self.buffer = b""
|
|
243
|
+
yield cipher_events
|
|
244
|
+
|
|
245
|
+
if self.buffer:
|
|
246
|
+
payload = self.buffer
|
|
247
|
+
self.buffer = b""
|
|
248
|
+
yield self._decrypt_or_passthrough(payload)
|
|
249
|
+
|
|
250
|
+
async def aclose(self) -> None:
|
|
251
|
+
if hasattr(self._raw_stream, "aclose"):
|
|
252
|
+
await self._raw_stream.aclose()
|
|
253
|
+
elif hasattr(self._raw_stream, "close"):
|
|
254
|
+
self._raw_stream.close()
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class SecureHttpClient(httpx.Client):
|
|
258
|
+
def __init__(self, *args, **kwargs):
|
|
259
|
+
self.aicc_config: Optional[AICCConfig] = None
|
|
260
|
+
|
|
261
|
+
if kwargs.get("aicc_config"):
|
|
262
|
+
try:
|
|
263
|
+
self.aicc_config = kwargs.pop("aicc_config")
|
|
264
|
+
secure_channel_config = jsc.ClientConfig.from_dict(self.aicc_config.get_jsc_config())
|
|
265
|
+
jsc_clients[self.aicc_config.aicc_llm_service_name] = jsc.Client(secure_channel_config)
|
|
266
|
+
except Exception as e:
|
|
267
|
+
logger.error(f"Error init jsc, aicc_llm_service_name={self.aicc_config.aicc_llm_service_name}, {e}")
|
|
268
|
+
self.aicc_config = None
|
|
269
|
+
raise AICCException("New deepseek client failed")
|
|
270
|
+
|
|
271
|
+
super().__init__(*args, **kwargs)
|
|
272
|
+
|
|
273
|
+
def jsc_encryption(self, msg: bytes) -> Tuple[str, ResponseKey]:
|
|
274
|
+
encrypt_key: Optional[ResponseKey] = None
|
|
275
|
+
|
|
276
|
+
if self.aicc_config:
|
|
277
|
+
jsc_client = jsc_clients.get(self.aicc_config.aicc_llm_service_name)
|
|
278
|
+
if jsc_client:
|
|
279
|
+
plain_len = len(msg)
|
|
280
|
+
msg, encrypt_key = jsc_client.encrypt_with_response(msg)
|
|
281
|
+
cipher_len = len(msg) if isinstance(msg, (bytes, bytearray, str)) else -1
|
|
282
|
+
logger.info(
|
|
283
|
+
f"[JSC] jsc_encryption invoked\n"
|
|
284
|
+
f" plainLen={plain_len}\n"
|
|
285
|
+
f" cipherLen={cipher_len}\n"
|
|
286
|
+
f" responseKeyExists={encrypt_key is not None}"
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
return msg, encrypt_key
|
|
290
|
+
|
|
291
|
+
def send(self, request: httpx.Request, *args, **kwargs):
|
|
292
|
+
encrypt_key: Optional[ResponseKey] = None
|
|
293
|
+
|
|
294
|
+
if self.aicc_config:
|
|
295
|
+
# 对所有请求进行加密(包括multipart请求)
|
|
296
|
+
request.headers["X-AICC-Encryption-Enable"] = "true"
|
|
297
|
+
request.headers["X-AICC-Encryption-SDK"] = "aicc"
|
|
298
|
+
request.headers["X-AICC-Encryption-Version"] = AICC_SDK_VERSION
|
|
299
|
+
|
|
300
|
+
# 先读取内容(处理流式请求)
|
|
301
|
+
try:
|
|
302
|
+
content = request.content
|
|
303
|
+
except httpx.RequestNotRead:
|
|
304
|
+
content = request.read()
|
|
305
|
+
|
|
306
|
+
encryption_content, encrypt_key = self.jsc_encryption(content)
|
|
307
|
+
if isinstance(encryption_content, str):
|
|
308
|
+
encryption_content = encryption_content.encode("utf-8")
|
|
309
|
+
|
|
310
|
+
request.stream = httpx.ByteStream(encryption_content)
|
|
311
|
+
if hasattr(request, "_content"):
|
|
312
|
+
delattr(request, "_content")
|
|
313
|
+
# 不修改Content-Type,保持原始的Content-Type(如multipart/form-data)
|
|
314
|
+
request.headers["Content-Length"] = str(len(encryption_content))
|
|
315
|
+
request.read()
|
|
316
|
+
|
|
317
|
+
resp = super().send(request, *args, **kwargs)
|
|
318
|
+
|
|
319
|
+
if 200 <= resp.status_code < 300:
|
|
320
|
+
if encrypt_key:
|
|
321
|
+
if not kwargs.get("stream", False):
|
|
322
|
+
content = resp.content
|
|
323
|
+
decrypted_content = encrypt_key.decrypt(content)
|
|
324
|
+
resp._content = decrypted_content
|
|
325
|
+
else:
|
|
326
|
+
resp.stream = DemoSyncByteStream(resp.stream, encrypt_key)
|
|
327
|
+
else:
|
|
328
|
+
if encrypt_key:
|
|
329
|
+
try:
|
|
330
|
+
if not kwargs.get("stream", False):
|
|
331
|
+
content = resp.content
|
|
332
|
+
decrypted_content = encrypt_key.decrypt(content)
|
|
333
|
+
resp._content = decrypted_content
|
|
334
|
+
else:
|
|
335
|
+
resp.stream = DemoSyncByteStream(resp.stream, encrypt_key)
|
|
336
|
+
except Exception as e:
|
|
337
|
+
pass
|
|
338
|
+
return resp
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
class AsyncSecureHttpClient(httpx.AsyncClient):
|
|
342
|
+
def __init__(self, *args, **kwargs):
|
|
343
|
+
self.aicc_config: Optional[AICCConfig] = None
|
|
344
|
+
|
|
345
|
+
if kwargs.get("aicc_config"):
|
|
346
|
+
try:
|
|
347
|
+
self.aicc_config = kwargs.pop("aicc_config")
|
|
348
|
+
secure_channel_config = jsc.ClientConfig.from_dict(self.aicc_config.get_jsc_config())
|
|
349
|
+
jsc_clients[self.aicc_config.aicc_llm_service_name] = jsc.Client(secure_channel_config)
|
|
350
|
+
except Exception as e:
|
|
351
|
+
logger.error(f"Error init jsc, aicc_llm_service_name={self.aicc_config.aicc_llm_service_name}, {e}")
|
|
352
|
+
self.aicc_config = None
|
|
353
|
+
raise AICCException("New async secure client failed")
|
|
354
|
+
|
|
355
|
+
super().__init__(*args, **kwargs)
|
|
356
|
+
|
|
357
|
+
def jsc_encryption(self, msg: bytes) -> Tuple[str, ResponseKey]:
|
|
358
|
+
encrypt_key: Optional[ResponseKey] = None
|
|
359
|
+
|
|
360
|
+
if self.aicc_config:
|
|
361
|
+
jsc_client = jsc_clients.get(self.aicc_config.aicc_llm_service_name)
|
|
362
|
+
if jsc_client:
|
|
363
|
+
msg, encrypt_key = jsc_client.encrypt_with_response(msg)
|
|
364
|
+
|
|
365
|
+
return msg, encrypt_key
|
|
366
|
+
|
|
367
|
+
async def send(self, request: httpx.Request, *args, **kwargs):
|
|
368
|
+
encrypt_key: Optional[ResponseKey] = None
|
|
369
|
+
|
|
370
|
+
if self.aicc_config:
|
|
371
|
+
request.headers["X-AICC-Encryption-Enable"] = "true"
|
|
372
|
+
request.headers["X-AICC-Encryption-SDK"] = "aicc"
|
|
373
|
+
request.headers["X-AICC-Encryption-Version"] = AICC_SDK_VERSION
|
|
374
|
+
|
|
375
|
+
encryption_content, encrypt_key = self.jsc_encryption(request.content)
|
|
376
|
+
if isinstance(encryption_content, str):
|
|
377
|
+
encryption_content = encryption_content.encode("utf-8")
|
|
378
|
+
|
|
379
|
+
request.stream = httpx.ByteStream(encryption_content)
|
|
380
|
+
if hasattr(request, "_content"):
|
|
381
|
+
delattr(request, "_content")
|
|
382
|
+
request.headers["Content-Type"] = "application/json"
|
|
383
|
+
request.headers["Content-Length"] = str(len(encryption_content))
|
|
384
|
+
|
|
385
|
+
resp = await super().send(request, *args, **kwargs)
|
|
386
|
+
|
|
387
|
+
if 200 <= resp.status_code < 300:
|
|
388
|
+
if encrypt_key:
|
|
389
|
+
if not kwargs.get("stream", False):
|
|
390
|
+
content = await resp.aread()
|
|
391
|
+
decrypted_content = encrypt_key.decrypt(content)
|
|
392
|
+
resp._content = decrypted_content
|
|
393
|
+
else:
|
|
394
|
+
resp.stream = DemoAsyncByteStream(resp.stream, encrypt_key)
|
|
395
|
+
else:
|
|
396
|
+
if encrypt_key:
|
|
397
|
+
try:
|
|
398
|
+
if not kwargs.get("stream", False):
|
|
399
|
+
content = await resp.aread()
|
|
400
|
+
decrypted_content = encrypt_key.decrypt(content)
|
|
401
|
+
resp._content = decrypted_content
|
|
402
|
+
else:
|
|
403
|
+
resp.stream = DemoAsyncByteStream(resp.stream, encrypt_key)
|
|
404
|
+
except Exception as e:
|
|
405
|
+
pass
|
|
406
|
+
return resp
|