Migrating from LangChain 1.x to 2.0

LangChainMigrationTutorial

Complete guide to migrating your LangChain application to version 2.0 with 10x performance improvements.

Migrating from LangChain 1.x to 2.0

Overview

LangChain 2.0 represents a complete architectural overhaul with significant performance improvements and a simplified API. This tutorial guides you through migrating your existing LangChain 1.x code to version 2.0.

By the end of this tutorial, you'll understand the key changes, have migrated a sample application, and know how to leverage the new features.

Prerequisites

  • Existing LangChain 1.x code
  • Python 3.10+ or Node.js 18+
  • Understanding of your current chain architecture

Step 1: Understand the Key Changes

What's Different?

AspectLangChain 1.xLangChain 2.0
Core ParadigmClass-based chainsLCEL (functional composition)
PerformanceBaseline10x faster
StreamingAdded onNative throughout
TypeScriptGoodExcellent
Parallel ExecutionManualBuilt-in
DebuggingBasicEnhanced tracing

Why Migrate?

  1. Performance: 10x faster for common operations
  2. Simplicity: Less boilerplate, more intuitive
  3. Streaming: Native at every layer
  4. Type Safety: Better TypeScript support
  5. Future-Proof: 1.x is in maintenance mode

Step 2: Update Dependencies

Python

# Remove old packages
pip uninstall langchain langchain-core langchain-community -y

# Install 2.0
pip install langchain==2.0.0
pip install langchain-core==2.0.0
pip install langchain-community==2.0.0
pip install langchain-openai==2.0.0
pip install langchain-anthropic==2.0.0

Node.js

npm uninstall @langchain/core @langchain/community
npm install @langchain/core@2.0.0
npm install @langchain/community@2.0.0
npm install @langchain/openai@2.0.0

Step 3: Update Imports

Common Import Changes

# OLD (1.x)
from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain, RetrievalQA
from langchain.agents import initialize_agent, Tool
from langchain.memory import ConversationBufferMemory

# NEW (2.0)
from langchain_openai import OpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain.chains import RetrievalQA  # Some still exist, but prefer LCEL
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.chat_history import InMemoryChatMessageHistory

Import Migration Guide

1.x Import2.0 Import
langchain.llms.OpenAIlangchain_openai.OpenAI
langchain.llms.Anthropiclangchain_anthropic.Anthropic
langchain.prompts.PromptTemplatelangchain_core.prompts.PromptTemplate
langchain.prompts.ChatPromptTemplatelangchain_core.prompts.ChatPromptTemplate
langchain.chains.LLMChainUse LCEL: prompt | llm | parser
langchain.agents.initialize_agentlangchain.agents.create_tool_calling_agent
langchain.memory.ConversationBufferMemorylangchain_core.chat_history.InMemoryChatMessageHistory

Step 4: Convert Chains to LCEL

Simple LLM Chain

# OLD (1.x)
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate

prompt = PromptTemplate.from_template("Tell me a joke about {topic}")
llm = OpenAI()
chain = LLMChain(llm=llm, prompt=prompt)
result = chain.run(topic="cats")

# NEW (2.0)
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

prompt = ChatPromptTemplate.from_template("Tell me a joke about {topic}")
llm = OpenAI()
parser = StrOutputParser()

chain = prompt | llm | parser
result = chain.invoke({"topic": "cats"})

RAG Chain

# OLD (1.x)
from langchain.chains import RetrievalQA

qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=retriever
)
result = qa_chain.run("What is the return policy?")

# NEW (2.0)
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

qa_prompt = ChatPromptTemplate.from_template("""
    Answer the question based only on the following context:
    {context}

    Question: {input}
""")

rag_chain = (
    {"context": retriever, "input": lambda x: x["input"]}
    | qa_prompt
    | llm
    | StrOutputParser()
)
result = rag_chain.invoke({"input": "What is the return policy?"})

Chain with Multiple Steps

# OLD (1.x)
from langchain.chains import TransformChain, LLMChain

first_chain = LLMChain(llm=llm, prompt=first_prompt)
second_chain = LLMChain(llm=llm, prompt=second_prompt)

# Combined manually
intermediate = first_chain.run(input)
result = second_chain.run(input=intermediate)

# NEW (2.0)
chain = (
    first_prompt | llm | StrOutputParser()
    | lambda x: second_prompt.format(input=x)
    | llm
    | StrOutputParser()
)
result = chain.invoke({"input": "text"})

Step 5: Update Memory Usage

Conversation Memory

# OLD (1.x)
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain

memory = ConversationBufferMemory()
chain = ConversationChain(llm=llm, memory=memory)

response1 = chain.run("Hello, my name is John")
response2 = chain.run("What's my name?")

# NEW (2.0)
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate

store = InMemoryChatMessageHistory()

chain = (
    ChatPromptTemplate.from_messages([
        ("system", "You are a helpful assistant."),
        ("human", "{input}"),
        ("placeholder", "{chat_history}"),
    ])
    | llm
    | StrOutputParser()
)

