| import { useState } from 'react'; |
| import { Button, OGDialog, OGDialogTemplate } from '@librechat/client'; |
| import { |
| AuthType, |
| RerankerTypes, |
| SearchProviders, |
| ScraperProviders, |
| SearchCategories, |
| } from 'librechat-data-provider'; |
| import type { SearchApiKeyFormData } from '~/hooks/Plugins/useAuthSearchTool'; |
| import type { UseFormRegister, UseFormHandleSubmit } from 'react-hook-form'; |
| import InputSection, { type DropdownOption } from './InputSection'; |
| import { useGetStartupConfig } from '~/data-provider'; |
| import { useLocalize } from '~/hooks'; |
|
|
| export default function ApiKeyDialog({ |
| isOpen, |
| onSubmit, |
| onRevoke, |
| onOpenChange, |
| authTypes, |
| isToolAuthenticated, |
| register, |
| handleSubmit, |
| triggerRef, |
| triggerRefs, |
| }: { |
| isOpen: boolean; |
| onOpenChange: (open: boolean) => void; |
| onSubmit: (data: SearchApiKeyFormData) => void; |
| onRevoke: () => void; |
| authTypes: [string, AuthType][]; |
| isToolAuthenticated: boolean; |
| register: UseFormRegister<SearchApiKeyFormData>; |
| handleSubmit: UseFormHandleSubmit<SearchApiKeyFormData>; |
| triggerRef?: React.RefObject<HTMLInputElement | HTMLButtonElement>; |
| triggerRefs?: React.RefObject<HTMLInputElement | HTMLButtonElement>[]; |
| }) { |
| const localize = useLocalize(); |
| const { data: config } = useGetStartupConfig(); |
|
|
| const [selectedProvider, setSelectedProvider] = useState( |
| config?.webSearch?.searchProvider || SearchProviders.SERPER, |
| ); |
| const [selectedReranker, setSelectedReranker] = useState( |
| config?.webSearch?.rerankerType || RerankerTypes.JINA, |
| ); |
| const [selectedScraper, setSelectedScraper] = useState( |
| config?.webSearch?.scraperProvider || ScraperProviders.FIRECRAWL, |
| ); |
|
|
| const providerOptions: DropdownOption[] = [ |
| { |
| key: SearchProviders.SERPER, |
| label: localize('com_ui_web_search_provider_serper'), |
| inputs: { |
| serperApiKey: { |
| placeholder: localize('com_ui_enter_api_key'), |
| type: 'password' as const, |
| link: { |
| url: 'https://serper.dev/api-keys', |
| text: localize('com_ui_web_search_provider_serper_key'), |
| }, |
| }, |
| }, |
| }, |
| { |
| key: SearchProviders.SEARXNG, |
| label: localize('com_ui_web_search_provider_searxng'), |
| inputs: { |
| searxngInstanceUrl: { |
| placeholder: localize('com_ui_web_search_searxng_instance_url'), |
| type: 'text' as const, |
| }, |
| searxngApiKey: { |
| placeholder: localize('com_ui_web_search_searxng_api_key'), |
| type: 'password' as const, |
| }, |
| }, |
| }, |
| ]; |
|
|
| const rerankerOptions: DropdownOption[] = [ |
| { |
| key: RerankerTypes.JINA, |
| label: localize('com_ui_web_search_reranker_jina'), |
| inputs: { |
| jinaApiKey: { |
| placeholder: localize('com_ui_web_search_jina_key'), |
| type: 'password' as const, |
| link: { |
| url: 'https://jina.ai/api-dashboard/', |
| text: localize('com_ui_web_search_reranker_jina_key'), |
| }, |
| }, |
| jinaApiUrl: { |
| placeholder: localize('com_ui_web_search_jina_url'), |
| type: 'text' as const, |
| link: { |
| url: 'https://api.jina.ai/v1/rerank', |
| text: localize('com_ui_web_search_reranker_jina_url_help'), |
| }, |
| }, |
| }, |
| }, |
| { |
| key: RerankerTypes.COHERE, |
| label: localize('com_ui_web_search_reranker_cohere'), |
| inputs: { |
| cohereApiKey: { |
| placeholder: localize('com_ui_web_search_cohere_key'), |
| type: 'password' as const, |
| link: { |
| url: 'https://dashboard.cohere.com/welcome/login', |
| text: localize('com_ui_web_search_reranker_cohere_key'), |
| }, |
| }, |
| }, |
| }, |
| ]; |
|
|
| const scraperOptions: DropdownOption[] = [ |
| { |
| key: ScraperProviders.FIRECRAWL, |
| label: localize('com_ui_web_search_scraper_firecrawl'), |
| inputs: { |
| firecrawlApiUrl: { |
| placeholder: localize('com_ui_web_search_firecrawl_url'), |
| type: 'text' as const, |
| }, |
| firecrawlApiKey: { |
| placeholder: localize('com_ui_enter_api_key'), |
| type: 'password' as const, |
| link: { |
| url: 'https://docs.firecrawl.dev/introduction#api-key', |
| text: localize('com_ui_web_search_scraper_firecrawl_key'), |
| }, |
| }, |
| }, |
| }, |
| { |
| key: ScraperProviders.SERPER, |
| label: localize('com_ui_web_search_scraper_serper'), |
| inputs: { |
| serperApiKey: { |
| placeholder: localize('com_ui_enter_api_key'), |
| type: 'password' as const, |
| link: { |
| url: 'https://serper.dev/api-keys', |
| text: localize('com_ui_web_search_scraper_serper_key'), |
| }, |
| }, |
| }, |
| }, |
| ]; |
|
|
| const [dropdownOpen, setDropdownOpen] = useState({ |
| provider: false, |
| reranker: false, |
| scraper: false, |
| }); |
|
|
| const providerAuthType = authTypes.find(([cat]) => cat === SearchCategories.PROVIDERS)?.[1]; |
| const scraperAuthType = authTypes.find(([cat]) => cat === SearchCategories.SCRAPERS)?.[1]; |
| const rerankerAuthType = authTypes.find(([cat]) => cat === SearchCategories.RERANKERS)?.[1]; |
|
|
| const handleProviderChange = (key: string) => { |
| setSelectedProvider(key as SearchProviders); |
| }; |
|
|
| const handleRerankerChange = (key: string) => { |
| setSelectedReranker(key as RerankerTypes); |
| }; |
|
|
| const handleScraperChange = (key: string) => { |
| setSelectedScraper(key as ScraperProviders); |
| }; |
|
|
| return ( |
| <OGDialog |
| open={isOpen} |
| onOpenChange={onOpenChange} |
| triggerRef={triggerRef} |
| triggerRefs={triggerRefs} |
| > |
| <OGDialogTemplate |
| className="w-11/12 sm:w-[500px]" |
| title="" |
| main={ |
| <> |
| <div className="mb-4 text-center font-medium">{localize('com_ui_web_search')}</div> |
| <form onSubmit={handleSubmit(onSubmit)}> |
| {/* Provider Section */} |
| {providerAuthType !== AuthType.SYSTEM_DEFINED && ( |
| <InputSection |
| title={localize('com_ui_web_search_provider')} |
| selectedKey={selectedProvider} |
| onSelectionChange={handleProviderChange} |
| dropdownOptions={providerOptions} |
| showDropdown={!config?.webSearch?.searchProvider} |
| register={register} |
| dropdownOpen={dropdownOpen.provider} |
| setDropdownOpen={(open) => |
| setDropdownOpen((prev) => ({ ...prev, provider: open })) |
| } |
| dropdownKey="provider" |
| /> |
| )} |
| |
| {/* Scraper Section */} |
| {scraperAuthType !== AuthType.SYSTEM_DEFINED && ( |
| <InputSection |
| title={localize('com_ui_web_search_scraper')} |
| selectedKey={selectedScraper} |
| onSelectionChange={handleScraperChange} |
| dropdownOptions={scraperOptions} |
| showDropdown={!config?.webSearch?.scraperProvider} |
| register={register} |
| dropdownOpen={dropdownOpen.scraper} |
| setDropdownOpen={(open) => |
| setDropdownOpen((prev) => ({ ...prev, scraper: open })) |
| } |
| dropdownKey="scraper" |
| /> |
| )} |
| |
| {/* Reranker Section */} |
| {rerankerAuthType !== AuthType.SYSTEM_DEFINED && ( |
| <InputSection |
| title={localize('com_ui_web_search_reranker')} |
| selectedKey={selectedReranker} |
| onSelectionChange={handleRerankerChange} |
| dropdownOptions={rerankerOptions} |
| showDropdown={!config?.webSearch?.rerankerType} |
| register={register} |
| dropdownOpen={dropdownOpen.reranker} |
| setDropdownOpen={(open) => |
| setDropdownOpen((prev) => ({ ...prev, reranker: open })) |
| } |
| dropdownKey="reranker" |
| /> |
| )} |
| </form> |
| </> |
| } |
| selection={{ |
| selectHandler: handleSubmit(onSubmit), |
| selectClasses: 'bg-green-500 hover:bg-green-600 text-white', |
| selectText: localize('com_ui_save'), |
| }} |
| buttons={ |
| isToolAuthenticated && ( |
| <Button |
| onClick={onRevoke} |
| className="bg-red-500 text-white hover:bg-red-600" |
| aria-label={localize('com_ui_revoke')} |
| > |
| {localize('com_ui_revoke')} |
| </Button> |
| ) |
| } |
| showCancelButton={true} |
| /> |
| </OGDialog> |
| ); |
| } |
|
|