Skip to content

Commit b3747ab

Browse files
Merge pull request #189 from dashscope/dev/issues-8
issues 8
2 parents 5d0a6fa + d706c4a commit b3747ab

21 files changed

Lines changed: 999 additions & 69 deletions

‎.github/workflows/pre-commit.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ jobs:
99
fail-fast: true
1010
env:
1111
OS: ubuntu-latest
12-
PYTHON: '3.8'
12+
PYTHON: '3.12'
1313
steps:
1414
- uses: actions/checkout@v4
1515
- name: Setup Python
1616
uses: actions/setup-python@v5
1717
with:
18-
python-version: '3.8'
18+
python-version: '3.12'
1919
- name: Update setuptools and wheel
2020
run: |
2121
pip install setuptools==68.2.2 wheel==0.41.2

‎.github/workflows/unit_test.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
strategy:
1717
matrix:
1818
os: [ ubuntu-latest ]
19-
python-version: [ '3.8' ]
19+
python-version: [ '3.9', '3.12', '3.13' ]
2020

2121

2222
steps:

‎README.md‎

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,87 @@ 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+
> **API keys are region-specific.** Each region issues its own API keys (`sk-` prefix) in its Model Studio console, and keys cannot be mixed across regions — using a key from another region fails with `401`. Switch `api_key` together with the region.
123+
124+
Region-specific notes:
125+
126+
- WebSocket endpoints (`wss://.../api-ws/v1/inference`) are only served in `cn-beijing` and `ap-southeast-1`. `set_region` still sets `base_websocket_api_url` for every region, but WebSocket-based realtime APIs (realtime speech recognition/synthesis, multimodal dialog, etc.) are not available in the other regions.
127+
- `eu-central-1` / `ap-northeast-1`: the deployment scope (Global, or EU / Japan) is chosen when the workspace is created in the console, not per API call.
128+
- `us-east-1`: model names with the `-us` suffix (e.g. `qwen-plus-us`) restrict inference to the US; names without the suffix default to global inference.
129+
- Batch inference, model fine-tuning and application development are currently only available in `cn-beijing` and `ap-southeast-1`.
130+
131+
> `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.
132+
133+
### Using environment variables
134+
135+
You can also select the region without code:
136+
137+
```shell
138+
export DASHSCOPE_API_REGION='ap-southeast-1' # default: cn-beijing
139+
export DASHSCOPE_WORKSPACE_ID='ws-xxx123' # used to resolve the endpoint subdomain
140+
```
141+
142+
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:
143+
144+
| Environment variable | Overrides |
145+
|----------------------|-----------|
146+
| `DASHSCOPE_HTTP_BASE_URL` | HTTP endpoint (`dashscope.base_http_api_url`) |
147+
| `DASHSCOPE_WEBSOCKET_BASE_URL` | WebSocket endpoint (`dashscope.base_websocket_api_url`) |
148+
| `DASHSCOPE_COMPATIBLE_BASE_URL` | OpenAI-compatible endpoint (`dashscope.base_compatible_api_url`) |
149+
150+
`set_region` always builds workspace-exclusive endpoints. Some regions also offer shared domains without a workspace subdomain — `dashscope.aliyuncs.com` (Beijing), `dashscope-intl.aliyuncs.com` (Singapore) and `dashscope-us.aliyuncs.com` (US Virginia); use the override variables above to point at them.
151+
152+
### OpenAI-compatible chat completions
153+
154+
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.
155+
156+
```python
157+
import dashscope
158+
from dashscope.aigc.chat_completion import Completions
159+
160+
dashscope.set_region(region="cn-hongkong", workspace_id="ws-hk-789")
161+
162+
response = Completions.create(
163+
model="qwen-max",
164+
messages=[{"role": "user", "content": "Hello"}],
165+
api_key="YOUR-DASHSCOPE-API-KEY",
166+
stream=False, # set True to get a generator of ChatCompletionChunk
167+
)
168+
print(response)
169+
```
170+
171+
A complete runnable example is available in [`samples/set_region_example.py`](samples/set_region_example.py).
172+
92173
## AI Assistant: DashScope SDK Expert
93174

94175
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: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,87 @@ 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+
> **各地域 API Key 相互独立。**每个地域的 API Key(`sk-` 前缀)需在对应地域的百炼控制台创建,不可跨地域混用——使用其他地域的 Key 会返回 `401`。切换地域时请同步更换 `api_key`。
122+
123+
地域特殊说明:
124+
125+
- WebSocket 端点(`wss://.../api-ws/v1/inference`)目前仅 `cn-beijing` 与 `ap-southeast-1` 提供。`set_region` 对所有地域都会设置 `base_websocket_api_url`,但实时语音识别/合成、多模态对话等基于 WebSocket 的实时 API 在其他地域不可用。
126+
- `eu-central-1` / `ap-northeast-1`:部署范围(全球,或欧盟 / 日本)在控制台创建业务空间时选择,不在 API 调用层配置。
127+
- `us-east-1`:模型名带 `-us` 后缀(如 `qwen-plus-us`)限定美国境内推理;不带后缀默认全球推理。
128+
- 批量推理、模型调优、应用开发等高级功能目前仅 `cn-beijing` 与 `ap-southeast-1` 支持。
129+
130+
> `set_region` 修改的是进程级全局变量,因此在单进程同时访问多个区域时并非并发安全。建议在启动时调用一次,或在每次切换前重新调用。
131+
132+
### 使用环境变量
133+
134+
也可以不写代码,直接通过环境变量选择区域:
135+
136+
```shell
137+
export DASHSCOPE_API_REGION='ap-southeast-1' # 默认:cn-beijing
138+
export DASHSCOPE_WORKSPACE_ID='ws-xxx123' # 用于解析端点子域名
139+
```
140+
141+
当通过 `DASHSCOPE_API_REGION` 设置了 MaaS 区域时,SDK 会构造对应的区域端点,并把 `DASHSCOPE_WORKSPACE_ID` 代入其中。你也可以直接覆盖每一个 base URL:
142+
143+
| 环境变量 | 覆盖的对象 |
144+
|----------------------|-----------|
145+
| `DASHSCOPE_HTTP_BASE_URL` | HTTP 端点(`dashscope.base_http_api_url`) |
146+
| `DASHSCOPE_WEBSOCKET_BASE_URL` | WebSocket 端点(`dashscope.base_websocket_api_url`) |
147+
| `DASHSCOPE_COMPATIBLE_BASE_URL` | OpenAI-compatible 端点(`dashscope.base_compatible_api_url`) |
148+
149+
`set_region` 构造的始终是业务空间专属域名。部分地域还提供不含 workspace 子域名的共享域名——北京 `dashscope.aliyuncs.com`、新加坡 `dashscope-intl.aliyuncs.com`、美国 `dashscope-us.aliyuncs.com`,如需使用可通过上面的环境变量直接覆盖。
150+
151+
### OpenAI-compatible 对话补全
152+
153+
SDK 提供了 OpenAI-compatible 的对话补全入口,它会请求 `dashscope.base_compatible_api_url`(请求路径 `chat/completions`)——无需额外安装 `openai` 库,并自动跟随上面配置的区域。
154+
155+
```python
156+
import dashscope
157+
from dashscope.aigc.chat_completion import Completions
158+
159+
dashscope.set_region(region="cn-hongkong", workspace_id="ws-hk-789")
160+
161+
response = Completions.create(
162+
model="qwen-max",
163+
messages=[{"role": "user", "content": "你好"}],
164+
api_key="YOUR-DASHSCOPE-API-KEY",
165+
stream=False, # 设为 True 则返回 ChatCompletionChunk 生成器
166+
)
167+
print(response)
168+
```
169+
170+
完整可运行示例见 [`samples/set_region_example.py`](samples/set_region_example.py)。
171+
91172
## AI 助手:DashScope SDK Expert
92173

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

