import { DefaultExecutor } from "./default.js"; import { PROVIDERS } from "../config/providers.js"; import { OAUTH_ENDPOINTS } from "../config/appConstants.js"; /** portal.qwen.ai — static fingerprint matching stable Qwen Code release */ const QWEN_USER_AGENT = "QwenCode/0.12.3 (linux; x64)"; const QWEN_STAINLESS = { os: "Linux", arch: "x64", lang: "js", runtime: "node", runtimeVersion: "v18.19.1", packageVersion: "5.11.0", retryCount: "1" }; const QWEN_DEFAULT_SYSTEM_MESSAGE = { role: "system", content: [{ type: "text", text: "", cache_control: { type: "ephemeral" } }] }; function ensureQwenSystemMessage(body) { if (!body || typeof body !== "object") return body; const next = { ...body }; if (Array.isArray(next.messages)) { next.messages = [QWEN_DEFAULT_SYSTEM_MESSAGE, ...next.messages]; } else { next.messages = [QWEN_DEFAULT_SYSTEM_MESSAGE]; } return next; } function isQwenThinkingActive(body) { const thinking = body?.thinking; if (thinking === true || body?.enable_thinking === true) return true; return typeof thinking === "object" && thinking !== null && !Array.isArray(thinking) && thinking.type === "enabled"; } // Qwen rejects tool_choice="required" or object forms when thinking is active; neutralize to "auto". function sanitizeQwenThinkingToolChoice(body) { if (!isQwenThinkingActive(body)) return body; const tc = body.tool_choice; const incompatible = tc === "required" || (typeof tc === "object" && tc !== null); if (!incompatible) return body; return { ...body, tool_choice: "auto" }; } function buildQwenUpstreamHeaders(credentials, stream = true) { const token = credentials?.apiKey || credentials?.accessToken || ""; const headers = { "Content-Type": "application/json", Authorization: `Bearer ${token}`, "User-Agent": QWEN_USER_AGENT, "X-DashScope-AuthType": "qwen-oauth", "X-DashScope-CacheControl": "enable", "X-DashScope-UserAgent": QWEN_USER_AGENT, "X-Stainless-Arch": QWEN_STAINLESS.arch, "X-Stainless-Lang": QWEN_STAINLESS.lang, "X-Stainless-Os": QWEN_STAINLESS.os, "X-Stainless-Package-Version": QWEN_STAINLESS.packageVersion, "X-Stainless-Retry-Count": QWEN_STAINLESS.retryCount, "X-Stainless-Runtime": QWEN_STAINLESS.runtime, "X-Stainless-Runtime-Version": QWEN_STAINLESS.runtimeVersion, Connection: "keep-alive", "Accept-Language": "*", "Sec-Fetch-Mode": "cors" }; headers.Accept = stream ? "text/event-stream" : "application/json"; return headers; } export class QwenExecutor extends DefaultExecutor { constructor() { super("qwen"); } // Qwen tokens are bound to a resource_url returned at OAuth time. // Using portal.qwen.ai when the token is issued for another shard returns 401/403. buildUrl(model, stream, urlIndex = 0, credentials = null) { const resourceUrl = credentials?.providerSpecificData?.resourceUrl; const host = resourceUrl ? resourceUrl.replace(/^https?:\/\//, "").replace(/\/$/, "") : "portal.qwen.ai"; return `https://${host}/v1/chat/completions`; } buildHeaders(credentials, stream = true) { return buildQwenUpstreamHeaders(credentials, stream); } transformRequest(model, body, stream, credentials) { let next = body && typeof body === "object" ? { ...body } : body; if (stream && next?.messages && !next.stream_options && !next.thinking && !next.enable_thinking && next.stream !== false) { next.stream_options = { include_usage: true }; } next = sanitizeQwenThinkingToolChoice(next); return ensureQwenSystemMessage(next); } // Override to capture resource_url from refresh response (required for buildUrl). async refreshCredentials(credentials, log) { if (!credentials?.refreshToken) return null; try { const response = await fetch(OAUTH_ENDPOINTS.qwen.token, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json" }, body: new URLSearchParams({ grant_type: "refresh_token", refresh_token: credentials.refreshToken, client_id: PROVIDERS.qwen.clientId }) }); if (!response.ok) return null; const tokens = await response.json(); log?.info?.("TOKEN", "qwen refreshed"); return { accessToken: tokens.access_token, refreshToken: tokens.refresh_token || credentials.refreshToken, expiresIn: tokens.expires_in, providerSpecificData: { ...(credentials.providerSpecificData || {}), ...(tokens.resource_url ? { resourceUrl: tokens.resource_url } : {}) } }; } catch (error) { log?.error?.("TOKEN", `qwen refresh error: ${error.message}`); return null; } } } export default QwenExecutor;