ChatOllama — native LangChain integration
LangChain provides ChatOllama as a first-class model wrapper. It works identically to ChatOpenAI — just change the import and model name.
pip install langchain-ollama
Before (OpenAI)
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="gpt-4o-mini",
temperature=0,
)
After (Ollama — zero API cost)
from langchain_ollama import ChatOllama
llm = ChatOllama(
model="llama3.1:8b",
temperature=0,
)
The rest of your chain — prompts, output parsers, tools, memory — works without any changes. This makes it trivial to switch between local and cloud models.
Using ChatOllama in a chain
from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
llm = ChatOllama(model="llama3.1:8b", temperature=0)
prompt = ChatPromptTemplate.from_messages([
("system", "You are a concise technical writer."),
("human", "Summarise this in 2 sentences: {text}"),
])
chain = prompt | llm | StrOutputParser()
result = chain.invoke({"text": "Your long document here..."})
print(result)
Local embeddings with OllamaEmbeddings
For fully local RAG pipelines, use Ollama's embedding models instead of OpenAI's. This keeps everything on-device at zero cost.
Pull the embedding model first
ollama pull nomic-embed-text
Use in LangChain
from langchain_ollama import OllamaEmbeddings
from langchain_community.vectorstores import FAISS
embeddings = OllamaEmbeddings(model="nomic-embed-text")
# Build the vector store locally — no OpenAI API calls
vectorstore = FAISS.from_texts(
["LangChain is a framework for LLM apps.",
"Ollama runs models locally on your machine."],
embedding=embeddings,
)
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
docs = retriever.invoke("How do I run models locally?")
nomic-embed-text (274M, fast, good quality), mxbai-embed-large (335M, slightly better), all-minilm (23M, ultra-fast, weaker).
OpenAI-compatible endpoint (for non-LangChain code)
If you're using the OpenAI Python SDK directly (not LangChain), swap the base URL — no other changes needed.
from langchain_openai import ChatOpenAI
# Point ChatOpenAI at your local Ollama server
llm = ChatOpenAI(
model="llama3.1:8b",
base_url="http://localhost:11434/v1",
api_key="ollama", # required but ignored
temperature=0,
)
# Works in any existing LangChain chain
chain = prompt | llm | StrOutputParser()
This approach lets you reuse existing OpenAI-based LangChain code without adding the langchain-ollama package. Useful when you want to test a chain locally before deploying to OpenAI.
Switching between local and cloud at runtime
A common pattern: use local models during development, cloud models in production, controlled by an environment variable.
import os
def get_llm(temperature: float = 0):
if os.getenv("USE_LOCAL_LLM", "false").lower() == "true":
from langchain_ollama import ChatOllama
return ChatOllama(model="llama3.1:8b", temperature=temperature)
else:
from langchain_openai import ChatOpenAI
return ChatOpenAI(model="gpt-4o-mini", temperature=temperature)
llm = get_llm()
chain = prompt | llm | StrOutputParser()
USE_LOCAL_LLM=true python app.py # free, local
python app.py # production, OpenAI
Structured output with local models
.with_structured_output() works with ChatOllama on models that support JSON mode.
Not all local models are reliable with structured output — test before depending on it.
from pydantic import BaseModel
from langchain_ollama import ChatOllama
class Sentiment(BaseModel):
label: str # "Positive", "Negative", "Neutral"
confidence: float # 0.0–1.0
llm = ChatOllama(model="llama3.1:8b", temperature=0)
structured_llm = llm.with_structured_output(Sentiment)
result = structured_llm.invoke("Review: The product is amazing!")
print(result.label) # "Positive"
print(result.confidence) # 0.95
Reliability varies by model. Llama 3.1 8B and Mistral 7B handle simple schemas well. Smaller models (3B) are less reliable. Always add validation and a retry fallback for production.
Production gotchas with ChatOllama
ChatOllama looks identical to ChatOpenAI on the surface, but a few behaviors diverge in ways that bite you only once the chain hits real traffic. These are the issues most teams run into in the first month.
Timeouts on cold-start inference
First request after Ollama loads the model can take 5–20 seconds while weights stream into RAM. LangChain's default request timeout (60s) is usually fine, but if you wrap the chain with .with_timeout(10) you'll get spurious failures on the first call. Either preload the model with ollama run llama3.1:8b "" on boot or bump the timeout.
Streaming token boundaries differ
ChatOpenAI streams roughly word-by-word; ChatOllama can emit larger chunks (sub-sentence) depending on the model and quantization. If your UI assumes character-by-character pacing, it will look jerky. Render to a fixed-rate output buffer instead of straight-piping the stream.
JSON mode is "best effort" on smaller models
.with_structured_output() on a 3B–7B model can still hallucinate fields, trail extra prose, or emit invalid JSON 1–5% of the time. Always wrap in try/except and fall back to a re-prompt or cloud model. On Llama 3.1 8B this rate drops to well under 1%, but never zero.
Concurrency: Ollama serializes per model
Ollama runs one inference at a time per loaded model by default. Hammering the endpoint from a LangChain agent with parallel tool calls will queue, not parallelize. Set OLLAMA_NUM_PARALLEL and have enough RAM, or expect latency to add up sequentially.
Tool calling syntax is model-dependent
LangChain's bind_tools() works with Llama 3.1, Mistral Large, and a few other tool-aware models. Older or smaller models silently ignore tools and return free-form prose. Always confirm the model card supports function calling before relying on it in an agent loop.
Embedding dimensions don't transfer
nomic-embed-text outputs 768-dim vectors; OpenAI's text-embedding-3-small is 1536. If you switch embedding models, you must re-embed your entire corpus — vectors from different models cannot be compared. Tag your vector store with the embedding model name to catch mismatches.
LangChain Local LLMs FAQ
Can I use LangChain with local LLMs instead of OpenAI?
Yes. LangChain provides ChatOllama as a first-class wrapper. Change one import and one model name, and your existing chains, prompts, and output parsers work without modification.
What is ChatOllama in LangChain?
ChatOllama is LangChain's native integration for Ollama. It works identically to ChatOpenAI — same interface, same chain compatibility — but routes inference to your local Ollama server instead of the OpenAI API.
How do I use local embeddings with LangChain?
Install langchain-ollama and use OllamaEmbeddings with a model like nomic-embed-text. It works as a drop-in replacement for OpenAIEmbeddings in FAISS, Chroma, or any other vector store.
Does structured output work with local models in LangChain?
Yes, .with_structured_output() works with ChatOllama on models that support JSON mode. Llama 3.1 8B and Mistral 7B handle simple Pydantic schemas well, though smaller models are less reliable.
Can I switch between local and cloud LLMs at runtime?
Yes. A common pattern is using an environment variable like USE_LOCAL_LLM to select ChatOllama for development and ChatOpenAI for production, keeping the rest of the chain identical.
Related tutorials
Put these LangChain skills to use by building a local RAG pipeline — a fully private document Q&A system. If you haven't set up Ollama yet, start with the Ollama setup guide. To understand the tradeoffs before going local, read about the limitations of local models.
Quick summary
ChatOllamais a drop-in replacement forChatOpenAI— change one import and one model nameOllamaEmbeddingswithnomic-embed-textenables fully local RAG pipelines- OpenAI-compatible endpoint (
localhost:11434/v1) works with any OpenAI SDK client - Use an environment variable to switch between local and cloud at runtime
- Structured output works on 7B+ models — test reliability on your specific use case