Skip to content

Commit fd02695

Browse files
author
luk384090-cloud
committed
feat(region): add multi-region endpoint and workspace support
- Introduce `set_region(region, workspace_id)` to switch HTTP, WebSocket, and OpenAI-compatible endpoints - Add support for MaaS international regions including Singapore, US East, Hong Kong, Germany, Japan, besides Beijing - Enable environment variable configuration for region and workspace ID - Implement runtime resolution of endpoint URLs with `{workspace_id}` placeholder substitution - Update internal API methods to support per-call workspace parameter for endpoint resolution - Enhance documentation with detailed usage examples of multi-region configuration - Add comprehensive unit tests covering region switching, URL resolving, and environment variable handling
1 parent 39c16c7 commit fd02695

14 files changed

Lines changed: 839 additions & 53 deletions

File tree

‎README.md‎

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,76 @@ save_api_key(api_key='YOUR-DASHSCOPE-API-KEY',
8989

9090
```
9191

92+
## Region and Endpoint Configuration
93+
94+
By default the SDK sends requests to the China (Beijing) public endpoint `dashscope.aliyuncs.com`. If your Model Studio (Bailian) workspace lives in another region, switch the endpoint before making calls.
95+
96+
### Using `set_region`
97+
98+
`dashscope.set_region(region, workspace_id)` points the HTTP, WebSocket and OpenAI-compatible base URLs at the given region in a single call. `workspace_id` is required and is used as the endpoint subdomain.
99+
100+
```python
101+
import dashscope
102+
103+
# Switch to the Singapore region for workspace "ws-xxx123"
104+
dashscope.set_region(region="ap-southeast-1", workspace_id="ws-xxx123")
105+
106+
# All subsequent calls use:
107+
# https://ws-xxx123.ap-southeast-1.maas.aliyuncs.com/api/v1
108+
print(dashscope.base_http_api_url)
109+
```
110+
111+
Supported regions:
112+
113+
| Region | Location |
114+
|--------|----------|
115+
| `cn-beijing` | China (Beijing) |
116+
| `cn-hongkong` | China (Hong Kong) |
117+
| `ap-southeast-1` | Singapore |
118+
| `ap-northeast-1` | Japan (Tokyo) |
119+
| `eu-central-1` | Germany (Frankfurt) |
120+
| `us-east-1` | US (Virginia) |
121+
122+
> `set_region` updates process-wide globals, so it is not concurrency-safe when a single process talks to multiple regions at the same time. Call it once at startup, or re-call it before each switch.
123+
124+
### Using environment variables
125+
126+
You can also select the region without code:
127+
128+
```shell
129+
export DASHSCOPE_API_REGION='ap-southeast-1' # default: cn-beijing
130+
export DASHSCOPE_WORKSPACE_ID='ws-xxx123' # used to resolve the endpoint subdomain
131+
```
132+
133+
When a MaaS region is set via `DASHSCOPE_API_REGION`, the SDK builds the regional endpoints and substitutes `DASHSCOPE_WORKSPACE_ID` into them. You can also override each base URL directly:
134+
135+
| Environment variable | Overrides |
136+
|----------------------|-----------|
137+
| `DASHSCOPE_HTTP_BASE_URL` | HTTP endpoint (`dashscope.base_http_api_url`) |
138+
| `DASHSCOPE_WEBSOCKET_BASE_URL` | WebSocket endpoint (`dashscope.base_websocket_api_url`) |
139+
| `DASHSCOPE_COMPATIBLE_BASE_URL` | OpenAI-compatible endpoint (`dashscope.base_compatible_api_url`) |
140+
141+
### OpenAI-compatible chat completions
142+
143+
The SDK exposes an OpenAI-compatible chat completions entry that talks to `dashscope.base_compatible_api_url` (request path `chat/completions`) — no extra `openai` package required. It follows the region configured above.
144+
145+
```python
146+
import dashscope
147+
from dashscope.aigc.chat_completion import Completions
148+
149+
dashscope.set_region(region="cn-hongkong", workspace_id="ws-hk-789")
150+
151+
response = Completions.create(
152+
model="qwen-max",
153+
messages=[{"role": "user", "content": "Hello"}],
154+
api_key="YOUR-DASHSCOPE-API-KEY",
155+
stream=False, # set True to get a generator of ChatCompletionChunk
156+
)
157+
print(response)
158+
```
159+
160+
A complete runnable example is available in [`samples/set_region_example.py`](samples/set_region_example.py).
161+
92162
## AI Assistant: DashScope SDK Expert
93163

94164
The SDK ships with an interactive AI assistant, **DashScope SDK Expert**, built on the bundled Agentic CLI (`dashscope/acli`) framework. For DashScope SDK/CLI users it is the recommended way to get development consultation and AI coding help — answering SDK/API questions, generating runnable examples, showing CLI usage, and diagnosing errors, right in your terminal.

‎README_zh.md‎

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,76 @@ save_api_key(api_key='YOUR-DASHSCOPE-API-KEY',
8888

8989
```
9090

91+
## 区域与端点配置
92+
93+
默认情况下,SDK 将请求发往华北2(北京)公共端点 `dashscope.aliyuncs.com`。如果你的百炼(Model Studio)业务空间位于其他区域,请在调用前先切换端点。
94+
95+
### 使用 `set_region`
96+
97+
`dashscope.set_region(region, workspace_id)` 会一次性把 HTTP、WebSocket 和 OpenAI-compatible 三个 base URL 指向指定区域。`workspace_id` 为必填项,会作为端点的子域名。
98+
99+
```python
100+
import dashscope
101+
102+
# 切换到新加坡区域,业务空间为 "ws-xxx123"
103+
dashscope.set_region(region="ap-southeast-1", workspace_id="ws-xxx123")
104+
105+
# 之后所有调用都会使用:
106+
# https://ws-xxx123.ap-southeast-1.maas.aliyuncs.com/api/v1
107+
print(dashscope.base_http_api_url)
108+
```
109+
110+
支持的区域:
111+
112+
| 区域 | 地理位置 |
113+
|--------|----------|
114+
| `cn-beijing` | 华北2(北京) |
115+
| `cn-hongkong` | 中国(香港) |
116+
| `ap-southeast-1` | 新加坡 |
117+
| `ap-northeast-1` | 日本(东京) |
118+
| `eu-central-1` | 德国(法兰克福) |
119+
| `us-east-1` | 美国(弗吉尼亚) |
120+
121+
> `set_region` 修改的是进程级全局变量,因此在单进程同时访问多个区域时并非并发安全。建议在启动时调用一次,或在每次切换前重新调用。
122+
123+
### 使用环境变量
124+
125+
也可以不写代码,直接通过环境变量选择区域:
126+
127+
```shell
128+
export DASHSCOPE_API_REGION='ap-southeast-1' # 默认:cn-beijing
129+
export DASHSCOPE_WORKSPACE_ID='ws-xxx123' # 用于解析端点子域名
130+
```
131+
132+
当通过 `DASHSCOPE_API_REGION` 设置了 MaaS 区域时,SDK 会构造对应的区域端点,并把 `DASHSCOPE_WORKSPACE_ID` 代入其中。你也可以直接覆盖每一个 base URL:
133+
134+
| 环境变量 | 覆盖的对象 |
135+
|----------------------|-----------|
136+
| `DASHSCOPE_HTTP_BASE_URL` | HTTP 端点(`dashscope.base_http_api_url`) |
137+
| `DASHSCOPE_WEBSOCKET_BASE_URL` | WebSocket 端点(`dashscope.base_websocket_api_url`) |
138+
| `DASHSCOPE_COMPATIBLE_BASE_URL` | OpenAI-compatible 端点(`dashscope.base_compatible_api_url`) |
139+
140+
### OpenAI-compatible 对话补全
141+
142+
SDK 提供了 OpenAI-compatible 的对话补全入口,它会请求 `dashscope.base_compatible_api_url`(请求路径 `chat/completions`)——无需额外安装 `openai` 库,并自动跟随上面配置的区域。
143+
144+
```python
145+
import dashscope
146+
from dashscope.aigc.chat_completion import Completions
147+
148+
dashscope.set_region(region="cn-hongkong", workspace_id="ws-hk-789")
149+
150+
response = Completions.create(
151+
model="qwen-max",
152+
messages=[{"role": "user", "content": "你好"}],
153+
api_key="YOUR-DASHSCOPE-API-KEY",
154+
stream=False, # 设为 True 则返回 ChatCompletionChunk 生成器
155+
)
156+
print(response)
157+
```
158+
159+
完整可运行示例见 [`samples/set_region_example.py`](samples/set_region_example.py)。
160+
91161
## AI 助手:DashScope SDK Expert
92162

93163
SDK 内置了交互式 AI 助手 **DashScope SDK Expert**,基于随包提供的 Agentic CLI(`dashscope/acli`)框架构建。对于 DashScope SDK/CLI 用户,它是获取开发咨询和 AI 编码帮助的推荐方式——直接在终端中解答 SDK/API 问题、生成可运行示例、展示 CLI 用法、诊断错误。

‎dashscope/__init__.py‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,54 @@
8181
list_tokenizers,
8282
)
8383

