LangChain (YT style)LangChain 1.4 · Python 3.12+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
46 small wins to finish your pathNext lesson →

Integrations: real base classes

An integration package is a pip-installed package, named langchain- and a provider, whose classes subclass the base classes you already met. That is why the Groq model you have used since the setup lesson fits anywhere DeskModel does, and why a store or saver from a package drops into the desk by one line.

Last updated: 27 Sep, 2026 · LangChain 1.4

A provider is a company that hosts models or runs a service. LangChain publishes a package per provider, named langchain- and the provider's name: langchain-openai, langchain-anthropic, langchain-chroma. There are hundreds of them, and they are all the same shape.

The shape is this: each package holds classes that subclass the base classes you have already met. This lesson, and the Chroma and SqliteSaver lessons at the end, use two packages you have not installed yet: a vector store and a saver.

pip install "langchain-chroma==1.1.0" "langgraph-checkpoint-sqlite==3.1.1"

Installing and importing an integration

python
# for illustration: langchain-openai is not installed in this course
# every integration is: pip install langchain-<provider>, then import its class
from langchain_openai import ChatOpenAI   # subclasses BaseChatModel
from langchain_chroma import Chroma       # subclasses VectorStore
Project files used on this pageThis lesson builds on a project from earlier lessons. The code below imports these files. Click a file to see its code, or follow the link to the lesson that wrote it. To run the code yourself, keep them in the same folder.
View the code here
shop_model.py
import re

from langchain.chat_models import BaseChatModel
from langchain.messages import AIMessage, ToolMessage
from langchain_core.outputs import ChatGeneration, ChatResult


class ShopModel(BaseChatModel):
    tools: list = []

    @property
    def _llm_type(self):
        return "shop"

    def bind_tools(self, tools, **kwargs):
        return self.model_copy(update={"tools": tools})   # a copy holding the tools

    def _generate(self, messages, stop=None, run_manager=None, **kwargs):
        message = self.decide(messages)                   # the reply comes from decide
        return ChatResult(generations=[ChatGeneration(message=message)])

    def decide(self, messages):
        results = []                                # the tool results at the end
        for m in reversed(messages):
            if not isinstance(m, ToolMessage):
                break
            results.insert(0, m.text)
        if results:                                 # results are back: answer with them
            return AIMessage(" ".join(results))
        text = messages[-1].text
        orders = re.findall(r"\b[A-Z]\d+\b", text)
        tool = "refund_order" if "refund" in text.lower() else "lookup_order"
        if orders and tool in [t.name for t in self.tools]:   # one call per order id
            calls = [{"name": tool, "args": {"order_id": o}, "id": f"call_{o}"}
                     for o in orders]
            return AIMessage("", tool_calls=calls)
        if orders:                                  # that tool is not bound
            return AIMessage(f"I have no way to look up {orders[0]} yet.")
        return AIMessage("Hello. Which order is this about?")
desk_model.py
import re

from langchain.messages import AIMessage
from shop_model import ShopModel


class DeskModel(ShopModel):
    def decide(self, messages):
        last = messages[-1]
        if last.type == "tool" and last.text == "No policy covers this.":
            return AIMessage("Our policies do not cover that. A person will reply.")
        if last.type == "tool" or re.findall(r"\b[A-Z]\d+\b", last.text):
            return super().decide(messages)
        query = {"name": "search_policies", "args": {"query": last.text}, "id": "call_p"}
        return AIMessage("", tool_calls=[query])
word_embeddings.py
import re
import zlib

from langchain_core.embeddings import Embeddings

COMMON = {"a", "an", "and", "are", "can", "do", "does", "for", "how", "i",
          "if", "is", "it", "my", "of", "on", "the", "to", "what", "with", "you", "your"}


class WordEmbeddings(Embeddings):
    def embed_query(self, text):
        vector = [0.0] * 256
        for word in re.findall(r"[a-z]+", text.lower()):
            if word not in COMMON:
                vector[zlib.crc32(word.rstrip("s").encode()) % 256] += 1.0
        return vector

    def embed_documents(self, texts):
        return [self.embed_query(text) for text in texts]
policies.py
from langchain_core.documents import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter

