@langchain/openai 0.3.14 → 0.3.16

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.
@@ -18,6 +18,7 @@ const openai_format_fndef_js_1 = require("./utils/openai-format-fndef.cjs");
18
18
  const tools_js_1 = require("./utils/tools.cjs");
19
19
  function extractGenericMessageCustomRole(message) {
20
20
  if (message.role !== "system" &&
21
+ message.role !== "developer" &&
21
22
  message.role !== "assistant" &&
22
23
  message.role !== "user" &&
23
24
  message.role !== "function" &&
@@ -71,13 +72,15 @@ function openAIResponseToChatMessage(message, rawResponse, includeRawResponse) {
71
72
  if (includeRawResponse !== undefined) {
72
73
  additional_kwargs.__raw_response = rawResponse;
73
74
  }
74
- let response_metadata;
75
- if (rawResponse.system_fingerprint) {
76
- response_metadata = {
77
- usage: { ...rawResponse.usage },
78
- system_fingerprint: rawResponse.system_fingerprint,
79
- };
80
- }
75
+ const response_metadata = {
76
+ model_name: rawResponse.model,
77
+ ...(rawResponse.system_fingerprint
78
+ ? {
79
+ usage: { ...rawResponse.usage },
80
+ system_fingerprint: rawResponse.system_fingerprint,
81
+ }
82
+ : {}),
83
+ };
81
84
  if (message.audio) {
82
85
  additional_kwargs.audio = message.audio;
83
86
  }
@@ -150,6 +153,15 @@ delta, rawResponse, defaultRole, includeRawResponse) {
150
153
  else if (role === "system") {
151
154
  return new messages_1.SystemMessageChunk({ content, response_metadata });
152
155
  }
156
+ else if (role === "developer") {
157
+ return new messages_1.SystemMessageChunk({
158
+ content,
159
+ response_metadata,
160
+ additional_kwargs: {
161
+ __openai_role__: "developer",
162
+ },
163
+ });
164
+ }
153
165
  else if (role === "function") {
154
166
  return new messages_1.FunctionMessageChunk({
155
167
  content,
@@ -171,12 +183,16 @@ delta, rawResponse, defaultRole, includeRawResponse) {
171
183
  }
172
184
  }
173
185
  // Used in LangSmith, export is important here
174
- function _convertMessagesToOpenAIParams(messages) {
186
+ function _convertMessagesToOpenAIParams(messages, model) {
175
187
  // TODO: Function messages do not support array content, fix cast
176
188
  return messages.flatMap((message) => {
189
+ let role = messageToOpenAIRole(message);
190
+ if (role === "system" && model?.startsWith("o1")) {
191
+ role = "developer";
192
+ }
177
193
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
178
194
  const completionParam = {
179
- role: messageToOpenAIRole(message),
195
+ role,
180
196
  content: message.content,
181
197
  };
182
198
  if (message.name != null) {
@@ -779,6 +795,7 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
779
795
  "promptIndex",
780
796
  "response_format",
781
797
  "seed",
798
+ "reasoning_effort",
782
799
  ];
783
800
  }
784
801
  get lc_secrets() {
@@ -1018,6 +1035,12 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1018
1035
  writable: true,
1019
1036
  value: void 0
1020
1037
  });
1038
+ Object.defineProperty(this, "reasoningEffort", {
1039
+ enumerable: true,
1040
+ configurable: true,
1041
+ writable: true,
1042
+ value: void 0
1043
+ });
1021
1044
  this.openAIApiKey =
1022
1045
  fields?.apiKey ??
1023
1046
  fields?.openAIApiKey ??
@@ -1068,6 +1091,7 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1068
1091
  this.__includeRawResponse = fields?.__includeRawResponse;
1069
1092
  this.audio = fields?.audio;
1070
1093
  this.modalities = fields?.modalities;
1094
+ this.reasoningEffort = fields?.reasoningEffort;
1071
1095
  if (this.azureOpenAIApiKey || this.azureADTokenProvider) {
1072
1096
  if (!this.azureOpenAIApiInstanceName &&
1073
1097
  !this.azureOpenAIBasePath &&
@@ -1200,6 +1224,10 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1200
1224
  if (options?.prediction !== undefined) {
1201
1225
  params.prediction = options.prediction;
1202
1226
  }
1227
+ const reasoningEffort = options?.reasoning_effort ?? this.reasoningEffort;
1228
+ if (reasoningEffort !== undefined) {
1229
+ params.reasoning_effort = reasoningEffort;
1230
+ }
1203
1231
  return params;
1204
1232
  }
1205
1233
  /** @ignore */
@@ -1211,7 +1239,7 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1211
1239
  };
1212
1240
  }
1213
1241
  async *_streamResponseChunks(messages, options, runManager) {
1214
- const messagesMapped = _convertMessagesToOpenAIParams(messages);
1242
+ const messagesMapped = _convertMessagesToOpenAIParams(messages, this.model);
1215
1243
  const params = {
1216
1244
  ...this.invocationParams(options, {
1217
1245
  streaming: true,
@@ -1251,6 +1279,7 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1251
1279
  // Only include system fingerprint in the last chunk for now
1252
1280
  // to avoid concatenation issues
1253
1281
  generationInfo.system_fingerprint = data.system_fingerprint;
1282
+ generationInfo.model_name = data.model;
1254
1283
  }
1255
1284
  if (this.logprobs) {
1256
1285
  generationInfo.logprobs = choice.logprobs;
@@ -1317,7 +1346,7 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1317
1346
  async _generate(messages, options, runManager) {
1318
1347
  const usageMetadata = {};
1319
1348
  const params = this.invocationParams(options);
1320
- const messagesMapped = _convertMessagesToOpenAIParams(messages);
1349
+ const messagesMapped = _convertMessagesToOpenAIParams(messages, this.model);
1321
1350
  if (params.stream) {
1322
1351
  const stream = this._streamResponseChunks(messages, options, runManager);
1323
1352
  const finalChunks = {};
@@ -1430,9 +1459,7 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1430
1459
  }
1431
1460
  // Fields are not serialized unless passed to the constructor
1432
1461
  // Doing this ensures all fields on the message are serialized
1433
- generation.message = new messages_1.AIMessage({
1434
- ...generation.message,
1435
- });
1462
+ generation.message = new messages_1.AIMessage(Object.fromEntries(Object.entries(generation.message).filter(([key]) => !key.startsWith("lc_"))));
1436
1463
  generations.push(generation);
1437
1464
  }
1438
1465
  return {
@@ -17,10 +17,10 @@ interface TokenUsage {
17
17
  interface OpenAILLMOutput {
18
18
  tokenUsage: TokenUsage;
19
19
  }
20
- type OpenAIRoleEnum = "system" | "assistant" | "user" | "function" | "tool";
20
+ type OpenAIRoleEnum = "system" | "developer" | "assistant" | "user" | "function" | "tool";
21
21
  type OpenAICompletionParam = OpenAIClient.Chat.Completions.ChatCompletionMessageParam;
22
22
  export declare function messageToOpenAIRole(message: BaseMessage): OpenAIRoleEnum;
23
- export declare function _convertMessagesToOpenAIParams(messages: BaseMessage[]): OpenAICompletionParam[];
23
+ export declare function _convertMessagesToOpenAIParams(messages: BaseMessage[], model?: string): OpenAICompletionParam[];
24
24
  type ChatOpenAIToolType = BindToolsInput | OpenAIClient.ChatCompletionTool;
25
25
  export interface ChatOpenAIStructuredOutputMethodOptions<IncludeRaw extends boolean> extends StructuredOutputMethodOptions<IncludeRaw> {
26
26
  /**
@@ -100,6 +100,11 @@ export interface ChatOpenAICallOptions extends OpenAICallOptions, BaseFunctionCa
100
100
  * [Learn more](https://platform.openai.com/docs/guides/latency-optimization#use-predicted-outputs).
101
101
  */
102
102
  prediction?: OpenAIClient.ChatCompletionPredictionContent;
103
+ /**
104
+ * Constrains effort on reasoning for reasoning models. Currently supported values are low, medium, and high.
105
+ * Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response.
106
+ */
107
+ reasoning_effort?: OpenAIClient.Chat.ChatCompletionReasoningEffort;
103
108
  }
104
109
  export interface ChatOpenAIFields extends Partial<OpenAIChatInput>, Partial<AzureOpenAIInput>, BaseChatModelParams {
105
110
  configuration?: ClientOptions & LegacyOpenAIInput;
@@ -686,6 +691,7 @@ export declare class ChatOpenAI<CallOptions extends ChatOpenAICallOptions = Chat
686
691
  supportsStrictToolCalling?: boolean;
687
692
  audio?: OpenAIClient.Chat.ChatCompletionAudioParam;
688
693
  modalities?: Array<OpenAIClient.Chat.ChatCompletionModality>;
694
+ reasoningEffort?: OpenAIClient.Chat.ChatCompletionReasoningEffort;
689
695
  constructor(fields?: ChatOpenAIFields,
690
696
  /** @deprecated */
691
697
  configuration?: ClientOptions & LegacyOpenAIInput);
@@ -15,6 +15,7 @@ import { formatFunctionDefinitions, } from "./utils/openai-format-fndef.js";
15
15
  import { _convertToOpenAITool } from "./utils/tools.js";
16
16
  function extractGenericMessageCustomRole(message) {
17
17
  if (message.role !== "system" &&
18
+ message.role !== "developer" &&
18
19
  message.role !== "assistant" &&
19
20
  message.role !== "user" &&
20
21
  message.role !== "function" &&
@@ -67,13 +68,15 @@ function openAIResponseToChatMessage(message, rawResponse, includeRawResponse) {
67
68
  if (includeRawResponse !== undefined) {
68
69
  additional_kwargs.__raw_response = rawResponse;
69
70
  }
70
- let response_metadata;
71
- if (rawResponse.system_fingerprint) {
72
- response_metadata = {
73
- usage: { ...rawResponse.usage },
74
- system_fingerprint: rawResponse.system_fingerprint,
75
- };
76
- }
71
+ const response_metadata = {
72
+ model_name: rawResponse.model,
73
+ ...(rawResponse.system_fingerprint
74
+ ? {
75
+ usage: { ...rawResponse.usage },
76
+ system_fingerprint: rawResponse.system_fingerprint,
77
+ }
78
+ : {}),
79
+ };
77
80
  if (message.audio) {
78
81
  additional_kwargs.audio = message.audio;
79
82
  }
@@ -146,6 +149,15 @@ delta, rawResponse, defaultRole, includeRawResponse) {
146
149
  else if (role === "system") {
147
150
  return new SystemMessageChunk({ content, response_metadata });
148
151
  }
152
+ else if (role === "developer") {
153
+ return new SystemMessageChunk({
154
+ content,
155
+ response_metadata,
156
+ additional_kwargs: {
157
+ __openai_role__: "developer",
158
+ },
159
+ });
160
+ }
149
161
  else if (role === "function") {
150
162
  return new FunctionMessageChunk({
151
163
  content,
@@ -167,12 +179,16 @@ delta, rawResponse, defaultRole, includeRawResponse) {
167
179
  }
168
180
  }
169
181
  // Used in LangSmith, export is important here
170
- export function _convertMessagesToOpenAIParams(messages) {
182
+ export function _convertMessagesToOpenAIParams(messages, model) {
171
183
  // TODO: Function messages do not support array content, fix cast
172
184
  return messages.flatMap((message) => {
185
+ let role = messageToOpenAIRole(message);
186
+ if (role === "system" && model?.startsWith("o1")) {
187
+ role = "developer";
188
+ }
173
189
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
174
190
  const completionParam = {
175
- role: messageToOpenAIRole(message),
191
+ role,
176
192
  content: message.content,
177
193
  };
178
194
  if (message.name != null) {
@@ -774,6 +790,7 @@ export class ChatOpenAI extends BaseChatModel {
774
790
  "promptIndex",
775
791
  "response_format",
776
792
  "seed",
793
+ "reasoning_effort",
777
794
  ];
778
795
  }
779
796
  get lc_secrets() {
@@ -1013,6 +1030,12 @@ export class ChatOpenAI extends BaseChatModel {
1013
1030
  writable: true,
1014
1031
  value: void 0
1015
1032
  });
1033
+ Object.defineProperty(this, "reasoningEffort", {
1034
+ enumerable: true,
1035
+ configurable: true,
1036
+ writable: true,
1037
+ value: void 0
1038
+ });
1016
1039
  this.openAIApiKey =
1017
1040
  fields?.apiKey ??
1018
1041
  fields?.openAIApiKey ??
@@ -1063,6 +1086,7 @@ export class ChatOpenAI extends BaseChatModel {
1063
1086
  this.__includeRawResponse = fields?.__includeRawResponse;
1064
1087
  this.audio = fields?.audio;
1065
1088
  this.modalities = fields?.modalities;
1089
+ this.reasoningEffort = fields?.reasoningEffort;
1066
1090
  if (this.azureOpenAIApiKey || this.azureADTokenProvider) {
1067
1091
  if (!this.azureOpenAIApiInstanceName &&
1068
1092
  !this.azureOpenAIBasePath &&
@@ -1195,6 +1219,10 @@ export class ChatOpenAI extends BaseChatModel {
1195
1219
  if (options?.prediction !== undefined) {
1196
1220
  params.prediction = options.prediction;
1197
1221
  }
1222
+ const reasoningEffort = options?.reasoning_effort ?? this.reasoningEffort;
1223
+ if (reasoningEffort !== undefined) {
1224
+ params.reasoning_effort = reasoningEffort;
1225
+ }
1198
1226
  return params;
1199
1227
  }
1200
1228
  /** @ignore */
@@ -1206,7 +1234,7 @@ export class ChatOpenAI extends BaseChatModel {
1206
1234
  };
1207
1235
  }
1208
1236
  async *_streamResponseChunks(messages, options, runManager) {
1209
- const messagesMapped = _convertMessagesToOpenAIParams(messages);
1237
+ const messagesMapped = _convertMessagesToOpenAIParams(messages, this.model);
1210
1238
  const params = {
1211
1239
  ...this.invocationParams(options, {
1212
1240
  streaming: true,
@@ -1246,6 +1274,7 @@ export class ChatOpenAI extends BaseChatModel {
1246
1274
  // Only include system fingerprint in the last chunk for now
1247
1275
  // to avoid concatenation issues
1248
1276
  generationInfo.system_fingerprint = data.system_fingerprint;
1277
+ generationInfo.model_name = data.model;
1249
1278
  }
1250
1279
  if (this.logprobs) {
1251
1280
  generationInfo.logprobs = choice.logprobs;
@@ -1312,7 +1341,7 @@ export class ChatOpenAI extends BaseChatModel {
1312
1341
  async _generate(messages, options, runManager) {
1313
1342
  const usageMetadata = {};
1314
1343
  const params = this.invocationParams(options);
1315
- const messagesMapped = _convertMessagesToOpenAIParams(messages);
1344
+ const messagesMapped = _convertMessagesToOpenAIParams(messages, this.model);
1316
1345
  if (params.stream) {
1317
1346
  const stream = this._streamResponseChunks(messages, options, runManager);
1318
1347
  const finalChunks = {};
@@ -1425,9 +1454,7 @@ export class ChatOpenAI extends BaseChatModel {
1425
1454
  }
1426
1455
  // Fields are not serialized unless passed to the constructor
1427
1456
  // Doing this ensures all fields on the message are serialized
1428
- generation.message = new AIMessage({
1429
- ...generation.message,
1430
- });
1457
+ generation.message = new AIMessage(Object.fromEntries(Object.entries(generation.message).filter(([key]) => !key.startsWith("lc_"))));
1431
1458
  generations.push(generation);
1432
1459
  }
1433
1460
  return {
package/dist/types.d.ts CHANGED
@@ -147,6 +147,11 @@ export interface OpenAIChatInput extends OpenAIBaseInput {
147
147
  * [Learn more](https://platform.openai.com/docs/guides/audio).
148
148
  */
149
149
  audio?: OpenAIClient.Chat.ChatCompletionAudioParam;
150
+ /**
151
+ * Constrains effort on reasoning for reasoning models. Currently supported values are low, medium, and high.
152
+ * Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response.
153
+ */
154
+ reasoningEffort?: OpenAIClient.ChatCompletionReasoningEffort;
150
155
  }
151
156
  export declare interface AzureOpenAIInput {
152
157
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/openai",
3
- "version": "0.3.14",
3
+ "version": "0.3.16",
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.71.0",
39
+ "openai": "^4.77.0",
40
40
  "zod": "^3.22.4",
41
41
  "zod-to-json-schema": "^3.22.3"
42
42
  },