@zhivex-ai/qwen
v0.17.1
Published
Qwen adapter for Zhivex AI SDK.
Maintainers
Readme
@zhivex-ai/qwen
Qwen and Alibaba Cloud Model Studio adapter for Zhivex AI SDK.
Install
bun add @zhivex-ai/sdk @zhivex-ai/qwen zodConfigure
import { createQwen } from "@zhivex-ai/qwen";
const qwen = createQwen({
apiKey: process.env.DASHSCOPE_API_KEY
});For Alibaba Cloud Model Studio workspace endpoints, configure the workspace once and the adapter derives the compatible HTTP, native task, and realtime URLs:
const qwen = createQwen({
apiKey: process.env.DASHSCOPE_API_KEY,
workspaceId: process.env.QWEN_WORKSPACE_ID,
region: "singapore"
});Supported region values are singapore, beijing, hong-kong, tokyo, frankfurt, and virginia. baseURL, taskBaseURL, and realtimeURL remain available for explicit regional endpoints. They require HTTPS/WSS and a consistent trusted host; a server-side private gateway must opt in explicitly with allowUnsafeEndpoints: true.
Qwen speech downloads accept HTTPS audio URLs only, validate every redirect manually, and never forward the provider API key to the media host.
Qwen3.8 Omni Flash
qwen3.8-omni-flash accepts text, images, audio, and video and returns text only over HTTP. It uses standard Model Studio credentials, including the default Singapore endpoint or a regional workspace endpoint. Both Chat Completions and Responses support streaming and non-streaming generation. This is a separate contract from streaming-only Omni 3/3.5 and from the Realtime model.
const result = await generateText({
model: qwen("qwen3.8-omni-flash"),
messages: [{
role: "user",
parts: [
{ type: "audio", data: "https://example.com/meeting.wav", mediaType: "audio/wav" },
{ type: "file", data: "https://example.com/meeting.mp4", mediaType: "video/mp4" },
{ type: "text", text: "Summarize the meeting with timestamps." }
]
}],
reasoning: { effort: "low" },
providerOptions: { apiMode: "responses" }
});- Auto mode permits audio/video on Responses, including with hosted web search. Chat is selected for ordinary requests with
maxTokensalone, thinking budgets, ortool_stream; explicit incompatible combinations are rejected. - Audio uses
AudioPart(URL, base64, or bytes), with format inferred from the MIME type or overridden byformat. SetAudioPart.providerMetadata.use_multichannel: truefor two/four-channel spatial audio. Video uses avideo/*FilePart. Audio/video is restricted to user messages; document files are rejected. - Chat maps media to
input_audioandvideo_url; Responses maps it toinput_audio.audio_urlandinput_video.video_url. - Thinking defaults to upstream
xhigh. All seven shared efforts are accepted; Chat mapsminimaltolow,high/maxtoxhigh, andnonedisables thinking. Chat preserves thinking history. Effort and budget cannot be combined. - Callable tools are supported; the only exposed hosted Responses tool is
qwenWebSearchTool(). MCP, hosted file search, code execution, and other hosted tools are rejected. Forced callable-tool selection requires thinking to be disabled. - Audio output settings are rejected. Structured output uses
generateObject({ mode: "prompted", ... })with local schema validation; native JSON Schema/JSON mode is not advertised pending a verified model-specific contract. - QwenCloud lists a 1M context window and $0.15/M input, $0.47/M output, and $0.016/M implicit-cache input tokens. Media tokenization and account limits still apply; these rates are not a flat price per minute.
Sources: QwenCloud model card, Omni API guide.
Run the dedicated opt-in live suite (fails if enabled without credentials):
QWEN_OMNI_INTEGRATION=1 bun --env-file=.env run test:integration packages/qwen/tests/omni-flash.integration.test.tsThe suite uses public Alibaba sample media, checks both HTTP APIs, reasoning streams, final usage, tool execution/continuation, prompted JSON, and hosted web search. It does not certify the separate Realtime model.
Qwen 3.8 Flash
qwen3.8-flash is the low-latency Qwen 3.8 production model exposed through standard QwenCloud/Alibaba Cloud Model Studio credentials and compatible endpoints. It provides a 1M-token context window, up to 128K output tokens, text/image/video input, hybrid reasoning, function calling, built-in tools, parallel function calls, and native JSON Schema structured output. Published pay-as-you-go pricing is $0.16 per million input tokens, $0.47 per million output tokens, and $0.016 per million implicit-cache input tokens.
const qwen = createQwen({
apiKey: process.env.DASHSCOPE_API_KEY
});
const model = qwen("qwen3.8-flash");Responses accepts all seven shared reasoning efforts (none, minimal, low, medium, high, xhigh, and max) directly. Chat Completions normalizes them to Qwen's native low, medium, and xhigh values; none disables thinking. Chat always preserves thinking history, uses max_completion_tokens, and routes video FilePart inputs as video_url. Reasoning effort and thinking budget are mutually exclusive, and the adapter caps thinking budgets at the documented 262144-token maximum.
The model supports both standard Model Studio endpoints and QwenCloud Token Plan. For Token Plan, explicitly pair a dedicated sk-sp- key with QWEN_TOKEN_PLAN_BASE_URL; see the setup below. Upstream model availability is still account-, plan-, and region-dependent.
Qwen 3.8 Max
qwen3.8-max is the production Qwen 3.8 flagship exposed through standard Alibaba Cloud Model Studio credentials and compatible endpoints, including regional workspace endpoints. It has a 1M-token context window and supports text, image and video understanding, hybrid reasoning, function calling, built-in tools, parallel function calls, and JSON structured output.
const qwen = createQwen({
apiKey: process.env.DASHSCOPE_API_KEY,
workspaceId: process.env.QWEN_WORKSPACE_ID,
region: "beijing"
});
const model = qwen("qwen3.8-max");Thinking is enabled by default but remains hybrid. Responses accepts the shared none, minimal, low, medium, high, xhigh, and max efforts directly. Chat Completions uses Qwen's native low, medium, and xhigh levels, so minimal maps to low, while high and max map to xhigh; none sends enable_thinking: false. A reasoning effort and a thinking budget are mutually exclusive, and budgets cannot exceed 262144 tokens. Chat requests always send preserve_thinking: true, while shared maxTokens uses the non-deprecated max_completion_tokens field.
Video input uses a FilePart with a video/* MIME type and automatically selects Chat Completions. Public HTTP(S) URLs, data URLs, and raw base64 strings are accepted; non-video FilePart values are rejected for this model. Set providerOptions.parallel_tool_calls to opt in to upstream parallel tool calls and providerOptions.tool_stream to select Chat tool-call streaming. While thinking is active, Qwen permits only toolChoice: "auto" or "none"; disable thinking before using "required" or selecting a named tool.
A standard Model Studio key is region-scoped, so keep the key, workspace, and selected region aligned. The adapter does not hard-code a region allowlist for this final model; model availability still depends on the selected Model Studio region.
For Token Plan, qwen3.8-max also accepts QWEN_TOKEN_PLAN_BASE_URL with a dedicated sk-sp- key. Keep plan credentials separate from standard Model Studio credentials.
QwenCloud Token Plan (Personal and Team)
Use qwen3.8-max or qwen3.8-flash with Token Plan. The plan also lists qwen3.7-max and qwen3.7-plus, which use the existing language-model adapter. Token Plan keys use the sk-sp- prefix and are not interchangeable with pay-as-you-go keys. Configure the dedicated endpoint explicitly; createQwen() still defaults to pay-as-you-go and does not select a billing mode from the key prefix:
import {
createQwen,
QWEN_TOKEN_PLAN_BASE_URL
} from "@zhivex-ai/qwen";
const qwenTokenPlan = createQwen({
apiKey: process.env.QWEN_TOKEN_PLAN_API_KEY,
baseURL:
process.env.QWEN_TOKEN_PLAN_BASE_URL ??
QWEN_TOKEN_PLAN_BASE_URL
});
const model = qwenTokenPlan("qwen3.8-max");QWEN_TOKEN_PLAN_BASE_URL resolves to https://token-plan.maas.qwencloudapi.com/compatible-mode/v1, documented for both Personal and Team plans. The previous Singapore endpoint https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1 remains accepted when explicitly configured for compatibility; its continued service availability is provider-controlled. Chat, Responses, streaming, callable tools, and model-specific structured output reuse the existing adapter. Hosted Harness tools use Responses.
QwenCloud has retired qwen3.8-max-preview and redirects that ID to qwen3.8-max. Migrate to the final ID to use its full capabilities. For compatibility, the SDK still sends the preview ID unchanged and retains its conservative legacy validation described below; it accepts only the current or previous exact Token Plan endpoint for that ID, rejecting pay-as-you-go, workspace, decorated, and lookalike endpoints before fetch. Omni and dated Qwen 3.8 snapshots are not inferred to have plan access.
This routing support does not certify all models or media APIs offered by the subscription, nor does it manage Credits, subscriptions, or seats. Contract tests use mocked HTTP; authenticated plan access must be verified separately.
Token Plan terms limit these credentials to interactive use in programming and agent tools. Do not use a Token Plan key for automated scripts, application backends, scheduled jobs, or non-interactive batch processing; use QwenCloud pay-as-you-go with a supported production model for those workloads.
Text, tools, and structured output
The default apiMode: "auto" selects the protocol required by the request:
- Responses for hosted tools, Qwen OCR file URLs, and
previous_response_idcontinuation. - Chat Completions for structured output, audio/video input (except Omni 3.8, which also supports Responses), ordinary requests with
maxTokensalone,reasoning.budgetTokens, orproviderOptions.tool_stream. Hosted search supports bounded Responses on documented families; see output limits. - Either path for ordinary text and local function tools; automatic mode prefers Responses.
Use providerOptions: { apiMode: "responses" } or { apiMode: "chat" } only when you need to force a compatible path. Unsupported combinations fail before the network request instead of silently dropping fields.
qwen3.5-omni-plus and qwen3.5-omni-flash are streaming-only Chat Completions models. Use streamText() for them; the adapter rejects generateText(), Responses-only hosted tools, file inputs, and reasoning controls that those models do not support. Text output is selected by default with modalities: ["text"], while an explicit compatible modalities option is preserved.
Qwen Chat Completions supports JSON-object mode across the compatible families. qwen3.8-flash additionally receives the native json_schema request shape; other supported models use JSON mode plus a schema system prompt, and every result is validated locally:
import { generateObject } from "@zhivex-ai/sdk";
import { z } from "zod";
const result = await generateObject({
model: qwen("qwen3.7-plus"),
prompt: "Return the city and temperature.",
schema: z.object({ city: z.string(), temperature: z.number() })
});For older Qwen families, Responses reasoning maps shared effort to reasoning.effort (low becomes Qwen minimal), while Chat reasoning maps to enable_thinking and budgetTokens to thinking_budget.
qwen3.8-flash and qwen3.8-max use the stricter production mapping described above, accept images through either compatible API, and route video through Chat. With no explicit reasoning option, the provider leaves Qwen's thinking-enabled default intact. Flash uses native JSON Schema; Max retains JSON-object mode with local schema validation.
The SDK retains these conservative legacy restrictions for the retired qwen3.8-max-preview ID; use qwen3.8-max for the current contract:
- Thinking is always enabled. Shared
effort: "none",providerOptions.enable_thinking: false, andproviderOptions.reasoning_effort: "none"are rejected before a request is sent. - Its native efforts are
low,medium, andxhigh. OpenAI-compatible aliases are normalized as documented by Qwen:minimal→low,high/max→xhigh. The adapter omits the effort when none is supplied, leaving the upstream default in effect. reasoning.effort/providerOptions.reasoning_effortcannot be combined withreasoning.budgetTokens/providerOptions.thinking_budget. Shared budgets must be positive and both paths enforce the model maximum of 262144; rawthinking_budgetalso accepts the documented value0.- Automatic mode uses Responses for ordinary text, images, local functions, and hosted tools. A thinking budget selects Chat Completions because
thinking_budgetis not available in Responses. For Chat requests, sharedmaxTokensis sent asmax_completion_tokens. - Chat requests always send
preserve_thinking: true, and reasoning returned by the adapter is kept as Qwenreasoning_contentprovider data so multi-turn and tool-call histories return it in the correct field. Settingpreserve_thinking: falseis rejected. - Under this legacy validation, forced or named tool choices are rejected; use
toolChoice: "auto"or"none". - The model supports text, images, function calling, and built-in tools. It does not support native structured output or JSON mode.
generateObject({ mode: "native" })is rejected through its capabilities; the defaultautomode can still use prompted output with local schema validation.
For the production model contracts, see QwenCloud's official Qwen 3.8 Flash model page, text-model guide, vision-model guide, OpenAI-compatible Chat contract, Responses contract, and structured-output guide. The Qwen 3.8 Max contract is also documented in Alibaba Cloud Model Studio's official model list.
For current plan routing and model availability, see QwenCloud's official Personal quickstart, Team quickstart, Token Plan terms, OpenAI-compatible Chat contract, and Responses contract.
Hosted tools
import { generateText } from "@zhivex-ai/sdk";
import {
qwenCodeInterpreterTool,
qwenFileSearchTool,
qwenMcpTool,
qwenWebExtractorTool,
qwenWebSearchTool
} from "@zhivex-ai/qwen";
const result = await generateText({
model: qwen("qwen3.7-plus"),
prompt: "Search current docs, extract the relevant page, and validate a calculation.",
tools: {
search: qwenWebSearchTool(),
extract: qwenWebExtractorTool(),
code: qwenCodeInterpreterTool(),
files: qwenFileSearchTool({ vector_store_ids: ["existing_knowledge_base_id"] }),
maps: qwenMcpTool({
server_label: "amap-maps",
server_protocol: "sse",
server_url: "https://dashscope-intl.aliyuncs.com/api/v1/mcps/amap-maps/sse",
headers: { Authorization: `Bearer ${process.env.DASHSCOPE_API_KEY}` }
})
}
});
console.log(result.text);qwenFileSearchTool() consumes an existing Alibaba Cloud knowledge-base/vector-store identifier. The package intentionally does not expose invented file-search-store CRUD endpoints; DashScope Files remains available for extraction and batch jobs.
Files and batch
Batch inference requires a JSONL file uploaded with purpose: "batch":
import { createBatch, uploadFile } from "@zhivex-ai/sdk";
const file = await uploadFile({
provider: qwen,
data: batchJsonl,
mediaType: "application/jsonl",
filename: "requests.jsonl",
providerOptions: { purpose: "batch" }
});
const batch = await createBatch({
provider: qwen,
modelId: "qwen3.7-plus",
fileName: file.name
});Speech and generative media
import {
generateImage,
generateSpeech,
generateVideo,
transcribeAudio
} from "@zhivex-ai/sdk";
const transcript = await transcribeAudio({
model: qwen.transcriptionModel("qwen3-asr-flash"),
audio: { data: audioBytes, mediaType: "audio/wav" }
});
const speech = await generateSpeech({
model: qwen.speechModel("qwen3-tts-flash"),
input: "Hello from Qwen.",
voice: "Cherry",
providerOptions: { language_type: "English" }
});
const image = await generateImage({
model: qwen.imageGenerationModel("qwen-image-2.0-pro"),
prompt: "A clean product icon",
size: "1024*1024"
});
const video = await generateVideo({
model: qwen.videoGenerationModel("wan2.7-t2v"),
prompt: "A clean product icon rotating slowly",
providerOptions: { resolution: "720P", watermark: false }
});Speech responses are bounded by createQwen({ responseLimits }). The default decoded speech limit is 16 MiB, base64 size is checked before decoding, and encoded audio is removed from SpeechResult.rawResponse after decoding to avoid retaining two copies.
Downloaded speech URLs are also protected against server-side request forgery. By default, the adapter accepts the HTTP(S) Alibaba OSS hosts used by Qwen TTS, follows at most three redirects manually, and validates every redirect target without forwarding the Qwen API key. Private gateways can provide an explicit speechAudioURLValidator; speechAudioMaxRedirects can be set from 0 through 10.
Multimodal embeddings and reranking
const embeddings = await qwen
.multimodalEmbeddingModel("tongyi-embedding-vision-plus")
.embed({
values: [
"product description",
{ uri: "https://example.com/product.png", mediaType: "image/png" }
]
});
const ranked = await qwen.rerankModel("qwen3-rerank").rerank({
query: "Qwen SDK support",
documents: ["unrelated", "Zhivex supports Qwen."],
topN: 1
});tongyi-embedding-vision-plus is the multimodal embedding model for the international/Singapore endpoint used by the provider defaults. qwen3-vl-embedding is available in Beijing; configure a Beijing workspace and region: "beijing" before selecting it.
qwen3-vl-rerank and other native multimodal rerank models also accept MediaInput query and document values.
Realtime
const session = await qwen
.realtimeModel("qwen3.5-omni-plus-realtime")
.connect({
instructions: "Be concise.",
turnDetection: { type: "server_vad" }
});
await session.sendText("Hello");
await session.sendMedia({ data: jpegBytes, mediaType: "image/jpeg" });The realtime adapter maps text, audio, transcripts, function calls, response completion, and session completion into the shared RealtimeEvent contract. JPEG image frames are supported by Qwen Omni realtime; browser-token minting is not exposed.
Qwen Realtime supports callable tools with automatic selection. toolChoice: "none" is enforced locally by omitting tools from the upstream session; "required" and named-tool selection are rejected before the WebSocket opens because the upstream realtime contract does not support them. The adapter also rejects providerOptions.enable_search: true when callable tools are present because Qwen Realtime does not allow both in one session.
Sessions request text output by default with modalities: ["text"]. Audio output is enabled only when voice or outputAudioMediaType is configured, in which case the adapter sends modalities: ["text", "audio"]. These checks are fail-closed and reflect the provider's current realtime contract.
Authenticated realtime connections use the package's Node/Bun ws transport by default. realtimeConnectionFactory remains available for custom runtimes. Do not expose a Model Studio API key in browser code.
Current catalog coverage
The default catalog includes current text and multimodal Qwen families plus the specialized IDs wired above: qwen3.8-flash and qwen3.8-max for standard Model Studio production traffic, qwen3.8-max-preview as a retired Token Plan compatibility ID, qwen3.7-plus, qwen3.7-max, qwen3.6-flash, qwen3.5-omni-plus, qwen3.5-omni-plus-realtime, qwen3.5-ocr, tongyi-embedding-vision-plus for international/Singapore, qwen3-vl-embedding for Beijing, qwen3-rerank, qwen3-asr-flash, qwen3-tts-flash, qwen-image-2.1-pro, qwen-image-2.1-turbo, qwen-image-2.0-pro, and wan2.7-t2v. Image 2.1 accepts up to ten reference images on the synchronous multimodal endpoint and rejects negative_prompt. Per-image rates stay in the media guide because the catalog prices tokens, not images.
Run opt-in live coverage with:
QWEN_EXTENDED_INTEGRATION=1 bun --env-file=.env run test:integration:qwenThe shared provider smoke keeps qwen3.7-plus as its compatibility default. Certify Flash explicitly with QWEN_INTEGRATION_MODEL=qwen3.8-flash, or Max with QWEN_INTEGRATION_MODEL=qwen3.8-max. For those two hybrid production IDs only, the tool-loop check disables thinking, forces the named tool on the first model step, and returns to automatic selection for the final answer; earlier Qwen families keep their existing smoke behavior, and the thinking-only preview is not given these overrides. The standard international endpoint can be used when the account exposes the selected model there, while QWEN_WORKSPACE_ID and QWEN_REGION select an explicit regional workspace. Set only the extended surfaces you want to exercise: QWEN_MULTIMODAL_EMBEDDING_MODEL, QWEN_MULTIMODAL_IMAGE_URL, QWEN_RERANK_MODEL, QWEN_ASR_MODEL, QWEN_ASR_AUDIO_URL, QWEN_TTS_MODEL, QWEN_IMAGE_MODEL, QWEN_VIDEO_MODEL, and QWEN_REALTIME_MODEL. For the default international endpoint, use tongyi-embedding-vision-plus; use qwen3-vl-embedding only with a Beijing workspace. Workspace and endpoint overrides use QWEN_WORKSPACE_ID, QWEN_REGION, QWEN_BASE_URL, QWEN_TASK_BASE_URL, and QWEN_REALTIME_URL.
The catalog records QwenCloud's published qwen3.8-flash input, output, and implicit-cache rates. It intentionally omits a price for qwen3.8-max until Alibaba Cloud publishes a stable public rate for that model.
Repository and full documentation:
Qwen 3.8 snapshots
qwen3.8-max-0902 preserves its exact upstream ID while inheriting the qwen3.8-max validation, vision, tools, and token-limit request mapping. Dated four-digit Max/Flash snapshots are recognized separately from Token Plan previews. qwen3.8-2.4t-a95b is listed without automatic recommendations and supports image/video mapping; its specialized behavior has not been live-certified. The full qwen3.8-max-2026-09-02 alias inherits the Max profile as well.
See the QwenCloud model inventory.
Gateway tool history
The language adapter advertises capabilities.toolHistory: "json" for compatible
models. Chat Completions and Responses preserve multiple results in a tool message.
When the low-level model input sets toolResultFormat: "envelope", callable tool
outputs use JSON { "output": value } or { "error": { "message": "..." } },
so error state survives the string-only tool output protocol. Direct SDK calls
retain their existing raw output format by default. See the
gateway history contract
for routing, exclusions and the isolated-consumer verification command.
Portable gateway replay defaults to non-thinking mode and rejects explicit thinking requests, because the accepted history has no provider-specific reasoning state. Thinking-only models are excluded from the gateway history capability.
Generation retries
Language-model generation and streaming startup validate HTTP failures inside the retry boundary. maxRetries applies to HTTP 408, 429 and 5xx responses, with bounded Retry-After waits. Other 4xx responses are not retried. Timeout and caller cancellation interrupt retry waits; successful stream bodies remain unread until consumption.
Third-party models hosted by QwenCloud
Use createQwen() with QwenCloud credentials for hosted DeepSeek, GLM, Kimi,
and MiniMax. The direct createDeepSeek(), createZai(), and createKimi()
providers target different services and do not configure QwenCloud access.
IDs are case-sensitive and are sent unchanged, including supplier prefixes.
import { generateText } from "@zhivex-ai/sdk";
import { createQwen } from "@zhivex-ai/qwen";
const cloud = createQwen({ apiKey: process.env.QWEN_API_KEY });
const answer = await generateText({
model: cloud("deepseek-v4.1-flash"),
prompt: "Explain this algorithm.",
reasoning: { effort: "high" }
});
console.log(answer.text);The explicit contract snapshot covers these models:
| Model IDs | Protocols | Images | Native JSON Object | Hosted tools |
| --- | --- | --- | --- | --- |
| deepseek-v4-pro, deepseek-v4-flash, deepseek-v4-pro-0813 | Chat, Responses | No | Yes | Search, extraction, code |
| deepseek-v4-flash-0731 | Chat, Responses | No | Not advertised | Search, extraction, code |
| deepseek-v4.1-flash | Chat, Responses | Yes | Yes | Search, extraction, code |
| glm-5.2 | Chat, Responses | No | Non-thinking only | Search, extraction, code |
| glm-5.3 | Chat, Responses | No | Yes | Not advertised |
| ZHIPU/GLM-5.3 | Chat | No | Yes | Not advertised |
| kimi-k3 | Chat, Responses | Yes | Yes | Search, extraction, code |
| MiniMax-M2.5 | Chat | No | No | No |
All listed models support text, streaming, and callable tools with preserved
assistant reasoning_content in Chat tool continuations. GLM Chat requests
set clear_thinking: false unless explicitly overridden. Thinking-only models
do not advertise portable non-thinking JSON tool history. File/audio/video,
remote MCP, file search, explicit parallel tool calls, and the separate
tool_stream option are not advertised for these profiles. Ordinary streaming
function calls remain supported. Unsupported combinations fail before fetch.
GLM 5.2 callable-tool requests automatically use Chat: the live Responses service currently rejects function-result continuation with an internal argument validation error, even when replaying full canonical history. Forced Responses with callable tools is rejected before fetch. Responses text, streaming and hosted-only tools remain available; mixed hosted/callable requests cannot be served until the upstream continuation issue is resolved.
auto prefers Responses when available; maxTokens, thinking budgets, and
native JSON use Chat under the existing Qwen routing rules. Hosted tools need
Responses and cannot be combined with those Chat-only controls. Use
providerOptions: { apiMode: "chat" } to select Chat explicitly.
Reasoning is model- and protocol-specific. DeepSeek V4 and GLM 5.2 map lower
levels to high, and xhigh to max. DeepSeek dated V4 snapshots map xhigh
to high in Chat and max in Responses. V4.1 maps it to high in both.
GLM 5.3 and Kimi K3 use low/high/max. GLM 5.3 cannot disable thinking;
MiniMax uses its default reasoning without configurable effort. Only GLM 5.2
accepts thinking_budget among these profiles. Conflicting controls are rejected.
The Qwen-specific preserve_thinking option is not sent to these models.
Native structured output uses JSON Object plus a schema prompt and SDK
validation, not provider-enforced JSON Schema. GLM 5.2 requires
reasoning: { effort: "none" } for native JSON. Use mode: "prompted" when
native JSON is unavailable. Catalog entries intentionally omit unverified
prices and automatic recommendations.
Region, workspace access, and Token Plan entitlements are provider-controlled;
a catalog entry does not grant access. For Token Plan, configure the matching
key and QWEN_TOKEN_PLAN_BASE_URL explicitly and follow the plan's usage terms.
Supplier-prefixed routes are separate models, not aliases for cloud deployments.
Other arbitrary model IDs retain the legacy generic adapter behavior; they do
not inherit these verified contract profiles.
Sources checked 2026-09-18: Chat contract, Responses contract, JSON modes, GLM contract, DeepSeek V4.1, Kimi K3, and GLM 5.2.
Contract tests use mocked HTTP responses. Live certification is separate and must be run for each model and configured region. To opt in with a standard pay-as-you-go key (the command incurs provider usage):
QWEN_THIRD_PARTY_INTEGRATION=1 \
QWEN_THIRD_PARTY_MODELS=deepseek-v4.1-flash,glm-5.2,kimi-k3,MiniMax-M2.5 \
bun --env-file=.env run test:integration:qwen:third-partyThe live matrix checks text, streaming, usage, and a full callable-tool cycle
for each selected model/protocol. GLM 5.2 Responses tool continuation is explicitly
skipped because of the upstream limitation described above. Set
QWEN_THIRD_PARTY_EXTENDED=1 to additionally validate native JSON, image input,
web search, and code interpreter where advertised, using a public Alibaba image
sample. A skipped run is not provider certification.
Validation snapshot (2026-09-18, local working tree, default Singapore endpoint):
- Unit suite: 2,019 passing tests, including 86 dedicated third-party contract tests. Documentation, typecheck, build, and whitespace checks passed.
- Live baseline: 50 distinct successful model/protocol checks across the five
DeepSeek IDs above,
glm-5.2,glm-5.3,ZHIPU/GLM-5.3, andkimi-k3, collected across the baseline, post-fix, and activation-recheck runs. GLM 5.2 has five baseline checks; its Responses callable-tool case is excluded for the documented upstream defect. - Extended live matrix: 15 distinct passing checks covering JSON on DeepSeek V4.1,
GLM 5.2/5.3,
ZHIPU/GLM-5.3, and Kimi K3; images on DeepSeek V4.1 and Kimi K3 in both protocols; web search and code interpreter on DeepSeek V4.1, GLM 5.2 and Kimi K3. MiniMax-M2.5: all three baseline attempts returnedmodel_not_found(404), indicating absence or lack of access for the configured credential.ZHIPU/GLM-5.3: the initial product-not-activated (400) blocker was cleared after activation. The recheck passed Chat text/usage, streaming, callable-tool continuation, and native JSON. The supplier route remains Chat-only in this adapter;glm-5.3is the separate cloud route supporting Responses.
MiniMax remains access-blocked, with contract-test coverage but no live certification. Web extraction, alternate regions, Token Plan, and complete reasoning-effort permutations were not live-certified by this snapshot. These results do not establish npm publication or production deployment.
Inline image regression smoke
A bounded smoke sends a synthetic red PNG through Chat generation and streaming with qwen3.8-flash, a 512-token budget, and no retries. It requires non-empty text, the expected color, and finishReason: "stop". It honors the existing workspace/region/base URL settings.
QWEN_IMAGE_INTEGRATION=1 bun --env-file=.env run test:integration packages/qwen/tests/image-input.integration.test.tsChat terminal tool calls
Named tool choices may return complete tool_calls fragments with a stop
finish reason. The adapter validates the complete batch at normal stream end
and emits those calls with normalized tool-calls, retaining the original
provider finish reason. Truncated or invalid batches, including explicit provider errors before stream completion, never emit tool calls;
ProviderToolCallError retains terminal token usage when supplied by Qwen.
Qwen 3.8 LiveTranslate
Use qwen.realtimeModel("qwen3.8-livetranslate-flash-realtime") for continuous speech translation over WebSocket. Standard credentials and regional workspace routing apply; availability depends on the account/region. This is not a Chat Completions model.
const session = await qwen.realtimeModel("qwen3.8-livetranslate-flash-realtime").connect({
mode: "translation",
translation: { targetLanguage: "es" },
outputAudioMediaType: "audio/pcm", // Omit for text-only output.
providerOptions: {
translation: { corpus: { phrases: { Zhivex: "Zhivex" } } }
}
});
const receive = (async () => {
for await (const event of session.eventStream()) {
if (event.type === "realtime-transcript") console.log(event.role, event.text, event.isFinal);
// Consume realtime-audio-output for playback and realtime-text-delta for text-only output.
}
})();
// Stream mono PCM16LE audio in paced chunks using session.sendAudio({
// data: pcmBytes, mediaType: "audio/pcm", sampleRateHz: 16000, channels: 1
// }). Output audio is mono PCM16 at 24000 Hz.
await session.close(); // Sends session.finish and waits for all final results and session.finished.
await receive;The adapter waits for session.updated, maps output_modalities and nested audio settings, and uses automatic speaker_detection. isFinal on an audio frame does not commit a manual turn or create a response; call close() after the final audio. Source transcription is always enabled. Source/translation events retain item/response IDs and complete provider metadata. Unmapped events, including speaker/turn lifecycle payloads, are exposed as realtime-provider-data; no undocumented speaker-ID schema is assumed.
All 60 documented target language codes are validated; 29 allow audio output. Text-only targets such as el and yue reject audio output. Glossaries accept up to 1000 nonempty source/translation pairs. JPEG frames require preceding audio, are limited to 500 KiB and two frames per rolling second; applications should follow the upstream maximum 1080p recommendation. Tools, text-message input, interruption commands, manual responses, legacy session fields and same_language_skip_options are rejected.
Voice cloning status
The exported QwenLiveTranslateProviderOptions type describes native session options. Cloning controls can be explicitly forwarded with providerOptions.enable_voice_clone: true, providerOptions.voice_clone_options: { frequency: "once" | "always" | "never" }, and voice. For once/always, set voice: "default"; for never, supply the opaque voice ID returned by enrollment. Audio output is required. The SDK validates these combinations and maps the voice under audio.output.voice.
Live evidence (2026-09-20): the Singapore default endpoint completed English-to-Spanish translation with source transcription, translated text, audio and acknowledged close. Both once and always requests completed; the always run also asserted that session.updated retained the cloning flag and frequency. This verifies protocol acceptance and generation, not perceptual voice similarity or multi-speaker quality. A subsequent live run created a voice with the exact qwen3.8-livetranslate-flash-realtime target, confirmed that never selected its opaque ID, received source transcripts, Spanish translation and 17.28 seconds of unclipped audio, and confirmed deletion of the temporary voice. This establishes the create/use/delete protocol for the tested Singapore account, not universal regional availability or perceptual voice fidelity.
The adapter explicitly selects Tina when no voice is supplied: live testing found that the server otherwise selected Chelsie and subsequently rejected it.
Sources: model guide, client events, server events.
Live verification
Provide a non-sensitive, mono 16 kHz PCM16LE file of at most 30 seconds:
QWEN_LIVETRANSLATE_INTEGRATION=1 QWEN_LIVETRANSLATE_PCM_FILE=/absolute/path/speech.pcm bun --env-file=.env run test:integration packages/qwen/tests/live-translate.integration.test.tsThe test requires real source transcripts, translated text, audio and acknowledged completion. Set QWEN_LIVETRANSLATE_EXPECT_TEXT to check expected Spanish text, or QWEN_LIVETRANSLATE_CLONE=once / always to test server acceptance of cloning settings (voice similarity still requires audio evaluation). Skipped tests are not live evidence.
Voice management and reproducible audit
qwen.voices.create(), .list() and .delete() implement the native qwen-voice-enrollment HTTP contract. Enrollment always requires an explicit targetModel; the client does not silently substitute 3.5 for 3.8. It accepts HTTPS audio URLs or audio data URIs, preserves opaque voice IDs and fallback-quality metadata, does not retry creation, and blocks credentialed redirects. This client is separate from the older voice-enrollment/CosyVoice API.
const voice = await qwen.voices.create({
targetModel: "qwen3.8-livetranslate-flash-realtime",
preferredName: "translationdemo",
audio: "data:audio/wav;base64,...", // Supply a real audio fixture.
language: "en"
});
try {
// Use voice.voice with frequency: "never" and outputAudioMediaType: "audio/pcm".
} finally {
await qwen.voices.delete({ voice: voice.voice });
}Creation is a persistent account mutation and the target must be available in your region. The exact 3.8 target and create/use/delete lifecycle were verified live on 2026-09-20 in Singapore. Official enrollment API.
The repository audit generates baseline/once/always WAVs, a JSON report and an HTML listening comparison in .release/qwen-live-translate-audit/:
QWEN_LIVETRANSLATE_ENROLL=0 QWEN_LIVETRANSLATE_PCM_FILE=/absolute/path/synthetic.pcm bun --env-file=.env scripts/qwen-live-translate-audit.tsSetting QWEN_LIVETRANSLATE_ENROLL=1 additionally creates a temporary voice for the explicit 3.8 target, tests never, and deletes only that new voice in finally. Set QWEN_LIVETRANSLATE_AUDIT_MODES=never for a focused enrollment check. Run that option only when creation is authorized. A private created-voice.json recovery file identifies the voice if cleanup fails; cleanup failure fails the audit. If a create request times out without an ID, inspect your account before retrying because server-side creation may have succeeded.
Signal duration, amplitude, clipping, returned transcripts and session acknowledgements are recorded separately from perceptual voice similarity. The report never equates nonempty audio or accepted cloning options with preservation of voice identity; compare the WAVs to evaluate that property.
An optional qualitative model review can compare the generated clips:
bun --env-file=.env scripts/qwen-live-translate-audio-review.tsIt uses qwen3.8-omni-flash, writes model-audio-review.json, and labels its conclusions as model evaluation rather than human certification. The initial short English-to-Spanish synthetic sample produced intelligible, unclipped audio, but its qualitative review did not establish consistent timbre preservation for both cloning modes. Use QWEN_LIVETRANSLATE_TARGET_LANGUAGE=en and a longer fixture for a same-language control; use QWEN_LIVETRANSLATE_AUDIT_DIR to keep separate runs. Do not treat this small synthetic sample as a population-level voice-cloning benchmark.
The longer same-language synthetic control also completed baseline/once/always with acknowledged cloning and output audio. Its model evaluator preferred the cloned prosody but still did not confirm source-timbre fidelity, and some source-voice descriptions were inconsistent across evaluations. Therefore these automated assessments are retained as limited evidence, not a passing perceptual certification. Enrollment and never subsequently passed the live create/use/delete check for the exact 3.8 target.
The focused never run also received a favorable qualitative timbre assessment from Qwen Omni, with the same limitations as the earlier model-based reviews. A post-cleanup inventory check confirmed that neither of the two temporary voices created during enrollment validation remained in the account.
Qwen Cloud specialized models
Configure baseURL: QWEN_CLOUD_BASE_URL to use Qwen Cloud with standard credentials. Task and realtime hosts follow that endpoint. QWEN_DECISION_PREVIEW_BASE_URL can be supplied explicitly as decisionBaseURL for the separately deployed Decision preview.
The provider exposes decisionModel().decide(), textEmbeddingModel().embedNative(), streamingASRModel(), fileTranscriptionModel(), imageTranslationModel() and worlds() alongside the portable text, embedding, image, video, speech and realtime factories. Dedicated models reject accidental use through the chat factory. Cloud Audio TTS/streaming ASR use binary inference WebSockets; optional binary methods extend the existing connection factory contract.
See the support matrix, usage and validation boundaries, Decision model with a small measured comparison, and native ASR contract. Public Cloud-specific types are exported from this package. Live tests are opt-in and favor tiny requests and Flash/Lite models.
Detailed contracts: audio and realtime, embeddings, and media and worlds. Use qwen.temporaryKeys.create({ expiresInSeconds: 60 }) on your server to issue short-lived tokens for client SDKs; the returned token inherits the primary key permissions. inferenceURL explicitly overrides binary speech transport when using a custom gateway.
Responses argument and stream errors
Responses function arguments must be JSON objects. Both generate and stream
reject invalid, empty or missing arguments with ProviderToolCallError, provider
qwen, transport responses, and diagnostic code
QWEN_RESPONSES_TOOL_CALL_INVALID. Reasons use the shared finite error contract;
non-object JSON uses invalid_json. Errors contain a fixed message, no raw
arguments or parser cause, and only validated terminal token accounting when
available. They are not automatically retried or repaired.
The streaming adapter buffers function calls until the response terminates and
validates the entire batch before emitting any tool-call. A malformed member,
incomplete response, truncated stream or contradictory completion rejects the
batch. Matching duplicate completion events materialize a call only once. Valid
call IDs and Qwen receipt metadata are preserved. Text and provider events may
already have been emitted; this does not undo prior steps or hosted effects.
effectsPossible is conservative when provider-side items have been observed;
the shared runtime additionally accounts for effects in prior local steps.
Cancellation keeps its original error instead of becoming a parser failure.
Malformed SSE JSON in Responses or Chat throws a separate bounded
QwenStreamEventError (QWEN_SSE_EVENT_INVALID, reason invalid_json or
invalid_event, explicit transport). It never retains the event body or a
sensitive SyntaxError cause. This diagnostic fix does not establish the cause
of historical Harness failures or certify model behavior.
Offline regression: bun run test packages/qwen/tests/responses-invalid-arguments.test.ts.
For an installed-artifact check, build and pack Core/Qwen with bun pm pack,
install both tarballs into an isolated consumer with a Core override pointing to
the same local tarball, copy scripts/qwen-responses-packed-smoke.mjs into that
consumer and run it with Bun. The smoke covers ten valid/invalid generate,
stream and SSE cases without provider credentials. Local tarball results do not
constitute registry publication or a live Harness matrix.
Bounded Responses search and citations
For documented Qwen 3.5/3.6/3.7/3.8 Responses model IDs, Qwen3-Max and supported
DeepSeek/GLM/Kimi Responses profiles, maxTokens maps to max_output_tokens when
using Responses, including hosted qwenWebSearchTool(). The limit must be a safe
integer of at least 16. A providerOptions.max_output_tokens limit is also
validated; conflicting limits are rejected. Unknown families cannot silently
ignore a requested limit. Automatic mode retains Chat for ordinary requests with
maxTokens alone; hosted search or explicit apiMode: "responses" uses Responses.
Thinking budgets and other Chat-only options remain incompatible with hosted tools.
In Qwen 3.8 the limit includes reasoning plus answer tokens; the other documented
families limit answer tokens. incomplete responses finish with length, preserving
usage; incomplete callable batches still raise the existing typed error.
Streaming emits public URL annotations from response.content_part.done,
response.output_item.done and terminal snapshots as:
{"type":"provider-data","provider":"qwen","data":{"type":"response.annotations","itemId":"msg_1","outputIndex":0,"contentIndex":0,"annotations":[{"type":"url_citation","url":"https://example.com","title":"Source","start_index":0,"end_index":5}]}}Append these annotation deltas per output/content index; identical citations are emitted once per response. Message IDs and character indices are retained when available. Empty annotations do not produce fabricated sources. Only public URL citation fields are forwarded, not message bodies, logprobs or arbitrary metadata. Limits: 1024 annotations per part, 4096 distinct citations per response, 8192 URL characters, 16384 title characters and 1024 ID characters. Invalid/unsupported annotations and fields beyond these bounds are omitted. Non-streaming messages carry the same data part; citation parts are never replayed as Responses input. Partial citations already delivered remain available if the stream fails.
Hosted search results are also emitted as provider-data with
data.type: "web_search_call", preserving data.action.sources. These items are
recovered from terminal response.completed/response.incomplete snapshots even
when no intermediate item event included the sources. Identical item snapshots
are deduplicated by provider item ID (or output index when the ID is absent).
If the terminal item adds sources, its updated snapshot is emitted too; consumers
should replace the earlier snapshot for that item rather than append it as a new
search call. Search counts come from the usage counter, not the source list.
The Responses contract notes that annotations are usually empty. This transport support does not certify that every model/region returns citations, nor does token usage include hosted-search charges. Consumers must certify their chosen route before enabling it.
Internal search counters and limits
generate() message parts and streaming provider-data preserve
usage.x_tools.web_search.count as Core/SDK HostedToolUsage data:
{"type":"hosted-tool-usage","audience":"internal","provider":"qwen","route":"responses","tool":"web_search","attemptId":"sdk-invocation-id","responseId":"upstream-id","unit":"call","quantity":2,"source":"usage.x_tools.web_search.count","aggregation":"snapshot","completeness":"complete","terminal":true}A nonnegative safe integer is reported, including zero. Missing/invalid counts
produce completeness: "unknown" with no quantity, even if citations are empty.
Search-enabled streams start with an unknown nonterminal record; their terminal
record replaces it. Identical terminal snapshots are not double-counted.
IDs are bounded to 1024 characters. No raw queries or message payloads are added.
They are not replayed into provider input. Follow the
shared metering contract
for snapshot aggregation, retries, budgeting and UI filtering.
model.capabilities.hostedTools describes Responses metering as provider-counter
and its effective hosted-search limit as unverified. max_tool_calls and
web_search config limits max_tool_calls/max_uses are rejected rather than
silently relying on OpenAI compatibility. maxTokens limits output tokens only.
The documented limit on Qwen's separate Anthropic-compatible route does not
certify Responses and is not exposed by this adapter.
Counter evidence: Model Studio web search.
Experimental portable Decisions
qwen.experimentalDecisionModel('decision-model-preview') exposes the shared
DecisionModel contract. It maps predicate to native noul, preserves fractional
scores and uses only the configured decision endpoint. Its text-only and string
choice capabilities are explicit. Native qwen.decisionModel() is unchanged.
See portable Decisions.
