Merge pull request #504 from devchat-ai/new-wf
[dev] Support workflow engine 2.0
This commit is contained in:
commit
94274fae74
2
gui
2
gui
@ -1 +1 @@
|
||||
Subproject commit fc781cece340c22ecf301c612e64efde3313a2dc
|
||||
Subproject commit fc5e916ec69fd0dcc18fe4ea92352ae8dd3d9e67
|
@ -192,7 +192,7 @@ export function registerInstallCommandsCommand(
|
||||
"DevChat.InstallCommands",
|
||||
async () => {
|
||||
const homePath = process.env.HOME || process.env.USERPROFILE || "";
|
||||
const sysDirPath = path.join(homePath, ".chat", "workflows", "sys");
|
||||
const sysDirPath = path.join(homePath, ".chat", "scripts");
|
||||
const pluginDirPath = path.join(
|
||||
UiUtilWrapper.extensionPath(),
|
||||
"workflowsCommands"
|
||||
@ -204,7 +204,7 @@ export function registerInstallCommandsCommand(
|
||||
await copyDirectory(pluginDirPath, sysDirPath);
|
||||
}
|
||||
|
||||
// Check if ~/.chat/workflows/sys directory exists
|
||||
// Check if ~/.chat/scripts directory exists
|
||||
if (!fs.existsSync(sysDirPath)) {
|
||||
// Directory does not exist, wait for updateSysCommand to finish
|
||||
await devchat.updateSysCommand();
|
||||
|
@ -5,38 +5,6 @@ import { ApiKeyManager } from '../util/apiKey';
|
||||
import DevChat from '../toolwrapper/devchat';
|
||||
|
||||
|
||||
export interface Command {
|
||||
name: string;
|
||||
pattern: string;
|
||||
description: string;
|
||||
path: string;
|
||||
args: number;
|
||||
recommend: number;
|
||||
handler: (commandName: string, userInput: string) => Promise<string>;
|
||||
}
|
||||
|
||||
async function getCommandListByDevChatRun(includeHide: boolean = false): Promise<Command[]> {
|
||||
// load commands from CustomCommands
|
||||
let newCommands: Command[] = [];
|
||||
|
||||
const devChat = new DevChat();
|
||||
const commandList = await devChat.commands();
|
||||
commandList.forEach(command => {
|
||||
const commandObj: Command = {
|
||||
name: command.name,
|
||||
pattern: command.name,
|
||||
description: command.description,
|
||||
path: command.path,
|
||||
recommend: command.recommend,
|
||||
args: 0,
|
||||
handler: async (commandName: string, userInput: string) => { return ''; }
|
||||
};
|
||||
newCommands.push(commandObj);
|
||||
});
|
||||
|
||||
return newCommands;
|
||||
}
|
||||
|
||||
let existPannel: vscode.WebviewPanel|vscode.WebviewView|undefined = undefined;
|
||||
|
||||
regInMessage({command: 'regCommandList'});
|
||||
@ -44,17 +12,8 @@ regOutMessage({command: 'regCommandList', result: [{name: '', pattern: '', descr
|
||||
export async function getWorkflowCommandList(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
|
||||
existPannel = panel;
|
||||
|
||||
const commandList = await getCommandListByDevChatRun();
|
||||
const commandCovertedList = commandList.map(command => {
|
||||
if (command.args > 0) {
|
||||
// replace {{prompt}} with {{["",""]}}, count of "" is args
|
||||
const prompt = Array.from({length: command.args}, () => "");
|
||||
command.pattern = command.pattern.replace('{{prompt}}', '{{' + JSON.stringify(prompt) + '}}');
|
||||
}
|
||||
return command;
|
||||
});
|
||||
|
||||
MessageHandler.sendMessage(panel, { command: 'regCommandList', result: commandCovertedList });
|
||||
const commandList = await new DevChat().commands();
|
||||
MessageHandler.sendMessage(panel, { command: 'regCommandList', result: commandList });
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -11,52 +11,24 @@ function copyFileSync(source: string, target: string) {
|
||||
if (!fs.existsSync(target)) {
|
||||
fs.writeFileSync(target, data);
|
||||
}
|
||||
}
|
||||
|
||||
function copyDirSync(source: string, target: string) {
|
||||
}
|
||||
|
||||
function copyDirSync(source: string, target: string) {
|
||||
// 创建目标目录
|
||||
fs.mkdirSync(target, { recursive: true });
|
||||
|
||||
|
||||
// 遍历目录中的所有文件和子目录
|
||||
const files = fs.readdirSync(source);
|
||||
for (const file of files) {
|
||||
const sourcePath = path.join(source, file);
|
||||
const targetPath = path.join(target, file);
|
||||
const stats = fs.statSync(sourcePath);
|
||||
if (stats.isDirectory()) {
|
||||
// 递归拷贝子目录
|
||||
copyDirSync(sourcePath, targetPath);
|
||||
} else {
|
||||
// 拷贝文件
|
||||
copyFileSync(sourcePath, targetPath);
|
||||
}
|
||||
const sourcePath = path.join(source, file);
|
||||
const targetPath = path.join(target, file);
|
||||
const stats = fs.statSync(sourcePath);
|
||||
if (stats.isDirectory()) {
|
||||
// 递归拷贝子目录
|
||||
copyDirSync(sourcePath, targetPath);
|
||||
} else {
|
||||
// 拷贝文件
|
||||
copyFileSync(sourcePath, targetPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createChatDirectoryAndCopyInstructionsSync(extensionUri: vscode.Uri) {
|
||||
const workspaceRoot = UiUtilWrapper.workspaceFoldersFirstPath();
|
||||
if (!workspaceRoot) {
|
||||
return;
|
||||
}
|
||||
|
||||
const chatWorkflowsDirPath = path.join(workspaceRoot, '.chat', 'workflows');
|
||||
const instructionsSrcPath = path.join(extensionUri.fsPath, 'workflows');
|
||||
|
||||
// if workflows directory exists, return
|
||||
if (fs.existsSync(chatWorkflowsDirPath)) {
|
||||
return ;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!fs.existsSync(chatWorkflowsDirPath)) {
|
||||
fs.mkdirSync(chatWorkflowsDirPath, {recursive: true});
|
||||
} else {
|
||||
// return;
|
||||
}
|
||||
|
||||
copyDirSync(instructionsSrcPath, chatWorkflowsDirPath);
|
||||
} catch (error) {
|
||||
logger.channel()?.error('Error occurred while creating the .chat directory and copying workflows:', error);
|
||||
logger.channel()?.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,9 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import WebviewManager from './webviewManager';
|
||||
|
||||
import '../handler/handlerRegister';
|
||||
import handleMessage from '../handler/messageHandler';
|
||||
import { createChatDirectoryAndCopyInstructionsSync } from '../init/chatConfig';
|
||||
import { ExtensionContextHolder } from '../util/extensionContext';
|
||||
import { UiUtilWrapper } from '../util/uiUtil';
|
||||
import { ChatContextManager } from '../context/contextManager';
|
||||
|
||||
|
||||
export class DevChatViewProvider implements vscode.WebviewViewProvider {
|
||||
@ -23,20 +19,7 @@ export class DevChatViewProvider implements vscode.WebviewViewProvider {
|
||||
return this._view;
|
||||
}
|
||||
|
||||
reloadCustomDefines() {
|
||||
const workspaceDir = UiUtilWrapper.workspaceFoldersFirstPath();
|
||||
if (workspaceDir) {
|
||||
const workflowsDir = path.join(workspaceDir!, '.chat', 'workflows');
|
||||
ChatContextManager.getInstance().loadCustomContexts(workflowsDir);
|
||||
}
|
||||
}
|
||||
|
||||
resolveWebviewView(webviewView: vscode.WebviewView, context: vscode.WebviewViewResolveContext, _token: vscode.CancellationToken): void {
|
||||
// 创建 .chat 目录并复制 workflows
|
||||
createChatDirectoryAndCopyInstructionsSync(ExtensionContextHolder.context?.extensionUri!);
|
||||
|
||||
this.reloadCustomDefines();
|
||||
|
||||
this._view = webviewView;
|
||||
|
||||
this._webviewManager = new WebviewManager(webviewView.webview, this._context.extensionUri);
|
||||
@ -46,7 +29,6 @@ export class DevChatViewProvider implements vscode.WebviewViewProvider {
|
||||
|
||||
public reloadWebview(): void {
|
||||
if (this._webviewManager) {
|
||||
this.reloadCustomDefines();
|
||||
this._webviewManager.reloadWebviewContent();
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ export function createStatusBarItem(context: vscode.ExtensionContext): vscode.St
|
||||
progressBar.update(`Checking dependencies: Success`, 0);
|
||||
progressBar.end();
|
||||
|
||||
// download workflows from github or gitlab
|
||||
// install devchat workflow commands
|
||||
if (!hasInstallCommands) {
|
||||
hasInstallCommands = true;
|
||||
await vscode.commands.executeCommand('DevChat.InstallCommands');
|
||||
|
@ -12,6 +12,8 @@ import { getFileContent } from '../util/commonUtil';
|
||||
import * as toml from '@iarna/toml';
|
||||
import { DevChatConfig } from '../util/config';
|
||||
|
||||
import { getMicromambaUrl } from '../util/python_installer/conda_url';
|
||||
|
||||
const readFileAsync = fs.promises.readFile;
|
||||
|
||||
const envPath = path.join(__dirname, '..', '.env');
|
||||
@ -208,7 +210,10 @@ class DevChat {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"PYTHONUTF8":1,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"PYTHONPATH": UiUtilWrapper.extensionPath() + "/tools/site-packages"
|
||||
"PYTHONPATH": UiUtilWrapper.extensionPath() + "/tools/site-packages",
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
"DEVCHAT_PROXY": DevChatConfig.getInstance().get('DEVCHAT_PROXY') || "",
|
||||
"MAMBA_BIN_PATH": getMicromambaUrl(),
|
||||
};
|
||||
|
||||
const pythonApp = DevChatConfig.getInstance().get('python_for_chat') || "python3";
|
||||
@ -260,6 +265,7 @@ class DevChat {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
...llmModelData.api_base? { "OPENAI_API_BASE": llmModelData.api_base, "OPENAI_BASE_URL": llmModelData.api_base } : {},
|
||||
"DEVCHAT_PROXY": DevChatConfig.getInstance().get('DEVCHAT_PROXY') || "",
|
||||
"MAMBA_BIN_PATH": getMicromambaUrl(),
|
||||
};
|
||||
|
||||
// build process options
|
||||
@ -422,44 +428,34 @@ class DevChat {
|
||||
|
||||
async loadRecommendCommands(): Promise<string[]> {
|
||||
try {
|
||||
// 获取用户的主目录
|
||||
const userHomeDir = os.homedir();
|
||||
// 构建配置文件路径
|
||||
const configFilePath = path.join(userHomeDir, '.chat', 'workflows', 'sys', 'configuration.toml');
|
||||
|
||||
// 异步读取配置文件内容
|
||||
const configFileContent = await readFileAsync(configFilePath, { encoding: 'utf8' });
|
||||
const args = ["-m", "devchat", "workflow", "config", "--json"];
|
||||
|
||||
// 解析TOML配置文件内容并返回命令列表
|
||||
return this.parseConfigFile(configFileContent);
|
||||
} catch (err) {
|
||||
console.error('Failed to load recommend commands:', err);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
const {code, stdout, stderr} = await this.runCommand(args);
|
||||
|
||||
// 解析TOML配置文件内容
|
||||
private parseConfigFile(content: string): string[] {
|
||||
interface Config {
|
||||
recommend?: {
|
||||
workflows?: string[];
|
||||
};
|
||||
assertValue(code !== 0, stderr || `Command exited with ${code}`);
|
||||
if (stderr.trim() !== "") {
|
||||
logger.channel()?.warn(`${stderr}`);
|
||||
}
|
||||
|
||||
let workflowConfig;
|
||||
try {
|
||||
workflowConfig = JSON.parse(stdout.trim());
|
||||
} catch (error) {
|
||||
logger.channel()?.error('Failed to parse commands JSON:', error);
|
||||
return [];
|
||||
}
|
||||
|
||||
return workflowConfig.recommend?.workflows || [];
|
||||
} catch (error: any) {
|
||||
logger.channel()?.error(`Error: ${error.message}`);
|
||||
logger.channel()?.show();
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const parsedData = toml.parse(content) as Config;
|
||||
if (parsedData.recommend && parsedData.recommend.workflows) {
|
||||
return parsedData.recommend.workflows;
|
||||
}
|
||||
} catch (err) {
|
||||
logger.channel()?.error(`Error parsing TOML content: ${err}`);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
async commands(): Promise<CommandEntry[]> {
|
||||
async commands(): Promise<any[]> {
|
||||
try {
|
||||
const args = ["-m", "devchat", "run", "--list"];
|
||||
const args = ["-m", "devchat", "workflow", "list", "--json"];
|
||||
|
||||
const {code, stdout, stderr} = await this.runCommand(args);
|
||||
|
||||
@ -475,11 +471,10 @@ class DevChat {
|
||||
logger.channel()?.error('Failed to parse commands JSON:', error);
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
// 确保每个CommandEntry对象的recommend字段默认为-1
|
||||
const recommendCommands = await this.loadRecommendCommands();
|
||||
commands = commands.map((cmd: CommandEntry) => ({
|
||||
commands = commands.map((cmd: any) => ({
|
||||
...cmd,
|
||||
recommend: recommendCommands.indexOf(cmd.name),
|
||||
}));
|
||||
@ -494,7 +489,7 @@ class DevChat {
|
||||
|
||||
async updateSysCommand(): Promise<string> {
|
||||
try {
|
||||
const args = ["-m", "devchat", "run", "--update-sys"];
|
||||
const args = ["-m", "devchat", "workflow", "update"];
|
||||
|
||||
const {code, stdout, stderr} = await this.runCommand(args);
|
||||
|
||||
|
@ -36,11 +36,17 @@ export async function installDevchat(): Promise<string> {
|
||||
const pythonApp = path.join(pythonTargetPath, "python.exe");
|
||||
const pythonPathFile = path.join(pythonTargetPath, "python311._pth");
|
||||
const sitepackagesPath = path.join(UiUtilWrapper.extensionPath(), "tools", "site-packages");
|
||||
|
||||
const userHomeDir = os.homedir();
|
||||
const WORKFLOWS_BASE_NAME = "scripts";
|
||||
const workflow_base_path = path.join(userHomeDir, ".chat", WORKFLOWS_BASE_NAME);
|
||||
|
||||
const new_python_path = [workflow_base_path, sitepackagesPath].join("\n");
|
||||
|
||||
// read content in pythonPathFile
|
||||
let content = fs.readFileSync(pythonPathFile, { encoding: 'utf-8' });
|
||||
// replace %PYTHONPATH% with sitepackagesPath
|
||||
content = content.replace(/%PYTHONPATH%/g, sitepackagesPath);
|
||||
content = content.replace(/%PYTHONPATH%/g, new_python_path);
|
||||
// write content to pythonPathFile
|
||||
fs.writeFileSync(pythonPathFile, content);
|
||||
|
||||
|
2
tools
2
tools
@ -1 +1 @@
|
||||
Subproject commit 1db2a69a5f1cd1576e582d9d7b3ae5a2d88392c2
|
||||
Subproject commit a761c6a462fb0d0e5eb8ec3328dba2015d455fe6
|
@ -1 +1 @@
|
||||
Subproject commit daea79e719527e93a5262a2b8f1d1cb83c47ebe7
|
||||
Subproject commit a9015f2a37a25e324a8e9e41a9c12b75a8b78e37
|
Loading…
x
Reference in New Issue
Block a user