✨ feat(vscode): 问 AI 输入框旁边能选 Claude profile,记住上次选的,问答灰字写用哪个 profile 答的
This commit is contained in:
1 parent
1eb42d8389
commit
9efae1f496
20 files changed
+397
-63
No files matched your search
@@ -4,6 +4,7 @@
|
||||
|
||||
import type { FileAttribute } from '../attributes'
|
||||
import type { BlockView } from '../blockView'
|
||||
import type { ProfileChoice } from '../qa/askProfiles'
|
||||
import type { RunningAsk } from '../qa/asks'
|
||||
import type { Excluded } from './blockFilter'
|
||||
|
||||
@@ -45,12 +46,14 @@ export interface DocumentMessage {
|
||||
reveal?: Reveal
|
||||
}
|
||||
|
||||
/** 问过的一次(给预览看的):问题、回答(Markdown)、什么时候问的(ISO 时间)。 */
|
||||
/** 问过的一次(给预览看的):问题、回答(Markdown)、什么时候问的(ISO 时间)、用哪个 profile 答的。 */
|
||||
export interface QaEntryView {
|
||||
id: string
|
||||
question: string
|
||||
answer: string
|
||||
createdAt: string
|
||||
/** 用哪个 profile 答的(profile 名);选「默认」时、以前存的没有 */
|
||||
profile?: string
|
||||
}
|
||||
|
||||
/** 挂在一块上的问答;记号对不上这份文件里任何一块的不显示。 */
|
||||
@@ -76,6 +79,7 @@ export interface QaMessage {
|
||||
* occurrencesFound:id 那次 findOccurrences 的回答,语言扩展没答(或没装)就是空的
|
||||
* filter:整个工作区共用的块筛选;ready 后先于第一份文档发一次,之后任何一个预览改了都发给所有预览
|
||||
* qa:这份文件的问答;ready 后发一次,问答、正在答的问题变了再发
|
||||
* askProfiles:「问 AI」输入框旁边的 profile 下拉:选项和选中的那项;ready 后发一次,任何一个预览改了都发给所有预览
|
||||
* answer:askId 那个问题的回答长了(text 是到现在为止的全文)、或换了在做的事
|
||||
* answerEnd:askId 那个问题答完了:done 带最终的回答和存下的那条的 id,failed 带给人看的原因,stopped 是被叫停
|
||||
*/
|
||||
@@ -86,6 +90,7 @@ export type ExtensionToWebview
|
||||
| { type: 'occurrencesFound', id: number, occurrences: Occurrence[] }
|
||||
| { type: 'filter', excluded: Excluded }
|
||||
| QaMessage
|
||||
| ({ type: 'askProfiles' } & ProfileChoice)
|
||||
| { type: 'answer', askId: string, text: string, activity?: string }
|
||||
| { type: 'answerEnd', askId: string, status: 'done' | 'failed' | 'stopped', text?: string, error?: string, entryId?: string }
|
||||
|
||||
@@ -97,9 +102,10 @@ export type ExtensionToWebview
|
||||
* goToDefinition:⌘ / Ctrl + 点击,跳到 offset 处名字的定义
|
||||
* findOccurrences:单击带下标的名字,问它在这份文档里出现在哪几处;id、version 同 probeDefinition
|
||||
* setFilter:改块筛选(整份替换),扩展端记下后发 filter 给所有预览
|
||||
* ask:问 anchor 那块代码;askId 由 webview 起,回答的消息带回去;扩展端接着这块最近一次问答的会话问
|
||||
* ask:问 anchor 那块代码;askId 由 webview 起,回答的消息带回去;扩展端接着这块最近一次问答的会话问,用眼下选的 profile
|
||||
* stopAsk:叫停 askId 那个问题,这次不存
|
||||
* deleteQa:删掉 anchor 那块下面的一次问答(已经理解了)
|
||||
* selectAskProfile:「问 AI」换 profile(id 是 profile 名,「默认」是 ''),扩展端记下后发 askProfiles 给所有预览
|
||||
*/
|
||||
export type WebviewToExtension
|
||||
= | { type: 'ready' }
|
||||
@@ -113,3 +119,4 @@ export type WebviewToExtension
|
||||
| { type: 'ask', askId: string, anchor: string, question: string }
|
||||
| { type: 'stopAsk', askId: string }
|
||||
| { type: 'deleteQa', anchor: string, entryId: string }
|
||||
| { type: 'selectAskProfile', id: string }
|
||||
@@ -143,6 +143,10 @@ class Preview {
|
||||
if (uris.some(uri => uri.toString() === document.uri.toString()))
|
||||
this.postQa()
|
||||
}),
|
||||
owner.qa.profiles.subscribe((choice) => {
|
||||
if (this.ready)
|
||||
this.post({ type: 'askProfiles', ...choice })
|
||||
}),
|
||||
owner.qa.onDidAnswer((event) => {
|
||||
if (event.owner !== document.uri.toString() || !this.ready)
|
||||
return
|
||||
@@ -218,18 +222,25 @@ class Preview {
|
||||
this.post(this.owner.qa.view(this.document.uri))
|
||||
}
|
||||
|
||||
/** profile 列表每次现读(同看板),读完页面还在才发。 */
|
||||
private async postProfiles(): Promise<void> {
|
||||
const choice = await this.owner.qa.profiles.choice()
|
||||
if (this.ready)
|
||||
this.post({ type: 'askProfiles', ...choice })
|
||||
}
|
||||
|
||||
/**
|
||||
* 问 anchor 那块代码:按最近一次发出去的块树找到它,把完整问题和追问的话都交给 CodeQa,接不接着上次的会话由它定。
|
||||
* 找不到这块(代码刚改过、还没重新发)或问不了时,直接回一条失败。
|
||||
*/
|
||||
private ask(askId: string, anchor: string, question: string): void {
|
||||
private async ask(askId: string, anchor: string, question: string): Promise<void> {
|
||||
const at = locate(this.document.uri)
|
||||
const subject = at && this.sent && { path: at.key, languageId: this.document.languageId, ...this.sent, anchor }
|
||||
const prompt = subject && askPrompt(subject, question)
|
||||
const followUp = subject && followUpPrompt(subject, question)
|
||||
const refused = prompt === undefined || followUp === undefined
|
||||
? '这块代码刚改过,找不到了。等预览刷新后重新点「问 AI」再问。'
|
||||
: this.owner.qa.ask(this.document.uri, { askId, anchor, question, prompt, followUp })
|
||||
: await this.owner.qa.ask(this.document.uri, { askId, anchor, question, prompt, followUp })
|
||||
if (refused !== undefined) {
|
||||
this.post({ type: 'answerEnd', askId, status: 'failed', error: refused })
|
||||
return
|
||||
@@ -249,10 +260,14 @@ class Preview {
|
||||
// 先于文档发:第一份文档按筛选画,不会先整份露出来再藏
|
||||
this.post({ type: 'filter', excluded: this.filter.excluded })
|
||||
this.postQa()
|
||||
void this.postProfiles()
|
||||
await this.push()
|
||||
return
|
||||
case 'ask':
|
||||
this.ask(msg.askId, msg.anchor, msg.question)
|
||||
await this.ask(msg.askId, msg.anchor, msg.question)
|
||||
return
|
||||
case 'selectAskProfile':
|
||||
await this.owner.qa.profiles.select(msg.id)
|
||||
return
|
||||
case 'stopAsk':
|
||||
this.owner.qa.stop(msg.askId)
|
||||
|
||||
Reference in new issue
Block a user