From 3ec578c7be359bd6000366205f6b599b98b7eec5 Mon Sep 17 00:00:00 2001 From: "bobo.yang" Date: Tue, 6 Jun 2023 17:52:31 +0800 Subject: [PATCH] load topic from DevChat --- src/handler/historyMessagesBase.ts | 20 ++++++---- src/toolwrapper/devchat.ts | 30 +++++++++++++++ src/topic/loadTopics.ts | 50 ------------------------ src/topic/topicManager.ts | 62 +++++------------------------- test/topic/loadTopics.test.ts | 49 ----------------------- 5 files changed, 52 insertions(+), 159 deletions(-) delete mode 100644 src/topic/loadTopics.ts delete mode 100644 test/topic/loadTopics.test.ts diff --git a/src/handler/historyMessagesBase.ts b/src/handler/historyMessagesBase.ts index a28c8de..ac9bf5c 100644 --- a/src/handler/historyMessagesBase.ts +++ b/src/handler/historyMessagesBase.ts @@ -1,7 +1,7 @@ import { TopicManager } from '../topic/topicManager'; -import { LogEntry } from '../toolwrapper/devchat'; +import DevChat, { LogEntry, LogOptions } from '../toolwrapper/devchat'; import messageHistory from '../util/messageHistory'; import { ApiKeyManager } from '../util/apiKey'; import { logger } from '../util/logger'; @@ -63,15 +63,19 @@ export async function isWaitForApiKey() { export async function loadTopicHistoryLogs() : Promise | undefined> { const topicId = TopicManager.getInstance().currentTopicId; - let logEntriesFlat: Array = []; - if (topicId) { - logEntriesFlat = await TopicManager.getInstance().getTopicHistory(topicId); + if (!topicId) { + return []; } - if (topicId !== TopicManager.getInstance().currentTopicId) { - logger.channel()?.info(`Current topic changed dure load topic hsitory!`) - return undefined; - } + const devChat = new DevChat(); + const logOptions: LogOptions = { + skip: 0, + maxCount: 10000, + topic: topicId + }; + const logEntries = await devChat.log(logOptions); + const logEntriesFlat = logEntries.flat(); + return logEntriesFlat; } diff --git a/src/toolwrapper/devchat.ts b/src/toolwrapper/devchat.ts index f458f6b..b1477b6 100644 --- a/src/toolwrapper/devchat.ts +++ b/src/toolwrapper/devchat.ts @@ -24,6 +24,7 @@ export interface ChatOptions { export interface LogOptions { skip?: number; maxCount?: number; + topic?: string; } export interface LogEntry { @@ -257,6 +258,31 @@ class DevChat { return JSON.parse(stdout.trim()).reverse(); } + async topics(): Promise { + const args = ["topic", "-l"]; + const devChat = this.getDevChatPath(); + const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath(); + + logger.channel()?.info(`Running devchat with args: ${args.join(" ")}`); + const spawnOptions = { + maxBuffer: 10 * 1024 * 1024, // Set maxBuffer to 10 MB + cwd: workspaceDir, + env: { + ...process.env + }, + }; + const { exitCode: code, stdout, stderr } = await this.commandRun.spawnAsync(devChat, args, spawnOptions, undefined, undefined, undefined, undefined); + + logger.channel()?.info(`Finish devchat with args: ${args.join(" ")}`); + if (stderr) { + logger.channel()?.error(`Error getting log: ${stderr}`); + logger.channel()?.show(); + return []; + } + + return JSON.parse(stdout.trim()).reverse(); + } + private buildLogArgs(options: LogOptions): string[] { let args = ["log"]; @@ -270,6 +296,10 @@ class DevChat { args.push('--max-count', `${maxLogCount}`); } + if (options.topic) { + args.push('--topic', `${options.topic}`); + } + return args; } diff --git a/src/topic/loadTopics.ts b/src/topic/loadTopics.ts deleted file mode 100644 index e26a2cc..0000000 --- a/src/topic/loadTopics.ts +++ /dev/null @@ -1,50 +0,0 @@ -import DevChat, { LogEntry } from "../toolwrapper/devchat"; - - -export class LinkedList { - logEntries: LogEntry[]; - - constructor() { - this.logEntries = []; - } - - append(chatLog: LogEntry): void { - this.logEntries.push(chatLog); - } -} - -export function loadTopicList(chatLogs: LogEntry[]): { [key: string]: LogEntry[] } { - const topicLists: { [key: string]: LogEntry[] } = {}; - - // create map from parent to hash - // collect logEntry with parent is null - const parentToHash: { [key: string]: LogEntry } = {}; - const rootHashes: LogEntry[] = []; - for (const chatLog of chatLogs) { - if (chatLog.parent) { - parentToHash[chatLog.parent] = chatLog; - } else { - rootHashes.push(chatLog); - } - } - - // visite logEntry with parent is null - // find all children Entries from map, then create LinkedList - for (const rootHash of rootHashes) { - const topicList = new LinkedList(); - topicList.append(rootHash); - let current: LogEntry|undefined = rootHash; - while (current) { - const parent: LogEntry = parentToHash[current.hash]; - if (parent) { - topicList.append(parent); - current = parent; - } else { - current = undefined; - } - } - topicLists[rootHash.hash] = topicList.logEntries; - } - - return topicLists; -} \ No newline at end of file diff --git a/src/topic/topicManager.ts b/src/topic/topicManager.ts index 8c25768..66bc51a 100644 --- a/src/topic/topicManager.ts +++ b/src/topic/topicManager.ts @@ -231,39 +231,20 @@ export class TopicManager { return deletedTopics.includes(topicId); } - // loadTopics - // 功能:将DevChat日志根据parentHash进行分组,当前条目与该条目parentHash对应条目归属一组,以此类推,直到parentHash为空 - // 返回值:多个链表,每个链表中当前元素的hash是下一个元素的parentHash - async loadLogEntries(): Promise<{ [key: string]: LogEntry[] }> { - // 通过DevChat获取日志 - const devChat = new DevChat(); - const logOptions: LogOptions = { - skip: 0, - maxCount: 10000 - }; - const logEntries = await devChat.log(logOptions); - const logEntriesFlat = logEntries.flat(); - - const logTopicLinkedList = loadTopicList(logEntriesFlat); - return logTopicLinkedList; - } - async loadTopics(): Promise { - // 删除已经加载的topic - // 重新构建topic信息 this._topics = {}; - const logEntriesMap = await this.loadLogEntries(); - for (const logEntriesList of Object.values(logEntriesMap)) { - // 使用logEntriesList第一个元素更新topic的firstMessageHash和name - // 使用logEntriesList最后一个元素更新topic的lastMessageHash和lastUpdated - if (logEntriesList.length === 0) { - continue; - } - const logEntry = logEntriesList[0]; + + const devChat = new DevChat(); + const logEntries: LogEntry[] = await devChat.topics(); + + // visite logEntries + // for each logEntry + let lastData: number = 0; + for (const logEntry of logEntries) { + lastData += 1; const name = this.createTopicName(logEntry.request, logEntry.response); - const lastLogEntry = logEntriesList[logEntriesList.length - 1]; const topic = new Topic(name, logEntry.hash, logEntry.hash, Number(logEntry.date)); - topic.updateLastMessageHashAndLastUpdated(lastLogEntry.hash, Number(lastLogEntry.date)); + topic.updateLastMessageHashAndLastUpdated(logEntry.hash, lastData); if (topic.firstMessageHash && this.isDeleteTopic(topic.firstMessageHash)) { continue; @@ -272,27 +253,4 @@ export class TopicManager { } this._notifyReloadTopicsListeners(Object.values(this._topics)); } - - - async getTopicHistory(topicId: string): Promise { - /** - * 获取topic历史聊天记录 - */ - // TOPIC对象中firstMessageHash可以作为日志查询的起始点 - // 在DevChat日志中,找出第一个hash为firstMessageHash的日志,然后向下遍历,直到找不到parentHash为当前日志hash的日志 - const topic = this._topics[topicId]; - if (!topic || !topic.firstMessageHash) { - logger.channel()?.info(`Topic ${topicId} not found`); - return []; - } - - const logEntriesMap = await this.loadLogEntries(); - if (!logEntriesMap[topic.firstMessageHash!]) { - logger.channel()?.info(`Topic ${topicId} not found in logEntriesMap`); - return []; - } - - const logEntriesFlat = logEntriesMap[topic.firstMessageHash!]; - return logEntriesFlat; - } } diff --git a/test/topic/loadTopics.test.ts b/test/topic/loadTopics.test.ts deleted file mode 100644 index c363fcc..0000000 --- a/test/topic/loadTopics.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { expect } from 'chai'; -import { describe, it } from 'mocha'; -import { LogEntry } from '../../src/toolwrapper/devchat'; -import { loadTopicList } from '../../src/topic/loadTopics'; - -describe('loadTopicList', () => { - it('should create topic lists from chat logs', () => { - const chatLogs: LogEntry[] = [ - { hash: '1', parent: '', user: 'user1', date: '2022-01-01', request: 'request1', response: 'response1', context: []}, - { hash: '2', parent: '1', user: 'user2', date: '2022-01-02', request: 'request2', response: 'response2', context: []}, - { hash: '3', parent: '2', user: 'user3', date: '2022-01-03', request: 'request3', response: 'response3', context: []}, - { hash: '4', parent: '', user: 'user4', date: '2022-01-04', request: 'request4', response: 'response4', context: []}, - { hash: '5', parent: '4', user: 'user5', date: '2022-01-05', request: 'request5', response: 'response5', context: []}, - ]; - - const expectedTopicLists = { - '1': [ - { hash: '1', parent: '', user: 'user1', date: '2022-01-01', request: 'request1', response: 'response1', context: []}, - { hash: '2', parent: '1', user: 'user2', date: '2022-01-02', request: 'request2', response: 'response2', context: []}, - { hash: '3', parent: '2', user: 'user3', date: '2022-01-03', request: 'request3', response: 'response3', context: []}, - ], - '4': [ - { hash: '4', parent: '', user: 'user4', date: '2022-01-04', request: 'request4', response: 'response4', context: []}, - { hash: '5', parent: '4', user: 'user5', date: '2022-01-05', request: 'request5', response: 'response5', context: []}, - ], - }; - - const topicLists = loadTopicList(chatLogs); - expect(topicLists).to.deep.equal(expectedTopicLists); - }); - - it('should handle empty chat logs', () => { - const chatLogs: LogEntry[] = []; - const expectedTopicLists = {}; - const topicLists = loadTopicList(chatLogs); - expect(topicLists).to.deep.equal(expectedTopicLists); - }); - - it('should handle chat logs with no root entries', () => { - const chatLogs: LogEntry[] = [ - { hash: '1', parent: '0', user: 'user1', date: '2022-01-01', request: 'request1', response: 'response1', context: []}, - { hash: '2', parent: '1', user: 'user2', date: '2022-01-02', request: 'request2', response: 'response2', context: []}, - ]; - - const expectedTopicLists = {}; - const topicLists = loadTopicList(chatLogs); - expect(topicLists).to.deep.equal(expectedTopicLists); - }); -}); \ No newline at end of file