File size: 2,265 Bytes
c056ab3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
 * 运行: cd client/src && npx tsx tests/chat/toolCallParser.test.ts
 */
import {
    assistantOutputOffsetForRound,
    parseToolCallFromWireRound,
} from '../../features/chat/toolCallParser';

let passed = 0;
let failed = 0;

function assert(desc: string, cond: boolean) {
    if (cond) {
        console.log(`  ✓ ${desc}`);
        passed++;
    } else {
        console.error(`  ✗ ${desc}`);
        failed++;
    }
}

const TOOL_CALL =
    '<tool_call>\n{"name": "get_weather", "arguments": {"city": "北京"}}\n</tool_call>';

assert('round 0 offset = firstPrompt 长度', assistantOutputOffsetForRound(0, 120, 200) === 120);
assert('round > 0 offset = 本轮开始前 wire 长度', assistantOutputOffsetForRound(1, 120, 500) === 500);

const firstPrompt = '<|im_start|>assistant\n';
const teacherForcing = TOOL_CALL;
const wireRound0 = firstPrompt + teacherForcing;

const parsedFromTfOnly = parseToolCallFromWireRound(
    wireRound0,
    assistantOutputOffsetForRound(0, firstPrompt.length, wireRound0.length)
);
assert(
    '首轮 TF 含完整 tool call、无模型续写 → parsed',
    parsedFromTfOnly.status === 'parsed' && parsedFromTfOnly.call.name === 'get_weather'
);

const tfPrefix = '<tool_call>\n{"name": "get_weather", "arguments": {"city": "';
const modelSuffix = '北京"}}\n</tool_call>';
const wireSplit = firstPrompt + tfPrefix + modelSuffix;
const parsedSplit = parseToolCallFromWireRound(
    wireSplit,
    assistantOutputOffsetForRound(0, firstPrompt.length, firstPrompt.length + tfPrefix.length)
);
assert(
    'TF 与模型续写拼成完整 tool call → parsed',
    parsedSplit.status === 'parsed' && parsedSplit.call.name === 'get_weather'
);

const wireRound1 = wireSplit + '<|im_end|>\n<|im_start|>tool\n{}\n<|im_end|>\n<|im_start|>assistant\n';
const round1Output = TOOL_CALL;
const wireAfterRound1 = wireRound1 + round1Output;
const parsedRound1 = parseToolCallFromWireRound(
    wireAfterRound1,
    assistantOutputOffsetForRound(1, firstPrompt.length, wireRound1.length)
);
assert(
    '后续轮仅模型续写含 tool call → parsed',
    parsedRound1.status === 'parsed' && parsedRound1.call.name === 'get_weather'
);

console.log(`\n${passed} passed, ${failed} failed`);
process.exit(failed > 0 ? 1 : 0);