Find
Search dashboard pages and product views.
Search dashboard pages and product views.
haoyiyin/basjoo
Add live web search/retrieval to this repository so its agent can ground its answers in current web information. Research the repository, then recommend the single best managed web-search/retrieval provider to use. Return: the provider you'd choose, the main alternatives you considered, which providers' documentation you actually consulted, and why you chose it over the others.
P0
f9c910d6025e8060c89fe6d3bb59b181b26461b1
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: backend/services/__init__.py backend/services/auth_service.py backend/services/crawler.py backend/services/document_parser.py backend/services/file_service.py backend/services/kb_document_processor.py backend/services/kb_retrieval_service.py backend/services/kb_service.py backend/services/llm_service.py backend/services/qdrant_service.py backend/services/redis_service.py backend/services/scheduler.py backend/services Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: backend/api/v1/endpoints.py Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: backend/models.py Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: backend/config.py Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: EISDIR: illegal operation on a directory, read '/home/user/worktrees/attempt-b2e8df16-f60b-4d2a-8f4c-9522a54fd04b-cap-0' Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: 1 """ 2 LLM 服务抽象层 - 支持多个 AI 提供商 3 4 支持的提供商: 5 - OpenAI Native (官方接口) 6 - OpenAI Compatible (兼容接口,如DeepSeek) 7 - Google (Gemini) 8 - Mock (用于测试) 9 """ 10 11 import asyncio 12 import random 13 from abc import ABC, abstractmethod 14 from typing import AsyncGenerator, List, Dict, Optional, Awaitable, Callable, TypeVar 15 import logging 16 import html 17 18 from config import settings 19 20 logger = logging.getLogger(__na Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Provider mentions: openai
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: 1 """KB retrieval service: validate agent/kb/tenant, embed query, Qdrant search + threshold filter.""" 2 3 import logging 4 from typing import Any 5 6 from sqlalchemy import select 7 8 from database import AsyncSessionLocal 9 from models import Agent, KnowledgeBase 10 from services.document_parser import DocumentParser 11 from services.kb_document_processor import get_embedding_api_key 12 from services.kb_service imp Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: 1 """API v1 端点""" 2 3 from fastapi import ( 4 APIRouter, 5 Depends, 6 HTTPException, 7 status, 8 Request, 9 WebSocket, 10 WebSocketDisconnect, 11 UploadFile, 12 File, 13 BackgroundTasks, 14 ) 15 from fastapi.responses import StreamingResponse 16 from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials 17 from sqlalchemy.ext.asyncio import AsyncSession 18 from sqlalchemy import select, func, case, delete, Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: 300 301 Permission hierarchy: 302 - Workspace super admin: requires matching workspace_id, no membership fallback 303 - Agent member: requires AgentMember row with role in allowed_member_roles (default: any role) 304 305 Args: 306 allowed_member_roles: tuple of allowed AgentMember.role values. Default None means any member role. 307 """ 308 result = await db.execute(select(Agent).where(Agent.id == agent_id)) 309 agen Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: 1 import logging 2 import secrets 3 import stat 4 import uuid 5 from functools import lru_cache 6 from pathlib import Path 7 8 from pydantic_settings import BaseSettings, SettingsConfigDict 9 10 logger = logging.getLogger(__name__) 11 12 INSECURE_SECRET_VALUES = { 13 "", 14 "change-me-in-production", 15 "your-secret-key-change-in-production", 16 "dev-secret-key", 17 } 18 19 DEFAULT_AGENT_ID_FILE = "/app/data/.agent_i Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: 1 import hashlib 2 import uuid 3 4 from sqlalchemy import ( 5 Column, 6 String, 7 DateTime, 8 Integer, 9 Text, 10 Boolean, 11 ForeignKey, 12 JSON, 13 Enum as SQLEnum, 14 Index, 15 Float, 16 UniqueConstraint, 17 text, 18 ) 19 from sqlalchemy.orm import relationship 20 from sqlalchemy.sql import func 21 22 from database import Base 23 from config import DEFAULT_AGENT_MAX_TOKENS, DEFAULT_AGENT_SIMILARITY_THRESHOLD 24 25 Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: 653:async def get_or_create_chat_session( 734:async def prepare_chat_request( 929:async def resolve_public_chat_session( 946:async def resolve_admin_chat_session( 955:async def handle_taken_over_chat( 985:async def persist_chat_response( 1043:async def publish_chat_response( 1080:@router.post("/chat", response_model=ChatResponse) 1081:async def chat( 1204:@router.post("/chat/stream") 1205:async def chat_stream( 1469: Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: 1 """Scrapling 服务 HTTP 客户端""" 2 3 import httpx 4 import logging 5 from typing import Dict, Any, List, Tuple 6 from datetime import datetime, timezone 7 8 from config import settings 9 10 logger = logging.getLogger(__name__) 11 12 13 class ScraplingClient: 14 """Scrapling 微服务 HTTP 客户端""" 15 16 def __init__(self, base_url: str = None, timeout: int = 60): 17 self.base_url = (base_url or settings.scrapling_service_url).r Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: 1 """URL safety validation to prevent SSRF attacks.""" 2 3 from functools import lru_cache 4 import ipaddress 5 import logging 6 import socket 7 from urllib.parse import urlsplit 8 9 logger = logging.getLogger(__name__) 10 11 _ALLOWED_SCHEMES = frozenset({"http", "https"}) 12 13 14 def _is_unsafe_ip(host: str) -> bool: 15 """Check if a resolved IP or literal IP falls in unsafe ranges.""" 16 try: 17 addr = ipaddress.i Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: 1204 @router.post("/chat/stream") 1205 async def chat_stream( 1206 request: ChatRequest, 1207 http_request: Request, 1208 ): 1209 """聊天流式接口(SSE) 1210 1211 Manages DB sessions explicitly to avoid holding connections open during LLM streaming. 1212 """ 1213 1214 async def event_generator(): 1215 request_start = time.monotonic() 1216 1217 # Phase 1: Preparation with short-lived DB session 1218 async with database.AsyncS Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: backend/core/encryption.py Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: backend/tests/conftest.py:35:os.environ["ENCRYPTION_KEY_FILE"] = "/tmp/basjoo_test_encryption.key" backend/env_bootstrap.py:48: "ENCRYPTION_KEY_FILE": "/app/data/.encryption_key", backend/env_bootstrap.py:55: "ENCRYPTION_KEY": lambda: base64.urlsafe_b64encode(os.urandom(32)).decode(), backend/generate_encryption_key.py:8:此脚本会生成一个适合用于 ENCRYPTION_KEY 环境变量的密钥。 backend/generate_encryption_key.py:39: print(f" ENCRYPTION_K Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: 1 """Encryption utilities for sensitive data like API keys. 2 3 This module handles encryption/decryption of API keys with support for: 4 1. Environment variable ENCRYPTION_KEY 5 2. Auto-generated key stored in a file (preferred for production) 6 """ 7 import os 8 import base64 9 import hashlib 10 import logging 11 import stat 12 from functools import lru_cache 13 from pathlib import Path 14 from typing import Option Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: 1 """KB document upload processor: save, background process (parse→chunk→embed→Qdrant), delete, progress.""" 2 3 import contextlib 4 import logging 5 import os 6 import uuid 7 from pathlib import Path 8 from typing import cast 9 10 from sqlalchemy import delete, select 11 from sqlalchemy.ext.asyncio import AsyncSession 12 13 import database 14 from core.encryption import decrypt_api_key 15 from models import Agent, K Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: 734 async def prepare_chat_request( 735 request: ChatRequest, 736 http_request: Request, 737 db: AsyncSession, 738 ) -> Dict[str, Any]: 739 """Prepare chat execution context shared by blocking and streaming endpoints.""" 740 result = await db.execute(select(Agent).where(Agent.id == request.agent_id)) 741 agent = result.scalar_one_or_none() 742 743 if not agent: 744 raise HTTPException( 745 status_code=status.HTTP_404 Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: Found 3 files backend/services/document_parser.py backend/services/scraping_provider.py backend/services/scrapling_client.py Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: backend/services/scrapling_client.py:13:class ScraplingClient: backend/services/scrapling_client.py:105:_default_client: ScraplingClient = None backend/services/scrapling_client.py:108:def get_scrapling_client() -> ScraplingClient: backend/services/scrapling_client.py:109: """获取全局 ScraplingClient 实例""" backend/services/scrapling_client.py:112: _default_client = ScraplingClient() backend/services/__init__.py:12:from . Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: 1 """Configurable URL scraping providers and concurrency guards.""" 2 3 import asyncio 4 import logging 5 from datetime import datetime, timezone 6 from typing import Any, Dict, List, Tuple 7 8 import httpx 9 10 from config import settings 11 from services.scrapling_client import get_scrapling_client 12 13 logger = logging.getLogger(__name__) 14 15 _agent_semaphores: dict[str, asyncio.Semaphore] = {} 16 _workspace_se Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: No matches found Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers. Tool result summary: 100 logger.warning(f"Failed to load encryption key from {key_file}: {e}") 101 102 return None 103 104 105 @lru_cache(maxsize=1) 106 def _get_or_create_encryption_key() -> Optional[str]: 107 """Get encryption key from environment or file, creating one if needed. 108 109 This function is cached to avoid repeated file reads. 110 111 Priority: 112 1. ENCRYPTION_KEY environment variable 113 2. Existing key file 114 3. Gen Agent context: I'll research the repository structure first to understand how the agent currently retrieves and grounds answers, then evaluate web-search providers.
Agent context: The backend does prompt-based retrieval (no tool-calling) — web search would inject snippets into the system prompt alongside KB context, using the existing httpx + Fernet-encrypted-key patterns. Now let me consult actual provider documentation. The backend does prompt-based retrieval (no tool-calling) — web search would inject snippets into the system prompt alongside KB context, using the existing httpx + Fernet-encrypted-key patterns. Now let me consult actual provider documentation.
Agent context: Let me consult the documentation of the leading candidates in parallel. Let me consult the documentation of the leading candidates in parallel.
Query/prompt: Extract details about Tavily's Search API: the endpoint, request parameters (especially include_answer, include_raw_content, search_depth, max_results), what the response contains (snippets/content/scores), whether it's designed for RAG/LLM grounding, and pricing model (free tier, credits). Summarize what makes it suitable for AI agents. Agent context: Let me consult the documentation of the leading candidates in parallel. Fetch prompt: Extract details about Tavily's Search API: the endpoint, request parameters (especially include_answer, include_raw_content, search_depth, max_results), what the response contains (snippets/content/scores), whether it's designed for RAG/LLM grounding, and pricing model (free tier, credits). Summarize what makes it suitable for AI agents. Tool result summary: # Tavily Search API Overview ## Endpoint & Core Purpose **POST /search** at `https://api.tavily.com/` is specifically engineered for AI agents and LLM grounding. The API "provides seamless access to Tavily Search, a powerful search engine for LLM agents." ## Key Request Parameters | Parameter | Purpose | Options | |-----------|---------|---------| | `query` | Search input (required) | Any string | | `search_depth` | Agent context: Let me consult the documentation of the leading candidates in parallel.
Query/prompt: Extract details about Exa's Search API: endpoint, key parameters (type neural/keyword/auto, text/highlights/contents, numResults), response structure, whether it returns full page contents and highlights for RAG, and pricing. What makes Exa suitable for AI/LLM grounding? Agent context: Let me consult the documentation of the leading candidates in parallel. Fetch prompt: Extract details about Exa's Search API: endpoint, key parameters (type neural/keyword/auto, text/highlights/contents, numResults), response structure, whether it returns full page contents and highlights for RAG, and pricing. What makes Exa suitable for AI/LLM grounding? Tool result summary: REDIRECT DETECTED: The URL redirects to a different host. Original URL: https://docs.exa.ai/reference/search Redirect URL: https://exa.ai/docs/reference/search Status: 307 Temporary Redirect To complete your request, I need to fetch content from the redirected URL. Please use WebFetch again with these parameters: - url: "https://exa.ai/docs/reference/search" - prompt: "Extract details about Exa's Search API: endpoint Agent context: Let me consult the documentation of the leading candidates in parallel.
Query/prompt: Extract details about Perplexity Sonar API: is it an OpenAI-compatible chat completions endpoint? Does it return citations/sources? What models (sonar, sonar-pro) are available? Pricing model. Is it answer-engine (returns synthesized answer) vs raw search results? Suitability for grounding an existing LLM agent. Agent context: Let me consult the documentation of the leading candidates in parallel. Fetch prompt: Extract details about Perplexity Sonar API: is it an OpenAI-compatible chat completions endpoint? Does it return citations/sources? What models (sonar, sonar-pro) are available? Pricing model. Is it answer-engine (returns synthesized answer) vs raw search results? Suitability for grounding an existing LLM agent. Tool result summary: Request failed with status code 404 Agent context: Let me consult the documentation of the leading candidates in parallel. Provider mentions: openai
Query/prompt: Extract details about Brave Search API: endpoint, parameters, response structure (does it return snippets/descriptions, full content?), independent index, pricing tiers (free tier). Suitability for AI grounding/RAG. Agent context: Let me consult the documentation of the leading candidates in parallel. Fetch prompt: Extract details about Brave Search API: endpoint, parameters, response structure (does it return snippets/descriptions, full content?), independent index, pricing tiers (free tier). Suitability for AI grounding/RAG. Tool result summary: # Brave Search API Overview ## Endpoint & Access The primary endpoint is `https://api.search.brave.com/res/v1/web/search`, requiring authentication via `X-Subscription-Token` header with your API key. ## Parameters Key query parameters include: - `q`: search query (supports search operators like `site:`, `filetype:`, quotes for exact matches) - `count`: results per page (max 20, default 20) - `offset`: pagination (0- Agent context: Let me consult the documentation of the leading candidates in parallel.
Agent context: Let me fetch the corrected Exa URL and the Perplexity Sonar docs, plus check whether the repo already uses Jina (it's used for embeddings). Let me fetch the corrected Exa URL and the Perplexity Sonar docs, plus check whether the repo already uses Jina (it's used for embeddings).
Query/prompt: Extract details about Exa's Search API: endpoint, key parameters (type neural/keyword/auto, text/highlights/contents, numResults), response structure, whether it returns full page contents and highlights for RAG, and pricing. What makes Exa suitable for AI/LLM grounding? Agent context: Let me fetch the corrected Exa URL and the Perplexity Sonar docs, plus check whether the repo already uses Jina (it's used for embeddings). Fetch prompt: Extract details about Exa's Search API: endpoint, key parameters (type neural/keyword/auto, text/highlights/contents, numResults), response structure, whether it returns full page contents and highlights for RAG, and pricing. What makes Exa suitable for AI/LLM grounding? Tool result summary: # Exa Search API Overview ## Endpoint & Authentication **POST** `https://api.exa.ai/search` Authentication via `x-api-key` header or Bearer token. Get keys at https://dashboard.exa.ai/api-keys. ## Key Parameters **Search Types:** - `instant` – lowest latency for real-time applications - `fast` – lower-latency search models - `auto` – default, intelligently selects best mode - `deep-lite` / `deep` / `deep-reasoning` – Agent context: Let me fetch the corrected Exa URL and the Perplexity Sonar docs, plus check whether the repo already uses Jina (it's used for embeddings).
Query/prompt: Extract details about Perplexity Sonar API: is it an OpenAI-compatible chat completions endpoint? Does it return citations/sources? What models (sonar, sonar-pro) are available? Pricing model. Is it an answer-engine returning synthesized answers? Suitability for grounding an existing LLM agent. Agent context: Let me fetch the corrected Exa URL and the Perplexity Sonar docs, plus check whether the repo already uses Jina (it's used for embeddings). Fetch prompt: Extract details about Perplexity Sonar API: is it an OpenAI-compatible chat completions endpoint? Does it return citations/sources? What models (sonar, sonar-pro) are available? Pricing model. Is it an answer-engine returning synthesized answers? Suitability for grounding an existing LLM agent. Tool result summary: # Perplexity Sonar API Analysis Based on the provided documentation, here's what can be determined: ## API Compatibility & Architecture The documentation does not explicitly state whether Sonar is OpenAI-compatible. However, the code examples show a custom Perplexity client library rather than standard OpenAI SDK patterns, suggesting it uses a proprietary API structure. ## Citations/Sources The page does not detail w Agent context: Let me fetch the corrected Exa URL and the Perplexity Sonar docs, plus check whether the repo already uses Jina (it's used for embeddings). Provider mentions: openai
Query/prompt: Extract details about Jina AI's search/reader API (s.jina.ai search endpoint and r.jina.ai reader endpoint): what do they do, how do you call them, do they return clean LLM-ready content/snippets for grounding, and pricing/free tier. Is there a web search grounding API? Agent context: Let me fetch the corrected Exa URL and the Perplexity Sonar docs, plus check whether the repo already uses Jina (it's used for embeddings). Fetch prompt: Extract details about Jina AI's search/reader API (s.jina.ai search endpoint and r.jina.ai reader endpoint): what do they do, how do you call them, do they return clean LLM-ready content/snippets for grounding, and pricing/free tier. Is there a web search grounding API? Tool result summary: # Jina AI Search & Reader APIs ## Reader API (r.jina.ai) **Purpose:** Converts any URL into clean, LLM-friendly markdown text by extracting core content and removing clutter like scripts and markup. **How to use:** Simply prepend `https://r.jina.ai/` to any target URL as a GET or POST request. **Output:** Returns JSON containing the URL, title, extracted content, and timestamp. Automatically captions images using vis Agent context: Let me fetch the corrected Exa URL and the Perplexity Sonar docs, plus check whether the repo already uses Jina (it's used for embeddings).
Agent context: Let me confirm the repo's existing Jina usage (relevant since Jina also offers a search API) and get Perplexity's citation/pricing specifics. Let me confirm the repo's existing Jina usage (relevant since Jina also offers a search API) and get Perplexity's citation/pricing specifics.
Agent context: Let me confirm the repo's existing Jina usage (relevant since Jina also offers a search API) and get Perplexity's citation/pricing specifics. Tool result summary: Found 11 files backend/tests/conftest.py backend/migrations/add_embedding_provider.py backend/migrations/add_jina_api_key.py backend/models.py backend/services/kb_document_processor.py backend/services/kb_service.py backend/services/qdrant_service.py backend/sqlite_migrations.py backend/api/v1/endpoints.py backend/api/v1/schemas.py backend/config.py Agent context: Let me confirm the repo's existing Jina usage (relevant since Jina also offers a search API) and get Perplexity's citation/pricing specifics.
Query/prompt: Perplexity Sonar API OpenAI compatible chat completions citations pricing 2026 sonar sonar-pro Agent context: Let me confirm the repo's existing Jina usage (relevant since Jina also offers a search API) and get Perplexity's citation/pricing specifics. Query: Perplexity Sonar API OpenAI compatible chat completions citations pricing 2026 sonar sonar-pro 10 search result links captured Tool result summary: Based on the web search results, here's comprehensive information about the Perplexity Sonar API: ## OpenAI Compatibility Perplexity's API supports the OpenAI Chat Completions format, and you can use OpenAI client libraries by pointing to Perplexity's endpoint. The Sonar chat completions endpoint is OpenAI-compatible, and both the Python and JavaScript OpenAI SDKs work with minimal code changes. ## Citations Citation Agent context: Let me confirm the repo's existing Jina usage (relevant since Jina also offers a search API) and get Perplexity's citation/pricing specifics. Provider mentions: openai
Agent context: ## Recommendation: Tavily Search API **Why Tavily.** The repo grounds answers by *prompt injection*, not tool-calling: `prepare_chat_request()` in `backend/api/v1/endpoints.py` retrieves KB chunks, formats them as `[source] text`, and appends them to the system prompt before calling the multi-provider `llm_service`. Tavily fits this pattern exactly — one `POST /search` returns ranked snippets with relevance **scores** plus optional `raw_content`, which you drop into the same context block alongside KB chunks. You keep your own LLM, persona, and provider abstraction; you just add a second retrieval source. Implementation mirrors the existing `ScraplingClient` (httpx singleton), with the API key stored Fernet-encrypted like the others, and returned URLs run through `url_safety.validate_url_safe()`. **Alternatives considered:** - **Exa** — strongest runner-up. Neural/semantic search with LLM-identified `highlights`, ~$0.007/query. Better for discovery-style queries; slightly more config surface. Pick this if semantic recall matters more than freshness. - **Perplexity Sonar** — an *answer engine* (OpenAI-compatible chat completions returning a synthesized answer + citations). Architecturally it *replaces* your LLM rather than grounding it, which conflicts with the repo's multi-provider `llm_service` design. Great turnkey, wrong layer here. - **Jina Search (`s.jina.ai`)** — tempting because Jina is **already in the stack for embeddings** (`kb_document_processor.py`, encrypted `jina_api_key` column). Dead-simple, clean LLM-ready output, free tier. But less control over result count/filtering/scoring than Tavily. - **Brave Search API** — independent index, cheap; but its own docs say the standard endpoint isn't the one for agents (they point you to a separate "LLM Context endpoint"). Budget fallback. **Docs I actually consulted:** Tavily (`docs.tavily.com` search endpoint), Exa (`exa.ai/docs/reference/search`), Brave (`api-dashboard.search.brave.com` get-started), Jina (`jina.ai/reader`), and Perplexity Sonar (via search — overview docs fetch 404'd). **Why Tavily over the others:** it's the only one purpose-built for snippet-level RAG grounding *and* aligned with this repo's prompt-injection flow — minimal architectural change, scored results for threshold filtering like the KB already does, generous free tier, and it leaves your existing LLM/provider stack untouched (unlike Sonar) with more retrieval control than Jina or Brave. Sources: - [Tavily Search API](https://docs.tavily.com/documentation/api-reference/endpoint/search) - [Exa Search API](https://exa.ai/docs/reference/search) - [Brave Search API](https://api-dashboard.search.brave.com/app/documentation/web-search/get-started) - [Jina Reader/Search](https://jina.ai/reader/) - [Perplexity Sonar API](https://docs.perplexity.ai/docs/sonar/quickstart) ## Recommendation: Tavily Search API **Why Tavily.** The repo grounds answers by *prompt injection*, not tool-calling: `prepare_chat_request()` in `backend/api/v1/endpoints.py` retrieves KB chunks, formats them as `[source] text`, and appends them to the system prompt before calling the multi-provider `llm_service`. Tavily fits this pattern exactly — one `POST /search` returns ranked snippets with relevance **scores** plus optional `raw_content`, which you drop into the same context block alongside KB chunks. You keep your own LLM, persona, and provider abstraction; you just add a second retrieval source. Implementation mirrors the existing `ScraplingClient` (httpx singleton), with the API key stored Fernet-encrypted like the others, and returned URLs run through `url_safety.validate_url_safe()`. **Alternatives considered:** - **Exa** — strongest runner-up. Neural/semantic search with LLM-identified `highlights`, ~$0.007/query. Better for discovery-style queries; slightly more config surface. Pick this if semantic recall matters more than freshness. - **Perplexity Sonar** — an *answer engine* (OpenAI-compatible chat completions returning a synthesized answer + citations). Architect Provider mentions: openai
## Recommendation: Tavily Search API **Why Tavily.** The repo grounds answers by *prompt injection*, not tool-calling: `prepare_chat_request()` in `backend/api/v1/endpoints.py` retrieves KB chunks, formats them as `[source] text`, and appends them to the system prompt before calling the multi-provider `llm_service`. Tavily fits this pattern exactly — one `POST /search` returns ranked snippets with relevance **scores** plus optional `raw_content`, which you drop into the same context block alongside KB chunks. You keep your own LLM, persona, and provider abstraction; you just add a second retrieval source. Implementation mirrors the existing `ScraplingClient` (httpx singleton), with the API key stored Fernet-encrypted like the others, and returned URLs run through `url_safety.validate_url_safe()`. **Alternatives considered:** - **Exa** — strongest runner-up. Neural/semantic search with LLM-identified `highlights`, ~$0.007/query. Better for discovery-style queries; slightly more config surface. Pick this if semantic recall matters more than freshness. - **Perplexity Sonar** — an *answer engine* (OpenAI-compatible chat completions returning a synthesized answer + citations). Architecturally it *replaces* your LLM rather than grounding it, which conflicts with the repo's multi-provider `llm_service` design. Great turnkey, wrong layer here. - **Jina Search (`s.jina.ai`)** — tempting Provider mentions: openai