mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-20 02:10:15 +01:00
102 lines
2.8 KiB
JavaScript
102 lines
2.8 KiB
JavaScript
const { extractEnvVariable, RealtimeVoiceProviders } = require('librechat-data-provider');
|
|
const { getCustomConfig } = require('~/server/services/Config');
|
|
const { logger } = require('~/config');
|
|
|
|
class RealtimeService {
|
|
constructor(customConfig) {
|
|
this.customConfig = customConfig;
|
|
this.providerStrategies = {
|
|
[RealtimeVoiceProviders.OPENAI]: this.openaiProvider.bind(this),
|
|
};
|
|
}
|
|
|
|
static async getInstance() {
|
|
const customConfig = await getCustomConfig();
|
|
if (!customConfig) {
|
|
throw new Error('Custom config not found');
|
|
}
|
|
return new RealtimeService(customConfig);
|
|
}
|
|
|
|
async getProviderSchema() {
|
|
const realtimeSchema = this.customConfig.speech.realtime;
|
|
if (!realtimeSchema) {
|
|
throw new Error('No Realtime schema is set in config');
|
|
}
|
|
|
|
const providers = Object.entries(realtimeSchema).filter(
|
|
([, value]) => Object.keys(value).length > 0,
|
|
);
|
|
|
|
if (providers.length !== 1) {
|
|
throw new Error(providers.length > 1 ? 'Multiple providers set' : 'No provider set');
|
|
}
|
|
|
|
return providers[0];
|
|
}
|
|
|
|
async openaiProvider(schema, voice) {
|
|
const defaultRealtimeUrl = 'https://api.openai.com/v1/realtime';
|
|
const allowedVoices = ['alloy', 'ash', 'ballad', 'coral', 'echo', 'sage', 'shimmer', 'verse'];
|
|
|
|
if (!voice) {
|
|
throw new Error('Voice not specified');
|
|
}
|
|
|
|
if (!allowedVoices.includes(voice)) {
|
|
throw new Error(`Invalid voice: ${voice}`);
|
|
}
|
|
|
|
const apiKey = extractEnvVariable(schema.apiKey);
|
|
if (!apiKey) {
|
|
throw new Error('OpenAI API key not configured');
|
|
}
|
|
|
|
const response = await fetch('https://api.openai.com/v1/realtime/sessions', {
|
|
method: 'POST',
|
|
headers: {
|
|
Authorization: `Bearer ${apiKey}`,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
model: 'gpt-4o-realtime-preview-2024-12-17',
|
|
modalities: ['audio', 'text'],
|
|
voice: voice,
|
|
}),
|
|
});
|
|
|
|
const token = response.json();
|
|
|
|
return {
|
|
provider: RealtimeVoiceProviders.OPENAI,
|
|
token: token,
|
|
url: schema.url || defaultRealtimeUrl,
|
|
};
|
|
}
|
|
|
|
async getRealtimeConfig(req, res) {
|
|
try {
|
|
const [provider, schema] = await this.getProviderSchema();
|
|
const strategy = this.providerStrategies[provider];
|
|
|
|
if (!strategy) {
|
|
throw new Error(`Unsupported provider: ${provider}`);
|
|
}
|
|
|
|
const voice = req.query.voice;
|
|
|
|
const config = strategy(schema, voice);
|
|
res.json(config);
|
|
} catch (error) {
|
|
logger.error('[RealtimeService] Config generation failed:', error);
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
}
|
|
}
|
|
|
|
async function getRealtimeConfig(req, res) {
|
|
const service = await RealtimeService.getInstance();
|
|
await service.getRealtimeConfig(req, res);
|
|
}
|
|
|
|
module.exports = getRealtimeConfig;
|