@agentforge/core 0.1.0
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 +147 -0
- package/dist/index.cjs +4048 -0
- package/dist/index.d.cts +4076 -0
- package/dist/index.d.ts +4076 -0
- package/dist/index.js +3905 -0
- package/package.json +68 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,4048 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
AgentError: () => AgentError,
|
|
34
|
+
BatchProcessor: () => BatchProcessor,
|
|
35
|
+
CircuitBreaker: () => CircuitBreaker,
|
|
36
|
+
ConnectionPool: () => ConnectionPool,
|
|
37
|
+
DatabasePool: () => DatabasePool,
|
|
38
|
+
HttpPool: () => HttpPool,
|
|
39
|
+
LogLevel: () => LogLevel,
|
|
40
|
+
ManagedTool: () => ManagedTool,
|
|
41
|
+
MemoryManager: () => MemoryManager,
|
|
42
|
+
MetricType: () => MetricType,
|
|
43
|
+
MiddlewareChain: () => MiddlewareChain,
|
|
44
|
+
MissingDescriptionError: () => MissingDescriptionError,
|
|
45
|
+
RegistryEvent: () => RegistryEvent,
|
|
46
|
+
TimeoutError: () => TimeoutError,
|
|
47
|
+
ToolBuilder: () => ToolBuilder,
|
|
48
|
+
ToolCategory: () => ToolCategory,
|
|
49
|
+
ToolCategorySchema: () => ToolCategorySchema,
|
|
50
|
+
ToolExampleSchema: () => ToolExampleSchema,
|
|
51
|
+
ToolMetadataSchema: () => ToolMetadataSchema,
|
|
52
|
+
ToolNameSchema: () => ToolNameSchema,
|
|
53
|
+
ToolRegistry: () => ToolRegistry,
|
|
54
|
+
batch: () => batch,
|
|
55
|
+
broadcast: () => broadcast,
|
|
56
|
+
cache: () => cache,
|
|
57
|
+
chain: () => chain,
|
|
58
|
+
chunk: () => chunk,
|
|
59
|
+
clearThread: () => clearThread,
|
|
60
|
+
collect: () => collect,
|
|
61
|
+
compose: () => compose,
|
|
62
|
+
composeGraphs: () => composeGraphs,
|
|
63
|
+
composeTool: () => composeTool,
|
|
64
|
+
composeWithOptions: () => composeWithOptions,
|
|
65
|
+
conditional: () => conditional,
|
|
66
|
+
configureLangSmith: () => configureLangSmith,
|
|
67
|
+
createBatchProcessor: () => createBatchProcessor,
|
|
68
|
+
createBinaryRouter: () => createBinaryRouter,
|
|
69
|
+
createCircuitBreaker: () => createCircuitBreaker,
|
|
70
|
+
createConditionalRouter: () => createConditionalRouter,
|
|
71
|
+
createConnectionPool: () => createConnectionPool,
|
|
72
|
+
createConversationConfig: () => createConversationConfig,
|
|
73
|
+
createDatabasePool: () => createDatabasePool,
|
|
74
|
+
createErrorReporter: () => createErrorReporter,
|
|
75
|
+
createHeartbeat: () => createHeartbeat,
|
|
76
|
+
createHttpPool: () => createHttpPool,
|
|
77
|
+
createLogger: () => createLogger,
|
|
78
|
+
createManagedTool: () => createManagedTool,
|
|
79
|
+
createMemoryCheckpointer: () => createMemoryCheckpointer,
|
|
80
|
+
createMemoryManager: () => createMemoryManager,
|
|
81
|
+
createMessage: () => createMessage,
|
|
82
|
+
createMetrics: () => createMetrics,
|
|
83
|
+
createMiddlewareContext: () => createMiddlewareContext,
|
|
84
|
+
createMockTool: () => createMockTool,
|
|
85
|
+
createMultiRouter: () => createMultiRouter,
|
|
86
|
+
createParallelWorkflow: () => createParallelWorkflow,
|
|
87
|
+
createProgressTracker: () => createProgressTracker,
|
|
88
|
+
createSSEFormatter: () => createSSEFormatter,
|
|
89
|
+
createSequentialWorkflow: () => createSequentialWorkflow,
|
|
90
|
+
createSqliteCheckpointer: () => createSqliteCheckpointer,
|
|
91
|
+
createStateAnnotation: () => createStateAnnotation,
|
|
92
|
+
createSubgraph: () => createSubgraph,
|
|
93
|
+
createThreadConfig: () => createThreadConfig,
|
|
94
|
+
createTool: () => createTool,
|
|
95
|
+
createToolExecutor: () => createToolExecutor,
|
|
96
|
+
createToolSimulator: () => createToolSimulator,
|
|
97
|
+
createToolUnsafe: () => createToolUnsafe,
|
|
98
|
+
createWebSocketHandler: () => createWebSocketHandler,
|
|
99
|
+
development: () => development,
|
|
100
|
+
filter: () => filter,
|
|
101
|
+
generateThreadId: () => generateThreadId,
|
|
102
|
+
getCheckpointHistory: () => getCheckpointHistory,
|
|
103
|
+
getLangSmithConfig: () => getLangSmithConfig,
|
|
104
|
+
getLatestCheckpoint: () => getLatestCheckpoint,
|
|
105
|
+
getMissingDescriptions: () => getMissingDescriptions,
|
|
106
|
+
getToolDescription: () => getToolDescription,
|
|
107
|
+
getToolJsonSchema: () => getToolJsonSchema,
|
|
108
|
+
isMemoryCheckpointer: () => isMemoryCheckpointer,
|
|
109
|
+
isTracingEnabled: () => isTracingEnabled,
|
|
110
|
+
map: () => map,
|
|
111
|
+
merge: () => merge,
|
|
112
|
+
mergeState: () => mergeState,
|
|
113
|
+
parallel: () => parallel,
|
|
114
|
+
parseSSEEvent: () => parseSSEEvent,
|
|
115
|
+
presets: () => presets,
|
|
116
|
+
production: () => production,
|
|
117
|
+
reduce: () => reduce,
|
|
118
|
+
retry: () => retry,
|
|
119
|
+
safeValidateSchemaDescriptions: () => safeValidateSchemaDescriptions,
|
|
120
|
+
sendMessage: () => sendMessage,
|
|
121
|
+
sequential: () => sequential,
|
|
122
|
+
sequentialBuilder: () => sequentialBuilder,
|
|
123
|
+
take: () => take,
|
|
124
|
+
testing: () => testing,
|
|
125
|
+
throttle: () => throttle,
|
|
126
|
+
timeout: () => timeout,
|
|
127
|
+
toLangChainTool: () => toLangChainTool,
|
|
128
|
+
toLangChainTools: () => toLangChainTools,
|
|
129
|
+
toolBuilder: () => toolBuilder,
|
|
130
|
+
validateSchemaDescriptions: () => validateSchemaDescriptions,
|
|
131
|
+
validateState: () => validateState,
|
|
132
|
+
validateTool: () => validateTool,
|
|
133
|
+
validateToolMetadata: () => validateToolMetadata,
|
|
134
|
+
validateToolName: () => validateToolName,
|
|
135
|
+
withErrorHandler: () => withErrorHandler,
|
|
136
|
+
withMetrics: () => withMetrics,
|
|
137
|
+
withRetry: () => withRetry,
|
|
138
|
+
withTimeout: () => withTimeout,
|
|
139
|
+
withTracing: () => withTracing
|
|
140
|
+
});
|
|
141
|
+
module.exports = __toCommonJS(index_exports);
|
|
142
|
+
|
|
143
|
+
// src/tools/types.ts
|
|
144
|
+
var ToolCategory = /* @__PURE__ */ ((ToolCategory2) => {
|
|
145
|
+
ToolCategory2["FILE_SYSTEM"] = "file-system";
|
|
146
|
+
ToolCategory2["WEB"] = "web";
|
|
147
|
+
ToolCategory2["CODE"] = "code";
|
|
148
|
+
ToolCategory2["DATABASE"] = "database";
|
|
149
|
+
ToolCategory2["API"] = "api";
|
|
150
|
+
ToolCategory2["UTILITY"] = "utility";
|
|
151
|
+
ToolCategory2["CUSTOM"] = "custom";
|
|
152
|
+
return ToolCategory2;
|
|
153
|
+
})(ToolCategory || {});
|
|
154
|
+
|
|
155
|
+
// src/tools/schemas.ts
|
|
156
|
+
var import_zod = require("zod");
|
|
157
|
+
var ToolCategorySchema = import_zod.z.nativeEnum(ToolCategory, {
|
|
158
|
+
errorMap: () => ({
|
|
159
|
+
message: `Must be a valid ToolCategory: ${Object.values(ToolCategory).join(", ")}`
|
|
160
|
+
})
|
|
161
|
+
});
|
|
162
|
+
var ToolExampleSchema = import_zod.z.object({
|
|
163
|
+
/**
|
|
164
|
+
* Description must be a non-empty string
|
|
165
|
+
*/
|
|
166
|
+
description: import_zod.z.string().min(1, "Example description cannot be empty"),
|
|
167
|
+
/**
|
|
168
|
+
* Input must be an object (can have any properties)
|
|
169
|
+
*/
|
|
170
|
+
input: import_zod.z.record(import_zod.z.unknown()),
|
|
171
|
+
/**
|
|
172
|
+
* Output is optional and can be anything
|
|
173
|
+
*/
|
|
174
|
+
output: import_zod.z.unknown().optional(),
|
|
175
|
+
/**
|
|
176
|
+
* Explanation is optional but must be non-empty if provided
|
|
177
|
+
*/
|
|
178
|
+
explanation: import_zod.z.string().min(1).optional()
|
|
179
|
+
});
|
|
180
|
+
var ToolNameSchema = import_zod.z.string().min(2, "Tool name must be at least 2 characters").max(50, "Tool name must be at most 50 characters").regex(
|
|
181
|
+
/^[a-z][a-z0-9-]*[a-z0-9]$/,
|
|
182
|
+
"Tool name must be kebab-case (lowercase letters, numbers, hyphens only, must start with a letter)"
|
|
183
|
+
);
|
|
184
|
+
var ToolMetadataSchema = import_zod.z.object({
|
|
185
|
+
// ===== REQUIRED FIELDS =====
|
|
186
|
+
/**
|
|
187
|
+
* Tool name - must be valid kebab-case
|
|
188
|
+
*/
|
|
189
|
+
name: ToolNameSchema,
|
|
190
|
+
/**
|
|
191
|
+
* Description - must be meaningful (at least 10 characters)
|
|
192
|
+
*/
|
|
193
|
+
description: import_zod.z.string().min(10, "Tool description must be at least 10 characters").max(500, "Tool description must be at most 500 characters"),
|
|
194
|
+
/**
|
|
195
|
+
* Category - must be a valid ToolCategory
|
|
196
|
+
*/
|
|
197
|
+
category: ToolCategorySchema,
|
|
198
|
+
// ===== OPTIONAL FIELDS =====
|
|
199
|
+
/**
|
|
200
|
+
* Display name - if provided, must be non-empty
|
|
201
|
+
*/
|
|
202
|
+
displayName: import_zod.z.string().min(1).optional(),
|
|
203
|
+
/**
|
|
204
|
+
* Tags - array of non-empty strings
|
|
205
|
+
*/
|
|
206
|
+
tags: import_zod.z.array(import_zod.z.string().min(1)).optional(),
|
|
207
|
+
/**
|
|
208
|
+
* Examples - array of valid ToolExample objects
|
|
209
|
+
*/
|
|
210
|
+
examples: import_zod.z.array(ToolExampleSchema).optional(),
|
|
211
|
+
/**
|
|
212
|
+
* Usage notes - if provided, must be meaningful
|
|
213
|
+
*/
|
|
214
|
+
usageNotes: import_zod.z.string().min(10).optional(),
|
|
215
|
+
/**
|
|
216
|
+
* Limitations - array of non-empty strings
|
|
217
|
+
*/
|
|
218
|
+
limitations: import_zod.z.array(import_zod.z.string().min(1)).optional(),
|
|
219
|
+
/**
|
|
220
|
+
* Version - if provided, should follow semver format
|
|
221
|
+
* Examples: '1.0.0', '2.1.3', '0.1.0-beta', '1.0.0-alpha.1'
|
|
222
|
+
*/
|
|
223
|
+
version: import_zod.z.string().regex(
|
|
224
|
+
/^\d+\.\d+\.\d+(-[a-z0-9.-]+)?$/i,
|
|
225
|
+
"Version should follow semantic versioning (e.g., 1.0.0)"
|
|
226
|
+
).optional(),
|
|
227
|
+
/**
|
|
228
|
+
* Author - if provided, must be non-empty
|
|
229
|
+
*/
|
|
230
|
+
author: import_zod.z.string().min(1).optional(),
|
|
231
|
+
/**
|
|
232
|
+
* Deprecated flag
|
|
233
|
+
*/
|
|
234
|
+
deprecated: import_zod.z.boolean().optional(),
|
|
235
|
+
/**
|
|
236
|
+
* Replacement tool name - if provided, must be valid tool name
|
|
237
|
+
*/
|
|
238
|
+
replacedBy: ToolNameSchema.optional()
|
|
239
|
+
});
|
|
240
|
+
function validateToolMetadata(metadata) {
|
|
241
|
+
return ToolMetadataSchema.safeParse(metadata);
|
|
242
|
+
}
|
|
243
|
+
function validateToolName(name) {
|
|
244
|
+
return ToolNameSchema.safeParse(name).success;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// src/tools/validation.ts
|
|
248
|
+
var import_zod2 = require("zod");
|
|
249
|
+
var MissingDescriptionError = class extends Error {
|
|
250
|
+
constructor(fieldPath, fieldType) {
|
|
251
|
+
super(
|
|
252
|
+
`Schema field "${fieldPath.join(".")}" (${fieldType}) is missing a description. All fields must have descriptions for LLM understanding. Use .describe("...") on this field.`
|
|
253
|
+
);
|
|
254
|
+
this.fieldPath = fieldPath;
|
|
255
|
+
this.fieldType = fieldType;
|
|
256
|
+
this.name = "MissingDescriptionError";
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
function validateSchemaDescriptions(schema, fieldPath = []) {
|
|
260
|
+
const def = schema._def;
|
|
261
|
+
const typeName = def.typeName;
|
|
262
|
+
if (schema instanceof import_zod2.z.ZodObject) {
|
|
263
|
+
const shape = schema.shape;
|
|
264
|
+
Object.entries(shape).forEach(([key, fieldSchema]) => {
|
|
265
|
+
validateSchemaDescriptions(fieldSchema, [...fieldPath, key]);
|
|
266
|
+
});
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
if (schema instanceof import_zod2.z.ZodArray) {
|
|
270
|
+
validateSchemaDescriptions(def.type, [...fieldPath, "[]"]);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
if (schema instanceof import_zod2.z.ZodOptional) {
|
|
274
|
+
const wrapperDescription = def.description;
|
|
275
|
+
const innerDescription = def.innerType._def.description;
|
|
276
|
+
if (fieldPath.length > 0 && !wrapperDescription && !innerDescription) {
|
|
277
|
+
throw new MissingDescriptionError(fieldPath, typeName);
|
|
278
|
+
}
|
|
279
|
+
if (!wrapperDescription) {
|
|
280
|
+
validateSchemaDescriptions(def.innerType, fieldPath);
|
|
281
|
+
}
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if (schema instanceof import_zod2.z.ZodNullable) {
|
|
285
|
+
const wrapperDescription = def.description;
|
|
286
|
+
const innerDescription = def.innerType._def.description;
|
|
287
|
+
if (fieldPath.length > 0 && !wrapperDescription && !innerDescription) {
|
|
288
|
+
throw new MissingDescriptionError(fieldPath, typeName);
|
|
289
|
+
}
|
|
290
|
+
if (!wrapperDescription) {
|
|
291
|
+
validateSchemaDescriptions(def.innerType, fieldPath);
|
|
292
|
+
}
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (schema instanceof import_zod2.z.ZodDefault) {
|
|
296
|
+
const wrapperDescription = def.description;
|
|
297
|
+
const innerDescription = def.innerType._def.description;
|
|
298
|
+
if (fieldPath.length > 0 && !wrapperDescription && !innerDescription) {
|
|
299
|
+
throw new MissingDescriptionError(fieldPath, typeName);
|
|
300
|
+
}
|
|
301
|
+
if (!wrapperDescription) {
|
|
302
|
+
validateSchemaDescriptions(def.innerType, fieldPath);
|
|
303
|
+
}
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (schema instanceof import_zod2.z.ZodUnion) {
|
|
307
|
+
def.options.forEach((option, index) => {
|
|
308
|
+
validateSchemaDescriptions(option, [...fieldPath, `option${index}`]);
|
|
309
|
+
});
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (schema instanceof import_zod2.z.ZodIntersection) {
|
|
313
|
+
validateSchemaDescriptions(def.left, fieldPath);
|
|
314
|
+
validateSchemaDescriptions(def.right, fieldPath);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
if (schema instanceof import_zod2.z.ZodRecord) {
|
|
318
|
+
validateSchemaDescriptions(def.valueType, [...fieldPath, "[key]"]);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
if (schema instanceof import_zod2.z.ZodTuple) {
|
|
322
|
+
def.items.forEach((item, index) => {
|
|
323
|
+
validateSchemaDescriptions(item, [...fieldPath, `[${index}]`]);
|
|
324
|
+
});
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (fieldPath.length > 0) {
|
|
328
|
+
const description = def.description;
|
|
329
|
+
if (!description || description.trim() === "") {
|
|
330
|
+
throw new MissingDescriptionError(fieldPath, typeName);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function safeValidateSchemaDescriptions(schema) {
|
|
335
|
+
try {
|
|
336
|
+
validateSchemaDescriptions(schema);
|
|
337
|
+
return { success: true };
|
|
338
|
+
} catch (error) {
|
|
339
|
+
if (error instanceof MissingDescriptionError) {
|
|
340
|
+
return { success: false, error };
|
|
341
|
+
}
|
|
342
|
+
throw error;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
function getMissingDescriptions(schema) {
|
|
346
|
+
const missing = [];
|
|
347
|
+
function check(s, path = []) {
|
|
348
|
+
const def = s._def;
|
|
349
|
+
const typeName = def.typeName;
|
|
350
|
+
if (s instanceof import_zod2.z.ZodObject) {
|
|
351
|
+
const shape = s.shape;
|
|
352
|
+
Object.entries(shape).forEach(([key, fieldSchema]) => {
|
|
353
|
+
check(fieldSchema, [...path, key]);
|
|
354
|
+
});
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
if (s instanceof import_zod2.z.ZodOptional || s instanceof import_zod2.z.ZodNullable || s instanceof import_zod2.z.ZodDefault) {
|
|
358
|
+
const wrapperDescription = def.description;
|
|
359
|
+
const innerDescription = def.innerType._def.description;
|
|
360
|
+
if (path.length > 0 && !wrapperDescription && !innerDescription) {
|
|
361
|
+
missing.push(path.join("."));
|
|
362
|
+
}
|
|
363
|
+
if (!wrapperDescription) {
|
|
364
|
+
check(def.innerType, path);
|
|
365
|
+
}
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (s instanceof import_zod2.z.ZodArray) {
|
|
369
|
+
check(def.type, [...path, "[]"]);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (path.length > 0) {
|
|
373
|
+
const description = def.description;
|
|
374
|
+
if (!description || description.trim() === "") {
|
|
375
|
+
missing.push(path.join("."));
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
check(schema);
|
|
380
|
+
return missing;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// src/tools/helpers.ts
|
|
384
|
+
function createTool(metadata, schema, execute) {
|
|
385
|
+
const metadataResult = validateToolMetadata(metadata);
|
|
386
|
+
if (!metadataResult.success) {
|
|
387
|
+
const errors = metadataResult.error.errors.map((err) => ` - ${err.path.join(".")}: ${err.message}`).join("\n");
|
|
388
|
+
throw new Error(`Invalid tool metadata:
|
|
389
|
+
${errors}`);
|
|
390
|
+
}
|
|
391
|
+
validateSchemaDescriptions(schema);
|
|
392
|
+
return {
|
|
393
|
+
metadata: metadataResult.data,
|
|
394
|
+
schema,
|
|
395
|
+
execute
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function createToolUnsafe(metadata, schema, execute) {
|
|
399
|
+
const metadataResult = validateToolMetadata(metadata);
|
|
400
|
+
if (!metadataResult.success) {
|
|
401
|
+
const errors = metadataResult.error.errors.map((err) => ` - ${err.path.join(".")}: ${err.message}`).join("\n");
|
|
402
|
+
throw new Error(`Invalid tool metadata:
|
|
403
|
+
${errors}`);
|
|
404
|
+
}
|
|
405
|
+
return {
|
|
406
|
+
metadata: metadataResult.data,
|
|
407
|
+
schema,
|
|
408
|
+
execute
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
function validateTool(tool) {
|
|
412
|
+
const errors = [];
|
|
413
|
+
const metadataResult = validateToolMetadata(tool.metadata);
|
|
414
|
+
if (!metadataResult.success) {
|
|
415
|
+
metadataResult.error.errors.forEach((err) => {
|
|
416
|
+
errors.push(`Metadata: ${err.path.join(".")}: ${err.message}`);
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
try {
|
|
420
|
+
validateSchemaDescriptions(tool.schema);
|
|
421
|
+
} catch (error) {
|
|
422
|
+
if (error instanceof Error) {
|
|
423
|
+
errors.push(`Schema: ${error.message}`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return {
|
|
427
|
+
success: errors.length === 0,
|
|
428
|
+
errors
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// src/tools/builder.ts
|
|
433
|
+
var ToolBuilder = class {
|
|
434
|
+
metadata = {};
|
|
435
|
+
_schema;
|
|
436
|
+
_execute;
|
|
437
|
+
/**
|
|
438
|
+
* Set the tool name (required)
|
|
439
|
+
*
|
|
440
|
+
* @param name - Tool name in kebab-case (e.g., 'read-file')
|
|
441
|
+
*/
|
|
442
|
+
name(name) {
|
|
443
|
+
this.metadata.name = name;
|
|
444
|
+
return this;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Set the tool description (required)
|
|
448
|
+
*
|
|
449
|
+
* @param description - Clear description of what the tool does
|
|
450
|
+
*/
|
|
451
|
+
description(description) {
|
|
452
|
+
this.metadata.description = description;
|
|
453
|
+
return this;
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Set the tool category (required)
|
|
457
|
+
*
|
|
458
|
+
* @param category - Tool category for organization
|
|
459
|
+
*/
|
|
460
|
+
category(category) {
|
|
461
|
+
this.metadata.category = category;
|
|
462
|
+
return this;
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Set the display name (optional)
|
|
466
|
+
*
|
|
467
|
+
* @param displayName - Human-friendly name for UI display
|
|
468
|
+
*/
|
|
469
|
+
displayName(displayName) {
|
|
470
|
+
this.metadata.displayName = displayName;
|
|
471
|
+
return this;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Set tags for searchability (optional)
|
|
475
|
+
*
|
|
476
|
+
* @param tags - Array of tags for categorization and search
|
|
477
|
+
*/
|
|
478
|
+
tags(tags) {
|
|
479
|
+
this.metadata.tags = tags;
|
|
480
|
+
return this;
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Add a single tag (optional)
|
|
484
|
+
*
|
|
485
|
+
* @param tag - Tag to add
|
|
486
|
+
*/
|
|
487
|
+
tag(tag) {
|
|
488
|
+
if (!this.metadata.tags) {
|
|
489
|
+
this.metadata.tags = [];
|
|
490
|
+
}
|
|
491
|
+
this.metadata.tags.push(tag);
|
|
492
|
+
return this;
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Add an example (optional)
|
|
496
|
+
*
|
|
497
|
+
* @param example - Usage example for the tool
|
|
498
|
+
*/
|
|
499
|
+
example(example) {
|
|
500
|
+
if (!this.metadata.examples) {
|
|
501
|
+
this.metadata.examples = [];
|
|
502
|
+
}
|
|
503
|
+
this.metadata.examples.push(example);
|
|
504
|
+
return this;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Set usage notes (optional)
|
|
508
|
+
*
|
|
509
|
+
* @param notes - Important usage information
|
|
510
|
+
*/
|
|
511
|
+
usageNotes(notes) {
|
|
512
|
+
this.metadata.usageNotes = notes;
|
|
513
|
+
return this;
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Set limitations (optional)
|
|
517
|
+
*
|
|
518
|
+
* @param limitations - Array of known limitations
|
|
519
|
+
*/
|
|
520
|
+
limitations(limitations) {
|
|
521
|
+
this.metadata.limitations = limitations;
|
|
522
|
+
return this;
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Add a single limitation (optional)
|
|
526
|
+
*
|
|
527
|
+
* @param limitation - Limitation to add
|
|
528
|
+
*/
|
|
529
|
+
limitation(limitation) {
|
|
530
|
+
if (!this.metadata.limitations) {
|
|
531
|
+
this.metadata.limitations = [];
|
|
532
|
+
}
|
|
533
|
+
this.metadata.limitations.push(limitation);
|
|
534
|
+
return this;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Set version (optional)
|
|
538
|
+
*
|
|
539
|
+
* @param version - Semantic version string
|
|
540
|
+
*/
|
|
541
|
+
version(version) {
|
|
542
|
+
this.metadata.version = version;
|
|
543
|
+
return this;
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Set author (optional)
|
|
547
|
+
*
|
|
548
|
+
* @param author - Tool author name
|
|
549
|
+
*/
|
|
550
|
+
author(author) {
|
|
551
|
+
this.metadata.author = author;
|
|
552
|
+
return this;
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Set the input schema (required)
|
|
556
|
+
*
|
|
557
|
+
* All fields MUST have .describe() for LLM understanding!
|
|
558
|
+
*
|
|
559
|
+
* @param schema - Zod schema for input validation
|
|
560
|
+
*/
|
|
561
|
+
schema(schema) {
|
|
562
|
+
this._schema = schema;
|
|
563
|
+
return this;
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Set the implementation function (required)
|
|
567
|
+
*
|
|
568
|
+
* @param execute - Async function that implements the tool
|
|
569
|
+
*/
|
|
570
|
+
implement(execute) {
|
|
571
|
+
this._execute = execute;
|
|
572
|
+
return this;
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Build the tool with validation
|
|
576
|
+
*
|
|
577
|
+
* Validates:
|
|
578
|
+
* - All required fields are present
|
|
579
|
+
* - Metadata is valid
|
|
580
|
+
* - Schema has descriptions on all fields
|
|
581
|
+
*
|
|
582
|
+
* @returns The validated tool
|
|
583
|
+
* @throws {Error} If validation fails
|
|
584
|
+
*/
|
|
585
|
+
build() {
|
|
586
|
+
if (!this.metadata.name) {
|
|
587
|
+
throw new Error("Tool name is required. Use .name() to set it.");
|
|
588
|
+
}
|
|
589
|
+
if (!this.metadata.description) {
|
|
590
|
+
throw new Error("Tool description is required. Use .description() to set it.");
|
|
591
|
+
}
|
|
592
|
+
if (!this.metadata.category) {
|
|
593
|
+
throw new Error("Tool category is required. Use .category() to set it.");
|
|
594
|
+
}
|
|
595
|
+
if (!this._schema) {
|
|
596
|
+
throw new Error("Tool schema is required. Use .schema() to set it.");
|
|
597
|
+
}
|
|
598
|
+
if (!this._execute) {
|
|
599
|
+
throw new Error("Tool implementation is required. Use .implement() to set it.");
|
|
600
|
+
}
|
|
601
|
+
return createTool(
|
|
602
|
+
this.metadata,
|
|
603
|
+
this._schema,
|
|
604
|
+
this._execute
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
function toolBuilder() {
|
|
609
|
+
return new ToolBuilder();
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// src/langchain/converter.ts
|
|
613
|
+
var import_tools = require("@langchain/core/tools");
|
|
614
|
+
var import_zod_to_json_schema = require("zod-to-json-schema");
|
|
615
|
+
function toLangChainTool(tool) {
|
|
616
|
+
return new import_tools.DynamicStructuredTool({
|
|
617
|
+
name: tool.metadata.name,
|
|
618
|
+
description: tool.metadata.description,
|
|
619
|
+
schema: tool.schema,
|
|
620
|
+
func: async (input) => {
|
|
621
|
+
const result = await tool.execute(input);
|
|
622
|
+
if (typeof result === "string") {
|
|
623
|
+
return result;
|
|
624
|
+
}
|
|
625
|
+
if (typeof result === "object" && result !== null) {
|
|
626
|
+
return JSON.stringify(result, null, 2);
|
|
627
|
+
}
|
|
628
|
+
return String(result);
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
function toLangChainTools(tools) {
|
|
633
|
+
return tools.map(toLangChainTool);
|
|
634
|
+
}
|
|
635
|
+
function getToolJsonSchema(tool) {
|
|
636
|
+
const jsonSchema = (0, import_zod_to_json_schema.zodToJsonSchema)(tool.schema, {
|
|
637
|
+
name: tool.metadata.name,
|
|
638
|
+
$refStrategy: "none"
|
|
639
|
+
// Don't use $ref for nested schemas
|
|
640
|
+
});
|
|
641
|
+
if (jsonSchema.$ref && jsonSchema.definitions) {
|
|
642
|
+
const refName = jsonSchema.$ref.replace("#/definitions/", "");
|
|
643
|
+
return jsonSchema.definitions[refName] || jsonSchema;
|
|
644
|
+
}
|
|
645
|
+
return jsonSchema;
|
|
646
|
+
}
|
|
647
|
+
function getToolDescription(tool) {
|
|
648
|
+
const { metadata } = tool;
|
|
649
|
+
const parts = [];
|
|
650
|
+
parts.push(`${metadata.name}: ${metadata.description}`);
|
|
651
|
+
if (metadata.displayName) {
|
|
652
|
+
parts.push(`Display Name: ${metadata.displayName}`);
|
|
653
|
+
}
|
|
654
|
+
parts.push(`Category: ${metadata.category}`);
|
|
655
|
+
if (metadata.tags && metadata.tags.length > 0) {
|
|
656
|
+
parts.push(`Tags: ${metadata.tags.join(", ")}`);
|
|
657
|
+
}
|
|
658
|
+
if (metadata.usageNotes) {
|
|
659
|
+
parts.push(`
|
|
660
|
+
Usage Notes: ${metadata.usageNotes}`);
|
|
661
|
+
}
|
|
662
|
+
if (metadata.limitations && metadata.limitations.length > 0) {
|
|
663
|
+
parts.push(`
|
|
664
|
+
Limitations:`);
|
|
665
|
+
metadata.limitations.forEach((limit) => {
|
|
666
|
+
parts.push(` - ${limit}`);
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
if (metadata.examples && metadata.examples.length > 0) {
|
|
670
|
+
parts.push(`
|
|
671
|
+
Examples:`);
|
|
672
|
+
metadata.examples.forEach((example, i) => {
|
|
673
|
+
parts.push(` ${i + 1}. ${example.description}`);
|
|
674
|
+
if (example.explanation) {
|
|
675
|
+
parts.push(` ${example.explanation}`);
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
return parts.join("\n");
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// src/tools/registry.ts
|
|
683
|
+
var RegistryEvent = /* @__PURE__ */ ((RegistryEvent2) => {
|
|
684
|
+
RegistryEvent2["TOOL_REGISTERED"] = "tool:registered";
|
|
685
|
+
RegistryEvent2["TOOL_REMOVED"] = "tool:removed";
|
|
686
|
+
RegistryEvent2["TOOL_UPDATED"] = "tool:updated";
|
|
687
|
+
RegistryEvent2["REGISTRY_CLEARED"] = "registry:cleared";
|
|
688
|
+
return RegistryEvent2;
|
|
689
|
+
})(RegistryEvent || {});
|
|
690
|
+
var ToolRegistry = class {
|
|
691
|
+
tools = /* @__PURE__ */ new Map();
|
|
692
|
+
eventHandlers = /* @__PURE__ */ new Map();
|
|
693
|
+
/**
|
|
694
|
+
* Register a tool in the registry
|
|
695
|
+
*
|
|
696
|
+
* @param tool - The tool to register
|
|
697
|
+
* @throws Error if a tool with the same name already exists
|
|
698
|
+
*
|
|
699
|
+
* @example
|
|
700
|
+
* ```ts
|
|
701
|
+
* registry.register(readFileTool);
|
|
702
|
+
* ```
|
|
703
|
+
*/
|
|
704
|
+
register(tool) {
|
|
705
|
+
const name = tool.metadata.name;
|
|
706
|
+
if (this.tools.has(name)) {
|
|
707
|
+
throw new Error(
|
|
708
|
+
`Tool with name "${name}" is already registered. Use update() to modify it.`
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
this.tools.set(name, tool);
|
|
712
|
+
this.emit("tool:registered" /* TOOL_REGISTERED */, tool);
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Get a tool by name
|
|
716
|
+
*
|
|
717
|
+
* @param name - The tool name
|
|
718
|
+
* @returns The tool, or undefined if not found
|
|
719
|
+
*
|
|
720
|
+
* @example
|
|
721
|
+
* ```ts
|
|
722
|
+
* const tool = registry.get('read-file');
|
|
723
|
+
* if (tool) {
|
|
724
|
+
* const result = await tool.execute({ path: './file.txt' });
|
|
725
|
+
* }
|
|
726
|
+
* ```
|
|
727
|
+
*/
|
|
728
|
+
get(name) {
|
|
729
|
+
return this.tools.get(name);
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Check if a tool exists in the registry
|
|
733
|
+
*
|
|
734
|
+
* @param name - The tool name
|
|
735
|
+
* @returns True if the tool exists
|
|
736
|
+
*
|
|
737
|
+
* @example
|
|
738
|
+
* ```ts
|
|
739
|
+
* if (registry.has('read-file')) {
|
|
740
|
+
* console.log('Tool exists!');
|
|
741
|
+
* }
|
|
742
|
+
* ```
|
|
743
|
+
*/
|
|
744
|
+
has(name) {
|
|
745
|
+
return this.tools.has(name);
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Remove a tool from the registry
|
|
749
|
+
*
|
|
750
|
+
* @param name - The tool name
|
|
751
|
+
* @returns True if the tool was removed, false if it didn't exist
|
|
752
|
+
*
|
|
753
|
+
* @example
|
|
754
|
+
* ```ts
|
|
755
|
+
* const removed = registry.remove('read-file');
|
|
756
|
+
* console.log(removed ? 'Removed' : 'Not found');
|
|
757
|
+
* ```
|
|
758
|
+
*/
|
|
759
|
+
remove(name) {
|
|
760
|
+
const tool = this.tools.get(name);
|
|
761
|
+
if (!tool) {
|
|
762
|
+
return false;
|
|
763
|
+
}
|
|
764
|
+
this.tools.delete(name);
|
|
765
|
+
this.emit("tool:removed" /* TOOL_REMOVED */, tool);
|
|
766
|
+
return true;
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Update an existing tool
|
|
770
|
+
*
|
|
771
|
+
* @param name - The tool name
|
|
772
|
+
* @param tool - The new tool definition
|
|
773
|
+
* @returns True if updated, false if the tool didn't exist
|
|
774
|
+
*
|
|
775
|
+
* @example
|
|
776
|
+
* ```ts
|
|
777
|
+
* const updated = registry.update('read-file', newReadFileTool);
|
|
778
|
+
* ```
|
|
779
|
+
*/
|
|
780
|
+
update(name, tool) {
|
|
781
|
+
if (!this.tools.has(name)) {
|
|
782
|
+
return false;
|
|
783
|
+
}
|
|
784
|
+
this.tools.set(name, tool);
|
|
785
|
+
this.emit("tool:updated" /* TOOL_UPDATED */, { name, tool });
|
|
786
|
+
return true;
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* Get all registered tools
|
|
790
|
+
*
|
|
791
|
+
* @returns Array of all tools
|
|
792
|
+
*
|
|
793
|
+
* @example
|
|
794
|
+
* ```ts
|
|
795
|
+
* const allTools = registry.getAll();
|
|
796
|
+
* console.log(`Total tools: ${allTools.length}`);
|
|
797
|
+
* ```
|
|
798
|
+
*/
|
|
799
|
+
getAll() {
|
|
800
|
+
return Array.from(this.tools.values());
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* Get tools by category
|
|
804
|
+
*
|
|
805
|
+
* @param category - The tool category
|
|
806
|
+
* @returns Array of tools in the category
|
|
807
|
+
*
|
|
808
|
+
* @example
|
|
809
|
+
* ```ts
|
|
810
|
+
* const fileTools = registry.getByCategory(ToolCategory.FILE_SYSTEM);
|
|
811
|
+
* ```
|
|
812
|
+
*/
|
|
813
|
+
getByCategory(category) {
|
|
814
|
+
return this.getAll().filter((tool) => tool.metadata.category === category);
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Get tools by tag
|
|
818
|
+
*
|
|
819
|
+
* @param tag - The tag to search for
|
|
820
|
+
* @returns Array of tools with the tag
|
|
821
|
+
*
|
|
822
|
+
* @example
|
|
823
|
+
* ```ts
|
|
824
|
+
* const fileTools = registry.getByTag('file');
|
|
825
|
+
* ```
|
|
826
|
+
*/
|
|
827
|
+
getByTag(tag) {
|
|
828
|
+
return this.getAll().filter(
|
|
829
|
+
(tool) => tool.metadata.tags?.includes(tag)
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Search tools by name or description
|
|
834
|
+
*
|
|
835
|
+
* Case-insensitive search across tool names, display names, and descriptions.
|
|
836
|
+
*
|
|
837
|
+
* @param query - The search query
|
|
838
|
+
* @returns Array of matching tools
|
|
839
|
+
*
|
|
840
|
+
* @example
|
|
841
|
+
* ```ts
|
|
842
|
+
* const results = registry.search('file');
|
|
843
|
+
* // Returns tools with 'file' in name or description
|
|
844
|
+
* ```
|
|
845
|
+
*/
|
|
846
|
+
search(query) {
|
|
847
|
+
const lowerQuery = query.toLowerCase();
|
|
848
|
+
return this.getAll().filter((tool) => {
|
|
849
|
+
const name = tool.metadata.name.toLowerCase();
|
|
850
|
+
const displayName = tool.metadata.displayName?.toLowerCase() || "";
|
|
851
|
+
const description = tool.metadata.description.toLowerCase();
|
|
852
|
+
return name.includes(lowerQuery) || displayName.includes(lowerQuery) || description.includes(lowerQuery);
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Register multiple tools at once
|
|
857
|
+
*
|
|
858
|
+
* @param tools - Array of tools to register
|
|
859
|
+
* @throws Error if any tool name conflicts with existing tools
|
|
860
|
+
*
|
|
861
|
+
* @example
|
|
862
|
+
* ```ts
|
|
863
|
+
* registry.registerMany([tool1, tool2, tool3]);
|
|
864
|
+
* ```
|
|
865
|
+
*/
|
|
866
|
+
registerMany(tools) {
|
|
867
|
+
const conflicts = [];
|
|
868
|
+
for (const tool of tools) {
|
|
869
|
+
if (this.tools.has(tool.metadata.name)) {
|
|
870
|
+
conflicts.push(tool.metadata.name);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
if (conflicts.length > 0) {
|
|
874
|
+
throw new Error(
|
|
875
|
+
`Cannot register tools: the following names already exist: ${conflicts.join(", ")}`
|
|
876
|
+
);
|
|
877
|
+
}
|
|
878
|
+
for (const tool of tools) {
|
|
879
|
+
this.register(tool);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Clear all tools from the registry
|
|
884
|
+
*
|
|
885
|
+
* @example
|
|
886
|
+
* ```ts
|
|
887
|
+
* registry.clear();
|
|
888
|
+
* console.log(registry.size()); // 0
|
|
889
|
+
* ```
|
|
890
|
+
*/
|
|
891
|
+
clear() {
|
|
892
|
+
this.tools.clear();
|
|
893
|
+
this.emit("registry:cleared" /* REGISTRY_CLEARED */, null);
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Get the number of registered tools
|
|
897
|
+
*
|
|
898
|
+
* @returns Number of tools in the registry
|
|
899
|
+
*
|
|
900
|
+
* @example
|
|
901
|
+
* ```ts
|
|
902
|
+
* console.log(`Registry has ${registry.size()} tools`);
|
|
903
|
+
* ```
|
|
904
|
+
*/
|
|
905
|
+
size() {
|
|
906
|
+
return this.tools.size;
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Get all tool names
|
|
910
|
+
*
|
|
911
|
+
* @returns Array of tool names
|
|
912
|
+
*
|
|
913
|
+
* @example
|
|
914
|
+
* ```ts
|
|
915
|
+
* const names = registry.getNames();
|
|
916
|
+
* console.log('Available tools:', names.join(', '));
|
|
917
|
+
* ```
|
|
918
|
+
*/
|
|
919
|
+
getNames() {
|
|
920
|
+
return Array.from(this.tools.keys());
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Register an event handler
|
|
924
|
+
*
|
|
925
|
+
* @param event - The event to listen for
|
|
926
|
+
* @param handler - The handler function
|
|
927
|
+
*
|
|
928
|
+
* @example
|
|
929
|
+
* ```ts
|
|
930
|
+
* registry.on(RegistryEvent.TOOL_REGISTERED, (tool) => {
|
|
931
|
+
* console.log('New tool:', tool.metadata.name);
|
|
932
|
+
* });
|
|
933
|
+
* ```
|
|
934
|
+
*/
|
|
935
|
+
on(event, handler) {
|
|
936
|
+
if (!this.eventHandlers.has(event)) {
|
|
937
|
+
this.eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
938
|
+
}
|
|
939
|
+
this.eventHandlers.get(event).add(handler);
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* Unregister an event handler
|
|
943
|
+
*
|
|
944
|
+
* @param event - The event to stop listening for
|
|
945
|
+
* @param handler - The handler function to remove
|
|
946
|
+
*
|
|
947
|
+
* @example
|
|
948
|
+
* ```ts
|
|
949
|
+
* const handler = (tool) => console.log(tool);
|
|
950
|
+
* registry.on(RegistryEvent.TOOL_REGISTERED, handler);
|
|
951
|
+
* registry.off(RegistryEvent.TOOL_REGISTERED, handler);
|
|
952
|
+
* ```
|
|
953
|
+
*/
|
|
954
|
+
off(event, handler) {
|
|
955
|
+
const handlers = this.eventHandlers.get(event);
|
|
956
|
+
if (handlers) {
|
|
957
|
+
handlers.delete(handler);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* Emit an event to all registered handlers
|
|
962
|
+
*
|
|
963
|
+
* @param event - The event to emit
|
|
964
|
+
* @param data - The event data
|
|
965
|
+
* @private
|
|
966
|
+
*/
|
|
967
|
+
emit(event, data) {
|
|
968
|
+
const handlers = this.eventHandlers.get(event);
|
|
969
|
+
if (handlers) {
|
|
970
|
+
handlers.forEach((handler) => {
|
|
971
|
+
try {
|
|
972
|
+
handler(data);
|
|
973
|
+
} catch (error) {
|
|
974
|
+
console.error(`Error in event handler for ${event}:`, error);
|
|
975
|
+
}
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Convert all registered tools to LangChain format
|
|
981
|
+
*
|
|
982
|
+
* This allows the entire registry to be used with LangChain agents.
|
|
983
|
+
*
|
|
984
|
+
* @returns Array of LangChain DynamicStructuredTools
|
|
985
|
+
*
|
|
986
|
+
* @example
|
|
987
|
+
* ```ts
|
|
988
|
+
* const registry = new ToolRegistry();
|
|
989
|
+
* registry.registerMany([tool1, tool2, tool3]);
|
|
990
|
+
*
|
|
991
|
+
* const langchainTools = registry.toLangChainTools();
|
|
992
|
+
*
|
|
993
|
+
* const agent = createAgent({
|
|
994
|
+
* model: new ChatOpenAI(),
|
|
995
|
+
* tools: langchainTools,
|
|
996
|
+
* });
|
|
997
|
+
* ```
|
|
998
|
+
*/
|
|
999
|
+
toLangChainTools() {
|
|
1000
|
+
return toLangChainTools(this.getAll());
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Generate a formatted prompt describing all tools
|
|
1004
|
+
*
|
|
1005
|
+
* Creates a human-readable description of all tools in the registry,
|
|
1006
|
+
* suitable for inclusion in LLM prompts.
|
|
1007
|
+
*
|
|
1008
|
+
* @param options - Options for customizing the prompt
|
|
1009
|
+
* @returns Formatted prompt string
|
|
1010
|
+
*
|
|
1011
|
+
* @example
|
|
1012
|
+
* ```ts
|
|
1013
|
+
* const prompt = registry.generatePrompt({
|
|
1014
|
+
* includeExamples: true,
|
|
1015
|
+
* groupByCategory: true,
|
|
1016
|
+
* maxExamplesPerTool: 2,
|
|
1017
|
+
* });
|
|
1018
|
+
*
|
|
1019
|
+
* console.log(prompt);
|
|
1020
|
+
* // Available Tools:
|
|
1021
|
+
* //
|
|
1022
|
+
* // FILE SYSTEM TOOLS:
|
|
1023
|
+
* // - read-file: Read a file from the file system
|
|
1024
|
+
* // Parameters: path (string)
|
|
1025
|
+
* // Example: Read a text file
|
|
1026
|
+
* // Input: { "path": "./README.md" }
|
|
1027
|
+
* // ...
|
|
1028
|
+
* ```
|
|
1029
|
+
*/
|
|
1030
|
+
generatePrompt(options = {}) {
|
|
1031
|
+
const {
|
|
1032
|
+
includeExamples = false,
|
|
1033
|
+
includeNotes = false,
|
|
1034
|
+
includeLimitations = false,
|
|
1035
|
+
groupByCategory = false,
|
|
1036
|
+
categories,
|
|
1037
|
+
maxExamplesPerTool
|
|
1038
|
+
} = options;
|
|
1039
|
+
let tools = this.getAll();
|
|
1040
|
+
if (categories && categories.length > 0) {
|
|
1041
|
+
tools = tools.filter((tool) => categories.includes(tool.metadata.category));
|
|
1042
|
+
}
|
|
1043
|
+
if (tools.length === 0) {
|
|
1044
|
+
return "No tools available.";
|
|
1045
|
+
}
|
|
1046
|
+
const lines = ["Available Tools:", ""];
|
|
1047
|
+
if (groupByCategory) {
|
|
1048
|
+
const toolsByCategory = /* @__PURE__ */ new Map();
|
|
1049
|
+
for (const tool of tools) {
|
|
1050
|
+
const category = tool.metadata.category;
|
|
1051
|
+
if (!toolsByCategory.has(category)) {
|
|
1052
|
+
toolsByCategory.set(category, []);
|
|
1053
|
+
}
|
|
1054
|
+
toolsByCategory.get(category).push(tool);
|
|
1055
|
+
}
|
|
1056
|
+
for (const [category, categoryTools] of toolsByCategory) {
|
|
1057
|
+
lines.push(`${category.toUpperCase().replace(/-/g, " ")} TOOLS:`);
|
|
1058
|
+
for (const tool of categoryTools) {
|
|
1059
|
+
lines.push(...this.formatToolForPrompt(tool, {
|
|
1060
|
+
includeExamples,
|
|
1061
|
+
includeNotes,
|
|
1062
|
+
includeLimitations,
|
|
1063
|
+
maxExamplesPerTool
|
|
1064
|
+
}));
|
|
1065
|
+
}
|
|
1066
|
+
lines.push("");
|
|
1067
|
+
}
|
|
1068
|
+
} else {
|
|
1069
|
+
for (const tool of tools) {
|
|
1070
|
+
lines.push(...this.formatToolForPrompt(tool, {
|
|
1071
|
+
includeExamples,
|
|
1072
|
+
includeNotes,
|
|
1073
|
+
includeLimitations,
|
|
1074
|
+
maxExamplesPerTool
|
|
1075
|
+
}));
|
|
1076
|
+
lines.push("");
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
return lines.join("\n").trim();
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Format a single tool for inclusion in a prompt
|
|
1083
|
+
*
|
|
1084
|
+
* @param tool - The tool to format
|
|
1085
|
+
* @param options - Formatting options
|
|
1086
|
+
* @returns Array of formatted lines
|
|
1087
|
+
* @private
|
|
1088
|
+
*/
|
|
1089
|
+
formatToolForPrompt(tool, options) {
|
|
1090
|
+
const { metadata } = tool;
|
|
1091
|
+
const lines = [];
|
|
1092
|
+
lines.push(`- ${metadata.name}: ${metadata.description}`);
|
|
1093
|
+
const schemaShape = tool.schema._def?.shape?.();
|
|
1094
|
+
if (schemaShape) {
|
|
1095
|
+
const params = Object.keys(schemaShape);
|
|
1096
|
+
if (params.length > 0) {
|
|
1097
|
+
const paramDescriptions = params.map((param) => {
|
|
1098
|
+
const field = schemaShape[param];
|
|
1099
|
+
const type = field._def?.typeName?.replace("Zod", "").toLowerCase() || "any";
|
|
1100
|
+
return `${param} (${type})`;
|
|
1101
|
+
});
|
|
1102
|
+
lines.push(` Parameters: ${paramDescriptions.join(", ")}`);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
if (options.includeNotes && metadata.usageNotes) {
|
|
1106
|
+
lines.push(` Notes: ${metadata.usageNotes}`);
|
|
1107
|
+
}
|
|
1108
|
+
if (options.includeExamples && metadata.examples && metadata.examples.length > 0) {
|
|
1109
|
+
const maxExamples = options.maxExamplesPerTool || metadata.examples.length;
|
|
1110
|
+
const examples = metadata.examples.slice(0, maxExamples);
|
|
1111
|
+
for (const example of examples) {
|
|
1112
|
+
lines.push(` Example: ${example.description}`);
|
|
1113
|
+
lines.push(` Input: ${JSON.stringify(example.input)}`);
|
|
1114
|
+
if (example.explanation) {
|
|
1115
|
+
lines.push(` ${example.explanation}`);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
if (options.includeLimitations && metadata.limitations && metadata.limitations.length > 0) {
|
|
1120
|
+
lines.push(` Limitations:`);
|
|
1121
|
+
for (const limitation of metadata.limitations) {
|
|
1122
|
+
lines.push(` - ${limitation}`);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
return lines;
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
|
|
1129
|
+
// src/tools/executor.ts
|
|
1130
|
+
var PRIORITY_ORDER = {
|
|
1131
|
+
critical: 0,
|
|
1132
|
+
high: 1,
|
|
1133
|
+
normal: 2,
|
|
1134
|
+
low: 3
|
|
1135
|
+
};
|
|
1136
|
+
function createToolExecutor(config = {}) {
|
|
1137
|
+
const {
|
|
1138
|
+
maxConcurrent = 5,
|
|
1139
|
+
timeout: timeout2 = 3e4,
|
|
1140
|
+
retryPolicy,
|
|
1141
|
+
priorityFn = () => "normal",
|
|
1142
|
+
onExecutionStart,
|
|
1143
|
+
onExecutionComplete,
|
|
1144
|
+
onExecutionError
|
|
1145
|
+
} = config;
|
|
1146
|
+
let activeExecutions = 0;
|
|
1147
|
+
const queue = [];
|
|
1148
|
+
const metrics = {
|
|
1149
|
+
totalExecutions: 0,
|
|
1150
|
+
successfulExecutions: 0,
|
|
1151
|
+
failedExecutions: 0,
|
|
1152
|
+
totalDuration: 0,
|
|
1153
|
+
averageDuration: 0,
|
|
1154
|
+
byPriority: { low: 0, normal: 0, high: 0, critical: 0 }
|
|
1155
|
+
};
|
|
1156
|
+
function calculateBackoff(attempt, policy) {
|
|
1157
|
+
const initialDelay = policy.initialDelay || 1e3;
|
|
1158
|
+
const maxDelay = policy.maxDelay || 3e4;
|
|
1159
|
+
let delay;
|
|
1160
|
+
switch (policy.backoff) {
|
|
1161
|
+
case "linear":
|
|
1162
|
+
delay = initialDelay * attempt;
|
|
1163
|
+
break;
|
|
1164
|
+
case "exponential":
|
|
1165
|
+
delay = initialDelay * Math.pow(2, attempt - 1);
|
|
1166
|
+
break;
|
|
1167
|
+
case "fixed":
|
|
1168
|
+
default:
|
|
1169
|
+
delay = initialDelay;
|
|
1170
|
+
}
|
|
1171
|
+
return Math.min(delay, maxDelay);
|
|
1172
|
+
}
|
|
1173
|
+
async function executeWithRetry(tool, input, policy) {
|
|
1174
|
+
if (!policy) {
|
|
1175
|
+
return await tool.invoke(input);
|
|
1176
|
+
}
|
|
1177
|
+
let lastError;
|
|
1178
|
+
for (let attempt = 1; attempt <= policy.maxAttempts; attempt++) {
|
|
1179
|
+
try {
|
|
1180
|
+
return await tool.invoke(input);
|
|
1181
|
+
} catch (error) {
|
|
1182
|
+
lastError = error;
|
|
1183
|
+
if (policy.retryableErrors && policy.retryableErrors.length > 0) {
|
|
1184
|
+
const isRetryable = policy.retryableErrors.some(
|
|
1185
|
+
(msg) => lastError.message.includes(msg)
|
|
1186
|
+
);
|
|
1187
|
+
if (!isRetryable) {
|
|
1188
|
+
throw lastError;
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
if (attempt < policy.maxAttempts) {
|
|
1192
|
+
const delay = calculateBackoff(attempt, policy);
|
|
1193
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
throw lastError;
|
|
1198
|
+
}
|
|
1199
|
+
async function executeSingle(tool, input, priority) {
|
|
1200
|
+
const startTime = Date.now();
|
|
1201
|
+
try {
|
|
1202
|
+
onExecutionStart?.(tool, input);
|
|
1203
|
+
const result = await Promise.race([
|
|
1204
|
+
executeWithRetry(tool, input, retryPolicy),
|
|
1205
|
+
new Promise(
|
|
1206
|
+
(_, reject) => setTimeout(() => reject(new Error("Tool execution timeout")), timeout2)
|
|
1207
|
+
)
|
|
1208
|
+
]);
|
|
1209
|
+
const duration = Date.now() - startTime;
|
|
1210
|
+
metrics.totalExecutions++;
|
|
1211
|
+
metrics.successfulExecutions++;
|
|
1212
|
+
metrics.totalDuration += duration;
|
|
1213
|
+
metrics.averageDuration = metrics.totalDuration / metrics.totalExecutions;
|
|
1214
|
+
metrics.byPriority[priority]++;
|
|
1215
|
+
onExecutionComplete?.(tool, input, result, duration);
|
|
1216
|
+
return result;
|
|
1217
|
+
} catch (error) {
|
|
1218
|
+
const duration = Date.now() - startTime;
|
|
1219
|
+
metrics.totalExecutions++;
|
|
1220
|
+
metrics.failedExecutions++;
|
|
1221
|
+
metrics.totalDuration += duration;
|
|
1222
|
+
metrics.averageDuration = metrics.totalDuration / metrics.totalExecutions;
|
|
1223
|
+
onExecutionError?.(tool, input, error, duration);
|
|
1224
|
+
throw error;
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
async function processQueue() {
|
|
1228
|
+
while (queue.length > 0 && activeExecutions < maxConcurrent) {
|
|
1229
|
+
queue.sort((a, b) => {
|
|
1230
|
+
const priorityDiff = PRIORITY_ORDER[a.priority] - PRIORITY_ORDER[b.priority];
|
|
1231
|
+
if (priorityDiff !== 0) return priorityDiff;
|
|
1232
|
+
return a.timestamp - b.timestamp;
|
|
1233
|
+
});
|
|
1234
|
+
const execution = queue.shift();
|
|
1235
|
+
if (!execution) break;
|
|
1236
|
+
activeExecutions++;
|
|
1237
|
+
executeSingle(execution.tool, execution.input, execution.priority).then((result) => {
|
|
1238
|
+
execution.resolve(result);
|
|
1239
|
+
}).catch((error) => {
|
|
1240
|
+
execution.reject(error);
|
|
1241
|
+
}).finally(() => {
|
|
1242
|
+
activeExecutions--;
|
|
1243
|
+
processQueue();
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
async function execute(tool, input, options = {}) {
|
|
1248
|
+
const priority = options.priority || priorityFn(tool);
|
|
1249
|
+
return new Promise((resolve, reject) => {
|
|
1250
|
+
queue.push({
|
|
1251
|
+
tool,
|
|
1252
|
+
input,
|
|
1253
|
+
priority,
|
|
1254
|
+
resolve,
|
|
1255
|
+
reject,
|
|
1256
|
+
timestamp: Date.now()
|
|
1257
|
+
});
|
|
1258
|
+
processQueue();
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1261
|
+
async function executeParallel(executions) {
|
|
1262
|
+
return Promise.all(
|
|
1263
|
+
executions.map(
|
|
1264
|
+
(exec) => execute(exec.tool, exec.input, { priority: exec.priority })
|
|
1265
|
+
)
|
|
1266
|
+
);
|
|
1267
|
+
}
|
|
1268
|
+
function getMetrics() {
|
|
1269
|
+
return { ...metrics };
|
|
1270
|
+
}
|
|
1271
|
+
function resetMetrics() {
|
|
1272
|
+
metrics.totalExecutions = 0;
|
|
1273
|
+
metrics.successfulExecutions = 0;
|
|
1274
|
+
metrics.failedExecutions = 0;
|
|
1275
|
+
metrics.totalDuration = 0;
|
|
1276
|
+
metrics.averageDuration = 0;
|
|
1277
|
+
metrics.byPriority = { low: 0, normal: 0, high: 0, critical: 0 };
|
|
1278
|
+
}
|
|
1279
|
+
function getQueueStatus() {
|
|
1280
|
+
return {
|
|
1281
|
+
queueLength: queue.length,
|
|
1282
|
+
activeExecutions,
|
|
1283
|
+
maxConcurrent
|
|
1284
|
+
};
|
|
1285
|
+
}
|
|
1286
|
+
return {
|
|
1287
|
+
execute,
|
|
1288
|
+
executeParallel,
|
|
1289
|
+
getMetrics,
|
|
1290
|
+
resetMetrics,
|
|
1291
|
+
getQueueStatus
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
// src/tools/lifecycle.ts
|
|
1296
|
+
var ManagedTool = class {
|
|
1297
|
+
name;
|
|
1298
|
+
description;
|
|
1299
|
+
initializeFn;
|
|
1300
|
+
executeFn;
|
|
1301
|
+
cleanupFn;
|
|
1302
|
+
healthCheckFn;
|
|
1303
|
+
autoCleanup;
|
|
1304
|
+
healthCheckInterval;
|
|
1305
|
+
_initialized = false;
|
|
1306
|
+
_context;
|
|
1307
|
+
_stats = {
|
|
1308
|
+
initialized: false,
|
|
1309
|
+
totalExecutions: 0,
|
|
1310
|
+
successfulExecutions: 0,
|
|
1311
|
+
failedExecutions: 0
|
|
1312
|
+
};
|
|
1313
|
+
_healthCheckTimer;
|
|
1314
|
+
constructor(config) {
|
|
1315
|
+
this.name = config.name;
|
|
1316
|
+
this.description = config.description;
|
|
1317
|
+
this.initializeFn = config.initialize?.bind(this);
|
|
1318
|
+
this.executeFn = config.execute.bind(this);
|
|
1319
|
+
this.cleanupFn = config.cleanup?.bind(this);
|
|
1320
|
+
this.healthCheckFn = config.healthCheck?.bind(this);
|
|
1321
|
+
this.autoCleanup = config.autoCleanup ?? true;
|
|
1322
|
+
this.healthCheckInterval = config.healthCheckInterval;
|
|
1323
|
+
this._context = config.context;
|
|
1324
|
+
if (this.autoCleanup) {
|
|
1325
|
+
process.on("beforeExit", () => {
|
|
1326
|
+
this.cleanup().catch(console.error);
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* Get the tool context (e.g., connection pool, API client)
|
|
1332
|
+
*/
|
|
1333
|
+
get context() {
|
|
1334
|
+
return this._context;
|
|
1335
|
+
}
|
|
1336
|
+
/**
|
|
1337
|
+
* Set the tool context
|
|
1338
|
+
*/
|
|
1339
|
+
set context(value) {
|
|
1340
|
+
this._context = value;
|
|
1341
|
+
}
|
|
1342
|
+
/**
|
|
1343
|
+
* Check if tool is initialized
|
|
1344
|
+
*/
|
|
1345
|
+
get initialized() {
|
|
1346
|
+
return this._initialized;
|
|
1347
|
+
}
|
|
1348
|
+
/**
|
|
1349
|
+
* Initialize the tool
|
|
1350
|
+
*/
|
|
1351
|
+
async initialize() {
|
|
1352
|
+
if (this._initialized) {
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
if (this.initializeFn) {
|
|
1356
|
+
await this.initializeFn();
|
|
1357
|
+
}
|
|
1358
|
+
this._initialized = true;
|
|
1359
|
+
this._stats.initialized = true;
|
|
1360
|
+
if (this.healthCheckInterval && this.healthCheckFn) {
|
|
1361
|
+
this._healthCheckTimer = setInterval(async () => {
|
|
1362
|
+
try {
|
|
1363
|
+
const result = await this.healthCheckFn();
|
|
1364
|
+
this._stats.lastHealthCheck = result;
|
|
1365
|
+
this._stats.lastHealthCheckTime = Date.now();
|
|
1366
|
+
} catch (error) {
|
|
1367
|
+
this._stats.lastHealthCheck = {
|
|
1368
|
+
healthy: false,
|
|
1369
|
+
error: error.message
|
|
1370
|
+
};
|
|
1371
|
+
this._stats.lastHealthCheckTime = Date.now();
|
|
1372
|
+
}
|
|
1373
|
+
}, this.healthCheckInterval);
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
/**
|
|
1377
|
+
* Execute the tool
|
|
1378
|
+
*/
|
|
1379
|
+
async execute(input) {
|
|
1380
|
+
if (!this._initialized) {
|
|
1381
|
+
throw new Error(`Tool ${this.name} is not initialized. Call initialize() first.`);
|
|
1382
|
+
}
|
|
1383
|
+
const startTime = Date.now();
|
|
1384
|
+
this._stats.totalExecutions++;
|
|
1385
|
+
try {
|
|
1386
|
+
const result = await this.executeFn(input);
|
|
1387
|
+
this._stats.successfulExecutions++;
|
|
1388
|
+
this._stats.lastExecutionTime = Date.now() - startTime;
|
|
1389
|
+
return result;
|
|
1390
|
+
} catch (error) {
|
|
1391
|
+
this._stats.failedExecutions++;
|
|
1392
|
+
this._stats.lastExecutionTime = Date.now() - startTime;
|
|
1393
|
+
throw error;
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
/**
|
|
1397
|
+
* Cleanup the tool
|
|
1398
|
+
*/
|
|
1399
|
+
async cleanup() {
|
|
1400
|
+
if (!this._initialized) {
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1403
|
+
if (this._healthCheckTimer) {
|
|
1404
|
+
clearInterval(this._healthCheckTimer);
|
|
1405
|
+
this._healthCheckTimer = void 0;
|
|
1406
|
+
}
|
|
1407
|
+
if (this.cleanupFn) {
|
|
1408
|
+
await this.cleanupFn();
|
|
1409
|
+
}
|
|
1410
|
+
this._initialized = false;
|
|
1411
|
+
this._stats.initialized = false;
|
|
1412
|
+
}
|
|
1413
|
+
/**
|
|
1414
|
+
* Run health check
|
|
1415
|
+
*/
|
|
1416
|
+
async healthCheck() {
|
|
1417
|
+
if (!this._initialized) {
|
|
1418
|
+
return {
|
|
1419
|
+
healthy: false,
|
|
1420
|
+
error: "Tool is not initialized"
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
if (!this.healthCheckFn) {
|
|
1424
|
+
return {
|
|
1425
|
+
healthy: true,
|
|
1426
|
+
metadata: { message: "No health check configured" }
|
|
1427
|
+
};
|
|
1428
|
+
}
|
|
1429
|
+
try {
|
|
1430
|
+
const result = await this.healthCheckFn();
|
|
1431
|
+
this._stats.lastHealthCheck = result;
|
|
1432
|
+
this._stats.lastHealthCheckTime = Date.now();
|
|
1433
|
+
return result;
|
|
1434
|
+
} catch (error) {
|
|
1435
|
+
const result = {
|
|
1436
|
+
healthy: false,
|
|
1437
|
+
error: error.message
|
|
1438
|
+
};
|
|
1439
|
+
this._stats.lastHealthCheck = result;
|
|
1440
|
+
this._stats.lastHealthCheckTime = Date.now();
|
|
1441
|
+
return result;
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
/**
|
|
1445
|
+
* Get tool statistics
|
|
1446
|
+
*/
|
|
1447
|
+
getStats() {
|
|
1448
|
+
return { ...this._stats };
|
|
1449
|
+
}
|
|
1450
|
+
/**
|
|
1451
|
+
* Reset statistics
|
|
1452
|
+
*/
|
|
1453
|
+
resetStats() {
|
|
1454
|
+
this._stats.totalExecutions = 0;
|
|
1455
|
+
this._stats.successfulExecutions = 0;
|
|
1456
|
+
this._stats.failedExecutions = 0;
|
|
1457
|
+
this._stats.lastExecutionTime = void 0;
|
|
1458
|
+
}
|
|
1459
|
+
/**
|
|
1460
|
+
* Convert to LangChain tool format
|
|
1461
|
+
*/
|
|
1462
|
+
toLangChainTool() {
|
|
1463
|
+
return {
|
|
1464
|
+
name: this.name,
|
|
1465
|
+
description: this.description,
|
|
1466
|
+
invoke: async (input) => {
|
|
1467
|
+
return await this.execute(input);
|
|
1468
|
+
}
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1471
|
+
};
|
|
1472
|
+
function createManagedTool(config) {
|
|
1473
|
+
return new ManagedTool(config);
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
// src/tools/composition.ts
|
|
1477
|
+
function sequential(tools) {
|
|
1478
|
+
return {
|
|
1479
|
+
name: `sequential(${tools.map((t) => t.name).join(" -> ")})`,
|
|
1480
|
+
description: `Execute tools sequentially: ${tools.map((t) => t.name).join(" -> ")}`,
|
|
1481
|
+
invoke: async (input) => {
|
|
1482
|
+
let result = input;
|
|
1483
|
+
for (const tool of tools) {
|
|
1484
|
+
result = await tool.invoke(result);
|
|
1485
|
+
}
|
|
1486
|
+
return result;
|
|
1487
|
+
}
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
function parallel(tools) {
|
|
1491
|
+
return {
|
|
1492
|
+
name: `parallel(${tools.map((t) => t.name).join(", ")})`,
|
|
1493
|
+
description: `Execute tools in parallel: ${tools.map((t) => t.name).join(", ")}`,
|
|
1494
|
+
invoke: async (input) => {
|
|
1495
|
+
const results = await Promise.all(tools.map((tool) => tool.invoke(input)));
|
|
1496
|
+
return results;
|
|
1497
|
+
}
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
function conditional(config) {
|
|
1501
|
+
return {
|
|
1502
|
+
name: `conditional(${config.onTrue.name} | ${config.onFalse.name})`,
|
|
1503
|
+
description: `Conditionally execute ${config.onTrue.name} or ${config.onFalse.name}`,
|
|
1504
|
+
invoke: async (input) => {
|
|
1505
|
+
const shouldExecuteTrue = await config.condition(input);
|
|
1506
|
+
const tool = shouldExecuteTrue ? config.onTrue : config.onFalse;
|
|
1507
|
+
return await tool.invoke(input);
|
|
1508
|
+
}
|
|
1509
|
+
};
|
|
1510
|
+
}
|
|
1511
|
+
function composeTool(config) {
|
|
1512
|
+
return {
|
|
1513
|
+
name: config.name,
|
|
1514
|
+
description: config.description || `Composed tool: ${config.name}`,
|
|
1515
|
+
invoke: async (input) => {
|
|
1516
|
+
let result = input;
|
|
1517
|
+
for (const step of config.steps) {
|
|
1518
|
+
if (Array.isArray(step)) {
|
|
1519
|
+
result = await parallel(step).invoke(result);
|
|
1520
|
+
} else if ("condition" in step) {
|
|
1521
|
+
result = await conditional(step).invoke(result);
|
|
1522
|
+
} else {
|
|
1523
|
+
result = await step.invoke(result);
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
if (config.transformResult) {
|
|
1527
|
+
result = config.transformResult(result);
|
|
1528
|
+
}
|
|
1529
|
+
return result;
|
|
1530
|
+
}
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
function retry(tool, options = {}) {
|
|
1534
|
+
const { maxAttempts = 3, delay = 1e3, backoff = "exponential" } = options;
|
|
1535
|
+
return {
|
|
1536
|
+
name: `retry(${tool.name})`,
|
|
1537
|
+
description: `${tool.description} (with retry)`,
|
|
1538
|
+
invoke: async (input) => {
|
|
1539
|
+
let lastError;
|
|
1540
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
1541
|
+
try {
|
|
1542
|
+
return await tool.invoke(input);
|
|
1543
|
+
} catch (error) {
|
|
1544
|
+
lastError = error;
|
|
1545
|
+
if (attempt < maxAttempts) {
|
|
1546
|
+
const waitTime = backoff === "exponential" ? delay * Math.pow(2, attempt - 1) : delay * attempt;
|
|
1547
|
+
await new Promise((resolve) => setTimeout(resolve, waitTime));
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
throw lastError;
|
|
1552
|
+
}
|
|
1553
|
+
};
|
|
1554
|
+
}
|
|
1555
|
+
function timeout(tool, ms) {
|
|
1556
|
+
return {
|
|
1557
|
+
name: `timeout(${tool.name})`,
|
|
1558
|
+
description: `${tool.description} (with ${ms}ms timeout)`,
|
|
1559
|
+
invoke: async (input) => {
|
|
1560
|
+
return Promise.race([
|
|
1561
|
+
tool.invoke(input),
|
|
1562
|
+
new Promise(
|
|
1563
|
+
(_, reject) => setTimeout(() => reject(new Error(`Tool ${tool.name} timed out after ${ms}ms`)), ms)
|
|
1564
|
+
)
|
|
1565
|
+
]);
|
|
1566
|
+
}
|
|
1567
|
+
};
|
|
1568
|
+
}
|
|
1569
|
+
function cache(tool, ttl) {
|
|
1570
|
+
const cacheMap = /* @__PURE__ */ new Map();
|
|
1571
|
+
return {
|
|
1572
|
+
name: `cache(${tool.name})`,
|
|
1573
|
+
description: `${tool.description} (with caching)`,
|
|
1574
|
+
invoke: async (input) => {
|
|
1575
|
+
const key = JSON.stringify(input);
|
|
1576
|
+
const cached = cacheMap.get(key);
|
|
1577
|
+
if (cached) {
|
|
1578
|
+
if (!ttl || Date.now() - cached.timestamp < ttl) {
|
|
1579
|
+
return cached.result;
|
|
1580
|
+
}
|
|
1581
|
+
cacheMap.delete(key);
|
|
1582
|
+
}
|
|
1583
|
+
const result = await tool.invoke(input);
|
|
1584
|
+
cacheMap.set(key, { result, timestamp: Date.now() });
|
|
1585
|
+
return result;
|
|
1586
|
+
}
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
// src/tools/testing.ts
|
|
1591
|
+
function createMockTool(config) {
|
|
1592
|
+
const {
|
|
1593
|
+
name,
|
|
1594
|
+
description = `Mock tool: ${name}`,
|
|
1595
|
+
responses = [],
|
|
1596
|
+
defaultResponse,
|
|
1597
|
+
latency,
|
|
1598
|
+
errorRate = 0
|
|
1599
|
+
} = config;
|
|
1600
|
+
const invocations = [];
|
|
1601
|
+
return {
|
|
1602
|
+
name,
|
|
1603
|
+
description,
|
|
1604
|
+
invoke: async (input) => {
|
|
1605
|
+
const startTime = Date.now();
|
|
1606
|
+
if (latency) {
|
|
1607
|
+
const delay = typeof latency === "number" ? latency : Math.random() * (latency.max - latency.min) + latency.min;
|
|
1608
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1609
|
+
}
|
|
1610
|
+
if (errorRate > 0 && Math.random() < errorRate) {
|
|
1611
|
+
const error2 = new Error(`Random error from ${name}`);
|
|
1612
|
+
invocations.push({
|
|
1613
|
+
input,
|
|
1614
|
+
error: error2,
|
|
1615
|
+
timestamp: startTime,
|
|
1616
|
+
duration: Date.now() - startTime
|
|
1617
|
+
});
|
|
1618
|
+
throw error2;
|
|
1619
|
+
}
|
|
1620
|
+
const matchingResponse = responses.find((r) => {
|
|
1621
|
+
if (typeof r.input === "function") {
|
|
1622
|
+
return r.input(input);
|
|
1623
|
+
}
|
|
1624
|
+
return JSON.stringify(r.input) === JSON.stringify(input);
|
|
1625
|
+
});
|
|
1626
|
+
if (matchingResponse) {
|
|
1627
|
+
if (matchingResponse.error) {
|
|
1628
|
+
invocations.push({
|
|
1629
|
+
input,
|
|
1630
|
+
error: matchingResponse.error,
|
|
1631
|
+
timestamp: startTime,
|
|
1632
|
+
duration: Date.now() - startTime
|
|
1633
|
+
});
|
|
1634
|
+
throw matchingResponse.error;
|
|
1635
|
+
}
|
|
1636
|
+
invocations.push({
|
|
1637
|
+
input,
|
|
1638
|
+
output: matchingResponse.output,
|
|
1639
|
+
timestamp: startTime,
|
|
1640
|
+
duration: Date.now() - startTime
|
|
1641
|
+
});
|
|
1642
|
+
return matchingResponse.output;
|
|
1643
|
+
}
|
|
1644
|
+
if (defaultResponse !== void 0) {
|
|
1645
|
+
invocations.push({
|
|
1646
|
+
input,
|
|
1647
|
+
output: defaultResponse,
|
|
1648
|
+
timestamp: startTime,
|
|
1649
|
+
duration: Date.now() - startTime
|
|
1650
|
+
});
|
|
1651
|
+
return defaultResponse;
|
|
1652
|
+
}
|
|
1653
|
+
const error = new Error(`No mock response configured for input: ${JSON.stringify(input)}`);
|
|
1654
|
+
invocations.push({
|
|
1655
|
+
input,
|
|
1656
|
+
error,
|
|
1657
|
+
timestamp: startTime,
|
|
1658
|
+
duration: Date.now() - startTime
|
|
1659
|
+
});
|
|
1660
|
+
throw error;
|
|
1661
|
+
},
|
|
1662
|
+
getInvocations: () => [...invocations],
|
|
1663
|
+
clearInvocations: () => {
|
|
1664
|
+
invocations.length = 0;
|
|
1665
|
+
}
|
|
1666
|
+
};
|
|
1667
|
+
}
|
|
1668
|
+
function createToolSimulator(config) {
|
|
1669
|
+
const { tools, errorRate = 0, latency, recordInvocations = true } = config;
|
|
1670
|
+
const toolMap = new Map(tools.map((t) => [t.name, t]));
|
|
1671
|
+
const invocations = /* @__PURE__ */ new Map();
|
|
1672
|
+
if (recordInvocations) {
|
|
1673
|
+
tools.forEach((t) => invocations.set(t.name, []));
|
|
1674
|
+
}
|
|
1675
|
+
function generateLatency() {
|
|
1676
|
+
if (!latency) return 0;
|
|
1677
|
+
const u1 = Math.random();
|
|
1678
|
+
const u2 = Math.random();
|
|
1679
|
+
const z3 = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
|
|
1680
|
+
return Math.max(0, latency.mean + z3 * latency.stddev);
|
|
1681
|
+
}
|
|
1682
|
+
return {
|
|
1683
|
+
execute: async (toolName, input) => {
|
|
1684
|
+
const tool = toolMap.get(toolName);
|
|
1685
|
+
if (!tool) {
|
|
1686
|
+
throw new Error(`Tool ${toolName} not found in simulator`);
|
|
1687
|
+
}
|
|
1688
|
+
const startTime = Date.now();
|
|
1689
|
+
if (latency) {
|
|
1690
|
+
const delay = generateLatency();
|
|
1691
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1692
|
+
}
|
|
1693
|
+
if (errorRate > 0 && Math.random() < errorRate) {
|
|
1694
|
+
const error = new Error(`Simulated error from ${toolName}`);
|
|
1695
|
+
if (recordInvocations) {
|
|
1696
|
+
invocations.get(toolName).push({
|
|
1697
|
+
input,
|
|
1698
|
+
error,
|
|
1699
|
+
timestamp: startTime,
|
|
1700
|
+
duration: Date.now() - startTime
|
|
1701
|
+
});
|
|
1702
|
+
}
|
|
1703
|
+
throw error;
|
|
1704
|
+
}
|
|
1705
|
+
try {
|
|
1706
|
+
const result = await tool.invoke(input);
|
|
1707
|
+
if (recordInvocations) {
|
|
1708
|
+
invocations.get(toolName).push({
|
|
1709
|
+
input,
|
|
1710
|
+
output: result,
|
|
1711
|
+
timestamp: startTime,
|
|
1712
|
+
duration: Date.now() - startTime
|
|
1713
|
+
});
|
|
1714
|
+
}
|
|
1715
|
+
return result;
|
|
1716
|
+
} catch (error) {
|
|
1717
|
+
if (recordInvocations) {
|
|
1718
|
+
invocations.get(toolName).push({
|
|
1719
|
+
input,
|
|
1720
|
+
error,
|
|
1721
|
+
timestamp: startTime,
|
|
1722
|
+
duration: Date.now() - startTime
|
|
1723
|
+
});
|
|
1724
|
+
}
|
|
1725
|
+
throw error;
|
|
1726
|
+
}
|
|
1727
|
+
},
|
|
1728
|
+
getInvocations: (toolName) => {
|
|
1729
|
+
return invocations.get(toolName) ? [...invocations.get(toolName)] : [];
|
|
1730
|
+
},
|
|
1731
|
+
getAllInvocations: () => {
|
|
1732
|
+
const all = {};
|
|
1733
|
+
invocations.forEach((invs, name) => {
|
|
1734
|
+
all[name] = [...invs];
|
|
1735
|
+
});
|
|
1736
|
+
return all;
|
|
1737
|
+
},
|
|
1738
|
+
clearInvocations: (toolName) => {
|
|
1739
|
+
if (toolName) {
|
|
1740
|
+
invocations.get(toolName)?.splice(0);
|
|
1741
|
+
} else {
|
|
1742
|
+
invocations.forEach((invs) => invs.splice(0));
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
};
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
// src/langgraph/state.ts
|
|
1749
|
+
var import_langgraph = require("@langchain/langgraph");
|
|
1750
|
+
function createStateAnnotation(config) {
|
|
1751
|
+
const stateDefinition = {};
|
|
1752
|
+
for (const [key, channelConfig] of Object.entries(config)) {
|
|
1753
|
+
if (channelConfig.reducer) {
|
|
1754
|
+
stateDefinition[key] = (0, import_langgraph.Annotation)({
|
|
1755
|
+
reducer: channelConfig.reducer,
|
|
1756
|
+
default: channelConfig.default
|
|
1757
|
+
});
|
|
1758
|
+
} else {
|
|
1759
|
+
stateDefinition[key] = (0, import_langgraph.Annotation)();
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
return import_langgraph.Annotation.Root(stateDefinition);
|
|
1763
|
+
}
|
|
1764
|
+
function validateState(state, config) {
|
|
1765
|
+
const validated = {};
|
|
1766
|
+
for (const [key, channelConfig] of Object.entries(config)) {
|
|
1767
|
+
if (channelConfig.schema && key in state) {
|
|
1768
|
+
validated[key] = channelConfig.schema.parse(state[key]);
|
|
1769
|
+
} else if (key in state) {
|
|
1770
|
+
validated[key] = state[key];
|
|
1771
|
+
} else if (channelConfig.default) {
|
|
1772
|
+
validated[key] = channelConfig.default();
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
return validated;
|
|
1776
|
+
}
|
|
1777
|
+
function mergeState(currentState, update, config) {
|
|
1778
|
+
const merged = { ...currentState };
|
|
1779
|
+
for (const [key, value] of Object.entries(update)) {
|
|
1780
|
+
const channelConfig = config[key];
|
|
1781
|
+
if (channelConfig?.reducer && key in merged) {
|
|
1782
|
+
merged[key] = channelConfig.reducer(merged[key], value);
|
|
1783
|
+
} else {
|
|
1784
|
+
merged[key] = value;
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
return merged;
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
// src/langgraph/builders/sequential.ts
|
|
1791
|
+
var import_langgraph2 = require("@langchain/langgraph");
|
|
1792
|
+
function createSequentialWorkflow(stateSchema, nodes, options = {}) {
|
|
1793
|
+
const { autoStartEnd = true, name } = options;
|
|
1794
|
+
if (nodes.length === 0) {
|
|
1795
|
+
throw new Error("Sequential workflow must have at least one node");
|
|
1796
|
+
}
|
|
1797
|
+
const nodeNames = /* @__PURE__ */ new Set();
|
|
1798
|
+
for (const node of nodes) {
|
|
1799
|
+
if (nodeNames.has(node.name)) {
|
|
1800
|
+
throw new Error(`Duplicate node name: ${node.name}`);
|
|
1801
|
+
}
|
|
1802
|
+
nodeNames.add(node.name);
|
|
1803
|
+
}
|
|
1804
|
+
const graph = new import_langgraph2.StateGraph(stateSchema);
|
|
1805
|
+
for (const { name: nodeName, node } of nodes) {
|
|
1806
|
+
graph.addNode(nodeName, node);
|
|
1807
|
+
}
|
|
1808
|
+
if (autoStartEnd) {
|
|
1809
|
+
graph.addEdge(import_langgraph2.START, nodes[0].name);
|
|
1810
|
+
}
|
|
1811
|
+
for (let i = 0; i < nodes.length - 1; i++) {
|
|
1812
|
+
graph.addEdge(nodes[i].name, nodes[i + 1].name);
|
|
1813
|
+
}
|
|
1814
|
+
if (autoStartEnd) {
|
|
1815
|
+
graph.addEdge(nodes[nodes.length - 1].name, import_langgraph2.END);
|
|
1816
|
+
}
|
|
1817
|
+
return graph;
|
|
1818
|
+
}
|
|
1819
|
+
function sequentialBuilder(stateSchema) {
|
|
1820
|
+
const nodes = [];
|
|
1821
|
+
let options = {};
|
|
1822
|
+
return {
|
|
1823
|
+
/**
|
|
1824
|
+
* Add a node to the sequential workflow
|
|
1825
|
+
*/
|
|
1826
|
+
addNode(name, node, description) {
|
|
1827
|
+
nodes.push({ name, node, description });
|
|
1828
|
+
return this;
|
|
1829
|
+
},
|
|
1830
|
+
/**
|
|
1831
|
+
* Set options for the workflow
|
|
1832
|
+
*/
|
|
1833
|
+
options(opts) {
|
|
1834
|
+
options = { ...options, ...opts };
|
|
1835
|
+
return this;
|
|
1836
|
+
},
|
|
1837
|
+
/**
|
|
1838
|
+
* Build the StateGraph
|
|
1839
|
+
*/
|
|
1840
|
+
build() {
|
|
1841
|
+
return createSequentialWorkflow(stateSchema, nodes, options);
|
|
1842
|
+
}
|
|
1843
|
+
};
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
// src/langgraph/builders/parallel.ts
|
|
1847
|
+
var import_langgraph3 = require("@langchain/langgraph");
|
|
1848
|
+
function createParallelWorkflow(stateSchema, config, options = {}) {
|
|
1849
|
+
const { parallel: parallel2, aggregate } = config;
|
|
1850
|
+
const { autoStartEnd = true, name } = options;
|
|
1851
|
+
if (parallel2.length === 0) {
|
|
1852
|
+
throw new Error("Parallel workflow must have at least one parallel node");
|
|
1853
|
+
}
|
|
1854
|
+
const nodeNames = /* @__PURE__ */ new Set();
|
|
1855
|
+
for (const node of parallel2) {
|
|
1856
|
+
if (nodeNames.has(node.name)) {
|
|
1857
|
+
throw new Error(`Duplicate node name: ${node.name}`);
|
|
1858
|
+
}
|
|
1859
|
+
nodeNames.add(node.name);
|
|
1860
|
+
}
|
|
1861
|
+
if (aggregate && nodeNames.has(aggregate.name)) {
|
|
1862
|
+
throw new Error(`Duplicate node name: ${aggregate.name}`);
|
|
1863
|
+
}
|
|
1864
|
+
const graph = new import_langgraph3.StateGraph(stateSchema);
|
|
1865
|
+
for (const { name: nodeName, node } of parallel2) {
|
|
1866
|
+
graph.addNode(nodeName, node);
|
|
1867
|
+
}
|
|
1868
|
+
if (aggregate) {
|
|
1869
|
+
graph.addNode(aggregate.name, aggregate.node);
|
|
1870
|
+
}
|
|
1871
|
+
if (autoStartEnd) {
|
|
1872
|
+
for (const { name: nodeName } of parallel2) {
|
|
1873
|
+
graph.addEdge(import_langgraph3.START, nodeName);
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
if (aggregate) {
|
|
1877
|
+
for (const { name: nodeName } of parallel2) {
|
|
1878
|
+
graph.addEdge(nodeName, aggregate.name);
|
|
1879
|
+
}
|
|
1880
|
+
if (autoStartEnd) {
|
|
1881
|
+
graph.addEdge(aggregate.name, import_langgraph3.END);
|
|
1882
|
+
}
|
|
1883
|
+
} else if (autoStartEnd) {
|
|
1884
|
+
for (const { name: nodeName } of parallel2) {
|
|
1885
|
+
graph.addEdge(nodeName, import_langgraph3.END);
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
return graph;
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
// src/langgraph/builders/conditional.ts
|
|
1892
|
+
function createConditionalRouter(config) {
|
|
1893
|
+
const { routes, condition, description } = config;
|
|
1894
|
+
if (Object.keys(routes).length === 0) {
|
|
1895
|
+
throw new Error("Conditional router must have at least one route");
|
|
1896
|
+
}
|
|
1897
|
+
if (typeof condition !== "function") {
|
|
1898
|
+
throw new Error("Conditional router must have a condition function");
|
|
1899
|
+
}
|
|
1900
|
+
return {
|
|
1901
|
+
routes,
|
|
1902
|
+
condition,
|
|
1903
|
+
description
|
|
1904
|
+
};
|
|
1905
|
+
}
|
|
1906
|
+
function createBinaryRouter(config) {
|
|
1907
|
+
const { condition, ifTrue, ifFalse, description } = config;
|
|
1908
|
+
return {
|
|
1909
|
+
routes: {
|
|
1910
|
+
true: ifTrue,
|
|
1911
|
+
false: ifFalse
|
|
1912
|
+
},
|
|
1913
|
+
condition: (state) => condition(state) ? "true" : "false",
|
|
1914
|
+
description
|
|
1915
|
+
};
|
|
1916
|
+
}
|
|
1917
|
+
function createMultiRouter(config) {
|
|
1918
|
+
const { discriminator, routes, default: defaultRoute, description } = config;
|
|
1919
|
+
return {
|
|
1920
|
+
routes,
|
|
1921
|
+
condition: (state) => {
|
|
1922
|
+
const key = discriminator(state);
|
|
1923
|
+
if (key in routes) {
|
|
1924
|
+
return key;
|
|
1925
|
+
}
|
|
1926
|
+
if (defaultRoute !== void 0) {
|
|
1927
|
+
return defaultRoute;
|
|
1928
|
+
}
|
|
1929
|
+
throw new Error(`No route found for discriminator value: ${key}`);
|
|
1930
|
+
},
|
|
1931
|
+
description
|
|
1932
|
+
};
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
// src/langgraph/builders/subgraph.ts
|
|
1936
|
+
var import_langgraph4 = require("@langchain/langgraph");
|
|
1937
|
+
function createSubgraph(stateSchema, builder) {
|
|
1938
|
+
const graph = new import_langgraph4.StateGraph(stateSchema);
|
|
1939
|
+
const configured = builder(graph);
|
|
1940
|
+
return configured.compile();
|
|
1941
|
+
}
|
|
1942
|
+
function composeGraphs(parentGraph, subgraph, options) {
|
|
1943
|
+
const { name } = options;
|
|
1944
|
+
parentGraph.addNode(name, subgraph);
|
|
1945
|
+
return parentGraph;
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
// src/langgraph/patterns/retry.ts
|
|
1949
|
+
function calculateDelay(attempt, strategy, initialDelay, maxDelay) {
|
|
1950
|
+
let delay;
|
|
1951
|
+
switch (strategy) {
|
|
1952
|
+
case "constant":
|
|
1953
|
+
delay = initialDelay;
|
|
1954
|
+
break;
|
|
1955
|
+
case "linear":
|
|
1956
|
+
delay = initialDelay * attempt;
|
|
1957
|
+
break;
|
|
1958
|
+
case "exponential":
|
|
1959
|
+
delay = initialDelay * Math.pow(2, attempt - 1);
|
|
1960
|
+
break;
|
|
1961
|
+
}
|
|
1962
|
+
return Math.min(delay, maxDelay);
|
|
1963
|
+
}
|
|
1964
|
+
function sleep(ms) {
|
|
1965
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1966
|
+
}
|
|
1967
|
+
function withRetry(node, options = {}) {
|
|
1968
|
+
const {
|
|
1969
|
+
maxAttempts = 3,
|
|
1970
|
+
backoff = "exponential",
|
|
1971
|
+
initialDelay = 1e3,
|
|
1972
|
+
maxDelay = 3e4,
|
|
1973
|
+
onRetry,
|
|
1974
|
+
shouldRetry = () => true
|
|
1975
|
+
} = options;
|
|
1976
|
+
return async (state) => {
|
|
1977
|
+
let lastError;
|
|
1978
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
1979
|
+
try {
|
|
1980
|
+
return await Promise.resolve(node(state));
|
|
1981
|
+
} catch (error) {
|
|
1982
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
1983
|
+
if (!shouldRetry(lastError)) {
|
|
1984
|
+
throw lastError;
|
|
1985
|
+
}
|
|
1986
|
+
if (attempt === maxAttempts) {
|
|
1987
|
+
throw lastError;
|
|
1988
|
+
}
|
|
1989
|
+
if (onRetry) {
|
|
1990
|
+
onRetry(lastError, attempt);
|
|
1991
|
+
}
|
|
1992
|
+
const delay = calculateDelay(attempt, backoff, initialDelay, maxDelay);
|
|
1993
|
+
await sleep(delay);
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
throw lastError || new Error("Retry failed");
|
|
1997
|
+
};
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
// src/langgraph/patterns/error-handler.ts
|
|
2001
|
+
function withErrorHandler(node, options) {
|
|
2002
|
+
const { onError, logError, rethrow = false } = options;
|
|
2003
|
+
return async (state) => {
|
|
2004
|
+
try {
|
|
2005
|
+
return await Promise.resolve(node(state));
|
|
2006
|
+
} catch (error) {
|
|
2007
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
2008
|
+
if (logError) {
|
|
2009
|
+
logError(err, state);
|
|
2010
|
+
}
|
|
2011
|
+
const result = await Promise.resolve(onError(err, state));
|
|
2012
|
+
if (rethrow) {
|
|
2013
|
+
throw err;
|
|
2014
|
+
}
|
|
2015
|
+
return result;
|
|
2016
|
+
}
|
|
2017
|
+
};
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
// src/langgraph/patterns/timeout.ts
|
|
2021
|
+
var TimeoutError = class extends Error {
|
|
2022
|
+
constructor(timeout2) {
|
|
2023
|
+
super(`Node execution timed out after ${timeout2}ms`);
|
|
2024
|
+
this.name = "TimeoutError";
|
|
2025
|
+
}
|
|
2026
|
+
};
|
|
2027
|
+
function withTimeout(node, options) {
|
|
2028
|
+
const { timeout: timeout2, onTimeout, logTimeout, throwOnTimeout = false } = options;
|
|
2029
|
+
return async (state) => {
|
|
2030
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
2031
|
+
setTimeout(() => {
|
|
2032
|
+
reject(new TimeoutError(timeout2));
|
|
2033
|
+
}, timeout2);
|
|
2034
|
+
});
|
|
2035
|
+
try {
|
|
2036
|
+
return await Promise.race([Promise.resolve(node(state)), timeoutPromise]);
|
|
2037
|
+
} catch (error) {
|
|
2038
|
+
if (error instanceof TimeoutError) {
|
|
2039
|
+
if (logTimeout) {
|
|
2040
|
+
logTimeout(state);
|
|
2041
|
+
}
|
|
2042
|
+
if (throwOnTimeout) {
|
|
2043
|
+
throw error;
|
|
2044
|
+
}
|
|
2045
|
+
return await Promise.resolve(onTimeout(state));
|
|
2046
|
+
}
|
|
2047
|
+
throw error;
|
|
2048
|
+
}
|
|
2049
|
+
};
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
// src/langgraph/persistence/checkpointer.ts
|
|
2053
|
+
var import_langgraph5 = require("@langchain/langgraph");
|
|
2054
|
+
function createMemoryCheckpointer(options) {
|
|
2055
|
+
return new import_langgraph5.MemorySaver();
|
|
2056
|
+
}
|
|
2057
|
+
async function createSqliteCheckpointer(options = {}) {
|
|
2058
|
+
const { path = ":memory:", autoMigrate = true } = options;
|
|
2059
|
+
try {
|
|
2060
|
+
const { SqliteSaver } = await import("@langchain/langgraph-checkpoint-sqlite");
|
|
2061
|
+
const checkpointer = SqliteSaver.fromConnString(path);
|
|
2062
|
+
if (autoMigrate) {
|
|
2063
|
+
await checkpointer.setup();
|
|
2064
|
+
}
|
|
2065
|
+
return checkpointer;
|
|
2066
|
+
} catch (error) {
|
|
2067
|
+
if (error instanceof Error && error.message.includes("Cannot find module")) {
|
|
2068
|
+
throw new Error(
|
|
2069
|
+
"SQLite checkpointer requires @langchain/langgraph-checkpoint-sqlite to be installed. Install it with: npm install @langchain/langgraph-checkpoint-sqlite"
|
|
2070
|
+
);
|
|
2071
|
+
}
|
|
2072
|
+
throw error;
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
function isMemoryCheckpointer(checkpointer) {
|
|
2076
|
+
return checkpointer instanceof import_langgraph5.MemorySaver;
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
// src/langgraph/persistence/thread.ts
|
|
2080
|
+
var import_crypto = require("crypto");
|
|
2081
|
+
function generateThreadId(seed) {
|
|
2082
|
+
if (seed && seed.length > 0) {
|
|
2083
|
+
const hash = Buffer.from(seed).toString("base64").replace(/[^a-zA-Z0-9]/g, "");
|
|
2084
|
+
return `thread-${hash}`;
|
|
2085
|
+
}
|
|
2086
|
+
return (0, import_crypto.randomUUID)();
|
|
2087
|
+
}
|
|
2088
|
+
function createThreadConfig(config = {}) {
|
|
2089
|
+
const { threadId = generateThreadId(), checkpointId, checkpointNamespace, metadata } = config;
|
|
2090
|
+
const runnableConfig = {
|
|
2091
|
+
configurable: {
|
|
2092
|
+
thread_id: threadId
|
|
2093
|
+
}
|
|
2094
|
+
};
|
|
2095
|
+
if (checkpointId) {
|
|
2096
|
+
runnableConfig.configurable.checkpoint_id = checkpointId;
|
|
2097
|
+
}
|
|
2098
|
+
if (checkpointNamespace) {
|
|
2099
|
+
runnableConfig.configurable.checkpoint_ns = checkpointNamespace;
|
|
2100
|
+
}
|
|
2101
|
+
if (metadata) {
|
|
2102
|
+
runnableConfig.metadata = metadata;
|
|
2103
|
+
}
|
|
2104
|
+
return runnableConfig;
|
|
2105
|
+
}
|
|
2106
|
+
function createConversationConfig(config) {
|
|
2107
|
+
const { userId, sessionId, metadata = {} } = config;
|
|
2108
|
+
const threadId = sessionId ? generateThreadId(`${userId}-${sessionId}`) : generateThreadId(userId);
|
|
2109
|
+
return createThreadConfig({
|
|
2110
|
+
threadId,
|
|
2111
|
+
metadata: {
|
|
2112
|
+
...metadata,
|
|
2113
|
+
userId,
|
|
2114
|
+
sessionId
|
|
2115
|
+
}
|
|
2116
|
+
});
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
// src/langgraph/persistence/utils.ts
|
|
2120
|
+
async function getCheckpointHistory(checkpointer, options) {
|
|
2121
|
+
const { threadId, limit = 10, before } = options;
|
|
2122
|
+
const config = {
|
|
2123
|
+
configurable: {
|
|
2124
|
+
thread_id: threadId
|
|
2125
|
+
}
|
|
2126
|
+
};
|
|
2127
|
+
if (before) {
|
|
2128
|
+
config.configurable.checkpoint_id = before;
|
|
2129
|
+
}
|
|
2130
|
+
const checkpoints = [];
|
|
2131
|
+
for await (const checkpoint of checkpointer.list(config, { limit })) {
|
|
2132
|
+
checkpoints.push(checkpoint);
|
|
2133
|
+
}
|
|
2134
|
+
return checkpoints;
|
|
2135
|
+
}
|
|
2136
|
+
async function getLatestCheckpoint(checkpointer, options) {
|
|
2137
|
+
const { threadId } = options;
|
|
2138
|
+
const config = {
|
|
2139
|
+
configurable: {
|
|
2140
|
+
thread_id: threadId
|
|
2141
|
+
}
|
|
2142
|
+
};
|
|
2143
|
+
const tuple = await checkpointer.getTuple(config);
|
|
2144
|
+
return tuple || null;
|
|
2145
|
+
}
|
|
2146
|
+
async function clearThread(checkpointer, options) {
|
|
2147
|
+
const { threadId } = options;
|
|
2148
|
+
const checkpoints = await getCheckpointHistory(checkpointer, {
|
|
2149
|
+
threadId,
|
|
2150
|
+
limit: 1e3
|
|
2151
|
+
// Get all checkpoints
|
|
2152
|
+
});
|
|
2153
|
+
if (checkpoints.length > 0) {
|
|
2154
|
+
throw new Error(
|
|
2155
|
+
"Clearing threads is not supported by the current checkpointer implementation. Consider using a new thread ID instead."
|
|
2156
|
+
);
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
// src/langgraph/observability/langsmith.ts
|
|
2161
|
+
var globalConfig = null;
|
|
2162
|
+
function configureLangSmith(config) {
|
|
2163
|
+
globalConfig = config;
|
|
2164
|
+
if (config.apiKey) {
|
|
2165
|
+
process.env.LANGSMITH_API_KEY = config.apiKey;
|
|
2166
|
+
}
|
|
2167
|
+
if (config.projectName) {
|
|
2168
|
+
process.env.LANGSMITH_PROJECT = config.projectName;
|
|
2169
|
+
}
|
|
2170
|
+
if (config.tracingEnabled !== void 0) {
|
|
2171
|
+
process.env.LANGSMITH_TRACING = config.tracingEnabled ? "true" : "false";
|
|
2172
|
+
} else if (config.apiKey) {
|
|
2173
|
+
process.env.LANGSMITH_TRACING = "true";
|
|
2174
|
+
}
|
|
2175
|
+
if (config.endpoint) {
|
|
2176
|
+
process.env.LANGSMITH_ENDPOINT = config.endpoint;
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
function getLangSmithConfig() {
|
|
2180
|
+
return globalConfig;
|
|
2181
|
+
}
|
|
2182
|
+
function isTracingEnabled() {
|
|
2183
|
+
return process.env.LANGSMITH_TRACING === "true";
|
|
2184
|
+
}
|
|
2185
|
+
function withTracing(node, options) {
|
|
2186
|
+
const { name, metadata, tags, runName } = options;
|
|
2187
|
+
return async (state) => {
|
|
2188
|
+
if (isTracingEnabled()) {
|
|
2189
|
+
const result = await Promise.resolve(node(state));
|
|
2190
|
+
return result;
|
|
2191
|
+
}
|
|
2192
|
+
return await Promise.resolve(node(state));
|
|
2193
|
+
};
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
// src/langgraph/observability/logger.ts
|
|
2197
|
+
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
2198
|
+
LogLevel2["DEBUG"] = "debug";
|
|
2199
|
+
LogLevel2["INFO"] = "info";
|
|
2200
|
+
LogLevel2["WARN"] = "warn";
|
|
2201
|
+
LogLevel2["ERROR"] = "error";
|
|
2202
|
+
return LogLevel2;
|
|
2203
|
+
})(LogLevel || {});
|
|
2204
|
+
var LOG_LEVEL_PRIORITY = {
|
|
2205
|
+
["debug" /* DEBUG */]: 0,
|
|
2206
|
+
["info" /* INFO */]: 1,
|
|
2207
|
+
["warn" /* WARN */]: 2,
|
|
2208
|
+
["error" /* ERROR */]: 3
|
|
2209
|
+
};
|
|
2210
|
+
var LoggerImpl = class _LoggerImpl {
|
|
2211
|
+
name;
|
|
2212
|
+
options;
|
|
2213
|
+
context;
|
|
2214
|
+
constructor(name, options = {}, context = {}) {
|
|
2215
|
+
this.name = name;
|
|
2216
|
+
this.context = context;
|
|
2217
|
+
this.options = {
|
|
2218
|
+
level: options.level ?? "info" /* INFO */,
|
|
2219
|
+
format: options.format ?? "pretty",
|
|
2220
|
+
destination: options.destination ?? process.stdout,
|
|
2221
|
+
includeTimestamp: options.includeTimestamp ?? true,
|
|
2222
|
+
includeContext: options.includeContext ?? true
|
|
2223
|
+
};
|
|
2224
|
+
}
|
|
2225
|
+
debug(message, data) {
|
|
2226
|
+
this.log("debug" /* DEBUG */, message, data);
|
|
2227
|
+
}
|
|
2228
|
+
info(message, data) {
|
|
2229
|
+
this.log("info" /* INFO */, message, data);
|
|
2230
|
+
}
|
|
2231
|
+
warn(message, data) {
|
|
2232
|
+
this.log("warn" /* WARN */, message, data);
|
|
2233
|
+
}
|
|
2234
|
+
error(message, data) {
|
|
2235
|
+
this.log("error" /* ERROR */, message, data);
|
|
2236
|
+
}
|
|
2237
|
+
withContext(context) {
|
|
2238
|
+
return new _LoggerImpl(this.name, this.options, { ...this.context, ...context });
|
|
2239
|
+
}
|
|
2240
|
+
log(level, message, data) {
|
|
2241
|
+
if (LOG_LEVEL_PRIORITY[level] < LOG_LEVEL_PRIORITY[this.options.level]) {
|
|
2242
|
+
return;
|
|
2243
|
+
}
|
|
2244
|
+
const entry = {
|
|
2245
|
+
level,
|
|
2246
|
+
name: this.name,
|
|
2247
|
+
message
|
|
2248
|
+
};
|
|
2249
|
+
if (this.options.includeTimestamp) {
|
|
2250
|
+
entry.timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
2251
|
+
}
|
|
2252
|
+
if (this.options.includeContext && Object.keys(this.context).length > 0) {
|
|
2253
|
+
entry.context = this.context;
|
|
2254
|
+
}
|
|
2255
|
+
if (data) {
|
|
2256
|
+
entry.data = data;
|
|
2257
|
+
}
|
|
2258
|
+
const output = this.format(entry);
|
|
2259
|
+
this.options.destination.write(output + "\n");
|
|
2260
|
+
}
|
|
2261
|
+
format(entry) {
|
|
2262
|
+
if (this.options.format === "json") {
|
|
2263
|
+
return JSON.stringify(entry);
|
|
2264
|
+
}
|
|
2265
|
+
const parts = [];
|
|
2266
|
+
if (entry.timestamp) {
|
|
2267
|
+
parts.push(`[${entry.timestamp}]`);
|
|
2268
|
+
}
|
|
2269
|
+
parts.push(`[${entry.level.toUpperCase()}]`);
|
|
2270
|
+
parts.push(`[${entry.name}]`);
|
|
2271
|
+
parts.push(entry.message);
|
|
2272
|
+
if (entry.context) {
|
|
2273
|
+
parts.push(`context=${JSON.stringify(entry.context)}`);
|
|
2274
|
+
}
|
|
2275
|
+
if (entry.data) {
|
|
2276
|
+
parts.push(`data=${JSON.stringify(entry.data)}`);
|
|
2277
|
+
}
|
|
2278
|
+
return parts.join(" ");
|
|
2279
|
+
}
|
|
2280
|
+
};
|
|
2281
|
+
function createLogger(name, options) {
|
|
2282
|
+
return new LoggerImpl(name, options);
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
// src/langgraph/observability/metrics.ts
|
|
2286
|
+
var MetricType = /* @__PURE__ */ ((MetricType2) => {
|
|
2287
|
+
MetricType2["COUNTER"] = "counter";
|
|
2288
|
+
MetricType2["GAUGE"] = "gauge";
|
|
2289
|
+
MetricType2["HISTOGRAM"] = "histogram";
|
|
2290
|
+
return MetricType2;
|
|
2291
|
+
})(MetricType || {});
|
|
2292
|
+
var MetricsImpl = class {
|
|
2293
|
+
name;
|
|
2294
|
+
metrics = [];
|
|
2295
|
+
counters = /* @__PURE__ */ new Map();
|
|
2296
|
+
constructor(name) {
|
|
2297
|
+
this.name = name;
|
|
2298
|
+
}
|
|
2299
|
+
increment(name, value = 1, labels) {
|
|
2300
|
+
const key = this.getKey(name, labels);
|
|
2301
|
+
const current = this.counters.get(key) ?? 0;
|
|
2302
|
+
this.counters.set(key, current + value);
|
|
2303
|
+
this.record({
|
|
2304
|
+
type: "counter" /* COUNTER */,
|
|
2305
|
+
name: this.prefixName(name),
|
|
2306
|
+
value: current + value,
|
|
2307
|
+
timestamp: Date.now(),
|
|
2308
|
+
labels
|
|
2309
|
+
});
|
|
2310
|
+
}
|
|
2311
|
+
decrement(name, value = 1, labels) {
|
|
2312
|
+
this.increment(name, -value, labels);
|
|
2313
|
+
}
|
|
2314
|
+
gauge(name, value, labels) {
|
|
2315
|
+
this.record({
|
|
2316
|
+
type: "gauge" /* GAUGE */,
|
|
2317
|
+
name: this.prefixName(name),
|
|
2318
|
+
value,
|
|
2319
|
+
timestamp: Date.now(),
|
|
2320
|
+
labels
|
|
2321
|
+
});
|
|
2322
|
+
}
|
|
2323
|
+
histogram(name, value, labels) {
|
|
2324
|
+
this.record({
|
|
2325
|
+
type: "histogram" /* HISTOGRAM */,
|
|
2326
|
+
name: this.prefixName(name),
|
|
2327
|
+
value,
|
|
2328
|
+
timestamp: Date.now(),
|
|
2329
|
+
labels
|
|
2330
|
+
});
|
|
2331
|
+
}
|
|
2332
|
+
startTimer(name, labels) {
|
|
2333
|
+
const startTime = Date.now();
|
|
2334
|
+
return {
|
|
2335
|
+
end: () => {
|
|
2336
|
+
const duration = Date.now() - startTime;
|
|
2337
|
+
this.histogram(name, duration, labels);
|
|
2338
|
+
return duration;
|
|
2339
|
+
}
|
|
2340
|
+
};
|
|
2341
|
+
}
|
|
2342
|
+
getMetrics() {
|
|
2343
|
+
return [...this.metrics];
|
|
2344
|
+
}
|
|
2345
|
+
clear() {
|
|
2346
|
+
this.metrics = [];
|
|
2347
|
+
this.counters.clear();
|
|
2348
|
+
}
|
|
2349
|
+
record(entry) {
|
|
2350
|
+
this.metrics.push(entry);
|
|
2351
|
+
}
|
|
2352
|
+
prefixName(name) {
|
|
2353
|
+
return `${this.name}.${name}`;
|
|
2354
|
+
}
|
|
2355
|
+
getKey(name, labels) {
|
|
2356
|
+
const labelStr = labels ? JSON.stringify(labels) : "";
|
|
2357
|
+
return `${name}:${labelStr}`;
|
|
2358
|
+
}
|
|
2359
|
+
};
|
|
2360
|
+
function createMetrics(name) {
|
|
2361
|
+
return new MetricsImpl(name);
|
|
2362
|
+
}
|
|
2363
|
+
function withMetrics(node, options) {
|
|
2364
|
+
const {
|
|
2365
|
+
name,
|
|
2366
|
+
trackDuration = true,
|
|
2367
|
+
trackErrors = true,
|
|
2368
|
+
trackInvocations = true,
|
|
2369
|
+
metrics = createMetrics(name)
|
|
2370
|
+
} = options;
|
|
2371
|
+
return async (state) => {
|
|
2372
|
+
if (trackInvocations) {
|
|
2373
|
+
metrics.increment(`${name}.invocations`);
|
|
2374
|
+
}
|
|
2375
|
+
const timer = trackDuration ? metrics.startTimer(`${name}.duration`) : null;
|
|
2376
|
+
try {
|
|
2377
|
+
const result = await Promise.resolve(node(state));
|
|
2378
|
+
metrics.increment(`${name}.success`);
|
|
2379
|
+
return result;
|
|
2380
|
+
} catch (error) {
|
|
2381
|
+
if (trackErrors) {
|
|
2382
|
+
metrics.increment(`${name}.errors`);
|
|
2383
|
+
}
|
|
2384
|
+
throw error;
|
|
2385
|
+
} finally {
|
|
2386
|
+
if (timer) {
|
|
2387
|
+
timer.end();
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
};
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
// src/langgraph/observability/errors.ts
|
|
2394
|
+
var AgentError = class _AgentError extends Error {
|
|
2395
|
+
code;
|
|
2396
|
+
node;
|
|
2397
|
+
state;
|
|
2398
|
+
metadata;
|
|
2399
|
+
cause;
|
|
2400
|
+
timestamp;
|
|
2401
|
+
constructor(message, context = {}) {
|
|
2402
|
+
super(message);
|
|
2403
|
+
this.name = "AgentError";
|
|
2404
|
+
this.code = context.code;
|
|
2405
|
+
this.node = context.node;
|
|
2406
|
+
this.state = context.state;
|
|
2407
|
+
this.metadata = context.metadata;
|
|
2408
|
+
this.cause = context.cause;
|
|
2409
|
+
this.timestamp = Date.now();
|
|
2410
|
+
if (Error.captureStackTrace) {
|
|
2411
|
+
Error.captureStackTrace(this, _AgentError);
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
/**
|
|
2415
|
+
* Convert error to JSON for logging/reporting
|
|
2416
|
+
*/
|
|
2417
|
+
toJSON() {
|
|
2418
|
+
return {
|
|
2419
|
+
name: this.name,
|
|
2420
|
+
message: this.message,
|
|
2421
|
+
code: this.code,
|
|
2422
|
+
node: this.node,
|
|
2423
|
+
state: this.state,
|
|
2424
|
+
metadata: this.metadata,
|
|
2425
|
+
timestamp: this.timestamp,
|
|
2426
|
+
stack: this.stack,
|
|
2427
|
+
cause: this.cause ? {
|
|
2428
|
+
name: this.cause.name,
|
|
2429
|
+
message: this.cause.message,
|
|
2430
|
+
stack: this.cause.stack
|
|
2431
|
+
} : void 0
|
|
2432
|
+
};
|
|
2433
|
+
}
|
|
2434
|
+
/**
|
|
2435
|
+
* Get a human-readable string representation
|
|
2436
|
+
*/
|
|
2437
|
+
toString() {
|
|
2438
|
+
const parts = [`${this.name}: ${this.message}`];
|
|
2439
|
+
if (this.code) {
|
|
2440
|
+
parts.push(`Code: ${this.code}`);
|
|
2441
|
+
}
|
|
2442
|
+
if (this.node) {
|
|
2443
|
+
parts.push(`Node: ${this.node}`);
|
|
2444
|
+
}
|
|
2445
|
+
if (this.cause) {
|
|
2446
|
+
parts.push(`Caused by: ${this.cause.message}`);
|
|
2447
|
+
}
|
|
2448
|
+
return parts.join("\n");
|
|
2449
|
+
}
|
|
2450
|
+
};
|
|
2451
|
+
var ErrorReporterImpl = class {
|
|
2452
|
+
options;
|
|
2453
|
+
constructor(options) {
|
|
2454
|
+
this.options = {
|
|
2455
|
+
onError: options.onError,
|
|
2456
|
+
includeStackTrace: options.includeStackTrace ?? true,
|
|
2457
|
+
includeState: options.includeState ?? false,
|
|
2458
|
+
rethrow: options.rethrow ?? true
|
|
2459
|
+
};
|
|
2460
|
+
}
|
|
2461
|
+
wrap(node, nodeName) {
|
|
2462
|
+
return async (state) => {
|
|
2463
|
+
try {
|
|
2464
|
+
return await Promise.resolve(node(state));
|
|
2465
|
+
} catch (error) {
|
|
2466
|
+
const agentError = this.toAgentError(error, {
|
|
2467
|
+
node: nodeName,
|
|
2468
|
+
state: this.options.includeState ? state : void 0
|
|
2469
|
+
});
|
|
2470
|
+
await this.report(agentError);
|
|
2471
|
+
if (this.options.rethrow) {
|
|
2472
|
+
throw agentError;
|
|
2473
|
+
}
|
|
2474
|
+
return state;
|
|
2475
|
+
}
|
|
2476
|
+
};
|
|
2477
|
+
}
|
|
2478
|
+
async report(error, context) {
|
|
2479
|
+
const agentError = this.toAgentError(error, context);
|
|
2480
|
+
try {
|
|
2481
|
+
await Promise.resolve(this.options.onError(agentError));
|
|
2482
|
+
} catch (reportError) {
|
|
2483
|
+
console.error("Error reporting failed:", reportError);
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
toAgentError(error, context) {
|
|
2487
|
+
if (error instanceof AgentError) {
|
|
2488
|
+
return error;
|
|
2489
|
+
}
|
|
2490
|
+
return new AgentError(error.message, {
|
|
2491
|
+
...context,
|
|
2492
|
+
cause: error
|
|
2493
|
+
});
|
|
2494
|
+
}
|
|
2495
|
+
};
|
|
2496
|
+
function createErrorReporter(options) {
|
|
2497
|
+
return new ErrorReporterImpl(options);
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2500
|
+
// src/langgraph/middleware/compose.ts
|
|
2501
|
+
function compose(...middleware) {
|
|
2502
|
+
return (node) => {
|
|
2503
|
+
return middleware.reduceRight(
|
|
2504
|
+
(wrappedNode, mw) => mw(wrappedNode),
|
|
2505
|
+
node
|
|
2506
|
+
);
|
|
2507
|
+
};
|
|
2508
|
+
}
|
|
2509
|
+
function composeWithOptions(options, ...middleware) {
|
|
2510
|
+
const { reverse = false, name, catchErrors = true } = options;
|
|
2511
|
+
return (node) => {
|
|
2512
|
+
const orderedMiddleware = reverse ? [...middleware].reverse() : middleware;
|
|
2513
|
+
let wrappedNode = orderedMiddleware.reduceRight(
|
|
2514
|
+
(wrappedNode2, mw) => mw(wrappedNode2),
|
|
2515
|
+
node
|
|
2516
|
+
);
|
|
2517
|
+
if (catchErrors) {
|
|
2518
|
+
const originalNode = wrappedNode;
|
|
2519
|
+
wrappedNode = async (state) => {
|
|
2520
|
+
try {
|
|
2521
|
+
return await Promise.resolve(originalNode(state));
|
|
2522
|
+
} catch (error) {
|
|
2523
|
+
const enhancedError = error instanceof Error ? error : new Error(String(error));
|
|
2524
|
+
if (name) {
|
|
2525
|
+
enhancedError.message = `[${name}] ${enhancedError.message}`;
|
|
2526
|
+
}
|
|
2527
|
+
throw enhancedError;
|
|
2528
|
+
}
|
|
2529
|
+
};
|
|
2530
|
+
}
|
|
2531
|
+
return wrappedNode;
|
|
2532
|
+
};
|
|
2533
|
+
}
|
|
2534
|
+
var MiddlewareChain = class {
|
|
2535
|
+
middleware = [];
|
|
2536
|
+
options = {};
|
|
2537
|
+
/**
|
|
2538
|
+
* Add middleware to the chain.
|
|
2539
|
+
*/
|
|
2540
|
+
use(middleware) {
|
|
2541
|
+
this.middleware.push(middleware);
|
|
2542
|
+
return this;
|
|
2543
|
+
}
|
|
2544
|
+
/**
|
|
2545
|
+
* Set composition options.
|
|
2546
|
+
*/
|
|
2547
|
+
withOptions(options) {
|
|
2548
|
+
this.options = { ...this.options, ...options };
|
|
2549
|
+
return this;
|
|
2550
|
+
}
|
|
2551
|
+
/**
|
|
2552
|
+
* Build the middleware chain and apply it to a node.
|
|
2553
|
+
*/
|
|
2554
|
+
build(node) {
|
|
2555
|
+
if (this.middleware.length === 0) {
|
|
2556
|
+
return node;
|
|
2557
|
+
}
|
|
2558
|
+
return composeWithOptions(this.options, ...this.middleware)(node);
|
|
2559
|
+
}
|
|
2560
|
+
/**
|
|
2561
|
+
* Get the number of middleware in the chain.
|
|
2562
|
+
*/
|
|
2563
|
+
get length() {
|
|
2564
|
+
return this.middleware.length;
|
|
2565
|
+
}
|
|
2566
|
+
};
|
|
2567
|
+
function chain() {
|
|
2568
|
+
return new MiddlewareChain();
|
|
2569
|
+
}
|
|
2570
|
+
function createMiddlewareContext() {
|
|
2571
|
+
return {
|
|
2572
|
+
executionId: `exec_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
2573
|
+
startTime: Date.now(),
|
|
2574
|
+
data: {},
|
|
2575
|
+
middlewareStack: []
|
|
2576
|
+
};
|
|
2577
|
+
}
|
|
2578
|
+
|
|
2579
|
+
// src/langgraph/middleware/logging.ts
|
|
2580
|
+
var withLogging = (options) => {
|
|
2581
|
+
const {
|
|
2582
|
+
logger: providedLogger,
|
|
2583
|
+
name = "node",
|
|
2584
|
+
level = "info",
|
|
2585
|
+
logInput = true,
|
|
2586
|
+
logOutput = true,
|
|
2587
|
+
logDuration = true,
|
|
2588
|
+
logErrors = true,
|
|
2589
|
+
extractData,
|
|
2590
|
+
onStart,
|
|
2591
|
+
onComplete,
|
|
2592
|
+
onError
|
|
2593
|
+
} = options;
|
|
2594
|
+
const logger = providedLogger || createLogger(name, { level });
|
|
2595
|
+
return (node) => {
|
|
2596
|
+
return async (state) => {
|
|
2597
|
+
const startTime = Date.now();
|
|
2598
|
+
try {
|
|
2599
|
+
if (logInput) {
|
|
2600
|
+
const data = extractData ? extractData(state) : { state };
|
|
2601
|
+
logger.info("Node execution started", data);
|
|
2602
|
+
}
|
|
2603
|
+
if (onStart) {
|
|
2604
|
+
onStart(state);
|
|
2605
|
+
}
|
|
2606
|
+
const result = await Promise.resolve(node(state));
|
|
2607
|
+
const duration = Date.now() - startTime;
|
|
2608
|
+
if (logOutput) {
|
|
2609
|
+
const data = extractData ? extractData(result) : { result };
|
|
2610
|
+
if (logDuration) {
|
|
2611
|
+
logger.info(`Node execution completed (${duration}ms)`, data);
|
|
2612
|
+
} else {
|
|
2613
|
+
logger.info("Node execution completed", data);
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
if (onComplete) {
|
|
2617
|
+
onComplete(state, result, duration);
|
|
2618
|
+
}
|
|
2619
|
+
return result;
|
|
2620
|
+
} catch (error) {
|
|
2621
|
+
const duration = Date.now() - startTime;
|
|
2622
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
2623
|
+
if (logErrors) {
|
|
2624
|
+
logger.error(`Node execution failed (${duration}ms)`, {
|
|
2625
|
+
error: err.message,
|
|
2626
|
+
stack: err.stack
|
|
2627
|
+
});
|
|
2628
|
+
}
|
|
2629
|
+
if (onError) {
|
|
2630
|
+
onError(err, duration);
|
|
2631
|
+
}
|
|
2632
|
+
throw error;
|
|
2633
|
+
}
|
|
2634
|
+
};
|
|
2635
|
+
};
|
|
2636
|
+
};
|
|
2637
|
+
|
|
2638
|
+
// src/langgraph/middleware/presets.ts
|
|
2639
|
+
function withRetry2(options) {
|
|
2640
|
+
return (node) => withRetry(node, options);
|
|
2641
|
+
}
|
|
2642
|
+
function withErrorHandler2(options) {
|
|
2643
|
+
return (node) => withErrorHandler(node, options);
|
|
2644
|
+
}
|
|
2645
|
+
function withTimeout2(options) {
|
|
2646
|
+
return (node) => withTimeout(node, options);
|
|
2647
|
+
}
|
|
2648
|
+
function withMetrics2(options) {
|
|
2649
|
+
return (node) => withMetrics(node, options);
|
|
2650
|
+
}
|
|
2651
|
+
function withTracing2(options) {
|
|
2652
|
+
return (node) => withTracing(node, options);
|
|
2653
|
+
}
|
|
2654
|
+
function withLogging2(options) {
|
|
2655
|
+
return (node) => withLogging(options)(node);
|
|
2656
|
+
}
|
|
2657
|
+
function production(node, options) {
|
|
2658
|
+
const {
|
|
2659
|
+
nodeName,
|
|
2660
|
+
logger,
|
|
2661
|
+
enableMetrics = true,
|
|
2662
|
+
enableTracing = true,
|
|
2663
|
+
enableRetry = true,
|
|
2664
|
+
timeout: timeout2 = 3e4,
|
|
2665
|
+
retryOptions = {},
|
|
2666
|
+
errorOptions = {}
|
|
2667
|
+
} = options;
|
|
2668
|
+
const actualLogger = logger || createLogger(nodeName, { level: "info" /* INFO */ });
|
|
2669
|
+
const middleware = [];
|
|
2670
|
+
middleware.push(
|
|
2671
|
+
withLogging2({
|
|
2672
|
+
logger: actualLogger,
|
|
2673
|
+
name: nodeName,
|
|
2674
|
+
logInput: false,
|
|
2675
|
+
// Don't log input in production by default
|
|
2676
|
+
logOutput: false,
|
|
2677
|
+
// Don't log output in production by default
|
|
2678
|
+
logDuration: true,
|
|
2679
|
+
logErrors: true
|
|
2680
|
+
})
|
|
2681
|
+
);
|
|
2682
|
+
middleware.push(
|
|
2683
|
+
withErrorHandler2({
|
|
2684
|
+
onError: (error, state) => {
|
|
2685
|
+
return state;
|
|
2686
|
+
},
|
|
2687
|
+
...errorOptions
|
|
2688
|
+
})
|
|
2689
|
+
);
|
|
2690
|
+
if (enableRetry) {
|
|
2691
|
+
middleware.push(
|
|
2692
|
+
withRetry2({
|
|
2693
|
+
maxAttempts: 3,
|
|
2694
|
+
backoff: "exponential",
|
|
2695
|
+
initialDelay: 1e3,
|
|
2696
|
+
...retryOptions
|
|
2697
|
+
})
|
|
2698
|
+
);
|
|
2699
|
+
}
|
|
2700
|
+
middleware.push(
|
|
2701
|
+
withTimeout2({
|
|
2702
|
+
timeout: timeout2,
|
|
2703
|
+
onTimeout: (state) => {
|
|
2704
|
+
return state;
|
|
2705
|
+
}
|
|
2706
|
+
})
|
|
2707
|
+
);
|
|
2708
|
+
if (enableMetrics) {
|
|
2709
|
+
middleware.push(
|
|
2710
|
+
withMetrics2({
|
|
2711
|
+
name: nodeName,
|
|
2712
|
+
trackDuration: true,
|
|
2713
|
+
trackErrors: true,
|
|
2714
|
+
trackInvocations: true
|
|
2715
|
+
})
|
|
2716
|
+
);
|
|
2717
|
+
}
|
|
2718
|
+
if (enableTracing) {
|
|
2719
|
+
middleware.push(
|
|
2720
|
+
withTracing2({
|
|
2721
|
+
name: nodeName,
|
|
2722
|
+
metadata: { preset: "production" }
|
|
2723
|
+
})
|
|
2724
|
+
);
|
|
2725
|
+
}
|
|
2726
|
+
return compose(...middleware)(node);
|
|
2727
|
+
}
|
|
2728
|
+
function development(node, options) {
|
|
2729
|
+
const {
|
|
2730
|
+
nodeName,
|
|
2731
|
+
verbose = true,
|
|
2732
|
+
logger
|
|
2733
|
+
} = options;
|
|
2734
|
+
const actualLogger = logger || createLogger(nodeName, { level: "debug" /* DEBUG */ });
|
|
2735
|
+
return withLogging2({
|
|
2736
|
+
logger: actualLogger,
|
|
2737
|
+
name: nodeName,
|
|
2738
|
+
logInput: verbose,
|
|
2739
|
+
logOutput: verbose,
|
|
2740
|
+
logDuration: true,
|
|
2741
|
+
logErrors: true
|
|
2742
|
+
})(node);
|
|
2743
|
+
}
|
|
2744
|
+
function testing(node, options) {
|
|
2745
|
+
const {
|
|
2746
|
+
nodeName,
|
|
2747
|
+
mockResponse,
|
|
2748
|
+
simulateError,
|
|
2749
|
+
delay = 0,
|
|
2750
|
+
trackInvocations = false
|
|
2751
|
+
} = options;
|
|
2752
|
+
const invocations = [];
|
|
2753
|
+
const wrappedNode = async (state) => {
|
|
2754
|
+
if (trackInvocations) {
|
|
2755
|
+
invocations.push(state);
|
|
2756
|
+
}
|
|
2757
|
+
if (delay > 0) {
|
|
2758
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2759
|
+
}
|
|
2760
|
+
if (simulateError) {
|
|
2761
|
+
throw simulateError;
|
|
2762
|
+
}
|
|
2763
|
+
if (mockResponse) {
|
|
2764
|
+
return { ...state, ...mockResponse };
|
|
2765
|
+
}
|
|
2766
|
+
return await Promise.resolve(node(state));
|
|
2767
|
+
};
|
|
2768
|
+
wrappedNode.invocations = invocations;
|
|
2769
|
+
return wrappedNode;
|
|
2770
|
+
}
|
|
2771
|
+
var presets = {
|
|
2772
|
+
production,
|
|
2773
|
+
development,
|
|
2774
|
+
testing
|
|
2775
|
+
};
|
|
2776
|
+
|
|
2777
|
+
// src/streaming/transformers.ts
|
|
2778
|
+
async function* chunk(stream, options) {
|
|
2779
|
+
const { size } = options;
|
|
2780
|
+
if (size <= 0) {
|
|
2781
|
+
throw new Error("Chunk size must be greater than 0");
|
|
2782
|
+
}
|
|
2783
|
+
let buffer = [];
|
|
2784
|
+
for await (const item of stream) {
|
|
2785
|
+
buffer.push(item);
|
|
2786
|
+
if (buffer.length >= size) {
|
|
2787
|
+
yield buffer;
|
|
2788
|
+
buffer = [];
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
if (buffer.length > 0) {
|
|
2792
|
+
yield buffer;
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
async function* batch(stream, options) {
|
|
2796
|
+
const { maxSize, maxWait } = options;
|
|
2797
|
+
if (maxSize <= 0) {
|
|
2798
|
+
throw new Error("Batch maxSize must be greater than 0");
|
|
2799
|
+
}
|
|
2800
|
+
if (maxWait <= 0) {
|
|
2801
|
+
throw new Error("Batch maxWait must be greater than 0");
|
|
2802
|
+
}
|
|
2803
|
+
let buffer = [];
|
|
2804
|
+
let timeoutId = null;
|
|
2805
|
+
const flushBuffer = () => {
|
|
2806
|
+
if (timeoutId) {
|
|
2807
|
+
clearTimeout(timeoutId);
|
|
2808
|
+
timeoutId = null;
|
|
2809
|
+
}
|
|
2810
|
+
if (buffer.length === 0) {
|
|
2811
|
+
return null;
|
|
2812
|
+
}
|
|
2813
|
+
const result = buffer;
|
|
2814
|
+
buffer = [];
|
|
2815
|
+
return result;
|
|
2816
|
+
};
|
|
2817
|
+
try {
|
|
2818
|
+
for await (const item of stream) {
|
|
2819
|
+
buffer.push(item);
|
|
2820
|
+
if (buffer.length >= maxSize) {
|
|
2821
|
+
const result = flushBuffer();
|
|
2822
|
+
if (result) {
|
|
2823
|
+
yield result;
|
|
2824
|
+
}
|
|
2825
|
+
} else if (!timeoutId) {
|
|
2826
|
+
timeoutId = setTimeout(() => {
|
|
2827
|
+
}, maxWait);
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2830
|
+
const remaining = flushBuffer();
|
|
2831
|
+
if (remaining) {
|
|
2832
|
+
yield remaining;
|
|
2833
|
+
}
|
|
2834
|
+
} finally {
|
|
2835
|
+
if (timeoutId) {
|
|
2836
|
+
clearTimeout(timeoutId);
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
async function* throttle(stream, options) {
|
|
2841
|
+
const { rate, per } = options;
|
|
2842
|
+
if (rate <= 0) {
|
|
2843
|
+
throw new Error("Throttle rate must be greater than 0");
|
|
2844
|
+
}
|
|
2845
|
+
if (per <= 0) {
|
|
2846
|
+
throw new Error("Throttle per must be greater than 0");
|
|
2847
|
+
}
|
|
2848
|
+
const interval = per / rate;
|
|
2849
|
+
let lastEmit = 0;
|
|
2850
|
+
for await (const item of stream) {
|
|
2851
|
+
const now = Date.now();
|
|
2852
|
+
const timeSinceLastEmit = now - lastEmit;
|
|
2853
|
+
if (timeSinceLastEmit < interval) {
|
|
2854
|
+
await new Promise((resolve) => setTimeout(resolve, interval - timeSinceLastEmit));
|
|
2855
|
+
}
|
|
2856
|
+
lastEmit = Date.now();
|
|
2857
|
+
yield item;
|
|
2858
|
+
}
|
|
2859
|
+
}
|
|
2860
|
+
|
|
2861
|
+
// src/streaming/aggregators.ts
|
|
2862
|
+
async function collect(stream) {
|
|
2863
|
+
const items = [];
|
|
2864
|
+
for await (const item of stream) {
|
|
2865
|
+
items.push(item);
|
|
2866
|
+
}
|
|
2867
|
+
return items;
|
|
2868
|
+
}
|
|
2869
|
+
async function reduce(stream, reducer, initialValue) {
|
|
2870
|
+
let accumulator = initialValue;
|
|
2871
|
+
for await (const item of stream) {
|
|
2872
|
+
accumulator = reducer(accumulator, item);
|
|
2873
|
+
}
|
|
2874
|
+
return accumulator;
|
|
2875
|
+
}
|
|
2876
|
+
async function* merge(streams) {
|
|
2877
|
+
if (streams.length === 0) {
|
|
2878
|
+
return;
|
|
2879
|
+
}
|
|
2880
|
+
const iterators = streams.map((stream) => stream[Symbol.asyncIterator]());
|
|
2881
|
+
const active = new Set(iterators);
|
|
2882
|
+
const promises = /* @__PURE__ */ new Map();
|
|
2883
|
+
const createPromise = (iterator) => {
|
|
2884
|
+
return iterator.next().then((result) => ({ iterator, result }));
|
|
2885
|
+
};
|
|
2886
|
+
for (const iterator of iterators) {
|
|
2887
|
+
promises.set(iterator, createPromise(iterator));
|
|
2888
|
+
}
|
|
2889
|
+
while (active.size > 0) {
|
|
2890
|
+
const { iterator, result } = await Promise.race(promises.values());
|
|
2891
|
+
if (result.done) {
|
|
2892
|
+
active.delete(iterator);
|
|
2893
|
+
promises.delete(iterator);
|
|
2894
|
+
} else {
|
|
2895
|
+
yield result.value;
|
|
2896
|
+
promises.set(iterator, createPromise(iterator));
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
async function* filter(stream, predicate) {
|
|
2901
|
+
for await (const item of stream) {
|
|
2902
|
+
if (await predicate(item)) {
|
|
2903
|
+
yield item;
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
async function* map(stream, mapper) {
|
|
2908
|
+
for await (const item of stream) {
|
|
2909
|
+
yield await mapper(item);
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
async function* take(stream, count) {
|
|
2913
|
+
if (count <= 0) {
|
|
2914
|
+
return;
|
|
2915
|
+
}
|
|
2916
|
+
let taken = 0;
|
|
2917
|
+
for await (const item of stream) {
|
|
2918
|
+
yield item;
|
|
2919
|
+
taken++;
|
|
2920
|
+
if (taken >= count) {
|
|
2921
|
+
break;
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2925
|
+
|
|
2926
|
+
// src/streaming/progress.ts
|
|
2927
|
+
function createProgressTracker(options) {
|
|
2928
|
+
const { total, onProgress, onComplete, onCancel } = options;
|
|
2929
|
+
if (total <= 0) {
|
|
2930
|
+
throw new Error("Total must be greater than 0");
|
|
2931
|
+
}
|
|
2932
|
+
let current = 0;
|
|
2933
|
+
let startTime = 0;
|
|
2934
|
+
let cancelled = false;
|
|
2935
|
+
const calculateProgress = () => {
|
|
2936
|
+
const elapsed = Date.now() - startTime;
|
|
2937
|
+
const percentage = Math.min(100, current / total * 100);
|
|
2938
|
+
let eta = 0;
|
|
2939
|
+
if (current > 0 && current < total) {
|
|
2940
|
+
const timePerItem = elapsed / current;
|
|
2941
|
+
const remaining = total - current;
|
|
2942
|
+
eta = Math.round(timePerItem * remaining / 1e3);
|
|
2943
|
+
}
|
|
2944
|
+
return {
|
|
2945
|
+
current,
|
|
2946
|
+
total,
|
|
2947
|
+
percentage: Math.round(percentage * 100) / 100,
|
|
2948
|
+
// Round to 2 decimal places
|
|
2949
|
+
eta,
|
|
2950
|
+
startTime,
|
|
2951
|
+
elapsed
|
|
2952
|
+
};
|
|
2953
|
+
};
|
|
2954
|
+
return {
|
|
2955
|
+
start() {
|
|
2956
|
+
if (startTime > 0) {
|
|
2957
|
+
throw new Error("Progress tracker already started");
|
|
2958
|
+
}
|
|
2959
|
+
startTime = Date.now();
|
|
2960
|
+
current = 0;
|
|
2961
|
+
cancelled = false;
|
|
2962
|
+
},
|
|
2963
|
+
update(newCurrent) {
|
|
2964
|
+
if (startTime === 0) {
|
|
2965
|
+
throw new Error("Progress tracker not started");
|
|
2966
|
+
}
|
|
2967
|
+
if (cancelled) {
|
|
2968
|
+
throw new Error("Progress tracker cancelled");
|
|
2969
|
+
}
|
|
2970
|
+
if (newCurrent < 0) {
|
|
2971
|
+
throw new Error("Current progress cannot be negative");
|
|
2972
|
+
}
|
|
2973
|
+
if (newCurrent > total) {
|
|
2974
|
+
newCurrent = total;
|
|
2975
|
+
}
|
|
2976
|
+
current = newCurrent;
|
|
2977
|
+
if (onProgress) {
|
|
2978
|
+
onProgress(calculateProgress());
|
|
2979
|
+
}
|
|
2980
|
+
},
|
|
2981
|
+
complete() {
|
|
2982
|
+
if (startTime === 0) {
|
|
2983
|
+
throw new Error("Progress tracker not started");
|
|
2984
|
+
}
|
|
2985
|
+
if (cancelled) {
|
|
2986
|
+
throw new Error("Progress tracker cancelled");
|
|
2987
|
+
}
|
|
2988
|
+
current = total;
|
|
2989
|
+
const progress = calculateProgress();
|
|
2990
|
+
if (onProgress) {
|
|
2991
|
+
onProgress(progress);
|
|
2992
|
+
}
|
|
2993
|
+
if (onComplete) {
|
|
2994
|
+
onComplete(progress);
|
|
2995
|
+
}
|
|
2996
|
+
},
|
|
2997
|
+
cancel() {
|
|
2998
|
+
if (startTime === 0) {
|
|
2999
|
+
throw new Error("Progress tracker not started");
|
|
3000
|
+
}
|
|
3001
|
+
if (cancelled) {
|
|
3002
|
+
return;
|
|
3003
|
+
}
|
|
3004
|
+
cancelled = true;
|
|
3005
|
+
if (onCancel) {
|
|
3006
|
+
onCancel();
|
|
3007
|
+
}
|
|
3008
|
+
},
|
|
3009
|
+
getProgress() {
|
|
3010
|
+
if (startTime === 0) {
|
|
3011
|
+
throw new Error("Progress tracker not started");
|
|
3012
|
+
}
|
|
3013
|
+
return calculateProgress();
|
|
3014
|
+
},
|
|
3015
|
+
isCancelled() {
|
|
3016
|
+
return cancelled;
|
|
3017
|
+
}
|
|
3018
|
+
};
|
|
3019
|
+
}
|
|
3020
|
+
|
|
3021
|
+
// src/streaming/sse.ts
|
|
3022
|
+
function formatSSEEvent(event) {
|
|
3023
|
+
const lines = [];
|
|
3024
|
+
if (event.id) {
|
|
3025
|
+
lines.push(`id: ${event.id}`);
|
|
3026
|
+
}
|
|
3027
|
+
if (event.event) {
|
|
3028
|
+
lines.push(`event: ${event.event}`);
|
|
3029
|
+
}
|
|
3030
|
+
if (event.retry !== void 0) {
|
|
3031
|
+
lines.push(`retry: ${event.retry}`);
|
|
3032
|
+
}
|
|
3033
|
+
const dataLines = event.data.split("\n");
|
|
3034
|
+
for (const line of dataLines) {
|
|
3035
|
+
lines.push(`data: ${line}`);
|
|
3036
|
+
}
|
|
3037
|
+
return lines.join("\n") + "\n\n";
|
|
3038
|
+
}
|
|
3039
|
+
function createSSEFormatter(options = {}) {
|
|
3040
|
+
const { eventTypes = {}, heartbeat = 0, retry: retry2 } = options;
|
|
3041
|
+
return {
|
|
3042
|
+
async *format(stream) {
|
|
3043
|
+
let heartbeatInterval = null;
|
|
3044
|
+
let lastEventId = 0;
|
|
3045
|
+
try {
|
|
3046
|
+
if (heartbeat > 0) {
|
|
3047
|
+
heartbeatInterval = setInterval(() => {
|
|
3048
|
+
}, heartbeat);
|
|
3049
|
+
}
|
|
3050
|
+
if (retry2 !== void 0) {
|
|
3051
|
+
yield formatSSEEvent({ data: "", retry: retry2 });
|
|
3052
|
+
}
|
|
3053
|
+
for await (const item of stream) {
|
|
3054
|
+
let event;
|
|
3055
|
+
let matched = false;
|
|
3056
|
+
for (const [type, mapper] of Object.entries(eventTypes)) {
|
|
3057
|
+
try {
|
|
3058
|
+
const mapped = mapper(item);
|
|
3059
|
+
if (mapped) {
|
|
3060
|
+
event = {
|
|
3061
|
+
...mapped,
|
|
3062
|
+
id: String(++lastEventId)
|
|
3063
|
+
};
|
|
3064
|
+
matched = true;
|
|
3065
|
+
break;
|
|
3066
|
+
}
|
|
3067
|
+
} catch {
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
if (!matched) {
|
|
3071
|
+
event = {
|
|
3072
|
+
data: JSON.stringify(item),
|
|
3073
|
+
id: String(++lastEventId)
|
|
3074
|
+
};
|
|
3075
|
+
}
|
|
3076
|
+
yield formatSSEEvent(event);
|
|
3077
|
+
if (heartbeatInterval) {
|
|
3078
|
+
clearInterval(heartbeatInterval);
|
|
3079
|
+
heartbeatInterval = setInterval(() => {
|
|
3080
|
+
}, heartbeat);
|
|
3081
|
+
}
|
|
3082
|
+
}
|
|
3083
|
+
} finally {
|
|
3084
|
+
if (heartbeatInterval) {
|
|
3085
|
+
clearInterval(heartbeatInterval);
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
};
|
|
3090
|
+
}
|
|
3091
|
+
function createHeartbeat() {
|
|
3092
|
+
return ": heartbeat\n\n";
|
|
3093
|
+
}
|
|
3094
|
+
function parseSSEEvent(eventString) {
|
|
3095
|
+
const lines = eventString.trim().split("\n");
|
|
3096
|
+
const event = { data: "" };
|
|
3097
|
+
for (const line of lines) {
|
|
3098
|
+
if (line.startsWith("id:")) {
|
|
3099
|
+
event.id = line.slice(3).trim();
|
|
3100
|
+
} else if (line.startsWith("event:")) {
|
|
3101
|
+
event.event = line.slice(6).trim();
|
|
3102
|
+
} else if (line.startsWith("retry:")) {
|
|
3103
|
+
event.retry = parseInt(line.slice(6).trim(), 10);
|
|
3104
|
+
} else if (line.startsWith("data:")) {
|
|
3105
|
+
const data = line.slice(5).trim();
|
|
3106
|
+
event.data = event.data ? `${event.data}
|
|
3107
|
+
${data}` : data;
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
return event.data !== void 0 ? event : null;
|
|
3111
|
+
}
|
|
3112
|
+
|
|
3113
|
+
// src/streaming/websocket.ts
|
|
3114
|
+
function createWebSocketHandler(options) {
|
|
3115
|
+
const { onConnect, onMessage, onError, onClose, heartbeat = 0 } = options;
|
|
3116
|
+
return function handler(ws, req) {
|
|
3117
|
+
let heartbeatInterval = null;
|
|
3118
|
+
let isAlive = true;
|
|
3119
|
+
if (heartbeat > 0) {
|
|
3120
|
+
heartbeatInterval = setInterval(() => {
|
|
3121
|
+
if (!isAlive) {
|
|
3122
|
+
if (heartbeatInterval) {
|
|
3123
|
+
clearInterval(heartbeatInterval);
|
|
3124
|
+
}
|
|
3125
|
+
ws.terminate();
|
|
3126
|
+
return;
|
|
3127
|
+
}
|
|
3128
|
+
isAlive = false;
|
|
3129
|
+
ws.ping();
|
|
3130
|
+
}, heartbeat);
|
|
3131
|
+
ws.on("pong", () => {
|
|
3132
|
+
isAlive = true;
|
|
3133
|
+
});
|
|
3134
|
+
}
|
|
3135
|
+
if (onConnect) {
|
|
3136
|
+
try {
|
|
3137
|
+
onConnect(ws, req);
|
|
3138
|
+
} catch (error) {
|
|
3139
|
+
if (onError) {
|
|
3140
|
+
onError(ws, error);
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
3144
|
+
ws.on("message", async (data) => {
|
|
3145
|
+
try {
|
|
3146
|
+
let message;
|
|
3147
|
+
if (typeof data === "string") {
|
|
3148
|
+
try {
|
|
3149
|
+
message = JSON.parse(data);
|
|
3150
|
+
} catch {
|
|
3151
|
+
message = data;
|
|
3152
|
+
}
|
|
3153
|
+
} else {
|
|
3154
|
+
message = data;
|
|
3155
|
+
}
|
|
3156
|
+
if (onMessage) {
|
|
3157
|
+
await onMessage(ws, message);
|
|
3158
|
+
}
|
|
3159
|
+
} catch (error) {
|
|
3160
|
+
if (onError) {
|
|
3161
|
+
onError(ws, error);
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
});
|
|
3165
|
+
ws.on("error", (error) => {
|
|
3166
|
+
if (onError) {
|
|
3167
|
+
onError(ws, error);
|
|
3168
|
+
}
|
|
3169
|
+
});
|
|
3170
|
+
ws.on("close", (code, reason) => {
|
|
3171
|
+
if (heartbeatInterval) {
|
|
3172
|
+
clearInterval(heartbeatInterval);
|
|
3173
|
+
}
|
|
3174
|
+
if (onClose) {
|
|
3175
|
+
onClose(ws, code, reason);
|
|
3176
|
+
}
|
|
3177
|
+
});
|
|
3178
|
+
};
|
|
3179
|
+
}
|
|
3180
|
+
function sendMessage(ws, message) {
|
|
3181
|
+
if (ws.readyState === 1) {
|
|
3182
|
+
ws.send(JSON.stringify(message));
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3185
|
+
function broadcast(clients, message) {
|
|
3186
|
+
const data = JSON.stringify(message);
|
|
3187
|
+
for (const client of clients) {
|
|
3188
|
+
if (client.readyState === 1) {
|
|
3189
|
+
client.send(data);
|
|
3190
|
+
}
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
function createMessage(type, data, error) {
|
|
3194
|
+
return { type, data, error };
|
|
3195
|
+
}
|
|
3196
|
+
|
|
3197
|
+
// src/resources/pool.ts
|
|
3198
|
+
var ConnectionPool = class {
|
|
3199
|
+
constructor(options) {
|
|
3200
|
+
this.options = options;
|
|
3201
|
+
const poolConfig = options.pool || {};
|
|
3202
|
+
const min = poolConfig.min || 0;
|
|
3203
|
+
if (min > 0) {
|
|
3204
|
+
this.initialize(min).catch((error) => {
|
|
3205
|
+
console.error("Failed to initialize pool:", error);
|
|
3206
|
+
});
|
|
3207
|
+
}
|
|
3208
|
+
const evictionInterval = poolConfig.evictionInterval || 6e4;
|
|
3209
|
+
this.evictionTimer = setInterval(() => {
|
|
3210
|
+
this.evictIdleConnections().catch((error) => {
|
|
3211
|
+
console.error("Failed to evict idle connections:", error);
|
|
3212
|
+
});
|
|
3213
|
+
}, evictionInterval);
|
|
3214
|
+
const healthCheck = options.healthCheck || {};
|
|
3215
|
+
if (healthCheck.enabled) {
|
|
3216
|
+
const interval = healthCheck.interval || 3e4;
|
|
3217
|
+
this.healthCheckTimer = setInterval(() => {
|
|
3218
|
+
this.performHealthChecks().catch((error) => {
|
|
3219
|
+
console.error("Health check failed:", error);
|
|
3220
|
+
options.onHealthCheckFail?.(error);
|
|
3221
|
+
});
|
|
3222
|
+
}, interval);
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
connections = [];
|
|
3226
|
+
pending = [];
|
|
3227
|
+
stats = {
|
|
3228
|
+
size: 0,
|
|
3229
|
+
available: 0,
|
|
3230
|
+
pending: 0,
|
|
3231
|
+
acquired: 0,
|
|
3232
|
+
created: 0,
|
|
3233
|
+
destroyed: 0,
|
|
3234
|
+
healthChecksPassed: 0,
|
|
3235
|
+
healthChecksFailed: 0
|
|
3236
|
+
};
|
|
3237
|
+
evictionTimer;
|
|
3238
|
+
healthCheckTimer;
|
|
3239
|
+
draining = false;
|
|
3240
|
+
async initialize(count) {
|
|
3241
|
+
const promises = Array.from({ length: count }, () => this.createConnection());
|
|
3242
|
+
await Promise.all(promises);
|
|
3243
|
+
}
|
|
3244
|
+
async createConnection() {
|
|
3245
|
+
const connection = await this.options.factory();
|
|
3246
|
+
this.connections.push({
|
|
3247
|
+
connection,
|
|
3248
|
+
createdAt: Date.now(),
|
|
3249
|
+
lastUsedAt: Date.now(),
|
|
3250
|
+
inUse: false
|
|
3251
|
+
});
|
|
3252
|
+
this.stats.created++;
|
|
3253
|
+
this.stats.size++;
|
|
3254
|
+
this.stats.available++;
|
|
3255
|
+
return connection;
|
|
3256
|
+
}
|
|
3257
|
+
async destroyConnection(pooled) {
|
|
3258
|
+
const index = this.connections.indexOf(pooled);
|
|
3259
|
+
if (index !== -1) {
|
|
3260
|
+
this.connections.splice(index, 1);
|
|
3261
|
+
}
|
|
3262
|
+
if (this.options.destroyer) {
|
|
3263
|
+
await this.options.destroyer(pooled.connection);
|
|
3264
|
+
}
|
|
3265
|
+
this.options.onDestroy?.(pooled.connection);
|
|
3266
|
+
this.stats.destroyed++;
|
|
3267
|
+
this.stats.size--;
|
|
3268
|
+
if (!pooled.inUse) {
|
|
3269
|
+
this.stats.available--;
|
|
3270
|
+
}
|
|
3271
|
+
}
|
|
3272
|
+
async acquire() {
|
|
3273
|
+
if (this.draining) {
|
|
3274
|
+
throw new Error("Pool is draining");
|
|
3275
|
+
}
|
|
3276
|
+
const available = this.connections.find((c) => !c.inUse);
|
|
3277
|
+
if (available) {
|
|
3278
|
+
available.inUse = true;
|
|
3279
|
+
available.lastUsedAt = Date.now();
|
|
3280
|
+
this.stats.available--;
|
|
3281
|
+
this.stats.acquired++;
|
|
3282
|
+
this.options.onAcquire?.(available.connection);
|
|
3283
|
+
return available.connection;
|
|
3284
|
+
}
|
|
3285
|
+
const poolConfig = this.options.pool || {};
|
|
3286
|
+
const max = poolConfig.max || 10;
|
|
3287
|
+
if (this.connections.length < max) {
|
|
3288
|
+
const connection = await this.createConnection();
|
|
3289
|
+
const pooled = this.connections.find((c) => c.connection === connection);
|
|
3290
|
+
pooled.inUse = true;
|
|
3291
|
+
this.stats.available--;
|
|
3292
|
+
this.stats.acquired++;
|
|
3293
|
+
this.options.onAcquire?.(connection);
|
|
3294
|
+
return connection;
|
|
3295
|
+
}
|
|
3296
|
+
return new Promise((resolve, reject) => {
|
|
3297
|
+
const timeout2 = poolConfig.acquireTimeout || 3e4;
|
|
3298
|
+
const timer = setTimeout(() => {
|
|
3299
|
+
const index = this.pending.findIndex((p) => p.resolve === resolve);
|
|
3300
|
+
if (index !== -1) {
|
|
3301
|
+
this.pending.splice(index, 1);
|
|
3302
|
+
this.stats.pending--;
|
|
3303
|
+
}
|
|
3304
|
+
reject(new Error("Acquire timeout"));
|
|
3305
|
+
}, timeout2);
|
|
3306
|
+
this.pending.push({ resolve, reject, timeout: timer });
|
|
3307
|
+
this.stats.pending++;
|
|
3308
|
+
});
|
|
3309
|
+
}
|
|
3310
|
+
async release(connection) {
|
|
3311
|
+
const pooled = this.connections.find((c) => c.connection === connection);
|
|
3312
|
+
if (!pooled) {
|
|
3313
|
+
throw new Error("Connection not found in pool");
|
|
3314
|
+
}
|
|
3315
|
+
if (!pooled.inUse) {
|
|
3316
|
+
throw new Error("Connection is not in use");
|
|
3317
|
+
}
|
|
3318
|
+
pooled.inUse = false;
|
|
3319
|
+
pooled.lastUsedAt = Date.now();
|
|
3320
|
+
this.stats.available++;
|
|
3321
|
+
this.stats.acquired--;
|
|
3322
|
+
this.options.onRelease?.(connection);
|
|
3323
|
+
const pending = this.pending.shift();
|
|
3324
|
+
if (pending) {
|
|
3325
|
+
clearTimeout(pending.timeout);
|
|
3326
|
+
this.stats.pending--;
|
|
3327
|
+
pooled.inUse = true;
|
|
3328
|
+
this.stats.available--;
|
|
3329
|
+
this.stats.acquired++;
|
|
3330
|
+
this.options.onAcquire?.(connection);
|
|
3331
|
+
pending.resolve(connection);
|
|
3332
|
+
}
|
|
3333
|
+
}
|
|
3334
|
+
async evictIdleConnections() {
|
|
3335
|
+
const poolConfig = this.options.pool || {};
|
|
3336
|
+
const idleTimeout = poolConfig.idleTimeout || 6e4;
|
|
3337
|
+
const min = poolConfig.min || 0;
|
|
3338
|
+
const now = Date.now();
|
|
3339
|
+
const toEvict = this.connections.filter(
|
|
3340
|
+
(c) => !c.inUse && now - c.lastUsedAt > idleTimeout && this.connections.length > min
|
|
3341
|
+
);
|
|
3342
|
+
for (const pooled of toEvict) {
|
|
3343
|
+
await this.destroyConnection(pooled);
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
async performHealthChecks() {
|
|
3347
|
+
if (!this.options.validator) {
|
|
3348
|
+
return;
|
|
3349
|
+
}
|
|
3350
|
+
const healthCheck = this.options.healthCheck || {};
|
|
3351
|
+
const timeout2 = healthCheck.timeout || 5e3;
|
|
3352
|
+
const retries = healthCheck.retries || 1;
|
|
3353
|
+
for (const pooled of this.connections) {
|
|
3354
|
+
if (pooled.inUse) {
|
|
3355
|
+
continue;
|
|
3356
|
+
}
|
|
3357
|
+
let healthy = false;
|
|
3358
|
+
for (let i = 0; i < retries; i++) {
|
|
3359
|
+
try {
|
|
3360
|
+
const result = await Promise.race([
|
|
3361
|
+
this.options.validator(pooled.connection),
|
|
3362
|
+
new Promise(
|
|
3363
|
+
(_, reject) => setTimeout(() => reject(new Error("Health check timeout")), timeout2)
|
|
3364
|
+
)
|
|
3365
|
+
]);
|
|
3366
|
+
if (result) {
|
|
3367
|
+
healthy = true;
|
|
3368
|
+
break;
|
|
3369
|
+
}
|
|
3370
|
+
} catch (error) {
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3373
|
+
if (healthy) {
|
|
3374
|
+
this.stats.healthChecksPassed++;
|
|
3375
|
+
} else {
|
|
3376
|
+
this.stats.healthChecksFailed++;
|
|
3377
|
+
await this.destroyConnection(pooled);
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3380
|
+
}
|
|
3381
|
+
async drain() {
|
|
3382
|
+
this.draining = true;
|
|
3383
|
+
for (const pending of this.pending) {
|
|
3384
|
+
clearTimeout(pending.timeout);
|
|
3385
|
+
pending.reject(new Error("Pool is draining"));
|
|
3386
|
+
}
|
|
3387
|
+
this.pending = [];
|
|
3388
|
+
this.stats.pending = 0;
|
|
3389
|
+
while (this.connections.some((c) => c.inUse)) {
|
|
3390
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
async clear() {
|
|
3394
|
+
if (this.evictionTimer) {
|
|
3395
|
+
clearInterval(this.evictionTimer);
|
|
3396
|
+
this.evictionTimer = void 0;
|
|
3397
|
+
}
|
|
3398
|
+
if (this.healthCheckTimer) {
|
|
3399
|
+
clearInterval(this.healthCheckTimer);
|
|
3400
|
+
this.healthCheckTimer = void 0;
|
|
3401
|
+
}
|
|
3402
|
+
const connections = [...this.connections];
|
|
3403
|
+
for (const pooled of connections) {
|
|
3404
|
+
await this.destroyConnection(pooled);
|
|
3405
|
+
}
|
|
3406
|
+
}
|
|
3407
|
+
getStats() {
|
|
3408
|
+
return { ...this.stats };
|
|
3409
|
+
}
|
|
3410
|
+
};
|
|
3411
|
+
function createConnectionPool(options) {
|
|
3412
|
+
return new ConnectionPool(options);
|
|
3413
|
+
}
|
|
3414
|
+
|
|
3415
|
+
// src/resources/database-pool.ts
|
|
3416
|
+
var MockDatabaseConnection = class {
|
|
3417
|
+
constructor(config) {
|
|
3418
|
+
this.config = config;
|
|
3419
|
+
}
|
|
3420
|
+
closed = false;
|
|
3421
|
+
async query(sql, params) {
|
|
3422
|
+
if (this.closed) {
|
|
3423
|
+
throw new Error("Connection is closed");
|
|
3424
|
+
}
|
|
3425
|
+
return [];
|
|
3426
|
+
}
|
|
3427
|
+
async execute(sql, params) {
|
|
3428
|
+
if (this.closed) {
|
|
3429
|
+
throw new Error("Connection is closed");
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
async close() {
|
|
3433
|
+
this.closed = true;
|
|
3434
|
+
}
|
|
3435
|
+
};
|
|
3436
|
+
var DatabasePool = class {
|
|
3437
|
+
constructor(options) {
|
|
3438
|
+
this.options = options;
|
|
3439
|
+
const healthCheckQuery = options.healthCheck?.query || "SELECT 1";
|
|
3440
|
+
this.pool = createConnectionPool({
|
|
3441
|
+
factory: async () => {
|
|
3442
|
+
const connection = new MockDatabaseConnection(options.config);
|
|
3443
|
+
options.onConnect?.(connection);
|
|
3444
|
+
return connection;
|
|
3445
|
+
},
|
|
3446
|
+
destroyer: async (connection) => {
|
|
3447
|
+
await connection.close();
|
|
3448
|
+
options.onDisconnect?.(connection);
|
|
3449
|
+
},
|
|
3450
|
+
validator: async (connection) => {
|
|
3451
|
+
try {
|
|
3452
|
+
await connection.query(healthCheckQuery);
|
|
3453
|
+
return true;
|
|
3454
|
+
} catch {
|
|
3455
|
+
return false;
|
|
3456
|
+
}
|
|
3457
|
+
},
|
|
3458
|
+
pool: options.pool,
|
|
3459
|
+
healthCheck: options.healthCheck
|
|
3460
|
+
});
|
|
3461
|
+
}
|
|
3462
|
+
pool;
|
|
3463
|
+
async acquire() {
|
|
3464
|
+
return this.pool.acquire();
|
|
3465
|
+
}
|
|
3466
|
+
async release(connection) {
|
|
3467
|
+
return this.pool.release(connection);
|
|
3468
|
+
}
|
|
3469
|
+
async query(sql, params) {
|
|
3470
|
+
const connection = await this.acquire();
|
|
3471
|
+
try {
|
|
3472
|
+
return await connection.query(sql, params);
|
|
3473
|
+
} finally {
|
|
3474
|
+
await this.release(connection);
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3477
|
+
async execute(sql, params) {
|
|
3478
|
+
const connection = await this.acquire();
|
|
3479
|
+
try {
|
|
3480
|
+
await connection.execute(sql, params);
|
|
3481
|
+
} finally {
|
|
3482
|
+
await this.release(connection);
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
async drain() {
|
|
3486
|
+
return this.pool.drain();
|
|
3487
|
+
}
|
|
3488
|
+
async clear() {
|
|
3489
|
+
return this.pool.clear();
|
|
3490
|
+
}
|
|
3491
|
+
getStats() {
|
|
3492
|
+
return this.pool.getStats();
|
|
3493
|
+
}
|
|
3494
|
+
};
|
|
3495
|
+
function createDatabasePool(options) {
|
|
3496
|
+
return new DatabasePool(options);
|
|
3497
|
+
}
|
|
3498
|
+
|
|
3499
|
+
// src/resources/http-pool.ts
|
|
3500
|
+
var MockHttpClient = class {
|
|
3501
|
+
constructor(config) {
|
|
3502
|
+
this.config = config;
|
|
3503
|
+
}
|
|
3504
|
+
closed = false;
|
|
3505
|
+
async get(url, config) {
|
|
3506
|
+
return this.request({ ...config, url, method: "GET" });
|
|
3507
|
+
}
|
|
3508
|
+
async post(url, data, config) {
|
|
3509
|
+
return this.request({ ...config, url, method: "POST", data });
|
|
3510
|
+
}
|
|
3511
|
+
async put(url, data, config) {
|
|
3512
|
+
return this.request({ ...config, url, method: "PUT", data });
|
|
3513
|
+
}
|
|
3514
|
+
async delete(url, config) {
|
|
3515
|
+
return this.request({ ...config, url, method: "DELETE" });
|
|
3516
|
+
}
|
|
3517
|
+
async request(config) {
|
|
3518
|
+
if (this.closed) {
|
|
3519
|
+
throw new Error("Client is closed");
|
|
3520
|
+
}
|
|
3521
|
+
return {
|
|
3522
|
+
data: {},
|
|
3523
|
+
status: 200,
|
|
3524
|
+
statusText: "OK",
|
|
3525
|
+
headers: {}
|
|
3526
|
+
};
|
|
3527
|
+
}
|
|
3528
|
+
async close() {
|
|
3529
|
+
this.closed = true;
|
|
3530
|
+
}
|
|
3531
|
+
};
|
|
3532
|
+
var HttpPool = class {
|
|
3533
|
+
constructor(options) {
|
|
3534
|
+
this.options = options;
|
|
3535
|
+
const healthCheckEndpoint = options.healthCheck?.endpoint || "/health";
|
|
3536
|
+
const healthCheckMethod = options.healthCheck?.method || "GET";
|
|
3537
|
+
this.pool = createConnectionPool({
|
|
3538
|
+
factory: async () => {
|
|
3539
|
+
const client = new MockHttpClient(options.config);
|
|
3540
|
+
options.onConnect?.(client);
|
|
3541
|
+
return client;
|
|
3542
|
+
},
|
|
3543
|
+
destroyer: async (client) => {
|
|
3544
|
+
await client.close();
|
|
3545
|
+
options.onDisconnect?.(client);
|
|
3546
|
+
},
|
|
3547
|
+
validator: async (client) => {
|
|
3548
|
+
try {
|
|
3549
|
+
const response = await client.request({
|
|
3550
|
+
url: healthCheckEndpoint,
|
|
3551
|
+
method: healthCheckMethod
|
|
3552
|
+
});
|
|
3553
|
+
return response.status >= 200 && response.status < 300;
|
|
3554
|
+
} catch {
|
|
3555
|
+
return false;
|
|
3556
|
+
}
|
|
3557
|
+
},
|
|
3558
|
+
pool: options.pool,
|
|
3559
|
+
healthCheck: options.healthCheck
|
|
3560
|
+
});
|
|
3561
|
+
}
|
|
3562
|
+
pool;
|
|
3563
|
+
async acquire() {
|
|
3564
|
+
return this.pool.acquire();
|
|
3565
|
+
}
|
|
3566
|
+
async release(client) {
|
|
3567
|
+
return this.pool.release(client);
|
|
3568
|
+
}
|
|
3569
|
+
async request(config) {
|
|
3570
|
+
const client = await this.acquire();
|
|
3571
|
+
try {
|
|
3572
|
+
return await client.request(config);
|
|
3573
|
+
} finally {
|
|
3574
|
+
await this.release(client);
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3577
|
+
async drain() {
|
|
3578
|
+
return this.pool.drain();
|
|
3579
|
+
}
|
|
3580
|
+
async clear() {
|
|
3581
|
+
return this.pool.clear();
|
|
3582
|
+
}
|
|
3583
|
+
getStats() {
|
|
3584
|
+
return this.pool.getStats();
|
|
3585
|
+
}
|
|
3586
|
+
};
|
|
3587
|
+
function createHttpPool(options) {
|
|
3588
|
+
return new HttpPool(options);
|
|
3589
|
+
}
|
|
3590
|
+
|
|
3591
|
+
// src/resources/memory.ts
|
|
3592
|
+
var MemoryManager = class {
|
|
3593
|
+
constructor(options) {
|
|
3594
|
+
this.options = options;
|
|
3595
|
+
}
|
|
3596
|
+
cleanupHandlers = /* @__PURE__ */ new Map();
|
|
3597
|
+
checkTimer;
|
|
3598
|
+
leakDetectionTimer;
|
|
3599
|
+
previousMemory;
|
|
3600
|
+
running = false;
|
|
3601
|
+
start() {
|
|
3602
|
+
if (this.running) {
|
|
3603
|
+
return;
|
|
3604
|
+
}
|
|
3605
|
+
this.running = true;
|
|
3606
|
+
const interval = this.options.checkInterval || 1e4;
|
|
3607
|
+
this.checkTimer = setInterval(() => {
|
|
3608
|
+
this.checkMemory();
|
|
3609
|
+
}, interval);
|
|
3610
|
+
const leakDetection = this.options.leakDetection || {};
|
|
3611
|
+
if (leakDetection.enabled) {
|
|
3612
|
+
const sampleInterval = leakDetection.sampleInterval || 6e4;
|
|
3613
|
+
this.leakDetectionTimer = setInterval(() => {
|
|
3614
|
+
this.detectLeaks();
|
|
3615
|
+
}, sampleInterval);
|
|
3616
|
+
}
|
|
3617
|
+
}
|
|
3618
|
+
stop() {
|
|
3619
|
+
if (!this.running) {
|
|
3620
|
+
return;
|
|
3621
|
+
}
|
|
3622
|
+
this.running = false;
|
|
3623
|
+
if (this.checkTimer) {
|
|
3624
|
+
clearInterval(this.checkTimer);
|
|
3625
|
+
this.checkTimer = void 0;
|
|
3626
|
+
}
|
|
3627
|
+
if (this.leakDetectionTimer) {
|
|
3628
|
+
clearInterval(this.leakDetectionTimer);
|
|
3629
|
+
this.leakDetectionTimer = void 0;
|
|
3630
|
+
}
|
|
3631
|
+
}
|
|
3632
|
+
registerCleanup(name, handler) {
|
|
3633
|
+
this.cleanupHandlers.set(name, handler);
|
|
3634
|
+
}
|
|
3635
|
+
unregisterCleanup(name) {
|
|
3636
|
+
this.cleanupHandlers.delete(name);
|
|
3637
|
+
}
|
|
3638
|
+
async cleanup(name) {
|
|
3639
|
+
if (name) {
|
|
3640
|
+
const handler = this.cleanupHandlers.get(name);
|
|
3641
|
+
if (handler) {
|
|
3642
|
+
await handler();
|
|
3643
|
+
}
|
|
3644
|
+
} else {
|
|
3645
|
+
for (const handler of this.cleanupHandlers.values()) {
|
|
3646
|
+
await handler();
|
|
3647
|
+
}
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3650
|
+
getStats() {
|
|
3651
|
+
const memUsage = process.memoryUsage();
|
|
3652
|
+
const total = this.options.maxMemory || memUsage.heapTotal;
|
|
3653
|
+
const used = memUsage.heapUsed;
|
|
3654
|
+
return {
|
|
3655
|
+
used,
|
|
3656
|
+
total,
|
|
3657
|
+
percentage: used / total * 100,
|
|
3658
|
+
heapUsed: memUsage.heapUsed,
|
|
3659
|
+
heapTotal: memUsage.heapTotal,
|
|
3660
|
+
external: memUsage.external,
|
|
3661
|
+
arrayBuffers: memUsage.arrayBuffers
|
|
3662
|
+
};
|
|
3663
|
+
}
|
|
3664
|
+
forceGC() {
|
|
3665
|
+
if (global.gc) {
|
|
3666
|
+
global.gc();
|
|
3667
|
+
} else {
|
|
3668
|
+
console.warn("Garbage collection is not exposed. Run with --expose-gc flag.");
|
|
3669
|
+
}
|
|
3670
|
+
}
|
|
3671
|
+
checkMemory() {
|
|
3672
|
+
const stats = this.getStats();
|
|
3673
|
+
const threshold = this.options.thresholdPercentage || 80;
|
|
3674
|
+
if (stats.percentage >= threshold) {
|
|
3675
|
+
this.options.onThreshold?.(stats);
|
|
3676
|
+
}
|
|
3677
|
+
if (this.options.maxMemory && stats.used >= this.options.maxMemory) {
|
|
3678
|
+
this.options.onLimit?.(stats).catch((error) => {
|
|
3679
|
+
console.error("Error in onLimit handler:", error);
|
|
3680
|
+
});
|
|
3681
|
+
}
|
|
3682
|
+
}
|
|
3683
|
+
detectLeaks() {
|
|
3684
|
+
const stats = this.getStats();
|
|
3685
|
+
if (this.previousMemory) {
|
|
3686
|
+
const growth = stats.used - this.previousMemory.used;
|
|
3687
|
+
const growthPercentage = growth / this.previousMemory.used * 100;
|
|
3688
|
+
const threshold = this.options.leakDetection?.growthThreshold || 10;
|
|
3689
|
+
if (growthPercentage > threshold) {
|
|
3690
|
+
this.options.onLeak?.(stats);
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3693
|
+
this.previousMemory = stats;
|
|
3694
|
+
}
|
|
3695
|
+
};
|
|
3696
|
+
function createMemoryManager(options) {
|
|
3697
|
+
return new MemoryManager(options);
|
|
3698
|
+
}
|
|
3699
|
+
|
|
3700
|
+
// src/resources/batch.ts
|
|
3701
|
+
var BatchProcessor = class {
|
|
3702
|
+
constructor(options) {
|
|
3703
|
+
this.options = options;
|
|
3704
|
+
}
|
|
3705
|
+
pending = [];
|
|
3706
|
+
timer;
|
|
3707
|
+
processing = false;
|
|
3708
|
+
stats = {
|
|
3709
|
+
totalBatches: 0,
|
|
3710
|
+
totalItems: 0,
|
|
3711
|
+
averageBatchSize: 0,
|
|
3712
|
+
averageWaitTime: 0,
|
|
3713
|
+
successfulBatches: 0,
|
|
3714
|
+
failedBatches: 0
|
|
3715
|
+
};
|
|
3716
|
+
add(input) {
|
|
3717
|
+
return new Promise((resolve, reject) => {
|
|
3718
|
+
this.pending.push({
|
|
3719
|
+
input,
|
|
3720
|
+
resolve,
|
|
3721
|
+
reject,
|
|
3722
|
+
addedAt: Date.now()
|
|
3723
|
+
});
|
|
3724
|
+
if (!this.timer) {
|
|
3725
|
+
this.timer = setTimeout(() => {
|
|
3726
|
+
this.processBatch();
|
|
3727
|
+
}, this.options.maxWaitTime);
|
|
3728
|
+
}
|
|
3729
|
+
if (this.pending.length >= this.options.maxBatchSize) {
|
|
3730
|
+
this.processBatch();
|
|
3731
|
+
}
|
|
3732
|
+
});
|
|
3733
|
+
}
|
|
3734
|
+
async flush() {
|
|
3735
|
+
if (this.pending.length > 0) {
|
|
3736
|
+
await this.processBatch();
|
|
3737
|
+
}
|
|
3738
|
+
}
|
|
3739
|
+
async processBatch() {
|
|
3740
|
+
if (this.processing || this.pending.length === 0) {
|
|
3741
|
+
return;
|
|
3742
|
+
}
|
|
3743
|
+
this.processing = true;
|
|
3744
|
+
if (this.timer) {
|
|
3745
|
+
clearTimeout(this.timer);
|
|
3746
|
+
this.timer = void 0;
|
|
3747
|
+
}
|
|
3748
|
+
const batchSize = Math.min(this.pending.length, this.options.maxBatchSize);
|
|
3749
|
+
const batch2 = this.pending.splice(0, batchSize);
|
|
3750
|
+
const inputs = batch2.map((item) => item.input);
|
|
3751
|
+
const now = Date.now();
|
|
3752
|
+
const waitTimes = batch2.map((item) => now - item.addedAt);
|
|
3753
|
+
const avgWaitTime = waitTimes.reduce((sum, time) => sum + time, 0) / waitTimes.length;
|
|
3754
|
+
this.stats.totalBatches++;
|
|
3755
|
+
this.stats.totalItems += batch2.length;
|
|
3756
|
+
this.stats.averageBatchSize = (this.stats.averageBatchSize * (this.stats.totalBatches - 1) + batch2.length) / this.stats.totalBatches;
|
|
3757
|
+
this.stats.averageWaitTime = (this.stats.averageWaitTime * (this.stats.totalBatches - 1) + avgWaitTime) / this.stats.totalBatches;
|
|
3758
|
+
this.options.onBatchStart?.(inputs);
|
|
3759
|
+
try {
|
|
3760
|
+
const results = await this.options.processor(inputs);
|
|
3761
|
+
if (results.length !== batch2.length) {
|
|
3762
|
+
throw new Error(
|
|
3763
|
+
`Processor returned ${results.length} results for ${batch2.length} inputs`
|
|
3764
|
+
);
|
|
3765
|
+
}
|
|
3766
|
+
for (let i = 0; i < batch2.length; i++) {
|
|
3767
|
+
batch2[i].resolve(results[i]);
|
|
3768
|
+
}
|
|
3769
|
+
this.stats.successfulBatches++;
|
|
3770
|
+
this.options.onBatchComplete?.(inputs, results);
|
|
3771
|
+
} catch (error) {
|
|
3772
|
+
this.stats.failedBatches++;
|
|
3773
|
+
this.options.onBatchError?.(inputs, error);
|
|
3774
|
+
for (const item of batch2) {
|
|
3775
|
+
if (this.options.onItemError) {
|
|
3776
|
+
const fallback = this.options.onItemError(item.input, error);
|
|
3777
|
+
if (fallback !== void 0) {
|
|
3778
|
+
item.resolve(fallback);
|
|
3779
|
+
} else {
|
|
3780
|
+
item.reject(error);
|
|
3781
|
+
}
|
|
3782
|
+
} else {
|
|
3783
|
+
item.reject(error);
|
|
3784
|
+
}
|
|
3785
|
+
}
|
|
3786
|
+
} finally {
|
|
3787
|
+
this.processing = false;
|
|
3788
|
+
if (this.pending.length > 0) {
|
|
3789
|
+
this.timer = setTimeout(() => {
|
|
3790
|
+
this.processBatch();
|
|
3791
|
+
}, this.options.maxWaitTime);
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
getStats() {
|
|
3796
|
+
return { ...this.stats };
|
|
3797
|
+
}
|
|
3798
|
+
getPendingCount() {
|
|
3799
|
+
return this.pending.length;
|
|
3800
|
+
}
|
|
3801
|
+
};
|
|
3802
|
+
function createBatchProcessor(options) {
|
|
3803
|
+
return new BatchProcessor(options);
|
|
3804
|
+
}
|
|
3805
|
+
|
|
3806
|
+
// src/resources/circuit-breaker.ts
|
|
3807
|
+
var CircuitBreaker = class {
|
|
3808
|
+
constructor(options) {
|
|
3809
|
+
this.options = options;
|
|
3810
|
+
}
|
|
3811
|
+
state = "closed";
|
|
3812
|
+
failures = 0;
|
|
3813
|
+
successes = 0;
|
|
3814
|
+
totalCalls = 0;
|
|
3815
|
+
stateChanges = 0;
|
|
3816
|
+
lastFailureTime;
|
|
3817
|
+
lastSuccessTime;
|
|
3818
|
+
resetTimer;
|
|
3819
|
+
callHistory = [];
|
|
3820
|
+
halfOpenAttempts = 0;
|
|
3821
|
+
async execute(fn) {
|
|
3822
|
+
if (this.state === "open") {
|
|
3823
|
+
throw new Error("Circuit breaker is open");
|
|
3824
|
+
}
|
|
3825
|
+
if (this.state === "half-open") {
|
|
3826
|
+
const maxAttempts = this.options.halfOpenRequests || 1;
|
|
3827
|
+
if (this.halfOpenAttempts >= maxAttempts) {
|
|
3828
|
+
throw new Error("Circuit breaker is half-open and at capacity");
|
|
3829
|
+
}
|
|
3830
|
+
this.halfOpenAttempts++;
|
|
3831
|
+
}
|
|
3832
|
+
this.totalCalls++;
|
|
3833
|
+
try {
|
|
3834
|
+
const result = await fn();
|
|
3835
|
+
this.onSuccess();
|
|
3836
|
+
return result;
|
|
3837
|
+
} catch (error) {
|
|
3838
|
+
this.onFailure(error);
|
|
3839
|
+
throw error;
|
|
3840
|
+
}
|
|
3841
|
+
}
|
|
3842
|
+
wrap(fn) {
|
|
3843
|
+
return async (...args) => {
|
|
3844
|
+
return this.execute(() => fn(...args));
|
|
3845
|
+
};
|
|
3846
|
+
}
|
|
3847
|
+
onSuccess() {
|
|
3848
|
+
this.successes++;
|
|
3849
|
+
this.lastSuccessTime = Date.now();
|
|
3850
|
+
this.recordCall(true);
|
|
3851
|
+
this.options.onSuccess?.();
|
|
3852
|
+
if (this.state === "half-open") {
|
|
3853
|
+
this.transitionTo("closed");
|
|
3854
|
+
this.failures = 0;
|
|
3855
|
+
this.halfOpenAttempts = 0;
|
|
3856
|
+
}
|
|
3857
|
+
}
|
|
3858
|
+
onFailure(error) {
|
|
3859
|
+
this.failures++;
|
|
3860
|
+
this.lastFailureTime = Date.now();
|
|
3861
|
+
this.recordCall(false);
|
|
3862
|
+
this.options.onFailure?.(error);
|
|
3863
|
+
const shouldTrip = this.options.shouldTrip?.(error) ?? true;
|
|
3864
|
+
if (!shouldTrip) {
|
|
3865
|
+
return;
|
|
3866
|
+
}
|
|
3867
|
+
if (this.state === "half-open") {
|
|
3868
|
+
this.transitionTo("open");
|
|
3869
|
+
this.halfOpenAttempts = 0;
|
|
3870
|
+
this.scheduleReset();
|
|
3871
|
+
} else if (this.state === "closed") {
|
|
3872
|
+
const recentFailures = this.getRecentFailures();
|
|
3873
|
+
if (recentFailures >= this.options.failureThreshold) {
|
|
3874
|
+
this.transitionTo("open");
|
|
3875
|
+
this.scheduleReset();
|
|
3876
|
+
}
|
|
3877
|
+
}
|
|
3878
|
+
}
|
|
3879
|
+
recordCall(success) {
|
|
3880
|
+
this.callHistory.push({
|
|
3881
|
+
timestamp: Date.now(),
|
|
3882
|
+
success
|
|
3883
|
+
});
|
|
3884
|
+
const monitoringPeriod = this.options.monitoringPeriod || 6e4;
|
|
3885
|
+
const cutoff = Date.now() - monitoringPeriod;
|
|
3886
|
+
this.callHistory = this.callHistory.filter((record) => record.timestamp >= cutoff);
|
|
3887
|
+
}
|
|
3888
|
+
getRecentFailures() {
|
|
3889
|
+
return this.callHistory.filter((record) => !record.success).length;
|
|
3890
|
+
}
|
|
3891
|
+
scheduleReset() {
|
|
3892
|
+
if (this.resetTimer) {
|
|
3893
|
+
clearTimeout(this.resetTimer);
|
|
3894
|
+
}
|
|
3895
|
+
this.resetTimer = setTimeout(() => {
|
|
3896
|
+
this.transitionTo("half-open");
|
|
3897
|
+
this.halfOpenAttempts = 0;
|
|
3898
|
+
}, this.options.resetTimeout);
|
|
3899
|
+
}
|
|
3900
|
+
transitionTo(newState) {
|
|
3901
|
+
const previousState = this.state;
|
|
3902
|
+
if (previousState === newState) {
|
|
3903
|
+
return;
|
|
3904
|
+
}
|
|
3905
|
+
this.state = newState;
|
|
3906
|
+
this.stateChanges++;
|
|
3907
|
+
this.options.onStateChange?.(newState, previousState);
|
|
3908
|
+
}
|
|
3909
|
+
getState() {
|
|
3910
|
+
return this.state;
|
|
3911
|
+
}
|
|
3912
|
+
getStats() {
|
|
3913
|
+
return {
|
|
3914
|
+
state: this.state,
|
|
3915
|
+
failures: this.failures,
|
|
3916
|
+
successes: this.successes,
|
|
3917
|
+
totalCalls: this.totalCalls,
|
|
3918
|
+
failureRate: this.totalCalls > 0 ? this.failures / this.totalCalls : 0,
|
|
3919
|
+
lastFailureTime: this.lastFailureTime,
|
|
3920
|
+
lastSuccessTime: this.lastSuccessTime,
|
|
3921
|
+
stateChanges: this.stateChanges
|
|
3922
|
+
};
|
|
3923
|
+
}
|
|
3924
|
+
reset() {
|
|
3925
|
+
this.state = "closed";
|
|
3926
|
+
this.failures = 0;
|
|
3927
|
+
this.successes = 0;
|
|
3928
|
+
this.halfOpenAttempts = 0;
|
|
3929
|
+
this.callHistory = [];
|
|
3930
|
+
if (this.resetTimer) {
|
|
3931
|
+
clearTimeout(this.resetTimer);
|
|
3932
|
+
this.resetTimer = void 0;
|
|
3933
|
+
}
|
|
3934
|
+
}
|
|
3935
|
+
};
|
|
3936
|
+
function createCircuitBreaker(options) {
|
|
3937
|
+
return new CircuitBreaker(options);
|
|
3938
|
+
}
|
|
3939
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
3940
|
+
0 && (module.exports = {
|
|
3941
|
+
AgentError,
|
|
3942
|
+
BatchProcessor,
|
|
3943
|
+
CircuitBreaker,
|
|
3944
|
+
ConnectionPool,
|
|
3945
|
+
DatabasePool,
|
|
3946
|
+
HttpPool,
|
|
3947
|
+
LogLevel,
|
|
3948
|
+
ManagedTool,
|
|
3949
|
+
MemoryManager,
|
|
3950
|
+
MetricType,
|
|
3951
|
+
MiddlewareChain,
|
|
3952
|
+
MissingDescriptionError,
|
|
3953
|
+
RegistryEvent,
|
|
3954
|
+
TimeoutError,
|
|
3955
|
+
ToolBuilder,
|
|
3956
|
+
ToolCategory,
|
|
3957
|
+
ToolCategorySchema,
|
|
3958
|
+
ToolExampleSchema,
|
|
3959
|
+
ToolMetadataSchema,
|
|
3960
|
+
ToolNameSchema,
|
|
3961
|
+
ToolRegistry,
|
|
3962
|
+
batch,
|
|
3963
|
+
broadcast,
|
|
3964
|
+
cache,
|
|
3965
|
+
chain,
|
|
3966
|
+
chunk,
|
|
3967
|
+
clearThread,
|
|
3968
|
+
collect,
|
|
3969
|
+
compose,
|
|
3970
|
+
composeGraphs,
|
|
3971
|
+
composeTool,
|
|
3972
|
+
composeWithOptions,
|
|
3973
|
+
conditional,
|
|
3974
|
+
configureLangSmith,
|
|
3975
|
+
createBatchProcessor,
|
|
3976
|
+
createBinaryRouter,
|
|
3977
|
+
createCircuitBreaker,
|
|
3978
|
+
createConditionalRouter,
|
|
3979
|
+
createConnectionPool,
|
|
3980
|
+
createConversationConfig,
|
|
3981
|
+
createDatabasePool,
|
|
3982
|
+
createErrorReporter,
|
|
3983
|
+
createHeartbeat,
|
|
3984
|
+
createHttpPool,
|
|
3985
|
+
createLogger,
|
|
3986
|
+
createManagedTool,
|
|
3987
|
+
createMemoryCheckpointer,
|
|
3988
|
+
createMemoryManager,
|
|
3989
|
+
createMessage,
|
|
3990
|
+
createMetrics,
|
|
3991
|
+
createMiddlewareContext,
|
|
3992
|
+
createMockTool,
|
|
3993
|
+
createMultiRouter,
|
|
3994
|
+
createParallelWorkflow,
|
|
3995
|
+
createProgressTracker,
|
|
3996
|
+
createSSEFormatter,
|
|
3997
|
+
createSequentialWorkflow,
|
|
3998
|
+
createSqliteCheckpointer,
|
|
3999
|
+
createStateAnnotation,
|
|
4000
|
+
createSubgraph,
|
|
4001
|
+
createThreadConfig,
|
|
4002
|
+
createTool,
|
|
4003
|
+
createToolExecutor,
|
|
4004
|
+
createToolSimulator,
|
|
4005
|
+
createToolUnsafe,
|
|
4006
|
+
createWebSocketHandler,
|
|
4007
|
+
development,
|
|
4008
|
+
filter,
|
|
4009
|
+
generateThreadId,
|
|
4010
|
+
getCheckpointHistory,
|
|
4011
|
+
getLangSmithConfig,
|
|
4012
|
+
getLatestCheckpoint,
|
|
4013
|
+
getMissingDescriptions,
|
|
4014
|
+
getToolDescription,
|
|
4015
|
+
getToolJsonSchema,
|
|
4016
|
+
isMemoryCheckpointer,
|
|
4017
|
+
isTracingEnabled,
|
|
4018
|
+
map,
|
|
4019
|
+
merge,
|
|
4020
|
+
mergeState,
|
|
4021
|
+
parallel,
|
|
4022
|
+
parseSSEEvent,
|
|
4023
|
+
presets,
|
|
4024
|
+
production,
|
|
4025
|
+
reduce,
|
|
4026
|
+
retry,
|
|
4027
|
+
safeValidateSchemaDescriptions,
|
|
4028
|
+
sendMessage,
|
|
4029
|
+
sequential,
|
|
4030
|
+
sequentialBuilder,
|
|
4031
|
+
take,
|
|
4032
|
+
testing,
|
|
4033
|
+
throttle,
|
|
4034
|
+
timeout,
|
|
4035
|
+
toLangChainTool,
|
|
4036
|
+
toLangChainTools,
|
|
4037
|
+
toolBuilder,
|
|
4038
|
+
validateSchemaDescriptions,
|
|
4039
|
+
validateState,
|
|
4040
|
+
validateTool,
|
|
4041
|
+
validateToolMetadata,
|
|
4042
|
+
validateToolName,
|
|
4043
|
+
withErrorHandler,
|
|
4044
|
+
withMetrics,
|
|
4045
|
+
withRetry,
|
|
4046
|
+
withTimeout,
|
|
4047
|
+
withTracing
|
|
4048
|
+
});
|