agentnet 0.0.2 → 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.
package/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  - [Introduction](#introduction)
6
6
  - [Installation](#installation)
7
+ - [API Keys Configuration](#api-keys-configuration)
7
8
  - [Super Simple Example (Quick Start)](#super-simple-example-quick-start)
8
9
  - [Declarative Agent Definitions (YAML & JavaScript)](#declarative-agent-definitions-yaml--javascript)
9
10
  - [State Management](#state-management)
@@ -41,6 +42,32 @@ Key aspects include:
41
42
  npm install agentnet
42
43
  ```
43
44
 
45
+ ## API Keys Configuration
46
+
47
+ Agentnet requires API keys for accessing the LLM providers you plan to use. The keys should be set as environment variables:
48
+
49
+ - **Gemini**: Set `GEMINI_API_KEY` for using Google's Gemini models
50
+ - **OpenAI**: Set `OPENAI_API_KEY` for using OpenAI's GPT models
51
+
52
+ You can set these environment variables in your deployment environment or use a `.env` file with a package like `dotenv`:
53
+
54
+ ```javascript
55
+ // In your main file, before importing agentnet
56
+ import dotenv from 'dotenv';
57
+ dotenv.config();
58
+
59
+ // Now the API keys are available to agentnet
60
+ import { Agent, Gemini } from "agentnet";
61
+ ```
62
+
63
+ Example `.env` file:
64
+ ```
65
+ GEMINI_API_KEY=your_gemini_api_key_here
66
+ OPENAI_API_KEY=your_openai_api_key_here
67
+ ```
68
+
69
+ > Note: If you try to use an LLM provider without setting the corresponding API key, Agentnet will throw an error indicating which environment variable is missing.
70
+
44
71
  ## Super Simple Example (Quick Start)
45
72
 
46
73
  Here's how you can quickly get an agent up and running:
@@ -88,7 +115,7 @@ Define agent metadata, LLM configurations, transport, tools, and discovery schem
88
115
 
89
116
  ```yaml
90
117
  ---
91
- apiVersion: agentnet.io/v1alpha1
118
+ apiVersion: agentnet/v1alpha1
92
119
  kind: AgentDefinition
93
120
  metadata:
94
121
  name: bookingAgent
@@ -131,6 +158,47 @@ spec:
131
158
  # parameters schema for the discovery...
132
159
  ```
133
160
 
161
+ ### API Versioning
162
+
163
+ Agentnet supports API versioning to maintain backwards compatibility while evolving the platform:
164
+
165
+ ```yaml
166
+ apiVersion: agentnet/v1alpha1 # Specify which API version this definition uses
167
+ ```
168
+
169
+ Currently supported API versions:
170
+
171
+ - `agentnet/v1alpha1`: Current API version
172
+
173
+ When creating agent definitions, you should specify which API version you're targeting. This allows Agentnet to:
174
+
175
+ 1. Apply the correct validation rules
176
+ 2. Handle differences in configuration format
177
+ 3. Maintain backward compatibility with older definitions
178
+ 4. Enable new features only available in newer versions
179
+
180
+ If you don't specify an `apiVersion`, Agentnet will default to `agentnet/v1alpha1` but will log a warning.
181
+
182
+ #### Version Migration Tool
183
+
184
+ Agentnet includes a command-line tool to help migrate your agent definitions to newer API versions:
185
+
186
+ ```bash
187
+ # Migrate a YAML file to the latest version
188
+ node src/tools/migrate-version.js ./agents.yaml
189
+
190
+ # Specify a target version
191
+ node src/tools/migrate-version.js ./agents.yaml --version agentnet.io/v1alpha1
192
+
193
+ # Write to a specific output file
194
+ node src/tools/migrate-version.js ./agents.yaml --output ./agents-new.yaml
195
+
196
+ # Just check if migration is needed without modifying
197
+ node src/tools/migrate-version.js ./agents.yaml --check
198
+ ```
199
+
200
+ This tool helps you keep your agent definitions up-to-date with the latest features while maintaining compatibility with the Agentnet platform.
201
+
134
202
  ### Dynamic Implementation (JavaScript)
135
203
 
136
204
  Load YAML definitions and bind tool implementations dynamically:
@@ -1,7 +1,7 @@
1
- import { AgentLoaderJSON, Message, Bindings, PostgresStore } from "../index.js"
1
+ import { AgentLoaderJSON, Message, Bindings, PostgresStore } from "../../src/index.js"
2
2
 
3
3
  const agentDefinition = {
4
- "apiVersion": "smartagent.io/v1alpha1",
4
+ "apiVersion": "agentnet/v1alpha1",
5
5
  "kind": "AgentDefinition",
6
6
  "metadata": {
7
7
  "name": "accomodationAgent",
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.2",
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",
@@ -19,5 +19,8 @@
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
  }
@@ -23,7 +23,8 @@ const DEFAULT_CONFIG = {
23
23
  metadata: {
24
24
  name: "default",
25
25
  namespace: "default",
26
- description: "A default agent"
26
+ description: "A default agent",
27
+ apiVersion: "agentnet/v1alpha1" // Default API version
27
28
  },
28
29
  runner: {
29
30
  maxRuns: 10
@@ -41,7 +42,8 @@ const AGENT_CONFIG_SCHEMA = {
41
42
  properties: {
42
43
  name: { type: 'string' },
43
44
  namespace: { type: 'string' },
44
- description: { type: 'string' }
45
+ description: { type: 'string' },
46
+ apiVersion: { type: 'string' }
45
47
  },
46
48
  required: ['name', 'namespace']
47
49
  },
@@ -1,7 +1,7 @@
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 {
@@ -19,11 +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,
24
27
  namespace,
25
28
  agentName,
26
- natsInterfaces,
29
+ ioInterfaces,
27
30
  discoverySchemas
28
31
  )
29
32
 
@@ -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
+ }