Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
30 changes: 26 additions & 4 deletions .github/workflows/unit_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,19 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ ubuntu-latest ]
python-version: [ '3.9', '3.10', '3.11', '3.12', '3.13' ]
# ubuntu-latest (26.04) ships no CPython 3.9 build, so pin the
# still-supported 3.9 leg to 24.04 and keep the rest on latest.
include:
- os: ubuntu-24.04
python-version: '3.9'
- os: ubuntu-latest
python-version: '3.10'
- os: ubuntu-latest
python-version: '3.11'
- os: ubuntu-latest
python-version: '3.12'
- os: ubuntu-latest
python-version: '3.13'

steps:
- name: Checkout repository
Expand Down Expand Up @@ -68,8 +79,19 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ ubuntu-latest ]
python-version: [ '3.9', '3.10', '3.11', '3.12', '3.13' ]
# ubuntu-latest (26.04) ships no CPython 3.9 build, so pin the
# still-supported 3.9 leg to 24.04 and keep the rest on latest.
include:
- os: ubuntu-24.04
python-version: '3.9'
- os: ubuntu-latest
python-version: '3.10'
- os: ubuntu-latest
python-version: '3.11'
- os: ubuntu-latest
python-version: '3.12'
- os: ubuntu-latest
python-version: '3.13'

