n8n-nodes-g4f
Version:
An n8n node to interact with the g4f (GPT-4-Free) API.
352 lines • 18.5 kB
JavaScript
;
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