diff --git a/nodes/src/nodes/llm_qwen/README.md b/nodes/src/nodes/llm_qwen/README.md index 0a0057fc3..4591b0d82 100644 --- a/nodes/src/nodes/llm_qwen/README.md +++ b/nodes/src/nodes/llm_qwen/README.md @@ -6,7 +6,7 @@ A RocketRide LLM node that connects Alibaba Cloud Qwen models to a pipeline via Provides Qwen chat completions to the pipeline. Used primarily as an `llm` invoke connection by agents and other nodes that need an LLM, and can also be used directly via lanes. -Uses **LangChain's `ChatOpenAI`** client pointed at DashScope's OpenAI-compatible endpoint. The regional endpoint is resolved from the `region` field at startup. Temperature is fixed at `0`, and `max_tokens` is taken from the profile's `modelOutputTokens`. +Uses **LangChain's `ChatOpenAI`** client pointed at DashScope's OpenAI-compatible endpoint. The endpoint is resolved at startup from the `base_url` field if set, otherwise from the `region` field. Temperature is fixed at `0`, and `max_tokens` is taken from the profile's `modelOutputTokens`. When the node configuration is validated, the node performs a live 1-token test request against the API to verify the key, model, and region actually work. Failures surface as configuration warnings with the provider's error message. @@ -29,6 +29,7 @@ The main setting is the **profile** (model selection, default `qwen-flash`). Eac | `profile` | enum, `qwen-flash` | Qwen AI model selection (see profiles below, or `custom`) | | `apikey` | string | DashScope API key. Must start with `sk-`. | | `region` | enum, `us` | DashScope regional endpoint: `us`, `intl`, or `cn` (see regions below) | +| `base_url` | string, empty | Optional endpoint override, wins over `region` (see regions below) | | `model` | string | Qwen model name (custom profile only) | | `modelTotalTokens` | number | Maximum context length in tokens (custom profile only, must be > 0) | @@ -36,20 +37,41 @@ The main setting is the **profile** (model selection, default `qwen-flash`). Eac ## Profiles -| Profile | Model | Context tokens | Output tokens | -|---------------------------------|---------------------------------|----------------|---------------| -| Qwen Flash *(default)* | `qwen-flash` | 131,072 | 4,096 | -| Qwen Plus | `qwen-plus` | 1,000,000 | 32,768 | -| Qwen2.5 72B Instruct | `qwen-2.5-72b-instruct` | 32,768 | 16,384 | -| Qwen2.5 7B Instruct | `qwen-2.5-7b-instruct` | 32,768 | 32,768 | -| Qwen2.5 Coder 32B Instruct | `qwen-2.5-coder-32b-instruct` | 32,768 | 4,096 | -| Qwen-Max | `qwen-max` | 32,768 | 8,192 | -| Qwen Plus 0728 | `qwen-plus-2025-07-28` | 1,000,000 | 32,768 | -| Qwen Plus 0728 (thinking) | `qwen-plus-2025-07-28:thinking` | 1,000,000 | 32,768 | -| Qwen-Turbo | `qwen-turbo` | 131,072 | 8,192 | +Profiles come in two kinds. + +**Stable aliases** always resolve to DashScope's current snapshot for their tier, so they do not go stale as new generations ship. Prefer these unless you need to pin a specific release: + +| Profile | Model | Context tokens | Output tokens | +|----------------------------------|---------------|----------------|---------------| +| Qwen Flash (latest) *(default)* | `qwen-flash` | 131,072 | 4,096 | +| Qwen Plus (latest) | `qwen-plus` | 1,000,000 | 32,768 | +| Qwen Max (latest) | `qwen-max` | 32,768 | 8,192 | +| Qwen Turbo (latest) | `qwen-turbo` | 131,072 | 8,192 | + +**Pinned releases** name a specific model version: + +| Profile | Model | Context tokens | Output tokens | +|------------------------|------------------------|----------------|---------------| +| Qwen3.7 Max | `qwen3.7-max` | 1,000,000 | 65,536 | +| Qwen3.7 Plus | `qwen3.7-plus` | 1,000,000 | 65,536 | +| Qwen3.6 Flash | `qwen3.6-flash` | 1,000,000 | 65,536 | +| Qwen Plus 0728 | `qwen-plus-2025-07-28` | 1,000,000 | 32,768 | Choose `custom` to set the model name and context length manually. +### Deprecated profiles + +These remain selectable so saved pipelines keep loading, but DashScope rejects their model IDs. They were introduced by OpenRouter fallback discovery in the model sync and carry OpenRouter/HuggingFace IDs rather than DashScope ones. Migrate to a profile above. + +| Profile | Model | Why it fails | +|----------------------------|---------------------------------|-----------------------------------------------------| +| Qwen2.5 72B Instruct | `qwen-2.5-72b-instruct` | DashScope uses `qwen2.5-72b-instruct` | +| Qwen2.5 7B Instruct | `qwen-2.5-7b-instruct` | DashScope uses `qwen2.5-7b-instruct` | +| Qwen2.5 Coder 32B Instruct | `qwen-2.5-coder-32b-instruct` | DashScope uses `qwen2.5-coder-32b-instruct` | +| Qwen Plus 0728 (thinking) | `qwen-plus-2025-07-28:thinking` | `:thinking` is OpenRouter variant syntax | + +DashScope has no `:thinking` model variants — reasoning is controlled with the `enable_thinking` request parameter instead. + --- ## Regions @@ -64,6 +86,8 @@ Choose `custom` to set the model name and context length manually. The default is `us`. An unrecognised value falls back to the US endpoint. +Setting `base_url` overrides this table entirely, which is how you reach a DashScope host Alibaba Cloud serves outside the three above — another Alibaba Cloud region, for instance. It is available on every live profile, including `custom`; the deprecated profiles above do not expose it. Leave it empty to use the regional endpoint. + Note: DashScope API keys are not interchangeable between regions. A key issued for one region will fail authentication against another region's endpoint. --- @@ -87,9 +111,22 @@ Rate-limit and connection errors are classified as retryable by the shared chat --- +## Keeping the model list current + +Profiles are maintained by the model sync tool, see [tools/sync_models](../../../../tools/sync_models/README.md): + +```bash +python tools/sync_models/src/sync_models.py --provider llm_qwen --enable-discovery --apply +``` + +Discovery — adding profiles — requires `ROCKETRIDE_QWEN_KEY`. Without it the command above still runs, but only enriches profiles that already exist: OpenRouter and LiteLLM can supply token counts, and neither may add a profile unless you also pass `--allow-fallback-discovery`. Avoid that flag here — it lets OpenRouter contribute the HuggingFace-style IDs DashScope does not accept, which is what the deprecated profiles above are. The stable aliases are listed in `protected_profiles` so a non-authoritative source cannot deprecate them. + +--- + ## Upstream docs - [DashScope API reference](https://help.aliyun.com/zh/dashscope/) +- [Alibaba Cloud Model Studio models](https://www.alibabacloud.com/help/en/model-studio/models) --- diff --git a/nodes/src/nodes/llm_qwen/qwen_client.py b/nodes/src/nodes/llm_qwen/qwen_client.py index cc97969ed..adcabdcb4 100644 --- a/nodes/src/nodes/llm_qwen/qwen_client.py +++ b/nodes/src/nodes/llm_qwen/qwen_client.py @@ -61,9 +61,13 @@ def __init__(self, provider: str, connConfig: Dict[str, Any], bag: Dict[str, Any if not apikey or not apikey.startswith('sk-'): raise ValueError('Invalid DashScope API key. Key must start with "sk-".') - # Resolve regional endpoint - region = config.get('region', 'us') - base_url = DASHSCOPE_REGIONS.get(region, DASHSCOPE_REGIONS['us']) + # Resolve the endpoint, an explicit base_url wins over the regional map + # so a profile can reach a host outside the compatible-mode endpoints + base_url = (config.get('base_url') or '').strip() + + if not base_url: + region = config.get('region', 'us') + base_url = DASHSCOPE_REGIONS.get(region, DASHSCOPE_REGIONS['us']) # Get the llm using OpenAI-compatible endpoint self._llm = ChatOpenAI( diff --git a/nodes/src/nodes/llm_qwen/services.json b/nodes/src/nodes/llm_qwen/services.json index 2f0805911..06ee3d6d9 100644 --- a/nodes/src/nodes/llm_qwen/services.json +++ b/nodes/src/nodes/llm_qwen/services.json @@ -47,7 +47,7 @@ // Optional: // Description to of this driver // - "description": ["A component that connects to Alibaba Cloud's Qwen large language models via the ", "DashScope API. It supports tasks such as text generation, summarization, question ", "answering, and chat-based interactions. With access to models like Qwen Plus ", "and Qwen Flash, this component enables high-quality, context-aware responses. Configurable ", "with API keys, it allows seamless integration into AI-driven workflows with scalable ", "performance."], + "description": ["A component that connects to Alibaba Cloud's Qwen large language models via the ", "DashScope API. It supports tasks such as text generation, summarization, question ", "answering, and chat-based interactions. With access to models like Qwen Max, Qwen Plus ", "and Qwen Flash, this component enables high-quality, context-aware responses. Configurable ", "with API keys, it allows seamless integration into AI-driven workflows with scalable ", "performance."], // // Optional: // The icon is the icon to display in the UI for this node @@ -79,93 +79,159 @@ // specified, unless the profile is 'absolute' "default": "qwen-flash", // Defines profiles used with the "profile": key + // + // Two kinds of profile live here: + // + // * Stable aliases (qwen-max, qwen-plus, qwen-flash, qwen-turbo) always + // resolve to DashScope's current snapshot for that tier, so they never + // go stale. They are listed in protected_profiles in + // tools/sync_models/src/sync_models.config.json so a non-authoritative + // source cannot deprecate them. + // * Pinned generation IDs (qwen3.x) name a specific model release. + // "profiles": { + "qwen3-7-max": { + "title": "Qwen3.7 Max", + "model": "qwen3.7-max", + "modelSource": "manual", + "modelTotalTokens": 1000000, // openrouter + "modelOutputTokens": 65536, // openrouter + "region": "us", + "base_url": "", + "apikey": "", + "capabilities": { + "reasoning": true + } + }, + "qwen3-7-plus": { + "title": "Qwen3.7 Plus", + "model": "qwen3.7-plus", + "modelSource": "manual", + "modelTotalTokens": 1000000, // openrouter + "modelOutputTokens": 65536, // openrouter + "region": "us", + "base_url": "", + "apikey": "", + "capabilities": { + "reasoning": true + } + }, + "qwen3-6-flash": { + "title": "Qwen3.6 Flash", + "model": "qwen3.6-flash", + "modelSource": "manual", + "modelTotalTokens": 1000000, // openrouter + "modelOutputTokens": 65536, // openrouter + "region": "us", + "base_url": "", + "apikey": "", + "capabilities": { + "reasoning": true + } + }, + "qwen-max": { + "title": "Qwen Max (latest)", + "model": "qwen-max", + "modelSource": "manual", + "modelTotalTokens": 32768, // manual + "modelOutputTokens": 8192, // manual + "region": "us", + "base_url": "", + "apikey": "" + }, "qwen-plus": { - "title": "Qwen Plus", + "title": "Qwen Plus (latest)", "model": "qwen-plus", "modelSource": "manual", "modelTotalTokens": 1000000, // openrouter "modelOutputTokens": 32768, // openrouter "region": "us", + "base_url": "", "apikey": "", "capabilities": { "reasoning": true } }, "qwen-flash": { - "title": "Qwen Flash", + "title": "Qwen Flash (latest)", "model": "qwen-flash", "modelSource": "manual", "modelTotalTokens": 131072, // manual "modelOutputTokens": 4096, // manual "region": "us", + "base_url": "", + "apikey": "" + }, + "qwen-turbo": { + "title": "Qwen Turbo (latest)", + "model": "qwen-turbo", + "modelSource": "manual", + "modelTotalTokens": 131072, // manual + "modelOutputTokens": 8192, // manual + "region": "us", + "base_url": "", "apikey": "" }, + "qwen-plus-2025-07-28": { + "title": "Qwen Plus 0728", + "model": "qwen-plus-2025-07-28", + "modelSource": "manual", + "modelTotalTokens": 1000000, // openrouter + "modelOutputTokens": 32768, // openrouter + "region": "us", + "base_url": "", + "apikey": "", + "capabilities": { + "reasoning": true + } + }, + // + // The profiles below were introduced by OpenRouter fallback discovery and + // carry OpenRouter/HuggingFace model IDs that the DashScope API rejects. + // They are deprecated rather than deleted so saved pipelines keep loading. + // "qwen-2-5-72b-instruct": { "title": "Qwen2.5 72B Instruct", "model": "qwen-2.5-72b-instruct", "modelSource": "openrouter", - "modelTotalTokens": 131072, // openrouter + "modelTotalTokens": 32768, // openrouter "modelOutputTokens": 16384, // openrouter + "deprecated": true, + "migration": "Not a valid DashScope model ID (this is an OpenRouter alias; DashScope uses qwen2.5-72b-instruct). Select Qwen Plus or a qwen3.x profile.", "apikey": "" }, "qwen-2-5-7b-instruct": { - "title": "Qwen: Qwen2.5 7B Instruct", + "title": "Qwen2.5 7B Instruct", "model": "qwen-2.5-7b-instruct", "modelSource": "openrouter", - "modelTotalTokens": 131072, // openrouter + "modelTotalTokens": 32768, // openrouter "modelOutputTokens": 32768, // openrouter + "deprecated": true, + "migration": "Not a valid DashScope model ID (this is an OpenRouter alias; DashScope uses qwen2.5-7b-instruct). Select Qwen Flash or a qwen3.x profile.", "apikey": "" }, "qwen-2-5-coder-32b-instruct": { "title": "Qwen2.5 Coder 32B Instruct", "model": "qwen-2.5-coder-32b-instruct", "modelSource": "openrouter", - "modelTotalTokens": 128000, // openrouter + "modelTotalTokens": 32768, // openrouter "modelOutputTokens": 32768, // openrouter - "apikey": "" - }, - "qwen-max": { - "title": "Qwen: Qwen-Max ", - "model": "qwen-max", - "modelSource": "openrouter", - "modelTotalTokens": 32768, // manual - "modelOutputTokens": 8192, // manual "deprecated": true, - "migration": "Model no longer listed in OpenRouter. Please select a current model.", + "migration": "Not a valid DashScope model ID (this is an OpenRouter alias; DashScope uses qwen2.5-coder-32b-instruct). Select a qwen3.x profile.", "apikey": "" }, - "qwen-plus-2025-07-28": { - "title": "Qwen: Qwen Plus 0728", - "model": "qwen-plus-2025-07-28", - "modelSource": "openrouter", - "modelTotalTokens": 1000000, // openrouter - "modelOutputTokens": 32768, // openrouter - "apikey": "", - "capabilities": { - "reasoning": true - } - }, "qwen-plus-2025-07-28-thinking": { - "title": "Qwen: Qwen Plus 0728 (thinking)", + "title": "Qwen Plus 0728 (thinking)", "model": "qwen-plus-2025-07-28:thinking", "modelSource": "openrouter", "modelTotalTokens": 1000000, // openrouter "modelOutputTokens": 32768, // openrouter + "deprecated": true, + "migration": "The ':thinking' suffix is OpenRouter variant syntax and is rejected by DashScope, which controls reasoning with the enable_thinking parameter. Select Qwen Plus 0728 or a qwen3.x profile.", "apikey": "", "capabilities": { "reasoning": true } - }, - "qwen-turbo": { - "title": "Qwen: Qwen-Turbo", - "model": "qwen-turbo", - "modelSource": "openrouter", - "modelTotalTokens": 131072, // manual - "modelOutputTokens": 8192, // manual - "deprecated": true, - "migration": "Model no longer listed in OpenRouter. Please select a current model.", - "apikey": "" } } }, @@ -206,6 +272,15 @@ ] ] }, + "qwen.base_url": { + "type": "string", + "title": "Base URL override", + "default": "", + "description": "Optional. Overrides the endpoint selected by Region. Leave empty to use the regional endpoint. Set this to reach a DashScope host other than the three listed above, for example another Alibaba Cloud region.", + "ui": { + "ui:placeholder": "https://dashscope-intl.aliyuncs.com/compatible-mode/v1" + } + }, "qwen.custom": { "object": "custom", "properties": [ @@ -213,54 +288,70 @@ "modelTotalTokens", "llm.cloud.apikey", "qwen.region", + "qwen.base_url", "llm.cloud.modelSource" ] }, - "qwen.qwen-plus": { - "object": "qwen-plus", + "qwen.qwen3-7-max": { + "object": "qwen3-7-max", "properties": [ "llm.cloud.apikey", "qwen.region", + "qwen.base_url", "llm.cloud.modelSource" ] }, - "qwen.qwen-flash": { - "object": "qwen-flash", + "qwen.qwen3-7-plus": { + "object": "qwen3-7-plus", "properties": [ "llm.cloud.apikey", "qwen.region", + "qwen.base_url", "llm.cloud.modelSource" ] }, - "qwen.qwen-2-5-72b-instruct": { - "object": "qwen-2-5-72b-instruct", + "qwen.qwen3-6-flash": { + "object": "qwen3-6-flash", "properties": [ "llm.cloud.apikey", "qwen.region", + "qwen.base_url", "llm.cloud.modelSource" ] }, - "qwen.qwen-2-5-7b-instruct": { - "object": "qwen-2-5-7b-instruct", + "qwen.qwen-max": { + "object": "qwen-max", "properties": [ "llm.cloud.apikey", "qwen.region", + "qwen.base_url", "llm.cloud.modelSource" ] }, - "qwen.qwen-2-5-coder-32b-instruct": { - "object": "qwen-2-5-coder-32b-instruct", + "qwen.qwen-plus": { + "object": "qwen-plus", "properties": [ "llm.cloud.apikey", "qwen.region", + "qwen.base_url", "llm.cloud.modelSource" ] }, - "qwen.qwen-max": { - "object": "qwen-max", + "qwen.qwen-flash": { + "object": "qwen-flash", "properties": [ "llm.cloud.apikey", "qwen.region", + "qwen.base_url", + "llm.cloud.modelSource" + ] + }, + "qwen.qwen-turbo": { + "object": "qwen-turbo", + "properties": [ + "llm.cloud.apikey", + "qwen.region", + "qwen.base_url", "llm.cloud.modelSource" ] }, @@ -269,19 +360,36 @@ "properties": [ "llm.cloud.apikey", "qwen.region", + "qwen.base_url", "llm.cloud.modelSource" ] }, - "qwen.qwen-plus-2025-07-28-thinking": { - "object": "qwen-plus-2025-07-28-thinking", + "qwen.qwen-2-5-72b-instruct": { + "object": "qwen-2-5-72b-instruct", "properties": [ "llm.cloud.apikey", "qwen.region", "llm.cloud.modelSource" ] }, - "qwen.qwen-turbo": { - "object": "qwen-turbo", + "qwen.qwen-2-5-7b-instruct": { + "object": "qwen-2-5-7b-instruct", + "properties": [ + "llm.cloud.apikey", + "qwen.region", + "llm.cloud.modelSource" + ] + }, + "qwen.qwen-2-5-coder-32b-instruct": { + "object": "qwen-2-5-coder-32b-instruct", + "properties": [ + "llm.cloud.apikey", + "qwen.region", + "llm.cloud.modelSource" + ] + }, + "qwen.qwen-plus-2025-07-28-thinking": { + "object": "qwen-plus-2025-07-28-thinking", "properties": [ "llm.cloud.apikey", "qwen.region", @@ -304,39 +412,45 @@ ] }, { - "value": "qwen-plus", + "value": "qwen3-7-max", "properties": [ - "qwen.qwen-plus" + "qwen.qwen3-7-max" ] }, { - "value": "qwen-flash", + "value": "qwen3-7-plus", "properties": [ - "qwen.qwen-flash" + "qwen.qwen3-7-plus" ] }, { - "value": "qwen-2-5-72b-instruct", + "value": "qwen3-6-flash", "properties": [ - "qwen.qwen-2-5-72b-instruct" + "qwen.qwen3-6-flash" ] }, { - "value": "qwen-2-5-7b-instruct", + "value": "qwen-max", "properties": [ - "qwen.qwen-2-5-7b-instruct" + "qwen.qwen-max" ] }, { - "value": "qwen-2-5-coder-32b-instruct", + "value": "qwen-plus", "properties": [ - "qwen.qwen-2-5-coder-32b-instruct" + "qwen.qwen-plus" ] }, { - "value": "qwen-max", + "value": "qwen-flash", "properties": [ - "qwen.qwen-max" + "qwen.qwen-flash" + ] + }, + { + "value": "qwen-turbo", + "properties": [ + "qwen.qwen-turbo" ] }, { @@ -346,15 +460,27 @@ ] }, { - "value": "qwen-plus-2025-07-28-thinking", + "value": "qwen-2-5-72b-instruct", "properties": [ - "qwen.qwen-plus-2025-07-28-thinking" + "qwen.qwen-2-5-72b-instruct" ] }, { - "value": "qwen-turbo", + "value": "qwen-2-5-7b-instruct", "properties": [ - "qwen.qwen-turbo" + "qwen.qwen-2-5-7b-instruct" + ] + }, + { + "value": "qwen-2-5-coder-32b-instruct", + "properties": [ + "qwen.qwen-2-5-coder-32b-instruct" + ] + }, + { + "value": "qwen-plus-2025-07-28-thinking", + "properties": [ + "qwen.qwen-plus-2025-07-28-thinking" ] } ] diff --git a/tools/sync_models/src/sync_models.config.json b/tools/sync_models/src/sync_models.config.json index f6dfca033..26e334608 100644 --- a/tools/sync_models/src/sync_models.config.json +++ b/tools/sync_models/src/sync_models.config.json @@ -363,18 +363,26 @@ "auth_header": "Authorization", "auth_prefix": "Bearer", "smoke_test": "chat", + // "qwen-" alone excludes every current model: DashScope names the modern + // families qwen3-max, qwen3.6-plus, qwen3.7-max, qwen3.8-max-preview — + // all of which start with "qwen3", not "qwen-". Match on the bare vendor + // name so new generations are picked up without another config change. "model_filter": { - "include_prefixes": ["qwen-"], + "include_prefixes": ["qwen"], "exclude_prefixes": [], - "exclude_patterns": ["embed", "vision", "audio", "vl"] + "exclude_patterns": ["embed", "vision", "audio", "vl", "omni", "ocr", "asr", "tts"] }, - // qwen-flash and qwen-plus are DashScope short aliases. OpenRouter uses - // full HuggingFace IDs (qwen-2.5-7b-instruct etc.) and doesn't list these - // aliases — protect them so the OpenRouter fallback won't deprecate them. + // qwen-max, qwen-plus, qwen-flash and qwen-turbo are DashScope stable + // aliases that always resolve to the current snapshot. OpenRouter lists + // full HuggingFace IDs (qwen-2.5-7b-instruct etc.) and not these aliases, + // so without protection the OpenRouter fallback deprecates working models + // — which is exactly what happened to qwen-max and qwen-turbo. "protected_profiles": [ ["custom", "2126-04-09"], - ["qwen-plus", "2026-10-09"], - ["qwen-flash", "2026-10-09"] + ["qwen-max", "2126-04-09"], + ["qwen-plus", "2126-04-09"], + ["qwen-flash", "2126-04-09"], + ["qwen-turbo", "2126-04-09"] ], "token_limit_overrides": { // "qwen-plus": 131072,