‎dashscope/__init__.py‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from dashscope.api_entities.http_request import close_shared_sync_session
3535
from dashscope.common.api_key import save_api_key
3636
from dashscope.common.env import (
37+
MAAS_REGIONS,
3738
api_key,
3839
api_key_file_path,
3940
base_compatible_api_url,
@@ -81,6 +82,48 @@
8182
list_tokenizers,
8283
)
8384

85+
86+
# cn-beijing defaults to the legacy dashscope.aliyuncs.com endpoints;
87+
# its MaaS URLs are only reachable via an explicit set_region() call.
88+
_MAAS_REGIONS = {*MAAS_REGIONS, "cn-beijing"}
89+
90+
91+
def set_region(region: str, workspace_id: str = None):
92+
"""Switch to a specific MaaS region.
93+
94+
Updates base_http_api_url, base_compatible_api_url and
95+
base_websocket_api_url to point to the MaaS endpoint for the
96+
given region and workspace.
97+
98+
Args:
99+
region (str): The MaaS region, e.g. "ap-southeast-1",
100+
"us-east-1", "cn-hongkong", "cn-beijing", "eu-central-1",
101+
"ap-northeast-1".
102+
workspace_id (str): The workspace ID, used as the subdomain
103+
of the MaaS endpoint.
104+
105+
Raises:
106+
ValueError: If region is not supported or workspace_id is
107+
empty.
108+
"""
109+
if region not in _MAAS_REGIONS:
110+
raise ValueError(
111+
f"Unsupported region '{region}'. "
112+
f"Supported regions: {sorted(_MAAS_REGIONS)}",
113+
)
114+
115+
if not workspace_id:
116+
raise ValueError("workspace_id is required")
117+
118+
global base_http_api_url, base_compatible_api_url
119+
global base_websocket_api_url
120+
121+
host = f"{workspace_id}.{region}.maas.aliyuncs.com"
122+
base_http_api_url = f"https://{host}/api/v1"
123+
base_compatible_api_url = f"https://{host}/compatible-mode/v1"
124+
base_websocket_api_url = f"wss://{host}/api-ws/v1/inference"
125+
126+
84127
__all__ = [
85128
"__version__",
86129
"base_compatible_api_url",
@@ -141,6 +184,7 @@
141184
"MessageFile",
142185
"AssistantFile",
143186
"VideoSynthesis",
187+
"set_region",
144188
]
145189

146190
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/acli/__init__.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import uuid
55

6-
__version__ = "0.6.4"
6+
__version__ = "0.6.5"
77

88
# Per-process identifier sent as x-dashscope-sdk-session-id so the
99
# backend can group multi-turn requests from one CLI run.

‎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",

0 commit comments

Comments
 (0)