Files
learningcircuit--local-deep…/docs/LANGCHAIN_RETRIEVER_INTEGRATION.md
LearningCircuit 81e7a5707a feat!: remove the auto and parallel meta search engines (#4534)
* feat!: remove the auto and parallel meta search engines

The langgraph-agent strategy (the default) selects search engines
dynamically per tool call, making the LLM-based meta-pickers redundant:

- 'auto' (MetaSearchEngine, alias 'meta') spent an extra LLM call
  picking 1-3 engines and returned the first success
- 'parallel'/'parallel_scientific' (ParallelSearchEngine) fanned out
  blindly across engines and merged results

Removal also deletes the entire meta-picker special-case surface in the
egress policy: the factory skip-PEP tuple (every engine now goes through
evaluate_engine), _META_PICKER_ENGINES, the strict_with_meta_picker /
meta_picker_delegator reason codes, validate_strict_meta_combo, and the
frontend STRICT-scope guard.

Migration 0013 rewrites stored per-user data at DB open: search.tool
settings -> searxng, orphaned search.engine.auto.* /
search.engine.web.parallel.* rows deleted, news subscription engines
NULLed (= use the user's default), queued research snapshots and saved
benchmark configs rewritten.

Also fixes /api/v1/quick_summary silently overriding the user's
configured engine with 'auto' when search_tool was omitted, and the
corrupted-settings repair paths re-introducing 'auto'.

BREAKING CHANGE: search_tool='auto'/'parallel' now raises ValueError;
pick a concrete engine. LDR_SEARCH_TOOL=auto env overrides must be
updated.

* test: exempt 0013's intentional no-op downgrade from the substantive-downgrade guard

Restoring 'auto'/'parallel' references on downgrade would recreate
broken state — those engines no longer exist. Same precedent as 0004.

* fix: align merged main code with meta-engine removal

The merge from main brought in _egress_audit_net (MCP) with the old
search.tool 'auto' code default — switch it to 'searxng' like the
other call sites.

* test: fix LLM-provider tests denied by adaptive egress + private primary

The fixture sweep changed search.tool from the removed 'auto' to the
private 'library' engine — the only one the factory instantiates from
these minimal snapshots. That passed while the missing-scope fallback was
'both', but merging main (#4465, fallback -> adaptive) made a private
primary resolve to PRIVATE_ONLY, forcing local LLM and denying the remote
OpenAI/OpenRouter providers these tests configure (provider_remote).

Pin policy.egress_scope='both' in the affected fixtures so these stay
LLM-provider-config tests, decoupled from egress-scope resolution.
2026-06-13 10:00:33 +02:00

4.4 KiB

LangChain Retriever Integration

LDR now supports using any LangChain retriever as a search engine. This allows you to use vector stores, databases, or any custom retriever implementation with LDR's research capabilities.

Quick Start

from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings
from local_deep_research.api import quick_summary

# Create your retriever (any LangChain retriever works)
embeddings = OpenAIEmbeddings()
vectorstore = FAISS.from_documents(documents, embeddings)
retriever = vectorstore.as_retriever()

# Use with LDR
result = quick_summary(
    query="What are our deployment procedures?",
    retrievers={"company_kb": retriever},
    search_tool="company_kb"
)

How It Works

  1. Pass retrievers as a dictionary to any research function
  2. Each retriever gets a name (the dictionary key)
  3. Use the retriever by setting search_tool to its name
  4. Retrievers work exactly like built-in search engines

Usage Examples

Single Retriever

result = quick_summary(
    query="Your question",
    retrievers={"my_kb": retriever},
    search_tool="my_kb"  # Use only this retriever
)

Multiple Retrievers

result = detailed_research(
    query="Complex question",
    retrievers={
        "vector_db": vector_retriever,
        "graph_db": graph_retriever,
        "sql_db": sql_retriever
    },
    search_tool="vector_db"  # Primary retriever; with the default
    # langgraph-agent strategy, every registered retriever is also
    # exposed to the research agent as a search tool
)

Hybrid Search (Retriever + Web)

result = quick_summary(
    query="Compare internal and external practices",
    retrievers={"internal": internal_retriever},
    search_tool="searxng"  # Web engine as primary; the langgraph-agent
    # strategy can also query the registered "internal" retriever
)

Selective Usage

# Pass multiple retrievers but use only one
all_retrievers = {
    "tech_docs": tech_retriever,
    "legal_docs": legal_retriever,
    "hr_docs": hr_retriever
}

# Use only tech docs for this query
result = quick_summary(
    query="Technical question",
    retrievers=all_retrievers,
    search_tool="tech_docs"  # Select specific retriever
)

Supported Retrievers

Any LangChain BaseRetriever implementation works:

  • Vector Stores: FAISS, Chroma, Pinecone, Weaviate, Qdrant, etc.
  • Cloud Services: Vertex AI, AWS Bedrock, Azure Cognitive Search
  • Databases: PostgreSQL, MongoDB, Elasticsearch
  • Custom: Any class inheriting from BaseRetriever

API Reference

Parameters

All research functions (quick_summary, detailed_research, generate_report) accept:

  • retrievers: Optional[Dict[str, BaseRetriever]] - Dictionary of retrievers
  • search_tool: str - Name of the retriever (or built-in engine) to use as the primary search source

Example with Complex Setup

from langchain.vectorstores import VertexAIVectorSearch
from langchain.embeddings import VertexAIEmbeddings
import os

# User handles proxy/auth setup
os.environ["HTTP_PROXY"] = "http://proxy.company.com:8080"

# Create retriever with complex configuration
embeddings = VertexAIEmbeddings(
    project="my-project",
    location="us-central1"
)
vectorstore = VertexAIVectorSearch(
    project="my-project",
    location="us-central1",
    index="my-index",
    endpoint="my-endpoint",
    embeddings=embeddings
)
retriever = vectorstore.as_retriever()

# Use with LDR - all complexity is hidden
result = quick_summary(
    query="Internal knowledge query",
    retrievers={"vertex_ai": retriever},
    search_tool="vertex_ai"
)

Benefits

  1. Zero Coupling: LDR doesn't need to know retriever internals
  2. Full Compatibility: Works with all LDR features (strategies, agentic engine selection, etc.)
  3. Clean API: Just pass a dictionary of retrievers
  4. Flexible: Mix retrievers with web search seamlessly

Testing Your Integration

from langchain.schema import Document, BaseRetriever

class TestRetriever(BaseRetriever):
    def get_relevant_documents(self, query: str):
        return [Document(page_content=f"Test doc about {query}")]

    async def aget_relevant_documents(self, query: str):
        return self.get_relevant_documents(query)

# Test it
result = quick_summary(
    query="test",
    retrievers={"test": TestRetriever()},
    search_tool="test"
)