Commercial LLMs natively and with LangChain
- Source
- From the RAG & Agents Engineering course
- By
- Sourav Banerjee
- Last updated
- 9 Nov 2026
Two SDKs, two response shapes
What you'll learn
- Explain what a LangChain chat model adds over a provider's own SDK.
- Read the three parts of an
AIMessage: content, token usage and provider metadata.- Diagnose a reply that stopped at the token limit from its metadata.
- Decide when a provider swap is ready to ship.
Lesson 1 of T4.1 placed the chat model in a partner package, the one part of a chain that lives outside langchain-core. This lesson looks at what that class gives you.
Larkspur's support team wants to compare OpenAI and Gemini on the refund question. The optional notebook calls both through their own SDKs, and the two calls share almost nothing:
| OpenAI SDK | Gemini SDK | |
|---|---|---|
| Set up | a key on the openai module |
genai.configure(api_key=...) |
| Send | a messages array with roles, to chat.completions.create |
a prompt string, to a GenerativeModel's generate_content |
| Read the text | response.choices[0].message.content |
response.text |
Table 1: The same request in the two native SDKs, from the optional notebook.
Every line that touches the response is provider-specific. Supporting a third provider means a third set of those lines, and every place that logs tokens or checks why a reply stopped has to know which provider answered. A common interface fixes that.
Chat models
A chat model is any class that implements LangChain's BaseChatModel interface: it takes messages and returns an AIMessage, whichever provider answers. The provider SDK still makes the call underneath; the class translates the request on the way out and the response on the way back:
Figure 1: A chat model wraps the provider's call. The chain sees one response type.
Every chat model has the same three calls:
| Call | Takes | Returns |
|---|---|---|
invoke |
a string, a message list or a PromptValue |
one AIMessage |
batch |
a list of inputs | a list of AIMessage, run concurrently on a thread pool |
stream |
one input | the reply in chunks as it arrives |
Table 2: The three calls every chat model offers.
What comes back
The AIMessage carries more than the text. Notebook 02a reads three attributes:
| Attribute | Holds | Same keys for every provider |
|---|---|---|
.content |
the reply text | ✓ |
.usage_metadata |
input, output and total token counts | ✓ |
.response_metadata |
what the provider reported, such as model_name and finish_reason |
✗ |
Table 3: The three parts of an AIMessage that lesson 2 of T4.1 reads.
The last row matters more than it looks. On OpenAI, a finish_reason of length means the reply hit the token limit and stopped mid-sentence, or mid-JSON. We call this the truncated reply: the text reads like an answer and ends early. Lesson 6 of T4.1 shows what each output parser makes of one.
The notebook also takes the chain apart. prompt | model builds a RunnableSequence, and so do prompt.pipe(model) and RunnableSequence(prompt, model); we ran all three with a stub model and each gave a sequence of 2 steps. Calling the template on its own returns a ChatPromptValue holding one HumanMessage ("Explain AI in 2 bullet points"), which is exactly what the model then receives. Invoking each step by hand is how you debug a prompt.
Changing provider
Changing provider changes one class. In the notebooks the same prompt | model chain runs on all of these:
| Class | Where the model runs | Model in the notebook |
|---|---|---|
ChatOpenAI |
OpenAI's API | gpt-4o-mini |
ChatGoogleGenerativeAI |
Google's Gemini API | gemini-2.5-flash |
ChatGroq |
Groq's hosted inference | llama-3.1-8b-instant |
ChatHuggingFace over HuggingFaceEndpoint |
Hugging Face Inference Providers | moonshotai/kimi-k2-instruct-0905 |
ChatHuggingFace over HuggingFacePipeline |
your own machine | TinyLlama/TinyLlama-1.1B-Chat-v1.0 |
Table 4: One chain, five model classes. The last three are lesson 3 of T4.1's open-source backends.
Lesson 3 of T4.1 runs those three. ChatHuggingFace is a wrapper: the two Hugging Face classes underneath are completion-style LLMs, and the wrapper gives them the chat interface (lesson 4 of T4.1).
init_chat_model from langchain moves the choice out of the import line and into a string, which is how notebook 01b loads its model. The string names the provider and the model together:
| String | Class it builds |
|---|---|
"openai:gpt-4o-mini" |
ChatOpenAI |
"groq:openai/gpt-oss-120b" |
ChatGroq |
Table 5: Two init_chat_model strings from notebook 01b. Swapping provider is an edit to the string.
Planted failure: the silent provider swap
The swap keeps the code running. It does not keep the replies the same: another model formats, refuses and rambles differently. We call this the silent provider swap: every call succeeds, and the answers drift. For Larkspur, the refund question might come back with the timing right and the citation format wrong, which no exception reports. A provider change is a release like any other, checked against the golden questions from T5.1 before it ships.
In practice: comparing two providers
For the Larkspur comparison, the chat model reduces the work to this:
- Build one chain from the answer template and a chat model named by an
init_chat_modelstring. - Run the golden questions through it once per provider string.
- Compare
.contentagainst the expected answers,.usage_metadatafor cost, andfinish_reasonin.response_metadatafor truncated replies. - Ship the provider that passes, and keep the string in configuration so a rollback is one edit.
Interview tip: "LangChain makes models interchangeable" is half true. The call shape is interchangeable. The behaviour is not, and a strong answer says how you would measure the difference.
Lesson 3 of T4.1 runs the same chain on open-source models, locally and hosted.
Run the notebooks
The code for this lesson is in these notebooks. Colab runs one in your browser: install the packages its first cell imports and set the API keys it reads. To run every notebook as written, clone the repository and use its uv environment.
| Notebook | GitHub | Colab |
|---|---|---|
| Commercial LLMs with LangChain | Open on GitHub | Open in Colab |
| Commercial LLMs Natively (optional) | Open on GitHub | Open in Colab |
Key Takeaways
- Every LangChain chat model offers
invoke,batchandstreamand returns anAIMessage, with token counts under the same keys for every provider. - Read
.response_metadataas well as.content. Afinish_reasonoflengthmeans the reply hit the token limit and stopped mid-sentence or mid-JSON. - Changing provider changes one class or one
init_chat_modelstring, but the replies change too. Treat a provider swap as a release and check it against the golden questions.
Check yourself
1. What does a LangChain chat model give you that calling each provider SDK directly does not?
The same response object for every provider. Each SDK returns its own object, while every BaseChatModel offers invoke, batch and stream and returns an AIMessage, with token counts under the same keys in .usage_metadata. Changing provider then changes one class.
2. A Larkspur reply on OpenAI stops mid-JSON, and its response metadata shows a finish reason of length. What happened?
The reply hit the token limit and stopped mid-sentence, or here mid-JSON. .response_metadata reports what the provider said, and finish_reason of length is OpenAI's signal for it. Lesson 6 of T4.1 shows what each output parser makes of such a truncated reply.
3. You change the init_chat_model string from an OpenAI model to a Groq model and the chain still runs. Can you ship the change?
Not yet. The swap keeps the code running, but another model formats, refuses and rambles differently, so the replies change. For Larkspur, a provider change is a release like any other, checked against the T5.1 golden questions before it ships.