mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-01 23:15:27 +08:00
fix(vercel): align JS stream parser with Go object-shaped content
This commit is contained in:
@@ -308,6 +308,10 @@ function parseChunkForContent(chunk, thinkingEnabled, currentType, stripReferenc
|
||||
}
|
||||
|
||||
if (val && typeof val === 'object') {
|
||||
const directContent = asContentString(val, stripReferenceMarkers);
|
||||
if (directContent) {
|
||||
parts.push({ text: directContent, type: partType });
|
||||
}
|
||||
const resp = val.response && typeof val.response === 'object' ? val.response : val;
|
||||
if (Array.isArray(resp.fragments)) {
|
||||
for (const frag of resp.fragments) {
|
||||
@@ -593,6 +597,12 @@ function asContentString(v, stripReferenceMarkers = true) {
|
||||
if (Object.prototype.hasOwnProperty.call(v, 'v')) {
|
||||
return asContentString(v.v, stripReferenceMarkers);
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(v, 'text')) {
|
||||
return asContentString(v.text, stripReferenceMarkers);
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(v, 'value')) {
|
||||
return asContentString(v.value, stripReferenceMarkers);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
if (v == null) {
|
||||
|
||||
@@ -227,6 +227,20 @@ test('vercel stream exhausts DeepSeek continue before synthetic retry', async ()
|
||||
assert.equal(fetchBodies.some((body) => String(body.prompt || '').includes('Previous reply had no visible output')), false);
|
||||
});
|
||||
|
||||
|
||||
|
||||
test('vercel stream usage completion_tokens does not double-count visible output', async () => {
|
||||
const sample = 'abcdefghijklmnopqrst';
|
||||
const { frames } = await runMockVercelStream([
|
||||
`data: ${JSON.stringify({ p: 'response/content', v: sample })}\n\n`,
|
||||
'data: [DONE]\n\n',
|
||||
]);
|
||||
const parsed = frames.filter((frame) => frame !== '[DONE]').map((frame) => JSON.parse(frame));
|
||||
const terminal = parsed.find((item) => Array.isArray(item.choices) && item.choices[0] && item.choices[0].finish_reason);
|
||||
assert.ok(terminal);
|
||||
assert.equal(terminal.usage.completion_tokens, 5);
|
||||
});
|
||||
|
||||
test('vercel stream reuses prior PoW when refresh fails', async () => {
|
||||
const originalFetch = global.fetch;
|
||||
const fetchURLs = [];
|
||||
@@ -525,6 +539,12 @@ test('parseChunkForContent supports wrapped response.fragments object shape', ()
|
||||
assert.equal(parsed.parts.map((p) => p.text).join(''), 'AB');
|
||||
});
|
||||
|
||||
test('parseChunkForContent reads object-shaped response/content payloads (Go parity)', () => {
|
||||
const parsed = parseChunkForContent({ p: 'response/content', v: { text: 'vision text' } }, false, 'text', true);
|
||||
assert.equal(parsed.parsed, true);
|
||||
assert.deepEqual(parsed.parts, [{ text: 'vision text', type: 'text' }]);
|
||||
});
|
||||
|
||||
test('parseChunkForContent preserves space-only content tokens', () => {
|
||||
const chunk = {
|
||||
p: 'response/content',
|
||||
|
||||
Reference in New Issue
Block a user