@ -33,6 +33,7 @@ export function GenerateImageNode({ id, data, selected }: NodeProps<NanoBananaNo
const [ isLoadingCarouselImage , setIsLoadingCarouselImage ] = useState ( false ) ;
const [ externalModels , setExternalModels ] = useState < ProviderModel [ ] > ( [ ] ) ;
const [ isLoadingModels , setIsLoadingModels ] = useState ( false ) ;
const [ modelsFetchError , setModelsFetchError ] = useState < string | null > ( null ) ;
// Get the current selected provider (default to gemini)
const currentProvider : ProviderType = nodeData . selectedModel ? . provider || "gemini" ;
@ -66,40 +67,51 @@ export function GenerateImageNode({ id, data, selected }: NodeProps<NanoBananaNo
} , [ id , nodeData . model , nodeData . selectedModel , updateNodeData ] ) ;
// Fetch models from external providers when provider changes
useEffect ( ( ) = > {
const fetchModels = async ( ) = > {
if ( currentProvider === "gemini" ) {
setExternalModels ( [ ] ) ;
return ;
}
const fetchModels = useCallback ( async ( ) = > {
if ( currentProvider === "gemini" ) {
setExternalModels ( [ ] ) ;
setModelsFetchError ( null ) ;
return ;
}
setIsLoadingModels ( true ) ;
try {
const capabilities = IMAGE_CAPABILITIES . join ( "," ) ;
const headers : HeadersInit = { } ;
if ( providerSettings . providers . replicate ? . apiKey ) {
headers [ "X-Replicate-Key" ] = providerSettings . providers . replicate . apiKey ;
}
if ( providerSettings . providers . fal ? . apiKey ) {
headers [ "X-Fal-Key" ] = providerSettings . providers . fal . apiKey ;
}
const response = await fetch ( ` /api/models?provider= ${ currentProvider } &capabilities= ${ capabilities } ` , { headers } ) ;
if ( response . ok ) {
const data = await response . json ( ) ;
setExternalModels ( data . models || [ ] ) ;
} else {
setExternalModels ( [ ] ) ;
}
} catch ( error ) {
console . error ( "Failed to fetch models:" , error ) ;
setIsLoadingModels ( true ) ;
setModelsFetchError ( null ) ;
try {
const capabilities = IMAGE_CAPABILITIES . join ( "," ) ;
const headers : HeadersInit = { } ;
if ( providerSettings . providers . replicate ? . apiKey ) {
headers [ "X-Replicate-Key" ] = providerSettings . providers . replicate . apiKey ;
}
if ( providerSettings . providers . fal ? . apiKey ) {
headers [ "X-Fal-Key" ] = providerSettings . providers . fal . apiKey ;
}
const response = await fetch ( ` /api/models?provider= ${ currentProvider } &capabilities= ${ capabilities } ` , { headers } ) ;
if ( response . ok ) {
const data = await response . json ( ) ;
setExternalModels ( data . models || [ ] ) ;
setModelsFetchError ( null ) ;
} else {
const errorData = await response . json ( ) . catch ( ( ) = > ( { } ) ) ;
const errorMsg = errorData . error || ` Failed to load models ( ${ response . status } ) ` ;
setExternalModels ( [ ] ) ;
} finally {
setIsLoadingModels ( false ) ;
setModelsFetchError (
currentProvider === "replicate" && response . status === 401
? "Invalid Replicate API key. Check your settings."
: errorMsg
) ;
}
} ;
} catch ( error ) {
console . error ( "Failed to fetch models:" , error ) ;
setExternalModels ( [ ] ) ;
setModelsFetchError ( "Failed to load models. Check your connection." ) ;
} finally {
setIsLoadingModels ( false ) ;
}
} , [ currentProvider , providerSettings ] ) ;
useEffect ( ( ) = > {
fetchModels ( ) ;
} , [ currentProvider , providerSettings ] ) ;
} , [ fetchModel s] ) ;
// Handle provider change
const handleProviderChange = useCallback (
@ -481,30 +493,43 @@ export function GenerateImageNode({ id, data, selected }: NodeProps<NanoBananaNo
< / select >
) : (
< >
< select
value = { nodeData . selectedModel ? . modelId || "" }
onChange = { handleExternalModelChange }
disabled = { isLoadingModels }
className = "w-full text-[10px] py-1 px-1.5 border border-neutral-700 rounded bg-neutral-900/50 focus:outline-none focus:ring-1 focus:ring-neutral-600 text-neutral-300 shrink-0 disabled:opacity-50"
>
{ isLoadingModels ? (
< option value = "" > Loading models . . . < / option >
) : externalModels . length === 0 ? (
< option value = "" > No models available < / option >
) : (
< >
< option value = "" > Select model . . . < / option >
{ externalModels . map ( ( m ) = > (
< option key = { m . id } value = { m . id } >
{ m . name }
< / option >
) ) }
< / >
) }
< / select >
{ /* Error state with retry button */ }
{ modelsFetchError ? (
< div className = "flex flex-col gap-1 shrink-0" >
< span className = "text-[9px] text-red-400" > { modelsFetchError } < / span >
< button
onClick = { fetchModels }
className = "text-[9px] px-2 py-0.5 bg-neutral-800 hover:bg-neutral-700 rounded text-neutral-300 transition-colors"
>
Retry
< / button >
< / div >
) : (
< select
value = { nodeData . selectedModel ? . modelId || "" }
onChange = { handleExternalModelChange }
disabled = { isLoadingModels }
className = "w-full text-[10px] py-1 px-1.5 border border-neutral-700 rounded bg-neutral-900/50 focus:outline-none focus:ring-1 focus:ring-neutral-600 text-neutral-300 shrink-0 disabled:opacity-50"
>
{ isLoadingModels ? (
< option value = "" > Loading models . . . < / option >
) : externalModels . length === 0 ? (
< option value = "" > No models available < / option >
) : (
< >
< option value = "" > Select model . . . < / option >
{ externalModels . map ( ( m ) = > (
< option key = { m . id } value = { m . id } >
{ m . name }
< / option >
) ) }
< / >
) }
< / select >
) }
{ /* Model-specific parameters for external providers */ }
{ nodeData . selectedModel ? . modelId && (
{ nodeData . selectedModel ? . modelId && ! modelsFetchError && (
< ModelParameters
modelId = { nodeData . selectedModel . modelId }
provider = { currentProvider }