agentnet 0.1.15 → 0.1.17

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.
@@ -5,6 +5,8 @@ metadata:
5
5
  name: entrypoint
6
6
  namespace: smartexample
7
7
  spec:
8
+ runner:
9
+ maxConversationLength: 100
8
10
  io:
9
11
  - type: NatsIO
10
12
  bindings:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentnet",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "type": "module",
5
5
  "description": "Agent library used by Smartness",
6
6
  "main": "src/index.js",
@@ -287,6 +287,10 @@ function configureRunner(agentBuilder, runnerSpec) {
287
287
  agentBuilder._config.runner.maxRuns = runnerSpec.maxRuns;
288
288
  }
289
289
 
290
+ if (runnerSpec.maxConversationLength !== undefined) {
291
+ agentBuilder._config.runner.maxConversationLength = runnerSpec.maxConversationLength;
292
+ }
293
+
290
294
  return agentBuilder;
291
295
  }
292
296
 
@@ -27,7 +27,8 @@ const DEFAULT_CONFIG = {
27
27
  apiVersion: "agentnet/v1alpha1" // Default API version
28
28
  },
29
29
  runner: {
30
- maxRuns: 10
30
+ maxRuns: 10,
31
+ maxConversationLength: 10
31
32
  }
32
33
  }
33
34
 
@@ -75,7 +76,8 @@ const AGENT_CONFIG_SCHEMA = {
75
76
  runner: {
76
77
  type: 'object',
77
78
  properties: {
78
- maxRuns: { type: 'number' }
79
+ maxRuns: { type: 'number' },
80
+ maxConversationLength: { type: 'number' }
79
81
  }
80
82
  },
81
83
  on: { type: 'object' }
@@ -214,6 +216,13 @@ export function Agent() {
214
216
  });
215
217
  }
216
218
 
219
+ validateType(config.runner.maxConversationLength, 'number', 'runner.maxConversationLength', 'agent_config');
220
+ if (config.runner.maxConversationLength <= 0) {
221
+ throw new ConfigurationError("runner.maxConversationLength must be greater than 0", {
222
+ maxConversationLength: config.runner.maxConversationLength
223
+ });
224
+ }
225
+
217
226
  logger.debug(`Agent ${config.metadata.name} configuration validated successfully`);
218
227
 
