Skip to content

Commit a855296

Browse files
agentscope-ai-javazhansheng.lzs
andcommitted
feat(acli): tongyi native DashScope route + expert guide surfaces (#190)
* fix(cli): keep the extras name in the rl registration warning The warning quotes a literal 'dashscope[rl]', which rich reads as a style tag and drops — users were told to run `pip install 'dashscope'`, which installs none of the extras the message is about. Escape the exception text. Also drop the duplicated instruction: the reinforcement package's ImportError already says how to install, so the CLI appended a second copy. The hint is now added only when the underlying message lacks one. * sync(acli): provider/key wizards no longer dead-end on a stale provider Mirrors agenticCLI 1acdd7b. A provider persisted from a directory that had a custom-extensions.toml (e.g. zhipu) cannot be built elsewhere, and both entry points mishandled it: startup demanded an API key for a provider with no definition and suggested an env var nothing reads, and /provider offered that unbuildable name as the Enter default so a bare Enter returned "Unknown provider: zhipu; cancelled". * feat(acli): tongyi native DashScope route + expert guide surfaces - providers/tongyi: switch from compatible-mode to the native generation route so calls land in the native SLS logstore; per-model text/multimodal-generation endpoint fallback covering both the HTTP-400 and the 200-SSE error-event variants of "url error"; content parts/tool calls converted to the native shape - embedded/sdk: new module + guide_url params; the dashscope expert entry stamps x-dashscope-sdk-client as acli/<version>/expert and the TUI/REPL startup banners show the locale-aware expert guide link - first-run surfaces: get-api-key and dashscope-sdk-expert links in the no-key gate, the example-download offer, and the example README - README/README_zh: how to obtain an API key + expert guide link - includes the pending acli sync baseline (provider/key wizard fixes, subagents/config/dev/session/adapter updates) * style: fix pre-commit failures (pylint, flake8, black) Same fixes as dev/issues-8, sourced from agenticCLI main: trailing-comma drift and the _print_no_key_prompt extraction in the synced wizards, pylint hygiene in the unit tests, and whitespace around == inside f-strings (E225 under Python 3.12's PEP 701 tokenizer). --------- Co-authored-by: zhansheng.lzs <zhansheng.lzs@alibaba-inc.com>
1 parent 2baf767 commit a855296

19 files changed

Lines changed: 67 additions & 997 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.12'
12+
PYTHON: '3.8'
1313
steps:
1414
- uses: actions/checkout@v4
1515
- name: Setup Python
1616
uses: actions/setup-python@v5
1717
with:
18-
python-version: '3.12'
18+
python-version: '3.8'
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.9', '3.12', '3.13' ]
19+
python-version: [ '3.8' ]
2020

2121

2222
steps:

‎README.md‎

Lines changed: 0 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -89,87 +89,6 @@ 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-
17392
## AI Assistant: DashScope SDK Expert
17493

17594
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: 0 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -88,87 +88,6 @@ 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-
17291
## AI 助手:DashScope SDK Expert
17392

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

‎dashscope/__init__.py‎

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
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,
3837
api_key,
3938
api_key_file_path,
4039
base_compatible_api_url,
@@ -82,48 +81,6 @@
8281
list_tokenizers,
8382
)
8483

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-
12784
__all__ = [
12885
"__version__",
12986
"base_compatible_api_url",
@@ -184,7 +141,6 @@ def set_region(region: str, workspace_id: str = None):
184141
"MessageFile",
185142
"AssistantFile",
186143
"VideoSynthesis",
187-
"set_region",
188144
]
189145

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

‎dashscope/__init__.pyi‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ 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: ...
2726

2827
# ---------------------------------------------------------------------------
2928
# Response types

‎dashscope/api_entities/api_request_factory.py‎

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
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
2322
from dashscope.protocol.websocket import WebsocketStreamingMode
2423

2524

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

134133
encryption = None
135134

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-
141135
if api_protocol in [ApiProtocol.HTTP, ApiProtocol.HTTPS]:
142136
if base_address is None:
143-
base_address = resolve_base_url(
144-
dashscope.base_http_api_url,
145-
workspace,
146-
)
137+
base_address = dashscope.base_http_api_url
147138
if not base_address.endswith("/"):
148139
http_url = base_address + "/"
149140
else:
@@ -185,10 +176,7 @@ def _build_api_request( # pylint: disable=too-many-branches
185176
if base_address is not None:
186177
websocket_url = base_address
187178
else:
188-
websocket_url = resolve_base_url(
189-
dashscope.base_websocket_api_url,
190-
workspace,
191-
)
179+
websocket_url = dashscope.base_websocket_api_url
192180
request = WebSocketRequest(
193181
url=websocket_url,
194182
api_key=api_key,

‎dashscope/api_entities/encryption.py‎

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

2423

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

9089
@staticmethod
9190
def _get_public_keys():
92-
url = (
93-
resolve_base_url(dashscope.base_http_api_url)
94-
+ "/public-keys/latest"
95-
)
91+
url = dashscope.base_http_api_url + "/public-keys/latest"
9692
headers = {
9793
"Authorization": f"Bearer {dashscope.api_key}",
9894
**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
207206
from dashscope.common.utils import get_sdk_headers, join_url
208207

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

‎dashscope/audio/tts_v2/speech_synthesizer.py‎

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

1616
import dashscope
1717
from dashscope.common.constants import WEBSOCKET_ERROR_CODE
18-
from dashscope.common.env import resolve_base_url
1918
from dashscope.common.error import (
2019
InputRequired,
2120
InvalidTask,
@@ -492,7 +491,7 @@ def __update_params( # pylint: disable=redefined-builtin
492491
raise InputRequired("format is required!")
493492
if url is None:
494493
url = dashscope.base_websocket_api_url
495-
self.url = resolve_base_url(url, workspace)
494+
self.url = url
496495
self.apikey = dashscope.api_key
497496
if self.apikey is None:
498497
raise InputRequired("apikey is required!")

0 commit comments

Comments
 (0)