Skip to content

Commit d85bc1f

Browse files
committed
feat: Add agents, an example on how to use the agents
1 parent f5dc916 commit d85bc1f

File tree

9 files changed

+2525
-139
lines changed

9 files changed

+2525
-139
lines changed

examples/weatherAgent/package.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "@neuron.js/examples-weatheragent",
3+
"type": "module",
4+
"scripts": {
5+
"dev": "tsx src/index.ts"
6+
},
7+
"dependencies": {
8+
"@neuron.js/core": "workspace:*",
9+
"axios": "^1.7.9",
10+
"dotenv": "^16.4.7"
11+
},
12+
"devDependencies": {
13+
"@types/node": "^22.10.1",
14+
"tsx": "^4.19.2"
15+
}
16+
}

examples/weatherAgent/src/index.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { Agent, Tool } from '@neuron.js/core';
2+
import 'dotenv/config'
3+
4+
const weatherTool = new Tool(
5+
'weather_gov_query',
6+
'Fetches real-time weather data from an weather.gov.',
7+
{
8+
properties: {
9+
latitude: {
10+
type: 'string',
11+
description: 'latitude of the location where weather data is required',
12+
required: true
13+
},
14+
longitude: {
15+
type: 'string',
16+
description: 'latitude of the location where weather data is required',
17+
required: true
18+
},
19+
locationName: {
20+
type: 'string',
21+
description: 'name of the location where weather data is required',
22+
required: true
23+
}
24+
},
25+
},
26+
async (inputs, secrets) => {
27+
try {
28+
const response = await fetch(
29+
`https://api.weather.gov/points/${inputs.latitude},${inputs.longitude}`
30+
);
31+
const data = await response.json();
32+
const forecastResponse = await fetch(data.properties.forecast);
33+
const forecastData = await forecastResponse.json();
34+
return `Weather forecast in ${inputs.locationName} is ${forecastData.properties.periods[0].detailedForecast}`;
35+
} catch (error) {
36+
console.error('Error:', error);
37+
throw error;
38+
}
39+
}
40+
)
41+
42+
43+
const WeatherAgent = new Agent(
44+
'WeatherAgent',
45+
{
46+
persona: 'You are a cheerful and approachable virtual assistant dedicated to delivering accurate, concise, and engaging weather updates. Your tone is warm, lively, and always focused on making weather information easy to understand and fun to receive.',
47+
goal: 'Provide the current weather for a specified location as soon as the city or location details are provided. Your response should be both informative and conversational, ensuring clarity and usefulness for the user.',
48+
secrets: {
49+
OPEN_WEATHER_API_KEY: process.env.OPEN_WEATHER_API_KEY || '',
50+
OPENAI_API_KEY: process.env.OPENAI_API_KEY || ''
51+
}
52+
}
53+
)
54+
55+
WeatherAgent.registerTool(weatherTool)
56+
57+
const result = await WeatherAgent.execute("I'm travelling to Tahoe, what is the weather there?")
58+
console.log("RESULT:", result);

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
"format": "prettier --write \"**/*.{ts,tsx,md}\""
1010
},
1111
"devDependencies": {
12+
"@parcel/packager-ts": "2.13.2",
13+
"@parcel/transformer-typescript-types": "2.13.2",
14+
"@types/node": "^22.10.1",
1215
"prettier": "^3.2.5",
1316
"turbo": "^2.3.3",
1417
"typescript": "5.5.4"

packages/core/package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
{
22
"name": "@neuron.js/core",
33
"version": "0.0.0",
4-
"main": "index.js",
4+
"type": "module",
5+
"private": "true",
6+
"source": "src/index.ts",
7+
"main": "dist/main.mjs",
8+
"types": "dist/types.d.ts",
59
"scripts": {
10+
"build": "parcel build --no-cache",
611
"test": "vitest --no-watch --no-cache"
712
},
813
"devDependencies": {
14+
"parcel": "^2.13.2",
915
"vitest": "^2.1.8"
1016
},
1117
"dependencies": {

packages/core/src/agent/index.ts

Lines changed: 51 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import { Tool } from "../tool";
44
import { AgentConfig, AgentResponse, LLMResult, Message, Provider, ToolConfig } from "../types";
55

66
export class Agent {
7-
public name: string;
8-
public prompt: string;
9-
public messages: Message[];
10-
public maxIterations: number;
7+
private name: string;
8+
private prompt: string;
9+
private messages: Message[];
10+
private maxIterations: number;
1111
private tools: Tool[];
1212
private llm: any;
1313
private logger: any;
@@ -23,7 +23,7 @@ export class Agent {
2323
this.maxIterations = config.maxIterations || 10;
2424
this.secrets = config.secrets;
2525
this.logger = config.logger || console;
26-
this.logger.info(this.prompt);
26+
this.logger.debug(this.prompt);
2727

2828
this.llm = new LLM({ provider: this.provider, apiKey: config.secrets.OPENAI_API_KEY, logger: this.logger });
2929
}
@@ -40,11 +40,16 @@ export class Agent {
4040
this.pushToMessages({ role: "system", content: "Provide a final answer" });
4141
}
4242

43-
result = await this.llm.call(this.messages, this.functions());
43+
try {
44+
result = await this.llm.call(this.messages, this.functions());
4445

45-
await this.handleLlmResult(result);
46+
await this.handleLlmResult(result);
47+
48+
if (result.content?.stop) break;
49+
} catch (error) {
50+
this.pushToMessages({ role: "assistant", content: `There was an error ${error.message}` });
51+
}
4652

47-
if (result.content?.stop) break;
4853
iterationCount++;
4954
}
5055

@@ -85,19 +90,22 @@ export class Agent {
8590
return `
8691
Persona: ${config.persona}
8792
Objective: ${config.goal}
93+
8894
Guidelines:
89-
- Work diligently until the stated objective is achieved.
90-
- Utilize only the provided tools for solving the task. Do not make up names of the functions
91-
- Set 'stop: true' when the objective is complete.
92-
- If you have enough information to provide the details to the user, prepare a final result collecting all the information you have.
93-
Output Structure:
94-
If you find a function, that can be used, directly call the function.
95-
When providing the final answer:
96-
{
97-
'thoughtProcess': 'Describe the reasoning and steps that will lead to the final result.',
98-
'output': 'The complete answer in text form.',
99-
'stop': true
100-
}
95+
1. Make sure that you break the task into logical steps and execute methodically until the objective is achieved. Do not attempt to solve before breaking it into steps.
96+
2. Provide the breakdown steps as thought process in the first step.
97+
3. Use only the provided tools, avoiding unnecessary or improvised function calls. If a tool can assist in solving the task, do not invoke it immediately rather provide reasoning first and then invoke the tool.
98+
4. Include thoughtProcess during intermediate steps, but omit it from the final answer.
99+
5. Mark the completion of the task by setting 'stop': true.
100+
6. Ensure outputs are structured in the specified JSON format:
101+
- thoughtProcess: A concise explanation of reasoning and next steps (only for intermediate responses).
102+
- output: The complete, user-friendly answer (final response only).
103+
- stop: Boolean indicator of task status (false for intermediate, true for final).
104+
105+
106+
Make sure that the format is followed properly:
107+
While processing: {thoughtProcess: '<reasoning>', stop: false}
108+
Upon completion: {output: '<final result>', stop: true}
101109
`;
102110
}
103111

@@ -108,13 +116,17 @@ export class Agent {
108116
}
109117

110118
private functions(): Array<{
111-
name: string;
112-
description: string;
113-
parameters: {
114-
type: string;
115-
properties: Record<string, { type: string; description: string }>;
116-
required: string[];
117-
};
119+
type: string,
120+
function:{
121+
name: string;
122+
description: string;
123+
parameters: {
124+
type: string;
125+
properties: Record<string, { type: string; description: string }>;
126+
required: string[];
127+
};
128+
}
129+
118130
}> {
119131
return this.tools.map(tool => {
120132
const properties: Record<string, { type: string; description: string }> = {};
@@ -131,19 +143,22 @@ export class Agent {
131143
.map(([name]) => name);
132144

133145
return {
134-
name: tool.name,
135-
description: tool.description,
136-
parameters: {
137-
type: "object",
138-
properties,
139-
required
146+
type: 'function',
147+
function: {
148+
name: tool.name,
149+
description: tool.description,
150+
parameters: {
151+
type: "object",
152+
properties,
153+
required
154+
}
140155
}
141156
};
142157
});
143158
}
144159

145160
private pushToMessages(message: Message): void {
146-
this.logger.info(`Message: ${JSON.stringify(message)}`);
161+
this.logger.debug(`Message: ${JSON.stringify(message)}`);
147162
this.messages.push(message);
148163
}
149164

@@ -156,9 +171,10 @@ export class Agent {
156171
return { output: "Invalid tool_name, please try again", stop: false };
157172
}
158173

159-
this.logger.info(
174+
this.logger.debug(
160175
`tool_call: ${toolCall.function.name}, ${toolCall.function.arguments}`,
161176
);
177+
this.pushToMessages({ role: "assistant", content: `Used the tool ${toolCall.function.name}` });
162178

163179
const functionArgs = JSON.parse(toolCall.function.arguments);
164180

packages/core/src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export * from './agent';
2+
export * from './tool';
3+
export * from './llm';
4+
export * from './errors';
5+
export * from './types';

packages/core/src/tool/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export class Tool {
1111
properties: Record<string, FunctionInput>,
1212
secrets?: string[],
1313
},
14-
private implementation?: (input: Record<string, unknown>, secrets: Record<string, unknown>) => void
14+
private implementation?: (input: Record<string, any>, secrets: Record<string, any>) => void
1515
) {
1616
this.validateConfig();
1717
}
@@ -20,7 +20,7 @@ export class Tool {
2020
this.implementation = implementation;
2121
}
2222

23-
execute(input: Record<string, unknown>, providedSecrets: Record<string, string> = {}): any {
23+
execute(input: Record<string, any>, providedSecrets: Record<string, string> = {}): any {
2424
this.validateSecrets(providedSecrets);
2525
this.validateInput(input);
2626

packages/core/src/types/index.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,10 @@ export interface AgentConfig {
4848
messages?: Message[];
4949
maxIterations?: number;
5050
persona?: string;
51-
provider: Provider;
51+
provider?: Provider;
5252
goal?: string;
53-
secrets: {
54-
OPENAI_API_KEY: string;
55-
};
56-
logger: Console
53+
secrets: Record<string, string>;
54+
logger?: Console
5755
}
5856

5957
export interface Message {

0 commit comments

Comments
 (0)