Skip to content

Commit 5a01b5b

Browse files
fix(openai): fix content in AIMessage for tool and function calls (#9636)
Co-authored-by: Hunter Lovell <40191806+hntrl@users.noreply.github.com>
1 parent ab78246 commit 5a01b5b

3 files changed

Lines changed: 103 additions & 2 deletions

File tree

.changeset/cold-laws-matter.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@langchain/openai": patch
3+
---
4+
5+
fix content in AIMessage for tool and function calls

libs/providers/langchain-openai/src/converters/completions.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -811,13 +811,11 @@ export const convertMessagesToCompletionsMessageParams: Converter<
811811
}
812812
if (message.additional_kwargs.function_call != null) {
813813
completionParam.function_call = message.additional_kwargs.function_call;
814-
completionParam.content = "";
815814
}
816815
if (AIMessage.isInstance(message) && !!message.tool_calls?.length) {
817816
completionParam.tool_calls = message.tool_calls.map(
818817
convertLangChainToolCallToOpenAI
819818
);
820-
completionParam.content = "";
821819
} else {
822820
if (message.additional_kwargs.tool_calls != null) {
823821
completionParam.tool_calls = message.additional_kwargs.tool_calls;

libs/providers/langchain-openai/src/converters/tests/completions.test.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
22
import { describe, it, expect } from "vitest";
33
import { ChatCompletionMessage } from "openai/resources";
4+
import { AIMessage } from "@langchain/core/messages";
45
import {
56
completionsApiContentBlockConverter,
67
convertCompletionsMessageToBaseMessage,
8+
convertMessagesToCompletionsMessageParams,
79
convertStandardContentBlockToCompletionsContentPart,
810
} from "../completions.js";
911

@@ -194,6 +196,102 @@ describe("convertCompletionsMessageToBaseMessage", () => {
194196
});
195197
});
196198

199+
describe("convertMessagesToCompletionsMessageParams", () => {
200+
it("should preserve AIMessage content when tool_calls are present", () => {
201+
const message = new AIMessage({
202+
content:
203+
"I'll check the status of item 730 for identifier X1110 to find out why it's not active.",
204+
tool_calls: [
205+
{
206+
id: "call_zGKlzVl2Ee3Lyob4AsyqfGXb",
207+
name: "getStatus",
208+
args: { identifier: "X1110", itemId: "730" },
209+
},
210+
],
211+
});
212+
213+
const result = convertMessagesToCompletionsMessageParams({
214+
messages: [message],
215+
});
216+
217+
expect(result).toHaveLength(1);
218+
expect(result[0]).toEqual({
219+
role: "assistant",
220+
content:
221+
"I'll check the status of item 730 for identifier X1110 to find out why it's not active.",
222+
tool_calls: [
223+
{
224+
id: "call_zGKlzVl2Ee3Lyob4AsyqfGXb",
225+
type: "function",
226+
function: {
227+
name: "getStatus",
228+
arguments: '{"identifier":"X1110","itemId":"730"}',
229+
},
230+
},
231+
],
232+
});
233+
});
234+
235+
it("should handle AIMessage with empty content and tool_calls", () => {
236+
const message = new AIMessage({
237+
content: "",
238+
tool_calls: [
239+
{
240+
id: "call_123",
241+
name: "someFunction",
242+
args: { key: "value" },
243+
},
244+
],
245+
});
246+
247+
const result = convertMessagesToCompletionsMessageParams({
248+
messages: [message],
249+
});
250+
251+
expect(result).toHaveLength(1);
252+
expect(result[0]).toEqual({
253+
role: "assistant",
254+
content: "",
255+
tool_calls: [
256+
{
257+
id: "call_123",
258+
type: "function",
259+
function: {
260+
name: "someFunction",
261+
arguments: '{"key":"value"}',
262+
},
263+
},
264+
],
265+
});
266+
});
267+
268+
it("should preserve content with function_call in additional_kwargs", () => {
269+
const message = new AIMessage({
270+
content: "Let me call a function for you.",
271+
additional_kwargs: {
272+
function_call: {
273+
name: "myFunction",
274+
arguments: '{"arg":"value"}',
275+
},
276+
},
277+
});
278+
279+
const result = convertMessagesToCompletionsMessageParams({
280+
messages: [message],
281+
});
282+
283+
expect(result).toHaveLength(1);
284+
expect(result[0]).toEqual({
285+
role: "assistant",
286+
content: "Let me call a function for you.",
287+
function_call: {
288+
name: "myFunction",
289+
arguments: '{"arg":"value"}',
290+
},
291+
});
292+
});
293+
});
294+
197295
describe("completionsApiContentBlockConverter.fromStandardFileBlock", () => {
198296
it("throws when base64 file block is missing filename/name/title metadata", () => {
199297
const block = {

0 commit comments

Comments
 (0)