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
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,8 @@ TESSERACT_PATH=/usr/bin/tesseract
# ==============================================================================
# 15b. SEARCH & WEB (agent web-search tools)
# ==============================================================================
# You.com - https://api.you.com/
YDC_API_KEY=
# Tavily - https://tavily.com/
TAVILY_API_KEY=
# Brave Search - https://brave.com/search/api/
Expand Down
4 changes: 3 additions & 1 deletion backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,9 @@ TESSERACT_PATH=/usr/bin/tesseract
# ==============================================================================
# 22. SEARCH & WEB
# ==============================================================================
# Tavily - https://tavily.com/ (agent web-search tool)
# You.com - https://api.you.com/ (primary web search provider)
YDC_API_KEY=
# Tavily - https://tavily.com/ (agent web-search tool, fallback)
TAVILY_API_KEY=
# Brave Search - https://brave.com/search/api/
BRAVE_SEARCH_API_KEY=
Expand Down
11 changes: 11 additions & 0 deletions backend/api/byok_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,17 @@ def _initialize_default_providers(self):
model="search",
reasoning_level=1
),
AIProviderConfig(
id="youcom",
name="You.com",
description="AI-powered search engine with real-time web results and citations",
api_key_env_var="YDC_API_KEY",
base_url="https://api.you.com",
supported_tasks=["search", "web_search", "research", "rag"],
cost_per_token=0.00001, # Per search query (estimated)
model="search",
reasoning_level=1
),
AIProviderConfig(
id="brightdata",
name="Bright Data",
Expand Down
13 changes: 12 additions & 1 deletion backend/core/byok_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,17 @@ def _initialize_default_providers(self):
model="search",
reasoning_level=1
),
AIProviderConfig(
id="youcom",
name="You.com",
description="AI-powered search engine with real-time web results and citations",
api_key_env_var="YDC_API_KEY",
base_url="https://api.you.com",
supported_tasks=["search", "web_search", "research", "rag"],
cost_per_token=0.00001, # Per search query (estimated)
model="search",
reasoning_level=1
),
AIProviderConfig(
id="glm_5",
name="Zhipu GLM 5",
Expand Down Expand Up @@ -1025,7 +1036,7 @@ async def store_api_key(
detail="Invalid API key: must be at least 10 characters"
)

valid_providers = ["openai", "anthropic", "deepseek", "gemini", "moonshot", "minimax", "qwen", "lux", "groq", "google", "google_flash", "google_flash_3_5", "gemini_flash", "gemini_flash_3_5", "mistral", "glm", "glm_5", "deepinfra", "tavily", "minimax_m3", "anthropic_opus_4_6", "openai_5_3", "xiaomi", "openrouter"]
valid_providers = ["openai", "anthropic", "deepseek", "gemini", "moonshot", "minimax", "qwen", "lux", "groq", "google", "google_flash", "google_flash_3_5", "gemini_flash", "gemini_flash_3_5", "mistral", "glm", "glm_5", "deepinfra", "tavily", "youcom", "minimax_m3", "anthropic_opus_4_6", "openai_5_3", "xiaomi", "openrouter"]
if provider_id not in valid_providers:
raise HTTPException(
status_code=400,
Expand Down
78 changes: 67 additions & 11 deletions backend/integrations/mcp_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -3107,27 +3107,80 @@ def _get_nested_field(
async def web_search(self, query: str = None, tenant_id: Optional[str] = None) -> Dict[str, Any]:
"""
Performs a web search using available search APIs or MCP servers.
Supports BYOK - checks tenant-specific Tavily key first, then falls back to env var.
Supports BYOK - checks tenant-specific keys, then falls back to env vars.
Priority order: You.com, Tavily
"""
logger.info(f"Performing web search for: {query} (tenant: {tenant_id})")

# Priority 1: Check for tenant-specific BYOK Tavily key
# Priority 1: Check for tenant-specific BYOK You.com key
youcom_api_key = None
if tenant_id:
try:
byok_manager = get_byok_manager()
youcom_api_key = byok_manager.get_tenant_api_key(tenant_id, "youcom")
if youcom_api_key:
logger.info(f"Using BYOK You.com key for tenant {tenant_id}")
except Exception as e:
logger.warning(f"Failed to get BYOK You.com key: {e}")

# Priority 2: Fall back to environment variable (platform-wide You.com key)
if not youcom_api_key:
youcom_api_key = os.getenv("YDC_API_KEY")

# If we have a You.com API key, try it first
if youcom_api_key:
try:
async with httpx.AsyncClient() as client:
headers = {"Authorization": f"Bearer {youcom_api_key}"}
response = await client.post(
"https://api.you.com/search",
headers=headers,
json={
"query": query,
"num_results": 10,
"safesearch": "moderate",
"country": "US"
},
timeout=10.0
)
if response.status_code == 200:
data = response.json()
# Transform You.com response to match expected format
return {
"query": query,
"results": [
{
"url": hit.get("url", ""),
"title": hit.get("title", ""),
"content": hit.get("description", ""),
"score": hit.get("relevance_score", 0.5)
}
for hit in data.get("hits", [])
],
"answer": data.get("answer", None),
"provider": "you.com"
}
else:
logger.warning(f"You.com search failed with status {response.status_code}")
except Exception as e:
logger.error(f"You.com search failed: {e}")

# Priority 3: Check for tenant-specific BYOK Tavily key (fallback)
tavily_api_key = None
if tenant_id:
try:
byok_manager = get_byok_manager()
with SessionLocal() as db:
tavily_api_key = byok_manager.get_tenant_api_key(tenant_id, "tavily", db=db)
tavily_api_key = byok_manager.get_tenant_api_key(tenant_id, "tavily")
if tavily_api_key:
logger.info(f"Using BYOK Tavily key for tenant {tenant_id}")
logger.info(f"Using BYOK Tavily key as fallback for tenant {tenant_id}")
except Exception as e:
logger.warning(f"Failed to get BYOK Tavily key: {e}")

# Priority 2: Fall back to environment variable (platform-wide key)
# Priority 4: Fall back to environment variable (platform-wide Tavily key)
if not tavily_api_key:
tavily_api_key = os.getenv("TAVILY_API_KEY")

# If we have a Tavily API key, use it
# If we have a Tavily API key, use it as fallback
if tavily_api_key:
try:
async with httpx.AsyncClient() as client:
Expand All @@ -3141,17 +3194,20 @@ async def web_search(self, query: str = None, tenant_id: Optional[str] = None) -
timeout=10.0
)
if response.status_code == 200:
return response.json()
data = response.json()
# Add provider info to Tavily response
data["provider"] = "tavily"
return data
except Exception as e:
logger.error(f"Tavily search failed: {e}")

# No search API key configured - return empty results with error
logger.warning("No search API key (TAVILY_API_KEY) configured. Search unavailable.")
# No search API keys configured - return empty results with error
logger.warning("No search API keys (YDC_API_KEY, TAVILY_API_KEY) configured. Search unavailable.")
return {
"query": query,
"results": [],
"answer": None,
"error": "Web search is not configured. Please add a Tavily API key in Settings > AI Intelligence (BYOK)."
"error": "Web search is not configured. Please add a You.com API key (YDC_API_KEY) or Tavily API key in Settings > AI Intelligence (BYOK)."
}

# Singleton instance
Expand Down
135 changes: 135 additions & 0 deletions docs/integrations/youcom-search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# You.com Search Integration

ATOM now supports **You.com** as a primary web search provider alongside Tavily. You.com provides AI-powered search with real-time web results, citations, and advanced research capabilities.

## Features

- **Real-time web search** with current information
- **Cited results** with source attribution
- **AI-powered summaries** and answers
- **BYOK (Bring Your Own Key)** support for tenant-specific API keys
- **Automatic fallback** to Tavily if You.com is unavailable
- **Provider transparency** - responses include provider information

## Configuration

### Environment Variables

Set your You.com API key using one of these methods:

```bash
# Platform-wide configuration
YDC_API_KEY=your_youcom_api_key_here

# Alternative: Tavily fallback
TAVILY_API_KEY=your_tavily_key_here
```

### BYOK Configuration

You can also configure API keys per tenant through the BYOK system:

1. Go to **Settings > AI Intelligence (BYOK)**
2. Add a new provider key:
- Provider: `youcom`
- API Key: Your You.com API key
- Name: Any descriptive name

## Usage

The integration works automatically through ATOM's existing web search functionality:

```python
# Agents automatically use You.com for web search
search_result = await mcp_service.web_search("latest AI research papers")

# Response includes provider information
print(search_result["provider"]) # "you.com" or "tavily"
```

### Priority Order

ATOM tries search providers in this order:

1. **Tenant-specific You.com key** (BYOK)
2. **Platform You.com key** (YDC_API_KEY env var)
3. **Tenant-specific Tavily key** (BYOK fallback)
4. **Platform Tavily key** (TAVILY_API_KEY env var)

## API Response Format

You.com responses are normalized to match ATOM's expected format:

```json
{
"query": "search query",
"results": [
{
"url": "https://example.com/article",
"title": "Article Title",
"content": "Article description/snippet",
"score": 0.85
}
],
"answer": "AI-generated summary answer",
"provider": "you.com"
}
```

## Benefits Over Tavily

- **More comprehensive results** - You.com often returns richer, more detailed information
- **Better AI summaries** - Advanced answer generation with citations
- **Real-time information** - Access to very recent web content
- **Citation quality** - Higher quality source attribution

## Troubleshooting

### No Search Results

If search returns empty results:

1. **Check API key**: Verify `YDC_API_KEY` is set correctly
2. **Check BYOK**: Ensure tenant has valid You.com key configured
3. **Check logs**: Look for "You.com search failed" messages
4. **Fallback**: System should automatically fall back to Tavily

### API Key Issues

Common API key problems:

- **Invalid key**: Check You.com dashboard for correct API key
- **Quota exceeded**: Verify your You.com account has available credits
- **Network issues**: Check connectivity to api.you.com

### Configuration Check

Verify your configuration:

```bash
# Check if YDC_API_KEY is set
echo $YDC_API_KEY

# Check ATOM logs for provider selection
tail -f backend/logs/app.log | grep "web_search"
```

## Getting You.com API Key

1. Visit [You.com Developer Portal](https://api.you.com)
2. Sign up or log in to your account
3. Navigate to API Keys section
4. Generate a new API key
5. Add to your ATOM configuration

## Implementation Details

The You.com integration:

- Uses the official You.com Search API (`https://api.you.com/search`)
- Implements bearer token authentication
- Includes safety defaults (moderate safesearch, US country code)
- Provides automatic response transformation to ATOM format
- Supports graceful fallback on API failures

For technical details, see `backend/integrations/mcp_service.py` and the BYOK provider configuration in `backend/api/byok_routes.py`.
Loading