Fix : Qwen provider

This commit is contained in:
decolua
2026-04-04 12:38:39 +07:00
parent d84489dba4
commit 2b1faeb2c6
3 changed files with 84 additions and 4 deletions

View File

@@ -79,10 +79,6 @@ export const PROVIDERS = {
qwen: {
baseUrl: "https://portal.qwen.ai/v1/chat/completions",
format: "openai",
headers: {
"User-Agent": "google-api-nodejs-client/9.15.1",
"X-Goog-Api-Client": "gl-node/22.17.0"
},
clientId: "f0304373b74a44d2b584a3fb70ca9e56",
tokenUrl: "https://chat.qwen.ai/api/v1/oauth2/token",
authUrl: "https://chat.qwen.ai/api/v1/oauth2/device/code"

View File

@@ -7,6 +7,7 @@ import { KiroExecutor } from "./kiro.js";
import { CodexExecutor } from "./codex.js";
import { CursorExecutor } from "./cursor.js";
import { VertexExecutor } from "./vertex.js";
import { QwenExecutor } from "./qwen.js";
import { DefaultExecutor } from "./default.js";
const executors = {
@@ -21,6 +22,7 @@ const executors = {
cu: new CursorExecutor(), // Alias for cursor
vertex: new VertexExecutor("vertex"),
"vertex-partner": new VertexExecutor("vertex-partner"),
qwen: new QwenExecutor(),
};
const defaultCache = new Map();
@@ -46,3 +48,4 @@ export { CodexExecutor } from "./codex.js";
export { CursorExecutor } from "./cursor.js";
export { VertexExecutor } from "./vertex.js";
export { DefaultExecutor } from "./default.js";
export { QwenExecutor } from "./qwen.js";

View File

@@ -0,0 +1,81 @@
import { platform, arch } from "os";
import { DefaultExecutor } from "./default.js";
/** portal.qwen.ai — aligned with CLIProxyAPI qwen_executor */
const qwenCodeVersion = "0.13.2";
const qwenStainless = {
runtimeVersion: "v22.17.0",
lang: "js",
packageVersion: "5.11.0",
retryCount: "0",
runtime: "node"
};
const qwenDefaultSystemMessage = {
role: "system",
content: [{ type: "text", text: "", cache_control: { type: "ephemeral" } }]
};
function qwenStainlessOsLabel() {
const p = platform();
if (p === "darwin") return "MacOS";
if (p === "win32") return "Windows";
if (p === "linux") return "Linux";
return p;
}
function qwenUserAgent() {
return `QwenCode/${qwenCodeVersion} (${platform()}; ${arch()})`;
}
function ensureQwenSystemMessage(body) {
if (!body || typeof body !== "object") return body;
const next = { ...body };
if (Array.isArray(next.messages)) {
next.messages = [qwenDefaultSystemMessage, ...next.messages];
} else {
next.messages = [qwenDefaultSystemMessage];
}
return next;
}
function buildQwenUpstreamHeaders(credentials, stream = true) {
const token = credentials?.apiKey || credentials?.accessToken || "";
const ua = qwenUserAgent();
const headers = {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
"User-Agent": ua,
"X-DashScope-UserAgent": ua,
"X-Stainless-Runtime-Version": qwenStainless.runtimeVersion,
"X-Stainless-Lang": qwenStainless.lang,
"X-Stainless-Arch": arch(),
"X-Stainless-Package-Version": qwenStainless.packageVersion,
"X-DashScope-CacheControl": "enable",
"X-Stainless-Retry-Count": qwenStainless.retryCount,
"X-Stainless-Os": qwenStainlessOsLabel(),
"X-DashScope-AuthType": "qwen-oauth",
"X-Stainless-Runtime": qwenStainless.runtime
};
headers.Accept = stream ? "text/event-stream" : "application/json";
return headers;
}
export class QwenExecutor extends DefaultExecutor {
constructor() {
super("qwen");
}
buildHeaders(credentials, stream = true) {
return buildQwenUpstreamHeaders(credentials, stream);
}
transformRequest(model, body, stream, credentials) {
const next = body && typeof body === "object" ? { ...body } : body;
if (stream && next?.messages && !next.stream_options) {
next.stream_options = { include_usage: true };
}
return ensureQwenSystemMessage(next);
}
}
export default QwenExecutor;