fix: use parent_message_id and fresh PoW headers for empty-output retry and continue

Previously retry/continue requests reused the initial PoW header and
lacked parent_message_id, causing them to land as disconnected root
messages in the DeepSeek session instead of proper follow-up turns.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
CJACK
2026-04-27 21:31:51 +08:00
parent fb43bd92f5
commit b82bc1311a
16 changed files with 324 additions and 32 deletions

View File

@@ -153,6 +153,9 @@ async function runMockVercelStreamSequence(upstreamSequences, prepareOverrides =
if (textURL.includes('__stream_prepare=1')) {
return jsonResponse(prepareBody);
}
if (textURL.includes('__stream_pow=1')) {
return jsonResponse({ pow_header: 'pow-header-refreshed' });
}
if (textURL.includes('__stream_release=1')) {
return jsonResponse({ success: true });
}
@@ -199,6 +202,7 @@ test('vercel stream retries empty output once and keeps one terminal frame', asy
const parsed = frames.filter((frame) => frame !== '[DONE]').map((frame) => JSON.parse(frame));
const completionBodies = fetchBodies.filter((body) => Object.hasOwn(body, 'prompt'));
assert.equal(fetchURLs.filter((url) => url === 'https://chat.deepseek.com/api/v0/chat/completion').length, 2);
assert.equal(fetchURLs.filter((url) => url.includes('__stream_pow=1')).length, 1);
assert.equal(frames.filter((frame) => frame === '[DONE]').length, 1);
assert.equal(parsed[0].choices[0].delta.content, 'visible');
assert.equal(parsed[1].choices[0].finish_reason, 'stop');
@@ -217,11 +221,67 @@ test('vercel stream exhausts DeepSeek continue before synthetic retry', async ()
const parsed = frames.filter((frame) => frame !== '[DONE]').map((frame) => JSON.parse(frame));
assert.equal(fetchURLs.filter((url) => url === 'https://chat.deepseek.com/api/v0/chat/completion').length, 1);
assert.equal(fetchURLs.filter((url) => url === 'https://chat.deepseek.com/api/v0/chat/continue').length, 1);
assert.equal(fetchURLs.filter((url) => url.includes('__stream_pow=1')).length, 1);
assert.equal(parsed[0].choices[0].delta.content, 'continued');
assert.equal(parsed[1].choices[0].finish_reason, 'stop');
assert.equal(fetchBodies.some((body) => String(body.prompt || '').includes('Previous reply had no visible output')), false);
});
test('vercel stream reuses prior PoW when refresh fails', async () => {
const originalFetch = global.fetch;
const fetchURLs = [];
const completionPowHeaders = [];
let completionCalls = 0;
global.fetch = async (url, init = {}) => {
const textURL = String(url);
fetchURLs.push(textURL);
if (textURL.includes('__stream_prepare=1')) {
return jsonResponse({
session_id: 'chatcmpl-test',
lease_id: 'lease-test',
model: 'gpt-test',
final_prompt: 'hello',
thinking_enabled: false,
search_enabled: false,
compat: { strip_reference_markers: true },
tool_names: [],
deepseek_token: 'deepseek-token',
pow_header: 'pow-header-initial',
payload: { prompt: 'hello' },
});
}
if (textURL.includes('__stream_pow=1')) {
return jsonResponse({}, 500);
}
if (textURL.includes('__stream_release=1')) {
return jsonResponse({ success: true });
}
if (textURL === 'https://chat.deepseek.com/api/v0/chat/completion') {
completionPowHeaders.push(init.headers['x-ds-pow-response']);
completionCalls += 1;
if (completionCalls === 1) {
return sseResponse(['data: [DONE]\n\n']);
}
return sseResponse(['data: {"p":"response/content","v":"visible"}\n\n', 'data: [DONE]\n\n']);
}
throw new Error(`unexpected fetch url: ${textURL}`);
};
try {
const req = new MockStreamRequest();
const res = new MockStreamResponse();
const payload = { model: 'gpt-test', stream: true };
await handleVercelStream(req, res, Buffer.from(JSON.stringify(payload)), payload);
const frames = parseSSEDataFrames(res.bodyText());
const parsed = frames.filter((frame) => frame !== '[DONE]').map((frame) => JSON.parse(frame));
assert.deepEqual(completionPowHeaders, ['pow-header-initial', 'pow-header-initial']);
assert.equal(fetchURLs.filter((url) => url.includes('__stream_pow=1')).length, 1);
assert.equal(parsed[0].choices[0].delta.content, 'visible');
assert.equal(parsed[1].choices[0].finish_reason, 'stop');
} finally {
global.fetch = originalFetch;
}
});
test('vercel stream emits content_filter failure when upstream filters empty output', async () => {
const { frames } = await runMockVercelStream(['data: {"code":"content_filter"}\n\n']);
assert.equal(frames.length, 2);