22
33import { useState , useEffect , useCallback , useMemo , useRef } from 'react' ;
44import { Loader2 , CircleDollarSign , Play , ChevronRight , RotateCcw , Dices } from 'lucide-react' ;
5- import { EndpointDefinition , EndpointParam , JsonValue } from '@/lib/types' ;
5+ import { EndpointDefinition , EndpointParam , JsonValue , DeApiModel } from '@/lib/types' ;
66import { useModelsContext } from '@/components/ModelsContext' ;
77import { ModelInfo } from '@/components/ModelInfo' ;
88import { FormField } from '@/components/form/FormField' ;
@@ -47,6 +47,29 @@ export function EndpointForm({ endpoint, onSubmit, onPriceCheck, isSubmitting }:
4747 const prevModelSlugRef = useRef < string | undefined > ( undefined ) ;
4848 const savedModelsRef = useRef < Record < string , string > > ( { } ) ;
4949
50+ // Resolve lang/voice default values (API returns names, selects use slugs)
51+ const resolveLangSlug = useCallback ( ( value : string , model : DeApiModel | undefined ) : string => {
52+ if ( ! model ?. languages ) return value ;
53+ const match = model . languages . find ( ( l ) => l . slug === value || l . name === value ) ;
54+ return match ? match . slug : value ;
55+ } , [ ] ) ;
56+
57+ const resolveVoiceSlug = useCallback ( ( value : string , langSlug : string , model : DeApiModel | undefined ) : string => {
58+ if ( ! model ?. languages ) return value ;
59+ const normalizedLangSlug = resolveLangSlug ( langSlug , model ) ;
60+ const lang = model . languages . find ( ( l ) => l . slug === normalizedLangSlug ) ;
61+ if ( lang ) {
62+ const match = lang . voices . find ( ( v ) => v . slug === value || v . name === value ) ;
63+ return match ? match . slug : value ;
64+ }
65+ // Search all languages as fallback
66+ for ( const l of model . languages ) {
67+ const match = l . voices . find ( ( v ) => v . slug === value || v . name === value ) ;
68+ if ( match ) return match . slug ;
69+ }
70+ return value ;
71+ } , [ resolveLangSlug ] ) ;
72+
5073 // Get model defaults/limits/features from API data
5174 const modelDefaults =
5275 selectedModel ?. info && ! Array . isArray ( selectedModel . info ) ? selectedModel . info . defaults : undefined ;
@@ -141,9 +164,14 @@ export function EndpointForm({ endpoint, onSubmit, onPriceCheck, isSubmitting }:
141164 } ) ;
142165 // Skip negative_prompt defaults — API returns placeholder text like "Negative prompt"
143166 // which is not a useful default value. Users should fill this in themselves.
144- // Auto-set lang/voice defaults for TTS
145- if ( defaults . lang !== undefined ) newValues [ 'lang' ] = defaults . lang as string ;
146- if ( defaults . voice !== undefined ) newValues [ 'voice' ] = defaults . voice as string ;
167+ // Auto-set lang/voice defaults for TTS (resolve names to slugs)
168+ if ( defaults . lang !== undefined ) {
169+ newValues [ 'lang' ] = resolveLangSlug ( defaults . lang as string , selectedModel ) ;
170+ }
171+ if ( defaults . voice !== undefined ) {
172+ const langSlug = ( newValues [ 'lang' ] ?? '' ) as string ;
173+ newValues [ 'voice' ] = resolveVoiceSlug ( defaults . voice as string , langSlug , selectedModel ) ;
174+ }
147175 if ( defaults . format !== undefined ) newValues [ 'format' ] = defaults . format as string ;
148176 if ( defaults . sample_rate !== undefined ) newValues [ 'sample_rate' ] = defaults . sample_rate as number ;
149177 return newValues ;
@@ -161,7 +189,7 @@ export function EndpointForm({ endpoint, onSubmit, onPriceCheck, isSubmitting }:
161189 } ) ;
162190 return newDisabled ;
163191 } ) ;
164- } , [ selectedModelSlug , selectedModel ] ) ;
192+ } , [ selectedModelSlug , selectedModel , resolveLangSlug , resolveVoiceSlug ] ) ;
165193
166194 const handleChange = useCallback ( ( name : string , value : JsonValue ) => {
167195 setValues ( ( prev ) => {
@@ -299,8 +327,14 @@ export function EndpointForm({ endpoint, onSubmit, onPriceCheck, isSubmitting }:
299327 newValues [ field ] = defaults [ field ] as number ;
300328 }
301329 } ) ;
302- if ( defaults . lang !== undefined ) newValues [ 'lang' ] = defaults . lang as string ;
303- if ( defaults . voice !== undefined ) newValues [ 'voice' ] = defaults . voice as string ;
330+ // Resolve names to slugs for TTS defaults
331+ if ( defaults . lang !== undefined ) {
332+ newValues [ 'lang' ] = resolveLangSlug ( defaults . lang as string , selectedModel ) ;
333+ }
334+ if ( defaults . voice !== undefined ) {
335+ const langSlug = ( newValues [ 'lang' ] ?? '' ) as string ;
336+ newValues [ 'voice' ] = resolveVoiceSlug ( defaults . voice as string , langSlug , selectedModel ) ;
337+ }
304338 if ( defaults . format !== undefined ) newValues [ 'format' ] = defaults . format as string ;
305339 if ( defaults . sample_rate !== undefined ) newValues [ 'sample_rate' ] = defaults . sample_rate as number ;
306340 return newValues ;
@@ -317,7 +351,7 @@ export function EndpointForm({ endpoint, onSubmit, onPriceCheck, isSubmitting }:
317351 } ) ;
318352 return newDisabled ;
319353 } ) ;
320- } , [ modelDefaults , modelSupportsField ] ) ;
354+ } , [ modelDefaults , modelSupportsField , selectedModel , resolveLangSlug , resolveVoiceSlug ] ) ;
321355
322356 // Get effective param with model limits/defaults applied
323357 const getEffectiveParam = useCallback ( ( param : EndpointParam ) : EndpointParam => {
@@ -366,20 +400,18 @@ export function EndpointForm({ endpoint, onSubmit, onPriceCheck, isSubmitting }:
366400
367401 if ( paramName === 'voice' && selectedModel . languages ) {
368402 const selectedLang = values [ 'lang' ] as string ;
369- const language = selectedModel . languages . find ( ( l ) => l . slug === selectedLang ) ;
403+ // Also try matching by name (API defaults may use name instead of slug)
404+ const language = selectedModel . languages . find (
405+ ( l ) => l . slug === selectedLang || l . name === selectedLang
406+ ) ;
370407 if ( language ) {
371408 return language . voices . map ( ( v ) => ( {
372409 value : v . slug ,
373410 label : `${ v . name } (${ v . gender === 'female' ? 'F' : 'M' } )` ,
374411 } ) ) ;
375412 }
376- // Fallback: show all voices grouped by language
377- return selectedModel . languages . flatMap ( ( l ) =>
378- l . voices . map ( ( v ) => ( {
379- value : v . slug ,
380- label : `${ v . name } (${ l . slug . toUpperCase ( ) } , ${ v . gender === 'female' ? 'F' : 'M' } )` ,
381- } ) )
382- ) ;
413+ // No language selected yet — return empty (defaults effect will set lang shortly)
414+ return [ ] ;
383415 }
384416
385417 return undefined ;
0 commit comments