n8n-nodes-siliconflow
Version:
n8n community node for SiliconFlow AI models - chat completions, vision language models, embeddings, and reranking
1,092 lines (1,091 loc) • 67 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SiliconFlow = void 0;
const n8n_workflow_1 = require("n8n-workflow");
const axios_1 = __importDefault(require("axios"));
class SiliconFlow {
constructor() {
this.description = {
displayName: 'SiliconFlow',
name: 'siliconFlow',
icon: 'file:siliconflow.svg',
group: ['transform'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Interact with SiliconFlow AI models',
defaults: {
name: 'SiliconFlow',
},
inputs: ["main" /* NodeConnectionType.Main */],
outputs: ["main" /* NodeConnectionType.Main */],
credentials: [
{
name: 'siliconFlowApi',
required: true,
},
],
requestDefaults: {
baseURL: '={{$credentials.baseUrl}}',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
},
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Chat',
value: 'chat',
},
{
name: 'Vision',
value: 'vision',
description: 'Vision language model with image understanding',
},
{
name: 'Embeddings',
value: 'embeddings',
},
{
name: 'Rerank',
value: 'rerank',
},
],
default: 'chat',
},
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['chat'],
},
},
options: [
{
name: 'Complete',
value: 'complete',
description: 'Create a chat completion',
action: 'Create a chat completion',
},
],
default: 'complete',
},
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['vision'],
},
},
options: [
{
name: 'Analyze',
value: 'analyze',
description: 'Analyze images with vision language model',
action: 'Analyze images with vision language model',
},
],
default: 'analyze',
},
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['embeddings'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create embeddings',
action: 'Create embeddings',
},
],
default: 'create',
},
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['rerank'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create rerank request',
action: 'Create rerank request',
},
],
default: 'create',
},
// Chat completion options
{
displayName: 'Model',
name: 'model',
type: 'options',
displayOptions: {
show: {
resource: ['chat'],
operation: ['complete'],
},
},
description: 'The model which will generate the completion. All models support tools calling.',
typeOptions: {
loadOptions: {
routing: {
request: {
method: 'GET',
url: '/models?sub_type=chat',
},
output: {
postReceive: [
{
type: 'rootProperty',
properties: {
property: 'data',
},
},
{
type: 'setKeyValue',
properties: {
name: '={{$responseItem.id}}',
value: '={{$responseItem.id}}',
},
},
{
type: 'sort',
properties: {
key: 'name',
},
},
],
},
},
},
},
default: 'THUDM/glm-4-plus',
required: true,
},
{
displayName: 'Messages',
name: 'messages',
type: 'fixedCollection',
displayOptions: {
show: {
resource: ['chat'],
operation: ['complete'],
},
},
default: {},
typeOptions: {
multipleValues: true,
},
options: [
{
name: 'messageValues',
displayName: 'Message',
values: [
{
displayName: 'Role',
name: 'role',
type: 'options',
options: [
{
name: 'System',
value: 'system',
},
{
name: 'User',
value: 'user',
},
{
name: 'Assistant',
value: 'assistant',
},
],
default: 'user',
},
{
displayName: 'Content',
name: 'content',
type: 'string',
default: '',
typeOptions: {
rows: 3,
},
},
],
},
],
},
{
displayName: 'Prompt',
name: 'prompt',
type: 'string',
displayOptions: {
show: {
resource: ['chat'],
operation: ['complete'],
},
},
default: '',
description: 'Simple prompt text (alternative to messages)',
typeOptions: {
rows: 3,
},
},
{
displayName: 'Output Mode',
name: 'outputMode',
type: 'options',
displayOptions: {
show: {
resource: ['chat'],
operation: ['complete'],
},
},
options: [
{
name: 'Simple (Message Only)',
value: 'simple',
description: 'Return only the message content as a string',
},
{
name: 'Detailed (With Metadata)',
value: 'detailed',
description: 'Return structured object with message, usage, and metadata',
},
],
default: 'simple',
description: 'Choose the output format',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['chat'],
operation: ['complete'],
},
},
options: [
{
displayName: 'Max Tokens',
name: 'max_tokens',
type: 'number',
default: 512,
typeOptions: {
minValue: 1,
maxValue: 16384,
},
description: 'The maximum number of tokens to generate (1-16384)',
},
{
displayName: 'Temperature',
name: 'temperature',
type: 'number',
default: 0.7,
typeOptions: {
minValue: 0,
maxValue: 2,
numberPrecision: 2,
},
description: 'Determines the degree of randomness in the response',
},
{
displayName: 'Top P',
name: 'top_p',
type: 'number',
default: 0.7,
typeOptions: {
minValue: 0,
maxValue: 1,
numberPrecision: 2,
},
description: 'The top_p (nucleus) parameter for dynamic choice adjustment',
},
{
displayName: 'Top K',
name: 'top_k',
type: 'number',
default: 50,
description: 'Top-k sampling parameter',
},
{
displayName: 'Min P',
name: 'min_p',
type: 'number',
default: 0.05,
typeOptions: {
minValue: 0,
maxValue: 1,
numberPrecision: 3,
},
description: 'Dynamic filtering threshold for Qwen3 models (0-1)',
},
{
displayName: 'Frequency Penalty',
name: 'frequency_penalty',
type: 'number',
default: 0.5,
typeOptions: {
numberPrecision: 2,
},
description: 'Frequency penalty parameter',
},
{
displayName: 'Number of Generations',
name: 'n',
type: 'number',
default: 1,
description: 'Number of generations to return',
},
{
displayName: 'Enable Thinking',
name: 'enable_thinking',
type: 'boolean',
default: true,
description: 'Switches between thinking and non-thinking modes (applies to Qwen3 and Hunyuan models)',
},
{
displayName: 'Thinking Budget',
name: 'thinking_budget',
type: 'number',
default: 4096,
typeOptions: {
minValue: 128,
maxValue: 32768,
},
description: 'Maximum tokens for chain-of-thought output (128-32768, applies to reasoning models)',
},
{
displayName: 'Stop Sequences',
name: 'stop',
type: 'string',
default: '',
description: 'Up to 4 sequences where the API will stop generating (comma-separated)',
},
{
displayName: 'Stream',
name: 'stream',
type: 'boolean',
default: true,
description: 'Whether to stream back partial progress as Server-Sent Events',
},
{
displayName: 'Response Format',
name: 'response_format',
type: 'fixedCollection',
default: {},
description: 'Format specification for the model output',
options: [
{
name: 'formatValues',
displayName: 'Format',
values: [
{
displayName: 'Type',
name: 'type',
type: 'options',
options: [
{
name: 'Text',
value: 'text',
},
{
name: 'JSON Object',
value: 'json_object',
},
],
default: 'text',
},
],
},
],
},
],
},
// Vision options
{
displayName: 'Model',
name: 'visionModel',
type: 'options',
displayOptions: {
show: {
resource: ['vision'],
operation: ['analyze'],
},
},
options: [
// Qwen VL 系列
{
name: 'Qwen2.5-VL-72B-Instruct (最强视觉理解)',
value: 'Qwen/Qwen2.5-VL-72B-Instruct',
},
{
name: 'Qwen2.5-VL-32B-Instruct (高性能)',
value: 'Qwen/Qwen2.5-VL-32B-Instruct',
},
{
name: 'QVQ-72B-Preview (视觉推理)',
value: 'Qwen/QVQ-72B-Preview',
},
{
name: 'Qwen2-VL-72B-Instruct',
value: 'Qwen/Qwen2-VL-72B-Instruct',
},
{
name: 'Qwen2-VL-7B-Instruct (Pro)',
value: 'Pro/Qwen/Qwen2-VL-7B-Instruct',
},
{
name: 'Qwen2.5-VL-7B-Instruct (Pro)',
value: 'Pro/Qwen/Qwen2.5-VL-7B-Instruct',
},
// DeepSeek VL2 系列
{
name: 'DeepSeek-VL2 (短上下文优化)',
value: 'deepseek-ai/deepseek-vl2',
},
],
default: 'Qwen/Qwen2.5-VL-32B-Instruct',
required: true,
description: 'The vision language model to use for image analysis',
},
{
displayName: 'Images',
name: 'images',
type: 'fixedCollection',
displayOptions: {
show: {
resource: ['vision'],
operation: ['analyze'],
},
},
default: {},
description: 'Images to analyze (supports binary data, URLs, or base64)',
typeOptions: {
multipleValues: true,
maxValue: 9,
},
options: [
{
name: 'imageValues',
displayName: 'Image',
values: [
{
displayName: 'Image Source',
name: 'imageSource',
type: 'options',
options: [
{
name: 'Binary Data',
value: 'binary',
description: 'Use binary data from previous node',
},
{
name: 'URL',
value: 'url',
description: 'Use image URL',
},
{
name: 'Base64',
value: 'base64',
description: 'Use base64 encoded image',
},
],
default: 'binary',
},
{
displayName: 'Binary Property',
name: 'binaryProperty',
type: 'string',
displayOptions: {
show: {
imageSource: ['binary'],
},
},
default: 'data',
description: 'Name of the binary property containing the image',
},
{
displayName: 'Image URL',
name: 'imageUrl',
type: 'string',
displayOptions: {
show: {
imageSource: ['url'],
},
},
default: '',
placeholder: 'https://example.com/image.jpg',
description: 'URL of the image to analyze',
},
{
displayName: 'Base64 Data',
name: 'base64Data',
type: 'string',
displayOptions: {
show: {
imageSource: ['base64'],
},
},
default: '',
description: 'Base64 encoded image data (without data:image prefix)',
typeOptions: {
rows: 4,
},
},
{
displayName: 'Image Format',
name: 'imageFormat',
type: 'options',
displayOptions: {
show: {
imageSource: ['binary', 'base64'],
},
},
options: [
{
name: 'Auto Detect',
value: 'auto',
},
{
name: 'JPEG',
value: 'jpeg',
},
{
name: 'PNG',
value: 'png',
},
{
name: 'WebP',
value: 'webp',
},
{
name: 'GIF',
value: 'gif',
},
],
default: 'auto',
description: 'Format of the image data',
},
{
displayName: 'Detail Level',
name: 'detail',
type: 'options',
options: [
{
name: 'Auto',
value: 'auto',
description: 'Automatic detail level selection',
},
{
name: 'Low',
value: 'low',
description: 'Low resolution (faster, cheaper)',
},
{
name: 'High',
value: 'high',
description: 'High resolution (slower, more detailed)',
},
],
default: 'auto',
description: 'Level of detail for image processing',
},
],
},
],
},
{
displayName: 'Prompt',
name: 'visionPrompt',
type: 'string',
displayOptions: {
show: {
resource: ['vision'],
operation: ['analyze'],
},
},
default: 'Describe what you see in this image.',
required: true,
description: 'Text prompt describing what you want to know about the image(s)',
typeOptions: {
rows: 3,
},
},
{
displayName: 'Additional Fields',
name: 'visionAdditionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['vision'],
operation: ['analyze'],
},
},
options: [
{
displayName: 'Max Tokens',
name: 'max_tokens',
type: 'number',
default: 1024,
typeOptions: {
minValue: 1,
maxValue: 8192,
},
description: 'Maximum number of tokens to generate',
},
{
displayName: 'Temperature',
name: 'temperature',
type: 'number',
default: 0.7,
typeOptions: {
minValue: 0,
maxValue: 2,
numberPrecision: 2,
},
description: 'Controls randomness in the response',
},
{
displayName: 'Top P',
name: 'top_p',
type: 'number',
default: 1,
typeOptions: {
minValue: 0,
maxValue: 1,
numberPrecision: 2,
},
description: 'Controls diversity via nucleus sampling',
},
{
displayName: 'Stream',
name: 'stream',
type: 'boolean',
default: false,
description: 'Whether to stream the response',
},
],
},
// Embeddings options
{
displayName: 'Model',
name: 'embeddingModel',
type: 'options',
displayOptions: {
show: {
resource: ['embeddings'],
operation: ['create'],
},
},
options: [
// BGE系列
{
name: 'BAAI/bge-large-zh-v1.5 (中文, 512 tokens)',
value: 'BAAI/bge-large-zh-v1.5',
},
{
name: 'BAAI/bge-large-en-v1.5 (英文, 512 tokens)',
value: 'BAAI/bge-large-en-v1.5',
},
{
name: 'BAAI/bge-m3 (多语言, 8192 tokens)',
value: 'BAAI/bge-m3',
},
{
name: 'Pro/BAAI/bge-m3 (多语言专业版, 8192 tokens)',
value: 'Pro/BAAI/bge-m3',
},
// Qwen嵌入系列
{
name: 'Qwen3-Embedding-8B (32768 tokens)',
value: 'Qwen/Qwen3-Embedding-8B',
},
{
name: 'Qwen3-Embedding-4B (32768 tokens)',
value: 'Qwen/Qwen3-Embedding-4B',
},
{
name: 'Qwen3-Embedding-0.6B (32768 tokens)',
value: 'Qwen/Qwen3-Embedding-0.6B',
},
// 网易有道
{
name: 'netease-youdao/bce-embedding-base_v1 (512 tokens)',
value: 'netease-youdao/bce-embedding-base_v1',
},
// 保留原有的sentence-transformers模型
{
name: 'sentence-transformers/all-MiniLM-L6-v2',
value: 'sentence-transformers/all-MiniLM-L6-v2',
},
],
default: 'BAAI/bge-large-zh-v1.5',
required: true,
description: 'The model to use for embeddings',
},
{
displayName: 'Input',
name: 'input',
type: 'string',
displayOptions: {
show: {
resource: ['embeddings'],
operation: ['create'],
},
},
default: '',
required: true,
description: 'Input text to embed',
typeOptions: {
rows: 3,
},
},
{
displayName: 'Additional Fields',
name: 'embeddingAdditionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['embeddings'],
operation: ['create'],
},
},
options: [
{
displayName: 'Encoding Format',
name: 'encoding_format',
type: 'options',
options: [
{
name: 'Float',
value: 'float',
},
{
name: 'Base64',
value: 'base64',
},
],
default: 'float',
description: 'The format to return the embeddings in',
},
],
},
// Rerank options
{
displayName: 'Model',
name: 'rerankModel',
type: 'options',
displayOptions: {
show: {
resource: ['rerank'],
operation: ['create'],
},
},
options: [
// Qwen重排序系列
{
name: 'Qwen3-Reranker-8B',
value: 'Qwen/Qwen3-Reranker-8B',
},
{
name: 'Qwen3-Reranker-4B',
value: 'Qwen/Qwen3-Reranker-4B',
},
{
name: 'Qwen3-Reranker-0.6B',
value: 'Qwen/Qwen3-Reranker-0.6B',
},
// BAAI BGE重排序系列
{
name: 'BAAI/bge-reranker-v2-m3',
value: 'BAAI/bge-reranker-v2-m3',
},
{
name: 'Pro/BAAI/bge-reranker-v2-m3 (专业版)',
value: 'Pro/BAAI/bge-reranker-v2-m3',
},
// 网易有道
{
name: 'netease-youdao/bce-reranker-base_v1',
value: 'netease-youdao/bce-reranker-base_v1',
},
],
default: 'BAAI/bge-reranker-v2-m3',
required: true,
description: 'The model to use for reranking',
},
{
displayName: 'Query',
name: 'query',
type: 'string',
displayOptions: {
show: {
resource: ['rerank'],
operation: ['create'],
},
},
default: '',
required: true,
description: 'The search query',
typeOptions: {
rows: 2,
},
},
{
displayName: 'Documents',
name: 'documents',
type: 'string',
displayOptions: {
show: {
resource: ['rerank'],
operation: ['create'],
},
},
default: '',
required: true,
description: 'Documents to rerank (one per line or comma-separated)',
typeOptions: {
rows: 5,
},
},
{
displayName: 'Additional Fields',
name: 'rerankAdditionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['rerank'],
operation: ['create'],
},
},
options: [
{
displayName: 'Top N',
name: 'top_n',
type: 'number',
default: 4,
typeOptions: {
minValue: 1,
},
description: 'Number of most relevant documents to return',
},
{
displayName: 'Return Documents',
name: 'return_documents',
type: 'boolean',
default: false,
description: 'Whether to include document text in the response',
},
{
displayName: 'Max Chunks Per Doc',
name: 'max_chunks_per_doc',
type: 'number',
default: 10,
description: 'Maximum chunks for long documents (BGE/YoudAo models only)',
},
{
displayName: 'Overlap Tokens',
name: 'overlap_tokens',
type: 'number',
default: 20,
typeOptions: {
maxValue: 80,
},
description: 'Token overlaps between chunks (BGE/YoudAo models only, max 80)',
},
],
},
],
};
}
async execute() {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
const items = this.getInputData();
const returnData = [];
const resource = this.getNodeParameter('resource', 0);
const operation = this.getNodeParameter('operation', 0);
const credentials = await this.getCredentials('siliconFlowApi');
for (let i = 0; i < items.length; i++) {
try {
if (resource === 'chat' && operation === 'complete') {
const model = this.getNodeParameter('model', i);
const prompt = this.getNodeParameter('prompt', i);
const messagesParam = this.getNodeParameter('messages', i);
const additionalFields = this.getNodeParameter('additionalFields', i);
let messages = [];
// Use messages if provided, otherwise use simple prompt
if ((messagesParam === null || messagesParam === void 0 ? void 0 : messagesParam.messageValues) && messagesParam.messageValues.length > 0) {
messages = messagesParam.messageValues;
}
else if (prompt) {
messages = [{ role: 'user', content: prompt }];
}
else {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Either messages or prompt must be provided');
}
const requestBody = {
model,
messages,
};
// Add optional parameters from additionalFields
if (additionalFields.max_tokens !== undefined) {
requestBody.max_tokens = additionalFields.max_tokens;
}
if (additionalFields.temperature !== undefined) {
requestBody.temperature = additionalFields.temperature;
}
if (additionalFields.top_p !== undefined) {
requestBody.top_p = additionalFields.top_p;
}
if (additionalFields.top_k !== undefined) {
requestBody.top_k = additionalFields.top_k;
}
if (additionalFields.min_p !== undefined) {
requestBody.min_p = additionalFields.min_p;
}
if (additionalFields.frequency_penalty !== undefined) {
requestBody.frequency_penalty = additionalFields.frequency_penalty;
}
if (additionalFields.n !== undefined) {
requestBody.n = additionalFields.n;
}
if (additionalFields.enable_thinking !== undefined) {
requestBody.enable_thinking = additionalFields.enable_thinking;
}
if (additionalFields.thinking_budget !== undefined) {
requestBody.thinking_budget = additionalFields.thinking_budget;
}
if (additionalFields.stream !== undefined) {
requestBody.stream = additionalFields.stream;
}
if (additionalFields.stop && additionalFields.stop.trim()) {
// Convert comma-separated string to array
requestBody.stop = additionalFields.stop
.split(',')
.map((s) => s.trim())
.filter((s) => s);
}
if (additionalFields.response_format && additionalFields.response_format.formatValues) {
requestBody.response_format = {
type: additionalFields.response_format.formatValues.type,
};
}
const response = await axios_1.default.post(`${credentials.baseUrl}/chat/completions`, requestBody, {
headers: {
Authorization: `Bearer ${credentials.apiKey}`,
'Content-Type': 'application/json',
},
});
// Extract and format the response data
const responseData = response.data;
const choice = (_a = responseData.choices) === null || _a === void 0 ? void 0 : _a[0];
if (!choice) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No response received from the model');
}
// Check if user wants simple output (just the message content)
const outputMode = this.getNodeParameter('outputMode', i, 'simple');
let outputData;
if (outputMode === 'simple') {
// Simple output - just the message content for easy use in workflows
outputData = ((_b = choice.message) === null || _b === void 0 ? void 0 : _b.content) || '';
}
else {
// Detailed output - structured data with metadata
outputData = {
// Main content - what users typically want
message: ((_c = choice.message) === null || _c === void 0 ? void 0 : _c.content) || '',
// Additional useful information
model: responseData.model,
finishReason: choice.finish_reason,
// Usage statistics
usage: responseData.usage,
// Include reasoning content if available (for reasoning models)
...(((_d = choice.message) === null || _d === void 0 ? void 0 : _d.reasoning_content) && {
reasoning: choice.message.reasoning_content,
}),
// Include tool calls if any
...(((_e = choice.message) === null || _e === void 0 ? void 0 : _e.tool_calls) && {
toolCalls: choice.message.tool_calls,
}),
// Raw response for advanced users (can be hidden in UI)
_rawResponse: responseData,
};
}
returnData.push({
json: outputData,
pairedItem: { item: i },
});
}
else if (resource === 'vision' && operation === 'analyze') {
const model = this.getNodeParameter('visionModel', i);
const prompt = this.getNodeParameter('visionPrompt', i);
const imagesParam = this.getNodeParameter('images', i);
const additionalFields = this.getNodeParameter('visionAdditionalFields', i);
// Build content array for the message
const content = [];
// Process images first
if ((imagesParam === null || imagesParam === void 0 ? void 0 : imagesParam.imageValues) && imagesParam.imageValues.length > 0) {
for (const imageConfig of imagesParam.imageValues) {
const { imageSource, detail = 'auto' } = imageConfig;
let imageUrl = '';
if (imageSource === 'url') {
imageUrl = imageConfig.imageUrl;
if (!imageUrl) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Image URL is required when using URL source');
}
}
else if (imageSource === 'base64') {
const base64Data = imageConfig.base64Data;
const imageFormat = imageConfig.imageFormat || 'auto';
if (!base64Data) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Base64 data is required when using base64 source');
}
// Clean base64 data (remove whitespace and data URL prefix if present)
let cleanedBase64 = base64Data.trim();
// Remove data URL prefix if present
if (cleanedBase64.startsWith('data:')) {
const base64Index = cleanedBase64.indexOf('base64,');
if (base64Index !== -1) {
cleanedBase64 = cleanedBase64.substring(base64Index + 7);
}
}
// Remove any remaining whitespace
cleanedBase64 = cleanedBase64.replace(/\s/g, '');
// Validate base64 format
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(cleanedBase64)) {