English | 中文
The DashScope Python SDK is the official client for Alibaba Cloud Model Studio (Bailian), covering text generation, multi-modal understanding, embeddings, reranking, image/video generation, speech synthesis & recognition, fine-tuning, agentic RL, applications and more.
Version 2.0 introduces a resource-style client — dashscope.Client() — modeled on the ergonomics of the OpenAI SDK: one client holds a shared connection pool, every capability hangs off it as a typed resource (client.chat, client.images, client.audio, …), responses are typed objects instead of raw dicts, and failures raise exceptions by default. The complete 1.x public surface remains available at its historical import paths and is implemented by facades under dashscope.legacy_1x; AgentStudio and Agentic RL share their large business cores with 2.0 without sharing public return or error contracts.
| 1.x | 2.0 | |
|---|---|---|
| Entry point | Module-level static classes: Generation.call(...) |
A resource-style client: client.chat.completions.create(...) |
| One-liner | — | dashscope.chat("qwen-plus", "Tell me a joke") |
| Response | Raw dict-like; check status_code == HTTPStatus.OK |
Typed object; read resp.output_text, resp.usage.total_tokens |
| Errors | Inspect resp.code / resp.message by hand |
Typed DashScopeError exceptions raised by default (evolving) |
| Streaming | Generator of raw frames | ChatStream with .text_stream, .reasoning_stream, .get_final_message() |
| Protocols | DashScope native + a separate compatible-mode entry | One client speaks native, openai, or anthropic via protocol= |
| Async | Separate Aio* classes |
dashscope.AsyncClient() mirrors the sync surface (except the duplex audio WebSocket sessions) |
| Connections | A fresh session per call | One keep-alive pool per client, shared across resources |
| Config | Module globals (dashscope.api_key, set_region) |
Layered: args → dashscope.settings → env → ~/.dashscope/config.toml |
pip install -U dashscopeThe base install covers SDK API calls, the dashscope CLI command, Agentic RL fine-tuning and the local tokenizer. Python 3.9+ is required. Optional feature groups configure the AI assistant provided by dashscope-agenticx; the assistant requires Python 3.10+:
| Extra | Provides | Install |
|---|---|---|
acli |
Interactive AI assistant (DashScope SDK Expert) | pip install "dashscope[acli]" |
acli-openai |
Run the assistant on an OpenAI-compatible model provider | pip install "dashscope[acli-openai]" |
acli-anthropic |
Run the assistant on an Anthropic model provider | pip install "dashscope[acli-anthropic]" |
acli-voice |
Voice input and spoken replies in the assistant | pip install "dashscope[acli-voice]" |
acli-camera |
Camera capture for image input in the assistant | pip install "dashscope[acli-camera]" |
acli-all |
All of the above (includes acli) |
pip install "dashscope[acli-all]" |
To install from a source checkout:
pip install -e .Set your API key (how to get one):
export DASHSCOPE_API_KEY="sk-xxx"One line — the fastest way to a completion:
import dashscope
completion = dashscope.chat("qwen-plus", "Tell me a joke")
print(completion.output_text)Client — the recommended form for anything real. One client, reused across calls:
import dashscope
client = dashscope.Client() # reads DASHSCOPE_API_KEY
resp = client.chat.completions.create(
model="qwen-plus",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Who are you?"},
],
)
print(resp.output_text)
print(resp.usage.total_tokens, "tokens |", resp.request_id)Streaming — stream=True returns a ChatStream:
stream = client.chat.completions.create(
model="qwen-plus",
messages=[{"role": "user", "content": "Write a haiku about the sea"}],
stream=True,
)
for text in stream.text_stream:
print(text, end="", flush=True)
# Or collapse the whole stream into one merged ChatCompletion (with usage):
# final = stream.get_final_message()Async — AsyncClient mirrors the sync surface:
import asyncio
import dashscope
async def main():
client = dashscope.AsyncClient()
resp = await client.chat.completions.create(
model="qwen-plus",
messages=[{"role": "user", "content": "Hello"}],
)
print(resp.output_text)
await client.aclose()
asyncio.run(main())dashscope.Client() (and dashscope.AsyncClient()) is the single entry point. It owns one keep-alive connection pool shared by every resource call, and exposes capabilities as attributes:
client = dashscope.Client(
api_key="sk-xxx", # default: DASHSCOPE_API_KEY
workspace="ws-xxx123", # default: DASHSCOPE_WORKSPACE
region="cn-beijing", # default: DASHSCOPE_REGION
protocol="native", # native | openai | anthropic
timeout=300.0,
max_retries=2,
base_url=None, # override the endpoint entirely; default: DASHSCOPE_BASE_URL
config_path=None, # alternate config.toml; default: DASHSCOPE_CONFIG
protocol_options=None, # extra kwargs forwarded to the selected protocol adapter
raise_on_error=True, # False: return error responses instead of raising
http_client=None, # your own httpx.Client (AsyncClient for AsyncClient)
)All options are keyword-only.
Use it as a context manager to close the pool automatically, or derive a variant with with_options() (unmentioned options are inherited; the connection pool is shared when connection-level options are unchanged):
with dashscope.Client() as client:
client.chat.completions.create(model="qwen-plus", messages=["hi"])
low_latency = client.with_options(timeout=10.0, max_retries=0)Any call also accepts per-request api_key=, workspace=, and timeout= overrides.
Need proxies, custom connection limits or event hooks? Pass your own http_client=httpx.Client(...) (httpx.AsyncClient for AsyncClient). The SDK still applies authentication, retries and absolute endpoint URLs on top of it, and never closes a client it does not own.
One client can speak three wire protocols, selected with protocol= (or DASHSCOPE_PROTOCOL / settings.protocol). The request/response objects stay the same canonical, typed shape regardless of protocol — only the wire format changes:
protocol |
Talks to | Use when |
|---|---|---|
native (default) |
DashScope native API (/api/v1) |
You want the full DashScope feature set |
openai |
OpenAI-compatible mode (/compatible-mode/v1) |
You're porting OpenAI code — no openai package needed |
anthropic |
Anthropic-compatible mode (/apps/anthropic) |
You're porting Anthropic Messages code |
client = dashscope.Client(protocol="openai")
resp = client.chat.completions.create(
model="qwen-plus",
messages=[{"role": "user", "content": "Hello"}],
)Every response is a typed object. For chat, read output_text for the first choice, or dig into choices, usage, search_info, request_id, and model:
resp = client.chat.completions.create(model="qwen-plus", messages=["hi"])
resp.output_text # choices[0].message.content
resp.choices[0].message.content # same, explicit
resp.choices[0].message.reasoning_content # thinking-model reasoning, if any
resp.choices[0].message.tool_calls # tool calls, if any
resp.usage.input_tokens # also .output_tokens / .total_tokens
resp.request_idAll responses share a common envelope: request_id, status_code, code, message, is_error, and raw headers.
List endpoints return a page object you can iterate and advance instead of looping page_no by hand:
page = client.models.list(page_no=1, page_size=10)
for model in page: # this page's items
...
page.total_count # catalog total, as reported by the gateway
page.has_next_page()
page.next_page() # None on the last page
for page in client.models.list(page_size=50).iter_pages(): # every page
...On AsyncClient use await page.next_page_async() and async for page in page.iter_pages_async(). Calling the synchronous traversal on an async-fetched page raises TypeError naming the async method, rather than handing back an un-awaited coroutine. client.files.list() and client.images.jobs.list() paginate the same way.
Serialize a page with model_dump(). Iterating a page yields its items, so dict(page) is not a way to read its fields.
models.list(provider=...) filters client-side: it narrows each page's models, while total_count stays the whole-catalog total. Pagination therefore walks the unfiltered catalog and the filter is applied to each page as it arrives.
Pass raw=True to get the response object itself, before any error check:
resp = client.models.list(raw=True)
resp.status_code
resp.headers # includes non-standard gateway headers
resp.request_id
resp.elapsed
resp.json() # decoded body
resp.parse(ModelList) # typed model, memoized per typeraw=True skips the error check, so a 4xx/5xx is returned to you instead of raising. raise_for_status() applies it after the fact.
parse(...) needs the model class, and the v2 resource models are not re-exported from the top-level package — import them from where they are defined:
from dashscope.resources.models import ModelList, Model
from dashscope.resources.files import FileList, File
from dashscope.resources._jobs import Job, JobList
from dashscope.resources.embeddings import EmbeddingResponseresp.parse(...) is the generic envelope mapping and does not replay a resource's own reshaping — on models.list, a page the gateway delivered as a bare list parses through it as empty. Use resp.json() when you need exactly what the resource method would have built.
Every resource method that makes an HTTP request takes the same three escape hatches, for anything this SDK does not model yet:
client.chat.completions.create(
model="qwen-plus",
messages=["hi"],
extra_headers={"x-gateway-experiment": "on"},
extra_query={"debug": "1"},
extra_body={"parameters": {"top_k": 5}},
)extra_headers sits above every other layer — the SDK identity headers, the client defaults, your headers=, and a per-call api_key= / workspace= — matched case-insensitively. extra_body deep-merges nested dicts, so adding one nested field keeps its siblings; lists and scalars replace. A request whose body is not a JSON object (a file upload, raw bytes) has nothing to merge into, so extra_body is dropped with a warning rather than silently lost.
max_retries narrows or widens the retry budget for a single call, so a request that must not be replayed no longer needs a whole derived client:
client.deployments.list(max_retries=0) # one attempt, no retry
client.with_options(max_retries=5) # still works, client-wideStreaming methods take all four: open_stream retries only while no event has reached you yet, so a mid-stream failure is never replayed. The websocket methods (audio.asr.recognize / recognize_file, tingwu.realtime) take none of them — that transport has no per-request options channel, so a parameter there would be silently inert.
stream=True returns a ChatStream (AsyncChatStream for AsyncClient) with typed helpers:
stream = client.chat.completions.create(model="qwen-plus", messages=["hi"], stream=True)
for text in stream.text_stream: # content deltas
...
# stream.reasoning_stream # reasoning deltas (thinking models)
# stream.tool_call_stream # tool-call deltas
# stream.get_final_message() # merged ChatCompletion, incl. usageBy default each frame is incremental. Pass incremental_output=False to receive cumulative snapshots instead (the SDK merges deltas for you).
Thinking models surface reasoning on .reasoning_stream and, in the merged message, as message.reasoning_content. Under protocol="anthropic" the SDK also keeps message.reasoning_signature plus the raw thinking / redacted_thinking blocks in message.reasoning_blocks, so echoing the assistant message back on the next turn preserves extended thinking.
Note: streaming retries stop at the handshake. The SDK retries connection failures and retryable HTTP statuses only before the first event reaches you; once a stream is open it is never replayed, so a mid-stream drop surfaces as an exception. Wrap stream consumption in your own retry/backoff for production — see
examples/04_real_world_scenarios/production_ready_rag_harness.py.
Still evolving. The 2.0 typed error surface is under active development — new exception classes may be added and the API-error-code → exception mapping may be refined. Catch the base
errors.DashScopeErrorif you need to stay forward-compatible.
Failures raise a typed DashScopeError by default (both non-2xx HTTP statuses and HTTP-200-with-a-business-error land here):
from dashscope import errors
try:
client.chat.completions.create(model="qwen-plus", messages=["hi"])
except errors.RateLimitError as exc:
print("throttled:", exc.code, exc.status_code, exc.request_id)
except errors.ContentFilterError as exc:
print("blocked by moderation:", exc.message)
except errors.DashScopeError as exc: # base class for all SDK errors
print("request failed:", exc.code, exc.message)Everything derives from DashScopeError, in two families:
- Server errors — under
APIStatusError:BadRequestError,AuthenticationError,PermissionDeniedError,NotFoundError,RequestTooLargeError,RateLimitError,InternalServerError,ServiceUnavailableError,GatewayTimeoutError, andContentFilterError(aBadRequestErrorsubclass). - Client-side errors — under
SdkError:APIConnectionError/APITimeoutErrorfor network failures, plus the WebSocket familyWsEventError,WsStateError,WsQueueCapacityExceededErrorandWsBinaryOwnerConflictError. Each carries a stablesdk.*code.
A 200 response whose body does not parse into the expected envelope is reported as InternalServerError rather than leaking a decoder exception.
Prefer to inspect errors inline instead of catching? Build the client with raise_on_error=False — failures come back as a normal response object with is_error=True, code, message, and status_code:
client = dashscope.Client(raise_on_error=False)
resp = client.chat.completions.create(model="qwen-plus", messages=["hi"])
if resp.is_error:
print(resp.code, resp.message)The SDK authenticates with an API key. Get one from the Model Studio console. Provide it in any of these ways (highest priority first):
- Per client —
dashscope.Client(api_key="sk-xxx") - Global settings —
dashscope.settings.api_key = "sk-xxx"(applies to clients created afterwards) - Environment variable —
export DASHSCOPE_API_KEY="sk-xxx" - Config file —
api_keyin~/.dashscope/config.toml - 1.x key file — the file written by
dashscope.save_api_key(...)(~/.dashscope/api_key, orDASHSCOPE_API_KEY_FILE_PATH), honored as the lowest tier so existing 1.x setups keep working
Any call also accepts a per-request api_key= override.
API keys are region-specific. Each region issues its own keys; a key from another region fails with
401. Switchapi_keytogether withregion.
Configuration resolves in layers — explicit arguments → dashscope.settings → environment variables → ~/.dashscope/config.toml. Set it once at startup, per client, or not at all.
Global settings facade:
import dashscope
dashscope.settings.api_key = "sk-xxx"
dashscope.settings.workspace = "ws-xxx123"
dashscope.settings.region = "ap-southeast-1"
dashscope.settings.protocol = "openai"
dashscope.settings.timeout = 60.0
dashscope.settings.max_retries = 3
dashscope.settings.log_level = "INFO"Environment variables:
| Variable | Sets | Default |
|---|---|---|
DASHSCOPE_API_KEY |
API key | — |
DASHSCOPE_WORKSPACE |
Workspace ID (endpoint subdomain) | — |
DASHSCOPE_REGION |
Region | cn-beijing |
DASHSCOPE_PROTOCOL |
native / openai / anthropic |
native |
DASHSCOPE_BASE_URL |
Override the HTTP endpoint entirely (escape hatch) | — |
DASHSCOPE_HTTP_BASE_URL |
1.x name for the same override, honored as a fallback | — |
DASHSCOPE_WEBSOCKET_BASE_URL |
Override the WebSocket endpoint | derived from region/workspace |
DASHSCOPE_TIMEOUT |
Request timeout (seconds) | 300 |
DASHSCOPE_MAX_RETRIES |
Automatic retries | 2 |
DASHSCOPE_STRICT_VALIDATION |
Raise on a response body that does not fit its typed model instead of degrading it | false |
DASHSCOPE_HTTP_MAX_CONNECTIONS |
Connection-pool size | 100 |
DASHSCOPE_HTTP_MAX_KEEPALIVE_CONNECTIONS |
Keep-alive connections held in the pool | 20 |
DASHSCOPE_HTTP_KEEPALIVE_EXPIRY |
Keep-alive expiry (seconds) | 5.0 |
DASHSCOPE_LOG |
Log level | WARNING |
DASHSCOPE_CONFIG |
Path to config.toml |
~/.dashscope/config.toml |
DASHSCOPE_API_KEY_FILE_PATH |
Path to the 1.x key file (lowest-priority auth tier) | ~/.dashscope/api_key |
The three DASHSCOPE_HTTP_* pool variables are also settable as http_max_connections, http_max_keepalive_connections and http_keepalive_expiry in settings and config.toml. Passing your own http_client= takes precedence over all of them.
By default a response body that does not fit the SDK's typed models is parsed in degraded form — the offending fields fall back to their defaults and the rest of the payload stays usable — with the drift recorded at DEBUG on the dashscope._logging.parse logger. Set DASHSCOPE_STRICT_VALIDATION=true (or Client(strict_validation=True), settings.strict_validation = True, strict_validation = true in config.toml) to raise APIResponseValidationError instead, which is what you want in CI or contract tests. Strict validation raises even under raise_on_error=False: that flag governs service errors, this one governs parse errors.
One caveat: DEBUG is below the SDK's default log level, so a degraded parse is silent unless you raise it — see Logging — and strict_validation is the only way to make drift fail loudly. The typed-parse path covers every resource, and not only the final response object: streamed frames (apps, deployments.stream_events, fine_tunes.stream_events, audio.tts.http.stream) and websocket results (audio.asr) degrade frame by frame as well, so one malformed event cannot break an iteration partway through with a pydantic.ValidationError that no except DashScopeError would catch.
Config file (~/.dashscope/config.toml):
api_key = "sk-xxx"
workspace = "ws-xxx123"
region = "ap-southeast-1"
protocol = "native"
timeout = 300.0
max_retries = 2By default requests go to the China (Beijing) gateway. When a workspace is set, endpoints are built from the workspace-scoped MaaS gateway https://{workspace}.{region}.maas.aliyuncs.com, with each protocol served under its own path. Point at another region with region= (or DASHSCOPE_REGION / settings.region):
client = dashscope.Client(region="ap-southeast-1", workspace="ws-xxx123")
# -> https://ws-xxx123.ap-southeast-1.maas.aliyuncs.com/api/v1| Region | Location |
|---|---|
cn-beijing |
China (Beijing) — default |
cn-hongkong |
China (Hong Kong) |
ap-southeast-1 |
Singapore |
ap-northeast-1 |
Japan (Tokyo) |
eu-central-1 |
Germany (Frankfurt) |
us-east-1 |
US (Virginia) |
Notes:
- No workspace configured? The SDK falls back to the legacy shared gateway (
https://dashscope.aliyuncs.comfor Beijing) and emits aUserWarning. It works, but the workspace-scoped gateway is recommended for performance and stability. - WebSocket-based realtime APIs (realtime speech, multimodal dialog) are served in
cn-beijingandap-southeast-1. - Batch inference, fine-tuning and application development are currently available in
cn-beijingandap-southeast-1. base_url=(orDASHSCOPE_BASE_URL) overrides everything — the escape hatch for custom/private gateways; it does not require a workspace.- The 1.x
dashscope.set_region(region, workspace_id)global still works for the 1.x module-level API.
Every capability is a resource on the client. Sync and async clients expose the same surface (async methods are awaited) — with one exception: the duplex WebSocket audio sessions are sync-only by design, as noted below.
| Capability | 2.0 resource | Example |
|---|---|---|
| Text chat / generation | client.chat.completions.create(...) |
text_generation/chat.py |
| Multi-modal chat (vision, video) | client.multimodal.chat.create(...) |
multimodal_vision/multimodal_chat.py |
| Text embedding | client.embeddings.create(...) (+ .jobs for batch) |
embeddings/embeddings.py |
| Multi-modal embedding | client.multimodal.embeddings.create(...) |
embeddings/ |
| Rerank | client.rerank.create(...) |
rerank/rerank.py |
| Decision (System One) | client.decision.create(...) |
decision/decision.py |
| Token counting | client.tokenization.count(...) |
text_generation/tokenization.py |
| Image generation | client.images.generate(...) (+ .jobs) |
image_video_synthesis/images.py |
| Video generation | client.videos.generate(...) (+ .jobs) |
image_video_synthesis/videos.py |
| Speech recognition (ASR) | Batch client.audio.asr.transcribe(...) / .jobs; realtime client.audio.asr.recognize(...) |
audio/asr_file.py · audio/asr_websocket.py |
| Speech synthesis (TTS) | HTTP client.audio.tts.http.synthesize(...) / .stream(...); WebSocket client.audio.tts.connect(...) |
audio/tts_http.py · audio/tts_websocket.py |
| Realtime omni dialog | client.audio.realtime.connect(...) |
audio/realtime_omni.py |
| Voice & vocabulary management | client.audio.voices · .voice_design · .vocabularies · .phrases |
audio/voice_clone.py |
| Meeting & audio understanding (TingWu) | client.tingwu.call(...) / .realtime(...) |
audio/tingwu.py |
| Files | client.files.upload/list/get/delete(...) |
files/files.py |
| Models | client.models.list/get(...) |
models/models.py |
| Fine-tuning | client.fine_tunes.create/get/list/wait/cancel/delete(...) |
fine_tuning/fine_tunes.py |
| Model deployments | client.deployments.create/scale/list/delete(...) |
fine_tuning/ |
| Agentic RL | client.rl (.jobs, register_functions, upload_data) |
fine_tuning/reinforcement_learning.py |
| Model Studio applications | client.apps.completions.create(...) |
applications/apps.py |
| AgentStudio (agents, sessions, memory) | client.agentstudio |
03_agent_recipes/agentstudio.py |
A few common shapes:
# Embeddings
resp = client.embeddings.create(model="text-embedding-v4", input=["hello", "world"], dimension=1024)
resp.embeddings[0].embedding
# Multi-modal chat (image understanding)
resp = client.multimodal.chat.create(
model="qwen3-vl-plus",
messages=[{"role": "user", "content": [{"image": "https://.../cat.png"}, {"text": "What is this?"}]}],
)
resp.output_text
# Image generation (submit + wait); use client.images.jobs for manual control
job = client.images.generate(model="wanx2.1-t2i-turbo", prompt="a cozy flower shop", size="1024*1024")
if job.succeeded:
print(job.image_urls)
# HTTP speech synthesis: one POST, audio comes back as a temporary URL
synth = client.audio.tts.http.synthesize(
"Hello there", model="cosyvoice-v3-flash", voice="longanhuan", format="pcm",
)
print(synth.audio_url)
# Streaming synthesis yields audio chunks as they are rendered
for chunk in client.audio.tts.http.stream(
"Hello there", model="cosyvoice-v3-flash", voice="longanhuan", format="pcm",
):
if chunk.audio:
... # chunk.is_final marks the closing part
# A Model Studio application
resp = client.apps.completions.create(app_id="YOUR_APP_ID", prompt="How do I make stew?")
resp.textAsyncClient covers the HTTP-shaped audio resources — await client.audio.asr.transcribe(...) for batch transcription, client.audio.tts.http.synthesize/.stream for synthesis, and the management resources (voices, voice_design, vocabularies, phrases). The duplex WebSocket sessions (asr.recognize, tts.connect, realtime.connect) are sync-only by design; run them in a thread if you need them inside an async app.
For latency-sensitive realtime audio, open a shared duplex socket and run multiple sessions on it instead of handshaking per task:
connection = client.ws_connection()
session = client.audio.asr.recognize(model="fun-asr-realtime", connection=connection)
# ... drive the session; the caller owns the connection ...
connection.close()Each task on the connection reads through a bounded queue (task_queue_size=, default 1024), so a slow consumer fails its own task with errors.WsQueueCapacityExceededError instead of holding audio in memory indefinitely or tearing down the shared socket.
| Category | Example models | 2.0 resource |
|---|---|---|
| Text generation | qwen-max, qwen-plus, qwen-turbo | client.chat |
| Multi-modal understanding | qwen3-vl-plus, qwen-omni | client.multimodal.chat |
| Text embedding | text-embedding-v4, text-embedding-v3 | client.embeddings |
| Multi-modal embedding | tongyi-embedding-vision-plus | client.multimodal.embeddings |
| Rerank | gte-rerank-v2, qwen3-rerank | client.rerank |
| Image generation | wanx2.1-t2i-turbo, wan2.6-image | client.images |
| Video generation | wanx2.1-t2v, wan2.7-i2v | client.videos |
| Speech synthesis (TTS) | cosyvoice-v3-flash, cosyvoice-v1, qwen-tts | client.audio.tts |
| Speech recognition (ASR) | fun-asr-realtime, paraformer-v1 | client.audio.asr |
| Omni (realtime) | qwen-omni-turbo-realtime | client.audio.realtime |
For the latest model list, visit the Model Studio model plaza.
The SDK integrates an interactive AI assistant, DashScope SDK Expert, powered by the optional dashscope-agenticx dependency. It answers SDK/API questions, generates runnable examples, shows CLI usage, and diagnoses errors right in your terminal.
- Run
dashscopewith no arguments to start the assistant. On first run it offers to download the Expert profile and per-domain skills into./.agenticx/; the same profile can be installed later withagenticx expert download dashscope-sdk-expert. - Ask it instead of reading docs — e.g.
dashscope expert "how do I stream chat output"(asks once and exits). Type/helpinside the assistant for available commands;dashscope expert --helpshows all forms. - With Python 3.10+, install the
acliextra:pip install "dashscope[acli]". Addacli-voice/acli-camerafor voice and camera input, oracli-allfor everything — see the extras table. - Full walkthrough: DashScope SDK Expert guide.
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:
python -m dashscope.cli apps completions create ...
python -m dashscope.legacy_1x.cli generation call ...Running dashscope with no arguments starts the AI assistant 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.
Run the command for your shell once, then restart the shell (or re-source your config):
| Shell | Install command |
|---|---|
| bash | dashscope --install-completion bash |
| zsh | dashscope --install-completion zsh |
| fish | dashscope --install-completion fish |
Preview the script without installing:
dashscope --show-completion bashCompletion is installed through the 2.0 dashscope entry point.
Set the level via settings or the environment:
dashscope.settings.log_level = "INFO"export DASHSCOPE_LOG="INFO"The examples/ directory is a production best-practices collection built on the 2.0 client, organized as an advancement path:
examples/
├── 01_atomic_capabilities/ Minimal runnable quickref per capability
├── 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 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.
The 1.x module-level API is unchanged and still supported — you can adopt 2.0 incrementally. When you're ready, the mapping is direct:
| 1.x | 2.0 |
|---|---|
Generation.call(model=..., messages=..., result_format="message") |
client.chat.completions.create(model=..., messages=...) |
resp.status_code == HTTPStatus.OK then resp.output.choices[0].message.content |
resp.output_text (errors raise by default) |
MultiModalConversation.call(...) |
client.multimodal.chat.create(...) |
TextEmbedding.call(...) |
client.embeddings.create(...) |
TextReRank.call(...) |
client.rerank.create(...) |
ImageSynthesis.call(...) / .async_call(...) |
client.images.generate(...) / client.images.jobs |
dashscope.api_key = "sk-xxx" |
dashscope.Client(api_key=...) or dashscope.settings.api_key = ... |
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 above.
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:
- the historical
dashscope.agentstudioanddashscope.finetune.agentic_rlimports keep the 1.x clients, methods, return models and exception classes; client.agentstudioandclient.rlexpose the 2.0 resource APIs, the httpx transport and thedashscope.errorsfamily.
Historical deep modules such as dashscope.aigc, dashscope.client.base_api, dashscope.files and dashscope.tokenizers remain importable through lazy aliases to dashscope.legacy_1x. The local get_tokenizer utility therefore keeps its 1.x exception contract, while 2.0 remote token counting is the independent client.tokenization resource. The application cores are internal implementation paths — 2.0 callers should use the client resource tree.
Concretely: the historical Agentic RL class still returns FineTune from submit_job and retains cancel/list/get/delete/logs, whereas the 2.0 job lifecycle is client.rl.jobs with errors from the SDK catalog. Likewise, standalone dashscope.agentstudio.Client keeps the old AgentStudioError hierarchy and its raw response attribute, while client.agentstudio raises dashscope.errors and exposes failed response data through body.
Breaking (2.0 AgentStudio errors): on the 2.0 surface —
client.agentstudio, whose exceptions come fromdashscope.applications.agentstudio—ConflictErroris an alias ofAPIStatusErrorrather than a 409-specific subclass, soexcept ConflictErrorcatches every status error (400/401/404/429/5xx), not just conflicts. Match onerr.status_code == 409if you need conflict-only handling. The historicaldashscope.agentstudio.ConflictErroris untouched and still 409-specific.
Breaking (
submit_job/ realtime WebSocket):submit_jobforwards extra keyword arguments straight to the transport, so the 1.xrequest_timeout=is renamedtimeout=, andworkspace=/headers=/stream=are no longer accepted (passing any of them raisesTypeError). The realtime WebSocket errors (WsStateError,WsQueueCapacityExceededError,WsBinaryOwnerConflictError) now derive fromDashScopeErrorinstead ofRuntimeError, so a bareexcept RuntimeErrorno longer catches them — catcherrors.DashScopeError(or the specific class) instead.
dashscope.save_api_key now comes from dashscope._config. It accepts an optional key-file path, expands ~, creates missing parent directories, and writes UTF-8. Logs from the current client use the independent dashscope._logging logger hierarchy and do not reconfigure the shared dashscope root logger.
python -m pytest tests --ignore=tests/legacy_1x -q # current
python -m pytest tests/legacy_1x -q # archived compatibility
python -m pytest tests -q # everythingFor current coverage, run coverage run -m pytest tests --ignore=tests/legacy_1x, then coverage report -m.
This project is licensed under the Apache License (Version 2.0).