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
# 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- written in Several tool calls at once
- written in Three tools and a model that picks
- written in Embeddings and a vector store
- written in Documents and splitting
View the code here
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?")
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])
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]
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.
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 WordEmbeddingsAsking Python about the base class
Then ask Python whether each one is a subclass of its base class.
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: True WordEmbeddings is a Embeddings: True Chroma is a VectorStore: True SqliteSaver is a BaseCheckpointSaver: True
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.
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())))InMemoryVectorStore refunds.md Chroma refunds.md
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, yourWordEmbeddings, an installedChromaand an installedSqliteSavereach subclass their base class. create_agentasks forBaseChatModel, not for your class, so the base class is the contract, and swapping one model for another is one line.refund_policywas written once and returned the same document from the in-memory store and from aChromastore.
Which piece has which base
| What the course used | Its base class | A real one | Package |
|---|---|---|---|
ShopModel, DeskModel | BaseChatModel | ChatOpenAI, ChatGroq | langchain-openai, langchain-groq |
WordEmbeddings | Embeddings | GoogleGenerativeAIEmbeddings, OpenAIEmbeddings | langchain-google-genai, langchain-openai |
InMemoryVectorStore | VectorStore | Chroma, QdrantVectorStore | langchain-chroma, langchain-qdrant |
InMemorySaver | BaseCheckpointSaver | SqliteSaver, PostgresSaver | langgraph-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.
Chroma without pip install langchain-chroma raises ModuleNotFoundError, not a LangChain error, so read the missing package name and install that one.Related
- Previous: Testing the desk agent
- Next: The desk on a hosted model
- Reference: Provider integrations
- Run the same
issubclasscheck againstInMemoryVectorStoreandInMemorySaver. - Print
Chroma.__mro__and findVectorStorein the list. - Install
langchain-qdrantand check its store againstVectorStoretoo.
You understood something today that you didn't yesterday.