Compare commits

...

1 commit

Author SHA1 Message Date
igardev
5cbbe87c17 - Fix bug for adding files by the agent
- Extract agent service from menu
- Adding agent from menu is now possible (not very user friendly, but working)
2025-10-03 00:47:57 +03:00
9 changed files with 382 additions and 253 deletions

View file

@ -23,6 +23,7 @@ import { HfModelStrategy } from "./services/hf-model-strategy";
import { LocalModelStrategy } from "./services/local-model-strategy";
import { ExternalModelStrategy } from "./services/external-model-strategy";
import { EnvService } from "./services/env-service";
import { AgentService } from "./services/agent-service";
export class Application {
private static instance: Application;
@ -49,6 +50,7 @@ export class Application {
public localModelStrategy: LocalModelStrategy
public externalModelStrategy: ExternalModelStrategy
public envService: EnvService
public agentService: AgentService
private constructor(context: vscode.ExtensionContext) {
this.configuration = new Configuration()
@ -75,6 +77,7 @@ export class Application {
this.externalModelStrategy = new ExternalModelStrategy(this)
this.modelService = new ModelService(this)
this.envService = new EnvService(this)
this.agentService = new AgentService(this)
}
public static getInstance(context: vscode.ExtensionContext): Application {

View file

@ -8,7 +8,7 @@ import {LlamaWebviewProvider} from './llama-webview-provider'
import { Utils } from './utils';
import { Env, LlmModel } from './types';
import { env } from 'process';
import { SETTING_NAME_FOR_LIST } from './constants';
import { PERSISTENCE_KEYS, SETTING_NAME_FOR_LIST } from './constants';
export class Architect {
private app: Application
@ -42,10 +42,10 @@ export class Architect {
}
}
let lastChat = this.app.persistence.getValue("selectedChat")
let lastChat = this.app.persistence.getValue(PERSISTENCE_KEYS.SELECTED_CHAT)
if (lastChat) this.app.menu.selectUpdateChat(lastChat)
let lastAgent = this.app.persistence.getValue("selectedAgent")
if (lastAgent) this.app.menu.selectAgent(lastAgent)
let lastAgent = this.app.persistence.getValue(PERSISTENCE_KEYS.SELECTED_AGENT)
if (lastAgent) this.app.agentService.selectAgent(lastAgent)
this.app.tools.init()
}

View file

@ -60,7 +60,7 @@ export class LlamaAgent {
}
selectChat = (chat: Chat) => {
if (chat && chat.defaultAgent) this.app.menu.selectAgent(chat.defaultAgent);
if (chat && chat.defaultAgent) this.app.agentService.selectAgent(chat.defaultAgent);
this.resetMessages();
if (chat){

View file

@ -94,7 +94,7 @@ export class LlamaWebviewProvider implements vscode.WebviewViewProvider {
await this.app.menu.deselectAndClearModel(ModelType.Tools);
break;
case 'deselectAgent':
await this.app.menu.deselectAgent();
await this.app.agentService.deselectAgent();
break;
case 'showCompletionModel':
this.app.modelService.showModelDetails(this.app.menu.getComplModel());
@ -109,11 +109,11 @@ export class LlamaWebviewProvider implements vscode.WebviewViewProvider {
this.app.modelService.showModelDetails(this.app.menu.getToolsModel());
break;
case 'showAgentDetails':
this.app.menu.showAgentDetails(this.app.menu.getAgent())
this.app.agentService.showAgentDetails(this.app.menu.getAgent())
break;
case 'selectAgent':
let agentsList = this.app.configuration.agents_list
await this.app.menu.selectAgentFromList(agentsList)
await this.app.agentService.pickAndSelectAgent(agentsList)
break;
case 'chatWithAI':
this.app.askAi.closeChatWithAi(false);

View file

@ -5,7 +5,7 @@ import { Utils } from "./utils";
import { Configuration } from "./configuration";
import * as fs from 'fs';
import * as path from 'path';
import { ModelType, LOCAL_MODEL_TEMPLATES, HF_MODEL_TEMPLATES, SETTING_TO_MODEL_TYPE, MODEL_TYPE_CONFIG, AGENT_NAME, UI_TEXT_KEYS, PERSISTENCE_KEYS, PREDEFINED_LISTS_KEYS, SETTING_NAME_FOR_LIST } from "./constants";
import { ModelType, AGENT_NAME, UI_TEXT_KEYS, PERSISTENCE_KEYS, PREDEFINED_LISTS_KEYS } from "./constants";
import { PREDEFINED_LISTS } from "./lists";
export class Menu {
@ -18,6 +18,7 @@ export class Menu {
private selectedEnv: Env = {name: ""}
private selectedAgent: Agent = {name: "", systemInstruction: []}
private selectedChat: Chat = {name:"", id:""}
private readonly startModelDetail = "Selects the model and if local also downloads the model (if not yet done) and starts a llama-server with it.";
constructor(application: Application) {
@ -176,7 +177,10 @@ export class Menu {
this.app.askAi.showChatWithAi(false, context);
break;
case this.app.configuration.getUiText(UI_TEXT_KEYS.chatWithAIAboutLlamaVscode):
this.selectAgent(this.app.configuration.agents_list.find(a => a.name === AGENT_NAME.llamaVscodeHelp));
const helpAgent = this.app.configuration.agents_list.find(a => a.name === AGENT_NAME.llamaVscodeHelp);
if (helpAgent) {
await this.app.agentService.selectAgent(helpAgent);
}
this.showAgentView();
break;
case this.app.configuration.getUiText(UI_TEXT_KEYS.showLlamaAgent) + " (Ctrl+Shif+A)":
@ -214,9 +218,9 @@ export class Menu {
if (envSelected) await this.app.envService.processActions(envSelected);
break;
case this.app.configuration.getUiText(UI_TEXT_KEYS.agents) ?? "":
let agentsActions: vscode.QuickPickItem[] = this.getAgentActions();
let agentsActions: vscode.QuickPickItem[] = this.app.agentService.getActions();
let actionSelected = await vscode.window.showQuickPick(agentsActions);
if (actionSelected) this.processAgentsActions(actionSelected);
if (actionSelected) await this.app.agentService.processActions(actionSelected);
break;
case this.app.configuration.getUiText(UI_TEXT_KEYS.agentCommands) ?? "":
let agentCommandsActions: vscode.QuickPickItem[] = this.getAgentCommandsActions();
@ -331,43 +335,10 @@ export class Menu {
}
}
selectAgentFromList = async (agentsList: Agent[]) => {
let allAgents = agentsList.concat(PREDEFINED_LISTS.get(PREDEFINED_LISTS_KEYS.AGENTS) as Agent[])
let agentsItems: QuickPickItem[] = this.getStandardQpList(agentsList, "");
agentsItems = agentsItems.concat(this.getStandardQpList(PREDEFINED_LISTS.get(PREDEFINED_LISTS_KEYS.AGENTS) as Agent[], "(predefined) ", agentsList.length));
let lastUsedAgent = this.app.persistence.getValue("selectedAgent")
if (lastUsedAgent) agentsItems.push({ label: (agentsItems.length+1) + ". Last used agent", description: lastUsedAgent.name });
const agent = await vscode.window.showQuickPick(agentsItems);
if (agent) {
let futureAgent: Agent;
if (agent.label.includes("Last used agent")){
futureAgent = lastUsedAgent;
} else {
futureAgent = allAgents[parseInt(agent.label.split(". ")[0], 10) - 1]
}
if(!futureAgent){
vscode.window.showWarningMessage("No agent selected. There is no last used agent.");
return;
}
this.selectedAgent = futureAgent;
await this.selectAgent(futureAgent)
this.app.llamaWebviewProvider.updateLlamaView();
// TODO ? when model is added to the agent type - select it
}
}
selectAgent = async (agent: Agent) => {
public setSelectedAgent(agent: Agent): void {
this.selectedAgent = agent;
if(!agent.tools || agent.tools.length == 0) return;
for (let toolFunc of this.app.tools.toolsFunc){
let toolName = toolFunc[0];
this.app.configuration.updateConfigValue("tool_" + toolName + "_enabled", agent.tools.includes(toolName))
}
await this.app.persistence.setValue(PERSISTENCE_KEYS.SELECTED_AGENT, this.selectedAgent);
this.app.llamaWebviewProvider.updateLlamaView();
if (agent.name) vscode.window.showInformationMessage("Agent " + agent.name + " is selected.")
}
private async processModelActions(modelType: ModelType) {
@ -378,8 +349,6 @@ export class Menu {
}
}
// selectStartModel removed, handled by ModelService
public async showAgentView() {
let isModelAvailable = await this.checkForToolsModel();
if (isModelAvailable) {
@ -453,51 +422,9 @@ export class Menu {
private getAgentActions(): vscode.QuickPickItem[] {
return [
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.selectStartAgent) ?? ""
},
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.deselectStopAgent) ?? ""
},
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.addAgent) ?? ""
},
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.viewAgentDetails) ?? ""
},
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.deleteAgent) ?? ""
},
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.exportAgent) ?? ""
},
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.importAgent) ?? ""
},
];
}
private getAgentCommandsActions(): vscode.QuickPickItem[] {
return [
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.addAgentCommand) ?? ""
},
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.viewAgentCommandDetails) ?? ""
},
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.deleteAgentCommand) ?? ""
},
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.exportAgentCommand) ?? ""
},
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.importAgentCommand) ?? ""
},
];
}
private getChatActions(): vscode.QuickPickItem[] {
@ -541,31 +468,11 @@ export class Menu {
);
}
private async viewAgentFromList(agentsList: any[]) {
let allAgents = agentsList.concat(PREDEFINED_LISTS.get(PREDEFINED_LISTS_KEYS.AGENTS) as Agent[])
let agentsItems: QuickPickItem[] = this.getStandardQpList(agentsList, "");
agentsItems = agentsItems.concat(this.getStandardQpList(PREDEFINED_LISTS.get(PREDEFINED_LISTS_KEYS.AGENTS) as Agent[], "(predefined) ", agentsList.length));
let agent = await vscode.window.showQuickPick(agentsItems);
if (agent) {
let agentIndex = parseInt(agent.label.split(". ")[0], 10) - 1;
let selectedAgent = allAgents[agentIndex];
await this.showAgentDetails(selectedAgent);
}
}
public async showAgentDetails(selectedAgent: any) {
await Utils.showOkDialog(
this.getAgentDetailsAsString(selectedAgent)
);
}
private getAgentDetailsAsString(selectedAgent: Agent): string {
return "Agent details: " +
"\nname: " + selectedAgent.name +
"\ndescription: " + selectedAgent.description +
"\nsystem prompt: \n" + selectedAgent.systemInstruction.join("\n") +
"\n\ntools: " + (selectedAgent.tools ? selectedAgent.tools.join(", ") : "");
}
private async viewAgentCommandFromList(agentCommands: any[]) {
let allAgentCommands = agentCommands.concat(PREDEFINED_LISTS.get(PREDEFINED_LISTS_KEYS.AGENT_COMMANDS) as Agent[])
@ -609,16 +516,7 @@ export class Menu {
"\n\ncontext: " + (selectedAgentCommand.context ? selectedAgentCommand.context.join(", ") : "");
}
private async persistAgentToSetting(newAgent: Agent, agentsList: any[], settingName: string) {
let modelDetails = this.getAgentDetailsAsString(newAgent);
const shouldAddModel = await Utils.confirmAction("A new agent will be added. Do you want to add the agent?", modelDetails);
if (shouldAddModel) {
agentsList.push(newAgent);
this.app.configuration.updateConfigValue(settingName, agentsList);
vscode.window.showInformationMessage("The agent is added.");
}
}
private async persistAgentCommandToSetting(newAgentCommand: AgentCommand, agentCommands: any[], settingName: string) {
let modelDetails = this.getAgentCommandDetailsAsString(newAgentCommand);
@ -631,32 +529,7 @@ export class Menu {
}
}
private async importAgentToList(agentList: any[], settingName: string) {
let name = "";
const uris = await vscode.window.showOpenDialog({
canSelectMany: false,
openLabel: 'Import Agent',
filters: {
'Agent Files': ['json'],
'All Files': ['*']
},
});
if (!uris || uris.length === 0) {
return;
}
const filePath = uris[0].fsPath;
const fileContent = fs.readFileSync(filePath, 'utf8');
const newAgent = JSON.parse(fileContent);
// Sanitize imported agent
if (newAgent.name) newAgent.name = this.app.modelService.sanitizeInput(newAgent.name);
if (newAgent.description) newAgent.description = this.app.modelService.sanitizeInput(newAgent.description);
if (newAgent.systemInstruction) newAgent.systemInstruction = newAgent.systemInstruction.map((s: string) => this.app.modelService.sanitizeInput(s));
await this.persistAgentToSetting(newAgent, agentList, settingName);
}
private async importAgentCommandToList(agentCommands: any[], settingName: string) {
let name = "";
@ -750,38 +623,7 @@ export class Menu {
"\napi key required: " + this.selectedToolsModel.isKeyRequired;
}
private async exportAgentFromList(agentsList: any[]) {
let allAgents = agentsList.concat(PREDEFINED_LISTS.get(PREDEFINED_LISTS_KEYS.AGENTS) as Agent[])
let agentsItems: QuickPickItem[] = this.getStandardQpList(agentsList, "");
agentsItems = agentsItems.concat(this.getStandardQpList(PREDEFINED_LISTS.get(PREDEFINED_LISTS_KEYS.AGENTS) as Agent[], "(predefined) ", agentsList.length));
let agent = await vscode.window.showQuickPick(agentsItems);
if (agent) {
let modelIndex = parseInt(agent.label.split(". ")[0], 10) - 1;
let selectedAgent = allAgents[modelIndex];
let shouldExport = await Utils.showYesNoDialog("Do you want to export the following agent? \n\n" +
this.getAgentCommandDetailsAsString(selectedAgent)
);
if (shouldExport){
const uri = await vscode.window.showSaveDialog({
defaultUri: vscode.Uri.file(path.join(vscode.workspace.rootPath || '', selectedAgent.name+'.json')),
filters: {
'Agent Files': ['json'],
'All Files': ['*']
},
saveLabel: 'Export Agent'
});
if (!uri) {
return;
}
const jsonContent = JSON.stringify(selectedAgent, null, 2);
fs.writeFileSync(uri.fsPath, jsonContent, 'utf8');
vscode.window.showInformationMessage("Agent is saved.")
}
}
}
private async exportAgentCommandFromList(agentCommands: any[]) {
let allAgentCommands = agentCommands.concat(PREDEFINED_LISTS.get(PREDEFINED_LISTS_KEYS.AGENT_COMMANDS) as Agent[])
@ -1001,39 +843,26 @@ export class Menu {
isChatSelected = (): boolean => {
return this.selectedChat != undefined && this.selectedChat.name.trim() != "";
}
}
// process*ModelsActions removed, handled by ModelService.processActions
// processEnvActions moved to EnvService.processActions
processAgentsActions = async (selected:vscode.QuickPickItem) => {
switch (selected.label) {
case this.app.configuration.getUiText(UI_TEXT_KEYS.selectStartAgent):
await this.selectAgentFromList(this.app.configuration.agents_list);
break;
case this.app.configuration.getUiText(UI_TEXT_KEYS.addAgent):
// await this.addModelToList(toolsTypeDetails);
Utils.showOkDialog("You could add an agent in setting agents_list or use export, modify and import.")
break;
case this.app.configuration.getUiText(UI_TEXT_KEYS.deleteAgent):
// await this.deleteModelFromList(this.app.configuration.tools_models_list, "tools_models_list");
Utils.showOkDialog("You could delete an agent in setting agents_list")
break;
case this.app.configuration.getUiText(UI_TEXT_KEYS.viewAgentDetails):
await this.viewAgentFromList(this.app.configuration.agents_list)
break;
case this.app.configuration.getUiText(UI_TEXT_KEYS.deselectStopAgent):
// await this.deselectStopModel(this.app.llamaServer.killToolsCmd, "selectedToolsModel");
this.selectedAgent = {name: "", systemInstruction: []};
vscode.window.showInformationMessage("The agent is deselected.")
break;
case this.app.configuration.getUiText(UI_TEXT_KEYS.exportAgent):
await this.exportAgentFromList(this.app.configuration.agents_list)
break;
case this.app.configuration.getUiText(UI_TEXT_KEYS.importAgent):
await this.importAgentToList(this.app.configuration.agents_list, SETTING_NAME_FOR_LIST.AGENTS)
break;
}
private getAgentCommandsActions(): vscode.QuickPickItem[] {
return [
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.addAgentCommand) ?? ""
},
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.viewAgentCommandDetails) ?? ""
},
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.deleteAgentCommand) ?? ""
},
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.exportAgentCommand) ?? ""
},
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.importAgentCommand) ?? ""
},
];
}
processAgentCommandsActions = async (selected:vscode.QuickPickItem) => {
@ -1121,23 +950,5 @@ export class Menu {
public showEnvView() {
vscode.commands.executeCommand('extension.showLlamaWebview');
setTimeout(() => this.app.llamaWebviewProvider.setView("addenv"), 500);
}
public deselectAgent() {
this.selectAgent({
name: "",
systemInstruction: [],
tools: [
"run_terminal_command",
"search_source",
"read_file",
"list_directory",
"regex_search",
"delete_file",
"get_diff",
"edit_file",
"ask_user"
]
});
}
}
}

