From b96a340d9778bcbef717165083c7bc36882fc14c Mon Sep 17 00:00:00 2001 From: Md Abid Hussain Date: Sun, 10 Nov 2024 00:09:47 +0530 Subject: [PATCH 1/2] Implement methods to work with MindsDB agents --- src/agents/agent.ts | 97 +++++++++++++++ src/agents/agentCompletion.ts | 24 ++++ src/agents/agentsApiClient.ts | 72 +++++++++++ src/agents/agentsModule.ts | 3 + src/agents/agentsRestApiClient.ts | 198 ++++++++++++++++++++++++++++++ 5 files changed, 394 insertions(+) create mode 100644 src/agents/agent.ts create mode 100644 src/agents/agentCompletion.ts create mode 100644 src/agents/agentsApiClient.ts create mode 100644 src/agents/agentsModule.ts create mode 100644 src/agents/agentsRestApiClient.ts diff --git a/src/agents/agent.ts b/src/agents/agent.ts new file mode 100644 index 0000000..9928971 --- /dev/null +++ b/src/agents/agent.ts @@ -0,0 +1,97 @@ +import Constants from '../constants'; +import { MindsDbError } from '../errors'; +import AgentCompletion from './agentCompletion'; +import AgentsRestApiClient from './agentsRestApiClient'; + +/** + * Represents an agent in MindsDB. + */ +export default class Agent { + /** Project name to which agent belongs to */ + project: string; + + /** Agent name */ + name: string; + /** Model name */ + model: string; + /** Skills of the agent */ + skills: Array; + /** Additional parameters */ + params: any; + /** Rest API client to use for executing agent operations */ + agentsRestApiClient: AgentsRestApiClient; + + /** + * Creates a new agent. + * + * @param {string} project - Project name to which agent belongs to + * @param {string} name - Agent name + * @param {string} model - Model name + * @param {Array} skills - Skills of the agent + * @param {any} params - Additional parameters + * @param {AgentsRestApiClient} agentsRestApiClient - Rest API client to use for executing agent operations + */ + constructor( + project: string, + name: string, + model: string, + skills: Array, + params: any, + agentsRestApiClient: AgentsRestApiClient + ) { + this.project = project; + this.name = name; + this.model = model; + this.skills = skills; + this.params = params; + this.agentsRestApiClient = agentsRestApiClient; + } + + /** + * Creates an agent from a JSON object. + * + * @param {string} project - Project name to which agent belongs to + * @param {any} json - JSON object representing the agent + * @param {AgentsRestApiClient} agentsRestApiClient - Rest API client to use for executing agent operations + * @returns {Agent} - The created agent + */ + static fromJson( + project: string, + json: any, + agentsRestApiClient: AgentsRestApiClient + ): Agent { + return new Agent( + project, + json.name, + json.model, + json.skills.map((skill: any) => skill.name), + json.params || {}, + agentsRestApiClient + ); + } + + /** + * Gets the agent completion. + * + * @param {Array} messages - Messages to send to the agent + * @returns {Promise} - The agent completion + */ + async completion(messages: Array): Promise { + const baseUrl = + this.agentsRestApiClient.client.defaults.baseURL || + Constants.BASE_CLOUD_API_ENDPOINT; + const agentsUrl = `${baseUrl}${Constants.BASE_PROJECTS_URI}/${this.project}/agents/${this.name}/completions`; + + try { + const response = await this.agentsRestApiClient.client.post(agentsUrl, { + messages: messages, + }); + + const content: string = response.data.message.content; + const context: Array = response.data.message.context || []; + return new AgentCompletion(content, context); + } catch (error) { + throw MindsDbError.fromHttpError(error, agentsUrl); + } + } +} diff --git a/src/agents/agentCompletion.ts b/src/agents/agentCompletion.ts new file mode 100644 index 0000000..bac4ff6 --- /dev/null +++ b/src/agents/agentCompletion.ts @@ -0,0 +1,24 @@ +/** + * Represents the completion of an agent with content and context. + */ +export default class AgentCompletion { + /** + * The content of the agent completion. + */ + content: string; + + /** + * The context in which the agent completion is made. + */ + context: Array; + + /** + * Creates an instance of AgentCompletion. + * @param {string} content - The content of the agent completion. + * @param {Array} context - The context in which the agent completion is made. + */ + constructor(content: string, context: Array) { + this.content = content; + this.context = context; + } +} diff --git a/src/agents/agentsApiClient.ts b/src/agents/agentsApiClient.ts new file mode 100644 index 0000000..ee90c6e --- /dev/null +++ b/src/agents/agentsApiClient.ts @@ -0,0 +1,72 @@ +import Agent from './agent'; + +/** + * Abstract class representing the API client for managing agents. + */ +export default abstract class AgentsApiClient { + /** + * Retrieves all agents for a given project. + * @param {string} project - The name of the project. + * @throws {MindsDbError} If the agents cannot be retrieved. + * @returns {Promise>} A promise that resolves to an array of agents. + */ + abstract getAllAgents(project: string): Promise>; + + /** + * Retrieves a specific agent by its ID. + * @param {string} project - The name of the project. + * @param {string} agentId - The ID of the agent. + * @throws {MindsDbError} If the agent does not exist. + * @returns {Promise} A promise that resolves to the agent. + */ + abstract getAgent(project: string, agentId: string): Promise; + + /** + * Creates a new agent. + * @param {string} project - The name of the project. + * @param {string} name - The name of the agent. + * @param {string} model - The model of the agent. + * @param {string} provider - The provider of the agent. + * @param {Array} skills - An array of skills for the agent. + * @param {any} [params] - Optional parameters for the agent. + * @throws {MindsDbError} If the agent cannot be created. + * @returns {Promise} A promise that resolves to the created agent. + */ + abstract createAgent( + project: string, + name: string, + model: string, + provider: string, + skills: Array, + params?: any + ): Promise; + + /** + * Updates an existing agent. + * @param {string} project - The name of the project. + * @param {string} agentName - The current name of the agent. + * @param {string} [updatedName] - The new name of the agent (optional). + * @param {string} [updatedModel] - The new model of the agent (optional). + * @param {Array} [updatedSkills] - An array of updated skills for the agent (optional). + * @param {any} [updatedParams] - Optional updated parameters for the agent. + * @throws {MindsDbError} If the agent cannot be updated. + * @returns {Promise} A promise that resolves to the updated agent. + */ + abstract updateAgent( + project: string, + agentName: string, + updatedName?: string, + updatedModel?: string, + updatedSkills?: Array, + updatedParams?: any + ): Promise; + + /** + * Deletes an agent. + * @param {string} project - The name of the project. + * @param {string} agent - The name of the agent to delete. + * @throws {MindsDbError} If the agent cannot be deleted. + * @returns {Promise} A promise that resolves when the agent is deleted. + */ + abstract deleteAgent(project: string, agent: string): Promise; +} diff --git a/src/agents/agentsModule.ts b/src/agents/agentsModule.ts new file mode 100644 index 0000000..e775a06 --- /dev/null +++ b/src/agents/agentsModule.ts @@ -0,0 +1,3 @@ +import AgentsRestApiClient from './agentsRestApiClient'; + +export default { AgentsRestApiClient }; diff --git a/src/agents/agentsRestApiClient.ts b/src/agents/agentsRestApiClient.ts new file mode 100644 index 0000000..cb9a583 --- /dev/null +++ b/src/agents/agentsRestApiClient.ts @@ -0,0 +1,198 @@ +import { Axios } from 'axios'; +import Constants from '../constants'; +import { MindsDbError } from '../errors'; +import Agent from './agent'; +import AgentsApiClient from './agentsApiClient'; + +const DEFAULT_LLM_PROMPT = + "Answer the user's question in a helpful way: {{question}}"; +const DEFAULT_LLM_MODEL = 'gpt-4o'; + +/** Implementation of AgentsApiClient that goes through the REST API */ +export default class AgentsRestApiClient extends AgentsApiClient { + /** Axios client to use for making HTTP requests */ + client: Axios; + + /** + * Creates a new AgentsRestApiClient. + * + * @param {Axios} client - Axios client to use for making HTTP requests + */ + constructor(client: Axios) { + super(); + this.client = client; + } + + private getAgentsUrl(project: string): string { + const baseUrl = + this.client.defaults.baseURL || Constants.BASE_CLOUD_API_ENDPOINT; + const agentsUrl = `${baseUrl}${Constants.BASE_PROJECTS_URI}/${project}/agents`; + return agentsUrl; + } + + /** + * Retrieves all agents for a given project. + * + * @param project Project name to which agent belongs to + * @returns {Promise>} A promise that resolves to an array of agents. + */ + override async getAllAgents(project: string): Promise> { + const agentsUrl = this.getAgentsUrl(project); + try { + const agentsResponse = await this.client.get(agentsUrl); + if (!agentsResponse.data) { + return []; + } + return agentsResponse.data.map((agent: any) => + Agent.fromJson(project, agent, this) + ); + } catch (error) { + throw MindsDbError.fromHttpError(error, agentsUrl); + } + } + + /** + * Retrieves a specific agent by its name. + * + * @param {string} project Project name to which agent belongs to + * @param {string} agent Agent name to retrieve + * @returns {Promise} A promise that resolves to the agent. + */ + override async getAgent(project: string, agent: string): Promise { + const agentsUrl = this.getAgentsUrl(project) + `/${agent}`; + try { + const agentResponse = await this.client.get(agentsUrl); + return Agent.fromJson(project, agentResponse.data, this); + } catch (error) { + throw MindsDbError.fromHttpError(error, agentsUrl); + } + } + + /** + * Creates a new agent. + * + * @param {string} project Project name to which agent belongs to + * @param {string} name Agent name to create + * @param {string} [model] Model to use for the agent + * @param {string} [provider] Provider to use for the agent + * @param {Array} [skills] Skills to assign to the agent + * @param {any} [params] Additional parameters for the agent + * @returns {Promise} A promise that resolves to the created agent. + */ + override async createAgent( + project: string, + name: string, + model?: string, + provider?: string, + skills?: Array, + params?: any + ): Promise { + const agentsUrl = this.getAgentsUrl(project); + + try { + const agentsParams: any = params ? { ...params } : {}; + if (!agentsParams.hasOwnProperty('prompt_template')) { + agentsParams['prompt_template'] = DEFAULT_LLM_PROMPT; + } + + const agent: { + name: string; + model_name?: string; + skills?: Array; + params?: any; + provider?: string | null; + } = { + name: name, + model_name: model || DEFAULT_LLM_MODEL, + skills: skills || [], + provider: provider || null, + params: agentsParams, + }; + + const agentResponse = await this.client.post(agentsUrl, { agent }); + + return Agent.fromJson(project, agentResponse.data, this); + } catch (error) { + throw MindsDbError.fromHttpError(error, agentsUrl); + } + } + + /** + * Updates an existing agent. + * + * @param {string} project Project name to which agent belongs to + * @param {string} agentName Name of the agent to update + * @param {string} [updatedName] New name for the agent + * @param {string} [updatedModel] New model for the agent + * @param {Array} [updatedSkills] New skills for the agent + * @param {any} [updatedParams] New parameters for the agent + * @returns {Promise} A promise that resolves to the updated agent. + */ + override async updateAgent( + project: string, + agentName: string, + updatedName?: string, + updatedModel?: string, + updatedSkills?: Array, + updatedParams?: any + ): Promise { + const agentsUrl = this.getAgentsUrl(project) + `/${agentName}`; + try { + const agent = await this.getAgent(project, agentName); + const updatedSkillSet = new Set(); + if (updatedSkills) { + updatedSkills?.forEach((skill) => updatedSkillSet.add(skill)); + } + const existingSkillSet = new Set(agent.skills); + const skillsToAddSet = new Set(updatedSkillSet); + existingSkillSet.forEach((skill) => { + if (skillsToAddSet.has(skill)) { + skillsToAddSet.delete(skill); + } + }); + const skillsToRemoveSet = new Set(existingSkillSet); + updatedSkillSet.forEach((skill) => { + if (skillsToRemoveSet.has(skill)) { + skillsToRemoveSet.delete(skill); + } + }); + + const updatedAgent: { + name: string; + model_name: string; + skills_to_add: Array; + skills_to_remove: Array; + params: any; + } = { + name: updatedName || agent.name, + model_name: updatedModel || agent.model, + skills_to_add: Array.from(skillsToAddSet), + skills_to_remove: Array.from(skillsToRemoveSet), + params: updatedParams || agent.params, + }; + + const agentResponse = await this.client.put(agentsUrl, { + agent: updatedAgent, + }); + + return Agent.fromJson(project, agentResponse.data, this); + } catch (error) { + throw MindsDbError.fromHttpError(error, agentsUrl); + } + } + + /** + * Deletes a specific agent by its name. + * + * @param {string} project Project name to which agent belongs to + * @param {string} agent Agent name to delete + */ + override async deleteAgent(project: string, agent: string): Promise { + const agentsUrl = this.getAgentsUrl(project) + `/${agent}`; + try { + await this.client.delete(agentsUrl); + } catch (error) { + throw MindsDbError.fromHttpError(error, agentsUrl); + } + } +} From 390e206785ef5365e370d560f70d88f9e9375edc Mon Sep 17 00:00:00 2001 From: Md Abid Hussain Date: Sun, 10 Nov 2024 00:10:51 +0530 Subject: [PATCH 2/2] added agent module in index.ts --- src/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/index.ts b/src/index.ts index 29a5f32..e13b5c0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,7 @@ import { retryUnauthenticatedRequest, } from './util/http'; import TablesModule from './tables/tablesModule'; +import AgentsModule from './agents/agentsModule'; import HttpAuthenticator from './httpAuthenticator'; import { Axios } from 'axios'; @@ -58,6 +59,7 @@ const MLEngines = new MLEnginesModule.MLEnginesRestApiClient( defaultAxiosInstance, httpAuthenticator ); +const Agents = new AgentsModule.AgentsRestApiClient(defaultAxiosInstance); const Callbacks = new CallbacksModule.CallbacksRestApiClient( defaultAxiosInstance, httpAuthenticator @@ -120,6 +122,7 @@ export default { Views, Jobs, MLEngines, + Agents, Callbacks, }; export {