@lhi/n8m 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/bin/dev.js +8 -0
- package/bin/run.js +7 -1
- package/dist/agentic/graph.d.ts +32 -8
- package/dist/agentic/nodes/architect.d.ts +15 -3
- package/dist/agentic/nodes/architect.js +11 -2
- package/dist/agentic/nodes/engineer.d.ts +1 -1
- package/dist/agentic/nodes/engineer.js +16 -9
- package/dist/agentic/nodes/qa.js +16 -4
- package/dist/agentic/nodes/reviewer.js +5 -3
- package/dist/resources/node-definitions-fallback.json +213 -0
- package/dist/services/ai.service.d.ts +33 -50
- package/dist/services/ai.service.js +184 -426
- package/dist/services/node-definitions.service.d.ts +5 -0
- package/dist/services/node-definitions.service.js +64 -8
- package/dist/utils/n8nClient.d.ts +10 -10
- package/dist/utils/n8nClient.js +80 -145
- package/oclif.manifest.json +1 -1
- package/package.json +6 -4
package/README.md
CHANGED
package/bin/dev.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node --loader ts-node/esm --no-warnings=ExperimentalWarning
|
|
2
2
|
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
import { config } from 'dotenv';
|
|
6
|
+
|
|
7
|
+
// Load .env from the package root regardless of the user's cwd
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
config({ path: join(__dirname, '..', '.env') });
|
|
10
|
+
|
|
3
11
|
import {execute} from '@oclif/core'
|
|
4
12
|
|
|
5
13
|
await execute({development: true, dir: import.meta.url})
|
package/bin/run.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import '
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import { dirname, join } from 'path';
|
|
4
|
+
import { config } from 'dotenv';
|
|
5
|
+
|
|
6
|
+
// Load .env from the package root regardless of the user's cwd
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
config({ path: join(__dirname, '..', '.env') });
|
|
3
9
|
|
|
4
10
|
import {execute} from '@oclif/core'
|
|
5
11
|
|
package/dist/agentic/graph.d.ts
CHANGED
|
@@ -129,9 +129,21 @@ export declare const graph: import("@langchain/langgraph").CompiledStateGraph<{
|
|
|
129
129
|
collaborationLog: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
|
|
130
130
|
}, import("@langchain/langgraph").StateDefinition, {
|
|
131
131
|
architect: {
|
|
132
|
-
spec:
|
|
133
|
-
strategies:
|
|
134
|
-
|
|
132
|
+
spec: import("../services/ai.service.js").WorkflowSpec;
|
|
133
|
+
strategies: {
|
|
134
|
+
strategyName: string;
|
|
135
|
+
aiModel: string;
|
|
136
|
+
suggestedName: string;
|
|
137
|
+
description: string;
|
|
138
|
+
nodes: {
|
|
139
|
+
type: string;
|
|
140
|
+
purpose: string;
|
|
141
|
+
config?: any;
|
|
142
|
+
}[];
|
|
143
|
+
questions?: string[];
|
|
144
|
+
aiProvider?: string;
|
|
145
|
+
}[];
|
|
146
|
+
needsClarification: boolean | undefined;
|
|
135
147
|
collaborationLog: string[];
|
|
136
148
|
};
|
|
137
149
|
engineer: {
|
|
@@ -141,8 +153,8 @@ export declare const graph: import("@langchain/langgraph").CompiledStateGraph<{
|
|
|
141
153
|
workflowJson?: undefined;
|
|
142
154
|
candidates?: undefined;
|
|
143
155
|
} | {
|
|
144
|
-
workflowJson: any;
|
|
145
156
|
candidates: any[];
|
|
157
|
+
workflowJson?: undefined;
|
|
146
158
|
};
|
|
147
159
|
reviewer: import("@langchain/langgraph").UpdateType<{
|
|
148
160
|
userGoal: {
|
|
@@ -318,9 +330,21 @@ export declare const runAgenticWorkflow: (goal: string, initialState?: Partial<t
|
|
|
318
330
|
*/
|
|
319
331
|
export declare const runAgenticWorkflowStream: (goal: string, threadId?: string) => Promise<import("@langchain/core/utils/stream").IterableReadableStream<{
|
|
320
332
|
architect?: {
|
|
321
|
-
spec:
|
|
322
|
-
strategies:
|
|
323
|
-
|
|
333
|
+
spec: import("../services/ai.service.js").WorkflowSpec;
|
|
334
|
+
strategies: {
|
|
335
|
+
strategyName: string;
|
|
336
|
+
aiModel: string;
|
|
337
|
+
suggestedName: string;
|
|
338
|
+
description: string;
|
|
339
|
+
nodes: {
|
|
340
|
+
type: string;
|
|
341
|
+
purpose: string;
|
|
342
|
+
config?: any;
|
|
343
|
+
}[];
|
|
344
|
+
questions?: string[];
|
|
345
|
+
aiProvider?: string;
|
|
346
|
+
}[];
|
|
347
|
+
needsClarification: boolean | undefined;
|
|
324
348
|
collaborationLog: string[];
|
|
325
349
|
} | undefined;
|
|
326
350
|
engineer?: {
|
|
@@ -330,8 +354,8 @@ export declare const runAgenticWorkflowStream: (goal: string, threadId?: string)
|
|
|
330
354
|
workflowJson?: undefined;
|
|
331
355
|
candidates?: undefined;
|
|
332
356
|
} | {
|
|
333
|
-
workflowJson: any;
|
|
334
357
|
candidates: any[];
|
|
358
|
+
workflowJson?: undefined;
|
|
335
359
|
} | undefined;
|
|
336
360
|
reviewer?: import("@langchain/langgraph").UpdateType<{
|
|
337
361
|
userGoal: {
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import { TeamState } from "../state.js";
|
|
2
2
|
export declare const architectNode: (state: typeof TeamState.State) => Promise<{
|
|
3
|
-
spec:
|
|
4
|
-
strategies:
|
|
5
|
-
|
|
3
|
+
spec: import("../../services/ai.service.js").WorkflowSpec;
|
|
4
|
+
strategies: {
|
|
5
|
+
strategyName: string;
|
|
6
|
+
aiModel: string;
|
|
7
|
+
suggestedName: string;
|
|
8
|
+
description: string;
|
|
9
|
+
nodes: {
|
|
10
|
+
type: string;
|
|
11
|
+
purpose: string;
|
|
12
|
+
config?: any;
|
|
13
|
+
}[];
|
|
14
|
+
questions?: string[];
|
|
15
|
+
aiProvider?: string;
|
|
16
|
+
}[];
|
|
17
|
+
needsClarification: boolean | undefined;
|
|
6
18
|
collaborationLog: string[];
|
|
7
19
|
}>;
|
|
@@ -33,9 +33,18 @@ export const architectNode = async (state) => {
|
|
|
33
33
|
// Multi-agent collaboration: generate an alternative strategy in parallel with the primary.
|
|
34
34
|
// Both are handed off to separate Engineer agents that run concurrently.
|
|
35
35
|
const alternativeSpec = await aiService.generateAlternativeSpec(state.userGoal, spec);
|
|
36
|
+
const alternativeModel = aiService.getAlternativeModel();
|
|
36
37
|
const strategies = [
|
|
37
|
-
{
|
|
38
|
-
|
|
38
|
+
{
|
|
39
|
+
...spec,
|
|
40
|
+
strategyName: "Primary Strategy",
|
|
41
|
+
aiModel: aiService.getDefaultModel()
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
...alternativeSpec,
|
|
45
|
+
strategyName: "Alternative Strategy",
|
|
46
|
+
aiModel: alternativeModel
|
|
47
|
+
},
|
|
39
48
|
];
|
|
40
49
|
const logEntry = `Architect: Generated 2 strategies — "${strategies[0].suggestedName}" (primary) and "${strategies[1].suggestedName}" (alternative)`;
|
|
41
50
|
console.log(`[Architect] ${logEntry}`);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AIService } from "../../services/ai.service.js";
|
|
2
2
|
import { NodeDefinitionsService } from "../../services/node-definitions.service.js";
|
|
3
|
+
import { jsonrepair } from "jsonrepair";
|
|
3
4
|
export const engineerNode = async (state) => {
|
|
4
5
|
const aiService = AIService.getInstance();
|
|
5
6
|
// RAG: Load and Search Node Definitions
|
|
@@ -9,8 +10,9 @@ export const engineerNode = async (state) => {
|
|
|
9
10
|
const queryText = (state.userGoal + (state.spec ? ` ${state.spec.suggestedName} ${state.spec.description}` : "")).replace(/\n/g, " ");
|
|
10
11
|
// Search for relevant nodes (limit 8 to save context)
|
|
11
12
|
const relevantDefs = nodeService.search(queryText, 8);
|
|
12
|
-
const
|
|
13
|
-
|
|
13
|
+
const staticRef = nodeService.getStaticReference();
|
|
14
|
+
const ragContext = (relevantDefs.length > 0 || staticRef)
|
|
15
|
+
? `\n\n[N8N NODE REFERENCE GUIDE]\n${staticRef}\n\n[AVAILABLE NODE SCHEMAS - USE THESE EXACT PARAMETERS]\n${nodeService.formatForLLM(relevantDefs)}`
|
|
14
16
|
: "";
|
|
15
17
|
if (relevantDefs.length > 0) {
|
|
16
18
|
console.log(`[Engineer] RAG: Found ${relevantDefs.length} relevant node schemas.`);
|
|
@@ -22,7 +24,8 @@ export const engineerNode = async (state) => {
|
|
|
22
24
|
// We pass the entire list of errors as context
|
|
23
25
|
const errorContext = state.validationErrors.join('\n');
|
|
24
26
|
// Use the robust fix logic from AIService
|
|
25
|
-
const fixedWorkflow = await aiService.generateWorkflowFix(state.workflowJson, errorContext,
|
|
27
|
+
const fixedWorkflow = await aiService.generateWorkflowFix(state.workflowJson, errorContext, state.spec?.aiModel, // Pass model if available
|
|
28
|
+
false, state.availableNodeTypes || []);
|
|
26
29
|
return {
|
|
27
30
|
workflowJson: fixedWorkflow,
|
|
28
31
|
// validationErrors will be overwritten by next QA run
|
|
@@ -74,23 +77,27 @@ export const engineerNode = async (state) => {
|
|
|
74
77
|
Output ONLY valid JSON. No commentary. No markdown.
|
|
75
78
|
`;
|
|
76
79
|
// Using AIService just for the LLM call to keep auth logic dry
|
|
77
|
-
const response = await aiService.generateContent(prompt
|
|
80
|
+
const response = await aiService.generateContent(prompt, {
|
|
81
|
+
provider: state.spec.aiProvider,
|
|
82
|
+
model: state.spec.aiModel
|
|
83
|
+
});
|
|
78
84
|
let cleanJson = response || "{}";
|
|
79
85
|
cleanJson = cleanJson.replace(/```json\n?|\n?```/g, "").trim();
|
|
80
86
|
let result;
|
|
81
87
|
try {
|
|
82
|
-
result = JSON.parse(cleanJson);
|
|
88
|
+
result = JSON.parse(jsonrepair(cleanJson));
|
|
83
89
|
}
|
|
84
|
-
catch (
|
|
85
|
-
console.error("Failed to parse workflow JSON from spec",
|
|
90
|
+
catch (e2) {
|
|
91
|
+
console.error("Failed to parse workflow JSON from spec", e2);
|
|
86
92
|
throw new Error("AI generated invalid JSON for workflow from spec");
|
|
87
93
|
}
|
|
88
94
|
if (result.workflows && Array.isArray(result.workflows)) {
|
|
89
95
|
result.workflows = result.workflows.map((wf) => fixHallucinatedNodes(wf));
|
|
90
96
|
}
|
|
91
97
|
return {
|
|
92
|
-
workflowJson
|
|
93
|
-
//
|
|
98
|
+
// Only push to candidates — the Supervisor sets workflowJson after fan-in.
|
|
99
|
+
// Writing workflowJson here would cause a LastValue conflict when two
|
|
100
|
+
// Engineers run in parallel via Send().
|
|
94
101
|
candidates: [result],
|
|
95
102
|
};
|
|
96
103
|
}
|
package/dist/agentic/nodes/qa.js
CHANGED
|
@@ -10,8 +10,9 @@ export const qaNode = async (state) => {
|
|
|
10
10
|
}
|
|
11
11
|
// 1. Load Credentials
|
|
12
12
|
const config = await ConfigManager.load();
|
|
13
|
-
|
|
14
|
-
const
|
|
13
|
+
// Env vars take priority over stored config so a fresh key in .env is always used
|
|
14
|
+
const n8nUrl = process.env.N8N_API_URL || config.n8nUrl;
|
|
15
|
+
const n8nKey = process.env.N8N_API_KEY || config.n8nKey;
|
|
15
16
|
if (!n8nUrl || !n8nKey) {
|
|
16
17
|
throw new Error('Credentials missing. Configure environment via \'n8m config\'.');
|
|
17
18
|
}
|
|
@@ -28,10 +29,21 @@ export const qaNode = async (state) => {
|
|
|
28
29
|
targetWorkflow = workflowJson.workflows[0];
|
|
29
30
|
}
|
|
30
31
|
const workflowName = targetWorkflow.name || 'Agentic_Test_Workflow';
|
|
32
|
+
// Drop timezone — sanitizeSettings in N8nClient strips it unconditionally
|
|
33
|
+
const rawSettings = { ...(targetWorkflow.settings || {}) };
|
|
34
|
+
delete rawSettings.timezone;
|
|
35
|
+
// Strip credentials from all nodes — n8n 2.x refuses to activate ("publish")
|
|
36
|
+
// a workflow that references credentials that don't exist on the instance.
|
|
37
|
+
// Structural validation can still run; only live execution of credentialed
|
|
38
|
+
// nodes will be skipped/fail, which is expected for an ephemeral test.
|
|
39
|
+
const strippedNodes = targetWorkflow.nodes.map((node) => {
|
|
40
|
+
const { credentials: _creds, ...rest } = node;
|
|
41
|
+
return rest;
|
|
42
|
+
});
|
|
31
43
|
const rootPayload = {
|
|
32
|
-
nodes:
|
|
44
|
+
nodes: strippedNodes,
|
|
33
45
|
connections: targetWorkflow.connections,
|
|
34
|
-
settings:
|
|
46
|
+
settings: rawSettings,
|
|
35
47
|
staticData: targetWorkflow.staticData || {},
|
|
36
48
|
name: `[n8m:test] ${workflowName}`,
|
|
37
49
|
};
|
|
@@ -66,6 +66,8 @@ export const reviewerNode = async (state) => {
|
|
|
66
66
|
// Triggers usually have no input.
|
|
67
67
|
// We use a broader check: any node with "trigger" or "webhook" in the name, plus generic start types.
|
|
68
68
|
const isTrigger = (type) => {
|
|
69
|
+
if (!type)
|
|
70
|
+
return false;
|
|
69
71
|
const lower = type.toLowerCase();
|
|
70
72
|
return lower.includes('trigger') ||
|
|
71
73
|
lower.includes('webhook') ||
|
|
@@ -79,9 +81,9 @@ export const reviewerNode = async (state) => {
|
|
|
79
81
|
// Sticky notes and Merge nodes can be tricky, but generally Merge needs input.
|
|
80
82
|
if (!node.type.includes('StickyNote')) {
|
|
81
83
|
// Double check for "On Execution" (custom trigger name sometimes used)
|
|
82
|
-
if (!node.name.toLowerCase().includes('trigger') && !node.name.toLowerCase().includes('webhook')) {
|
|
83
|
-
console.log(theme.warn(`[Reviewer] Validated disconnection: Node "${node.name}" has no incoming connections.`));
|
|
84
|
-
validationErrors.push(`Node "${node.name}" (${node.type}) is disconnected (orphaned). Connect it or remove it.`);
|
|
84
|
+
if (!node.name || (!node.name.toLowerCase().includes('trigger') && !node.name.toLowerCase().includes('webhook'))) {
|
|
85
|
+
console.log(theme.warn(`[Reviewer] Validated disconnection: Node "${node.name || 'Unnamed'}" has no incoming connections.`));
|
|
86
|
+
validationErrors.push(`Node "${node.name || 'Unnamed'}" (${node.type || 'unknown type'}) is disconnected (orphaned). Connect it or remove it.`);
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
89
|
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "n8n-nodes-base.start",
|
|
4
|
+
"displayName": "Manual Trigger",
|
|
5
|
+
"description": "The starting point for manual execution.",
|
|
6
|
+
"properties": []
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"name": "n8n-nodes-base.httpRequest",
|
|
10
|
+
"displayName": "HTTP Request",
|
|
11
|
+
"description": "Send HTTP requests to any API",
|
|
12
|
+
"properties": [
|
|
13
|
+
{
|
|
14
|
+
"name": "method",
|
|
15
|
+
"displayName": "Method",
|
|
16
|
+
"type": "options",
|
|
17
|
+
"default": "GET",
|
|
18
|
+
"options": [
|
|
19
|
+
{ "name": "GET", "value": "GET" },
|
|
20
|
+
{ "name": "POST", "value": "POST" },
|
|
21
|
+
{ "name": "PUT", "value": "PUT" },
|
|
22
|
+
{ "name": "DELETE", "value": "DELETE" }
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "url",
|
|
27
|
+
"displayName": "URL",
|
|
28
|
+
"type": "string",
|
|
29
|
+
"default": "",
|
|
30
|
+
"description": "The URL to send the request to"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "authentication",
|
|
34
|
+
"displayName": "Authentication",
|
|
35
|
+
"type": "options",
|
|
36
|
+
"default": "none",
|
|
37
|
+
"options": [
|
|
38
|
+
{ "name": "None", "value": "none" },
|
|
39
|
+
{ "name": "Predefined Credential Type", "value": "predefinedCredentialType" }
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"name": "sendBody",
|
|
44
|
+
"displayName": "Send Body",
|
|
45
|
+
"type": "boolean",
|
|
46
|
+
"default": false
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "body",
|
|
50
|
+
"displayName": "Body",
|
|
51
|
+
"type": "json",
|
|
52
|
+
"default": ""
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "n8n-nodes-base.slack",
|
|
58
|
+
"displayName": "Slack",
|
|
59
|
+
"description": "Interact with Slack",
|
|
60
|
+
"properties": [
|
|
61
|
+
{
|
|
62
|
+
"name": "resource",
|
|
63
|
+
"displayName": "Resource",
|
|
64
|
+
"type": "options",
|
|
65
|
+
"default": "message",
|
|
66
|
+
"options": [
|
|
67
|
+
{ "name": "Message", "value": "message" },
|
|
68
|
+
{ "name": "Channel", "value": "channel" }
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"name": "operation",
|
|
73
|
+
"displayName": "Operation",
|
|
74
|
+
"type": "options",
|
|
75
|
+
"default": "post",
|
|
76
|
+
"options": [
|
|
77
|
+
{ "name": "Post", "value": "post" },
|
|
78
|
+
{ "name": "Update", "value": "update" }
|
|
79
|
+
]
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"name": "channel",
|
|
83
|
+
"displayName": "Channel",
|
|
84
|
+
"type": "string",
|
|
85
|
+
"default": ""
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"name": "text",
|
|
89
|
+
"displayName": "Text",
|
|
90
|
+
"type": "string",
|
|
91
|
+
"default": ""
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"name": "n8n-nodes-base.googleSheets",
|
|
97
|
+
"displayName": "Google Sheets",
|
|
98
|
+
"description": "Interact with Google Sheets",
|
|
99
|
+
"properties": [
|
|
100
|
+
{
|
|
101
|
+
"name": "resource",
|
|
102
|
+
"displayName": "Resource",
|
|
103
|
+
"type": "options",
|
|
104
|
+
"default": "sheet",
|
|
105
|
+
"options": [
|
|
106
|
+
{ "name": "Sheet", "value": "sheet" }
|
|
107
|
+
]
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"name": "operation",
|
|
111
|
+
"displayName": "Operation",
|
|
112
|
+
"type": "options",
|
|
113
|
+
"default": "append",
|
|
114
|
+
"options": [
|
|
115
|
+
{ "name": "Append", "value": "append" },
|
|
116
|
+
{ "name": "Read", "value": "read" },
|
|
117
|
+
{ "name": "Update", "value": "update" }
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"name": "sheetId",
|
|
122
|
+
"displayName": "Sheet ID",
|
|
123
|
+
"type": "string",
|
|
124
|
+
"default": ""
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"name": "range",
|
|
128
|
+
"displayName": "Range",
|
|
129
|
+
"type": "string",
|
|
130
|
+
"default": "Sheet1!A:Z"
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"name": "n8n-nodes-base.if",
|
|
136
|
+
"displayName": "IF",
|
|
137
|
+
"description": "Conditional logic: True/False",
|
|
138
|
+
"properties": [
|
|
139
|
+
{
|
|
140
|
+
"name": "conditions",
|
|
141
|
+
"displayName": "Conditions",
|
|
142
|
+
"type": "json",
|
|
143
|
+
"default": {}
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"name": "n8n-nodes-base.set",
|
|
149
|
+
"displayName": "Set",
|
|
150
|
+
"description": "Set variables or values",
|
|
151
|
+
"properties": [
|
|
152
|
+
{
|
|
153
|
+
"name": "values",
|
|
154
|
+
"displayName": "Values",
|
|
155
|
+
"type": "fixedCollection",
|
|
156
|
+
"default": {}
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"name": "n8n-nodes-base.webhook",
|
|
162
|
+
"displayName": "Webhook",
|
|
163
|
+
"description": "Receive HTTP requests",
|
|
164
|
+
"properties": [
|
|
165
|
+
{
|
|
166
|
+
"name": "httpMethod",
|
|
167
|
+
"displayName": "HTTP Method",
|
|
168
|
+
"type": "options",
|
|
169
|
+
"default": "GET",
|
|
170
|
+
"options": [
|
|
171
|
+
{ "name": "GET", "value": "GET" },
|
|
172
|
+
{ "name": "POST", "value": "POST" }
|
|
173
|
+
]
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"name": "path",
|
|
177
|
+
"displayName": "Path",
|
|
178
|
+
"type": "string",
|
|
179
|
+
"default": ""
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"name": "n8n-nodes-base.code",
|
|
185
|
+
"displayName": "Code",
|
|
186
|
+
"description": "Run JavaScript/TypeScript",
|
|
187
|
+
"properties": [
|
|
188
|
+
{
|
|
189
|
+
"name": "jsCode",
|
|
190
|
+
"displayName": "JS Code",
|
|
191
|
+
"type": "string",
|
|
192
|
+
"default": "// Your code here\nreturn items;"
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"name": "n8n-nodes-base.merge",
|
|
198
|
+
"displayName": "Merge",
|
|
199
|
+
"description": "Merge data from multiple inputs",
|
|
200
|
+
"properties": [
|
|
201
|
+
{
|
|
202
|
+
"name": "mode",
|
|
203
|
+
"displayName": "Mode",
|
|
204
|
+
"type": "options",
|
|
205
|
+
"default": "append",
|
|
206
|
+
"options": [
|
|
207
|
+
{ "name": "Append", "value": "append" },
|
|
208
|
+
{ "name": "Merge by Index", "value": "mergeByIndex" }
|
|
209
|
+
]
|
|
210
|
+
}
|
|
211
|
+
]
|
|
212
|
+
}
|
|
213
|
+
]
|
|
@@ -1,64 +1,47 @@
|
|
|
1
1
|
export interface GenerateOptions {
|
|
2
2
|
model?: string;
|
|
3
|
+
provider?: string;
|
|
3
4
|
temperature?: number;
|
|
4
5
|
}
|
|
6
|
+
export interface WorkflowSpec {
|
|
7
|
+
suggestedName: string;
|
|
8
|
+
description: string;
|
|
9
|
+
nodes: {
|
|
10
|
+
type: string;
|
|
11
|
+
purpose: string;
|
|
12
|
+
config?: any;
|
|
13
|
+
}[];
|
|
14
|
+
questions?: string[];
|
|
15
|
+
strategyName?: string;
|
|
16
|
+
aiModel?: string;
|
|
17
|
+
aiProvider?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare const PROVIDER_PRESETS: Record<string, {
|
|
20
|
+
baseURL?: string;
|
|
21
|
+
defaultModel: string;
|
|
22
|
+
models: string[];
|
|
23
|
+
}>;
|
|
5
24
|
export declare class AIService {
|
|
6
25
|
private static instance;
|
|
7
|
-
private
|
|
8
|
-
private
|
|
26
|
+
private clients;
|
|
27
|
+
private defaultProvider;
|
|
28
|
+
private defaultModel;
|
|
29
|
+
private apiKey;
|
|
30
|
+
private baseURL?;
|
|
9
31
|
private constructor();
|
|
32
|
+
private getClient;
|
|
10
33
|
static getInstance(): AIService;
|
|
11
|
-
|
|
12
|
-
* Core generation method — works with any OpenAI-compatible API
|
|
13
|
-
*/
|
|
34
|
+
private callAnthropicNative;
|
|
14
35
|
generateContent(prompt: string, options?: GenerateOptions): Promise<string>;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
generateSpec(description: string): Promise<any>;
|
|
23
|
-
/**
|
|
24
|
-
* Refine a Specification based on user feedback
|
|
25
|
-
*/
|
|
26
|
-
refineSpec(spec: any, feedback: string): Promise<any>;
|
|
27
|
-
/**
|
|
28
|
-
* Generate workflow JSONs from an approved Specification
|
|
29
|
-
*/
|
|
30
|
-
generateWorkflowFromSpec(spec: any): Promise<any>;
|
|
31
|
-
/**
|
|
32
|
-
* Generate mock data for a workflow execution
|
|
33
|
-
*/
|
|
34
|
-
generateMockData(context: string, previousFailures?: string[]): Promise<any>;
|
|
35
|
-
/**
|
|
36
|
-
* Diagnostic Repair: Fix a workflow based on execution error
|
|
37
|
-
*/
|
|
38
|
-
generateWorkflowFix(workflowJson: any, errorContext: string, model?: string, _useSearch?: boolean, validNodeTypes?: string[]): Promise<any>;
|
|
39
|
-
/**
|
|
40
|
-
* Auto-correct common n8n node type hallucinations
|
|
41
|
-
*/
|
|
42
|
-
private fixHallucinatedNodes;
|
|
43
|
-
/**
|
|
44
|
-
* Force-fix connection structure to prevent "object is not iterable" errors
|
|
45
|
-
*/
|
|
46
|
-
private fixN8nConnections;
|
|
47
|
-
/**
|
|
48
|
-
* Generate an alternative workflow specification with a different approach to the same goal.
|
|
49
|
-
* Used by the Architect node to produce a second strategy for parallel Engineer execution.
|
|
50
|
-
*/
|
|
51
|
-
generateAlternativeSpec(goal: string, primarySpec: any): Promise<any>;
|
|
52
|
-
/**
|
|
53
|
-
* Evaluate multiple workflow candidates and select the best one for the given goal.
|
|
54
|
-
* Used by the Supervisor node to choose between parallel Engineer outputs.
|
|
55
|
-
*/
|
|
36
|
+
getAlternativeModel(): string;
|
|
37
|
+
getDefaultModel(): string;
|
|
38
|
+
getDefaultProvider(): string;
|
|
39
|
+
generateSpec(goal: string): Promise<WorkflowSpec>;
|
|
40
|
+
generateAlternativeSpec(goal: string, primarySpec: WorkflowSpec): Promise<WorkflowSpec>;
|
|
41
|
+
generateWorkflowFix(workflow: any, error: string, model?: string, stream?: boolean, validNodeTypes?: string[]): Promise<any>;
|
|
42
|
+
generateMockData(context: string): Promise<any>;
|
|
56
43
|
evaluateCandidates(goal: string, candidates: any[]): Promise<{
|
|
57
44
|
selectedIndex: number;
|
|
58
45
|
reason: string;
|
|
59
46
|
}>;
|
|
60
|
-
/**
|
|
61
|
-
* Validate against real node types and shim unknown ones
|
|
62
|
-
*/
|
|
63
|
-
validateAndShim(workflow: any, validNodeTypes?: string[], explicitlyInvalid?: string[]): any;
|
|
64
47
|
}
|