#!/usr/bin/env python3 """ Example of using a custom LLM with a custom retriever in Local Deep Research. This demonstrates how to integrate your own LLM implementation and custom retrieval system for programmatic access. """ from typing import List, Dict from langchain_ollama import ChatOllama, OllamaEmbeddings from langchain_core.retrievers import Document from langchain_community.vectorstores import FAISS # Import the search system from local_deep_research.search_system import AdvancedSearchSystem # Re-enable logging after import from loguru import logger import sys logger.remove() # diagnose=False: loguru defaults to True, which renders repr() of every # local in every traceback frame on exception. Users copy this snippet # into their own scripts, so leaving the default on would propagate the # credential-in-traceback leak (#4185) wherever the snippet lands. logger.add( sys.stderr, level="INFO", format="{time} {level} {message}", diagnose=False, ) logger.enable("local_deep_research") class CustomRetriever: """Custom retriever that can fetch from multiple sources.""" def __init__(self): # Initialize with sample documents for demonstration self.documents = [ { "content": "Quantum computing uses quantum bits (qubits) that can exist in superposition, " "allowing parallel computation of multiple states simultaneously.", "title": "Quantum Computing Fundamentals", "source": "quantum_basics.pdf", "metadata": {"topic": "quantum", "year": 2024}, }, { "content": "Machine learning algorithms can be categorized into supervised, unsupervised, " "and reinforcement learning approaches, each suited for different tasks.", "title": "ML Algorithm Categories", "source": "ml_overview.pdf", "metadata": {"topic": "ml", "year": 2024}, }, { "content": "Neural networks are inspired by biological neurons and consist of interconnected " "nodes that process information through weighted connections.", "title": "Neural Network Architecture", "source": "nn_architecture.pdf", "metadata": {"topic": "neural_networks", "year": 2023}, }, { "content": "Natural language processing enables computers to understand, interpret, and " "generate human language, powering applications like chatbots and translation.", "title": "NLP Applications", "source": "nlp_apps.pdf", "metadata": {"topic": "nlp", "year": 2024}, }, ] # Create embeddings for similarity search logger.info("Initializing custom retriever with embeddings...") self.embeddings = OllamaEmbeddings(model="nomic-embed-text") # Create vector store from documents docs = [ Document( page_content=doc["content"], metadata={ "title": doc["title"], "source": doc["source"], **doc["metadata"], }, ) for doc in self.documents ] self.vectorstore = FAISS.from_documents(docs, self.embeddings) def retrieve(self, query: str, k: int = 3) -> List[Dict]: """Custom retrieval logic.""" logger.info(f"Custom Retriever: Searching for '{query}'") # Use vector similarity search similar_docs = self.vectorstore.similarity_search(query, k=k) # Convert to expected format results = [] for i, doc in enumerate(similar_docs): results.append( { "title": doc.metadata.get("title", f"Document {i + 1}"), "link": doc.metadata.get("source", "custom_source"), "snippet": doc.page_content[:150] + "...", "full_content": doc.page_content, "rank": i + 1, "metadata": doc.metadata, } ) logger.info( f"Custom Retriever: Found {len(results)} relevant documents" ) return results class CustomSearchEngine: """Adapter to integrate custom retriever with the search system.""" def __init__(self, retriever: CustomRetriever, settings_snapshot=None): self.retriever = retriever self.settings_snapshot = settings_snapshot or {} def run(self, query: str, research_context=None) -> List[Dict]: """Execute search using custom retriever.""" return self.retriever.retrieve(query, k=5) def main(): """Demonstrate custom LLM and retriever integration.""" print("=== Custom LLM and Retriever Example ===\n") # 1. Create custom LLM (just using regular Ollama for simplicity) print("1. Initializing LLM...") llm = ChatOllama(model="gemma3:12b", temperature=0.7) # 2. Create custom retriever print("2. Setting up custom retriever...") custom_retriever = CustomRetriever() # 3. Create settings settings = { "search.iterations": 2, "search.questions_per_iteration": 3, "search.strategy": "source-based", "rate_limiting.enabled": False, # Disable rate limiting for custom setup } # 4. Create search engine adapter print("3. Creating search engine adapter...") search_engine = CustomSearchEngine(custom_retriever, settings) # 5. Initialize the search system print("4. Initializing AdvancedSearchSystem with custom components...") # Pass programmatic_mode=True to avoid database dependencies search_system = AdvancedSearchSystem( llm=llm, search=search_engine, settings_snapshot=settings, programmatic_mode=True, ) # 6. Run research queries queries = [ "How do quantum computers differ from classical computers?", "What are the main types of machine learning algorithms?", ] for query in queries: print(f"\n{'=' * 60}") print(f"Research Query: {query}") print("=" * 60) result = search_system.analyze_topic(query) # Display results print("\n=== FINDINGS ===") print(result["formatted_findings"]) # Show metadata print("\n=== SEARCH METADATA ===") print(f"• Total findings: {len(result['findings'])}") print(f"• Iterations: {result['iterations']}") # Get actual sources from all_links_of_system or search_results all_links = result.get("all_links_of_system", []) for finding in result.get("findings", []): if "search_results" in finding and finding["search_results"]: all_links = finding["search_results"] break print(f"• Sources found: {len(all_links)}") if all_links and len(all_links) > 0: print("\n=== SOURCES ===") for i, link in enumerate(all_links[:5], 1): # Show first 5 if isinstance(link, dict): title = link.get("title", "No title") url = link.get("link", link.get("source", "Unknown")) print(f" [{i}] {title}") print(f" URL: {url}") # Show generated questions if result.get("questions_by_iteration"): print("\n=== RESEARCH QUESTIONS GENERATED ===") for iteration, questions in result[ "questions_by_iteration" ].items(): print(f"\nIteration {iteration}:") for q in questions[:3]: # Show first 3 questions print(f" • {q}") print("\n✓ Custom LLM and Retriever integration successful!") if __name__ == "__main__": main()