View file

@ -0,0 +1,313 @@
import * as vscode from "vscode";
import { QuickPickItem } from "vscode";
import { Application } from "../application";
import { Agent } from "../types";
import { Utils } from "../utils";
import * as fs from "fs";
import * as path from "path";
import { Configuration } from "../configuration";
import { PREDEFINED_LISTS } from "../lists";
import { UI_TEXT_KEYS, PERSISTENCE_KEYS, SETTING_NAME_FOR_LIST, PREDEFINED_LISTS_KEYS } from "../constants";
export class AgentService {
private app: Application;
constructor(app: Application) {
this.app = app;
}
getActions(): vscode.QuickPickItem[] {
return [
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.selectStartAgent) ?? ""
},
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.deselectStopAgent) ?? ""
},
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.addAgent) ?? "",
},
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.viewAgentDetails) ?? ""
},
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.deleteAgent) ?? ""
},
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.exportAgent) ?? ""
},
{
label: this.app.configuration.getUiText(UI_TEXT_KEYS.importAgent) ?? ""
},
];
}
async processActions(selected: vscode.QuickPickItem): Promise<void> {
switch (selected.label) {
case this.app.configuration.getUiText(UI_TEXT_KEYS.selectStartAgent):
await this.pickAndSelectAgent(this.app.configuration.agents_list);
break;
case this.app.configuration.getUiText(UI_TEXT_KEYS.addAgent):
await this.addAgent(this.app.configuration.agents_list, SETTING_NAME_FOR_LIST.AGENTS);
break;
case this.app.configuration.getUiText(UI_TEXT_KEYS.deleteAgent):
await this.deleteAgent(this.app.configuration.agents_list, SETTING_NAME_FOR_LIST.AGENTS);
break;
case this.app.configuration.getUiText(UI_TEXT_KEYS.viewAgentDetails):
await this.viewAgent(this.app.configuration.agents_list);
break;
case this.app.configuration.getUiText(UI_TEXT_KEYS.deselectStopAgent):
await this.deselectAgent();
break;
case this.app.configuration.getUiText(UI_TEXT_KEYS.exportAgent):
await this.exportAgent(this.app.configuration.agents_list);
break;
case this.app.configuration.getUiText(UI_TEXT_KEYS.importAgent):
await this.importAgent(this.app.configuration.agents_list, SETTING_NAME_FOR_LIST.AGENTS);
break;
}
}
public async pickAndSelectAgent(agentsList: Agent[]): Promise<Agent | undefined> {
let allAgents = agentsList.concat(PREDEFINED_LISTS.get(PREDEFINED_LISTS_KEYS.AGENTS) as Agent[]);
let agentsItems: QuickPickItem[] = this.getStandardQpList(agentsList, "");
agentsItems = agentsItems.concat(this.getStandardQpList(PREDEFINED_LISTS.get(PREDEFINED_LISTS_KEYS.AGENTS) as Agent[], "(predefined) ", agentsList.length));
let lastUsedAgent = this.app.persistence.getValue(PERSISTENCE_KEYS.SELECTED_AGENT) as Agent;
if (lastUsedAgent && lastUsedAgent.name.trim() !== "") {
agentsItems.push({ label: (agentsItems.length + 1) + ". Last used agent", description: lastUsedAgent.name });
}
const agentItem = await vscode.window.showQuickPick(agentsItems);
if (agentItem) {
let selectedAgent: Agent;
if (agentItem.label.includes("Last used agent")) {
selectedAgent = lastUsedAgent;
} else {
const index = parseInt(agentItem.label.split(". ")[0], 10) - 1;
selectedAgent = allAgents[index];
}
if (selectedAgent) {
await this.selectAgent(selectedAgent);
vscode.window.showInformationMessage(`Agent is selected: ${selectedAgent.name}`);
return selectedAgent;
}
}
return undefined;
}
async selectAgent(agent: Agent): Promise<void> {
this.app.menu.setSelectedAgent(agent);
const allTools = Array.from(this.app.tools.toolsFunc.keys());
for (let toolName of allTools) {
this.app.configuration.updateConfigValue(`tool_${toolName}_enabled`, agent.tools?.includes(toolName) ?? false);
}
await this.app.persistence.setValue(PERSISTENCE_KEYS.SELECTED_AGENT, agent);
this.app.llamaWebviewProvider.updateLlamaView();
if (agent.name.trim() !== "") {
vscode.window.showInformationMessage(`Agent ${agent.name} is selected.`);
}
}
async deselectAgent(): Promise<void> {
const emptyAgent = { name: "", systemInstruction: [] };
this.app.menu.setSelectedAgent(emptyAgent);
const allTools = Array.from(this.app.tools.toolsFunc.keys());
for (let toolName of allTools) {
this.app.configuration.updateConfigValue(`tool_${toolName}_enabled`, true);
}
await this.app.persistence.setValue(PERSISTENCE_KEYS.SELECTED_AGENT, emptyAgent);
this.app.llamaWebviewProvider.updateLlamaView();
vscode.window.showInformationMessage("The agent is deselected.");
}
async addAgent(agentsList: Agent[], settingName: string): Promise<void> {
let name = await Utils.getValidatedInput(
'name for your agent (required)',
(input) => input.trim() !== '',
5,
{
placeHolder: 'Enter a user friendly name for your agent (required)',
value: ''
}
);
if (name === undefined) {
vscode.window.showInformationMessage("Agent addition cancelled.");
return;
}
name = this.app.modelService.sanitizeInput(name);
let description = await vscode.window.showInputBox({
placeHolder: 'description for the agent - what is the purpose, when to select etc. ',
prompt: 'Enter description for the agent.',
value: ''
});
description = this.app.modelService.sanitizeInput(description || '');
// Collect system instruction lines
let systemInstruction: string[] = [];
let line: string | undefined;
do {
line = await vscode.window.showInputBox({
placeHolder: 'Enter a line for system instruction (empty to finish)',
prompt: 'System instruction line',
value: ''
});
if (line && line.trim() !== '') {
systemInstruction.push(this.app.modelService.sanitizeInput(line));
}
} while (line && line.trim() !== '');
if (systemInstruction.length === 0) {
vscode.window.showWarningMessage("No system instruction provided. Agent may not function properly.");
}
// Select tools
const availableTools = Array.from(this.app.tools.toolsFunc.keys()).map(tool => ({
label: tool,
picked: true // default all
}));
const selectedToolsItems = await vscode.window.showQuickPick(availableTools, {
canPickMany: true,
placeHolder: 'Select tools for the agent (Ctrl+click to select multiple)'
});
const tools = selectedToolsItems ? selectedToolsItems.map(item => item.label) : Array.from(this.app.tools.toolsFunc.keys());
let newAgent: Agent = {
name: name,
description: description,
systemInstruction: systemInstruction,
tools: tools
};
await this.persistAgent(newAgent, agentsList, settingName);
}
private async persistAgent(newAgent: Agent, agentsList: Agent[], settingName: string): Promise<void> {
let agentDetails = this.getAgentDetailsAsString(newAgent);
const shouldAddAgent = await Utils.confirmAction("A new agent will be added. Do you want to add the agent?", agentDetails);
if (shouldAddAgent) {
agentsList.push(newAgent);
this.app.configuration.updateConfigValue(settingName, agentsList);
vscode.window.showInformationMessage("The agent is added.");
}
}
async deleteAgent(agentsList: Agent[], settingName: string): Promise<void> {
const agentsItems: QuickPickItem[] = this.getStandardQpList(agentsList, "");
const agentItem = await vscode.window.showQuickPick(agentsItems);
if (agentItem) {
let agentIndex = parseInt(agentItem.label.split(". ")[0], 10) - 1;
const shouldDeleteAgent = await Utils.confirmAction("Are you sure you want to delete the following agent?",
this.getAgentDetailsAsString(agentsList[agentIndex])
);
if (shouldDeleteAgent) {
agentsList.splice(agentIndex, 1);
this.app.configuration.updateConfigValue(settingName, agentsList);
vscode.window.showInformationMessage("The agent is deleted.")
}
}
}
async viewAgent(agentsList: Agent[]): Promise<void> {
let allAgents = agentsList.concat(PREDEFINED_LISTS.get(PREDEFINED_LISTS_KEYS.AGENTS) as Agent[]);
let agentsItems: QuickPickItem[] = this.getStandardQpList(agentsList, "");
agentsItems = agentsItems.concat(this.getStandardQpList(PREDEFINED_LISTS.get(PREDEFINED_LISTS_KEYS.AGENTS) as Agent[], "(predefined) ", agentsList.length));
let agentItem = await vscode.window.showQuickPick(agentsItems);
if (agentItem) {
let agentIndex = parseInt(agentItem.label.split(". ")[0], 10) - 1;
let selectedAgent = allAgents[agentIndex];
await this.showAgentDetails(selectedAgent);
}
}
public async showAgentDetails(selectedAgent: Agent) {
let agentDetails = this.getAgentDetailsAsString(selectedAgent);
await Utils.showOkDialog(agentDetails);
}
async exportAgent(agentsList: Agent[]): Promise<void> {
let allAgents = agentsList.concat(PREDEFINED_LISTS.get(PREDEFINED_LISTS_KEYS.AGENTS) as Agent[]);
let agentsItems: QuickPickItem[] = this.getStandardQpList(agentsList, "");
agentsItems = agentsItems.concat(this.getStandardQpList(PREDEFINED_LISTS.get(PREDEFINED_LISTS_KEYS.AGENTS) as Agent[], "(predefined) ", agentsList.length));
let agentItem = await vscode.window.showQuickPick(agentsItems);
if (agentItem) {
let agentIndex = parseInt(agentItem.label.split(". ")[0], 10) - 1;
let selectedAgent = allAgents[agentIndex];
let shouldExport = await Utils.showYesNoDialog("Do you want to export the following agent? \n\n" +
this.getAgentDetailsAsString(selectedAgent)
);
if (shouldExport) {
const uri = await vscode.window.showSaveDialog({
defaultUri: vscode.Uri.file(path.join(vscode.workspace.rootPath || '', selectedAgent.name + '.json')),
filters: {
'Agent Files': ['json'],
'All Files': ['*']
},
saveLabel: 'Export Agent'
});
if (uri) {
const jsonContent = JSON.stringify(selectedAgent, null, 2);
fs.writeFileSync(uri.fsPath, jsonContent, 'utf8');
vscode.window.showInformationMessage("Agent is saved.")
}
}
}
}
async importAgent(agentsList: Agent[], settingName: string): Promise<void> {
const uris = await vscode.window.showOpenDialog({
canSelectMany: false,
openLabel: 'Import Agent',
filters: {
'Agent Files': ['json'],
'All Files': ['*']
},
});
if (!uris || uris.length === 0) {
return;
}
const filePath = uris[0].fsPath;
const fileContent = fs.readFileSync(filePath, 'utf8');
let newAgent: Agent = JSON.parse(fileContent);
// Sanitize imported agent
this.sanitizeAgent(newAgent);
await this.persistAgent(newAgent, agentsList, settingName);
}
private sanitizeAgent(agent: Agent): void {
if (agent.name) agent.name = this.app.modelService.sanitizeInput(agent.name);
if (agent.description) agent.description = this.app.modelService.sanitizeInput(agent.description);
if (agent.systemInstruction) {
agent.systemInstruction = agent.systemInstruction.map((s: string) => this.app.modelService.sanitizeInput(s));
}
// tools are strings, no need
}
public getAgentDetailsAsString(agent: Agent): string {
return "Agent details: " +
"\nname: " + agent.name +
"\ndescription: " + agent.description +
"\nsystem prompt: \n" + agent.systemInstruction.join("\n") +
"\n\ntools: " + (agent.tools ? agent.tools.join(", ") : "");
}
private getStandardQpList(list: Agent[], prefix: string, lastAgentNumber: number = 0): QuickPickItem[] {
const items: QuickPickItem[] = [];
let i = lastAgentNumber;
for (let elem of list) {
i++;
items.push({
label: i + ". " + prefix + elem.name,
description: elem.description,
});
}
return items;
}
}

