1
1
import { createModel } from "@/db/dexie/models"
2
- import { getAllOpenAIConfig } from "@/db/dexie/openai"
2
+ import { getAllOpenAIConfig , getOpenAIConfigById } from "@/db/dexie/openai"
3
+ import { getAllOpenAIModels } from "@/libs/openai"
3
4
import { useMutation , useQuery , useQueryClient } from "@tanstack/react-query"
4
- import { Input , Modal , Form , Select , Radio } from "antd"
5
+ import { Input , Modal , Form , Select , Radio , AutoComplete , Spin } from "antd"
5
6
import { Loader2 } from "lucide-react"
6
7
import { useTranslation } from "react-i18next"
8
+ import { useMemo } from "react"
9
+ import { ProviderIcons } from "@/components/Common/ProviderIcon"
7
10
8
11
type Props = {
9
12
open : boolean
@@ -14,6 +17,8 @@ export const AddCustomModelModal: React.FC<Props> = ({ open, setOpen }) => {
14
17
const { t } = useTranslation ( [ "openai" ] )
15
18
const [ form ] = Form . useForm ( )
16
19
const queryClient = useQueryClient ( )
20
+ const selectedProviderId = Form . useWatch ( "provider_id" , form )
21
+ const searchValue = Form . useWatch ( "model_id" , form )
17
22
18
23
const { data, isPending } = useQuery ( {
19
24
queryKey : [ "fetchProviders" ] ,
@@ -23,6 +28,36 @@ export const AddCustomModelModal: React.FC<Props> = ({ open, setOpen }) => {
23
28
}
24
29
} )
25
30
31
+ const {
32
+ data : providerModels ,
33
+ isFetching : isFetchingModels ,
34
+ status : modelsStatus
35
+ } = useQuery ( {
36
+ queryKey : [ "providerModels" , selectedProviderId ] ,
37
+ queryFn : async ( ) => {
38
+ const config = await getOpenAIConfigById ( selectedProviderId as string )
39
+ const models = await getAllOpenAIModels ( {
40
+ baseUrl : config . baseUrl ,
41
+ apiKey : config . apiKey ,
42
+ customHeaders : config . headers
43
+ } )
44
+ return models
45
+ } ,
46
+ enabled : ! ! selectedProviderId
47
+ } )
48
+
49
+ const autoCompleteOptions = useMemo ( ( ) => {
50
+ const list = providerModels ?? [ ]
51
+ const cleaned = list . map ( ( m ) => ( {
52
+ value : m . id ,
53
+ label : `${ m . name ?? m . id } ` . replaceAll ( / a c c o u n t s \/ [ ^ \/ ] + \/ m o d e l s \/ / g, "" )
54
+ } ) )
55
+ if ( searchValue && ! cleaned . some ( ( o ) => o . value === searchValue ) ) {
56
+ return [ { value : searchValue , label : searchValue } , ...cleaned ]
57
+ }
58
+ return cleaned
59
+ } , [ providerModels , searchValue ] )
60
+
26
61
const onFinish = async ( values : {
27
62
model_id : string
28
63
model_type : "chat" | "embedding"
@@ -68,9 +103,32 @@ export const AddCustomModelModal: React.FC<Props> = ({ open, setOpen }) => {
68
103
message : t ( "manageModels.modal.form.name.required" )
69
104
}
70
105
] } >
71
- < Input
106
+ < AutoComplete
107
+ options = { autoCompleteOptions }
72
108
placeholder = { t ( "manageModels.modal.form.name.placeholder" ) }
73
109
size = "large"
110
+ disabled = { ! selectedProviderId }
111
+ filterOption = { ( inputValue , option ) =>
112
+ ( option ?. label as string )
113
+ ?. toLowerCase ( )
114
+ . includes ( inputValue . toLowerCase ( ) ) ||
115
+ ( option ?. value as string )
116
+ ?. toLowerCase ( )
117
+ . includes ( inputValue . toLowerCase ( ) )
118
+ }
119
+ notFoundContent = {
120
+ selectedProviderId ? (
121
+ modelsStatus === "pending" || isFetchingModels ? (
122
+ < div className = "flex items-center justify-center py-2" >
123
+ < Spin size = "small" />
124
+ </ div >
125
+ ) : (
126
+ t ( "noModelFound" )
127
+ )
128
+ ) : (
129
+ t ( "manageModels.modal.form.provider.placeholder" )
130
+ )
131
+ }
74
132
/>
75
133
</ Form . Item >
76
134
@@ -86,13 +144,35 @@ export const AddCustomModelModal: React.FC<Props> = ({ open, setOpen }) => {
86
144
< Select
87
145
placeholder = { t ( "manageModels.modal.form.provider.placeholder" ) }
88
146
size = "large"
89
- loading = { isPending } >
90
- { data ?. map ( ( provider : any ) => (
147
+ loading = { isPending }
148
+ showSearch
149
+ filterOption = { ( input , option ) => {
150
+ //@ts -ignore
151
+ return (
152
+ option ?. label ?. props [ "data-title" ]
153
+ ?. toLowerCase ( )
154
+ ?. indexOf ( input . toLowerCase ( ) ) >= 0
155
+ )
156
+ } }
157
+ options = { data ?. map ( ( e : any ) => ( {
158
+ value : e . id ,
159
+ label : (
160
+ < span
161
+ key = { e . id }
162
+ data-title = { e . name }
163
+ className = "flex flex-row gap-3 items-center " >
164
+ < ProviderIcons provider = { e ?. provider } className = "size-4" />
165
+ < span className = "line-clamp-2" > { e . name } </ span >
166
+ </ span >
167
+ )
168
+ } ) ) }
169
+ />
170
+ { /* {data?.map((provider: any) => (
91
171
<Select.Option key={provider.id} value={provider.id}>
92
172
{provider.name}
93
173
</Select.Option>
94
- ) ) }
95
- </ Select >
174
+ ))} */ }
175
+ { /* </Select> */ }
96
176
</ Form . Item >
97
177
98
178
< Form . Item
0 commit comments