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 +69 -1
- package/examples/simple/simple.js +2 -2
- package/jest.config.js +1 -0
- package/package.json +5 -2
- package/src/agent/agent-loader.js +75 -12
- package/src/agent/agent.js +4 -2
- package/src/agent/runtime.js +7 -4
- package/src/llm/base.js +131 -0
- package/src/llm/gemini.js +137 -122
- package/src/llm/gpt.js +131 -109
- package/src/tests/agent.test.js +350 -0
- package/src/tools/migrate-version.js +250 -0
- package/src/transport/README.md +123 -0
- package/src/transport/base.js +237 -0
- package/src/transport/index.js +89 -0
- package/src/transport/kafka.js +474 -0
- package/src/transport/nats.js +521 -0
- package/src/transport/rabbitmq.js +722 -0
- package/src/transport/redis.js +532 -0
- package/src/utils/version.js +212 -0
- package/src/agent/runtimes/nats.js +0 -506
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Transport Module
|
|
2
|
+
|
|
3
|
+
This module provides a common interface for different messaging transports used in the agent communication system.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
The transport module follows an abstract factory pattern with the following components:
|
|
8
|
+
|
|
9
|
+
- **Transport Interface**: A base class that defines common methods for all transports
|
|
10
|
+
- **TransportMessage**: A base class for messages exchanged between agents
|
|
11
|
+
- **DiscoveryMessage**: A specialized message class for agent capability discovery
|
|
12
|
+
- **Concrete Implementations**: Implementations of the Transport interface for different messaging systems
|
|
13
|
+
|
|
14
|
+
## Supported Transports
|
|
15
|
+
|
|
16
|
+
- NATS
|
|
17
|
+
- Kafka (template implementation)
|
|
18
|
+
- Redis (template implementation)
|
|
19
|
+
- RabbitMQ (template implementation)
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
### Creating a Transport
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
import { createTransport } from './transport/index.js';
|
|
27
|
+
|
|
28
|
+
// Create a specific transport
|
|
29
|
+
const natsTransport = createTransport('nats');
|
|
30
|
+
const kafkaTransport = createTransport('kafka');
|
|
31
|
+
const redisTransport = createTransport('redis');
|
|
32
|
+
const rabbitMqTransport = createTransport('rabbitmq');
|
|
33
|
+
|
|
34
|
+
// Connect to the messaging system
|
|
35
|
+
await natsTransport.connect(config);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Creating an Agent Runtime
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
import { createAgentRuntime } from './transport/index.js';
|
|
42
|
+
|
|
43
|
+
// Create a runtime for an agent with a specific transport
|
|
44
|
+
const runtime = await createAgentRuntime('nats', namespace, agentName, ioInterfaces, discoverySchemas);
|
|
45
|
+
|
|
46
|
+
// Use the runtime to handle tasks
|
|
47
|
+
await runtime.handleTask(async (message) => {
|
|
48
|
+
// Process the task
|
|
49
|
+
return responseMessage;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Access discovered agents
|
|
53
|
+
const discoveredAgents = runtime.discoveredAgents;
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Using the Transport Directly
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
import { createTransport } from './transport/index.js';
|
|
60
|
+
|
|
61
|
+
// Create and connect a transport
|
|
62
|
+
const transport = createTransport('nats');
|
|
63
|
+
await transport.connect(config);
|
|
64
|
+
|
|
65
|
+
// Publish a message
|
|
66
|
+
await transport.publish('topic', message);
|
|
67
|
+
|
|
68
|
+
// Subscribe to a topic
|
|
69
|
+
await transport.subscribe('topic', handleMessage);
|
|
70
|
+
|
|
71
|
+
// Send a request and wait for a response
|
|
72
|
+
const response = await transport.request('target', message);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Extending with New Transports
|
|
76
|
+
|
|
77
|
+
To add a new transport:
|
|
78
|
+
|
|
79
|
+
1. Create a new file (e.g., `myTransport.js`) that implements the Transport interface
|
|
80
|
+
2. Create a factory function that returns a new instance of your transport
|
|
81
|
+
3. Register the factory in the `transportFactories` map in `index.js`
|
|
82
|
+
4. Create a runtime function and register it in the `runtimeFactories` map
|
|
83
|
+
|
|
84
|
+
Example:
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
// myTransport.js
|
|
88
|
+
import { Transport } from './base.js';
|
|
89
|
+
|
|
90
|
+
export class MyTransport extends Transport {
|
|
91
|
+
// Implement all required methods
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function createMyTransport() {
|
|
95
|
+
return new MyTransport();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function MyTransportIOAgentRuntime(namespace, agentName, ioInterfaces, discoverySchemas) {
|
|
99
|
+
// Implementation
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// index.js - update the factory maps
|
|
103
|
+
const transportFactories = {
|
|
104
|
+
// ...existing transports
|
|
105
|
+
'mytransport': createMyTransport
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const runtimeFactories = {
|
|
109
|
+
// ...existing runtimes
|
|
110
|
+
'mytransport': MyTransportIOAgentRuntime
|
|
111
|
+
};
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Common Interface
|
|
115
|
+
|
|
116
|
+
All transports must implement these key methods:
|
|
117
|
+
|
|
118
|
+
- `connect(config)`: Connect to the messaging system
|
|
119
|
+
- `disconnect()`: Disconnect from the messaging system
|
|
120
|
+
- `publish(topic, message)`: Publish a message to a topic
|
|
121
|
+
- `subscribe(topic, options)`: Subscribe to a topic
|
|
122
|
+
- `request(target, message, options)`: Send a request and wait for a response
|
|
123
|
+
- `createRuntime(namespace, agentName, discoverySchemas, config)`: Create a runtime for agent communication
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Transport Interface
|
|
3
|
+
* Defines common functionality across different transport implementations (NATS, Kafka, RabbitMQ, Redis, etc.)
|
|
4
|
+
*/
|
|
5
|
+
import { TransportError } from '../errors/index.js';
|
|
6
|
+
import { logger } from '../utils/logger.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Base TransportMessage class that can be extended by specific transport implementations
|
|
10
|
+
*/
|
|
11
|
+
export class TransportMessage {
|
|
12
|
+
constructor(type, payload) {
|
|
13
|
+
this.type = type;
|
|
14
|
+
this.payload = payload;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
serialize() {
|
|
18
|
+
return JSON.stringify({
|
|
19
|
+
type: this.type,
|
|
20
|
+
payload: this.payload
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static fromString(data) {
|
|
25
|
+
try {
|
|
26
|
+
const parsed = JSON.parse(data);
|
|
27
|
+
return new TransportMessage(parsed.type, parsed.payload);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
throw new Error(`Failed to parse message: ${error.message}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Discovery Message format for agent discovery
|
|
36
|
+
*/
|
|
37
|
+
export class DiscoveryMessage extends TransportMessage {
|
|
38
|
+
constructor(namespace, agentName, schemas) {
|
|
39
|
+
if (!namespace) throw new Error('Namespace is required');
|
|
40
|
+
if (!agentName) throw new Error('Agent name is required');
|
|
41
|
+
if (!Array.isArray(schemas)) throw new Error('Schemas must be an array');
|
|
42
|
+
|
|
43
|
+
const payload = {
|
|
44
|
+
network: `${namespace}.${agentName}`,
|
|
45
|
+
agentName: agentName,
|
|
46
|
+
schemas: schemas
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
super('discovery', payload);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get network() {
|
|
53
|
+
return this.payload.network;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
get agentName() {
|
|
57
|
+
return this.payload.agentName;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get schemas() {
|
|
61
|
+
return this.payload.schemas;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static fromString(data) {
|
|
65
|
+
const message = TransportMessage.fromString(data);
|
|
66
|
+
|
|
67
|
+
if (message.type !== 'discovery') {
|
|
68
|
+
throw new Error('Not a discovery message');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Extract namespace from network (format: namespace.agentName)
|
|
72
|
+
const networkParts = message.payload.network.split('.');
|
|
73
|
+
if (networkParts.length !== 2) {
|
|
74
|
+
throw new Error('Invalid network format in discovery message');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const namespace = networkParts[0];
|
|
78
|
+
return new DiscoveryMessage(
|
|
79
|
+
namespace,
|
|
80
|
+
message.payload.agentName,
|
|
81
|
+
message.payload.schemas
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
static isValid(payload) {
|
|
86
|
+
return (
|
|
87
|
+
payload &&
|
|
88
|
+
typeof payload === 'object' &&
|
|
89
|
+
payload.type === 'discovery' &&
|
|
90
|
+
typeof payload.payload.network === 'string' &&
|
|
91
|
+
typeof payload.payload.agentName === 'string' &&
|
|
92
|
+
Array.isArray(payload.payload.schemas)
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Base Transport Interface - all transport implementations should implement this interface
|
|
99
|
+
*/
|
|
100
|
+
export class Transport {
|
|
101
|
+
/**
|
|
102
|
+
* Create a new transport instance
|
|
103
|
+
* @param {string} transportType - The type of transport (e.g., 'NATS', 'Kafka')
|
|
104
|
+
*/
|
|
105
|
+
constructor(transportType) {
|
|
106
|
+
this.transportType = transportType;
|
|
107
|
+
this.connected = false;
|
|
108
|
+
this.intervals = [];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Connect to the transport
|
|
113
|
+
* @param {Object} config - Configuration options
|
|
114
|
+
* @returns {Promise<any>} - The connection instance
|
|
115
|
+
*/
|
|
116
|
+
async connect(config) {
|
|
117
|
+
throw new Error('Method connect() must be implemented by subclass');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Disconnect from the transport
|
|
122
|
+
* @returns {Promise<void>}
|
|
123
|
+
*/
|
|
124
|
+
async disconnect() {
|
|
125
|
+
// Clean up intervals
|
|
126
|
+
this.intervals.forEach(clearInterval);
|
|
127
|
+
this.intervals = [];
|
|
128
|
+
|
|
129
|
+
this.connected = false;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Publish a message to a topic/channel
|
|
134
|
+
* @param {string} topic - The topic/channel to publish to
|
|
135
|
+
* @param {string|Buffer|Object} message - The message to publish
|
|
136
|
+
* @returns {Promise<void>}
|
|
137
|
+
*/
|
|
138
|
+
async publish(topic, message) {
|
|
139
|
+
throw new Error('Method publish() must be implemented by subclass');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Subscribe to a topic/channel
|
|
144
|
+
* @param {string} topic - The topic/channel to subscribe to
|
|
145
|
+
* @param {Object} options - Subscription options
|
|
146
|
+
* @returns {Promise<any>} - The subscription instance
|
|
147
|
+
*/
|
|
148
|
+
async subscribe(topic, options = {}) {
|
|
149
|
+
throw new Error('Method subscribe() must be implemented by subclass');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Send a request and wait for a response
|
|
154
|
+
* @param {string} target - The target to send the request to
|
|
155
|
+
* @param {string|Buffer|Object} message - The message to send
|
|
156
|
+
* @param {Object} options - Request options
|
|
157
|
+
* @returns {Promise<any>} - The response
|
|
158
|
+
*/
|
|
159
|
+
async request(target, message, options = {}) {
|
|
160
|
+
throw new Error('Method request() must be implemented by subclass');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Set up heartbeat to announce agent capabilities
|
|
165
|
+
* @param {string} topic - The topic to publish heartbeats to
|
|
166
|
+
* @param {string} namespace - The agent namespace
|
|
167
|
+
* @param {string} agentName - The agent name
|
|
168
|
+
* @param {Array} schemas - The agent capability schemas
|
|
169
|
+
* @param {number} interval - The heartbeat interval in milliseconds
|
|
170
|
+
* @returns {number} - The interval ID
|
|
171
|
+
*/
|
|
172
|
+
setupHeartbeat(topic, namespace, agentName, schemas, interval = 1000) {
|
|
173
|
+
const heartbeatInterval = setInterval(async () => {
|
|
174
|
+
try {
|
|
175
|
+
const discoveryMessage = new DiscoveryMessage(namespace, agentName, schemas);
|
|
176
|
+
await this.publish(topic, discoveryMessage.serialize());
|
|
177
|
+
} catch (error) {
|
|
178
|
+
logger.error(`Failed to publish heartbeat for ${agentName}`, {
|
|
179
|
+
error,
|
|
180
|
+
transportType: this.transportType
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}, interval);
|
|
184
|
+
|
|
185
|
+
this.intervals.push(heartbeatInterval);
|
|
186
|
+
return heartbeatInterval;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Creates a runtime for agent communication
|
|
191
|
+
* @param {string} namespace - The agent namespace
|
|
192
|
+
* @param {string} agentName - The agent name
|
|
193
|
+
* @param {Array} discoverySchemas - The agent capability schemas for discovery
|
|
194
|
+
* @param {Object} config - Additional configuration
|
|
195
|
+
* @returns {Promise<Object>} - The runtime { handleTask, discoveredAgents }
|
|
196
|
+
*/
|
|
197
|
+
async createRuntime(namespace, agentName, discoverySchemas, config = {}) {
|
|
198
|
+
throw new Error('Method createRuntime() must be implemented by subclass');
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Safely connect to a transport with retry logic
|
|
204
|
+
* @param {Transport} transport - The transport instance
|
|
205
|
+
* @param {Object} config - Connection configuration
|
|
206
|
+
* @param {Object} options - Retry options
|
|
207
|
+
* @returns {Promise<any>} - The connection instance
|
|
208
|
+
*/
|
|
209
|
+
export async function safeConnect(transport, config, options = {}) {
|
|
210
|
+
const { maxRetries = 5 } = options;
|
|
211
|
+
|
|
212
|
+
let attempt = 0;
|
|
213
|
+
let lastError = null;
|
|
214
|
+
|
|
215
|
+
while (attempt < maxRetries) {
|
|
216
|
+
try {
|
|
217
|
+
attempt++;
|
|
218
|
+
return await transport.connect(config);
|
|
219
|
+
} catch (error) {
|
|
220
|
+
lastError = error;
|
|
221
|
+
logger.warn(`Transport connect attempt ${attempt}/${maxRetries} failed, retrying...`, {
|
|
222
|
+
transportType: transport.transportType,
|
|
223
|
+
error: error.message
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// Exponential backoff
|
|
227
|
+
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
|
|
228
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
throw new TransportError(
|
|
233
|
+
`Failed to connect after ${maxRetries} attempts: ${lastError?.message}`,
|
|
234
|
+
transport.transportType,
|
|
235
|
+
{ details: lastError?.message }
|
|
236
|
+
);
|
|
237
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport Factory Module
|
|
3
|
+
* Provides a unified interface to create transport instances
|
|
4
|
+
*/
|
|
5
|
+
import { Transport } from './base.js';
|
|
6
|
+
import { createNatsTransport, NatsIOAgentRuntime } from './nats.js';
|
|
7
|
+
import { createKafkaTransport, KafkaIOAgentRuntime } from './kafka.js';
|
|
8
|
+
import { createRedisTransport, RedisIOAgentRuntime } from './redis.js';
|
|
9
|
+
import { createRabbitMQTransport, RabbitMQIOAgentRuntime } from './rabbitmq.js';
|
|
10
|
+
import { TransportError } from '../errors/index.js';
|
|
11
|
+
import { logger } from '../utils/logger.js';
|
|
12
|
+
|
|
13
|
+
// Map of transport types to their factory functions
|
|
14
|
+
const transportFactories = {
|
|
15
|
+
'nats': createNatsTransport,
|
|
16
|
+
'kafka': createKafkaTransport,
|
|
17
|
+
'redis': createRedisTransport,
|
|
18
|
+
'rabbitmq': createRabbitMQTransport,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Map of transport types to their runtime functions
|
|
22
|
+
const runtimeFactories = {
|
|
23
|
+
'nats': NatsIOAgentRuntime,
|
|
24
|
+
'kafka': KafkaIOAgentRuntime,
|
|
25
|
+
'redis': RedisIOAgentRuntime,
|
|
26
|
+
'rabbitmq': RabbitMQIOAgentRuntime,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Create a transport instance based on the specified type
|
|
31
|
+
* @param {string} type - The transport type (e.g., 'nats', 'kafka', 'redis', 'rabbitmq')
|
|
32
|
+
* @returns {Transport} - A transport instance
|
|
33
|
+
* @throws {TransportError} - If the transport type is not supported
|
|
34
|
+
*/
|
|
35
|
+
export function createTransport(type) {
|
|
36
|
+
const factoryFn = transportFactories[type.toLowerCase()];
|
|
37
|
+
|
|
38
|
+
if (!factoryFn) {
|
|
39
|
+
throw new TransportError(
|
|
40
|
+
`Unsupported transport type: ${type}`,
|
|
41
|
+
'TransportFactory',
|
|
42
|
+
{ supportedTypes: Object.keys(transportFactories) }
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return factoryFn();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Create a transport runtime for an agent
|
|
51
|
+
* @param {string} type - The transport type (e.g., 'nats', 'kafka', 'redis', 'rabbitmq')
|
|
52
|
+
* @param {string} namespace - The agent namespace
|
|
53
|
+
* @param {string} agentName - The agent name
|
|
54
|
+
* @param {Array} ioInterfaces - The IO interfaces
|
|
55
|
+
* @param {Array} discoverySchemas - The agent capability schemas for discovery
|
|
56
|
+
* @returns {Promise<Object>} - The runtime { handleTask, discoveredAgents }
|
|
57
|
+
* @throws {TransportError} - If the transport type is not supported
|
|
58
|
+
*/
|
|
59
|
+
export async function createAgentRuntime(type, namespace, agentName, ioInterfaces, discoverySchemas) {
|
|
60
|
+
const runtimeFn = runtimeFactories[type.toLowerCase()];
|
|
61
|
+
|
|
62
|
+
if (!runtimeFn) {
|
|
63
|
+
throw new TransportError(
|
|
64
|
+
`Unsupported transport runtime type: ${type}`,
|
|
65
|
+
'TransportFactory',
|
|
66
|
+
{ supportedTypes: Object.keys(runtimeFactories) }
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
return await runtimeFn(namespace, agentName, ioInterfaces, discoverySchemas);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
logger.error(`Failed to create ${type} agent runtime`, { error, agentName });
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Export base classes and interfaces for extensibility
|
|
80
|
+
*/
|
|
81
|
+
export * from './base.js';
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Export specific transport implementations
|
|
85
|
+
*/
|
|
86
|
+
export * from './nats.js';
|
|
87
|
+
export * from './kafka.js';
|
|
88
|
+
export * from './redis.js';
|
|
89
|
+
export * from './rabbitmq.js';
|