POLICIES = {
    "refunds.md": "Refunds go back to the card you paid with. They take up to 5 working days to arrive."
                  "\n\nYou can ask for a refund within 30 days of delivery. Opened items can be refunded if they are faulty.",
    "shipping.md": "Standard shipping takes 3 to 5 working days. Shipping is free on orders over 50 euros."
                   "\n\nExpress shipping arrives the next working day and costs 9 euros.",
    "accounts.md": "To reset your password, use the reset link on the sign-in page. Support staff never ask for your password.",
}

docs = [Document(page_content=text, metadata={"source": name}) for name, text in POLICIES.items()]
splitter = RecursiveCharacterTextSplitter(chunk_size=120, chunk_overlap=0, add_start_index=True)
chunks = splitter.split_documents(docs)

Lining up each class with its base

Line up four classes with the base classes they extend: two you wrote, DeskModel and WordEmbeddings, and two from the packages you installed above, Chroma and SqliteSaver.

python
from langchain_chroma import Chroma
from langchain_core.embeddings import Embeddings
from langchain_core.language_models import BaseChatModel
from langchain_core.vectorstores import VectorStore
from langgraph.checkpoint.base import BaseCheckpointSaver
from langgraph.checkpoint.sqlite import SqliteSaver
from desk_model import DeskModel
from word_embeddings import WordEmbeddings

Asking Python about the base class

Then ask Python whether each one is a subclass of its base class.

Example
for cls, base in [(DeskModel, BaseChatModel), (WordEmbeddings, Embeddings),
                  (Chroma, VectorStore), (SqliteSaver, BaseCheckpointSaver)]:
    print(f"{cls.__name__:16} is a {base.__name__}: {issubclass(cls, base)}")

DeskModel is a BaseChatModel and Chroma is a VectorStore, so code written against those bases cannot tell them from a hosted model or the in-memory store. create_agent asks for the base class, not for your class.

The same function, either store

Code written against a base class does not care which one it is handed. This function is given the store you have used all along, and then one from a package you have never imported.

Example
from langchain_chroma import Chroma
from langchain_core.vectorstores import InMemoryVectorStore
from policies import chunks
from word_embeddings import WordEmbeddings


def refund_policy(store):
    store.add_documents(chunks)
    found = store.similarity_search("how long does a refund take", k=1)
    return f"{type(store).__name__:22} {found[0].metadata['source']}"


print(refund_policy(InMemoryVectorStore(WordEmbeddings())))
print(refund_policy(Chroma(collection_name="either-store", embedding_function=WordEmbeddings())))

Same question, same document, two different stores, and refund_policy was written once. Everything in the three lessons after this is that, applied to the desk.

What the base classes guarantee

  • All four checks print True: your DeskModel, your WordEmbeddings, an installed Chroma and an installed SqliteSaver each subclass their base class.
  • create_agent asks for BaseChatModel, not for your class, so the base class is the contract, and swapping one model for another is one line.
  • refund_policy was written once and returned the same document from the in-memory store and from a Chroma store.

Which piece has which base

What the course usedIts base classA real onePackage
ShopModel, DeskModelBaseChatModelChatOpenAI, ChatGroqlangchain-openai, langchain-groq
WordEmbeddingsEmbeddingsGoogleGenerativeAIEmbeddings, OpenAIEmbeddingslangchain-google-genai, langchain-openai
InMemoryVectorStoreVectorStoreChroma, QdrantVectorStorelangchain-chroma, langchain-qdrant
InMemorySaverBaseCheckpointSaverSqliteSaver, PostgresSaverlanggraph-checkpoint-sqlite, langgraph-checkpoint-postgres

Middleware is an integration point too. The guardrail slot that held your no_passwords check is where LangChain's own middleware plugs in, such as SummarizationMiddleware, ModelFallbackMiddleware and HumanInTheLoopMiddleware from earlier lessons, each a real class you import and add.

A hosted service reads its credentials from the environment, the way the setup lesson set GROQ_API_KEY in your environment. The three lessons after this one take the rows in order: the model, the store, and the saver.

When you move to a provider package

  • Moving from a stand-in to a hosted model, store or saver when you go from learning to running for real.
  • Choosing between providers, since every one of them presents the same base class to your code.
Watch out. Each provider is a separate install. Importing Chroma without pip install langchain-chroma raises ModuleNotFoundError, not a LangChain error, so read the missing package name and install that one.
Try it yourself
  • Run the same issubclass check against InMemoryVectorStore and InMemorySaver.
  • Print Chroma.__mro__ and find VectorStore in the list.
  • Install langchain-qdrant and check its store against VectorStore too.

You understood something today that you didn't yesterday.