response1 = chain.invoke(
    {"input": "Hello, my name is John"},
    config={"configurable": {"chat_history": store.messages}}
)
response2 = chain.invoke(
    {"input": "What's my name?"},
    config={"configurable": {"chat_history": store.messages}}
)

Step 6: Update Agents

Tool-Calling Agent

# OLD (1.x)
from langchain.agents import initialize_agent, Tool, AgentType
from langchain.tools import DuckDuckGoSearchRun

tools = [
    Tool(
        name="Search",
        func=DuckDuckGoSearchRun().run,
        description="Search the web"
    )
]

agent = initialize_agent(
    tools, 
    llm, 
    agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION
)
result = agent.run("What's the weather in Tokyo?")

# NEW (2.0)
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain.tools import DuckDuckGoSearchRun
from langchain_core.prompts import ChatPromptTemplate

tools = [DuckDuckGoSearchRun()]

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant."),
    ("placeholder", "{agent_scratchpad}"),
    ("human", "{input}"),
])

agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools)
result = executor.invoke({"input": "What's the weather in Tokyo?"})

Step 7: Add Streaming

# OLD (1.x) - Limited streaming support
for token in chain.stream("What is AI?"):
    print(token, end="", flush=True)

# NEW (2.0) - Native streaming
for chunk in chain.stream({"input": "What is AI?"}):
    print(chunk, end="", flush=True)

# Event-based streaming (2.0)
for event in chain.astream_events(
    {"input": "What is AI?"}, 
    version="v2"
):
    kind = event["event"]
    if kind == "on_chain_stream":
        print(event["data"]["chunk"], end="", flush=True)

Step 8: Add Parallel Execution

# NEW (2.0) - Parallel execution
from langchain_core.runnables import RunnableParallel

parallel_chain = RunnableParallel({
    "summary": summary_chain,
    "keywords": keyword_extractor,
    "sentiment": sentiment_analyzer
})

results = parallel_chain.invoke({"text": document})
# All three run in parallel!
print(results["summary"])
print(results["keywords"])
print(results["sentiment"])

Step 9: Add Error Handling

# NEW (2.0) - Built-in retry
chain = base_chain.with_retry(
    max_retries=3,
    wait_exponential_multiplier=1000,
    wait_exponential_max=10000
)

# Fallbacks
chain = primary_chain.with_fallbacks([
    ChatAnthropic(),
    ChatGoogleGenerativeAI()
])

Step 10: Test Your Migration

Create a Test Suite

import pytest

class TestMigratedChain:
    def test_simple_query(self):
        result = chain.invoke({"topic": "cats"})
        assert len(result) > 0
    
    def test_streaming(self):
        chunks = list(chain.stream({"topic": "cats"}))
        assert len(chunks) > 0
    
    def test_error_handling(self):
        # Test with invalid input
        result = chain.invoke({"topic": ""})
        # Should handle gracefully

Run Tests

pytest tests/ -v

Complete Migration Example

Before (1.x)

from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
from langchain.llms import OpenAI
from langchain.memory import ConversationBufferMemory

class QASystem:
    def __init__(self):
        self.llm = OpenAI()
        self.memory = ConversationBufferMemory()
        self.qa_chain = RetrievalQA.from_chain_type(
            llm=self.llm,
            chain_type="stuff",
            retriever=self.retriever
        )
    
    def ask(self, question: str) -> str:
        return self.qa_chain.run(question)

After (2.0)

from langchain_openai import OpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.chat_history import InMemoryChatMessageHistory

class QASystem:
    def __init__(self):
        self.llm = OpenAI()
        self.history = InMemoryChatMessageHistory()
        self.qa_chain = self._create_chain()
    
    def _create_chain(self):
        prompt = ChatPromptTemplate.from_template("""
            Answer based on context:
            {context}
            
            Question: {input}
        """)
        return (
            {"context": self.retriever, "input": lambda x: x["input"]}
            | prompt
            | self.llm
            | StrOutputParser()
        )
    
    def ask(self, question: str) -> str:
        return self.qa_chain.invoke(
            {"input": question},
            config={"configurable": {"chat_history": self.history.messages}}
        )

Common Issues and Solutions

Issue: Import Errors

Solution: Make sure you've installed all required packages:

pip install langchain langchain-core langchain-community langchain-openai

Issue: Chain Not Working

Solution: Check that you're using the correct LCEL syntax:

# Correct
chain = prompt | llm | parser

# Incorrect (old style won't work)
chain = LLMChain(llm=llm, prompt=prompt)  # Deprecated

Issue: Memory Not Persisting

Solution: Pass memory through config:

chain.invoke(input, config={"configurable": {"chat_history": messages}})

Best Practices

  1. Test Incrementally: Migrate one chain at a time
  2. Use LCEL: Prefer functional composition over classes
  3. Add Streaming: Take advantage of native streaming
  4. Use Parallel: Where appropriate, use parallel execution
  5. Add Error Handling: Use built-in retry and fallbacks

Resources