DS DevShelfHub Projects · AI tools
Tutorials / Run LLMs Locally / Local Models with LangChain
Run LLMs Locally Intermediate · 10 min read Page 8 of 9

Local Models with LangChain: ChatOllama as a Drop-in for OpenAI

By DevShelfHub

How to use Ollama as a drop-in replacement for OpenAI in your LangChain chains — ChatOllama, local embeddings, and the OpenAI-compatible endpoint.

Series progress8 / 9
LangChain with local LLMs — ChatOllama drop-in replacement for OpenAI

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.

Bash
pip install langchain-ollama

Before (OpenAI)

Python
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="gpt-4o-mini",
    temperature=0,
)

After (Ollama — zero API cost)

Python
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

Python
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

Bash
ollama pull nomic-embed-text

Use in LangChain

Python
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?")
Recommended local embedding models: 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.

Python
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.

Python
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()
Bash
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.

Python
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.

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

  • ChatOllama is a drop-in replacement for ChatOpenAI — change one import and one model name
  • OllamaEmbeddings with nomic-embed-text enables 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