haixunMaster/lib/services/web-search.ts

126 lines
3.2 KiB
TypeScript
Raw Permalink Normal View History

2026-06-21 12:50:31 +00:00
/**
* lib/searchBrave Search API
*
*
* 1. Threads APIlib/threads-api / lib/search
* 2. Brave Search APIlib/search
* 3. lib/threads-browser
*
* provider
* SerpAPI, Google Search API, Google Custom Search, Bing Search API,
* Serper, Tavily, Exa, DuckDuckGo wrapper, SearXNG, SERP
*/
import {
detectLegacySearchEnvKeys,
executeBraveSearch,
getSearchConfig,
LEGACY_SEARCH_ENV_KEYS,
} from "@/lib/search";
export type SearchProvider = "brave";
export { detectLegacySearchEnvKeys, LEGACY_SEARCH_ENV_KEYS };
export interface WebSearchResult {
title: string;
snippet: string;
link: string;
provider: SearchProvider;
}
const PROVIDER_LABELS: Record<SearchProvider, string> = {
brave: "Brave Search",
};
export function getProviderLabel(provider: SearchProvider): string {
return PROVIDER_LABELS[provider];
}
function toWebResults(
results: Array<{ title: string; snippet: string; url: string }>
): WebSearchResult[] {
return results.map((item) => ({
title: item.title,
snippet: item.snippet,
link: item.url,
provider: "brave" as const,
}));
}
export interface BraveWebSearchOptions {
limit?: number;
/** 巡邏模式:檢查 high priority + 每日額度(預設依 context */
patrolMode?: boolean;
priority?: "high" | "medium" | "low";
/** 僅保留 threads.com 結果 */
threadsOnly?: boolean;
}
/** Brave Search API需 BRAVE_SEARCH_API_KEY */
export async function searchBrave(
query: string,
limit = 5,
options?: BraveWebSearchOptions
): Promise<WebSearchResult[]> {
const cfg = getSearchConfig().brave;
if (!cfg.enabled || !cfg.apiKey) return [];
const patrolMode = options?.patrolMode ?? true;
const executed = await executeBraveSearch({
keyword: query,
query,
limit,
priority: options?.priority ?? (patrolMode ? "high" : undefined),
patrolMode,
threadsOnly: options?.threadsOnly ?? true,
});
if (executed.status === "skipped" && executed.skipReason !== "cache_hit") {
return [];
}
return toWebResults(executed.results);
}
/** 單一查詢Brave */
export async function searchWeb(
query: string,
limit = 5,
options?: BraveWebSearchOptions
): Promise<{
results: WebSearchResult[];
provider: SearchProvider;
providerLabel: string;
}> {
const results = await searchBrave(query, limit, options);
return {
results,
provider: "brave",
providerLabel: results.length > 0 ? PROVIDER_LABELS.brave : "無搜尋結果",
};
}
/** 網路搜尋入口(僅 Brave */
export async function searchWebThorough(
query: string,
limit = 6,
options?: BraveWebSearchOptions
): Promise<{
results: WebSearchResult[];
provider: SearchProvider;
providerLabel: string;
sources: SearchProvider[];
}> {
const results = await searchBrave(query, limit, options);
return {
results,
provider: "brave",
sources: results.length > 0 ? ["brave"] : [],
providerLabel: results.length > 0 ? PROVIDER_LABELS.brave : "無搜尋結果",
};
}
export function isBraveSearchConfigured(): boolean {
const cfg = getSearchConfig().brave;
return cfg.enabled && !!cfg.apiKey;
}