mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-08 02:15:27 +08:00
feat: Introduce DetermineCaller for auth without account pooling and make wide_input_strict_output configurable.
This commit is contained in:
@@ -85,7 +85,8 @@ module.exports = async function handler(req, res) {
|
||||
const finalPrompt = asString(prep.body.final_prompt);
|
||||
const thinkingEnabled = toBool(prep.body.thinking_enabled);
|
||||
const searchEnabled = toBool(prep.body.search_enabled);
|
||||
const toolNames = extractToolNames(payload.tools);
|
||||
const toolPolicy = resolveToolcallPolicy(prep.body, payload.tools);
|
||||
const toolNames = toolPolicy.toolNames;
|
||||
|
||||
if (!model || !leaseID || !deepseekToken || !powHeader || !completionPayload) {
|
||||
writeOpenAIError(res, 500, 'invalid vercel prepare response');
|
||||
@@ -156,7 +157,8 @@ module.exports = async function handler(req, res) {
|
||||
let currentType = thinkingEnabled ? 'thinking' : 'text';
|
||||
let thinkingText = '';
|
||||
let outputText = '';
|
||||
const toolSieveEnabled = toolNames.length > 0;
|
||||
const toolSieveEnabled = toolPolicy.toolSieveEnabled;
|
||||
const emitEarlyToolDeltas = toolPolicy.emitEarlyToolDeltas;
|
||||
const toolSieveState = createToolSieveState();
|
||||
let toolCallsEmitted = false;
|
||||
const streamToolCallIDs = new Map();
|
||||
@@ -297,6 +299,9 @@ module.exports = async function handler(req, res) {
|
||||
const events = processToolSieveChunk(toolSieveState, p.text, toolNames);
|
||||
for (const evt of events) {
|
||||
if (evt.type === 'tool_call_deltas' && Array.isArray(evt.deltas) && evt.deltas.length > 0) {
|
||||
if (!emitEarlyToolDeltas) {
|
||||
continue;
|
||||
}
|
||||
toolCallsEmitted = true;
|
||||
sendDeltaFrame({ tool_calls: formatIncrementalToolCallDeltas(evt.deltas, streamToolCallIDs) });
|
||||
continue;
|
||||
@@ -407,6 +412,37 @@ function relayPreparedFailure(res, prep) {
|
||||
writeOpenAIError(res, prep.status || 500, 'vercel prepare failed');
|
||||
}
|
||||
|
||||
function resolveToolcallPolicy(prepBody, payloadTools) {
|
||||
const preparedToolNames = normalizePreparedToolNames(prepBody && prepBody.tool_names);
|
||||
const toolNames = preparedToolNames.length > 0 ? preparedToolNames : extractToolNames(payloadTools);
|
||||
const featureMatchEnabled = boolDefaultTrue(prepBody && prepBody.toolcall_feature_match);
|
||||
const emitEarlyToolDeltas = boolDefaultTrue(prepBody && prepBody.toolcall_early_emit_high);
|
||||
return {
|
||||
toolNames,
|
||||
toolSieveEnabled: toolNames.length > 0 && featureMatchEnabled,
|
||||
emitEarlyToolDeltas,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizePreparedToolNames(v) {
|
||||
if (!Array.isArray(v) || v.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const out = [];
|
||||
for (const item of v) {
|
||||
const name = asString(item);
|
||||
if (!name) {
|
||||
continue;
|
||||
}
|
||||
out.push(name);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function boolDefaultTrue(v) {
|
||||
return v !== false;
|
||||
}
|
||||
|
||||
async function safeReadText(resp) {
|
||||
if (!resp) {
|
||||
return '';
|
||||
@@ -933,4 +969,7 @@ module.exports.__test = {
|
||||
extractContentRecursive,
|
||||
shouldSkipPath,
|
||||
asString,
|
||||
resolveToolcallPolicy,
|
||||
normalizePreparedToolNames,
|
||||
boolDefaultTrue,
|
||||
};
|
||||
|
||||
@@ -10,10 +10,50 @@ const {
|
||||
flushToolSieve,
|
||||
} = require('./helpers/stream-tool-sieve');
|
||||
|
||||
const { parseChunkForContent } = handler.__test;
|
||||
const {
|
||||
parseChunkForContent,
|
||||
resolveToolcallPolicy,
|
||||
normalizePreparedToolNames,
|
||||
boolDefaultTrue,
|
||||
} = handler.__test;
|
||||
|
||||
test('chat-stream exposes parser test hooks', () => {
|
||||
assert.equal(typeof parseChunkForContent, 'function');
|
||||
assert.equal(typeof resolveToolcallPolicy, 'function');
|
||||
});
|
||||
|
||||
test('resolveToolcallPolicy defaults to feature-match + early emit when prepare flags missing', () => {
|
||||
const policy = resolveToolcallPolicy(
|
||||
{},
|
||||
[{ type: 'function', function: { name: 'read_file', parameters: { type: 'object' } } }],
|
||||
);
|
||||
assert.deepEqual(policy.toolNames, ['read_file']);
|
||||
assert.equal(policy.toolSieveEnabled, true);
|
||||
assert.equal(policy.emitEarlyToolDeltas, true);
|
||||
});
|
||||
|
||||
test('resolveToolcallPolicy respects prepare flags and prepared tool names', () => {
|
||||
const policy = resolveToolcallPolicy(
|
||||
{
|
||||
tool_names: [' prepped_tool ', '', null],
|
||||
toolcall_feature_match: false,
|
||||
toolcall_early_emit_high: false,
|
||||
},
|
||||
[{ type: 'function', function: { name: 'fallback_tool', parameters: { type: 'object' } } }],
|
||||
);
|
||||
assert.deepEqual(policy.toolNames, ['prepped_tool']);
|
||||
assert.equal(policy.toolSieveEnabled, false);
|
||||
assert.equal(policy.emitEarlyToolDeltas, false);
|
||||
});
|
||||
|
||||
test('normalizePreparedToolNames filters empty values', () => {
|
||||
assert.deepEqual(normalizePreparedToolNames([' a ', '', null, 'b']), ['a', 'b']);
|
||||
});
|
||||
|
||||
test('boolDefaultTrue keeps false only when explicitly false', () => {
|
||||
assert.equal(boolDefaultTrue(false), false);
|
||||
assert.equal(boolDefaultTrue(true), true);
|
||||
assert.equal(boolDefaultTrue(undefined), true);
|
||||
});
|
||||
|
||||
test('parseChunkForContent keeps split response/content fragments inside response array', () => {
|
||||
|
||||
Reference in New Issue
Block a user