dashscope 1.23.8__py3-none-any.whl → 1.24.0__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/app/application_response.py +48 -1
- dashscope/assistants/assistant_types.py +9 -0
- dashscope/assistants/assistants.py +31 -2
- dashscope/assistants/files.py +7 -1
- dashscope/audio/__init__.py +2 -2
- dashscope/audio/qwen_omni/__init__.py +11 -0
- dashscope/audio/qwen_omni/omni_realtime.py +415 -0
- dashscope/audio/qwen_tts_realtime/__init__.py +10 -0
- dashscope/audio/qwen_tts_realtime/qwen_tts_realtime.py +314 -0
- dashscope/audio/tts_v2/speech_synthesizer.py +38 -22
- dashscope/cli.py +54 -0
- dashscope/embeddings/text_embedding.py +1 -0
- dashscope/multimodal/multimodal_request_params.py +10 -1
- dashscope/threads/runs/runs.py +21 -0
- dashscope/threads/thread_types.py +12 -0
- dashscope/utils/oss_utils.py +3 -0
- dashscope/version.py +1 -1
- {dashscope-1.23.8.dist-info → dashscope-1.24.0.dist-info}/METADATA +1 -1
- {dashscope-1.23.8.dist-info → dashscope-1.24.0.dist-info}/RECORD +23 -19
- {dashscope-1.23.8.dist-info → dashscope-1.24.0.dist-info}/WHEEL +0 -0
- {dashscope-1.23.8.dist-info → dashscope-1.24.0.dist-info}/entry_points.txt +0 -0
- {dashscope-1.23.8.dist-info → dashscope-1.24.0.dist-info}/licenses/LICENSE +0 -0
- {dashscope-1.23.8.dist-info → dashscope-1.24.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# Copyright (c) Alibaba, Inc. and its affiliates.
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import platform
|
|
5
|
+
import threading
|
|
6
|
+
import time
|
|
7
|
+
import uuid
|
|
8
|
+
from enum import Enum, unique
|
|
9
|
+
|
|
10
|
+
import dashscope
|
|
11
|
+
import websocket
|
|
12
|
+
from dashscope.common.error import InputRequired, ModelRequired
|
|
13
|
+
from dashscope.common.logging import logger
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class QwenTtsRealtimeCallback:
|
|
17
|
+
"""
|
|
18
|
+
An interface that defines callback methods for getting omni-realtime results. # noqa E501
|
|
19
|
+
Derive from this class and implement its function to provide your own data.
|
|
20
|
+
"""
|
|
21
|
+
def on_open(self) -> None:
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
def on_close(self, close_status_code, close_msg) -> None:
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
def on_event(self, message: str) -> None:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@unique
|
|
32
|
+
class AudioFormat(Enum):
|
|
33
|
+
# format, sample_rate, channels, bit_rate, name
|
|
34
|
+
PCM_24000HZ_MONO_16BIT = ('pcm', 24000, 'mono', '16bit', 'pcm16')
|
|
35
|
+
|
|
36
|
+
def __init__(self, format, sample_rate, channels, bit_rate, format_str):
|
|
37
|
+
self.format = format
|
|
38
|
+
self.sample_rate = sample_rate
|
|
39
|
+
self.channels = channels
|
|
40
|
+
self.bit_rate = bit_rate
|
|
41
|
+
self.format_str = format_str
|
|
42
|
+
|
|
43
|
+
def __repr__(self):
|
|
44
|
+
return self.format_str
|
|
45
|
+
|
|
46
|
+
def __str__(self):
|
|
47
|
+
return f'{self.format.upper()} with {self.sample_rate}Hz sample rate, {self.channels} channel, {self.bit_rate} bit rate: {self.format_str}'
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class QwenTtsRealtime:
|
|
51
|
+
def __init__(
|
|
52
|
+
self,
|
|
53
|
+
model,
|
|
54
|
+
headers=None,
|
|
55
|
+
callback: QwenTtsRealtimeCallback = None,
|
|
56
|
+
workspace=None,
|
|
57
|
+
url=None,
|
|
58
|
+
additional_params=None,
|
|
59
|
+
):
|
|
60
|
+
"""
|
|
61
|
+
Qwen Tts Realtime SDK
|
|
62
|
+
Parameters:
|
|
63
|
+
-----------
|
|
64
|
+
model: str
|
|
65
|
+
Model name.
|
|
66
|
+
headers: Dict
|
|
67
|
+
User-defined headers.
|
|
68
|
+
callback: OmniRealtimeCallback
|
|
69
|
+
Callback to receive real-time omni results.
|
|
70
|
+
workspace: str
|
|
71
|
+
Dashscope workspace ID.
|
|
72
|
+
url: str
|
|
73
|
+
Dashscope WebSocket URL.
|
|
74
|
+
additional_params: Dict
|
|
75
|
+
Additional parameters for the Dashscope API.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
if model is None:
|
|
79
|
+
raise ModelRequired('Model is required!')
|
|
80
|
+
if url is None:
|
|
81
|
+
url = f'wss://dashscope.aliyuncs.com/api-ws/v1/realtime?model={model}'
|
|
82
|
+
else:
|
|
83
|
+
url = f'{url}?model={model}'
|
|
84
|
+
self.url = url
|
|
85
|
+
self.apikey = dashscope.api_key
|
|
86
|
+
self.user_headers = headers
|
|
87
|
+
self.user_workspace = workspace
|
|
88
|
+
self.model = model
|
|
89
|
+
self.config = {}
|
|
90
|
+
self.callback = callback
|
|
91
|
+
self.ws = None
|
|
92
|
+
self.session_id = None
|
|
93
|
+
self.last_message = None
|
|
94
|
+
self.last_response_id = None
|
|
95
|
+
self.last_first_text_time = None
|
|
96
|
+
self.last_first_audio_delay = None
|
|
97
|
+
self.metrics = []
|
|
98
|
+
|
|
99
|
+
def _generate_event_id(self):
|
|
100
|
+
'''
|
|
101
|
+
generate random event id: event_xxxx
|
|
102
|
+
'''
|
|
103
|
+
return 'event_' + uuid.uuid4().hex
|
|
104
|
+
|
|
105
|
+
def _get_websocket_header(self, ):
|
|
106
|
+
ua = 'dashscope/%s; python/%s; platform/%s; processor/%s' % (
|
|
107
|
+
'1.18.0', # dashscope version
|
|
108
|
+
platform.python_version(),
|
|
109
|
+
platform.platform(),
|
|
110
|
+
platform.processor(),
|
|
111
|
+
)
|
|
112
|
+
headers = {
|
|
113
|
+
'user-agent': ua,
|
|
114
|
+
'Authorization': 'bearer ' + self.apikey,
|
|
115
|
+
}
|
|
116
|
+
if self.user_headers:
|
|
117
|
+
headers = {**self.user_headers, **headers}
|
|
118
|
+
if self.user_workspace:
|
|
119
|
+
headers = {
|
|
120
|
+
**headers,
|
|
121
|
+
'X-DashScope-WorkSpace': self.user_workspace,
|
|
122
|
+
}
|
|
123
|
+
return headers
|
|
124
|
+
|
|
125
|
+
def connect(self) -> None:
|
|
126
|
+
'''
|
|
127
|
+
connect to server, create session and return default session configuration
|
|
128
|
+
'''
|
|
129
|
+
self.ws = websocket.WebSocketApp(
|
|
130
|
+
self.url,
|
|
131
|
+
header=self._get_websocket_header(),
|
|
132
|
+
on_message=self.on_message,
|
|
133
|
+
on_error=self.on_error,
|
|
134
|
+
on_close=self.on_close,
|
|
135
|
+
)
|
|
136
|
+
self.thread = threading.Thread(target=self.ws.run_forever)
|
|
137
|
+
self.thread.daemon = True
|
|
138
|
+
self.thread.start()
|
|
139
|
+
timeout = 5 # 最长等待时间(秒)
|
|
140
|
+
start_time = time.time()
|
|
141
|
+
while (not (self.ws.sock and self.ws.sock.connected)
|
|
142
|
+
and (time.time() - start_time) < timeout):
|
|
143
|
+
time.sleep(0.1) # 短暂休眠,避免密集轮询
|
|
144
|
+
if not (self.ws.sock and self.ws.sock.connected):
|
|
145
|
+
raise TimeoutError(
|
|
146
|
+
'websocket connection could not established within 5s. '
|
|
147
|
+
'Please check your network connection, firewall settings, or server status.'
|
|
148
|
+
)
|
|
149
|
+
self.callback.on_open()
|
|
150
|
+
|
|
151
|
+
def __send_str(self, data: str, enable_log: bool = True):
|
|
152
|
+
if enable_log:
|
|
153
|
+
logger.debug('[qwen tts realtime] send string: {}'.format(data))
|
|
154
|
+
self.ws.send(data)
|
|
155
|
+
|
|
156
|
+
def update_session(self,
|
|
157
|
+
voice: str,
|
|
158
|
+
response_format: AudioFormat = AudioFormat.
|
|
159
|
+
PCM_24000HZ_MONO_16BIT,
|
|
160
|
+
mode: str = 'server_commit',
|
|
161
|
+
**kwargs) -> None:
|
|
162
|
+
'''
|
|
163
|
+
update session configuration, should be used before create response
|
|
164
|
+
|
|
165
|
+
Parameters
|
|
166
|
+
----------
|
|
167
|
+
voice: str
|
|
168
|
+
voice to be used in session
|
|
169
|
+
response_format: AudioFormat
|
|
170
|
+
output audio format
|
|
171
|
+
mode: str
|
|
172
|
+
response mode, server_commit or commit
|
|
173
|
+
'''
|
|
174
|
+
self.config = {
|
|
175
|
+
'voice': voice,
|
|
176
|
+
'mode': mode,
|
|
177
|
+
'response_format': response_format.format,
|
|
178
|
+
'sample_rate': response_format.sample_rate,
|
|
179
|
+
}
|
|
180
|
+
self.config.update(kwargs)
|
|
181
|
+
self.__send_str(
|
|
182
|
+
json.dumps({
|
|
183
|
+
'event_id': self._generate_event_id(),
|
|
184
|
+
'type': 'session.update',
|
|
185
|
+
'session': self.config
|
|
186
|
+
}))
|
|
187
|
+
|
|
188
|
+
def append_text(self, text: str) -> None:
|
|
189
|
+
'''
|
|
190
|
+
send text
|
|
191
|
+
|
|
192
|
+
Parameters
|
|
193
|
+
----------
|
|
194
|
+
text: str
|
|
195
|
+
text to send
|
|
196
|
+
'''
|
|
197
|
+
self.__send_str(
|
|
198
|
+
json.dumps({
|
|
199
|
+
'event_id': self._generate_event_id(),
|
|
200
|
+
'type': 'input_text_buffer.append',
|
|
201
|
+
'text': text
|
|
202
|
+
}))
|
|
203
|
+
if self.last_first_text_time is None:
|
|
204
|
+
self.last_first_text_time = time.time() * 1000
|
|
205
|
+
|
|
206
|
+
def commit(self, ) -> None:
|
|
207
|
+
'''
|
|
208
|
+
commit the text sent before, create response and start synthesis audio.
|
|
209
|
+
'''
|
|
210
|
+
self.__send_str(
|
|
211
|
+
json.dumps({
|
|
212
|
+
'event_id': self._generate_event_id(),
|
|
213
|
+
'type': 'input_text_buffer.commit'
|
|
214
|
+
}))
|
|
215
|
+
|
|
216
|
+
def clear_appended_text(self, ) -> None:
|
|
217
|
+
'''
|
|
218
|
+
clear the text sent to server before.
|
|
219
|
+
'''
|
|
220
|
+
self.__send_str(
|
|
221
|
+
json.dumps({
|
|
222
|
+
'event_id': self._generate_event_id(),
|
|
223
|
+
'type': 'input_text_buffer.clear'
|
|
224
|
+
}))
|
|
225
|
+
|
|
226
|
+
def cancel_response(self, ) -> None:
|
|
227
|
+
'''
|
|
228
|
+
cancel the current response
|
|
229
|
+
'''
|
|
230
|
+
self.__send_str(
|
|
231
|
+
json.dumps({
|
|
232
|
+
'event_id': self._generate_event_id(),
|
|
233
|
+
'type': 'response.cancel'
|
|
234
|
+
}))
|
|
235
|
+
|
|
236
|
+
def send_raw(self, raw_data: str) -> None:
|
|
237
|
+
'''
|
|
238
|
+
send raw data to server
|
|
239
|
+
'''
|
|
240
|
+
self.__send_str(raw_data)
|
|
241
|
+
|
|
242
|
+
def finish(self, ) -> None:
|
|
243
|
+
'''
|
|
244
|
+
finish input text stream, server will synthesis all text in buffer and close the connection
|
|
245
|
+
'''
|
|
246
|
+
self.__send_str(
|
|
247
|
+
json.dumps({
|
|
248
|
+
'event_id': self._generate_event_id(),
|
|
249
|
+
'type': 'session.finish'
|
|
250
|
+
}))
|
|
251
|
+
|
|
252
|
+
def close(self, ) -> None:
|
|
253
|
+
'''
|
|
254
|
+
close the connection to server
|
|
255
|
+
'''
|
|
256
|
+
self.ws.close()
|
|
257
|
+
|
|
258
|
+
# 监听消息的回调函数
|
|
259
|
+
def on_message(self, ws, message):
|
|
260
|
+
if isinstance(message, str):
|
|
261
|
+
logger.debug('[omni realtime] receive string {}'.format(
|
|
262
|
+
message[:1024]))
|
|
263
|
+
try:
|
|
264
|
+
# 尝试将消息解析为JSON
|
|
265
|
+
json_data = json.loads(message)
|
|
266
|
+
self.last_message = json_data
|
|
267
|
+
self.callback.on_event(json_data)
|
|
268
|
+
if 'type' in message:
|
|
269
|
+
if 'session.created' == json_data['type']:
|
|
270
|
+
self.session_id = json_data['session']['id']
|
|
271
|
+
if 'response.created' == json_data['type']:
|
|
272
|
+
self.last_response_id = json_data['response']['id']
|
|
273
|
+
elif 'response.audio.delta' == json_data['type']:
|
|
274
|
+
if self.last_first_text_time and self.last_first_audio_delay is None:
|
|
275
|
+
self.last_first_audio_delay = time.time(
|
|
276
|
+
) * 1000 - self.last_first_text_time
|
|
277
|
+
elif 'response.done' == json_data['type']:
|
|
278
|
+
logger.debug(
|
|
279
|
+
'[Metric] response: {}, first audio delay: {}'
|
|
280
|
+
.format(self.last_response_id,
|
|
281
|
+
self.last_first_audio_delay))
|
|
282
|
+
except json.JSONDecodeError:
|
|
283
|
+
logger.error('Failed to parse message as JSON.')
|
|
284
|
+
raise Exception('Failed to parse message as JSON.')
|
|
285
|
+
elif isinstance(message, (bytes, bytearray)):
|
|
286
|
+
# 如果失败,认为是二进制消息
|
|
287
|
+
logger.error(
|
|
288
|
+
'should not receive binary message in omni realtime api')
|
|
289
|
+
logger.debug('[omni realtime] receive binary {} bytes'.format(
|
|
290
|
+
len(message)))
|
|
291
|
+
|
|
292
|
+
def on_close(self, ws, close_status_code, close_msg):
|
|
293
|
+
logger.debug(
|
|
294
|
+
'[omni realtime] connection closed with code {} and message {}'.format(
|
|
295
|
+
close_status_code, close_msg))
|
|
296
|
+
self.callback.on_close(close_status_code, close_msg)
|
|
297
|
+
|
|
298
|
+
# WebSocket发生错误的回调函数
|
|
299
|
+
def on_error(self, ws, error):
|
|
300
|
+
print(f'websocket closed due to {error}')
|
|
301
|
+
raise Exception(f'websocket closed due to {error}')
|
|
302
|
+
|
|
303
|
+
# 获取上一个任务的taskId
|
|
304
|
+
def get_session_id(self):
|
|
305
|
+
return self.session_id
|
|
306
|
+
|
|
307
|
+
def get_last_message(self):
|
|
308
|
+
return self.last_message
|
|
309
|
+
|
|
310
|
+
def get_last_response_id(self):
|
|
311
|
+
return self.last_response_id
|
|
312
|
+
|
|
313
|
+
def get_first_audio_delay(self):
|
|
314
|
+
return self.last_first_audio_delay
|
|
@@ -43,28 +43,39 @@ class ResultCallback:
|
|
|
43
43
|
|
|
44
44
|
@unique
|
|
45
45
|
class AudioFormat(Enum):
|
|
46
|
-
DEFAULT = ('Default', 0, '0',
|
|
47
|
-
WAV_8000HZ_MONO_16BIT = ('wav', 8000, 'mono',
|
|
48
|
-
WAV_16000HZ_MONO_16BIT = ('wav', 16000, 'mono',
|
|
49
|
-
WAV_22050HZ_MONO_16BIT = ('wav', 22050, 'mono',
|
|
50
|
-
WAV_24000HZ_MONO_16BIT = ('wav', 24000, 'mono',
|
|
51
|
-
WAV_44100HZ_MONO_16BIT = ('wav', 44100, 'mono',
|
|
52
|
-
WAV_48000HZ_MONO_16BIT = ('wav', 48000, 'mono',
|
|
53
|
-
|
|
54
|
-
MP3_8000HZ_MONO_128KBPS = ('mp3', 8000, 'mono',
|
|
55
|
-
MP3_16000HZ_MONO_128KBPS = ('mp3', 16000, 'mono',
|
|
56
|
-
MP3_22050HZ_MONO_256KBPS = ('mp3', 22050, 'mono',
|
|
57
|
-
MP3_24000HZ_MONO_256KBPS = ('mp3', 24000, 'mono',
|
|
58
|
-
MP3_44100HZ_MONO_256KBPS = ('mp3', 44100, 'mono',
|
|
59
|
-
MP3_48000HZ_MONO_256KBPS = ('mp3', 48000, 'mono',
|
|
60
|
-
|
|
61
|
-
PCM_8000HZ_MONO_16BIT = ('pcm', 8000, 'mono',
|
|
62
|
-
PCM_16000HZ_MONO_16BIT = ('pcm', 16000, 'mono',
|
|
63
|
-
PCM_22050HZ_MONO_16BIT = ('pcm', 22050, 'mono',
|
|
64
|
-
PCM_24000HZ_MONO_16BIT = ('pcm', 24000, 'mono',
|
|
65
|
-
PCM_44100HZ_MONO_16BIT = ('pcm', 44100, 'mono',
|
|
66
|
-
PCM_48000HZ_MONO_16BIT = ('pcm', 48000, 'mono',
|
|
67
|
-
|
|
46
|
+
DEFAULT = ('Default', 0, '0', 0)
|
|
47
|
+
WAV_8000HZ_MONO_16BIT = ('wav', 8000, 'mono', 0)
|
|
48
|
+
WAV_16000HZ_MONO_16BIT = ('wav', 16000, 'mono', 16)
|
|
49
|
+
WAV_22050HZ_MONO_16BIT = ('wav', 22050, 'mono', 16)
|
|
50
|
+
WAV_24000HZ_MONO_16BIT = ('wav', 24000, 'mono', 16)
|
|
51
|
+
WAV_44100HZ_MONO_16BIT = ('wav', 44100, 'mono', 16)
|
|
52
|
+
WAV_48000HZ_MONO_16BIT = ('wav', 48000, 'mono', 16)
|
|
53
|
+
|
|
54
|
+
MP3_8000HZ_MONO_128KBPS = ('mp3', 8000, 'mono', 128)
|
|
55
|
+
MP3_16000HZ_MONO_128KBPS = ('mp3', 16000, 'mono', 128)
|
|
56
|
+
MP3_22050HZ_MONO_256KBPS = ('mp3', 22050, 'mono', 256)
|
|
57
|
+
MP3_24000HZ_MONO_256KBPS = ('mp3', 24000, 'mono', 256)
|
|
58
|
+
MP3_44100HZ_MONO_256KBPS = ('mp3', 44100, 'mono', 256)
|
|
59
|
+
MP3_48000HZ_MONO_256KBPS = ('mp3', 48000, 'mono', 256)
|
|
60
|
+
|
|
61
|
+
PCM_8000HZ_MONO_16BIT = ('pcm', 8000, 'mono', 16)
|
|
62
|
+
PCM_16000HZ_MONO_16BIT = ('pcm', 16000, 'mono', 16)
|
|
63
|
+
PCM_22050HZ_MONO_16BIT = ('pcm', 22050, 'mono', 16)
|
|
64
|
+
PCM_24000HZ_MONO_16BIT = ('pcm', 24000, 'mono', 16)
|
|
65
|
+
PCM_44100HZ_MONO_16BIT = ('pcm', 44100, 'mono', 16)
|
|
66
|
+
PCM_48000HZ_MONO_16BIT = ('pcm', 48000, 'mono', 16)
|
|
67
|
+
|
|
68
|
+
OGG_OPUS_8KHZ_MONO_32KBPS = ("opus", 8000, "mono", 32)
|
|
69
|
+
OGG_OPUS_8KHZ_MONO_16KBPS = ("opus", 8000, "mono", 16)
|
|
70
|
+
OGG_OPUS_16KHZ_MONO_16KBPS = ("opus", 16000, "mono", 16)
|
|
71
|
+
OGG_OPUS_16KHZ_MONO_32KBPS = ("opus", 16000, "mono", 32)
|
|
72
|
+
OGG_OPUS_16KHZ_MONO_64KBPS = ("opus", 16000, "mono", 64)
|
|
73
|
+
OGG_OPUS_24KHZ_MONO_16KBPS = ("opus", 24000, "mono", 16)
|
|
74
|
+
OGG_OPUS_24KHZ_MONO_32KBPS = ("opus", 24000, "mono", 32)
|
|
75
|
+
OGG_OPUS_24KHZ_MONO_64KBPS = ("opus", 24000, "mono", 64)
|
|
76
|
+
OGG_OPUS_48KHZ_MONO_16KBPS = ("opus", 48000, "mono", 16)
|
|
77
|
+
OGG_OPUS_48KHZ_MONO_32KBPS = ("opus", 48000, "mono", 32)
|
|
78
|
+
OGG_OPUS_48KHZ_MONO_64KBPS = ("opus", 48000, "mono", 64)
|
|
68
79
|
def __init__(self, format, sample_rate, channels, bit_rate):
|
|
69
80
|
self.format = format
|
|
70
81
|
self.sample_rate = sample_rate
|
|
@@ -83,6 +94,7 @@ class Request:
|
|
|
83
94
|
voice,
|
|
84
95
|
format='wav',
|
|
85
96
|
sample_rate=16000,
|
|
97
|
+
bit_rate=64000,
|
|
86
98
|
volume=50,
|
|
87
99
|
speech_rate=1.0,
|
|
88
100
|
pitch_rate=1.0,
|
|
@@ -93,6 +105,7 @@ class Request:
|
|
|
93
105
|
self.model = model
|
|
94
106
|
self.format = format
|
|
95
107
|
self.sample_rate = sample_rate
|
|
108
|
+
self.bit_rate = bit_rate
|
|
96
109
|
self.volume = volume
|
|
97
110
|
self.speech_rate = speech_rate
|
|
98
111
|
self.pitch_rate = pitch_rate
|
|
@@ -146,6 +159,8 @@ class Request:
|
|
|
146
159
|
},
|
|
147
160
|
},
|
|
148
161
|
}
|
|
162
|
+
if self.format == 'opus':
|
|
163
|
+
cmd['payload']['parameters']['bit_rate'] = self.bit_rate
|
|
149
164
|
if additional_params:
|
|
150
165
|
cmd['payload']['parameters'].update(additional_params)
|
|
151
166
|
return json.dumps(cmd)
|
|
@@ -252,6 +267,7 @@ class SpeechSynthesizer:
|
|
|
252
267
|
voice=voice,
|
|
253
268
|
format=format.format,
|
|
254
269
|
sample_rate=format.sample_rate,
|
|
270
|
+
bit_rate = format.bit_rate,
|
|
255
271
|
volume=volume,
|
|
256
272
|
speech_rate=speech_rate,
|
|
257
273
|
pitch_rate=pitch_rate,
|
dashscope/cli.py
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
import argparse
|
|
3
3
|
import sys
|
|
4
4
|
import time
|
|
5
|
+
import os
|
|
5
6
|
from http import HTTPStatus
|
|
6
7
|
|
|
7
8
|
import dashscope
|
|
8
9
|
from dashscope.aigc import Generation
|
|
9
10
|
from dashscope.common.constants import (DeploymentStatus, FilePurpose,
|
|
10
11
|
TaskStatus)
|
|
12
|
+
from dashscope.utils.oss_utils import OssUtils
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
def print_failed_message(rsp):
|
|
@@ -193,6 +195,34 @@ class FineTunes:
|
|
|
193
195
|
else:
|
|
194
196
|
print_failed_message(rsp)
|
|
195
197
|
|
|
198
|
+
class Oss:
|
|
199
|
+
@classmethod
|
|
200
|
+
def upload(cls, args):
|
|
201
|
+
print('Start oss.upload: model=%s, file=%s, api_key=%s' % (args.model, args.file, args.api_key))
|
|
202
|
+
if not args.file or not args.model:
|
|
203
|
+
print('Please specify the model and file path')
|
|
204
|
+
return
|
|
205
|
+
|
|
206
|
+
file_path = os.path.expanduser(args.file)
|
|
207
|
+
if not os.path.exists(file_path):
|
|
208
|
+
print('File %s does not exist' % file_path)
|
|
209
|
+
return
|
|
210
|
+
|
|
211
|
+
api_key = os.environ.get('DASHSCOPE_API_KEY', args.api_key)
|
|
212
|
+
if not api_key:
|
|
213
|
+
print('Please set your DashScope API key as environment variable '
|
|
214
|
+
'DASHSCOPE_API_KEY or pass it as argument by -k/--api_key')
|
|
215
|
+
return
|
|
216
|
+
|
|
217
|
+
oss_url = OssUtils.upload(model=args.model,
|
|
218
|
+
file_path=file_path,
|
|
219
|
+
api_key=api_key)
|
|
220
|
+
|
|
221
|
+
if not oss_url:
|
|
222
|
+
print('Failed to upload file: %s' % file_path)
|
|
223
|
+
return
|
|
224
|
+
|
|
225
|
+
print('Uploaded oss url: %s' % oss_url)
|
|
196
226
|
|
|
197
227
|
class Files:
|
|
198
228
|
@classmethod
|
|
@@ -477,6 +507,30 @@ def main():
|
|
|
477
507
|
help='The fine-tune job id.')
|
|
478
508
|
fine_tune_cancel.set_defaults(func=FineTunes.cancel)
|
|
479
509
|
|
|
510
|
+
oss_upload = sub_parsers.add_parser('oss.upload')
|
|
511
|
+
oss_upload.add_argument(
|
|
512
|
+
'-f',
|
|
513
|
+
'--file',
|
|
514
|
+
type=str,
|
|
515
|
+
required=True,
|
|
516
|
+
help='The file path to upload',
|
|
517
|
+
)
|
|
518
|
+
oss_upload.add_argument(
|
|
519
|
+
'-m',
|
|
520
|
+
'--model',
|
|
521
|
+
type=str,
|
|
522
|
+
required=True,
|
|
523
|
+
help='The model name',
|
|
524
|
+
)
|
|
525
|
+
oss_upload.add_argument(
|
|
526
|
+
'-k',
|
|
527
|
+
'--api_key',
|
|
528
|
+
type=str,
|
|
529
|
+
required=False,
|
|
530
|
+
help='The dashscope api key',
|
|
531
|
+
)
|
|
532
|
+
oss_upload.set_defaults(func=Oss.upload)
|
|
533
|
+
|
|
480
534
|
file_upload = sub_parsers.add_parser('files.upload')
|
|
481
535
|
file_upload.add_argument(
|
|
482
536
|
'-f',
|
|
@@ -83,12 +83,15 @@ class Upstream:
|
|
|
83
83
|
# sample_rate: int # 合成音频采样率
|
|
84
84
|
|
|
85
85
|
def to_dict(self):
|
|
86
|
-
|
|
86
|
+
upstream: dict = {
|
|
87
87
|
"type": self.type,
|
|
88
88
|
"mode": self.mode,
|
|
89
89
|
"audio_format": self.audio_format,
|
|
90
90
|
# "sample_rate": self.sample_rate
|
|
91
91
|
}
|
|
92
|
+
if self.pass_through_params is not None:
|
|
93
|
+
upstream.update(self.pass_through_params)
|
|
94
|
+
return upstream
|
|
92
95
|
|
|
93
96
|
|
|
94
97
|
@dataclass
|
|
@@ -105,6 +108,7 @@ class Downstream:
|
|
|
105
108
|
volume: int = field(default=50) # 语音音量 0-100
|
|
106
109
|
pitch_rate: int = field(default=100) # 语音语调 50-200
|
|
107
110
|
speech_rate: int = field(default=100) # 语音语速 50-200
|
|
111
|
+
pass_through_params: dict = field(default=None)
|
|
108
112
|
|
|
109
113
|
def to_dict(self):
|
|
110
114
|
stream: dict = {
|
|
@@ -120,6 +124,8 @@ class Downstream:
|
|
|
120
124
|
stream["voice"] = self.voice
|
|
121
125
|
if self.sample_rate != 0:
|
|
122
126
|
stream["sample_rate"] = self.sample_rate
|
|
127
|
+
if self.pass_through_params is not None:
|
|
128
|
+
stream.update(self.pass_through_params)
|
|
123
129
|
return stream
|
|
124
130
|
|
|
125
131
|
|
|
@@ -199,6 +205,7 @@ class BizParams:
|
|
|
199
205
|
user_prompt_params: dict = field(default=None)
|
|
200
206
|
user_query_params: dict = field(default=None)
|
|
201
207
|
videos: list = field(default=None)
|
|
208
|
+
pass_through_params: dict = field(default=None)
|
|
202
209
|
|
|
203
210
|
def to_dict(self):
|
|
204
211
|
params = {}
|
|
@@ -214,6 +221,8 @@ class BizParams:
|
|
|
214
221
|
params["user_query_params"] = self.user_query_params
|
|
215
222
|
if self.videos is not None:
|
|
216
223
|
params["videos"] = self.videos
|
|
224
|
+
if self.pass_through_params is not None:
|
|
225
|
+
params.update(self.pass_through_params)
|
|
217
226
|
return params
|
|
218
227
|
|
|
219
228
|
|
dashscope/threads/runs/runs.py
CHANGED
|
@@ -82,6 +82,10 @@ class Runs(CreateMixin, CancelMixin, ListObjectMixin, GetStatusMixin,
|
|
|
82
82
|
workspace: str = None,
|
|
83
83
|
extra_body: Optional[Dict] = None,
|
|
84
84
|
api_key: str = None,
|
|
85
|
+
top_p: Optional[float] = None,
|
|
86
|
+
top_k: Optional[int] = None,
|
|
87
|
+
temperature: Optional[float] = None,
|
|
88
|
+
max_tokens: Optional[int] = None,
|
|
85
89
|
**kwargs) -> Run:
|
|
86
90
|
"""Create a run.
|
|
87
91
|
|
|
@@ -122,6 +126,15 @@ class Runs(CreateMixin, CancelMixin, ListObjectMixin, GetStatusMixin,
|
|
|
122
126
|
if extra_body is not None and extra_body:
|
|
123
127
|
data = {**data, **extra_body}
|
|
124
128
|
|
|
129
|
+
if top_p is not None:
|
|
130
|
+
data['top_p'] = top_p
|
|
131
|
+
if top_k is not None:
|
|
132
|
+
data['top_k'] = top_k
|
|
133
|
+
if temperature is not None:
|
|
134
|
+
data['temperature'] = temperature
|
|
135
|
+
if max_tokens is not None:
|
|
136
|
+
data['max_tokens'] = max_tokens
|
|
137
|
+
|
|
125
138
|
response = super().call(data=data,
|
|
126
139
|
path=f'threads/{thread_id}/runs',
|
|
127
140
|
api_key=api_key,
|
|
@@ -180,6 +193,10 @@ class Runs(CreateMixin, CancelMixin, ListObjectMixin, GetStatusMixin,
|
|
|
180
193
|
workspace: str = None,
|
|
181
194
|
extra_body: Optional[Dict] = None,
|
|
182
195
|
api_key: str = None,
|
|
196
|
+
top_p: Optional[float] = None,
|
|
197
|
+
top_k: Optional[int] = None,
|
|
198
|
+
temperature: Optional[float] = None,
|
|
199
|
+
max_tokens: Optional[int] = None,
|
|
183
200
|
**kwargs) -> Run:
|
|
184
201
|
"""Create a run.
|
|
185
202
|
|
|
@@ -214,6 +231,10 @@ class Runs(CreateMixin, CancelMixin, ListObjectMixin, GetStatusMixin,
|
|
|
214
231
|
workspace=workspace,
|
|
215
232
|
extra_body=extra_body,
|
|
216
233
|
api_key=api_key,
|
|
234
|
+
top_p=top_p,
|
|
235
|
+
top_k=top_k,
|
|
236
|
+
temperature=temperature,
|
|
237
|
+
max_tokens=max_tokens,
|
|
217
238
|
**kwargs)
|
|
218
239
|
|
|
219
240
|
@classmethod
|
|
@@ -51,6 +51,13 @@ class Usage(BaseObjectMixin):
|
|
|
51
51
|
|
|
52
52
|
total_tokens: int
|
|
53
53
|
"""Total number of tokens used (prompt + completion)."""
|
|
54
|
+
|
|
55
|
+
input_tokens: int
|
|
56
|
+
"""Input tokens used (prompt)."""
|
|
57
|
+
|
|
58
|
+
output_tokens: int
|
|
59
|
+
"""Output tokens used (completion)."""
|
|
60
|
+
|
|
54
61
|
def __init__(self, **kwargs):
|
|
55
62
|
super().__init__(**kwargs)
|
|
56
63
|
|
|
@@ -302,6 +309,11 @@ class Run(BaseObjectMixin):
|
|
|
302
309
|
|
|
303
310
|
tools: List[Tool]
|
|
304
311
|
|
|
312
|
+
top_p: Optional[float] = None
|
|
313
|
+
top_k: Optional[int] = None
|
|
314
|
+
temperature: Optional[float] = None
|
|
315
|
+
max_tokens: Optional[int] = None
|
|
316
|
+
|
|
305
317
|
usage: Optional[Usage] = None
|
|
306
318
|
|
|
307
319
|
def __init__(self, **kwargs):
|
dashscope/utils/oss_utils.py
CHANGED
|
@@ -154,7 +154,10 @@ def check_and_upload_local(model: str, content: str, api_key: str):
|
|
|
154
154
|
return True, file_url
|
|
155
155
|
else:
|
|
156
156
|
raise InvalidInput('The file: %s is not exists!' % file_path)
|
|
157
|
+
elif content.startswith('oss://'):
|
|
158
|
+
return True, content
|
|
157
159
|
elif not content.startswith('http'):
|
|
160
|
+
content = os.path.expanduser(content)
|
|
158
161
|
if os.path.isfile(content):
|
|
159
162
|
file_url = OssUtils.upload(model=model,
|
|
160
163
|
file_path=content,
|
dashscope/version.py
CHANGED