mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 16:30:15 +01:00
🔊 fix: Validate language format for OpenAI STT model (#10875)
Some checks are pending
Publish `@librechat/client` to NPM / build-and-publish (push) Waiting to run
Docker Dev Images Build / build (Dockerfile, librechat-dev, node) (push) Waiting to run
Docker Dev Images Build / build (Dockerfile.multi, librechat-dev-api, api-build) (push) Waiting to run
Sync Locize Translations & Create Translation PR / Sync Translation Keys with Locize (push) Waiting to run
Sync Locize Translations & Create Translation PR / Create Translation PR on Version Published (push) Blocked by required conditions
Some checks are pending
Publish `@librechat/client` to NPM / build-and-publish (push) Waiting to run
Docker Dev Images Build / build (Dockerfile, librechat-dev, node) (push) Waiting to run
Docker Dev Images Build / build (Dockerfile.multi, librechat-dev-api, api-build) (push) Waiting to run
Sync Locize Translations & Create Translation PR / Sync Translation Keys with Locize (push) Waiting to run
Sync Locize Translations & Create Translation PR / Create Translation PR on Version Published (push) Blocked by required conditions
* 🔊 fix: Validate language format for OpenAI STT model
* fix: Normalize input language model assignment in STTService
* refactor: Enhance error logging and language validation in STT and TTS services
* fix: Improve language validation in getValidatedLanguageCode function
This commit is contained in:
parent
11923b9b96
commit
5879b3f518
2 changed files with 49 additions and 17 deletions
|
|
@ -3,8 +3,8 @@ const fs = require('fs').promises;
|
||||||
const FormData = require('form-data');
|
const FormData = require('form-data');
|
||||||
const { Readable } = require('stream');
|
const { Readable } = require('stream');
|
||||||
const { logger } = require('@librechat/data-schemas');
|
const { logger } = require('@librechat/data-schemas');
|
||||||
const { genAzureEndpoint } = require('@librechat/api');
|
|
||||||
const { HttpsProxyAgent } = require('https-proxy-agent');
|
const { HttpsProxyAgent } = require('https-proxy-agent');
|
||||||
|
const { genAzureEndpoint, logAxiosError } = require('@librechat/api');
|
||||||
const { extractEnvVariable, STTProviders } = require('librechat-data-provider');
|
const { extractEnvVariable, STTProviders } = require('librechat-data-provider');
|
||||||
const { getAppConfig } = require('~/server/services/Config');
|
const { getAppConfig } = require('~/server/services/Config');
|
||||||
|
|
||||||
|
|
@ -35,6 +35,34 @@ const MIME_TO_EXTENSION_MAP = {
|
||||||
'audio/x-flac': 'flac',
|
'audio/x-flac': 'flac',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates and extracts ISO-639-1 language code from a locale string.
|
||||||
|
* @param {string} language - The language/locale string (e.g., "en-US", "en", "zh-CN")
|
||||||
|
* @returns {string|null} The ISO-639-1 language code (e.g., "en") or null if invalid
|
||||||
|
*/
|
||||||
|
function getValidatedLanguageCode(language) {
|
||||||
|
try {
|
||||||
|
if (!language) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedLanguage = language.toLowerCase();
|
||||||
|
const isValidLocaleCode = /^[a-z]{2}(-[a-z]{2})?$/.test(normalizedLanguage);
|
||||||
|
|
||||||
|
if (isValidLocaleCode) {
|
||||||
|
return normalizedLanguage.split('-')[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.warn(
|
||||||
|
`[STT] Invalid language format "${language}". Expected ISO-639-1 locale code like "en-US" or "en". Skipping language parameter.`,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`[STT] Error validating language code "${language}":`, error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the file extension from the MIME type.
|
* Gets the file extension from the MIME type.
|
||||||
* @param {string} mimeType - The MIME type.
|
* @param {string} mimeType - The MIME type.
|
||||||
|
|
@ -173,10 +201,9 @@ class STTService {
|
||||||
model: sttSchema.model,
|
model: sttSchema.model,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (language) {
|
const validLanguage = getValidatedLanguageCode(language);
|
||||||
/** Converted locale code (e.g., "en-US") to ISO-639-1 format (e.g., "en") */
|
if (validLanguage) {
|
||||||
const isoLanguage = language.split('-')[0];
|
data.language = validLanguage;
|
||||||
data.language = isoLanguage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
|
|
@ -221,10 +248,9 @@ class STTService {
|
||||||
contentType: audioFile.mimetype,
|
contentType: audioFile.mimetype,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (language) {
|
const validLanguage = getValidatedLanguageCode(language);
|
||||||
/** Converted locale code (e.g., "en-US") to ISO-639-1 format (e.g., "en") */
|
if (validLanguage) {
|
||||||
const isoLanguage = language.split('-')[0];
|
formData.append('language', validLanguage);
|
||||||
formData.append('language', isoLanguage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
|
|
@ -286,7 +312,7 @@ class STTService {
|
||||||
|
|
||||||
return response.data.text.trim();
|
return response.data.text.trim();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`STT request failed for provider ${provider}:`, error);
|
logAxiosError({ message: `STT request failed for provider ${provider}:`, error });
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -316,7 +342,7 @@ class STTService {
|
||||||
const text = await this.sttRequest(provider, sttSchema, { audioBuffer, audioFile, language });
|
const text = await this.sttRequest(provider, sttSchema, { audioBuffer, audioFile, language });
|
||||||
res.json({ text });
|
res.json({ text });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('An error occurred while processing the audio:', error);
|
logAxiosError({ message: 'An error occurred while processing the audio:', error });
|
||||||
res.sendStatus(500);
|
res.sendStatus(500);
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
const { logger } = require('@librechat/data-schemas');
|
const { logger } = require('@librechat/data-schemas');
|
||||||
const { genAzureEndpoint } = require('@librechat/api');
|
|
||||||
const { HttpsProxyAgent } = require('https-proxy-agent');
|
const { HttpsProxyAgent } = require('https-proxy-agent');
|
||||||
|
const { genAzureEndpoint, logAxiosError } = require('@librechat/api');
|
||||||
const { extractEnvVariable, TTSProviders } = require('librechat-data-provider');
|
const { extractEnvVariable, TTSProviders } = require('librechat-data-provider');
|
||||||
const { getRandomVoiceId, createChunkProcessor, splitTextIntoChunks } = require('./streamAudio');
|
const { getRandomVoiceId, createChunkProcessor, splitTextIntoChunks } = require('./streamAudio');
|
||||||
const { getAppConfig } = require('~/server/services/Config');
|
const { getAppConfig } = require('~/server/services/Config');
|
||||||
|
|
@ -274,7 +274,7 @@ class TTSService {
|
||||||
try {
|
try {
|
||||||
return await axios.post(url, data, options);
|
return await axios.post(url, data, options);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`TTS request failed for provider ${provider}:`, error);
|
logAxiosError({ message: `TTS request failed for provider ${provider}:`, error });
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -330,7 +330,10 @@ class TTSService {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (innerError) {
|
} catch (innerError) {
|
||||||
logger.error('Error processing manual update:', chunk, innerError);
|
logAxiosError({
|
||||||
|
message: `[TTS] Error processing manual update for chunk: ${chunk?.text?.substring(0, 50)}...`,
|
||||||
|
error: innerError,
|
||||||
|
});
|
||||||
if (!res.headersSent) {
|
if (!res.headersSent) {
|
||||||
return res.status(500).end();
|
return res.status(500).end();
|
||||||
}
|
}
|
||||||
|
|
@ -342,7 +345,7 @@ class TTSService {
|
||||||
res.end();
|
res.end();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error creating the audio stream:', error);
|
logAxiosError({ message: '[TTS] Error creating the audio stream:', error });
|
||||||
if (!res.headersSent) {
|
if (!res.headersSent) {
|
||||||
return res.status(500).send('An error occurred');
|
return res.status(500).send('An error occurred');
|
||||||
}
|
}
|
||||||
|
|
@ -412,7 +415,10 @@ class TTSService {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (innerError) {
|
} catch (innerError) {
|
||||||
logger.error('Error processing audio stream update:', update, innerError);
|
logAxiosError({
|
||||||
|
message: `[TTS] Error processing audio stream update: ${update?.text?.substring(0, 50)}...`,
|
||||||
|
error: innerError,
|
||||||
|
});
|
||||||
if (!res.headersSent) {
|
if (!res.headersSent) {
|
||||||
return res.status(500).end();
|
return res.status(500).end();
|
||||||
}
|
}
|
||||||
|
|
@ -429,7 +435,7 @@ class TTSService {
|
||||||
res.end();
|
res.end();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Failed to fetch audio:', error);
|
logAxiosError({ message: '[TTS] Failed to fetch audio:', error });
|
||||||
if (!res.headersSent) {
|
if (!res.headersSent) {
|
||||||
res.status(500).end();
|
res.status(500).end();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue