feat(knowledge): add short-term ask memory
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import test from 'node:test';
|
||||
|
||||
const read = (path) => readFile(new URL(path, import.meta.url), 'utf8');
|
||||
|
||||
test('问师傅为文字、媒体和数据工具请求携带同一短期会话版本', async () => {
|
||||
const service = await read('../src/services/knowledge.ts');
|
||||
|
||||
assert.match(service, /conversationId:\s*conversation\?\.conversationId/);
|
||||
assert.match(service, /contextVersion:\s*conversation\?\.contextVersion/);
|
||||
assert.match(service, /conversationId:\s*conversation\.conversationId/);
|
||||
assert.match(service, /resources:\s*response\.resources \|\| \[\]/);
|
||||
});
|
||||
|
||||
test('问师傅支持显式新对话、版本回写和受控原文件或视频卡', async () => {
|
||||
const page = await read('../src/pages/user/sop/index.vue');
|
||||
|
||||
assert.match(page, /@click="startNewConversation">新对话/);
|
||||
assert.match(page, /const contextVersion = ref\(0\)/);
|
||||
assert.match(page, /applyConversationState\(nextResult\)/);
|
||||
assert.match(page, /authenticatedFileUrl\(resource\.contentUrl\)/);
|
||||
assert.match(page, /releaseAuthenticatedFileUrl\(resource\.localUrl\)/);
|
||||
assert.match(page, /resource\.type === 'VIDEO' && resource\.localUrl/);
|
||||
assert.match(page, /msg\.result\.rewrittenQuery !== msg\.result\.queryText/);
|
||||
assert.match(page, /已结合本次对话理解/);
|
||||
});
|
||||
@@ -22,14 +22,14 @@ test('SOP 查询框不再覆盖 textarea 的 box-sizing', async () => {
|
||||
assert.equal(queryPanelStyle.includes('box-sizing'), false);
|
||||
});
|
||||
|
||||
test('员工首页地图主标题保留真实工具名称', async () => {
|
||||
test('员工首页核心工具保留真实业务入口', async () => {
|
||||
const source = await pageSource('../src/pages/user/today/index.vue');
|
||||
|
||||
assert.match(source, /class="map-symbol"/);
|
||||
assert.match(source, /<text class="map-title">\{\{ tile\.title \}\}<\/text>/);
|
||||
assert.match(source, /title:\s*'问老师傅'/);
|
||||
assert.match(source, /title:\s*'刻意练习'/);
|
||||
assert.match(source, /shortTitle:\s*'问'/);
|
||||
assert.match(source, /<text class="voice-title">问老师傅<\/text>/);
|
||||
assert.match(source, /<text class="question-board-title">问题榜<\/text>/);
|
||||
assert.match(source, /@click="goSop"/);
|
||||
assert.match(source, /const goPractice = \(\) =>/);
|
||||
assert.match(source, /const openQuestions = \(\) =>/);
|
||||
});
|
||||
|
||||
test('训练页提交失败会回滚本次乐观追加的对话', async () => {
|
||||
@@ -74,7 +74,7 @@ test('共享视觉基座包含员工端主色和卡片圆角 token', async () =>
|
||||
assert.match(source, /\.employee-card\s*\{/);
|
||||
});
|
||||
|
||||
test('员工端三页接入共享页面和卡片基座', async () => {
|
||||
test('员工端首页、问师傅和训练页接入当前共享基座', async () => {
|
||||
const [todaySource, sopSource, practiceSource] = await Promise.all([
|
||||
pageSource('../src/pages/user/today/index.vue'),
|
||||
pageSource('../src/pages/user/sop/index.vue'),
|
||||
@@ -83,7 +83,8 @@ test('员工端三页接入共享页面和卡片基座', async () => {
|
||||
|
||||
assert.match(todaySource, /employee-page-shell/);
|
||||
assert.match(todaySource, /section-card employee-card/);
|
||||
assert.match(sopSource, /query-panel employee-card/);
|
||||
assert.match(sopSource, /sop-chat-page/);
|
||||
assert.match(sopSource, /<ChatComposer/);
|
||||
assert.match(practiceSource, /scenario-card employee-card/);
|
||||
});
|
||||
|
||||
@@ -122,13 +123,13 @@ test('主管可切换到学员显示模式且组织角色保持不变', async ()
|
||||
assert.doesNotMatch(supervisorProfile, /setStorageSync\(roleKey,\s*'user'/);
|
||||
});
|
||||
|
||||
test('员工首页保留师傅区、语音条、今日安排、我的地图四段结构', async () => {
|
||||
test('员工首页保留师傅区、语音条、今日安排和问题榜结构', async () => {
|
||||
const source = await pageSource('../src/pages/user/today/index.vue');
|
||||
|
||||
assert.match(source, /mentor-hero/);
|
||||
assert.match(source, /voice-panel/);
|
||||
assert.match(source, /今日安排/);
|
||||
assert.match(source, /我的地图/);
|
||||
assert.match(source, /问题榜/);
|
||||
assert.match(source, /brand-bar/);
|
||||
assert.match(source, /ask-card/);
|
||||
assert.match(source, /小贴士/);
|
||||
@@ -319,14 +320,17 @@ test('主管专项从正式团队名单按姓名多选并一次批量派发', as
|
||||
});
|
||||
|
||||
test('问师傅支持现场媒体、答案播报和总结卡图片下载', async () => {
|
||||
const [source, knowledgeService, speechService, imageService] = await Promise.all([
|
||||
const [source, composerSource, knowledgeService, speechService, imageService] = await Promise.all([
|
||||
pageSource('../src/pages/user/sop/index.vue'),
|
||||
pageSource('../src/components/chat/ChatComposer.vue'),
|
||||
pageSource('../src/services/knowledge.ts'),
|
||||
pageSource('../src/services/speech.ts'),
|
||||
pageSource('../src/services/summary-card-image.ts')
|
||||
]);
|
||||
|
||||
assert.match(source, /上传图片或视频/);
|
||||
assert.match(source, /@pick-media="pickMedia"/);
|
||||
assert.match(composerSource, /图片\/视频/);
|
||||
assert.match(composerSource, /仅用于本次现场分析/);
|
||||
assert.match(source, /searchKnowledgeWithMedia/);
|
||||
assert.match(source, /toggleAnswerSpeech/);
|
||||
assert.match(source, /toggleSummarySpeech/);
|
||||
@@ -340,18 +344,21 @@ test('问师傅支持现场媒体、答案播报和总结卡图片下载', async
|
||||
});
|
||||
|
||||
test('问师傅恢复旧版语音提问并在录音不可用时回退选择音频', async () => {
|
||||
const [source, speechService] = await Promise.all([
|
||||
const [source, composerSource, speechService] = await Promise.all([
|
||||
pageSource('../src/pages/user/sop/index.vue'),
|
||||
pageSource('../src/components/chat/ChatComposer.vue'),
|
||||
pageSource('../src/services/speech.ts')
|
||||
]);
|
||||
|
||||
assert.match(source, /语音提问/);
|
||||
assert.match(source, /toggleVoiceRecording/);
|
||||
assert.match(composerSource, /按住 说话/);
|
||||
assert.match(composerSource, /startHold/);
|
||||
assert.match(composerSource, /endHold/);
|
||||
assert.match(composerSource, /navigator\.mediaDevices\?\.getUserMedia/);
|
||||
assert.match(composerSource, /emit\('pick-audio'\)/);
|
||||
assert.match(composerSource, /const teardown = \(\) =>/);
|
||||
assert.match(source, /pickVoiceAudio/);
|
||||
assert.match(source, /transcribeSpeechBlob/);
|
||||
assert.match(source, /transcribeSpeechFile/);
|
||||
assert.match(source, /navigator\.mediaDevices\?\.getUserMedia/);
|
||||
assert.match(source, /cancelVoiceRecording/);
|
||||
assert.match(speechService, /\/api\/ai\/asr/);
|
||||
});
|
||||
|
||||
@@ -359,7 +366,10 @@ test('生成的每日题和对练对话恢复语音播报入口', async () => {
|
||||
const source = await pageSource('../src/pages/user/practice/index.vue');
|
||||
|
||||
assert.match(source, /daily-question-voice/);
|
||||
assert.match(source, /turn\.role !== 'trainee'/);
|
||||
assert.match(source, /turn\.role === 'customer'/);
|
||||
assert.match(source, /turn\.role === 'coach'/);
|
||||
assert.match(source, /playTurnVoice/);
|
||||
assert.match(source, /speakGenerated/);
|
||||
assert.match(source, /createSpeechPlaybackController/);
|
||||
assert.match(source, /停止播报/);
|
||||
});
|
||||
@@ -383,10 +393,9 @@ test('语音、媒体和私密画像在切页或退出后不会继续写回', as
|
||||
pageSource('../src/services/knowledge.ts')
|
||||
]);
|
||||
|
||||
assert.match(sopSource, /onHide\(\(\) => \{[\s\S]*speechPlayback\.stop\(\);[\s\S]*cancelVoiceRecording\(\);[\s\S]*cancelPendingMediaSearch\(\);/);
|
||||
assert.match(sopSource, /onHide\(\(\) => \{[\s\S]*speechPlayback\.stop\(\);[\s\S]*composerRef\.value\?\.teardown\(\);[\s\S]*cancelVoiceTranscription\(\);[\s\S]*cancelPendingMediaSearch\(\);/);
|
||||
assert.match(sopSource, /activeMediaUploadTask\?\.abort\(\)/);
|
||||
assert.match(sopSource, /generation !== knowledgeSearchGeneration/);
|
||||
assert.match(sopSource, /voiceStarting\.value/);
|
||||
assert.match(sopSource, /abortVoiceTranscription\?\.\(\)/);
|
||||
assert.match(sopSource, /generation !== voiceTranscriptionGeneration/);
|
||||
assert.match(practiceSource, /onHide\(\(\) => \{[\s\S]*speechPlayback\.stop\(\);[\s\S]*cancelRecording\(\);/);
|
||||
@@ -409,7 +418,8 @@ test('播报复用已合成音频且总结图按内容高度生成并及时释
|
||||
|
||||
assert.match(speechService, /const sourceCache = new Map<string, string>\(\)/);
|
||||
assert.match(speechService, /const maxCachedSources = 4/);
|
||||
assert.match(speechService, /const cachedSource = sourceCache\.get\(value\)/);
|
||||
assert.match(speechService, /const sourceKey = cacheKey\(value, profile\)/);
|
||||
assert.match(speechService, /const cachedSource = sourceCache\.get\(sourceKey\)/);
|
||||
assert.match(speechService, /sourceCache\.clear\(\)/);
|
||||
assert.match(speechService, /registerAbort\?\.\(\(\) => task\.abort\(\)\)/);
|
||||
assert.match(speechService, /registerAbort\?\.\(\(\) => xhr\.abort\(\)\)/);
|
||||
|
||||
Reference in New Issue
Block a user