diff --git a/open-sse/config/providers.js b/open-sse/config/providers.js index f995601..f6cd561 100644 --- a/open-sse/config/providers.js +++ b/open-sse/config/providers.js @@ -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" diff --git a/open-sse/executors/index.js b/open-sse/executors/index.js index f2be733..0c314fb 100644 --- a/open-sse/executors/index.js +++ b/open-sse/executors/index.js @@ -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"; diff --git a/open-sse/executors/qwen.js b/open-sse/executors/qwen.js new file mode 100644 index 0000000..2cadc41 --- /dev/null +++ b/open-sse/executors/qwen.js @@ -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;