1+ import i18n from "@/i18n" ;
12import { AiRoute , AiUpstream } from '@/interfaces/ai-route' ;
2- import { RoutePredicate } from '@/interfaces/route' ;
3+ import { fetchPluginsByRoute , RoutePredicate } from '@/interfaces/route' ;
4+ import { WasmPluginData } from '@/interfaces/wasm-plugin' ;
5+ import { getI18nValue } from "@/pages/plugin/utils" ;
36import { addAiRoute , deleteAiRoute , getAiRoutes , updateAiRoute } from '@/services/ai-route' ;
7+ import { getWasmPlugins } from '@/services' ;
48import { ArrowRightOutlined , ExclamationCircleOutlined , RedoOutlined , SearchOutlined } from '@ant-design/icons' ;
59import { PageContainer } from '@ant-design/pro-layout' ;
610import { useRequest } from 'ahooks' ;
7- import { Button , Col , Drawer , Form , FormProps , Input , Modal , Row , Space , Table } from 'antd' ;
11+ import { Button , Col , Drawer , Form , FormProps , Input , message , Modal , Row , Space , Table } from 'antd' ;
812import { history } from 'ice' ;
913import React , { useEffect , useRef , useState } from 'react' ;
1014import { Trans , useTranslation } from 'react-i18next' ;
@@ -33,7 +37,14 @@ const AiRouteList: React.FC = () => {
3337 if ( ! Array . isArray ( value ) || ! value . length ) {
3438 return '-' ;
3539 }
36- return value . map ( ( token ) => < span > { token } </ span > ) . reduce ( ( prev , curr ) => [ prev , < br /> , curr ] ) ;
40+ const elements : React . ReactNode [ ] = [ ] ;
41+ value . forEach ( ( token , index ) => {
42+ if ( index > 0 ) {
43+ elements . push ( < br key = { `br-${ index } ` } /> ) ;
44+ }
45+ elements . push ( < span key = { `span-${ index } ` } > { token } </ span > ) ;
46+ } ) ;
47+ return elements ;
3748 } ,
3849 } ,
3950 {
@@ -78,15 +89,23 @@ const AiRouteList: React.FC = () => {
7889 elements . push ( `${ upstream . provider } : ${ upstream . weight } %` ) ;
7990 } ) ;
8091 }
81- if ( record . fallbackUpstream ?. provider ) {
92+ if ( record . fallbackConfig ?. enabled && record . fallbackConfig . upstreams && record . fallbackConfig . upstreams . length > 0 ) {
93+ const fallbackProviders = record . fallbackConfig . upstreams . map ( u => u . provider ) . join ( ', ' ) ;
8294 elements . push ( (
8395 < >
8496 < ArrowRightOutlined style = { { marginRight : '5px' } } />
85- { record . fallbackUpstream . provider }
97+ { fallbackProviders }
8698 </ >
87- ) )
99+ ) ) ;
88100 }
89- return elements . map ( ( ele ) => < span > { ele } </ span > ) . reduce ( ( prev , curr ) => [ prev , < br /> , curr ] ) ;
101+ const result : React . ReactNode [ ] = [ ] ;
102+ elements . forEach ( ( ele , index ) => {
103+ if ( index > 0 ) {
104+ result . push ( < br key = { `br-${ index } ` } /> ) ;
105+ }
106+ result . push ( < span key = { `span-${ index } ` } > { ele } </ span > ) ;
107+ } ) ;
108+ return result ;
90109 } ,
91110 } ,
92111 {
@@ -96,20 +115,27 @@ const AiRouteList: React.FC = () => {
96115 render : ( value , record ) => {
97116 const { authConfig } = record ;
98117 if ( ! authConfig || ! authConfig . enabled ) {
99- return t ( 'aiRoute.authNotEnabled' )
118+ return t ( 'aiRoute.authNotEnabled' ) ;
100119 }
101120 if ( ! Array . isArray ( value ) || ! value . length ) {
102- return t ( 'aiRoute.authEnabledWithoutConsumer' )
121+ return t ( 'aiRoute.authEnabledWithoutConsumer' ) ;
103122 }
104- return value . map ( ( consumer ) => < span > { consumer } </ span > ) . reduce ( ( prev , curr ) => [ prev , < br /> , curr ] ) ;
123+ const result : React . ReactNode [ ] = [ ] ;
124+ value . forEach ( ( consumer , index ) => {
125+ if ( index > 0 ) {
126+ result . push ( < br key = { `br-${ index } ` } /> ) ;
127+ }
128+ result . push ( < span key = { `span-${ index } ` } > { consumer } </ span > ) ;
129+ } ) ;
130+ return result ;
105131 } ,
106132 } ,
107133 {
108134 title : t ( 'misc.actions' ) ,
109135 dataIndex : 'action' ,
110136 key : 'action' ,
111137 width : 240 ,
112- align : 'center' ,
138+ align : 'center' as const ,
113139 render : ( _ , record ) => (
114140 < Space size = "small" >
115141 < a onClick = { ( ) => onUsageDrawer ( record ) } > { t ( 'aiRoute.usage' ) } </ a >
@@ -132,7 +158,20 @@ const AiRouteList: React.FC = () => {
132158 const [ isLoading , setIsLoading ] = useState < boolean > ( false ) ;
133159 const [ confirmLoading , setConfirmLoading ] = useState ( false ) ;
134160 const [ usageDrawer , setUsageDrawer ] = useState ( false )
135- const [ usageCommand , setUsageCommand ] = useState ( '' )
161+ const [ usageCommand , setUsageCommand ] = useState < string > ( '' )
162+ const [ expandedKeys , setExpandedKeys ] = useState < string [ ] > ( [ ] ) ;
163+ const [ pluginData , setPluginsData ] = useState < Record < string , WasmPluginData [ ] > > ( { } ) ;
164+ const [ pluginInfoList , setPluginInfoList ] = useState < WasmPluginData [ ] > ( [ ] ) ;
165+
166+ const { loading : wasmLoading , run : loadWasmPlugins } = useRequest ( ( ) => {
167+ return getWasmPlugins ( i18n . language )
168+ } , {
169+ manual : true ,
170+ onSuccess : ( result = [ ] ) => {
171+ let plugins = result || [ ] ;
172+ setPluginInfoList ( plugins ) ;
173+ } ,
174+ } ) ;
136175
137176 const { loading, run, refresh } = useRequest ( getAiRoutes , {
138177 manual : true ,
@@ -149,6 +188,14 @@ const AiRouteList: React.FC = () => {
149188
150189 useEffect ( ( ) => {
151190 run ( ) ;
191+ loadWasmPlugins ( ) ;
192+
193+ const handleLanguageChange = ( ) => loadWasmPlugins ( ) ;
194+ i18n . on ( 'languageChanged' , handleLanguageChange ) ;
195+
196+ return ( ) => {
197+ i18n . off ( 'languageChanged' , handleLanguageChange ) ;
198+ } ;
152199 } , [ ] ) ;
153200
154201 const buildUsageCommand = ( aiRoute : AiRoute ) : string => {
@@ -179,7 +226,7 @@ const AiRouteList: React.FC = () => {
179226 } ;
180227
181228 const closeUsage = ( ) => {
182- setUsageCommand ( null ) ;
229+ setUsageCommand ( '' ) ;
183230 setUsageDrawer ( false ) ;
184231 }
185232
@@ -258,6 +305,9 @@ const AiRouteList: React.FC = () => {
258305 } ;
259306
260307 const handleModalOk = async ( ) => {
308+ if ( ! currentAiRoute ) {
309+ return ;
310+ }
261311 setConfirmLoading ( true ) ;
262312 try {
263313 await deleteAiRoute ( currentAiRoute . name ) ;
@@ -279,6 +329,35 @@ const AiRouteList: React.FC = () => {
279329 setCurrentAiRoute ( null ) ;
280330 } ;
281331
332+ const onShowStrategyList = async ( record : AiRoute , expanded : boolean ) => {
333+ if ( expanded ) {
334+ try {
335+ const routeResourceName = `ai-route-${ record . name } .internal` ;
336+ const plugins = await fetchPluginsByRoute ( { name : routeResourceName } as any ) ;
337+ const mergedPlugins = plugins . map ( ( plugin ) => {
338+ const pluginInfo = pluginInfoList . find (
339+ info => info . name === plugin . name && ! plugin . internal ,
340+ ) ;
341+ return {
342+ ...plugin ,
343+ title : pluginInfo ?. title || plugin . title || '' ,
344+ description : pluginInfo ?. description || plugin . description || '' ,
345+ } ;
346+ } )
347+ setPluginsData ( ( prev ) => ( {
348+ ...prev ,
349+ [ record . name ] : mergedPlugins ,
350+ } ) ) ;
351+ } catch ( error ) {
352+ message . error ( 'Failed to fetch strategies, error:' , error ) ;
353+ setExpandedKeys ( ( prev ) => prev . filter ( ( key ) => key !== record . name ) ) ;
354+ }
355+ } else {
356+ setExpandedKeys ( ( prev ) =>
357+ prev . filter ( ( key ) => key !== record . name ) ) ;
358+ }
359+ } ;
360+
282361 return (
283362 < PageContainer >
284363 < Form
@@ -320,6 +399,43 @@ const AiRouteList: React.FC = () => {
320399 dataSource = { dataSource }
321400 columns = { columns }
322401 pagination = { false }
402+ expandable = { {
403+ expandedRowKeys : expandedKeys ,
404+ onExpand : async ( expanded , record ) => {
405+ if ( expanded ) {
406+ setExpandedKeys ( [ ...expandedKeys , record . name ] ) ;
407+ } else {
408+ setExpandedKeys ( expandedKeys . filter ( key => key !== record . name ) ) ;
409+ }
410+ await onShowStrategyList ( record , expanded ) ;
411+ } ,
412+ expandedRowRender : ( record ) => {
413+ const plugins = ( pluginData [ record . name ] || [ ] ) . filter ( plugin => plugin . enabled ) ;
414+ return (
415+ < Table
416+ dataSource = { plugins }
417+ columns = { [
418+ {
419+ title : t ( 'plugins.title' ) ,
420+ render : ( _ , plugin ) => {
421+ return getI18nValue ( plugin , 'title' ) ;
422+ } ,
423+ key : 'title' ,
424+ } ,
425+ {
426+ title : t ( 'plugins.description' ) ,
427+ render : ( _ , plugin ) => {
428+ return getI18nValue ( plugin , 'description' ) ;
429+ } ,
430+ key : 'description' ,
431+ } ,
432+ ] }
433+ pagination = { false }
434+ rowKey = { ( plugin ) => `${ plugin . name } -${ plugin . internal } ` }
435+ />
436+ ) ;
437+ } ,
438+ } }
323439 />
324440 < Drawer
325441 title = { t ( currentAiRoute ? "aiRoute.edit" : "aiRoute.create" ) }
0 commit comments