@langchain/openai 0.3.9 → 0.3.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -78,9 +78,11 @@ function openAIResponseToChatMessage(message, rawResponse, includeRawResponse) {
78
78
  system_fingerprint: rawResponse.system_fingerprint,
79
79
  };
80
80
  }
81
- const content = message.audio ? [message.audio] : message.content;
81
+ if (message.audio) {
82
+ additional_kwargs.audio = message.audio;
83
+ }
82
84
  return new messages_1.AIMessage({
83
- content: content || "",
85
+ content: message.content || "",
84
86
  tool_calls: toolCalls,
85
87
  invalid_tool_calls: invalidToolCalls,
86
88
  additional_kwargs,
@@ -96,18 +98,7 @@ function _convertDeltaToMessageChunk(
96
98
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
97
99
  delta, rawResponse, defaultRole, includeRawResponse) {
98
100
  const role = delta.role ?? defaultRole;
99
- let content;
100
- if (delta.audio) {
101
- content = [
102
- {
103
- ...delta.audio,
104
- index: rawResponse.choices[0].index,
105
- },
106
- ];
107
- }
108
- else {
109
- content = delta.content ?? "";
110
- }
101
+ const content = delta.content ?? "";
111
102
  let additional_kwargs;
112
103
  if (delta.function_call) {
113
104
  additional_kwargs = {
@@ -125,6 +116,12 @@ delta, rawResponse, defaultRole, includeRawResponse) {
125
116
  if (includeRawResponse) {
126
117
  additional_kwargs.__raw_response = rawResponse;
127
118
  }
119
+ if (delta.audio) {
120
+ additional_kwargs.audio = {
121
+ ...delta.audio,
122
+ index: rawResponse.choices[0].index,
123
+ };
124
+ }
128
125
  const response_metadata = { usage: { ...rawResponse.usage } };
129
126
  if (role === "user") {
130
127
  return new messages_1.HumanMessageChunk({ content, response_metadata });
@@ -176,7 +173,7 @@ delta, rawResponse, defaultRole, includeRawResponse) {
176
173
  // Used in LangSmith, export is important here
177
174
  function _convertMessagesToOpenAIParams(messages) {
178
175
  // TODO: Function messages do not support array content, fix cast
179
- return messages.map((message) => {
176
+ return messages.flatMap((message) => {
180
177
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
181
178
  const completionParam = {
182
179
  role: messageToOpenAIRole(message),
@@ -201,6 +198,17 @@ function _convertMessagesToOpenAIParams(messages) {
201
198
  completionParam.tool_call_id = message.tool_call_id;
202
199
  }
203
200
  }
201
+ if (message.additional_kwargs.audio &&
202
+ typeof message.additional_kwargs.audio === "object" &&
203
+ "id" in message.additional_kwargs.audio) {
204
+ const audioMessage = {
205
+ role: "assistant",
206
+ audio: {
207
+ id: message.additional_kwargs.audio.id,
208
+ },
209
+ };
210
+ return [completionParam, audioMessage];
211
+ }
204
212
  return completionParam;
205
213
  });
206
214
  }
@@ -718,6 +726,43 @@ function _convertChatOpenAIToolTypeToOpenAITool(tool, fields) {
718
726
  * </details>
719
727
  *
720
728
  * <br />
729
+ *
730
+ * <details>
731
+ * <summary><strong>Audio Outputs</strong></summary>
732
+ *
733
+ * ```typescript
734
+ * import { ChatOpenAI } from "@langchain/openai";
735
+ *
736
+ * const modelWithAudioOutput = new ChatOpenAI({
737
+ * model: "gpt-4o-audio-preview",
738
+ * // You may also pass these fields to `.bind` as a call argument.
739
+ * modalities: ["text", "audio"], // Specifies that the model should output audio.
740
+ * audio: {
741
+ * voice: "alloy",
742
+ * format: "wav",
743
+ * },
744
+ * });
745
+ *
746
+ * const audioOutputResult = await modelWithAudioOutput.invoke("Tell me a joke about cats.");
747
+ * const castAudioContent = audioOutputResult.additional_kwargs.audio as Record<string, any>;
748
+ *
749
+ * console.log({
750
+ * ...castAudioContent,
751
+ * data: castAudioContent.data.slice(0, 100) // Sliced for brevity
752
+ * })
753
+ * ```
754
+ *
755
+ * ```txt
756
+ * {
757
+ * id: 'audio_67117718c6008190a3afad3e3054b9b6',
758
+ * data: 'UklGRqYwBgBXQVZFZm10IBAAAAABAAEAwF0AAIC7AAACABAATElTVBoAAABJTkZPSVNGVA4AAABMYXZmNTguMjkuMTAwAGRhdGFg',
759
+ * expires_at: 1729201448,
760
+ * transcript: 'Sure! Why did the cat sit on the computer? Because it wanted to keep an eye on the mouse!'
761
+ * }
762
+ * ```
763
+ * </details>
764
+ *
765
+ * <br />
721
766
  */
722
767
  class ChatOpenAI extends chat_models_1.BaseChatModel {
723
768
  static lc_name() {
@@ -1202,6 +1247,10 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1202
1247
  prompt: options.promptIndex ?? 0,
1203
1248
  completion: choice.index ?? 0,
1204
1249
  };
1250
+ if (typeof chunk.content !== "string") {
1251
+ console.log("[WARNING]: Received non-string content from OpenAI. This is currently not supported.");
1252
+ continue;
1253
+ }
1205
1254
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1206
1255
  const generationInfo = { ...newTokenIndices };
1207
1256
  if (choice.finish_reason != null) {
@@ -1215,7 +1264,7 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1215
1264
  }
1216
1265
  const generationChunk = new outputs_1.ChatGenerationChunk({
1217
1266
  message: chunk,
1218
- text: typeof chunk.content === "string" ? chunk.content : "",
1267
+ text: chunk.content,
1219
1268
  generationInfo,
1220
1269
  });
1221
1270
  yield generationChunk;
@@ -1374,8 +1423,9 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1374
1423
  }
1375
1424
  const generations = [];
1376
1425
  for (const part of data?.choices ?? []) {
1426
+ const text = part.message?.content ?? "";
1377
1427
  const generation = {
1378
- text: part.message?.content ?? "",
1428
+ text,
1379
1429
  message: openAIResponseToChatMessage(part.message ?? { role: "assistant" }, data, this.__includeRawResponse),
1380
1430
  };
1381
1431
  generation.generationInfo = {
@@ -18,8 +18,9 @@ interface OpenAILLMOutput {
18
18
  tokenUsage: TokenUsage;
19
19
  }
20
20
  type OpenAIRoleEnum = "system" | "assistant" | "user" | "function" | "tool";
21
+ type OpenAICompletionParam = OpenAIClient.Chat.Completions.ChatCompletionMessageParam;
21
22
  export declare function messageToOpenAIRole(message: BaseMessage): OpenAIRoleEnum;
22
- export declare function _convertMessagesToOpenAIParams(messages: BaseMessage[]): OpenAIClient.Chat.Completions.ChatCompletionMessageParam[];
23
+ export declare function _convertMessagesToOpenAIParams(messages: BaseMessage[]): OpenAICompletionParam[];
23
24
  type ChatOpenAIToolType = BindToolsInput | OpenAIClient.ChatCompletionTool;
24
25
  export interface ChatOpenAIStructuredOutputMethodOptions<IncludeRaw extends boolean> extends StructuredOutputMethodOptions<IncludeRaw> {
25
26
  /**
@@ -596,6 +597,43 @@ export interface ChatOpenAIFields extends Partial<OpenAIChatInput>, Partial<Azur
596
597
  * </details>
597
598
  *
598
599
  * <br />
600
+ *
601
+ * <details>
602
+ * <summary><strong>Audio Outputs</strong></summary>
603
+ *
604
+ * ```typescript
605
+ * import { ChatOpenAI } from "@langchain/openai";
606
+ *
607
+ * const modelWithAudioOutput = new ChatOpenAI({
608
+ * model: "gpt-4o-audio-preview",
609
+ * // You may also pass these fields to `.bind` as a call argument.
610
+ * modalities: ["text", "audio"], // Specifies that the model should output audio.
611
+ * audio: {
612
+ * voice: "alloy",
613
+ * format: "wav",
614
+ * },
615
+ * });
616
+ *
617
+ * const audioOutputResult = await modelWithAudioOutput.invoke("Tell me a joke about cats.");
618
+ * const castAudioContent = audioOutputResult.additional_kwargs.audio as Record<string, any>;
619
+ *
620
+ * console.log({
621
+ * ...castAudioContent,
622
+ * data: castAudioContent.data.slice(0, 100) // Sliced for brevity
623
+ * })
624
+ * ```
625
+ *
626
+ * ```txt
627
+ * {
628
+ * id: 'audio_67117718c6008190a3afad3e3054b9b6',
629
+ * data: 'UklGRqYwBgBXQVZFZm10IBAAAAABAAEAwF0AAIC7AAACABAATElTVBoAAABJTkZPSVNGVA4AAABMYXZmNTguMjkuMTAwAGRhdGFg',
630
+ * expires_at: 1729201448,
631
+ * transcript: 'Sure! Why did the cat sit on the computer? Because it wanted to keep an eye on the mouse!'
632
+ * }
633
+ * ```
634
+ * </details>
635
+ *
636
+ * <br />
599
637
  */
600
638
  export declare class ChatOpenAI<CallOptions extends ChatOpenAICallOptions = ChatOpenAICallOptions> extends BaseChatModel<CallOptions, AIMessageChunk> implements OpenAIChatInput, AzureOpenAIInput {
601
639
  static lc_name(): string;
@@ -74,9 +74,11 @@ function openAIResponseToChatMessage(message, rawResponse, includeRawResponse) {
74
74
  system_fingerprint: rawResponse.system_fingerprint,
75
75
  };
76
76
  }
77
- const content = message.audio ? [message.audio] : message.content;
77
+ if (message.audio) {
78
+ additional_kwargs.audio = message.audio;
79
+ }
78
80
  return new AIMessage({
79
- content: content || "",
81
+ content: message.content || "",
80
82
  tool_calls: toolCalls,
81
83
  invalid_tool_calls: invalidToolCalls,
82
84
  additional_kwargs,
@@ -92,18 +94,7 @@ function _convertDeltaToMessageChunk(
92
94
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
93
95
  delta, rawResponse, defaultRole, includeRawResponse) {
94
96
  const role = delta.role ?? defaultRole;
95
- let content;
96
- if (delta.audio) {
97
- content = [
98
- {
99
- ...delta.audio,
100
- index: rawResponse.choices[0].index,
101
- },
102
- ];
103
- }
104
- else {
105
- content = delta.content ?? "";
106
- }
97
+ const content = delta.content ?? "";
107
98
  let additional_kwargs;
108
99
  if (delta.function_call) {
109
100
  additional_kwargs = {
@@ -121,6 +112,12 @@ delta, rawResponse, defaultRole, includeRawResponse) {
121
112
  if (includeRawResponse) {
122
113
  additional_kwargs.__raw_response = rawResponse;
123
114
  }
115
+ if (delta.audio) {
116
+ additional_kwargs.audio = {
117
+ ...delta.audio,
118
+ index: rawResponse.choices[0].index,
119
+ };
120
+ }
124
121
  const response_metadata = { usage: { ...rawResponse.usage } };
125
122
  if (role === "user") {
126
123
  return new HumanMessageChunk({ content, response_metadata });
@@ -172,7 +169,7 @@ delta, rawResponse, defaultRole, includeRawResponse) {
172
169
  // Used in LangSmith, export is important here
173
170
  export function _convertMessagesToOpenAIParams(messages) {
174
171
  // TODO: Function messages do not support array content, fix cast
175
- return messages.map((message) => {
172
+ return messages.flatMap((message) => {
176
173
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
177
174
  const completionParam = {
178
175
  role: messageToOpenAIRole(message),
@@ -197,6 +194,17 @@ export function _convertMessagesToOpenAIParams(messages) {
197
194
  completionParam.tool_call_id = message.tool_call_id;
198
195
  }
199
196
  }
197
+ if (message.additional_kwargs.audio &&
198
+ typeof message.additional_kwargs.audio === "object" &&
199
+ "id" in message.additional_kwargs.audio) {
200
+ const audioMessage = {
201
+ role: "assistant",
202
+ audio: {
203
+ id: message.additional_kwargs.audio.id,
204
+ },
205
+ };
206
+ return [completionParam, audioMessage];
207
+ }
200
208
  return completionParam;
201
209
  });
202
210
  }
@@ -713,6 +721,43 @@ function _convertChatOpenAIToolTypeToOpenAITool(tool, fields) {
713
721
  * </details>
714
722
  *
715
723
  * <br />
724
+ *
725
+ * <details>
726
+ * <summary><strong>Audio Outputs</strong></summary>
727
+ *
728
+ * ```typescript
729
+ * import { ChatOpenAI } from "@langchain/openai";
730
+ *
731
+ * const modelWithAudioOutput = new ChatOpenAI({
732
+ * model: "gpt-4o-audio-preview",
733
+ * // You may also pass these fields to `.bind` as a call argument.
734
+ * modalities: ["text", "audio"], // Specifies that the model should output audio.
735
+ * audio: {
736
+ * voice: "alloy",
737
+ * format: "wav",
738
+ * },
739
+ * });
740
+ *
741
+ * const audioOutputResult = await modelWithAudioOutput.invoke("Tell me a joke about cats.");
742
+ * const castAudioContent = audioOutputResult.additional_kwargs.audio as Record<string, any>;
743
+ *
744
+ * console.log({
745
+ * ...castAudioContent,
746
+ * data: castAudioContent.data.slice(0, 100) // Sliced for brevity
747
+ * })
748
+ * ```
749
+ *
750
+ * ```txt
751
+ * {
752
+ * id: 'audio_67117718c6008190a3afad3e3054b9b6',
753
+ * data: 'UklGRqYwBgBXQVZFZm10IBAAAAABAAEAwF0AAIC7AAACABAATElTVBoAAABJTkZPSVNGVA4AAABMYXZmNTguMjkuMTAwAGRhdGFg',
754
+ * expires_at: 1729201448,
755
+ * transcript: 'Sure! Why did the cat sit on the computer? Because it wanted to keep an eye on the mouse!'
756
+ * }
757
+ * ```
758
+ * </details>
759
+ *
760
+ * <br />
716
761
  */
717
762
  export class ChatOpenAI extends BaseChatModel {
718
763
  static lc_name() {
@@ -1197,6 +1242,10 @@ export class ChatOpenAI extends BaseChatModel {
1197
1242
  prompt: options.promptIndex ?? 0,
1198
1243
  completion: choice.index ?? 0,
1199
1244
  };
1245
+ if (typeof chunk.content !== "string") {
1246
+ console.log("[WARNING]: Received non-string content from OpenAI. This is currently not supported.");
1247
+ continue;
1248
+ }
1200
1249
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1201
1250
  const generationInfo = { ...newTokenIndices };
1202
1251
  if (choice.finish_reason != null) {
@@ -1210,7 +1259,7 @@ export class ChatOpenAI extends BaseChatModel {
1210
1259
  }
1211
1260
  const generationChunk = new ChatGenerationChunk({
1212
1261
  message: chunk,
1213
- text: typeof chunk.content === "string" ? chunk.content : "",
1262
+ text: chunk.content,
1214
1263
  generationInfo,
1215
1264
  });
1216
1265
  yield generationChunk;
@@ -1369,8 +1418,9 @@ export class ChatOpenAI extends BaseChatModel {
1369
1418
  }
1370
1419
  const generations = [];
1371
1420
  for (const part of data?.choices ?? []) {
1421
+ const text = part.message?.content ?? "";
1372
1422
  const generation = {
1373
- text: part.message?.content ?? "",
1423
+ text,
1374
1424
  message: openAIResponseToChatMessage(part.message ?? { role: "assistant" }, data, this.__includeRawResponse),
1375
1425
  };
1376
1426
  generation.generationInfo = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/openai",
3
- "version": "0.3.9",
3
+ "version": "0.3.11",
4
4
  "description": "OpenAI integrations for LangChain.js",
5
5
  "type": "module",
6
6
  "engines": {