✨ feat(vscode): 问 AI 收起再展开、重启后接着这块最近的会话问;代码改了对不上的问答不再显示
This commit is contained in:
1 parent
cbe447ade8
commit
f0a3ad80e1
16 files changed
+82
-211
No files matched your search
@@ -1,13 +1,12 @@
|
||||
import type { QaAction, QaState } from './qa'
|
||||
import type { BlockView, QaMessage, QaThreadView } from './types'
|
||||
import type { QaMessage, QaThreadView } from './types'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { answerPreview, asksOf, EMPTY_QA, historyOf, orphanThreads, qaReducer, relativeTime, resumeFor, whereLabel } from './qa'
|
||||
import { answerPreview, asksOf, EMPTY_QA, historyOf, qaReducer, relativeTime } from './qa'
|
||||
|
||||
const IF = 'function get_user > if @3fa1c2d0'
|
||||
const RETURN = 'function get_user > return @9b1e2c3d'
|
||||
|
||||
function thread(anchor: string, ...ids: string[]): QaThreadView {
|
||||
return { anchor, where: [], entries: ids.map(id => ({ id, question: `问 ${id}`, answer: `答 ${id}`, createdAt: '2026-09-25T10:00:00.000Z' })) }
|
||||
return { anchor, entries: ids.map(id => ({ id, question: `问 ${id}`, answer: `答 ${id}`, createdAt: '2026-09-25T10:00:00.000Z' })) }
|
||||
}
|
||||
|
||||
function message(threads: QaThreadView[], running: QaMessage['running'] = []): QaMessage {
|
||||
@@ -18,10 +17,6 @@ function qa(threads: QaThreadView[], running: QaMessage['running'] = []): QaActi
|
||||
return { type: 'qa', message: message(threads, running) }
|
||||
}
|
||||
|
||||
function block(anchor: string, children: BlockView[] = []): BlockView {
|
||||
return { id: anchor, anchor, kind: 'statement', startLine: 0, endLine: 0, code: '', codeTokens: [], attributes: {}, children, translation: [] }
|
||||
}
|
||||
|
||||
function run(...actions: QaAction[]): QaState {
|
||||
return actions.reduce(qaReducer, EMPTY_QA)
|
||||
}
|
||||
@@ -33,34 +28,23 @@ describe('问 AI 的状态', () => {
|
||||
expect(asksOf(state, IF)).toEqual([{ askId: 'a1', anchor: IF, question: '为什么?', text: '', status: 'running' }])
|
||||
state = qaReducer(state, { type: 'answer', askId: 'a1', text: '因为', activity: '读取 a.py' })
|
||||
expect(asksOf(state, IF)[0]).toMatchObject({ text: '因为', activity: '读取 a.py' })
|
||||
state = qaReducer(state, { type: 'answerEnd', end: { type: 'answerEnd', askId: 'a1', status: 'done', text: '因为要鉴权。', entryId: 'new', sessionId: 's1' } })
|
||||
state = qaReducer(state, { type: 'answerEnd', end: { type: 'answerEnd', askId: 'a1', status: 'done', text: '因为要鉴权。', entryId: 'new' } })
|
||||
state = qaReducer(state, qa([thread(IF, 'old', 'new')]))
|
||||
expect(asksOf(state, IF)).toEqual([{ askId: 'a1', anchor: IF, question: '为什么?', text: '因为要鉴权。', status: 'done', entryId: 'new', sessionId: 's1' }])
|
||||
expect(asksOf(state, IF)).toEqual([{ askId: 'a1', anchor: IF, question: '为什么?', text: '因为要鉴权。', status: 'done', entryId: 'new' }])
|
||||
expect(historyOf(state, IF).map(entry => entry.id)).toEqual(['old'])
|
||||
})
|
||||
|
||||
it('追问接着这一轮最近答成的会话;这一轮还没答成过就重新开', () => {
|
||||
let state = run(qa([]), { type: 'ask', askId: 'a1', anchor: IF, question: '1' })
|
||||
expect(resumeFor(state, IF)).toBeUndefined()
|
||||
state = qaReducer(state, { type: 'answerEnd', end: { type: 'answerEnd', askId: 'a1', status: 'done', entryId: 'e1', sessionId: 's1' } })
|
||||
state = qaReducer(state, { type: 'ask', askId: 'a2', anchor: IF, question: '2' })
|
||||
state = qaReducer(state, { type: 'answerEnd', end: { type: 'answerEnd', askId: 'a2', status: 'failed', error: '网络断了' } })
|
||||
expect(resumeFor(state, IF)).toBe('s1')
|
||||
expect(resumeFor(state, RETURN)).toBeUndefined()
|
||||
})
|
||||
|
||||
it('收起就结束这一轮:答完的回到历史,还在答的留着;再展开', () => {
|
||||
let state = run(
|
||||
qa([thread(IF, 'e1')]),
|
||||
{ type: 'ask', askId: 'a1', anchor: IF, question: '1' },
|
||||
{ type: 'answerEnd', end: { type: 'answerEnd', askId: 'a1', status: 'done', entryId: 'e1', sessionId: 's1' } },
|
||||
{ type: 'answerEnd', end: { type: 'answerEnd', askId: 'a1', status: 'done', entryId: 'e1' } },
|
||||
{ type: 'ask', askId: 'a2', anchor: IF, question: '2' },
|
||||
{ type: 'close', anchor: IF },
|
||||
)
|
||||
expect(state.open).toEqual([])
|
||||
expect(asksOf(state, IF).map(ask => ask.askId)).toEqual(['a2'])
|
||||
expect(historyOf(state, IF).map(entry => entry.id)).toEqual(['e1'])
|
||||
expect(resumeFor(state, IF)).toBeUndefined()
|
||||
state = qaReducer(state, { type: 'open', anchor: IF })
|
||||
expect(state.open).toEqual([IF])
|
||||
})
|
||||
@@ -83,19 +67,6 @@ describe('问 AI 的状态', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('找不到原来位置的问答', () => {
|
||||
it('记号对不上任何一层的块才算', () => {
|
||||
const threads = [thread(IF, 'a'), thread(RETURN, 'b'), thread('function gone', 'c')]
|
||||
expect(orphanThreads(threads, [block('function get_user', [block(IF)])]).map(item => item.anchor)).toEqual([RETURN, 'function gone'])
|
||||
})
|
||||
|
||||
it('原来在哪:种类取翻译插件起的名字,没名字的块只写种类', () => {
|
||||
const names: Record<string, string> = { class: '类', function: '函数', if: '条件' }
|
||||
expect(whereLabel([{ kind: 'class', name: 'ReferralRelation' }, { kind: 'function', name: 'display' }, { kind: 'if' }], kind => names[kind] ?? kind))
|
||||
.toBe('类 ReferralRelation › 函数 display › 条件')
|
||||
})
|
||||
})
|
||||
|
||||
describe('问的时间', () => {
|
||||
const now = new Date(2026, 8, 26, 15, 0).getTime()
|
||||
const at = (year: number, month: number, day: number, hour: number, minute: number, second = 0): string =>
|
||||
|
||||
Reference in new issue
Block a user