11import { Modal , Button , Switch , App } from 'antd'
2- import { DeleteOutlined , ExclamationCircleFilled , RightOutlined } from '@ant-design/icons'
2+ import { DeleteOutlined , ExclamationCircleFilled , RightOutlined , ReloadOutlined } from '@ant-design/icons'
33import { useState } from 'react'
44import { ModelOption , ModelType , ModelSource } from '@/types/config'
55import { modelService } from '@/services/modelService'
6- import { ModelEditDialog } from './ModelEditDialog'
6+ import { ModelEditDialog , ProviderConfigEditDialog } from './ModelEditDialog'
77import { useConfig } from '@/hooks/useConfig'
88import { useTranslation } from 'react-i18next'
99
@@ -30,6 +30,8 @@ export const ModelDeleteDialog = ({
3030 const [ providerModels , setProviderModels ] = useState < any [ ] > ( [ ] )
3131 const [ pendingSelectedProviderIds , setPendingSelectedProviderIds ] = useState < Set < string > > ( new Set ( ) )
3232 const [ loadingSource , setLoadingSource ] = useState < ModelSource | null > ( null )
33+ const [ isProviderConfigOpen , setIsProviderConfigOpen ] = useState < boolean > ( false )
34+ const [ isConfirmLoading , setIsConfirmLoading ] = useState < boolean > ( false )
3335
3436 // 获取模型的颜色方案
3537 const getModelColorScheme = ( type : ModelType ) : { bg : string ; text : string ; border : string } => {
@@ -187,6 +189,11 @@ export const ModelDeleteDialog = ({
187189 } finally {
188190 setLoadingSource ( null )
189191 }
192+ } else if ( source === 'openai' ) {
193+ // For OpenAI source, just set the selected source without prefetching
194+ // TODO: Call the relevant API to fetch OpenAI models
195+ setSelectedSource ( source )
196+ return
190197 }
191198 setSelectedSource ( source )
192199 }
@@ -284,6 +291,43 @@ export const ModelDeleteDialog = ({
284291 onClose ( )
285292 }
286293
294+ // Handle provider config save
295+ const handleProviderConfigSave = async ( { apiKey, maxTokens } : { apiKey : string ; maxTokens : number } ) => {
296+ if ( selectedSource === 'silicon' && deletingModelType ) {
297+ try {
298+ const currentIds = new Set (
299+ customModels
300+ . filter ( m => m . type === deletingModelType && m . source === 'silicon' )
301+ . map ( m => m . name )
302+ )
303+
304+ // Build payload items for the current silicon models in required format
305+ const currentModelPayloads = customModels
306+ . filter ( m => m . type === deletingModelType && m . source === 'silicon' && currentIds . has ( m . name ) )
307+ . map ( m => ( {
308+ model_id : m . id ,
309+ apiKey : apiKey || m . apiKey ,
310+ maxTokens : maxTokens || m . maxTokens ,
311+ } ) )
312+
313+ const result = await modelService . updateBatchModel ( currentModelPayloads )
314+
315+ if ( result . code !== 200 ) {
316+ message . error ( t ( 'model.dialog.error.noModelsFetched' ) )
317+ } else {
318+ message . success ( t ( 'model.dialog.success.updateSuccess' ) )
319+ }
320+
321+ // Optionally use currentModelPayloads for subsequent API calls if needed
322+
323+ } catch ( e ) {
324+ message . error ( t ( 'model.dialog.error.noModelsFetched' ) )
325+ }
326+ }
327+ await onSuccess ( )
328+ setIsProviderConfigOpen ( false )
329+ }
330+
287331 return (
288332 // Refactor: Styles are embedded within the component
289333 < Modal
@@ -294,42 +338,60 @@ export const ModelDeleteDialog = ({
294338 < Button key = "close" onClick = { handleClose } >
295339 { t ( 'common.button.close' ) }
296340 </ Button > ,
297- < Button key = "confirm" type = "primary" onClick = { async ( ) => {
298- // Only apply changes when silicon source is selected
299- if ( selectedSource === 'silicon' && deletingModelType ) {
341+ // Only show confirm button for silicon and openai sources, not for OpenAI-API-Compatible
342+ ( selectedSource !== "OpenAI-API-Compatible" ) && (
343+ < Button key = "confirm" type = "primary" loading = { isConfirmLoading } onClick = { async ( ) => {
344+ setIsConfirmLoading ( true )
300345 try {
301- // Get all currently enabled models (including originally enabled and newly enabled ones)
302- const allEnabledModels = providerModels . filter ( ( pm : any ) =>
303- pendingSelectedProviderIds . has ( pm . id )
304- )
346+ // Handle changes for both silicon and openai sources
347+ if ( selectedSource === 'silicon' && deletingModelType ) {
348+ try {
349+ // Get all currently enabled models (including originally enabled and newly enabled ones)
350+ const allEnabledModels = providerModels . filter ( ( pm : any ) =>
351+ pendingSelectedProviderIds . has ( pm . id )
352+ )
305353
306- if ( allEnabledModels . length > 0 ) {
307- const apiKey = getApiKeyByType ( deletingModelType )
308- // Pass all currently enabled models
309- await modelService . addBatchCustomModel ( {
310- api_key : apiKey && apiKey . trim ( ) !== '' ? apiKey : 'sk-no-api-key' ,
311- provider : 'silicon' ,
312- type : deletingModelType ,
313- max_tokens : 0 ,
314- models : allEnabledModels
315- } )
316- }
354+ if ( allEnabledModels . length > 0 ) {
355+ const apiKey = getApiKeyByType ( deletingModelType )
356+ // Pass all currently enabled models
357+ await modelService . addBatchCustomModel ( {
358+ api_key : apiKey && apiKey . trim ( ) !== '' ? apiKey : 'sk-no-api-key' ,
359+ provider : 'silicon' ,
360+ type : deletingModelType ,
361+ max_tokens : 0 ,
362+ models : allEnabledModels
363+ } )
364+ }
317365
318- // Refresh list
319- await onSuccess ( )
320- // Re-fetch provider models and sync switch states
321- await prefetchSiliconProviderModels ( deletingModelType )
322- message . success ( 'Update successful' )
323- // Close dialog
324- handleClose ( )
325- } catch ( e ) {
326- console . error ( 'Failed to apply model updates' , e )
327- message . error ( t ( 'model.dialog.error.addFailed' , { error : e as any } ) )
366+ // Refresh list
367+ await onSuccess ( )
368+ // Re-fetch provider models and sync switch states
369+ await prefetchSiliconProviderModels ( deletingModelType )
370+ message . success ( t ( 'model.dialog.success.updateSuccess' ) )
371+ // Close dialog
372+ handleClose ( )
373+ } catch ( e ) {
374+ console . error ( 'Failed to apply model updates' , e )
375+ message . error ( t ( 'model.dialog.error.addFailed' , { error : e as any } ) )
376+ }
377+ } else if ( selectedSource === 'openai' && deletingModelType ) {
378+ try {
379+ // For OpenAI source, just refresh the list and close dialog
380+ await onSuccess ( )
381+ message . success ( t ( 'model.dialog.success.updateSuccess' ) )
382+ handleClose ( )
383+ } catch ( e ) {
384+ console . error ( 'Failed to apply OpenAI model updates' , e )
385+ message . error ( t ( 'model.dialog.error.addFailed' , { error : e as any } ) )
386+ }
387+ }
388+ } finally {
389+ setIsConfirmLoading ( false )
328390 }
329- }
330- } } disabled = { selectedSource !== 'silicon' } >
331- { t ( 'common.confirm' ) }
332- </ Button > ,
391+ } } >
392+ { t ( 'common.confirm' ) }
393+ </ Button >
394+ ) ,
333395 ] }
334396 width = { 520 }
335397 destroyOnClose
@@ -458,7 +520,7 @@ export const ModelDeleteDialog = ({
458520 </ div >
459521 ) : (
460522 < div >
461- < div className = "flex items-center mb-4" >
523+ < div className = "flex items-center justify-between mb-4" >
462524 < button
463525 onClick = { ( ) => { setSelectedSource ( null ) ; setProviderModels ( [ ] ) } }
464526 className = "text-blue-500 hover:text-blue-700 flex items-center"
@@ -477,6 +539,30 @@ export const ModelDeleteDialog = ({
477539 </ svg >
478540 { t ( 'common.back' ) }
479541 </ button >
542+
543+ { selectedSource !== 'OpenAI-API-Compatible' && (
544+ < div className = "flex gap-2" >
545+ < Button
546+ size = "small"
547+ icon = { < ReloadOutlined className = "text-blue-500" /> }
548+ onClick = { async ( ) => {
549+ if ( selectedSource === 'silicon' && deletingModelType ) {
550+ try {
551+ await prefetchSiliconProviderModels ( deletingModelType )
552+ message . success ( t ( 'common.message.refreshSuccess' ) )
553+ } catch ( error ) {
554+ message . error ( t ( 'common.message.refreshFailed' ) )
555+ }
556+ }
557+ } }
558+ className = "border-none shadow-none hover:bg-blue-50"
559+ >
560+ </ Button >
561+ < Button size = "small" onClick = { ( ) => setIsProviderConfigOpen ( true ) } >
562+ { t ( 'common.button.editConfig' ) }
563+ </ Button >
564+ </ div >
565+ ) }
480566 </ div >
481567
482568 { selectedSource === 'silicon' && providerModels . length > 0 ? (
@@ -520,8 +606,10 @@ export const ModelDeleteDialog = ({
520606 . filter ( ( model ) => model . type === deletingModelType && model . source === selectedSource )
521607 . map ( ( model ) => (
522608 < div key = { model . name }
523- onClick = { ( ) => handleEditModel ( model ) }
524- className = "p-2 flex justify-between items-center hover:bg-gray-50 text-sm cursor-pointer" >
609+ onClick = { selectedSource === 'OpenAI-API-Compatible' ? ( ) => handleEditModel ( model ) : undefined }
610+ className = { `p-2 flex justify-between items-center hover:bg-gray-50 text-sm ${
611+ selectedSource === 'OpenAI-API-Compatible' ? 'cursor-pointer' : ''
612+ } `} >
525613 < div className = "flex-1 min-w-0" >
526614 < div className = "font-medium truncate" title = { model . name } >
527615 { model . displayName || model . name } ({ model . name } )
@@ -583,7 +671,10 @@ export const ModelDeleteDialog = ({
583671 < p className = "font-bold text-medium" > { t ( 'common.notice' ) } </ p >
584672 </ div >
585673 < p className = "mt-0.5 ml-6" >
586- { t ( 'model.dialog.delete.warning' ) }
674+ { selectedSource === 'OpenAI-API-Compatible'
675+ ? t ( 'model.dialog.delete.warning' )
676+ : t ( 'model.dialog.edit.warning' )
677+ }
587678 </ p >
588679 </ div >
589680 </ div >
@@ -602,6 +693,14 @@ export const ModelDeleteDialog = ({
602693 }
603694 } }
604695 />
696+ < ProviderConfigEditDialog
697+ isOpen = { isProviderConfigOpen }
698+ onClose = { ( ) => setIsProviderConfigOpen ( false ) }
699+ initialApiKey = { getApiKeyByType ( deletingModelType ) }
700+ initialMaxTokens = { ( customModels . find ( m => m . type === deletingModelType && m . source === 'silicon' ) ?. maxTokens || 4096 ) . toString ( ) }
701+ modelType = { deletingModelType || undefined }
702+ onSave = { handleProviderConfigSave }
703+ />
605704 </ Modal >
606705 )
607706}
0 commit comments