agentnet 0.0.1 → 0.0.3

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 { AgentLoaderFile, AgentClient, NatsIO, Bindings, Message, PostgresStore } from "../index.js"
1
+ import { AgentLoaderFile, AgentClient, NatsIO, Bindings, Message, PostgresStore, RedisStore, MemoryStore } from "../../src/index.js"
2
2
 
3
3
  // NatsIO instance
4
4
  const io = NatsIO({
@@ -6,16 +6,16 @@ const io = NatsIO({
6
6
  })
7
7
 
8
8
  // Load the agents from the YAML file
9
- const agents = await AgentLoaderFile('./src/examples/agents-smartness.yaml', {
10
- bindings: { [Bindings.NatsIO]: io, [Bindings.Postgres]: PostgresStore() }
9
+ const agents = await AgentLoaderFile('./examples/smartness/agents-smartness.yaml', {
10
+ bindings: { [Bindings.NatsIO]: io, [Bindings.Postgres]: PostgresStore(), [Bindings.Redis]: RedisStore(), [Bindings.Memory]: MemoryStore() }
11
11
  })
12
12
 
13
13
  // Entry point
14
- const agentSmartness = await agents.smartnessAgent
14
+ const agentSmartness = await agents.entrypoint
15
15
  await agentSmartness.compile()
16
16
 
17
17
  // Accomodation agent
18
- const agentAccomodation = await agents.accomodationAgent
18
+ const agentAccomodation = await agents.accomodation
19
19
  agentAccomodation.tools.getRoomsListTool.bind(async (state, input) => {
20
20
  return { 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." }
21
21
  })
@@ -29,21 +29,21 @@ agentAccomodation.prompt(async (state, input) => {
29
29
  await agentAccomodation.compile()
30
30
 
31
31
  // Booking agent
32
- const agentBooking = await agents.bookingAgent
32
+ const agentBooking = await agents.booking
33
33
  agentBooking.tools.bookRoomTool.bind(async (state, input) => {
34
34
  return { answer: "The room " + input.roomName + " has been booked for the dates " + input.checkinDate + " to " + input.checkoutDate + "." }
35
35
  })
36
36
  await agentBooking.compile()
37
37
 
38
38
  // Hotel review agent
39
- const agentHotelReview = await agents.hotelReviewAgent
39
+ const agentHotelReview = await agents.review
40
40
  agentHotelReview.tools.getHotelReviewsTool.bind(async (state, input) => {
41
41
  return { answer: "The hotel " + input.hotelName + " has a 4.5 star rating and a 9.2 out of 10 guest satisfaction score." }
42
42
  })
43
43
  await agentHotelReview.compile()
44
44
 
45
45
  // Pricing agent
46
- const agentPricing = await agents.pricingAgent
46
+ const agentPricing = await agents.pricing
47
47
  agentPricing.tools.getPricingTool.bind(async (state, input) => {
48
48
  return { answer: "The room " + input.roomName + " has a price of 200€ per night." }
49
49
  })
@@ -60,7 +60,7 @@ const message = new Message({
60
60
  id: "67a71e42-a7d8-1db2-ad17-64e1c8546b21"
61
61
  }
62
62
  })
63
- const res = await agentClient.queryIo(io, 'smartnessAgent', message)
63
+ const res = await agentClient.queryIo(io, 'entrypoint', message)
64
64
  console.log("=======\n", res.getContent())
65
65
  console.log("=======\n", res.getSession())
66
66
  //const res2 = await agentClient.queryIo(io, 'smartnessAgent', "Quanto costa la camera doppia del Flora per il 10-05-2025 per due persone? Prenotala se costa meno di 100€ la camera double con vista mare per il 10-05-2025 al hotel Flora")
package/jest.config.js ADDED
@@ -0,0 +1 @@
1
+ export default { transform: {} }
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "agentnet",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "description": "Agent library used by Smartness",
6
6
  "main": "index.js",
7
7
  "scripts": {
8
- "test": "echo \"Error: no test specified\" && exit 1"
8
+ "test": "node --experimental-vm-modules ./node_modules/.bin/jest --rootDir=src/tests"
9
9
  },
10
10
  "author": "",
11
11
  "license": "ISC",
@@ -15,9 +15,12 @@
15
15
  "@nats-io/transport-node": "^3.0.2",
16
16
  "colors": "^1.4.0",
17
17
  "openai": "^4.97.0",
18
- "pg": "^8.15.6",
18
+ "pg-promise": "^11.13.0",
19
19
  "redis": "^5.0.1",
20
20
  "uuid": "^11.1.0",
21
21
  "yaml": "^2.7.1"
22
+ },
23
+ "devDependencies": {
24
+ "jest": "^29.7.0"
22
25
  }
23
26
  }
@@ -2,6 +2,18 @@ import fs from 'fs'
2
2
  import { parse } from 'yaml'
3
3
  import { Gemini } from "../index.js"
4
4
  import { Agent } from "./agent.js"
5
+ import { logger } from "../utils/logger.js"
6
+ import { ConfigurationError } from "../errors/index.js"
7
+ import { validateApiVersion, DEFAULT_API_VERSION, API_VERSIONS } from '../utils/version.js'
8
+
9
+ /**
10
+ * Version handlers for different API versions
11
+ */
12
+ const VERSION_HANDLERS = {
13
+ 'smartagent.io/v1alpha1': processV1Alpha1Definition,
14
+ 'agentnet.io/v1alpha1': processV1Alpha1Definition
15
+ // Additional version handlers can be added here as the API evolves
16
+ };
5
17
 
6
18
  /**
7
19
  * Loads agent definitions from a YAML file
@@ -47,10 +59,10 @@ function parseAgentDefinitionsFromYaml(yamlContent) {
47
59
  if (isValidAgentDefinition(definition)) {
48
60
  agentDefinitions.push(definition);
49
61
  } else {
50
- console.warn("Skipping invalid or non-AgentDefinition document in YAML.");
62
+ logger.warn("Skipping invalid or non-AgentDefinition document in YAML.");
51
63
  }
52
64
  } catch (error) {
53
- console.warn(`Failed to parse YAML document: ${error.message}`);
65
+ logger.warn(`Failed to parse YAML document: ${error.message}`);
54
66
  }
55
67
  }
56
68
 
@@ -66,6 +78,58 @@ function isValidAgentDefinition(definition) {
66
78
  return definition && definition.kind === 'AgentDefinition' && definition.spec;
67
79
  }
68
80
 
81
+ /**
82
+ * Gets the appropriate version handler for a definition
83
+ * @param {object} definition - Agent definition
84
+ * @returns {Function} Version handler function
85
+ * @throws {ConfigurationError} If version is unsupported
86
+ */
87
+ function getVersionHandler(definition) {
88
+ // Validate the API version using our utility
89
+ const versionData = validateApiVersion(definition);
90
+ const apiVersion = versionData.version;
91
+
92
+ // Get the appropriate handler for this version
93
+ const handler = VERSION_HANDLERS[apiVersion];
94
+
95
+ if (!handler) {
96
+ throw new ConfigurationError(
97
+ `No implementation handler for apiVersion '${apiVersion}'`,
98
+ { apiVersion, supportedHandlers: Object.keys(VERSION_HANDLERS) }
99
+ );
100
+ }
101
+
102
+ return handler;
103
+ }
104
+
105
+ /**
106
+ * Process a v1alpha1 agent definition
107
+ * @param {object} definition - Agent definition
108
+ * @param {object} agentBuilder - Agent builder
109
+ * @param {object} bindings - IO and store bindings
110
+ * @returns {object} Processed agent interface and tool map
111
+ */
112
+ async function processV1Alpha1Definition(definition, agentBuilder, bindings) {
113
+ const spec = definition.spec;
114
+
115
+ // Add apiVersion to agent metadata
116
+ agentBuilder.setMetadata({
117
+ ...agentBuilder._config.metadata,
118
+ apiVersion: definition.apiVersion || DEFAULT_API_VERSION
119
+ });
120
+
121
+ // Configure different aspects of the agent
122
+ agentBuilder = configureIO(agentBuilder, spec.io, bindings);
123
+ agentBuilder = configureStore(agentBuilder, spec.store, bindings);
124
+ agentBuilder = await configureLLM(agentBuilder, spec.llm);
125
+ agentBuilder = configureDiscoverySchemas(agentBuilder, spec.discoverySchemas);
126
+
127
+ // Set up tools
128
+ const toolMap = configureTools(agentBuilder, spec.tools);
129
+
130
+ return { agentBuilder, toolMap };
131
+ }
132
+
69
133
  /**
70
134
  * Loads an LLM provider instance
71
135
  * @param {string} providerName - Name of the provider
@@ -237,7 +301,6 @@ async function AgentLoader(agentsDefinitions, config = {}) {
237
301
  throw new Error(`Invalid agent definition: missing spec`);
238
302
  }
239
303
 
240
- const spec = definition.spec;
241
304
  const metadata = definition.metadata || {
242
305
  name: "default",
243
306
  description: "Agent from definition"
@@ -250,21 +313,21 @@ async function AgentLoader(agentsDefinitions, config = {}) {
250
313
  // Initialize agent builder with metadata
251
314
  let agentBuilder = Agent().setMetadata(metadata);
252
315
 
253
- // Configure different aspects of the agent
254
- agentBuilder = configureIO(agentBuilder, spec.io, bindings);
255
- agentBuilder = configureStore(agentBuilder, spec.store, bindings);
256
- agentBuilder = await configureLLM(agentBuilder, spec.llm);
257
- agentBuilder = configureDiscoverySchemas(agentBuilder, spec.discoverySchemas);
316
+ // Get the appropriate version handler
317
+ const versionHandler = getVersionHandler(definition);
258
318
 
259
- // Set up tools
260
- const toolMap = configureTools(agentBuilder, spec.tools);
319
+ // Process according to API version
320
+ const { agentBuilder: updatedBuilder, toolMap } =
321
+ await versionHandler(definition, agentBuilder, bindings);
261
322
 
262
323
  // Create the agent interface
263
- loadedAgents[metadata.name] = createAgentInterface(agentBuilder, toolMap);
324
+ loadedAgents[metadata.name] = createAgentInterface(updatedBuilder, toolMap);
325
+
326
+ logger.info(`Agent '${metadata.name}' loaded successfully with apiVersion: ${definition.apiVersion || DEFAULT_API_VERSION}`);
264
327
 
265
328
  } catch (error) {
266
329
  const agentName = definition.metadata?.name || 'Unnamed Agent';
267
- console.error(`Failed to load agent "${agentName}": ${error.message}`);
330
+ logger.error(`Failed to load agent "${agentName}": ${error.message}`, { error });
268
331
  // Optional: decide whether to throw or just log and continue
269
332
  // throw error;
270
333
  }
@@ -22,7 +22,9 @@ const DEFAULT_HOOKS = {
22
22
  const DEFAULT_CONFIG = {
23
23
  metadata: {
24
24
  name: "default",
25
- description: "A default agent"
25
+ namespace: "default",
26
+ description: "A default agent",
27
+ apiVersion: "agentnet/v1alpha1" // Default API version
26
28
  },
27
29
  runner: {
28
30
  maxRuns: 10
@@ -39,9 +41,11 @@ const AGENT_CONFIG_SCHEMA = {
39
41
  type: 'object',
40
42
  properties: {
41
43
  name: { type: 'string' },
42
- description: { type: 'string' }
44
+ namespace: { type: 'string' },
45
+ description: { type: 'string' },
46
+ apiVersion: { type: 'string' }
43
47
  },
44
- required: ['name']
48
+ required: ['name', 'namespace']
45
49
  },
46
50
  llm: {
47
51
  type: 'object',
@@ -118,6 +122,12 @@ export function Agent() {
118
122
  metadata: config.metadata
119
123
  });
120
124
  }
125
+
126
+ if (!config.metadata.namespace.trim()) {
127
+ throw new ConfigurationError("Agent namespace cannot be empty", {
128
+ metadata: config.metadata
129
+ });
130
+ }
121
131
 
122
132
  // LLM API validation
123
133
  if (typeof config.llm.api !== 'object' || config.llm.api === null) {
@@ -1,14 +1,14 @@
1
1
  import { build, makeToolsAndHandoffsMap } from "./executor.js"
2
- import { NatsIOAgentRuntime } from "./runtimes/nats.js"
3
2
  import { logger } from "../utils/logger.js"
4
3
  import { Response, SessionStore } from "../index.js"
4
+ import { createAgentRuntime } from "../transport/index.js"
5
5
 
6
6
  export async function AgentRuntime(agentConfig) {
7
7
  const {
8
8
  toolsAndHandoffsMap,
9
9
  hooks,
10
10
  store,
11
- metadata: { name: agentName },
11
+ metadata: { name: agentName, namespace },
12
12
  llm: { api: llmApi, config: llmConfig },
13
13
  runner,
14
14
  toolsSchemas: tools,
@@ -19,10 +19,14 @@ export async function AgentRuntime(agentConfig) {
19
19
  } = agentConfig
20
20
 
21
21
  // Initialize IO runtime
22
- const natsInterfaces = ioInterfaces.filter(x => x.type === 'NatsIO')
23
- const { handleTask, discoveredAgents } = await NatsIOAgentRuntime(
22
+ const transportType = ioInterfaces.length > 0 ? ioInterfaces[0].type.replace('IO', '').toLowerCase() : 'nats';
23
+ logger.info(`Creating agent runtime with transport type: ${transportType}`);
24
+
25
+ const { handleTask, discoveredAgents } = await createAgentRuntime(
26
+ transportType,
27
+ namespace,
24
28
  agentName,
25
- natsInterfaces,
29
+ ioInterfaces,
26
30
  discoverySchemas
27
31
  )
28
32
 
@@ -94,7 +98,6 @@ export async function AgentRuntime(agentConfig) {
94
98
 
95
99
  // Execute agent runtime
96
100
  const result = await taskFunction(storeState.state, storeState.conversation, promptContent);
97
-
98
101
  // Process result through response hook
99
102
  const responseMessage = await response(storeState.state, storeState.conversation, result);
100
103
  // Save session state and session data
@@ -0,0 +1,131 @@
1
+ import { logger } from '../utils/logger.js'
2
+ import { LLMError } from '../errors/index.js'
3
+
4
+ /**
5
+ * Base class for LLM implementations
6
+ * Provides common functionality and defines required interface
7
+ */
8
+ export class BaseLLM {
9
+ /**
10
+ * @param {string} providerType - The LLM provider type (e.g., 'gemini', 'openai')
11
+ */
12
+ constructor(providerType) {
13
+ this.type = providerType;
14
+ }
15
+
16
+ /**
17
+ * Initialize and get the LLM client
18
+ * @returns {Promise<any>} Initialized LLM client
19
+ * @throws {LLMError} If initialization fails
20
+ */
21
+ async getClient() {
22
+ throw new Error('getClient() must be implemented by subclasses');
23
+ }
24
+
25
+ /**
26
+ * Call the LLM model with the provided configuration and context
27
+ * @param {Object} config - LLM-specific configuration
28
+ * @param {Object} context - Context containing client, tools map and conversation
29
+ * @returns {Promise<Object>} The model response
30
+ * @throws {LLMError} If the API call fails
31
+ */
32
+ async callModel(config, context) {
33
+ throw new Error('callModel() must be implemented by subclasses');
34
+ }
35
+
36
+ /**
37
+ * Process the model response, handling text responses and function calls
38
+ * @param {Object} state - Current application state
39
+ * @param {Array} conversation - The conversation history
40
+ * @param {Object} toolsAndHandoffsMap - Map of available tools
41
+ * @param {Object} response - The model response to process
42
+ * @returns {Promise<string|null>} Text response or null if processing tool calls
43
+ */
44
+ async onResponse(state, conversation, toolsAndHandoffsMap, response) {
45
+ throw new Error('onResponse() must be implemented by subclasses');
46
+ }
47
+
48
+ /**
49
+ * Add a user prompt to the conversation
50
+ * @param {Array} conversation - The conversation history
51
+ * @param {string} formattedPrompt - The formatted user prompt
52
+ * @returns {Promise<void>}
53
+ */
54
+ async prompt(conversation, formattedPrompt) {
55
+ logger.debug('Adding user prompt to conversation', {
56
+ promptPreview: formattedPrompt.substring(0, 100)
57
+ });
58
+
59
+ // Subclasses must implement appropriate conversation format
60
+ }
61
+
62
+ /**
63
+ * Check if required API key is set in environment variables
64
+ * @param {string} keyName - Environment variable name for the API key
65
+ * @returns {void}
66
+ * @throws {LLMError} If API key is not set
67
+ */
68
+ checkApiKey(keyName) {
69
+ if (!process.env[keyName]) {
70
+ throw new LLMError(
71
+ `${keyName} environment variable is not set`,
72
+ this.type
73
+ );
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Execute a tool call from the model response
79
+ * @param {Object} toolCall - The tool call to execute
80
+ * @param {Object} state - Current application state
81
+ * @param {Array} conversation - The conversation history
82
+ * @param {Object} toolsAndHandoffsMap - Map of available tools
83
+ * @returns {Promise<any>} Result of the tool execution
84
+ */
85
+ async executeToolCall(toolCall, name, args, state, conversation, toolsAndHandoffsMap) {
86
+ logger.debug(`Executing tool from ${this.type}`, {
87
+ toolName: name,
88
+ argsPreview: JSON.stringify(args).substring(0, 100)
89
+ });
90
+
91
+ try {
92
+ if (!toolsAndHandoffsMap[name] || !toolsAndHandoffsMap[name].function) {
93
+ throw new Error(`Tool "${name}" not found or has no function implementation`);
94
+ }
95
+
96
+ let result = null;
97
+ if (toolsAndHandoffsMap[name].type === 'handoff') {
98
+ result = await toolsAndHandoffsMap[name].function(conversation, state, args);
99
+ // Process handoff results if needed
100
+ this.processHandoffResult(result, state);
101
+ } else {
102
+ result = await toolsAndHandoffsMap[name].function(state, args);
103
+ }
104
+
105
+ logger.debug('Tool execution successful', { toolName: name });
106
+ return result;
107
+
108
+ } catch (error) {
109
+ logger.error(`Error executing tool "${name}"`, { error });
110
+ throw error;
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Process the result of a handoff operation
116
+ * @param {string} result - The JSON string result from handoff
117
+ * @param {Object} state - The state to update
118
+ */
119
+ processHandoffResult(result, state) {
120
+ try {
121
+ const resultParsed = JSON.parse(result);
122
+ if (resultParsed.session) {
123
+ Object.entries(resultParsed.session).forEach(([key, value]) => {
124
+ state[key] = value;
125
+ });
126
+ }
127
+ } catch (error) {
128
+ logger.error('Failed to process handoff result', { error });
129
+ }
130
+ }
131
+ }