File size: 2,880 Bytes
097fb32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/**
 * test/unit-vision.mjs
 *
 * 单元测试:Vision 拦截器仅处理 user 图片消息
 * 运行方式:node test/unit-vision.mjs
 */

let passed = 0;
let failed = 0;

async function test(name, fn) {
    try {
        await fn();
        console.log(`  ✅  ${name}`);
        passed++;
    } catch (e) {
        console.error(`  ❌  ${name}`);
        console.error(`      ${e.message}`);
        failed++;
    }
}

function assert(condition, msg) {
    if (!condition) throw new Error(msg || 'Assertion failed');
}

async function applyVisionInterceptor(messages) {
    for (const msg of messages) {
        if (msg.role !== 'user') continue;
        if (!Array.isArray(msg.content)) continue;

        const newContent = [];
        const imagesToAnalyze = [];

        for (const block of msg.content) {
            if (block.type === 'image') {
                imagesToAnalyze.push(block);
            } else {
                newContent.push(block);
            }
        }

        if (imagesToAnalyze.length > 0) {
            newContent.push({
                type: 'text',
                text: `[System: The user attached ${imagesToAnalyze.length} image(s). Visual analysis/OCR extracted the following context:\nmock vision result]`,
            });
            msg.content = newContent;
        }
    }
}

console.log('\n📦 [1] Vision 角色范围\n');

await test('仅处理 user 消息中的图片', async () => {
    const messages = [
        {
            role: 'assistant',
            content: [
                { type: 'text', text: 'assistant says hi' },
                { type: 'image', source: { type: 'url', data: 'https://example.com/a.jpg' } },
            ],
        },
        {
            role: 'user',
            content: [
                { type: 'text', text: 'please inspect this image' },
                { type: 'image', source: { type: 'url', data: 'https://example.com/b.jpg' } },
            ],
        },
    ];

    await applyVisionInterceptor(messages);

    assert(messages[0].content.some(block => block.type === 'image'), 'assistant image should remain untouched');
    assert(messages[1].content.every(block => block.type !== 'image'), 'user images should be converted away');
    assert(messages[1].content.some(block => block.type === 'text' && block.text.includes('mock vision result')), 'user message should receive vision text');
});

await test('忽略非数组内容的 user 消息', async () => {
    const messages = [{ role: 'user', content: 'plain text only' }];
    await applyVisionInterceptor(messages);
    assert(messages[0].content === 'plain text only', 'plain text content should stay unchanged');
});

console.log('\n' + '═'.repeat(55));
console.log(`  结果: ${passed} 通过 / ${failed} 失败 / ${passed + failed} 总计`);
console.log('═'.repeat(55) + '\n');

if (failed > 0) process.exit(1);