@n8n/node-cli 0.19.0 → 0.21.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/dist/build.tsbuildinfo +1 -1
- package/dist/commands/new/index.js +30 -1
- package/dist/commands/new/index.js.map +1 -1
- package/dist/commands/new/prompts.d.ts +2 -0
- package/dist/commands/new/prompts.js +46 -2
- package/dist/commands/new/prompts.js.map +1 -1
- package/dist/template/templates/index.d.ts +4 -0
- package/dist/template/templates/index.js +10 -2
- package/dist/template/templates/index.js.map +1 -1
- package/dist/template/templates/index.ts +8 -0
- package/dist/template/templates/programmatic/ai/memory-custom/template/README.md +46 -0
- package/dist/template/templates/programmatic/ai/memory-custom/template/nodes/ExampleChatMemory/ExampleChatMemory.node.json +18 -0
- package/dist/template/templates/programmatic/ai/memory-custom/template/nodes/ExampleChatMemory/ExampleChatMemory.node.ts +84 -0
- package/dist/template/templates/programmatic/ai/memory-custom/template/nodes/ExampleChatMemory/example.dark.svg +13 -0
- package/dist/template/templates/programmatic/ai/memory-custom/template/nodes/ExampleChatMemory/example.svg +13 -0
- package/dist/template/templates/programmatic/ai/memory-custom/template/nodes/ExampleChatMemory/memory.ts +29 -0
- package/dist/template/templates/programmatic/ai/memory-custom/template/package.json +50 -0
- package/dist/template/templates/programmatic/ai/memory-custom/template/tsconfig.json +25 -0
- package/dist/template/templates/programmatic/ai/memory-custom/template.d.ts +1 -0
- package/dist/template/templates/programmatic/ai/memory-custom/template.js +14 -0
- package/dist/template/templates/programmatic/ai/memory-custom/template.js.map +1 -0
- package/dist/template/templates/programmatic/ai/memory-custom/template.ts +9 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom/template/README.md +46 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom/template/credentials/ExampleApi.credentials.ts +54 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom/template/icons/example.dark.svg +13 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom/template/icons/example.svg +13 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom/template/nodes/ExampleChatModel/ExampleChatModel.node.json +18 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom/template/nodes/ExampleChatModel/ExampleChatModel.node.ts +113 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom/template/nodes/ExampleChatModel/model.ts +115 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom/template/package.json +52 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom/template/tsconfig.json +25 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom/template.d.ts +1 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom/template.js +14 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom/template.js.map +1 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom/template.ts +9 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom-example/template/README.md +46 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom-example/template/credentials/ExampleApi.credentials.ts +52 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom-example/template/icons/example.dark.svg +13 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom-example/template/icons/example.svg +13 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom-example/template/nodes/ExampleChatModel/ExampleChatModel.node.json +18 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom-example/template/nodes/ExampleChatModel/ExampleChatModel.node.ts +114 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom-example/template/nodes/ExampleChatModel/common.ts +43 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom-example/template/nodes/ExampleChatModel/model.ts +534 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom-example/template/nodes/ExampleChatModel/properties.ts +130 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom-example/template/package.json +52 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom-example/template/tsconfig.json +25 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom-example/template.d.ts +1 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom-example/template.js +14 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom-example/template.js.map +1 -0
- package/dist/template/templates/programmatic/ai/model-ai-custom-example/template.ts +9 -0
- package/dist/template/templates/programmatic/ai/model-openai-compatible/template/README.md +46 -0
- package/dist/template/templates/programmatic/ai/model-openai-compatible/template/credentials/ExampleApi.credentials.ts +52 -0
- package/dist/template/templates/programmatic/ai/model-openai-compatible/template/icons/example.dark.svg +13 -0
- package/dist/template/templates/programmatic/ai/model-openai-compatible/template/icons/example.svg +13 -0
- package/dist/template/templates/programmatic/ai/model-openai-compatible/template/nodes/ExampleChatModel/ExampleChatModel.node.json +18 -0
- package/dist/template/templates/programmatic/ai/model-openai-compatible/template/nodes/ExampleChatModel/ExampleChatModel.node.ts +84 -0
- package/dist/template/templates/programmatic/ai/model-openai-compatible/template/package.json +52 -0
- package/dist/template/templates/programmatic/ai/model-openai-compatible/template/tsconfig.json +25 -0
- package/dist/template/templates/programmatic/ai/model-openai-compatible/template.d.ts +1 -0
- package/dist/template/templates/programmatic/ai/model-openai-compatible/template.js +14 -0
- package/dist/template/templates/programmatic/ai/model-openai-compatible/template.js.map +1 -0
- package/dist/template/templates/programmatic/ai/model-openai-compatible/template.ts +9 -0
- package/dist/template/templates/programmatic/example/template/nodes/Example/Example.node.ts +1 -1
- package/dist/template/templates/shared/default/.github/workflows/ci.yml +1 -1
- package/package.json +5 -4
package/dist/template/templates/programmatic/ai/model-ai-custom/template/icons/example.dark.svg
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="aquamarine"
|
|
2
|
+
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-cpu">
|
|
3
|
+
<rect x="4" y="4" width="16" height="16" rx="2" ry="2"></rect>
|
|
4
|
+
<rect x="9" y="9" width="6" height="6"></rect>
|
|
5
|
+
<line x1="9" y1="1" x2="9" y2="4"></line>
|
|
6
|
+
<line x1="15" y1="1" x2="15" y2="4"></line>
|
|
7
|
+
<line x1="9" y1="20" x2="9" y2="23"></line>
|
|
8
|
+
<line x1="15" y1="20" x2="15" y2="23"></line>
|
|
9
|
+
<line x1="20" y1="9" x2="23" y2="9"></line>
|
|
10
|
+
<line x1="20" y1="14" x2="23" y2="14"></line>
|
|
11
|
+
<line x1="1" y1="9" x2="4" y2="9"></line>
|
|
12
|
+
<line x1="1" y1="14" x2="4" y2="14"></line>
|
|
13
|
+
</svg>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="darkblue"
|
|
2
|
+
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-cpu">
|
|
3
|
+
<rect x="4" y="4" width="16" height="16" rx="2" ry="2"></rect>
|
|
4
|
+
<rect x="9" y="9" width="6" height="6"></rect>
|
|
5
|
+
<line x1="9" y1="1" x2="9" y2="4"></line>
|
|
6
|
+
<line x1="15" y1="1" x2="15" y2="4"></line>
|
|
7
|
+
<line x1="9" y1="20" x2="9" y2="23"></line>
|
|
8
|
+
<line x1="15" y1="20" x2="15" y2="23"></line>
|
|
9
|
+
<line x1="20" y1="9" x2="23" y2="9"></line>
|
|
10
|
+
<line x1="20" y1="14" x2="23" y2="14"></line>
|
|
11
|
+
<line x1="1" y1="9" x2="4" y2="9"></line>
|
|
12
|
+
<line x1="1" y1="14" x2="4" y2="14"></line>
|
|
13
|
+
</svg>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"node": "{{nodePackageName}}",
|
|
3
|
+
"nodeVersion": "1.0",
|
|
4
|
+
"codexVersion": "1.0",
|
|
5
|
+
"categories": ["Development", "Developer Tools"],
|
|
6
|
+
"resources": {
|
|
7
|
+
"credentialDocumentation": [
|
|
8
|
+
{
|
|
9
|
+
"url": "https://github.com/org/repo?tab=readme-ov-file#credentials"
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"primaryDocumentation": [
|
|
13
|
+
{
|
|
14
|
+
"url": "https://github.com/org/repo?tab=readme-ov-file"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { INodeType, INodeTypeDescription, ISupplyDataFunctions } from 'n8n-workflow';
|
|
2
|
+
import { NodeConnectionTypes } from 'n8n-workflow';
|
|
3
|
+
import { supplyModel } from '@n8n/ai-node-sdk';
|
|
4
|
+
import { CustomChatModel } from './model';
|
|
5
|
+
|
|
6
|
+
type ModelOptions = {
|
|
7
|
+
temperature?: number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export class ExampleChatModel implements INodeType {
|
|
11
|
+
description: INodeTypeDescription = {
|
|
12
|
+
displayName: 'Example Chat Model',
|
|
13
|
+
name: 'exampleChatModel',
|
|
14
|
+
icon: { light: 'file:../../icons/example.svg', dark: 'file:../../icons/example.dark.svg' },
|
|
15
|
+
group: ['transform'],
|
|
16
|
+
version: [1],
|
|
17
|
+
description: 'Custom Chat Model Node',
|
|
18
|
+
defaults: {
|
|
19
|
+
name: 'Example Chat Model',
|
|
20
|
+
},
|
|
21
|
+
codex: {
|
|
22
|
+
categories: ['assistant'],
|
|
23
|
+
subcategories: {
|
|
24
|
+
AI: ['Language Models', 'Root Nodes'],
|
|
25
|
+
'Language Models': ['Chat Models (Recommended)'],
|
|
26
|
+
},
|
|
27
|
+
resources: {
|
|
28
|
+
primaryDocumentation: [],
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
inputs: [],
|
|
33
|
+
|
|
34
|
+
outputs: [NodeConnectionTypes.AiLanguageModel],
|
|
35
|
+
outputNames: ['Model'],
|
|
36
|
+
credentials: [
|
|
37
|
+
{
|
|
38
|
+
name: 'exampleApi',
|
|
39
|
+
required: true,
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
properties: [
|
|
43
|
+
{
|
|
44
|
+
displayName: 'Model',
|
|
45
|
+
name: 'model',
|
|
46
|
+
type: 'string',
|
|
47
|
+
default: '',
|
|
48
|
+
description: 'The model which will generate the completion',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
displayName: 'Options',
|
|
52
|
+
name: 'options',
|
|
53
|
+
placeholder: 'Add Option',
|
|
54
|
+
description: 'Additional options to add',
|
|
55
|
+
type: 'collection',
|
|
56
|
+
default: {},
|
|
57
|
+
options: [
|
|
58
|
+
{
|
|
59
|
+
displayName: 'Sampling Temperature',
|
|
60
|
+
name: 'temperature',
|
|
61
|
+
default: 0.7,
|
|
62
|
+
typeOptions: { maxValue: 2, minValue: 0, numberPrecision: 1 },
|
|
63
|
+
description:
|
|
64
|
+
'Controls randomness: Lowering results in less random completions. As the temperature approaches zero, the model will become deterministic and repetitive.',
|
|
65
|
+
type: 'number',
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
async supplyData(this: ISupplyDataFunctions, itemIndex: number) {
|
|
73
|
+
const credentials = await this.getCredentials('exampleApi');
|
|
74
|
+
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
|
75
|
+
const options = this.getNodeParameter('options', itemIndex, {}) as ModelOptions;
|
|
76
|
+
|
|
77
|
+
const model = new CustomChatModel(
|
|
78
|
+
modelName,
|
|
79
|
+
{
|
|
80
|
+
httpRequest: async () => {
|
|
81
|
+
// make a request to the API using this.helpers.httpRequestWithAuthentication.call
|
|
82
|
+
return {
|
|
83
|
+
body: {
|
|
84
|
+
response: 'Hello World!',
|
|
85
|
+
tokenUsage: {
|
|
86
|
+
promptTokens: 10,
|
|
87
|
+
completionTokens: 10,
|
|
88
|
+
totalTokens: 20,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
openStream: async () => {
|
|
94
|
+
// make a request to the API using this.helpers.httpRequestWithAuthentication.call
|
|
95
|
+
const mockStream = (async function* () {
|
|
96
|
+
yield 'Hello ';
|
|
97
|
+
yield 'World';
|
|
98
|
+
yield '!';
|
|
99
|
+
})();
|
|
100
|
+
return {
|
|
101
|
+
body: mockStream,
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
url: credentials.url as string,
|
|
107
|
+
temperature: options.temperature,
|
|
108
|
+
},
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
return supplyModel(this, model);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { IHttpRequestMethods } from 'n8n-workflow';
|
|
2
|
+
import {
|
|
3
|
+
BaseChatModel,
|
|
4
|
+
type ChatModelConfig,
|
|
5
|
+
type GenerateResult,
|
|
6
|
+
type Message,
|
|
7
|
+
type StreamChunk,
|
|
8
|
+
} from '@n8n/ai-node-sdk';
|
|
9
|
+
|
|
10
|
+
interface ModelConfig extends ChatModelConfig {
|
|
11
|
+
url: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface ProviderResponse {
|
|
15
|
+
id?: string;
|
|
16
|
+
response: string;
|
|
17
|
+
tokenUsage?: {
|
|
18
|
+
promptTokens: number;
|
|
19
|
+
completionTokens: number;
|
|
20
|
+
totalTokens: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface RequestConfig {
|
|
25
|
+
httpRequest: (
|
|
26
|
+
method: IHttpRequestMethods,
|
|
27
|
+
url: string,
|
|
28
|
+
body?: object,
|
|
29
|
+
headers?: Record<string, string>,
|
|
30
|
+
) => Promise<{ body: unknown }>;
|
|
31
|
+
openStream: (
|
|
32
|
+
method: IHttpRequestMethods,
|
|
33
|
+
url: string,
|
|
34
|
+
body?: object,
|
|
35
|
+
headers?: Record<string, string>,
|
|
36
|
+
) => Promise<{ body: AsyncIterableIterator<string> }>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class CustomChatModel extends BaseChatModel<ModelConfig> {
|
|
40
|
+
private baseURL: string;
|
|
41
|
+
|
|
42
|
+
constructor(
|
|
43
|
+
modelId: string = 'my-model',
|
|
44
|
+
private requests: RequestConfig,
|
|
45
|
+
config: ModelConfig,
|
|
46
|
+
) {
|
|
47
|
+
super('custom-provider', modelId, config);
|
|
48
|
+
this.baseURL = config.url;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async generate(messages: Message[], config?: ModelConfig): Promise<GenerateResult> {
|
|
52
|
+
const merged = this.mergeConfig(config);
|
|
53
|
+
// Convert n8n messages to provider format
|
|
54
|
+
const providerMessages = messages.map((m) => ({
|
|
55
|
+
role: m.role,
|
|
56
|
+
content: m.content
|
|
57
|
+
.filter((c) => c.type === 'text')
|
|
58
|
+
.map((c) => c.text)
|
|
59
|
+
.join('\n'),
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
const requestBody = {
|
|
63
|
+
model: this.modelId,
|
|
64
|
+
messages: providerMessages,
|
|
65
|
+
temperature: merged.temperature,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const response = await this.requests.httpRequest(
|
|
69
|
+
'POST',
|
|
70
|
+
`${this.baseURL}/generate`,
|
|
71
|
+
requestBody,
|
|
72
|
+
);
|
|
73
|
+
const body = response.body as ProviderResponse;
|
|
74
|
+
|
|
75
|
+
// Convert provider response to n8n message
|
|
76
|
+
const message: Message = {
|
|
77
|
+
role: 'assistant',
|
|
78
|
+
content: [{ type: 'text', text: body.response }],
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
id: body.id,
|
|
83
|
+
finishReason: 'stop',
|
|
84
|
+
usage: {
|
|
85
|
+
promptTokens: body.tokenUsage?.promptTokens ?? 0,
|
|
86
|
+
completionTokens: body.tokenUsage?.completionTokens ?? 0,
|
|
87
|
+
totalTokens: body.tokenUsage?.totalTokens ?? 0,
|
|
88
|
+
},
|
|
89
|
+
message,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async *stream(messages: Message[], config?: ModelConfig): AsyncIterable<StreamChunk> {
|
|
94
|
+
const merged = this.mergeConfig(config);
|
|
95
|
+
// Convert n8n messages to provider format
|
|
96
|
+
const providerMessages = messages.map((m) => ({
|
|
97
|
+
role: m.role,
|
|
98
|
+
content: m.content
|
|
99
|
+
.filter((c) => c.type === 'text')
|
|
100
|
+
.map((c) => c.text)
|
|
101
|
+
.join('\n'),
|
|
102
|
+
}));
|
|
103
|
+
|
|
104
|
+
const requestBody = {
|
|
105
|
+
model: this.modelId,
|
|
106
|
+
messages: providerMessages,
|
|
107
|
+
temperature: merged.temperature,
|
|
108
|
+
};
|
|
109
|
+
const response = await this.requests.openStream('POST', `${this.baseURL}/stream`, requestBody);
|
|
110
|
+
for await (const chunk of response.body) {
|
|
111
|
+
yield { type: 'text-delta', delta: chunk };
|
|
112
|
+
}
|
|
113
|
+
yield { type: 'finish', finishReason: 'stop' };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{nodePackageName}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"n8n-community-node-package"
|
|
9
|
+
],
|
|
10
|
+
"author": {
|
|
11
|
+
"name": "{{user.name}}",
|
|
12
|
+
"email": "{{user.email}}"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/<...>/n8n-nodes-<...>.git"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "n8n-node build",
|
|
20
|
+
"build:watch": "tsc --watch",
|
|
21
|
+
"dev": "n8n-node dev",
|
|
22
|
+
"lint": "n8n-node lint",
|
|
23
|
+
"lint:fix": "n8n-node lint --fix",
|
|
24
|
+
"release": "n8n-node release",
|
|
25
|
+
"prepublishOnly": "n8n-node prerelease"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
30
|
+
"n8n": {
|
|
31
|
+
"n8nNodesApiVersion": 1,
|
|
32
|
+
"aiNodeSdkVersion": 1,
|
|
33
|
+
"strict": true,
|
|
34
|
+
"credentials": [
|
|
35
|
+
"dist/credentials/ExampleApi.credentials.js"
|
|
36
|
+
],
|
|
37
|
+
"nodes": [
|
|
38
|
+
"dist/nodes/ExampleChatModel/ExampleChatModel.node.js"
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@n8n/node-cli": "*",
|
|
43
|
+
"eslint": "9.32.0",
|
|
44
|
+
"prettier": "3.6.2",
|
|
45
|
+
"release-it": "^19.0.4",
|
|
46
|
+
"typescript": "5.9.2"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"n8n-workflow": "*",
|
|
50
|
+
"@n8n/ai-node-sdk": "*"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"strict": true,
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"target": "es2019",
|
|
7
|
+
"lib": ["es2019", "es2020", "es2022.error"],
|
|
8
|
+
"removeComments": true,
|
|
9
|
+
"useUnknownInCatchVariables": false,
|
|
10
|
+
"forceConsistentCasingInFileNames": true,
|
|
11
|
+
"noImplicitAny": true,
|
|
12
|
+
"noImplicitReturns": true,
|
|
13
|
+
"noUnusedLocals": true,
|
|
14
|
+
"strictNullChecks": true,
|
|
15
|
+
"preserveConstEnums": true,
|
|
16
|
+
"esModuleInterop": true,
|
|
17
|
+
"resolveJsonModule": true,
|
|
18
|
+
"incremental": true,
|
|
19
|
+
"declaration": true,
|
|
20
|
+
"sourceMap": true,
|
|
21
|
+
"skipLibCheck": true,
|
|
22
|
+
"outDir": "./dist/"
|
|
23
|
+
},
|
|
24
|
+
"include": ["credentials/**/*", "nodes/**/*", "nodes/**/*.json", "package.json"]
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const customChatModelTemplate: import("../../../../core").TemplateWithRun<object>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.customChatModelTemplate = void 0;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const core_1 = require("../../../../core");
|
|
9
|
+
exports.customChatModelTemplate = (0, core_1.createTemplate)({
|
|
10
|
+
name: 'Custom chat model node',
|
|
11
|
+
description: 'Chat model node with custom implementation',
|
|
12
|
+
path: node_path_1.default.join(__dirname, 'template'),
|
|
13
|
+
});
|
|
14
|
+
//# sourceMappingURL=template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../../../../../src/template/templates/programmatic/ai/model-ai-custom/template.ts"],"names":[],"mappings":";;;;;;AAAA,0DAA6B;AAE7B,2CAAkD;AAErC,QAAA,uBAAuB,GAAG,IAAA,qBAAc,EAAC;IACrD,IAAI,EAAE,wBAAwB;IAC9B,WAAW,EAAE,4CAA4C;IACzD,IAAI,EAAE,mBAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC;CACtC,CAAC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
import { createTemplate } from '../../../../core';
|
|
4
|
+
|
|
5
|
+
export const customChatModelTemplate = createTemplate({
|
|
6
|
+
name: 'Custom chat model node',
|
|
7
|
+
description: 'Chat model node with custom implementation',
|
|
8
|
+
path: path.join(__dirname, 'template'),
|
|
9
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# {{nodePackageName}}
|
|
2
|
+
|
|
3
|
+
This is an n8n community node. It lets you use _app/service name_ in your n8n workflows.
|
|
4
|
+
|
|
5
|
+
_App/service name_ is _one or two sentences describing the service this node integrates with_.
|
|
6
|
+
|
|
7
|
+
[n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/sustainable-use-license/) workflow automation platform.
|
|
8
|
+
|
|
9
|
+
[Installation](#installation)
|
|
10
|
+
[Operations](#operations)
|
|
11
|
+
[Credentials](#credentials)
|
|
12
|
+
[Compatibility](#compatibility)
|
|
13
|
+
[Usage](#usage)
|
|
14
|
+
[Resources](#resources)
|
|
15
|
+
[Version history](#version-history)
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.
|
|
20
|
+
|
|
21
|
+
## Operations
|
|
22
|
+
|
|
23
|
+
_List the operations supported by your node._
|
|
24
|
+
|
|
25
|
+
## Credentials
|
|
26
|
+
|
|
27
|
+
_If users need to authenticate with the app/service, provide details here. You should include prerequisites (such as signing up with the service), available authentication methods, and how to set them up._
|
|
28
|
+
|
|
29
|
+
## Compatibility
|
|
30
|
+
|
|
31
|
+
_State the minimum n8n version, as well as which versions you test against. You can also include any known version incompatibility issues._
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
_This is an optional section. Use it to help users with any difficult or confusing aspects of the node._
|
|
36
|
+
|
|
37
|
+
_By the time users are looking for community nodes, they probably already know n8n basics. But if you expect new users, you can link to the [Try it out](https://docs.n8n.io/try-it-out/) documentation to help them get started._
|
|
38
|
+
|
|
39
|
+
## Resources
|
|
40
|
+
|
|
41
|
+
* [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)
|
|
42
|
+
* _Link to app/service documentation._
|
|
43
|
+
|
|
44
|
+
## Version history
|
|
45
|
+
|
|
46
|
+
_This is another optional section. If your node has multiple versions, include a short description of available versions and what changed, as well as any compatibility impact._
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ICredentialDataDecryptedObject,
|
|
3
|
+
ICredentialTestRequest,
|
|
4
|
+
ICredentialType,
|
|
5
|
+
IHttpRequestOptions,
|
|
6
|
+
INodeProperties,
|
|
7
|
+
Icon,
|
|
8
|
+
} from 'n8n-workflow';
|
|
9
|
+
|
|
10
|
+
export class ExampleApi implements ICredentialType {
|
|
11
|
+
name = 'exampleApi';
|
|
12
|
+
|
|
13
|
+
displayName = 'Example API';
|
|
14
|
+
|
|
15
|
+
icon: Icon = { light: 'file:../icons/example.svg', dark: 'file:../icons/example.dark.svg' };
|
|
16
|
+
|
|
17
|
+
properties: INodeProperties[] = [
|
|
18
|
+
{
|
|
19
|
+
displayName: 'API Key',
|
|
20
|
+
name: 'apiKey',
|
|
21
|
+
type: 'string',
|
|
22
|
+
typeOptions: { password: true },
|
|
23
|
+
required: true,
|
|
24
|
+
default: '',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
displayName: 'Base URL',
|
|
28
|
+
name: 'url',
|
|
29
|
+
type: 'string',
|
|
30
|
+
default: '',
|
|
31
|
+
description: 'Override the default base URL for the API',
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
test: ICredentialTestRequest = {
|
|
36
|
+
request: {
|
|
37
|
+
baseURL: '={{$credentials?.url}}',
|
|
38
|
+
url: '/models',
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
async authenticate(
|
|
43
|
+
credentials: ICredentialDataDecryptedObject,
|
|
44
|
+
requestOptions: IHttpRequestOptions,
|
|
45
|
+
): Promise<IHttpRequestOptions> {
|
|
46
|
+
requestOptions.headers ??= {};
|
|
47
|
+
|
|
48
|
+
requestOptions.headers['Authorization'] = `Bearer ${credentials.apiKey}`;
|
|
49
|
+
|
|
50
|
+
return requestOptions;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="aquamarine"
|
|
2
|
+
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-cpu">
|
|
3
|
+
<rect x="4" y="4" width="16" height="16" rx="2" ry="2"></rect>
|
|
4
|
+
<rect x="9" y="9" width="6" height="6"></rect>
|
|
5
|
+
<line x1="9" y1="1" x2="9" y2="4"></line>
|
|
6
|
+
<line x1="15" y1="1" x2="15" y2="4"></line>
|
|
7
|
+
<line x1="9" y1="20" x2="9" y2="23"></line>
|
|
8
|
+
<line x1="15" y1="20" x2="15" y2="23"></line>
|
|
9
|
+
<line x1="20" y1="9" x2="23" y2="9"></line>
|
|
10
|
+
<line x1="20" y1="14" x2="23" y2="14"></line>
|
|
11
|
+
<line x1="1" y1="9" x2="4" y2="9"></line>
|
|
12
|
+
<line x1="1" y1="14" x2="4" y2="14"></line>
|
|
13
|
+
</svg>
|
package/dist/template/templates/programmatic/ai/model-ai-custom-example/template/icons/example.svg
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="darkblue"
|
|
2
|
+
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-cpu">
|
|
3
|
+
<rect x="4" y="4" width="16" height="16" rx="2" ry="2"></rect>
|
|
4
|
+
<rect x="9" y="9" width="6" height="6"></rect>
|
|
5
|
+
<line x1="9" y1="1" x2="9" y2="4"></line>
|
|
6
|
+
<line x1="15" y1="1" x2="15" y2="4"></line>
|
|
7
|
+
<line x1="9" y1="20" x2="9" y2="23"></line>
|
|
8
|
+
<line x1="15" y1="20" x2="15" y2="23"></line>
|
|
9
|
+
<line x1="20" y1="9" x2="23" y2="9"></line>
|
|
10
|
+
<line x1="20" y1="14" x2="23" y2="14"></line>
|
|
11
|
+
<line x1="1" y1="9" x2="4" y2="9"></line>
|
|
12
|
+
<line x1="1" y1="14" x2="4" y2="14"></line>
|
|
13
|
+
</svg>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"node": "{{nodePackageName}}",
|
|
3
|
+
"nodeVersion": "1.0",
|
|
4
|
+
"codexVersion": "1.0",
|
|
5
|
+
"categories": ["Development", "Developer Tools"],
|
|
6
|
+
"resources": {
|
|
7
|
+
"credentialDocumentation": [
|
|
8
|
+
{
|
|
9
|
+
"url": "https://github.com/org/repo?tab=readme-ov-file#credentials"
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"primaryDocumentation": [
|
|
13
|
+
{
|
|
14
|
+
"url": "https://github.com/org/repo?tab=readme-ov-file"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
INodeType,
|
|
3
|
+
INodeTypeDescription,
|
|
4
|
+
ISupplyDataFunctions,
|
|
5
|
+
IDataObject,
|
|
6
|
+
} from 'n8n-workflow';
|
|
7
|
+
import { NodeConnectionTypes } from 'n8n-workflow';
|
|
8
|
+
import { ProviderTool, supplyModel } from '@n8n/ai-node-sdk';
|
|
9
|
+
import { OpenAIChatModel } from './model';
|
|
10
|
+
import { openAiProperties } from './properties';
|
|
11
|
+
import { formatBuiltInTools } from './common';
|
|
12
|
+
|
|
13
|
+
type ModelOptions = {
|
|
14
|
+
temperature?: number;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export class ExampleChatModel implements INodeType {
|
|
18
|
+
description: INodeTypeDescription = {
|
|
19
|
+
displayName: 'Example Chat Model',
|
|
20
|
+
name: 'exampleChatModel',
|
|
21
|
+
icon: { light: 'file:../../icons/example.svg', dark: 'file:../../icons/example.dark.svg' },
|
|
22
|
+
group: ['transform'],
|
|
23
|
+
version: [1],
|
|
24
|
+
description: 'Custom Chat Model Node',
|
|
25
|
+
defaults: {
|
|
26
|
+
name: 'Example Chat Model',
|
|
27
|
+
},
|
|
28
|
+
codex: {
|
|
29
|
+
categories: ['assistant'],
|
|
30
|
+
subcategories: {
|
|
31
|
+
AI: ['Language Models', 'Root Nodes'],
|
|
32
|
+
'Language Models': ['Chat Models (Recommended)'],
|
|
33
|
+
},
|
|
34
|
+
resources: {
|
|
35
|
+
primaryDocumentation: [],
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
inputs: [],
|
|
40
|
+
|
|
41
|
+
outputs: [NodeConnectionTypes.AiLanguageModel],
|
|
42
|
+
outputNames: ['Model'],
|
|
43
|
+
credentials: [
|
|
44
|
+
{
|
|
45
|
+
name: 'exampleApi',
|
|
46
|
+
required: true,
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
requestDefaults: {
|
|
50
|
+
ignoreHttpStatusErrors: true,
|
|
51
|
+
baseURL:
|
|
52
|
+
'={{ $credentials?.url?.split("/").slice(0,-1).join("/") || "https://api.openai.com" }}',
|
|
53
|
+
},
|
|
54
|
+
properties: openAiProperties,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
async supplyData(this: ISupplyDataFunctions, itemIndex: number) {
|
|
58
|
+
const credentials = await this.getCredentials('exampleApi');
|
|
59
|
+
const modelName = this.getNodeParameter('model', itemIndex) as string;
|
|
60
|
+
const options = this.getNodeParameter('options', itemIndex, {}) as ModelOptions;
|
|
61
|
+
const providerTools: ProviderTool[] = [];
|
|
62
|
+
const builtInToolsParams = formatBuiltInTools(
|
|
63
|
+
this.getNodeParameter('builtInTools', itemIndex, {}) as IDataObject,
|
|
64
|
+
);
|
|
65
|
+
if (builtInToolsParams.length) {
|
|
66
|
+
providerTools.push(...builtInToolsParams);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const model = new OpenAIChatModel(
|
|
70
|
+
modelName,
|
|
71
|
+
{
|
|
72
|
+
httpRequest: async (method, url, body, headers) => {
|
|
73
|
+
const response = await this.helpers.httpRequestWithAuthentication.call(
|
|
74
|
+
this,
|
|
75
|
+
'exampleApi',
|
|
76
|
+
{
|
|
77
|
+
url,
|
|
78
|
+
method,
|
|
79
|
+
body,
|
|
80
|
+
headers,
|
|
81
|
+
},
|
|
82
|
+
);
|
|
83
|
+
return {
|
|
84
|
+
body: response,
|
|
85
|
+
};
|
|
86
|
+
},
|
|
87
|
+
openStream: async (method, url, body, headers) => {
|
|
88
|
+
const response = await this.helpers.httpRequestWithAuthentication.call(
|
|
89
|
+
this,
|
|
90
|
+
'exampleApi',
|
|
91
|
+
{
|
|
92
|
+
method,
|
|
93
|
+
url,
|
|
94
|
+
body,
|
|
95
|
+
headers,
|
|
96
|
+
encoding: 'stream',
|
|
97
|
+
},
|
|
98
|
+
);
|
|
99
|
+
return {
|
|
100
|
+
body: response,
|
|
101
|
+
};
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
baseURL: credentials.url as string,
|
|
106
|
+
apiKey: credentials.apiKey as string,
|
|
107
|
+
providerTools,
|
|
108
|
+
temperature: options.temperature,
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
return supplyModel(this, model);
|
|
113
|
+
}
|
|
114
|
+
}
|