n8n-nodes-local-ai-stack
Version:
n8n custom nodes for AI services including image captionning, OCR, face detection, and more AI-powered features
238 lines (237 loc) • 11 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.AIHub = void 0;
class AIHub {
constructor() {
this.description = {
displayName: "Local AI Stack",
name: "localAIStack",
icon: "file:local_ai_stack.png",
group: ["transform"],
version: 1,
description: "Local AI Stack services",
subtitle: '={{ $parameter["services"] && $parameter["services"].length > 0 ' + '? $parameter["services"].join(", ") ' + ': "[Aucun service sélectionné]" }}',
defaults: {
name: "Local AI Stack",
},
inputs: ["main"],
outputs: ["main"],
properties: [
{
displayName: "🚀 Débloquez des fonctionnalités Premium : Audio Transcription, Vision LLM, Text-to-Speech, traitement d'images/vidéos avancé, Embeddings multimodaux, Vector DB et plus encore. <a href='https://tally.so/r/w76z7A' target='_blank'>Rejoignez la liste d'attente ici</a>",
name: "premiumNotice",
type: "notice",
default: "",
},
{
displayName: "Services",
name: "services",
type: "multiOptions",
options: [
{
name: "Extract Text from an image",
value: "ocr",
description: "Extract text from images using OCR",
},
{
name: "Describe an image",
value: "image_caption",
description: "Generate a caption describing the content of an image",
},
{
name: "Detect Faces from an image",
value: "faces",
description: "Detect and analyze faces in images",
},
{
name: "💎 Audio Transcription (Speech-to-Text)",
value: "audio_transcription",
description: "Premium feature - Not available in free tier",
},
{
name: "💎 Visual Question Answering (Vision LLM)",
value: "visual_qa",
description: "Premium feature - Not available in free tier",
},
{
name: "💎 Text-to-Speech",
value: "text_to_speech",
description: "Premium feature - Not available in free tier",
},
{
name: "💎 Image Utils (Upscale, Edit...)",
value: "image_utils",
description: "Premium feature - Not available in free tier",
},
{
name: "💎 Video Utils (Trim, Edit...)",
value: "video_utils",
description: "Premium feature - Not available in free tier",
},
{
name: "💎 Multimodal Embeddings (Text-Image Similarity...)",
value: "multimodal_embeddings",
description: "Premium feature - Not available in free tier",
},
{
name: "💎 Vector DB",
value: "vector_db",
description: "Premium feature - Not available in free tier",
},
],
default: ["ocr"],
required: true,
description: "Select one or more AI services to use (they will run in parallel)",
},
{
displayName: "Image Source",
name: "imageSource",
type: "options",
options: [
{
name: "URL",
value: "url",
},
{
name: "Upload File",
value: "file",
},
],
default: "url",
description: "Choose whether to provide an image URL or upload a file",
},
{
displayName: "Image URL",
name: "imageUrl",
type: "string",
default: "",
placeholder: "https://example.com/image.jpg",
required: true,
description: "URL of the image to send to the AI service",
displayOptions: {
show: {
imageSource: ["url"],
},
},
},
{
displayName: "Image File",
name: "imageFile",
type: "string",
default: "data",
required: true,
description: "The input binary field containing the image file to process",
displayOptions: {
show: {
imageSource: ["file"],
},
},
hint: "The name of the binary property that contains the image data",
},
{
displayName: "Server URL",
name: "serverUrl",
type: "string",
default: "http://127.0.0.1:9454",
placeholder: "http://127.0.0.1:9454",
required: false,
description: "AI-Hub server URL (optional)",
},
],
};
}
async execute() {
const items = this.getInputData();
const returnData = [];
// Liste des services premium
const premiumServices = ["audio_transcription", "visual_qa", "text_to_speech", "image_utils", "video_utils", "multimodal_embeddings", "vector_db"];
for (let i = 0; i < items.length; i++) {
try {
const serverUrl = this.getNodeParameter("serverUrl", i, "http://localhost:9454");
const services = this.getNodeParameter("services", i);
const imageSource = this.getNodeParameter("imageSource", i);
// Vérifier si des services premium sont sélectionnés
const selectedPremiumServices = services.filter((service) => premiumServices.includes(service));
if (selectedPremiumServices.length > 0) {
throw new Error("⚠️ Les services suivants ne sont disponibles qu'en version Premium : " +
selectedPremiumServices.join(", ") +
". " +
"Rejoignez notre liste d'attente pour être parmi les premiers à bénéficier de ces fonctionnalités : https://tally.so/r/w76z7A");
}
// Build the base URL
const baseUrl = serverUrl.endsWith("/") ? serverUrl.slice(0, -1) : serverUrl;
// Fonction pour exécuter un service
const executeService = async (service) => {
const fullUrl = `${baseUrl}/${service}/detect`;
if (imageSource === "url") {
// Use image URL
const imageUrl = this.getNodeParameter("imageUrl", i);
return await this.helpers.request({
method: "POST",
uri: fullUrl,
body: { image_url: imageUrl },
json: true,
});
}
else {
// Use uploaded file
const binaryPropertyName = this.getNodeParameter("imageFile", i);
const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName);
const buffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
return await this.helpers.request({
method: "POST",
uri: fullUrl,
formData: {
image_file: {
value: buffer,
options: {
filename: binaryData.fileName || "image.jpg",
contentType: binaryData.mimeType || "image/jpeg",
},
},
},
json: true,
});
}
};
// Exécuter tous les services en parallèle
const results = await Promise.all(services.map(async (service) => {
try {
const result = await executeService(service);
return { service, result, error: null };
}
catch (error) {
return { service, result: null, error: error.message };
}
}));
// Si une erreur est présente dans un service, arrêter et lancer une exception
for (const { service, error } of results) {
if (error) {
throw new Error(`Erreur lors de l'exécution du service '${service}': ${error}`);
}
}
// Si tout est OK, merger les résultats normalement
const mergedResults = {};
for (const { service, result } of results) {
mergedResults[service] = result;
}
returnData.push({
json: mergedResults,
});
}
catch (error) {
if (this.continueOnFail()) {
returnData.push({
json: {
error: error.message,
},
});
continue;
}
throw error;
}
}
return [returnData];
}
}
exports.AIHub = AIHub;