| import { DefaultExecutor } from "./default.js"; |
| import { PROVIDERS } from "../config/providers.js"; |
| import { OAUTH_ENDPOINTS } from "../config/appConstants.js"; |
|
|
| |
| 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"; |
| } |
|
|
| |
| 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"); |
| } |
|
|
| |
| |
| 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); |
| } |
|
|
| |
| 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; |
|
|