@langchain/openai 0.4.5-rc.0 → 0.4.6

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.
@@ -108,6 +108,27 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
108
108
  }
109
109
  if (role === "tool") {
110
110
  const toolMessage = lcMsg;
111
+ // Handle computer call output
112
+ if (toolMessage.additional_kwargs?.type === "computer_call_output") {
113
+ const output = (() => {
114
+ if (typeof toolMessage.content === "string") {
115
+ return {
116
+ type: "computer_screenshot",
117
+ image_url: toolMessage.content,
118
+ };
119
+ }
120
+ if (Array.isArray(toolMessage.content)) {
121
+ return toolMessage.content.find((i) => i.type === "computer_screenshot");
122
+ }
123
+ return toolMessage.content;
124
+ })();
125
+ return {
126
+ type: "computer_call_output",
127
+ output,
128
+ call_id: toolMessage.tool_call_id,
129
+ id: toolMessage.id,
130
+ };
131
+ }
111
132
  return {
112
133
  type: "function_call_output",
113
134
  call_id: toolMessage.tool_call_id,
@@ -127,7 +148,8 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
127
148
  name: toolCall.name,
128
149
  arguments: JSON.stringify(toolCall.args),
129
150
  call_id: toolCall.id,
130
- id: functionCallIds[toolCall.id],
151
+ // @ts-expect-error Might come from a non-Responses API message
152
+ id: functionCallIds?.[toolCall.id],
131
153
  }));
132
154
  }
133
155
  if (lcMsg.additional_kwargs.tool_calls != null) {
@@ -135,7 +157,8 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
135
157
  type: "function_call",
136
158
  name: toolCall.function.name,
137
159
  call_id: toolCall.id,
138
- id: functionCallIds[toolCall.id],
160
+ // @ts-expect-error Might come from a non-Responses API message
161
+ id: functionCallIds?.[toolCall.id],
139
162
  arguments: toolCall.function.arguments,
140
163
  }));
141
164
  }
