fix(qoder): Cosy auth fallback for PAT tokens + vision support for qwen3-vl-plus (#2629)

Integrated into release/v3.8.3
This commit is contained in:
Hernan Javier Ardila Sanchez
2026-05-23 22:06:35 +02:00
committed by GitHub
parent fcc21aaa61
commit 6bd247bbb4
4 changed files with 112 additions and 4 deletions

View File

@@ -278,6 +278,53 @@ test("QoderExecutor: non-stream calls target DashScope for non-PAT tokens and ma
}
});
test("QoderExecutor: PAT token falls back to Cosy auth when Bearer returns 401", async () => {
const executor = new QoderExecutor();
const originalFetch = globalThis.fetch;
let callCount = 0;
globalThis.fetch = async (url, options) => {
callCount++;
if (callCount === 1) {
// First call to api.qoder.com returns 401 TOKEN_INVALID
assert.equal(String(url), "https://api.qoder.com/v1/chat/completions");
assert.equal(options.headers.Authorization, "Bearer pt-0pUI-test-token");
return new Response(
JSON.stringify({ code: "TOKEN_INVALID", message: "invalid apikey" }),
{ status: 401, headers: { "Content-Type": "application/json" } }
);
}
// Second call to api1.qoder.sh (Cosy fallback) returns SSE response
assert.ok(String(url).includes("api1.qoder.sh"));
assert.ok(String(url).includes("agent_chat_generation"));
assert.ok(options.headers["Cosy-Key"]);
assert.ok(options.headers["Cosy-User"]);
assert.ok(options.headers["Cosy-Date"]);
assert.ok(options.headers.Authorization.startsWith("Bearer COSY."));
return new Response(
"data: {\"message\":{\"content\":\"Hello from Cosy\"}}\n\ndata: [DONE]\n\n",
{ status: 200, headers: { "Content-Type": "text/event-stream" } }
);
};
try {
const { response, url } = await executor.execute({
model: "qwen3.5-plus",
body: { messages: [{ role: "user", content: "Reply with OK only." }] },
stream: false,
credentials: { apiKey: "pt-0pUI-test-token" },
});
assert.equal(callCount, 2, "Should have made 2 fetch calls (1 Bearer + 1 Cosy)");
assert.equal(response.status, 200);
const payload = await response.json();
assert.equal(payload.object, "chat.completion");
assert.equal(payload.choices[0].message.content, "Hello from Cosy");
} finally {
globalThis.fetch = originalFetch;
}
});
test("QoderExecutor: stream calls pass through successful SSE responses", async () => {
const executor = new QoderExecutor();
const originalFetch = globalThis.fetch;