diff --git a/src/globals.ts b/src/globals.ts index 6b06b1cdd..ee960d290 100644 --- a/src/globals.ts +++ b/src/globals.ts @@ -105,6 +105,7 @@ export const THREE_ZERO_TWO_AI: string = '302ai'; export const MESHY: string = 'meshy'; export const TRIPO3D: string = 'tripo3d'; export const NEXTBIT: string = 'nextbit'; +export const VEENA_MAX: string = 'veenamax'; export const VALID_PROVIDERS = [ ANTHROPIC, @@ -173,6 +174,7 @@ export const VALID_PROVIDERS = [ MESHY, TRIPO3D, NEXTBIT, + VEENA_MAX, ]; export const CONTENT_TYPES = { diff --git a/src/providers/index.ts b/src/providers/index.ts index fb5a9f788..050180478 100644 --- a/src/providers/index.ts +++ b/src/providers/index.ts @@ -66,6 +66,7 @@ import AI302Config from './302ai'; import MeshyConfig from './meshy'; import Tripo3DConfig from './tripo3d'; import { NextBitConfig } from './nextbit'; +import VeenaMaxConfig from './veenamax'; const Providers: { [key: string]: ProviderConfigs } = { openai: OpenAIConfig, @@ -132,6 +133,7 @@ const Providers: { [key: string]: ProviderConfigs } = { meshy: MeshyConfig, nextbit: NextBitConfig, tripo3d: Tripo3DConfig, + veenamax: VeenaMaxConfig, }; export default Providers; diff --git a/src/providers/veenamax/api.ts b/src/providers/veenamax/api.ts new file mode 100644 index 000000000..96be936aa --- /dev/null +++ b/src/providers/veenamax/api.ts @@ -0,0 +1,22 @@ +import { ProviderAPIConfig } from '../types'; + +const VeenaMaxAPIConfig: ProviderAPIConfig = { + getBaseURL: () => 'https://flash.mayaresearch.ai', + headers: ({ providerOptions }) => { + const headersObj: Record = { + Authorization: `Bearer ${providerOptions.apiKey}`, + 'Content-Type': 'application/json', + }; + return headersObj; + }, + getEndpoint: ({ fn }) => { + switch (fn) { + case 'createSpeech': + return '/generate'; + default: + return ''; + } + }, +}; + +export default VeenaMaxAPIConfig; diff --git a/src/providers/veenamax/createSpeech.ts b/src/providers/veenamax/createSpeech.ts new file mode 100644 index 000000000..3488c949b --- /dev/null +++ b/src/providers/veenamax/createSpeech.ts @@ -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(), + }, + VEENA_MAX + ); + } + + let errorMessage = 'TTS request failed'; + let errorType = 'api_error'; + + switch (responseStatus) { + case 400: + errorMessage = + '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); +}; diff --git a/src/providers/veenamax/index.ts b/src/providers/veenamax/index.ts new file mode 100644 index 000000000..f674ba3e8 --- /dev/null +++ b/src/providers/veenamax/index.ts @@ -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;