diff --git a/README.md b/README.md index 6190b4e..02b6ab4 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,3 @@ -
- English -
-
-
-
-
-
# DashScope Python SDK
The DashScope Python SDK provides a comprehensive interface to [Alibaba Cloud Model Studio (Bailian)](https://www.alibabacloud.com/help/en/model-studio/) APIs, covering text generation, multi-modal understanding, embeddings, reranking, image/video generation, speech synthesis & recognition, and more.
diff --git a/dashscope/aigc/image_synthesis.py b/dashscope/aigc/image_synthesis.py
index 0491d7e..0409bfc 100644
--- a/dashscope/aigc/image_synthesis.py
+++ b/dashscope/aigc/image_synthesis.py
@@ -261,16 +261,16 @@ def _get_input( # pylint: disable=too-many-branches
kwargs["headers"] = headers
def __get_i2i_task(task, model) -> str:
- # 处理task参数:优先使用有效的task值
+ # Handle task parameter: prefer valid task value
if task is not None and task != "":
return task
- # 根据model确定任务类型
+ # Determine task type based on model
if model is not None and model != "":
if "imageedit" in model or "wan2.5-i2i" in model:
return "image2image"
- # 默认返回文本到图像任务
+ # Default to text-to-image task
return ImageSynthesis.task
task = __get_i2i_task(task, model)
diff --git a/dashscope/api_entities/aio_session.py b/dashscope/api_entities/aio_session.py
new file mode 100644
index 0000000..d2365db
--- /dev/null
+++ b/dashscope/api_entities/aio_session.py
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) Alibaba, Inc. and its affiliates.
+"""Shared aiohttp session pool with cached SSL context.
+
+Provides connection reuse across async API calls. Each event loop gets
+its own ClientSession (aiohttp sessions are loop-bound). The SSL context
+is created once and shared across all sessions.
+"""
+import asyncio
+import ssl
+import threading
+import weakref
+from typing import Optional
+
+import aiohttp
+import certifi
+
+_shared_ssl_context: Optional[ssl.SSLContext] = None
+_aio_sessions: "weakref.WeakKeyDictionary" = weakref.WeakKeyDictionary()
+_lock = threading.RLock()
+
+
+def get_ssl_context() -> ssl.SSLContext:
+ global _shared_ssl_context
+ with _lock:
+ if _shared_ssl_context is None:
+ _shared_ssl_context = ssl.create_default_context(
+ cafile=certifi.where(),
+ )
+ return _shared_ssl_context
+
+
+async def get_shared_aio_session() -> aiohttp.ClientSession:
+ """Return a shared aiohttp.ClientSession bound to the running event loop.
+
+ The session is lazily created on first use and reused for all
+ subsequent calls on the same event loop. Connection pooling (keep-alive)
+ is handled by the underlying TCPConnector.
+ """
+ loop = asyncio.get_running_loop()
+
+ with _lock:
+ session = _aio_sessions.get(loop)
+ if session is not None and not session.closed:
+ return session
+
+ connector = aiohttp.TCPConnector(ssl=get_ssl_context())
+ session = aiohttp.ClientSession(connector=connector)
+ _aio_sessions[loop] = session
+ return session
+
+
+async def close_shared_aio_session() -> None:
+ """Close the shared session for the current event loop."""
+ loop = asyncio.get_running_loop()
+ with _lock:
+ session = _aio_sessions.pop(loop, None)
+ if session is not None and not session.closed:
+ await session.close()
diff --git a/dashscope/api_entities/aiohttp_request.py b/dashscope/api_entities/aiohttp_request.py
index c89491e..598be25 100644
--- a/dashscope/api_entities/aiohttp_request.py
+++ b/dashscope/api_entities/aiohttp_request.py
@@ -6,6 +6,7 @@
import aiohttp
+from dashscope.api_entities.aio_session import get_shared_aio_session
from dashscope.api_entities.base_request import AioBaseRequest
from dashscope.api_entities.dashscope_response import DashScopeAPIResponse
from dashscope.common.constants import (
@@ -38,7 +39,9 @@ def __init__(
api_key (str): The api key.
method (str): The http method(GET|POST).
stream (bool, optional): Is stream request. Defaults to True.
- timeout (int, optional): Total request timeout.
+ timeout (int, optional): Request timeout in seconds. For streaming
+ requests, this is the idle timeout between chunks (sock_read);
+ for non-streaming requests, this is the total request timeout.
Defaults to DEFAULT_REQUEST_TIMEOUT_SECONDS.
user_agent (str, optional): Additional user agent string to
append. Defaults to ''.
@@ -245,43 +248,51 @@ async def _handle_response( # pylint: disable=too-many-branches
)
async def _handle_request(self):
- async with aiohttp.ClientSession(
- timeout=aiohttp.ClientTimeout(total=self.timeout),
- headers=self.headers,
- ) as session:
- logger.debug("Starting request: %s", self.url)
- if self.method == HTTPMethod.POST:
- is_form, obj = False, {}
- if hasattr(self, "data") and self.data is not None:
- is_form, obj = self.data.get_aiohttp_payload()
- if is_form:
- headers = {**self.headers, **obj.headers}
- response = await session.post(
- url=self.url,
- data=obj,
- headers=headers,
- )
- else:
- response = await session.request(
- "POST",
- url=self.url,
- json=obj,
- headers=self.headers,
- )
- elif self.method == HTTPMethod.GET:
- params = {}
- if hasattr(self, "data") and self.data is not None:
- params = getattr(self.data, "parameters", {})
- response = await session.get(
+ session = await get_shared_aio_session()
+ if self.stream:
+ request_timeout = aiohttp.ClientTimeout(
+ total=None,
+ sock_read=self.timeout,
+ )
+ else:
+ request_timeout = aiohttp.ClientTimeout(total=self.timeout)
+
+ logger.debug("Starting request: %s", self.url)
+ if self.method == HTTPMethod.POST:
+ is_form, obj = False, {}
+ if hasattr(self, "data") and self.data is not None:
+ is_form, obj = self.data.get_aiohttp_payload()
+ if is_form:
+ headers = {**self.headers, **obj.headers}
+ response = await session.post(
url=self.url,
- params=params,
- headers=self.headers,
+ data=obj,
+ headers=headers,
+ timeout=request_timeout,
)
else:
- raise UnsupportedHTTPMethod(
- f"Unsupported http method: {self.method}",
+ response = await session.request(
+ "POST",
+ url=self.url,
+ json=obj,
+ headers=self.headers,
+ timeout=request_timeout,
)
- logger.debug("Response returned: %s", self.url)
- async with response:
- async for rsp in self._handle_response(response):
- yield rsp
+ elif self.method == HTTPMethod.GET:
+ params = {}
+ if hasattr(self, "data") and self.data is not None:
+ params = getattr(self.data, "parameters", {})
+ response = await session.get(
+ url=self.url,
+ params=params,
+ headers=self.headers,
+ timeout=request_timeout,
+ )
+ else:
+ raise UnsupportedHTTPMethod(
+ f"Unsupported http method: {self.method}",
+ )
+ logger.debug("Response returned: %s", self.url)
+ async with response:
+ async for rsp in self._handle_response(response):
+ yield rsp
diff --git a/dashscope/api_entities/encryption.py b/dashscope/api_entities/encryption.py
index 266a8c4..59698e4 100644
--- a/dashscope/api_entities/encryption.py
+++ b/dashscope/api_entities/encryption.py
@@ -115,69 +115,69 @@ def _generate_iv():
@staticmethod
def _encrypt_text_with_aes(plaintext, key, iv):
- """使用AES-GCM加密数据"""
+ """Encrypt data with AES-GCM"""
- # 创建AES-GCM加密器
+ # Create AES-GCM encryptor
aes_gcm = Cipher(
algorithms.AES(key),
modes.GCM(iv, tag=None),
backend=default_backend(),
).encryptor()
- # 关联数据设为空(根据需求可调整)
+ # Set associated data to empty (adjustable as needed)
aes_gcm.authenticate_additional_data(b"")
- # 加密数据
+ # Encrypt data
ciphertext = (
aes_gcm.update(plaintext.encode("utf-8")) + aes_gcm.finalize()
)
- # 获取认证标签
+ # Get authentication tag
tag = aes_gcm.tag
- # 组合密文和标签
+ # Combine ciphertext and tag
encrypted_data = ciphertext + tag
- # 返回Base64编码结果
+ # Return Base64 encoded result
return base64.b64encode(encrypted_data).decode("utf-8")
@staticmethod
def _decrypt_text_with_aes(base64_ciphertext, aes_key, iv):
- """使用AES-GCM解密响应"""
+ """Decrypt response with AES-GCM"""
- # 解码Base64数据
+ # Decode Base64 data
encrypted_data = base64.b64decode(base64_ciphertext)
- # 分离密文和标签(标签长度16字节)
+ # Separate ciphertext and tag (tag length is 16 bytes)
ciphertext = encrypted_data[:-16]
tag = encrypted_data[-16:]
- # 创建AES-GCM解密器
+ # Create AES-GCM decryptor
aes_gcm = Cipher(
algorithms.AES(aes_key),
modes.GCM(iv, tag),
backend=default_backend(),
).decryptor()
- # 验证关联数据(与加密时一致)
+ # Verify associated data (same as during encryption)
aes_gcm.authenticate_additional_data(b"")
- # 解密数据
+ # Decrypt data
decrypted_bytes = aes_gcm.update(ciphertext) + aes_gcm.finalize()
- # 明文
+ # Plaintext
plaintext = decrypted_bytes.decode("utf-8")
return json.loads(plaintext)
@staticmethod
def _encrypt_aes_key_with_rsa(aes_key, public_key_str):
- """使用RSA公钥加密AES密钥"""
+ """Encrypt AES key with RSA public key"""
- # 解码Base64格式的公钥
+ # Decode Base64 formatted public key
public_key_bytes = base64.b64decode(public_key_str)
- # 加载公钥
+ # Load public key
public_key = serialization.load_der_public_key(
public_key_bytes,
backend=default_backend(),
@@ -185,7 +185,7 @@ def _encrypt_aes_key_with_rsa(aes_key, public_key_str):
base64_aes_key = base64.b64encode(aes_key).decode("utf-8")
- # 使用RSA加密
+ # Encrypt with RSA
encrypted_bytes = public_key.encrypt(
base64_aes_key.encode("utf-8"),
padding.PKCS1v15(),
diff --git a/dashscope/api_entities/http_request.py b/dashscope/api_entities/http_request.py
index e361503..3142328 100644
--- a/dashscope/api_entities/http_request.py
+++ b/dashscope/api_entities/http_request.py
@@ -2,14 +2,13 @@
# Copyright (c) Alibaba, Inc. and its affiliates.
import datetime
import json
-import ssl
from http import HTTPStatus
from typing import Optional, Dict, Union
import aiohttp
-import certifi
import requests
+from dashscope.api_entities.aio_session import get_shared_aio_session
from dashscope.api_entities.base_request import AioBaseRequest
from dashscope.api_entities.dashscope_response import DashScopeAPIResponse
from dashscope.common.constants import (
@@ -53,7 +52,9 @@ def __init__(
api_key (str): The api key.
method (str): The http method(GET|POST).
stream (bool, optional): Is stream request. Defaults to True.
- timeout (int, optional): Total request timeout.
+ timeout (int, optional): Request timeout in seconds. For streaming
+ requests, this is the idle timeout between chunks (sock_read);
+ for non-streaming requests, this is the total request timeout.
Defaults to DEFAULT_REQUEST_TIMEOUT_SECONDS.
user_agent (str, optional): Additional user agent string to
append. Defaults to ''.
@@ -161,67 +162,61 @@ async def aio_call(self):
async def _handle_aio_request(self): # pylint: disable=too-many-branches
# Use external aio_session if provided,
- # otherwise create temporary session
+ # otherwise use shared session with connection pooling
if self._external_aio_session is not None:
session = self._external_aio_session
- should_close = False
else:
- connector = aiohttp.TCPConnector(
- ssl=ssl.create_default_context(
- cafile=certifi.where(),
- ),
- )
- session = aiohttp.ClientSession(
- connector=connector,
- timeout=aiohttp.ClientTimeout(total=self.timeout),
- headers=self.headers,
+ session = await get_shared_aio_session()
+
+ if self.stream:
+ request_timeout = aiohttp.ClientTimeout(
+ total=None,
+ sock_read=self.timeout,
)
- should_close = True
+ else:
+ request_timeout = aiohttp.ClientTimeout(total=self.timeout)
- try:
- logger.debug("Starting request: %s", self.url)
- if self.method == HTTPMethod.POST:
- is_form, obj = False, {}
- if hasattr(self, "data") and self.data is not None:
- is_form, obj = self.data.get_aiohttp_payload()
- if is_form:
- headers = {**self.headers, **obj.headers}
- response = await session.post(
- url=self.url,
- data=obj,
- headers=headers,
- )
- else:
- response = await session.request(
- "POST",
- url=self.url,
- json=obj,
- headers=self.headers,
- )
- elif self.method == HTTPMethod.GET:
- # 添加条件判断
- params = {}
- if hasattr(self, "data") and self.data is not None:
- params = getattr(self.data, "parameters", {})
- if params:
- params = self.__handle_parameters(params)
- response = await session.get(
+ logger.debug("Starting request: %s", self.url)
+ if self.method == HTTPMethod.POST:
+ is_form, obj = False, {}
+ if hasattr(self, "data") and self.data is not None:
+ is_form, obj = self.data.get_aiohttp_payload()
+ if is_form:
+ headers = {**self.headers, **obj.headers}
+ response = await session.post(
url=self.url,
- params=params,
- headers=self.headers,
+ data=obj,
+ headers=headers,
+ timeout=request_timeout,
)
else:
- raise UnsupportedHTTPMethod(
- f"Unsupported http method: {self.method}",
+ response = await session.request(
+ "POST",
+ url=self.url,
+ json=obj,
+ headers=self.headers,
+ timeout=request_timeout,
)
- logger.debug("Response returned: %s", self.url)
- async with response:
- async for rsp in self._handle_aio_response(response):
- yield rsp
- finally:
- # Only close if we created the session
- if should_close:
- await session.close()
+ elif self.method == HTTPMethod.GET:
+ params = {}
+ if hasattr(self, "data") and self.data is not None:
+ params = getattr(self.data, "parameters", {})
+ if params:
+ params = self.__handle_parameters(params)
+ response = await session.get(
+ url=self.url,
+ params=params,
+ headers=self.headers,
+ timeout=request_timeout,
+ )
+ else:
+ raise UnsupportedHTTPMethod(
+ f"Unsupported http method: {self.method}",
+ )
+ logger.debug("Response returned: %s", self.url)
+ async with response:
+ async for rsp in self._handle_aio_response(response):
+ yield rsp
@staticmethod
def __handle_parameters(params: dict) -> dict:
diff --git a/dashscope/audio/asr/recognition.py b/dashscope/audio/asr/recognition.py
index f243d44..8b988f6 100644
--- a/dashscope/audio/asr/recognition.py
+++ b/dashscope/audio/asr/recognition.py
@@ -600,6 +600,6 @@ def get_last_package_delay(self):
"""Last Package Delay is the time between stop sending audio and receive last words package""" # noqa: E501 # pylint: disable=line-too-long
return self._on_complete_timestamp - self._stop_stream_timestamp
- # 获取上一个任务的taskId
+ # Get the requestId of the last task
def get_last_request_id(self):
return self.last_request_id
diff --git a/dashscope/audio/asr/translation_recognizer.py b/dashscope/audio/asr/translation_recognizer.py
index 83dcaa5..f677dd2 100644
--- a/dashscope/audio/asr/translation_recognizer.py
+++ b/dashscope/audio/asr/translation_recognizer.py
@@ -744,7 +744,7 @@ def get_last_package_delay(self):
"""Last Package Delay is the time between stop sending audio and receive last words package""" # noqa: E501 # pylint: disable=line-too-long
return self._on_complete_timestamp - self._stop_stream_timestamp
- # 获取上一个任务的taskId
+ # Get the taskId of the last task
def get_last_request_id(self):
return self.last_request_id
@@ -1087,6 +1087,6 @@ def get_last_package_delay(self):
"""Last Package Delay is the time between stop sending audio and receive last words package""" # noqa: E501 # pylint: disable=line-too-long
return self._on_complete_timestamp - self._stop_stream_timestamp
- # 获取上一个任务的taskId
+ # Get the taskId of the last task
def get_last_request_id(self):
return self.last_request_id
diff --git a/dashscope/audio/asr/vocabulary.py b/dashscope/audio/asr/vocabulary.py
index 1e96e28..8fd2ce8 100644
--- a/dashscope/audio/asr/vocabulary.py
+++ b/dashscope/audio/asr/vocabulary.py
@@ -86,11 +86,12 @@ def create_vocabulary(
vocabulary: List[dict],
) -> str:
"""
- 创建热词表
- param: target_model 热词表对应的语音识别模型版本
- param: prefix 热词表自定义前缀,仅允许数字和小写字母,小于十个字符。
- param: vocabulary 热词表字典
- return: 热词表标识符 vocabulary_id
+ Create a hot word table.
+ param: target_model ASR model version for the hot word table
+ param: prefix Custom hot word table prefix, only digits and
+ lowercase letters allowed, less than 10 characters.
+ param: vocabulary Hot word table dictionary
+ return: Hot word table identifier vocabulary_id
"""
# pylint: disable=no-value-for-parameter
response = self.__call_with_input(
@@ -119,11 +120,12 @@ def list_vocabularies(
page_size: int = 10,
) -> List[dict]:
"""
- 查询已创建的所有热词表
- param: prefix 自定义前缀,如果设定则只返回指定前缀的热词表标识符列表。
- param: page_index 查询的页索引
- param: page_size 查询页大小
- return: 热词表标识符列表
+ List all created hot word tables.
+ param: prefix Custom prefix, if set only returns hot word table
+ identifiers with the specified prefix.
+ param: page_index Page index for query
+ param: page_size Page size
+ return: List of hot word table identifiers
"""
if prefix:
# pylint: disable=no-value-for-parameter
@@ -157,9 +159,9 @@ def list_vocabularies(
def query_vocabulary(self, vocabulary_id: str) -> List[dict]:
"""
- 获取热词表内容
- param: vocabulary_id 热词表标识符
- return: 热词表
+ Get hot word table contents.
+ param: vocabulary_id Hot word table identifier
+ return: Hot word table
"""
# pylint: disable=no-value-for-parameter
response = self.__call_with_input(
@@ -185,9 +187,9 @@ def update_vocabulary(
vocabulary: List[dict],
) -> None:
"""
- 用新的热词表替换已有热词表
- param: vocabulary_id 需要替换的热词表标识符
- param: vocabulary 热词表
+ Replace existing hot word table with a new one.
+ param: vocabulary_id Hot word table identifier to replace
+ param: vocabulary Hot word table
"""
# pylint: disable=no-value-for-parameter
response = self.__call_with_input(
@@ -210,8 +212,8 @@ def update_vocabulary(
def delete_vocabulary(self, vocabulary_id: str) -> None:
"""
- 删除热词表
- param: vocabulary_id 需要删除的热词表标识符
+ Delete hot word table.
+ param: vocabulary_id Hot word table identifier to delete
"""
# pylint: disable=no-value-for-parameter
response = self.__call_with_input(
diff --git a/dashscope/audio/qwen_omni/omni_realtime.py b/dashscope/audio/qwen_omni/omni_realtime.py
index dc7cda6..9aeabdd 100644
--- a/dashscope/audio/qwen_omni/omni_realtime.py
+++ b/dashscope/audio/qwen_omni/omni_realtime.py
@@ -151,7 +151,7 @@ def __init__(
self.last_first_text_delay = None
self.last_first_audio_delay = None
self.metrics = []
- # 添加用于同步等待连接关闭的事件
+ # Add event for synchronously waiting on connection close
self.disconnect_event = None
def _generate_event_id(self):
@@ -189,13 +189,13 @@ def connect(self) -> None:
self.thread = threading.Thread(target=self.ws.run_forever)
self.thread.daemon = True
self.thread.start()
- timeout = 5 # 最长等待时间(秒)
+ timeout = 5 # max wait time in seconds
start_time = time.time()
while (
not (self.ws.sock and self.ws.sock.connected)
and (time.time() - start_time) < timeout
):
- time.sleep(0.1) # 短暂休眠,避免密集轮询
+ time.sleep(0.1) # Brief sleep to avoid busy polling
if not (self.ws.sock and self.ws.sock.connected):
raise TimeoutError(
"websocket connection could not established within 5s. "
@@ -376,7 +376,7 @@ def end_session_async(self) -> None:
"""
end session asynchronously. you need close the connection manually
"""
- # 发送结束会话消息
+ # Send end session message
self.__send_str(
json.dumps(
{
@@ -511,7 +511,7 @@ def close(self) -> None:
"""
self.ws.close()
- # 监听消息的回调函数
+ # Callback for listening to messages
def _on_message( # pylint: disable=unused-argument,too-many-branches
self,
ws,
@@ -523,7 +523,7 @@ def _on_message( # pylint: disable=unused-argument,too-many-branches
message[:1024],
)
try:
- # 尝试将消息解析为JSON
+ # Attempt to parse message as JSON
json_data = json.loads(message)
self.last_message = json_data
self.callback.on_event(json_data)
@@ -574,7 +574,7 @@ def _on_message( # pylint: disable=unused-argument,too-many-branches
# pylint: disable=broad-exception-raised,raise-missing-from
raise Exception("Failed to parse message as JSON.")
elif isinstance(message, (bytes, bytearray)):
- # 如果失败,认为是二进制消息
+ # If parsing fails, treat as binary message
logger.error(
"should not receive binary message in omni realtime api",
)
@@ -591,13 +591,13 @@ def _on_close( # pylint: disable=unused-argument
):
self.callback.on_close(close_status_code, close_msg)
- # WebSocket发生错误的回调函数
+ # Callback for WebSocket error
def _on_error(self, ws, error): # pylint: disable=unused-argument
# pylint: disable=broad-exception-raised
logger.error("websocket closed due to %s", error)
raise Exception(f"websocket closed due to {error}")
- # 获取上一个任务的taskId
+ # Get the taskId of the last task
def get_session_id(self) -> str:
return self.session_id # type: ignore[return-value]
diff --git a/dashscope/audio/qwen_tts_realtime/qwen_tts_realtime.py b/dashscope/audio/qwen_tts_realtime/qwen_tts_realtime.py
index 7142a66..415c098 100644
--- a/dashscope/audio/qwen_tts_realtime/qwen_tts_realtime.py
+++ b/dashscope/audio/qwen_tts_realtime/qwen_tts_realtime.py
@@ -141,13 +141,13 @@ def connect(self) -> None:
self.thread = threading.Thread(target=self.ws.run_forever)
self.thread.daemon = True
self.thread.start()
- timeout = 5 # 最长等待时间(秒)
+ timeout = 5 # max wait time in seconds
start_time = time.time()
while (
not (self.ws.sock and self.ws.sock.connected)
and (time.time() - start_time) < timeout
):
- time.sleep(0.1) # 短暂休眠,避免密集轮询
+ time.sleep(0.1) # Brief sleep to avoid busy polling
if not (self.ws.sock and self.ws.sock.connected):
raise TimeoutError(
"websocket connection could not established within 5s. "
@@ -215,14 +215,16 @@ def update_session(
"response_format": response_format.format,
"sample_rate": response_format.sample_rate,
}
- if sample_rate is not None: # 如果配置,则更新
+ if sample_rate is not None: # update if configured
self.config["sample_rate"] = sample_rate
if volume is not None:
self.config["volume"] = volume
if speech_rate is not None:
self.config["speech_rate"] = speech_rate
if audio_format is not None:
- self.config["response_format"] = audio_format # 如果配置,则更新
+ self.config[
+ "response_format"
+ ] = audio_format # update if configured
if pitch_rate is not None:
self.config["pitch_rate"] = pitch_rate
if bit_rate is not None:
@@ -332,7 +334,7 @@ def close(self) -> None:
"""
self.ws.close()
- # 监听消息的回调函数
+ # Callback for listening to messages
def on_message( # pylint: disable=unused-argument
self,
ws,
@@ -344,7 +346,7 @@ def on_message( # pylint: disable=unused-argument
message[:1024],
)
try:
- # 尝试将消息解析为JSON
+ # Attempt to parse message as JSON
json_data = json.loads(message)
self.last_message = json_data
self.callback.on_event(json_data)
@@ -372,7 +374,7 @@ def on_message( # pylint: disable=unused-argument
# pylint: disable=broad-exception-raised,raise-missing-from
raise Exception("Failed to parse message as JSON.")
elif isinstance(message, (bytes, bytearray)):
- # 如果失败,认为是二进制消息
+ # If parsing fails, treat as binary message
logger.error(
"should not receive binary message in omni realtime api",
)
@@ -394,13 +396,13 @@ def on_close( # pylint: disable=unused-argument
)
self.callback.on_close(close_status_code, close_msg)
- # WebSocket发生错误的回调函数
+ # Callback for WebSocket error
def on_error(self, ws, error): # pylint: disable=unused-argument
print(f"websocket closed due to {error}")
# pylint: disable=broad-exception-raised
raise Exception(f"websocket closed due to {error}")
- # 获取上一个任务的taskId
+ # Get the taskId of the last task
def get_session_id(self):
return self.session_id
diff --git a/dashscope/audio/tts_v2/enrollment.py b/dashscope/audio/tts_v2/enrollment.py
index 2483862..9a728c3 100644
--- a/dashscope/audio/tts_v2/enrollment.py
+++ b/dashscope/audio/tts_v2/enrollment.py
@@ -92,13 +92,15 @@ def create_voice(
**kwargs,
) -> str:
"""
- 创建新克隆音色
- param: target_model 克隆音色对应的语音合成模型版本
- param: prefix 音色自定义前缀,仅允许数字和小写字母,小于十个字符。
- param: url 用于克隆的音频文件url
- param: language_hints 克隆音色目标语言
- param: max_prompt_audio_length 音频预处理输出的prompt audio最长长度。单位为秒。默认为10s。
- param: kwargs 额外参数
+ Create a new cloned voice.
+ param: target_model TTS model version for the cloned voice
+ param: prefix Custom voice prefix, only digits and lowercase
+ letters allowed, less than 10 characters.
+ param: url Audio file URL for voice cloning
+ param: language_hints Target language for the cloned voice
+ param: max_prompt_audio_length Max length of prompt audio output
+ from audio preprocessing, in seconds. Default is 10s.
+ param: kwargs Additional parameters
return: voice_id
"""
@@ -133,10 +135,11 @@ def list_voices(
page_size: int = 10,
) -> List[dict]:
"""
- 查询已创建的所有音色
- param: page_index 查询的页索引
- param: page_size 查询页大小
- return: List[dict] 音色列表,包含每个音色的id,创建时间,修改时间,状态。
+ List all created voices.
+ param: page_index Page index for query
+ param: page_size Page size
+ return: List[dict] Voice list, including id, creation time,
+ modification time, and status for each voice.
"""
if prefix:
# pylint: disable=no-value-for-parameter
@@ -170,9 +173,9 @@ def list_voices(
def query_voice(self, voice_id: str) -> List[str]:
"""
- 查询已创建的所有音色
- param: voice_id 需要查询的音色
- return: bytes 注册音色使用的音频
+ Query voice details.
+ param: voice_id Voice ID to query
+ return: bytes Audio used for voice registration
"""
# pylint: disable=no-value-for-parameter
response = self.__call_with_input(
@@ -194,9 +197,9 @@ def query_voice(self, voice_id: str) -> List[str]:
def update_voice(self, voice_id: str, url: str) -> None:
"""
- 更新音色
- param: voice_id 音色id
- param: url 用于克隆的音频文件url
+ Update voice.
+ param: voice_id Voice ID
+ param: url Audio file URL for cloning
"""
# pylint: disable=no-value-for-parameter
response = self.__call_with_input(
@@ -219,8 +222,8 @@ def update_voice(self, voice_id: str, url: str) -> None:
def delete_voice(self, voice_id: str) -> None:
"""
- 删除音色
- param: voice_id 需要删除的音色
+ Delete voice.
+ param: voice_id Voice ID to delete
"""
# pylint: disable=no-value-for-parameter
response = self.__call_with_input(
diff --git a/dashscope/audio/tts_v2/speech_synthesizer.py b/dashscope/audio/tts_v2/speech_synthesizer.py
index 5f5af14..4420456 100644
--- a/dashscope/audio/tts_v2/speech_synthesizer.py
+++ b/dashscope/audio/tts_v2/speech_synthesizer.py
@@ -167,7 +167,7 @@ def __init__( # pylint: disable=redefined-builtin
self.language_hints = language_hints
def gen_uid(self):
- # 生成随机UUID
+ # Generate random UUID
return uuid.uuid4().hex
def get_websocket_headers(self, headers, workspace):
@@ -403,13 +403,13 @@ def __connect(self, timeout_seconds=5) -> None:
self.thread = threading.Thread(target=self.ws.run_forever)
self.thread.daemon = True
self.thread.start()
- # 等待连接建立
+ # Wait for connection to be established
start_time = time.time()
while (
not (self.ws.sock and self.ws.sock.connected)
and (time.time() - start_time) < timeout_seconds
):
- time.sleep(0.1) # 短暂休眠,避免密集轮询
+ time.sleep(0.1) # Brief sleep to avoid busy polling
if not (self.ws.sock and self.ws.sock.connected):
raise TimeoutError(
"websocket connection could not established within 5s. "
@@ -540,10 +540,10 @@ def __start_stream(self):
if self._is_started:
raise InvalidTask("task has already started.")
- # 建立ws连接
+ # Establish WebSocket connection
if self.ws is None:
self.__connect(5)
- # 发送run-task指令
+ # Send run-task command
request = self.request.get_start_request(self.additional_params)
self.__send_str(request)
if not self.start_event.wait(10):
@@ -680,7 +680,7 @@ def streaming_cancel(self):
self.start_event.set()
self.complete_event.set()
- # 监听消息的回调函数
+ # Callback for listening to messages
def on_message( # pylint: disable=unused-argument,too-many-branches
self,
ws,
@@ -689,11 +689,11 @@ def on_message( # pylint: disable=unused-argument,too-many-branches
if isinstance(message, str):
logger.debug("<<