@cascadeflow/n8n-nodes-cascadeflow 0.7.7 → 1.0.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.
Files changed (33) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +271 -301
  3. package/package.json +12 -13
  4. package/dist/credentials/CascadeFlowApi.credentials.d.ts +0 -10
  5. package/dist/credentials/CascadeFlowApi.credentials.d.ts.map +0 -1
  6. package/dist/credentials/CascadeFlowApi.credentials.js +0 -73
  7. package/dist/credentials/CascadeFlowApi.credentials.js.map +0 -1
  8. package/dist/nodes/CascadeFlowAgent/CascadeFlowAgent.node.d.ts +0 -41
  9. package/dist/nodes/CascadeFlowAgent/CascadeFlowAgent.node.d.ts.map +0 -1
  10. package/dist/nodes/CascadeFlowAgent/CascadeFlowAgent.node.js +0 -720
  11. package/dist/nodes/CascadeFlowAgent/CascadeFlowAgent.node.js.map +0 -1
  12. package/dist/nodes/CascadeFlowAgent/__tests__/cascadeflow-agent-executor.test.d.ts +0 -2
  13. package/dist/nodes/CascadeFlowAgent/__tests__/cascadeflow-agent-executor.test.d.ts.map +0 -1
  14. package/dist/nodes/CascadeFlowAgent/__tests__/cascadeflow-agent-executor.test.js +0 -98
  15. package/dist/nodes/CascadeFlowAgent/__tests__/cascadeflow-agent-executor.test.js.map +0 -1
  16. package/dist/nodes/CascadeFlowAgent/cascadeflow.svg +0 -15
  17. package/dist/nodes/LmChatCascadeFlow/LmChatCascadeFlow.node.d.ts +0 -119
  18. package/dist/nodes/LmChatCascadeFlow/LmChatCascadeFlow.node.d.ts.map +0 -1
  19. package/dist/nodes/LmChatCascadeFlow/LmChatCascadeFlow.node.js +0 -1483
  20. package/dist/nodes/LmChatCascadeFlow/LmChatCascadeFlow.node.js.map +0 -1
  21. package/dist/nodes/LmChatCascadeFlow/__tests__/lm-chat-cascadeflow-node.test.d.ts +0 -2
  22. package/dist/nodes/LmChatCascadeFlow/__tests__/lm-chat-cascadeflow-node.test.d.ts.map +0 -1
  23. package/dist/nodes/LmChatCascadeFlow/__tests__/lm-chat-cascadeflow-node.test.js +0 -59
  24. package/dist/nodes/LmChatCascadeFlow/__tests__/lm-chat-cascadeflow-node.test.js.map +0 -1
  25. package/dist/nodes/LmChatCascadeFlow/cascade-metadata.d.ts +0 -27
  26. package/dist/nodes/LmChatCascadeFlow/cascade-metadata.d.ts.map +0 -1
  27. package/dist/nodes/LmChatCascadeFlow/cascade-metadata.js +0 -21
  28. package/dist/nodes/LmChatCascadeFlow/cascade-metadata.js.map +0 -1
  29. package/dist/nodes/LmChatCascadeFlow/cascadeflow.svg +0 -15
  30. package/dist/nodes/LmChatCascadeFlow/config.d.ts +0 -42
  31. package/dist/nodes/LmChatCascadeFlow/config.d.ts.map +0 -1
  32. package/dist/nodes/LmChatCascadeFlow/config.js +0 -87
  33. package/dist/nodes/LmChatCascadeFlow/config.js.map +0 -1
