langchain-ai / langchain-ai/langgraph
Add configurable retry limit to self-RAG + update deprecated APIs
- 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
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