diff --git a/core/llm/index.ts b/core/llm/index.ts index 8872281d6e7..88613b165e2 100644 --- a/core/llm/index.ts +++ b/core/llm/index.ts @@ -7,6 +7,8 @@ import { constructLlmApi, } from "@continuedev/openai-adapters"; import Handlebars from "handlebars"; +import * as path from "path"; +import * as vscode from "vscode"; import { DevDataSqliteDb } from "../data/devdataSqlite.js"; import { DataLogger } from "../data/log.js"; @@ -62,6 +64,42 @@ import { toFimBody, } from "./openaiTypeConverters.js"; +const { spawn } = require("child_process"); + +function callPythonFunction( + path: string, + name: string, + ws: string, +): Promise { + return new Promise((resolve, reject) => { + let stdoutData = ""; + let stderrData = ""; + + const pythonProcess = spawn("python", [path, name, ws]); + pythonProcess.stdout.on("data", (data: Buffer) => { + stdoutData += data.toString(); + console.log(`Output: ${data.toString()}`); + }); + + pythonProcess.stderr.on("data", (data: Buffer) => { + stderrData += data.toString(); + console.error(`Error: ${data.toString()}`); + }); + + pythonProcess.on("close", (code: number) => { + if (code !== 0) { + reject( + new Error( + `Python process exited with code ${code}: ${stderrData.trim()}`, + ), + ); + } else { + resolve(stdoutData.trim()); + } + }); + }); +} + export class LLMError extends Error { constructor( message: string, @@ -926,6 +964,7 @@ export abstract class BaseLLM implements ILLM { let thinking = ""; let completion = ""; + let response = ""; try { if (this.templateMessages) { @@ -984,6 +1023,7 @@ export abstract class BaseLLM implements ILLM { )) { if (chunk.role === "assistant") { completion += this._formatChatMessage(chunk); + response += chunk.content; } else if (chunk.role === "thinking") { thinking += chunk.content; } @@ -996,6 +1036,49 @@ export abstract class BaseLLM implements ILLM { } } } + + let script_response = ""; + const rootPath = vscode.workspace.workspaceFolders[0].uri.fsPath; + if (rootPath) { + try { + script_response = await callPythonFunction( + rootPath + "\\script.py" /* TODO: do not hardcode script name */, + response, + vscode.workspace.workspaceFolders[0].uri.fsPath, + ); + } catch (error) {} + + /* analyze the resonse from python script */ + const lines = script_response + .split("\n") + .filter((line) => line.trim() !== ""); + const result: Record = {}; + + lines.forEach((line) => { + const [key, value] = line.split("="); + if (key && value) { + result[key.trim()] = value.trim(); + } + }); + + const startLine = Number(result.STARTLINE); + const endLine = Number(result.ENDLINE); + const filename = result.FILENAME || ""; + const selection = new vscode.Selection( + new vscode.Position(startLine, 0), + new vscode.Position(endLine, 0), + ); + const filePath = path.join(rootPath, filename); + const fileUri = vscode.Uri.file(filePath); + /* TODO: check if it exists before opening */ + const document = await vscode.workspace.openTextDocument(fileUri); + await vscode.window.showTextDocument(document, { + selection: selection, + viewColumn: vscode.ViewColumn.Active, + preserveFocus: false, + }); + } + status = this._logEnd( completionOptions.model, prompt, diff --git a/manual-testing-sandbox/script.py b/manual-testing-sandbox/script.py new file mode 100644 index 00000000000..1fc435500f5 --- /dev/null +++ b/manual-testing-sandbox/script.py @@ -0,0 +1,18 @@ +import os +import sys + +def create_file(filename, content): + # Create a new file with the given filename and content + with open(filename, 'w') as f: + f.write(content) + +if __name__ == "__main__": + filename = "D:/02.Continental/AI/example.txt" + content = "[This is the response]\n" + content += "STARTLINE=2\n" + content += "ENDLINE=10\n" + content += "FILENAME=data.json\n" + content += "\n---\n" + sys.argv[1] + content += "\n---\n" + sys.argv[2] + print(content) + create_file(filename, content) \ No newline at end of file diff --git a/script.py b/script.py new file mode 100644 index 00000000000..d7d70ddfbaf --- /dev/null +++ b/script.py @@ -0,0 +1,18 @@ +import os +import sys + +def create_file(filename, content): + # Create a new file with the given filename and content + with open(filename, 'w') as f: + f.write(content) + +if __name__ == "__main__": + filename = "script.py.txt" + content = "[This is the response]\n" + content += "STARTLINE=2\n" + content += "ENDLINE=10\n" + content += "FILENAME=manual-testing-sandbox\data.json\n" + content += "\n---\n" + sys.argv[1] + content += "\n---\n" + sys.argv[2] + print(content) + create_file(filename, content) diff --git a/tryrun/README b/tryrun/README new file mode 100644 index 00000000000..00ff7fa6fb7 --- /dev/null +++ b/tryrun/README @@ -0,0 +1,14 @@ +使用AI CHAT时,在获取AI RESPONSE后,会默认调用当前打开工程目录下的script.py。 +关于script.py: +- 输入 + arg[0]: 程序名, 如:script.py + arg[1]: AI RESPONSE + arg[2]: 当前工作目录 +- 输出 + 如果script.py根据AI RESPONE修改了一些文件,那么需要按照如下格式说明修改哪个文件的哪些行。 + 即:在标准输出需要输出一下内容,VScode会根据此信息打开FILENAME, 同时选中STARTLINE到ENDLINE。 + STARTLINE=${linenum} + ENDLINE=${linenum} + FILENAME=${filename} + +!!! This is just a temporary solution !!! \ No newline at end of file diff --git a/tryrun/TRYRUN_CSSDC_DEBUG_01.png b/tryrun/TRYRUN_CSSDC_DEBUG_01.png new file mode 100644 index 00000000000..01f482313d5 Binary files /dev/null and b/tryrun/TRYRUN_CSSDC_DEBUG_01.png differ diff --git a/tryrun/TRYRUN_CSSDC_DEBUG_02.png b/tryrun/TRYRUN_CSSDC_DEBUG_02.png new file mode 100644 index 00000000000..40530848532 Binary files /dev/null and b/tryrun/TRYRUN_CSSDC_DEBUG_02.png differ