Every LangChain app starts with a model; the uniform interface lets you swap providers in one line.
A chat model takes a list of messages and returns an AIMessage. Create one with the provider-agnostic helper:
model = init_chat_model("openai:gpt-5-mini", temperature=0)The string is provider:model; the matching provider package must be installed. Equivalent explicit classes exist, such as ChatOpenAI(model=...) or ChatAnthropic(model=...). Common parameters: temperature, max_tokens, timeout, max_retries.
All chat models share the Runnable interface:
model.invoke("Hello")ormodel.invoke([messages])returns anAIMessage.model.batch([...])runs several inputs in parallel.model.stream(...)yieldsAIMessageChunks as tokens arrive.ainvoke,abatch,astreamare the async versions.
The returned AIMessage has:
.content: a string or a list of provider-specific content parts,.text: the plain text,.content_blocks: a standardized, provider-independent list of blocks (text, reasoning, tool calls, images),.tool_callswhen the model wants to call tools,.usage_metadatawith input and output token counts,.response_metadatawith the raw provider details.
Because the interface is uniform, comparing providers or adding a fallback is a configuration change, not a rewrite.
Example
from langchain.chat_models import init_chat_model
model = init_chat_model("openai:gpt-5-mini", temperature=0) # needs OPENAI_API_KEY
# model = init_chat_model("anthropic:claude-sonnet-4-5") # same interface
reply = model.invoke("In one sentence, what is a vector database?")
print(type(reply).__name__) # AIMessage
print(reply.text) # A vector database stores embeddings ...
print(reply.usage_metadata) # {'input_tokens': 17, 'output_tokens': 25, ...}
answers = model.batch(["Capital of Japan?", "Capital of Kenya?"])
print([a.text for a in answers]) # ['Tokyo.', 'Nairobi.'] (wording varies)
for chunk in model.stream("Count to five."):
print(chunk.text, end="", flush=True) # 1, 2, 3, 4, 5Remember
init_chat_model("provider:model")creates any supported chat model- invoke, batch and stream (plus async variants) work the same everywhere
- Read
.text,.content_blocks,.tool_callsand.usage_metadatafrom the AIMessage
Watch out for
- Hard-coding a provider class everywhere, which makes switching or adding fallbacks a large refactor.