agentnet 0.1.30 → 0.1.32

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.
@@ -1,4 +1,4 @@
1
- import { Agent, GPT, Message, MemoryStore } from "../src/index.js"
1
+ import { Agent, GPT, Gemini, Message, MemoryStore } from "../src/index.js"
2
2
 
3
3
  // Example: Creating an agent directly without using AgentLoader
4
4
  async function createAgentDirectly() {
@@ -9,8 +9,8 @@ async function createAgentDirectly() {
9
9
  namespace: "smartchat",
10
10
  description: "A highly advanced accommodation manager agent"
11
11
  })
12
- .withLLM(GPT, {
13
- model: "gpt-4o-mini",
12
+ .withLLM(Gemini, {
13
+ model: "gemini-2.5-flash",
14
14
  instructions: "You are a highly advanced accommodation manager agent. \nPrioritize clarity and helpfulness.\nUse tools effectively to gather information."
15
15
  })
16
16
  .withStore(MemoryStore(), {
@@ -20,7 +20,7 @@ async function createAgentDirectly() {
20
20
  maxRuns: 10, // Maximum number of LLM calls per query
21
21
  maxConversationLength: 10 // Maximum conversation history length
22
22
  })
23
- .addTool({
23
+ /*.addTool({
24
24
  name: "get_rooms_list_tool",
25
25
  description: "Retrieves a list of available rooms based on criteria.",
26
26
  type: "function",
@@ -67,8 +67,12 @@ async function createAgentDirectly() {
67
67
  return {
68
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
69
  }
70
- })
70
+ })*/
71
71
  // Optional: Add prompt handler
72
+ .onLLMResponse(async (state, response, usage) => {
73
+ console.log("LLM response:", state, usage)
74
+ return response
75
+ })
72
76
  .prompt(async (state, formattedInput) => {
73
77
  console.log("Processing prompt:", formattedInput)
74
78
  return formattedInput
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentnet",
3
- "version": "0.1.30",
3
+ "version": "0.1.32",
4
4
  "type": "module",
5
5
  "description": "Agent library used by Smartness",
6
6
  "main": "src/index.js",
@@ -301,18 +301,25 @@ function configureRunner(agentBuilder, runnerSpec) {
301
301
  * @returns {object} Agent interface
302
302
  */
303
303
  function createAgentInterface(agentBuilder, toolMap) {
304
- return {
304
+ const agentInterface = {
305
305
  tools: toolMap,
306
306
  prompt: (callback) => {
307
307
  agentBuilder._config.on.prompt = callback;
308
+ return agentInterface;
308
309
  },
309
310
  response: (callback) => {
310
311
  agentBuilder._config.on.response = callback;
312
+ return agentInterface;
313
+ },
314
+ onLLMResponse: (callback) => {
315
+ agentBuilder._config.on.onLLMResponse = callback;
316
+ return agentInterface;
311
317
  },
312
318
  compile: async () => {
313
319
  return await agentBuilder.compile();
314
320
  }
315
321
  };
322
+ return agentInterface;
316
323
  }
317
324
 
318
325
  /**
@@ -13,7 +13,8 @@ import { logger } from "../utils/logger.js"
13
13
  */
14
14
  const DEFAULT_HOOKS = {
15
15
  prompt: async (state, formattedInput) => formattedInput,
16
- response: async (state, conversation, result) => result
16
+ response: async (state, conversation, result) => result,
17
+ onLLMResponse: null // Optional: called after each LLM call with (state, response, usage)
17
18
  }
18
19
 
19
20
  /**
@@ -200,6 +201,10 @@ export function Agent() {
200
201
 
201
202
  // Event handlers validation
202
203
  Object.entries(config.on).forEach(([eventName, handler]) => {
204
+ // Skip null handlers (optional hooks like onLLMResponse)
205
+ if (handler === null) {
206
+ return;
207
+ }
203
208
  if (typeof handler !== 'function') {
204
209
  throw new ConfigurationError(`Event handler for '${eventName}' must be a function`, {
205
210
  eventName,
@@ -438,6 +443,25 @@ export function Agent() {
438
443
  return this;
439
444
  }
440
445
 
446
+ /**
447
+ * Sets the LLM response handler - called after each LLM API call
448
+ * @param {Function} handler - Handler function (state, response, usage) => void
449
+ * - state: Current agent state
450
+ * - response: Raw LLM response object
451
+ * - usage: Normalized token usage { inputTokens, outputTokens, totalTokens, reasoningTokens, cachedTokens, provider, model, raw }
452
+ * @returns {Object} Agent builder for chaining
453
+ */
454
+ function onLLMResponse(handler) {
455
+ if (typeof handler !== 'function') {
456
+ throw new ConfigurationError("onLLMResponse handler must be a function", {
457
+ provided: typeof handler
458
+ });
459
+ }
460
+
461
+ config.on.onLLMResponse = handler;
462
+ return this;
463
+ }
464
+
441
465
  /**
442
466
  * Sets agent metadata
443
467
  * @param {Object} metadata - Agent metadata
@@ -542,6 +566,7 @@ export function Agent() {
542
566
  bindTool,
543
567
  prompt,
544
568
  response,
569
+ onLLMResponse,
545
570
  compile,
546
571
  setMetadata,
547
572
  getToolsSchemas,
@@ -166,6 +166,8 @@ async function safeExecute(func, name, type, state, input, timeout = DEFAULT_TOO
166
166
  * @param {Object} api - LLM API
167
167
  * @param {Object} llmConfig - LLM configuration
168
168
  * @param {Object} runner - Runner configuration
169
+ * @param {Object} callbacks - Callback functions
170
+ * @param {Function} callbacks.onLLMResponse - Called after each LLM call with (state, response, usage)
169
171
  * @returns {Function} Executor function
170
172
  */
171
173
  export async function build(
@@ -174,7 +176,8 @@ export async function build(
174
176
  agentName,
175
177
  api,
176
178
  llmConfig,
177
- runner
179
+ runner,
180
+ callbacks = {}
178
181
  ) {
179
182
  const maxRuns = runner?.maxRuns || 10;
180
183
 
@@ -238,7 +241,7 @@ export async function build(
238
241
  }
239
242
  }
240
243
 
241
- if (run >= maxRuns) {
244
+ if (run > maxRuns) {
242
245
  logger.warn(`Agent ${agentName} max over runs reached: ${run}/${maxRuns}`);
243
246
  api.resetCalledTools();
244
247
  const rawConversation = contents.getRawConversation();
@@ -287,6 +290,16 @@ export async function build(
287
290
 
288
291
  logger.debug(`LLM response received for agent ${agentName}`);
289
292
 
293
+ // Call onLLMResponse callback if defined
294
+ if (callbacks.onLLMResponse) {
295
+ try {
296
+ const usage = api.extractUsage(response);
297
+ await callbacks.onLLMResponse(state, response, usage);
298
+ } catch (error) {
299
+ logger.warn(`Error in onLLMResponse callback for agent ${agentName}`, { error });
300
+ }
301
+ }
302
+
290
303
  // Process the response
291
304
  const finished = await api.onResponse(state, contents, toolsAndHandoffsMap, response);
292
305
 
@@ -16,7 +16,7 @@ export async function AgentRuntime(agentConfig) {
16
16
  handoffs,
17
17
  io: ioInterfaces,
18
18
  discoverySchemas,
19
- on: { prompt, response }
19
+ on: { prompt, response, onLLMResponse }
20
20
  } = agentConfig
21
21
 
22
22
  // Initialize IO runtime
@@ -38,7 +38,8 @@ export async function AgentRuntime(agentConfig) {
38
38
  agentName,
39
39
  llmApi,
40
40
  llmConfig,
41
- runner
41
+ runner,
42
+ { onLLMResponse }
42
43
  )
43
44
 
44
45
  // Create task processing function
package/src/llm/base.js CHANGED
@@ -28,6 +28,26 @@ export class BaseLLM {
28
28
  return config;
29
29
  }
30
30
 
31
+ /**
32
+ * Extracts and normalizes token usage from the LLM response
33
+ * @param {Object} response - The raw LLM response
34
+ * @returns {Object} Normalized usage object
35
+ */
36
+ extractUsage(response) {
37
+ // Default implementation returns empty usage
38
+ // Subclasses should override this
39
+ return {
40
+ inputTokens: 0,
41
+ outputTokens: 0,
42
+ totalTokens: 0,
43
+ reasoningTokens: 0,
44
+ cachedTokens: 0,
45
+ provider: this.type,
46
+ model: null,
47
+ raw: null
48
+ };
49
+ }
50
+
31
51
  getCalledTools() {
32
52
  return this.calledTools;
33
53
  }
package/src/llm/gemini.js CHANGED
@@ -33,6 +33,25 @@ class GeminiLLM extends BaseLLM {
33
33
  }
34
34
  }
35
35
 
36
+ /**
37
+ * Extracts and normalizes token usage from the Gemini response
38
+ * @param {Object} response - The raw Gemini response
39
+ * @returns {Object} Normalized usage object
40
+ */
41
+ extractUsage(response) {
42
+ const usage = response?.usageMetadata;
43
+ return {
44
+ inputTokens: usage?.promptTokenCount ?? 0,
45
+ outputTokens: usage?.candidatesTokenCount ?? 0,
46
+ totalTokens: usage?.totalTokenCount ?? 0,
47
+ reasoningTokens: usage?.thoughtsTokenCount ?? 0,
48
+ cachedTokens: 0, // Gemini doesn't report cached tokens
49
+ provider: this.type,
50
+ model: response?.modelVersion || null,
51
+ raw: usage || null
52
+ };
53
+ }
54
+
36
55
  /**
37
56
  * Calls the Gemini model with the provided configuration and context
38
57
  * @param {Object} llmClientConfig - Configuration for the Gemini model
@@ -64,8 +83,8 @@ class GeminiLLM extends BaseLLM {
64
83
  });
65
84
 
66
85
  try {
67
-
68
86
  const res = await client.models.generateContent(input);
87
+
69
88
  logger.debug('Gemini response', res)
70
89
  logger.debug('Gemini response received', {
71
90
  responseType: res.response?.candidates ? 'candidates' : 'unknown',
@@ -197,6 +216,7 @@ export default {
197
216
  prompt: geminiLLM.prompt.bind(geminiLLM),
198
217
  callModel: geminiLLM.callModel.bind(geminiLLM),
199
218
  onResponse: geminiLLM.onResponse.bind(geminiLLM),
219
+ extractUsage: geminiLLM.extractUsage.bind(geminiLLM),
200
220
  getCalledTools: geminiLLM.getCalledTools.bind(geminiLLM),
201
221
  resetCalledTools: geminiLLM.resetCalledTools.bind(geminiLLM)
202
222
  }
package/src/llm/gpt.js CHANGED
@@ -38,6 +38,25 @@ class OpenAILLM extends BaseLLM {
38
38
  return llmClientConfig;
39
39
  }
40
40
 
41
+ /**
42
+ * Extracts and normalizes token usage from the OpenAI response
43
+ * @param {Object} response - The raw OpenAI response
44
+ * @returns {Object} Normalized usage object
45
+ */
46
+ extractUsage(response) {
47
+ const usage = response?.usage;
48
+ return {
49
+ inputTokens: usage?.input_tokens ?? 0,
50
+ outputTokens: usage?.output_tokens ?? 0,
51
+ totalTokens: usage?.total_tokens ?? 0,
52
+ reasoningTokens: usage?.output_tokens_details?.reasoning_tokens ?? 0,
53
+ cachedTokens: usage?.input_tokens_details?.cached_tokens ?? 0,
54
+ provider: this.type,
55
+ model: response?.model || null,
56
+ raw: usage || null
57
+ };
58
+ }
59
+
41
60
  /**
42
61
  * Calls the OpenAI model with the provided configuration and context
43
62
  * @param {Object} llmClientConfig - Configuration for the OpenAI model
@@ -61,6 +80,7 @@ class OpenAILLM extends BaseLLM {
61
80
  //console.log(JSON.stringify(input, null, 2))
62
81
  try {
63
82
  const response = await client.responses.create(input);
83
+
64
84
  logger.debug('OpenAI response received');
65
85
  return response;
66
86
  } catch (error) {
@@ -197,6 +217,7 @@ export default {
197
217
  prompt: openaiLLM.prompt.bind(openaiLLM),
198
218
  callModel: openaiLLM.callModel.bind(openaiLLM),
199
219
  onResponse: openaiLLM.onResponse.bind(openaiLLM),
220
+ extractUsage: openaiLLM.extractUsage.bind(openaiLLM),
200
221
  getCalledTools: openaiLLM.getCalledTools.bind(openaiLLM),
201
222
  resetCalledTools: openaiLLM.resetCalledTools.bind(openaiLLM)
202
223
  }