mirror of
https://github.com/ggml-org/llama.vscode.git
synced 2026-05-07 01:15:23 +00:00
Compare commits
3 commits
master
...
agent_edit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff3dfe6142 | ||
|
|
87cd6bbdfd | ||
|
|
42b500add2 |
20 changed files with 1125 additions and 93 deletions
|
|
@ -124,6 +124,38 @@ You could delete the GGUF files from this folder. If they are missing, but are n
|
|||
|
||||
|
||||
|
||||
## Edit Agent
|
||||
|
||||
### Overview
|
||||
Edit agent view is used for adding and editing agents. From there it is also possible to delete and copy an existing agent as a new one. The identifier of an agent is it's name. For now there is no tools model as part of the agent (the currently selected tools model will be used)
|
||||
|
||||
### How to use it
|
||||
Edit existing agent:
|
||||
1. Click Select button and load an agent to be edited.
|
||||
2. Change the Description and System Instructions fields (if needed)
|
||||
3. Click Add Tools button and select the tools to be used for by the agent.
|
||||
4. Click Save button
|
||||
|
||||
Add new agent:
|
||||
1. Click New button
|
||||
2. Enter Name, Description and System Instructions for the agent
|
||||
3. Click Add Tools button and select the tools to be used for by the agent.
|
||||
4. Click Save button
|
||||
|
||||
Copy existing agent as a new one:
|
||||
1. Click Copy as New button
|
||||
2. Edit Name, Description and System Instructions for the agent
|
||||
3. Click Add Tools button and select the tools to be used for by the agent.
|
||||
4. Click Save button
|
||||
|
||||
Delete agent:
|
||||
1. Click Delete button
|
||||
3. Select an agent to be deleted from the list
|
||||
4. Confirm the deletion of the agent
|
||||
|
||||
|
||||
|
||||
|
||||
## Edit with AI
|
||||
|
||||
### Requred servers
|
||||
|
|
@ -167,12 +199,12 @@ This generate a commit message, based on the current changes.
|
|||
|
||||

