mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-11 03:37:40 +08:00
feat: align Go/Node DSML tool-call parsing drift tolerance and update API docs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -85,6 +85,33 @@ async function fetchStreamPow(req, leaseID) {
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchStreamSwitch(req, leaseID) {
|
||||
const url = buildInternalGoURL(req);
|
||||
url.searchParams.set('__stream_switch', '1');
|
||||
|
||||
const upstream = await fetch(url.toString(), {
|
||||
method: 'POST',
|
||||
headers: buildInternalGoHeaders(req, { withInternalToken: true, withContentType: true }),
|
||||
body: Buffer.from(JSON.stringify({ lease_id: leaseID })),
|
||||
});
|
||||
|
||||
const text = await upstream.text();
|
||||
let body = {};
|
||||
try {
|
||||
body = JSON.parse(text || '{}');
|
||||
} catch (_err) {
|
||||
body = {};
|
||||
}
|
||||
|
||||
return {
|
||||
ok: upstream.ok,
|
||||
status: upstream.status,
|
||||
contentType: upstream.headers.get('content-type') || 'application/json',
|
||||
text,
|
||||
body,
|
||||
};
|
||||
}
|
||||
|
||||
function relayPreparedFailure(res, prep) {
|
||||
if (prep.status === 401 && looksLikeVercelAuthPage(prep.text)) {
|
||||
writeOpenAIError(
|
||||
@@ -223,6 +250,7 @@ module.exports = {
|
||||
readRawBody,
|
||||
fetchStreamPrepare,
|
||||
fetchStreamPow,
|
||||
fetchStreamSwitch,
|
||||
relayPreparedFailure,
|
||||
safeReadText,
|
||||
buildInternalGoURL,
|
||||
|
||||
@@ -7,9 +7,9 @@ const {
|
||||
SKIP_EXACT_PATHS,
|
||||
} = require('../shared/deepseek-constants');
|
||||
|
||||
const LEAKED_BOS_MARKER_PATTERN = /<[||]\s*begin[_▁]of[_▁]sentence\s*[||]>/gi;
|
||||
const LEAKED_THOUGHT_MARKER_PATTERN = /<[||]\s*(?:begin[_▁])?[_▁]*of[_▁]thought\s*[||]>/gi;
|
||||
const LEAKED_META_MARKER_PATTERN = /<[||]\s*(?:assistant|tool|end[_▁]of[_▁]sentence|end[_▁]of[_▁]thinking|end[_▁]of[_▁]thought|end[_▁]of[_▁]toolresults|end[_▁]of[_▁]instructions)\s*[||]>/gi;
|
||||
const LEAKED_BOS_MARKER_PATTERN = /<[\|\uFF5C]\s*begin[_▁]of[_▁]sentence\s*[\|\uFF5C]>/gi;
|
||||
const LEAKED_THOUGHT_MARKER_PATTERN = /<[\|\uFF5C]\s*(?:begin[_▁])?[_▁]*of[_▁]thought\s*[\|\uFF5C]>/gi;
|
||||
const LEAKED_META_MARKER_PATTERN = /<[\|\uFF5C]\s*(?:assistant|tool|end[_▁]of[_▁]sentence|end[_▁]of[_▁]thinking|end[_▁]of[_▁]thought|end[_▁]of[_▁]toolresults|end[_▁]of[_▁]instructions)\s*[\|\uFF5C]>/gi;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ const {
|
||||
isAbortError,
|
||||
fetchStreamPrepare,
|
||||
fetchStreamPow,
|
||||
fetchStreamSwitch,
|
||||
relayPreparedFailure,
|
||||
createLeaseReleaser,
|
||||
} = require('./http_internal');
|
||||
@@ -46,11 +47,11 @@ async function handleVercelStream(req, res, rawBody, payload) {
|
||||
}
|
||||
|
||||
const model = asString(prep.body.model) || asString(payload.model);
|
||||
const sessionID = asString(prep.body.session_id) || `chatcmpl-${Date.now()}`;
|
||||
const responseID = asString(prep.body.session_id) || `chatcmpl-${Date.now()}`;
|
||||
const leaseID = asString(prep.body.lease_id);
|
||||
const deepseekToken = asString(prep.body.deepseek_token);
|
||||
let deepseekToken = asString(prep.body.deepseek_token);
|
||||
const initialPowHeader = asString(prep.body.pow_header);
|
||||
const completionPayload = prep.body.payload && typeof prep.body.payload === 'object' ? prep.body.payload : null;
|
||||
let completionPayload = prep.body.payload && typeof prep.body.payload === 'object' ? prep.body.payload : null;
|
||||
const finalPrompt = asString(prep.body.final_prompt);
|
||||
const thinkingEnabled = toBool(prep.body.thinking_enabled);
|
||||
const searchEnabled = toBool(prep.body.search_enabled);
|
||||
@@ -133,13 +134,14 @@ async function handleVercelStream(req, res, rawBody, payload) {
|
||||
}
|
||||
};
|
||||
const fetchCompletion = (bodyPayload) => fetchDeepSeekStream(DEEPSEEK_COMPLETION_URL, bodyPayload, currentPowHeader);
|
||||
let activeDeepSeekSessionID = responseID;
|
||||
const fetchContinue = async (messageID) => {
|
||||
const powHeader = await refreshPowHeader('continue');
|
||||
if (!powHeader) {
|
||||
return null;
|
||||
}
|
||||
return fetchDeepSeekStream(DEEPSEEK_CONTINUE_URL, {
|
||||
chat_session_id: sessionID,
|
||||
chat_session_id: activeDeepSeekSessionID,
|
||||
message_id: messageID,
|
||||
fallback_to_resume: true,
|
||||
}, powHeader);
|
||||
@@ -185,7 +187,7 @@ async function handleVercelStream(req, res, rawBody, payload) {
|
||||
let ended = false;
|
||||
const { sendFrame, sendDeltaFrame } = createChatCompletionEmitter({
|
||||
res,
|
||||
sessionID,
|
||||
sessionID: responseID,
|
||||
created,
|
||||
model,
|
||||
isClosed: () => clientClosed,
|
||||
@@ -242,7 +244,7 @@ async function handleVercelStream(req, res, rawBody, payload) {
|
||||
}
|
||||
ended = true;
|
||||
sendFrame({
|
||||
id: sessionID,
|
||||
id: responseID,
|
||||
object: 'chat.completion.chunk',
|
||||
created,
|
||||
model,
|
||||
@@ -261,7 +263,7 @@ async function handleVercelStream(req, res, rawBody, payload) {
|
||||
|
||||
const processStream = async (initialResponse, allowDeferEmpty) => {
|
||||
let currentResponse = initialResponse;
|
||||
let continueState = createContinueState(sessionID);
|
||||
let continueState = createContinueState(activeDeepSeekSessionID);
|
||||
let continueRounds = 0;
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
@@ -412,13 +414,39 @@ async function handleVercelStream(req, res, rawBody, payload) {
|
||||
};
|
||||
|
||||
let retryAttempts = 0;
|
||||
let accountSwitchAttempted = false;
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
const processed = await processStream(completionRes, retryAttempts < EMPTY_OUTPUT_RETRY_MAX_ATTEMPTS);
|
||||
const allowDeferEmpty = retryAttempts < EMPTY_OUTPUT_RETRY_MAX_ATTEMPTS || !accountSwitchAttempted;
|
||||
const processed = await processStream(completionRes, allowDeferEmpty);
|
||||
if (processed.terminal) {
|
||||
return;
|
||||
}
|
||||
if (!processed.retryable || retryAttempts >= EMPTY_OUTPUT_RETRY_MAX_ATTEMPTS) {
|
||||
if (!processed.retryable) {
|
||||
await finish('stop');
|
||||
return;
|
||||
}
|
||||
if (retryAttempts >= EMPTY_OUTPUT_RETRY_MAX_ATTEMPTS) {
|
||||
if (!accountSwitchAttempted) {
|
||||
accountSwitchAttempted = true;
|
||||
const switched = await fetchStreamSwitch(req, leaseID);
|
||||
if (switched.ok && switched.body && switched.body.payload && typeof switched.body.payload === 'object') {
|
||||
completionPayload = switched.body.payload;
|
||||
deepseekToken = asString(switched.body.deepseek_token) || deepseekToken;
|
||||
currentPowHeader = asString(switched.body.pow_header) || currentPowHeader;
|
||||
activeDeepSeekSessionID = asString(switched.body.session_id) || activeDeepSeekSessionID;
|
||||
usagePrompt = finalPrompt;
|
||||
completionRes = await fetchCompletion(completionPayload);
|
||||
if (completionRes === null) {
|
||||
return;
|
||||
}
|
||||
if (!completionRes.ok || !completionRes.body) {
|
||||
await finish('stop');
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
await finish('stop');
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user