ChatOpenAI
You can use OpenAI's chat models as follows:
- npm
- Yarn
- pnpm
npm install @langchain/openai
yarn add @langchain/openai
pnpm add @langchain/openai
We're unifying model params across all packages. We now suggest using model
instead of modelName
, and apiKey
for API keys.
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage } from "@langchain/core/messages";
const model = new ChatOpenAI({
temperature: 0.9,
apiKey: "YOUR-API-KEY", // In Node.js defaults to process.env.OPENAI_API_KEY
});
// You can also pass tools or functions to the model, learn more here
// https://platform.openai.com/docs/guides/gpt/function-calling
const modelForFunctionCalling = new ChatOpenAI({
model: "gpt-4",
temperature: 0,
});
await modelForFunctionCalling.invoke(
[new HumanMessage("What is the weather in New York?")],
{
functions: [
{
name: "get_current_weather",
description: "Get the current weather in a given location",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "The city and state, e.g. San Francisco, CA",
},
unit: { type: "string", enum: ["celsius", "fahrenheit"] },
},
required: ["location"],
},
},
],
// You can set the `function_call` arg to force the model to use a function
function_call: {
name: "get_current_weather",
},
}
);
/*
AIMessage {
text: '',
name: undefined,
additional_kwargs: {
function_call: {
name: 'get_current_weather',
arguments: '{\n "location": "New York"\n}'
}
}
}
*/
// Coerce response type with JSON mode.
// Requires "gpt-4-1106-preview" or later
const jsonModeModel = new ChatOpenAI({
model: "gpt-4-1106-preview",
maxTokens: 128,
}).bind({
response_format: {
type: "json_object",
},
});
// Must be invoked with a system message containing the string "JSON":
// https://platform.openai.com/docs/guides/text-generation/json-mode
const res = await jsonModeModel.invoke([
["system", "Only return JSON"],
["human", "Hi there!"],
]);
console.log(res);
/*
AIMessage {
content: '{\n "response": "How can I assist you today?"\n}',
name: undefined,
additional_kwargs: { function_call: undefined, tool_calls: undefined }
}
*/
API Reference:
- ChatOpenAI from
@langchain/openai
- HumanMessage from
@langchain/core/messages
If you're part of an organization, you can set process.env.OPENAI_ORGANIZATION
with your OpenAI organization id, or pass it in as organization
when
initializing the model.
Multimodal messages
This feature is currently in preview. The message schema may change in future releases.
OpenAI supports interleaving images with text in input messages with their gpt-4-vision-preview
. Here's an example of how this looks:
import * as fs from "node:fs/promises";
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage } from "@langchain/core/messages";
const imageData = await fs.readFile("./hotdog.jpg");
const chat = new ChatOpenAI({
model: "gpt-4-vision-preview",
maxTokens: 1024,
});
const message = new HumanMessage({
content: [
{
type: "text",
text: "What's in this image?",
},
{
type: "image_url",
image_url: {
url: `data:image/jpeg;base64,${imageData.toString("base64")}`,
},
},
],
});
const res = await chat.invoke([message]);
console.log({ res });
/*
{
res: AIMessage {
content: 'The image shows a hot dog, which consists of a grilled or steamed sausage served in the slit of a partially sliced bun. This particular hot dog appears to be plain, without any visible toppings or condiments.',
additional_kwargs: { function_call: undefined }
}
}
*/
const hostedImageMessage = new HumanMessage({
content: [
{
type: "text",
text: "What does this image say?",
},
{
type: "image_url",
image_url:
"https://www.freecodecamp.org/news/content/images/2023/05/Screenshot-2023-05-29-at-5.40.38-PM.png",
},
],
});
const res2 = await chat.invoke([hostedImageMessage]);
console.log({ res2 });
/*
{
res2: AIMessage {
content: 'The image contains the text "LangChain" with a graphical depiction of a parrot on the left and two interlocked rings on the left side of the text.',
additional_kwargs: { function_call: undefined }
}
}
*/
const lowDetailImage = new HumanMessage({
content: [
{
type: "text",
text: "Summarize the contents of this image.",
},
{
type: "image_url",
image_url: {
url: "https://blog.langchain.dev/content/images/size/w1248/format/webp/2023/10/Screenshot-2023-10-03-at-4.55.29-PM.png",
detail: "low",
},
},
],
});
const res3 = await chat.invoke([lowDetailImage]);
console.log({ res3 });
/*
{
res3: AIMessage {
content: 'The image shows a user interface for a service named "WebLangChain," which appears to be powered by "Twalv." It includes a text box with the prompt "Ask me anything about anything!" suggesting that users can enter questions on various topics. Below the text box, there are example questions that users might ask, such as "what is langchain?", "history of mesopotamia," "how to build a discord bot," "leonardo dicaprio girlfriend," "fun gift ideas for software engineers," "how does a prism separate light," and "what beer is best." The interface also includes a round blue button with a paper plane icon, presumably to submit the question. The overall theme of the image is dark with blue accents.',
additional_kwargs: { function_call: undefined }
}
}
*/
API Reference:
- ChatOpenAI from
@langchain/openai
- HumanMessage from
@langchain/core/messages
Tool calling
This feature is currently only available for gpt-3.5-turbo-1106
and gpt-4-1106-preview
models.
More recent OpenAI chat models support calling multiple functions to get all required data to answer a question. Here's an example how a conversation turn with this functionality might look:
import { ChatOpenAI } from "@langchain/openai";
import { ToolMessage } from "@langchain/core/messages";
// Mocked out function, could be a database/API call in production
function getCurrentWeather(location: string, _unit?: string) {
if (location.toLowerCase().includes("tokyo")) {
return JSON.stringify({ location, temperature: "10", unit: "celsius" });
} else if (location.toLowerCase().includes("san francisco")) {
return JSON.stringify({
location,
temperature: "72",
unit: "fahrenheit",
});
} else {
return JSON.stringify({ location, temperature: "22", unit: "celsius" });
}
}
// Bind function to the model as a tool
const chat = new ChatOpenAI({
model: "gpt-3.5-turbo-1106",
maxTokens: 128,
}).bind({
tools: [
{
type: "function",
function: {
name: "get_current_weather",
description: "Get the current weather in a given location",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "The city and state, e.g. San Francisco, CA",
},
unit: { type: "string", enum: ["celsius", "fahrenheit"] },
},
required: ["location"],
},
},
},
],
tool_choice: "auto",
});
// Ask initial question that requires multiple tool calls
const res = await chat.invoke([
["human", "What's the weather like in San Francisco, Tokyo, and Paris?"],
]);
console.log(res.additional_kwargs.tool_calls);
/*
[
{
id: 'call_IiOsjIZLWvnzSh8iI63GieUB',
type: 'function',
function: {
name: 'get_current_weather',
arguments: '{"location": "San Francisco", "unit": "celsius"}'
}
},
{
id: 'call_blQ3Oz28zSfvS6Bj6FPEUGA1',
type: 'function',
function: {
name: 'get_current_weather',
arguments: '{"location": "Tokyo", "unit": "celsius"}'
}
},
{
id: 'call_Kpa7FaGr3F1xziG8C6cDffsg',
type: 'function',
function: {
name: 'get_current_weather',
arguments: '{"location": "Paris", "unit": "celsius"}'
}
}
]
*/
// Format the results from calling the tool calls back to OpenAI as ToolMessages
const toolMessages = res.additional_kwargs.tool_calls?.map((toolCall) => {
const toolCallResult = getCurrentWeather(
JSON.parse(toolCall.function.arguments).location
);
return new ToolMessage({
tool_call_id: toolCall.id,
name: toolCall.function.name,
content: toolCallResult,
});
});
// Send the results back as the next step in the conversation
const finalResponse = await chat.invoke([
["human", "What's the weather like in San Francisco, Tokyo, and Paris?"],
res,
...(toolMessages ?? []),
]);
console.log(finalResponse);
/*
AIMessage {
content: 'The current weather in:\n' +
'- San Francisco is 72°F\n' +
'- Tokyo is 10°C\n' +
'- Paris is 22°C',
additional_kwargs: { function_call: undefined, tool_calls: undefined }
}
*/
API Reference:
- ChatOpenAI from
@langchain/openai
- ToolMessage from
@langchain/core/messages
.withStructuredOutput({ ... })
The .withStructuredOutput
method is in beta. It is actively being worked on, so the API may change.
You can also use the .withStructuredOutput({ ... })
method to coerce ChatOpenAI
into returning a structured output.
The method allows for passing in either a Zod object, or a valid JSON schema (like what is returned from zodToJsonSchema
).
Using the method is simple. Just define your LLM and call .withStructuredOutput({ ... })
on it, passing the desired schema.
Here is an example using a Zod schema and the functionCalling
mode (default mode):
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { ChatOpenAI } from "@langchain/openai";
import { z } from "zod";
const model = new ChatOpenAI({
temperature: 0,
model: "gpt-4-turbo-preview",
});
const calculatorSchema = z.object({
operation: z.enum(["add", "subtract", "multiply", "divide"]),
number1: z.number(),
number2: z.number(),
});
const modelWithStructuredOutput = model.withStructuredOutput(calculatorSchema);
const prompt = ChatPromptTemplate.fromMessages([
["system", "You are VERY bad at math and must always use a calculator."],
["human", "Please help me!! What is 2 + 2?"],
]);
const chain = prompt.pipe(modelWithStructuredOutput);
const result = await chain.invoke({});
console.log(result);
/*
{ operation: 'add', number1: 2, number2: 2 }
*/
/**
* You can also specify 'includeRaw' to return the parsed
* and raw output in the result.
*/
const includeRawModel = model.withStructuredOutput(calculatorSchema, {
name: "calculator",
includeRaw: true,
});
const includeRawChain = prompt.pipe(includeRawModel);
const includeRawResult = await includeRawChain.invoke({});
console.log(JSON.stringify(includeRawResult, null, 2));
/*
{
"raw": {
"kwargs": {
"content": "",
"additional_kwargs": {
"tool_calls": [
{
"id": "call_A8yzNBDMiRrCB8dFYqJLhYW7",
"type": "function",
"function": {
"name": "calculator",
"arguments": "{\"operation\":\"add\",\"number1\":2,\"number2\":2}"
}
}
]
}
}
},
"parsed": {
"operation": "add",
"number1": 2,
"number2": 2
}
}
*/
API Reference:
- ChatPromptTemplate from
@langchain/core/prompts
- ChatOpenAI from
@langchain/openai
Additionally, you can pass in an OpenAI function definition or JSON schema directly:
If using jsonMode
as the method
you must include context in your prompt about the structured output you want. This must include the keyword: JSON
.
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { ChatOpenAI } from "@langchain/openai";
const model = new ChatOpenAI({
temperature: 0,
model: "gpt-4-turbo-preview",
});
const calculatorSchema = {
type: "object",
properties: {
operation: {
type: "string",
enum: ["add", "subtract", "multiply", "divide"],
},
number1: { type: "number" },
number2: { type: "number" },
},
required: ["operation", "number1", "number2"],
};
// Default mode is "functionCalling"
const modelWithStructuredOutput = model.withStructuredOutput(calculatorSchema);
const prompt = ChatPromptTemplate.fromMessages([
[
"system",
`You are VERY bad at math and must always use a calculator.
Respond with a JSON object containing three keys:
'operation': the type of operation to execute, either 'add', 'subtract', 'multiply' or 'divide',
'number1': the first number to operate on,
'number2': the second number to operate on.
`,
],
["human", "Please help me!! What is 2 + 2?"],
]);
const chain = prompt.pipe(modelWithStructuredOutput);
const result = await chain.invoke({});
console.log(result);
/*
{ operation: 'add', number1: 2, number2: 2 }
*/
/**
* You can also specify 'includeRaw' to return the parsed
* and raw output in the result, as well as a "name" field
* to give the LLM additional context as to what you are generating.
*/
const includeRawModel = model.withStructuredOutput(calculatorSchema, {
name: "calculator",
includeRaw: true,
method: "jsonMode",
});
const includeRawChain = prompt.pipe(includeRawModel);
const includeRawResult = await includeRawChain.invoke({});
console.log(JSON.stringify(includeRawResult, null, 2));
/*
{
"raw": {
"kwargs": {
"content": "{\n \"operation\": \"add\",\n \"number1\": 2,\n \"number2\": 2\n}",
"additional_kwargs": {}
}
},
"parsed": {
"operation": "add",
"number1": 2,
"number2": 2
}
}
*/
API Reference:
- ChatPromptTemplate from
@langchain/core/prompts
- ChatOpenAI from
@langchain/openai
Custom URLs
You can customize the base URL the SDK sends requests to by passing a configuration
parameter like this:
import { ChatOpenAI } from "@langchain/openai";
const model = new ChatOpenAI({
temperature: 0.9,
configuration: {
baseURL: "https://your_custom_url.com",
},
});
const message = await model.invoke("Hi there!");
console.log(message);
/*
AIMessage {
content: 'Hello! How can I assist you today?',
additional_kwargs: { function_call: undefined }
}
*/
API Reference:
- ChatOpenAI from
@langchain/openai
You can also pass other ClientOptions
parameters accepted by the official SDK.
If you are hosting on Azure OpenAI, see the dedicated page instead.
Calling fine-tuned models
You can call fine-tuned OpenAI models by passing in your corresponding modelName
parameter.
This generally takes the form of ft:{OPENAI_MODEL_NAME}:{ORG_NAME}::{MODEL_ID}
. For example:
import { ChatOpenAI } from "@langchain/openai";
const model = new ChatOpenAI({
temperature: 0.9,
model: "ft:gpt-3.5-turbo-0613:{ORG_NAME}::{MODEL_ID}",
});
const message = await model.invoke("Hi there!");
console.log(message);
/*
AIMessage {
content: 'Hello! How can I assist you today?',
additional_kwargs: { function_call: undefined }
}
*/
API Reference:
- ChatOpenAI from
@langchain/openai
Generation metadata
If you need additional information like logprobs or token usage, these will be returned directly in the .invoke
response.
Requires @langchain/core
version >=0.1.48.
import { ChatOpenAI } from "@langchain/openai";
// See https://cookbook.openai.com/examples/using_logprobs for details
const model = new ChatOpenAI({
logprobs: true,
// topLogprobs: 5,
});
const responseMessage = await model.invoke("Hi there!");
console.log(JSON.stringify(responseMessage, null, 2));
/*
{
"lc": 1,
"type": "constructor",
"id": [
"langchain_core",
"messages",
"AIMessage"
],
"kwargs": {
"content": "Hello! How can I assist you today?",
"additional_kwargs": {},
"response_metadata": {
"tokenUsage": {
"completionTokens": 9,
"promptTokens": 10,
"totalTokens": 19
},
"finish_reason": "stop",
"logprobs": {
"content": [
{
"token": "Hello",
"logprob": -0.0006793116,
"bytes": [
72,
101,
108,
108,
111
],
"top_logprobs": []
},
{
"token": "!",
"logprob": -0.00011725161,
"bytes": [
33
],
"top_logprobs": []
},
{
"token": " How",
"logprob": -0.000038457987,
"bytes": [
32,
72,
111,
119
],
"top_logprobs": []
},
{
"token": " can",
"logprob": -0.00094290765,
"bytes": [
32,
99,
97,
110
],
"top_logprobs": []
},
{
"token": " I",
"logprob": -0.0000013856493,
"bytes": [
32,
73
],
"top_logprobs": []
},
{
"token": " assist",
"logprob": -0.14702488,
"bytes": [
32,
97,
115,
115,
105,
115,
116
],
"top_logprobs": []
},
{
"token": " you",
"logprob": -0.000001147242,
"bytes": [
32,
121,
111,
117
],
"top_logprobs": []
},
{
"token": " today",
"logprob": -0.000067901296,
"bytes": [
32,
116,
111,
100,
97,
121
],
"top_logprobs": []
},
{
"token": "?",
"logprob": -0.000014974867,
"bytes": [
63
],
"top_logprobs": []
}
]
}
}
}
}
*/
API Reference:
- ChatOpenAI from
@langchain/openai
With callbacks
You can also use the callbacks system:
import { ChatOpenAI } from "@langchain/openai";
// See https://cookbook.openai.com/examples/using_logprobs for details
const model = new ChatOpenAI({
logprobs: true,
// topLogprobs: 5,
});
const result = await model.invoke("Hi there!", {
callbacks: [
{
handleLLMEnd(output) {
console.log("GENERATION OUTPUT:", JSON.stringify(output, null, 2));
},
},
],
});
console.log("FINAL OUTPUT", result);
/*
GENERATION OUTPUT: {
"generations": [
[
{
"text": "Hello! How can I assist you today?",
"message": {
"lc": 1,
"type": "constructor",
"id": [
"langchain_core",
"messages",
"AIMessage"
],
"kwargs": {
"content": "Hello! How can I assist you today?",
"additional_kwargs": {}
}
},
"generationInfo": {
"finish_reason": "stop",
"logprobs": {
"content": [
{
"token": "Hello",
"logprob": -0.0010195904,
"bytes": [
72,
101,
108,
108,
111
],
"top_logprobs": []
},
{
"token": "!",
"logprob": -0.0004447316,
"bytes": [
33
],
"top_logprobs": []
},
{
"token": " How",
"logprob": -0.00006682846,
"bytes": [
32,
72,
111,
119
],
"top_logprobs": []
},
...
]
}
}
}
]
],
"llmOutput": {
"tokenUsage": {
"completionTokens": 9,
"promptTokens": 10,
"totalTokens": 19
}
}
}
FINAL OUTPUT AIMessage {
content: 'Hello! How can I assist you today?',
additional_kwargs: { function_call: undefined, tool_calls: undefined }
}
*/
API Reference:
- ChatOpenAI from
@langchain/openai
With .generate()
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage } from "@langchain/core/messages";
// See https://cookbook.openai.com/examples/using_logprobs for details
const model = new ChatOpenAI({
logprobs: true,
// topLogprobs: 5,
});
const generations = await model.invoke([new HumanMessage("Hi there!")]);
console.log(JSON.stringify(generations, null, 2));
/*
{
"generations": [
[
{
"text": "Hello! How can I assist you today?",
"message": {
"lc": 1,
"type": "constructor",
"id": [
"langchain_core",
"messages",
"AIMessage"
],
"kwargs": {
"content": "Hello! How can I assist you today?",
"additional_kwargs": {}
}
},
"generationInfo": {
"finish_reason": "stop",
"logprobs": {
"content": [
{
"token": "Hello",
"logprob": -0.0011337858,
"bytes": [
72,
101,
108,
108,
111
],
"top_logprobs": []
},
{
"token": "!",
"logprob": -0.00044127836,
"bytes": [
33
],
"top_logprobs": []
},
{
"token": " How",
"logprob": -0.000065994034,
"bytes": [
32,
72,
111,
119
],
"top_logprobs": []
},
...
]
}
}
}
]
],
"llmOutput": {
"tokenUsage": {
"completionTokens": 9,
"promptTokens": 10,
"totalTokens": 19
}
}
}
*/
API Reference:
- ChatOpenAI from
@langchain/openai
- HumanMessage from
@langchain/core/messages
Streaming tokens
OpenAI supports streaming token counts via an opt-in call option. This can be set by passing { stream_options: { include_usage: true } }
.
Setting this call option will cause the model to return an additional chunk at the end of the stream, containing the token usage.
import { AIMessageChunk } from "@langchain/core/messages";
import { ChatOpenAI } from "@langchain/openai";
// Instantiate the model
const model = new ChatOpenAI();
const response = await model.stream("Hello, how are you?", {
// Pass the stream options
stream_options: {
include_usage: true,
},
});
// Iterate over the response, only saving the last chunk
let finalResult: AIMessageChunk | undefined;
for await (const chunk of response) {
if (finalResult) {
finalResult = finalResult.concat(chunk);
} else {
finalResult = chunk;
}
}
console.log(finalResult?.usage_metadata);
/*
{ input_tokens: 13, output_tokens: 30, total_tokens: 43 }
*/
API Reference:
- AIMessageChunk from
@langchain/core/messages
- ChatOpenAI from
@langchain/openai
See the LangSmith trace here