11import { useMemo , useRef , useState } from "react"
22
33import { CaretRight , Plus , X } from "@phosphor-icons/react"
4- import { Select , Input , Button , Divider , InputRef , Popover } from "antd"
4+ import { Button , Divider , Input , InputRef , Popover , Select , Tooltip , Typography } from "antd"
55import clsx from "clsx"
66
77import useLazyEffect from "@/oss/hooks/useLazyEffect"
88import { useVaultSecret } from "@/oss/hooks/useVaultSecret"
99import { capitalize } from "@/oss/lib/helpers/utils"
10- import { SecretDTOProvider , PROVIDER_LABELS } from "@/oss/lib/Types"
10+ import { PROVIDER_LABELS , SecretDTOProvider } from "@/oss/lib/Types"
1111
1212import LLMIcons from "../LLMIcons"
1313import Anthropic from "../LLMIcons/assets/Anthropic"
@@ -25,6 +25,7 @@ interface ProviderOption {
2525 label : string
2626 value : string
2727 key ?: string
28+ metadata ?: Record < string , any >
2829}
2930
3031interface ProviderGroup {
@@ -169,6 +170,7 @@ const SelectLLMProvider = ({
169170 label : resolvedLabel ,
170171 value : resolvedValue ,
171172 key : option ?. key ?? resolvedValue ,
173+ metadata : option ?. metadata ,
172174 }
173175 } )
174176 . filter ( Boolean ) as ProviderOption [ ] ) ?? [ ] ,
@@ -208,6 +210,68 @@ const SelectLLMProvider = ({
208210 setTimeout ( ( ) => setOpen ( false ) , 0 )
209211 }
210212
213+ const formatCost = ( cost : number ) => {
214+ const value = Number ( cost )
215+ if ( isNaN ( value ) ) return "N/A"
216+ return value < 0.01 ? value . toFixed ( 4 ) : value . toFixed ( 2 )
217+ }
218+
219+ const renderTooltipContent = ( metadata : Record < string , any > ) => (
220+ < div className = "flex flex-col gap-0.5" >
221+ { ( metadata . input !== undefined || metadata . output !== undefined ) && (
222+ < >
223+ < div className = "flex justify-between gap-4" >
224+ < Typography . Text className = "text-[10px] text-nowrap" >
225+ Input:
226+ </ Typography . Text >
227+ < Typography . Text className = "text-[10px] text-nowrap" >
228+ ${ formatCost ( metadata . input ) } / 1M
229+ </ Typography . Text >
230+ </ div >
231+ < div className = "flex justify-between gap-4" >
232+ < Typography . Text className = "text-[10px] text-nowrap" >
233+ Output:{ " " }
234+ </ Typography . Text >
235+ < Typography . Text className = "text-[10px] text-nowrap" >
236+ ${ formatCost ( metadata . output ) } / 1M
237+ </ Typography . Text >
238+ </ div >
239+ </ >
240+ ) }
241+ </ div >
242+ )
243+
244+ const renderOptionContent = ( option : ProviderOption ) => {
245+ const Icon = getProviderIcon ( option . value ) || LLMIcons [ option . label ]
246+ return (
247+ < div className = "flex items-center gap-2 w-full justify-between group h-full" >
248+ < div className = "flex items-center gap-2 overflow-hidden w-full" >
249+ { Icon && < Icon className = "w-4 h-4 flex-shrink-0" /> }
250+ < span className = "truncate" > { option . label } </ span >
251+ </ div >
252+ </ div >
253+ )
254+ }
255+
256+ const renderOption = ( option : ProviderOption ) => {
257+ const content = renderOptionContent ( option )
258+
259+ if ( option . metadata ) {
260+ return (
261+ < Tooltip
262+ title = { renderTooltipContent ( option . metadata ) }
263+ placement = "right"
264+ mouseEnterDelay = { 0.3 }
265+ color = "white"
266+ >
267+ { content }
268+ </ Tooltip >
269+ )
270+ }
271+
272+ return content
273+ }
274+
211275 return (
212276 < >
213277 < Select
@@ -225,6 +289,7 @@ const SelectLLMProvider = ({
225289 placeholder = "Select a provider"
226290 style = { { width : "100%" } }
227291 virtual = { false }
292+ optionLabelProp = "label"
228293 className = { clsx ( [
229294 "[&_.ant-select-item-option-content]:flex [&_.ant-select-item-option-content]:items-center [&_.ant-select-item-option-content]:gap-2 [&_.ant-select-selection-item]:!flex [&_.ant-select-selection-item]:!items-center [&_.ant-select-selection-item]:!gap-2" ,
230295 className ,
@@ -292,10 +357,7 @@ const SelectLLMProvider = ({
292357 handleSelect ( option . value )
293358 } }
294359 >
295- { Icon && (
296- < Icon className = "w-4 h-4 flex-shrink-0" />
297- ) }
298- < span > { option . label } </ span >
360+ { renderOption ( option ) }
299361 </ div >
300362 ) ) }
301363 </ div >
@@ -369,27 +431,26 @@ const SelectLLMProvider = ({
369431 }
370432 >
371433 { group . options ?. map ( ( option ) => {
372- const Icon =
373- getProviderIcon ( group . label || "" ) || LLMIcons [ option . label ]
374434 return (
375- < Option key = { option . key ?? option . value } value = { option . value } >
376- < div className = "flex items-center gap-2" >
377- { Icon && < Icon className = "w-4 h-4 flex-shrink-0" /> }
378- < span > { option . label } </ span >
379- </ div >
435+ < Option
436+ key = { option . key ?? option . value }
437+ value = { option . value }
438+ label = { renderOptionContent ( option ) }
439+ >
440+ { renderOption ( option ) }
380441 </ Option >
381442 )
382443 } ) }
383444 </ OptGroup >
384445 ) : (
385446 group . options ?. map ( ( option ) => {
386- const Icon = getProviderIcon ( option . value ) || LLMIcons [ option . label ]
387447 return (
388- < Option key = { option . key ?? option . value } value = { option . value } >
389- < div className = "flex items-center gap-2" >
390- { Icon && < Icon className = "w-4 h-4 flex-shrink-0" /> }
391- < span > { option . label } </ span >
392- </ div >
448+ < Option
449+ key = { option . key ?? option . value }
450+ value = { option . value }
451+ label = { renderOptionContent ( option ) }
452+ >
453+ { renderOption ( option ) }
393454 </ Option >
394455 )
395456 } )
0 commit comments