From bfd330e9a191c915dbe3d9c58dad580aa8bead15 Mon Sep 17 00:00:00 2001 From: kshitij <82637225+kshitijk4poor@users.noreply.github.com> Date: Fri, 13 Mar 2026 05:18:21 +0530 Subject: [PATCH] feat: add Tavily setup support --- hermes_cli/config.py | 12 +++++++++++- hermes_cli/setup.py | 6 ++++++ hermes_cli/status.py | 1 + hermes_cli/tools_config.py | 9 ++++++++- tests/hermes_cli/test_config.py | 11 +++++++++++ tests/hermes_cli/test_status.py | 14 ++++++++++++++ 6 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 tests/hermes_cli/test_status.py diff --git a/hermes_cli/config.py b/hermes_cli/config.py index c05ebd5a4573..976c83bafe4e 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -262,6 +262,7 @@ def ensure_hermes_home(): 4: ["VOICE_TOOLS_OPENAI_KEY", "ELEVENLABS_API_KEY"], 5: ["WHATSAPP_ENABLED", "WHATSAPP_MODE", "WHATSAPP_ALLOWED_USERS", "SLACK_BOT_TOKEN", "SLACK_APP_TOKEN", "SLACK_ALLOWED_USERS"], + 7: ["TAVILY_API_KEY"], } # Required environment variables with metadata for migration prompts. @@ -380,6 +381,14 @@ def ensure_hermes_home(): "password": True, "category": "tool", }, + "TAVILY_API_KEY": { + "description": "Tavily API key for Tavily-based research skills and integrations", + "prompt": "Tavily API key", + "url": "https://app.tavily.com/home", + "tools": ["tavily skills", "research integrations"], + "password": True, + "category": "tool", + }, "FIRECRAWL_API_URL": { "description": "Firecrawl API URL for self-hosted instances (optional)", "prompt": "Firecrawl API URL (leave empty for cloud)", @@ -1054,6 +1063,7 @@ def show_config(): ("ANTHROPIC_API_KEY", "Anthropic"), ("VOICE_TOOLS_OPENAI_KEY", "OpenAI (STT/TTS)"), ("FIRECRAWL_API_KEY", "Firecrawl"), + ("TAVILY_API_KEY", "Tavily"), ("BROWSERBASE_API_KEY", "Browserbase"), ("FAL_KEY", "FAL"), ] @@ -1199,7 +1209,7 @@ def set_config_value(key: str, value: str): # Check if it's an API key (goes to .env) api_keys = [ 'OPENROUTER_API_KEY', 'OPENAI_API_KEY', 'ANTHROPIC_API_KEY', 'VOICE_TOOLS_OPENAI_KEY', - 'FIRECRAWL_API_KEY', 'FIRECRAWL_API_URL', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', + 'FIRECRAWL_API_KEY', 'TAVILY_API_KEY', 'FIRECRAWL_API_URL', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'FAL_KEY', 'TELEGRAM_BOT_TOKEN', 'DISCORD_BOT_TOKEN', 'TERMINAL_SSH_HOST', 'TERMINAL_SSH_USER', 'TERMINAL_SSH_KEY', 'SUDO_PASSWORD', 'SLACK_BOT_TOKEN', 'SLACK_APP_TOKEN', diff --git a/hermes_cli/setup.py b/hermes_cli/setup.py index 3ac622fd27a5..863385057543 100644 --- a/hermes_cli/setup.py +++ b/hermes_cli/setup.py @@ -434,6 +434,12 @@ def _print_setup_summary(config: dict, hermes_home): else: tool_status.append(("Web Search & Extract", False, "FIRECRAWL_API_KEY")) + # Tavily (skills / integrations) + if get_env_value("TAVILY_API_KEY"): + tool_status.append(("Tavily Research Integrations", True, None)) + else: + tool_status.append(("Tavily Research Integrations", False, "TAVILY_API_KEY")) + # Browser tools (local Chromium or Browserbase cloud) import shutil diff --git a/hermes_cli/status.py b/hermes_cli/status.py index 971dad47fad7..e012b1026476 100644 --- a/hermes_cli/status.py +++ b/hermes_cli/status.py @@ -84,6 +84,7 @@ def show_status(args): "MiniMax": "MINIMAX_API_KEY", "MiniMax-CN": "MINIMAX_CN_API_KEY", "Firecrawl": "FIRECRAWL_API_KEY", + "Tavily": "TAVILY_API_KEY", "Browserbase": "BROWSERBASE_API_KEY", # Optional — local browser works without this "FAL": "FAL_KEY", "Tinker": "TINKER_API_KEY", diff --git a/hermes_cli/tools_config.py b/hermes_cli/tools_config.py index cb9b99657599..01073b4a2a23 100644 --- a/hermes_cli/tools_config.py +++ b/hermes_cli/tools_config.py @@ -150,9 +150,16 @@ def _prompt_yes_no(question: str, default: bool = True) -> bool: "web": { "name": "Web Search & Extract", "setup_title": "Select Search Provider", - "setup_note": "A free DuckDuckGo search skill is also included — skip this if you don't need Firecrawl.", + "setup_note": "A free DuckDuckGo search skill is also included, and Tavily-based skills/integrations can be configured here too.", "icon": "🔍", "providers": [ + { + "name": "Tavily", + "tag": "Search-first research for Tavily-based skills and integrations", + "env_vars": [ + {"key": "TAVILY_API_KEY", "prompt": "Tavily API key", "url": "https://app.tavily.com/home"}, + ], + }, { "name": "Firecrawl Cloud", "tag": "Recommended - hosted service", diff --git a/tests/hermes_cli/test_config.py b/tests/hermes_cli/test_config.py index df647fb6c390..9411486e79af 100644 --- a/tests/hermes_cli/test_config.py +++ b/tests/hermes_cli/test_config.py @@ -10,6 +10,8 @@ from hermes_cli.config import ( DEFAULT_CONFIG, + ENV_VARS_BY_VERSION, + OPTIONAL_ENV_VARS, get_hermes_home, ensure_hermes_home, load_config, @@ -151,3 +153,12 @@ def test_atomic_write_creates_valid_yaml(self, tmp_path): raw = yaml.safe_load(f) assert raw["model"] == "test/atomic-model" assert raw["agent"]["max_turns"] == 77 + + +class TestOptionalToolKeys: + def test_tavily_api_key_is_registered_for_setup_and_migration(self): + tavily = OPTIONAL_ENV_VARS["TAVILY_API_KEY"] + assert tavily["prompt"] == "Tavily API key" + assert tavily["category"] == "tool" + assert "research" in tavily["description"].lower() + assert "TAVILY_API_KEY" in ENV_VARS_BY_VERSION[7] diff --git a/tests/hermes_cli/test_status.py b/tests/hermes_cli/test_status.py new file mode 100644 index 000000000000..374e57b29eee --- /dev/null +++ b/tests/hermes_cli/test_status.py @@ -0,0 +1,14 @@ +from types import SimpleNamespace + +from hermes_cli.status import show_status + + +def test_show_status_includes_tavily_key(monkeypatch, capsys, tmp_path): + monkeypatch.setenv("HERMES_HOME", str(tmp_path)) + monkeypatch.setenv("TAVILY_API_KEY", "tvly-1234567890abcdef") + + show_status(SimpleNamespace(all=False, deep=False)) + + output = capsys.readouterr().out + assert "Tavily" in output + assert "tvly...cdef" in output