UNPKG

@mastra/voice-sarvam

Version:

Mastra Sarvam AI voice integration

1 lines 12.2 kB
{"version":3,"sources":["../src/voices.ts","../src/index.ts"],"names":["MastraVoice","stream","PassThrough"],"mappings":"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","file":"index.cjs","sourcesContent":["export const SARVAM_VOICES = [\n 'meera',\n 'pavithra',\n 'maitreyi',\n 'arvind',\n 'amol',\n 'amartya',\n 'diya',\n 'neel',\n 'misha',\n 'vian',\n 'arjun',\n 'maya',\n] as const;\n\nexport const SARVAM_TTS_LANGUAGES = [\n 'hi-IN',\n 'bn-IN',\n 'kn-IN',\n 'ml-IN',\n 'mr-IN',\n 'od-IN',\n 'pa-IN',\n 'ta-IN',\n 'te-IN',\n 'en-IN',\n 'gu-IN',\n] as const;\n\nexport const SARVAM_STT_LANGUAGES = [...SARVAM_TTS_LANGUAGES, 'unknown'] as const;\n\nexport const SARVAM_TTS_MODELS = ['bulbul:v1'] as const;\nexport const SARVAM_STT_MODELS = ['saarika:v1', 'saarika:v2', 'saarika:flash'] as const;\n\nexport type SarvamVoiceId = (typeof SARVAM_VOICES)[number];\n\nexport type SarvamTTSLanguage = (typeof SARVAM_TTS_LANGUAGES)[number];\nexport type SarvamSTTLanguage = (typeof SARVAM_STT_LANGUAGES)[number];\n\nexport type SarvamTTSModel = (typeof SARVAM_TTS_MODELS)[number];\nexport type SarvamSTTModel = (typeof SARVAM_STT_MODELS)[number];\n","import { PassThrough } from 'stream';\n\nimport { MastraVoice } from '@mastra/core/voice';\nimport { SARVAM_VOICES } from './voices';\nimport type { SarvamTTSLanguage, SarvamSTTLanguage, SarvamSTTModel, SarvamTTSModel, SarvamVoiceId } from './voices';\n\ninterface SarvamVoiceConfig {\n apiKey?: string;\n model?: SarvamTTSModel;\n language?: SarvamTTSLanguage;\n properties?: {\n pitch?: number;\n pace?: number;\n loudness?: number;\n speech_sample_rate?: 8000 | 16000 | 22050;\n enable_preprocessing?: boolean;\n eng_interpolation_wt?: number;\n };\n}\n\ninterface SarvamListenOptions {\n apiKey?: string;\n model?: SarvamSTTModel;\n languageCode?: SarvamSTTLanguage;\n filetype?: 'mp3' | 'wav';\n}\n\nconst defaultSpeechModel = {\n model: 'bulbul:v1' as const,\n apiKey: process.env.SARVAM_API_KEY,\n language: 'en-IN' as const,\n};\n\nconst defaultListeningModel = {\n model: 'saarika:v2' as const,\n apiKey: process.env.SARVAM_API_KEY,\n language_code: 'unknown' as const,\n};\n\nexport class SarvamVoice extends MastraVoice {\n private apiKey?: string;\n private model: SarvamTTSModel = 'bulbul:v1';\n private language: SarvamTTSLanguage = 'en-IN';\n private properties: Record<string, any> = {};\n speaker: SarvamVoiceId = 'meera';\n private baseUrl = 'https://api.sarvam.ai';\n\n constructor({\n speechModel,\n speaker,\n listeningModel,\n }: {\n speechModel?: SarvamVoiceConfig;\n speaker?: SarvamVoiceId;\n listeningModel?: SarvamListenOptions;\n } = {}) {\n super({\n speechModel: {\n name: speechModel?.model ?? defaultSpeechModel.model,\n apiKey: speechModel?.apiKey ?? defaultSpeechModel.apiKey,\n },\n listeningModel: {\n name: listeningModel?.model ?? defaultListeningModel.model,\n apiKey: listeningModel?.model ?? defaultListeningModel.apiKey,\n },\n speaker,\n });\n\n this.apiKey = speechModel?.apiKey || defaultSpeechModel.apiKey;\n if (!this.apiKey) {\n throw new Error('SARVAM_API_KEY must be set');\n }\n this.model = speechModel?.model || defaultSpeechModel.model;\n this.language = speechModel?.language || defaultSpeechModel.language;\n this.properties = speechModel?.properties || {};\n this.speaker = speaker || 'meera';\n }\n\n private async makeRequest(endpoint: string, payload: any) {\n const headers = new Headers({\n 'api-subscription-key': this.apiKey!,\n 'Content-Type': 'application/json',\n });\n const response = await fetch(`${this.baseUrl}${endpoint}`, {\n method: 'POST',\n headers,\n body: JSON.stringify(payload),\n });\n if (!response.ok) {\n let errorMessage;\n try {\n const error = (await response.json()) as { message?: string };\n errorMessage = error.message || response.statusText;\n } catch {\n errorMessage = response.statusText;\n }\n throw new Error(`Sarvam AI API Error: ${errorMessage}`);\n }\n\n return response;\n }\n private async streamToString(stream: NodeJS.ReadableStream): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of stream) {\n if (typeof chunk === 'string') {\n chunks.push(Buffer.from(chunk));\n } else {\n chunks.push(chunk);\n }\n }\n return Buffer.concat(chunks).toString('utf-8');\n }\n async speak(\n input: string | NodeJS.ReadableStream,\n options?: { speaker?: SarvamVoiceId },\n ): Promise<NodeJS.ReadableStream> {\n const text = typeof input === 'string' ? input : await this.streamToString(input);\n\n return this.traced(async () => {\n const payload = {\n inputs: [text],\n target_language_code: this.language,\n speaker: options?.speaker || this.speaker,\n model: this.model,\n ...this.properties,\n };\n\n const response = await this.makeRequest('/text-to-speech', payload);\n\n const { audios } = (await response.json()) as { audios: any };\n\n if (!audios || !audios.length) {\n throw new Error('No audio received from Sarvam AI');\n }\n\n // Convert base64 to buffer\n const audioBuffer = Buffer.from(audios[0], 'base64');\n\n // Create a PassThrough stream for the audio\n const stream = new PassThrough();\n stream.write(audioBuffer);\n stream.end();\n\n return stream;\n }, 'voice.sarvam.speak')();\n }\n\n async getSpeakers() {\n return this.traced(async () => {\n return SARVAM_VOICES.map(voice => ({\n voiceId: voice,\n }));\n }, 'voice.deepgram.getSpeakers')();\n }\n\n /**\n * Checks if listening capabilities are enabled.\n *\n * @returns {Promise<{ enabled: boolean }>}\n */\n async getListener() {\n return { enabled: true };\n }\n\n async listen(input: NodeJS.ReadableStream, options?: SarvamListenOptions): Promise<string> {\n return this.traced(async () => {\n // Collect audio data into buffer\n const chunks: Buffer[] = [];\n for await (const chunk of input) {\n if (typeof chunk === 'string') {\n chunks.push(Buffer.from(chunk));\n } else {\n chunks.push(chunk);\n }\n }\n const audioBuffer = Buffer.concat(chunks);\n\n const form = new FormData();\n const mimeType = options?.filetype === 'mp3' ? 'audio/mpeg' : 'audio/wav';\n const blob = new Blob([audioBuffer], { type: mimeType });\n\n form.append('file', blob);\n form.append('model', options?.model || 'saarika:v2');\n form.append('language_code', options?.languageCode || 'unknown');\n const requestOptions = {\n method: 'POST',\n headers: {\n 'api-subscription-key': this.apiKey!,\n },\n body: form,\n };\n\n try {\n const response = await fetch(`${this.baseUrl}/speech-to-text`, requestOptions);\n const result = (await response.json()) as any;\n //console.log(result);\n return result.transcript;\n } catch (error) {\n console.error('Error during speech-to-text request:', error);\n throw error;\n }\n }, 'voice.sarvam.listen')();\n }\n}\n"]}