Skip to content

Commit cf672b5

Browse files
authored
fix(langchain): Fix serialization for initChatModel (#7222)
1 parent d420b71 commit cf672b5

2 files changed

Lines changed: 63 additions & 21 deletions

File tree

langchain/src/chat_models/tests/universal.int.test.ts

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { ChatPromptTemplate, PromptTemplate } from "@langchain/core/prompts";
66
import { RunLogPatch, StreamEvent } from "@langchain/core/tracers/log_stream";
77
import { AIMessageChunk } from "@langchain/core/messages";
88
import { concat } from "@langchain/core/utils/stream";
9+
import { awaitAllCallbacks } from "@langchain/core/callbacks/promises";
910
import { AgentExecutor, createReactAgent } from "../../agents/index.js";
1011
import { pull } from "../../hub.js";
1112
import { initChatModel } from "../universal.js";
@@ -32,7 +33,7 @@ const googleApiKey = process.env.GOOGLE_API_KEY;
3233
process.env.GOOGLE_API_KEY = "";
3334

3435
test("Initialize non-configurable models", async () => {
35-
const gpt4 = await initChatModel("gpt-4", {
36+
const gpt4 = await initChatModel("gpt-4o-mini", {
3637
modelProvider: "openai",
3738
temperature: 0.25, // Funky temperature to verify it's being set properly.
3839
apiKey: openAIApiKey,
@@ -67,7 +68,7 @@ test("Create a partially configurable model with no default model", async () =>
6768

6869
const gpt4Result = await configurableModel.invoke("what's your name", {
6970
configurable: {
70-
model: "gpt-4",
71+
model: "gpt-4o-mini",
7172
apiKey: openAIApiKey,
7273
},
7374
});
@@ -85,7 +86,7 @@ test("Create a partially configurable model with no default model", async () =>
8586
});
8687

8788
test("Create a fully configurable model with a default model and a config prefix", async () => {
88-
const configurableModelWithDefault = await initChatModel("gpt-4", {
89+
const configurableModelWithDefault = await initChatModel("gpt-4o-mini", {
8990
modelProvider: "openai",
9091
configurableFields: "any",
9192
configPrefix: "foo",
@@ -155,7 +156,7 @@ test("Bind tools to a configurable model", async () => {
155156
}
156157
);
157158

158-
const configurableModel = await initChatModel("gpt-4", {
159+
const configurableModel = await initChatModel("gpt-4o-mini", {
159160
configurableFields: ["model", "modelProvider", "apiKey"],
160161
temperature: 0,
161162
});
@@ -602,3 +603,38 @@ describe("Can call base runnable methods", () => {
602603
expect(result.tool_calls?.[0].name).toBe("GetWeather");
603604
});
604605
});
606+
607+
describe("Serialization", () => {
608+
it("does not contain additional fields", async () => {
609+
const gpt4 = await initChatModel("gpt-4o-mini", {
610+
modelProvider: "openai",
611+
temperature: 0.25, // Funky temperature to verify it's being set properly.
612+
apiKey: openAIApiKey,
613+
});
614+
let serializedRepresentation;
615+
const res = await gpt4.invoke("foo", {
616+
callbacks: [
617+
{
618+
handleChatModelStart(llm) {
619+
serializedRepresentation = llm;
620+
},
621+
},
622+
],
623+
configurable: { extra: "bar" },
624+
});
625+
await awaitAllCallbacks();
626+
expect(res).toBeDefined();
627+
const { ChatOpenAI } = await import("@langchain/openai");
628+
expect(serializedRepresentation).toEqual(
629+
JSON.parse(
630+
JSON.stringify(
631+
new ChatOpenAI({
632+
model: "gpt-4o-mini",
633+
temperature: 0.25,
634+
apiKey: openAIApiKey,
635+
})
636+
)
637+
)
638+
);
639+
});
640+
});

langchain/src/chat_models/universal.ts

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -73,50 +73,51 @@ async function _initChatModelHelper(
7373
`Unable to infer model provider for { model: ${model} }, please specify modelProvider directly.`
7474
);
7575
}
76+
const { modelProvider: _unused, ...passedParams } = params;
7677

7778
try {
7879
switch (modelProviderCopy) {
7980
case "openai": {
8081
const { ChatOpenAI } = await import("@langchain/openai");
81-
return new ChatOpenAI({ model, ...params });
82+
return new ChatOpenAI({ model, ...passedParams });
8283
}
8384
case "anthropic": {
8485
const { ChatAnthropic } = await import("@langchain/anthropic");
85-
return new ChatAnthropic({ model, ...params });
86+
return new ChatAnthropic({ model, ...passedParams });
8687
}
8788
case "azure_openai": {
8889
const { AzureChatOpenAI } = await import("@langchain/openai");
89-
return new AzureChatOpenAI({ model, ...params });
90+
return new AzureChatOpenAI({ model, ...passedParams });
9091
}
9192
case "cohere": {
9293
const { ChatCohere } = await import("@langchain/cohere");
93-
return new ChatCohere({ model, ...params });
94+
return new ChatCohere({ model, ...passedParams });
9495
}
9596
case "google-vertexai": {
9697
const { ChatVertexAI } = await import("@langchain/google-vertexai");
97-
return new ChatVertexAI({ model, ...params });
98+
return new ChatVertexAI({ model, ...passedParams });
9899
}
99100
case "google-genai": {
100101
const { ChatGoogleGenerativeAI } = await import(
101102
"@langchain/google-genai"
102103
);
103-
return new ChatGoogleGenerativeAI({ model, ...params });
104+
return new ChatGoogleGenerativeAI({ model, ...passedParams });
104105
}
105106
case "ollama": {
106107
const { ChatOllama } = await import("@langchain/ollama");
107-
return new ChatOllama({ model, ...params });
108+
return new ChatOllama({ model, ...passedParams });
108109
}
109110
case "mistralai": {
110111
const { ChatMistralAI } = await import("@langchain/mistralai");
111-
return new ChatMistralAI({ model, ...params });
112+
return new ChatMistralAI({ model, ...passedParams });
112113
}
113114
case "groq": {
114115
const { ChatGroq } = await import("@langchain/groq");
115-
return new ChatGroq({ model, ...params });
116+
return new ChatGroq({ model, ...passedParams });
116117
}
117118
case "bedrock": {
118119
const { ChatBedrockConverse } = await import("@langchain/aws");
119-
return new ChatBedrockConverse({ model, ...params });
120+
return new ChatBedrockConverse({ model, ...passedParams });
120121
}
121122
case "fireworks": {
122123
const { ChatFireworks } = await import(
@@ -127,7 +128,7 @@ async function _initChatModelHelper(
127128
// @ts-ignore - Can not install as a proper dependency due to circular dependency
128129
"@langchain/community/chat_models/fireworks"
129130
);
130-
return new ChatFireworks({ model, ...params });
131+
return new ChatFireworks({ model, ...passedParams });
131132
}
132133
case "together": {
133134
const { ChatTogetherAI } = await import(
@@ -138,7 +139,7 @@ async function _initChatModelHelper(
138139
// @ts-ignore - Can not install as a proper dependency due to circular dependency
139140
"@langchain/community/chat_models/togetherai"
140141
);
141-
return new ChatTogetherAI({ model, ...params });
142+
return new ChatTogetherAI({ model, ...passedParams });
142143
}
143144
default: {
144145
const supported = _SUPPORTED_PROVIDERS.join(", ");
@@ -247,7 +248,10 @@ class _ConfigurableModel<
247248
if (fields.configurableFields === "any") {
248249
this._configurableFields = "any";
249250
} else {
250-
this._configurableFields = fields.configurableFields ?? "any";
251+
this._configurableFields = fields.configurableFields ?? [
252+
"model",
253+
"modelProvider",
254+
];
251255
}
252256

253257
if (fields.configPrefix) {
@@ -786,12 +790,14 @@ export async function initChatModel<
786790
configPrefix: "",
787791
...(fields ?? {}),
788792
};
789-
let configurableFieldsCopy = configurableFields;
793+
let configurableFieldsCopy = Array.isArray(configurableFields)
794+
? [...configurableFields]
795+
: configurableFields;
790796

791-
if (!model && !configurableFieldsCopy) {
797+
if (!model && configurableFieldsCopy === undefined) {
792798
configurableFieldsCopy = ["model", "modelProvider"];
793799
}
794-
if (configPrefix && !configurableFieldsCopy) {
800+
if (configPrefix && configurableFieldsCopy === undefined) {
795801
console.warn(
796802
`{ configPrefix: ${configPrefix} } has been set but no fields are configurable. Set ` +
797803
`{ configurableFields: [...] } to specify the model params that are ` +
@@ -802,7 +808,7 @@ export async function initChatModel<
802808
// eslint-disable-next-line @typescript-eslint/no-explicit-any
803809
const paramsCopy: Record<string, any> = { ...params };
804810

805-
if (!configurableFieldsCopy) {
811+
if (configurableFieldsCopy === undefined) {
806812
return new _ConfigurableModel<RunInput, CallOptions>({
807813
defaultConfig: {
808814
...paramsCopy,

0 commit comments

Comments
 (0)