mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-10-07 14:20:05 +08:00
feat: 重构问问微信并完善知识库增量检索
统一问问微信与 Agent Hub 的查询链路 完善查询覆盖度、新鲜度和部分结果表达,避免索引滞后产生错误结论 基于会话实现真正的增量追新与历史补齐 支持后台同步、取消恢复、重启续传以及同步期间继续查询 优化知识库跨会话检索、同步状态、进度展示和侧栏布局
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
KNOWLEDGE_FRESHNESS_TOLERANCE_MS,
|
||||
isKnowledgeFresh,
|
||||
type KnowledgePassProgress,
|
||||
type KnowledgeRuntimeStatus
|
||||
} from '../../src/shared/knowledge'
|
||||
import { formatIndexDate, knowledgeIsStale, knowledgeStateLabel } from '../../src/renderer/src/components/search/searchFormatters'
|
||||
|
||||
type StatusPatch = Partial<
|
||||
Pick<KnowledgeRuntimeStatus, 'state' | 'indexLatestAt' | 'sourceLatestAt' | 'pass'>
|
||||
>
|
||||
|
||||
const makePass = (phase: KnowledgePassProgress['phase']): KnowledgePassProgress => ({
|
||||
phase,
|
||||
cancellable: phase !== 'idle',
|
||||
startedAt: 0,
|
||||
scannedMessages: 0,
|
||||
indexedMessages: 0,
|
||||
processedConversations: 0,
|
||||
totalConversations: 0,
|
||||
skippedConversations: 0,
|
||||
mainLoopLagMs: 0
|
||||
})
|
||||
|
||||
const status = (patch: StatusPatch): KnowledgeRuntimeStatus =>
|
||||
({
|
||||
accountId: 'fixture',
|
||||
state: 'ready',
|
||||
indexedMessageCount: 1,
|
||||
indexedChunkCount: 1,
|
||||
sourceMessageCount: 1,
|
||||
processedMessages: 1,
|
||||
totalMessages: 1,
|
||||
estimatedRemainingMs: null,
|
||||
databaseBytes: 0,
|
||||
walBytes: 0,
|
||||
shmBytes: 0,
|
||||
indexLatestAt: null,
|
||||
sourceLatestAt: null,
|
||||
...patch
|
||||
})
|
||||
|
||||
describe('isKnowledgeFresh(READY 与 FRESH 是两个概念)', () => {
|
||||
it('索引追平源数据时算 fresh', () => {
|
||||
expect(isKnowledgeFresh({ indexLatestAt: 1000, sourceLatestAt: 1000 })).toBe(true)
|
||||
})
|
||||
|
||||
it('容差内算 fresh:吸收 Session 元数据晚于消息落库的漂移', () => {
|
||||
expect(
|
||||
isKnowledgeFresh({ indexLatestAt: 1000, sourceLatestAt: 1000 + KNOWLEDGE_FRESHNESS_TOLERANCE_MS })
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('明显落后时算 stale', () => {
|
||||
expect(
|
||||
isKnowledgeFresh({ indexLatestAt: 1000, sourceLatestAt: 1000 + KNOWLEDGE_FRESHNESS_TOLERANCE_MS + 1 })
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('任一侧口径缺失时返回 null(无法判定,不冒充 fresh)', () => {
|
||||
expect(isKnowledgeFresh({ indexLatestAt: null, sourceLatestAt: 1000 })).toBeNull()
|
||||
expect(isKnowledgeFresh({ indexLatestAt: 1000, sourceLatestAt: null })).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Knowledge UI 状态文案', () => {
|
||||
const indexLatestAt = new Date('2026-08-26T11:37:24+08:00').getTime()
|
||||
const sourceLatestAt = new Date('2026-09-11T11:57:24+08:00').getTime()
|
||||
|
||||
it('索引落后时显示「可用 · 待追新」,不得显示「已同步」', () => {
|
||||
const label = knowledgeStateLabel(status({ indexLatestAt, sourceLatestAt }))
|
||||
// READY ≠ FRESH:落后时既要说"还能用",又要如实说"还没追平"。
|
||||
expect(label).toBe('可用 · 待追新')
|
||||
expect(label).not.toBe('已同步')
|
||||
expect(label).not.toContain('已追至最新')
|
||||
expect(knowledgeIsStale(status({ indexLatestAt, sourceLatestAt }))).toBe(true)
|
||||
// 卡片必须能给出真实的覆盖边界。
|
||||
expect(formatIndexDate(indexLatestAt)).toBe('8/26')
|
||||
})
|
||||
|
||||
it('索引真正追平源数据最新时才显示「可用 · 已追至最新」', () => {
|
||||
expect(knowledgeStateLabel(status({ indexLatestAt: sourceLatestAt, sourceLatestAt }))).toBe(
|
||||
'可用 · 已追至最新'
|
||||
)
|
||||
expect(knowledgeIsStale(status({ indexLatestAt: sourceLatestAt, sourceLatestAt }))).toBe(false)
|
||||
})
|
||||
|
||||
it('无法判定 freshness 时不能把"不确定"说成已经追平', () => {
|
||||
// 安全侧:无法确认就按"可能还没追平"表达,绝不说「已追至最新」。
|
||||
expect(knowledgeStateLabel(status({ indexLatestAt, sourceLatestAt: null }))).toBe(
|
||||
'可用 · 待追新'
|
||||
)
|
||||
expect(knowledgeIsStale(status({ indexLatestAt, sourceLatestAt: null }))).toBe(false)
|
||||
})
|
||||
|
||||
it('同步中必须区分「正在追新」与「正在补齐历史」', () => {
|
||||
expect(knowledgeStateLabel(status({ state: 'syncing', indexLatestAt, sourceLatestAt }))).toBe(
|
||||
'可用 · 正在追新'
|
||||
)
|
||||
// 首轮全量建立历史时不能谎称"只是在追新"。
|
||||
expect(
|
||||
knowledgeStateLabel(
|
||||
status({
|
||||
state: 'syncing',
|
||||
indexLatestAt,
|
||||
sourceLatestAt,
|
||||
pass: makePass('full')
|
||||
})
|
||||
)
|
||||
).toBe('可用 · 正在补齐历史')
|
||||
})
|
||||
|
||||
it('取消与失败都不能丢掉「可用」前缀(已建立的分片仍然能查)', () => {
|
||||
expect(knowledgeStateLabel(status({ state: 'cancelled' }))).toBe('可用 · 同步已取消')
|
||||
expect(knowledgeStateLabel(status({ state: 'error' }))).toBe('可用 · 更新失败')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user