@@ -229,6 +252,9 @@ function _convertOpenAIResponsesMessageToBaseMessage(response) {
229
252
  // TODO: how to handle refusals?
230
253
  content.push(...item.content.map((part) => {
231
254
  if (part.type === "output_text") {
255
+ if ("parsed" in part && part.parsed != null) {
256
+ additional_kwargs.parsed = part.parsed;
257
+ }
232
258
  return {
233
259
  type: "text",
234
260
  text: part.text,
@@ -256,6 +282,8 @@ function _convertOpenAIResponsesMessageToBaseMessage(response) {
256
282
  }
257
283
  invalid_tool_calls.push((0, openai_tools_1.makeInvalidToolCall)(fnAdapter, errMessage));
258
284
  }
285
+ additional_kwargs[_FUNCTION_CALL_IDS_MAP_KEY] ??= {};
286
+ additional_kwargs[_FUNCTION_CALL_IDS_MAP_KEY][item.call_id] = item.id;
259
287
  }
260
288
  else if (item.type === "reasoning") {
261
289
  additional_kwargs.reasoning = item;
@@ -326,7 +354,15 @@ function _convertOpenAIResponsesDeltaToBaseMessageChunk(chunk) {
326
354
  response_metadata.model = chunk.response.model;
327
355
  }
328
356
  else if (chunk.type === "response.completed") {
357
+ const msg = _convertOpenAIResponsesMessageToBaseMessage(chunk.response);
329
358
  usage_metadata = chunk.response.usage;
359
+ if (chunk.response.text?.format?.type === "json_schema") {
360
+ additional_kwargs.parsed ??= JSON.parse(msg.text);
361
+ }
362
+ for (const [key, value] of Object.entries(chunk.response)) {
363
+ if (key !== "id")
364
+ response_metadata[key] = value;
365
+ }
330
366
  }
331
367
  else if (chunk.type === "response.function_call_arguments.delta") {
332
368
  tool_call_chunks.push({
@@ -1966,8 +2002,11 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
1966
2002
  return this.caller.call(async () => {
1967
2003
  const requestOptions = this._getClientOptions(options);
1968
2004
  try {
1969
- const res = await this.client.responses.create(request, requestOptions);
1970
- return res;
2005
+ // use parse if dealing with json_schema
2006
+ if (request.text?.format?.type === "json_schema" && !request.stream) {
2007
+ return await this.client.responses.parse(request, requestOptions);
2008
+ }
2009
+ return await this.client.responses.create(request, requestOptions);
1971
2010
  }
1972
2011
  catch (e) {
1973
2012
  const error = (0, openai_js_1.wrapOpenAIClientError)(e);
@@ -2097,7 +2136,13 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
2097
2136
  },
2098
2137
  });
2099
2138
  if (isZodSchema(schema)) {
2100
- outputParser = output_parsers_1.StructuredOutputParser.fromZodSchema(schema);
2139
+ const altParser = output_parsers_1.StructuredOutputParser.fromZodSchema(schema);
2140
+ outputParser = runnables_1.RunnableLambda.from((aiMessage) => {
2141
+ if ("parsed" in aiMessage.additional_kwargs) {
2142
+ return aiMessage.additional_kwargs.parsed;
2143
+ }
2144
+ return altParser;
2145
+ });
2101
2146
  }
2102
2147
  else {
2103
2148
  outputParser = new output_parsers_1.JsonOutputParser();
@@ -2185,12 +2230,7 @@ class ChatOpenAI extends chat_models_1.BaseChatModel {
2185
2230
  const parsedWithFallback = parserAssign.withFallbacks({
2186
2231
  fallbacks: [parserNone],
2187
2232
  });
2188
- return runnables_1.RunnableSequence.from([
2189
- {
2190
- raw: llm,
2191
- },
2192
- parsedWithFallback,
2193
- ]);
2233
+ return runnables_1.RunnableSequence.from([{ raw: llm }, parsedWithFallback]);
2194
2234
  }
2195
2235
  }
2196
2236
  exports.ChatOpenAI = ChatOpenAI;
@@ -4,7 +4,7 @@ import { ChatGenerationChunk, } from "@langchain/core/outputs";
4
4
  import { getEnvironmentVariable } from "@langchain/core/utils/env";
5
5
  import { BaseChatModel, } from "@langchain/core/language_models/chat_models";
6
6
  import { isOpenAITool, } from "@langchain/core/language_models/base";
7
- import { RunnablePassthrough, RunnableSequence, } from "@langchain/core/runnables";
7
+ import { RunnableLambda, RunnablePassthrough, RunnableSequence, } from "@langchain/core/runnables";
8
8
  import { JsonOutputParser, StructuredOutputParser, } from "@langchain/core/output_parsers";
9
9
  import { JsonOutputKeyToolsParser, convertLangChainToolCallToOpenAI, makeInvalidToolCall, parseToolCall, } from "@langchain/core/output_parsers/openai_tools";
10
10
  import { zodToJsonSchema } from "zod-to-json-schema";
@@ -103,6 +103,27 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
103
103
  }
104
104
  if (role === "tool") {
105
105
  const toolMessage = lcMsg;
106
+ // Handle computer call output
107
+ if (toolMessage.additional_kwargs?.type === "computer_call_output") {
108
+ const output = (() => {
109
+ if (typeof toolMessage.content === "string") {
110
+ return {
111
+ type: "computer_screenshot",
112
+ image_url: toolMessage.content,
113
+ };
114
+ }
115
+ if (Array.isArray(toolMessage.content)) {
116
+ return toolMessage.content.find((i) => i.type === "computer_screenshot");
117
+ }
118
+ return toolMessage.content;
119
+ })();
120
+ return {
121
+ type: "computer_call_output",
122
+ output,
123
+ call_id: toolMessage.tool_call_id,
124
+ id: toolMessage.id,
125
+ };
126
+ }
106
127
  return {
107
128
  type: "function_call_output",
108
129
  call_id: toolMessage.tool_call_id,
@@ -122,7 +143,8 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
122
143
  name: toolCall.name,
123
144
  arguments: JSON.stringify(toolCall.args),
124
145
  call_id: toolCall.id,
125
- id: functionCallIds[toolCall.id],
146
+ // @ts-expect-error Might come from a non-Responses API message
147
+ id: functionCallIds?.[toolCall.id],
126
148
  }));
127
149
  }
128
150
  if (lcMsg.additional_kwargs.tool_calls != null) {
@@ -130,7 +152,8 @@ function _convertMessagesToOpenAIResponsesParams(messages, model) {
130
152
  type: "function_call",
131
153
  name: toolCall.function.name,
132
154
  call_id: toolCall.id,
133
- id: functionCallIds[toolCall.id],
155
+ // @ts-expect-error Might come from a non-Responses API message
156
+ id: functionCallIds?.[toolCall.id],
134
157
  arguments: toolCall.function.arguments,
135
158
  }));
136
159
  }
@@ -224,6 +247,9 @@ function _convertOpenAIResponsesMessageToBaseMessage(response) {
224
247
  // TODO: how to handle refusals?
225
248
  content.push(...item.content.map((part) => {
226
249
  if (part.type === "output_text") {
250
+ if ("parsed" in part && part.parsed != null) {
251
+ additional_kwargs.parsed = part.parsed;
252
+ }
227
253
  return {
228
254
  type: "text",
229
255
  text: part.text,
@@ -251,6 +277,8 @@ function _convertOpenAIResponsesMessageToBaseMessage(response) {
251
277
  }
252
278
  invalid_tool_calls.push(makeInvalidToolCall(fnAdapter, errMessage));
253
279
  }
280
+ additional_kwargs[_FUNCTION_CALL_IDS_MAP_KEY] ??= {};
281
+ additional_kwargs[_FUNCTION_CALL_IDS_MAP_KEY][item.call_id] = item.id;
254
282
  }
255
283
  else if (item.type === "reasoning") {
256
284
  additional_kwargs.reasoning = item;
@@ -321,7 +349,15 @@ function _convertOpenAIResponsesDeltaToBaseMessageChunk(chunk) {
321
349
  response_metadata.model = chunk.response.model;
322
350
  }
323
351
  else if (chunk.type === "response.completed") {
352
+ const msg = _convertOpenAIResponsesMessageToBaseMessage(chunk.response);
324
353
  usage_metadata = chunk.response.usage;
354
+ if (chunk.response.text?.format?.type === "json_schema") {
355
+ additional_kwargs.parsed ??= JSON.parse(msg.text);
356
+ }
357
+ for (const [key, value] of Object.entries(chunk.response)) {
358
+ if (key !== "id")
359
+ response_metadata[key] = value;
360
+ }
325
361
  }
326
362
  else if (chunk.type === "response.function_call_arguments.delta") {
327
363
  tool_call_chunks.push({
@@ -1961,8 +1997,11 @@ export class ChatOpenAI extends BaseChatModel {
1961
1997
  return this.caller.call(async () => {
1962
1998
  const requestOptions = this._getClientOptions(options);
1963
1999
  try {
1964
- const res = await this.client.responses.create(request, requestOptions);
1965
- return res;
2000
+ // use parse if dealing with json_schema
2001
+ if (request.text?.format?.type === "json_schema" && !request.stream) {
2002
+ return await this.client.responses.parse(request, requestOptions);
2003
+ }
2004
+ return await this.client.responses.create(request, requestOptions);
1966
2005
  }
1967
2006
  catch (e) {
1968
2007
  const error = wrapOpenAIClientError(e);
@@ -2092,7 +2131,13 @@ export class ChatOpenAI extends BaseChatModel {
2092
2131
  },
2093
2132
  });
2094
2133
  if (isZodSchema(schema)) {
2095
- outputParser = StructuredOutputParser.fromZodSchema(schema);
2134
+ const altParser = StructuredOutputParser.fromZodSchema(schema);
2135
+ outputParser = RunnableLambda.from((aiMessage) => {
2136
+ if ("parsed" in aiMessage.additional_kwargs) {
2137
+ return aiMessage.additional_kwargs.parsed;
2138
+ }
2139
+ return altParser;
2140
+ });
2096
2141
  }
2097
2142
  else {
2098
2143
  outputParser = new JsonOutputParser();
@@ -2180,12 +2225,7 @@ export class ChatOpenAI extends BaseChatModel {
2180
2225
  const parsedWithFallback = parserAssign.withFallbacks({
2181
2226
  fallbacks: [parserNone],
2182
2227
  });
2183
- return RunnableSequence.from([
2184
- {
2185
- raw: llm,
2186
- },
2187
- parsedWithFallback,
2188
- ]);
2228
+ return RunnableSequence.from([{ raw: llm }, parsedWithFallback]);
2189
2229
  }
2190
2230
  }
2191
2231
  function isZodSchema(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/openai",
3
- "version": "0.4.5-rc.0",
3
+ "version": "0.4.6",
4
4
  "description": "OpenAI integrations for LangChain.js",
5
5
  "type": "module",
6
6
  "engines": {