84+
85+
# Supported MaaS regions and their default base URLs
86+
_MAAS_REGIONS = {
87+
"cn-beijing",
88+
"ap-southeast-1",
89+
"us-east-1",
90+
"cn-hongkong",
91+
"eu-central-1",
92+
"ap-northeast-1",
93+
}
94+
95+
96+
def set_region(region: str, workspace_id: str = None):
97+
"""Switch to a specific MaaS region.
98+
99+
Updates base_http_api_url, base_compatible_api_url and
100+
base_websocket_api_url to point to the MaaS endpoint for the
101+
given region and workspace.
102+
103+
Args:
104+
region (str): The MaaS region, e.g. "ap-southeast-1",
105+
"us-east-1", "cn-hongkong", "cn-beijing", "eu-central-1",
106+
"ap-northeast-1".
107+
workspace_id (str): The workspace ID, used as the subdomain
108+
of the MaaS endpoint.
109+
110+
Raises:
111+
ValueError: If region is not supported or workspace_id is
112+
empty.
113+
"""
114+
if region not in _MAAS_REGIONS:
115+
raise ValueError(
116+
f"Unsupported region '{region}'. "
117+
f"Supported regions: {sorted(_MAAS_REGIONS)}",
118+
)
119+
120+
if not workspace_id:
121+
raise ValueError("workspace_id is required")
122+
123+
global base_http_api_url, base_compatible_api_url
124+
global base_websocket_api_url
125+
126+
host = f"{workspace_id}.{region}.maas.aliyuncs.com"
127+
base_http_api_url = f"https://{host}/api/v1"
128+
base_compatible_api_url = f"https://{host}/compatible-mode/v1"
129+
base_websocket_api_url = f"wss://{host}/api-ws/v1/inference"
130+
131+
84132
__all__ = [
85133
"__version__",
86134
"base_compatible_api_url",
@@ -141,6 +189,7 @@
141189
"MessageFile",
142190
"AssistantFile",
143191
"VideoSynthesis",
192+
"set_region",
144193
]
145194

