Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
8595b42
[http] cancel
Jun 8, 2026
13229a0
Fix DashScopeAPIResponse missing attribute handling
Jun 9, 2026
b3f331b
feat: add AioTextReRank async API
Jun 9, 2026
2676efd
fix: add timeout guard for async task polling
Jun 9, 2026
1514362
fix: support Windows file URI upload for multimodal inputs
Jun 9, 2026
842f110
fix: avoid passing default wait timeout to legacy wait methods
Jun 9, 2026
dbbe5b2
feat: add global trust_env config for aiohttp sessions
Jun 10, 2026
cfa1adb
fix: improve streaming interruption diagnostics
Jun 11, 2026
ed83a14
[log] request id
Jun 11, 2026
652f5e9
fix: improve qwen tts realtime websocket error handling
Jun 11, 2026
f3f0929
fix: handle local file URI hosts and string wait timeout
Jun 11, 2026
7f90814
fix: respect remaining wait timeout and reset SSE event type
Jun 11, 2026
d6de042
fix: preserve async task kwargs and tighten realtime connect timeout
Jun 11, 2026
82d726a
test: fix qwen tts realtime test lint issues
Jun 11, 2026
54dc3af
fix: handle aiohttp timeout as request failure
Jun 11, 2026
10b3fda
fix: avoid forwarding task kwarg to async wait
Jun 11, 2026
5ee8745
Revert "fix: avoid forwarding task kwarg to async wait"
Jun 12, 2026
f80a1b2
Revert "fix: handle aiohttp timeout as request failure"
Jun 12, 2026
1c1470d
Revert "test: fix qwen tts realtime test lint issues"
Jun 12, 2026
e1ae587
Revert "fix: preserve async task kwargs and tighten realtime connect …
Jun 12, 2026
2cf91df
Revert "fix: respect remaining wait timeout and reset SSE event type"
Jun 12, 2026
8cba414
Revert "fix: handle local file URI hosts and string wait timeout"
Jun 12, 2026
0324ff1
Revert "fix: improve qwen tts realtime websocket error handling"
Jun 12, 2026
44f334f
Revert "fix: improve streaming interruption diagnostics"
Jun 12, 2026
03a84e0
Revert "feat: add global trust_env config for aiohttp sessions"
Jun 12, 2026
6536dff
Revert "fix: add timeout guard for async task polling"
Jun 12, 2026
4795ed4
refactor(api): remove redundant exception handling in request modules
Jun 15, 2026
7a245df
fix(api): add defensive checks for self.data access in request modules
Jun 15, 2026
a48ca3c
Introduce shared aiohttp session pool for connection reuse
Jun 12, 2026
0e99336
Fix async streaming timeout: use sock_read instead of total
Jun 12, 2026
56f53e8
Fix session pool: remove broken atexit handler and clean stale entries
Jun 12, 2026
a33b55a
Bump version to 1.25.22 and translate all Chinese comments and docstr…
Jun 15, 2026
fdb4284
Remove redundant HTML header block from README
Jun 15, 2026
4c77007
Fix pre-commit lint issues
Jun 15, 2026
274a896
Fix leftover try block in async request handler from cherry-pick
Jun 15, 2026
e031f88
Add threading.RLock to session pool for thread safety
Jun 15, 2026
1c7f3cd
Merge pull request #139 from dashscope/reapply/aiohttp-fix
lzsweb Jun 15, 2026
0012d0b
refactor: add defensive checks for self.data access and improve error…
Jun 15, 2026
34f8cc9
refactor: add defensive checks for self.data access and improve error…
Jun 15, 2026
956fba3
refactor: add defensive checks for self.data access and improve error…
Jun 15, 2026
b092e11
refactor: restore thread safety for aio session and simplify exceptio…
Jun 15, 2026
13aee1a
fix aio_session
Jun 15, 2026
c84d268
feat: add external session support for AioHttpRequest and export clos…
Jun 16, 2026
54f65bf
Merge pull request #141 from dashscope/dev/repair
lzsweb Jun 16, 2026
7b249cf
feat(model/fun-asr): add payload.input support
songguocola Jun 15, 2026
97e5cff
feat(model/transcription): add timeout in sync wait api
songguocola Jun 15, 2026
791659f
Merge pull request #145 from songguocola/dev/audio_0618
lzsweb Jun 17, 2026
4e4b25f
feat: support env proxy and merge incremental stream output for Appli…
Jun 17, 2026
ff0cb75
Fix tiktoken StackOverflow on long text inputs
Jun 17, 2026
5070eed
Fix end_session() to handle server errors immediately
Jun 17, 2026
ee294c2
refactor: migrate incremental_to_full flag from headers to user_agent…
Jun 17, 2026
7a841b8
fix: resolve KeyError in ImageGeneration.async_call when headers is m…
Jun 18, 2026
dcff9cd
Fix proxy support and user_agent header extraction
Jun 18, 2026
01c0b57
Bump version to 1.25.23 and fix pre-commit lint issues
Jun 18, 2026
687834c
Fix pre-commit lint issues
Jun 18, 2026
01278db
Merge pull request #147 from dashscope/dev/issue-1
lzsweb Jun 18, 2026
d983305
fix: downgrade redundant logger.error(e) to debug in audio modules
Jun 18, 2026
cfb77f2
fix: use bare raise and correct exception types in audio modules
Jun 18, 2026
b2bc338
fix: correct exception types in transcription modules
Jun 18, 2026
a7edd37
Merge pull request #148 from dashscope/dev/issue-1
lzsweb Jun 18, 2026
309936a
Merge branch 'dashscope:main' into http
luozirong2025 Jun 22, 2026
dad233b
Update dashscope/finetune/reinforcement/component/server/server.py
luozirong2025 Jun 22, 2026
64c70aa
Update dashscope/finetune/reinforcement/common/utils.py
luozirong2025 Jun 22, 2026
cbbf03f
Update dashscope/finetune/reinforcement/component/server/server.py
luozirong2025 Jun 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
<h4 align="center">
<p>
<b>English</b>
<p>
</h4>


