n8n-nodes-gemini-search
Version:
n8n nodes to interact with Google Gemini API for search and content generation
324 lines • 14.5 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GeminiSearch = void 0;
const GenericFunctions_1 = require("./GenericFunctions");
const axios_1 = __importDefault(require("axios"));
const instructionBuilder_1 = require("./instructionBuilder");
class GeminiSearch {
constructor() {
this.description = {
displayName: 'Gemini Search',
name: 'geminiSearch',
icon: 'file:gemini.svg',
group: ['transform'],
version: 1,
subtitle: '={{$parameter["operation"]}}',
description: 'Perform searches and generate content using Google Gemini API',
defaults: {
name: 'Gemini Search',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'geminiSearchApi',
required: true,
},
],
properties: [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Web Search',
value: 'webSearch',
description: 'Perform a web search using Gemini',
action: 'Perform a web search using Gemini',
},
{
name: 'Generate Content',
value: 'generateContent',
description: 'Generate content using Gemini',
action: 'Generate content using Gemini',
},
],
default: 'webSearch',
},
{
displayName: 'Model',
name: 'model',
type: 'options',
options: [
{
name: 'Gemini 1.0 Pro',
value: 'gemini-1.0-pro',
},
{
name: 'Gemini 1.5 Pro',
value: 'gemini-1.5-pro',
},
{
name: 'Gemini 2.0 Flash',
value: 'gemini-2.0-flash',
},
{
name: 'Gemini 2.0 Pro',
value: 'gemini-2.0-pro',
},
{
name: 'Gemini 2.5 Pro',
value: 'gemini-2.5-pro',
},
],
default: 'gemini-2.0-flash',
description: 'The Gemini model to use',
},
{
displayName: 'Prompt',
name: 'prompt',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: ['webSearch', 'generateContent'],
},
},
description: 'The prompt to send to Gemini',
},
{
displayName: 'Organization Context',
name: 'organization',
type: 'string',
default: '',
displayOptions: {
show: {
operation: ['webSearch'],
},
},
description: 'Optional organization name to use as context for search',
},
{
displayName: 'Restrict Search to URLs',
name: 'restrictUrls',
type: 'string',
default: '',
placeholder: 'example.com,docs.example.com',
displayOptions: {
show: {
operation: ['webSearch'],
},
},
description: 'Optional comma-separated list of URLs to restrict search to',
},
{
displayName: 'System Instruction',
name: 'systemInstruction',
type: 'string',
typeOptions: {
rows: 4,
},
default: '',
displayOptions: {
show: {
operation: ['webSearch', 'generateContent'],
},
},
description: 'Optional system instruction to guide the model behavior',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
options: [
{
displayName: 'Temperature',
name: 'temperature',
type: 'number',
typeOptions: {
minValue: 0,
maxValue: 1,
numberPrecision: 1,
},
default: 0.6,
description: 'Controls randomness in the response (0-1)',
},
{
displayName: 'Max Output Tokens',
name: 'maxOutputTokens',
type: 'number',
default: 2048,
description: 'Maximum number of tokens to generate',
},
{
displayName: 'Top P',
name: 'topP',
type: 'number',
default: 1,
description: 'Nucleus sampling parameter',
},
{
displayName: 'Top K',
name: 'topK',
type: 'number',
default: 1,
description: 'Top K sampling parameter',
},
{
displayName: 'Extract Source URL',
name: 'extractSourceUrl',
type: 'boolean',
default: false,
description: 'Whether to extract the source URL from the response',
},
],
},
],
};
}
async execute() {
var _a, _b, _c, _d, _e;
const items = this.getInputData();
const returnData = [];
for (let i = 0; i < items.length; i++) {
try {
const operation = this.getNodeParameter('operation', i);
const model = this.getNodeParameter('model', i);
const prompt = this.getNodeParameter('prompt', i);
const options = this.getNodeParameter('options', i, {});
const systemInstruction = this.getNodeParameter('systemInstruction', i, '');
const organization = operation === 'webSearch'
? this.getNodeParameter('organization', i, '')
: '';
const restrictUrls = operation === 'webSearch'
? this.getNodeParameter('restrictUrls', i, '')
: '';
const finalSystemInstruction = (0, instructionBuilder_1.buildSystemInstruction)({
systemInstruction,
organization,
restrictUrls,
});
const requestBody = {
contents: [
{
role: 'user',
parts: [
{
text: prompt,
},
],
},
],
generationConfig: {
temperature: options.temperature || 0.6,
topP: options.topP || 1,
topK: options.topK || 1,
maxOutputTokens: options.maxOutputTokens || 2048,
responseMimeType: 'text/plain',
},
};
if (operation === 'webSearch') {
requestBody.tools = [
{
googleSearch: {},
},
];
}
if (finalSystemInstruction) {
requestBody.systemInstruction = {
parts: [
{
text: finalSystemInstruction,
},
],
};
}
const response = await GenericFunctions_1.geminiRequest.call(this, model, requestBody);
const extractSourceUrl = (responseObj) => {
var _a, _b, _c, _d, _e, _f;
const possiblePaths = [
(_f = (_e = (_d = (_c = (_b = (_a = responseObj === null || responseObj === void 0 ? void 0 : responseObj.candidates) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.groundingMetadata) === null || _c === void 0 ? void 0 : _c.groundingChunks) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.web) === null || _f === void 0 ? void 0 : _f.uri,
];
return (possiblePaths.find((url) => typeof url === 'string' &&
(url.startsWith('http://') || url.startsWith('https://'))) || '');
};
const getRedirectedUrl = async (url) => {
var _a, _b, _c, _d;
if (!url)
return '';
try {
const response = await axios_1.default.head(url, {
maxRedirects: 10,
timeout: 5000,
validateStatus: null,
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
},
});
const finalUrl = ((_b = (_a = response.request) === null || _a === void 0 ? void 0 : _a.res) === null || _b === void 0 ? void 0 : _b.responseUrl) ||
((_c = response.request) === null || _c === void 0 ? void 0 : _c.responseURL) ||
(typeof response.request === 'object' && 'res' in response.request
? (_d = response.request.res.headers) === null || _d === void 0 ? void 0 : _d.location
: null) ||
url;
return finalUrl;
}
catch (error) {
console.error(`Error fetching redirected URL: ${error.message}`);
return url;
}
};
const outputJson = {
response: ((_e = (_d = (_c = (_b = (_a = response.candidates) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.content) === null || _c === void 0 ? void 0 : _c.parts) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.text) || '',
fullResponse: response,
};
if (operation === 'webSearch') {
const restrictUrls = this.getNodeParameter('restrictUrls', i, '');
if (restrictUrls) {
outputJson.restrictedUrls = restrictUrls;
}
}
if (options.extractSourceUrl) {
const sourceUrl = extractSourceUrl(response);
outputJson.sourceUrl = sourceUrl;
if (sourceUrl) {
try {
outputJson.redirectedSourceUrl = await getRedirectedUrl(sourceUrl);
}
catch (error) {
outputJson.redirectedSourceUrl = sourceUrl;
outputJson.redirectError = error.message;
}
}
}
returnData.push({
json: outputJson,
pairedItem: { item: i },
});
}
catch (error) {
if (this.continueOnFail()) {
returnData.push({
json: {
error: error.message,
},
pairedItem: { item: i },
});
continue;
}
throw error;
}
}
return [returnData];
}
}
exports.GeminiSearch = GeminiSearch;
//# sourceMappingURL=GeminiSearch.node.js.map