langchain-ai / langchain-ai/langgraph

Add configurable retry limit to self-RAG + update deprecated APIs

Open
#7,481 2 comments 0 reactions 0 assignees View on GitHub
bug external
Dominant language
Python
Stars
41.8k
Forks
7.1k
Avg merge
23h 7m
Merged PRs (30d)
30

Description

### Checked other resources

- [x] This is a bug, not a usage question.
- [x] I added a clear and descriptive title that summarizes this issue.
- [x] I used the GitHub search to find a similar question and didn't find it.
- [x] I am sure that this is a bug in LangGraph rather than my code.
- [x] The bug is not resolved by updating to the latest stable version of LangGraph (or the specific integration package).
- [x] This is not related to the langchain-community package.
- [x] I posted a self-contained, minimal, reproducible example. A maintainer can copy it and run it AS IS.

### Related Issues / PRs

_No response_

### Reproduction Steps / Example Code (Python)

```python
from langsmith import Client

# Prompt
client = Client()
prompt = client.pull_prompt("rlm/rag-prompt")
##############################
from pydantic import BaseModel, Field
###############################
docs = retriever.invoke(question)
##############################
class GraphState(TypedDict):
"""
Represents the state of our graph.

Attributes:
question: question
generation: LLM generation
documents: list of documents
"""

question: str
generation: str
documents: List[str]
retries: int
max_retries: int

def max_retries_reached(state):
"""
Returns a fallback response when the maximum number of retries is reached.

This function is triggered when the self-RAG pipeline exceeds the allowed
retry limit. It generates a user-friendly message indicating that no
relevant answer could be found within the specified number of attempts.

Args:
state (dict): Contains the current pipeline state, including 'max_retries'.

Returns:
dict: A response containing the fallback generation message.
"""
return {"generation": f"Sorry, I could not find a relevant answer after {state['max_retries']} attempts."}

def decide_to_generate(state):
"""
Determines whether to generate an answer, or re-generate a question.

Args:
state (dict): The current graph state

Returns:
str: Binary decision for next node to call
"""

print("---ASSESS GRADED DOCUMENTS---")
state["question"]
filtered_documents = state["documents"]
retries = state["retries"]
max_retries = state["max_retries"]

# break the main loop if it hits the max number of attemps
if retries >= max_retries:
print("---DECISION: MAX RETRIES REACHED, RETURN FALLBACK RESPONSE---")
return "max_retries_reached"

if not filtered_documents:
# All documents have been filtered check_relevance
# We will re-generate a new query
print(
"---DECISION: ALL DOCUMENTS ARE NOT RELEVANT TO QUESTION, TRANSFORM QUERY---"
)
return "transform_query"
else:
# We have relevant documents, so generate answer
print("---DECISION: GENERATE---")
return "generate"

workflow.add_node("retrieve", retrieve) # retrieve
workflow.add_node("grade_documents", grade_documents) # grade documents
workflow.add_node("generate", generate) # generate
workflow.add_node("transform_query", transform_query) # transform_query
workflow.add_node("max_retries_reached", max_retries_reached)

# Build graph
workflow.add_edge(START, "retrieve")
workflow.add_edge("retrieve", "grade_documents")
workflow.add_conditional_edges(
"grade_documents",
decide_to_generate,
{
"transform_query": "transform_query",
"generate": "generate",
"max_retries_reached": "max_retries_reached",
},
)
workflow.add_edge("transform_query", "retrieve")
workflow.add_conditional_edges(
"generate",
grade_generation_v_documents_and_question,
{
"not supported": "generate",
"useful": END,
"not useful": "transform_query",
},
)
workflow.add_edge("max_retries_reached", END)

inputs = {"question": "what is elephants' food?", "retries": 0, "max_retries": 3}
for output in app.stream(inputs):
for key, value in output.items():
# Node
pprint(f"Node '{key}':")
# Optional: print full state at each node
# pprint.pprint(value["keys"], indent=2, width=80, depth=None)
pprint("\n---\n")

# Final generation
pprint(value["generation"])
```

### Error Message and Stack Trace (if applicable)

```shell

```

### Description

- I'm adding retry control to self-RAG and replacing deprecated LangChain/LangSmith APIs.
- I expect the self-RAG to stop gracefully after a configurable number of retries and all APIs to use their modern equivalents.
- Previously, there was no retry limit in self-RAG and the code used deprecated `langchain.pydantic_v1`, `retriever.get_relevant_documents()`, and `hub.pull()`.

### System Info

System Information
------------------
> OS: Darwin
> OS Version: Darwin Kernel Version 25.4.0: Thu Mar 19 19:33:25 PDT 2026; root:xnu-12377.101.15~1/RELEASE_ARM64_T6041
> Python Version: 3.12.13 (main, Mar 3 2026, 12:39:30) [Clang 17.0.0 (clang-1700.6.3.2)]

Package Information
-------------------
> langchain_core: 1.2.26
> langchain: 1.2.15
> langchain_community: 0.4.1
> langsmith: 0.7.22
> langchain_classic: 1.0.3
> langchain_cohere: 0.5.0
> langchain_groq: 1.1.2
> langchain_huggingface: 1.2.1
> langchain_openai: 1.1.11
> langchain_openrouter: 0.2.1
> langchain_text_splitters: 1.1.1
> langchain_yt_dlp: 0.0.8
> langchainhub: 0.1.21
> langgraph_sdk: 0.3.12

Optional packages not installed
-------------------------------
> deepagents
> deepagents-cli

Other Dependencies
------------------
> aiohttp: 3.13.3
> cohere: 5.21.1
> dataclasses-json: 0.6.7
> groq: 0.37.1
> httpx: 0.28.1
> httpx-sse: 0.4.3
> huggingface-hub: 1.7.2
> jsonpatch: 1.33
> langgraph: 1.1.6
> numpy: 2.4.3
> openai: 2.29.0
> openrouter: 0.8.1
> opentelemetry-api: 1.40.0
> opentelemetry-sdk: 1.40.0
> orjson: 3.11.7
> packaging: 24.2
> pydantic: 2.12.5
> pydantic-settings: 2.13.1
> PyYAML: 6.0.3
> pyyaml: 6.0.3
> requests: 2.32.5
> requests-toolbelt: 1.0.0
> rich: 14.3.3
> sentence-transformers: 5.3.0
> SQLAlchemy: 2.0.48
> sqlalchemy: 2.0.48
> tenacity: 9.1.4
> tiktoken: 0.12.0
> tokenizers: 0.22.2
> transformers: 5.3.0
> types-pyyaml: 6.0.12.20250915
> types-requests: 2.32.4.20260107
> typing-extensions: 4.15.0
> uuid-utils: 0.14.1
> websockets: 16.0
> wrapt: 2.1.2
> xxhash: 3.6.0
> yt-dlp: 2026.3.17
> zstandard: 0.25.0

Contributor guide

Open the contributing guide

Research direction

Start with the self-RAG workflow entry points shown in the reproduction, especially GraphState and decide_to_generate, and review the Client, retriever.invoke, and prompt-loading calls described there. Done means the workflow stops with its fallback after the configured retry limit and the deprecated API usages are replaced with their modern equivalents.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
ai
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.