@@ -1,720 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CascadeFlowAgent = exports.CascadeFlowAgentExecutor = void 0;
4
- const n8n_workflow_1 = require("n8n-workflow");
5
- const messages_1 = require("@langchain/core/messages");
6
- const LmChatCascadeFlow_node_1 = require("../LmChatCascadeFlow/LmChatCascadeFlow.node");
7
- const config_1 = require("../LmChatCascadeFlow/config");
8
- // Tool cascade validator - optional import
9
- let ToolCascadeValidator;
10
- try {
11
- const cascadeCore = require('@cascadeflow/core');
12
- ToolCascadeValidator = cascadeCore.ToolCascadeValidator;
13
- }
14
- catch (e) {
15
- // @cascadeflow/core not available
16
- }
17
- class CascadeFlowAgentExecutor {
18
- constructor(cascadeModel, tools, routingRules, maxIterations, enableToolCascadeValidation = false) {
19
- this.cascadeModel = cascadeModel;
20
- this.maxIterations = maxIterations;
21
- this.toolMap = new Map(tools.filter((tool) => tool?.name).map((tool) => [tool.name, tool]));
22
- this.routingRules = new Map(routingRules.map((rule) => [rule.toolName, rule.routing]));
23
- this.enableToolCascadeValidation = enableToolCascadeValidation;
24
- if (enableToolCascadeValidation && ToolCascadeValidator) {
25
- try {
26
- this.toolCascadeValidator = new ToolCascadeValidator();
27
- }
28
- catch {
29
- this.toolCascadeValidator = null;
30
- }
31
- }
32
- else {
33
- this.toolCascadeValidator = null;
34
- }
35
- }
36
- normalizeMessages(input) {
37
- const isBaseMessage = (value) => {
38
- return (value &&
39
- typeof value === 'object' &&
40
- typeof value._getType === 'function' &&
41
- 'content' in value);
42
- };
43
- const coerceMessage = (value) => {
44
- if (isBaseMessage(value)) {
45
- return value;
46
- }
47
- if (typeof value === 'string') {
48
- return new messages_1.HumanMessage(value);
49
- }
50
- if (!value || typeof value !== 'object') {
51
- return new messages_1.HumanMessage(JSON.stringify(value ?? null));
52
- }
53
- const role = (value.role ?? value.type ?? '').toString().toLowerCase();
54
- const content = value.content ?? value.text ?? '';
55
- if (role === 'system') {
56
- return new messages_1.SystemMessage(content);
57
- }
58
- if (role === 'assistant' || role === 'ai') {
59
- return new messages_1.AIMessage(content);
60
- }
61
- if (role === 'tool') {
62
- const toolCallId = value.tool_call_id ??
63
- value.toolCallId ??
64
- value.tool_callId ??
65
- value.id ??
66
- 'tool';
67
- return new messages_1.ToolMessage({
68
- content: typeof content === 'string' ? content : JSON.stringify(content),
69
- tool_call_id: String(toolCallId),
70
- });
71
- }
72
- // Default: treat as user/human.
73
- return new messages_1.HumanMessage(content);
74
- };
75
- const normalizeList = (values) => values.map(coerceMessage);
76
- if (Array.isArray(input)) {
77
- return normalizeList(input);
78
- }
79
- if (typeof input === 'string') {
80
- return [new messages_1.HumanMessage(input)];
81
- }
82
- if (input?.messages && Array.isArray(input.messages)) {
83
- return normalizeList(input.messages);
84
- }
85
- if (input?.chatHistory && Array.isArray(input.chatHistory)) {
86
- const history = normalizeList(input.chatHistory);
87
- if (typeof input.input === 'string') {
88
- return [...history, new messages_1.HumanMessage(input.input)];
89
- }
90
- return history;
91
- }
92
- if (typeof input?.input === 'string') {
93
- return [new messages_1.HumanMessage(input.input)];
94
- }
95
- return [new messages_1.HumanMessage(JSON.stringify(input ?? null))];
96
- }
97
- extractToolCalls(message) {
98
- const additionalKwargs = message.additional_kwargs || {};
99
- const responseMetadata = message.response_metadata || {};
100
- const toolCalls = additionalKwargs.tool_calls || responseMetadata.tool_calls;
101
- if (Array.isArray(toolCalls)) {
102
- return toolCalls
103
- .map((toolCall) => {
104
- const args = toolCall?.function?.arguments ?? toolCall?.arguments ?? '{}';
105
- let parsedArgs = {};
106
- try {
107
- parsedArgs = typeof args === 'string' ? JSON.parse(args) : args;
108
- }
109
- catch {
110
- parsedArgs = { raw: args };
111
- }
112
- return {
113
- id: toolCall?.id ?? toolCall?.function?.name ?? `tool_${Date.now()}`,
114
- name: toolCall?.function?.name ?? toolCall?.name,
115
- args: parsedArgs,
116
- };
117
- })
118
- .filter((call) => Boolean(call.name));
119
- }
120
- if (additionalKwargs.function_call) {
121
- const functionCall = additionalKwargs.function_call;
122
- let parsedArgs = {};
123
- try {
124
- parsedArgs = functionCall.arguments ? JSON.parse(functionCall.arguments) : {};
125
- }
126
- catch {
127
- parsedArgs = { raw: functionCall.arguments };
128
- }
129
- return [
130
- {
131
- id: functionCall.name ?? `tool_${Date.now()}`,
132
- name: functionCall.name,
133
- args: parsedArgs,
134
- },
135
- ];
136
- }
137
- return [];
138
- }
139
- async executeTool(call) {
140
- const tool = this.toolMap.get(call.name);
141
- if (!tool) {
142
- return `Tool '${call.name}' not found`;
143
- }
144
- if (tool.invoke) {
145
- return JSON.stringify(await tool.invoke(call.args));
146
- }
147
- if (tool.call) {
148
- return JSON.stringify(await tool.call(call.args));
149
- }
150
- if (tool.run) {
151
- return JSON.stringify(await tool.run(call.args));
152
- }
153
- return `Tool '${call.name}' has no callable method`;
154
- }
155
- resolveRouting(toolCalls) {
156
- for (const call of toolCalls) {
157
- const routing = this.routingRules.get(call.name);
158
- if (routing === 'verifier') {
159
- return 'verifier';
160
- }
161
- }
162
- return 'cascade';
163
- }
164
- /**
165
- * Validate tool calls using ToolCascadeValidator.
166
- * Returns { valid, score, errors } or null if unavailable.
167
- */
168
- validateToolCalls(toolCalls) {
169
- if (!this.enableToolCascadeValidation || !this.toolCascadeValidator) {
170
- return null;
171
- }
172
- try {
173
- const normalized = toolCalls.map((tc) => ({
174
- id: tc.id,
175
- name: tc.name,
176
- arguments: tc.args,
177
- }));
178
- const result = this.toolCascadeValidator.validate(normalized, []);
179
- return {
180
- valid: result.valid,
181
- score: result.score,
182
- errors: result.errors || [],
183
- };
184
- }
185
- catch {
186
- return null;
187
- }
188
- }
189
- buildTraceEntry(message, toolCalls = []) {
190
- const responseMetadata = message.response_metadata || {};
191
- const cf = responseMetadata.cf || {};
192
- const cascadeflow = responseMetadata.cascadeflow || {};
193
- return {
194
- model_used: cf.model_used ?? cascadeflow.model_used ?? 'unknown',
195
- domain: cf.domain ?? cascadeflow.domain ?? null,
196
- confidence: cf.confidence ?? cascadeflow.confidence ?? null,
197
- complexity: cf.complexity ?? cascadeflow.complexity ?? null,
198
- costs: cf.costs ?? null,
199
- savings: cf.savings ?? null,
200
- flow: cascadeflow.flow ?? null,
201
- reason: cascadeflow.reason ?? null,
202
- tool_calls: toolCalls.map((call) => call.name),
203
- };
204
- }
205
- async invoke(input, options) {
206
- const messages = this.normalizeMessages(input);
207
- const trace = [];
208
- let currentMessages = [...messages];
209
- let finalMessage = null;
210
- let iterations = 0;
211
- while (iterations < this.maxIterations) {
212
- const message = await this.cascadeModel.invoke(currentMessages, options);
213
- const toolCalls = this.extractToolCalls(message);
214
- trace.push(this.buildTraceEntry(message, toolCalls));
215
- if (toolCalls.length === 0) {
216
- finalMessage = message;
217
- break;
218
- }
219
- // Validate tool calls if enabled
220
- const validation = this.validateToolCalls(toolCalls);
221
- if (validation && !validation.valid) {
222
- console.log(`🔧 Agent tool call validation failed (score: ${validation.score.toFixed(2)}), escalating to verifier`);
223
- const verifierMessage = await this.cascadeModel.invokeVerifierDirect(currentMessages, options);
224
- const verifierToolCalls = this.extractToolCalls(verifierMessage);
225
- trace.push({
226
- ...this.buildTraceEntry(verifierMessage, verifierToolCalls),
227
- tool_validation_failed: true,
228
- tool_validation_score: validation.score,
229
- tool_validation_errors: validation.errors,
230
- });
231
- if (verifierToolCalls.length === 0) {
232
- finalMessage = verifierMessage;
233
- break;
234
- }
235
- // Use verifier's tool calls instead
236
- currentMessages = [...currentMessages, verifierMessage];
237
- for (const call of verifierToolCalls) {
238
- const toolResult = await this.executeTool(call);
239
- currentMessages.push(new messages_1.ToolMessage({
240
- content: toolResult,
241
- tool_call_id: call.id,
242
- }));
243
- }
244
- iterations += 1;
245
- continue;
246
- }
247
- const routing = this.resolveRouting(toolCalls);
248
- currentMessages = [...currentMessages, message];
249
- for (const call of toolCalls) {
250
- const toolResult = await this.executeTool(call);
251
- currentMessages.push(new messages_1.ToolMessage({
252
- content: toolResult,
253
- tool_call_id: call.id,
254
- }));
255
- }
256
- if (routing === 'verifier') {
257
- const verifierMessage = await this.cascadeModel.invokeVerifierDirect(currentMessages, options);
258
- trace.push(this.buildTraceEntry(verifierMessage));
259
- finalMessage = verifierMessage;
260
- break;
261
- }
262
- iterations += 1;
263
- }
264
- if (!finalMessage) {
265
- finalMessage = new messages_1.AIMessage('Agent did not produce a final response.');
266
- }
267
- if (!finalMessage.response_metadata) {
268
- finalMessage.response_metadata = {};
269
- }
270
- finalMessage.response_metadata.cf = {
271
- ...(finalMessage.response_metadata.cf || {}),
272
- trace,
273
- };
274
- return {
275
- output: finalMessage.content.toString(),
276
- message: finalMessage,
277
- trace,
278
- };
279
- }
280
- async *stream(input, options) {
281
- const messages = this.normalizeMessages(input);
282
- // For tool-driven execution we currently return a single final response.
283
- if (this.toolMap.size > 0) {
284
- yield await this.invoke(messages, options);
285
- return;
286
- }
287
- const stream = await this.cascadeModel.stream(messages, options);
288
- for await (const chunk of stream) {
289
- yield chunk;
290
- }
291
- }
292
- }
293
- exports.CascadeFlowAgentExecutor = CascadeFlowAgentExecutor;
294
- // =============================================================================
295
- // Generate domain toggle properties for n8n UI (reused from LmChatCascadeFlow)
296
- // =============================================================================
297
- function generateDomainProperties() {
298
- const domainOptions = Object.entries(config_1.DOMAINS).map(([_key, value]) => ({
299
- name: config_1.DOMAIN_DISPLAY_NAMES[value],
300
- value: value,
301
- description: config_1.DOMAIN_DESCRIPTIONS[value],
302
- }));
303
- const domainToggleProperties = [];
304
- for (const { domain, toggleName } of config_1.DOMAIN_UI_CONFIGS) {
305
- const displayName = config_1.DOMAIN_DISPLAY_NAMES[domain];
306
- domainToggleProperties.push({
307
- displayName: `Enable ${displayName} Domain`,
308
- name: toggleName,
309
- type: 'boolean',
310
- default: false,
311
- displayOptions: { show: { enableDomainRouting: [true] } },
312
- description: `Whether to enable ${config_1.DOMAIN_DESCRIPTIONS[domain]}. When enabled, adds a "${displayName}" input port.`,
313
- });
314
- }
315
- return [
316
- {
317
- displayName: 'Enable Domain Routing',
318
- name: 'enableDomainRouting',
319
- type: 'boolean',
320
- default: false,
321
- description: 'Whether to enable intelligent routing based on detected query domain (math, code, legal, etc.)',
322
- },
323
- {
324
- displayName: 'Enable Domain Verifiers',
325
- name: 'enableDomainVerifiers',
326
- type: 'boolean',
327
- default: false,
328
- displayOptions: { show: { enableDomainRouting: [true] } },
329
- description: 'Whether to add a domain-specific verifier port for each enabled domain. Connect a model to override the global verifier for that domain.',
330
- },
331
- {
332
- displayName: 'Enable the domains you want to route to. Each enabled domain adds a model input port on the node.',
333
- name: 'domainsNotice',
334
- type: 'notice',
335
- default: '',
336
- displayOptions: { show: { enableDomainRouting: [true] } },
337
- },
338
- ...domainToggleProperties,
339
- {
340
- displayName: 'Domain-Specific Settings',
341
- name: 'domainSettings',
342
- type: 'fixedCollection',
343
- typeOptions: {
344
- multipleValues: true,
345
- },
346
- displayOptions: {
347
- show: {
348
- enableDomainRouting: [true],
349
- },
350
- },
351
- default: {},
352
- options: [
353
- {
354
- name: 'domainConfig',
355
- displayName: 'Domain Configuration',
356
- values: [
357
- {
358
- displayName: 'Domain',
359
- name: 'domain',
360
- type: 'options',
361
- options: domainOptions,
362
- default: 'general',
363
- description: 'Select the domain to configure',
364
- },
365
- {
366
- displayName: 'Quality Threshold',
367
- name: 'threshold',
368
- type: 'number',
369
- default: 0.4,
370
- typeOptions: {
371
- minValue: 0,
372
- maxValue: 1,
373
- numberPrecision: 2,
374
- },
375
- description: 'Quality threshold for this domain (overrides global threshold)',
376
- },
377
- {
378
- displayName: 'Temperature',
379
- name: 'temperature',
380
- type: 'number',
381
- default: 0.7,
382
- typeOptions: {
383
- minValue: 0,
384
- maxValue: 2,
385
- numberPrecision: 1,
386
- },
387
- description: 'Temperature setting for this domain',
388
- },
389
- ],
390
- },
391
- ],
392
- description: 'Configure per-domain quality thresholds and temperatures',
393
- },
394
- ];
395
- }
396
- class CascadeFlowAgent {
397
- constructor() {
398
- this.description = {
399
- displayName: 'CascadeFlow Agent',
400
- name: 'cascadeFlowAgent',
401
- icon: 'file:cascadeflow.svg',
402
- group: ['transform'],
403
- version: 2,
404
- description: 'CascadeFlow AI Agent with drafter/verifier orchestration, tool routing, domain routing, and trace metadata.',
405
- defaults: {
406
- name: 'CascadeFlow Agent',
407
- },
408
- codex: {
409
- categories: ['AI'],
410
- subcategories: {
411
- AI: ['Agents'],
412
- },
413
- resources: {
414
- primaryDocumentation: [
415
- {
416
- url: 'https://github.com/lemony-ai/cascadeflow',
417
- },
418
- ],
419
- },
420
- },
421
- // eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
422
- inputs: `={{ ((params) => {
423
- const inputs = [
424
- { displayName: 'Verifier', type: 'ai_languageModel', maxConnections: 1, required: true },
425
- { displayName: 'Drafter', type: 'ai_languageModel', maxConnections: 1, required: true },
426
- { displayName: 'Tools', type: 'ai_tool', maxConnections: 99, required: false },
427
- ];
428
-
429
- if (params?.enableDomainRouting) {
430
- const dv = !!params?.enableDomainVerifiers;
431
- const domains = [
432
- { t: 'enableCodeDomain', l: 'Code' },
433
- { t: 'enableDataDomain', l: 'Data' },
434
- { t: 'enableStructuredDomain', l: 'Struct.' },
435
- { t: 'enableRagDomain', l: 'RAG' },
436
- { t: 'enableConversationDomain', l: 'Conv.' },
437
- { t: 'enableToolDomain', l: 'Tool' },
438
- { t: 'enableCreativeDomain', l: 'Creative' },
439
- { t: 'enableSummaryDomain', l: 'Summary' },
440
- { t: 'enableTranslationDomain', l: 'Transl.' },
441
- { t: 'enableMathDomain', l: 'Math' },
442
- { t: 'enableScienceDomain', l: 'Science' },
443
- { t: 'enableMedicalDomain', l: 'Medical' },
444
- { t: 'enableLegalDomain', l: 'Legal' },
445
- { t: 'enableFinancialDomain', l: 'Finance' },
446
- { t: 'enableMultimodalDomain', l: 'Multi.' },
447
- { t: 'enableGeneralDomain', l: 'General' },
448
- ];
449
- for (const d of domains) {
450
- if (params?.[d.t]) {
451
- inputs.push({ displayName: d.l, type: 'ai_languageModel', maxConnections: 1, required: false });
452
- if (dv) {
453
- inputs.push({ displayName: d.l + ' V.', type: 'ai_languageModel', maxConnections: 1, required: false });
454
- }
455
- }
456
- }
457
- }
458
-
459
- return inputs;
460
- })($parameter) }}`,
461
- // eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong
462
- outputs: ['ai_agent'],
463
- outputNames: ['Agent'],
464
- properties: [
465
- {
466
- displayName: 'Quality Threshold',
467
- name: 'qualityThreshold',
468
- type: 'number',
469
- default: 0.4,
470
- typeOptions: {
471
- minValue: 0,
472
- maxValue: 1,
473
- numberPrecision: 2,
474
- },
475
- description: 'Minimum quality score (0-1) to accept drafter response',
476
- },
477
- {
478
- displayName: 'Use Complexity Thresholds',
479
- name: 'useComplexityThresholds',
480
- type: 'boolean',
481
- default: true,
482
- description: 'Whether to use per-complexity confidence thresholds (trivial → expert)',
483
- },
484
- {
485
- displayName: 'Trivial Threshold',
486
- name: 'trivialThreshold',
487
- type: 'number',
488
- default: 0.25,
489
- displayOptions: { show: { useComplexityThresholds: [true] } },
490
- typeOptions: {
491
- minValue: 0,
492
- maxValue: 1,
493
- numberPrecision: 2,
494
- },
495
- description: 'Minimum confidence for trivial queries',
496
- },
497
- {
498
- displayName: 'Simple Threshold',
499
- name: 'simpleThreshold',
500
- type: 'number',
501
- default: 0.4,
502
- displayOptions: { show: { useComplexityThresholds: [true] } },
503
- typeOptions: {
504
- minValue: 0,
505
- maxValue: 1,
506
- numberPrecision: 2,
507
- },
508
- description: 'Minimum confidence for simple queries',
509
- },
510
- {
511
- displayName: 'Moderate Threshold',
512
- name: 'moderateThreshold',
513
- type: 'number',
514
- default: 0.55,
515
- displayOptions: { show: { useComplexityThresholds: [true] } },
516
- typeOptions: {
517
- minValue: 0,
518
- maxValue: 1,
519
- numberPrecision: 2,
520
- },
521
- description: 'Minimum confidence for moderate queries',
522
- },
523
- {
524
- displayName: 'Hard Threshold',
525
- name: 'hardThreshold',
526
- type: 'number',
527
- default: 0.7,
528
- displayOptions: { show: { useComplexityThresholds: [true] } },
529
- typeOptions: {
530
- minValue: 0,
531
- maxValue: 1,
532
- numberPrecision: 2,
533
- },
534
- description: 'Minimum confidence for hard queries',
535
- },
536
- {
537
- displayName: 'Expert Threshold',
538
- name: 'expertThreshold',
539
- type: 'number',
540
- default: 0.8,
541
- displayOptions: { show: { useComplexityThresholds: [true] } },
542
- typeOptions: {
543
- minValue: 0,
544
- maxValue: 1,
545
- numberPrecision: 2,
546
- },
547
- description: 'Minimum confidence for expert queries',
548
- },
549
- {
550
- displayName: 'Enable Alignment Scoring',
551
- name: 'useAlignmentScoring',
552
- type: 'boolean',
553
- default: true,
554
- description: 'Whether to score query-response alignment for improved validation accuracy',
555
- },
556
- {
557
- displayName: 'Enable Complexity Routing',
558
- name: 'useComplexityRouting',
559
- type: 'boolean',
560
- default: true,
561
- description: 'Whether to route complex queries directly to the verifier',
562
- },
563
- {
564
- displayName: 'Enable Tool Call Validation',
565
- name: 'enableToolCascadeValidation',
566
- type: 'boolean',
567
- default: true,
568
- description: 'Whether to validate drafter tool calls (JSON syntax, schema, safety) before executing them. When validation fails, tool calls are re-generated by the verifier.',
569
- },
570
- {
571
- displayName: 'Max Tool Iterations',
572
- name: 'maxIterations',
573
- type: 'number',
574
- default: 3,
575
- typeOptions: {
576
- minValue: 1,
577
- maxValue: 10,
578
- },
579
- description: 'Maximum number of tool-call iterations before returning',
580
- },
581
- {
582
- displayName: 'Tool Routing Rules',
583
- name: 'toolRoutingRules',
584
- type: 'fixedCollection',
585
- typeOptions: {
586
- multipleValues: true,
587
- },
588
- default: {},
589
- options: [
590
- {
591
- name: 'rule',
592
- displayName: 'Routing Rule',
593
- values: [
594
- {
595
- displayName: 'Tool Name',
596
- name: 'toolName',
597
- type: 'string',
598
- default: '',
599
- },
600
- {
601
- displayName: 'Routing',
602
- name: 'routing',
603
- type: 'options',
604
- options: [
605
- {
606
- name: 'Cascade (Default)',
607
- value: 'cascade',
608
- },
609
- {
610
- name: 'Verifier',
611
- value: 'verifier',
612
- },
613
- ],
614
- default: 'cascade',
615
- },
616
- ],
617
- },
618
- ],
619
- description: 'Override routing for specific tools (e.g., force verifier after tool call)',
620
- },
621
- {
622
- displayName: 'Domain Routing',
623
- name: 'domainRoutingHeading',
624
- type: 'notice',
625
- default: '',
626
- },
627
- ...generateDomainProperties(),
628
- ],
629
- };
630
- }
631
- async supplyData() {
632
- const qualityThreshold = this.getNodeParameter('qualityThreshold', 0, 0.4);
633
- const useAlignmentScoring = this.getNodeParameter('useAlignmentScoring', 0, true);
634
- const useComplexityRouting = this.getNodeParameter('useComplexityRouting', 0, true);
635
- const useComplexityThresholds = this.getNodeParameter('useComplexityThresholds', 0, true);
636
- const enableToolCascadeValidation = this.getNodeParameter('enableToolCascadeValidation', 0, false);
637
- const maxIterations = this.getNodeParameter('maxIterations', 0, 3);
638
- const confidenceThresholds = useComplexityThresholds
639
- ? {
640
- trivial: this.getNodeParameter('trivialThreshold', 0, config_1.DEFAULT_COMPLEXITY_THRESHOLDS.trivial),
641
- simple: this.getNodeParameter('simpleThreshold', 0, config_1.DEFAULT_COMPLEXITY_THRESHOLDS.simple),
642
- moderate: this.getNodeParameter('moderateThreshold', 0, config_1.DEFAULT_COMPLEXITY_THRESHOLDS.moderate),
643
- hard: this.getNodeParameter('hardThreshold', 0, config_1.DEFAULT_COMPLEXITY_THRESHOLDS.hard),
644
- expert: this.getNodeParameter('expertThreshold', 0, config_1.DEFAULT_COMPLEXITY_THRESHOLDS.expert),
645
- }
646
- : undefined;
647
- const toolRoutingRaw = this.getNodeParameter('toolRoutingRules', 0, { rule: [] });
648
- const toolRoutingRules = (toolRoutingRaw?.rule ?? []);
649
- // Get domain routing parameters
650
- const enableDomainRouting = this.getNodeParameter('enableDomainRouting', 0, false);
651
- const enabledDomains = [];
652
- if (enableDomainRouting) {
653
- const toggleParams = {};
654
- for (const { toggleName } of config_1.DOMAIN_UI_CONFIGS) {
655
- toggleParams[toggleName] = this.getNodeParameter(toggleName, 0, false);
656
- }
657
- enabledDomains.push(...(0, config_1.getEnabledDomains)(toggleParams));
658
- }
659
- // Get domain-specific settings
660
- const domainSettingsRaw = this.getNodeParameter('domainSettings', 0, { domainConfig: [] });
661
- const domainConfigs = new Map();
662
- if (domainSettingsRaw?.domainConfig) {
663
- for (const config of domainSettingsRaw.domainConfig) {
664
- domainConfigs.set(config.domain, {
665
- enabled: enabledDomains.includes(config.domain),
666
- threshold: config.threshold || qualityThreshold,
667
- temperature: config.temperature || 0.7,
668
- });
669
- }
670
- }
671
- // Resolve ALL connected language models in a single getInputConnectionData call.
672
- // n8n resolves ALL sub-nodes of the given connectionType (the 2nd param is a
673
- // data-item index, NOT a port selector). The returned array is in reversed slot
674
- // order due to internal unshift, so we reverse it back. This matches the
675
- // built-in AI Agent node pattern (getChatModel in ToolsAgent/common.ts).
676
- const allModelData = await this.getInputConnectionData('ai_languageModel', 0);
677
- const allModels = Array.isArray(allModelData)
678
- ? [...allModelData].reverse()
679
- : [allModelData];
680
- // Port order after reverse: 0=Verifier, 1=Drafter, 2+=domain models/verifiers
681
- const resolvedVerifier = allModels[0];
682
- if (!resolvedVerifier) {
683
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Verifier model is required. Please connect your VERIFIER model to the Verifier port.');
684
- }
685
- const verifierModelGetter = async () => resolvedVerifier;
686
- const resolvedDrafter = allModels[1];
687
- if (!resolvedDrafter) {
688
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Drafter model is required. Please connect your DRAFTER model to the Drafter port.');
689
- }
690
- const drafterModelGetter = async () => resolvedDrafter;
691
- // Tools use a separate connectionType so they have their own single call
692
- const toolsData = await this.getInputConnectionData('ai_tool', 0).catch(() => []);
693
- const tools = (Array.isArray(toolsData) ? toolsData : toolsData ? [toolsData] : []);
694
- // Domain models and domain verifiers occupy indices 2+ in slot definition order
695
- const domainModelGetters = new Map();
696
- const domainVerifierGetters = new Map();
697
- const enableDomainVerifiers = this.getNodeParameter('enableDomainVerifiers', 0, false);
698
- let nextModelIndex = 2; // After Verifier (0) and Drafter (1)
699
- for (const { domain } of config_1.DOMAIN_UI_CONFIGS) {
700
- if (!enabledDomains.includes(domain))
701
- continue;
702
- const model = allModels[nextModelIndex++];
703
- domainModelGetters.set(domain, async () => model || undefined);
704
- if (enableDomainVerifiers) {
705
- const verifierModel = allModels[nextModelIndex++];
706
- domainVerifierGetters.set(domain, async () => verifierModel || undefined);
707
- }
708
- }
709
- // Keep semantic validation off in n8n to avoid OOM / heavy model loads.
710
- const useSemanticValidation = false;
711
- const cascadeModel = new LmChatCascadeFlow_node_1.CascadeChatModel(drafterModelGetter, verifierModelGetter, qualityThreshold, useSemanticValidation, useAlignmentScoring, useComplexityRouting, useComplexityThresholds, enableDomainRouting, enabledDomains, domainModelGetters, domainConfigs, confidenceThresholds, false, // enableToolCallValidation handled by the agent executor
712
- domainVerifierGetters);
713
- const agentExecutor = new CascadeFlowAgentExecutor(cascadeModel, tools, toolRoutingRules, maxIterations, enableToolCascadeValidation);
714
- return {
715
- response: agentExecutor,
716
- };
717
- }
718
- }
719
- exports.CascadeFlowAgent = CascadeFlowAgent;
720
- //# sourceMappingURL=CascadeFlowAgent.node.js.map