pi-provider-qoder-cn 0.2.19 → 0.2.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +85 -28
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2388,6 +2388,28 @@ function formatQoderStreamError(statusCode, rawBody, now = Date.now(), mode = "c
|
|
|
2388
2388
|
`- Next step: run /login qoder. You can also refresh credentials by exporting QODER_PERSONAL_ACCESS_TOKEN (PAT).`
|
|
2389
2389
|
].join("\n");
|
|
2390
2390
|
}
|
|
2391
|
+
if (code === "103" || /duplicate request/i.test(message)) {
|
|
2392
|
+
return isCn ? [
|
|
2393
|
+
`[Qoder CN \u91CD\u590D\u8BF7\u6C42] \u7F51\u5173\u628A\u8FD9\u6B21\u8BF7\u6C42\u5224\u5B9A\u4E3A\u91CD\u653E\u5E76\u62D2\u7EDD (\u9519\u8BEF\u7801 103 Duplicate request)`,
|
|
2394
|
+
`- \u539F\u56E0\uFF1A\u540C\u4E00\u4E2A\u5DF2\u7B7E\u540D\u7684\u8BF7\u6C42\u88AB\u518D\u6B21\u63D0\u4EA4\uFF08COSY Authorization/\u65F6\u95F4\u6233/\u7B7E\u540D \u672A\u5237\u65B0\uFF09\uFF0C\u5E76\u975E\u989D\u5EA6\u6216\u51ED\u8BC1\u95EE\u9898\u3002`,
|
|
2395
|
+
`- \u89E3\u51B3\u5EFA\u8BAE\uFF1A\u5BA2\u6237\u7AEF\u5E94\u4E3A\u6BCF\u6B21\u91CD\u8BD5\u91CD\u65B0\u7B7E\u540D\uFF1B\u82E5\u4ECD\u590D\u73B0\uFF0C\u8BF7\u7528 /model \u5207\u6362 Provider \u7A0D\u540E\u91CD\u8BD5\u3002`
|
|
2396
|
+
].join("\n") : [
|
|
2397
|
+
`[Qoder duplicate request] The gateway rejected this request as a replay (code 103 Duplicate request)`,
|
|
2398
|
+
`- Reason: an already-signed request (same COSY Authorization/timestamp/signature) was submitted again. This is a transient server error, not a quota or credential problem.`,
|
|
2399
|
+
`- Next step: every retry must be re-signed; if it keeps repeating, switch provider with /model and try again shortly.`
|
|
2400
|
+
].join("\n");
|
|
2401
|
+
}
|
|
2402
|
+
if (code === "10605" || /"isQueued"\s*:\s*true|isQueued/.test(message)) {
|
|
2403
|
+
return isCn ? [
|
|
2404
|
+
`[Qoder CN \u4E0A\u6E38\u6392\u961F] \u6A21\u578B\u7F51\u5173\u5F53\u524D\u5BB9\u91CF\u5DF2\u6EE1\uFF0C\u8BF7\u6C42\u8FDB\u5165\u7B49\u5F85\u961F\u5217 (\u9519\u8BEF\u7801 10605)`,
|
|
2405
|
+
`- \u8BF4\u660E\uFF1A\u51ED\u8BC1\u4E0E\u989D\u5EA6\u5747\u6B63\u5E38\uFF0C\u7EAF\u7CB9\u662F\u4E0A\u6E38\u6392\u961F\uFF1B\u5DF2\u6309\u7F51\u5173\u7ED9\u51FA\u7684 Retry-After \u91CD\u8BD5\u4ECD\u672A\u653E\u884C\u3002`,
|
|
2406
|
+
`- \u89E3\u51B3\u5EFA\u8BAE\uFF1A\u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u7528 /model \u5207\u6362\u5230\u5176\u4ED6 Provider\uFF08\u5982\u5176\u4ED6\u6A21\u578B/\u6E20\u9053\uFF09\u3002`
|
|
2407
|
+
].join("\n") : [
|
|
2408
|
+
`[Qoder upstream queue] The model gateway is at capacity and parked this request (code 10605 isQueued)`,
|
|
2409
|
+
`- Meaning: credentials and quota are fine \u2014 this is pure upstream queuing; the gateway's own Retry-After wait did not clear it.`,
|
|
2410
|
+
`- Next step: retry shortly, or switch provider with /model while the queue drains.`
|
|
2411
|
+
].join("\n");
|
|
2412
|
+
}
|
|
2391
2413
|
if (rawBody === void 0 || rawBody === null) {
|
|
2392
2414
|
return `Upstream status ${statusCode}`;
|
|
2393
2415
|
}
|
|
@@ -2808,9 +2830,22 @@ function parseQueueNotice(envelope) {
|
|
|
2808
2830
|
return {
|
|
2809
2831
|
retryAfterMs: Math.min(retryMs, MAX_WAIT_MS),
|
|
2810
2832
|
waitTimeSeconds: positiveNumber(payload.waitTime, 0),
|
|
2811
|
-
queueCount: positiveNumber(payload.queueCount, 0)
|
|
2833
|
+
queueCount: positiveNumber(payload.queueCount, 0),
|
|
2834
|
+
// Only a real boolean is surfaced; a missing field must not be read as
|
|
2835
|
+
// "unavailable" (that would collapse the normal queue into a hard stop).
|
|
2836
|
+
serviceAvailable: typeof payload.serviceAvailable === "boolean" ? payload.serviceAvailable : void 0
|
|
2812
2837
|
};
|
|
2813
2838
|
}
|
|
2839
|
+
function formatQueueFigure(n) {
|
|
2840
|
+
if (!Number.isFinite(n) || n <= 0) return "0";
|
|
2841
|
+
if (n < 100) return String(Math.round(n));
|
|
2842
|
+
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
2843
|
+
return `${(n / 1e3).toFixed(1)}k`;
|
|
2844
|
+
}
|
|
2845
|
+
function formatWaitMinutes(seconds) {
|
|
2846
|
+
if (!Number.isFinite(seconds) || seconds <= 0) return "<1";
|
|
2847
|
+
return String(Math.min(99, Math.max(1, Math.round(seconds / 60))));
|
|
2848
|
+
}
|
|
2814
2849
|
function sleep(ms, signal) {
|
|
2815
2850
|
return new Promise((resolve, reject) => {
|
|
2816
2851
|
if (signal?.aborted) {
|
|
@@ -3188,10 +3223,12 @@ function stableHash(prefix, ...inputs) {
|
|
|
3188
3223
|
}
|
|
3189
3224
|
return hash.digest("hex").slice(0, 16);
|
|
3190
3225
|
}
|
|
3191
|
-
function stableChatRecordID(model, messages, tools, maxTokens) {
|
|
3226
|
+
function stableChatRecordID(model, messages, tools, maxTokens, salt) {
|
|
3192
3227
|
const hash = crypto3.createHash("sha256");
|
|
3193
3228
|
hash.update("qoder-record");
|
|
3194
3229
|
hash.update("\0");
|
|
3230
|
+
hash.update(salt);
|
|
3231
|
+
hash.update("\0");
|
|
3195
3232
|
hash.update(model);
|
|
3196
3233
|
for (const msg of messages) {
|
|
3197
3234
|
if (msg?.role) {
|
|
@@ -3372,7 +3409,13 @@ function streamQoder(model, context, options) {
|
|
|
3372
3409
|
maxTokens = options.maxTokens;
|
|
3373
3410
|
}
|
|
3374
3411
|
const toolsRaw = resolved.tools.length > 0 ? transformTools(resolved.tools) : void 0;
|
|
3375
|
-
const recordID = stableChatRecordID(
|
|
3412
|
+
const recordID = stableChatRecordID(
|
|
3413
|
+
qoderModel,
|
|
3414
|
+
normalizedMessages,
|
|
3415
|
+
toolsRaw,
|
|
3416
|
+
maxTokens,
|
|
3417
|
+
`${sessionID}:${userID}`
|
|
3418
|
+
);
|
|
3376
3419
|
const requestedLevel = options?.reasoning;
|
|
3377
3420
|
const clamped = requestedLevel ? clampThinkingLevel(model, requestedLevel) : void 0;
|
|
3378
3421
|
const reasoningLevel = clamped === "off" ? void 0 : clamped;
|
|
@@ -3387,7 +3430,9 @@ function streamQoder(model, context, options) {
|
|
|
3387
3430
|
} else {
|
|
3388
3431
|
parameters.enable_thinking = false;
|
|
3389
3432
|
}
|
|
3390
|
-
const
|
|
3433
|
+
const modelSource = modelConfig.source || "system";
|
|
3434
|
+
const chatURL = getQoderChatURL(providerMode);
|
|
3435
|
+
const buildRequestBody = (attempt) => ({
|
|
3391
3436
|
request_id: crypto3.randomUUID(),
|
|
3392
3437
|
request_set_id: recordID,
|
|
3393
3438
|
chat_record_id: recordID,
|
|
@@ -3395,7 +3440,7 @@ function streamQoder(model, context, options) {
|
|
|
3395
3440
|
stream: true,
|
|
3396
3441
|
chat_task: "FREE_INPUT",
|
|
3397
3442
|
is_reply: true,
|
|
3398
|
-
is_retry:
|
|
3443
|
+
is_retry: attempt > 0,
|
|
3399
3444
|
source: 1,
|
|
3400
3445
|
version: "3",
|
|
3401
3446
|
session_type: "qodercli",
|
|
@@ -3436,18 +3481,31 @@ function streamQoder(model, context, options) {
|
|
|
3436
3481
|
name: lastUserText.substring(0, 30),
|
|
3437
3482
|
begin_at: Date.now()
|
|
3438
3483
|
}
|
|
3439
|
-
};
|
|
3440
|
-
const bodyBytes = Buffer.from(JSON.stringify(reqBody));
|
|
3441
|
-
const encodedBytes = qoderEncodeBody(bodyBytes);
|
|
3442
|
-
const chatURL = getQoderChatURL(providerMode);
|
|
3443
|
-
const headers = buildAuthHeaders(encodedBytes, chatURL, {
|
|
3444
|
-
userID,
|
|
3445
|
-
authToken: accessToken,
|
|
3446
|
-
name,
|
|
3447
|
-
email,
|
|
3448
|
-
machineID
|
|
3449
3484
|
});
|
|
3450
|
-
const
|
|
3485
|
+
const buildAttempt = (attempt) => {
|
|
3486
|
+
const encoded = qoderEncodeBody(Buffer.from(JSON.stringify(buildRequestBody(attempt))));
|
|
3487
|
+
return {
|
|
3488
|
+
encoded,
|
|
3489
|
+
headers: {
|
|
3490
|
+
"Content-Type": "application/json",
|
|
3491
|
+
Accept: "text/event-stream",
|
|
3492
|
+
"Cache-Control": "no-cache",
|
|
3493
|
+
"Accept-Encoding": "identity",
|
|
3494
|
+
"X-Model-Key": qoderModel,
|
|
3495
|
+
"X-Model-Source": modelSource,
|
|
3496
|
+
// Re-signed per attempt: the COSY payload carries its own requestId,
|
|
3497
|
+
// a second-granularity timestamp and a signature over the body, and
|
|
3498
|
+
// reusing any of them is what the gateway reads as a replay.
|
|
3499
|
+
...buildAuthHeaders(encoded, chatURL, {
|
|
3500
|
+
userID,
|
|
3501
|
+
authToken: accessToken,
|
|
3502
|
+
name,
|
|
3503
|
+
email,
|
|
3504
|
+
machineID
|
|
3505
|
+
})
|
|
3506
|
+
}
|
|
3507
|
+
};
|
|
3508
|
+
};
|
|
3451
3509
|
let queuedAttempt = 0;
|
|
3452
3510
|
await reader?.cancel().catch(() => {
|
|
3453
3511
|
});
|
|
@@ -3456,18 +3514,11 @@ function streamQoder(model, context, options) {
|
|
|
3456
3514
|
let buffer = "";
|
|
3457
3515
|
let bufferStart = 0;
|
|
3458
3516
|
for (; ; ) {
|
|
3517
|
+
const current = buildAttempt(queuedAttempt);
|
|
3459
3518
|
const response = await fetch(chatURL, {
|
|
3460
3519
|
method: "POST",
|
|
3461
|
-
headers:
|
|
3462
|
-
|
|
3463
|
-
Accept: "text/event-stream",
|
|
3464
|
-
"Cache-Control": "no-cache",
|
|
3465
|
-
"Accept-Encoding": "identity",
|
|
3466
|
-
"X-Model-Key": qoderModel,
|
|
3467
|
-
"X-Model-Source": modelSource,
|
|
3468
|
-
...headers
|
|
3469
|
-
},
|
|
3470
|
-
body: encodedBytes,
|
|
3520
|
+
headers: current.headers,
|
|
3521
|
+
body: current.encoded,
|
|
3471
3522
|
signal: options?.signal
|
|
3472
3523
|
});
|
|
3473
3524
|
if (!response.ok) {
|
|
@@ -3484,9 +3535,15 @@ function streamQoder(model, context, options) {
|
|
|
3484
3535
|
if (queue) {
|
|
3485
3536
|
await reader.cancel().catch(() => {
|
|
3486
3537
|
});
|
|
3487
|
-
|
|
3538
|
+
const stopEarly = queue.serviceAvailable === false;
|
|
3539
|
+
if (queuedAttempt >= (stopEarly ? 1 : MAX_QUEUE_RETRIES)) {
|
|
3540
|
+
if (stopEarly) {
|
|
3541
|
+
throw new Error(
|
|
3542
|
+
`Qoder \u4E0A\u6E38\u6682\u4E0D\u53EF\u670D\u52A1\uFF08\u7F51\u5173\u62A5 serviceAvailable:false\uFF1B\u961F\u5217\u4F4D\u7F6E ${formatQueueFigure(queue.queueCount)}\uFF0C\u9884\u8BA1 ${formatWaitMinutes(queue.waitTimeSeconds)} \u5206\u949F\u5185\u653E\u884C\uFF09\u3002\u5DF2\u6309 Retry-After \u7B49\u5F85\u5E76\u91CD\u8BD5 1 \u6B21\uFF0C\u4ECD\u672A\u653E\u884C\u3002\u8FD9\u4E0D\u662F\u51ED\u8BC1\u6216\u989D\u5EA6\u95EE\u9898\uFF0C\u8BF7\u7528 /model \u5207\u6362\u5176\u4ED6 Provider\uFF0C\u6216\u7A0D\u540E\u91CD\u8BD5\u3002`
|
|
3543
|
+
);
|
|
3544
|
+
}
|
|
3488
3545
|
throw new Error(
|
|
3489
|
-
`Qoder is at capacity: the request stayed queued after ${queuedAttempt + 1} attempts (~${queue.waitTimeSeconds}
|
|
3546
|
+
`Qoder is at capacity: the request stayed queued after ${queuedAttempt + 1} attempts (~${formatWaitMinutes(queue.waitTimeSeconds)} min wait reported). Try again shortly.`
|
|
3490
3547
|
);
|
|
3491
3548
|
}
|
|
3492
3549
|
queuedAttempt++;
|
package/package.json
CHANGED