section-web-search.tsx
1 'use client' 2 3 import type { SettingsSectionProps } from './types' 4 5 export function WebSearchSection({ appSettings, patchSettings, inputClass }: SettingsSectionProps) { 6 const provider = appSettings.webSearchProvider || 'duckduckgo' 7 const hasTavilyKey = appSettings.tavilyApiKeyConfigured === true 8 const hasBraveKey = appSettings.braveApiKeyConfigured === true 9 const hasExaKey = appSettings.exaApiKeyConfigured === true 10 11 return ( 12 <div className="mb-10"> 13 <h3 className="font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2"> 14 Web Search 15 </h3> 16 <p className="text-[12px] text-text-3 mb-5"> 17 Choose which search engine agents use for the <code className="text-[11px] font-mono text-text-2">web_search</code> tool. 18 </p> 19 <div className="p-6 rounded-[18px] bg-surface border border-white/[0.06]"> 20 <div className="mb-5"> 21 <label className="block font-display text-[11px] font-600 text-text-3 uppercase tracking-[0.08em] mb-2">Search Provider</label> 22 <select 23 value={provider} 24 onChange={(e) => patchSettings({ webSearchProvider: e.target.value as typeof provider })} 25 className={inputClass} 26 style={{ fontFamily: 'inherit' }} 27 > 28 <option value="duckduckgo">DuckDuckGo (default, no key required)</option> 29 <option value="google">Google (scraping, no key required)</option> 30 <option value="bing">Bing (scraping, no key required)</option> 31 <option value="searxng">SearXNG (self-hosted, no key required)</option> 32 <option value="tavily">Tavily (API key required)</option> 33 <option value="brave">Brave Search (API key required)</option> 34 <option value="exa">Exa (API key required)</option> 35 </select> 36 </div> 37 38 {provider === 'searxng' && ( 39 <div> 40 <label className="block font-display text-[11px] font-600 text-text-3 uppercase tracking-[0.08em] mb-2">SearXNG URL</label> 41 <input 42 type="text" 43 value={appSettings.searxngUrl || ''} 44 onChange={(e) => patchSettings({ searxngUrl: e.target.value || undefined })} 45 placeholder="http://localhost:8080" 46 className={inputClass} 47 style={{ fontFamily: 'inherit' }} 48 /> 49 </div> 50 )} 51 52 {provider === 'tavily' && ( 53 <div> 54 <label className="block font-display text-[11px] font-600 text-text-3 uppercase tracking-[0.08em] mb-2">Tavily API Key</label> 55 <input 56 type="password" 57 value={appSettings.tavilyApiKey || ''} 58 onChange={(e) => patchSettings({ tavilyApiKey: e.target.value || null })} 59 placeholder={hasTavilyKey ? 'Stored securely. Enter a new key to replace it.' : 'tvly-...'} 60 className={inputClass} 61 style={{ fontFamily: 'inherit' }} 62 /> 63 {hasTavilyKey && ( 64 <p className="text-[11px] text-emerald-400/90 mt-1.5">Stored securely. Clear the field and save to remove it.</p> 65 )} 66 <p className="text-[11px] text-text-3/60 mt-2">Get your API key from <a href="https://tavily.com" target="_blank" rel="noopener noreferrer" className="text-accent-bright hover:underline">tavily.com</a></p> 67 </div> 68 )} 69 70 {provider === 'brave' && ( 71 <div> 72 <label className="block font-display text-[11px] font-600 text-text-3 uppercase tracking-[0.08em] mb-2">Brave Search API Key</label> 73 <input 74 type="password" 75 value={appSettings.braveApiKey || ''} 76 onChange={(e) => patchSettings({ braveApiKey: e.target.value || null })} 77 placeholder={hasBraveKey ? 'Stored securely. Enter a new key to replace it.' : 'BSA...'} 78 className={inputClass} 79 style={{ fontFamily: 'inherit' }} 80 /> 81 {hasBraveKey && ( 82 <p className="text-[11px] text-emerald-400/90 mt-1.5">Stored securely. Clear the field and save to remove it.</p> 83 )} 84 <p className="text-[11px] text-text-3/60 mt-2">Get your API key from <a href="https://brave.com/search/api/" target="_blank" rel="noopener noreferrer" className="text-accent-bright hover:underline">brave.com/search/api</a></p> 85 </div> 86 )} 87 88 {provider === 'exa' && ( 89 <div> 90 <label className="block font-display text-[11px] font-600 text-text-3 uppercase tracking-[0.08em] mb-2">Exa API Key</label> 91 <input 92 type="password" 93 value={appSettings.exaApiKey || ''} 94 onChange={(e) => patchSettings({ exaApiKey: e.target.value || null })} 95 placeholder={hasExaKey ? 'Stored securely. Enter a new key to replace it.' : 'exa-...'} 96 className={inputClass} 97 style={{ fontFamily: 'inherit' }} 98 /> 99 {hasExaKey && ( 100 <p className="text-[11px] text-emerald-400/90 mt-1.5">Stored securely. Clear the field and save to remove it.</p> 101 )} 102 <p className="text-[11px] text-text-3/60 mt-2">Get your API key from <a href="https://exa.ai" target="_blank" rel="noopener noreferrer" className="text-accent-bright hover:underline">exa.ai</a>. You can also set the <code className="text-[11px] font-mono text-text-2">EXA_API_KEY</code> environment variable.</p> 103 </div> 104 )} 105 </div> 106 </div> 107 ) 108 }