Skip to content
Closed
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
84 changes: 84 additions & 0 deletions examples/basic_modules/textual_memory_internet_search_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,83 @@
print("\n Get your credentials from:")
print(" https://developers.google.com/custom-search/v1/overview")

# ============================================================================
# Step 7: Test Tavily Search API (Optional)
# ============================================================================
print("\n" + "=" * 80)
print("TAVILY SEARCH API TEST")
print("=" * 80)

tavily_api_key = os.environ.get("TAVILY_API_KEY", "")

if tavily_api_key:
print("\n[Step 7.1] Configuring Tavily Search retriever...")

tavily_retriever_config = InternetRetrieverConfigFactory.model_validate(
{
"backend": "tavily",
"config": {
"api_key": tavily_api_key,
"max_results": 5,
},
}
)

print("✓ Tavily retriever configured")
print(f" Max results: {tavily_retriever_config.config.max_results}")

print("\n[Step 7.2] Creating Tavily retriever instance...")
tavily_retriever = InternetRetrieverFactory.from_config(tavily_retriever_config, embedder)
print("✓ Tavily retriever initialized")

print("\n[Step 7.3] Performing Tavily web search...")
tavily_query = "latest AI research breakthroughs 2024"
print(f" Query: '{tavily_query}'")
print(" Searching via Tavily Search API...\n")

tavily_results = tavily_retriever.retrieve_from_internet(tavily_query)

print("✓ Tavily search completed!")
print(f"✓ Retrieved {len(tavily_results)} memory items from Tavily search\n")

print("=" * 80)
print("TAVILY SEARCH RESULTS")
print("=" * 80)

if not tavily_results:
print("\nNo results found from Tavily.")
print(" This might indicate:")
print(" - Invalid Tavily API key")
print(" - API quota exceeded")
print(" - Network connectivity issues")
else:
for idx, item in enumerate(tavily_results, 1):
print(f"\n[Tavily Result #{idx}]")
print("-" * 80)

content = item.memory
if len(content) > 300:
print(f"Content: {content[:300]}...")
print(f" (... {len(content) - 300} more characters)")
else:
print(f"Content: {content}")

if hasattr(item, "metadata") and item.metadata:
metadata = item.metadata
if hasattr(metadata, "sources") and metadata.sources:
print(f"Source: {metadata.sources[0] if metadata.sources else 'N/A'}")

print()

print("=" * 80)
print("Tavily Search Test completed!")
print("=" * 80)
else:
print("\n Skipping Tavily Search API test")
print(" To enable this test, set the following environment variable:")
print(" - TAVILY_API_KEY: Your Tavily API key")
print("\n Get your API key from: https://app.tavily.com")

print("\n" + "=" * 80)
print("ALL TESTS COMPLETED")
print("=" * 80)
Expand All @@ -297,6 +374,10 @@
print(" ✓ Tested Google Custom Search API")
else:
print(" ⏭️ Skipped Google Custom Search API (credentials not set)")
if tavily_api_key:
print(" Tested Tavily Search API")
else:
print(" Skipped Tavily Search API (credentials not set)")
print("\n💡 Quick Start:")
print(" # Set BochaAI API key")
print(" export BOCHA_API_KEY='sk-your-bocha-api-key'")
Expand All @@ -305,5 +386,8 @@
print(" export GOOGLE_API_KEY='your-google-api-key'")
print(" export GOOGLE_SEARCH_ENGINE_ID='your-search-engine-id'")
print(" ")
print(" # Set Tavily API key (optional)")
print(" export TAVILY_API_KEY='tvly-your-tavily-api-key'")
print(" ")
print(" # Run the example")
print(" python examples/basic_modules/textual_memory_internet_search_example.py\n")
6 changes: 6 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ skill-mem = [
"alibabacloud-oss-v2 (>=1.2.2,<1.2.3)",
]

# Tavily Search
tavily = [
"tavily-python (>=0.5.0)",
]

# All optional dependencies
# Allow users to install with `pip install MemoryOS[all]`
all = [
Expand Down Expand Up @@ -129,6 +134,7 @@ all = [
"nltk (>=3.9.1,<4.0.0)",
"rake-nltk (>=1.0.6,<1.1.0)",
"alibabacloud-oss-v2 (>=1.2.2,<1.2.3)",
"tavily-python (>=0.5.0)",

# Uncategorized dependencies
]
Expand Down
15 changes: 14 additions & 1 deletion src/memos/api/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,20 @@ def get_oss_config() -> dict[str, Any] | None:
return config

def get_internet_config() -> dict[str, Any]:
"""Get embedder configuration."""
"""Get internet retriever configuration."""
tavily_api_key = os.getenv("TAVILY_API_KEY", "")
bocha_api_key = os.getenv("BOCHA_API_KEY", "")

# Use Tavily if TAVILY_API_KEY is set and BOCHA_API_KEY is absent
if tavily_api_key and not bocha_api_key:
return {
"backend": "tavily",
"config": {
"api_key": tavily_api_key,
"max_results": 15,
},
}

reader_config = APIConfig.get_reader_config()
return {
"backend": "bocha",
Expand Down
7 changes: 7 additions & 0 deletions src/memos/configs/internet_retriever.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ class BochaSearchConfig(BaseInternetRetrieverConfig):
)


class TavilySearchConfig(BaseInternetRetrieverConfig):
"""Configuration class for Tavily Search API."""

max_results: int = Field(default=20, description="Maximum number of results to retrieve")


class InternetRetrieverConfigFactory(BaseConfig):
"""Factory class for creating internet retriever configurations."""

Expand All @@ -82,6 +88,7 @@ class InternetRetrieverConfigFactory(BaseConfig):
"bing": BingSearchConfig,
"xinyu": XinyuSearchConfig,
"bocha": BochaSearchConfig,
"tavily": TavilySearchConfig,
}

@field_validator("backend")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from memos.memories.textual.tree_text_memory.retrieve.internet_retriever import (
InternetGoogleRetriever,
)
from memos.memories.textual.tree_text_memory.retrieve.tavilysearch import TavilySearchRetriever
from memos.memories.textual.tree_text_memory.retrieve.xinyusearch import XinyuSearchRetriever
from memos.memos_tools.singleton import singleton_factory

Expand All @@ -21,6 +22,7 @@ class InternetRetrieverFactory:
"bing": InternetGoogleRetriever, # TODO: Implement BingRetriever
"xinyu": XinyuSearchRetriever,
"bocha": BochaAISearchRetriever,
"tavily": TavilySearchRetriever,
}

@classmethod
Expand Down Expand Up @@ -81,6 +83,12 @@ def from_config(
reader=MemReaderFactory.from_config(config.reader),
max_results=config.max_results,
)
elif backend == "tavily":
return retriever_class(
api_key=config.api_key,
embedder=embedder,
max_results=config.max_results,
)
else:
raise ValueError(f"Unsupported backend: {backend}")

Expand Down
Loading