From 13c4453651096e1eb91aa8946403dd9374b43900 Mon Sep 17 00:00:00 2001 From: "bobo.yang" Date: Tue, 9 May 2023 13:24:33 +0800 Subject: [PATCH] Refactor sendMessage and improve DevChat response handling - Import ChatResponse from DevChat in sendMessage.ts - Update sendMessage function to handle parent_hash and partial responses - Refactor DevChat.chat to parse response data and handle partial stdout - Remove unused lastPromptHash variable --- src/handler/sendMessage.ts | 34 +++++------ src/toolwrapper/devchat.ts | 112 ++++++++++++++++++++----------------- 2 files changed, 77 insertions(+), 69 deletions(-) diff --git a/src/handler/sendMessage.ts b/src/handler/sendMessage.ts index e7d5f35..e394b2d 100644 --- a/src/handler/sendMessage.ts +++ b/src/handler/sendMessage.ts @@ -2,7 +2,7 @@ import * as vscode from 'vscode'; import * as fs from 'fs'; import * as path from 'path'; -import DevChat from '../toolwrapper/devchat'; +import DevChat, { ChatResponse } from '../toolwrapper/devchat'; import CommandManager from '../command/commandManager'; import { logger } from '../util/logger'; import { MessageHandler } from './messageHandler'; @@ -70,14 +70,22 @@ function getInstructionFiles(): string[] { } -let lastPromptHash: string | undefined; +// message: { command: 'sendMessage', text: 'xxx', parent_hash: 'xxx'} +// return message: +// { command: 'receiveMessage', text: 'xxxx', hash: 'xxx', user: 'xxx', date: 'xxx'} +// { command: 'receiveMessagePartial', text: 'xxxx', user: 'xxx', date: 'xxx'} export async function sendMessage(message: any, panel: vscode.WebviewPanel): Promise { const devChat = new DevChat(); const newText2 = await CommandManager.getInstance().processText(message.text); const parsedMessage = parseMessage(newText2); - const chatOptions: any = lastPromptHash ? { parent: lastPromptHash } : {}; + const chatOptions: any = {}; + + logger.channel()?.info(`parent_hash: ${message.parent_hash}`) + if (message.parent_hash) { + chatOptions.parent = message.parent_hash; + } if (parsedMessage.context.length > 0) { chatOptions.context = parsedMessage.context; @@ -92,25 +100,13 @@ export async function sendMessage(message: any, panel: vscode.WebviewPanel): Pro chatOptions.reference = parsedMessage.reference; } - let partialData = ""; - const onData = (partialResponse: string) => { - partialData += partialResponse; - MessageHandler.sendMessage(panel, { command: 'receiveMessagePartial', text: partialData }, false); + const onData = (partialResponse: ChatResponse) => { + MessageHandler.sendMessage(panel, { command: 'receiveMessagePartial', text: partialResponse.response, user: partialResponse.user, date: partialResponse.date }, false); }; const chatResponse = await devChat.chat(parsedMessage.text, chatOptions, onData); - if (chatResponse && typeof chatResponse === 'object' && !Array.isArray(chatResponse) && !(chatResponse instanceof String)) { - // 检查 "prompt-hash" 是否在 chatResponse 中 - if ('prompt-hash' in chatResponse) { - // 检查 chatResponse['prompt-hash'] 是否不为空 - if (chatResponse['prompt-hash']) { - // 如果 "prompt-hash" 在 chatResponse 中且不为空,则更新 lastPromptHash 的值 - lastPromptHash = chatResponse['prompt-hash']; - } - } - } - const response = chatResponse.response; - MessageHandler.sendMessage(panel, { command: 'receiveMessage', text: response }); + + MessageHandler.sendMessage(panel, { command: 'receiveMessage', text: chatResponse.response, hash: chatResponse['prompt-hash'], user: chatResponse.user, date: chatResponse.date, isError: chatResponse.isError }); return; } diff --git a/src/toolwrapper/devchat.ts b/src/toolwrapper/devchat.ts index 08e15db..d7268d2 100644 --- a/src/toolwrapper/devchat.ts +++ b/src/toolwrapper/devchat.ts @@ -69,7 +69,7 @@ export interface ChatResponse { class DevChat { - async chat(content: string, options: ChatOptions = {}, onData: (data: string) => void): Promise { + async chat(content: string, options: ChatOptions = {}, onData: (data: ChatResponse) => void): Promise { let args = ["prompt"]; if (options.reference) { @@ -123,6 +123,64 @@ class DevChat { fs.writeFileSync(configPath, configJson); try { + const parseOutData = (stdout: string, isPartial: boolean) => { + const responseLines = stdout.trim().split("\n"); + + if (responseLines.length < 2) { + return { + "prompt-hash": "", + user: "", + date: "", + response: "", + isError: isPartial ? false : true, + }; + } + + const userLine = responseLines.shift()!; + const user = (userLine.match(/User: (.+)/)?.[1]) ?? ""; + + const dateLine = responseLines.shift()!; + const date = (dateLine.match(/Date: (.+)/)?.[1]) ?? ""; + + + let promptHashLine = ""; + for (let i = responseLines.length - 1; i >= 0; i--) { + if (responseLines[i].startsWith("prompt")) { + promptHashLine = responseLines[i]; + responseLines.splice(i, 1); + break; + } + } + + if (!promptHashLine) { + return { + "prompt-hash": "", + user: user, + date: date, + response: responseLines.join("\n"), + isError: isPartial ? false : true, + }; + } + + const promptHash = promptHashLine.split(" ")[1]; + const response = responseLines.join("\n"); + + return { + "prompt-hash": promptHash, + user, + date, + response, + isError: false, + }; + }; + + let receviedStdout = ""; + const onStdoutPartial = (stdout: string) => { + receviedStdout += stdout; + const data = parseOutData(receviedStdout, true); + onData(data); + }; + logger.channel()?.info(`Running devchat with args: ${args.join(" ")}`); const { code, stdout, stderr } = await spawnAsync('devchat', args, { maxBuffer: 10 * 1024 * 1024, // Set maxBuffer to 10 MB @@ -131,7 +189,7 @@ class DevChat { ...process.env, OPENAI_API_KEY: openaiApiKey, }, - }, onData); + }, onStdoutPartial); if (stderr) { const errorMessage = stderr.trim().match(/Error:(.+)/)?.[1]; @@ -144,54 +202,8 @@ class DevChat { }; } - const responseLines = stdout.trim().split("\n"); - - if (responseLines.length === 0) { - return { - "prompt-hash": "", - user: "", - date: "", - response: "", - isError: true, - }; - } - - let promptHashLine = ""; - for (let i = responseLines.length - 1; i >= 0; i--) { - if (responseLines[i].startsWith("prompt")) { - promptHashLine = responseLines[i]; - responseLines.splice(i, 1); - break; - } - } - - if (!promptHashLine) { - return { - "prompt-hash": "", - user: "", - date: "", - response: responseLines.join("\n"), - isError: true, - }; - } - - const promptHash = promptHashLine.split(" ")[1]; - - const userLine = responseLines.shift()!; - const user = (userLine.match(/User: (.+)/)?.[1]) ?? ""; - - const dateLine = responseLines.shift()!; - const date = (dateLine.match(/Date: (.+)/)?.[1]) ?? ""; - - const response = responseLines.join("\n"); - - return { - "prompt-hash": promptHash, - user, - date, - response, - isError: false, - }; + const response = parseOutData(stdout, false); + return response; } catch (error: any) { return { "prompt-hash": "",