|
||||
|
||||
## Version 0.0.27 is released (21.09.2025)
|
||||
## Version 0.0.32 is released (05.10.2025)
|
||||
## What is new
|
||||
- xAI Grog4 free (from OpenRouter) added to the initial models
|
||||
- Chat with AI with project context removed (agent does it better)
|
||||
- Chat with AI about llama-vscode is now with agent, not using webui
|
||||
- Agent - new buttons "Tools Model" and "Agent" - possibility to view the selected model and agent and to change them.
|
||||
- predefined model DeepSeek V3.1 free 163,800 context (OpenRouter) added
|
||||
- predefined model Z.AI: GLM 4.5 Air (free): GLM 4.5 Air - 128.000 context (OpenRouter) added
|
||||
- Added agent "Ask" is for review, analysis and suggestions for the code without changing the files
|
||||
- Some bugs are fixed
|
||||
|
||||
## Setup instructions for llama.cpp server
|
||||
|
||||
|
|
@ -320,7 +352,7 @@ https://github.com/user-attachments/assets/e75e96de-878b-43db-a45b-47cc0c554697
|
|||
### Overview
|
||||
Agent is combination of system prompt and tools. If an agent is selected, it will be used by the Llama Agent UI. On slecting and agent, the selected llama-vscode tools are updated.
|
||||
|
||||
They have properties: name, description, syste prompt, tools.
|
||||
They have properties: name, description, system prompt, tools.
|
||||
|
||||
Agent could be added/deleted/viewed/selected/deselected/exported/imported
|
||||
|
||||
|
|
@ -330,6 +362,12 @@ Select "Agents..." from llama-vscode menu
|
|||
- Add agent...
|
||||
Adds an agent
|
||||
|
||||
- Edit agent...
|
||||
Edits an agent
|
||||
|
||||
- Copy agent...
|
||||
Copies an agent
|
||||
|
||||
- Delete agent...
|
||||
Deletes an agent
|
||||
|
||||
|
|
@ -390,6 +428,9 @@ Deselect the currently selected model. If the model is local, the llama.cpp serv
|
|||
- Add model from huggingface
|
||||
Enter search words to find a model from huggingface. If the model is selected it will be automatically downloaded (if not yet done) and a llama.cpp server will be started with it.
|
||||
|
||||
- Add chat model from OpenAI compatible provider
|
||||
Add chat model from OpenAI compatible provider - OpenRouter or custom (for example local/external llama.cpp server).
|
||||
|
||||
- Export
|
||||
A model could be exported as a .json files. This file could be shared with other users, modified if needed and imported again. Select a model to export it.
|
||||
|
||||
|
|
@ -455,7 +496,7 @@ Select the model you want to delete from the list and delete it.
|
|||
- View
|
||||
Select a model from the list to view all the details for this model
|
||||
|
||||
- Selected
|
||||
- Select
|
||||
Select a model from the list to select it. If the model is a local one (has a command in local start command) a llama.cpp server with this model will be started. Only one completion model could be selected at a time.
|
||||
|
||||
- Deselect
|
||||
|
|
@ -464,6 +505,9 @@ Deselect the currently selected model. If the model is local, the llama.cpp serv
|
|||
- Add model from huggingface
|
||||
Enter search words to find a model from huggingface. If the model is selected it will be automatically downloaded (if not yet done) and a llama.cpp server will be started with it.
|
||||
|
||||
- Add completion model from OpenAI compatible provider
|
||||
Add completion model from OpenAI compatible provider - OpenRouter or custom (for example local/external llama.cpp server).
|
||||
|
||||
- Export
|
||||
A model could be exported as a .json files. This file could be shared with other users, modified if needed and imported again. Select a model to export it.
|
||||
|
||||
|
|
@ -511,6 +555,9 @@ Deselect the currently selected model. If the model is local, the llama.cpp serv
|
|||
- Add model from huggingface
|
||||
Enter search words to find a model from huggingface. If the model is selected it will be automatically downloaded (if not yet done) and a llama.cpp server will be started with it.
|
||||
|
||||
- Add embeddings model from OpenAI compatible provider
|
||||
Add embeddings model from OpenAI compatible provider - OpenRouter or custom (for example local/external llama.cpp server).
|
||||
|
||||
- Export
|
||||
A model could be exported as a .json files. This file could be shared with others used, modified if needed and imported again. Select a model to export it.
|
||||
|
||||
|
|
@ -608,6 +655,9 @@ Deselect the currently selected model. If the model is local, the llama.cpp serv
|
|||
- Add model from huggingface
|
||||
Enter search words to find a model from huggingface. If the model is selected it will be automatically downloaded (if not yet done) and a llama.cpp server will be started with it.
|
||||
|
||||
- Add tools model from OpenAI compatible provider
|
||||
Add tools model from OpenAI compatible provider - OpenRouter or custom (for example local/external llama.cpp server).
|
||||
|
||||
- Export
|
||||
A model could be exported as a .json files. This file could be shared with other users, modified if needed and imported again. Select a model to export it.
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import { ChatService } from "./services/chat-service";
|
|||
import { Agent, Chat, Env, LlmModel } from "./types";
|
||||
import { ModelType, PERSISTENCE_KEYS } from "./constants";
|
||||
import { ApiKeyService } from "./services/api-key-service";
|
||||
import { OpenAiCompModelStrategy } from "./services/openai-comp-model-strategy";
|
||||
|
||||
export class Application {
|
||||
|
||||
|
|
@ -54,6 +55,7 @@ export class Application {
|
|||
public hfModelStrategy: HfModelStrategy
|
||||
public localModelStrategy: LocalModelStrategy
|
||||
public externalModelStrategy: ExternalModelStrategy
|
||||
public openAiCompModelStrategy: OpenAiCompModelStrategy
|
||||
public envService: EnvService
|
||||
public agentService: AgentService
|
||||
public agentCommandService: AgentCommandService
|
||||
|
|
@ -91,6 +93,7 @@ export class Application {
|
|||
this.hfModelStrategy = new HfModelStrategy(this)
|
||||
this.localModelStrategy = new LocalModelStrategy(this)
|
||||
this.externalModelStrategy = new ExternalModelStrategy(this)
|
||||
this.openAiCompModelStrategy = new OpenAiCompModelStrategy(this)
|
||||
this.modelService = new ModelService(this)
|
||||
this.envService = new EnvService(this)
|
||||
this.agentService = new AgentService(this)
|
||||
|
|
|
|||
|
|
@ -454,7 +454,7 @@ export class Architect {
|
|||
if (result.includes("command not found") || result.includes("is not recognized")) {
|
||||
let questionInstall = "llama.cpp will be installed as it is requred by llama-vscode extension.";
|
||||
if (process.platform == 'win32') questionInstall += "\nVS Code will be restarted.";
|
||||
let shouldInstall = await Utils.showUserChoiceDialog(questionInstall, "Confirm");
|
||||
let [shouldInstall, shouldStopAsking] = await Utils.showYesNoNodontAskDialog(questionInstall, "Confirm");
|
||||
if (shouldInstall) {
|
||||
await this.app.menu.installLlamacpp();
|
||||
this.app.persistence.setGlobalValue("last_llama_cpp", (new Date()).toISOString());
|
||||
|
|
@ -464,8 +464,6 @@ export class Architect {
|
|||
}, 2000);
|
||||
}
|
||||
} else {
|
||||
let questionStopAskingLlamaCppInstall = "Do you prefer to stop getting a suggestion to install llama.cpp?"
|
||||
let shouldStopAsking = await Utils.showUserChoiceDialog(questionStopAskingLlamaCppInstall, "Yes");
|
||||
if (shouldStopAsking) this.app.configuration.updateConfigValue("ask_install_llamacpp", false);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -473,7 +471,7 @@ export class Architect {
|
|||
let lastUpgradeDateStr = this.app.persistence.getGlobalValue("last_llama_cpp");
|
||||
if (!lastUpgradeDateStr || Utils.isTimeToUpgrade(new Date(lastUpgradeDateStr), new Date(), this.app.configuration.ask_upgrade_llamacpp_hours)) {
|
||||
let questionInstall = "Do you want to upgrade llama.cpp (used for running local models)? (recommended).";
|
||||
let shouldInstall = await Utils.showUserChoiceDialog(questionInstall, "Confirm"); //yes, don't ask again
|
||||
let [shouldInstall, shouldStopAsking] = await Utils.showYesNoNodontAskDialog(questionInstall, "Confirm");
|
||||
if (shouldInstall) {
|
||||
await this.app.menu.installLlamacpp();
|
||||
this.app.persistence.setGlobalValue("last_llama_cpp", (new Date()).toISOString());
|
||||
|
|
@ -492,8 +490,6 @@ export class Architect {
|
|||
this.app.configuration.updateConfigValue(SETTING_NAME_FOR_LIST.ENVS, envs);
|
||||
}
|
||||
} else {
|
||||
let questionStopAskingLlamaCppUpgrade = "Do you prefer to stop getting a suggestion to upgrade llama.cpp?"
|
||||
let shouldStopAsking = await Utils.showUserChoiceDialog(questionStopAskingLlamaCppUpgrade, "Yes");
|
||||
if (shouldStopAsking){
|
||||
if (!lastUpgradeDateStr) this.app.persistence.setGlobalValue("last_llama_cpp", (new Date()).toISOString());
|
||||
this.app.configuration.updateConfigValue("ask_upgrade_llamacpp_hours", 72000); // more than 8 years
|
||||
|
|
|
|||
|
|
@ -175,6 +175,8 @@ export const UI_TEXT_KEYS = {
|
|||
selectStartAgent: "Select/start agent...",
|
||||
deselectStopAgent: "Deselect/stop agent...",
|
||||
addAgent: "Add agent...",
|
||||
editAgent: "Edit agent...",
|
||||
copyAgent: "Copy agent...",
|
||||
viewAgentDetails: "View agent details...",
|
||||
deleteAgent: "Delete agent...",
|
||||
exportAgent: "Export agent...",
|
||||
|
|
@ -199,6 +201,7 @@ export const UI_TEXT_KEYS = {
|
|||
addLocalCompletionModel: "Add local completion model...",
|
||||
addExternalCompletionModel: "Add external completion model...",
|
||||
addCompletionModelFromHuggingface: "Add completion model from huggingface...",
|
||||
addCompletionOpenAiCompModel: "Add completion model from OpenAI compatible provider...",
|
||||
viewCompletionModelDetails: "View completion model details...",
|
||||
deleteCompletionModel: "Delete completion model...",
|
||||
exportCompletionModel: "Export completion model...",
|
||||
|
|
@ -210,6 +213,7 @@ export const UI_TEXT_KEYS = {
|
|||
addLocalChatModel: "Add local chat model...",
|
||||
addExternalChatModel: "Add external chat model...",
|
||||
addChatModelFromHuggingface: "Add chat model from huggingface...",
|
||||
addChatOpenAiCompModel: "Add chat model from OpenAI compatible provider...",
|
||||
viewChatModelDetails: "View chat model details...",
|
||||
deleteChatModel: "Delete chat model...",
|
||||
exportChatModel: "Export chat model...",
|
||||
|
|
@ -221,6 +225,7 @@ export const UI_TEXT_KEYS = {
|
|||
addLocalEmbeddingsModel: "Add local embeddings model...",
|
||||
addExternalEmbeddingsModel: "Add external embeddings model...",
|
||||
addEmbeddingsModelFromHuggingface: "Add embeddings model from huggingface...",
|
||||
addEmbeddingsOpenAiCompModel: "Add embeddings model from OpenAI compatible provider...",
|
||||
viewEmbeddingsModelDetails: "View embeddings model details...",
|
||||
deleteEmbeddingsModel: "Delete embeddings model...",
|
||||
exportEmbeddingsModel: "Export embeddings model...",
|
||||
|
|
@ -232,6 +237,7 @@ export const UI_TEXT_KEYS = {
|
|||
addLocalToolsModel: "Add local tools model...",
|
||||
addExternalToolsModel: "Add external tools model...",
|
||||
addToolsModelFromHuggingface: "Add tools model from huggingface...",
|
||||
addToolsOpenAiCompModel: "Add tools model from OpenAI compatible provider...",
|
||||
viewToolsModelDetails: "View tools model details...",
|
||||
deleteToolsModel: "Delete tools model...",
|
||||
exportToolsModel: "Export tools model...",
|
||||
|
|
@ -262,4 +268,13 @@ export const PREDEFINED_LISTS_KEYS = {
|
|||
ENVS: SETTING_NAME_FOR_LIST.ENVS,
|
||||
AGENTS: SETTING_NAME_FOR_LIST.AGENTS,
|
||||
AGENT_COMMANDS: SETTING_NAME_FOR_LIST.AGENT_COMMANDS,
|
||||
} as const;
|
||||
} as const;
|
||||
|
||||
export enum OpenAiProvidersKeys {
|
||||
OpenRouter = 'OpenRouter...',
|
||||
Custom = 'Custom...'
|
||||
}
|
||||
export const OPENAI_COMP_PROVIDERS = {
|
||||
[OpenAiProvidersKeys.OpenRouter]: "https://openrouter.ai/api",
|
||||
[OpenAiProvidersKeys.Custom]: ""
|
||||
} as const
|
||||
|
|
@ -629,7 +629,7 @@ export const PREDEFINED_LISTS = new Map<string, any>([
|
|||
},
|
||||
{
|
||||
"name": "default",
|
||||
"description": "This is the default agent.",
|
||||
"description": "Agent for agentic programming - could answer questions, change/add/delete file, execute terminal commands, etc.",
|
||||
"systemInstruction": [
|
||||
"You are an agent for software development - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user.",
|
||||
"Only terminate your turn when you are sure that the problem is solved.",
|
||||
|
|
|
|||
|
|
@ -241,7 +241,7 @@ export class LlamaServer {
|
|||
"temperature": 0.8,
|
||||
"top_p": 0.95,
|
||||
...(model.trim() != "" && { model: model}),
|
||||
"tools": [...this.app.tools.tools, ...this.app.tools.vscodeTools],
|
||||
"tools": [...this.app.tools.getTools(), ...this.app.tools.vscodeTools],
|
||||
"tool_choice": "auto"
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,16 @@ export class LlamaWebviewProvider implements vscode.WebviewViewProvider {
|
|||
case 'configureTools':
|
||||
await this.app.tools.selectTools()
|
||||
break;
|
||||
case 'configureEditTools':
|
||||
const selectedTools = await this.app.agentService.selectTools(message.tools)
|
||||
this.app.agentService.resetEditedAgentTools();
|
||||
selectedTools.map(toolName => this.app.agentService.addEditedAgentTools(toolName,""))
|
||||
let selAgentTools = this.app.agentService.getEditedAgentTools();
|
||||
webviewView.webview.postMessage({
|
||||
command: 'updateAgentTools',
|
||||
files: Array.from(selAgentTools.entries())
|
||||
});
|
||||
break;
|
||||
case 'stopSession':
|
||||
this.app.llamaAgent.stopAgent();
|
||||
break;
|
||||
|
|
@ -84,6 +94,21 @@ export class LlamaWebviewProvider implements vscode.WebviewViewProvider {
|
|||
case 'deselectCompletionModel':
|
||||
await this.app.modelService.deselectAndClearModel(ModelType.Completion);
|
||||
break;
|
||||
case 'moreCompletionModel':
|
||||
await this.app.modelService.processModelActions(ModelType.Completion);
|
||||
break;
|
||||
case 'moreChatModel':
|
||||
await this.app.modelService.processModelActions(ModelType.Chat);
|
||||
break;
|
||||
case 'moreEmbeddingsModel':
|
||||
await this.app.modelService.processModelActions(ModelType.Embeddings);
|
||||
break;
|
||||
case 'moreToolsModel':
|
||||
await this.app.modelService.processModelActions(ModelType.Tools);
|
||||
break;
|
||||
case 'moreAgent':
|
||||
await this.app.agentService.processActions();
|
||||
break;
|
||||
case 'deselectChatModel':
|
||||
await this.app.modelService.deselectAndClearModel(ModelType.Chat);
|
||||
break;
|
||||
|
|
@ -96,6 +121,9 @@ export class LlamaWebviewProvider implements vscode.WebviewViewProvider {
|
|||
case 'deselectAgent':
|
||||
await this.app.agentService.deselectAgent();
|
||||
break;
|
||||
case 'selectEditAgent':
|
||||
await this.app.agentService.editAgent(this.app.configuration.agents_list)
|
||||
break;
|
||||
case 'showCompletionModel':
|
||||
this.app.modelService.showModelDetails(this.app.getComplModel());
|
||||
break;
|
||||
|
|
@ -137,6 +165,9 @@ export class LlamaWebviewProvider implements vscode.WebviewViewProvider {
|
|||
case 'showAgentView':
|
||||
this.showAgentView();
|
||||
break;
|
||||
case 'showAgentEditor':
|
||||
this.showAgentEditor();
|
||||
break;
|
||||
case 'showSelectedModels':
|
||||
await this.app.envService.showCurrentEnv();
|
||||
break;
|
||||
|
|
@ -163,6 +194,13 @@ export class LlamaWebviewProvider implements vscode.WebviewViewProvider {
|
|||
files: agentCommands
|
||||
});
|
||||
break;
|
||||
case 'getAgentTools':
|
||||
let agentTools = this.app.tools.getTools().map(tool => tool.function.name + " | " + tool.function.description)
|
||||
webviewView.webview.postMessage({
|
||||
command: 'updateFileList',
|
||||
files: agentTools
|
||||
});
|
||||
break;
|
||||
case 'addContextProjectFile':
|
||||
let fileNames = message.fileLongName.split("|");
|
||||
this.app.llamaAgent.addContextProjectFile(fileNames[1].trim(),fileNames[0].trim());
|
||||
|
|
@ -180,6 +218,62 @@ export class LlamaWebviewProvider implements vscode.WebviewViewProvider {
|
|||
files: Array.from(updatedContextFiles.entries())
|
||||
});
|
||||
break;
|
||||
case 'addEditedAgentTool':
|
||||
let toolsNames = message.fileLongName.split("|");
|
||||
this.app.agentService.addEditedAgentTools(toolsNames[0].trim(),toolsNames[1].trim());
|
||||
const editedAgentTools = this.app.agentService.getEditedAgentTools();
|
||||
webviewView.webview.postMessage({
|
||||
command: 'updateAgentTools',
|
||||
files: Array.from(editedAgentTools.entries())
|
||||
});
|
||||
break;
|
||||
case 'removeEditedAgentTool':
|
||||
this.app.agentService.removeEditedAgentTools(message.fileLongName);
|
||||
const updatedTools = this.app.agentService.getEditedAgentTools();
|
||||
webviewView.webview.postMessage({
|
||||
command: 'updateAgentTools',
|
||||
files: Array.from(updatedTools.entries())
|
||||
});
|
||||
break;
|
||||
case 'saveEditAgent':
|
||||
if (!message.name) {
|
||||
vscode.window.showErrorMessage("Agent should have a name!")
|
||||
return;
|
||||
}
|
||||
let agentToSave: Agent = {
|
||||
name: message.name,
|
||||
description: message.description,
|
||||
systemInstruction: message.systemInstruction.split(/\r?\n/),
|
||||
tools: message.tools
|
||||
}
|
||||
await this.app.agentService.addUpdateAgent(agentToSave)
|
||||
break;
|
||||
case 'refreshEditedAgentTool':
|
||||
const refreshedTols = this.app.agentService.getEditedAgentTools();
|
||||
webviewView.webview.postMessage({
|
||||
command: 'updateAgentTools',
|
||||
files: Array.from(refreshedTols.entries())
|
||||
});
|
||||
break;
|
||||
case 'editSelectedAgent':
|
||||
const selectedAgent = this.app.getAgent()
|
||||
this.addEditAgent(selectedAgent);
|
||||
break
|
||||
case 'addEditAgent':
|
||||
const newAgent: Agent = {
|
||||
name: message.name,
|
||||
description: message.description,
|
||||
systemInstruction: message.systemInstruction,
|
||||
tools: message.tools
|
||||
}
|
||||
this.addEditAgent(newAgent);
|
||||
break
|
||||
case 'copyAsNewAgent':
|
||||
this.app.agentService.copyAgent()
|
||||
break;
|
||||
case 'deleteAgent':
|
||||
this.app.agentService.deleteAgent()
|
||||
break;
|
||||
case 'openContextFile':
|
||||
const uri = vscode.Uri.file(message.fileLongName);
|
||||
const document = await vscode.workspace.openTextDocument(uri);
|
||||
|
|
@ -223,8 +317,21 @@ export class LlamaWebviewProvider implements vscode.WebviewViewProvider {
|
|||
}, 1000);
|
||||
}
|
||||
|
||||
public addEditAgent(agent: Agent) {
|
||||
this.app.agentService.resetEditedAgentTools();
|
||||
agent.tools?.map(tool => this.app.agentService.addEditedAgentTools(tool, ""));
|
||||
const edAgtools = this.app.agentService.getEditedAgentTools();
|
||||
vscode.commands.executeCommand('llama-vscode.webview.postMessage', {
|
||||
command: 'loadAgent',
|
||||
name: agent?.name,
|
||||
description: agent?.description,
|
||||
systemInstruction: agent?.systemInstruction.join("\n"),
|
||||
tools: Array.from(edAgtools.entries())
|
||||
});
|
||||
}
|
||||
|
||||
private updateSettingInEnvView(key: string, settingValue: any) {
|
||||
vscode.commands.executeCommand('llama-vscode.webview.postMessage', {
|
||||
vscode.commands.executeCommand('llama-vscode.webview.postMessage', {
|
||||
command: 'vscodeSettingValue',
|
||||
key: key,
|
||||
value: settingValue
|
||||
|
|
@ -333,6 +440,11 @@ export class LlamaWebviewProvider implements vscode.WebviewViewProvider {
|
|||
setTimeout(() => this.setView("addenv"), 500);
|
||||
}
|
||||
|
||||
public showAgentEditor() {
|
||||
vscode.commands.executeCommand('extension.showLlamaWebview');
|
||||
setTimeout(() => this.setView("agenteditor"), 400);
|
||||
}
|
||||
|
||||
public updateLlamaView() {
|
||||
this.updateToolsModel();
|
||||
this.updateChatModel();
|
||||
|
|
|
|||
|
|
@ -246,9 +246,7 @@ 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.app.agentService.getActions();
|
||||
let actionSelected = await vscode.window.showQuickPick(agentsActions);
|
||||
if (actionSelected) await this.app.agentService.processActions(actionSelected);
|
||||
await this.app.agentService.processActions();
|
||||
break;
|
||||
case this.app.configuration.getUiText(UI_TEXT_KEYS.agentCommands) ?? "":
|
||||
let agentCommandsActions: vscode.QuickPickItem[] = this.app.agentCommandService.getAgentCommandsActions();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import * as vscode from "vscode";
|
||||
import { QuickPickItem } from "vscode";
|
||||
import { Application } from "../application";
|
||||
|
|
@ -11,6 +10,7 @@ import { UI_TEXT_KEYS, PERSISTENCE_KEYS, SETTING_NAME_FOR_LIST, PREDEFINED_LISTS
|
|||
|
||||
export class AgentService {
|
||||
private app: Application;
|
||||
public editedAgentTools: Map<string,string> = new Map();
|
||||
|
||||
constructor(app: Application) {
|
||||
this.app = app;
|
||||
|
|
@ -27,6 +27,12 @@ export class AgentService {
|
|||
{
|
||||
label: this.app.configuration.getUiText(UI_TEXT_KEYS.addAgent) ?? "",
|
||||
},
|
||||
{
|
||||
label: this.app.configuration.getUiText(UI_TEXT_KEYS.editAgent) ?? "",
|
||||
},
|
||||
{
|
||||
label: this.app.configuration.getUiText(UI_TEXT_KEYS.copyAgent) ?? "",
|
||||
},
|
||||
{
|
||||
label: this.app.configuration.getUiText(UI_TEXT_KEYS.viewAgentDetails) ?? ""
|
||||
},
|
||||
|
|
@ -42,16 +48,25 @@ export class AgentService {
|
|||
];
|
||||
}
|
||||
|
||||
async processActions(selected: vscode.QuickPickItem): Promise<void> {
|
||||
switch (selected.label) {
|
||||
async processActions(): Promise<void> {
|
||||
let agentsActions: vscode.QuickPickItem[] = this.app.agentService.getActions();
|
||||
let actionSelected = await vscode.window.showQuickPick(agentsActions);
|
||||
if (!actionSelected) return
|
||||
switch (actionSelected.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.editAgent):
|
||||
this.editAgent(this.app.configuration.agents_list);
|
||||
break;
|
||||
case this.app.configuration.getUiText(UI_TEXT_KEYS.copyAgent):
|
||||
this.copyAgent();
|
||||
break;
|
||||
case this.app.configuration.getUiText(UI_TEXT_KEYS.deleteAgent):
|
||||
await this.deleteAgent(this.app.configuration.agents_list, SETTING_NAME_FOR_LIST.AGENTS);
|
||||
await this.deleteAgent();
|
||||
break;
|
||||
case this.app.configuration.getUiText(UI_TEXT_KEYS.viewAgentDetails):
|
||||
await this.viewAgent(this.app.configuration.agents_list);
|
||||
|
|
@ -120,70 +135,27 @@ export class AgentService {
|
|||
}
|
||||
|
||||
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);
|
||||
this.app.llamaWebviewProvider.showAgentEditor();
|
||||
this.app.llamaWebviewProvider.addEditAgent({name: "", description: "", systemInstruction: [], tools: []})
|
||||
}
|
||||
|
||||
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
|
||||
selectTools = async (currentTools: string[]): Promise<string[]> => {
|
||||
const availableTools = Array.from(this.app.tools.toolsFunc.keys()).map(tool => ({
|
||||
label: tool,
|
||||
picked: true // default all
|
||||
picked: currentTools.includes(tool)
|
||||
}));
|
||||
const selectedToolsItems = await vscode.window.showQuickPick(availableTools, {
|
||||
canPickMany: true,
|
||||
placeHolder: 'Select tools for the agent (Ctrl+click to select multiple)'
|
||||
placeHolder: 'Select tools for the agent'
|
||||
});
|
||||
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);
|
||||
return tools;
|
||||
}
|
||||
|
||||
private async persistAgent(newAgent: Agent, agentsList: Agent[], settingName: string): Promise<void> {
|
||||
private async persistNewAgent(newAgent: Agent, agentsList: Agent[], settingName: string, confirmMessage: 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);
|
||||
const shouldAddAgent = await Utils.confirmAction(confirmMessage, agentDetails);
|
||||
|
||||
if (shouldAddAgent) {
|
||||
agentsList.push(newAgent);
|
||||
|
|
@ -192,7 +164,40 @@ export class AgentService {
|
|||
}
|
||||
}
|
||||
|
||||
async deleteAgent(agentsList: Agent[], settingName: string): Promise<void> {
|
||||
private async persistEditedAgent(editedAgent: Agent, agentsList: Agent[], settingName: string): Promise<void> {
|
||||
let agentDetails = this.getAgentDetailsAsString(editedAgent);
|
||||
const shouldAddAgent = await Utils.confirmAction("Do you want to update agent?", agentDetails);
|
||||
|
||||
if (shouldAddAgent) {
|
||||
let agentExisting = agentsList.find(agn => agn.name.trim() == editedAgent.name.trim())
|
||||
if (agentExisting){
|
||||
agentExisting.description = editedAgent.description
|
||||
agentExisting.systemInstruction = editedAgent.systemInstruction
|
||||
agentExisting.tools = editedAgent.tools
|
||||
this.app.configuration.updateConfigValue(settingName, agentsList);
|
||||
vscode.window.showInformationMessage("The agent is updated: " + agentExisting.name);
|
||||
} else {
|
||||
vscode.window.showWarningMessage("The agent to update is not found!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addUpdateAgent = async (agentToAddUpdate: Agent) => {
|
||||
let agentsList = this.app.configuration.agents_list;
|
||||
if (agentsList.findIndex(agn => agn.name.trim() == agentToAddUpdate.name.trim()) == -1){
|
||||
await this.persistNewAgent(
|
||||
agentToAddUpdate,
|
||||
agentsList,
|
||||
SETTING_NAME_FOR_LIST.AGENTS,
|
||||
"A new agent will be added. Do you want to add the agent?"
|
||||
);
|
||||
} else {
|
||||
await this.persistEditedAgent(agentToAddUpdate, agentsList, SETTING_NAME_FOR_LIST.AGENTS)
|
||||
}
|
||||
}
|
||||
|
||||
async deleteAgent(): Promise<void> {
|
||||
let agentsList: Agent[] = this.app.configuration.agents_list;
|
||||
const agentsItems: QuickPickItem[] = this.getStandardQpList(agentsList, "");
|
||||
const agentItem = await vscode.window.showQuickPick(agentsItems);
|
||||
if (agentItem) {
|
||||
|
|
@ -202,12 +207,66 @@ export class AgentService {
|
|||
);
|
||||
if (shouldDeleteAgent) {
|
||||
agentsList.splice(agentIndex, 1);
|
||||
this.app.configuration.updateConfigValue(settingName, agentsList);
|
||||
this.app.configuration.updateConfigValue(SETTING_NAME_FOR_LIST.AGENTS, agentsList);
|
||||
vscode.window.showInformationMessage("The agent is deleted.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async editAgent(agentsList: Agent[]): 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;
|
||||
this.app.llamaWebviewProvider.showAgentEditor();
|
||||
setTimeout(() => {
|
||||
this.app.llamaWebviewProvider.addEditAgent(agentsList[agentIndex])
|
||||
}, 500);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
async copyAgent(): Promise<void> {
|
||||
let agentsList = this.app.configuration.agents_list
|
||||
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;
|
||||
this.app.llamaWebviewProvider.showAgentEditor();
|
||||
const selectedAgent = allAgents[agentIndex];
|
||||
const newAgent: Agent = {
|
||||
name: "Copy of " + agentItem.label,
|
||||
description: selectedAgent.description,
|
||||
systemInstruction: selectedAgent.systemInstruction,
|
||||
tools: selectedAgent.tools
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.app.llamaWebviewProvider.addEditAgent(newAgent)
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// let agentsItems: QuickPickItem[] = this.getStandardQpList(agentsList, "");
|
||||
// agentsItems = agentsItems.concat(this.getStandardQpList(PREDEFINED_LISTS.get(PREDEFINED_LISTS_KEYS.AGENTS) as Agent[], "(predefined) ", agentsList.length));
|
||||
// const agentItem = await vscode.window.showQuickPick(agentsItems);
|
||||
// if (agentItem) {
|
||||
// let agentIndex = parseInt(agentItem.label.split(". ")[0], 10) - 1;
|
||||
// this.app.llamaWebviewProvider.showAgentEditor();
|
||||
// const selectedAgent = agentsList[agentIndex];
|
||||
// const newAgent: Agent = {
|
||||
// name: "Copyt of " + selectedAgent.name,
|
||||
// description: selectedAgent.description,
|
||||
// systemInstruction: selectedAgent.systemInstruction,
|
||||
// tools: selectedAgent.tools
|
||||
// }
|
||||
// setTimeout(() => {
|
||||
// this.app.llamaWebviewProvider.addEditAgent(newAgent)
|
||||
// }, 500);
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
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, "");
|
||||
|
|
@ -277,7 +336,7 @@ export class AgentService {
|
|||
// Sanitize imported agent
|
||||
this.sanitizeAgent(newAgent);
|
||||
|
||||
await this.persistAgent(newAgent, agentsList, settingName);
|
||||
await this.persistNewAgent(newAgent, agentsList, settingName,"A new agent will be added. Do you want to add the agent?");
|
||||
}
|
||||
|
||||
private sanitizeAgent(agent: Agent): void {
|
||||
|
|
@ -309,4 +368,21 @@ export class AgentService {
|
|||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
resetEditedAgentTools = () => {
|
||||
this.editedAgentTools.clear();
|
||||
this.app.llamaWebviewProvider.updateContextFilesInfo();
|
||||
}
|
||||
|
||||
addEditedAgentTools = (toolName: string, toolDescription: string) => {
|
||||
this.editedAgentTools.set(toolName, toolDescription);
|
||||
}
|
||||
|
||||
removeEditedAgentTools = (toolName: string) => {
|
||||
this.editedAgentTools.delete(toolName);
|
||||
}
|
||||
|
||||
getEditedAgentTools = () => {
|
||||
return this.editedAgentTools;
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ export class ChatService {
|
|||
const chat = await vscode.window.showQuickPick(chatsItems);
|
||||
if (chat) {
|
||||
let futureChat: Chat;
|
||||
futureChat = chatsList[parseInt(chat.label.split(". ")[0], 10) - 1]
|
||||
futureChat = chatsList[parseInt(chat.label.split(". ")[0], 10) - 1]
|
||||
if(!futureChat){
|
||||
vscode.window.showWarningMessage("No chat selected.");
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ export class ModelService {
|
|||
this.strategies = {
|
||||
local: this.app.localModelStrategy,
|
||||
external: this.app.externalModelStrategy,
|
||||
hf: this.app.hfModelStrategy
|
||||
hf: this.app.hfModelStrategy,
|
||||
oaiComp: this.app.openAiCompModelStrategy
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -31,6 +32,7 @@ export class ModelService {
|
|||
UI_TEXT_KEYS.addLocalCompletionModel,
|
||||
UI_TEXT_KEYS.addExternalCompletionModel,
|
||||
UI_TEXT_KEYS.addCompletionModelFromHuggingface,
|
||||
UI_TEXT_KEYS.addCompletionOpenAiCompModel,
|
||||
UI_TEXT_KEYS.viewCompletionModelDetails,
|
||||
UI_TEXT_KEYS.deleteCompletionModel,
|
||||
UI_TEXT_KEYS.exportCompletionModel,
|
||||
|
|
@ -42,6 +44,7 @@ export class ModelService {
|
|||
UI_TEXT_KEYS.addLocalChatModel,
|
||||
UI_TEXT_KEYS.addExternalChatModel,
|
||||
UI_TEXT_KEYS.addChatModelFromHuggingface,
|
||||
UI_TEXT_KEYS.addChatOpenAiCompModel,
|
||||
UI_TEXT_KEYS.viewChatModelDetails,
|
||||
UI_TEXT_KEYS.deleteChatModel,
|
||||
UI_TEXT_KEYS.exportChatModel,
|
||||
|
|
@ -53,6 +56,7 @@ export class ModelService {
|
|||
UI_TEXT_KEYS.addLocalEmbeddingsModel,
|
||||
UI_TEXT_KEYS.addExternalEmbeddingsModel,
|
||||
UI_TEXT_KEYS.addEmbeddingsModelFromHuggingface,
|
||||
UI_TEXT_KEYS.addEmbeddingsOpenAiCompModel,
|
||||
UI_TEXT_KEYS.viewEmbeddingsModelDetails,
|
||||
UI_TEXT_KEYS.deleteEmbeddingsModel,
|
||||
UI_TEXT_KEYS.exportEmbeddingsModel,
|
||||
|
|
@ -64,6 +68,7 @@ export class ModelService {
|
|||
UI_TEXT_KEYS.addLocalToolsModel,
|
||||
UI_TEXT_KEYS.addExternalToolsModel,
|
||||
UI_TEXT_KEYS.addToolsModelFromHuggingface,
|
||||
UI_TEXT_KEYS.addToolsOpenAiCompModel,
|
||||
UI_TEXT_KEYS.viewToolsModelDetails,
|
||||
UI_TEXT_KEYS.deleteToolsModel,
|
||||
UI_TEXT_KEYS.exportToolsModel,
|
||||
|
|
@ -107,6 +112,9 @@ export class ModelService {
|
|||
case 'addHf':
|
||||
await this.addModel(type, 'hf');
|
||||
break;
|
||||
case 'addOaiComp':
|
||||
await this.addModel(type, 'oaiComp');
|
||||
break;
|
||||
case 'delete':
|
||||
await this.deleteModel(details.modelsList, details.modelsListSettingName);
|
||||
break;
|
||||
|
|
@ -130,6 +138,7 @@ export class ModelService {
|
|||
addLocal: this.app.configuration.getUiText(UI_TEXT_KEYS[`addLocal${typeStr}Model` as keyof typeof UI_TEXT_KEYS]) ?? "",
|
||||
addExternal: this.app.configuration.getUiText(UI_TEXT_KEYS[`addExternal${typeStr}Model` as keyof typeof UI_TEXT_KEYS]) ?? "",
|
||||
addHf: this.app.configuration.getUiText(UI_TEXT_KEYS[`add${typeStr}ModelFromHuggingface` as keyof typeof UI_TEXT_KEYS]) ?? "",
|
||||
addOaiComp: this.app.configuration.getUiText(UI_TEXT_KEYS[`add${typeStr}OpenAiCompModel` as keyof typeof UI_TEXT_KEYS]) ?? "",
|
||||
view: this.app.configuration.getUiText(UI_TEXT_KEYS[`view${typeStr}ModelDetails` as keyof typeof UI_TEXT_KEYS]) ?? "",
|
||||
delete: this.app.configuration.getUiText(UI_TEXT_KEYS[`delete${typeStr}Model` as keyof typeof UI_TEXT_KEYS]) ?? "",
|
||||
export: this.app.configuration.getUiText(UI_TEXT_KEYS[`export${typeStr}Model` as keyof typeof UI_TEXT_KEYS]) ?? "",
|
||||
|
|
@ -142,6 +151,7 @@ export class ModelService {
|
|||
let allModels = modelsList.concat(PREDEFINED_LISTS.get(type) as LlmModel[])
|
||||
let modelsItems: QuickPickItem[] = this.getModels(modelsList, "", true);
|
||||
modelsItems = modelsItems.concat(this.getModels(PREDEFINED_LISTS.get(type) as LlmModel[], "(predefined) ", true, modelsList.length));
|
||||
modelsItems = modelsItems.concat(this.getModels(PREDEFINED_LISTS.get(type) as LlmModel[], "(predefined) ", true, modelsList.length));
|
||||
|
||||
const launchToEndpoint = new Map([
|
||||
["launch_completion", "endpoint"],
|
||||
|
|
@ -188,7 +198,7 @@ export class ModelService {
|
|||
await this.app.persistence.setValue(this.getSelectedProp(type), model);
|
||||
}
|
||||
|
||||
public async addModel(type: ModelType, kind: 'local' | 'external' | 'hf'): Promise<void> {
|
||||
public async addModel(type: ModelType, kind: 'local' | 'external' | 'hf' | 'oaiComp'): Promise<void> {
|
||||
const details = this.getTypeDetails(type);
|
||||
const strategy = this.strategies[kind];
|
||||
if (strategy) {
|
||||
|
|
|
|||
171
src/services/openai-comp-model-strategy.ts
Normal file
171
src/services/openai-comp-model-strategy.ts
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
import * as vscode from "vscode";
|
||||
import { QuickPickItem } from "vscode";
|
||||
import { Application } from "../application";
|
||||
import { IAddStrategy, LlmModel, ModelTypeDetails } from "../types";
|
||||
import { Utils } from "../utils";
|
||||
import * as axios from "axios";
|
||||
import { OPENAI_COMP_PROVIDERS, OpenAiProvidersKeys, SETTING_TO_MODEL_TYPE } from "../constants";
|
||||
|
||||
interface OpenAiCompModel {
|
||||
name: string;
|
||||
id: string;
|
||||
description: string;
|
||||
context_length: string;
|
||||
pricing: {
|
||||
completion: number;
|
||||
prompt: number;
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenAiCompModelStrategy implements IAddStrategy {
|
||||
private app: Application;
|
||||
|
||||
constructor(app: Application) {
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
add = async (details: ModelTypeDetails): Promise<void> => {
|
||||
const modelType = SETTING_TO_MODEL_TYPE[details.modelsListSettingName];
|
||||
const openAiCompProviders: QuickPickItem[] = [];
|
||||
for (const [key, value] of Object.entries(OPENAI_COMP_PROVIDERS)) {
|
||||
openAiCompProviders.push({
|
||||
label: key,
|
||||
description: value
|
||||
});
|
||||
}
|
||||
const selProvider = await vscode.window.showQuickPick(openAiCompProviders);
|
||||
if (selProvider && selProvider.label) {
|
||||
//if custom - ask for endpoint
|
||||
let endpoint = selProvider.description??""
|
||||
let isKeyRequired = true;
|
||||
if (selProvider.label == OpenAiProvidersKeys.Custom){
|
||||
endpoint = await vscode.window.showInputBox({
|
||||
placeHolder: 'endpoint (URL to the API) of an OpenAI compatible provider',
|
||||
prompt: 'example: http://localhost:8080 or https://openrauter.ai/api'
|
||||
})??""
|
||||
isKeyRequired = await Utils.confirmAction(`Is API key required for this endpoint (${endpoint})?`, "");
|
||||
}
|
||||
if (!endpoint){
|
||||
vscode.window.showWarningMessage("Endpoint is not provided!")
|
||||
return;
|
||||
}
|
||||
const providerModels: QuickPickItem[] = [];
|
||||
const models = await this.getModels(endpoint);
|
||||
if (models.length == 0) {
|
||||
vscode.window.showInformationMessage("No models are found.")
|
||||
return
|
||||
}
|
||||
for (let mdl of models) {
|
||||
providerModels.push({
|
||||
label: mdl.name,
|
||||
description: mdl.context_length + " context | $" + (mdl.pricing?.prompt*1000000).toFixed(2) + "/M input tokens | $" + (mdl.pricing?.completion*1000000).toFixed(2) + "/M output tokens",
|
||||
detail: mdl.description + " | "+ mdl.id,
|
||||
});
|
||||
}
|
||||
const selModel = await vscode.window.showQuickPick(providerModels);
|
||||
if (!selModel){
|
||||
vscode.window.showWarningMessage("No model is selected!")
|
||||
return;
|
||||
}
|
||||
let aiModel = selModel.detail
|
||||
if(aiModel) aiModel = aiModel.split("|").slice(-1)[0]?.trim()
|
||||
let provider = selProvider.label
|
||||
if (provider.endsWith("...")) provider = provider.slice(0,-3)
|
||||
let newModel: LlmModel = {
|
||||
name: provider + ": " + selModel.label,
|
||||
localStartCommand: "",
|
||||
endpoint: endpoint,
|
||||
aiModel: aiModel == "undefined" ? "" : aiModel??"",
|
||||
isKeyRequired: isKeyRequired
|
||||
};
|
||||
|
||||
const shouldAddModel = await Utils.confirmAction("You have entered:",
|
||||
this.getModelDetailsAsString(newModel) +
|
||||
"\n\nDo you want to add a model with these properties?"
|
||||
);
|
||||
|
||||
if (shouldAddModel) {
|
||||
let shouldOverwrite = false;
|
||||
[newModel.name, shouldOverwrite] = await this.getUniqueModelName(details.modelsList, newModel);
|
||||
if (!newModel.name) {
|
||||
vscode.window.showInformationMessage("The model was not added as the name was not provided.")
|
||||
return;
|
||||
}
|
||||
if (shouldOverwrite) {
|
||||
const index = details.modelsList.findIndex(model => model.name === newModel.name);
|
||||
if (index !== -1) {
|
||||
details.modelsList.splice(index, 1);
|
||||
}
|
||||
}
|
||||
details.modelsList.push(newModel);
|
||||
this.app.configuration.updateConfigValue(details.modelsListSettingName, details.modelsList);
|
||||
vscode.window.showInformationMessage("The model is added: " + newModel.name)
|
||||
const shouldSelect = await Utils.confirmAction("Do you want to select/start the newly added model?", "");
|
||||
if (shouldSelect) {
|
||||
await this.app.modelService.selectStartModel(newModel, modelType, details);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async getModels(endpoint: string): Promise<OpenAiCompModel[]> {
|
||||
let hfEndpoint = Utils.trimTrailingSlash(endpoint) +"/v1/models";
|
||||
try {
|
||||
let result = await axios.default.get(
|
||||
`${Utils.trimTrailingSlash(hfEndpoint)}`
|
||||
);
|
||||
let models: OpenAiCompModel[] = [];
|
||||
|
||||
let modelsList: OpenAiCompModel[] = []
|
||||
if (result && result.data && result.data.models) modelsList = result.data.models
|
||||
else if (result && result.data && result.data.data) modelsList = result.data.data
|
||||
if (modelsList.length > 0){
|
||||
for(let mdl of modelsList){
|
||||
models.push(mdl)
|
||||
}
|
||||
}
|
||||
|
||||
return models;
|
||||
} catch (error){
|
||||
vscode.window.showErrorMessage("Error getting provider models): " + error)
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
private sanitizeInput(input: string): string {
|
||||
return input ? input.trim() : '';
|
||||
}
|
||||
|
||||
private async getUniqueModelName(modelsList: LlmModel[], newModel: LlmModel): Promise<[string, boolean]> {
|
||||
let uniqueName = newModel.name;
|
||||
let shouldOverwrite = false;
|
||||
let modelSameName = modelsList.find(model => model.name === uniqueName);
|
||||
while (uniqueName && !shouldOverwrite && modelSameName !== undefined) {
|
||||
shouldOverwrite = await Utils.confirmAction("A model with the same name already exists. Do you want to overwrite the existing model?",
|
||||
"Existing model:\n" +
|
||||
this.getModelDetailsAsString(modelSameName) +
|
||||
"\n\nNew model:\n" +
|
||||
this.getModelDetailsAsString(newModel)
|
||||
);
|
||||
if (!shouldOverwrite) {
|
||||
uniqueName = (await vscode.window.showInputBox({
|
||||
placeHolder: 'a unique name for your new model',
|
||||
prompt: 'Enter a unique name for your new model. Leave empty to cancel entering.',
|
||||
value: newModel.name
|
||||
})) ?? "";
|
||||
uniqueName = this.sanitizeInput(uniqueName);
|
||||
if (uniqueName) modelSameName = modelsList.find(model => model.name === uniqueName);
|
||||
}
|
||||
}
|
||||
|
||||
return [uniqueName, shouldOverwrite]
|
||||
}
|
||||
|
||||
private getModelDetailsAsString(model: LlmModel): string {
|
||||
return "name: " + model.name +
|
||||
"\nlocal start command: " + model.localStartCommand +
|
||||
"\nendpoint: " + model.endpoint +
|
||||
"\nmodel name for provider: " + model.aiModel +
|
||||
"\napi key required: " + model.isKeyRequired
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ export class Tools {
|
|||
private app: Application;
|
||||
toolsFunc: ToolsMap = new Map();
|
||||
toolsFuncDesc: ToolsMap = new Map();
|
||||
tools: any[] = [];
|
||||
private tools: any[] = [];
|
||||
vscodeTools: any[] = [];
|
||||
vscodeToolsSelected: Map<string, boolean> = new Map();
|
||||
|
||||
|
|
@ -652,6 +652,10 @@ export class Tools {
|
|||
|
||||
}
|
||||
|
||||
getTools = () => {
|
||||
return this.tools;
|
||||
}
|
||||
|
||||
selectTools = async () => {
|
||||
// Define items with initial selection state
|
||||
const toolItems: vscode.QuickPickItem[] = []
|
||||
|
|
|
|||
|
|
@ -189,4 +189,10 @@ export const translations: string[][] = [
|
|||
["Import", "Импортирай", "Importieren", "Импортировать", "Importar", "导入", "Importer"],
|
||||
["Enter agent command name", "Въведете име на команда на агент", "Agentenbefehlsnamen eingeben", "Введите имя команды агента", "Ingresar nombre de comando de agente", "输入代理命令名称", "Entrer le nom de la commande de l'agent"],
|
||||
["RAG", "RAG", "RAG", "RAG", "RAG", "RAG", "RAG"],
|
||||
["Add completion model from OpenAI compatible provider...", "Добавяне на модел за допълване от съвместим с OpenAI доставчик...", "Ergänzungsmodell von einem mit OpenAI kompatiblen Anbieter hinzufügen...", "Добавить модель завершения от совместимого с OpenAI поставщика...", "Añadir modelo de finalización de un proveedor compatible con OpenAI...", "添加来自与 OpenAI 兼容提供程序的补全模型...", "Ajouter un modèle de complétion d'un fournisseur compatible OpenAI..."],
|
||||
["Add chat model from OpenAI compatible provider...", "Добавяне на чат модел от съвместим с OpenAI доставчик...", "Chat-Modell von einem mit OpenAI kompatiblen Anbieter hinzufügen...", "Добавить чат-модель от совместимого с OpenAI поставщика...", "Añadir modelo de chat de un proveedor compatible con OpenAI...", "添加来自与 OpenAI 兼容提供程序的聊天模型...", "Ajouter un modèle de chat d'un fournisseur compatible OpenAI..."],
|
||||
["Add embeddings model from OpenAI compatible provider...", "Добавяне на модел за ембединг от съвместим с OpenAI доставчик...", "Einbettungsmodell von einem mit OpenAI kompatiblen Anbieter hinzufügen...", "Добавить модель эмбеддингов от совместимого с OpenAI поставщика...", "Añadir modelo de incrustaciones de un proveedor compatible con OpenAI...", "添加来自与 OpenAI 兼容提供程序的嵌入模型...", "Ajouter un modèle d'incorporations d'un fournisseur compatible OpenAI..."],
|
||||
["Add tools model from OpenAI compatible provider...", "Добавяне на модел за инструменти от съвместим с OpenAI доставчик...", "Werkzeugmodell von einem mit OpenAI kompatiblen Anbieter hinzufügen...", "Добавить модель инструментов от совместимого с OpenAI поставщика...", "Añadir modelo de herramientas de un proveedor compatible con OpenAI...", "添加来自与 OpenAI 兼容提供程序的工具模型...", "Ajouter un modèle d'outils d'un fournisseur compatible OpenAI..."],
|
||||
["Edit agent...", "Редактиране на агент...", "Agent bearbeiten...", "Редактировать агента...", "Editar agente...", "编辑代理...", "Modifier l'agent..."],
|
||||
["Copy agent...", "Копиране на агент...", "Agent kopieren...", "Копировать агента...", "Copiar agente...", "复制代理...", "Copier l'agent..."],
|
||||
];
|
||||
|
|
|
|||
18
src/utils.ts
18
src/utils.ts
|
|
@ -321,14 +321,14 @@ export class Utils {
|
|||
return choice === 'Yes';
|
||||
}
|
||||
|
||||
static showUserChoiceDialog = async (message: string, acceptLable: string): Promise<boolean> => {
|
||||
static showUserChoiceDialog = async (message: string, acceptLabel: string): Promise<boolean> => {
|
||||
const choice = await vscode.window.showInformationMessage(
|
||||
"llama-vscode \n\n" + message,
|
||||
{ modal: true }, // Makes the dialog modal (blocks interaction until resolved)
|
||||
acceptLable
|
||||
acceptLabel
|
||||
);
|
||||
|
||||
return choice === acceptLable;
|
||||
return choice === acceptLabel;
|
||||
}
|
||||
|
||||
static showYesYesdontaskNoDialog = async (message: string): Promise<[boolean, boolean]> => {
|
||||
|
|
@ -343,6 +343,18 @@ export class Utils {
|
|||
return [choice === 'Yes' || choice === "Yes, don't ask again", choice === "Yes, don't ask again"];
|
||||
}
|
||||
|
||||
static showYesNoNodontAskDialog = async (message: string, acceptLabel: string): Promise<[boolean, boolean]> => {
|
||||
const choice = await vscode.window.showInformationMessage(
|
||||
"llama-vscode \n\n" + message,
|
||||
{ modal: true }, // Makes the dialog modal (blocks interaction until resolved)
|
||||
acceptLabel,
|
||||
'No',
|
||||
"No, don't ask again"
|
||||
);
|
||||
|
||||
return [choice === acceptLabel, choice === "No, don't ask again"];
|
||||
}
|
||||
|
||||
static showOkDialog = async (message: string) => {
|
||||
const choice = await vscode.window.showInformationMessage(
|
||||
"llama-vscode \n\n" + message,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { AgentView, AIRunnerView, AddEnvView } from './components';
|
||||
import { AgentView, AIRunnerView, AddEnvView, AgentEditor } from './components';
|
||||
import { vscode } from './types/vscode';
|
||||
|
||||
interface AppProps { }
|
||||
|
|
@ -9,6 +9,7 @@ const App: React.FC<AppProps> = () => {
|
|||
const noModelSelected = 'No model selected';
|
||||
const noAgentSelected = 'No agent selected';
|
||||
const noViewSet = 'agent';
|
||||
const viewAgentEditor = 'agenteditor'
|
||||
const viewAiRunner = "airunner"
|
||||
const viewAddEnv = "addenv"
|
||||
|
||||
|
|
@ -45,6 +46,8 @@ const App: React.FC<AppProps> = () => {
|
|||
initialState.view || noViewSet
|
||||
);
|
||||
const [contextFiles, setContextFiles] = useState<Map<string, string>>(new Map());
|
||||
const [agentEditTools, setAgentEditTools] = useState<Map<string, string>>(new Map());
|
||||
const [agentEditDetails, setAgentEditDetails] = useState<{name:string, description:string, systemInstruction:string}>({name:"", description:"", systemInstruction:""});
|
||||
|
||||
// Save state to VS Code whenever it changes
|
||||
useEffect(() => {
|
||||
|
|
@ -91,6 +94,13 @@ const App: React.FC<AppProps> = () => {
|
|||
case 'updateContextFiles':
|
||||
setContextFiles(new Map(message.files || []));
|
||||
break;
|
||||
case 'updateAgentEdit':
|
||||
setAgentEditDetails({name: message.name, description: message.description, systemInstruction: message.systemInstruction});
|
||||
break;
|
||||
case 'loadAgent':
|
||||
setAgentEditDetails({name: message.name, description: message.description, systemInstruction: message.systemInstruction});
|
||||
setAgentEditTools(new Map(message.tools || []));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -124,6 +134,11 @@ const App: React.FC<AppProps> = () => {
|
|||
});
|
||||
};
|
||||
|
||||
const handleShowAgentEditor = () => {
|
||||
vscode.postMessage({
|
||||
command: 'showAgentEditor'
|
||||
});
|
||||
};
|
||||
|
||||
const handleSelectedModels = () => {
|
||||
vscode.postMessage({
|
||||
|
|
@ -162,18 +177,23 @@ const App: React.FC<AppProps> = () => {
|
|||
<button
|
||||
onClick={handleShowAgentView}
|
||||
className="header-btn secondary"
|
||||
title="Show Llama Agent"
|
||||
title="Show Llama Agent View"
|
||||
>
|
||||
{"💬"}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleShowAgentEditor}
|
||||
className="header-btn secondary"
|
||||
title="Show Edit Agent View"
|
||||
>
|
||||
{"✎"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{view === noViewSet && (
|
||||
<div>
|
||||
{/* Environment Selection Header for Agent View */}
|
||||
|
||||
|
||||
<AgentView
|
||||
displayText={displayText}
|
||||
setDisplayText={setDisplayText}
|
||||
|
|
@ -189,6 +209,24 @@ const App: React.FC<AppProps> = () => {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{view === viewAgentEditor && (
|
||||
<div>
|
||||
{/* Environment Selection Header for Agent View */}
|
||||
<AgentEditor
|
||||
inputText={inputText}
|
||||
setInputText={setInputText}
|
||||
currentToolsModel={currentToolsModel}
|
||||
currentAgent={currentAgent}
|
||||
currentState={currentState}
|
||||
setCurrentState={setCurrentState}
|
||||
contextFiles={agentEditTools}
|
||||
setContextFiles={setAgentEditTools}
|
||||
agentEditDetails={agentEditDetails}
|
||||
setAgentEditDetails = {setAgentEditDetails}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{view === viewAiRunner && (
|
||||
<div className="content">
|
||||
<AIRunnerView currentChatModel={currentChatModel} />
|
||||
|
|
|
|||
|
|
@ -96,6 +96,36 @@ const AddEnvView: React.FC<AddEnvViewProps> = ({
|
|||
command: 'deselectCompletionModel'
|
||||
});
|
||||
};
|
||||
|
||||
const handleMoreCompletionModel = () => {
|
||||
vscode.postMessage({
|
||||
command: 'moreCompletionModel'
|
||||
});
|
||||
};
|
||||
|
||||
const handleMoreChatModel = () => {
|
||||
vscode.postMessage({
|
||||
command: 'moreChatModel'
|
||||
});
|
||||
};
|
||||
|
||||
const handleMoreEmbeddingsModel = () => {
|
||||
vscode.postMessage({
|
||||
command: 'moreEmbeddingsModel'
|
||||
});
|
||||
};
|
||||
|
||||
const handleMoreToolsModel = () => {
|
||||
vscode.postMessage({
|
||||
command: 'moreToolsModel'
|
||||
});
|
||||
};
|
||||
|
||||
const handleMoreAgent = () => {
|
||||
vscode.postMessage({
|
||||
command: 'moreAgent'
|
||||
});
|
||||
};
|
||||
|
||||
const handleDeselectChatModel = () => {
|
||||
vscode.postMessage({
|
||||
|
|
@ -145,12 +175,28 @@ const AddEnvView: React.FC<AddEnvViewProps> = ({
|
|||
});
|
||||
};
|
||||
|
||||
const handleShowAgentModel = () => {
|
||||
const handleShowAgent = () => {
|
||||
vscode.postMessage({
|
||||
command: 'showAgentDetails'
|
||||
});
|
||||
};
|
||||
|
||||
const handleEditAgent = () => {
|
||||
vscode.postMessage({
|
||||
command: 'showAgentEditor'
|
||||
});
|
||||
|
||||
vscode.postMessage({
|
||||
command: 'editSelectedAgent',
|
||||
agent: currentAgent,
|
||||
});
|
||||
|
||||
vscode.postMessage({
|
||||
command: 'refreshEditedAgentTool',
|
||||
agent: currentAgent,
|
||||
});
|
||||
};
|
||||
|
||||
const handleSelectAgent = () => {
|
||||
vscode.postMessage({
|
||||
command: 'selectAgent'
|
||||
|
|
@ -189,6 +235,7 @@ const AddEnvView: React.FC<AddEnvViewProps> = ({
|
|||
|
||||
return (
|
||||
<div>
|
||||
<h1 style={{ margin: '0 0 20px 0', fontSize: '24px', fontWeight: 'bold' }}>Environment</h1>
|
||||
<div className="llm-buttons">
|
||||
<div className="single-button-frame">
|
||||
<div className="frame-label">Compl Model</div>
|
||||
|
|
@ -211,6 +258,15 @@ const AddEnvView: React.FC<AddEnvViewProps> = ({
|
|||
</button>
|
||||
)}
|
||||
<span className="model-text">{currentCompletionModel}</span>
|
||||
{currentCompletionModel === noModelSelected && (
|
||||
<button
|
||||
onClick={handleMoreCompletionModel}
|
||||
title={`Add/Delete/View/Export/Import Completion Model`}
|
||||
className="modern-btn secondary"
|
||||
>
|
||||
More
|
||||
</button>
|
||||
)}
|
||||
{currentCompletionModel != noModelSelected && (
|
||||
<button
|
||||
onClick={handleShowCompletionModel}
|
||||
|
|
@ -244,6 +300,15 @@ const AddEnvView: React.FC<AddEnvViewProps> = ({
|
|||
</button>
|
||||
)}
|
||||
<span className="model-text">{currentChatModel}</span>
|
||||
{currentChatModel === noModelSelected && (
|
||||
<button
|
||||
onClick={handleMoreChatModel}
|
||||
title={`Add/Delete/View/Export/Import Chat Model`}
|
||||
className="modern-btn secondary"
|
||||
>
|
||||
More
|
||||
</button>
|
||||
)}
|
||||
{currentChatModel != noModelSelected && (
|
||||
<button
|
||||
onClick={handleShowChatModel}
|
||||
|
|
@ -277,6 +342,15 @@ const AddEnvView: React.FC<AddEnvViewProps> = ({
|
|||
</button>
|
||||
)}
|
||||
<span className="model-text">{currentEmbeddingsModel}</span>
|
||||
{currentEmbeddingsModel === noModelSelected && (
|
||||
<button
|
||||
onClick={handleMoreEmbeddingsModel}
|
||||
title={`Add/Delete/View/Export/Import Embeddings Model`}
|
||||
className="modern-btn secondary"
|
||||
>
|
||||
More
|
||||
</button>
|
||||
)}
|
||||
{currentEmbeddingsModel != noModelSelected && (
|
||||
<button
|
||||
onClick={handleShowEmbsModel}
|
||||
|
|
@ -311,6 +385,15 @@ const AddEnvView: React.FC<AddEnvViewProps> = ({
|
|||
</button>
|
||||
)}
|
||||
<span className="model-text">{currentToolsModel}</span>
|
||||
{currentToolsModel === noModelSelected && (
|
||||
<button
|
||||
onClick={handleMoreToolsModel}
|
||||
title={`Add/Delete/View/Export/Import Tools Model`}
|
||||
className="modern-btn secondary"
|
||||
>
|
||||
More
|
||||
</button>
|
||||
)}
|
||||
{currentToolsModel != noModelSelected && (
|
||||
<button
|
||||
onClick={handleShowToolsModel}
|
||||
|
|
@ -344,15 +427,33 @@ const AddEnvView: React.FC<AddEnvViewProps> = ({
|
|||
</button>
|
||||
)}
|
||||
<span className="model-text">{currentAgent}</span>
|
||||
{currentAgent === noAgentSelected && (
|
||||
<button
|
||||
onClick={handleMoreAgent}
|
||||
title={`Add/Delete/View/Export/Import Agentl`}
|
||||
className="modern-btn secondary"
|
||||
>
|
||||
More
|
||||
</button>
|
||||
)}
|
||||
{currentAgent != noAgentSelected && (
|
||||
<button
|
||||
onClick={handleShowAgentModel}
|
||||
onClick={handleShowAgent}
|
||||
title={`Show Agent Details`}
|
||||
className="modern-btn secondary"
|
||||
>
|
||||
...
|
||||
</button>
|
||||
)}
|
||||
{currentAgent != noAgentSelected && (
|
||||
<button
|
||||
onClick={handleEditAgent}
|
||||
title={`Show Agent Details`}
|
||||
className="modern-btn secondary"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="llm-buttons">
|
||||
|
|
|
|||
440
ui/src/components/AgentEditor.tsx
Normal file
440
ui/src/components/AgentEditor.tsx
Normal file
|
|
@ -0,0 +1,440 @@
|
|||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { vscode } from '../types/vscode';
|
||||
|
||||
interface AgentEditorProps {
|
||||
inputText: string;
|
||||
setInputText: (text: string) => void;
|
||||
currentToolsModel: string;
|
||||
currentAgent: string;
|
||||
currentState: string;
|
||||
setCurrentState: (state: string) => void;
|
||||
contextFiles: Map<string, string>;
|
||||
setContextFiles: (files: Map<string, string>) => void;
|
||||
agentEditDetails: {name: string, description: string, systemInstruction: string}
|
||||
setAgentEditDetails:(agentDetails: {name: string, description: string, systemInstruction: string}) => void
|
||||
}
|
||||
|
||||
const AgentEditor: React.FC<AgentEditorProps> = ({
|
||||
inputText,
|
||||
setInputText,
|
||||
currentToolsModel,
|
||||
currentAgent,
|
||||
currentState,
|
||||
setCurrentState,
|
||||
contextFiles: agentTools,
|
||||
setContextFiles: setTools,
|
||||
agentEditDetails,
|
||||
setAgentEditDetails
|
||||
}) => {
|
||||
const [showFileSelector, setShowFileSelector] = useState<boolean>(false);
|
||||
const [fileList, setFileList] = useState<string[]>([]);
|
||||
const [fileFilter, setFileFilter] = useState<string>('');
|
||||
const [selectedIndex, setSelectedIndex] = useState<number>(0);
|
||||
|
||||
// Create refs
|
||||
const elemSystemPromptRef = useRef<HTMLTextAreaElement>(null);
|
||||
const elemDescriptionRef = useRef<HTMLTextAreaElement>(null);
|
||||
const elemNameRef = useRef<HTMLTextAreaElement>(null);
|
||||
const fileListRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Auto-focus the textarea when the component mounts
|
||||
useEffect(() => {
|
||||
if (elemSystemPromptRef.current) {
|
||||
elemSystemPromptRef.current.focus();
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Filter files based on user input
|
||||
const filteredFiles = fileList.filter(file =>
|
||||
file.toLowerCase().includes(fileFilter.toLowerCase())
|
||||
);
|
||||
|
||||
// Reset selected index when file list or filter changes
|
||||
useEffect(() => {
|
||||
setSelectedIndex(0);
|
||||
}, [fileList, fileFilter]);
|
||||
|
||||
// Auto-scroll to keep selected item visible
|
||||
useEffect(() => {
|
||||
if (showFileSelector && fileListRef.current && filteredFiles.length > 0) {
|
||||
const fileItems = fileListRef.current.querySelectorAll('.file-item');
|
||||
if (fileItems[selectedIndex]) {
|
||||
fileItems[selectedIndex].scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'nearest'
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [selectedIndex, showFileSelector, filteredFiles.length]);
|
||||
|
||||
useEffect(() => {
|
||||
// Listen for messages from the extension
|
||||
const handleMessage = (event: MessageEvent) => {
|
||||
const message = event.data;
|
||||
console.log('Received message from extension:', message);
|
||||
switch (message.command) {
|
||||
case 'focusTextarea':
|
||||
// Focus the textarea when requested by the extension
|
||||
if (elemSystemPromptRef.current) {
|
||||
elemSystemPromptRef.current.focus();
|
||||
}
|
||||
break;
|
||||
case 'updateCurrentState':
|
||||
setCurrentState(message.text || '');
|
||||
break;
|
||||
case 'updateFileList':
|
||||
setFileList(message.files || []);
|
||||
setShowFileSelector(true);
|
||||
break;
|
||||
case 'updateAgentTools':
|
||||
setTools(new Map(message.files || []));
|
||||
break;
|
||||
case 'loadAgent':
|
||||
setAgentEditDetails({name: message.name, description: message.description, systemInstruction: message.systemInstruction});
|
||||
setTools(new Map(message.tools || []));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('message', handleMessage);
|
||||
return () => window.removeEventListener('message', handleMessage);
|
||||
}, [setCurrentState, setTools]);
|
||||
|
||||
// Function to focus the textarea (can be called from extension)
|
||||
const focusTextarea = () => {
|
||||
if (elemSystemPromptRef.current) {
|
||||
elemSystemPromptRef.current.focus();
|
||||
}
|
||||
};
|
||||
|
||||
// Expose the focus function to the extension
|
||||
useEffect(() => {
|
||||
// @ts-ignore - Adding to window for extension access
|
||||
window.focusTextarea = focusTextarea;
|
||||
}, []);
|
||||
|
||||
const handlеSaveAgent = () => {
|
||||
vscode.postMessage({
|
||||
command: 'saveEditAgent',
|
||||
name: agentEditDetails.name,
|
||||
description: agentEditDetails.description,
|
||||
systemInstruction: agentEditDetails.systemInstruction,
|
||||
tools: Array.from(agentTools.keys())
|
||||
});
|
||||
};
|
||||
|
||||
const handleSelectTools = () => {
|
||||
vscode.postMessage({
|
||||
command: "configureEditTools",
|
||||
tools: Array.from(agentTools.keys())
|
||||
});
|
||||
}
|
||||
|
||||
const handleSelectToolsModel = () => {
|
||||
vscode.postMessage({
|
||||
command: 'selectModelWithTools'
|
||||
});
|
||||
};
|
||||
|
||||
const handleSelectEditAgent = () => {
|
||||
vscode.postMessage({
|
||||
command: 'selectEditAgent'
|
||||
});
|
||||
}
|
||||
|
||||
const handleNewAgent = () => {
|
||||
vscode.postMessage({
|
||||
command: 'addEditAgent',
|
||||
name: "",
|
||||
description: "",
|
||||
systemInstruction: [],
|
||||
tools: []
|
||||
});
|
||||
}
|
||||
|
||||
const handleCopyAgent = () => {
|
||||
vscode.postMessage({
|
||||
command: 'copyAsNewAgent'
|
||||
});
|
||||
}
|
||||
|
||||
const handleDeleteAgent = () => {
|
||||
vscode.postMessage({
|
||||
command: 'deleteAgent'
|
||||
});
|
||||
}
|
||||
|
||||
const handleFileSelect = (fileLongName: string) => {
|
||||
// Send the selected file to the extension
|
||||
setShowFileSelector(false);
|
||||
setFileFilter('');
|
||||
vscode.postMessage({
|
||||
command: 'addEditedAgentTool',
|
||||
fileLongName: fileLongName
|
||||
});
|
||||
|
||||
if (elemSystemPromptRef.current) {
|
||||
elemSystemPromptRef.current.focus();
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancelFileSelect = () => {
|
||||
setShowFileSelector(false);
|
||||
setFileFilter('');
|
||||
if (elemSystemPromptRef.current) {
|
||||
elemSystemPromptRef.current.focus();
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveTool = (fileLongName: string) => {
|
||||
vscode.postMessage({
|
||||
command: 'removeEditedAgentTool',
|
||||
fileLongName: fileLongName
|
||||
});
|
||||
};
|
||||
|
||||
const handleOpenContextFile = (fileLongName: string) => {
|
||||
vscode.postMessage({
|
||||
command: 'openContextFile',
|
||||
fileLongName: fileLongName
|
||||
});
|
||||
};
|
||||
|
||||
// Handle keyboard navigation in file selector
|
||||
const handleFileSelectorKeyDown = (e: React.KeyboardEvent) => {
|
||||
if (!showFileSelector || filteredFiles.length === 0) return;
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
setSelectedIndex(prev =>
|
||||
prev < filteredFiles.length - 1 ? prev + 1 : prev
|
||||
);
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
setSelectedIndex(prev => prev > 0 ? prev - 1 : 0);
|
||||
break;
|
||||
case 'Enter':
|
||||
e.preventDefault();
|
||||
if (filteredFiles.length > 0) {
|
||||
handleFileSelect(filteredFiles[selectedIndex]);
|
||||
}
|
||||
break;
|
||||
case 'Escape':
|
||||
e.preventDefault();
|
||||
handleCancelFileSelect();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// Handle keyboard events in the search input
|
||||
const handleSearchKeyDown = (e: React.KeyboardEvent) => {
|
||||
if (!showFileSelector || filteredFiles.length === 0) return;
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
// Focus the file list and set first item as selected
|
||||
if (fileListRef.current) {
|
||||
fileListRef.current.focus();
|
||||
setSelectedIndex(0);
|
||||
}
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
// Focus the file list and set last item as selected
|
||||
if (fileListRef.current) {
|
||||
fileListRef.current.focus();
|
||||
setSelectedIndex(filteredFiles.length - 1);
|
||||
}
|
||||
break;
|
||||
case 'Enter':
|
||||
e.preventDefault();
|
||||
// If there are files, focus the list and select the first one
|
||||
if (filteredFiles.length > 0) {
|
||||
if (fileListRef.current) {
|
||||
fileListRef.current.focus();
|
||||
setSelectedIndex(0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'Escape':
|
||||
e.preventDefault();
|
||||
handleCancelFileSelect();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ height: '100vh', display: 'flex', flexDirection: 'column' }}>
|
||||
{/* Main Content */}
|
||||
<div className="content" style={{ flex: 1, display: 'flex', flexDirection: 'column', minHeight: 0 }}>
|
||||
<div className="input-section" style={{ flexShrink: 0 }}>
|
||||
<div className="input-container">
|
||||
{/* Page Title */}
|
||||
<h1 style={{ margin: '0 0 20px 0', fontSize: '24px', fontWeight: 'bold' }}>Agent Editor</h1>
|
||||
<div style={{ display: 'flex', gap: '10px', marginBottom: '20px' }}>
|
||||
<button
|
||||
onClick={handleSelectEditAgent}
|
||||
title={`Select Agent to Edit`}
|
||||
className="modern-btn secondary"
|
||||
>
|
||||
Select
|
||||
</button>
|
||||
<button
|
||||
onClick={handleNewAgent}
|
||||
title={`New Agent`}
|
||||
className="modern-btn secondary"
|
||||
>
|
||||
New
|
||||
</button>
|
||||
<button
|
||||
onClick={handleCopyAgent}
|
||||
title={`Copy Existing Agent as New`}
|
||||
className="modern-btn secondary"
|
||||
>
|
||||
Copy as New
|
||||
</button>
|
||||
<button
|
||||
onClick={handleDeleteAgent}
|
||||
title={`Select and Delete Existing Agent`}
|
||||
className="modern-btn secondary"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Context Files */}
|
||||
{agentTools.size > 0 && (
|
||||
<div className="context-chips">
|
||||
{Array.from(agentTools.entries()).map(([toolName, toolDescription]) => (
|
||||
<div key={toolName} className="context-chip">
|
||||
<span
|
||||
className="file-name clickable"
|
||||
onClick={() => handleOpenContextFile(toolName)}
|
||||
>
|
||||
{toolName}
|
||||
</span>
|
||||
<button
|
||||
className="remove-btn"
|
||||
onClick={() => handleRemoveTool(toolName)}
|
||||
title={`Remove ${toolName} from context`}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
<span>{'Name (agent identifier)'}</span>
|
||||
{/* Modern Textarea */}
|
||||
<textarea
|
||||
ref={elemNameRef}
|
||||
value={agentEditDetails.name}
|
||||
onChange={(e) => setAgentEditDetails({name: e.target.value, description: agentEditDetails.description, systemInstruction: agentEditDetails.systemInstruction})}
|
||||
placeholder="Enter agent name."
|
||||
className="modern-textarea"
|
||||
rows={1}
|
||||
style={{ height: 'auto', minHeight: '1.5em', resize: 'none' }}
|
||||
/>
|
||||
|
||||
<span>{'Descriptoin'}</span>
|
||||
{/* Modern Textarea */}
|
||||
<textarea
|
||||
ref={elemDescriptionRef}
|
||||
value={agentEditDetails.description}
|
||||
onChange={(e) => setAgentEditDetails({name: agentEditDetails.name, description: e.target.value, systemInstruction: agentEditDetails.systemInstruction})}
|
||||
placeholder="Enter agent description."
|
||||
className="modern-textarea"
|
||||
rows={2}
|
||||
style={{ height: 'auto', minHeight: '3em', resize: 'none' }}
|
||||
/>
|
||||
|
||||
<span>{'System Instruction'}</span>
|
||||
{/* Modern Textarea */}
|
||||
<textarea
|
||||
ref={elemSystemPromptRef}
|
||||
value={agentEditDetails.systemInstruction}
|
||||
onChange={(e) => setAgentEditDetails({name: agentEditDetails.name, description: agentEditDetails.description, systemInstruction: e.target.value})}
|
||||
placeholder="Enter system instructions for the agent."
|
||||
className="modern-textarea"
|
||||
rows={10}
|
||||
style={{ height: 'auto', minHeight: '15em', resize: 'vertical' }}
|
||||
/>
|
||||
|
||||
{/* Input Actions */}
|
||||
<div className="input-actions">
|
||||
<div className="input-buttons">
|
||||
<button
|
||||
onClick={handleSelectTools}
|
||||
className="modern-btn secondary"
|
||||
title="Add file to context"
|
||||
>
|
||||
Add Tools
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
onClick={handlеSaveAgent}
|
||||
className={`modern-btn ${inputText.trim() === '' ? 'secondary' : ''}`}
|
||||
title={"Saves the agent changes or creates a new agent if the name does not exit."}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* File Selection Dialog */}
|
||||
{showFileSelector && (
|
||||
<div className="file-selector-overlay">
|
||||
<div className="file-selector-dialog">
|
||||
<div className="file-selector-header">
|
||||
<h3>Select an item to add to context</h3>
|
||||
<button onClick={handleCancelFileSelect} className="close-btn">×</button>
|
||||
</div>
|
||||
<div className="file-selector-search">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Filter ..."
|
||||
value={fileFilter}
|
||||
onChange={(e) => setFileFilter(e.target.value)}
|
||||
onKeyDown={handleSearchKeyDown}
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
ref={fileListRef}
|
||||
className="file-selector-list"
|
||||
onKeyDown={handleFileSelectorKeyDown}
|
||||
tabIndex={0}
|
||||
>
|
||||
{filteredFiles.length > 0 ? (
|
||||
filteredFiles.map((file, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`file-item ${index === selectedIndex ? 'selected' : ''}`}
|
||||
onClick={() => handleFileSelect(file)}
|
||||
>
|
||||
{file}
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="no-files">
|
||||
{fileFilter ? 'No files match your filter' : 'No files available'}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AgentEditor;
|
||||
|
|
@ -321,7 +321,6 @@ const AgentView: React.FC<AgentViewProps> = ({
|
|||
{/* Modern Header */}
|
||||
<div className="header">
|
||||
<div className="header-content">
|
||||
// TODO Remove this check once you are sure it works fine without it
|
||||
{!currentToolsModel.includes('No model selected...') && (
|
||||
<div className="header-left">
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
export { default as AgentView } from './AgentView';
|
||||
export { default as AIRunnerView } from './AIRunnerView';
|
||||
export { default as AddEnvView } from './AddEnvView';
|
||||
export { default as AgentEditor } from './AgentEditor';
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue