chore: establish learning module baseline
This commit is contained in:
commit
2d04197f3f
2409 files changed
+533050
No files matched your search
@@ -0,0 +1,131 @@
|
||||
import type { TTSVoiceInfo } from '@/lib/audio/types';
|
||||
import { buildVoiceDesignPrompt, type VoiceDesign } from '@/lib/audio/voice-design';
|
||||
|
||||
export const VOXCPM_TTS_PROVIDER_ID = 'voxcpm-tts' as const;
|
||||
export const VOXCPM_MODEL_ID = 'VoxCPM2';
|
||||
export const VOXCPM_VLLM_MODEL_ID = 'voxcpm2';
|
||||
export const VOXCPM_AUTO_VOICE_ID = 'voxcpm:auto';
|
||||
export const VOXCPM_PROFILE_VOICE_PREFIX = 'voxcpm:profile:';
|
||||
const VOXCPM_AUTO_VOICE_PROMPT_MAX_CHARS = 200;
|
||||
|
||||
export const VOXCPM_BACKENDS = [
|
||||
{
|
||||
id: 'vllm-omni',
|
||||
name: 'vLLM-Omni',
|
||||
endpoint: '/v1/audio/speech',
|
||||
description: 'OpenAI-compatible speech endpoint',
|
||||
},
|
||||
{
|
||||
id: 'python-api',
|
||||
name: 'Python API',
|
||||
endpoint: '/tts/upload',
|
||||
description: 'FastAPI deployment backed by the VoxCPM Python runtime',
|
||||
},
|
||||
{
|
||||
id: 'nano-vllm',
|
||||
name: 'Nano-vLLM',
|
||||
endpoint: '/generate',
|
||||
description: 'Nano-vLLM VoxCPM FastAPI deployment',
|
||||
},
|
||||
] as const;
|
||||
|
||||
export type VoxCPMBackendType = (typeof VOXCPM_BACKENDS)[number]['id'];
|
||||
|
||||
export const DEFAULT_VOXCPM_BACKEND: VoxCPMBackendType = 'vllm-omni';
|
||||
|
||||
export interface VoxCPMVoicePromptContext {
|
||||
agentName?: string;
|
||||
role?: string;
|
||||
persona?: string;
|
||||
language?: string;
|
||||
locale?: string;
|
||||
voiceDesign?: VoiceDesign;
|
||||
backend?: VoxCPMBackendType;
|
||||
}
|
||||
|
||||
export interface VoxCPMProviderOptions {
|
||||
backend?: VoxCPMBackendType;
|
||||
voiceMode?: 'auto' | 'prompt' | 'clone';
|
||||
voicePrompt?: string;
|
||||
promptText?: string;
|
||||
referenceAudioBase64?: string;
|
||||
referenceAudioMimeType?: string;
|
||||
referenceAudioName?: string;
|
||||
cfgValue?: number;
|
||||
inferenceTimesteps?: number;
|
||||
normalize?: boolean;
|
||||
denoise?: boolean;
|
||||
registeredVoiceId?: string;
|
||||
}
|
||||
|
||||
export const VOXCPM_AUTO_VOICE: TTSVoiceInfo = {
|
||||
id: VOXCPM_AUTO_VOICE_ID,
|
||||
name: 'Auto Voice',
|
||||
language: 'auto',
|
||||
gender: 'neutral',
|
||||
description: 'Generate a voice prompt from agent metadata',
|
||||
};
|
||||
|
||||
export function normalizeVoxCPMBackend(value: unknown): VoxCPMBackendType {
|
||||
return VOXCPM_BACKENDS.some((backend) => backend.id === value)
|
||||
? (value as VoxCPMBackendType)
|
||||
: DEFAULT_VOXCPM_BACKEND;
|
||||
}
|
||||
|
||||
export function getVoxCPMBackendEndpoint(backend: VoxCPMBackendType): string {
|
||||
return VOXCPM_BACKENDS.find((item) => item.id === backend)?.endpoint || '/v1/audio/speech';
|
||||
}
|
||||
|
||||
export function voxCPMBackendSupportsReferenceAudio(backend: VoxCPMBackendType): boolean {
|
||||
return backend === 'vllm-omni' || backend === 'python-api' || backend === 'nano-vllm';
|
||||
}
|
||||
|
||||
export function buildVoxCPMBackendUrl(baseUrl: string, backend: VoxCPMBackendType): string {
|
||||
const cleanBaseUrl = baseUrl.replace(/\/$/, '');
|
||||
if (backend === 'vllm-omni' && cleanBaseUrl.endsWith('/v1')) {
|
||||
return `${cleanBaseUrl}/audio/speech`;
|
||||
}
|
||||
return `${cleanBaseUrl}${getVoxCPMBackendEndpoint(backend)}`;
|
||||
}
|
||||
|
||||
export function getVoxCPMProfileVoiceId(profileId: string): string {
|
||||
return `${VOXCPM_PROFILE_VOICE_PREFIX}${profileId}`;
|
||||
}
|
||||
|
||||
export function getVoxCPMProfileIdFromVoiceId(voiceId: string): string | null {
|
||||
if (!voiceId.startsWith(VOXCPM_PROFILE_VOICE_PREFIX)) return null;
|
||||
return voiceId.slice(VOXCPM_PROFILE_VOICE_PREFIX.length);
|
||||
}
|
||||
|
||||
function sanitizeAutoVoicePromptPart(value?: string): string {
|
||||
return (value || '')
|
||||
.replace(/[\p{C}]+/gu, ' ')
|
||||
.replace(/\s+/gu, ' ')
|
||||
.trim()
|
||||
.slice(0, VOXCPM_AUTO_VOICE_PROMPT_MAX_CHARS)
|
||||
.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a VoxCPM backend exposes a runtime voice-registration API
|
||||
* (POST /v1/audio/voices) for reference-by-id timbre stability.
|
||||
*/
|
||||
export function voxCPMBackendSupportsVoiceRegistration(backend: VoxCPMBackendType): boolean {
|
||||
return backend === 'vllm-omni';
|
||||
}
|
||||
|
||||
export function buildAutoVoxCPMVoicePrompt(context: VoxCPMVoicePromptContext = {}): string {
|
||||
if (context.voiceDesign) {
|
||||
const designPrompt = sanitizeAutoVoicePromptPart(buildVoiceDesignPrompt(context.voiceDesign));
|
||||
if (designPrompt) return designPrompt;
|
||||
}
|
||||
|
||||
const persona = sanitizeAutoVoicePromptPart(context.persona);
|
||||
if (persona) return persona;
|
||||
|
||||
const fallbackParts = [context.role, context.agentName]
|
||||
.map(sanitizeAutoVoicePromptPart)
|
||||
.filter(Boolean);
|
||||
const fallbackPrompt = sanitizeAutoVoicePromptPart(fallbackParts.join(' '));
|
||||
return fallbackPrompt || 'natural classroom voice';
|
||||
}
|
||||
Reference in new issue
Block a user