feat: add Tavily search option to legacy web search skill#5
Conversation
There was a problem hiding this comment.
Code Review
This pull request integrates the Tavily search API into the web search skill, providing it as a primary option when an API key is available while maintaining DuckDuckGo as a fallback. The requirements.txt file was updated to include the tavily-python package. Feedback was provided to refactor the perform_web_search function to eliminate code duplication between the two search provider paths by normalizing the result format before generating the final summary string.
| def perform_web_search(query): | ||
| from ddgs import DDGS | ||
| try: | ||
| results = DDGS().text(query, max_results=5) | ||
| if not results: | ||
| return "No results found." | ||
|
|
||
| summary = "" | ||
| for r in results: | ||
| summary += f"- [{r['title']}]({r['href']}): {r['body']}\n" | ||
| return summary | ||
| except Exception as e: | ||
| return f"Error performing search: {e}" | ||
| tavily_api_key = os.environ.get("TAVILY_API_KEY") | ||
| if tavily_api_key: | ||
| try: | ||
| from tavily import TavilyClient | ||
| client = TavilyClient(api_key=tavily_api_key) | ||
| response = client.search(query=query, max_results=5, search_depth="basic") | ||
| results = response.get("results", []) | ||
| if not results: | ||
| return "No results found." | ||
| summary = "" | ||
| for r in results: | ||
| title = r.get("title", "No title") | ||
| url = r.get("url", "") | ||
| content = r.get("content", "") | ||
| summary += f"- [{title}]({url}): {content}\n" | ||
| return summary | ||
| except Exception as e: | ||
| return f"Error performing search with Tavily: {e}" | ||
| else: | ||
| from ddgs import DDGS | ||
| try: | ||
| results = DDGS().text(query, max_results=5) | ||
| if not results: | ||
| return "No results found." | ||
| summary = "" | ||
| for r in results: | ||
| summary += f"- [{r['title']}]({r['href']}): {r['body']}\n" | ||
| return summary | ||
| except Exception as e: | ||
| return f"Error performing search: {e}" |
There was a problem hiding this comment.
The perform_web_search function contains significant code duplication between the Tavily and DuckDuckGo search paths. Both branches implement nearly identical logic for checking results and formatting the markdown summary. Refactoring this to normalize the results into a common format before generating the summary would improve maintainability and reduce the risk of logic drift between providers.
def perform_web_search(query):
tavily_api_key = os.environ.get("TAVILY_API_KEY")
provider = "Tavily" if tavily_api_key else "DuckDuckGo"
try:
if tavily_api_key:
from tavily import TavilyClient
client = TavilyClient(api_key=tavily_api_key)
response = client.search(query=query, max_results=5, search_depth="basic")
results = [
{"title": r.get("title", "No title"), "url": r.get("url", ""), "body": r.get("content", "")}
for r in response.get("results", [])
]
else:
from ddgs import DDGS
ddgs_results = DDGS().text(query, max_results=5)
results = [
{"title": r.get("title", "No title"), "url": r.get("href", ""), "body": r.get("body", "")}
for r in (ddgs_results or [])
]
if not results:
return "No results found."
summary = ""
for r in results:
summary += f"- [{r['title']}]({r['url']}): {r['body']}\n"
return summary
except Exception as e:
return f"Error performing search with {provider}: {e}"
Summary
_legacy/skills/web_search.pyTAVILY_API_KEYenvironment variable is set,perform_web_search()usesTavilyClient.search()withmax_results=5andsearch_depth="basic"TAVILY_API_KEYis not set, the existing DuckDuckGo (DDGS) search path is used unchangedFiles changed
_legacy/skills/web_search.py— Addedosimport and Tavily-gated branch inperform_web_search()requirements.txt— Addedtavily-pythondependencyDependency changes
tavily-pythontorequirements.txtEnvironment variable changes
TAVILY_API_KEY— When set, enables Tavily search in the legacy skill layer (optional; DuckDuckGo remains the default)Notes for reviewers
_legacy/is still actively deployedAutomated Review
_legacy/skills/web_search.pyusing an additive/fallback pattern. Tavily SDK usage is valid (TavilyClient,client.search(), and response field namestitle/url/contentare all correct). The DDGS fallback is fully preserved.tavily-pythonis added torequirements.txt.TAVILY_API_KEYdocumentation inenv.exampleis handled by the prerequisite unitactive-web-search-ddgsand is not required here. No regressions, no unintended changes, no dead code.