agentnet 0.0.1

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/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "agentnet",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "description": "Agent library used by Smartness",
6
+ "main": "index.js",
7
+ "scripts": {
8
+ "test": "echo \"Error: no test specified\" && exit 1"
9
+ },
10
+ "author": "",
11
+ "license": "ISC",
12
+ "dependencies": {
13
+ "@google/genai": "^0.12.0",
14
+ "@nats-io/nats-core": "^3.0.2",
15
+ "@nats-io/transport-node": "^3.0.2",
16
+ "colors": "^1.4.0",
17
+ "openai": "^4.97.0",
18
+ "pg": "^8.15.6",
19
+ "redis": "^5.0.1",
20
+ "uuid": "^11.1.0",
21
+ "yaml": "^2.7.1"
22
+ }
23
+ }
@@ -0,0 +1,274 @@
1
+ import fs from 'fs'
2
+ import { parse } from 'yaml'
3
+ import { Gemini } from "../index.js"
4
+ import { Agent } from "./agent.js"
5
+
6
+ /**
7
+ * Loads agent definitions from a YAML file
8
+ * @param {string} path - Path to the YAML file
9
+ * @param {object} config - Configuration options
10
+ * @returns {Promise<object>} Map of loaded agents
11
+ */
12
+ export async function AgentLoaderFile(path, config) {
13
+ try {
14
+ const yamlFileContent = fs.readFileSync(path, 'utf8');
15
+ const agentDefinitions = parseAgentDefinitionsFromYaml(yamlFileContent);
16
+ return await AgentLoader(agentDefinitions, config);
17
+ } catch (error) {
18
+ throw new Error(`Failed to load agents from file ${path}: ${error.message}`);
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Loads agent definitions from JSON
24
+ * @param {object} json - JSON definition
25
+ * @param {object} config - Configuration options
26
+ * @returns {Promise<object>} Map of loaded agents
27
+ */
28
+ export async function AgentLoaderJSON(json, config) {
29
+ return await AgentLoader([json], config);
30
+ }
31
+
32
+ /**
33
+ * Parses agent definitions from YAML content
34
+ * @param {string} yamlContent - YAML content to parse
35
+ * @returns {Array} Array of agent definitions
36
+ */
37
+ function parseAgentDefinitionsFromYaml(yamlContent) {
38
+ const agentDefsYaml = yamlContent.split(/^---$/m)
39
+ .map(s => s.trim())
40
+ .filter(s => s);
41
+
42
+ const agentDefinitions = [];
43
+
44
+ for (const agentDef of agentDefsYaml) {
45
+ try {
46
+ const definition = parse(agentDef);
47
+ if (isValidAgentDefinition(definition)) {
48
+ agentDefinitions.push(definition);
49
+ } else {
50
+ console.warn("Skipping invalid or non-AgentDefinition document in YAML.");
51
+ }
52
+ } catch (error) {
53
+ console.warn(`Failed to parse YAML document: ${error.message}`);
54
+ }
55
+ }
56
+
57
+ return agentDefinitions;
58
+ }
59
+
60
+ /**
61
+ * Checks if a definition is a valid agent definition
62
+ * @param {object} definition - Definition to validate
63
+ * @returns {boolean} Whether definition is valid
64
+ */
65
+ function isValidAgentDefinition(definition) {
66
+ return definition && definition.kind === 'AgentDefinition' && definition.spec;
67
+ }
68
+
69
+ /**
70
+ * Loads an LLM provider instance
71
+ * @param {string} providerName - Name of the provider
72
+ * @returns {object} LLM provider instance
73
+ */
74
+ async function loadLlmProvider(providerName) {
75
+ if (providerName === 'Gemini') {
76
+ return Gemini;
77
+ }
78
+
79
+ try {
80
+ return global[providerName] || await import(providerName);
81
+ } catch (error) {
82
+ throw new Error(`LLM Provider "${providerName}" could not be loaded: ${error.message}`);
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Configures IO for an agent
88
+ * @param {object} agentBuilder - Agent builder instance
89
+ * @param {Array} ioDefinitions - IO definitions
90
+ * @param {object} bindings - IO bindings
91
+ * @returns {object} Updated agent builder
92
+ */
93
+ function configureIO(agentBuilder, ioDefinitions, bindings) {
94
+ if (!ioDefinitions || !Array.isArray(ioDefinitions)) {
95
+ return agentBuilder;
96
+ }
97
+
98
+ for (const ioDef of ioDefinitions) {
99
+ if (!bindings[ioDef.type]) {
100
+ throw new Error(`Missing binding for IO type: ${ioDef.type}`);
101
+ }
102
+
103
+ if (ioDef.type === 'NatsIO') {
104
+ agentBuilder = agentBuilder.addIO(bindings[ioDef.type], ioDef);
105
+ } else {
106
+ throw new Error(`Unsupported IO type: ${ioDef.type}`);
107
+ }
108
+ }
109
+
110
+ return agentBuilder;
111
+ }
112
+
113
+ /**
114
+ * Configures store for an agent
115
+ * @param {object} agentBuilder - Agent builder instance
116
+ * @param {object} storeSpec - Store specification
117
+ * @param {object} bindings - Store bindings
118
+ * @returns {object} Updated agent builder
119
+ */
120
+ function configureStore(agentBuilder, storeSpec, bindings) {
121
+ if (!storeSpec) {
122
+ return agentBuilder;
123
+ }
124
+
125
+ const storeType = storeSpec.type;
126
+
127
+ if (!bindings[storeType]) {
128
+ throw new Error(`Missing binding for store type: ${storeType}`);
129
+ }
130
+
131
+ // Add store to agent builder
132
+ return agentBuilder.withStore(bindings[storeType], storeSpec);
133
+ }
134
+
135
+ /**
136
+ * Configures LLM for an agent
137
+ * @param {object} agentBuilder - Agent builder instance
138
+ * @param {object} llmSpec - LLM specification
139
+ * @returns {Promise<object>} Updated agent builder
140
+ */
141
+ async function configureLLM(agentBuilder, llmSpec) {
142
+ if (!llmSpec) {
143
+ return agentBuilder;
144
+ }
145
+
146
+ const llmProviderInstance = await loadLlmProvider(llmSpec.provider);
147
+
148
+ return agentBuilder.withLLM(llmProviderInstance, {
149
+ model: llmSpec.model,
150
+ systemInstruction: llmSpec.systemInstruction,
151
+ config: llmSpec.config
152
+ });
153
+ }
154
+
155
+ /**
156
+ * Configures discovery schemas for an agent
157
+ * @param {object} agentBuilder - Agent builder instance
158
+ * @param {Array} schemas - Discovery schemas
159
+ * @returns {object} Updated agent builder
160
+ */
161
+ function configureDiscoverySchemas(agentBuilder, schemas) {
162
+ if (!schemas || !Array.isArray(schemas)) {
163
+ return agentBuilder;
164
+ }
165
+
166
+ for (const schema of schemas) {
167
+ agentBuilder = agentBuilder.addDiscoverySchema(schema);
168
+ }
169
+
170
+ return agentBuilder;
171
+ }
172
+
173
+ /**
174
+ * Configures tools for an agent
175
+ * @param {object} agentBuilder - Agent builder instance
176
+ * @param {Array} toolsSpec - Tools specification
177
+ * @returns {object} Tool map
178
+ */
179
+ function configureTools(agentBuilder, toolsSpec) {
180
+ const toolMap = {};
181
+
182
+ if (!toolsSpec || !Array.isArray(toolsSpec)) {
183
+ return toolMap;
184
+ }
185
+
186
+ for (const toolDef of toolsSpec) {
187
+ agentBuilder._config.toolsSchemas[toolDef.name] = {
188
+ name: toolDef.name,
189
+ schema: toolDef,
190
+ function: null
191
+ };
192
+
193
+ toolMap[toolDef.name] = {
194
+ bind: (handlerFunction) => {
195
+ agentBuilder._config.toolsSchemas[toolDef.name].function = handlerFunction;
196
+ }
197
+ };
198
+ }
199
+
200
+ return toolMap;
201
+ }
202
+
203
+ /**
204
+ * Creates an agent interface
205
+ * @param {object} agentBuilder - Agent builder instance
206
+ * @param {object} toolMap - Tool map
207
+ * @returns {object} Agent interface
208
+ */
209
+ function createAgentInterface(agentBuilder, toolMap) {
210
+ return {
211
+ tools: toolMap,
212
+ prompt: (callback) => {
213
+ agentBuilder._config.on.prompt = callback;
214
+ },
215
+ response: (callback) => {
216
+ agentBuilder._config.on.response = callback;
217
+ },
218
+ compile: async () => {
219
+ return await agentBuilder.compile();
220
+ }
221
+ };
222
+ }
223
+
224
+ /**
225
+ * Main agent loading function
226
+ * @param {Array} agentsDefinitions - Array of agent definitions
227
+ * @param {object} config - Configuration options
228
+ * @returns {Promise<object>} Map of loaded agents
229
+ */
230
+ async function AgentLoader(agentsDefinitions, config = {}) {
231
+ const bindings = config.bindings || {};
232
+ const loadedAgents = {};
233
+
234
+ for (const definition of agentsDefinitions) {
235
+ try {
236
+ if (!definition.spec) {
237
+ throw new Error(`Invalid agent definition: missing spec`);
238
+ }
239
+
240
+ const spec = definition.spec;
241
+ const metadata = definition.metadata || {
242
+ name: "default",
243
+ description: "Agent from definition"
244
+ };
245
+
246
+ if (!metadata.name) {
247
+ throw new Error("Agent definition is missing metadata.name");
248
+ }
249
+
250
+ // Initialize agent builder with metadata
251
+ let agentBuilder = Agent().setMetadata(metadata);
252
+
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);
258
+
259
+ // Set up tools
260
+ const toolMap = configureTools(agentBuilder, spec.tools);
261
+
262
+ // Create the agent interface
263
+ loadedAgents[metadata.name] = createAgentInterface(agentBuilder, toolMap);
264
+
265
+ } catch (error) {
266
+ const agentName = definition.metadata?.name || 'Unnamed Agent';
267
+ console.error(`Failed to load agent "${agentName}": ${error.message}`);
268
+ // Optional: decide whether to throw or just log and continue
269
+ // throw error;
270
+ }
271
+ }
272
+
273
+ return loadedAgents;
274
+ }
@@ -0,0 +1,416 @@
1
+ import { AgentRuntime } from "./runtime.js"
2
+ import {
3
+ validateRequired,
4
+ validateType,
5
+ validateObject,
6
+ validateEnum
7
+ } from "../utils/validation.js"
8
+ import { CompilationError, ConfigurationError } from "../errors/index.js"
9
+ import { logger } from "../utils/logger.js"
10
+
11
+ /**
12
+ * Default hooks for agent events
13
+ */
14
+ const DEFAULT_HOOKS = {
15
+ prompt: async (state, formattedInput) => formattedInput,
16
+ response: async (state, conversation, result) => result
17
+ }
18
+
19
+ /**
20
+ * Default agent configuration
21
+ */
22
+ const DEFAULT_CONFIG = {
23
+ metadata: {
24
+ name: "default",
25
+ description: "A default agent"
26
+ },
27
+ runner: {
28
+ maxRuns: 10
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Schema for agent configuration validation
34
+ */
35
+ const AGENT_CONFIG_SCHEMA = {
36
+ type: 'object',
37
+ properties: {
38
+ metadata: {
39
+ type: 'object',
40
+ properties: {
41
+ name: { type: 'string' },
42
+ description: { type: 'string' }
43
+ },
44
+ required: ['name']
45
+ },
46
+ llm: {
47
+ type: 'object',
48
+ properties: {
49
+ api: { type: 'object' },
50
+ config: { type: 'object' }
51
+ },
52
+ required: ['api']
53
+ },
54
+ store: {
55
+ type: 'object',
56
+ properties: {
57
+ instance: { type: 'object' },
58
+ config: { type: 'object' }
59
+ }
60
+ },
61
+ io: { type: 'array' },
62
+ handoffs: { type: 'array' },
63
+ discoverySchemas: { type: 'array' },
64
+ toolsAndHandoffsMap: {
65
+ type: 'object',
66
+ properties: {
67
+ tools: { type: 'array' }
68
+ }
69
+ },
70
+ toolsSchemas: { type: 'object' },
71
+ runner: {
72
+ type: 'object',
73
+ properties: {
74
+ maxRuns: { type: 'number' }
75
+ }
76
+ },
77
+ on: { type: 'object' }
78
+ },
79
+ required: ['metadata', 'llm']
80
+ }
81
+
82
+ /**
83
+ * Creates a new Agent builder
84
+ * @returns {Object} Agent builder interface
85
+ */
86
+ export function Agent() {
87
+ // Initialize agent configuration
88
+ const config = {
89
+ metadata: { ...DEFAULT_CONFIG.metadata },
90
+ llm: {},
91
+ store: null,
92
+ io: [],
93
+ handoffs: [],
94
+ discoverySchemas: [],
95
+ hooks: null,
96
+ toolsAndHandoffsMap: {
97
+ tools: []
98
+ },
99
+ toolsSchemas: {},
100
+ runner: { ...DEFAULT_CONFIG.runner },
101
+ on: { ...DEFAULT_HOOKS }
102
+ }
103
+
104
+ /**
105
+ * Validates that required configuration is present and well-formed
106
+ * @throws {ConfigurationError} If configuration is invalid
107
+ */
108
+ function validateConfiguration() {
109
+ try {
110
+ // Validate overall structure against schema
111
+ validateObject(config, AGENT_CONFIG_SCHEMA, 'agent_config');
112
+
113
+ // Additional specific validations
114
+
115
+ // Metadata validation
116
+ if (!config.metadata.name.trim()) {
117
+ throw new ConfigurationError("Agent name cannot be empty", {
118
+ metadata: config.metadata
119
+ });
120
+ }
121
+
122
+ // LLM API validation
123
+ if (typeof config.llm.api !== 'object' || config.llm.api === null) {
124
+ throw new ConfigurationError("LLM API must be a valid object", {
125
+ api: config.llm.api
126
+ });
127
+ }
128
+
129
+ if (!config.llm.api.getClient || typeof config.llm.api.getClient !== 'function') {
130
+ throw new ConfigurationError("LLM API must have a getClient method", {
131
+ apiMethods: Object.keys(config.llm.api)
132
+ });
133
+ }
134
+
135
+ if (!config.llm.api.callModel || typeof config.llm.api.callModel !== 'function') {
136
+ throw new ConfigurationError("LLM API must have a callModel method", {
137
+ apiMethods: Object.keys(config.llm.api)
138
+ });
139
+ }
140
+
141
+ // Store validation if present
142
+ if (config.store) {
143
+ if (typeof config.store.instance !== 'object' || config.store.instance === null) {
144
+ throw new ConfigurationError("Store instance must be a valid object", {
145
+ store: config.store
146
+ });
147
+ }
148
+
149
+ if (!config.store.instance.connect || typeof config.store.instance.connect !== 'function') {
150
+ throw new ConfigurationError("Store instance must have a connect method", {
151
+ storeInstanceMethods: Object.keys(config.store.instance)
152
+ });
153
+ }
154
+ }
155
+
156
+ // IO validation
157
+ if (config.io.length > 0) {
158
+ config.io.forEach((io, index) => {
159
+ if (!io.type) {
160
+ throw new ConfigurationError(`IO interface at index ${index} has no type`, {
161
+ io
162
+ });
163
+ }
164
+
165
+ if (!io.instance) {
166
+ throw new ConfigurationError(`IO interface ${io.type} at index ${index} has no instance`, {
167
+ io
168
+ });
169
+ }
170
+
171
+ if (!io.config) {
172
+ throw new ConfigurationError(`IO interface ${io.type} at index ${index} has no configuration`, {
173
+ io
174
+ });
175
+ }
176
+ });
177
+ }
178
+
179
+ // Tool schemas validation
180
+ const toolSchemas = Object.values(config.toolsSchemas);
181
+ toolSchemas.forEach(schema => {
182
+ if (!schema.name) {
183
+ throw new ConfigurationError("Tool schema must have a name", {
184
+ schema
185
+ });
186
+ }
187
+ });
188
+
189
+ // Event handlers validation
190
+ Object.entries(config.on).forEach(([eventName, handler]) => {
191
+ if (typeof handler !== 'function') {
192
+ throw new ConfigurationError(`Event handler for '${eventName}' must be a function`, {
193
+ eventName,
194
+ handlerType: typeof handler
195
+ });
196
+ }
197
+ });
198
+
199
+ // Runner validation
200
+ validateType(config.runner.maxRuns, 'number', 'runner.maxRuns', 'agent_config');
201
+ if (config.runner.maxRuns <= 0) {
202
+ throw new ConfigurationError("runner.maxRuns must be greater than 0", {
203
+ maxRuns: config.runner.maxRuns
204
+ });
205
+ }
206
+
207
+ logger.debug(`Agent ${config.metadata.name} configuration validated successfully`);
208
+
209
+ } catch (error) {
210
+ if (error instanceof ConfigurationError) {
211
+ logger.error(`Agent configuration validation failed: ${error.message}`, {
212
+ configContext: error.configContext,
213
+ agentName: config.metadata?.name || 'unknown'
214
+ });
215
+ throw error;
216
+ }
217
+
218
+ // Wrap other errors
219
+ logger.error(`Agent configuration validation failed with unexpected error`, {
220
+ error,
221
+ agentName: config.metadata?.name || 'unknown'
222
+ });
223
+
224
+ throw new ConfigurationError(
225
+ `Agent configuration validation failed: ${error.message}`,
226
+ { cause: error }
227
+ );
228
+ }
229
+ }
230
+
231
+ /**
232
+ * Adds an IO interface to the agent
233
+ * @param {Object} instance - IO provider instance
234
+ * @param {Object} ioConfig - IO configuration
235
+ * @returns {Object} Agent builder for chaining
236
+ */
237
+ function addIO(instance, ioConfig) {
238
+ if (!instance || !instance.type) {
239
+ throw new ConfigurationError("IO instance must have a type", {
240
+ instance: instance ? Object.keys(instance) : null
241
+ });
242
+ }
243
+
244
+ config.io.push({
245
+ type: instance.type,
246
+ instance: instance,
247
+ config: ioConfig || {}
248
+ });
249
+
250
+ return this;
251
+ }
252
+
253
+ /**
254
+ * Configures the LLM for the agent
255
+ * @param {Object} llmApi - LLM API provider
256
+ * @param {Object} llmConfig - LLM configuration
257
+ * @returns {Object} Agent builder for chaining
258
+ */
259
+ function withLLM(llmApi, llmConfig) {
260
+ if (!llmApi) {
261
+ throw new ConfigurationError("LLM API is required", {
262
+ provided: llmApi
263
+ });
264
+ }
265
+
266
+ config.llm = {
267
+ api: llmApi,
268
+ config: llmConfig || {}
269
+ };
270
+
271
+ return this;
272
+ }
273
+
274
+ /**
275
+ * Configures the store for the agent
276
+ * @param {Object} storeInstance - Store instance
277
+ * @param {Object} storeConfig - Store configuration
278
+ * @returns {Object} Agent builder for chaining
279
+ */
280
+ function withStore(storeInstance, storeConfig) {
281
+ if (!storeInstance) {
282
+ throw new ConfigurationError("Store instance is required", {
283
+ provided: storeInstance
284
+ });
285
+ }
286
+
287
+ config.store = {
288
+ instance: storeInstance,
289
+ config: storeConfig || {}
290
+ };
291
+
292
+ return this;
293
+ }
294
+
295
+ /**
296
+ * Registers an event handler
297
+ * @param {String} eventName - Event name
298
+ * @param {Function} handler - Event handler function
299
+ * @returns {Object} Agent builder for chaining
300
+ */
301
+ function on(eventName, handler) {
302
+ if (typeof handler !== 'function') {
303
+ throw new ConfigurationError(`Event handler for ${eventName} must be a function`, {
304
+ provided: typeof handler
305
+ });
306
+ }
307
+
308
+ config.on[eventName] = handler;
309
+ return this;
310
+ }
311
+
312
+ /**
313
+ * Adds a discovery schema
314
+ * @param {Object} schema - Discovery schema
315
+ * @returns {Object} Agent builder for chaining
316
+ */
317
+ function addDiscoverySchema(schema) {
318
+ if (!schema) {
319
+ throw new ConfigurationError("Discovery schema is required", {
320
+ provided: schema
321
+ });
322
+ }
323
+
324
+ config.discoverySchemas.push(schema);
325
+ return this;
326
+ }
327
+
328
+ /**
329
+ * Adds a tool schema
330
+ * @param {Object} schema - Tool schema
331
+ * @returns {Object} Agent builder for chaining
332
+ */
333
+ function addToolSchema(schema) {
334
+ if (!schema || !schema.name) {
335
+ throw new ConfigurationError("Tool schema must have a name", {
336
+ schema: schema
337
+ });
338
+ }
339
+
340
+ config.toolsSchemas[schema.name] = schema;
341
+ return this;
342
+ }
343
+
344
+ /**
345
+ * Sets agent metadata
346
+ * @param {Object} metadata - Agent metadata
347
+ * @returns {Object} Agent builder for chaining
348
+ */
349
+ function setMetadata(metadata) {
350
+ if (!metadata) {
351
+ throw new ConfigurationError("Metadata is required", {
352
+ provided: metadata
353
+ });
354
+ }
355
+
356
+ config.metadata = {
357
+ ...config.metadata,
358
+ ...metadata
359
+ };
360
+
361
+ return this;
362
+ }
363
+
364
+ /**
365
+ * Gets all registered tool schemas
366
+ * @returns {Object} Map of tool schemas
367
+ */
368
+ function getToolsSchemas() {
369
+ return { ...config.toolsSchemas };
370
+ }
371
+
372
+ /**
373
+ * Compiles the agent configuration into a runnable agent
374
+ * @returns {Promise<Object>} Compiled agent interface
375
+ */
376
+ async function compile() {
377
+ // Validate configuration before compiling
378
+ validateConfiguration();
379
+
380
+ try {
381
+ logger.info(`Compiling agent ${config.metadata.name}`);
382
+ const runtime = await AgentRuntime(config);
383
+
384
+ return {
385
+ query: runtime
386
+ };
387
+ } catch (error) {
388
+ logger.error(`Agent compilation error: ${error.message}`, {
389
+ agentName: config.metadata.name,
390
+ error
391
+ });
392
+
393
+ throw new CompilationError(
394
+ `Failed to compile agent ${config.metadata.name}: ${error.message}`,
395
+ config.metadata.name,
396
+ error
397
+ );
398
+ }
399
+ }
400
+
401
+ // Return the agent builder interface
402
+ return {
403
+ addIO,
404
+ withLLM,
405
+ withStore,
406
+ on,
407
+ addDiscoverySchema,
408
+ addToolSchema,
409
+ compile,
410
+ setMetadata,
411
+ getToolsSchemas,
412
+
413
+ // Expose config for backward compatibility
414
+ _config: config
415
+ };
416
+ }