pi-provider-qoder-cn 0.2.28 → 0.2.29
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 +133 -16
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3008,6 +3008,21 @@ var THINKING_TAG_VARIANTS = [
|
|
|
3008
3008
|
{ open: "<thought>", close: "</thought>" }
|
|
3009
3009
|
];
|
|
3010
3010
|
var ALL_THINKING_TAGS = THINKING_TAG_VARIANTS.flatMap((variant) => [variant.open, variant.close]);
|
|
3011
|
+
function hasAnyCompleteThinkingTag(text) {
|
|
3012
|
+
return THINKING_TAG_VARIANTS.some((variant) => text.includes(variant.open) || text.includes(variant.close));
|
|
3013
|
+
}
|
|
3014
|
+
function hasOrphanThinkingCloser(text) {
|
|
3015
|
+
for (const variant of THINKING_TAG_VARIANTS) {
|
|
3016
|
+
const closePos = text.indexOf(variant.close);
|
|
3017
|
+
if (closePos === -1) continue;
|
|
3018
|
+
const openerBefore = THINKING_TAG_VARIANTS.some((other) => {
|
|
3019
|
+
const openPos = text.indexOf(other.open);
|
|
3020
|
+
return openPos !== -1 && openPos < closePos;
|
|
3021
|
+
});
|
|
3022
|
+
if (!openerBefore) return true;
|
|
3023
|
+
}
|
|
3024
|
+
return false;
|
|
3025
|
+
}
|
|
3011
3026
|
function getTrailingPossibleTagPrefixLength(text, tag) {
|
|
3012
3027
|
const maxPrefixLength = Math.min(text.length, tag.length - 1);
|
|
3013
3028
|
for (let len = maxPrefixLength; len > 0; len--) {
|
|
@@ -3055,6 +3070,23 @@ var ThinkingTagParser = class {
|
|
|
3055
3070
|
textBlockIndex = null;
|
|
3056
3071
|
lastTextBlockIndex = null;
|
|
3057
3072
|
activeEndTag = THINKING_TAG_VARIANTS[0].close;
|
|
3073
|
+
orphanCloserMeansThinking = false;
|
|
3074
|
+
/**
|
|
3075
|
+
* Must be called before the first `processChunk`.
|
|
3076
|
+
*
|
|
3077
|
+
* When the reasoning_content channel carried no thinking body this turn
|
|
3078
|
+
* (only a bare opener tag, or nothing at all), an orphan closer in the
|
|
3079
|
+
* content stream is preceded by THINKING text rather than answer text.
|
|
3080
|
+
* stream.ts decides this once, at the first content delta, because Qoder
|
|
3081
|
+
* sends the reasoning channel before the content channel.
|
|
3082
|
+
*
|
|
3083
|
+
* Default false preserves the historical behaviour: the opener and its
|
|
3084
|
+
* thinking text arrived via reasoning_content, so text before the orphan
|
|
3085
|
+
* closer in the content stream is visible answer text.
|
|
3086
|
+
*/
|
|
3087
|
+
setOrphanCloserMeansThinking(value) {
|
|
3088
|
+
this.orphanCloserMeansThinking = value;
|
|
3089
|
+
}
|
|
3058
3090
|
processChunk(chunk) {
|
|
3059
3091
|
this.textBuffer += chunk;
|
|
3060
3092
|
while (this.textBuffer.length > 0) {
|
|
@@ -3091,6 +3123,17 @@ var ThinkingTagParser = class {
|
|
|
3091
3123
|
content: block.thinking,
|
|
3092
3124
|
partial: this.output
|
|
3093
3125
|
});
|
|
3126
|
+
} else if (this.orphanCloserMeansThinking && !this.thinkingExtracted) {
|
|
3127
|
+
this.emitThinking(this.textBuffer);
|
|
3128
|
+
if (this.thinkingBlockIndex !== null) {
|
|
3129
|
+
const block = this.output.content[this.thinkingBlockIndex];
|
|
3130
|
+
this.stream.push({
|
|
3131
|
+
type: "thinking_end",
|
|
3132
|
+
contentIndex: this.thinkingBlockIndex,
|
|
3133
|
+
content: block.thinking,
|
|
3134
|
+
partial: this.output
|
|
3135
|
+
});
|
|
3136
|
+
}
|
|
3094
3137
|
} else {
|
|
3095
3138
|
this.emitText(this.textBuffer);
|
|
3096
3139
|
}
|
|
@@ -3124,6 +3167,27 @@ var ThinkingTagParser = class {
|
|
|
3124
3167
|
return;
|
|
3125
3168
|
}
|
|
3126
3169
|
if (bestCloseVariant !== null) {
|
|
3170
|
+
if (this.orphanCloserMeansThinking && bestClosePos > 0) {
|
|
3171
|
+
const thinkingText = this.textBuffer.slice(0, bestClosePos);
|
|
3172
|
+
this.emitThinking(thinkingText);
|
|
3173
|
+
if (this.thinkingBlockIndex !== null) {
|
|
3174
|
+
const block = this.output.content[this.thinkingBlockIndex];
|
|
3175
|
+
this.stream.push({
|
|
3176
|
+
type: "thinking_end",
|
|
3177
|
+
contentIndex: this.thinkingBlockIndex,
|
|
3178
|
+
content: block.thinking,
|
|
3179
|
+
partial: this.output
|
|
3180
|
+
});
|
|
3181
|
+
this.thinkingBlockIndex = null;
|
|
3182
|
+
}
|
|
3183
|
+
this.textBuffer = this.textBuffer.slice(bestClosePos + bestCloseVariant.close.length);
|
|
3184
|
+
if (this.textBuffer.startsWith("\n\n")) this.textBuffer = this.textBuffer.slice(2);
|
|
3185
|
+
else if (this.textBuffer.startsWith("\n")) this.textBuffer = this.textBuffer.slice(1);
|
|
3186
|
+
this.thinkingExtracted = true;
|
|
3187
|
+
this.lastTextBlockIndex = this.textBlockIndex;
|
|
3188
|
+
this.textBlockIndex = null;
|
|
3189
|
+
return;
|
|
3190
|
+
}
|
|
3127
3191
|
if (bestClosePos > 0) this.emitText(this.textBuffer.slice(0, bestClosePos));
|
|
3128
3192
|
this.textBuffer = this.textBuffer.slice(bestClosePos + bestCloseVariant.close.length);
|
|
3129
3193
|
if (this.textBuffer.startsWith("\n\n")) this.textBuffer = this.textBuffer.slice(2);
|
|
@@ -3133,7 +3197,9 @@ var ThinkingTagParser = class {
|
|
|
3133
3197
|
const trailingPrefixLength = getMaxTrailingPossibleTagPrefixLength(this.textBuffer, ALL_THINKING_TAGS);
|
|
3134
3198
|
const safeLen = this.textBuffer.length - trailingPrefixLength;
|
|
3135
3199
|
if (safeLen > 0) {
|
|
3136
|
-
this.
|
|
3200
|
+
const safeText = this.textBuffer.slice(0, safeLen);
|
|
3201
|
+
if (this.orphanCloserMeansThinking) this.emitThinking(safeText);
|
|
3202
|
+
else this.emitText(safeText);
|
|
3137
3203
|
this.textBuffer = this.textBuffer.slice(safeLen);
|
|
3138
3204
|
}
|
|
3139
3205
|
}
|
|
@@ -3357,6 +3423,7 @@ function transformMessagesForQoder(messages) {
|
|
|
3357
3423
|
// src/protocol/stream.ts
|
|
3358
3424
|
var healCooldowns = /* @__PURE__ */ new Map();
|
|
3359
3425
|
var HEAL_COOLDOWN_MS = 6e4;
|
|
3426
|
+
var CONTENT_CLASSIFY_BUFFER_LIMIT = 8 * 1024;
|
|
3360
3427
|
function stableHash(prefix, ...inputs) {
|
|
3361
3428
|
const hash = crypto3.createHash("sha256");
|
|
3362
3429
|
hash.update(prefix);
|
|
@@ -3739,13 +3806,43 @@ function streamQoder(model, context, options) {
|
|
|
3739
3806
|
const toolCallsState = [];
|
|
3740
3807
|
const thinkingEnabled = options?.reasoning !== false && options?.reasoning !== "off";
|
|
3741
3808
|
const thinkingParser = thinkingEnabled ? new ThinkingTagParser(output, stream) : null;
|
|
3809
|
+
let sawDone = false;
|
|
3810
|
+
let sawFinishReason = false;
|
|
3811
|
+
let sawAnyDelta = false;
|
|
3812
|
+
let reasoningChannelHadBody = false;
|
|
3813
|
+
let contentClassificationDone = false;
|
|
3814
|
+
let contentBuffer = "";
|
|
3815
|
+
const flushContentBuffer = (final = false) => {
|
|
3816
|
+
if (!contentBuffer) return;
|
|
3817
|
+
if (!contentClassificationDone && (final || hasAnyCompleteThinkingTag(contentBuffer) || contentBuffer.length >= CONTENT_CLASSIFY_BUFFER_LIMIT)) {
|
|
3818
|
+
if (thinkingParser && !reasoningChannelHadBody && hasOrphanThinkingCloser(contentBuffer)) {
|
|
3819
|
+
thinkingParser.setOrphanCloserMeansThinking(true);
|
|
3820
|
+
}
|
|
3821
|
+
contentClassificationDone = true;
|
|
3822
|
+
}
|
|
3823
|
+
if (!contentClassificationDone) return;
|
|
3824
|
+
const pending = contentBuffer;
|
|
3825
|
+
contentBuffer = "";
|
|
3826
|
+
if (thinkingParser) thinkingParser.processChunk(pending);
|
|
3827
|
+
else emitPlainText(pending);
|
|
3828
|
+
};
|
|
3829
|
+
const emitPlainText = (text) => {
|
|
3830
|
+
if (!text) return;
|
|
3831
|
+
if (contentBlockIndex === -1) {
|
|
3832
|
+
contentBlockIndex = output.content.length;
|
|
3833
|
+
output.content.push({ type: "text", text: "" });
|
|
3834
|
+
stream.push({ type: "text_start", contentIndex: contentBlockIndex, partial: output });
|
|
3835
|
+
}
|
|
3836
|
+
const block = output.content[contentBlockIndex];
|
|
3837
|
+
block.text += text;
|
|
3838
|
+
stream.push({ type: "text_delta", contentIndex: contentBlockIndex, delta: text, partial: output });
|
|
3839
|
+
};
|
|
3742
3840
|
if (!started) {
|
|
3743
3841
|
started = true;
|
|
3744
3842
|
} else {
|
|
3745
3843
|
output.content.length = 0;
|
|
3746
3844
|
}
|
|
3747
3845
|
stream.push({ type: "start", partial: output });
|
|
3748
|
-
let sawDone = false;
|
|
3749
3846
|
while (!sawDone) {
|
|
3750
3847
|
const { done, value } = await reader.read();
|
|
3751
3848
|
if (done) break;
|
|
@@ -3796,9 +3893,11 @@ function streamQoder(model, context, options) {
|
|
|
3796
3893
|
const choice = inner.choices[0];
|
|
3797
3894
|
const delta = choice.delta;
|
|
3798
3895
|
if (delta) {
|
|
3896
|
+
sawAnyDelta = true;
|
|
3799
3897
|
if (delta.reasoning_content) {
|
|
3800
3898
|
rawReasoningTail = (rawReasoningTail + delta.reasoning_content).slice(-512);
|
|
3801
3899
|
const reasoningChunk = stripDsmlResidue(stripThinkingTags(delta.reasoning_content));
|
|
3900
|
+
if (reasoningChunk.trim().length > 0) reasoningChannelHadBody = true;
|
|
3802
3901
|
if (reasoningChunk) {
|
|
3803
3902
|
if (thinkingBlockIndex === -1) {
|
|
3804
3903
|
thinkingBlockIndex = output.content.length;
|
|
@@ -3827,24 +3926,14 @@ function streamQoder(model, context, options) {
|
|
|
3827
3926
|
thinkingBlockIndex = -1;
|
|
3828
3927
|
}
|
|
3829
3928
|
if (thinkingParser) {
|
|
3830
|
-
|
|
3929
|
+
contentBuffer += delta.content;
|
|
3930
|
+
flushContentBuffer();
|
|
3831
3931
|
} else {
|
|
3832
|
-
|
|
3833
|
-
contentBlockIndex = output.content.length;
|
|
3834
|
-
output.content.push({ type: "text", text: "" });
|
|
3835
|
-
stream.push({ type: "text_start", contentIndex: contentBlockIndex, partial: output });
|
|
3836
|
-
}
|
|
3837
|
-
const block = output.content[contentBlockIndex];
|
|
3838
|
-
block.text += delta.content;
|
|
3839
|
-
stream.push({
|
|
3840
|
-
type: "text_delta",
|
|
3841
|
-
contentIndex: contentBlockIndex,
|
|
3842
|
-
delta: delta.content,
|
|
3843
|
-
partial: output
|
|
3844
|
-
});
|
|
3932
|
+
emitPlainText(delta.content);
|
|
3845
3933
|
}
|
|
3846
3934
|
}
|
|
3847
3935
|
if (delta.tool_calls && Array.isArray(delta.tool_calls)) {
|
|
3936
|
+
flushContentBuffer(true);
|
|
3848
3937
|
for (const tc of delta.tool_calls) {
|
|
3849
3938
|
const idx = tc.index ?? 0;
|
|
3850
3939
|
if (!toolCallsState[idx]) {
|
|
@@ -3883,6 +3972,7 @@ function streamQoder(model, context, options) {
|
|
|
3883
3972
|
}
|
|
3884
3973
|
}
|
|
3885
3974
|
if (choice.finish_reason) {
|
|
3975
|
+
sawFinishReason = true;
|
|
3886
3976
|
output.stopReason = choice.finish_reason;
|
|
3887
3977
|
}
|
|
3888
3978
|
}
|
|
@@ -3899,6 +3989,7 @@ function streamQoder(model, context, options) {
|
|
|
3899
3989
|
}
|
|
3900
3990
|
await reader.cancel().catch(() => {
|
|
3901
3991
|
});
|
|
3992
|
+
flushContentBuffer(true);
|
|
3902
3993
|
if (thinkingParser) {
|
|
3903
3994
|
thinkingParser.finalize();
|
|
3904
3995
|
}
|
|
@@ -3944,6 +4035,32 @@ function streamQoder(model, context, options) {
|
|
|
3944
4035
|
stream.end();
|
|
3945
4036
|
return;
|
|
3946
4037
|
}
|
|
4038
|
+
if (!sawAnyDelta) {
|
|
4039
|
+
if (!options?.signal?.aborted) {
|
|
4040
|
+
output.stopReason = "error";
|
|
4041
|
+
output.errorMessage = "Qoder server error: incomplete stream (no content deltas received)";
|
|
4042
|
+
stream.push({ type: "error", reason: "error", error: output });
|
|
4043
|
+
stream.end();
|
|
4044
|
+
return;
|
|
4045
|
+
}
|
|
4046
|
+
output.stopReason = "aborted";
|
|
4047
|
+
stream.push({ type: "error", reason: "aborted", error: output });
|
|
4048
|
+
stream.end();
|
|
4049
|
+
return;
|
|
4050
|
+
}
|
|
4051
|
+
if (!sawDone && !sawFinishReason) {
|
|
4052
|
+
if (!options?.signal?.aborted) {
|
|
4053
|
+
output.stopReason = "error";
|
|
4054
|
+
output.errorMessage = "Qoder server error: incomplete stream (response ended without [DONE] or finish_reason)";
|
|
4055
|
+
stream.push({ type: "error", reason: "error", error: output });
|
|
4056
|
+
stream.end();
|
|
4057
|
+
return;
|
|
4058
|
+
}
|
|
4059
|
+
output.stopReason = "aborted";
|
|
4060
|
+
stream.push({ type: "error", reason: "aborted", error: output });
|
|
4061
|
+
stream.end();
|
|
4062
|
+
return;
|
|
4063
|
+
}
|
|
3947
4064
|
stream.push({
|
|
3948
4065
|
type: "done",
|
|
3949
4066
|
reason: output.stopReason,
|
package/package.json
CHANGED