146195
logging.getLogger(__name__).addHandler(NullHandler())

‎dashscope/__init__.pyi‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ base_websocket_api_url: str
2323
def save_api_key(api_key: str, file_path: Optional[str] = ...) -> None: ...
2424
def close_shared_aio_session() -> None: ...
2525
def close_shared_sync_session() -> None: ...
26+
def set_region(region: str, workspace_id: Optional[str] = ...) -> None: ...
2627

2728
# ---------------------------------------------------------------------------
2829
# Response types

‎dashscope/api_entities/api_request_factory.py‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from dashscope.common.error import InputDataRequired, UnsupportedApiProtocol
2020
from dashscope.common.logging import logger
2121
from dashscope.common.utils import get_sdk_headers
22+
from dashscope.common.env import resolve_base_url
2223
from dashscope.protocol.websocket import WebsocketStreamingMode
2324

2425

@@ -132,9 +133,17 @@ def _build_api_request( # pylint: disable=too-many-branches
132133

133134
encryption = None
134135

136+
# Resolve {workspace_id} placeholder for MaaS international regions
137+
workspace = kwargs.pop("workspace", None)
138+
if base_address is not None:
139+
base_address = resolve_base_url(base_address, workspace)
140+
135141
if api_protocol in [ApiProtocol.HTTP, ApiProtocol.HTTPS]:
136142
if base_address is None:
137-
base_address = dashscope.base_http_api_url
143+
base_address = resolve_base_url(
144+
dashscope.base_http_api_url,
145+
workspace,
146+
)
138147
if not base_address.endswith("/"):
139148
http_url = base_address + "/"
140149
else:
@@ -176,7 +185,10 @@ def _build_api_request( # pylint: disable=too-many-branches
176185
if base_address is not None:
177186
websocket_url = base_address
178187
else:
179-
websocket_url = dashscope.base_websocket_api_url
188+
websocket_url = resolve_base_url(
189+
dashscope.base_websocket_api_url,
190+
workspace,
191+
)
180192
request = WebSocketRequest(
181193
url=websocket_url,
182194
api_key=api_key,

‎dashscope/api_entities/encryption.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
DEFAULT_REQUEST_TIMEOUT_SECONDS,
1919
)
2020
from dashscope.common.logging import logger
21+
from dashscope.common.env import resolve_base_url
2122
from dashscope.common.utils import get_sdk_headers
2223

2324

@@ -88,7 +89,10 @@ def get_base64_iv_str(self):
8889

8990
@staticmethod
9091
def _get_public_keys():
91-
url = dashscope.base_http_api_url + "/public-keys/latest"
92+
url = (
93+
resolve_base_url(dashscope.base_http_api_url)
94+
+ "/public-keys/latest"
95+
)
9296
headers = {
9397
"Authorization": f"Bearer {dashscope.api_key}",
9498
**get_sdk_headers(module="utils"),

‎dashscope/audio/http_tts/http_speech_synthesizer.py‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -203,12 +203,12 @@ def _http_call(
203203

204204
# Get base URL
205205
import dashscope
206+
from dashscope.common.env import resolve_base_url
206207
from dashscope.common.utils import get_sdk_headers, join_url
207208

208-
if url:
209-
base_url = url
210-
else:
211-
base_url = dashscope.base_http_api_url
209+
if not url:
210+
url = dashscope.base_http_api_url
211+
base_url = resolve_base_url(url, workspace)
212212
url_for_call = join_url(
213213
base_url,
214214
"services/audio/tts/SpeechSynthesizer",

‎dashscope/audio/tts_v2/speech_synthesizer.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import dashscope
1717
from dashscope.common.constants import WEBSOCKET_ERROR_CODE
18+
from dashscope.common.env import resolve_base_url
1819
from dashscope.common.error import (
1920
InputRequired,
2021
InvalidTask,
@@ -491,7 +492,7 @@ def __update_params( # pylint: disable=redefined-builtin
491492
raise InputRequired("format is required!")
492493
if url is None:
493494
url = dashscope.base_websocket_api_url
494-
self.url = url
495+
self.url = resolve_base_url(url, workspace)
495496
self.apikey = dashscope.api_key
496497
if self.apikey is None:
497498
raise InputRequired("apikey is required!")

0 commit comments

Comments
 (0)