@rexdug7005/nvidia-llama4
Version:
Integración de NVIDIA Llama4 con LangChain.js, con soporte para Tools Agent de n8n
133 lines (132 loc) • 4.61 kB
JavaScript
import { Embeddings } from "@langchain/core/embeddings";
import axios from "axios";
/**
* Implementación de Embeddings de NVIDIA para LangChain
*/
export class NvidiaEmbeddings extends Embeddings {
constructor(fields) {
super(fields);
Object.defineProperty(this, "apiKey", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "baseUrl", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "modelName", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "inputType", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "encodingFormat", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "truncate", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "maxRetries", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "defaultOptions", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this.apiKey = fields.apiKey;
this.baseUrl =
fields.baseUrl || "https://integrate.api.nvidia.com/v1/embeddings";
this.modelName = fields.model || "nvidia/nv-embedcode-7b-v1";
this.inputType = fields.inputType || "query";
this.encodingFormat = fields.encodingFormat || "float";
this.truncate = fields.truncate || "NONE";
this.maxRetries = fields.maxRetries ?? 3;
// Extraer las opciones que no son parte de la configuración principal
const keysToExclude = [
"apiKey",
"baseUrl",
"model",
"inputType",
"encodingFormat",
"truncate",
"maxRetries",
];
// Creamos un objeto con todas las propiedades que no son de configuración principal
this.defaultOptions = Object.fromEntries(Object.entries(fields).filter(([key]) => !keysToExclude.includes(key)));
}
/**
* Método para realizar la llamada a la API con reintentos
*/
async embeddingWithRetry(text) {
const texts = Array.isArray(text) ? text : [text];
// Preparar payload para la API
const payload = {
model: this.modelName,
input: texts,
input_type: this.inputType,
encoding_format: this.encodingFormat,
truncate: this.truncate,
...this.defaultOptions,
};
// Opciones para la petición
const requestOptions = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${this.apiKey}`,
},
};
// Implementación de backoff exponencial para reintentos
let error = "";
for (let i = 0; i < this.maxRetries; i += 1) {
try {
const response = await axios.post(this.baseUrl, payload, requestOptions);
return response.data.data.map((item) => item.embedding);
}
catch (err) {
error = String(err);
// Esperar antes de reintentar (backoff exponencial)
const waitTime = 2 ** i * 1000 + Math.random() * 100;
// eslint-disable-next-line no-await-in-loop
await new Promise((resolve) => {
setTimeout(resolve, waitTime);
});
}
}
// Si llegamos aquí, todos los reintentos fallaron
throw new Error(`Error al generar embeddings después de ${this.maxRetries} intentos: ${error}`);
}
/**
* Generar embedding para un solo texto
*/
async embedQuery(text) {
const embeddings = await this.embeddingWithRetry(text);
return embeddings[0];
}
/**
* Generar embeddings para múltiples textos
*/
async embedDocuments(documents) {
return this.embeddingWithRetry(documents);
}
}