n8n-nodes-siliconflow-ai 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 nam2009
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,185 @@
1
+ # n8n-nodes-siliconflow-ai
2
+
3
+ 一个面向 [n8n](https://n8n.io) 的社区节点,封装 [SiliconFlow(硅基流动)](https://siliconflow.cn) 的 OpenAI 兼容 REST API:**Chat Completion**、**Embedding**、**Image Generation**、**Rerank**。
4
+
5
+ > 🎯 **核心目的**:解决 [QixYuanmeng/n8n-nodes-siliconflow](https://github.com/QixYuanmeng/n8n-nodes-siliconflow) 在最新 n8n 中因 `langchain` 与 `@langchain/core` 版本冲突(`ERESOLVE`)导致的安装失败问题。
6
+ >
7
+ > 本节点**零运行时三方依赖**(除 `n8n-workflow` 作为 peer dep),完全使用 n8n 内置 HTTP helper 直连 SiliconFlow,永不与宿主 n8n 冲突。
8
+
9
+ ---
10
+
11
+ ## 📦 安装
12
+
13
+ ### 方式 1:通过 n8n UI 安装(推荐)
14
+
15
+ 需要 n8n >= 1.0。
16
+
17
+ 1. 启动 n8n
18
+ 2. 打开 **Settings → Community Nodes**
19
+ 3. 点击 **Install**
20
+ 4. 输入包名:`n8n-nodes-siliconflow-ai`
21
+ 5. 同意风险提示 → Install
22
+
23
+ ### 方式 2:本地加载(无需发布到 npm)
24
+
25
+ 启动 n8n 前设置环境变量:
26
+
27
+ ```bash
28
+ # Linux / macOS / Git Bash
29
+ export N8N_CUSTOM_EXTENSIONS="/d/GITHUB/n8n-nodes-siliconflow-ai"
30
+
31
+ # Windows CMD
32
+ set N8N_CUSTOM_EXTENSIONS=D:\GITHUB\n8n-nodes-siliconflow-ai
33
+
34
+ # Windows PowerShell
35
+ $env:N8N_CUSTOM_EXTENSIONS="D:\GITHUB\n8n-nodes-siliconflow-ai"
36
+ ```
37
+
38
+ 或者把 `dist/` + `package.json` 复制到 `~/.n8n/custom/n8n-nodes-siliconflow-ai/`。
39
+
40
+ ### 方式 3:Docker 自定义镜像
41
+
42
+ ```dockerfile
43
+ FROM n8nio/n8n:latest
44
+ USER root
45
+ RUN mkdir -p /home/node/.n8n/custom/n8n-nodes-siliconflow-ai
46
+ COPY --chown=node:node dist /home/node/.n8n/custom/n8n-nodes-siliconflow-ai/dist
47
+ COPY --chown=node:node package.json /home/node/.n8n/custom/n8n-nodes-siliconflow-ai/
48
+ USER node
49
+ ```
50
+
51
+ ---
52
+
53
+ ## 🔑 凭证配置
54
+
55
+ 1. 登录 [SiliconFlow 控制台](https://cloud.siliconflow.cn/account/ak) 创建 API Key(新用户注册送 2000 万 token 免费额度)
56
+ 2. n8n → **Credentials → New → SiliconFlow API**
57
+ 3. 填写:
58
+ - **API Key**:你的密钥
59
+ - **Base URL**:默认 `https://api.siliconflow.cn/v1`(一般无需修改)
60
+ 4. 点击 **Test connection**,会请求 `/v1/models` 验证
61
+
62
+ ---
63
+
64
+ ## ✨ 支持的能力
65
+
66
+ | Resource | Operation | 端点 | 说明 |
67
+ |---|---|---|---|
68
+ | Chat Completion | Send Message | `POST /chat/completions` | 兼容 OpenAI 格式的对话 |
69
+ | Embedding | Create Embeddings | `POST /embeddings` | 文本向量化 |
70
+ | Image Generation | Generate | `POST /images/generations` | 文生图 |
71
+ | Rerank | Rerank Documents | `POST /rerank` | 文档重排序(RAG 后处理) |
72
+
73
+ ---
74
+
75
+ ## 🚀 典型用法
76
+
77
+ ### 1. 简易聊天工作流
78
+
79
+ ```
80
+ [Manual Trigger] → [SiliconFlow (Chat)] → [Set] → [Respond to Webhook]
81
+ ```
82
+
83
+ **SiliconFlow 节点配置:**
84
+ - Resource: `Chat Completion`
85
+ - Model: `Qwen/Qwen2.5-7B-Instruct`
86
+ - Messages: `=[{"role":"system","content":"你是一个翻译助手"},{"role":"user","content":"{{$json.text}}"}]`
87
+ - Additional Options → Temperature: `0.5`,Max Tokens: `1024`
88
+
89
+ **Set 节点**:把 `choices[0].message.content` 提取到 `output` 字段。
90
+
91
+ ### 2. 批量 Embedding
92
+
93
+ 把上游节点的 `texts` 字段传入:
94
+
95
+ - Resource: `Embedding`
96
+ - Model: `BAAI/bge-m3`
97
+ - Input: `={{$json.texts}}`(值为字符串数组)
98
+ - Additional Options → Encoding Format: `Float`
99
+
100
+ 下游节点用 `data[0].embedding` 取第一条向量。
101
+
102
+ ### 3. RAG 中的 Rerank 流程
103
+
104
+ ```
105
+ [Vector Store] → [SiliconFlow (Rerank)] → [Top-K 送入 LLM]
106
+ ```
107
+
108
+ - Resource: `Rerank`
109
+ - Model: `BAAI/bge-reranker-v2-m3`
110
+ - Query: `{{$json.question}}`
111
+ - Documents: `={{$json.docs}}`(如 `["doc1","doc2",...]` 或 `[{"text":"doc1"}]`)
112
+ - Top N: `3`
113
+
114
+ 输出 `results` 数组中每项含 `index`、`relevance_score` 和 `document`(开启 Return Documents 时)。
115
+
116
+ ### 4. 文生图
117
+
118
+ - Resource: `Image Generation`
119
+ - Model: `stabilityai/stable-diffusion-2-1`
120
+ - Prompt: `a cute cat astronaut, cinematic lighting`
121
+ - Image Size: `1024x1024`,Batch Size: `1`
122
+
123
+ 输出 `images[0].url` 即图片 URL(部分模型返回 base64)。
124
+
125
+ ---
126
+
127
+ ## 🧪 常见模型参考
128
+
129
+ | 用途 | 模型示例 |
130
+ |---|---|
131
+ | 对话 | `Qwen/Qwen2.5-7B-Instruct`、`deepseek-ai/DeepSeek-V3`、`THUDM/glm-4-9b-chat` |
132
+ | Embedding | `BAAI/bge-m3`、`Pro/BAAI/bge-m3` |
133
+ | 图像 | `stabilityai/stable-diffusion-2-1`、`black-forest-labs/FLUX.1-schnell` |
134
+ | Rerank | `BAAI/bge-reranker-v2-m3` |
135
+
136
+ 完整列表见 [SiliconFlow 模型广场](https://siliconflow.cn/models)。
137
+
138
+ ---
139
+
140
+ ## 🆚 与原 n8n-nodes-siliconflow 的区别
141
+
142
+ | 项 | 原项目 | 本项目 |
143
+ |---|---|---|
144
+ | 运行时依赖 | `langchain` + `@langchain/core` + `axios` + `zod` | **零**(只用 n8n 内置 helper) |
145
+ | 最新 n8n 安装 | ❌ `ERESOLVE` 报错 | ✅ 干净通过 |
146
+ | 包大小 | ~50MB+ | < 50KB |
147
+ | 维护难度 | 跟随 langchain 升级 | 跟随 SiliconFlow API 变更即可 |
148
+
149
+ ---
150
+
151
+ ## 🛠 开发
152
+
153
+ ```bash
154
+ # 克隆
155
+ git clone https://github.com/nam2009/n8n-nodes-siliconflow-ai.git
156
+ cd n8n-nodes-siliconflow-ai
157
+
158
+ # 安装开发依赖
159
+ npm install
160
+
161
+ # 编译
162
+ npm run build
163
+
164
+ # 监听模式(自动编译)
165
+ npm run dev
166
+ ```
167
+
168
+ 修改源码后建议在本地 n8n 验证(见上文「方式 2:本地加载」)。
169
+
170
+ ---
171
+
172
+ ## 📚 参考文档
173
+
174
+ - [SiliconFlow API 总览](https://docs.siliconflow.cn/cn/api-reference/authentication)
175
+ - [SiliconFlow Chat Completions](https://docs.siliconflow.cn/cn/api-reference/chat-completions/chat-completions)
176
+ - [SiliconFlow Embeddings](https://docs.siliconflow.cn/cn/api-reference/embeddings/embeddings)
177
+ - [SiliconFlow Images](https://docs.siliconflow.cn/cn/api-reference/images/images)
178
+ - [SiliconFlow Rerank](https://docs.siliconflow.cn/cn/api-reference/rerank/rerank)
179
+ - [n8n 社区节点开发指南](https://docs.n8n.io/integrations/community-nodes/building-community-nodes/)
180
+
181
+ ---
182
+
183
+ ## 📄 License
184
+
185
+ MIT
@@ -0,0 +1,15 @@
1
+ import { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
2
+ /**
3
+ * SiliconFlow(硅基流动)API 凭证
4
+ *
5
+ * 文档:https://docs.siliconflow.cn/cn/api-reference/authentication
6
+ * 获取 API Key:https://cloud.siliconflow.cn/account/ak
7
+ */
8
+ export declare class SiliconFlowApi implements ICredentialType {
9
+ name: string;
10
+ displayName: string;
11
+ documentationUrl: string;
12
+ properties: INodeProperties[];
13
+ authenticate: IAuthenticateGeneric;
14
+ test: ICredentialTestRequest;
15
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SiliconFlowApi = void 0;
4
+ /**
5
+ * SiliconFlow(硅基流动)API 凭证
6
+ *
7
+ * 文档:https://docs.siliconflow.cn/cn/api-reference/authentication
8
+ * 获取 API Key:https://cloud.siliconflow.cn/account/ak
9
+ */
10
+ class SiliconFlowApi {
11
+ constructor() {
12
+ this.name = 'siliconFlowApi';
13
+ this.displayName = 'SiliconFlow API';
14
+ this.documentationUrl = 'https://docs.siliconflow.cn/';
15
+ this.properties = [
16
+ {
17
+ displayName: 'API Key',
18
+ name: 'apiKey',
19
+ type: 'string',
20
+ typeOptions: {
21
+ password: true,
22
+ },
23
+ default: '',
24
+ required: true,
25
+ description: '在 https://cloud.siliconflow.cn/account/ak 创建的 API 密钥',
26
+ },
27
+ {
28
+ displayName: 'Base URL',
29
+ name: 'baseUrl',
30
+ type: 'string',
31
+ default: 'https://api.siliconflow.cn/v1',
32
+ required: true,
33
+ description: 'SiliconFlow OpenAI 兼容 API 根地址。国内用户一般无需修改;如使用海外站点请改为对应地址。',
34
+ },
35
+ ];
36
+ // 在每个请求头里注入 Bearer Token
37
+ this.authenticate = {
38
+ type: 'generic',
39
+ properties: {
40
+ headers: {
41
+ Authorization: '=Bearer {{$credentials.apiKey}}',
42
+ },
43
+ },
44
+ };
45
+ // 凭证测试:调用 /models 公开端点验证 Key 是否有效
46
+ this.test = {
47
+ request: {
48
+ baseURL: '={{$credentials.baseUrl}}',
49
+ url: '/models',
50
+ method: 'GET',
51
+ },
52
+ };
53
+ }
54
+ }
55
+ exports.SiliconFlowApi = SiliconFlowApi;
@@ -0,0 +1,18 @@
1
+ import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ /**
3
+ * SiliconFlow(硅基流动)AI 节点 v0.2.0
4
+ *
5
+ * 本节点使用 n8n 内置的 helpers.httpRequestWithAuthentication 直接调用 SiliconFlow
6
+ * 的 OpenAI 兼容 REST API,不引入任何 langchain 或 axios 等三方依赖,从而彻底规避
7
+ * 宿主 n8n 环境的 peer dependency 冲突。
8
+ *
9
+ * 支持的能力:
10
+ * - Chat Completion (POST /chat/completions)
11
+ * - Embedding (POST /embeddings)
12
+ * - Image Generation (POST /images/generations)
13
+ * - Rerank (POST /rerank)
14
+ */
15
+ export declare class SiliconFlow implements INodeType {
16
+ description: INodeTypeDescription;
17
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
18
+ }
@@ -0,0 +1,676 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SiliconFlow = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ /**
6
+ * SiliconFlow(硅基流动)AI 节点 v0.2.0
7
+ *
8
+ * 本节点使用 n8n 内置的 helpers.httpRequestWithAuthentication 直接调用 SiliconFlow
9
+ * 的 OpenAI 兼容 REST API,不引入任何 langchain 或 axios 等三方依赖,从而彻底规避
10
+ * 宿主 n8n 环境的 peer dependency 冲突。
11
+ *
12
+ * 支持的能力:
13
+ * - Chat Completion (POST /chat/completions)
14
+ * - Embedding (POST /embeddings)
15
+ * - Image Generation (POST /images/generations)
16
+ * - Rerank (POST /rerank)
17
+ */
18
+ class SiliconFlow {
19
+ constructor() {
20
+ this.description = {
21
+ displayName: 'SiliconFlow',
22
+ name: 'siliconFlow',
23
+ icon: 'file:siliconflow.svg',
24
+ group: ['transform'],
25
+ version: 1,
26
+ subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
27
+ description: '调用 SiliconFlow(硅基流动)AI 能力:Chat Completion / Embedding / Image / Rerank',
28
+ defaults: {
29
+ name: 'SiliconFlow',
30
+ },
31
+ inputs: ['main'],
32
+ outputs: ['main'],
33
+ credentials: [
34
+ {
35
+ name: 'siliconFlowApi',
36
+ required: true,
37
+ },
38
+ ],
39
+ requestDefaults: {
40
+ baseURL: '={{$credentials.baseUrl}}',
41
+ headers: {
42
+ 'Content-Type': 'application/json',
43
+ },
44
+ },
45
+ properties: [
46
+ // ============================================================
47
+ // Resource
48
+ // ============================================================
49
+ {
50
+ displayName: 'Resource',
51
+ name: 'resource',
52
+ type: 'options',
53
+ noDataExpression: true,
54
+ options: [
55
+ {
56
+ name: 'Chat Completion',
57
+ value: 'chat',
58
+ description: '与大语言模型对话(OpenAI 兼容)',
59
+ },
60
+ {
61
+ name: 'Embedding',
62
+ value: 'embedding',
63
+ description: '将文本转换为向量',
64
+ },
65
+ {
66
+ name: 'Image Generation',
67
+ value: 'image',
68
+ description: '文生图',
69
+ },
70
+ {
71
+ name: 'Rerank',
72
+ value: 'rerank',
73
+ description: '文档重排序(用于 RAG 检索后处理)',
74
+ },
75
+ ],
76
+ default: 'chat',
77
+ },
78
+ // ============================================================
79
+ // Chat 相关字段
80
+ // ============================================================
81
+ {
82
+ displayName: 'Operation',
83
+ name: 'operation',
84
+ type: 'options',
85
+ noDataExpression: true,
86
+ displayOptions: {
87
+ show: {
88
+ resource: ['chat'],
89
+ },
90
+ },
91
+ options: [
92
+ {
93
+ name: 'Send Message',
94
+ value: 'send',
95
+ action: 'Send a chat completion request',
96
+ },
97
+ ],
98
+ default: 'send',
99
+ },
100
+ {
101
+ displayName: 'Model',
102
+ name: 'model',
103
+ type: 'string',
104
+ default: 'Qwen/Qwen2.5-7B-Instruct',
105
+ required: true,
106
+ description: '模型 ID。完整列表:https://docs.siliconflow.cn/cn/api-reference/chat-completions/chat-completions',
107
+ displayOptions: {
108
+ show: {
109
+ resource: ['chat'],
110
+ },
111
+ },
112
+ },
113
+ {
114
+ displayName: 'Messages',
115
+ name: 'messages',
116
+ type: 'json',
117
+ default: '=[{"role":"user","content":"Hello"}]',
118
+ required: true,
119
+ description: 'OpenAI 格式的 messages 数组。支持 JSON 表达式,可引用上游数据,例如:[{"role":"user","content":"{{$json.text}}"}]',
120
+ displayOptions: {
121
+ show: {
122
+ resource: ['chat'],
123
+ },
124
+ },
125
+ },
126
+ {
127
+ displayName: 'Additional Options',
128
+ name: 'additionalOptions',
129
+ type: 'collection',
130
+ placeholder: 'Add Option',
131
+ default: {},
132
+ displayOptions: {
133
+ show: {
134
+ resource: ['chat'],
135
+ },
136
+ },
137
+ options: [
138
+ {
139
+ displayName: 'Temperature',
140
+ name: 'temperature',
141
+ type: 'number',
142
+ default: 0.7,
143
+ typeOptions: {
144
+ minValue: 0,
145
+ maxValue: 2,
146
+ },
147
+ description: '采样温度,0 表示更确定,2 表示更发散',
148
+ },
149
+ {
150
+ displayName: 'Max Tokens',
151
+ name: 'max_tokens',
152
+ type: 'number',
153
+ default: 512,
154
+ description: '生成的最大 token 数',
155
+ },
156
+ {
157
+ displayName: 'Top P',
158
+ name: 'top_p',
159
+ type: 'number',
160
+ default: 0.9,
161
+ typeOptions: {
162
+ minValue: 0,
163
+ maxValue: 1,
164
+ },
165
+ },
166
+ {
167
+ displayName: 'Stream',
168
+ name: 'stream',
169
+ type: 'boolean',
170
+ default: false,
171
+ description: '是否启用流式响应(启用时本节点仍按非流式处理,因为 n8n 不支持边收边发)',
172
+ },
173
+ ],
174
+ },
175
+ // ============================================================
176
+ // Embedding 相关字段
177
+ // ============================================================
178
+ {
179
+ displayName: 'Operation',
180
+ name: 'operation',
181
+ type: 'options',
182
+ noDataExpression: true,
183
+ displayOptions: {
184
+ show: {
185
+ resource: ['embedding'],
186
+ },
187
+ },
188
+ options: [
189
+ {
190
+ name: 'Create Embeddings',
191
+ value: 'create',
192
+ action: 'Create embeddings',
193
+ },
194
+ ],
195
+ default: 'create',
196
+ },
197
+ {
198
+ displayName: 'Model',
199
+ name: 'model',
200
+ type: 'string',
201
+ default: 'BAAI/bge-m3',
202
+ required: true,
203
+ description: 'Embedding 模型 ID,例如 BAAI/bge-m3、Pro/BAAI/bge-m3',
204
+ displayOptions: {
205
+ show: {
206
+ resource: ['embedding'],
207
+ },
208
+ },
209
+ },
210
+ {
211
+ displayName: 'Input',
212
+ name: 'input',
213
+ type: 'json',
214
+ default: '="hello"',
215
+ required: true,
216
+ description: '要编码的文本。可为单个字符串(如 "hello")或字符串数组(如 ["text1","text2"])。支持 JSON 表达式,可引用上游 {{$json.texts}}。',
217
+ displayOptions: {
218
+ show: {
219
+ resource: ['embedding'],
220
+ },
221
+ },
222
+ },
223
+ {
224
+ displayName: 'Additional Options',
225
+ name: 'additionalOptions',
226
+ type: 'collection',
227
+ placeholder: 'Add Option',
228
+ default: {},
229
+ displayOptions: {
230
+ show: {
231
+ resource: ['embedding'],
232
+ },
233
+ },
234
+ options: [
235
+ {
236
+ displayName: 'Encoding Format',
237
+ name: 'encoding_format',
238
+ type: 'options',
239
+ default: 'float',
240
+ options: [
241
+ { name: 'Float', value: 'float' },
242
+ { name: 'Base64', value: 'base64' },
243
+ ],
244
+ },
245
+ ],
246
+ },
247
+ // ============================================================
248
+ // Image Generation 相关字段
249
+ // ============================================================
250
+ {
251
+ displayName: 'Operation',
252
+ name: 'operation',
253
+ type: 'options',
254
+ noDataExpression: true,
255
+ displayOptions: {
256
+ show: {
257
+ resource: ['image'],
258
+ },
259
+ },
260
+ options: [
261
+ {
262
+ name: 'Generate',
263
+ value: 'generate',
264
+ action: 'Generate an image from a prompt',
265
+ },
266
+ ],
267
+ default: 'generate',
268
+ },
269
+ {
270
+ displayName: 'Model',
271
+ name: 'model',
272
+ type: 'string',
273
+ default: 'stabilityai/stable-diffusion-2-1',
274
+ required: true,
275
+ description: '图像模型 ID,例如 stabilityai/stable-diffusion-2-1、black-forest-labs/FLUX.1-schnell',
276
+ displayOptions: {
277
+ show: {
278
+ resource: ['image'],
279
+ },
280
+ },
281
+ },
282
+ {
283
+ displayName: 'Prompt',
284
+ name: 'prompt',
285
+ type: 'string',
286
+ typeOptions: {
287
+ rows: 3,
288
+ },
289
+ default: '',
290
+ required: true,
291
+ description: '用于生成图像的文本提示词',
292
+ displayOptions: {
293
+ show: {
294
+ resource: ['image'],
295
+ },
296
+ },
297
+ },
298
+ {
299
+ displayName: 'Additional Options',
300
+ name: 'additionalOptions',
301
+ type: 'collection',
302
+ placeholder: 'Add Option',
303
+ default: {},
304
+ displayOptions: {
305
+ show: {
306
+ resource: ['image'],
307
+ },
308
+ },
309
+ options: [
310
+ {
311
+ displayName: 'Negative Prompt',
312
+ name: 'negative_prompt',
313
+ type: 'string',
314
+ typeOptions: { rows: 2 },
315
+ default: '',
316
+ description: '不希望出现的元素描述',
317
+ },
318
+ {
319
+ displayName: 'Image Size',
320
+ name: 'image_size',
321
+ type: 'options',
322
+ default: '1024x1024',
323
+ options: [
324
+ { name: '512x512', value: '512x512' },
325
+ { name: '768x768', value: '768x768' },
326
+ { name: '1024x1024', value: '1024x1024' },
327
+ { name: '1024x576 (16:9)', value: '1024x576' },
328
+ { name: '576x1024 (9:16)', value: '576x1024' },
329
+ ],
330
+ },
331
+ {
332
+ displayName: 'Num Images',
333
+ name: 'batch_size',
334
+ type: 'number',
335
+ default: 1,
336
+ typeOptions: { minValue: 1, maxValue: 4 },
337
+ },
338
+ {
339
+ displayName: 'Seed',
340
+ name: 'seed',
341
+ type: 'number',
342
+ default: 0,
343
+ description: '随机种子;0 表示随机',
344
+ },
345
+ {
346
+ displayName: 'Guidance Scale',
347
+ name: 'guidance_scale',
348
+ type: 'number',
349
+ default: 7.5,
350
+ typeOptions: { minValue: 1, maxValue: 20 },
351
+ },
352
+ {
353
+ displayName: 'Num Inference Steps',
354
+ name: 'num_inference_steps',
355
+ type: 'number',
356
+ default: 20,
357
+ typeOptions: { minValue: 1, maxValue: 100 },
358
+ },
359
+ ],
360
+ },
361
+ // ============================================================
362
+ // Rerank 相关字段
363
+ // ============================================================
364
+ {
365
+ displayName: 'Operation',
366
+ name: 'operation',
367
+ type: 'options',
368
+ noDataExpression: true,
369
+ displayOptions: {
370
+ show: {
371
+ resource: ['rerank'],
372
+ },
373
+ },
374
+ options: [
375
+ {
376
+ name: 'Rerank Documents',
377
+ value: 'rerank',
378
+ action: 'Rerank documents against a query',
379
+ },
380
+ ],
381
+ default: 'rerank',
382
+ },
383
+ {
384
+ displayName: 'Model',
385
+ name: 'model',
386
+ type: 'string',
387
+ default: 'BAAI/bge-reranker-v2-m3',
388
+ required: true,
389
+ description: 'Rerank 模型 ID,例如 BAAI/bge-reranker-v2-m3',
390
+ displayOptions: {
391
+ show: {
392
+ resource: ['rerank'],
393
+ },
394
+ },
395
+ },
396
+ {
397
+ displayName: 'Query',
398
+ name: 'query',
399
+ type: 'string',
400
+ typeOptions: {
401
+ rows: 2,
402
+ },
403
+ default: '',
404
+ required: true,
405
+ description: '用户查询',
406
+ displayOptions: {
407
+ show: {
408
+ resource: ['rerank'],
409
+ },
410
+ },
411
+ },
412
+ {
413
+ displayName: 'Documents',
414
+ name: 'documents',
415
+ type: 'json',
416
+ default: '=[{"text":"示例文档 1"},{"text":"示例文档 2"}]',
417
+ required: true,
418
+ description: '文档数组。可以是字符串数组 ["doc1","doc2"] 或对象数组 [{"text":"doc1"}]。支持 JSON 表达式,可引用上游数据。',
419
+ displayOptions: {
420
+ show: {
421
+ resource: ['rerank'],
422
+ },
423
+ },
424
+ },
425
+ {
426
+ displayName: 'Additional Options',
427
+ name: 'additionalOptions',
428
+ type: 'collection',
429
+ placeholder: 'Add Option',
430
+ default: {},
431
+ displayOptions: {
432
+ show: {
433
+ resource: ['rerank'],
434
+ },
435
+ },
436
+ options: [
437
+ {
438
+ displayName: 'Top N',
439
+ name: 'top_n',
440
+ type: 'number',
441
+ default: 5,
442
+ description: '返回前 N 个最相关文档',
443
+ },
444
+ {
445
+ displayName: 'Return Documents',
446
+ name: 'return_documents',
447
+ type: 'boolean',
448
+ default: true,
449
+ description: '是否在响应中包含原始文档内容',
450
+ },
451
+ {
452
+ displayName: 'Max Chunks Per Doc',
453
+ name: 'max_chunks_per_doc',
454
+ type: 'number',
455
+ default: 0,
456
+ description: '每个文档的最大切分数(0 表示不切分)',
457
+ },
458
+ {
459
+ displayName: 'Overlap Tokens',
460
+ name: 'overlap_tokens',
461
+ type: 'number',
462
+ default: 80,
463
+ },
464
+ ],
465
+ },
466
+ ],
467
+ };
468
+ }
469
+ async execute() {
470
+ const items = this.getInputData();
471
+ const returnData = [];
472
+ const resource = this.getNodeParameter('resource', 0);
473
+ for (let i = 0; i < items.length; i++) {
474
+ try {
475
+ let responseData;
476
+ if (resource === 'chat') {
477
+ responseData = await handleChat(this, i);
478
+ }
479
+ else if (resource === 'embedding') {
480
+ responseData = await handleEmbedding(this, i);
481
+ }
482
+ else if (resource === 'image') {
483
+ responseData = await handleImage(this, i);
484
+ }
485
+ else if (resource === 'rerank') {
486
+ responseData = await handleRerank(this, i);
487
+ }
488
+ else {
489
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `未知的 Resource: ${resource}`, {
490
+ itemIndex: i,
491
+ });
492
+ }
493
+ const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(responseData), { itemData: { item: i } });
494
+ returnData.push(...executionData);
495
+ }
496
+ catch (error) {
497
+ if (this.continueOnFail()) {
498
+ returnData.push({
499
+ json: { error: error.message },
500
+ pairedItem: { item: i },
501
+ });
502
+ continue;
503
+ }
504
+ throw new n8n_workflow_1.NodeApiError(this.getNode(), error, { itemIndex: i });
505
+ }
506
+ }
507
+ return [returnData];
508
+ }
509
+ }
510
+ exports.SiliconFlow = SiliconFlow;
511
+ // ----------------------------------------------------------------
512
+ // Helper functions(独立于类外,正确接收 IExecuteFunctions 上下文)
513
+ // ----------------------------------------------------------------
514
+ /**
515
+ * Chat Completion 处理函数
516
+ */
517
+ async function handleChat(ctx, itemIndex) {
518
+ const model = ctx.getNodeParameter('model', itemIndex);
519
+ const messages = ctx.getNodeParameter('messages', itemIndex);
520
+ const additionalOptions = ctx.getNodeParameter('additionalOptions', itemIndex, {});
521
+ const body = {
522
+ model,
523
+ messages,
524
+ stream: false,
525
+ };
526
+ if (typeof additionalOptions.temperature === 'number') {
527
+ body.temperature = additionalOptions.temperature;
528
+ }
529
+ if (typeof additionalOptions.max_tokens === 'number') {
530
+ body.max_tokens = additionalOptions.max_tokens;
531
+ }
532
+ if (typeof additionalOptions.top_p === 'number') {
533
+ body.top_p = additionalOptions.top_p;
534
+ }
535
+ const requestOptions = {
536
+ method: 'POST',
537
+ url: '/chat/completions',
538
+ body,
539
+ json: true,
540
+ };
541
+ return (await ctx.helpers.httpRequestWithAuthentication.call(ctx, 'siliconFlowApi', requestOptions));
542
+ }
543
+ /**
544
+ * Embedding 处理函数
545
+ */
546
+ async function handleEmbedding(ctx, itemIndex) {
547
+ const model = ctx.getNodeParameter('model', itemIndex);
548
+ const inputRaw = ctx.getNodeParameter('input', itemIndex);
549
+ const additionalOptions = ctx.getNodeParameter('additionalOptions', itemIndex, {});
550
+ // input 可能是字符串或字符串数组;优先按 JSON 解析
551
+ let input;
552
+ if (typeof inputRaw === 'string') {
553
+ const trimmed = inputRaw.trim();
554
+ if (trimmed.startsWith('[')) {
555
+ try {
556
+ const parsed = JSON.parse(trimmed);
557
+ if (Array.isArray(parsed)) {
558
+ input = parsed;
559
+ }
560
+ else {
561
+ input = trimmed;
562
+ }
563
+ }
564
+ catch {
565
+ input = trimmed;
566
+ }
567
+ }
568
+ else {
569
+ input = trimmed;
570
+ }
571
+ }
572
+ else if (Array.isArray(inputRaw)) {
573
+ input = inputRaw;
574
+ }
575
+ else {
576
+ input = String(inputRaw);
577
+ }
578
+ const body = { model, input };
579
+ if (additionalOptions.encoding_format) {
580
+ body.encoding_format = additionalOptions.encoding_format;
581
+ }
582
+ const requestOptions = {
583
+ method: 'POST',
584
+ url: '/embeddings',
585
+ body,
586
+ json: true,
587
+ };
588
+ return (await ctx.helpers.httpRequestWithAuthentication.call(ctx, 'siliconFlowApi', requestOptions));
589
+ }
590
+ /**
591
+ * Image Generation 处理函数
592
+ */
593
+ async function handleImage(ctx, itemIndex) {
594
+ const model = ctx.getNodeParameter('model', itemIndex);
595
+ const prompt = ctx.getNodeParameter('prompt', itemIndex);
596
+ const additionalOptions = ctx.getNodeParameter('additionalOptions', itemIndex, {});
597
+ const body = {
598
+ model,
599
+ prompt,
600
+ image_size: additionalOptions.image_size ?? '1024x1024',
601
+ batch_size: additionalOptions.batch_size ?? 1,
602
+ };
603
+ if (additionalOptions.negative_prompt) {
604
+ body.negative_prompt = additionalOptions.negative_prompt;
605
+ }
606
+ if (typeof additionalOptions.seed === 'number' && additionalOptions.seed !== 0) {
607
+ body.seed = additionalOptions.seed;
608
+ }
609
+ if (typeof additionalOptions.guidance_scale === 'number') {
610
+ body.guidance_scale = additionalOptions.guidance_scale;
611
+ }
612
+ if (typeof additionalOptions.num_inference_steps === 'number') {
613
+ body.num_inference_steps = additionalOptions.num_inference_steps;
614
+ }
615
+ const requestOptions = {
616
+ method: 'POST',
617
+ url: '/images/generations',
618
+ body,
619
+ json: true,
620
+ };
621
+ return (await ctx.helpers.httpRequestWithAuthentication.call(ctx, 'siliconFlowApi', requestOptions));
622
+ }
623
+ /**
624
+ * Rerank 处理函数
625
+ */
626
+ async function handleRerank(ctx, itemIndex) {
627
+ const model = ctx.getNodeParameter('model', itemIndex);
628
+ const query = ctx.getNodeParameter('query', itemIndex);
629
+ const documentsRaw = ctx.getNodeParameter('documents', itemIndex);
630
+ const additionalOptions = ctx.getNodeParameter('additionalOptions', itemIndex, {});
631
+ // documents: 接受字符串数组或 {text:string} 数组
632
+ let documents;
633
+ if (typeof documentsRaw === 'string') {
634
+ const trimmed = documentsRaw.trim();
635
+ try {
636
+ const parsed = JSON.parse(trimmed);
637
+ if (Array.isArray(parsed)) {
638
+ documents = parsed;
639
+ }
640
+ else {
641
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'Documents 字段必须为 JSON 数组', {
642
+ itemIndex,
643
+ });
644
+ }
645
+ }
646
+ catch (err) {
647
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Documents 字段不是合法的 JSON: ${err.message}`, { itemIndex });
648
+ }
649
+ }
650
+ else if (Array.isArray(documentsRaw)) {
651
+ documents = documentsRaw;
652
+ }
653
+ else {
654
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'Documents 字段类型不支持', { itemIndex });
655
+ }
656
+ const body = {
657
+ model,
658
+ query,
659
+ documents,
660
+ top_n: additionalOptions.top_n ?? 5,
661
+ return_documents: additionalOptions.return_documents ?? true,
662
+ };
663
+ if (typeof additionalOptions.max_chunks_per_doc === 'number') {
664
+ body.max_chunks_per_doc = additionalOptions.max_chunks_per_doc;
665
+ }
666
+ if (typeof additionalOptions.overlap_tokens === 'number') {
667
+ body.overlap_tokens = additionalOptions.overlap_tokens;
668
+ }
669
+ const requestOptions = {
670
+ method: 'POST',
671
+ url: '/rerank',
672
+ body,
673
+ json: true,
674
+ };
675
+ return (await ctx.helpers.httpRequestWithAuthentication.call(ctx, 'siliconFlowApi', requestOptions));
676
+ }
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" width="60" height="60">
3
+ <defs>
4
+ <linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
5
+ <stop offset="0%" stop-color="#4F46E5"/>
6
+ <stop offset="100%" stop-color="#06B6D4"/>
7
+ </linearGradient>
8
+ </defs>
9
+ <rect x="2" y="2" width="56" height="56" rx="12" fill="url(#g)"/>
10
+ <text x="30" y="40" text-anchor="middle" font-family="Helvetica, Arial, sans-serif"
11
+ font-size="22" font-weight="700" fill="#ffffff">SF</text>
12
+ </svg>
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "n8n-nodes-siliconflow-ai",
3
+ "version": "0.2.0",
4
+ "description": "n8n community node for SiliconFlow AI (SiliconFlow / 硅基流动). Zero-dependency, direct REST API client. Supports Chat Completion, Embedding, Image Generation and Rerank.",
5
+ "keywords": [
6
+ "n8n",
7
+ "n8n-community-node-package",
8
+ "siliconflow",
9
+ "silicon-flow",
10
+ "硅基流动",
11
+ "ai",
12
+ "llm",
13
+ "embedding",
14
+ "image-generation",
15
+ "rerank"
16
+ ],
17
+ "license": "MIT",
18
+ "author": {
19
+ "name": "nam2009",
20
+ "email": "nam2009@users.noreply.github.com"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/nam2009/n8n-nodes-siliconflow-ai.git"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/nam2009/n8n-nodes-siliconflow-ai/issues"
28
+ },
29
+ "homepage": "https://github.com/nam2009/n8n-nodes-siliconflow-ai#readme",
30
+ "main": "index.js",
31
+ "files": [
32
+ "dist"
33
+ ],
34
+ "scripts": {
35
+ "build": "tsc && node scripts/copy-assets.js",
36
+ "build:watch": "tsc --watch",
37
+ "lint": "eslint nodes credentials --ext .ts",
38
+ "lint:fix": "eslint nodes credentials --ext .ts --fix",
39
+ "format": "prettier --write \"nodes/**/*.ts\" \"credentials/**/*.ts\"",
40
+ "prepublishOnly": "npm run build"
41
+ },
42
+ "engines": {
43
+ "node": ">=18.0.0"
44
+ },
45
+ "dependencies": {},
46
+ "devDependencies": {
47
+ "@types/node": "^20.10.0",
48
+ "n8n-workflow": "^1.0.0",
49
+ "typescript": "^5.4.0"
50
+ },
51
+ "peerDependencies": {
52
+ "n8n-workflow": "^1.0.0"
53
+ },
54
+ "n8n": {
55
+ "n8nNodesApiVersion": 1,
56
+ "credentials": [
57
+ "dist/credentials/SiliconFlowApi.credentials.js"
58
+ ],
59
+ "nodes": [
60
+ "dist/nodes/SiliconFlow/SiliconFlow.node.js"
61
+ ]
62
+ }
63
+ }