From b3793d2d327797aa8e96a468ad0c9160ff98d6d1 Mon Sep 17 00:00:00 2001 From: Ryan Chen Date: Sun, 11 Jan 2026 17:35:05 -0500 Subject: [PATCH] Adding web search infra --- docker-compose.dev.yml | 1 + .../raggr/blueprints/conversation/agents.py | 44 ++++++++++++++++++- services/raggr/pyproject.toml | 2 + services/raggr/uv.lock | 31 +++++++++++++ 4 files changed, 77 insertions(+), 1 deletion(-) diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 4716f9e..2e60619 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -40,6 +40,7 @@ services: - FLASK_ENV=development - PYTHONUNBUFFERED=1 - NODE_ENV=development + - TAVILY_KEY=${TAVILIY_KEY} depends_on: postgres: condition: service_healthy diff --git a/services/raggr/blueprints/conversation/agents.py b/services/raggr/blueprints/conversation/agents.py index 02d5394..71c472f 100644 --- a/services/raggr/blueprints/conversation/agents.py +++ b/services/raggr/blueprints/conversation/agents.py @@ -1,10 +1,52 @@ +import os +from typing import cast + from langchain.agents import create_agent +from langchain.chat_models import BaseChatModel from langchain.tools import tool +from langchain_ollama import ChatOllama from langchain_openai import ChatOpenAI +from tavily import AsyncTavilyClient from blueprints.rag.logic import query_vector_store openai_gpt_5_mini = ChatOpenAI(model="gpt-5-mini") +ollama_deepseek = ChatOllama(model="llama3.1:8b", base_url=os.getenv("OLLAMA_URL")) +model_with_fallback = cast( + BaseChatModel, ollama_deepseek.with_fallbacks([openai_gpt_5_mini]) +) +client = AsyncTavilyClient(os.getenv("TAVILY_KEY"), "") + + +@tool +async def web_search(query: str) -> str: + """Search the web for current information using Tavily. + + Use this tool when you need to: + - Find current information not in the knowledge base + - Look up recent events, news, or updates + - Verify facts or get additional context + - Search for information outside of Simba's documents + + Args: + query: The search query to look up on the web + + Returns: + Search results from the web with titles, content, and source URLs + """ + response = await client.search(query=query, search_depth="basic") + results = response.get("results", []) + + if not results: + return "No results found for the query." + + formatted = "\n\n".join( + [ + f"**{result['title']}**\n{result['content']}\nSource: {result['url']}" + for result in results[:5] + ] + ) + return formatted @tool(response_format="content_and_artifact") @@ -33,4 +75,4 @@ async def simba_search(query: str): return serialized, docs -main_agent = create_agent(model=openai_gpt_5_mini, tools=[simba_search]) +main_agent = create_agent(model=model_with_fallback, tools=[simba_search, web_search]) diff --git a/services/raggr/pyproject.toml b/services/raggr/pyproject.toml index b795328..337f023 100644 --- a/services/raggr/pyproject.toml +++ b/services/raggr/pyproject.toml @@ -34,6 +34,8 @@ dependencies = [ "langchain-chroma>=1.0.0", "langchain-community>=0.4.1", "jq>=1.10.0", + "langchain-ollama>=1.0.1", + "tavily-python>=0.7.17", ] [tool.aerich] diff --git a/services/raggr/uv.lock b/services/raggr/uv.lock index 3dbab9c..5eb29e4 100644 --- a/services/raggr/uv.lock +++ b/services/raggr/uv.lock @@ -1281,6 +1281,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/83/bd/9df897cbc98290bf71140104ee5b9777cf5291afb80333aa7da5a497339b/langchain_core-1.2.5-py3-none-any.whl", hash = "sha256:3255944ef4e21b2551facb319bfc426057a40247c0a05de5bd6f2fc021fbfa34", size = 484851, upload-time = "2025-12-22T23:45:30.525Z" }, ] +[[package]] +name = "langchain-ollama" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "langchain-core" }, + { name = "ollama" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/51/72cd04d74278f3575f921084f34280e2f837211dc008c9671c268c578afe/langchain_ollama-1.0.1.tar.gz", hash = "sha256:e37880c2f41cdb0895e863b1cfd0c2c840a117868b3f32e44fef42569e367443", size = 153850, upload-time = "2025-12-12T21:48:28.68Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/46/f2907da16dc5a5a6c679f83b7de21176178afad8d2ca635a581429580ef6/langchain_ollama-1.0.1-py3-none-any.whl", hash = "sha256:37eb939a4718a0255fe31e19fbb0def044746c717b01b97d397606ebc3e9b440", size = 29207, upload-time = "2025-12-12T21:48:27.832Z" }, +] + [[package]] name = "langchain-openai" version = "1.1.6" @@ -2521,6 +2534,7 @@ dependencies = [ { name = "langchain" }, { name = "langchain-chroma" }, { name = "langchain-community" }, + { name = "langchain-ollama" }, { name = "langchain-openai" }, { name = "ollama" }, { name = "openai" }, @@ -2533,6 +2547,7 @@ dependencies = [ { name = "python-dotenv" }, { name = "quart" }, { name = "quart-jwt-extended" }, + { name = "tavily-python" }, { name = "tomlkit" }, { name = "tortoise-orm" }, { name = "tortoise-orm-stubs" }, @@ -2554,6 +2569,7 @@ requires-dist = [ { name = "langchain", specifier = ">=1.2.0" }, { name = "langchain-chroma", specifier = ">=1.0.0" }, { name = "langchain-community", specifier = ">=0.4.1" }, + { name = "langchain-ollama", specifier = ">=1.0.1" }, { name = "langchain-openai", specifier = ">=1.1.6" }, { name = "ollama", specifier = ">=0.6.0" }, { name = "openai", specifier = ">=2.0.1" }, @@ -2566,6 +2582,7 @@ requires-dist = [ { name = "python-dotenv", specifier = ">=1.0.0" }, { name = "quart", specifier = ">=0.20.0" }, { name = "quart-jwt-extended", specifier = ">=0.1.0" }, + { name = "tavily-python", specifier = ">=0.7.17" }, { name = "tomlkit", specifier = ">=0.13.3" }, { name = "tortoise-orm", specifier = ">=0.25.1" }, { name = "tortoise-orm-stubs", specifier = ">=1.0.2" }, @@ -2847,6 +2864,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, ] +[[package]] +name = "tavily-python" +version = "0.7.17" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpx" }, + { name = "requests" }, + { name = "tiktoken" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/eb/d7371ee68119380ab6561c6998eacf3031327ba89c6081d36128ab4a2184/tavily_python-0.7.17.tar.gz", hash = "sha256:437ba064639dfdce1acdbc37cbb73246abe500ab735e988a4b8698a8d5fb7df7", size = 21321, upload-time = "2025-12-17T17:08:39.3Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/ce/88565f0c9f7654bc90e19f1e76b3bffee7ff9c1741a2124ec2f2900fb080/tavily_python-0.7.17-py3-none-any.whl", hash = "sha256:a2725b9cba71e404e73d19ff277df916283c10100137c336e07f8e1bd7789fcf", size = 18214, upload-time = "2025-12-17T17:08:38.442Z" }, +] + [[package]] name = "tenacity" version = "9.1.2"