View file

@ -183,7 +183,7 @@ export class EnvService {
// Set agent
const agent = env.agent ?? currentAgent;
if (agent) {
await this.app.menu.selectAgent(agent);
await this.app.agentService.selectAgent(agent);
}
// Set configs if specified in env
@ -299,7 +299,7 @@ export class EnvService {
this.app.menu.setSelectedModel(ModelType.Embeddings, { name: "", localStartCommand: "" });
await this.app.llamaServer.killToolsCmd();
this.app.menu.setSelectedModel(ModelType.Tools, { name: "", localStartCommand: "" });
this.app.menu.deselectAgent();
await this.app.agentService.deselectAgent();
this.app.menu.setSelectedEnv({ name: "" });
this.app.llamaWebviewProvider.updateLlamaView();
vscode.window.showInformationMessage("Env, models and agent are deselected.")

View file

@ -227,9 +227,6 @@ export class Tools {
if (params.input == undefined) return "The input is not provided."
let filePath = this.getFilePath(params.input);
if (!filePath) return "The file is not provided.";
if (!fs.existsSync(filePath)) {
return "File not found: " + filePath;
}
try {
if (!this.app.configuration.tool_permit_file_changes){
let [yesApply, yesDontAsk] = await Utils.showYesYesdontaskNoDialog("Do you permit file " + filePath + " to be changed?")

View file

@ -576,21 +576,26 @@ export class Utils {
const workspaceRoot = vscode.workspace.workspaceFolders[0].uri.fsPath;
absolutePath = path.join(workspaceRoot, filePath);
}
// Ensure only \n is used for new line
const fileExists = await fs.promises.access(absolutePath).then(() => true).catch(() => false);
if (!fileExists){
await fs.promises.mkdir(path.dirname(absolutePath), { recursive: true });
try {
const fileExists = await fs.promises.access(absolutePath).then(() => true).catch(() => false);
if (!fileExists){
await fs.promises.mkdir(path.dirname(absolutePath), { recursive: true });
await fs.promises.writeFile(absolutePath, result);
}
// Ensure only \n is used for new line
result = (await fs.promises.readFile(absolutePath, 'utf-8')).split(/\r?\n/).join("\n");
// Handle empty search text case
if (searchText.trim() === '') {
result += '\n' + replaceText;
} else if (result.includes(searchText)) {
result = result.split(searchText).join(replaceText);
}
await fs.promises.writeFile(absolutePath, result);
} catch (error) {
if (error instanceof Error) return "Error edititing file " + filePath + " - " + error.message;
else return "Error edititing file " + filePath + " - " + error;
}
result = (await fs.promises.readFile(absolutePath, 'utf-8')).split(/\r?\n/).join("\n");
// Handle empty search text case
if (searchText.trim() === '') {
result += '\n' + replaceText;
} else if (result.includes(searchText)) {
result = result.split(searchText).join(replaceText);
}
await fs.promises.writeFile(absolutePath, result);
}
}
}