Skip to content

Migrating LangChain 0.0.x Imports to 0.1 and Later

10 min read · updated August 11, 2026

LangChain used to be one package with a flat import surface. It is now a small core, a set of per-provider packages, a large community package and a legacy package, and an import written against the old layout fails in one of three different ways depending on how far through that history your installed version is.

The errors you arrive with

Three strings, in rough chronological order of the version that emits them.

  • LangChainDeprecationWarning: Importing chat models from langchain is deprecated — the 0.1 era. The import still works. The message tells you it will not be supported as of langchain==0.2.0 and names the package to install instead. Nothing is broken yet, which is exactly why these get ignored until they are.
  • ImportError: cannot import name 'ChatOpenAI' from 'langchain.chat_models' — the deadline arrived. The re-export was removed.
  • ModuleNotFoundError: No module named 'langchain_openai' — you followed the deprecation message but did not install the package it named. The partner packages are separate distributions; upgrading langchain does not pull them in.

The last one is the most confusing because the warning that sent you there reads like a rename. It is not a rename. It is a package split, and a split means a new line in your requirements file for every provider you use.

The four layers, and what belongs in each

The reason for the split is dependency weight. A single package containing every integration meant that installing LangChain to call one model dragged in the transitive surface of everything, and that a bug in any integration required a release of the whole framework.

  • langchain-core — the abstractions and nothing else: message types, prompt templates, output parsers, the Runnable protocol that LCEL is built on. Deliberately near-dependency-free, because everything else depends on it.
  • partner packages langchain-openai, langchain-anthropic, langchain-chroma and the rest. One provider each, versioned and released independently, maintained closest to the vendor SDK they wrap. This is where the implementation you actually call lives.
  • langchain-community — the long tail of integrations without a partner package. The project has been steadily draining this one; treat an import from here as a signal to check whether a dedicated package now exists.
  • langchain — what used to be everything, now the composition layer.

The mapping for the common imports

# models
from langchain.chat_models import ChatOpenAI
  -> from langchain_openai import ChatOpenAI
from langchain.embeddings import OpenAIEmbeddings
  -> from langchain_openai import OpenAIEmbeddings
from langchain.chat_models import ChatAnthropic
  -> from langchain_anthropic import ChatAnthropic

# prompts, parsers, messages, runnables  (all core)
from langchain.prompts import ChatPromptTemplate
  -> from langchain_core.prompts import ChatPromptTemplate
from langchain.schema import HumanMessage, AIMessage, SystemMessage
  -> from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
from langchain.schema.output_parser import StrOutputParser
  -> from langchain_core.output_parsers import StrOutputParser
from langchain.schema.runnable import RunnablePassthrough
  -> from langchain_core.runnables import RunnablePassthrough

# document loaders, vector stores, tools without a partner package
from langchain.document_loaders import PyPDFLoader
  -> from langchain_community.document_loaders import PyPDFLoader
from langchain.vectorstores import FAISS
  -> from langchain_community.vectorstores import FAISS

The rule that recovers most of this from memory: if the thing is an abstraction it is in langchain_core; if it talks to one named vendor and that vendor is popular enough it has its own package; if it talks to one named vendor and does not, it is in langchain_community.

The project ships a codemod for the bulk of this in the langchain-cli package, which is worth running before doing any of it by hand. Read its diff rather than accepting it: a codemod can rewrite an import path but cannot know which of several candidate classes you meant, which is the subject of the next section.

Two exceptions to that rule are worth memorising because they defeat it. Text splitters live in their own distribution, langchain-text-splitters, so from langchain.text_splitter import RecursiveCharacterTextSplitter becomes an import from langchain_text_splitters — not from core and not from community, which is where most people look first. And a handful of vendors have both a partner package and a lingering community module of the same name, which is the subject of the next section.

Why the same class exists three times

For a period, ChatOpenAI was importable from langchain.chat_models, from langchain_community.chat_models and from langchain_openai. They were not aliases of one object. They were separate implementations at different stages of being moved, and the partner-package one was the maintained one.

The failure this causes is nastier than an import error, because everything imports fine. Two of your modules pick different paths, and then an isinstance check between them is false, a caching layer keyed on class identity misses, or a callback registered against one class never fires for the other. If you are debugging behaviour that differs between two call sites that look identical, print type(obj).__module__ at both.

Migrate the whole repository in one commit for this reason. A half-migrated tree is not a tree with half the work left; it is a tree with a new class of bug in it. The same argument applies to pinning: pin langchain-core explicitly, since every other package depends on it and a resolver is free to satisfy them with a version none of you chose.

What moved again in 1.0

The split did not stop at 0.2. In the 1.0 line the langchain package was reduced again, to agent building blocks — create_agent, init_chat_model, the @tool decorator, message and embedding re-exports from core. Legacy chains, the older retrievers, the indexing API, the hub module and the community re-exports moved to a package called langchain-classic, per LangChain’s own v1 migration guide.

from langchain.chains import LLMChain
  -> from langchain_classic.chains import LLMChain
from langchain.retrievers import MultiQueryRetriever
  -> from langchain_classic.retrievers import MultiQueryRetriever
from langchain import hub
  -> from langchain_classic import hub

Treat langchain-classic as what its name says: a place to land so the build goes green, not a destination. If you are moving imports into it, the better use of the same afternoon is usually to move the chain itself onto composed runnables, which is a separate piece of work with its own page.

Doing the migration

  1. Record what you have: pip freeze | grep langchain. The set of langchain distributions installed tells you which era you are in more reliably than the version of any one of them.
  2. Add the partner packages you need to requirements before touching code — langchain-openai, langchain-anthropic and so on. Missing distributions produce ModuleNotFoundError that looks like a wrong path.
  3. Run the langchain-cli codemod over the tree, then read the diff for any class that could have come from more than one package.
  4. Escalate warnings to errors in your test run so the remaining deprecations are visible rather than scrolled past.
  5. Grep for type( checks and isinstance against LangChain classes. These are the ones that survive the codemod and change meaning.
  6. Commit the whole thing at once. Then pin the versions you landed on, including langchain-core.
Package names and the contents of each layer are the state at the time of writing, and this project has moved contents between packages more than once. The one durable takeaway is the shape: abstractions in a core package, one package per provider, and a legacy bucket for everything the maintainers no longer want in the main line.