@flink-app/flink 0.14.3 → 2.0.0-alpha.48
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/CHANGELOG.md +66 -0
- package/cli/build.ts +8 -1
- package/cli/run.ts +8 -1
- package/dist/cli/build.js +8 -1
- package/dist/cli/run.js +8 -1
- package/dist/src/FlinkApp.d.ts +33 -0
- package/dist/src/FlinkApp.js +279 -35
- package/dist/src/FlinkContext.d.ts +21 -0
- package/dist/src/FlinkHttpHandler.d.ts +152 -9
- package/dist/src/FlinkHttpHandler.js +37 -1
- package/dist/src/TypeScriptCompiler.d.ts +42 -0
- package/dist/src/TypeScriptCompiler.js +346 -4
- package/dist/src/TypeScriptUtils.js +4 -0
- package/dist/src/ai/AgentRunner.d.ts +39 -0
- package/dist/src/ai/AgentRunner.js +625 -0
- package/dist/src/ai/FlinkAgent.d.ts +446 -0
- package/dist/src/ai/FlinkAgent.js +633 -0
- package/dist/src/ai/FlinkTool.d.ts +37 -0
- package/dist/src/ai/FlinkTool.js +2 -0
- package/dist/src/ai/LLMAdapter.d.ts +119 -0
- package/dist/src/ai/LLMAdapter.js +2 -0
- package/dist/src/ai/SubAgentExecutor.d.ts +36 -0
- package/dist/src/ai/SubAgentExecutor.js +220 -0
- package/dist/src/ai/ToolExecutor.d.ts +35 -0
- package/dist/src/ai/ToolExecutor.js +237 -0
- package/dist/src/ai/index.d.ts +5 -0
- package/dist/src/ai/index.js +21 -0
- package/dist/src/handlers/StreamWriterFactory.d.ts +20 -0
- package/dist/src/handlers/StreamWriterFactory.js +83 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +4 -0
- package/dist/src/utils.d.ts +30 -0
- package/dist/src/utils.js +52 -0
- package/package.json +16 -2
- package/readme.md +425 -0
- package/spec/AgentDuplicateDetection.spec.ts +112 -0
- package/spec/AgentRunner.spec.ts +527 -0
- package/spec/ConversationHooks.spec.ts +290 -0
- package/spec/FlinkAgent.spec.ts +310 -0
- package/spec/FlinkApp.onError.spec.ts +1 -2
- package/spec/FlinkApp.query.spec.ts +107 -0
- package/spec/FlinkApp.validationMode.spec.ts +155 -0
- package/spec/StreamingIntegration.spec.ts +138 -0
- package/spec/SubAgentSupport.spec.ts +941 -0
- package/spec/ToolExecutor.spec.ts +360 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCar.js +57 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCar2.js +59 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema.js +53 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema2.js +53 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema3.js +53 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithLiteralSchema.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithLiteralSchema2.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithSchemaInFile.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithSchemaInFile2.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/ManuallyAddedHandler.js +53 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/ManuallyAddedHandler2.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchCar.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchOnboardingSession.js +76 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchOrderWithComplexTypes.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchProductWithIntersection.js +59 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchUserWithUnion.js +59 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PostCar.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PostLogin.js +56 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PostLogout.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PutCar.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/index.js +83 -0
- package/spec/mock-project/dist/spec/mock-project/src/repos/CarRepo.js +26 -0
- package/spec/mock-project/dist/spec/mock-project/src/schemas/Car.js +2 -0
- package/spec/mock-project/dist/spec/mock-project/src/schemas/DefaultExportSchema.js +2 -0
- package/spec/mock-project/dist/spec/mock-project/src/schemas/FileWithTwoSchemas.js +2 -0
- package/spec/mock-project/dist/src/FlinkApp.js +1012 -0
- package/spec/mock-project/dist/src/FlinkContext.js +2 -0
- package/spec/mock-project/dist/src/FlinkErrors.js +143 -0
- package/spec/mock-project/dist/src/FlinkHttpHandler.js +47 -0
- package/spec/mock-project/dist/src/FlinkJob.js +2 -0
- package/spec/mock-project/dist/src/FlinkLog.js +26 -0
- package/spec/mock-project/dist/src/FlinkPlugin.js +2 -0
- package/spec/mock-project/dist/src/FlinkRepo.js +224 -0
- package/spec/mock-project/dist/src/FlinkResponse.js +2 -0
- package/spec/mock-project/dist/src/ai/AgentExecutor.js +279 -0
- package/spec/mock-project/dist/src/ai/AgentRunner.js +625 -0
- package/spec/mock-project/dist/src/ai/FlinkAgent.js +633 -0
- package/spec/mock-project/dist/src/ai/FlinkTool.js +2 -0
- package/spec/mock-project/dist/src/ai/LLMAdapter.js +2 -0
- package/spec/mock-project/dist/src/ai/SubAgentExecutor.js +220 -0
- package/spec/mock-project/dist/src/ai/ToolExecutor.js +237 -0
- package/spec/mock-project/dist/src/auth/FlinkAuthPlugin.js +2 -0
- package/spec/mock-project/dist/src/auth/FlinkAuthUser.js +2 -0
- package/spec/mock-project/dist/src/handlers/StreamWriterFactory.js +83 -0
- package/spec/mock-project/dist/src/index.js +17 -69
- package/spec/mock-project/dist/src/mock-data-generator.js +9 -0
- package/spec/mock-project/dist/src/utils.js +290 -0
- package/spec/mock-project/tsconfig.json +6 -1
- package/spec/testHelpers.ts +49 -0
- package/spec/utils.caseConversion.spec.ts +80 -0
- package/spec/utils.spec.ts +13 -13
- package/src/FlinkApp.ts +275 -8
- package/src/FlinkContext.ts +22 -0
- package/src/FlinkHttpHandler.ts +164 -10
- package/src/TypeScriptCompiler.ts +398 -7
- package/src/TypeScriptUtils.ts +5 -0
- package/src/ai/AgentRunner.ts +549 -0
- package/src/ai/FlinkAgent.ts +770 -0
- package/src/ai/FlinkTool.ts +40 -0
- package/src/ai/LLMAdapter.ts +96 -0
- package/src/ai/SubAgentExecutor.ts +199 -0
- package/src/ai/ToolExecutor.ts +193 -0
- package/src/ai/index.ts +5 -0
- package/src/handlers/StreamWriterFactory.ts +84 -0
- package/src/index.ts +4 -0
- package/src/utils.ts +52 -0
- package/tsconfig.json +6 -1
package/dist/src/FlinkApp.js
CHANGED
|
@@ -50,7 +50,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
50
50
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
51
51
|
};
|
|
52
52
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
|
-
exports.FlinkApp = exports.autoRegisteredJobs = exports.autoRegisteredRepos = exports.autoRegisteredHandlers = exports.expressFn = void 0;
|
|
53
|
+
exports.FlinkApp = exports.autoRegisteredAgents = exports.autoRegisteredTools = exports.autoRegisteredJobs = exports.autoRegisteredRepos = exports.autoRegisteredHandlers = exports.expressFn = void 0;
|
|
54
54
|
var ajv_1 = __importDefault(require("ajv"));
|
|
55
55
|
var ajv_formats_1 = __importDefault(require("ajv-formats"));
|
|
56
56
|
var body_parser_1 = __importDefault(require("body-parser"));
|
|
@@ -62,7 +62,9 @@ var ms_1 = __importDefault(require("ms"));
|
|
|
62
62
|
var toad_scheduler_1 = require("toad-scheduler");
|
|
63
63
|
var uuid_1 = require("uuid");
|
|
64
64
|
var FlinkErrors_1 = require("./FlinkErrors");
|
|
65
|
+
var FlinkHttpHandler_1 = require("./FlinkHttpHandler");
|
|
65
66
|
var FlinkLog_1 = require("./FlinkLog");
|
|
67
|
+
var StreamWriterFactory_1 = require("./handlers/StreamWriterFactory");
|
|
66
68
|
var mock_data_generator_1 = __importDefault(require("./mock-data-generator"));
|
|
67
69
|
var utils_1 = require("./utils");
|
|
68
70
|
var ajv = new ajv_1.default();
|
|
@@ -92,8 +94,19 @@ exports.autoRegisteredRepos = [];
|
|
|
92
94
|
* are picked up by TypeScript compiler
|
|
93
95
|
*/
|
|
94
96
|
exports.autoRegisteredJobs = [];
|
|
97
|
+
/**
|
|
98
|
+
* This will be populated at compile time when the apps tools
|
|
99
|
+
* are picked up by TypeScript compiler
|
|
100
|
+
*/
|
|
101
|
+
exports.autoRegisteredTools = [];
|
|
102
|
+
/**
|
|
103
|
+
* This will be populated at compile time when the apps agents
|
|
104
|
+
* are picked up by TypeScript compiler
|
|
105
|
+
*/
|
|
106
|
+
exports.autoRegisteredAgents = [];
|
|
95
107
|
var FlinkApp = /** @class */ (function () {
|
|
96
108
|
function FlinkApp(opts) {
|
|
109
|
+
var _a;
|
|
97
110
|
this.handlers = [];
|
|
98
111
|
this.started = false;
|
|
99
112
|
this.debug = false;
|
|
@@ -101,6 +114,9 @@ var FlinkApp = /** @class */ (function () {
|
|
|
101
114
|
this.routingConfigured = false;
|
|
102
115
|
this.disableHttpServer = false;
|
|
103
116
|
this.repos = {};
|
|
117
|
+
this.llmAdapters = new Map();
|
|
118
|
+
this.tools = {};
|
|
119
|
+
this.agents = {}; // FlinkAgent<C> instances
|
|
104
120
|
/**
|
|
105
121
|
* Internal cache used to track registered handlers and potentially any overlapping routes
|
|
106
122
|
*/
|
|
@@ -123,6 +139,11 @@ var FlinkApp = /** @class */ (function () {
|
|
|
123
139
|
this.disableHttpServer = !!opts.disableHttpServer;
|
|
124
140
|
this.accessLog = __assign({ enabled: true, format: "dev" }, opts.accessLog);
|
|
125
141
|
this.onError = opts.onError;
|
|
142
|
+
// Register LLM adapters if configured
|
|
143
|
+
if ((_a = opts.ai) === null || _a === void 0 ? void 0 : _a.llms) {
|
|
144
|
+
// Convert plain object to Map for internal use
|
|
145
|
+
this.llmAdapters = new Map(Object.entries(opts.ai.llms));
|
|
146
|
+
}
|
|
126
147
|
}
|
|
127
148
|
Object.defineProperty(FlinkApp.prototype, "ctx", {
|
|
128
149
|
get: function () {
|
|
@@ -151,13 +172,42 @@ var FlinkApp = /** @class */ (function () {
|
|
|
151
172
|
offsetTime = Date.now();
|
|
152
173
|
FlinkLog_1.log.bgColorLog("cyan", "Init db took ".concat(offsetTime - startTime, " ms"));
|
|
153
174
|
}
|
|
175
|
+
// Build initial context (without agents - they'll be added later)
|
|
154
176
|
return [4 /*yield*/, this.buildContext()];
|
|
155
177
|
case 2:
|
|
178
|
+
// Build initial context (without agents - they'll be added later)
|
|
156
179
|
_e.sent();
|
|
157
180
|
if (this.debug) {
|
|
158
181
|
FlinkLog_1.log.bgColorLog("cyan", "Build context took ".concat(Date.now() - offsetTime, " ms"));
|
|
159
182
|
offsetTime = Date.now();
|
|
160
183
|
}
|
|
184
|
+
// Register tools (needs context for ToolExecutor)
|
|
185
|
+
return [4 /*yield*/, this.registerAutoRegisterableTools()];
|
|
186
|
+
case 3:
|
|
187
|
+
// Register tools (needs context for ToolExecutor)
|
|
188
|
+
_e.sent();
|
|
189
|
+
if (this.debug) {
|
|
190
|
+
FlinkLog_1.log.bgColorLog("cyan", "Register tools took ".concat(Date.now() - offsetTime, " ms"));
|
|
191
|
+
offsetTime = Date.now();
|
|
192
|
+
}
|
|
193
|
+
// Register agents (creates agent instances)
|
|
194
|
+
return [4 /*yield*/, this.registerAutoRegisterableAgents()];
|
|
195
|
+
case 4:
|
|
196
|
+
// Register agents (creates agent instances)
|
|
197
|
+
_e.sent();
|
|
198
|
+
if (this.debug) {
|
|
199
|
+
FlinkLog_1.log.bgColorLog("cyan", "Register agents took ".concat(Date.now() - offsetTime, " ms"));
|
|
200
|
+
offsetTime = Date.now();
|
|
201
|
+
}
|
|
202
|
+
// Initialize agents now that context and tools are ready
|
|
203
|
+
return [4 /*yield*/, this.initializeAgents()];
|
|
204
|
+
case 5:
|
|
205
|
+
// Initialize agents now that context and tools are ready
|
|
206
|
+
_e.sent();
|
|
207
|
+
if (this.debug) {
|
|
208
|
+
FlinkLog_1.log.bgColorLog("cyan", "Initialize agents took ".concat(Date.now() - offsetTime, " ms"));
|
|
209
|
+
offsetTime = Date.now();
|
|
210
|
+
}
|
|
161
211
|
if (this.isSchedulingEnabled) {
|
|
162
212
|
this.scheduler = new toad_scheduler_1.ToadScheduler();
|
|
163
213
|
}
|
|
@@ -183,45 +233,45 @@ var FlinkApp = /** @class */ (function () {
|
|
|
183
233
|
});
|
|
184
234
|
}
|
|
185
235
|
_b = 0, _c = this.plugins;
|
|
186
|
-
_e.label =
|
|
187
|
-
case
|
|
188
|
-
if (!(_b < _c.length)) return [3 /*break*/,
|
|
236
|
+
_e.label = 6;
|
|
237
|
+
case 6:
|
|
238
|
+
if (!(_b < _c.length)) return [3 /*break*/, 12];
|
|
189
239
|
plugin = _c[_b];
|
|
190
240
|
db = void 0;
|
|
191
|
-
if (!plugin.db) return [3 /*break*/,
|
|
241
|
+
if (!plugin.db) return [3 /*break*/, 8];
|
|
192
242
|
return [4 /*yield*/, this.initPluginDb(plugin)];
|
|
193
|
-
case
|
|
243
|
+
case 7:
|
|
194
244
|
db = _e.sent();
|
|
195
|
-
_e.label =
|
|
196
|
-
case
|
|
197
|
-
if (!plugin.init) return [3 /*break*/,
|
|
245
|
+
_e.label = 8;
|
|
246
|
+
case 8:
|
|
247
|
+
if (!plugin.init) return [3 /*break*/, 10];
|
|
198
248
|
return [4 /*yield*/, plugin.init(this, db)];
|
|
199
|
-
case
|
|
249
|
+
case 9:
|
|
200
250
|
_e.sent();
|
|
201
|
-
_e.label =
|
|
202
|
-
case
|
|
251
|
+
_e.label = 10;
|
|
252
|
+
case 10:
|
|
203
253
|
FlinkLog_1.log.info("Initialized plugin '".concat(plugin.id, "'"));
|
|
204
|
-
_e.label =
|
|
205
|
-
case
|
|
254
|
+
_e.label = 11;
|
|
255
|
+
case 11:
|
|
206
256
|
_b++;
|
|
207
|
-
return [3 /*break*/,
|
|
208
|
-
case
|
|
209
|
-
case
|
|
257
|
+
return [3 /*break*/, 6];
|
|
258
|
+
case 12: return [4 /*yield*/, this.registerAutoRegisterableHandlers()];
|
|
259
|
+
case 13:
|
|
210
260
|
_e.sent();
|
|
211
261
|
if (this.debug) {
|
|
212
262
|
FlinkLog_1.log.bgColorLog("cyan", "Register handlers took ".concat(Date.now() - offsetTime, " ms"));
|
|
213
263
|
offsetTime = Date.now();
|
|
214
264
|
}
|
|
215
|
-
if (!this.isSchedulingEnabled) return [3 /*break*/,
|
|
265
|
+
if (!this.isSchedulingEnabled) return [3 /*break*/, 15];
|
|
216
266
|
return [4 /*yield*/, this.registerAutoRegisterableJobs()];
|
|
217
|
-
case
|
|
267
|
+
case 14:
|
|
218
268
|
_e.sent();
|
|
219
269
|
if (this.debug) {
|
|
220
270
|
FlinkLog_1.log.bgColorLog("cyan", "Register jobs took ".concat(Date.now() - offsetTime, " ms"));
|
|
221
271
|
offsetTime = Date.now();
|
|
222
272
|
}
|
|
223
|
-
_e.label =
|
|
224
|
-
case
|
|
273
|
+
_e.label = 15;
|
|
274
|
+
case 15:
|
|
225
275
|
// Register 404 with slight delay to allow all manually added routes to be added
|
|
226
276
|
// TODO: Is there a better solution to force this handler to always run last?
|
|
227
277
|
setTimeout(function () {
|
|
@@ -324,7 +374,7 @@ var FlinkApp = /** @class */ (function () {
|
|
|
324
374
|
var _this = this;
|
|
325
375
|
this.handlers.push(handlerConfig);
|
|
326
376
|
var routeProps = handlerConfig.routeProps, _a = handlerConfig.schema, schema = _a === void 0 ? {} : _a;
|
|
327
|
-
var method = routeProps.method;
|
|
377
|
+
var method = routeProps.method, streamFormat = routeProps.streamFormat;
|
|
328
378
|
if (!method) {
|
|
329
379
|
FlinkLog_1.log.error("Route ".concat(routeProps.path, " is missing http method"));
|
|
330
380
|
}
|
|
@@ -335,24 +385,28 @@ var FlinkApp = /** @class */ (function () {
|
|
|
335
385
|
}
|
|
336
386
|
var validateReq_1;
|
|
337
387
|
var validateRes_1;
|
|
338
|
-
if
|
|
388
|
+
// Determine validation mode (default to Validate if not specified)
|
|
389
|
+
var validationMode = routeProps.validation || FlinkHttpHandler_1.ValidationMode.Validate;
|
|
390
|
+
// Compile request schema if validation mode requires it
|
|
391
|
+
if (schema.reqSchema && validationMode !== FlinkHttpHandler_1.ValidationMode.SkipValidation && validationMode !== FlinkHttpHandler_1.ValidationMode.ValidateResponse) {
|
|
339
392
|
validateReq_1 = ajv.compile(schema.reqSchema);
|
|
340
393
|
}
|
|
341
|
-
|
|
394
|
+
// Skip response validation for streaming handlers (responses are stream chunks, not final JSON)
|
|
395
|
+
if (!streamFormat && schema.resSchema && validationMode !== FlinkHttpHandler_1.ValidationMode.SkipValidation && validationMode !== FlinkHttpHandler_1.ValidationMode.ValidateRequest) {
|
|
342
396
|
validateRes_1 = ajv.compile(schema.resSchema);
|
|
343
397
|
}
|
|
344
398
|
this.expressApp[method](routeProps.path, function (req, res) { return __awaiter(_this, void 0, void 0, function () {
|
|
345
|
-
var valid, formattedErrors, data, handlerRes, err_1, errorResponse, result, valid, formattedErrors;
|
|
346
|
-
return __generator(this, function (
|
|
347
|
-
switch (
|
|
399
|
+
var valid, formattedErrors, data, normalizedQuery, _i, _a, _b, key, value, stream, handlerRes, err_1, errorResponse, result, valid, formattedErrors;
|
|
400
|
+
return __generator(this, function (_c) {
|
|
401
|
+
switch (_c.label) {
|
|
348
402
|
case 0:
|
|
349
403
|
if (!routeProps.permissions) return [3 /*break*/, 2];
|
|
350
404
|
return [4 /*yield*/, this.authenticate(req, routeProps.permissions)];
|
|
351
405
|
case 1:
|
|
352
|
-
if (!(
|
|
406
|
+
if (!(_c.sent())) {
|
|
353
407
|
return [2 /*return*/, res.status(401).json((0, FlinkErrors_1.unauthorized)())];
|
|
354
408
|
}
|
|
355
|
-
|
|
409
|
+
_c.label = 2;
|
|
356
410
|
case 2:
|
|
357
411
|
if (validateReq_1) {
|
|
358
412
|
valid = validateReq_1(req.body);
|
|
@@ -369,7 +423,8 @@ var FlinkApp = /** @class */ (function () {
|
|
|
369
423
|
})];
|
|
370
424
|
}
|
|
371
425
|
}
|
|
372
|
-
|
|
426
|
+
// Skip mock API for streaming handlers
|
|
427
|
+
if (routeProps.mockApi && schema.resSchema && !streamFormat) {
|
|
373
428
|
FlinkLog_1.log.warn("Mock response for ".concat(req.method.toUpperCase(), " ").concat(req.path));
|
|
374
429
|
data = (0, mock_data_generator_1.default)(schema.resSchema);
|
|
375
430
|
res.status(200).json({
|
|
@@ -378,20 +433,51 @@ var FlinkApp = /** @class */ (function () {
|
|
|
378
433
|
});
|
|
379
434
|
return [2 /*return*/];
|
|
380
435
|
}
|
|
381
|
-
|
|
436
|
+
// Normalize query parameters to predictable string or string[] types
|
|
437
|
+
// Express query parser can produce numbers, booleans, objects, etc.
|
|
438
|
+
// We normalize everything to strings or string arrays for consistency
|
|
439
|
+
if (req.query && typeof req.query === "object") {
|
|
440
|
+
normalizedQuery = {};
|
|
441
|
+
for (_i = 0, _a = Object.entries(req.query); _i < _a.length; _i++) {
|
|
442
|
+
_b = _a[_i], key = _b[0], value = _b[1];
|
|
443
|
+
if (Array.isArray(value)) {
|
|
444
|
+
// Handle array values (e.g., ?tag=a&tag=b)
|
|
445
|
+
normalizedQuery[key] = value.map(function (v) { return String(v); });
|
|
446
|
+
}
|
|
447
|
+
else if (value !== undefined && value !== null) {
|
|
448
|
+
// Convert single values to strings
|
|
449
|
+
normalizedQuery[key] = String(value);
|
|
450
|
+
}
|
|
451
|
+
// Skip undefined/null values - they won't appear in the normalized query
|
|
452
|
+
}
|
|
453
|
+
req.query = normalizedQuery;
|
|
454
|
+
}
|
|
455
|
+
stream = streamFormat ? StreamWriterFactory_1.StreamWriterFactory.create(res, streamFormat) : undefined;
|
|
456
|
+
_c.label = 3;
|
|
382
457
|
case 3:
|
|
383
|
-
|
|
458
|
+
_c.trys.push([3, 5, , 6]);
|
|
384
459
|
return [4 /*yield*/, handler({
|
|
385
460
|
req: req,
|
|
386
461
|
ctx: this.ctx,
|
|
387
462
|
origin: routeProps.origin,
|
|
463
|
+
stream: stream,
|
|
388
464
|
})];
|
|
389
465
|
case 4:
|
|
390
466
|
// 👇 This is where the actual handler gets invoked
|
|
391
|
-
handlerRes =
|
|
467
|
+
handlerRes = _c.sent();
|
|
392
468
|
return [3 /*break*/, 6];
|
|
393
469
|
case 5:
|
|
394
|
-
err_1 =
|
|
470
|
+
err_1 = _c.sent();
|
|
471
|
+
// Handle errors for streaming handlers
|
|
472
|
+
if (streamFormat && stream) {
|
|
473
|
+
FlinkLog_1.log.error("Streaming handler error on ".concat(req.method.toUpperCase(), " ").concat(req.path, ": ").concat(err_1.message), {
|
|
474
|
+
error: err_1,
|
|
475
|
+
path: req.path,
|
|
476
|
+
method: req.method,
|
|
477
|
+
});
|
|
478
|
+
stream.error(err_1);
|
|
479
|
+
return [2 /*return*/];
|
|
480
|
+
}
|
|
395
481
|
errorResponse = void 0;
|
|
396
482
|
// duck typing to check if it is a FlinkError
|
|
397
483
|
if (typeof err_1.status === "number" && err_1.status >= 400 && err_1.status < 600 && err_1.error) {
|
|
@@ -432,6 +518,14 @@ var FlinkApp = /** @class */ (function () {
|
|
|
432
518
|
}
|
|
433
519
|
return [2 /*return*/, res.status(errorResponse.status || 500).json(errorResponse)];
|
|
434
520
|
case 6:
|
|
521
|
+
// Skip response handling for streaming handlers (stream controls response lifecycle)
|
|
522
|
+
if (streamFormat) {
|
|
523
|
+
return [2 /*return*/];
|
|
524
|
+
}
|
|
525
|
+
// Ensure handlerRes is defined for non-streaming handlers
|
|
526
|
+
if (!handlerRes) {
|
|
527
|
+
return [2 /*return*/, res.status(204).send()];
|
|
528
|
+
}
|
|
435
529
|
if (validateRes_1 && !(0, utils_1.isError)(handlerRes)) {
|
|
436
530
|
valid = validateRes_1(JSON.parse(JSON.stringify(handlerRes.data)));
|
|
437
531
|
if (!valid) {
|
|
@@ -459,7 +553,7 @@ var FlinkApp = /** @class */ (function () {
|
|
|
459
553
|
}
|
|
460
554
|
else {
|
|
461
555
|
this.handlerRouteCache.set(methodAndRoute_1, JSON.stringify(routeProps));
|
|
462
|
-
FlinkLog_1.log.info("Registered route ".concat(methodAndRoute_1));
|
|
556
|
+
FlinkLog_1.log.info("Registered ".concat(streamFormat ? 'streaming ' : '', "route ").concat(methodAndRoute_1).concat(streamFormat ? " (".concat(streamFormat, ")") : ''));
|
|
463
557
|
}
|
|
464
558
|
}
|
|
465
559
|
};
|
|
@@ -601,6 +695,137 @@ var FlinkApp = /** @class */ (function () {
|
|
|
601
695
|
// TODO: Find out if we need to set ctx here or wanted not to if plugin has its own context
|
|
602
696
|
// repoInstance.ctx = this.ctx;
|
|
603
697
|
};
|
|
698
|
+
FlinkApp.prototype.registerAutoRegisterableTools = function () {
|
|
699
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
700
|
+
var ToolExecutor, getRepoInstanceName, _i, autoRegisteredTools_1, toolFile, toolId, toolInstanceName, toolExecutor;
|
|
701
|
+
return __generator(this, function (_a) {
|
|
702
|
+
ToolExecutor = require("./ai/ToolExecutor").ToolExecutor;
|
|
703
|
+
getRepoInstanceName = require("./utils").getRepoInstanceName;
|
|
704
|
+
for (_i = 0, autoRegisteredTools_1 = exports.autoRegisteredTools; _i < autoRegisteredTools_1.length; _i++) {
|
|
705
|
+
toolFile = autoRegisteredTools_1[_i];
|
|
706
|
+
if (!toolFile.Tool) {
|
|
707
|
+
FlinkLog_1.log.error("Missing FlinkToolProps export in tool ".concat(toolFile.__file));
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
if (!toolFile.default) {
|
|
711
|
+
FlinkLog_1.log.error("Missing exported tool function in tool ".concat(toolFile.__file));
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
toolId = toolFile.Tool.id;
|
|
715
|
+
if (!toolId) {
|
|
716
|
+
FlinkLog_1.log.error("Tool ".concat(toolFile.__file, " missing 'id' property"));
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
toolInstanceName = getRepoInstanceName(toolId);
|
|
720
|
+
toolExecutor = new ToolExecutor(toolFile.Tool, toolFile.default, this.ctx);
|
|
721
|
+
this.tools[toolInstanceName] = toolExecutor;
|
|
722
|
+
FlinkLog_1.log.info("Registered tool ".concat(toolInstanceName, " (").concat(toolId, ")"));
|
|
723
|
+
}
|
|
724
|
+
return [2 /*return*/];
|
|
725
|
+
});
|
|
726
|
+
});
|
|
727
|
+
};
|
|
728
|
+
FlinkApp.prototype.registerAutoRegisterableAgents = function () {
|
|
729
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
730
|
+
var _a, getRepoInstanceName, toKebabCase, SubAgentExecutor, _loop_2, this_2, _i, autoRegisteredAgents_1, agentFile, _b, autoRegisteredAgents_2, agentFile, AgentClass, agentInstance, _c, _d, agentRef, subAgentInstanceName;
|
|
731
|
+
return __generator(this, function (_e) {
|
|
732
|
+
_a = require("./utils"), getRepoInstanceName = _a.getRepoInstanceName, toKebabCase = _a.toKebabCase;
|
|
733
|
+
SubAgentExecutor = require("./ai/SubAgentExecutor").SubAgentExecutor;
|
|
734
|
+
_loop_2 = function (agentFile) {
|
|
735
|
+
// agentFile now exports a class, not a config object
|
|
736
|
+
var AgentClass = agentFile.default;
|
|
737
|
+
if (!AgentClass) {
|
|
738
|
+
FlinkLog_1.log.error("Missing default export in agent ".concat(agentFile.__file));
|
|
739
|
+
return "continue";
|
|
740
|
+
}
|
|
741
|
+
// Instantiate agent (similar to repo instantiation)
|
|
742
|
+
var agentInstance = new AgentClass();
|
|
743
|
+
// Derive instance name from class name (camelCase)
|
|
744
|
+
var agentInstanceName = getRepoInstanceName(AgentClass.name);
|
|
745
|
+
// Get agent ID (kebab-case) - either explicit or derived
|
|
746
|
+
var agentId = agentInstance.id || toKebabCase(AgentClass.name);
|
|
747
|
+
// Check for duplicate instance name
|
|
748
|
+
if (this_2.agents[agentInstanceName]) {
|
|
749
|
+
var existingAgent = this_2.agents[agentInstanceName];
|
|
750
|
+
throw new Error("Duplicate agent instance name: \"".concat(agentInstanceName, "\". ") +
|
|
751
|
+
"Agent class \"".concat(AgentClass.name, "\" conflicts with existing agent \"").concat(existingAgent.constructor.name, "\". ") +
|
|
752
|
+
"Instance names are derived by lowercasing the first letter of the class name. " +
|
|
753
|
+
"Rename one of the classes or use a unique explicit 'id' property.");
|
|
754
|
+
}
|
|
755
|
+
// Check for duplicate agent ID
|
|
756
|
+
var existingAgentWithSameId = Object.values(this_2.agents).find(function (agent) {
|
|
757
|
+
var existingId = agent.id || toKebabCase(agent.constructor.name);
|
|
758
|
+
return existingId === agentId;
|
|
759
|
+
});
|
|
760
|
+
if (existingAgentWithSameId) {
|
|
761
|
+
throw new Error("Duplicate agent ID: \"".concat(agentId, "\". ") +
|
|
762
|
+
"Agent class \"".concat(AgentClass.name, "\" conflicts with existing agent \"").concat(existingAgentWithSameId.constructor.name, "\". ") +
|
|
763
|
+
"Agent IDs are derived from class names using kebab-case (e.g., CarAgent \u2192 car-agent). " +
|
|
764
|
+
"Use an explicit 'id' property to resolve this conflict:\n" +
|
|
765
|
+
" id = \"my-unique-id\";");
|
|
766
|
+
}
|
|
767
|
+
// Validate tools exist
|
|
768
|
+
for (var _f = 0, _g = agentInstance.tools; _f < _g.length; _f++) {
|
|
769
|
+
var toolRef = _g[_f];
|
|
770
|
+
// Handle both string IDs and tool file references
|
|
771
|
+
var toolId = typeof toolRef === "string"
|
|
772
|
+
? toolRef
|
|
773
|
+
: toolRef.Tool.id; // Extract ID from FlinkToolFile
|
|
774
|
+
var tool = this_2.tools[toolId];
|
|
775
|
+
if (!tool) {
|
|
776
|
+
FlinkLog_1.log.error("Agent ".concat(AgentClass.name, " references tool ").concat(toolId, " which is not registered"));
|
|
777
|
+
throw new Error("Invalid tool reference in agent ".concat(AgentClass.name));
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
// Validate and register sub-agents
|
|
781
|
+
if (agentInstance.agents && agentInstance.agents.length > 0) {
|
|
782
|
+
for (var _h = 0, _j = agentInstance.agents; _h < _j.length; _h++) {
|
|
783
|
+
var agentRef = _j[_h];
|
|
784
|
+
// Get instance name directly from class reference or string
|
|
785
|
+
var subAgentInstanceName = typeof agentRef === "string" ? agentRef : getRepoInstanceName(agentRef.name);
|
|
786
|
+
// Validate that sub-agent will exist (will be registered in this loop)
|
|
787
|
+
// For now, just log - actual validation happens at runtime
|
|
788
|
+
FlinkLog_1.log.debug("Agent ".concat(AgentClass.name, " references sub-agent ").concat(subAgentInstanceName));
|
|
789
|
+
// Create a SubAgentExecutor as a special tool
|
|
790
|
+
var subAgentToolName = "ask_".concat(subAgentInstanceName);
|
|
791
|
+
var subAgentExecutor = new SubAgentExecutor(subAgentInstanceName, this_2.ctx);
|
|
792
|
+
// Register as a tool so it appears in the agent's tool list
|
|
793
|
+
this_2.tools[subAgentToolName] = subAgentExecutor;
|
|
794
|
+
FlinkLog_1.log.debug("Created sub-agent tool ".concat(subAgentToolName, " for ").concat(subAgentInstanceName));
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
// Register agent (duplicate checks already performed above)
|
|
798
|
+
this_2.agents[agentInstanceName] = agentInstance;
|
|
799
|
+
FlinkLog_1.log.info("Registered agent ".concat(agentInstanceName, " (").concat(AgentClass.name, ") with ID: ").concat(agentId));
|
|
800
|
+
};
|
|
801
|
+
this_2 = this;
|
|
802
|
+
for (_i = 0, autoRegisteredAgents_1 = exports.autoRegisteredAgents; _i < autoRegisteredAgents_1.length; _i++) {
|
|
803
|
+
agentFile = autoRegisteredAgents_1[_i];
|
|
804
|
+
_loop_2(agentFile);
|
|
805
|
+
}
|
|
806
|
+
// Second pass: validate all sub-agent references
|
|
807
|
+
for (_b = 0, autoRegisteredAgents_2 = exports.autoRegisteredAgents; _b < autoRegisteredAgents_2.length; _b++) {
|
|
808
|
+
agentFile = autoRegisteredAgents_2[_b];
|
|
809
|
+
AgentClass = agentFile.default;
|
|
810
|
+
if (!AgentClass) {
|
|
811
|
+
continue;
|
|
812
|
+
}
|
|
813
|
+
agentInstance = new AgentClass();
|
|
814
|
+
if (agentInstance.agents && agentInstance.agents.length > 0) {
|
|
815
|
+
for (_c = 0, _d = agentInstance.agents; _c < _d.length; _c++) {
|
|
816
|
+
agentRef = _d[_c];
|
|
817
|
+
subAgentInstanceName = typeof agentRef === "string" ? agentRef : getRepoInstanceName(agentRef.name);
|
|
818
|
+
if (!this.agents[subAgentInstanceName]) {
|
|
819
|
+
FlinkLog_1.log.error("Agent ".concat(AgentClass.name, " references sub-agent ").concat(subAgentInstanceName, " which is not registered"));
|
|
820
|
+
throw new Error("Invalid sub-agent reference in agent ".concat(AgentClass.name, ": ").concat(subAgentInstanceName));
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
return [2 /*return*/];
|
|
826
|
+
});
|
|
827
|
+
});
|
|
828
|
+
};
|
|
604
829
|
/**
|
|
605
830
|
* Constructs the app context. Will inject context in all components
|
|
606
831
|
* except for handlers which are handled in later stage.
|
|
@@ -631,6 +856,7 @@ var FlinkApp = /** @class */ (function () {
|
|
|
631
856
|
repos: this.repos,
|
|
632
857
|
plugins: pluginCtx,
|
|
633
858
|
auth: this.auth,
|
|
859
|
+
agents: this.agents,
|
|
634
860
|
};
|
|
635
861
|
for (_b = 0, _c = Object.values(this.repos); _b < _c.length; _b++) {
|
|
636
862
|
repo = _c[_b];
|
|
@@ -640,6 +866,24 @@ var FlinkApp = /** @class */ (function () {
|
|
|
640
866
|
});
|
|
641
867
|
});
|
|
642
868
|
};
|
|
869
|
+
/**
|
|
870
|
+
* Initialize agents after they've been registered and context is ready
|
|
871
|
+
* Must be called after registerAutoRegisterableAgents()
|
|
872
|
+
*/
|
|
873
|
+
FlinkApp.prototype.initializeAgents = function () {
|
|
874
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
875
|
+
var _i, _a, agent;
|
|
876
|
+
return __generator(this, function (_b) {
|
|
877
|
+
// Inject context and initialize agents
|
|
878
|
+
for (_i = 0, _a = Object.values(this.agents); _i < _a.length; _i++) {
|
|
879
|
+
agent = _a[_i];
|
|
880
|
+
agent.ctx = this.ctx;
|
|
881
|
+
agent.__init(this.llmAdapters, this.tools);
|
|
882
|
+
}
|
|
883
|
+
return [2 /*return*/];
|
|
884
|
+
});
|
|
885
|
+
});
|
|
886
|
+
};
|
|
643
887
|
/**
|
|
644
888
|
* Connects to database.
|
|
645
889
|
*/
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FlinkAuthPlugin } from "./auth/FlinkAuthPlugin";
|
|
2
2
|
import { FlinkRepo } from "./FlinkRepo";
|
|
3
|
+
import { FlinkAgent } from "./ai/FlinkAgent";
|
|
3
4
|
export interface FlinkContext<P = any> {
|
|
4
5
|
repos: {
|
|
5
6
|
[x: string]: FlinkRepo<any, any>;
|
|
@@ -9,4 +10,24 @@ export interface FlinkContext<P = any> {
|
|
|
9
10
|
* Type of authentication, if any.
|
|
10
11
|
*/
|
|
11
12
|
auth?: FlinkAuthPlugin;
|
|
13
|
+
/**
|
|
14
|
+
* AI namespace containing agents
|
|
15
|
+
*
|
|
16
|
+
* Define agents directly in your context interface:
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* interface AppCtx extends FlinkContext<PluginCtx> {
|
|
20
|
+
* auth: JwtAuthPlugin;
|
|
21
|
+
* repos: {
|
|
22
|
+
* carRepo: CarRepo;
|
|
23
|
+
* };
|
|
24
|
+
* agents: {
|
|
25
|
+
* carAgent: CarAgent;
|
|
26
|
+
* userAgent: UserAgent;
|
|
27
|
+
* };
|
|
28
|
+
* }
|
|
29
|
+
*/
|
|
30
|
+
agents?: {
|
|
31
|
+
[x: string]: FlinkAgent<any>;
|
|
32
|
+
};
|
|
12
33
|
}
|