</div>

# 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.
Expand Down
5 changes: 4 additions & 1 deletion dashscope/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
HttpSpeechSynthesizer,
)
from dashscope.audio.tts.speech_synthesizer import SpeechSynthesizer
from dashscope.api_entities.aio_session import close_shared_aio_session
from dashscope.common.api_key import save_api_key
from dashscope.common.env import (
api_key,
Expand All @@ -46,7 +47,7 @@
from dashscope.files import Files
from dashscope.models import Models
from dashscope.nlp.understanding import Understanding
from dashscope.rerank.text_rerank import TextReRank
from dashscope.rerank import AioTextReRank, TextReRank
from dashscope.threads import (
MessageFile,
Messages,
Expand Down Expand Up @@ -75,6 +76,7 @@
"api_key",
"api_key_file_path",
"save_api_key",
"close_shared_aio_session",
"AioGeneration",
"Conversation",
"Generation",
Expand Down Expand Up @@ -106,6 +108,7 @@
"list_tokenizers",
"Application",
"TextReRank",
"AioTextReRank",
"Assistants",
"Threads",
"Messages",
Expand Down
20 changes: 12 additions & 8 deletions dashscope/aigc/generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,13 @@ def call( # pylint: disable=arguments-renamed,too-many-branches,too-many-statem
to_merge_incremental_output = True
parameters["incremental_output"] = True

# Pass incremental_to_full flag via headers user-agent
if "headers" not in parameters:
parameters["headers"] = {}
# Pass incremental_to_full flag via user_agent parameter
flag = "1" if to_merge_incremental_output else "0"
parameters["headers"]["user-agent"] = f"incremental_to_full/{flag}"
existing_ua = parameters.get("user_agent", "")
new_ua = f"incremental_to_full/{flag}"
parameters["user_agent"] = (
f"{existing_ua}; {new_ua}".strip() if existing_ua else new_ua
)

response = super().call(
model=model,
Expand Down Expand Up @@ -434,11 +436,13 @@ async def call( # type: ignore[override] # pylint: disable=arguments-renamed,to
to_merge_incremental_output = True
parameters["incremental_output"] = True

# Pass incremental_to_full flag via headers user-agent
if "headers" not in parameters:
parameters["headers"] = {}
# Pass incremental_to_full flag via user_agent parameter
flag = "1" if to_merge_incremental_output else "0"
parameters["headers"]["user-agent"] = f"incremental_to_full/{flag}"
existing_ua = parameters.get("user_agent", "")
new_ua = f"incremental_to_full/{flag}"
parameters["user_agent"] = (
f"{existing_ua}; {new_ua}".strip() if existing_ua else new_ua
)

response = await super().call(
model=model,
Expand Down
46 changes: 32 additions & 14 deletions dashscope/aigc/image_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,15 @@ def call( # type: ignore[override]
to_merge_incremental_output = True
kwargs["incremental_output"] = True

# Pass incremental_to_full flag via headers user-agent
if "headers" not in kwargs:
kwargs["headers"] = {}

# Pass incremental_to_full flag via user_agent parameter
flag = "1" if to_merge_incremental_output else "0"
kwargs["headers"]["user-agent"] = f"incremental_to_full/{flag}"
existing_ua = kwargs.get("user_agent", "")
new_ua = f"incremental_to_full/{flag}"
kwargs["user_agent"] = (
f"{existing_ua}; {new_ua}".strip() if existing_ua else new_ua
)
if kwargs.get("is_async", False):
kwargs["headers"]["X-DashScope-Async"] = "enable"
kwargs.setdefault("headers", {})["X-DashScope-Async"] = "enable"
task = cls.async_task
else:
task = cls.sync_task
Expand Down Expand Up @@ -177,6 +178,7 @@ def wait(
task: Union[str, ImageGenerationResponse], # type: ignore[override]
api_key: str = None,
workspace: str = None,
wait_timeout: int = -1,
**kwargs,
) -> DashScopeAPIResponse:
"""Wait for image(s) synthesis task to complete, and return the result.
Expand All @@ -186,11 +188,18 @@ def wait(
ImageGenerationResponse return by async_call().
api_key (str, optional): The api api_key. Defaults to None.
workspace (str): The dashscope workspace id.
wait_timeout (int, optional): The maximum seconds to wait.
Default is -1 (no timeout).

Returns:
DashScopeAPIResponse: The task result.
"""
response = super().wait(task, api_key, workspace=workspace)
response = super().wait(
task,
api_key,
workspace=workspace,
wait_timeout=wait_timeout,
)
return ImageGenerationResponse.from_api_response(response)

@classmethod
Expand Down Expand Up @@ -408,14 +417,15 @@ async def call( # type: ignore[override]
to_merge_incremental_output = True
kwargs["incremental_output"] = True

# Pass incremental_to_full flag via headers user-agent
if "headers" not in kwargs:
kwargs["headers"] = {}

# Pass incremental_to_full flag via user_agent parameter
flag = "1" if to_merge_incremental_output else "0"
kwargs["headers"]["user-agent"] = f"incremental_to_full/{flag}"
existing_ua = kwargs.get("user_agent", "")
new_ua = f"incremental_to_full/{flag}"
kwargs["user_agent"] = (
f"{existing_ua}; {new_ua}".strip() if existing_ua else new_ua
)
if kwargs.get("is_async", False):
kwargs["headers"]["X-DashScope-Async"] = "enable"
kwargs.setdefault("headers", {})["X-DashScope-Async"] = "enable"
task = cls.async_task
else:
task = cls.sync_task
Expand Down Expand Up @@ -487,6 +497,7 @@ async def wait(
task: Union[str, ImageGenerationResponse], # type: ignore[override]
api_key: str = None,
workspace: str = None,
wait_timeout: int = -1,
**kwargs,
) -> DashScopeAPIResponse:
"""Wait for image(s) synthesis task to complete, and return the result.
Expand All @@ -496,11 +507,18 @@ async def wait(
ImageGenerationResponse return by async_call().
api_key (str, optional): The api api_key. Defaults to None.
workspace (str): The dashscope workspace id.
wait_timeout (int, optional): The maximum seconds to wait.
Default is -1 (no timeout).

Returns:
DashScopeAPIResponse: The task result.
"""
response = await super().wait(task, api_key, workspace=workspace)
response = await super().wait(
task,
api_key,
workspace=workspace,
wait_timeout=wait_timeout,
)
return ImageGenerationResponse.from_api_response(response)

@classmethod
Expand Down
24 changes: 19 additions & 5 deletions dashscope/aigc/image_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -387,6 +387,7 @@ def wait( # type: ignore[override]
task: Union[str, ImageSynthesisResponse],
api_key: str = None,
workspace: str = None,
**kwargs,
) -> ImageSynthesisResponse:
"""Wait for image(s) synthesis task to complete, and return the result.

Expand All @@ -399,7 +400,12 @@ def wait( # type: ignore[override]
Returns:
ImageSynthesisResponse: The task result.
"""
response = super().wait(task, api_key, workspace=workspace)
response = super().wait(
task,
api_key,
workspace=workspace,
**kwargs,
)
return ImageSynthesisResponse.from_api_response(response)

@classmethod
Expand Down Expand Up @@ -733,6 +739,7 @@ async def wait(
task: Union[str, ImageSynthesisResponse], # type: ignore[override]
api_key: str = None,
workspace: str = None,
wait_timeout: int = -1,
**kwargs,
) -> ImageSynthesisResponse:
"""Wait for image(s) synthesis task to complete, and return the result.
Expand All @@ -742,11 +749,18 @@ async def wait(
ImageSynthesisResponse return by async_call().
api_key (str, optional): The api api_key. Defaults to None.
workspace (str): The dashscope workspace id.
wait_timeout (int, optional): The maximum seconds to wait.
Default is -1 (no timeout).

Returns:
ImageSynthesisResponse: The task result.
"""
response = await super().wait(task, api_key, workspace=workspace)
response = await super().wait(
task,
api_key,
workspace=workspace,
wait_timeout=wait_timeout,
)
return ImageSynthesisResponse.from_api_response(response)

@classmethod
Expand Down
10 changes: 6 additions & 4 deletions dashscope/aigc/multimodal_conversation.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,13 @@ def call( # pylint: disable=arguments-renamed,too-many-branches,too-many-statem
to_merge_incremental_output = True
kwargs["incremental_output"] = True

# Pass incremental_to_full flag via headers user-agent
if "headers" not in kwargs:
kwargs["headers"] = {}
# Pass incremental_to_full flag via user_agent parameter
flag = "1" if to_merge_incremental_output else "0"
kwargs["headers"]["user-agent"] = f"incremental_to_full/{flag}"
existing_ua = kwargs.get("user_agent", "")
new_ua = f"incremental_to_full/{flag}"
kwargs["user_agent"] = (
f"{existing_ua}; {new_ua}".strip() if existing_ua else new_ua
)

response = super().call(
model=model,
Expand Down
18 changes: 16 additions & 2 deletions dashscope/aigc/video_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ def wait( # type: ignore[override]
task: Union[str, VideoSynthesisResponse],
api_key: str = None,
workspace: str = None,
**kwargs,
) -> VideoSynthesisResponse:
"""Wait for video synthesis task to complete, and return the result.

Expand All @@ -521,7 +522,12 @@ def wait( # type: ignore[override]
Returns:
VideoSynthesisResponse: The task result.
"""
response = super().wait(task, api_key, workspace=workspace)
response = super().wait(
task,
api_key,
workspace=workspace,
**kwargs,
)
return VideoSynthesisResponse.from_api_response(response)

@classmethod
Expand Down Expand Up @@ -888,6 +894,7 @@ async def wait(
task: Union[str, VideoSynthesisResponse], # type: ignore[override]
api_key: str = None,
workspace: str = None,
wait_timeout: int = -1,
**kwargs,
) -> VideoSynthesisResponse:
"""Wait for video synthesis task to complete, and return the result.
Expand All @@ -897,11 +904,18 @@ async def wait(
VideoSynthesisResponse return by async_call().
api_key (str, optional): The api api_key. Defaults to None.
workspace (str): The dashscope workspace id.
wait_timeout (int, optional): The maximum seconds to wait.
Default is -1 (no timeout).

Returns:
VideoSynthesisResponse: The task result.
"""
response = await super().wait(task, api_key, workspace=workspace)
response = await super().wait(
task,
api_key,
workspace=workspace,
wait_timeout=wait_timeout,
)
return VideoSynthesisResponse.from_api_response(response)

@classmethod
Expand Down
59 changes: 59 additions & 0 deletions dashscope/api_entities/aio_session.py
Original file line number Diff line number Diff line change
@@ -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, trust_env=True)
_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()
Loading