steps:
- name: Checkout repository
Expand Down
27 changes: 26 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,26 @@ The SDK ships with an interactive AI assistant, **DashScope SDK Expert**, built
- Install with the `acli` extra: `pip install "dashscope[acli]"`. Add `acli-voice` / `acli-camera` for voice and camera input, or `acli-all` for everything — see the [extras table](#installation).
- Full walkthrough: [DashScope SDK Expert guide](https://help.aliyun.com/en/model-studio/dashscope-sdk-expert).

## Command line

`pip install -U dashscope` puts two independent commands on your `PATH`. `dashscope` serves the SDK-shaped **2.0** command tree; `dashscope1` serves the frozen **1.x** implementation:

| Entry point | Implementation | Examples |
|-------------|----------------|----------|
| `dashscope` | 2.0, following the SDK resource tree | `chat create`, `apps completions create`, `multimodal chat create`, `rl jobs create`, `rl functions register`, `images jobs create`, `audio asr transcribe`, `agentstudio sessions events send` |
| `dashscope1` | 1.x, using the 1.x SDK | `generation call`, `application call`, `image-synthesis create`, `multimodal-conversation call`, `fine-tunes call`, `agentic-rl run`, `auth login`, `oss upload` |

The two CLI trees do not dispatch to or normalize arguments for each other. Running a 1.x-only command through `dashscope` exits with a usage error and points to `dashscope1`. Their module entry points are likewise independent:

```bash
python -m dashscope.cli apps completions create ...
python -m dashscope.legacy_1x.cli generation call ...
```

Running `dashscope` with no arguments starts the [AI assistant](#ai-assistant-dashscope-sdk-expert) instead of the CLI.

For the complete 2.0 CLI command map, output formats, exit codes, configuration precedence and CI recipes, see the CLI section in [`examples/README.md`](examples/README.md).

## Shell completion

Run the command for your shell once, then restart the shell (or re-source your config):
Expand All @@ -453,6 +473,8 @@ Preview the script without installing:
dashscope --show-completion bash
```

Completion is installed through the 2.0 `dashscope` entry point.

## Logging

Set the level via settings or the environment:
Expand All @@ -475,10 +497,11 @@ examples/
├── 02_advanced_engineering/ Mock debugging, semantic cache, circuit breaker + multi-region DR
├── 03_agent_recipes/ Tool-call loop, RAG, MCP server, AgentStudio
├── 04_real_world_scenarios/ End-to-end production harnesses (RAG, realtime meetings, customer service)
├── 05_cli/ Offline CLI output/error and subprocess CI harnesses
└── legacy_1x/ 1.x module-level API examples, for reference
```

See [`examples/README.md`](examples/README.md). Several flagship cases run fully offline (mock transports) with no API key.
See [`examples/README.md`](examples/README.md) for the full guide, including the complete CLI command map and shell/CI recipes. Several flagship cases and both CLI harnesses run fully offline with no API key.

## Migrating from 1.x

Expand All @@ -496,6 +519,8 @@ The 1.x module-level API is unchanged and still supported — you can adopt 2.0
| `dashscope.set_region(region, workspace_id)` | `dashscope.Client(region=..., workspace=...)` |
| OpenAI-compatible via `dashscope.aigc.chat_completion.Completions` | `dashscope.Client(protocol="openai")` |

The CLI follows the same split — see [Command line](#command-line) above.

### Package layout and compatibility

The 2.0 implementation sits directly under `dashscope`; the former `dashscope.v2` namespace is gone. AgentStudio and FineTune/Agentic RL each have a single business core under `dashscope.applications`, with two thin public facades on top:
Expand Down
27 changes: 26 additions & 1 deletion README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,26 @@ SDK 内置了交互式 AI 助手 **DashScope SDK Expert**,基于随包提供
- 通过 `acli` extra 安装:`pip install "dashscope[acli]"`。需要语音/摄像头输入可加装 `acli-voice` / `acli-camera`,或直接用 `acli-all` 装齐 —— 详见[安装](#安装)一节的 extra 表格。
- 完整使用指南:[DashScope SDK Expert 文档](https://help.aliyun.com/zh/model-studio/dashscope-sdk-expert)。

## 命令行

`pip install -U dashscope` 会安装两个相互独立的命令。`dashscope` 提供按 SDK 资源树设计的 **2.0** 命令;`dashscope1` 提供冻结的 **1.x** 实现:

| 入口 | 实现 | 示例 |
|------|------|------|
| `dashscope` | 2.0,对齐 SDK 资源树 | `chat create`、`apps completions create`、`multimodal chat create`、`rl jobs create`、`rl functions register`、`images jobs create`、`audio asr transcribe`、`agentstudio sessions events send` |
| `dashscope1` | 1.x,调用 1.x SDK | `generation call`、`application call`、`image-synthesis create`、`multimodal-conversation call`、`fine-tunes call`、`agentic-rl run`、`auth login`、`oss upload` |

两套 CLI 不会相互分发命令或转换参数。通过 `dashscope` 运行 1.x 专有命令会返回用法错误,并提示改用 `dashscope1`。两者的模块入口也相互独立:

```bash
python -m dashscope.cli apps completions create ...
python -m dashscope.legacy_1x.cli generation call ...
```

不带参数运行 `dashscope` 会启动 [AI 助手](#ai-助手dashscope-sdk-expert),而不是 CLI。

完整的 2.0 CLI 命令地图、输出格式、退出码、配置优先级和 CI 配方,见 [`examples/README_zh.md`](examples/README_zh.md) 的命令行章节。

## Shell 命令补全

针对你的 Shell 运行一次对应命令,然后重启 Shell(或重新 source 配置文件):
Expand All @@ -453,6 +473,8 @@ SDK 内置了交互式 AI 助手 **DashScope SDK Expert**,基于随包提供
dashscope --show-completion bash
```

命令补全通过 2.0 的 `dashscope` 入口安装。

## 日志

通过设置或环境变量配置日志级别:
Expand All @@ -475,10 +497,11 @@ examples/
├── 02_advanced_engineering/ 本地 mock 调试、语义缓存、熔断 + 多地域容灾
├── 03_agent_recipes/ 工具调用循环、RAG、MCP server、AgentStudio
├── 04_real_world_scenarios/ 端到端生产级 harness(RAG、实时会议、智能客服)
├── 05_cli/ 离线 CLI 输出/错误契约与子进程 CI 示例
└── legacy_1x/ 1.x 模块级 API 示例,仅供参考
```

详见 [`examples/README_zh.md`](examples/README_zh.md)。其中若干旗舰案例可完全离线运行(mock 传输),无需 API Key。
完整指南见 [`examples/README_zh.md`](examples/README_zh.md),其中包含 CLI 命令地图和 shell/CI 配方。若干旗舰案例与两个 CLI 示例都可完全离线运行,无需 API Key。

## 从 1.x 迁移

Expand All @@ -496,6 +519,8 @@ examples/
| `dashscope.set_region(region, workspace_id)` | `dashscope.Client(region=..., workspace=...)` |
| OpenAI 兼容通过 `dashscope.aigc.chat_completion.Completions` | `dashscope.Client(protocol="openai")` |

CLI 也遵循同样的拆分 —— 见上方[命令行](#命令行)一节。

### 包布局与兼容策略

2.0 实现直接位于 `dashscope` 根包,原 `dashscope.v2` 命名空间已移除。
Expand Down
1 change: 0 additions & 1 deletion dashscope/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@ def set_region(region: str, workspace_id: str = None):
"applications",
"assistants",
"audio",
"cli",
"common",
"embeddings",
"files",
Expand Down
2 changes: 1 addition & 1 deletion dashscope/_shared/_sdk_ident.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
(``dashscope/<version>; python/...; platform/...; processor/...``) and is
deliberately not shared with the 2.0 transport's own
``dashscope.transport.http.default_user_agent()``
(``dashscope-v2-python/<version>``). Those are two distinct backend
(``dashscope-sdk-python/<version>``). Those are two distinct backend
contracts, not two spellings of one value, so they must not be merged.

Only the stdlib is imported at module level; ``dashscope.version`` is
Expand Down
143 changes: 143 additions & 0 deletions dashscope/acli/expert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# -*- coding: utf-8 -*-
"""Shared DashScope SDK Expert adapter for both console entry points."""

import sys
from pathlib import Path

from rich.console import Console

import dashscope

err_console = Console(stderr=True)

_MD_MARKER = "<!-- managed-by: dashscope-expert -->"
_PY_MARKER = "# managed-by: dashscope-expert"
_EXPERT_EXAMPLE = "dashscope-sdk-expert"


def _cleanup_legacy_expert_sync():
"""Remove files the deleted expert bundling synced into ~/.acli.

Only files carrying the managed marker are deleted — without it the file
is user-owned and left untouched. Needed so stale global copies don't
shadow the workspace ./.acli/ configs installed via example download.
"""
try:
from dashscope.acli.config import CONFIG_DIR
except ImportError:
return
md_targets = []
for subdir in ("skills", "references"):
target_dir = CONFIG_DIR / subdir
if target_dir.is_dir():
md_targets.extend(target_dir.glob("*.md"))
for path in md_targets:
try:
if _MD_MARKER in path.read_text(encoding="utf-8"):
path.unlink()
except OSError:
pass
build = CONFIG_DIR / "build_sdk_index.py"
try:
if build.is_file() and _PY_MARKER in build.read_text(encoding="utf-8"):
build.unlink()
except OSError:
pass


def _maybe_offer_example_download():
"""First interactive run without ./.acli: offer the SDK-expert example.

Accepting merges the example into ./.acli/ via the same code path as
`example download` (copied from the packaged install dir when present);
declining writes a marker into ./.acli/ so we never ask again here.
"""
if not sys.stdin.isatty() or (Path.cwd() / ".acli").is_dir():
return
try:
from dashscope.acli.cli.examples import _handle_example_command
from dashscope.acli.cli.handlers_key import _GUIDE_DOC, _doc_locale
except ImportError:
return
err_console.print(
"[cyan]First run:[/cyan] download the dashscope-sdk-expert "
"example config into ./.acli/\n"
"(SDK Q&A expert persona + skill templates + SDK knowledge "
"index; pure config, editable anytime).",
)
err_console.print(
f"[dim]Guide: {_GUIDE_DOC.format(_doc_locale())}[/dim]",
)
try:
answer = input("Download the example config? [Y/n] ").strip().lower()
except (EOFError, KeyboardInterrupt):
err_console.print()
return
if answer in ("n", "no"):
try:
marker = Path.cwd() / ".acli" / ".dashscope-example-declined"
marker.parent.mkdir(parents=True, exist_ok=True)
marker.write_text("declined\n", encoding="utf-8")
except OSError:
pass
err_console.print(
"[dim]Skipped. To customize later, run: "
"python -m dashscope.acli example download "
"dashscope-sdk-expert[/dim]",
)
return
_handle_example_command(["download", _EXPERT_EXAMPLE])


def _route_to_expert(command, tui=None):
"""Run the vendored acli agent (bare ``dashscope`` or ``dashscope expert``).

``command`` is a one-shot prompt, or None for the interactive REPL.
"""
try:
from dashscope.acli.cli.handlers_key import _GUIDE_DOC, _doc_locale
from dashscope.acli.ui.embedded import run
except ImportError as exception:
err_console.print(
"[yellow]Tip:[/yellow] Install dashscope[acli] to enable "
"the agent "
f"({exception}).",
)
return

if command is None and not sys.stdin.isatty():
# The interactive REPL needs a real terminal; prompt_toolkit crashes
# on non-tty stdin (e.g. kqueue rejects /dev/null with EINVAL).
# Treat piped text as a one-shot prompt instead.
try:
piped = sys.stdin.read().strip()
except OSError:
piped = ""
if not piped:
err_console.print(
"[red]Error:[/red] Interactive mode requires a terminal. "
'Pass a prompt instead, e.g. dashscope expert "your question".',
)
sys.exit(2)
command = piped

_cleanup_legacy_expert_sync()
if command is None:
_maybe_offer_example_download()

try:
run(
app_name="DashScope SDK Expert",
prompt_symbol="dashscope> ",
api_key=dashscope.api_key or None,
command=command,
tui=tui,
module="expert",
guide_url=_GUIDE_DOC.format(_doc_locale()),
)
except SystemExit:
pass
except Exception as exception: # pylint: disable=broad-except
err_console.print(
f"[yellow]Tip:[/yellow] Agent failed to start ({exception}).",
)
13 changes: 13 additions & 0 deletions dashscope/cli/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
# Copyright (c) Alibaba, Inc. and its affiliates.
"""The dashscope CLI (v2).

Deliberately empty. ``import dashscope.cli`` runs for shell completion and
``--help``, so it must not pull in the router, Typer, or the SDK.

There is no ``main`` re-export here on purpose: :mod:`dashscope.cli.main`
is a submodule, and binding the same name on the package would make
``from dashscope.cli import main`` resolve to a function or a module
depending on import order. The console script names the module directly
(``dashscope.cli.main:main``).
"""
8 changes: 8 additions & 0 deletions dashscope/cli/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
# Copyright (c) Alibaba, Inc. and its affiliates.
"""``python -m dashscope.cli``."""

from dashscope.cli.main import main

if __name__ == "__main__":
main()
Loading
Loading