UNPKG

n8n-nodes-g4f

Version:

An n8n node to interact with the g4f (GPT-4-Free) API.

352 lines 18.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.G4f = void 0; const n8n_workflow_1 = require("n8n-workflow"); class G4f { constructor() { this.description = { displayName: 'G4f', name: 'g4f', icon: 'file:g4f.svg', group: ['ai'], version: 1, subtitle: '={{$parameter["operation"]}}', description: 'Interact with the g4f API to use various LLMs', defaults: { name: 'G4f', color: '#1A845F', }, inputs: ['main'], outputs: ['main'], credentials: [ { name: 'g4fApi', required: true, }, ], properties: [ { displayName: 'Operation', name: 'operation', type: 'options', noDataExpression: true, options: [ { name: 'Ask AI', value: 'ask', description: 'Chat with a language model', action: 'Ask AI' }, { name: 'Generate Image', value: 'image', description: 'Generate an image from a prompt', action: 'Generate an image' }, ], default: 'ask', }, { displayName: 'Provider', name: 'provider', type: 'options', displayOptions: { show: { operation: ['ask'], }, }, typeOptions: { loadOptionsMethod: 'getProviders', }, default: 'g4f.Provider.Auto', required: true, description: 'The provider to use. The list is loaded dynamically from your API.', }, { displayName: 'Model', name: 'model', type: 'options', displayOptions: { show: { operation: ['ask'], }, }, typeOptions: { loadOptionsMethod: 'getModels', loadOptionsDependsOn: ['provider'], }, default: '', required: true, description: 'The model to use. The list is loaded dynamically based on the selected provider.', }, { displayName: 'Messages', name: 'messages', type: 'fixedCollection', displayOptions: { show: { operation: ['ask'] } }, typeOptions: { multipleValues: true, }, default: { messageUi: [] }, options: [ { name: 'messageUi', displayName: 'Message', values: [ { displayName: 'Message', name: 'content', type: 'string', default: '', typeOptions: { multiline: true, rows: 5, }, description: 'The message content to send to the model', }, { displayName: 'Role', name: 'role', type: 'options', options: [ { name: 'User', value: 'user' }, { name: 'Assistant', value: 'assistant' }, { name: 'System', value: 'system' }, ], default: 'user', description: 'The role of the message sender', }, ], }, ], placeholder: 'Add Message', description: 'The conversation history to send to the model. The last message should be the user\'s prompt.', }, { displayName: 'Options', name: 'options', type: 'collection', displayOptions: { show: { operation: ['ask'] } }, placeholder: 'Add Option', default: {}, description: 'Additional options to control the model\'s output', options: [ { displayName: 'Stream', name: 'stream', type: 'boolean', default: false, description: 'Whether to stream the response. If true, the output will be a single string.' }, { displayName: 'Max Tokens', name: 'max_tokens', type: 'number', typeOptions: { minValue: 1 }, default: 2048, description: 'The maximum number of tokens to generate in the completion' }, { displayName: 'Temperature', name: 'temperature', type: 'number', typeOptions: { minValue: 0, maxValue: 2 }, default: 0.7, description: 'Controls randomness: lower is more deterministic.' }, { displayName: 'Top P', name: 'top_p', type: 'number', typeOptions: { minValue: 0, maxValue: 1 }, default: 1, description: 'Nucleus sampling: considers tokens with top_p probability mass.' }, { displayName: 'JSON Mode', name: 'jsonMode', type: 'boolean', default: false, description: 'Whether to enable JSON mode. The prompt should instruct to generate JSON.' }, ], }, ], }; this.methods = { loadOptions: { async getProviders() { try { const credentials = await this.getCredentials('g4fApi'); const baseUrl = credentials.baseUrl; const headers = { 'Accept': 'application/json', 'Content-Type': 'application/json' }; if (credentials.apiKey) { headers.Authorization = `Bearer ${credentials.apiKey}`; } console.log(`Making request to ${baseUrl}/v1/providers with headers:`, headers); const response = await this.helpers.request({ method: 'GET', url: `${baseUrl}/v1/providers`, headers, json: true, }); console.log('Providers response:', response); if (Array.isArray(response)) { return response.map((provider) => { if (typeof provider === 'string') { return { name: provider, value: provider }; } else if (provider && typeof provider === 'object') { const name = provider.name || provider.id || String(provider); const value = provider.id || provider.name || String(provider); return { name, value }; } return { name: String(provider), value: String(provider) }; }); } else if (response && typeof response === 'object' && Array.isArray(response.providers)) { return response.providers.map((provider) => { if (typeof provider === 'string') { return { name: provider, value: provider }; } else if (provider && typeof provider === 'object') { const name = provider.name || provider.id || String(provider); const value = provider.id || provider.name || String(provider); return { name, value }; } return { name: String(provider), value: String(provider) }; }); } return []; } catch (error) { console.error('Error fetching providers:', error); return []; } }, async getModels() { const provider = this.getNodeParameter('provider', ''); if (!provider) { return []; } try { const credentials = await this.getCredentials('g4fApi'); const baseUrl = credentials.baseUrl; const headers = { 'Accept': 'application/json', 'Content-Type': 'application/json' }; if (credentials.apiKey) { headers.Authorization = `Bearer ${credentials.apiKey}`; } console.log(`Making request to ${baseUrl}/api/${provider}/models with headers:`, headers); const response = await this.helpers.request({ method: 'GET', url: `${baseUrl}/api/${provider}/models`, headers, json: true, }); console.log('Models response:', response); if (Array.isArray(response)) { return response.map((model) => { if (typeof model === 'string') { return { name: model, value: model }; } else if (model && typeof model === 'object') { const name = model.name || model.id || String(model); const value = model.id || model.name || String(model); return { name, value }; } return { name: String(model), value: String(model) }; }); } else if (response && typeof response === 'object' && Array.isArray(response.models)) { return response.models.map((model) => { if (typeof model === 'string') { return { name: model, value: model }; } else if (model && typeof model === 'object') { const name = model.name || model.id || String(model); const value = model.id || model.name || String(model); return { name, value }; } return { name: String(model), value: String(model) }; }); } else if (response && typeof response === 'object' && Array.isArray(response.data)) { return response.data.map((model) => { if (typeof model === 'string') { return { name: model, value: model }; } else if (model && typeof model === 'object') { const name = model.name || model.id || String(model); const value = model.id || model.name || String(model); return { name, value }; } return { name: String(model), value: String(model) }; }); } return []; } catch (error) { console.error('Error fetching models:', error); return []; } }, }, }; } async execute() { const items = this.getInputData(); const returnData = []; const operation = this.getNodeParameter('operation', 0); const credentials = await this.getCredentials('g4fApi'); for (let i = 0; i < items.length; i++) { try { if (operation === 'ask') { const provider = this.getNodeParameter('provider', i); const model = this.getNodeParameter('model', i); const messagesCollection = this.getNodeParameter('messages', i, { messageUi: [] }); const options = this.getNodeParameter('options', i, {}); const messages = messagesCollection.messageUi || []; const body = { provider, model, messages, ...options }; if (options.jsonMode) body.response_format = { type: 'json_object' }; delete body.jsonMode; const headers = { 'Accept': 'application/json', 'Content-Type': 'application/json' }; if (credentials.apiKey) { headers.Authorization = `Bearer ${credentials.apiKey}`; } console.log(`Making chat completion request to ${credentials.baseUrl}/v1/chat/completions with headers:`, headers); console.log('Request body:', body); let json; try { const responseData = await this.helpers.request({ method: 'POST', url: `${credentials.baseUrl}/v1/chat/completions`, headers, body, json: !options.stream, returnStream: options.stream, }); console.log('Chat completion response:', options.stream ? 'Stream response' : responseData); if (options.stream) { const responseStream = responseData; let streamedContent = ''; for await (const chunk of responseStream) streamedContent += chunk.toString(); json = { role: 'assistant', content: streamedContent }; } else { if (responseData.choices && responseData.choices[0] && responseData.choices[0].message) { json = { ...responseData.choices[0].message, usage: responseData.usage, model: responseData.model, id: responseData.id, fullResponse: responseData }; } else if (responseData.content || responseData.text) { json = { role: 'assistant', content: responseData.content || responseData.text, fullResponse: responseData }; } else { json = { role: 'assistant', content: typeof responseData === 'string' ? responseData : JSON.stringify(responseData), fullResponse: responseData }; } } } catch (error) { console.error('Error in chat completion:', error); throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to get response from G4F API: ${error.message}`, { itemIndex: i }); } returnData.push({ json, pairedItem: { item: i } }); } else if (operation === 'image') { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Image generation is not yet implemented.'); } } catch (error) { if (this.continueOnFail()) { const json = this.getInputData(i)[0].json; returnData.push({ json: { ...json, error: error.message }, pairedItem: { item: i } }); continue; } throw error; } } return [returnData]; } } exports.G4f = G4f; //# sourceMappingURL=G4f.node.js.map