load topic from DevChat
This commit is contained in:
parent
823eed357d
commit
3ec578c7be
@ -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<Array<LogEntry> | undefined> {
|
||||
const topicId = TopicManager.getInstance().currentTopicId;
|
||||
let logEntriesFlat: Array<LogEntry> = [];
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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<LogEntry[]> {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
@ -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<void> {
|
||||
// 删除已经加载的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<LogEntry[]> {
|
||||
/**
|
||||
* 获取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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user