diff --git a/src/renderer/packages/web-search/bocha.ts b/src/renderer/packages/web-search/bocha.ts new file mode 100644 index 000000000..bf6ee7fc8 --- /dev/null +++ b/src/renderer/packages/web-search/bocha.ts @@ -0,0 +1,47 @@ +import { ofetch } from 'ofetch' +import WebSearch from './base' +import { SearchResult } from 'src/shared/types' + +export class BoChaSearch extends WebSearch { + private readonly BOCHA_SEARCH_URL = 'https://api.bochaai.com/v1/web-search' + + private apiKey: string + + constructor( + apiKey: string, + ) { + super() + this.apiKey = apiKey + } + + async search(query: string, signal?: AbortSignal): Promise { + try { + const requestBody = { + query: query, + } + const response = await ofetch(this.BOCHA_SEARCH_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.apiKey}`, + }, + body: requestBody, + signal, + }) + if (response.code !== 200) { + console.error('BoCha search API error:', response.code) + return { items: [] } + } + const items = response.data.webPages.value.map((page: any) => ({ + title: page.name, + link: page.url, + snippet: page.snippet, + })) + + return { items } + } catch (error) { + console.error('BoCha search error:', error) + return { items: [] } + } + } +} \ No newline at end of file diff --git a/src/renderer/packages/web-search/index.ts b/src/renderer/packages/web-search/index.ts index 2e91a941d..d5d84ea81 100644 --- a/src/renderer/packages/web-search/index.ts +++ b/src/renderer/packages/web-search/index.ts @@ -8,6 +8,7 @@ import { BingSearch } from './bing' import { BingNewsSearch } from './bing-news' import { ChatboxSearch } from './chatbox-search' import { TavilySearch } from './tavily' +import { BoChaSearch} from './bocha' const MAX_CONTEXT_ITEMS = 10 @@ -50,6 +51,12 @@ function getSearchProviders() { ) ) break + case 'bocha': + if (!settings.webSearch.bochaApiKey) { + throw ChatboxAIAPIError.fromCodeName('bocha_api_key_required', 'bocha_api_key_required') + } + selectedProviders.push(new BoChaSearch(settings.webSearch.bochaApiKey)) + break default: throw new Error(`Unsupported search provider: ${provider}`) } diff --git a/src/renderer/routes/settings/web-search.tsx b/src/renderer/routes/settings/web-search.tsx index 42717d820..9b041b254 100644 --- a/src/renderer/routes/settings/web-search.tsx +++ b/src/renderer/routes/settings/web-search.tsx @@ -43,6 +43,31 @@ export function RouteComponent() { } } } + const [checkingBoCha, setCheckingBoCha] = useState(false) + const [bochaAvaliable, setBoChaAvaliable] = useState() + const checkBoCha = async () => { + if (extension.webSearch.bochaApiKey) { + setCheckingBoCha(true) + setBoChaAvaliable(undefined) + try { + await ofetch('https://api.bochaai.com/v1/web-search', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${extension.webSearch.bochaApiKey}`, + }, + body: { + query: 'Chatbox', + }, + }) + setBoChaAvaliable(true) + } catch (e) { + setBoChaAvaliable(false) + } finally { + setCheckingBoCha(false) + } + } + } return ( @@ -54,6 +79,7 @@ export function RouteComponent() { { value: 'build-in', label: 'Chatbox Search (Pro)' }, { value: 'bing', label: 'Bing Search (Free)' }, { value: 'tavily', label: 'Tavily' }, + {value: 'bocha', label: 'BoCha' }, ]} value={extension.webSearch.provider} onChange={(e) => @@ -275,6 +301,58 @@ export function RouteComponent() { )} + {/* Bocha API Key */} + {extension.webSearch.provider === 'bocha' && ( + + {t('BoCha API Key')} + + { + setBoChaAvaliable(undefined) + setSettings({ + extension: { + ...extension, + webSearch: { + ...extension.webSearch, + bochaApiKey: e.currentTarget.value, + }, + }, + }) + }} + placeholder={t('Enter your BoCha API Key') || 'Enter your BoCha API Key'} + error={bochaAvaliable === false} + /> + + + + {typeof bochaAvaliable === 'boolean' ? ( + bochaAvaliable ? ( + + {t('Connection successful!')} + + ) : ( + + {t('API key invalid!')} + + ) + ) : null} + + + + )} ) } diff --git a/src/shared/defaults.ts b/src/shared/defaults.ts index 3d6c3c84d..6aed05701 100644 --- a/src/shared/defaults.ts +++ b/src/shared/defaults.ts @@ -127,6 +127,7 @@ export function settings(): Settings { webSearch: { provider: 'build-in', tavilyApiKey: '', + bochaApiKey: '', }, knowledgeBase: { models: { diff --git a/src/shared/types/settings.ts b/src/shared/types/settings.ts index 2813104bb..6319746e2 100644 --- a/src/shared/types/settings.ts +++ b/src/shared/types/settings.ts @@ -156,12 +156,13 @@ const ShortcutSettingSchema = z.object({ const ExtensionSettingsSchema = z.object({ webSearch: z.object({ - provider: z.enum(['build-in', 'bing', 'tavily']), + provider: z.enum(['build-in', 'bing', 'tavily', 'bocha']), tavilyApiKey: z.string().optional(), tavilySearchDepth: z.string().optional(), tavilyMaxResults: z.number().optional(), tavilyTimeRange: z.string().optional(), tavilyIncludeRawContent: z.string().optional(), + bochaApiKey: z.string().optional(), }), knowledgeBase: z .object({