n8n-nodes-groq-speech
Version:
N8N Community Node for Groq Text-to-Speech API integration
364 lines (363 loc) • 16.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.GroqTts = void 0;
const n8n_workflow_1 = require("n8n-workflow");
// Using fetch directly for TTS API calls
class GroqTts {
constructor() {
this.description = {
displayName: 'Groq TTS',
name: 'groqTts',
icon: 'file:groq-icon-logo-png_seeklogo-605779.png',
group: ['transform'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Convert text to speech using Groq API',
defaults: {
name: 'Groq TTS',
},
inputs: ["main" /* NodeConnectionType.Main */],
outputs: ["main" /* NodeConnectionType.Main */],
credentials: [
{
name: 'groqApi',
required: true,
},
],
requestDefaults: {
baseURL: 'https://api.groq.com/openai/v1',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
},
properties: [
{
displayName: '⚠️ First Time Setup Required',
name: 'setupNotice',
type: 'notice',
default: '',
displayOptions: {
show: {},
},
typeOptions: {
theme: 'warning',
},
},
{
displayName: 'Before first use, you must accept model terms at: https://console.groq.com/playground?model=playai-tts (one-time setup per Groq account)',
name: 'setupInstructions',
type: 'notice',
default: '',
displayOptions: {
show: {},
},
},
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Audio',
value: 'audio',
},
],
default: 'audio',
},
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['audio'],
},
},
options: [
{
name: 'Text to Speech',
value: 'textToSpeech',
action: 'Convert text to speech',
description: 'Convert text to speech using Groq TTS',
},
],
default: 'textToSpeech',
},
{
displayName: 'Text',
name: 'text',
type: 'string',
displayOptions: {
show: {
resource: ['audio'],
operation: ['textToSpeech'],
},
},
default: '',
placeholder: 'Enter the text to convert to speech',
description: 'The text to convert to speech',
required: true,
},
{
displayName: 'Model',
name: 'model',
type: 'options',
displayOptions: {
show: {
resource: ['audio'],
operation: ['textToSpeech'],
},
},
options: [
{
name: 'PlayAI TTS',
value: 'playai-tts',
},
{
name: 'PlayAI TTS Arabic',
value: 'playai-tts-arabic',
},
],
default: 'playai-tts',
description: 'The TTS model to use. Note: You must accept the model terms at https://console.groq.com/playground before first use.',
},
{
displayName: 'Voice',
name: 'voice',
type: 'options',
displayOptions: {
show: {
resource: ['audio'],
operation: ['textToSpeech'],
},
},
options: [
{ name: 'Arista', value: 'Arista-PlayAI' },
{ name: 'Atlas', value: 'Atlas-PlayAI' },
{ name: 'Basil', value: 'Basil-PlayAI' },
{ name: 'Briggs', value: 'Briggs-PlayAI' },
{ name: 'Calum', value: 'Calum-PlayAI' },
{ name: 'Celeste', value: 'Celeste-PlayAI' },
{ name: 'Cheyenne', value: 'Cheyenne-PlayAI' },
{ name: 'Chip', value: 'Chip-PlayAI' },
{ name: 'Cillian', value: 'Cillian-PlayAI' },
{ name: 'Deedee', value: 'Deedee-PlayAI' },
{ name: 'Fritz', value: 'Fritz-PlayAI' },
{ name: 'Gail', value: 'Gail-PlayAI' },
{ name: 'Indigo', value: 'Indigo-PlayAI' },
{ name: 'Mamaw', value: 'Mamaw-PlayAI' },
{ name: 'Mason', value: 'Mason-PlayAI' },
{ name: 'Mikail', value: 'Mikail-PlayAI' },
{ name: 'Mitch', value: 'Mitch-PlayAI' },
{ name: 'Quinn', value: 'Quinn-PlayAI' },
{ name: 'Thunder', value: 'Thunder-PlayAI' },
],
default: 'Fritz-PlayAI',
description: 'The voice to use for speech synthesis',
},
{
displayName: 'Response Format',
name: 'responseFormat',
type: 'options',
displayOptions: {
show: {
resource: ['audio'],
operation: ['textToSpeech'],
},
},
options: [
{
name: 'MP3',
value: 'mp3',
},
{
name: 'WAV',
value: 'wav',
},
{
name: 'FLAC',
value: 'flac',
},
{
name: 'OGG',
value: 'ogg',
},
],
default: 'mp3',
description: 'The audio format for the output',
},
{
displayName: 'Speed',
name: 'speed',
type: 'number',
displayOptions: {
show: {
resource: ['audio'],
operation: ['textToSpeech'],
},
},
default: 1.0,
typeOptions: {
minValue: 0.25,
maxValue: 4.0,
numberStepSize: 0.25,
},
description: 'The speed of the generated audio. Select a value from 0.25 to 4.0. 1.0 is the default.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['audio'],
operation: ['textToSpeech'],
},
},
options: [
{
displayName: 'Output File Name',
name: 'fileName',
type: 'string',
default: 'speech',
description: 'Name for the output audio file (without extension)',
},
{
displayName: 'Include Base64 Output',
name: 'includeBase64',
type: 'boolean',
default: false,
description: 'Include base64 encoded audio in JSON output (useful for WhatsApp APIs)',
},
],
},
],
};
}
async execute() {
var _a, _b;
const items = this.getInputData();
const returnData = [];
const resource = this.getNodeParameter('resource', 0);
const operation = this.getNodeParameter('operation', 0);
for (let i = 0; i < items.length; i++) {
try {
if (resource === 'audio') {
if (operation === 'textToSpeech') {
// Get credentials
const credentials = await this.getCredentials('groqApi');
// Note: We use fetch directly instead of groq-sdk for TTS
// Get parameters
const text = this.getNodeParameter('text', i);
const model = this.getNodeParameter('model', i);
const voice = this.getNodeParameter('voice', i);
const responseFormat = this.getNodeParameter('responseFormat', i);
const speed = this.getNodeParameter('speed', i);
const additionalFields = this.getNodeParameter('additionalFields', i);
if (!text) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Text is required for text-to-speech conversion');
}
// Prepare request body
const requestBody = {
model: model,
voice: voice,
input: text,
};
// Add optional parameters
if (responseFormat !== 'mp3') {
requestBody.response_format = responseFormat;
}
if (speed !== 1.0) {
requestBody.speed = speed;
}
// Make API call using fetch directly
const response = await fetch('https://api.groq.com/openai/v1/audio/speech', {
method: 'POST',
headers: {
'Authorization': `Bearer ${credentials.apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody),
});
if (!response.ok) {
const errorText = await response.text();
let errorMessage = `Groq API error: ${response.status} ${response.statusText}`;
try {
const errorData = JSON.parse(errorText);
if (((_a = errorData.error) === null || _a === void 0 ? void 0 : _a.code) === 'model_terms_required') {
errorMessage = `🔐 SETUP REQUIRED: You need to accept terms for the "${model}" model.\n\n` +
`👉 Click here: https://console.groq.com/playground?model=${model}\n` +
`📋 Steps: 1) Login to Groq 2) Select the model 3) Accept terms when prompted\n` +
`✅ This is a one-time setup per Groq account.`;
}
else if ((_b = errorData.error) === null || _b === void 0 ? void 0 : _b.message) {
errorMessage = `Groq API error: ${errorData.error.message}`;
}
}
catch {
errorMessage += ` - ${errorText}`;
}
throw new n8n_workflow_1.NodeOperationError(this.getNode(), errorMessage);
}
// Convert response to buffer
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
// Prepare file name
const fileName = additionalFields.fileName || 'speech';
const fullFileName = `${fileName}.${responseFormat}`;
// Create binary data
const binaryData = await this.helpers.prepareBinaryData(buffer, fullFileName, `audio/${responseFormat}`);
// Prepare JSON data
const jsonData = {
success: true,
fileName: fullFileName,
format: responseFormat,
voice: voice,
model: model,
speed: speed,
textLength: text.length,
binaryDataSize: buffer.length,
};
// Add base64 if requested
if (additionalFields.includeBase64 === true) {
const base64Audio = buffer.toString('base64');
jsonData.audioBase64 = base64Audio;
jsonData.audioDataUri = `data:audio/${responseFormat};base64,${base64Audio}`;
jsonData.base64Enabled = true;
}
else {
jsonData.base64Enabled = false;
}
// Ensure binary data is properly set
const newItem = {
json: jsonData,
binary: {
data: binaryData,
},
};
returnData.push(newItem);
}
}
}
catch (error) {
if (this.continueOnFail()) {
returnData.push({
json: {
error: error.message,
success: false,
},
});
continue;
}
throw error;
}
}
return [returnData];
}
}
exports.GroqTts = GroqTts;