Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented set of functions for AI Agents #105

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions src/agents/agent.ts
Original file line number Diff line number Diff line change
@@ -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<string>;
/** 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<string>} 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<string>,
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<any>} messages - Messages to send to the agent
* @returns {Promise<AgentCompletion>} - The agent completion
*/
async completion(messages: Array<any>): Promise<AgentCompletion> {
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<string> = response.data.message.context || [];
return new AgentCompletion(content, context);
} catch (error) {
throw MindsDbError.fromHttpError(error, agentsUrl);
}
}
}
24 changes: 24 additions & 0 deletions src/agents/agentCompletion.ts
Original file line number Diff line number Diff line change
@@ -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<string>;

/**
* Creates an instance of AgentCompletion.
* @param {string} content - The content of the agent completion.
* @param {Array<string>} context - The context in which the agent completion is made.
*/
constructor(content: string, context: Array<string>) {
this.content = content;
this.context = context;
}
}
72 changes: 72 additions & 0 deletions src/agents/agentsApiClient.ts
Original file line number Diff line number Diff line change
@@ -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<Array<Agent>>} A promise that resolves to an array of agents.
*/
abstract getAllAgents(project: string): Promise<Array<Agent>>;

/**
* 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<Agent>} A promise that resolves to the agent.
*/
abstract getAgent(project: string, agentId: string): Promise<Agent>;

/**
* 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<string>} 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<Agent>} A promise that resolves to the created agent.
*/
abstract createAgent(
project: string,
name: string,
model: string,
provider: string,
skills: Array<string>,
params?: any
): Promise<Agent>;

/**
* 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<string>} [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<Agent>} A promise that resolves to the updated agent.
*/
abstract updateAgent(
project: string,
agentName: string,
updatedName?: string,
updatedModel?: string,
updatedSkills?: Array<string>,
updatedParams?: any
): Promise<Agent>;

/**
* 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<void>} A promise that resolves when the agent is deleted.
*/
abstract deleteAgent(project: string, agent: string): Promise<void>;
}
3 changes: 3 additions & 0 deletions src/agents/agentsModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import AgentsRestApiClient from './agentsRestApiClient';

export default { AgentsRestApiClient };
198 changes: 198 additions & 0 deletions src/agents/agentsRestApiClient.ts
Original file line number Diff line number Diff line change
@@ -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<Array<Agent>>} A promise that resolves to an array of agents.
*/
override async getAllAgents(project: string): Promise<Array<Agent>> {
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<Agent>} A promise that resolves to the agent.
*/
override async getAgent(project: string, agent: string): Promise<Agent> {
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<string>} [skills] Skills to assign to the agent
* @param {any} [params] Additional parameters for the agent
* @returns {Promise<Agent>} A promise that resolves to the created agent.
*/
override async createAgent(
project: string,
name: string,
model?: string,
provider?: string,
skills?: Array<string>,
params?: any
): Promise<Agent> {
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<string>;
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<string>} [updatedSkills] New skills for the agent
* @param {any} [updatedParams] New parameters for the agent
* @returns {Promise<Agent>} A promise that resolves to the updated agent.
*/
override async updateAgent(
project: string,
agentName: string,
updatedName?: string,
updatedModel?: string,
updatedSkills?: Array<string>,
updatedParams?: any
): Promise<Agent> {
const agentsUrl = this.getAgentsUrl(project) + `/${agentName}`;
try {
const agent = await this.getAgent(project, agentName);
const updatedSkillSet = new Set<string>();
if (updatedSkills) {
updatedSkills?.forEach((skill) => updatedSkillSet.add(skill));
}
const existingSkillSet = new Set<string>(agent.skills);
const skillsToAddSet = new Set<string>(updatedSkillSet);
existingSkillSet.forEach((skill) => {
if (skillsToAddSet.has(skill)) {
skillsToAddSet.delete(skill);
}
});
const skillsToRemoveSet = new Set<string>(existingSkillSet);
updatedSkillSet.forEach((skill) => {
if (skillsToRemoveSet.has(skill)) {
skillsToRemoveSet.delete(skill);
}
});

const updatedAgent: {
name: string;
model_name: string;
skills_to_add: Array<string>;
skills_to_remove: Array<string>;
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<void> {
const agentsUrl = this.getAgentsUrl(project) + `/${agent}`;
try {
await this.client.delete(agentsUrl);
} catch (error) {
throw MindsDbError.fromHttpError(error, agentsUrl);
}
}
}
Loading