数字我 Digital Me:全量入库(大文件走 Git LFS)
nginx serve /uploads direct (audio speed) / patch-nginx (push) Failing after 1m34s

This commit is contained in:
2026-10-10 16:24:43 +08:00
commit 0593ff7e62
1893 changed files with 361723 additions and 0 deletions
+77
View File
@@ -0,0 +1,77 @@
// 能力中枢助手:确定性部分的测试(检索 / 语音清洗 / 无 key 降级 / 总览)。
// 不设 DASHSCOPE / ANTHROPIC key,走 fallback 分支,无需网络。
delete process.env.DASHSCOPE_API_KEY;
delete process.env.ANTHROPIC_API_KEY;
import { test } from 'node:test';
import assert from 'node:assert/strict';
const { searchCapabilities, CAPABILITIES, getCapability, listCapabilities } = await import('../src/capabilities.js');
const { speechClean, toSegments, answer, overviewText } = await import('../src/services/assistant.js');
test('检索:口语提问能命中对应功能', () => {
assert.equal(searchCapabilities('怎么克隆我的声音')[0].id, 'voice');
assert.equal(searchCapabilities('想让分身出镜说话拍视频')[0].id, 'talkvideo');
assert.equal(searchCapabilities('只想发给特定的人不公开')[0].id, 'share');
assert.equal(searchCapabilities('养得好不好怎么看')[0].id, 'grounding');
});
// 目录只能列【页面上真的存在】的功能。收费模式受 FEATURE_MARKETPLACE 门控、默认关闭——
// 关着的时候它必须彻底检索不到,否则助手会把用户送去一个页面上根本没有的按钮。
test('门控:marketplace 关闭时,收费模式不出现在目录/检索里', () => {
assert.equal(searchCapabilities('怎么用分身挣钱').length, 0);
assert.equal(getCapability('paid'), null);
assert.ok(!listCapabilities().some((c) => c.id === 'paid'));
});
test('语音清洗:去掉 Markdown / 网址 / 表情 / 序号', () => {
const dirty = '# 标题\n1. 第一步 **加粗** `代码` 😀 看 https://x.com/y [链接](https://z.com)';
const clean = speechClean(dirty);
assert.doesNotMatch(clean, /[#*`]/, '不应残留 Markdown 符号');
assert.doesNotMatch(clean, /https?:\/\//, '不应残留网址');
assert.doesNotMatch(clean, /😀/, '不应残留表情');
assert.match(clean, /链接/, '链接文字应保留');
});
test('分句:按句末标点切分', () => {
const segs = toSegments('第一句。第二句!第三句?');
assert.equal(segs.length, 3);
assert.equal(segs[0], '第一句。');
});
test('无 key 降级:命中功能时产出可朗读话术(不含 Markdown)', async () => {
const r = await answer('怎么克隆声音');
assert.equal(r.source, 'fallback');
assert.ok(r.spoken.length > 0);
assert.equal(r.matched[0].id, 'voice');
assert.ok(r.segments.length >= 1);
assert.doesNotMatch(r.spoken, /[#*`]|https?:\/\//);
// 韵律/情感输出:ssml(语调)+ style(情感风格)供合成网关使用。
assert.match(r.ssml, /^<speak[\s\S]*<prosody/, 'ssml 应含 speak + prosody');
assert.equal(r.style, 'friendly');
});
test('多句话术的 ssml 含句间停顿 break', async () => {
const r = await answer('你好呀今天天气不错'); // 走总览(多句)
assert.match(r.ssml, /<break/, '多句 ssml 应含句间停顿');
});
test('无 key 降级:没命中时给平台总览引导', async () => {
const r = await answer('你好呀今天天气不错');
assert.equal(r.source, 'fallback');
assert.match(r.spoken, /能力助手|数字分身|想先了解/);
});
test('总览话术非空且可朗读', () => {
const t = overviewText();
assert.ok(t.length > 20);
assert.doesNotMatch(t, /[#*`]/);
});
test('每条能力都具备助手所需字段', () => {
for (const c of CAPABILITIES) {
for (const k of ['id', 'name', 'category', 'summary', 'howto', 'where', 'speak']) {
assert.ok(c[k] !== undefined && c[k] !== '', `能力 ${c.id} 缺字段 ${k}`);
}
}
});