n8n-nodes-groq
Version:
N8N community node for Groq API - Speech-to-Text transcription using Whisper AI. Convert audio to text with high accuracy. Perfect for WhatsApp voice messages, audio files, and voice automation workflows.
342 lines (341 loc) • 14.5 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Groq = void 0;
const n8n_workflow_1 = require("n8n-workflow");
const groq_sdk_1 = __importDefault(require("groq-sdk"));
class Groq {
constructor() {
this.description = {
displayName: 'Groq Speech to Text',
name: 'groq',
icon: 'file:groq-icon-logo-png_seeklogo-605779.png',
group: ['transform'],
version: 1,
subtitle: '={{$parameter["operation"]}}',
description: 'Interact with Groq API for AI operations',
defaults: {
name: 'Groq Speech to Text',
},
inputs: ["main" /* NodeConnectionType.Main */],
outputs: ["main" /* NodeConnectionType.Main */],
credentials: [
{
name: 'groqApi',
required: true,
},
],
properties: [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Speech to Text',
value: 'speechToText',
description: 'Convert audio to text using Whisper model',
action: 'Convert speech to text',
},
],
default: 'speechToText',
},
{
displayName: 'Audio Input',
name: 'audioInput',
type: 'options',
displayOptions: {
show: {
operation: ['speechToText'],
},
},
options: [
{
name: 'Binary Data',
value: 'binaryData',
description: 'Use audio file from binary data',
},
{
name: 'URL',
value: 'url',
description: 'Download audio from URL',
},
],
default: 'binaryData',
},
{
displayName: 'Binary Property',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
required: true,
displayOptions: {
show: {
operation: ['speechToText'],
audioInput: ['binaryData'],
},
},
description: 'Name of the binary property containing the audio file',
},
{
displayName: 'Audio URL',
name: 'audioUrl',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
operation: ['speechToText'],
audioInput: ['url'],
},
},
description: 'URL of the audio file to transcribe',
},
{
displayName: 'Model',
name: 'model',
type: 'options',
displayOptions: {
show: {
operation: ['speechToText'],
},
},
options: [
{
name: 'Whisper Large V3 Turbo',
value: 'whisper-large-v3-turbo',
description: 'Fastest and most efficient model',
},
{
name: 'Whisper Large V3',
value: 'whisper-large-v3',
description: 'High accuracy model',
},
{
name: 'Distil Whisper Large V3 EN',
value: 'distil-whisper-large-v3-en',
description: 'Optimized model for English audio',
},
],
default: 'whisper-large-v3-turbo',
description: 'The model to use for transcription',
},
{
displayName: 'Response Format',
name: 'responseFormat',
type: 'options',
displayOptions: {
show: {
operation: ['speechToText'],
},
},
options: [
{
name: 'Text',
value: 'text',
description: 'Plain text output',
},
{
name: 'JSON',
value: 'json',
description: 'JSON format with metadata',
},
{
name: 'Verbose JSON',
value: 'verbose_json',
description: 'Detailed JSON with timestamps',
},
],
default: 'text',
description: 'Format of the transcription response',
},
{
displayName: 'Language',
name: 'language',
type: 'string',
displayOptions: {
show: {
operation: ['speechToText'],
},
},
default: '',
placeholder: 'en',
description: 'Language of the audio (ISO 639-1 code, e.g., "en" for English)',
},
{
displayName: 'Prompt (Optional)',
name: 'prompt',
type: 'string',
displayOptions: {
show: {
operation: ['speechToText'],
},
},
default: '',
description: 'Texto opcional para guiar o estilo do modelo ou continuar um segmento de áudio anterior',
},
{
displayName: 'Temperature (Optional)',
name: 'temperature',
type: 'number',
displayOptions: {
show: {
operation: ['speechToText'],
},
},
default: 0,
typeOptions: {
minValue: 0,
maxValue: 1,
numberPrecision: 1,
},
description: 'Sampling temperature between 0 and 1. Higher values make output more random.',
},
{
displayName: 'Timestamp Granularities',
name: 'timestampGranularities',
type: 'multiOptions',
displayOptions: {
show: {
operation: ['speechToText'],
responseFormat: ['verbose_json'],
},
},
options: [
{
name: 'Word',
value: 'word',
description: 'Word-level timestamps',
},
{
name: 'Segment',
value: 'segment',
description: 'Segment-level timestamps',
},
],
default: ['segment'],
description: 'Timestamp granularities to populate for this transcription',
},
],
};
}
async execute() {
const items = this.getInputData();
const returnData = [];
const credentials = await this.getCredentials('groqApi');
const groq = new groq_sdk_1.default({
apiKey: credentials.apiKey,
});
for (let i = 0; i < items.length; i++) {
let tempFilePath;
try {
const operation = this.getNodeParameter('operation', i);
if (operation === 'speechToText') {
const audioInput = this.getNodeParameter('audioInput', i);
const model = this.getNodeParameter('model', i);
const responseFormat = this.getNodeParameter('responseFormat', i);
const language = this.getNodeParameter('language', i);
const prompt = this.getNodeParameter('prompt', i);
const temperature = this.getNodeParameter('temperature', i);
let audioBuffer;
let filename = 'audio.wav';
if (audioInput === 'binaryData') {
const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i);
const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName);
audioBuffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
filename = binaryData.fileName || filename;
}
else {
// URL input - download the file
const audioUrl = this.getNodeParameter('audioUrl', i);
const response = await this.helpers.httpRequest({
method: 'GET',
url: audioUrl,
encoding: 'arraybuffer',
});
audioBuffer = Buffer.from(response);
filename = audioUrl.split('/').pop() || filename;
}
// Create a File-like object that works with FormData
const fs = require('fs');
const path = require('path');
const os = require('os');
// Create temporary file
const tempDir = os.tmpdir();
tempFilePath = path.join(tempDir, `groq_audio_${Date.now()}_${filename}`);
// Write buffer to temporary file
fs.writeFileSync(tempFilePath, audioBuffer);
// Create readable stream from file
const audioFile = fs.createReadStream(tempFilePath);
const transcriptionParams = {
file: audioFile,
model,
response_format: responseFormat,
};
if (language)
transcriptionParams.language = language;
if (prompt)
transcriptionParams.prompt = prompt;
if (temperature !== 0)
transcriptionParams.temperature = temperature;
if (responseFormat === 'verbose_json') {
const timestampGranularities = this.getNodeParameter('timestampGranularities', i);
if (timestampGranularities.length > 0) {
transcriptionParams.timestamp_granularities = timestampGranularities;
}
}
const transcription = await groq.audio.transcriptions.create(transcriptionParams);
// Clean up temporary file
try {
fs.unlinkSync(tempFilePath);
}
catch (cleanupError) {
// Ignore cleanup errors
}
returnData.push({
json: {
transcription,
model,
responseFormat,
filename,
},
pairedItem: {
item: i,
},
});
}
}
catch (error) {
// Clean up temporary file if it exists
if (typeof tempFilePath !== 'undefined') {
try {
const fs = require('fs');
fs.unlinkSync(tempFilePath);
}
catch (cleanupError) {
// Ignore cleanup errors
}
}
if (this.continueOnFail()) {
returnData.push({
json: {
error: error.message,
},
pairedItem: {
item: i,
},
});
continue;
}
throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, {
itemIndex: i,
});
}
}
return [returnData];
}
}
exports.Groq = Groq;