feat(web): allow Keenable search without an API key
Keenable's public endpoint serves the free tier (1000 req/hour) without auth. Route to /v1/search/public with the X-Keenable-Title header when no key is configured, instead of falling back to DuckDuckGo; keep the authenticated /v1/search path when an apiKey or KEENABLE_API_KEY is set. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
Xubin Ren
co-authored by
Claude Opus 4.8
parent
a5768a4ebe
commit
74daa81a1b
@@ -318,8 +318,7 @@ class WebSearchTool(Tool):
|
||||
)
|
||||
return "volcengine" if api_key else "duckduckgo"
|
||||
if provider == "keenable":
|
||||
api_key = self.config.api_key or os.environ.get("KEENABLE_API_KEY", "")
|
||||
return "keenable" if api_key else "duckduckgo"
|
||||
return "keenable"
|
||||
return provider
|
||||
|
||||
@property
|
||||
@@ -491,19 +490,21 @@ class WebSearchTool(Tool):
|
||||
|
||||
async def _search_keenable(self, query: str, n: int) -> str:
|
||||
api_key = self.config.api_key or os.environ.get("KEENABLE_API_KEY", "")
|
||||
if not api_key:
|
||||
logger.warning("KEENABLE_API_KEY not set, falling back to DuckDuckGo")
|
||||
return await self._search_duckduckgo(query, n)
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": self.user_agent,
|
||||
"X-Keenable-Title": "nanobot",
|
||||
"X-API-Key": api_key,
|
||||
}
|
||||
# Without a key, the token-less /public endpoint serves the free tier.
|
||||
url = "https://api.keenable.ai/v1/search"
|
||||
if api_key:
|
||||
headers["X-API-Key"] = api_key
|
||||
else:
|
||||
url += "/public"
|
||||
try:
|
||||
async with httpx.AsyncClient(proxy=self.proxy) as client:
|
||||
r = await client.post(
|
||||
"https://api.keenable.ai/v1/search",
|
||||
url,
|
||||
headers=headers,
|
||||
json={"query": query},
|
||||
timeout=float(self.config.timeout),
|
||||
|
||||
Reference in New Issue
Block a user