-
Notifications
You must be signed in to change notification settings - Fork 786
VeenaMAX TTS Provider Integration #1357
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import { ProviderAPIConfig } from '../types'; | ||
|
|
||
| const VeenaMaxAPIConfig: ProviderAPIConfig = { | ||
| getBaseURL: () => 'https://flash.mayaresearch.ai', | ||
| headers: ({ providerOptions }) => { | ||
| const headersObj: Record<string, string> = { | ||
| Authorization: `Bearer ${providerOptions.apiKey}`, | ||
| 'Content-Type': 'application/json', | ||
| }; | ||
| return headersObj; | ||
| }, | ||
| getEndpoint: ({ fn }) => { | ||
| switch (fn) { | ||
| case 'createSpeech': | ||
| return '/generate'; | ||
| default: | ||
| return ''; | ||
| } | ||
| }, | ||
| }; | ||
|
|
||
| export default VeenaMaxAPIConfig; |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,136 @@ | ||||||||||
| import { VEENA_MAX } from '../../globals'; | ||||||||||
| import { ErrorResponse, ProviderConfig } from '../types'; | ||||||||||
| import { | ||||||||||
| generateErrorResponse, | ||||||||||
| generateInvalidProviderResponseError, | ||||||||||
| } from '../utils'; | ||||||||||
|
|
||||||||||
| // VeenaMAX API request interface | ||||||||||
| export interface VeenaMaxTTSRequest { | ||||||||||
| text: string; | ||||||||||
| speaker_id: string; | ||||||||||
| streaming?: boolean; | ||||||||||
| normalize?: boolean; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // VeenaMAX API response interface | ||||||||||
| export interface VeenaMaxTTSResponse { | ||||||||||
| // VeenaMAX returns audio data directly as Response | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // VeenaMAX API error response interface | ||||||||||
| export interface VeenaMaxErrorResponse { | ||||||||||
| error: { | ||||||||||
| message: string; | ||||||||||
| code: number; | ||||||||||
| type: string; | ||||||||||
| }; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // Parameter configuration mapping OpenAI format to VeenaMAX format | ||||||||||
| export const VeenaMaxCreateSpeechConfig: ProviderConfig = { | ||||||||||
| model: { | ||||||||||
| param: 'model', | ||||||||||
| required: false, | ||||||||||
| default: 'veenamax-1', | ||||||||||
| }, | ||||||||||
| input: { | ||||||||||
| param: 'text', | ||||||||||
| required: true, | ||||||||||
| }, | ||||||||||
| voice: { | ||||||||||
| param: 'speaker_id', | ||||||||||
| required: true, | ||||||||||
| default: 'charu_soft', | ||||||||||
| }, | ||||||||||
| response_format: { | ||||||||||
| param: 'response_format', | ||||||||||
| required: false, | ||||||||||
| default: 'wav', | ||||||||||
| }, | ||||||||||
| speed: { | ||||||||||
| param: 'speed', | ||||||||||
| required: false, | ||||||||||
| default: 1, | ||||||||||
| min: 0.25, | ||||||||||
| max: 4.0, | ||||||||||
| }, | ||||||||||
|
|
||||||||||
| streaming: { | ||||||||||
| param: 'streaming', | ||||||||||
| required: false, | ||||||||||
| default: false, | ||||||||||
| }, | ||||||||||
| normalize: { | ||||||||||
| param: 'normalize', | ||||||||||
| required: false, | ||||||||||
| default: true, | ||||||||||
| }, | ||||||||||
| }; | ||||||||||
|
|
||||||||||
| export const VeenaMaxCreateSpeechResponseTransform = ( | ||||||||||
| response: VeenaMaxTTSResponse | VeenaMaxErrorResponse | Response, | ||||||||||
| responseStatus: number | ||||||||||
| ) => { | ||||||||||
| if (responseStatus !== 200) { | ||||||||||
| if (response && typeof response === 'object' && 'error' in response) { | ||||||||||
| const errorResponse = response as VeenaMaxErrorResponse; | ||||||||||
| return generateErrorResponse( | ||||||||||
| { | ||||||||||
| message: errorResponse.error.message, | ||||||||||
| type: errorResponse.error.type, | ||||||||||
| param: null, | ||||||||||
| code: | ||||||||||
| errorResponse.error.code?.toString() || responseStatus.toString(), | ||||||||||
|
Comment on lines
+83
to
+84
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🐛 Bug Fix Issue: Error response handling fails when
Suggested change
|
||||||||||
| }, | ||||||||||
| VEENA_MAX | ||||||||||
| ); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| let errorMessage = 'TTS request failed'; | ||||||||||
| let errorType = 'api_error'; | ||||||||||
|
|
||||||||||
| switch (responseStatus) { | ||||||||||
| case 400: | ||||||||||
| errorMessage = | ||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey @saicherry93479 I would not recommend keeping a map of the error messages here in the gateway, please forward whatever error message is returned from the upstream provider in the https response |
||||||||||
| 'Invalid request format. Check JSON syntax and required fields.'; | ||||||||||
| errorType = 'invalid_request_error'; | ||||||||||
| break; | ||||||||||
| case 401: | ||||||||||
| errorMessage = | ||||||||||
| 'Authentication failed. Verify API key and Bearer token format.'; | ||||||||||
| errorType = 'authentication_error'; | ||||||||||
| break; | ||||||||||
| case 403: | ||||||||||
| errorMessage = 'Access denied. Check API permissions and quotas.'; | ||||||||||
| errorType = 'permission_error'; | ||||||||||
| break; | ||||||||||
| case 429: | ||||||||||
| errorMessage = 'Rate limit exceeded. Implement exponential backoff.'; | ||||||||||
| errorType = 'rate_limit_error'; | ||||||||||
| break; | ||||||||||
| case 500: | ||||||||||
| errorMessage = 'Internal server error. Contact support if persistent.'; | ||||||||||
| errorType = 'api_error'; | ||||||||||
| break; | ||||||||||
| default: | ||||||||||
| errorMessage = `VeenaMAX TTS request failed with status ${responseStatus}`; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| return generateErrorResponse( | ||||||||||
| { | ||||||||||
| message: errorMessage, | ||||||||||
| type: errorType, | ||||||||||
| param: null, | ||||||||||
| code: responseStatus.toString(), | ||||||||||
| }, | ||||||||||
| VEENA_MAX | ||||||||||
| ); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| if (response instanceof Response) { | ||||||||||
| return response; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| return generateInvalidProviderResponseError(response, VEENA_MAX); | ||||||||||
| }; | ||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import { ProviderConfigs } from '../types'; | ||
| import VeenaMaxAPIConfig from './api'; | ||
| import { | ||
| VeenaMaxCreateSpeechConfig, | ||
| VeenaMaxCreateSpeechResponseTransform, | ||
| } from './createSpeech'; | ||
|
|
||
| const VeenaMaxConfig: ProviderConfigs = { | ||
| api: VeenaMaxAPIConfig, | ||
| createSpeech: VeenaMaxCreateSpeechConfig, | ||
| responseTransforms: { | ||
| createSpeech: VeenaMaxCreateSpeechResponseTransform, | ||
| }, | ||
| }; | ||
|
|
||
| export default VeenaMaxConfig; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Code Refactor
Issue: Redundant type assertion after
inoperator checkFix: Remove unnecessary type assertion since
inoperator already confirms typeImpact: Cleaner code without runtime impact