@amitdeshmukh/ax-crew 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +3 -0
- package/README.md +87 -0
- package/agent_config.example.yaml +41 -0
- package/dist/agents/agentConfig.js +113 -0
- package/dist/agents/index.js +93 -0
- package/dist/config/index.js +24 -0
- package/dist/functions/dateTime.js +36 -0
- package/dist/functions/index.js +6 -0
- package/dist/index.js +2 -0
- package/dist/state/createState.js +32 -0
- package/dist/utils/index.js +25 -0
- package/package.json +27 -0
- package/src/agents/agentConfig.ts +157 -0
- package/src/agents/index.ts +133 -0
- package/src/config/index.ts +44 -0
- package/src/functions/dateTime.ts +39 -0
- package/src/functions/index.ts +8 -0
- package/src/index.ts +3 -0
- package/src/state/createState.ts +46 -0
- package/src/utils/index.ts +27 -0
- package/test.js +36 -0
- package/tsconfig.json +17 -0
package/.env.example
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Agents
|
|
2
|
+
|
|
3
|
+
This repo is designed to be used as a module. It simplifies development of AI Agents via config rather than code, so that they can be either reused or rapidly deployed to different applications and processes. The core functionality revolves around the use of AI models to process and respond to queries.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
### Installation
|
|
8
|
+
```bash
|
|
9
|
+
npm install @buddhic-ai/agents
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
### Environment Setup
|
|
13
|
+
Refer to the [.env.example](.env.example) file for the required environment variables. These will need to be set in the environment where the agents are run.
|
|
14
|
+
|
|
15
|
+
## Crew Configuration
|
|
16
|
+
A Crew is a team of agents that work together to achieve a common goal. The configuration file for a crew is a YAML file that defines the agents in the crew, along with their configuration.
|
|
17
|
+
|
|
18
|
+
An example configuration is provided in `agent_config.example.yaml`.
|
|
19
|
+
|
|
20
|
+
### Creating the Crew
|
|
21
|
+
To initialize a crew of agents, pass the path to the configuration file to the `AgentCrew` class.
|
|
22
|
+
|
|
23
|
+
```javascript
|
|
24
|
+
// Import the AgentCrew class
|
|
25
|
+
import AgentCrew from './src/agents/index.js';
|
|
26
|
+
|
|
27
|
+
// Create a new instance of AgentCrew
|
|
28
|
+
const configFilePath = './agent_config.example.yaml';
|
|
29
|
+
const crew = new AgentCrew(configFilePath);
|
|
30
|
+
```
|
|
31
|
+
### Adding Agents to the Crew
|
|
32
|
+
You can add a sub-set of defined agents from the configuration file to the crew by passing their names as an array to the `addAgents` method.
|
|
33
|
+
|
|
34
|
+
Please ensure that the agents are defined in the configuration file before adding them to the crew. Also, the order in which the agents are added to the crew is important, as an error will be thrown if an agent is added before its dependent agents.
|
|
35
|
+
|
|
36
|
+
For example, the `Manager` agent in the configuration file depends on the `Planner` and `Calculator` agents. Therefore, the `Planner` and `Calculator` agents must be added to the crew before the `Manager` agent.
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
// Add agents by providing their names
|
|
40
|
+
const agentNames = ['Planner', 'Calculator', 'Manager'];
|
|
41
|
+
const agents = crew.addAgentsToCrew(agentNames);
|
|
42
|
+
|
|
43
|
+
// Get agent instances
|
|
44
|
+
const Planner = agents.Planner;
|
|
45
|
+
const Manager = agents.Manager;
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### State Management
|
|
49
|
+
|
|
50
|
+
The `StatefulAxAgent` class in `src/agents/index.js` allows for shared state functionality across agents. Sub-agents can be added to an agent to create complex behaviors. All agents in the crew have access to the shared state. State can also be shared with functions that are passed to the agents. To do this, pass the `state` object as an argument to the function class as shown here https://axllm.dev/guides/functions-1/
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
// Set some state (key/value) for the crew
|
|
55
|
+
crew.state.set('name', 'Crew1');
|
|
56
|
+
crew.state.set('location', 'Earth');
|
|
57
|
+
|
|
58
|
+
// Get the state for the crew
|
|
59
|
+
crew.state.get('name'); // 'Crew1'
|
|
60
|
+
crew.state.getAll(); // { name: 'Crew1', location: 'Earth' }
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
State can also be set/get by individual agents in the crew. This state is shared with all agents and functions if passed in through the functions class constructor.
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
Planner.state.set('plan', 'some plan');
|
|
67
|
+
console.log(Manager.state.getAll()); // { name: 'Crew1', location: 'Earth', plan: 'some plan' }
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Completing a task
|
|
71
|
+
|
|
72
|
+
An example of how to complete a task using the agents is shown below. The `Planner` agent is used to plan the task, and the `Manager` agent is used to execute the task.
|
|
73
|
+
|
|
74
|
+
```javascript
|
|
75
|
+
const userQuery = "i referred a friend to active and they were hired and started work on 1st july. But i did not receive my referral bonus. what amount should i have received?";
|
|
76
|
+
|
|
77
|
+
console.log(`\n\nQuestion: ${userQuery}`);
|
|
78
|
+
|
|
79
|
+
const planResponse = await Planner.forward({ task: userQuery });
|
|
80
|
+
const managerResponse = await Manager.forward({ question: userQuery, plan: planResponse.plan });
|
|
81
|
+
|
|
82
|
+
const plan = planResponse.plan;
|
|
83
|
+
const answer = managerResponse.answer;
|
|
84
|
+
|
|
85
|
+
console.log(`\n\nPlan: ${plan}`);
|
|
86
|
+
console.log(`\n\nAnswer: ${answer}`);
|
|
87
|
+
```
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# AxLLM Crew config
|
|
2
|
+
crew:
|
|
3
|
+
- name: Planner
|
|
4
|
+
description: Creates a plan to complete a task
|
|
5
|
+
signature: task:string "a task to be completed" -> plan:string "a plan to execute the task based on company policies and operations"
|
|
6
|
+
provider: anthropic
|
|
7
|
+
provider_key_name: ANTHROPIC_API_KEY
|
|
8
|
+
ai:
|
|
9
|
+
model: claude-3-5-sonnet-20240620
|
|
10
|
+
temperature: 0
|
|
11
|
+
options:
|
|
12
|
+
debug: true
|
|
13
|
+
|
|
14
|
+
- name: Calculator
|
|
15
|
+
description: Solves math problems
|
|
16
|
+
signature: mathProblem:string "a math problem to be solved using Python code" -> solution:string "the solution to the math problem"
|
|
17
|
+
provider: google-gemini
|
|
18
|
+
provider_key_name: GEMINI_API_KEY
|
|
19
|
+
ai:
|
|
20
|
+
model: gemini-1.5-pro
|
|
21
|
+
temperature: 0
|
|
22
|
+
options:
|
|
23
|
+
debug: true
|
|
24
|
+
codeExecution: true
|
|
25
|
+
|
|
26
|
+
- name: Manager
|
|
27
|
+
description: Answers questions about company policies and operations
|
|
28
|
+
signature: question:string "a question from a user about company policies and operations", plan:string "a suggested plan to answer the question" -> answer:string "the answer"
|
|
29
|
+
provider: anthropic
|
|
30
|
+
provider_key_name: ANTHROPIC_API_KEY
|
|
31
|
+
ai:
|
|
32
|
+
model: claude-3-5-sonnet-20240620
|
|
33
|
+
temperature: 0
|
|
34
|
+
options:
|
|
35
|
+
debug: true
|
|
36
|
+
functions:
|
|
37
|
+
- CurrentDateTime
|
|
38
|
+
- DaysBetweenDates
|
|
39
|
+
agents:
|
|
40
|
+
- Calculator
|
|
41
|
+
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import yaml from 'js-yaml';
|
|
3
|
+
import { PROVIDER_API_KEYS } from '../config/index';
|
|
4
|
+
import { functions as importedFunctions } from '../functions/index';
|
|
5
|
+
import { AxAIAnthropic, AxAIOpenAI, AxAIAzureOpenAI, AxAICohere, AxAIDeepSeek, AxAIGoogleGemini, AxAIGroq, AxAIHuggingFace, AxAIMistral, AxAIOllama, AxAITogether } from '@ax-llm/ax';
|
|
6
|
+
// Define a mapping from provider names to their respective constructors
|
|
7
|
+
const AIConstructors = {
|
|
8
|
+
'anthropic': AxAIAnthropic,
|
|
9
|
+
'azure-openai': AxAIAzureOpenAI,
|
|
10
|
+
'cohere': AxAICohere,
|
|
11
|
+
'deepseek': AxAIDeepSeek,
|
|
12
|
+
'google-gemini': AxAIGoogleGemini,
|
|
13
|
+
'groq': AxAIGroq,
|
|
14
|
+
'huggingFace': AxAIHuggingFace,
|
|
15
|
+
'mistral': AxAIMistral,
|
|
16
|
+
'ollama': AxAIOllama,
|
|
17
|
+
'openai': AxAIOpenAI,
|
|
18
|
+
'together': AxAITogether
|
|
19
|
+
};
|
|
20
|
+
const functions = importedFunctions;
|
|
21
|
+
/**
|
|
22
|
+
* Reads the AI parameters from the YAML configuration file.
|
|
23
|
+
* @param {string} agentConfigFilePath - The path to the agent_config.yaml file.
|
|
24
|
+
* @returns {Object} The parsed agent configs from the config.yaml file.
|
|
25
|
+
* @throws Will throw an error if reading the file fails.
|
|
26
|
+
*/
|
|
27
|
+
const parseAgentConfig = (agentConfigFilePath) => {
|
|
28
|
+
try {
|
|
29
|
+
const fileContents = fs.readFileSync(agentConfigFilePath, 'utf8');
|
|
30
|
+
const parsedConfigs = yaml.load(fileContents);
|
|
31
|
+
return parsedConfigs;
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
console.error('Error reading agent config file:', e);
|
|
35
|
+
throw e;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Initializes the AI agent using the specified agent name and configuration file path.
|
|
40
|
+
* This function parses the agent's configuration, validates the presence of the necessary API key,
|
|
41
|
+
* and creates an instance of the AI agent with the appropriate settings.
|
|
42
|
+
*
|
|
43
|
+
* @param {string} agentName - The identifier for the AI agent to be initialized.
|
|
44
|
+
* @param {string} agentConfigFilePath - The file path to the YAML configuration for the agent.
|
|
45
|
+
* @param {Object} state - The state object for the agent.
|
|
46
|
+
* @returns {Object} An object containing the Agents AI instance, its name, description, signature, functions and subAgentList.
|
|
47
|
+
* @throws {Error} Throws an error if the agent configuration is missing, the provider is unsupported,
|
|
48
|
+
* the API key is not found, or the provider key name is not specified in the configuration.
|
|
49
|
+
*/
|
|
50
|
+
const getAgentConfigParams = (agentName, agentConfigFilePath, state) => {
|
|
51
|
+
try {
|
|
52
|
+
// Retrieve the parameters for the specified AI agent from a config file in yaml format
|
|
53
|
+
const agentConfig = parseAgentConfig(agentConfigFilePath).crew.find(agent => agent.name === agentName);
|
|
54
|
+
if (!agentConfig) {
|
|
55
|
+
throw new Error(`AI agent with name ${agentName} is not configured`);
|
|
56
|
+
}
|
|
57
|
+
// Get the constructor for the AI agent's provider
|
|
58
|
+
const AIConstructor = AIConstructors[agentConfig.provider];
|
|
59
|
+
if (!AIConstructor) {
|
|
60
|
+
throw new Error(`AI provider ${agentConfig.provider} is not supported. Did you mean '${agentConfig.provider.toLowerCase()}'?`);
|
|
61
|
+
}
|
|
62
|
+
// If an API Key property is present, get the API key for the AI agent from the environment variables
|
|
63
|
+
let apiKey = '';
|
|
64
|
+
if (agentConfig.provider_key_name) {
|
|
65
|
+
apiKey = PROVIDER_API_KEYS[agentConfig.provider_key_name] || '';
|
|
66
|
+
if (!apiKey) {
|
|
67
|
+
throw new Error(`API key for provider ${agentConfig.provider} is not set in environment variables`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
throw new Error(`Provider key name is missing in the agent configuration`);
|
|
72
|
+
}
|
|
73
|
+
// Create an instance of the AI agent
|
|
74
|
+
const ai = new AIConstructor({
|
|
75
|
+
apiKey,
|
|
76
|
+
config: agentConfig.ai,
|
|
77
|
+
options: {
|
|
78
|
+
debug: agentConfig.debug || false
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
// If an apiURL is provided in the agent config, set it in the AI agent
|
|
82
|
+
if (agentConfig.apiURL) {
|
|
83
|
+
ai.setAPIURL(agentConfig.apiURL);
|
|
84
|
+
}
|
|
85
|
+
// Set all options from the agent configuration
|
|
86
|
+
ai.setOptions({ ...agentConfig.options });
|
|
87
|
+
// Prepare functions for the AI agent
|
|
88
|
+
const agentFunctions = (agentConfig.functions || [])
|
|
89
|
+
.map(funcName => {
|
|
90
|
+
const func = functions[funcName];
|
|
91
|
+
if (!func) {
|
|
92
|
+
console.warn(`Warning: Function ${funcName} not found.`);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
return func;
|
|
96
|
+
})
|
|
97
|
+
.filter(Boolean);
|
|
98
|
+
// Return AI instance and Agent parameters
|
|
99
|
+
return {
|
|
100
|
+
ai,
|
|
101
|
+
name: agentName,
|
|
102
|
+
description: agentConfig.description,
|
|
103
|
+
signature: agentConfig.signature,
|
|
104
|
+
functions: agentFunctions,
|
|
105
|
+
subAgentNames: agentConfig.agents || []
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
console.error(error);
|
|
110
|
+
throw new Error(`Error setting up AI agent`);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
export { getAgentConfigParams };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
+
import { getAgentConfigParams } from './agentConfig';
|
|
3
|
+
import { AxAgent } from '@ax-llm/ax';
|
|
4
|
+
import { createState } from '../state/createState';
|
|
5
|
+
// Extend the AxAgent class to include shared state functionality
|
|
6
|
+
class StatefulAxAgent extends AxAgent {
|
|
7
|
+
constructor(ai, options, state) {
|
|
8
|
+
const formattedOptions = {
|
|
9
|
+
...options,
|
|
10
|
+
functions: options.functions?.map(fn => typeof fn === 'function' ? fn() : fn)
|
|
11
|
+
};
|
|
12
|
+
super(ai, formattedOptions);
|
|
13
|
+
this.state = state;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Represents a crew of agents with shared state functionality.
|
|
18
|
+
*/
|
|
19
|
+
class AgentCrew {
|
|
20
|
+
/**
|
|
21
|
+
* Creates an instance of AgentCrew.
|
|
22
|
+
* @param {string} configFilePath - The file path to the agent configuration.
|
|
23
|
+
* @param {string} [crewId=uuidv4()] - The unique identifier for the crew.
|
|
24
|
+
*/
|
|
25
|
+
constructor(configFilePath, crewId = uuidv4()) {
|
|
26
|
+
/**
|
|
27
|
+
* Factory function for creating an agent.
|
|
28
|
+
* @param {string} agentName - The name of the agent to create.
|
|
29
|
+
* @returns {StatefulAxAgent} The created StatefulAxAgent instance.
|
|
30
|
+
* @throws Will throw an error if the agent creation fails.
|
|
31
|
+
*/
|
|
32
|
+
this.createAgent = (agentName) => {
|
|
33
|
+
try {
|
|
34
|
+
const agentConfigParams = getAgentConfigParams(agentName, this.configFilePath, this.state);
|
|
35
|
+
// Destructure with type assertion
|
|
36
|
+
const { ai, name, description, signature, functions, subAgentNames } = agentConfigParams;
|
|
37
|
+
// Get subagents for the AI agent
|
|
38
|
+
const subAgents = subAgentNames.map((subAgentName) => {
|
|
39
|
+
if (!this.agents?.get(subAgentName)) {
|
|
40
|
+
throw new Error(`Sub-agent '${subAgentName}' does not exist in available agents.`);
|
|
41
|
+
}
|
|
42
|
+
return this.agents?.get(subAgentName);
|
|
43
|
+
});
|
|
44
|
+
// Create an instance of StatefulAxAgent
|
|
45
|
+
const agent = new StatefulAxAgent(ai, {
|
|
46
|
+
name,
|
|
47
|
+
description,
|
|
48
|
+
signature,
|
|
49
|
+
functions: functions.filter((fn) => fn !== undefined),
|
|
50
|
+
agents: subAgents.filter((agent) => agent !== undefined)
|
|
51
|
+
}, this.state);
|
|
52
|
+
return agent;
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
console.error(`Failed to create agent '${agentName}':`, error);
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
this.configFilePath = configFilePath;
|
|
60
|
+
this.crewId = crewId;
|
|
61
|
+
this.agents = new Map();
|
|
62
|
+
this.state = createState(crewId);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Adds an agent to the crew by name.
|
|
66
|
+
* @param {string} agentName - The name of the agent to add.
|
|
67
|
+
*/
|
|
68
|
+
addAgent(agentName) {
|
|
69
|
+
if (this.agents && !this.agents.has(agentName)) {
|
|
70
|
+
this.agents.set(agentName, this.createAgent(agentName));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Sets up agents by their names.
|
|
75
|
+
* For each name provided, it will add the agent to the crew if it's not already present.
|
|
76
|
+
* @param {string[]} agentNames - An array of agent names to set up.
|
|
77
|
+
* @returns {Map<string, StatefulAxAgent> | null} A map of agent names to their corresponding instances.
|
|
78
|
+
*/
|
|
79
|
+
setupAgents(agentNames) {
|
|
80
|
+
agentNames.forEach(agentName => {
|
|
81
|
+
this.addAgent(agentName);
|
|
82
|
+
});
|
|
83
|
+
return this.agents;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Cleans up the crew by dereferencing agents and resetting the state.
|
|
87
|
+
*/
|
|
88
|
+
destroy() {
|
|
89
|
+
this.agents = null;
|
|
90
|
+
this.state.reset();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export default AgentCrew;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
dotenv.config();
|
|
3
|
+
const DEBUG = process.env.DEBUG === 'true';
|
|
4
|
+
// Weaviate
|
|
5
|
+
const WEAVIATE_SCHEME = process.env.WEAVIATE_SCHEME || 'http';
|
|
6
|
+
const WEAVIATE_HOST = process.env.WEAVIATE_HOST || 'localhost:8080';
|
|
7
|
+
const WEAVIATE_APIKEY = process.env.WEAVIATE_APIKEY || '';
|
|
8
|
+
// AI API keys
|
|
9
|
+
const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
|
|
10
|
+
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
|
|
11
|
+
const COHERE_API_KEY = process.env.COHERE_API_KEY;
|
|
12
|
+
const GEMINI_API_KEY = process.env.GEMINI_API_KEY;
|
|
13
|
+
const PROVIDER_API_KEYS = {
|
|
14
|
+
COHERE_API_KEY,
|
|
15
|
+
GEMINI_API_KEY,
|
|
16
|
+
OPENAI_API_KEY,
|
|
17
|
+
ANTHROPIC_API_KEY,
|
|
18
|
+
};
|
|
19
|
+
// GCP
|
|
20
|
+
const GCP_PROJECT_ID = process.env.GCP_PROJECT_ID;
|
|
21
|
+
const GCP_LOCATION = process.env.GCP_LOCATION;
|
|
22
|
+
const GCP_RAG_KEYRING = process.env.GCP_RAG_KEYRING;
|
|
23
|
+
const GCP_CRYPTO_SECRET = process.env.GCP_CRYPTO_SECRET;
|
|
24
|
+
export { DEBUG, WEAVIATE_SCHEME, WEAVIATE_HOST, WEAVIATE_APIKEY, PROVIDER_API_KEYS, GCP_PROJECT_ID, GCP_LOCATION, GCP_RAG_KEYRING, GCP_CRYPTO_SECRET };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export const CurrentDateTime = {
|
|
2
|
+
name: 'CurrentDateTime',
|
|
3
|
+
description: 'Get the current date and time.',
|
|
4
|
+
parameters: {
|
|
5
|
+
type: 'object',
|
|
6
|
+
properties: {},
|
|
7
|
+
required: []
|
|
8
|
+
},
|
|
9
|
+
func: () => new Date().toISOString()
|
|
10
|
+
};
|
|
11
|
+
export const DaysBetweenDates = {
|
|
12
|
+
name: 'DaysBetweenDates',
|
|
13
|
+
description: 'Calculate the number of days between two dates in ISO format.',
|
|
14
|
+
parameters: {
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
startDate: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
description: 'The start date in ISO format.'
|
|
20
|
+
},
|
|
21
|
+
endDate: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
description: 'The end date in ISO format.'
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
required: ['startDate', 'endDate']
|
|
27
|
+
},
|
|
28
|
+
func: (args) => {
|
|
29
|
+
const { startDate, endDate } = args;
|
|
30
|
+
const start = new Date(startDate);
|
|
31
|
+
const end = new Date(endDate);
|
|
32
|
+
const differenceInTime = end.getTime() - start.getTime();
|
|
33
|
+
const differenceInDays = differenceInTime / (1000 * 3600 * 24);
|
|
34
|
+
return Math.round(differenceInDays);
|
|
35
|
+
}
|
|
36
|
+
};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const stateInstances = {};
|
|
2
|
+
const createState = (id) => {
|
|
3
|
+
if (!id) {
|
|
4
|
+
throw new Error('An ID is required to create a new state instance.');
|
|
5
|
+
}
|
|
6
|
+
if (stateInstances[id]) {
|
|
7
|
+
return stateInstances[id];
|
|
8
|
+
}
|
|
9
|
+
let data = {};
|
|
10
|
+
stateInstances[id] = {
|
|
11
|
+
set: (key, value) => {
|
|
12
|
+
data[key] = value;
|
|
13
|
+
},
|
|
14
|
+
get: (key) => {
|
|
15
|
+
return data[key];
|
|
16
|
+
},
|
|
17
|
+
getAll: () => {
|
|
18
|
+
return data;
|
|
19
|
+
},
|
|
20
|
+
getId: () => {
|
|
21
|
+
return id;
|
|
22
|
+
},
|
|
23
|
+
reset: () => {
|
|
24
|
+
data = {};
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
return stateInstances[id];
|
|
28
|
+
};
|
|
29
|
+
const getState = (id) => {
|
|
30
|
+
return stateInstances[id];
|
|
31
|
+
};
|
|
32
|
+
export { createState, getState };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if a given function is a class constructor.
|
|
3
|
+
*
|
|
4
|
+
* @param {Function} obj - The object to check.
|
|
5
|
+
* @returns {boolean} True if the object is a class constructor, false otherwise.
|
|
6
|
+
*/
|
|
7
|
+
export function isClass(obj) {
|
|
8
|
+
// A class must be a function
|
|
9
|
+
if (typeof obj !== 'function') {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
// The function must have a prototype, but not be an instance of another function
|
|
13
|
+
if (obj.prototype === undefined || obj.prototype.constructor !== obj) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
// The function's prototype must have isPrototypeOf method
|
|
17
|
+
if (!Object.prototype.hasOwnProperty.call(obj.prototype, 'isPrototypeOf')) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
// The function must not have its own 'isPrototypeOf' property
|
|
21
|
+
if (obj.hasOwnProperty('isPrototypeOf')) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
return true;
|
|
25
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "module",
|
|
3
|
+
"name": "@amitdeshmukh/ax-crew",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "Build and launch a crew of AI agents with shared state. Built with axllm.dev",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=21.0.0"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc --outDir dist",
|
|
12
|
+
"release": "npm run build && npm publish --access public"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@ax-llm/ax": "^9.0.23",
|
|
16
|
+
"dotenv": "^16.4.5",
|
|
17
|
+
"js-yaml": "^4.1.0",
|
|
18
|
+
"uuid": "^10.0.0"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/js-yaml": "^4.0.9",
|
|
23
|
+
"@types/node": "^20.14.9",
|
|
24
|
+
"@types/uuid": "^10.0.0",
|
|
25
|
+
"typescript": "^5.5.3"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import yaml from 'js-yaml';
|
|
3
|
+
import { PROVIDER_API_KEYS } from '../config/index';
|
|
4
|
+
import { functions as importedFunctions } from '../functions/index';
|
|
5
|
+
|
|
6
|
+
import { AxAIAnthropic, AxAIOpenAI, AxAIAzureOpenAI, AxAICohere, AxAIDeepSeek, AxAIGoogleGemini, AxAIGroq, AxAIHuggingFace, AxAIMistral, AxAIOllama, AxAITogether } from '@ax-llm/ax';
|
|
7
|
+
import type { AxAI, AxModelConfig, AxFunction, AxSignature } from '@ax-llm/ax';
|
|
8
|
+
|
|
9
|
+
// Define a mapping from provider names to their respective constructors
|
|
10
|
+
const AIConstructors: Record<string, any> = {
|
|
11
|
+
'anthropic': AxAIAnthropic,
|
|
12
|
+
'azure-openai': AxAIAzureOpenAI,
|
|
13
|
+
'cohere': AxAICohere,
|
|
14
|
+
'deepseek': AxAIDeepSeek,
|
|
15
|
+
'google-gemini': AxAIGoogleGemini,
|
|
16
|
+
'groq': AxAIGroq,
|
|
17
|
+
'huggingFace': AxAIHuggingFace,
|
|
18
|
+
'mistral': AxAIMistral,
|
|
19
|
+
'ollama': AxAIOllama,
|
|
20
|
+
'openai': AxAIOpenAI,
|
|
21
|
+
'together': AxAITogether
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type ExtendedAxModelConfig = AxModelConfig & {
|
|
25
|
+
model: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Define a mapping from function names to their respective handlers
|
|
29
|
+
type FunctionMap = {
|
|
30
|
+
[key: string]: AxFunction | { toFunction: () => AxFunction };
|
|
31
|
+
};
|
|
32
|
+
const functions: FunctionMap = importedFunctions;
|
|
33
|
+
|
|
34
|
+
interface AgentConfig {
|
|
35
|
+
name: string;
|
|
36
|
+
description: string;
|
|
37
|
+
signature: AxSignature;
|
|
38
|
+
provider: string;
|
|
39
|
+
provider_key_name?: string;
|
|
40
|
+
ai: ExtendedAxModelConfig;
|
|
41
|
+
debug?: boolean;
|
|
42
|
+
apiURL?: string;
|
|
43
|
+
options?: Record<string, any>;
|
|
44
|
+
functions?: string[];
|
|
45
|
+
agents?: string[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface Agent {
|
|
49
|
+
ai: AxAI;
|
|
50
|
+
name: string;
|
|
51
|
+
description: string;
|
|
52
|
+
signature?: AxSignature;
|
|
53
|
+
functions: AxFunction[];
|
|
54
|
+
subAgentNames: string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Reads the AI parameters from the YAML configuration file.
|
|
60
|
+
* @param {string} agentConfigFilePath - The path to the agent_config.yaml file.
|
|
61
|
+
* @returns {Object} The parsed agent configs from the config.yaml file.
|
|
62
|
+
* @throws Will throw an error if reading the file fails.
|
|
63
|
+
*/
|
|
64
|
+
const parseAgentConfig = (agentConfigFilePath: string): {crew: AgentConfig[]} => {
|
|
65
|
+
try {
|
|
66
|
+
const fileContents = fs.readFileSync(agentConfigFilePath, 'utf8');
|
|
67
|
+
const parsedConfigs = yaml.load(fileContents) as { crew: AgentConfig[] };
|
|
68
|
+
return parsedConfigs;
|
|
69
|
+
} catch (e) {
|
|
70
|
+
console.error('Error reading agent config file:', e);
|
|
71
|
+
throw e;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Initializes the AI agent using the specified agent name and configuration file path.
|
|
77
|
+
* This function parses the agent's configuration, validates the presence of the necessary API key,
|
|
78
|
+
* and creates an instance of the AI agent with the appropriate settings.
|
|
79
|
+
*
|
|
80
|
+
* @param {string} agentName - The identifier for the AI agent to be initialized.
|
|
81
|
+
* @param {string} agentConfigFilePath - The file path to the YAML configuration for the agent.
|
|
82
|
+
* @param {Object} state - The state object for the agent.
|
|
83
|
+
* @returns {Object} An object containing the Agents AI instance, its name, description, signature, functions and subAgentList.
|
|
84
|
+
* @throws {Error} Throws an error if the agent configuration is missing, the provider is unsupported,
|
|
85
|
+
* the API key is not found, or the provider key name is not specified in the configuration.
|
|
86
|
+
*/
|
|
87
|
+
const getAgentConfigParams = (agentName: string, agentConfigFilePath: string, state: Record<string, any>) => {
|
|
88
|
+
try{
|
|
89
|
+
// Retrieve the parameters for the specified AI agent from a config file in yaml format
|
|
90
|
+
const agentConfig = parseAgentConfig(agentConfigFilePath).crew.find(agent => agent.name === agentName);
|
|
91
|
+
if (!agentConfig) {
|
|
92
|
+
throw new Error(`AI agent with name ${agentName} is not configured`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Get the constructor for the AI agent's provider
|
|
96
|
+
const AIConstructor = AIConstructors[agentConfig.provider];
|
|
97
|
+
if (!AIConstructor) {
|
|
98
|
+
throw new Error(`AI provider ${agentConfig.provider} is not supported. Did you mean '${agentConfig.provider.toLowerCase()}'?`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// If an API Key property is present, get the API key for the AI agent from the environment variables
|
|
102
|
+
let apiKey = '';
|
|
103
|
+
if (agentConfig.provider_key_name) {
|
|
104
|
+
apiKey = PROVIDER_API_KEYS[agentConfig.provider_key_name] || '';
|
|
105
|
+
|
|
106
|
+
if (!apiKey) {
|
|
107
|
+
throw new Error(`API key for provider ${agentConfig.provider} is not set in environment variables`);
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
throw new Error(`Provider key name is missing in the agent configuration`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Create an instance of the AI agent
|
|
114
|
+
const ai = new AIConstructor({
|
|
115
|
+
apiKey,
|
|
116
|
+
config: agentConfig.ai,
|
|
117
|
+
options: {
|
|
118
|
+
debug: agentConfig.debug || false
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// If an apiURL is provided in the agent config, set it in the AI agent
|
|
123
|
+
if (agentConfig.apiURL) {
|
|
124
|
+
ai.setAPIURL(agentConfig.apiURL);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Set all options from the agent configuration
|
|
128
|
+
ai.setOptions({ ...agentConfig.options });
|
|
129
|
+
|
|
130
|
+
// Prepare functions for the AI agent
|
|
131
|
+
const agentFunctions = (agentConfig.functions || [])
|
|
132
|
+
.map(funcName => {
|
|
133
|
+
const func = functions[funcName];
|
|
134
|
+
if (!func) {
|
|
135
|
+
console.warn(`Warning: Function ${funcName} not found.`);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
return func;
|
|
139
|
+
})
|
|
140
|
+
.filter(Boolean);
|
|
141
|
+
|
|
142
|
+
// Return AI instance and Agent parameters
|
|
143
|
+
return {
|
|
144
|
+
ai,
|
|
145
|
+
name: agentName,
|
|
146
|
+
description: agentConfig.description,
|
|
147
|
+
signature: agentConfig.signature,
|
|
148
|
+
functions: agentFunctions,
|
|
149
|
+
subAgentNames: agentConfig.agents || []
|
|
150
|
+
};
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error(error);
|
|
153
|
+
throw new Error(`Error setting up AI agent`);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export { getAgentConfigParams }
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
+
import { getAgentConfigParams } from './agentConfig';
|
|
3
|
+
import { AxAgent, AxAI } from '@ax-llm/ax';
|
|
4
|
+
import type { AxSignature, AxAgentic, AxFunction, AxAIService } from '@ax-llm/ax';
|
|
5
|
+
import { createState, StateInstance } from '../state/createState';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
// Define interfaces for the agent configurations and return types
|
|
9
|
+
interface Agents {
|
|
10
|
+
[key: string]: StatefulAxAgent;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface AgentConfigParams {
|
|
14
|
+
ai: AxAI;
|
|
15
|
+
name: string;
|
|
16
|
+
description: string;
|
|
17
|
+
signature: AxSignature;
|
|
18
|
+
functions: (AxFunction | { toFunction: () => AxFunction; } | undefined)[];
|
|
19
|
+
subAgentNames: string[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Extend the AxAgent class to include shared state functionality
|
|
23
|
+
class StatefulAxAgent extends AxAgent<any, any> {
|
|
24
|
+
state: StateInstance;
|
|
25
|
+
constructor(ai: AxAI, options: Readonly<{ name: string; description: string; signature: string | AxSignature; agents?: AxAgentic[] | undefined; functions?: (AxFunction | (() => AxFunction))[] | undefined; }>, state: StateInstance) {
|
|
26
|
+
const formattedOptions = {
|
|
27
|
+
...options,
|
|
28
|
+
functions: options.functions?.map(fn => typeof fn === 'function' ? fn() : fn) as AxFunction[] | undefined
|
|
29
|
+
};
|
|
30
|
+
super(ai, formattedOptions);
|
|
31
|
+
this.state = state;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Represents a crew of agents with shared state functionality.
|
|
38
|
+
*/
|
|
39
|
+
class AgentCrew {
|
|
40
|
+
private configFilePath: string;
|
|
41
|
+
crewId: string;
|
|
42
|
+
agents: Map<string, StatefulAxAgent> | null;
|
|
43
|
+
state: StateInstance;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Creates an instance of AgentCrew.
|
|
47
|
+
* @param {string} configFilePath - The file path to the agent configuration.
|
|
48
|
+
* @param {string} [crewId=uuidv4()] - The unique identifier for the crew.
|
|
49
|
+
*/
|
|
50
|
+
constructor(configFilePath: string, crewId: string = uuidv4()) {
|
|
51
|
+
this.configFilePath = configFilePath;
|
|
52
|
+
this.crewId = crewId;
|
|
53
|
+
this.agents = new Map<string, StatefulAxAgent>();
|
|
54
|
+
this.state = createState(crewId);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Factory function for creating an agent.
|
|
59
|
+
* @param {string} agentName - The name of the agent to create.
|
|
60
|
+
* @returns {StatefulAxAgent} The created StatefulAxAgent instance.
|
|
61
|
+
* @throws Will throw an error if the agent creation fails.
|
|
62
|
+
*/
|
|
63
|
+
createAgent = (agentName: string): StatefulAxAgent => {
|
|
64
|
+
try {
|
|
65
|
+
const agentConfigParams: AgentConfigParams = getAgentConfigParams(agentName, this.configFilePath, this.state);
|
|
66
|
+
|
|
67
|
+
// Destructure with type assertion
|
|
68
|
+
const {
|
|
69
|
+
ai,
|
|
70
|
+
name,
|
|
71
|
+
description,
|
|
72
|
+
signature,
|
|
73
|
+
functions,
|
|
74
|
+
subAgentNames
|
|
75
|
+
} = agentConfigParams;
|
|
76
|
+
|
|
77
|
+
// Get subagents for the AI agent
|
|
78
|
+
const subAgents = subAgentNames.map((subAgentName: string) => {
|
|
79
|
+
if (!this.agents?.get(subAgentName)) {
|
|
80
|
+
throw new Error(`Sub-agent '${subAgentName}' does not exist in available agents.`);
|
|
81
|
+
}
|
|
82
|
+
return this.agents?.get(subAgentName);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Create an instance of StatefulAxAgent
|
|
86
|
+
const agent = new StatefulAxAgent(ai, {
|
|
87
|
+
name,
|
|
88
|
+
description,
|
|
89
|
+
signature,
|
|
90
|
+
functions: functions.filter((fn): fn is AxFunction => fn !== undefined),
|
|
91
|
+
agents: subAgents.filter((agent): agent is StatefulAxAgent => agent !== undefined)
|
|
92
|
+
}, this.state);
|
|
93
|
+
|
|
94
|
+
return agent;
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error(`Failed to create agent '${agentName}':`, error);
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Adds an agent to the crew by name.
|
|
103
|
+
* @param {string} agentName - The name of the agent to add.
|
|
104
|
+
*/
|
|
105
|
+
addAgent(agentName: string): void {
|
|
106
|
+
if (this.agents && !this.agents.has(agentName)) {
|
|
107
|
+
this.agents.set(agentName, this.createAgent(agentName));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Sets up agents by their names.
|
|
113
|
+
* For each name provided, it will add the agent to the crew if it's not already present.
|
|
114
|
+
* @param {string[]} agentNames - An array of agent names to set up.
|
|
115
|
+
* @returns {Map<string, StatefulAxAgent> | null} A map of agent names to their corresponding instances.
|
|
116
|
+
*/
|
|
117
|
+
setupAgents(agentNames: string[]): Map<string, StatefulAxAgent> | null {
|
|
118
|
+
agentNames.forEach(agentName => {
|
|
119
|
+
this.addAgent(agentName);
|
|
120
|
+
});
|
|
121
|
+
return this.agents;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Cleans up the crew by dereferencing agents and resetting the state.
|
|
126
|
+
*/
|
|
127
|
+
destroy() {
|
|
128
|
+
this.agents = null;
|
|
129
|
+
this.state.reset();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export default AgentCrew;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
dotenv.config();
|
|
3
|
+
|
|
4
|
+
const DEBUG: boolean = process.env.DEBUG === 'true';
|
|
5
|
+
|
|
6
|
+
// Weaviate
|
|
7
|
+
const WEAVIATE_SCHEME: string = process.env.WEAVIATE_SCHEME || 'http';
|
|
8
|
+
const WEAVIATE_HOST: string = process.env.WEAVIATE_HOST || 'localhost:8080';
|
|
9
|
+
const WEAVIATE_APIKEY: string = process.env.WEAVIATE_APIKEY || '';
|
|
10
|
+
|
|
11
|
+
// AI API keys
|
|
12
|
+
const ANTHROPIC_API_KEY: string | undefined = process.env.ANTHROPIC_API_KEY;
|
|
13
|
+
const OPENAI_API_KEY: string | undefined = process.env.OPENAI_API_KEY;
|
|
14
|
+
const COHERE_API_KEY: string | undefined = process.env.COHERE_API_KEY;
|
|
15
|
+
const GEMINI_API_KEY: string | undefined = process.env.GEMINI_API_KEY;
|
|
16
|
+
|
|
17
|
+
interface ProviderApiKeys {
|
|
18
|
+
[key: string]: string | undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const PROVIDER_API_KEYS: ProviderApiKeys = {
|
|
22
|
+
COHERE_API_KEY,
|
|
23
|
+
GEMINI_API_KEY,
|
|
24
|
+
OPENAI_API_KEY,
|
|
25
|
+
ANTHROPIC_API_KEY,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// GCP
|
|
29
|
+
const GCP_PROJECT_ID: string | undefined = process.env.GCP_PROJECT_ID;
|
|
30
|
+
const GCP_LOCATION: string | undefined = process.env.GCP_LOCATION;
|
|
31
|
+
const GCP_RAG_KEYRING: string | undefined = process.env.GCP_RAG_KEYRING;
|
|
32
|
+
const GCP_CRYPTO_SECRET: string | undefined = process.env.GCP_CRYPTO_SECRET;
|
|
33
|
+
|
|
34
|
+
export {
|
|
35
|
+
DEBUG,
|
|
36
|
+
WEAVIATE_SCHEME,
|
|
37
|
+
WEAVIATE_HOST,
|
|
38
|
+
WEAVIATE_APIKEY,
|
|
39
|
+
PROVIDER_API_KEYS,
|
|
40
|
+
GCP_PROJECT_ID,
|
|
41
|
+
GCP_LOCATION,
|
|
42
|
+
GCP_RAG_KEYRING,
|
|
43
|
+
GCP_CRYPTO_SECRET
|
|
44
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { AxFunction } from "@ax-llm/ax";
|
|
2
|
+
|
|
3
|
+
export const CurrentDateTime: AxFunction = {
|
|
4
|
+
name: 'CurrentDateTime',
|
|
5
|
+
description: 'Get the current date and time.',
|
|
6
|
+
parameters: {
|
|
7
|
+
type: 'object',
|
|
8
|
+
properties: {},
|
|
9
|
+
required: []
|
|
10
|
+
},
|
|
11
|
+
func: () => new Date().toISOString()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const DaysBetweenDates: AxFunction = {
|
|
15
|
+
name: 'DaysBetweenDates',
|
|
16
|
+
description: 'Calculate the number of days between two dates in ISO format.',
|
|
17
|
+
parameters: {
|
|
18
|
+
type: 'object',
|
|
19
|
+
properties: {
|
|
20
|
+
startDate: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
description: 'The start date in ISO format.'
|
|
23
|
+
},
|
|
24
|
+
endDate: {
|
|
25
|
+
type: 'string',
|
|
26
|
+
description: 'The end date in ISO format.'
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
required: ['startDate', 'endDate']
|
|
30
|
+
},
|
|
31
|
+
func: (args: { startDate: string; endDate: string }) => {
|
|
32
|
+
const { startDate, endDate } = args;
|
|
33
|
+
const start = new Date(startDate);
|
|
34
|
+
const end = new Date(endDate);
|
|
35
|
+
const differenceInTime = end.getTime() - start.getTime();
|
|
36
|
+
const differenceInDays = differenceInTime / (1000 * 3600 * 24);
|
|
37
|
+
return Math.round(differenceInDays);
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
interface StateInstance {
|
|
2
|
+
set: (key: string, value: any) => void;
|
|
3
|
+
get: (key: string) => any;
|
|
4
|
+
getAll: () => { [key: string]: any };
|
|
5
|
+
getId: () => string;
|
|
6
|
+
reset: () => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const stateInstances: { [id: string]: StateInstance } = {};
|
|
10
|
+
|
|
11
|
+
const createState = (id: string): StateInstance => {
|
|
12
|
+
if (!id) {
|
|
13
|
+
throw new Error('An ID is required to create a new state instance.');
|
|
14
|
+
}
|
|
15
|
+
if (stateInstances[id]) {
|
|
16
|
+
return stateInstances[id];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let data: { [key: string]: any } = {};
|
|
20
|
+
|
|
21
|
+
stateInstances[id] = {
|
|
22
|
+
set: (key, value) => {
|
|
23
|
+
data[key] = value;
|
|
24
|
+
},
|
|
25
|
+
get: (key) => {
|
|
26
|
+
return data[key];
|
|
27
|
+
},
|
|
28
|
+
getAll: () => {
|
|
29
|
+
return data;
|
|
30
|
+
},
|
|
31
|
+
getId: () => {
|
|
32
|
+
return id;
|
|
33
|
+
},
|
|
34
|
+
reset: () => {
|
|
35
|
+
data = {};
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return stateInstances[id];
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const getState = (id: string): StateInstance | undefined => {
|
|
43
|
+
return stateInstances[id];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export { createState, getState, StateInstance };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { AxFunction } from "@ax-llm/ax";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Checks if a given function is a class constructor.
|
|
5
|
+
*
|
|
6
|
+
* @param {Function} obj - The object to check.
|
|
7
|
+
* @returns {boolean} True if the object is a class constructor, false otherwise.
|
|
8
|
+
*/
|
|
9
|
+
export function isClass(obj: AxFunction | ClassDecorator): boolean {
|
|
10
|
+
// A class must be a function
|
|
11
|
+
if (typeof obj !== 'function') {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
// The function must have a prototype, but not be an instance of another function
|
|
15
|
+
if (obj.prototype === undefined || obj.prototype.constructor !== obj) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
// The function's prototype must have isPrototypeOf method
|
|
19
|
+
if (!Object.prototype.hasOwnProperty.call(obj.prototype, 'isPrototypeOf')) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
// The function must not have its own 'isPrototypeOf' property
|
|
23
|
+
if (obj.hasOwnProperty('isPrototypeOf')) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
return true;
|
|
27
|
+
}
|
package/test.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Import the AgentCrew class
|
|
2
|
+
import AgentCrew from './dist/agents/index.js';
|
|
3
|
+
|
|
4
|
+
// Create a new instance of AgentCrew
|
|
5
|
+
const configFilePath = './agent_config.example.yaml';
|
|
6
|
+
const crew = new AgentCrew(configFilePath);
|
|
7
|
+
|
|
8
|
+
// Add agents by providing their names
|
|
9
|
+
const agentNames = ['Planner', 'Calculator', 'Manager'];
|
|
10
|
+
const agents = crew.addAgentsToCrew(agentNames);
|
|
11
|
+
|
|
12
|
+
crew.state.set('clientId', '0906-978189-7350');
|
|
13
|
+
console.log(`Set ClientId: ${crew.state.get('clientId')}`);
|
|
14
|
+
|
|
15
|
+
const ManagerAgent = agents.Manager;
|
|
16
|
+
const PlannerAgent = agents['Planner'];
|
|
17
|
+
|
|
18
|
+
console.log(crew.state.getAll())
|
|
19
|
+
PlannerAgent.state.set('plan', 'plan 1 2 3')
|
|
20
|
+
console.log(ManagerAgent.state.getAll())
|
|
21
|
+
|
|
22
|
+
const userQuery = "i referred a friend to active and they were hired and started work on 1st july. But i did not receive my referral bonus. what amount should i have received?";
|
|
23
|
+
|
|
24
|
+
console.log(`\n\nQuestion: ${userQuery}`);
|
|
25
|
+
|
|
26
|
+
const planResponse = await PlannerAgent.forward({ task: userQuery });
|
|
27
|
+
const managerResponse = await ManagerAgent.forward({ question: userQuery, plan: planResponse.plan });
|
|
28
|
+
|
|
29
|
+
const plan = planResponse.plan;
|
|
30
|
+
const answer = managerResponse.answer;
|
|
31
|
+
const reason = managerResponse.reason;
|
|
32
|
+
|
|
33
|
+
console.log(`\n\nPlan: ${plan}`);
|
|
34
|
+
console.log(`\n\nAnswer: ${answer}`);
|
|
35
|
+
|
|
36
|
+
console.log(crew.state.getAll());
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "ESNext",
|
|
4
|
+
"target": "ES2020",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"allowSyntheticDefaultImports": true,
|
|
8
|
+
"strict": true,
|
|
9
|
+
"outDir": "./dist",
|
|
10
|
+
"rootDir": "./src",
|
|
11
|
+
"allowJs": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"typeRoots": ["./node_modules/@types", "./types"]
|
|
14
|
+
},
|
|
15
|
+
"include": ["src/**/*"],
|
|
16
|
+
"exclude": ["node_modules", "**/*.spec.ts"]
|
|
17
|
+
}
|