Add smart ACK provider for WeChat MP replies
Replace fixed ackText with a rule-based AckProvider that picks response templates by message type and intent (translate, summary, rewrite, poster, ppt, mindmap, code, search, schedule). Pure sync, zero I/O, auto-falls back to config.ackText on any error. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { buildVisionPayload, extractImageUrlsFromMessage } from './tkmind-proxy.mjs';
|
||||
|
||||
test('extractImageUrlsFromMessage reads metadata image urls', () => {
|
||||
const urls = extractImageUrlsFromMessage({
|
||||
metadata: {
|
||||
imageUrls: ['/api/mindspace/v1/assets/asset-1/download?inline=1'],
|
||||
},
|
||||
content: [{ type: 'text', text: '根据图片生成页面' }],
|
||||
});
|
||||
|
||||
assert.deepEqual(urls, ['/api/mindspace/v1/assets/asset-1/download?inline=1']);
|
||||
});
|
||||
|
||||
test('extractImageUrlsFromMessage falls back to formatted image lines', () => {
|
||||
const urls = extractImageUrlsFromMessage({
|
||||
metadata: { userVisible: true, agentVisible: true },
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text:
|
||||
'根据图片生成一个主题页面\n\n' +
|
||||
'[图片1]: https://m.tkmind.cn/api/mindspace/v1/assets/asset-2/download?inline=1&v=123',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
assert.deepEqual(urls, [
|
||||
'https://m.tkmind.cn/api/mindspace/v1/assets/asset-2/download?inline=1&v=123',
|
||||
]);
|
||||
});
|
||||
|
||||
test('extractImageUrlsFromMessage deduplicates image urls', () => {
|
||||
const url = '/api/mindspace/v1/assets/asset-3/download?inline=1';
|
||||
const urls = extractImageUrlsFromMessage({
|
||||
metadata: { imageUrls: [url] },
|
||||
content: [{ type: 'text', text: `[图片1]: ${url}` }],
|
||||
});
|
||||
|
||||
assert.deepEqual(urls, [url]);
|
||||
});
|
||||
|
||||
test('buildVisionPayload marks one billable image analysis when vision succeeds', async () => {
|
||||
const result = await buildVisionPayload({
|
||||
userId: 'user-1',
|
||||
publishLayout: { publicUrl: 'https://m.tkmind.cn/MindSpace/user-1' },
|
||||
userMessage: {
|
||||
content: [{ type: 'text', text: '根据图片生成页面\n\n[图片1]: /api/mindspace/v1/assets/asset-1/download?inline=1' }],
|
||||
},
|
||||
localFetchAsset: async () => ({
|
||||
buffer: Buffer.from('fake-image'),
|
||||
mimeType: 'image/png',
|
||||
}),
|
||||
llmProviderService: {
|
||||
analyzeImagesWithVision: async () => '一位小女孩正在餐厅里开心地吃饭',
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(result?.billableImageCount, 1);
|
||||
assert.match(result?.userMessage?.content?.[0]?.text ?? '', /Qwen VL 图片描述/);
|
||||
});
|
||||
|
||||
test('buildVisionPayload does not mark billable usage when vision analysis fails', async () => {
|
||||
const result = await buildVisionPayload({
|
||||
userId: 'user-1',
|
||||
publishLayout: { publicUrl: 'https://m.tkmind.cn/MindSpace/user-1' },
|
||||
userMessage: {
|
||||
content: [{ type: 'text', text: '根据图片生成页面\n\n[图片1]: /api/mindspace/v1/assets/asset-1/download?inline=1' }],
|
||||
},
|
||||
localFetchAsset: async () => ({
|
||||
buffer: Buffer.from('fake-image'),
|
||||
mimeType: 'image/png',
|
||||
}),
|
||||
llmProviderService: {
|
||||
analyzeImagesWithVision: async () => {
|
||||
throw new Error('vision unavailable');
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(result?.billableImageCount, 0);
|
||||
assert.doesNotMatch(result?.userMessage?.content?.[0]?.text ?? '', /Qwen VL 图片描述/);
|
||||
});
|
||||
Reference in New Issue
Block a user