agentnet 0.1.19 → 0.1.30
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/README.md +276 -0
- package/docs/direct-agent-usage.md +200 -0
- package/examples/direct-agent-usage.js +114 -0
- package/examples/previous-conversation-example.js +77 -0
- package/package.json +1 -1
- package/src/agent/agent.js +117 -0
- package/src/agent/executor.js +26 -12
- package/src/agent/runtime.js +7 -0
- package/src/index.js +14 -3
- package/src/llm/base.js +14 -0
- package/src/llm/gemini.js +9 -2
- package/src/llm/gpt.js +10 -2
- package/src/store/store.js +16 -0
- package/test-tools-debug.js +78 -0
package/README.md
CHANGED
|
@@ -13,6 +13,282 @@
|
|
|
13
13
|
- [Direct Access to Agent Fluent Interface](#direct-access-to-agent-fluent-interface)
|
|
14
14
|
- [Examples](#examples)
|
|
15
15
|
|
|
16
|
+
## Basic usage
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
import { Agent, LLMRuntime } from '/path/to/agentnet/src/index.js'
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Core Agent Building Pattern
|
|
23
|
+
|
|
24
|
+
AgentNet uses a fluent/builder pattern for creating agents:
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
const agent = await Agent()
|
|
28
|
+
.setMetadata(metadata)
|
|
29
|
+
.withStore(store, initialState)
|
|
30
|
+
.withLLM(runtime, config)
|
|
31
|
+
.withRunner(runnerConfig)
|
|
32
|
+
.prompt(promptFunction)
|
|
33
|
+
.addTool(toolDefinition, handlerFunction) // optional, can add multiple
|
|
34
|
+
.compile()
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Agent Configuration Methods
|
|
38
|
+
|
|
39
|
+
### `.setMetadata(metadata)`
|
|
40
|
+
Sets agent metadata for identification and description.
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
.setMetadata({
|
|
44
|
+
name: "agent_name",
|
|
45
|
+
description: "Agent description"
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### `.withStore(store, initialState)`
|
|
50
|
+
Configures the agent's state storage.
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
.withStore(store, {}) // Empty initial state
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### `.withLLM(runtime, config)`
|
|
57
|
+
Configures the Language Model runtime and settings.
|
|
58
|
+
|
|
59
|
+
**For GPT:**
|
|
60
|
+
```javascript
|
|
61
|
+
.withLLM(LLMRuntime.GPT, {
|
|
62
|
+
model: "gpt-4-turbo-preview",
|
|
63
|
+
instructions: "System instructions here",
|
|
64
|
+
temperature: 0,
|
|
65
|
+
tool_choice: "auto" | "required" | "none",
|
|
66
|
+
text: {
|
|
67
|
+
format: {
|
|
68
|
+
type: "json_schema",
|
|
69
|
+
name: "schema_name",
|
|
70
|
+
schema: { /* JSON Schema object */ }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**For Gemini:**
|
|
77
|
+
```javascript
|
|
78
|
+
.withLLM(LLMRuntime.GEMINI, {
|
|
79
|
+
model: "gemini-2.0-flash-exp",
|
|
80
|
+
systemInstruction: "System instructions here",
|
|
81
|
+
config: {
|
|
82
|
+
temperature: 0.0,
|
|
83
|
+
responseMimeType: "application/json", // For JSON responses
|
|
84
|
+
responseSchema: { /* JSON Schema */ },
|
|
85
|
+
toolConfig: {
|
|
86
|
+
functionCallingConfig: {
|
|
87
|
+
mode: "AUTO" | "ANY" | "NONE"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### `.withRunner(config)`
|
|
95
|
+
Configures execution parameters.
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
.withRunner({
|
|
99
|
+
maxRuns: 5, // Maximum number of execution runs
|
|
100
|
+
maxConversationLength: 200 // Maximum conversation length
|
|
101
|
+
})
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### `.prompt(function)`
|
|
105
|
+
Defines the prompt generation function.
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
.prompt(async (state, input) => {
|
|
109
|
+
// state: Current agent state
|
|
110
|
+
// input: Input message/data
|
|
111
|
+
return `Formatted prompt with ${input} and ${state.someValue}`
|
|
112
|
+
})
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### `.addTool(definition, handler)`
|
|
116
|
+
Adds tools/functions the agent can call.
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
.addTool(
|
|
120
|
+
{
|
|
121
|
+
name: "tool_name",
|
|
122
|
+
description: "Tool description",
|
|
123
|
+
mode: "stopAfterOneExecution", // Optional mode
|
|
124
|
+
parameters: {
|
|
125
|
+
type: "object",
|
|
126
|
+
properties: {
|
|
127
|
+
param1: { type: "string", description: "Parameter description" }
|
|
128
|
+
},
|
|
129
|
+
required: ["param1"]
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
async (state, args) => {
|
|
133
|
+
// Handler function
|
|
134
|
+
// args contains the parameters passed by the LLM
|
|
135
|
+
return "Tool execution result"
|
|
136
|
+
}
|
|
137
|
+
)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### `.compile()`
|
|
141
|
+
Finalizes and compiles the agent configuration.
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
const compiledAgent = await agent.compile()
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Using Compiled Agents
|
|
148
|
+
|
|
149
|
+
### Query Execution
|
|
150
|
+
```javascript
|
|
151
|
+
const response = await compiledAgent.query(inputMessage)
|
|
152
|
+
const content = response.getContent()
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### LLM Runtime Enums
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
LLMRuntime.GPT // For OpenAI GPT models
|
|
159
|
+
LLMRuntime.GEMINI // For Google Gemini models
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### JSON Schema Response Format
|
|
163
|
+
|
|
164
|
+
### Structured Output Configuration
|
|
165
|
+
|
|
166
|
+
For enforcing JSON responses with specific schemas:
|
|
167
|
+
|
|
168
|
+
**GPT Configuration:**
|
|
169
|
+
```javascript
|
|
170
|
+
text: {
|
|
171
|
+
format: {
|
|
172
|
+
type: "json_schema",
|
|
173
|
+
name: "response_name",
|
|
174
|
+
schema: {
|
|
175
|
+
type: "object",
|
|
176
|
+
properties: {
|
|
177
|
+
field1: { type: "string", description: "..." },
|
|
178
|
+
field2: { type: "number", description: "..." }
|
|
179
|
+
},
|
|
180
|
+
required: ["field1", "field2"],
|
|
181
|
+
additionalProperties: false
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Gemini Configuration:**
|
|
188
|
+
```javascript
|
|
189
|
+
config: {
|
|
190
|
+
responseMimeType: "application/json",
|
|
191
|
+
responseSchema: {
|
|
192
|
+
type: "object",
|
|
193
|
+
properties: {
|
|
194
|
+
field1: { type: "string", description: "..." },
|
|
195
|
+
field2: { type: "number", description: "..." }
|
|
196
|
+
},
|
|
197
|
+
required: ["field1", "field2"]
|
|
198
|
+
// Note: Gemini doesn't support additionalProperties
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Tool Choice Configuration
|
|
204
|
+
|
|
205
|
+
### GPT Tool Choice Options
|
|
206
|
+
- `"auto"` - Model decides whether to call tools
|
|
207
|
+
- `"required"` - Model must call at least one tool
|
|
208
|
+
- `"none"` - Model cannot call tools
|
|
209
|
+
|
|
210
|
+
### Gemini Tool Choice (via functionCallingConfig)
|
|
211
|
+
- `"AUTO"` - Model decides whether to call tools
|
|
212
|
+
- `"ANY"` - Model must call at least one tool
|
|
213
|
+
- `"NONE"` - Model cannot call tools
|
|
214
|
+
|
|
215
|
+
### Special Tool Mode
|
|
216
|
+
|
|
217
|
+
```javascript
|
|
218
|
+
{
|
|
219
|
+
name: "tool_name",
|
|
220
|
+
mode: "stopAfterOneExecution", // Stops after single execution
|
|
221
|
+
// ... rest of tool definition
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### State Management
|
|
226
|
+
|
|
227
|
+
The state object passed to prompt functions and tool handlers maintains conversation context and can store custom data between agent interactions.
|
|
228
|
+
|
|
229
|
+
```javascript
|
|
230
|
+
.prompt(async (state, input) => {
|
|
231
|
+
// Access state properties
|
|
232
|
+
console.log(state.chatHistory)
|
|
233
|
+
console.log(state.customProperty)
|
|
234
|
+
return promptString
|
|
235
|
+
})
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Error Handling & Response Access
|
|
239
|
+
|
|
240
|
+
```javascript
|
|
241
|
+
const response = await agent.query(message)
|
|
242
|
+
const content = response.getContent() // Extract response content
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Complete Example
|
|
246
|
+
|
|
247
|
+
```javascript
|
|
248
|
+
import { Agent, LLMRuntime } from '/path/to/agentnet/index.js'
|
|
249
|
+
|
|
250
|
+
const agent = await Agent()
|
|
251
|
+
.setMetadata({
|
|
252
|
+
name: "example_agent",
|
|
253
|
+
description: "An example agent"
|
|
254
|
+
})
|
|
255
|
+
.withStore(store, {})
|
|
256
|
+
.withLLM(LLMRuntime.GPT, {
|
|
257
|
+
model: "gpt-4-turbo-preview",
|
|
258
|
+
instructions: "You are a helpful assistant",
|
|
259
|
+
temperature: 0,
|
|
260
|
+
tool_choice: "auto"
|
|
261
|
+
})
|
|
262
|
+
.withRunner({
|
|
263
|
+
maxRuns: 3,
|
|
264
|
+
maxConversationLength: 100
|
|
265
|
+
})
|
|
266
|
+
.prompt(async (state, input) => {
|
|
267
|
+
return `Process this request: ${input}`
|
|
268
|
+
})
|
|
269
|
+
.addTool(
|
|
270
|
+
{
|
|
271
|
+
name: "search",
|
|
272
|
+
description: "Search for information",
|
|
273
|
+
parameters: {
|
|
274
|
+
type: "object",
|
|
275
|
+
properties: {
|
|
276
|
+
query: { type: "string" }
|
|
277
|
+
},
|
|
278
|
+
required: ["query"]
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
async (state, args) => {
|
|
282
|
+
return `Search results for: ${args.query}`
|
|
283
|
+
}
|
|
284
|
+
)
|
|
285
|
+
.compile()
|
|
286
|
+
|
|
287
|
+
// Use the agent
|
|
288
|
+
const response = await agent.query("Find information about hotels")
|
|
289
|
+
console.log(response.getContent())
|
|
290
|
+
```
|
|
291
|
+
|
|
16
292
|
|
|
17
293
|
## Introduction
|
|
18
294
|
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Direct Agent Usage
|
|
2
|
+
|
|
3
|
+
This document describes how to create agents directly using the Agent interface without using the AgentLoader.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The Agent interface now provides all the necessary methods to create and configure agents programmatically without requiring YAML definitions or the AgentLoader. This gives you more flexibility and control when creating agents dynamically.
|
|
8
|
+
|
|
9
|
+
## New Methods Added
|
|
10
|
+
|
|
11
|
+
The following methods have been added to the Agent interface to enable direct agent creation:
|
|
12
|
+
|
|
13
|
+
### Tool Management
|
|
14
|
+
|
|
15
|
+
- **`addTool(toolDefinition, handlerFunction)`**: Adds a tool with its schema and optional handler function
|
|
16
|
+
- `toolDefinition`: Object containing name, description, type, and parameters
|
|
17
|
+
- `handlerFunction`: Optional function to handle tool execution
|
|
18
|
+
|
|
19
|
+
- **`bindTool(toolName, handlerFunction)`**: Binds a handler function to an existing tool
|
|
20
|
+
- `toolName`: Name of the previously added tool
|
|
21
|
+
- `handlerFunction`: Function to handle tool execution
|
|
22
|
+
|
|
23
|
+
### Event Handlers
|
|
24
|
+
|
|
25
|
+
- **`prompt(handler)`**: Sets the prompt preprocessing handler
|
|
26
|
+
- `handler`: Function that receives (state, formattedInput) and returns modified input
|
|
27
|
+
|
|
28
|
+
- **`response(handler)`**: Sets the response postprocessing handler
|
|
29
|
+
- `handler`: Function that receives (state, conversation, result) and returns modified result
|
|
30
|
+
|
|
31
|
+
### Configuration
|
|
32
|
+
|
|
33
|
+
- **`withRunner(runnerConfig)`**: Configures runner settings
|
|
34
|
+
- `runnerConfig.maxRuns`: Maximum number of runs (default: 10)
|
|
35
|
+
- `runnerConfig.maxConversationLength`: Maximum conversation length (default: 10)
|
|
36
|
+
|
|
37
|
+
## Example Usage
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
import { Agent, GPT, Message, MemoryStore } from "agentnet"
|
|
41
|
+
|
|
42
|
+
// Create an agent directly without AgentLoader
|
|
43
|
+
const agent = Agent()
|
|
44
|
+
.setMetadata({
|
|
45
|
+
name: "myAgent",
|
|
46
|
+
namespace: "myNamespace",
|
|
47
|
+
description: "My custom agent"
|
|
48
|
+
})
|
|
49
|
+
.withLLM(GPT, {
|
|
50
|
+
model: "gpt-4o-mini",
|
|
51
|
+
instructions: "You are a helpful assistant."
|
|
52
|
+
})
|
|
53
|
+
.withStore(MemoryStore(), {
|
|
54
|
+
type: "Memory"
|
|
55
|
+
})
|
|
56
|
+
.withRunner({
|
|
57
|
+
maxRuns: 10,
|
|
58
|
+
maxConversationLength: 10
|
|
59
|
+
})
|
|
60
|
+
.addTool({
|
|
61
|
+
name: "my_tool",
|
|
62
|
+
description: "Does something useful",
|
|
63
|
+
type: "function",
|
|
64
|
+
parameters: {
|
|
65
|
+
type: "object",
|
|
66
|
+
properties: {
|
|
67
|
+
input: {
|
|
68
|
+
type: "string",
|
|
69
|
+
description: "The input parameter"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
required: ["input"]
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
.bindTool("my_tool", async (state, input) => {
|
|
76
|
+
// Tool implementation
|
|
77
|
+
return { result: `Processed: ${input.input}` }
|
|
78
|
+
})
|
|
79
|
+
.prompt(async (state, formattedInput) => {
|
|
80
|
+
// Optional: Preprocess the prompt
|
|
81
|
+
console.log("Prompt:", formattedInput)
|
|
82
|
+
return formattedInput
|
|
83
|
+
})
|
|
84
|
+
.response(async (state, conversation, result) => {
|
|
85
|
+
// Optional: Postprocess the response
|
|
86
|
+
console.log("Response:", result)
|
|
87
|
+
return result
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
// Compile the agent
|
|
91
|
+
const agentInstance = await agent.compile()
|
|
92
|
+
|
|
93
|
+
// Use the agent
|
|
94
|
+
const message = new Message("Hello, can you help me?")
|
|
95
|
+
const result = await agentInstance.query(message)
|
|
96
|
+
console.log(result.getContent())
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Method Chaining
|
|
100
|
+
|
|
101
|
+
All builder methods return the agent instance, allowing for fluent method chaining:
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
const agent = Agent()
|
|
105
|
+
.setMetadata({ name: "agent1", namespace: "ns1" })
|
|
106
|
+
.withLLM(GPT, { model: "gpt-4" })
|
|
107
|
+
.addTool(tool1)
|
|
108
|
+
.addTool(tool2)
|
|
109
|
+
.bindTool("tool1", handler1)
|
|
110
|
+
.bindTool("tool2", handler2)
|
|
111
|
+
.compile()
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Adding Tools with Handlers
|
|
115
|
+
|
|
116
|
+
You can add tools in two ways:
|
|
117
|
+
|
|
118
|
+
### 1. Add tool with handler in one call
|
|
119
|
+
```javascript
|
|
120
|
+
.addTool({
|
|
121
|
+
name: "get_weather",
|
|
122
|
+
description: "Gets weather information",
|
|
123
|
+
type: "function",
|
|
124
|
+
parameters: { /* ... */ }
|
|
125
|
+
}, async (state, input) => {
|
|
126
|
+
// Handler implementation
|
|
127
|
+
return { weather: "sunny" }
|
|
128
|
+
})
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### 2. Add tool first, bind handler later
|
|
132
|
+
```javascript
|
|
133
|
+
.addTool({
|
|
134
|
+
name: "get_weather",
|
|
135
|
+
description: "Gets weather information",
|
|
136
|
+
type: "function",
|
|
137
|
+
parameters: { /* ... */ }
|
|
138
|
+
})
|
|
139
|
+
.bindTool("get_weather", async (state, input) => {
|
|
140
|
+
// Handler implementation
|
|
141
|
+
return { weather: "sunny" }
|
|
142
|
+
})
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Comparison with AgentLoader
|
|
146
|
+
|
|
147
|
+
### Using AgentLoader (YAML-based)
|
|
148
|
+
```javascript
|
|
149
|
+
const agents = await AgentLoaderFile("agents.yaml", { bindings })
|
|
150
|
+
agents.myAgent.tools.my_tool.bind(handler)
|
|
151
|
+
const instance = await agents.myAgent.compile()
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Using Direct Agent Interface
|
|
155
|
+
```javascript
|
|
156
|
+
const agent = Agent()
|
|
157
|
+
.setMetadata({ name: "myAgent" })
|
|
158
|
+
.withLLM(GPT, config)
|
|
159
|
+
.addTool(toolDef, handler)
|
|
160
|
+
const instance = await agent.compile()
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Benefits of Direct Usage
|
|
164
|
+
|
|
165
|
+
1. **No YAML files required**: Create agents entirely in code
|
|
166
|
+
2. **Dynamic configuration**: Build agents based on runtime conditions
|
|
167
|
+
3. **Type safety**: Better IDE support and type checking
|
|
168
|
+
4. **Simpler testing**: Easier to unit test agent creation
|
|
169
|
+
5. **Flexibility**: Mix and match configurations programmatically
|
|
170
|
+
|
|
171
|
+
## Migration from AgentLoader
|
|
172
|
+
|
|
173
|
+
If you have existing YAML-based agents, you can migrate them to direct usage:
|
|
174
|
+
|
|
175
|
+
1. Extract the agent configuration from YAML
|
|
176
|
+
2. Convert each spec section to the corresponding method call
|
|
177
|
+
3. Add tools using `addTool()` instead of YAML tool definitions
|
|
178
|
+
4. Bind tool handlers using `bindTool()` or pass them directly to `addTool()`
|
|
179
|
+
|
|
180
|
+
## Error Handling
|
|
181
|
+
|
|
182
|
+
All methods include validation and will throw `ConfigurationError` if:
|
|
183
|
+
- Required parameters are missing
|
|
184
|
+
- Invalid types are provided
|
|
185
|
+
- Tools are bound before being added
|
|
186
|
+
- Invalid handler functions are provided
|
|
187
|
+
|
|
188
|
+
Example:
|
|
189
|
+
```javascript
|
|
190
|
+
try {
|
|
191
|
+
const agent = Agent()
|
|
192
|
+
.bindTool("nonexistent", handler) // Error: Tool not found
|
|
193
|
+
} catch (error) {
|
|
194
|
+
console.error(error.message)
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Complete Example
|
|
199
|
+
|
|
200
|
+
See `examples/direct-agent-usage.js` for a complete working example of creating an accommodation booking agent using the direct Agent interface.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { Agent, GPT, Message, MemoryStore } from "../src/index.js"
|
|
2
|
+
|
|
3
|
+
// Example: Creating an agent directly without using AgentLoader
|
|
4
|
+
async function createAgentDirectly() {
|
|
5
|
+
// Create a new agent using the Agent builder interface
|
|
6
|
+
const agent = Agent()
|
|
7
|
+
.setMetadata({
|
|
8
|
+
name: "accomodationAgent",
|
|
9
|
+
namespace: "smartchat",
|
|
10
|
+
description: "A highly advanced accommodation manager agent"
|
|
11
|
+
})
|
|
12
|
+
.withLLM(GPT, {
|
|
13
|
+
model: "gpt-4o-mini",
|
|
14
|
+
instructions: "You are a highly advanced accommodation manager agent. \nPrioritize clarity and helpfulness.\nUse tools effectively to gather information."
|
|
15
|
+
})
|
|
16
|
+
.withStore(MemoryStore(), {
|
|
17
|
+
type: "Memory"
|
|
18
|
+
})
|
|
19
|
+
.withRunner({
|
|
20
|
+
maxRuns: 10, // Maximum number of LLM calls per query
|
|
21
|
+
maxConversationLength: 10 // Maximum conversation history length
|
|
22
|
+
})
|
|
23
|
+
.addTool({
|
|
24
|
+
name: "get_rooms_list_tool",
|
|
25
|
+
description: "Retrieves a list of available rooms based on criteria.",
|
|
26
|
+
type: "function",
|
|
27
|
+
parameters: {
|
|
28
|
+
type: "object",
|
|
29
|
+
properties: {
|
|
30
|
+
checkinDate: {
|
|
31
|
+
type: "string",
|
|
32
|
+
description: "The check-in date."
|
|
33
|
+
},
|
|
34
|
+
checkoutDate: {
|
|
35
|
+
type: "string",
|
|
36
|
+
description: "The check-out date."
|
|
37
|
+
},
|
|
38
|
+
guests: {
|
|
39
|
+
type: "integer",
|
|
40
|
+
description: "Number of guests."
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
required: ["checkinDate", "checkoutDate"]
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
.addTool({
|
|
47
|
+
name: "get_room_detail_tool",
|
|
48
|
+
description: "Retrieves detailed information about a specific room.",
|
|
49
|
+
type: "function",
|
|
50
|
+
parameters: {
|
|
51
|
+
type: "object",
|
|
52
|
+
properties: {
|
|
53
|
+
roomName: {
|
|
54
|
+
type: "string",
|
|
55
|
+
description: "The name of the room."
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
required: ["roomName"]
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
.bindTool("get_rooms_list_tool", async (state, input) => {
|
|
62
|
+
return {
|
|
63
|
+
answer: "We have Double room with a view of the sea and a single room with a view of the pool, and a suite with a view of the city."
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
.bindTool("get_room_detail_tool", async (state, input) => {
|
|
67
|
+
return {
|
|
68
|
+
answer: "The Double room with a view of the sea has a king size bed, a private balcony, and a view of the sea."
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
// Optional: Add prompt handler
|
|
72
|
+
.prompt(async (state, formattedInput) => {
|
|
73
|
+
console.log("Processing prompt:", formattedInput)
|
|
74
|
+
return formattedInput
|
|
75
|
+
})
|
|
76
|
+
// Optional: Add response handler
|
|
77
|
+
.response(async (state, conversation, result) => {
|
|
78
|
+
console.log("Generated response:", result)
|
|
79
|
+
return result
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// Compile the agent
|
|
83
|
+
const agentInstance = await agent.compile()
|
|
84
|
+
|
|
85
|
+
return agentInstance
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Usage example
|
|
89
|
+
async function main() {
|
|
90
|
+
try {
|
|
91
|
+
// Create the agent directly
|
|
92
|
+
const agentInstance = await createAgentDirectly()
|
|
93
|
+
|
|
94
|
+
// Create a message
|
|
95
|
+
const input = new Message({
|
|
96
|
+
content: "What rooms do you have from 2025-05-10 to 2025-05-15 for 2 guests?",
|
|
97
|
+
session: {
|
|
98
|
+
id: "67a71e42-a7d8-1db2-ad17-64e1c8546b21",
|
|
99
|
+
propertySetId: "123"
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
// Query the agent
|
|
104
|
+
const result = await agentInstance.query(input)
|
|
105
|
+
|
|
106
|
+
console.log("Agent response:", result.getContent())
|
|
107
|
+
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error("Error:", error)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Run the example
|
|
114
|
+
main()
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Message } from '../src/index.js';
|
|
2
|
+
|
|
3
|
+
// Example of using the new previousConversation capability
|
|
4
|
+
console.log('=== Previous Conversation Example ===\n');
|
|
5
|
+
|
|
6
|
+
// Example 1: Generic format (works with all LLM providers)
|
|
7
|
+
const genericPreviousConversation = [
|
|
8
|
+
{ role: 'user', content: 'Hello, I need help with my React project.' },
|
|
9
|
+
{ role: 'assistant', content: 'I\'d be happy to help! What specific aspect of your React project do you need assistance with?' },
|
|
10
|
+
{ role: 'user', content: 'I\'m having trouble with state management.' },
|
|
11
|
+
{ role: 'assistant', content: 'State management can be tricky. Are you using useState, useReducer, or a library like Redux?' }
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
const messageWithGeneric = new Message({
|
|
15
|
+
content: 'Can you help me implement a shopping cart?',
|
|
16
|
+
session: { id: 'session-123', userId: 'user-456' },
|
|
17
|
+
previousConversation: genericPreviousConversation
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
console.log('Generic Format Message:');
|
|
21
|
+
console.log('- Content:', messageWithGeneric.getContent());
|
|
22
|
+
console.log('- Session ID:', messageWithGeneric.getSessionId());
|
|
23
|
+
console.log('- Previous Conversation Length:', messageWithGeneric.getPreviousConversation()?.length || 0);
|
|
24
|
+
console.log('- First Previous Message:', messageWithGeneric.getPreviousConversation()?.[0]);
|
|
25
|
+
console.log();
|
|
26
|
+
|
|
27
|
+
// Example 2: Mixed format (Gemini + OpenAI formats)
|
|
28
|
+
const mixedPreviousConversation = [
|
|
29
|
+
{ role: 'user', content: 'What is machine learning?' },
|
|
30
|
+
{ role: 'model', parts: [{ text: 'Machine learning is a subset of AI...' }] }, // Gemini format
|
|
31
|
+
{ role: 'user', content: 'Can you give me an example?' },
|
|
32
|
+
{ role: 'assistant', content: 'Sure! A common example is email spam detection...' } // OpenAI format
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const messageWithMixed = new Message({
|
|
36
|
+
content: 'How does neural networks work?',
|
|
37
|
+
session: { id: 'session-456' },
|
|
38
|
+
previousConversation: mixedPreviousConversation
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
console.log('Mixed Format Message:');
|
|
42
|
+
console.log('- Content:', messageWithMixed.getContent());
|
|
43
|
+
console.log('- Previous Conversation Length:', messageWithMixed.getPreviousConversation()?.length || 0);
|
|
44
|
+
console.log('- Mixed formats detected:', {
|
|
45
|
+
geminiFormat: messageWithMixed.getPreviousConversation()?.some(msg => msg.role === 'model'),
|
|
46
|
+
openaiFormat: messageWithMixed.getPreviousConversation()?.some(msg => msg.role === 'assistant')
|
|
47
|
+
});
|
|
48
|
+
console.log();
|
|
49
|
+
|
|
50
|
+
// Example 3: Serialization/Deserialization
|
|
51
|
+
console.log('Serialization Example:');
|
|
52
|
+
const serialized = messageWithGeneric.serialize();
|
|
53
|
+
console.log('- Serialized length:', serialized.length, 'characters');
|
|
54
|
+
|
|
55
|
+
const newMessage = new Message({ content: '' });
|
|
56
|
+
newMessage.deserialize(serialized);
|
|
57
|
+
console.log('- Deserialized content matches:', newMessage.getContent() === messageWithGeneric.getContent());
|
|
58
|
+
console.log('- Deserialized previous conversation matches:',
|
|
59
|
+
JSON.stringify(newMessage.getPreviousConversation()) === JSON.stringify(messageWithGeneric.getPreviousConversation()));
|
|
60
|
+
console.log();
|
|
61
|
+
|
|
62
|
+
// Example 4: Backward compatibility (no previous conversation)
|
|
63
|
+
const simpleMessage = new Message({
|
|
64
|
+
content: 'Simple question without previous context',
|
|
65
|
+
session: { id: 'session-789' }
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
console.log('Backward Compatibility:');
|
|
69
|
+
console.log('- Content:', simpleMessage.getContent());
|
|
70
|
+
console.log('- Previous Conversation:', simpleMessage.getPreviousConversation()); // Should be null
|
|
71
|
+
console.log('- String constructor still works:', new Message('Just a string').getContent());
|
|
72
|
+
|
|
73
|
+
console.log('\n=== Implementation Complete ===');
|
|
74
|
+
console.log('✅ Message class extended with previousConversation field');
|
|
75
|
+
console.log('✅ Runtime integration handles conversation prefilling');
|
|
76
|
+
console.log('✅ Backward compatibility maintained');
|
|
77
|
+
console.log('✅ Auto-detection of different LLM formats supported');
|
package/package.json
CHANGED
package/src/agent/agent.js
CHANGED
|
@@ -360,6 +360,84 @@ export function Agent() {
|
|
|
360
360
|
return this;
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
+
/**
|
|
364
|
+
* Adds a tool with its schema and optional handler function
|
|
365
|
+
* @param {Object} toolDefinition - Tool definition including name, description, parameters
|
|
366
|
+
* @param {Function} handlerFunction - Optional handler function for the tool
|
|
367
|
+
* @returns {Object} Agent builder for chaining
|
|
368
|
+
*/
|
|
369
|
+
function addTool(toolDefinition, handlerFunction = null) {
|
|
370
|
+
if (!toolDefinition || !toolDefinition.name) {
|
|
371
|
+
throw new ConfigurationError("Tool definition must have a name", {
|
|
372
|
+
toolDefinition: toolDefinition
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Store the tool schema in the format expected by executor
|
|
377
|
+
config.toolsSchemas[toolDefinition.name] = {
|
|
378
|
+
name: toolDefinition.name,
|
|
379
|
+
schema: toolDefinition,
|
|
380
|
+
function: handlerFunction
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
return this;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Binds a handler function to an existing tool
|
|
388
|
+
* @param {String} toolName - Name of the tool
|
|
389
|
+
* @param {Function} handlerFunction - Handler function for the tool
|
|
390
|
+
* @returns {Object} Agent builder for chaining
|
|
391
|
+
*/
|
|
392
|
+
function bindTool(toolName, handlerFunction) {
|
|
393
|
+
if (!config.toolsSchemas[toolName]) {
|
|
394
|
+
throw new ConfigurationError(`Tool ${toolName} not found. Add the tool first using addTool()`, {
|
|
395
|
+
availableTools: Object.keys(config.toolsSchemas)
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (typeof handlerFunction !== 'function') {
|
|
400
|
+
throw new ConfigurationError(`Handler for tool ${toolName} must be a function`, {
|
|
401
|
+
provided: typeof handlerFunction
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
config.toolsSchemas[toolName].function = handlerFunction;
|
|
406
|
+
return this;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Sets the prompt handler
|
|
411
|
+
* @param {Function} handler - Prompt handler function
|
|
412
|
+
* @returns {Object} Agent builder for chaining
|
|
413
|
+
*/
|
|
414
|
+
function prompt(handler) {
|
|
415
|
+
if (typeof handler !== 'function') {
|
|
416
|
+
throw new ConfigurationError("Prompt handler must be a function", {
|
|
417
|
+
provided: typeof handler
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
config.on.prompt = handler;
|
|
422
|
+
return this;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Sets the response handler
|
|
427
|
+
* @param {Function} handler - Response handler function
|
|
428
|
+
* @returns {Object} Agent builder for chaining
|
|
429
|
+
*/
|
|
430
|
+
function response(handler) {
|
|
431
|
+
if (typeof handler !== 'function') {
|
|
432
|
+
throw new ConfigurationError("Response handler must be a function", {
|
|
433
|
+
provided: typeof handler
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
config.on.response = handler;
|
|
438
|
+
return this;
|
|
439
|
+
}
|
|
440
|
+
|
|
363
441
|
/**
|
|
364
442
|
* Sets agent metadata
|
|
365
443
|
* @param {Object} metadata - Agent metadata
|
|
@@ -380,6 +458,39 @@ export function Agent() {
|
|
|
380
458
|
return this;
|
|
381
459
|
}
|
|
382
460
|
|
|
461
|
+
/**
|
|
462
|
+
* Configures runner settings
|
|
463
|
+
* @param {Object} runnerConfig - Runner configuration
|
|
464
|
+
* @returns {Object} Agent builder for chaining
|
|
465
|
+
*/
|
|
466
|
+
function withRunner(runnerConfig) {
|
|
467
|
+
if (!runnerConfig) {
|
|
468
|
+
throw new ConfigurationError("Runner configuration is required", {
|
|
469
|
+
provided: runnerConfig
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (runnerConfig.maxRuns !== undefined) {
|
|
474
|
+
if (typeof runnerConfig.maxRuns !== 'number' || runnerConfig.maxRuns <= 0) {
|
|
475
|
+
throw new ConfigurationError("maxRuns must be a positive number", {
|
|
476
|
+
provided: runnerConfig.maxRuns
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
config.runner.maxRuns = runnerConfig.maxRuns;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (runnerConfig.maxConversationLength !== undefined) {
|
|
483
|
+
if (typeof runnerConfig.maxConversationLength !== 'number' || runnerConfig.maxConversationLength <= 0) {
|
|
484
|
+
throw new ConfigurationError("maxConversationLength must be a positive number", {
|
|
485
|
+
provided: runnerConfig.maxConversationLength
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
config.runner.maxConversationLength = runnerConfig.maxConversationLength;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
return this;
|
|
492
|
+
}
|
|
493
|
+
|
|
383
494
|
/**
|
|
384
495
|
* Gets all registered tool schemas
|
|
385
496
|
* @returns {Object} Map of tool schemas
|
|
@@ -396,6 +507,7 @@ export function Agent() {
|
|
|
396
507
|
// Validate configuration before compiling
|
|
397
508
|
validateConfiguration();
|
|
398
509
|
|
|
510
|
+
|
|
399
511
|
try {
|
|
400
512
|
logger.info(`Compiling agent ${config.metadata.name}`);
|
|
401
513
|
const runtime = await AgentRuntime(config);
|
|
@@ -422,9 +534,14 @@ export function Agent() {
|
|
|
422
534
|
addIO,
|
|
423
535
|
withLLM,
|
|
424
536
|
withStore,
|
|
537
|
+
withRunner,
|
|
425
538
|
on,
|
|
426
539
|
addDiscoverySchema,
|
|
427
540
|
addToolSchema,
|
|
541
|
+
addTool,
|
|
542
|
+
bindTool,
|
|
543
|
+
prompt,
|
|
544
|
+
response,
|
|
428
545
|
compile,
|
|
429
546
|
setMetadata,
|
|
430
547
|
getToolsSchemas,
|
package/src/agent/executor.js
CHANGED
|
@@ -215,24 +215,38 @@ export async function build(
|
|
|
215
215
|
state: state,
|
|
216
216
|
contents: contents
|
|
217
217
|
});
|
|
218
|
-
|
|
218
|
+
|
|
219
|
+
const calledTools = api.getCalledTools();
|
|
220
|
+
const modeStopTools = toolsAndHandoffsMap.tools.filter(tool => tool.mode === 'stopAfterOneExecution').map(tool => tool.name);
|
|
221
|
+
|
|
222
|
+
if (modeStopTools.some(tool => calledTools.includes(tool))) {
|
|
223
|
+
api.resetCalledTools();
|
|
224
|
+
llmConfig = await api.resetToolConfig(llmConfig);
|
|
225
|
+
}
|
|
226
|
+
|
|
219
227
|
// Check for max runs exceeded
|
|
220
|
-
if (run
|
|
221
|
-
logger.
|
|
222
|
-
|
|
223
|
-
await emit(hooks, 'executorMaxRuns', {
|
|
224
|
-
agentName: agentName,
|
|
225
|
-
run: run,
|
|
226
|
-
state: state,
|
|
227
|
-
contents: contents
|
|
228
|
-
});
|
|
228
|
+
if (run == maxRuns - 1) {
|
|
229
|
+
logger.info(`Agent ${agentName} almost max runs reached: ${run}/${maxRuns}`);
|
|
229
230
|
|
|
230
|
-
//
|
|
231
|
+
//return rawConversation[rawConversation.length - 1];
|
|
232
|
+
// toolsAndHandoffsMap = { tools: [] }
|
|
233
|
+
try {
|
|
234
|
+
llmConfig = await api.resetToolConfig(llmConfig);
|
|
235
|
+
api.resetCalledTools();
|
|
236
|
+
} catch (error) {
|
|
237
|
+
logger.error(`Error resetting tool config for agent ${agentName}`, { error });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (run >= maxRuns) {
|
|
242
|
+
logger.warn(`Agent ${agentName} max over runs reached: ${run}/${maxRuns}`);
|
|
243
|
+
api.resetCalledTools();
|
|
231
244
|
const rawConversation = contents.getRawConversation();
|
|
232
|
-
return rawConversation
|
|
245
|
+
return rawConversation.join('\n');
|
|
233
246
|
}
|
|
234
247
|
|
|
235
248
|
try {
|
|
249
|
+
|
|
236
250
|
// Prepare input for LLM
|
|
237
251
|
const input = {
|
|
238
252
|
client: client,
|
package/src/agent/runtime.js
CHANGED
|
@@ -68,6 +68,7 @@ export async function AgentRuntime(agentConfig) {
|
|
|
68
68
|
const content = message.getContent()
|
|
69
69
|
const session = message.getSession()
|
|
70
70
|
const sessionId = message.getSessionId()
|
|
71
|
+
const previousConversation = message.getPreviousConversation()
|
|
71
72
|
const storeStateSessionId = namespace + "." + agentName + "." + sessionId
|
|
72
73
|
|
|
73
74
|
// Load and merge session state and session data
|
|
@@ -89,6 +90,12 @@ export async function AgentRuntime(agentConfig) {
|
|
|
89
90
|
logger.info(`Loaded session state for agent ${agentName} with session id ${storeStateSessionId}, current conversation length ${storeState.conversation.getRawConversation().length}`);
|
|
90
91
|
}
|
|
91
92
|
|
|
93
|
+
// Import previous conversation if provided and current conversation is empty
|
|
94
|
+
if (previousConversation && storeState.conversation.getMessages().length === 0) {
|
|
95
|
+
storeState.conversation.importFromArray(previousConversation)
|
|
96
|
+
logger.info(`Imported ${previousConversation.length} previous messages for session ${storeStateSessionId}`)
|
|
97
|
+
}
|
|
98
|
+
|
|
92
99
|
const formattedInput = typeof content === 'string' ? content : JSON.stringify(content);
|
|
93
100
|
|
|
94
101
|
logger.debug(`Query to agent ${agentName}`, {
|
package/src/index.js
CHANGED
|
@@ -7,7 +7,8 @@ import {
|
|
|
7
7
|
redisStore,
|
|
8
8
|
postgresStore,
|
|
9
9
|
memoryStore,
|
|
10
|
-
session
|
|
10
|
+
session,
|
|
11
|
+
noStore
|
|
11
12
|
} from "./store/store.js";
|
|
12
13
|
import { Conversation } from "./utils/conversation.js";
|
|
13
14
|
|
|
@@ -27,8 +28,10 @@ export const GPT = _GPT
|
|
|
27
28
|
export const PostgresStore = postgresStore
|
|
28
29
|
export const RedisStore = redisStore
|
|
29
30
|
export const MemoryStore = memoryStore
|
|
31
|
+
export const NoStore = noStore
|
|
30
32
|
export const SessionStore = session
|
|
31
33
|
|
|
34
|
+
|
|
32
35
|
import { connect } from "@nats-io/transport-node"
|
|
33
36
|
export const NatsIO = (config) => {
|
|
34
37
|
let connected = false
|
|
@@ -53,18 +56,21 @@ export const Bindings = {
|
|
|
53
56
|
NatsIO: 'NatsIO',
|
|
54
57
|
Postgres: 'Postgres',
|
|
55
58
|
Redis: 'Redis',
|
|
56
|
-
Memory: 'Memory'
|
|
59
|
+
Memory: 'Memory',
|
|
60
|
+
NoStore: 'NoStore'
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
export class Message {
|
|
60
64
|
#content
|
|
61
65
|
#session
|
|
66
|
+
#previousConversation
|
|
62
67
|
constructor(input) {
|
|
63
68
|
if (typeof input === 'string') {
|
|
64
69
|
this.#content = input
|
|
65
70
|
} else {
|
|
66
71
|
this.#content = input.content
|
|
67
72
|
this.#session = input.session || {}
|
|
73
|
+
this.#previousConversation = input.previousConversation || null
|
|
68
74
|
}
|
|
69
75
|
}
|
|
70
76
|
getContent() {
|
|
@@ -76,16 +82,21 @@ export class Message {
|
|
|
76
82
|
getSession() {
|
|
77
83
|
return this.#session
|
|
78
84
|
}
|
|
85
|
+
getPreviousConversation() {
|
|
86
|
+
return this.#previousConversation
|
|
87
|
+
}
|
|
79
88
|
serialize() {
|
|
80
89
|
return JSON.stringify({
|
|
81
90
|
content: this.#content,
|
|
82
|
-
session: this.#session
|
|
91
|
+
session: this.#session,
|
|
92
|
+
previousConversation: this.#previousConversation
|
|
83
93
|
})
|
|
84
94
|
}
|
|
85
95
|
deserialize(data) {
|
|
86
96
|
const parsed = JSON.parse(data)
|
|
87
97
|
this.#content = parsed.content
|
|
88
98
|
this.#session = parsed.session || {}
|
|
99
|
+
this.#previousConversation = parsed.previousConversation || null
|
|
89
100
|
}
|
|
90
101
|
}
|
|
91
102
|
|
package/src/llm/base.js
CHANGED
|
@@ -12,6 +12,7 @@ export class BaseLLM {
|
|
|
12
12
|
*/
|
|
13
13
|
constructor(providerType) {
|
|
14
14
|
this.type = providerType;
|
|
15
|
+
this.calledTools = [];
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -23,6 +24,18 @@ export class BaseLLM {
|
|
|
23
24
|
throw new Error('getClient() must be implemented by subclasses');
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
async resetToolConfig(config) {
|
|
28
|
+
return config;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
getCalledTools() {
|
|
32
|
+
return this.calledTools;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
resetCalledTools() {
|
|
36
|
+
this.calledTools = [];
|
|
37
|
+
}
|
|
38
|
+
|
|
26
39
|
/**
|
|
27
40
|
* Call the LLM model with the provided configuration and context
|
|
28
41
|
* @param {Object} config - LLM-specific configuration
|
|
@@ -106,6 +119,7 @@ export class BaseLLM {
|
|
|
106
119
|
} else {
|
|
107
120
|
result = await toolsAndHandoffsMap[name].function(state, args);
|
|
108
121
|
}
|
|
122
|
+
this.calledTools.push(name);
|
|
109
123
|
|
|
110
124
|
logger.debug('Tool execution successful', { toolName: name });
|
|
111
125
|
return result;
|
package/src/llm/gemini.js
CHANGED
|
@@ -50,7 +50,11 @@ class GeminiLLM extends BaseLLM {
|
|
|
50
50
|
input.config.tools = toolsAndHandoffsMap.tools;
|
|
51
51
|
} else if (toolsAndHandoffsMap.tools.length > 0) {
|
|
52
52
|
input.config = input.config || {};
|
|
53
|
-
|
|
53
|
+
// remove the mode key from the tools, for gpt is not necessary beacuase it doesn't bother if is present or not
|
|
54
|
+
input.config.tools = [{ functionDeclarations: toolsAndHandoffsMap.tools.map(tool => {
|
|
55
|
+
const { mode, ...toolWithoutMode } = tool;
|
|
56
|
+
return toolWithoutMode;
|
|
57
|
+
}) }];
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
logger.debug('Calling Gemini model', {
|
|
@@ -189,7 +193,10 @@ const geminiLLM = new GeminiLLM();
|
|
|
189
193
|
export default {
|
|
190
194
|
type: geminiLLM.type,
|
|
191
195
|
getClient: geminiLLM.getClient.bind(geminiLLM),
|
|
196
|
+
resetToolConfig: geminiLLM.resetToolConfig.bind(geminiLLM),
|
|
192
197
|
prompt: geminiLLM.prompt.bind(geminiLLM),
|
|
193
198
|
callModel: geminiLLM.callModel.bind(geminiLLM),
|
|
194
|
-
onResponse: geminiLLM.onResponse.bind(geminiLLM)
|
|
199
|
+
onResponse: geminiLLM.onResponse.bind(geminiLLM),
|
|
200
|
+
getCalledTools: geminiLLM.getCalledTools.bind(geminiLLM),
|
|
201
|
+
resetCalledTools: geminiLLM.resetCalledTools.bind(geminiLLM)
|
|
195
202
|
}
|
package/src/llm/gpt.js
CHANGED
|
@@ -10,7 +10,7 @@ import { Conversation } from '../utils/conversation.js'
|
|
|
10
10
|
class OpenAILLM extends BaseLLM {
|
|
11
11
|
constructor() {
|
|
12
12
|
super('openai');
|
|
13
|
-
}
|
|
13
|
+
}
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Initializes and returns an OpenAI client
|
|
@@ -33,6 +33,11 @@ class OpenAILLM extends BaseLLM {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
async resetToolConfig (llmClientConfig) {
|
|
37
|
+
llmClientConfig.tool_choice = 'auto';
|
|
38
|
+
return llmClientConfig;
|
|
39
|
+
}
|
|
40
|
+
|
|
36
41
|
/**
|
|
37
42
|
* Calls the OpenAI model with the provided configuration and context
|
|
38
43
|
* @param {Object} llmClientConfig - Configuration for the OpenAI model
|
|
@@ -188,7 +193,10 @@ const openaiLLM = new OpenAILLM();
|
|
|
188
193
|
export default {
|
|
189
194
|
type: openaiLLM.type,
|
|
190
195
|
getClient: openaiLLM.getClient.bind(openaiLLM),
|
|
196
|
+
resetToolConfig: openaiLLM.resetToolConfig.bind(openaiLLM),
|
|
191
197
|
prompt: openaiLLM.prompt.bind(openaiLLM),
|
|
192
198
|
callModel: openaiLLM.callModel.bind(openaiLLM),
|
|
193
|
-
onResponse: openaiLLM.onResponse.bind(openaiLLM)
|
|
199
|
+
onResponse: openaiLLM.onResponse.bind(openaiLLM),
|
|
200
|
+
getCalledTools: openaiLLM.getCalledTools.bind(openaiLLM),
|
|
201
|
+
resetCalledTools: openaiLLM.resetCalledTools.bind(openaiLLM)
|
|
194
202
|
}
|
package/src/store/store.js
CHANGED
|
@@ -202,6 +202,22 @@ export function memoryStore () {
|
|
|
202
202
|
},
|
|
203
203
|
get: async function (key) {
|
|
204
204
|
return state[key] || null
|
|
205
|
+
},
|
|
206
|
+
getAll: async function () {
|
|
207
|
+
return state
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function noStore () {
|
|
213
|
+
return {
|
|
214
|
+
connect: async function () {},
|
|
215
|
+
disconnect: async function () {},
|
|
216
|
+
set: async function (key, value) {
|
|
217
|
+
return null
|
|
218
|
+
},
|
|
219
|
+
get: async function (key) {
|
|
220
|
+
return null
|
|
205
221
|
}
|
|
206
222
|
}
|
|
207
223
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Agent, GPT, Message, MemoryStore } from "./src/index.js"
|
|
2
|
+
|
|
3
|
+
// Test to debug the tools array issue
|
|
4
|
+
async function testToolsFormat() {
|
|
5
|
+
console.log("=== Testing Tools Format ===\n");
|
|
6
|
+
|
|
7
|
+
// Create a simple agent with one tool
|
|
8
|
+
const agent = Agent()
|
|
9
|
+
.setMetadata({
|
|
10
|
+
name: "testAgent",
|
|
11
|
+
namespace: "test"
|
|
12
|
+
})
|
|
13
|
+
.withLLM(GPT, {
|
|
14
|
+
model: "gpt-4o-mini",
|
|
15
|
+
instructions: "You are a test agent."
|
|
16
|
+
})
|
|
17
|
+
.withStore(MemoryStore(), {})
|
|
18
|
+
.addTool({
|
|
19
|
+
name: "test_tool",
|
|
20
|
+
description: "A test tool",
|
|
21
|
+
type: "function",
|
|
22
|
+
parameters: {
|
|
23
|
+
type: "object",
|
|
24
|
+
properties: {
|
|
25
|
+
input: {
|
|
26
|
+
type: "string",
|
|
27
|
+
description: "Test input"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
required: ["input"]
|
|
31
|
+
}
|
|
32
|
+
}, async (state, input) => {
|
|
33
|
+
return { result: "test" }
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Check the internal structure
|
|
37
|
+
console.log("1. Agent config toolsSchemas:");
|
|
38
|
+
console.log(JSON.stringify(agent._config.toolsSchemas, null, 2));
|
|
39
|
+
|
|
40
|
+
console.log("\n2. Agent config toolsAndHandoffsMap:");
|
|
41
|
+
console.log(JSON.stringify(agent._config.toolsAndHandoffsMap, null, 2));
|
|
42
|
+
|
|
43
|
+
// Now let's see what Object.values gives us
|
|
44
|
+
console.log("\n3. Object.values(toolsSchemas):");
|
|
45
|
+
const toolsArray = Object.values(agent._config.toolsSchemas);
|
|
46
|
+
console.log(JSON.stringify(toolsArray, null, 2));
|
|
47
|
+
|
|
48
|
+
// Check what each tool looks like
|
|
49
|
+
console.log("\n4. First tool structure:");
|
|
50
|
+
if (toolsArray.length > 0) {
|
|
51
|
+
const firstTool = toolsArray[0];
|
|
52
|
+
console.log("Type of first tool:", typeof firstTool);
|
|
53
|
+
console.log("First tool keys:", Object.keys(firstTool));
|
|
54
|
+
console.log("First tool:", JSON.stringify(firstTool, (key, value) => {
|
|
55
|
+
if (typeof value === 'function') return '[Function]';
|
|
56
|
+
return value;
|
|
57
|
+
}, 2));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Simulate what makeToolsAndHandoffsMap would do
|
|
61
|
+
console.log("\n5. Simulating makeToolsAndHandoffsMap:");
|
|
62
|
+
const simulatedMap = { tools: [] };
|
|
63
|
+
for (const tool of toolsArray) {
|
|
64
|
+
if (!tool.schema) {
|
|
65
|
+
console.log(" - Tool has no schema, pushing tool itself");
|
|
66
|
+
simulatedMap.tools.push(tool);
|
|
67
|
+
} else {
|
|
68
|
+
console.log(" - Tool has schema, pushing tool.schema");
|
|
69
|
+
simulatedMap.tools.push(tool.schema);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
console.log("Simulated tools array:", JSON.stringify(simulatedMap.tools, null, 2));
|
|
73
|
+
|
|
74
|
+
console.log("\n=== End Test ===");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Run the test
|
|
78
|
+
testToolsFormat().catch(console.error);
|