Compare commits

...

1 commit

Author SHA1 Message Date
igardev
f87ab502b0 Adding a default model for agent is not possible 2025-11-03 00:01:09 +02:00
8 changed files with 197 additions and 40 deletions

View file

@ -342,6 +342,38 @@
"description": "The system instructions for this agent",
"default": ""
},
"toolsModel":{
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name for this model to be shown to the user"
},
"endpoint": {
"type": "string",
"description": "The endpoint, from where to access the model",
"default": ""
},
"aiModel": {
"type": "string",
"description": "The name of the AI model as expected by the provider",
"default": ""
},
"isKeyRequired": {
"type": "boolean",
"description": "Is key requried for the endpoint",
"default": false
},
"localStartCommand": {
"type": "string",
"description": "Command to be used for sterting the model locally.",
"default": ""
}
},
"required": [
"name"
]
},
"tools": {
"type": "array",
"items": {

View file

@ -32,7 +32,7 @@ import { ApiKeyService } from "./services/api-key-service";
import { OpenAiCompModelStrategy } from "./services/openai-comp-model-strategy";
export class Application {
public static readonly emptyModel = {name: ""};
private static instance: Application;
public configuration: Configuration;
public extraContext: ExtraContext;
@ -64,10 +64,11 @@ export class Application {
public chatService: ChatService
public apiKeyService: ApiKeyService
private selectedComplModel: LlmModel = ModelService.emptyModel
private selectedModel: LlmModel = ModelService.emptyModel
private selectedEmbeddingsModel: LlmModel = ModelService.emptyModel
private selectedToolsModel: LlmModel = ModelService.emptyModel
private selectedComplModel: LlmModel = Application.emptyModel
private selectedModel: LlmModel = Application.emptyModel
private selectedEmbeddingsModel: LlmModel = Application.emptyModel
private selectedToolsModel: LlmModel = Application.emptyModel
private selectedTmpAgentModel: LlmModel = Application.emptyModel
private selectedEnv: Env = {name: ""}
private selectedAgent: Agent = {name: "", systemInstruction: []}
private selectedChat: Chat = {name: "", id: ""}
@ -110,7 +111,7 @@ export class Application {
Application.instance = new Application(context);
}
return Application.instance;
}
}
getComplModel = (): LlmModel => {
return this.selectedComplModel;
@ -128,6 +129,10 @@ export class Application {
return this.selectedEmbeddingsModel;
}
getTmpAgentModel = (): LlmModel => {
return this.selectedTmpAgentModel;
}
getEnv = (): Env => {
return this.selectedEnv;
}
@ -161,7 +166,11 @@ export class Application {
}
isEmbeddingsModelSelected = (): boolean => {
return this.selectedEmbeddingsModel != undefined && this.selectedToolsModel.name. trim() != "";
return this.selectedEmbeddingsModel != undefined && this.selectedEmbeddingsModel.name. trim() != "";
}
isTmpAgentModelSelected = (): boolean => {
return this.selectedTmpAgentModel != undefined && this.selectedTmpAgentModel.name. trim() != "";
}
isEnvSelected = (): boolean => {
@ -181,21 +190,26 @@ export class Application {
setSelectedModel = (type: ModelType, model: LlmModel | undefined) => {
switch (type) {
case ModelType.Completion:
this.selectedComplModel = model??ModelService.emptyModel;
this.selectedComplModel = model??Application.emptyModel;
break;
case ModelType.Chat:
this.selectedModel = model??ModelService.emptyModel;
this.selectedModel = model??Application.emptyModel;
break;
case ModelType.Embeddings:
this.selectedEmbeddingsModel = model??ModelService.emptyModel;
this.selectedEmbeddingsModel = model??Application.emptyModel;
break;
case ModelType.Tools:
this.selectedToolsModel = model??ModelService.emptyModel;
this.selectedToolsModel = model??Application.emptyModel;
break;
}
this.llamaWebviewProvider.updateLlamaView();
}
setAgentModel = (model: LlmModel | undefined) => {
this.selectedTmpAgentModel = model??Application.emptyModel;
this.llamaWebviewProvider.updateLlamaView();
}
public setSelectedEnv(env: Env): void {
this.selectedEnv = env;
this.persistence.setValue(PERSISTENCE_KEYS.SELECTED_ENV, env);

View file

@ -97,6 +97,9 @@ export class LlamaWebviewProvider implements vscode.WebviewViewProvider {
case 'moreCompletionModel':
await this.app.modelService.processModelActions(ModelType.Completion);
break;
case 'selectAgentModel':
await this.app.modelService.selectAgentModel(ModelType.Tools, this.app.configuration.tools_models_list);
break;
case 'moreChatModel':
await this.app.modelService.processModelActions(ModelType.Chat);
break;
@ -118,6 +121,10 @@ export class LlamaWebviewProvider implements vscode.WebviewViewProvider {
case 'deselectToolsModel':
await this.app.modelService.deselectAndClearModel(ModelType.Tools);
break;
case 'deselectAgentModel':
this.app.setAgentModel(undefined);
this.updateLlamaView();
break;
case 'deselectAgent':
await this.app.agentService.deselectAgent();
break;
@ -240,10 +247,13 @@ export class LlamaWebviewProvider implements vscode.WebviewViewProvider {
vscode.window.showErrorMessage("Agent should have a name!")
return;
}
let agentModelToSave: LlmModel | undefined = undefined
if (message.toolsModel) agentModelToSave = this.app.getTmpAgentModel();
let agentToSave: Agent = {
name: message.name,
description: message.description,
systemInstruction: message.systemInstruction.split(/\r?\n/),
toolsModel: agentModelToSave,
tools: message.tools
}
await this.app.agentService.addUpdateAgent(agentToSave)
@ -260,10 +270,13 @@ export class LlamaWebviewProvider implements vscode.WebviewViewProvider {
this.addEditAgent(selectedAgent);
break
case 'addEditAgent':
let toolsModel = Application.emptyModel
if (message.toolsModel) toolsModel = this.app.getTmpAgentModel();
const newAgent: Agent = {
name: message.name,
description: message.description,
systemInstruction: message.systemInstruction,
toolsModel: toolsModel,
tools: message.tools
}
this.addEditAgent(newAgent);
@ -321,11 +334,13 @@ export class LlamaWebviewProvider implements vscode.WebviewViewProvider {
this.app.agentService.resetEditedAgentTools();
agent.tools?.map(tool => this.app.agentService.addEditedAgentTools(tool, ""));
const edAgtools = this.app.agentService.getEditedAgentTools();
this.app.setAgentModel(agent.toolsModel);
vscode.commands.executeCommand('llama-vscode.webview.postMessage', {
command: 'loadAgent',
name: agent?.name,
description: agent?.description,
systemInstruction: agent?.systemInstruction.join("\n"),
toolsModel: agent?.toolsModel?.name??'',
tools: Array.from(edAgtools.entries())
});
}
@ -368,6 +383,14 @@ export class LlamaWebviewProvider implements vscode.WebviewViewProvider {
});
}
private updateTmpAgentModel() {
const currentTmpAgentModel: LlmModel = this.app.getTmpAgentModel();
vscode.commands.executeCommand('llama-vscode.webview.postMessage', {
command: 'updateTmpAgentModel',
model: currentTmpAgentModel.name || ''
});
}
private updateComplsModel() {
const currentToolsModel: LlmModel = this.app.getComplModel();
vscode.commands.executeCommand('llama-vscode.webview.postMessage', {
@ -450,6 +473,7 @@ export class LlamaWebviewProvider implements vscode.WebviewViewProvider {
this.updateChatModel();
this.updateEmbsModel();
this.updateComplsModel();
this.updateTmpAgentModel();
this.updateAgent();
this.updateEnv();
this.updateSettingsInView();

View file

@ -6,7 +6,7 @@ import { Utils } from "../utils";
import * as fs from "fs";
import * as path from "path";
import { PREDEFINED_LISTS } from "../lists";
import { UI_TEXT_KEYS, PERSISTENCE_KEYS, SETTING_NAME_FOR_LIST, PREDEFINED_LISTS_KEYS } from "../constants";
import { UI_TEXT_KEYS, PERSISTENCE_KEYS, SETTING_NAME_FOR_LIST, PREDEFINED_LISTS_KEYS, ModelType } from "../constants";
export class AgentService {
private app: Application;
@ -115,6 +115,9 @@ export class AgentService {
for (let toolName of allTools) {
this.app.configuration.updateConfigValue(`tool_${toolName}_enabled`, agent.tools?.includes(toolName) ?? false);
}
if (agent.toolsModel && agent.toolsModel.name) {
this.app.modelService.selectStartModel(agent.toolsModel, ModelType.Tools, this.app.modelService.getTypeDetails(ModelType.Tools))
}
await this.app.persistence.setValue(PERSISTENCE_KEYS.SELECTED_AGENT, agent);
this.app.llamaWebviewProvider.updateLlamaView();
if (agent.name.trim() !== "") {
@ -173,6 +176,7 @@ export class AgentService {
if (agentExisting){
agentExisting.description = editedAgent.description
agentExisting.systemInstruction = editedAgent.systemInstruction
agentExisting.toolsModel = editedAgent.toolsModel
agentExisting.tools = editedAgent.tools
this.app.configuration.updateConfigValue(settingName, agentsList);
vscode.window.showInformationMessage("The agent is updated: " + agentExisting.name);
@ -353,6 +357,7 @@ export class AgentService {
"\nname: " + agent.name +
"\ndescription: " + agent.description +
"\nsystem prompt: \n" + agent.systemInstruction.join("\n") +
"\ntools model: \n" + agent.toolsModel?.name +
"\n\ntools: " + (agent.tools ? agent.tools.join(", ") : "");
}

View file

@ -10,7 +10,7 @@ import { Configuration } from "../configuration";
import { PREDEFINED_LISTS } from "../lists";
export class ModelService {
public static readonly emptyModel = {name: ""};
private app: Application;
private strategies: Record<string, IAddStrategy>;
@ -146,7 +146,7 @@ export class ModelService {
};
}
selectModel = async (type: ModelType, modelsList: LlmModel[]): Promise<LlmModel | undefined> => {
selectModel = async (type: ModelType, modelsList: LlmModel[], shoudStartModel:boolean = true): Promise<LlmModel | undefined> => {
const details = this.getTypeDetails(type);
let allModels = modelsList.concat(PREDEFINED_LISTS.get(type) as LlmModel[])
let modelsItems: QuickPickItem[] = this.getModels(modelsList, "", true);
@ -182,7 +182,7 @@ export class ModelService {
model = allModels[index];
}
await this.selectStartModel(model, type, details);
if (shoudStartModel) await this.selectStartModel(model, type, details);
return model;
}
@ -235,7 +235,7 @@ export class ModelService {
}
public async showModelDetails(model: LlmModel): Promise<void> {
await Utils.showOkDialog("Model details: " + this.getDetails(model));
await Utils.showOkDialog("Model details: \n\n" + this.getDetails(model));
}
async exportModel(type: ModelType, modelsList: LlmModel[]): Promise<void> {
@ -391,7 +391,7 @@ export class ModelService {
}
clearModel = (type: ModelType) => {
this.app.setSelectedModel(type, ModelService.emptyModel);
this.app.setSelectedModel(type, Application.emptyModel);
this.app.llamaWebviewProvider.updateLlamaView();
}
@ -406,8 +406,13 @@ export class ModelService {
this.app.setSelectedModel(modelType, model);
}
public async selectAgentModel(modelType: ModelType, modelsList: LlmModel[]) {
let model = await this.app.modelService.selectModel(modelType, modelsList, false);
this.app.setAgentModel(model);
}
public getEmptyModel(): LlmModel {
return ModelService.emptyModel
return Application.emptyModel
}
public async checkForToolsModel() {

View file

@ -60,7 +60,8 @@ export interface Env {
export interface Agent {
name: string,
description?: string,
systemInstruction: string[]
systemInstruction: string[],
toolsModel?: LlmModel,
tools?: string[]
}

View file

@ -24,6 +24,9 @@ const App: React.FC<AppProps> = () => {
const [currentToolsModel, setCurrentToolsModel] = useState<string>(
initialState.currentToolsModel || noModelSelected
);
const [currentAgentModel, setCurrentAgentModel] = useState<string>(
initialState.currentAgentModel || noModelSelected
);
const [currentChatModel, setCurrentChatModel] = useState<string>(
initialState.currentChatModel || noModelSelected
);
@ -47,7 +50,7 @@ const App: React.FC<AppProps> = () => {
);
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:""});
const [agentEditDetails, setAgentEditDetails] = useState<{name:string, description:string, systemInstruction:string, toolsModel: string}>({name:"", description:"", systemInstruction:"", toolsModel:""});
// Save state to VS Code whenever it changes
useEffect(() => {
@ -58,12 +61,13 @@ const App: React.FC<AppProps> = () => {
currentChatModel,
currentEmbeddingsModel,
currentCompletionModel,
currentAgentModel,
currentAgent,
currentEnv: currentEnv,
currentState,
view
});
}, [displayText, inputText, currentToolsModel, currentChatModel, currentEmbeddingsModel, currentCompletionModel, currentAgent, currentEnv, currentState, view]);
}, [displayText, inputText, currentToolsModel, currentChatModel, currentEmbeddingsModel, currentCompletionModel, currentAgentModel, currentAgent, currentEnv, currentState, view]);
useEffect(() => {
// Listen for messages from the extension
@ -73,6 +77,9 @@ const App: React.FC<AppProps> = () => {
case 'updateToolsModel':
setCurrentToolsModel(message.model || noModelSelected);
break;
case 'updateTmpAgentModel':
setCurrentAgentModel(message.model || "");
break;
case 'updateChatModel':
setCurrentChatModel(message.model || noModelSelected);
break;
@ -95,10 +102,11 @@ const App: React.FC<AppProps> = () => {
setContextFiles(new Map(message.files || []));
break;
case 'updateAgentEdit':
setAgentEditDetails({name: message.name, description: message.description, systemInstruction: message.systemInstruction});
setAgentEditDetails({name: message.name, description: message.description, systemInstruction: message.systemInstruction, toolsModel: message.toolsModel});
break;
case 'loadAgent':
setAgentEditDetails({name: message.name, description: message.description, systemInstruction: message.systemInstruction});
setAgentEditDetails({name: message.name, description: message.description, systemInstruction: message.systemInstruction, toolsModel: message.toolsModel});
setCurrentAgentModel(message.toolsModel);
setAgentEditTools(new Map(message.tools || []));
break;
default:
@ -215,7 +223,7 @@ const App: React.FC<AppProps> = () => {
<AgentEditor
inputText={inputText}
setInputText={setInputText}
currentToolsModel={currentToolsModel}
currentAgentModel={currentAgentModel}
currentAgent={currentAgent}
currentState={currentState}
setCurrentState={setCurrentState}

View file

@ -4,20 +4,20 @@ import { vscode } from '../types/vscode';
interface AgentEditorProps {
inputText: string;
setInputText: (text: string) => void;
currentToolsModel: string;
currentAgentModel: 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
agentEditDetails: {name: string, description: string, systemInstruction: string, toolsModel: string}
setAgentEditDetails:(agentDetails: {name: string, description: string, systemInstruction: string, toolsModel: string}) => void
}
const AgentEditor: React.FC<AgentEditorProps> = ({
inputText,
setInputText,
currentToolsModel,
currentAgentModel,
currentAgent,
currentState,
setCurrentState,
@ -90,8 +90,9 @@ const AgentEditor: React.FC<AgentEditorProps> = ({
setTools(new Map(message.files || []));
break;
case 'loadAgent':
setAgentEditDetails({name: message.name, description: message.description, systemInstruction: message.systemInstruction});
setAgentEditDetails({name: message.name, description: message.description, systemInstruction: message.systemInstruction, toolsModel: message.toolsModel});
setTools(new Map(message.tools || []));
currentAgentModel = message.toolsModel;
break;
default:
break;
@ -121,6 +122,7 @@ const AgentEditor: React.FC<AgentEditorProps> = ({
name: agentEditDetails.name,
description: agentEditDetails.description,
systemInstruction: agentEditDetails.systemInstruction,
toolsModel: currentAgentModel,
tools: Array.from(agentTools.keys())
});
};
@ -132,9 +134,27 @@ const AgentEditor: React.FC<AgentEditorProps> = ({
});
}
const handleDeselectToolsModel = () => {
vscode.postMessage({
command: 'deselectAgentModel'
});
};
const handleSelectToolsModel = () => {
vscode.postMessage({
command: 'selectModelWithTools'
command: 'selectAgentModel'
});
};
const handleMoreToolsModel = () => {
vscode.postMessage({
command: 'moreToolsModel'
});
};
const handleShowToolsModel = () => {
vscode.postMessage({
command: 'showToolsModel'
});
};
@ -150,6 +170,7 @@ const AgentEditor: React.FC<AgentEditorProps> = ({
name: "",
description: "",
systemInstruction: [],
toolsModel: "",
tools: []
});
}
@ -306,8 +327,8 @@ const AgentEditor: React.FC<AgentEditorProps> = ({
Delete
</button>
</div>
{/* Context Files */}
<span style={{ display: 'block', marginTop: '20px', marginBottom: '10px', fontWeight: 'bold' }}>{'Tools'}</span>
{/* Tools */}
{agentTools.size > 0 && (
<div className="context-chips">
{Array.from(agentTools.entries()).map(([toolName, toolDescription]) => (
@ -331,51 +352,98 @@ const AgentEditor: React.FC<AgentEditorProps> = ({
)}
<span>{'Name (agent identifier)'}</span>
<span style={{ display: 'block', marginTop: '20px', marginBottom: '10px', fontWeight: 'bold' }}>{'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})}
onChange={(e) => setAgentEditDetails({name: e.target.value, description: agentEditDetails.description, systemInstruction: agentEditDetails.systemInstruction, toolsModel: agentEditDetails.toolsModel})}
placeholder="Enter agent name."
className="modern-textarea"
rows={1}
style={{ height: 'auto', minHeight: '1.5em', resize: 'none' }}
/>
<span>{'Descriptoin'}</span>
<span style={{ display: 'block', marginTop: '20px', marginBottom: '10px', fontWeight: 'bold' }}>{'Descriptoin'}</span>
{/* Modern Textarea */}
<textarea
ref={elemDescriptionRef}
value={agentEditDetails.description}
onChange={(e) => setAgentEditDetails({name: agentEditDetails.name, description: e.target.value, systemInstruction: agentEditDetails.systemInstruction})}
onChange={(e) => setAgentEditDetails({name: agentEditDetails.name, description: e.target.value, systemInstruction: agentEditDetails.systemInstruction, toolsModel: agentEditDetails.toolsModel})}
placeholder="Enter agent description."
className="modern-textarea"
rows={2}
style={{ height: 'auto', minHeight: '3em', resize: 'none' }}
/>
<span>{'System Instruction'}</span>
<span style={{ display: 'block', marginTop: '20px', marginBottom: '10px', fontWeight: 'bold' }}>{'System Instruction'}</span>
{/* Modern Textarea */}
<textarea
ref={elemSystemPromptRef}
value={agentEditDetails.systemInstruction}
onChange={(e) => setAgentEditDetails({name: agentEditDetails.name, description: agentEditDetails.description, systemInstruction: e.target.value})}
onChange={(e) => setAgentEditDetails({name: agentEditDetails.name, description: agentEditDetails.description, systemInstruction: e.target.value, toolsModel: agentEditDetails.toolsModel})}
placeholder="Enter system instructions for the agent."
className="modern-textarea"
rows={10}
style={{ height: 'auto', minHeight: '15em', resize: 'vertical' }}
/>
<div className="single-button-frame">
<div className="frame-label">Agent Model (Optional)</div>
{currentAgentModel == "" && (
<button
onClick={handleSelectToolsModel}
title={`Add Default Agent (Tools) Model`}
className="modern-btn secondary"
>
Add
</button>
)}
{
currentAgentModel && (
<button
onClick={handleDeselectToolsModel}
title={`Remove Agent Model`}
className="modern-btn secondary"
>
Remove
</button>
)
}
<span className="model-text">{currentAgentModel}</span>
{
currentAgentModel === "" && (
<button
onClick={handleMoreToolsModel}
title={`Add/Delete/View/Export/Import Tools Model`}
className="modern-btn secondary"
>
More
</button>
)
}
{
currentAgentModel && (
<button
onClick={handleShowToolsModel}
title={`Show Tools Model Details`}
className="modern-btn secondary"
>
...
</button>
)
}
</div>
{/* Input Actions */}
<div className="input-actions">
<div className="input-buttons">
<button
onClick={handleSelectTools}
className="modern-btn secondary"
title="Add file to context"
title="Add/remove tools to the agent"
>
Add Tools
Add/Remove Tools
</button>
</div>
<button