dashscope 1.23.5__py3-none-any.whl → 1.23.7__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.
Potentially problematic release.
This version of dashscope might be problematic. Click here for more details.
- dashscope/api_entities/api_request_factory.py +16 -3
- dashscope/api_entities/encryption.py +179 -0
- dashscope/api_entities/http_request.py +26 -1
- dashscope/common/constants.py +3 -0
- dashscope/multimodal/multimodal_constants.py +1 -0
- dashscope/multimodal/multimodal_dialog.py +42 -7
- dashscope/multimodal/multimodal_request_params.py +5 -2
- dashscope/version.py +1 -1
- {dashscope-1.23.5.dist-info → dashscope-1.23.7.dist-info}/METADATA +18 -5
- {dashscope-1.23.5.dist-info → dashscope-1.23.7.dist-info}/RECORD +14 -13
- {dashscope-1.23.5.dist-info → dashscope-1.23.7.dist-info}/WHEEL +1 -1
- {dashscope-1.23.5.dist-info → dashscope-1.23.7.dist-info}/entry_points.txt +0 -1
- {dashscope-1.23.5.dist-info → dashscope-1.23.7.dist-info/licenses}/LICENSE +0 -0
- {dashscope-1.23.5.dist-info → dashscope-1.23.7.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
# Copyright (c) Alibaba, Inc. and its affiliates.
|
|
2
|
-
|
|
3
2
|
from urllib.parse import urlencode
|
|
4
3
|
|
|
5
4
|
import dashscope
|
|
@@ -10,8 +9,9 @@ from dashscope.common.constants import (REQUEST_TIMEOUT_KEYWORD,
|
|
|
10
9
|
SERVICE_API_PATH, ApiProtocol,
|
|
11
10
|
HTTPMethod)
|
|
12
11
|
from dashscope.common.error import InputDataRequired, UnsupportedApiProtocol
|
|
12
|
+
from dashscope.common.logging import logger
|
|
13
13
|
from dashscope.protocol.websocket import WebsocketStreamingMode
|
|
14
|
-
|
|
14
|
+
from dashscope.api_entities.encryption import Encryption
|
|
15
15
|
|
|
16
16
|
def _get_protocol_params(kwargs):
|
|
17
17
|
api_protocol = kwargs.pop('api_protocol', ApiProtocol.HTTPS)
|
|
@@ -49,6 +49,9 @@ def _build_api_request(model: str,
|
|
|
49
49
|
base_address, flattened_output,
|
|
50
50
|
extra_url_parameters) = _get_protocol_params(kwargs)
|
|
51
51
|
task_id = kwargs.pop('task_id', None)
|
|
52
|
+
enable_encryption = kwargs.pop('enable_encryption', False)
|
|
53
|
+
encryption = None
|
|
54
|
+
|
|
52
55
|
if api_protocol in [ApiProtocol.HTTP, ApiProtocol.HTTPS]:
|
|
53
56
|
if base_address is None:
|
|
54
57
|
base_address = dashscope.base_http_api_url
|
|
@@ -69,6 +72,12 @@ def _build_api_request(model: str,
|
|
|
69
72
|
if extra_url_parameters is not None and extra_url_parameters:
|
|
70
73
|
http_url += '?' + urlencode(extra_url_parameters)
|
|
71
74
|
|
|
75
|
+
if enable_encryption is True:
|
|
76
|
+
encryption = Encryption()
|
|
77
|
+
encryption.initialize()
|
|
78
|
+
if encryption.is_valid():
|
|
79
|
+
logger.debug('encryption enabled')
|
|
80
|
+
|
|
72
81
|
request = HttpRequest(url=http_url,
|
|
73
82
|
api_key=api_key,
|
|
74
83
|
http_method=http_method,
|
|
@@ -77,7 +86,8 @@ def _build_api_request(model: str,
|
|
|
77
86
|
query=query,
|
|
78
87
|
timeout=request_timeout,
|
|
79
88
|
task_id=task_id,
|
|
80
|
-
flattened_output=flattened_output
|
|
89
|
+
flattened_output=flattened_output,
|
|
90
|
+
encryption=encryption)
|
|
81
91
|
elif api_protocol == ApiProtocol.WEBSOCKET:
|
|
82
92
|
if base_address is not None:
|
|
83
93
|
websocket_url = base_address
|
|
@@ -103,6 +113,9 @@ def _build_api_request(model: str,
|
|
|
103
113
|
if input is None and form is None:
|
|
104
114
|
raise InputDataRequired('There is no input data and form data')
|
|
105
115
|
|
|
116
|
+
if encryption and encryption.is_valid():
|
|
117
|
+
input = encryption.encrypt(input)
|
|
118
|
+
|
|
106
119
|
request_data = ApiRequestData(model,
|
|
107
120
|
task_group=task_group,
|
|
108
121
|
task=task,
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Copyright (c) Alibaba, Inc. and its affiliates.
|
|
2
|
+
import base64
|
|
3
|
+
import json
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
import os
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
|
10
|
+
from cryptography.hazmat.primitives import serialization, hashes
|
|
11
|
+
from cryptography.hazmat.primitives.asymmetric import padding
|
|
12
|
+
from cryptography.hazmat.backends import default_backend
|
|
13
|
+
|
|
14
|
+
import dashscope
|
|
15
|
+
from dashscope.common.constants import ENCRYPTION_AES_SECRET_KEY_BYTES, ENCRYPTION_AES_IV_LENGTH
|
|
16
|
+
from dashscope.common.logging import logger
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Encryption:
|
|
20
|
+
def __init__(self):
|
|
21
|
+
self.pub_key_id: str = ''
|
|
22
|
+
self.pub_key_str: str = ''
|
|
23
|
+
self.aes_key_bytes: bytes = b''
|
|
24
|
+
self.encrypted_aes_key_str: str = ''
|
|
25
|
+
self.iv_bytes: bytes = b''
|
|
26
|
+
self.base64_iv_str: str = ''
|
|
27
|
+
self.valid: bool = False
|
|
28
|
+
|
|
29
|
+
def initialize(self):
|
|
30
|
+
public_keys = self._get_public_keys()
|
|
31
|
+
if not public_keys:
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
public_key_str = public_keys.get('public_key')
|
|
35
|
+
public_key_id = public_keys.get('public_key_id')
|
|
36
|
+
if not public_key_str or not public_key_id:
|
|
37
|
+
logger.error("public keys data not valid")
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
aes_key_bytes = self._generate_aes_secret_key()
|
|
41
|
+
iv_bytes = self._generate_iv()
|
|
42
|
+
|
|
43
|
+
encrypted_aes_key_str = self._encrypt_aes_key_with_rsa(aes_key_bytes, public_key_str)
|
|
44
|
+
base64_iv_str = base64.b64encode(iv_bytes).decode('utf-8')
|
|
45
|
+
|
|
46
|
+
self.pub_key_id = public_key_id
|
|
47
|
+
self.pub_key_str = public_key_str
|
|
48
|
+
self.aes_key_bytes = aes_key_bytes
|
|
49
|
+
self.encrypted_aes_key_str = encrypted_aes_key_str
|
|
50
|
+
self.iv_bytes = iv_bytes
|
|
51
|
+
self.base64_iv_str = base64_iv_str
|
|
52
|
+
|
|
53
|
+
self.valid = True
|
|
54
|
+
|
|
55
|
+
def encrypt(self, dict_plaintext):
|
|
56
|
+
return self._encrypt_text_with_aes(json.dumps(dict_plaintext, ensure_ascii=False),
|
|
57
|
+
self.aes_key_bytes, self.iv_bytes)
|
|
58
|
+
|
|
59
|
+
def decrypt(self, base64_ciphertext):
|
|
60
|
+
return self._decrypt_text_with_aes(base64_ciphertext, self.aes_key_bytes, self.iv_bytes)
|
|
61
|
+
|
|
62
|
+
def is_valid(self):
|
|
63
|
+
return self.valid
|
|
64
|
+
|
|
65
|
+
def get_pub_key_id(self):
|
|
66
|
+
return self.pub_key_id
|
|
67
|
+
|
|
68
|
+
def get_encrypted_aes_key_str(self):
|
|
69
|
+
return self.encrypted_aes_key_str
|
|
70
|
+
|
|
71
|
+
def get_base64_iv_str(self):
|
|
72
|
+
return self.base64_iv_str
|
|
73
|
+
|
|
74
|
+
@staticmethod
|
|
75
|
+
def _get_public_keys():
|
|
76
|
+
url = dashscope.base_http_api_url + '/public-keys/latest'
|
|
77
|
+
headers = {
|
|
78
|
+
"Authorization": f"Bearer {dashscope.api_key}"
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
response = requests.get(url, headers=headers)
|
|
82
|
+
if response.status_code != 200:
|
|
83
|
+
logger.error("exceptional public key response: %s" % response)
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
json_resp = response.json()
|
|
87
|
+
response_data = json_resp.get('data')
|
|
88
|
+
|
|
89
|
+
if not response_data:
|
|
90
|
+
logger.error("no valid data in public key response")
|
|
91
|
+
return None
|
|
92
|
+
|
|
93
|
+
return response_data
|
|
94
|
+
|
|
95
|
+
@staticmethod
|
|
96
|
+
def _generate_aes_secret_key():
|
|
97
|
+
return os.urandom(ENCRYPTION_AES_SECRET_KEY_BYTES)
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def _generate_iv():
|
|
101
|
+
return os.urandom(ENCRYPTION_AES_IV_LENGTH)
|
|
102
|
+
|
|
103
|
+
@staticmethod
|
|
104
|
+
def _encrypt_text_with_aes(plaintext, key, iv):
|
|
105
|
+
"""使用AES-GCM加密数据"""
|
|
106
|
+
|
|
107
|
+
# 创建AES-GCM加密器
|
|
108
|
+
aes_gcm = Cipher(
|
|
109
|
+
algorithms.AES(key),
|
|
110
|
+
modes.GCM(iv, tag=None),
|
|
111
|
+
backend=default_backend()
|
|
112
|
+
).encryptor()
|
|
113
|
+
|
|
114
|
+
# 关联数据设为空(根据需求可调整)
|
|
115
|
+
aes_gcm.authenticate_additional_data(b'')
|
|
116
|
+
|
|
117
|
+
# 加密数据
|
|
118
|
+
ciphertext = aes_gcm.update(plaintext.encode('utf-8')) + aes_gcm.finalize()
|
|
119
|
+
|
|
120
|
+
# 获取认证标签
|
|
121
|
+
tag = aes_gcm.tag
|
|
122
|
+
|
|
123
|
+
# 组合密文和标签
|
|
124
|
+
encrypted_data = ciphertext + tag
|
|
125
|
+
|
|
126
|
+
# 返回Base64编码结果
|
|
127
|
+
return base64.b64encode(encrypted_data).decode('utf-8')
|
|
128
|
+
|
|
129
|
+
@staticmethod
|
|
130
|
+
def _decrypt_text_with_aes(base64_ciphertext, aes_key, iv):
|
|
131
|
+
"""使用AES-GCM解密响应"""
|
|
132
|
+
|
|
133
|
+
# 解码Base64数据
|
|
134
|
+
encrypted_data = base64.b64decode(base64_ciphertext)
|
|
135
|
+
|
|
136
|
+
# 分离密文和标签(标签长度16字节)
|
|
137
|
+
ciphertext = encrypted_data[:-16]
|
|
138
|
+
tag = encrypted_data[-16:]
|
|
139
|
+
|
|
140
|
+
# 创建AES-GCM解密器
|
|
141
|
+
aes_gcm = Cipher(
|
|
142
|
+
algorithms.AES(aes_key),
|
|
143
|
+
modes.GCM(iv, tag),
|
|
144
|
+
backend=default_backend()
|
|
145
|
+
).decryptor()
|
|
146
|
+
|
|
147
|
+
# 验证关联数据(与加密时一致)
|
|
148
|
+
aes_gcm.authenticate_additional_data(b'')
|
|
149
|
+
|
|
150
|
+
# 解密数据
|
|
151
|
+
decrypted_bytes = aes_gcm.update(ciphertext) + aes_gcm.finalize()
|
|
152
|
+
|
|
153
|
+
# 明文
|
|
154
|
+
plaintext = decrypted_bytes.decode('utf-8')
|
|
155
|
+
|
|
156
|
+
return json.loads(plaintext)
|
|
157
|
+
|
|
158
|
+
@staticmethod
|
|
159
|
+
def _encrypt_aes_key_with_rsa(aes_key, public_key_str):
|
|
160
|
+
"""使用RSA公钥加密AES密钥"""
|
|
161
|
+
|
|
162
|
+
# 解码Base64格式的公钥
|
|
163
|
+
public_key_bytes = base64.b64decode(public_key_str)
|
|
164
|
+
|
|
165
|
+
# 加载公钥
|
|
166
|
+
public_key = serialization.load_der_public_key(
|
|
167
|
+
public_key_bytes,
|
|
168
|
+
backend=default_backend()
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
base64_aes_key = base64.b64encode(aes_key).decode('utf-8')
|
|
172
|
+
|
|
173
|
+
# 使用RSA加密
|
|
174
|
+
encrypted_bytes = public_key.encrypt(
|
|
175
|
+
base64_aes_key.encode('utf-8'),
|
|
176
|
+
padding.PKCS1v15()
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
return base64.b64encode(encrypted_bytes).decode('utf-8')
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
from http import HTTPStatus
|
|
5
|
+
from typing import Optional
|
|
5
6
|
|
|
6
7
|
import aiohttp
|
|
7
8
|
import requests
|
|
@@ -16,6 +17,7 @@ from dashscope.common.utils import (_handle_aio_stream,
|
|
|
16
17
|
_handle_aiohttp_failed_response,
|
|
17
18
|
_handle_http_failed_response,
|
|
18
19
|
_handle_stream)
|
|
20
|
+
from dashscope.api_entities.encryption import Encryption
|
|
19
21
|
|
|
20
22
|
|
|
21
23
|
class HttpRequest(AioBaseRequest):
|
|
@@ -28,7 +30,8 @@ class HttpRequest(AioBaseRequest):
|
|
|
28
30
|
query: bool = False,
|
|
29
31
|
timeout: int = DEFAULT_REQUEST_TIMEOUT_SECONDS,
|
|
30
32
|
task_id: str = None,
|
|
31
|
-
flattened_output: bool = False
|
|
33
|
+
flattened_output: bool = False,
|
|
34
|
+
encryption: Optional[Encryption] = None) -> None:
|
|
32
35
|
"""HttpSSERequest, processing http server sent event stream.
|
|
33
36
|
|
|
34
37
|
Args:
|
|
@@ -44,11 +47,23 @@ class HttpRequest(AioBaseRequest):
|
|
|
44
47
|
self.url = url
|
|
45
48
|
self.flattened_output = flattened_output
|
|
46
49
|
self.async_request = async_request
|
|
50
|
+
self.encryption = encryption
|
|
47
51
|
self.headers = {
|
|
48
52
|
'Accept': 'application/json',
|
|
49
53
|
'Authorization': 'Bearer %s' % api_key,
|
|
50
54
|
**self.headers,
|
|
51
55
|
}
|
|
56
|
+
|
|
57
|
+
if encryption and encryption.is_valid():
|
|
58
|
+
self.headers = {
|
|
59
|
+
"X-DashScope-EncryptionKey": json.dumps({
|
|
60
|
+
"public_key_id": encryption.get_pub_key_id(),
|
|
61
|
+
"encrypt_key": encryption.get_encrypted_aes_key_str(),
|
|
62
|
+
"iv": encryption.get_base64_iv_str()
|
|
63
|
+
}),
|
|
64
|
+
**self.headers,
|
|
65
|
+
}
|
|
66
|
+
|
|
52
67
|
self.query = query
|
|
53
68
|
if self.async_request and self.query is False:
|
|
54
69
|
self.headers = {
|
|
@@ -168,6 +183,8 @@ class HttpRequest(AioBaseRequest):
|
|
|
168
183
|
code=msg['code'],
|
|
169
184
|
message=msg['message'])
|
|
170
185
|
else:
|
|
186
|
+
if self.encryption and self.encryption.is_valid():
|
|
187
|
+
output = self.encryption.decrypt(output)
|
|
171
188
|
yield DashScopeAPIResponse(request_id=request_id,
|
|
172
189
|
status_code=HTTPStatus.OK,
|
|
173
190
|
output=output,
|
|
@@ -183,6 +200,8 @@ class HttpRequest(AioBaseRequest):
|
|
|
183
200
|
output[part.name] = await part.read()
|
|
184
201
|
if 'request_id' in output:
|
|
185
202
|
request_id = output['request_id']
|
|
203
|
+
if self.encryption and self.encryption.is_valid():
|
|
204
|
+
output = self.encryption.decrypt(output)
|
|
186
205
|
yield DashScopeAPIResponse(request_id=request_id,
|
|
187
206
|
status_code=HTTPStatus.OK,
|
|
188
207
|
output=output)
|
|
@@ -196,6 +215,8 @@ class HttpRequest(AioBaseRequest):
|
|
|
196
215
|
usage = json_content['usage']
|
|
197
216
|
if 'request_id' in json_content:
|
|
198
217
|
request_id = json_content['request_id']
|
|
218
|
+
if self.encryption and self.encryption.is_valid():
|
|
219
|
+
output = self.encryption.decrypt(output)
|
|
199
220
|
yield DashScopeAPIResponse(request_id=request_id,
|
|
200
221
|
status_code=HTTPStatus.OK,
|
|
201
222
|
output=output,
|
|
@@ -243,6 +264,8 @@ class HttpRequest(AioBaseRequest):
|
|
|
243
264
|
if self.flattened_output:
|
|
244
265
|
yield msg
|
|
245
266
|
else:
|
|
267
|
+
if self.encryption and self.encryption.is_valid():
|
|
268
|
+
output = self.encryption.decrypt(output)
|
|
246
269
|
yield DashScopeAPIResponse(request_id=request_id,
|
|
247
270
|
status_code=HTTPStatus.OK,
|
|
248
271
|
output=output,
|
|
@@ -263,6 +286,8 @@ class HttpRequest(AioBaseRequest):
|
|
|
263
286
|
if self.flattened_output:
|
|
264
287
|
yield json_content
|
|
265
288
|
else:
|
|
289
|
+
if self.encryption and self.encryption.is_valid():
|
|
290
|
+
output = self.encryption.decrypt(output)
|
|
266
291
|
yield DashScopeAPIResponse(request_id=request_id,
|
|
267
292
|
status_code=HTTPStatus.OK,
|
|
268
293
|
output=output,
|
dashscope/common/constants.py
CHANGED
|
@@ -37,6 +37,9 @@ REQUEST_CONTENT_IMAGE = 'image'
|
|
|
37
37
|
REQUEST_CONTENT_AUDIO = 'audio'
|
|
38
38
|
FILE_PATH_SCHEMA = 'file://'
|
|
39
39
|
|
|
40
|
+
ENCRYPTION_AES_SECRET_KEY_BYTES = 32
|
|
41
|
+
ENCRYPTION_AES_IV_LENGTH = 12
|
|
42
|
+
|
|
40
43
|
REPEATABLE_STATUS = [
|
|
41
44
|
HTTPStatus.SERVICE_UNAVAILABLE, HTTPStatus.GATEWAY_TIMEOUT
|
|
42
45
|
]
|
|
@@ -25,3 +25,4 @@ RESPONSE_NAME_RESPONDING_ENDED = "RespondingEnded" # AI语音应答结束
|
|
|
25
25
|
RESPONSE_NAME_SPEECH_CONTENT = "SpeechContent" # 用户语音识别出的文本,流式全量输出
|
|
26
26
|
RESPONSE_NAME_RESPONDING_CONTENT = "RespondingContent" # 统对外输出的文本,流式全量输出
|
|
27
27
|
RESPONSE_NAME_ERROR = "Error" # 服务端对话中报错
|
|
28
|
+
RESPONSE_NAME_HEART_BEAT = "HeartBeat" # 心跳消息
|
|
@@ -127,10 +127,10 @@ class MultiModalDialog:
|
|
|
127
127
|
"""
|
|
128
128
|
|
|
129
129
|
def __init__(self,
|
|
130
|
-
workspace_id: str,
|
|
131
130
|
app_id: str,
|
|
132
131
|
request_params: RequestParameters,
|
|
133
132
|
multimodal_callback: MultiModalCallback,
|
|
133
|
+
workspace_id: str = None,
|
|
134
134
|
url: str = None,
|
|
135
135
|
api_key: str = None,
|
|
136
136
|
dialog_id: str = None,
|
|
@@ -140,7 +140,7 @@ class MultiModalDialog:
|
|
|
140
140
|
创建一个语音对话会话。
|
|
141
141
|
|
|
142
142
|
此方法用于初始化一个新的voice_chat会话,设置必要的参数以准备开始与模型的交互。
|
|
143
|
-
:param workspace_id: 客户的workspace_id
|
|
143
|
+
:param workspace_id: 客户的workspace_id 主工作空间id,非必填字段
|
|
144
144
|
:param app_id: 客户在管控台创建的应用id,可以根据值规律确定使用哪个对话系统
|
|
145
145
|
:param request_params: 请求参数集合
|
|
146
146
|
:param url: (str) API的URL地址。
|
|
@@ -185,7 +185,8 @@ class MultiModalDialog:
|
|
|
185
185
|
|
|
186
186
|
def _on_close(self, ws, close_status_code, close_msg):
|
|
187
187
|
try:
|
|
188
|
-
logger.debug(
|
|
188
|
+
logger.debug(
|
|
189
|
+
"WebSocket connection closed with status {} and message {}".format(close_status_code, close_msg))
|
|
189
190
|
if close_status_code is None:
|
|
190
191
|
close_status_code = 1000
|
|
191
192
|
if close_msg is None:
|
|
@@ -257,6 +258,16 @@ class MultiModalDialog:
|
|
|
257
258
|
_send_speech_json = self.request.generate_common_direction_request("LocalRespondingEnded", self.dialog_id)
|
|
258
259
|
self._send_text_frame(_send_speech_json)
|
|
259
260
|
|
|
261
|
+
def send_heart_beat(self):
|
|
262
|
+
"""发送心跳"""
|
|
263
|
+
_send_speech_json = self.request.generate_common_direction_request("HeartBeat", self.dialog_id)
|
|
264
|
+
self._send_text_frame(_send_speech_json)
|
|
265
|
+
|
|
266
|
+
def update_info(self, parameters: RequestToRespondParameters = None):
|
|
267
|
+
"""更新信息"""
|
|
268
|
+
_send_speech_json = self.request.generate_update_info_json(direction_name="UpdateInfo", dialog_id=self.dialog_id, parameters=parameters)
|
|
269
|
+
self._send_text_frame(_send_speech_json)
|
|
270
|
+
|
|
260
271
|
def stop(self):
|
|
261
272
|
if self.ws is None or not self.ws.sock or not self.ws.sock.connected:
|
|
262
273
|
self._callback.on_close(1001, "websocket is not connected")
|
|
@@ -372,10 +383,10 @@ class _Request:
|
|
|
372
383
|
|
|
373
384
|
def generate_start_request(self, direction_name: str,
|
|
374
385
|
dialog_id: str,
|
|
375
|
-
workspace_id: str,
|
|
376
386
|
app_id: str,
|
|
377
387
|
request_params: RequestParameters,
|
|
378
|
-
model: str = None
|
|
388
|
+
model: str = None,
|
|
389
|
+
workspace_id: str = None
|
|
379
390
|
) -> str:
|
|
380
391
|
"""
|
|
381
392
|
构建语音聊天服务的启动请求数据.
|
|
@@ -383,7 +394,7 @@ class _Request:
|
|
|
383
394
|
:param request_params: start请求body中的parameters
|
|
384
395
|
:param direction_name:
|
|
385
396
|
:param dialog_id: 对话ID.
|
|
386
|
-
:param workspace_id:
|
|
397
|
+
:param workspace_id: 管控台工作空间id, 非必填字段。
|
|
387
398
|
:param model: 模型
|
|
388
399
|
:return: 启动请求字典.
|
|
389
400
|
"""
|
|
@@ -428,7 +439,6 @@ class _Request:
|
|
|
428
439
|
}
|
|
429
440
|
return json.dumps(cmd)
|
|
430
441
|
|
|
431
|
-
@abstractmethod
|
|
432
442
|
def generate_request_to_response_json(self, direction_name: str, dialog_id: str, request_type: str, text: str,
|
|
433
443
|
parameters: RequestToRespondParameters = None) -> str:
|
|
434
444
|
"""
|
|
@@ -458,6 +468,29 @@ class _Request:
|
|
|
458
468
|
}
|
|
459
469
|
return json.dumps(cmd)
|
|
460
470
|
|
|
471
|
+
def generate_update_info_json(self, direction_name: str, dialog_id: str,parameters: RequestToRespondParameters = None) -> str:
|
|
472
|
+
"""
|
|
473
|
+
构建语音聊天服务的命令请求数据.
|
|
474
|
+
:param direction_name: 命令.
|
|
475
|
+
:param parameters: 命令请求body中的parameters
|
|
476
|
+
:return: 命令请求字典.
|
|
477
|
+
"""
|
|
478
|
+
self._get_dash_request_header(ActionType.CONTINUE)
|
|
479
|
+
|
|
480
|
+
custom_input = RequestToRespondBodyInput(
|
|
481
|
+
app_id=self.app_id,
|
|
482
|
+
directive=direction_name,
|
|
483
|
+
dialog_id=dialog_id,
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
self._get_dash_request_payload(direction_name, dialog_id, self.app_id, request_params=parameters,
|
|
487
|
+
custom_input=custom_input)
|
|
488
|
+
cmd = {
|
|
489
|
+
"header": self.header,
|
|
490
|
+
"payload": self.payload
|
|
491
|
+
}
|
|
492
|
+
return json.dumps(cmd)
|
|
493
|
+
|
|
461
494
|
def _get_dash_request_header(self, action: str):
|
|
462
495
|
"""
|
|
463
496
|
构建多模对话请求的请求协议Header
|
|
@@ -560,6 +593,8 @@ class _Response:
|
|
|
560
593
|
self._handle_responding_content(payload)
|
|
561
594
|
elif response_event == RESPONSE_NAME_ERROR:
|
|
562
595
|
self._callback.on_error(json.dumps(response_json))
|
|
596
|
+
elif response_event == RESPONSE_NAME_HEART_BEAT:
|
|
597
|
+
logger.debug("Server response heart beat")
|
|
563
598
|
else:
|
|
564
599
|
logger.error("Unknown response name: {}", response_event)
|
|
565
600
|
except json.JSONDecodeError:
|
|
@@ -198,6 +198,7 @@ class BizParams:
|
|
|
198
198
|
tool_prompts: dict = field(default=None)
|
|
199
199
|
user_prompt_params: dict = field(default=None)
|
|
200
200
|
user_query_params: dict = field(default=None)
|
|
201
|
+
videos: list = field(default=None)
|
|
201
202
|
|
|
202
203
|
def to_dict(self):
|
|
203
204
|
params = {}
|
|
@@ -211,6 +212,8 @@ class BizParams:
|
|
|
211
212
|
params["user_prompt_params"] = self.user_prompt_params
|
|
212
213
|
if self.user_query_params is not None:
|
|
213
214
|
params["user_query_params"] = self.user_query_params
|
|
215
|
+
if self.videos is not None:
|
|
216
|
+
params["videos"] = self.videos
|
|
214
217
|
return params
|
|
215
218
|
|
|
216
219
|
|
|
@@ -256,7 +259,7 @@ class RequestToRespondBodyInput(DashPayloadInput):
|
|
|
256
259
|
app_id: str
|
|
257
260
|
directive: str
|
|
258
261
|
dialog_id: str
|
|
259
|
-
type_: str = field(metadata={"alias": "type"})
|
|
262
|
+
type_: str = field(metadata={"alias": "type"}, default= None)
|
|
260
263
|
text: str = field(default="")
|
|
261
264
|
|
|
262
265
|
def to_dict(self):
|
|
@@ -266,4 +269,4 @@ class RequestToRespondBodyInput(DashPayloadInput):
|
|
|
266
269
|
"dialog_id": self.dialog_id,
|
|
267
270
|
"type": self.type_,
|
|
268
271
|
"text": self.text
|
|
269
|
-
}
|
|
272
|
+
}
|
dashscope/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: dashscope
|
|
3
|
-
Version: 1.23.
|
|
3
|
+
Version: 1.23.7
|
|
4
4
|
Summary: dashscope client sdk library
|
|
5
5
|
Home-page: https://dashscope.aliyun.com/
|
|
6
6
|
Author: Alibaba Cloud
|
|
@@ -18,11 +18,26 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
19
|
Requires-Python: >=3.8.0
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
21
22
|
Requires-Dist: aiohttp
|
|
22
23
|
Requires-Dist: requests
|
|
23
24
|
Requires-Dist: websocket-client
|
|
25
|
+
Requires-Dist: cryptography
|
|
24
26
|
Provides-Extra: tokenizer
|
|
25
|
-
Requires-Dist: tiktoken
|
|
27
|
+
Requires-Dist: tiktoken; extra == "tokenizer"
|
|
28
|
+
Dynamic: author
|
|
29
|
+
Dynamic: author-email
|
|
30
|
+
Dynamic: classifier
|
|
31
|
+
Dynamic: description
|
|
32
|
+
Dynamic: description-content-type
|
|
33
|
+
Dynamic: home-page
|
|
34
|
+
Dynamic: license
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
Dynamic: platform
|
|
37
|
+
Dynamic: provides-extra
|
|
38
|
+
Dynamic: requires-dist
|
|
39
|
+
Dynamic: requires-python
|
|
40
|
+
Dynamic: summary
|
|
26
41
|
|
|
27
42
|
<h4 align="center">
|
|
28
43
|
<p>
|
|
@@ -221,5 +236,3 @@ Coming soon.
|
|
|
221
236
|
|
|
222
237
|
## License
|
|
223
238
|
This project is licensed under the Apache License (Version 2.0).
|
|
224
|
-
|
|
225
|
-
|
|
@@ -3,7 +3,7 @@ dashscope/cli.py,sha256=amegoTkGOs6TlHMdoo4JVOqBePo3lGs745rc7leEyrE,24020
|
|
|
3
3
|
dashscope/files.py,sha256=vRDQygm3lOqBZR73o7KNHs1iTBVuvLncuwJNxIYjzAU,3981
|
|
4
4
|
dashscope/model.py,sha256=B5v_BtYLPqj6raClejBgdKg6WTGwhH_f-20pvsQqmsk,1491
|
|
5
5
|
dashscope/models.py,sha256=dE4mzXkl85G343qVylSGpURPRdA5pZSqXlx6PcxqC_Q,1275
|
|
6
|
-
dashscope/version.py,sha256=
|
|
6
|
+
dashscope/version.py,sha256=HwgMDxa-K4tdaSJnt84_qDc7qTyV4eqR_QjdjT4ulxc,74
|
|
7
7
|
dashscope/aigc/__init__.py,sha256=AuRhu_vA1K0tbs_C6DgcZYhTvxMuzDgpwHJNHzEPIHg,442
|
|
8
8
|
dashscope/aigc/chat_completion.py,sha256=ONlyyssIbfaKKcFo7cEKhHx5OCF2XX810HFzIExW1ho,14813
|
|
9
9
|
dashscope/aigc/code_generation.py,sha256=p_mxDKJLQMW0IjFD46JRlZuEZCRESSVKEfLlAevBtqw,10936
|
|
@@ -15,11 +15,12 @@ dashscope/aigc/video_synthesis.py,sha256=XQ3-NKYFmj5cIbUbLTbI0-FyC_fQp8eds6QmD1Z
|
|
|
15
15
|
dashscope/api_entities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
dashscope/api_entities/aiohttp_request.py,sha256=1L7XdIJ9L65cQmX8x9JCR4t5hNIMDrbiWADfKKp9yfo,10280
|
|
17
17
|
dashscope/api_entities/api_request_data.py,sha256=04rpYPNK1HkT3iTPJmZpquH621xcBbe8R8EGrDJSLt0,5514
|
|
18
|
-
dashscope/api_entities/api_request_factory.py,sha256=
|
|
18
|
+
dashscope/api_entities/api_request_factory.py,sha256=18A40aHL0t3s01VdbkIWRGNeVJyX0GXRHTZUxau7po4,5640
|
|
19
19
|
dashscope/api_entities/base_request.py,sha256=W2SzrSAGFS6V8DErfSrayQtSL0T4iO7BrC8flr7nt1w,977
|
|
20
20
|
dashscope/api_entities/chat_completion_types.py,sha256=1WMWPszhM3HaJBVz-ZXx-El4D8-RfVUL3ym65xsDRLk,11435
|
|
21
21
|
dashscope/api_entities/dashscope_response.py,sha256=qNNB86h5Gb_4uHjBD_4lx6UckyQzSdaTgjze1prc12M,22073
|
|
22
|
-
dashscope/api_entities/
|
|
22
|
+
dashscope/api_entities/encryption.py,sha256=rUCZx3wwVvS5oyKXEeWgyWPxM8Y5d4AaVdgxLhizBqA,5517
|
|
23
|
+
dashscope/api_entities/http_request.py,sha256=p2xfmq79evNON4ctCVXCcrJo8jnKABn0XzdTkTDgbLM,14540
|
|
23
24
|
dashscope/api_entities/websocket_request.py,sha256=PS0FU854-HjTbKa68f4GHa7-noFRMzKySJGfPkrrBjw,16146
|
|
24
25
|
dashscope/app/__init__.py,sha256=xvSvU8O7m5u7vgIvJXTJektJZxmjT2Rpt_YwePH88XE,113
|
|
25
26
|
dashscope/app/application.py,sha256=Whf_ij4RHOaY12_xdS8uj8HVNCwkTp_MRdrFTryF1Kg,9472
|
|
@@ -47,7 +48,7 @@ dashscope/client/base_api.py,sha256=aWNy_xm02GXuLKVgWnYJht2nI4ZHSGfYIcr52SML15A,
|
|
|
47
48
|
dashscope/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
49
|
dashscope/common/api_key.py,sha256=yqFCAteq8CNQGnlLv6fxNFWsLqsQDbSzOpgAlUmDkaE,2037
|
|
49
50
|
dashscope/common/base_type.py,sha256=2OQDqFlEH43wn54i-691cbarV_eKRLvRsPGfyb_GS0g,4670
|
|
50
|
-
dashscope/common/constants.py,sha256=
|
|
51
|
+
dashscope/common/constants.py,sha256=Ry3IBz2w9amRoRmfwC5L1dXHb7Iz2slj_I_wobmo-6Q,2446
|
|
51
52
|
dashscope/common/env.py,sha256=9yWWdKqfYuHlTQSvbTBaQhGbASh5Lq6SbM9pPx8hB40,920
|
|
52
53
|
dashscope/common/error.py,sha256=sXQqBGWCUBPyKa5rAI6DWc0sEidH01sR8zlIBfrTTDU,2690
|
|
53
54
|
dashscope/common/logging.py,sha256=lX86X9ND1MC5mA_qKAktwaVXd_BufLgmSGPggUiEJZo,1035
|
|
@@ -66,9 +67,9 @@ dashscope/io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
66
67
|
dashscope/io/input_output.py,sha256=0aXrRJFo1ZqYm_AJWR_w88O4-Btn9np2zUhrrUdBdfw,3992
|
|
67
68
|
dashscope/multimodal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
68
69
|
dashscope/multimodal/dialog_state.py,sha256=CtOdfGWhq0ePG3bc8-7inhespETtPD4QDli1513hd1A,1522
|
|
69
|
-
dashscope/multimodal/multimodal_constants.py,sha256=
|
|
70
|
-
dashscope/multimodal/multimodal_dialog.py,sha256=
|
|
71
|
-
dashscope/multimodal/multimodal_request_params.py,sha256=
|
|
70
|
+
dashscope/multimodal/multimodal_constants.py,sha256=z_QVq01E43FAqKQnDu9vdf89d1zuYlWyANewWTEXVJM,1282
|
|
71
|
+
dashscope/multimodal/multimodal_dialog.py,sha256=HymlaQYp7SgJdoKbT27SNiviyRRoM91zklNBwTHmm1Q,23939
|
|
72
|
+
dashscope/multimodal/multimodal_request_params.py,sha256=7A4UhsbYjcX7aAJwWI1xZEt0e1bSgPcu5pJAinaZyx0,7907
|
|
72
73
|
dashscope/nlp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
73
74
|
dashscope/nlp/understanding.py,sha256=00ado-ibYEzBRT0DgKGd3bohQDNW73xnFhJ_1aa87lw,2880
|
|
74
75
|
dashscope/protocol/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -92,9 +93,9 @@ dashscope/tokenizers/tokenizer.py,sha256=3FQVDvMNkCW9ccYeJdjrd_PIMMD3Xv7aNZkaYOE
|
|
|
92
93
|
dashscope/tokenizers/tokenizer_base.py,sha256=5EJIFuizMWESEmLmbd38yJnfeHmPnzZPwsO4aOGjpl4,707
|
|
93
94
|
dashscope/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
94
95
|
dashscope/utils/oss_utils.py,sha256=L5LN3lN8etVxSL_jkZydstvEKpnTG9CY0zcvPGQ5LBo,7383
|
|
95
|
-
dashscope-1.23.
|
|
96
|
-
dashscope-1.23.
|
|
97
|
-
dashscope-1.23.
|
|
98
|
-
dashscope-1.23.
|
|
99
|
-
dashscope-1.23.
|
|
100
|
-
dashscope-1.23.
|
|
96
|
+
dashscope-1.23.7.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
97
|
+
dashscope-1.23.7.dist-info/METADATA,sha256=JxFpTi5zU9f1O1uLoTH1OoV6iV3K3aAqshhD1MTDtFY,7123
|
|
98
|
+
dashscope-1.23.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
99
|
+
dashscope-1.23.7.dist-info/entry_points.txt,sha256=e9C3sOf9zDYL0O5ROEGX6FT8w-QK_kaGRWmPZDHAFys,49
|
|
100
|
+
dashscope-1.23.7.dist-info/top_level.txt,sha256=woqavFJK9zas5xTqynmALqOtlafghjsk63Xk86powTU,10
|
|
101
|
+
dashscope-1.23.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|