219
228
  } catch (error) {
@@ -200,7 +200,7 @@ export async function build(
200
200
 
201
201
  // Create the executor function
202
202
  const executor = async function(state, contents, run = 0) {
203
- logger.info(`Running agent ${agentName} (run ${run}/${maxRuns}), conversation length: ${contents.length}`);
203
+ logger.info(`Running agent ${agentName} (run ${run}/${maxRuns}), conversation length: ${contents.getRawConversation().length}`);
204
204
 
205
205
  // Emit run event
206
206
  await emit(hooks, 'executorRun', {
@@ -222,7 +222,8 @@ export async function build(
222
222
  });
223
223
 
224
224
  // Return the last message as the result
225
- return contents[contents.length - 1];
225
+ const rawConversation = contents.getRawConversation();
226
+ return rawConversation[rawConversation.length - 1];
226
227
  }
227
228
 
228
229
  try {
@@ -2,6 +2,7 @@ import { build, makeToolsAndHandoffsMap } from "./executor.js"
2
2
  import { logger } from "../utils/logger.js"
3
3
  import { Response, SessionStore } from "../index.js"
4
4
  import { createAgentRuntime } from "../transport/index.js"
5
+ import { Conversation } from "../utils/conversation.js"
5
6
 
6
7
  export async function AgentRuntime(agentConfig) {
7
8
  const {
@@ -72,7 +73,7 @@ export async function AgentRuntime(agentConfig) {
72
73
  // Load and merge session state and session data
73
74
  let storeState = {
74
75
  state: {},
75
- conversation: []
76
+ conversation: new Conversation()
76
77
  }
77
78
  if (store && sessionId) {
78
79
  const sessionStore = new SessionStore(storeStateSessionId)
@@ -85,7 +86,7 @@ export async function AgentRuntime(agentConfig) {
85
86
  storeState.state[key] = session[key]
86
87
  }
87
88
  storeState.conversation = _storeState.conversation
88
- logger.info(`Loaded session state for agent ${agentName} with session id ${storeStateSessionId}, current conversation length ${storeState.conversation.length}`);
89
+ logger.info(`Loaded session state for agent ${agentName} with session id ${storeStateSessionId}, current conversation length ${storeState.conversation.getRawConversation().length}`);
89
90
  }
90
91
 
91
92
  const formattedInput = typeof content === 'string' ? content : JSON.stringify(content);
@@ -107,9 +108,9 @@ export async function AgentRuntime(agentConfig) {
107
108
  await store.instance.connect()
108
109
  sessionStore.setConversation(storeState.conversation)
109
110
  sessionStore.setState(storeState.state)
110
- sessionStore.trimConversation(10)
111
+ sessionStore.trimConversation(runner?.maxConversationLength || 10)
111
112
  await sessionStore.dump(store.instance)
112
- logger.info(`Dumped session state for agent ${agentName} with session id ${storeStateSessionId}, current conversation length ${storeState.conversation.length}`);
113
+ logger.info(`Dumped session state for agent ${agentName} with session id ${storeStateSessionId}, current conversation length ${storeState.conversation.getRawConversation().length}`);
113
114
  }
114
115
 
115
116
  // Before returning, remove _ from the state
package/src/llm/base.js CHANGED
@@ -48,7 +48,7 @@ export class BaseLLM {
48
48
 
49
49
  /**
50
50
  * Add a user prompt to the conversation
51
- * @param {Array|Conversation} conversation - The conversation history
51
+ * @param {Conversation} conversation - The conversation history
52
52
  * @param {string} formattedPrompt - The formatted user prompt
53
53
  * @returns {Promise<void>}
54
54
  */
@@ -57,17 +57,14 @@ export class BaseLLM {
57
57
  promptPreview: formattedPrompt.substring(0, 100)
58
58
  });
59
59
 
60
- // Handle both raw array and Conversation object
61
- if (conversation instanceof Conversation) {
62
- // Subclasses should override this to add their specific format
63
- // But we'll add a generic format by default
64
- conversation.addUserMessage({
65
- role: 'user',
66
- content: formattedPrompt
67
- });
68
- }
69
- // For array, we rely on subclass implementations
70
- // Subclasses must implement appropriate conversation format
60
+ // conversation is now always a Conversation object
61
+ // Subclasses should override this to add their specific format
62
+ // But we'll add a generic format by default
63
+ conversation.addUserMessage({
64
+ role: 'user',
65
+ content: formattedPrompt
66
+ });
67
+ // Removed logic for plain arrays
71
68
  }
72
69
 
73
70
  /**
package/src/llm/gemini.js CHANGED
@@ -43,12 +43,8 @@ class GeminiLLM extends BaseLLM {
43
43
  async callModel(llmClientConfig, context) {
44
44
  const { client, toolsAndHandoffsMap, conversation } = context;
45
45
 
46
- // Get raw conversation if it's a Conversation object
47
- const conversationArray = conversation instanceof Conversation
48
- ? conversation.getRawConversation()
49
- : conversation;
50
-
51
- const input = { ...llmClientConfig, contents: conversationArray };
46
+ // conversation is always a Conversation object
47
+ const input = { ...llmClientConfig, contents: conversation.getRawConversation() };
52
48
 
53
49
  // Configure tools if provided
54
50
  if (input.config !== undefined && input.tools !== undefined) {
@@ -60,7 +56,7 @@ class GeminiLLM extends BaseLLM {
60
56
 
61
57
  logger.debug('Calling Gemini model', {
62
58
  model: input.model,
63
- conversationLength: conversationArray.length,
59
+ conversationLength: conversation.getRawConversation().length,
64
60
  toolsCount: toolsAndHandoffsMap.tools.length
65
61
  });
66
62
 
@@ -95,7 +91,7 @@ class GeminiLLM extends BaseLLM {
95
91
  * Handle a specific tool call from Gemini response
96
92
  * @param {Object} toolCall - The tool call to process
97
93
  * @param {Object} state - Current application state
98
- * @param {Array|Conversation} conversation - The conversation history
94
+ * @param {Conversation} conversation - The conversation history
99
95
  * @param {Object} toolsAndHandoffsMap - Map of available tools
100
96
  */
101
97
  async handleToolCall(toolCall, state, conversation, toolsAndHandoffsMap) {
@@ -105,7 +101,6 @@ class GeminiLLM extends BaseLLM {
105
101
  try {
106
102
  const result = await super.executeToolCall(toolCall, name, args, state, toolsAndHandoffsMap);
107
103
 
108
- // Add function call and response to conversation in Gemini-specific format
109
104
  const function_response_part = {
110
105
  name: name,
111
106
  response: typeof result === 'string' ? { answer: result } : result
@@ -114,16 +109,10 @@ class GeminiLLM extends BaseLLM {
114
109
  const functionCallMessage = { role: 'model', parts: [{ functionCall: toolCall }] };
115
110
  const functionResponseMessage = { role: 'user', parts: [{ functionResponse: function_response_part }] };
116
111
 
117
- if (conversation instanceof Conversation) {
118
- conversation.addFunctionCall(functionCallMessage);
119
- conversation.addFunctionResult(functionResponseMessage);
120
- } else {
121
- conversation.push(functionCallMessage);
122
- conversation.push(functionResponseMessage);
123
- }
112
+ conversation.addFunctionCall(functionCallMessage);
113
+ conversation.addFunctionResult(functionResponseMessage);
124
114
 
125
115
  } catch (error) {
126
- // Return error as function response in Gemini-specific format
127
116
  const errorResponse = {
128
117
  name: name,
129
118
  response: { error: error.message }
@@ -132,36 +121,28 @@ class GeminiLLM extends BaseLLM {
132
121
  const functionCallMessage = { role: 'model', parts: [{ functionCall: toolCall }] };
133
122
  const functionResponseMessage = { role: 'user', parts: [{ functionResponse: errorResponse }] };
134
123
 
135
- if (conversation instanceof Conversation) {
136
- conversation.addFunctionCall(functionCallMessage);
137
- conversation.addFunctionResult(functionResponseMessage);
138
- } else {
139
- conversation.push(functionCallMessage);
140
- conversation.push(functionResponseMessage);
141
- }
124
+ conversation.addFunctionCall(functionCallMessage);
125
+ conversation.addFunctionResult(functionResponseMessage);
142
126
  }
143
127
  }
144
128
 
145
129
  /**
146
130
  * Processes the model response, handling text responses and function calls
147
131
  * @param {Object} state - Current application state
148
- * @param {Array|Conversation} conversation - The conversation history
132
+ * @param {Conversation} conversation - The conversation history
149
133
  * @param {Object} toolsAndHandoffsMap - Map of available tools
150
134
  * @param {Object} response - The model response to process
151
135
  * @returns {Promise<string|null>} Text response or null if processing tool calls
152
136
  */
153
137
  async onResponse(state, conversation, toolsAndHandoffsMap, response) {
154
- // Handle simple text response
155
138
  logger.info('Gemini onResponse', {text: response.text !== undefined, functionCalls: response.functionCalls?.length });
156
139
 
157
140
  if (response.text !== undefined) {
158
- if (conversation instanceof Conversation) {
159
- const modelResponse = {
160
- role: 'model',
161
- parts: [{ text: response.text }]
162
- };
163
- conversation.addModelResponse(modelResponse);
164
- }
141
+ const modelResponse = {
142
+ role: 'model',
143
+ parts: [{ text: response.text }]
144
+ };
145
+ conversation.addModelResponse(modelResponse);
165
146
  }
166
147
 
167
148
  if (response.text !== undefined && (response.functionCalls === undefined || response.functionCalls.length === 0)) {
@@ -186,23 +167,19 @@ class GeminiLLM extends BaseLLM {
186
167
 
187
168
  /**
188
169
  * Adds a user prompt to the conversation
189
- * @param {Array|Conversation} conversation - The conversation history
170
+ * @param {Conversation} conversation - The conversation history
190
171
  * @param {string} formattedPrompt - The formatted user prompt
191
172
  * @returns {Promise<void>}
192
173
  */
193
174
  async prompt(conversation, formattedPrompt) {
194
- await super.prompt(conversation, formattedPrompt);
175
+ // super.prompt(conversation, formattedPrompt); // Removed to avoid duplicate/conflicting user message addition
195
176
 
196
177
  const userMessage = {
197
178
  role: 'user',
198
179
  parts: [{ text: formattedPrompt }]
199
180
  };
200
181
 
201
- if (conversation instanceof Conversation) {
202
- conversation.addUserMessage(userMessage);
203
- } else {
204
- conversation.push(userMessage);
205
- }
182
+ conversation.addUserMessage(userMessage);
206
183
  }
207
184
  }
208
185
 
package/src/llm/gpt.js CHANGED
@@ -45,14 +45,12 @@ class OpenAILLM extends BaseLLM {
45
45
  const input = { ...llmClientConfig };
46
46
  input.tools = toolsAndHandoffsMap.tools;
47
47
 
48
- // Get raw conversation if it's a Conversation object
49
- input.input = conversation instanceof Conversation
50
- ? conversation.getRawConversation()
51
- : conversation;
48
+ // conversation is always a Conversation object
49
+ input.input = conversation.getRawConversation();
52
50
 
53
51
  logger.debug('Calling OpenAI model', {
54
52
  model: input.model,
55
- conversationLength: input.input.length,
53
+ conversationLength: conversation.getRawConversation().length,
56
54
  toolsCount: toolsAndHandoffsMap.tools.length
57
55
  });
58
56
  //console.log(JSON.stringify(input, null, 2))
@@ -81,7 +79,7 @@ class OpenAILLM extends BaseLLM {
81
79
  * Handle a specific tool call from OpenAI response
82
80
  * @param {Object} toolCall - The tool call to process
83
81
  * @param {Object} state - Current application state
84
- * @param {Array|Conversation} conversation - The conversation history
82
+ * @param {Conversation} conversation - The conversation history
85
83
  * @param {Object} toolsAndHandoffsMap - Map of available tools
86
84
  */
87
85
  async handleToolCall(toolCall, state, conversation, toolsAndHandoffsMap) {
@@ -98,11 +96,7 @@ class OpenAILLM extends BaseLLM {
98
96
  const result = await super.executeToolCall(toolCall, name, args, state, toolsAndHandoffsMap);
99
97
 
100
98
  // Add function call to conversation
101
- if (conversation instanceof Conversation) {
102
- conversation.addFunctionCall(toolCall);
103
- } else {
104
- conversation.push(toolCall);
105
- }
99
+ conversation.addFunctionCall(toolCall);
106
100
 
107
101
  const resultString = typeof result === 'string' ? result : JSON.stringify(result);
108
102
 
@@ -118,40 +112,27 @@ class OpenAILLM extends BaseLLM {
118
112
  };
119
113
 
120
114
  // Add function result to conversation
121
- if (conversation instanceof Conversation) {
122
- conversation.addFunctionResult(functionOutput);
123
- } else {
124
- conversation.push(functionOutput);
125
- }
115
+ conversation.addFunctionResult(functionOutput);
126
116
  } catch (error) {
127
117
  logger.error(`Error executing tool "${toolCall.name}"`, { error });
128
118
 
129
119
  // Add error as function output
130
- if (conversation instanceof Conversation) {
131
- conversation.addFunctionCall(toolCall);
132
-
133
- const errorOutput = {
134
- type: "function_call_output",
135
- call_id: toolCall.call_id,
136
- output: JSON.stringify({ error: error.message })
137
- };
138
-
139
- conversation.addFunctionResult(errorOutput);
140
- } else {
141
- conversation.push(toolCall);
142
- conversation.push({
143
- type: "function_call_output",
144
- call_id: toolCall.call_id,
145
- output: JSON.stringify({ error: error.message })
146
- });
147
- }
120
+ conversation.addFunctionCall(toolCall);
121
+
122
+ const errorOutput = {
123
+ type: "function_call_output",
124
+ call_id: toolCall.call_id,
125
+ output: JSON.stringify({ error: error.message })
126
+ };
127
+
128
+ conversation.addFunctionResult(errorOutput);
148
129
  }
149
130
  }
150
131
 
151
132
  /**
152
133
  * Processes the model response, handling text responses and function calls
153
134
  * @param {Object} state - Current application state
154
- * @param {Array|Conversation} conversation - The conversation history
135
+ * @param {Conversation} conversation - The conversation history
155
136
  * @param {Object} toolsAndHandoffsMap - Map of available tools
156
137
  * @param {Object} response - The model response to process
157
138
  * @returns {Promise<string|null>} Text response or null if processing tool calls
@@ -160,13 +141,11 @@ class OpenAILLM extends BaseLLM {
160
141
  if (response.output_text !== undefined && response.output_text.length > 0 ) {
161
142
  logger.debug('OpenAI response contains text, returning directly');
162
143
 
163
- // Add model response to conversation if using Conversation object
164
- if (conversation instanceof Conversation) {
165
- conversation.addModelResponse({
166
- role: 'assistant',
167
- content: response.output_text
168
- });
169
- }
144
+ // Add model response to conversation
145
+ conversation.addModelResponse({
146
+ role: 'assistant',
147
+ content: response.output_text
148
+ });
170
149
 
171
150
  return response.output_text;
172
151
  }
@@ -181,11 +160,7 @@ class OpenAILLM extends BaseLLM {
181
160
 
182
161
  // Add reasoning to conversation
183
162
  for (const res of reasoning) {
184
- if (conversation instanceof Conversation) {
185
- conversation.addModelResponse(res);
186
- } else {
187
- conversation.push(res);
188
- }
163
+ conversation.addModelResponse(res);
189
164
  }
190
165
 
191
166
  // Process all tool calls sequentially
@@ -198,23 +173,12 @@ class OpenAILLM extends BaseLLM {
198
173
 
199
174
  /**
200
175
  * Adds a user prompt to the conversation
201
- * @param {Array|Conversation} conversation - The conversation history
176
+ * @param {Conversation} conversation - The conversation history
202
177
  * @param {string} formattedPrompt - The formatted user prompt
203
178
  * @returns {Promise<void>}
204
179
  */
205
180
  async prompt(conversation, formattedPrompt) {
206
181
  await super.prompt(conversation, formattedPrompt);
207
-
208
- const userMessage = {
209
- role: 'user',
210
- content: formattedPrompt
211
- };
212
-
213
- if (conversation instanceof Conversation) {
214
- conversation.addUserMessage(userMessage);
215
- } else {
216
- conversation.push(userMessage);
217
- }
218
182
  }
219
183
  }
220
184
 
@@ -9,7 +9,7 @@ export function session (id) {
9
9
 
10
10
  return {
11
11
  query: async function (agentInstance, input) {
12
- return await agentInstance.query(state, conversationManager.getRawConversation(), input)
12
+ return await agentInstance.query(state, conversationManager, input)
13
13
  },
14
14
  setState: function (_state) {
15
15
  state = _state
@@ -26,16 +26,15 @@ export function session (id) {
26
26
  conversationManager.trim(elementsToKeep)
27
27
  },
28
28
  setConversation: function (_conversation) {
29
- // Support both array and Conversation objects
30
29
  if (_conversation instanceof Conversation) {
31
30
  conversationManager = _conversation;
32
- } else if (Array.isArray(_conversation)) {
33
- conversationManager.importFromArray(_conversation);
31
+ } else {
32
+ console.warn("Attempted to set conversation with an invalid type. Expected a Conversation object.");
33
+ conversationManager = new Conversation();
34
34
  }
35
35
  },
36
36
  getConversation: function () {
37
- // Return raw conversation for backward compatibility
38
- return conversationManager.getRawConversation()
37
+ return conversationManager
39
38
  },
40
39
  getConversationManager: function() {
41
40
  return conversationManager
@@ -48,26 +47,23 @@ export function session (id) {
48
47
  // Handle both old-style conversation array and new-style serialized Conversation
49
48
  if (parsedState.conversationData) {
50
49
  conversationManager.deserialize(parsedState.conversationData)
51
- } else if (Array.isArray(parsedState.conversation)) {
52
- conversationManager.importFromArray(parsedState.conversation)
53
50
  }
54
51
 
55
52
  state = parsedState.state || {}
56
53
  return {
57
54
  // Return raw conversation for backward compatibility
58
- conversation: conversationManager.getRawConversation(),
55
+ conversation: conversationManager,
59
56
  state: state
60
57
  }
61
58
  }
62
59
  return {
63
- conversation: [],
60
+ conversation: new Conversation(), // Ensure it's a Conversation object
64
61
  state: {}
65
62
  }
66
63
  },
67
64
  dump: async function (stateStore) {
68
65
  return await stateStore.set(id, JSON.stringify({
69
- // Keep conversation array for backward compatibility
70
- conversation: conversationManager.getRawConversation(),
66
+ // Keep conversation array for backward compatibility - REMOVED
71
67
  // Add serialized conversation data with metadata
72
68
  conversationData: conversationManager.serialize(),
73
69
  state: state