@langchain/openai 0.3.8 → 0.3.9

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,8 +78,9 @@ 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
82
  return new messages_1.AIMessage({
82
- content: message.content || "",
83
+ content: content || "",
83
84
  tool_calls: toolCalls,
84
85
  invalid_tool_calls: invalidToolCalls,
85
86
  additional_kwargs,
@@ -95,7 +96,18 @@ function _convertDeltaToMessageChunk(
95
96
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
96
97
  delta, rawResponse, defaultRole, includeRawResponse) {
97
98
  const role = delta.role ?? defaultRole;
98
- const content = delta.content ?? "";
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
+ }
99
111
  let additional_kwargs;
100
112
  if (delta.function_call) {
101
113
  additional_kwargs = {
@@ -669,6 +681,43 @@ function _convertChatOpenAIToolTypeToOpenAITool(tool, fields) {
669
681
  * </details>
670
682
  *
671
683
  * <br />
684
+ *
685
+ * <details>
686
+ * <summary><strong>Audio Outputs</strong></summary>
687
+ *
688
+ * ```typescript
689
+ * import { ChatOpenAI } from "@langchain/openai";
690
+ *
691
+ * const modelWithAudioOutput = new ChatOpenAI({
692
+ * model: "gpt-4o-audio-preview",
693
+ * // You may also pass these fields to `.bind` as a call argument.
694
+ * modalities: ["text", "audio"], // Specifies that the model should output audio.
695
+ * audio: {
696
+ * voice: "alloy",
697
+ * format: "wav",
698
+ * },
699
+ * });
700
+ *
701
+ * const audioOutputResult = await modelWithAudioOutput.invoke("Tell me a joke about cats.");
702
+ * const castMessageContent = audioOutputResult.content[0] as Record<string, any>;
703
+ *
704
+ * console.log({
705
+ * ...castMessageContent,
706
+ * data: castMessageContent.data.slice(0, 100) // Sliced for brevity
707
+ * })
708
+ * ```
709
+ *
710
+ * ```txt
711
+ * {
712
+ * id: 'audio_67117718c6008190a3afad3e3054b9b6',
713
+ * data: 'UklGRqYwBgBXQVZFZm10IBAAAAABAAEAwF0AAIC7AAACABAATElTVBoAAABJTkZPSVNGVA4AAABMYXZmNTguMjkuMTAwAGRhdGFg',
714
+ * expires_at: 1729201448,
715
+ * transcript: 'Sure! Why did the cat sit on the computer? Because it wanted to keep an eye on the mouse!'
716
+ * }
717
+ * ```
718
+ * </details>
719
+ *
720
+ * <br />
672
721
  */
673
722
  class ChatOpenAI extends chat_models_1.BaseChatModel {
674
723
  static lc_name() {
@@ -912,6 +961,18 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
912
961
  writable: true,
913
962
  value: void 0
914
963
  });
964
+ Object.defineProperty(this, "audio", {
965
+ enumerable: true,
966
+ configurable: true,
967
+ writable: true,
968
+ value: void 0
969
+ });
970
+ Object.defineProperty(this, "modalities", {
971
+ enumerable: true,
972
+ configurable: true,
973
+ writable: true,
974
+ value: void 0
975
+ });
915
976
  this.openAIApiKey =
916
977
  fields?.apiKey ??
917
978
  fields?.openAIApiKey ??
@@ -960,6 +1021,8 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
960
1021
  this.stopSequences = this?.stop;
961
1022
  this.user = fields?.user;
962
1023
  this.__includeRawResponse = fields?.__includeRawResponse;
1024
+ this.audio = fields?.audio;
1025
+ this.modalities = fields?.modalities;
963
1026
  if (this.azureOpenAIApiKey || this.azureADTokenProvider) {
964
1027
  if (!this.azureOpenAIApiInstanceName &&
965
1028
  !this.azureOpenAIBasePath &&
@@ -1081,6 +1144,12 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1081
1144
  seed: options?.seed,
1082
1145
  ...streamOptionsConfig,
1083
1146
  parallel_tool_calls: options?.parallel_tool_calls,
1147
+ ...(this.audio || options?.audio
1148
+ ? { audio: this.audio || options?.audio }
1149
+ : {}),
1150
+ ...(this.modalities || options?.modalities
1151
+ ? { modalities: this.modalities || options?.modalities }
1152
+ : {}),
1084
1153
  ...this.modelKwargs,
1085
1154
  };
1086
1155
  return params;
@@ -1116,7 +1185,7 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1116
1185
  const streamIterable = await this.completionWithRetry(params, options);
1117
1186
  let usage;
1118
1187
  for await (const data of streamIterable) {
1119
- const choice = data?.choices[0];
1188
+ const choice = data?.choices?.[0];
1120
1189
  if (data.usage) {
1121
1190
  usage = data.usage;
1122
1191
  }
@@ -1133,10 +1202,6 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1133
1202
  prompt: options.promptIndex ?? 0,
1134
1203
  completion: choice.index ?? 0,
1135
1204
  };
1136
- if (typeof chunk.content !== "string") {
1137
- console.log("[WARNING]: Received non-string content from OpenAI. This is currently not supported.");
1138
- continue;
1139
- }
1140
1205
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1141
1206
  const generationInfo = { ...newTokenIndices };
1142
1207
  if (choice.finish_reason != null) {
@@ -1150,7 +1215,7 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1150
1215
  }
1151
1216
  const generationChunk = new outputs_1.ChatGenerationChunk({
1152
1217
  message: chunk,
1153
- text: chunk.content,
1218
+ text: typeof chunk.content === "string" ? chunk.content : "",
1154
1219
  generationInfo,
1155
1220
  });
1156
1221
  yield generationChunk;
@@ -1309,9 +1374,8 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1309
1374
  }
1310
1375
  const generations = [];
1311
1376
  for (const part of data?.choices ?? []) {
1312
- const text = part.message?.content ?? "";
1313
1377
  const generation = {
1314
- text,
1378
+ text: part.message?.content ?? "",
1315
1379
  message: openAIResponseToChatMessage(part.message ?? { role: "assistant" }, data, this.__includeRawResponse),
1316
1380
  };
1317
1381
  generation.generationInfo = {
@@ -75,6 +75,25 @@ export interface ChatOpenAICallOptions extends OpenAICallOptions, BaseFunctionCa
75
75
  * @version 0.2.6
76
76
  */
77
77
  strict?: boolean;
78
+ /**
79
+ * Output types that you would like the model to generate for this request. Most
80
+ * models are capable of generating text, which is the default:
81
+ *
82
+ * `["text"]`
83
+ *
84
+ * The `gpt-4o-audio-preview` model can also be used to
85
+ * [generate audio](https://platform.openai.com/docs/guides/audio). To request that
86
+ * this model generate both text and audio responses, you can use:
87
+ *
88
+ * `["text", "audio"]`
89
+ */
90
+ modalities?: Array<OpenAIClient.Chat.ChatCompletionModality>;
91
+ /**
92
+ * Parameters for audio output. Required when audio output is requested with
93
+ * `modalities: ["audio"]`.
94
+ * [Learn more](https://platform.openai.com/docs/guides/audio).
95
+ */
96
+ audio?: OpenAIClient.Chat.ChatCompletionAudioParam;
78
97
  }
79
98
  export interface ChatOpenAIFields extends Partial<OpenAIChatInput>, Partial<AzureOpenAIInput>, BaseChatModelParams {
80
99
  configuration?: ClientOptions & LegacyOpenAIInput;
@@ -540,6 +559,43 @@ export interface ChatOpenAIFields extends Partial<OpenAIChatInput>, Partial<Azur
540
559
  * </details>
541
560
  *
542
561
  * <br />
562
+ *
563
+ * <details>
564
+ * <summary><strong>Audio Outputs</strong></summary>
565
+ *
566
+ * ```typescript
567
+ * import { ChatOpenAI } from "@langchain/openai";
568
+ *
569
+ * const modelWithAudioOutput = new ChatOpenAI({
570
+ * model: "gpt-4o-audio-preview",
571
+ * // You may also pass these fields to `.bind` as a call argument.
572
+ * modalities: ["text", "audio"], // Specifies that the model should output audio.
573
+ * audio: {
574
+ * voice: "alloy",
575
+ * format: "wav",
576
+ * },
577
+ * });
578
+ *
579
+ * const audioOutputResult = await modelWithAudioOutput.invoke("Tell me a joke about cats.");
580
+ * const castMessageContent = audioOutputResult.content[0] as Record<string, any>;
581
+ *
582
+ * console.log({
583
+ * ...castMessageContent,
584
+ * data: castMessageContent.data.slice(0, 100) // Sliced for brevity
585
+ * })
586
+ * ```
587
+ *
588
+ * ```txt
589
+ * {
590
+ * id: 'audio_67117718c6008190a3afad3e3054b9b6',
591
+ * data: 'UklGRqYwBgBXQVZFZm10IBAAAAABAAEAwF0AAIC7AAACABAATElTVBoAAABJTkZPSVNGVA4AAABMYXZmNTguMjkuMTAwAGRhdGFg',
592
+ * expires_at: 1729201448,
593
+ * transcript: 'Sure! Why did the cat sit on the computer? Because it wanted to keep an eye on the mouse!'
594
+ * }
595
+ * ```
596
+ * </details>
597
+ *
598
+ * <br />
543
599
  */
544
600
  export declare class ChatOpenAI<CallOptions extends ChatOpenAICallOptions = ChatOpenAICallOptions> extends BaseChatModel<CallOptions, AIMessageChunk> implements OpenAIChatInput, AzureOpenAIInput {
545
601
  static lc_name(): string;
@@ -585,6 +641,8 @@ export declare class ChatOpenAI<CallOptions extends ChatOpenAICallOptions = Chat
585
641
  * If `undefined` the `strict` argument will not be passed to OpenAI.
586
642
  */
587
643
  supportsStrictToolCalling?: boolean;
644
+ audio?: OpenAIClient.Chat.ChatCompletionAudioParam;
645
+ modalities?: Array<OpenAIClient.Chat.ChatCompletionModality>;
588
646
  constructor(fields?: ChatOpenAIFields,
589
647
  /** @deprecated */
590
648
  configuration?: ClientOptions & LegacyOpenAIInput);
@@ -74,8 +74,9 @@ 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
78
  return new AIMessage({
78
- content: message.content || "",
79
+ content: content || "",
79
80
  tool_calls: toolCalls,
80
81
  invalid_tool_calls: invalidToolCalls,
81
82
  additional_kwargs,
@@ -91,7 +92,18 @@ function _convertDeltaToMessageChunk(
91
92
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
92
93
  delta, rawResponse, defaultRole, includeRawResponse) {
93
94
  const role = delta.role ?? defaultRole;
94
- const content = delta.content ?? "";
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
+ }
95
107
  let additional_kwargs;
96
108
  if (delta.function_call) {
97
109
  additional_kwargs = {
@@ -664,6 +676,43 @@ function _convertChatOpenAIToolTypeToOpenAITool(tool, fields) {
664
676
  * </details>
665
677
  *
666
678
  * <br />
679
+ *
680
+ * <details>
681
+ * <summary><strong>Audio Outputs</strong></summary>
682
+ *
683
+ * ```typescript
684
+ * import { ChatOpenAI } from "@langchain/openai";
685
+ *
686
+ * const modelWithAudioOutput = new ChatOpenAI({
687
+ * model: "gpt-4o-audio-preview",
688
+ * // You may also pass these fields to `.bind` as a call argument.
689
+ * modalities: ["text", "audio"], // Specifies that the model should output audio.
690
+ * audio: {
691
+ * voice: "alloy",
692
+ * format: "wav",
693
+ * },
694
+ * });
695
+ *
696
+ * const audioOutputResult = await modelWithAudioOutput.invoke("Tell me a joke about cats.");
697
+ * const castMessageContent = audioOutputResult.content[0] as Record<string, any>;
698
+ *
699
+ * console.log({
700
+ * ...castMessageContent,
701
+ * data: castMessageContent.data.slice(0, 100) // Sliced for brevity
702
+ * })
703
+ * ```
704
+ *
705
+ * ```txt
706
+ * {
707
+ * id: 'audio_67117718c6008190a3afad3e3054b9b6',
708
+ * data: 'UklGRqYwBgBXQVZFZm10IBAAAAABAAEAwF0AAIC7AAACABAATElTVBoAAABJTkZPSVNGVA4AAABMYXZmNTguMjkuMTAwAGRhdGFg',
709
+ * expires_at: 1729201448,
710
+ * transcript: 'Sure! Why did the cat sit on the computer? Because it wanted to keep an eye on the mouse!'
711
+ * }
712
+ * ```
713
+ * </details>
714
+ *
715
+ * <br />
667
716
  */
668
717
  export class ChatOpenAI extends BaseChatModel {
669
718
  static lc_name() {
@@ -907,6 +956,18 @@ export class ChatOpenAI extends BaseChatModel {
907
956
  writable: true,
908
957
  value: void 0
909
958
  });
959
+ Object.defineProperty(this, "audio", {
960
+ enumerable: true,
961
+ configurable: true,
962
+ writable: true,
963
+ value: void 0
964
+ });
965
+ Object.defineProperty(this, "modalities", {
966
+ enumerable: true,
967
+ configurable: true,
968
+ writable: true,
969
+ value: void 0
970
+ });
910
971
  this.openAIApiKey =
911
972
  fields?.apiKey ??
912
973
  fields?.openAIApiKey ??
@@ -955,6 +1016,8 @@ export class ChatOpenAI extends BaseChatModel {
955
1016
  this.stopSequences = this?.stop;
956
1017
  this.user = fields?.user;
957
1018
  this.__includeRawResponse = fields?.__includeRawResponse;
1019
+ this.audio = fields?.audio;
1020
+ this.modalities = fields?.modalities;
958
1021
  if (this.azureOpenAIApiKey || this.azureADTokenProvider) {
959
1022
  if (!this.azureOpenAIApiInstanceName &&
960
1023
  !this.azureOpenAIBasePath &&
@@ -1076,6 +1139,12 @@ export class ChatOpenAI extends BaseChatModel {
1076
1139
  seed: options?.seed,
1077
1140
  ...streamOptionsConfig,
1078
1141
  parallel_tool_calls: options?.parallel_tool_calls,
1142
+ ...(this.audio || options?.audio
1143
+ ? { audio: this.audio || options?.audio }
1144
+ : {}),
1145
+ ...(this.modalities || options?.modalities
1146
+ ? { modalities: this.modalities || options?.modalities }
1147
+ : {}),
1079
1148
  ...this.modelKwargs,
1080
1149
  };
1081
1150
  return params;
@@ -1111,7 +1180,7 @@ export class ChatOpenAI extends BaseChatModel {
1111
1180
  const streamIterable = await this.completionWithRetry(params, options);
1112
1181
  let usage;
1113
1182
  for await (const data of streamIterable) {
1114
- const choice = data?.choices[0];
1183
+ const choice = data?.choices?.[0];
1115
1184
  if (data.usage) {
1116
1185
  usage = data.usage;
1117
1186
  }
@@ -1128,10 +1197,6 @@ export class ChatOpenAI extends BaseChatModel {
1128
1197
  prompt: options.promptIndex ?? 0,
1129
1198
  completion: choice.index ?? 0,
1130
1199
  };
1131
- if (typeof chunk.content !== "string") {
1132
- console.log("[WARNING]: Received non-string content from OpenAI. This is currently not supported.");
1133
- continue;
1134
- }
1135
1200
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1136
1201
  const generationInfo = { ...newTokenIndices };
1137
1202
  if (choice.finish_reason != null) {
@@ -1145,7 +1210,7 @@ export class ChatOpenAI extends BaseChatModel {
1145
1210
  }
1146
1211
  const generationChunk = new ChatGenerationChunk({
1147
1212
  message: chunk,
1148
- text: chunk.content,
1213
+ text: typeof chunk.content === "string" ? chunk.content : "",
1149
1214
  generationInfo,
1150
1215
  });
1151
1216
  yield generationChunk;
@@ -1304,9 +1369,8 @@ export class ChatOpenAI extends BaseChatModel {
1304
1369
  }
1305
1370
  const generations = [];
1306
1371
  for (const part of data?.choices ?? []) {
1307
- const text = part.message?.content ?? "";
1308
1372
  const generation = {
1309
- text,
1373
+ text: part.message?.content ?? "",
1310
1374
  message: openAIResponseToChatMessage(part.message ?? { role: "assistant" }, data, this.__includeRawResponse),
1311
1375
  };
1312
1376
  generation.generationInfo = {
package/dist/types.d.ts CHANGED
@@ -128,6 +128,25 @@ export interface OpenAIChatInput extends OpenAIBaseInput {
128
128
  * If `undefined` the `strict` argument will not be passed to OpenAI.
129
129
  */
130
130
  supportsStrictToolCalling?: boolean;
131
+ /**
132
+ * Output types that you would like the model to generate for this request. Most
133
+ * models are capable of generating text, which is the default:
134
+ *
135
+ * `["text"]`
136
+ *
137
+ * The `gpt-4o-audio-preview` model can also be used to
138
+ * [generate audio](https://platform.openai.com/docs/guides/audio). To request that
139
+ * this model generate both text and audio responses, you can use:
140
+ *
141
+ * `["text", "audio"]`
142
+ */
143
+ modalities?: Array<OpenAIClient.Chat.ChatCompletionModality>;
144
+ /**
145
+ * Parameters for audio output. Required when audio output is requested with
146
+ * `modalities: ["audio"]`.
147
+ * [Learn more](https://platform.openai.com/docs/guides/audio).
148
+ */
149
+ audio?: OpenAIClient.Chat.ChatCompletionAudioParam;
131
150
  }
132
151
  export declare interface AzureOpenAIInput {
133
152
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/openai",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
4
4
  "description": "OpenAI integrations for LangChain.js",
5
5
  "type": "module",
6
6
  "engines": {
@@ -36,7 +36,7 @@
36
36
  "license": "MIT",
37
37
  "dependencies": {
38
38
  "js-tiktoken": "^1.0.12",
39
- "openai": "^4.67.2",
39
+ "openai": "^4.68.0",
40
40
  "zod": "^3.22.4",
41
41
  "zod-to-json-schema": "^3.22.3"
42
42
  },