Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,15 @@ def load_config():
})

# Default values if missing

# Default values if missing
if 'ollama' not in config: config['ollama'] = {'model': 'llama3'}


# Search config
if 'search' not in config: config['search'] = {}
config['search']['provider'] = os.getenv('SEARCH_PROVIDER', config['search'].get('provider', 'duckduckgo'))
config['search']['tavily_api_key'] = os.getenv('TAVILY_API_KEY', config['search'].get('tavily_api_key', ''))

return config
except FileNotFoundError:
logging.error(f"Configuration file {CONFIG_FILE} not found.")
Expand Down
4 changes: 4 additions & 0 deletions config/config.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ email:
# ssl: true
# enabled: true

search:
provider: "duckduckgo" # duckduckgo or tavily
tavily_api_key: "" # Required when provider is tavily

servers:
- name: "Local System"
type: "local"
Expand Down
4 changes: 3 additions & 1 deletion env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
TELEGRAM_BOT_TOKEN=
TELEGRAM_CHAT_ID=
GBYTE_ERP_URL=""
API_KEY=secret-agent-key
API_KEY=secret-agent-key
SEARCH_PROVIDER=duckduckgo # duckduckgo or tavily
TAVILY_API_KEY= # Required when SEARCH_PROVIDER=tavily
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pyyaml
dateparser
pytz
ddgs
tavily-python
youtube-transcript-api
python-dateutil
playwright
Expand Down
38 changes: 36 additions & 2 deletions tools/web_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@
Web Search Tool — Search the web and summarize content.
"""
import logging
import os
import re
from langchain_core.tools import tool
import config as app_config


def perform_web_search(query):
def perform_duckduckgo_search(query):
"""Performs a DuckDuckGo search."""
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"
Expand All @@ -23,6 +24,39 @@ def perform_web_search(query):
return f"Error performing search: {e}"


def perform_tavily_search(query):
"""Performs a Tavily search."""
from tavily import TavilyClient
try:
client = TavilyClient()
response = client.search(query=query, max_results=5)
results = response.get("results", [])
if not results:
return "No results found."

summary = ""
for r in results:
summary += f"- [{r['title']}]({r['url']}): {r.get('content', '')}\n"
return summary
except Exception as e:
return f"Error performing search: {e}"


def perform_web_search(query):
"""Dispatches search to the configured provider (duckduckgo or tavily)."""
conf = app_config.load_config()
provider = (conf.get("search", {}).get("provider", "duckduckgo") if conf else os.environ.get("SEARCH_PROVIDER", "duckduckgo")).lower()
if provider not in ("duckduckgo", "tavily"):
logging.warning(f'Unknown SEARCH_PROVIDER "{provider}", falling back to duckduckgo')
provider = "duckduckgo"
if provider == "tavily":
tavily_key = conf.get("search", {}).get("tavily_api_key", "") if conf else ""
if tavily_key:
os.environ.setdefault("TAVILY_API_KEY", tavily_key)
return perform_tavily_search(query)
return perform_duckduckgo_search(query)
Comment on lines +45 to +57

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current implementation for selecting the search provider and handling the API key in perform_web_search is complex and contains a bug.

  1. The logic is packed into dense one-liners, which harms readability.
  2. There's a bug where if the config file is missing, the code doesn't fall back to using the TAVILY_API_KEY environment variable.
  3. If Tavily is the selected provider but the API key is missing, the search will fail instead of gracefully falling back to another provider.

I suggest refactoring the entire function to be more explicit and robust. This change improves readability, fixes the bug, and adds a safe fallback to DuckDuckGo if the Tavily key is missing.

def perform_web_search(query):
    """Dispatches search to the configured provider (duckduckgo or tavily)."""
    conf = app_config.load_config()

    if conf:
        search_conf = conf.get("search", {})
        provider = search_conf.get("provider", "duckduckgo")
        tavily_key = search_conf.get("tavily_api_key")
    else:
        provider = os.getenv("SEARCH_PROVIDER", "duckduckgo")
        tavily_key = os.getenv("TAVILY_API_KEY")

    provider = provider.lower()
    if provider not in ("duckduckgo", "tavily"):
        logging.warning(f'Unknown SEARCH_PROVIDER "{provider}", falling back to duckduckgo')
        provider = "duckduckgo"

    if provider == "tavily":
        if tavily_key:
            os.environ.setdefault("TAVILY_API_KEY", tavily_key)
            return perform_tavily_search(query)
        logging.warning("SEARCH_PROVIDER is 'tavily' but TAVILY_API_KEY is not set. Falling back to duckduckgo.")
    
    return perform_duckduckgo_search(query)



def get_youtube_video_id(url):
"""Extracts YouTube video ID from URL."""
patterns = [
Expand Down