langchain-core 0.4.0.dev0__py3-none-any.whl → 1.0.0a2__py3-none-any.whl

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.
Files changed (74) hide show
  1. langchain_core/_api/beta_decorator.py +2 -2
  2. langchain_core/_api/deprecation.py +1 -1
  3. langchain_core/beta/runnables/context.py +1 -1
  4. langchain_core/callbacks/base.py +14 -23
  5. langchain_core/callbacks/file.py +13 -2
  6. langchain_core/callbacks/manager.py +74 -157
  7. langchain_core/callbacks/streaming_stdout.py +3 -4
  8. langchain_core/callbacks/usage.py +2 -12
  9. langchain_core/chat_history.py +6 -6
  10. langchain_core/documents/base.py +1 -1
  11. langchain_core/documents/compressor.py +9 -6
  12. langchain_core/indexing/base.py +2 -2
  13. langchain_core/language_models/_utils.py +232 -101
  14. langchain_core/language_models/base.py +35 -23
  15. langchain_core/language_models/chat_models.py +248 -54
  16. langchain_core/language_models/fake_chat_models.py +28 -81
  17. langchain_core/load/dump.py +3 -4
  18. langchain_core/messages/__init__.py +30 -24
  19. langchain_core/messages/ai.py +188 -30
  20. langchain_core/messages/base.py +164 -25
  21. langchain_core/messages/block_translators/__init__.py +89 -0
  22. langchain_core/messages/block_translators/anthropic.py +451 -0
  23. langchain_core/messages/block_translators/bedrock.py +45 -0
  24. langchain_core/messages/block_translators/bedrock_converse.py +47 -0
  25. langchain_core/messages/block_translators/google_genai.py +45 -0
  26. langchain_core/messages/block_translators/google_vertexai.py +47 -0
  27. langchain_core/messages/block_translators/groq.py +45 -0
  28. langchain_core/messages/block_translators/langchain_v0.py +164 -0
  29. langchain_core/messages/block_translators/ollama.py +45 -0
  30. langchain_core/messages/block_translators/openai.py +798 -0
  31. langchain_core/messages/{content_blocks.py → content.py} +303 -278
  32. langchain_core/messages/human.py +29 -9
  33. langchain_core/messages/system.py +29 -9
  34. langchain_core/messages/tool.py +94 -13
  35. langchain_core/messages/utils.py +34 -234
  36. langchain_core/output_parsers/base.py +14 -50
  37. langchain_core/output_parsers/json.py +2 -5
  38. langchain_core/output_parsers/list.py +2 -7
  39. langchain_core/output_parsers/openai_functions.py +5 -28
  40. langchain_core/output_parsers/openai_tools.py +49 -90
  41. langchain_core/output_parsers/pydantic.py +2 -3
  42. langchain_core/output_parsers/transform.py +12 -53
  43. langchain_core/output_parsers/xml.py +9 -17
  44. langchain_core/prompt_values.py +8 -112
  45. langchain_core/prompts/chat.py +1 -3
  46. langchain_core/runnables/base.py +500 -451
  47. langchain_core/runnables/branch.py +1 -1
  48. langchain_core/runnables/fallbacks.py +4 -4
  49. langchain_core/runnables/history.py +1 -1
  50. langchain_core/runnables/passthrough.py +3 -3
  51. langchain_core/runnables/retry.py +1 -1
  52. langchain_core/runnables/router.py +1 -1
  53. langchain_core/structured_query.py +3 -7
  54. langchain_core/tools/base.py +14 -41
  55. langchain_core/tools/convert.py +2 -22
  56. langchain_core/tools/retriever.py +1 -8
  57. langchain_core/tools/structured.py +2 -10
  58. langchain_core/tracers/_streaming.py +6 -7
  59. langchain_core/tracers/base.py +7 -14
  60. langchain_core/tracers/core.py +4 -27
  61. langchain_core/tracers/event_stream.py +4 -15
  62. langchain_core/tracers/langchain.py +3 -14
  63. langchain_core/tracers/log_stream.py +2 -3
  64. langchain_core/utils/_merge.py +45 -7
  65. langchain_core/utils/function_calling.py +22 -9
  66. langchain_core/utils/utils.py +29 -0
  67. langchain_core/version.py +1 -1
  68. {langchain_core-0.4.0.dev0.dist-info → langchain_core-1.0.0a2.dist-info}/METADATA +7 -9
  69. {langchain_core-0.4.0.dev0.dist-info → langchain_core-1.0.0a2.dist-info}/RECORD +71 -64
  70. langchain_core/v1/__init__.py +0 -1
  71. langchain_core/v1/chat_models.py +0 -1047
  72. langchain_core/v1/messages.py +0 -755
  73. {langchain_core-0.4.0.dev0.dist-info → langchain_core-1.0.0a2.dist-info}/WHEEL +0 -0
  74. {langchain_core-0.4.0.dev0.dist-info → langchain_core-1.0.0a2.dist-info}/entry_points.txt +0 -0
@@ -11,7 +11,6 @@ from typing import (
11
11
  Optional,
12
12
  TypeVar,
13
13
  Union,
14
- cast,
15
14
  )
16
15
 
17
16
  from typing_extensions import override
@@ -21,22 +20,19 @@ from langchain_core.messages import AnyMessage, BaseMessage
21
20
  from langchain_core.outputs import ChatGeneration, Generation
22
21
  from langchain_core.runnables import Runnable, RunnableConfig, RunnableSerializable
23
22
  from langchain_core.runnables.config import run_in_executor
24
- from langchain_core.v1.messages import AIMessage, MessageV1, MessageV1Types
25
23
 
26
24
  if TYPE_CHECKING:
27
25
  from langchain_core.prompt_values import PromptValue
28
26
 
29
27
  T = TypeVar("T")
30
- OutputParserLike = Runnable[Union[LanguageModelOutput, AIMessage], T]
28
+ OutputParserLike = Runnable[LanguageModelOutput, T]
31
29
 
32
30
 
33
31
  class BaseLLMOutputParser(ABC, Generic[T]):
34
32
  """Abstract base class for parsing the outputs of a model."""
35
33
 
36
34
  @abstractmethod
37
- def parse_result(
38
- self, result: Union[list[Generation], AIMessage], *, partial: bool = False
39
- ) -> T:
35
+ def parse_result(self, result: list[Generation], *, partial: bool = False) -> T:
40
36
  """Parse a list of candidate model Generations into a specific format.
41
37
 
42
38
  Args:
@@ -50,7 +46,7 @@ class BaseLLMOutputParser(ABC, Generic[T]):
50
46
  """
51
47
 
52
48
  async def aparse_result(
53
- self, result: Union[list[Generation], AIMessage], *, partial: bool = False
49
+ self, result: list[Generation], *, partial: bool = False
54
50
  ) -> T:
55
51
  """Async parse a list of candidate model Generations into a specific format.
56
52
 
@@ -75,7 +71,7 @@ class BaseGenerationOutputParser(
75
71
  @override
76
72
  def InputType(self) -> Any:
77
73
  """Return the input type for the parser."""
78
- return Union[str, AnyMessage, MessageV1]
74
+ return Union[str, AnyMessage]
79
75
 
80
76
  @property
81
77
  @override
@@ -88,7 +84,7 @@ class BaseGenerationOutputParser(
88
84
  @override
89
85
  def invoke(
90
86
  self,
91
- input: Union[str, BaseMessage, MessageV1],
87
+ input: Union[str, BaseMessage],
92
88
  config: Optional[RunnableConfig] = None,
93
89
  **kwargs: Any,
94
90
  ) -> T:
@@ -101,16 +97,9 @@ class BaseGenerationOutputParser(
101
97
  config,
102
98
  run_type="parser",
103
99
  )
104
- if isinstance(input, MessageV1Types):
105
- return self._call_with_config(
106
- lambda inner_input: self.parse_result(inner_input),
107
- input,
108
- config,
109
- run_type="parser",
110
- )
111
100
  return self._call_with_config(
112
101
  lambda inner_input: self.parse_result([Generation(text=inner_input)]),
113
- cast("str", input),
102
+ input,
114
103
  config,
115
104
  run_type="parser",
116
105
  )
@@ -131,13 +120,6 @@ class BaseGenerationOutputParser(
131
120
  config,
132
121
  run_type="parser",
133
122
  )
134
- if isinstance(input, MessageV1Types):
135
- return await self._acall_with_config(
136
- lambda inner_input: self.aparse_result(inner_input),
137
- input,
138
- config,
139
- run_type="parser",
140
- )
141
123
  return await self._acall_with_config(
142
124
  lambda inner_input: self.aparse_result([Generation(text=inner_input)]),
143
125
  input,
@@ -147,7 +129,7 @@ class BaseGenerationOutputParser(
147
129
 
148
130
 
149
131
  class BaseOutputParser(
150
- BaseLLMOutputParser, RunnableSerializable[Union[LanguageModelOutput, AIMessage], T]
132
+ BaseLLMOutputParser, RunnableSerializable[LanguageModelOutput, T]
151
133
  ):
152
134
  """Base class to parse the output of an LLM call.
153
135
 
@@ -180,7 +162,7 @@ class BaseOutputParser(
180
162
  @override
181
163
  def InputType(self) -> Any:
182
164
  """Return the input type for the parser."""
183
- return Union[str, AnyMessage, MessageV1]
165
+ return Union[str, AnyMessage]
184
166
 
185
167
  @property
186
168
  @override
@@ -207,7 +189,7 @@ class BaseOutputParser(
207
189
  @override
208
190
  def invoke(
209
191
  self,
210
- input: Union[str, BaseMessage, MessageV1],
192
+ input: Union[str, BaseMessage],
211
193
  config: Optional[RunnableConfig] = None,
212
194
  **kwargs: Any,
213
195
  ) -> T:
@@ -220,16 +202,9 @@ class BaseOutputParser(
220
202
  config,
221
203
  run_type="parser",
222
204
  )
223
- if isinstance(input, MessageV1Types):
224
- return self._call_with_config(
225
- lambda inner_input: self.parse_result(inner_input),
226
- input,
227
- config,
228
- run_type="parser",
229
- )
230
205
  return self._call_with_config(
231
206
  lambda inner_input: self.parse_result([Generation(text=inner_input)]),
232
- cast("str", input),
207
+ input,
233
208
  config,
234
209
  run_type="parser",
235
210
  )
@@ -237,7 +212,7 @@ class BaseOutputParser(
237
212
  @override
238
213
  async def ainvoke(
239
214
  self,
240
- input: Union[str, BaseMessage, MessageV1],
215
+ input: Union[str, BaseMessage],
241
216
  config: Optional[RunnableConfig] = None,
242
217
  **kwargs: Optional[Any],
243
218
  ) -> T:
@@ -250,24 +225,15 @@ class BaseOutputParser(
250
225
  config,
251
226
  run_type="parser",
252
227
  )
253
- if isinstance(input, MessageV1Types):
254
- return await self._acall_with_config(
255
- lambda inner_input: self.aparse_result(inner_input),
256
- input,
257
- config,
258
- run_type="parser",
259
- )
260
228
  return await self._acall_with_config(
261
229
  lambda inner_input: self.aparse_result([Generation(text=inner_input)]),
262
- cast("str", input),
230
+ input,
263
231
  config,
264
232
  run_type="parser",
265
233
  )
266
234
 
267
235
  @override
268
- def parse_result(
269
- self, result: Union[list[Generation], AIMessage], *, partial: bool = False
270
- ) -> T:
236
+ def parse_result(self, result: list[Generation], *, partial: bool = False) -> T:
271
237
  """Parse a list of candidate model Generations into a specific format.
272
238
 
273
239
  The return value is parsed from only the first Generation in the result, which
@@ -282,8 +248,6 @@ class BaseOutputParser(
282
248
  Returns:
283
249
  Structured output.
284
250
  """
285
- if isinstance(result, AIMessage):
286
- return self.parse(result.text)
287
251
  return self.parse(result[0].text)
288
252
 
289
253
  @abstractmethod
@@ -298,7 +262,7 @@ class BaseOutputParser(
298
262
  """
299
263
 
300
264
  async def aparse_result(
301
- self, result: Union[list[Generation], AIMessage], *, partial: bool = False
265
+ self, result: list[Generation], *, partial: bool = False
302
266
  ) -> T:
303
267
  """Async parse a list of candidate model Generations into a specific format.
304
268
 
@@ -21,7 +21,6 @@ from langchain_core.utils.json import (
21
21
  parse_json_markdown,
22
22
  parse_partial_json,
23
23
  )
24
- from langchain_core.v1.messages import AIMessage
25
24
 
26
25
  # Union type needs to be last assignment to PydanticBaseModel to make mypy happy.
27
26
  PydanticBaseModel = Union[BaseModel, pydantic.BaseModel]
@@ -54,9 +53,7 @@ class JsonOutputParser(BaseCumulativeTransformOutputParser[Any]):
54
53
  return pydantic_object.schema()
55
54
  return None
56
55
 
57
- def parse_result(
58
- self, result: Union[list[Generation], AIMessage], *, partial: bool = False
59
- ) -> Any:
56
+ def parse_result(self, result: list[Generation], *, partial: bool = False) -> Any:
60
57
  """Parse the result of an LLM call to a JSON object.
61
58
 
62
59
  Args:
@@ -73,7 +70,7 @@ class JsonOutputParser(BaseCumulativeTransformOutputParser[Any]):
73
70
  Raises:
74
71
  OutputParserException: If the output is not valid JSON.
75
72
  """
76
- text = result.text if isinstance(result, AIMessage) else result[0].text
73
+ text = result[0].text
77
74
  text = text.strip()
78
75
  if partial:
79
76
  try:
@@ -13,7 +13,6 @@ from typing_extensions import override
13
13
 
14
14
  from langchain_core.messages import BaseMessage
15
15
  from langchain_core.output_parsers.transform import BaseTransformOutputParser
16
- from langchain_core.v1.messages import AIMessage
17
16
 
18
17
  if TYPE_CHECKING:
19
18
  from collections.abc import AsyncIterator, Iterator
@@ -72,7 +71,7 @@ class ListOutputParser(BaseTransformOutputParser[list[str]]):
72
71
 
73
72
  @override
74
73
  def _transform(
75
- self, input: Iterator[Union[str, BaseMessage, AIMessage]]
74
+ self, input: Iterator[Union[str, BaseMessage]]
76
75
  ) -> Iterator[list[str]]:
77
76
  buffer = ""
78
77
  for chunk in input:
@@ -82,8 +81,6 @@ class ListOutputParser(BaseTransformOutputParser[list[str]]):
82
81
  if not isinstance(chunk_content, str):
83
82
  continue
84
83
  buffer += chunk_content
85
- elif isinstance(chunk, AIMessage):
86
- buffer += chunk.text
87
84
  else:
88
85
  # add current chunk to buffer
89
86
  buffer += chunk
@@ -108,7 +105,7 @@ class ListOutputParser(BaseTransformOutputParser[list[str]]):
108
105
 
109
106
  @override
110
107
  async def _atransform(
111
- self, input: AsyncIterator[Union[str, BaseMessage, AIMessage]]
108
+ self, input: AsyncIterator[Union[str, BaseMessage]]
112
109
  ) -> AsyncIterator[list[str]]:
113
110
  buffer = ""
114
111
  async for chunk in input:
@@ -118,8 +115,6 @@ class ListOutputParser(BaseTransformOutputParser[list[str]]):
118
115
  if not isinstance(chunk_content, str):
119
116
  continue
120
117
  buffer += chunk_content
121
- elif isinstance(chunk, AIMessage):
122
- buffer += chunk.text
123
118
  else:
124
119
  # add current chunk to buffer
125
120
  buffer += chunk
@@ -17,7 +17,6 @@ from langchain_core.output_parsers import (
17
17
  )
18
18
  from langchain_core.output_parsers.json import parse_partial_json
19
19
  from langchain_core.outputs import ChatGeneration, Generation
20
- from langchain_core.v1.messages import AIMessage
21
20
 
22
21
 
23
22
  class OutputFunctionsParser(BaseGenerationOutputParser[Any]):
@@ -27,9 +26,7 @@ class OutputFunctionsParser(BaseGenerationOutputParser[Any]):
27
26
  """Whether to only return the arguments to the function call."""
28
27
 
29
28
  @override
30
- def parse_result(
31
- self, result: Union[list[Generation], AIMessage], *, partial: bool = False
32
- ) -> Any:
29
+ def parse_result(self, result: list[Generation], *, partial: bool = False) -> Any:
33
30
  """Parse the result of an LLM call to a JSON object.
34
31
 
35
32
  Args:
@@ -42,12 +39,6 @@ class OutputFunctionsParser(BaseGenerationOutputParser[Any]):
42
39
  Raises:
43
40
  OutputParserException: If the output is not valid JSON.
44
41
  """
45
- if isinstance(result, AIMessage):
46
- msg = (
47
- "This output parser does not support v1 AIMessages. Use "
48
- "JsonOutputToolsParser instead."
49
- )
50
- raise TypeError(msg)
51
42
  generation = result[0]
52
43
  if not isinstance(generation, ChatGeneration):
53
44
  msg = "This output parser can only be used with a chat generation."
@@ -86,9 +77,7 @@ class JsonOutputFunctionsParser(BaseCumulativeTransformOutputParser[Any]):
86
77
  def _diff(self, prev: Optional[Any], next: Any) -> Any:
87
78
  return jsonpatch.make_patch(prev, next).patch
88
79
 
89
- def parse_result(
90
- self, result: Union[list[Generation], AIMessage], *, partial: bool = False
91
- ) -> Any:
80
+ def parse_result(self, result: list[Generation], *, partial: bool = False) -> Any:
92
81
  """Parse the result of an LLM call to a JSON object.
93
82
 
94
83
  Args:
@@ -101,12 +90,6 @@ class JsonOutputFunctionsParser(BaseCumulativeTransformOutputParser[Any]):
101
90
  Raises:
102
91
  OutputParserException: If the output is not valid JSON.
103
92
  """
104
- if isinstance(result, AIMessage):
105
- msg = (
106
- "This output parser does not support v1 AIMessages. Use "
107
- "JsonOutputToolsParser instead."
108
- )
109
- raise TypeError(msg)
110
93
  if len(result) != 1:
111
94
  msg = f"Expected exactly one result, but got {len(result)}"
112
95
  raise OutputParserException(msg)
@@ -177,9 +160,7 @@ class JsonKeyOutputFunctionsParser(JsonOutputFunctionsParser):
177
160
  key_name: str
178
161
  """The name of the key to return."""
179
162
 
180
- def parse_result(
181
- self, result: Union[list[Generation], AIMessage], *, partial: bool = False
182
- ) -> Any:
163
+ def parse_result(self, result: list[Generation], *, partial: bool = False) -> Any:
183
164
  """Parse the result of an LLM call to a JSON object.
184
165
 
185
166
  Args:
@@ -273,9 +254,7 @@ class PydanticOutputFunctionsParser(OutputFunctionsParser):
273
254
  return values
274
255
 
275
256
  @override
276
- def parse_result(
277
- self, result: Union[list[Generation], AIMessage], *, partial: bool = False
278
- ) -> Any:
257
+ def parse_result(self, result: list[Generation], *, partial: bool = False) -> Any:
279
258
  """Parse the result of an LLM call to a JSON object.
280
259
 
281
260
  Args:
@@ -315,9 +294,7 @@ class PydanticAttrOutputFunctionsParser(PydanticOutputFunctionsParser):
315
294
  """The name of the attribute to return."""
316
295
 
317
296
  @override
318
- def parse_result(
319
- self, result: Union[list[Generation], AIMessage], *, partial: bool = False
320
- ) -> Any:
297
+ def parse_result(self, result: list[Generation], *, partial: bool = False) -> Any:
321
298
  """Parse the result of an LLM call to a JSON object.
322
299
 
323
300
  Args:
@@ -4,7 +4,7 @@ import copy
4
4
  import json
5
5
  import logging
6
6
  from json import JSONDecodeError
7
- from typing import Annotated, Any, Optional, Union
7
+ from typing import Annotated, Any, Optional
8
8
 
9
9
  from pydantic import SkipValidation, ValidationError
10
10
 
@@ -16,7 +16,6 @@ from langchain_core.output_parsers.transform import BaseCumulativeTransformOutpu
16
16
  from langchain_core.outputs import ChatGeneration, Generation
17
17
  from langchain_core.utils.json import parse_partial_json
18
18
  from langchain_core.utils.pydantic import TypeBaseModel
19
- from langchain_core.v1.messages import AIMessage as AIMessageV1
20
19
 
21
20
  logger = logging.getLogger(__name__)
22
21
 
@@ -157,9 +156,7 @@ class JsonOutputToolsParser(BaseCumulativeTransformOutputParser[Any]):
157
156
  If no tool calls are found, None will be returned.
158
157
  """
159
158
 
160
- def parse_result(
161
- self, result: Union[list[Generation], AIMessageV1], *, partial: bool = False
162
- ) -> Any:
159
+ def parse_result(self, result: list[Generation], *, partial: bool = False) -> Any:
163
160
  """Parse the result of an LLM call to a list of tool calls.
164
161
 
165
162
  Args:
@@ -176,45 +173,31 @@ class JsonOutputToolsParser(BaseCumulativeTransformOutputParser[Any]):
176
173
  Raises:
177
174
  OutputParserException: If the output is not valid JSON.
178
175
  """
179
- if isinstance(result, list):
180
- generation = result[0]
181
- if not isinstance(generation, ChatGeneration):
182
- msg = (
183
- "This output parser can only be used with a chat generation or "
184
- "v1 AIMessage."
185
- )
186
- raise OutputParserException(msg)
187
- message = generation.message
188
- if isinstance(message, AIMessage) and message.tool_calls:
189
- tool_calls = [dict(tc) for tc in message.tool_calls]
190
- for tool_call in tool_calls:
191
- if not self.return_id:
192
- _ = tool_call.pop("id")
193
- else:
194
- try:
195
- raw_tool_calls = copy.deepcopy(
196
- message.additional_kwargs["tool_calls"]
197
- )
198
- except KeyError:
199
- return []
200
- tool_calls = parse_tool_calls(
201
- raw_tool_calls,
202
- partial=partial,
203
- strict=self.strict,
204
- return_id=self.return_id,
205
- )
206
- elif result.tool_calls:
207
- # v1 message
208
- tool_calls = [dict(tc) for tc in result.tool_calls]
176
+ generation = result[0]
177
+ if not isinstance(generation, ChatGeneration):
178
+ msg = "This output parser can only be used with a chat generation."
179
+ raise OutputParserException(msg)
180
+ message = generation.message
181
+ if isinstance(message, AIMessage) and message.tool_calls:
182
+ tool_calls = [dict(tc) for tc in message.tool_calls]
209
183
  for tool_call in tool_calls:
210
184
  if not self.return_id:
211
185
  _ = tool_call.pop("id")
212
186
  else:
213
- return []
214
-
187
+ try:
188
+ raw_tool_calls = copy.deepcopy(message.additional_kwargs["tool_calls"])
189
+ except KeyError:
190
+ return []
191
+ tool_calls = parse_tool_calls(
192
+ raw_tool_calls,
193
+ partial=partial,
194
+ strict=self.strict,
195
+ return_id=self.return_id,
196
+ )
215
197
  # for backwards compatibility
216
198
  for tc in tool_calls:
217
199
  tc["type"] = tc.pop("name")
200
+
218
201
  if self.first_tool_only:
219
202
  return tool_calls[0] if tool_calls else None
220
203
  return tool_calls
@@ -237,9 +220,7 @@ class JsonOutputKeyToolsParser(JsonOutputToolsParser):
237
220
  key_name: str
238
221
  """The type of tools to return."""
239
222
 
240
- def parse_result(
241
- self, result: Union[list[Generation], AIMessageV1], *, partial: bool = False
242
- ) -> Any:
223
+ def parse_result(self, result: list[Generation], *, partial: bool = False) -> Any:
243
224
  """Parse the result of an LLM call to a list of tool calls.
244
225
 
245
226
  Args:
@@ -253,47 +234,34 @@ class JsonOutputKeyToolsParser(JsonOutputToolsParser):
253
234
  Returns:
254
235
  The parsed tool calls.
255
236
  """
256
- if isinstance(result, list):
257
- generation = result[0]
258
- if not isinstance(generation, ChatGeneration):
259
- msg = "This output parser can only be used with a chat generation."
260
- raise OutputParserException(msg)
261
- message = generation.message
262
- if isinstance(message, AIMessage) and message.tool_calls:
263
- parsed_tool_calls = [dict(tc) for tc in message.tool_calls]
264
- for tool_call in parsed_tool_calls:
265
- if not self.return_id:
266
- _ = tool_call.pop("id")
267
- else:
268
- try:
269
- raw_tool_calls = copy.deepcopy(
270
- message.additional_kwargs["tool_calls"]
271
- )
272
- except KeyError:
273
- if self.first_tool_only:
274
- return None
275
- return []
276
- parsed_tool_calls = parse_tool_calls(
277
- raw_tool_calls,
278
- partial=partial,
279
- strict=self.strict,
280
- return_id=self.return_id,
281
- )
282
- elif result.tool_calls:
283
- # v1 message
284
- parsed_tool_calls = [dict(tc) for tc in result.tool_calls]
237
+ generation = result[0]
238
+ if not isinstance(generation, ChatGeneration):
239
+ msg = "This output parser can only be used with a chat generation."
240
+ raise OutputParserException(msg)
241
+ message = generation.message
242
+ if isinstance(message, AIMessage) and message.tool_calls:
243
+ parsed_tool_calls = [dict(tc) for tc in message.tool_calls]
285
244
  for tool_call in parsed_tool_calls:
286
245
  if not self.return_id:
287
246
  _ = tool_call.pop("id")
288
247
  else:
289
- if self.first_tool_only:
290
- return None
291
- return []
292
-
248
+ try:
249
+ # This exists purely for backward compatibility / cached messages
250
+ # All new messages should use `message.tool_calls`
251
+ raw_tool_calls = copy.deepcopy(message.additional_kwargs["tool_calls"])
252
+ except KeyError:
253
+ if self.first_tool_only:
254
+ return None
255
+ return []
256
+ parsed_tool_calls = parse_tool_calls(
257
+ raw_tool_calls,
258
+ partial=partial,
259
+ strict=self.strict,
260
+ return_id=self.return_id,
261
+ )
293
262
  # For backwards compatibility
294
263
  for tc in parsed_tool_calls:
295
264
  tc["type"] = tc.pop("name")
296
-
297
265
  if self.first_tool_only:
298
266
  parsed_result = list(
299
267
  filter(lambda x: x["type"] == self.key_name, parsed_tool_calls)
@@ -333,9 +301,7 @@ class PydanticToolsParser(JsonOutputToolsParser):
333
301
 
334
302
  # TODO: Support more granular streaming of objects. Currently only streams once all
335
303
  # Pydantic object fields are present.
336
- def parse_result(
337
- self, result: Union[list[Generation], AIMessageV1], *, partial: bool = False
338
- ) -> Any:
304
+ def parse_result(self, result: list[Generation], *, partial: bool = False) -> Any:
339
305
  """Parse the result of an LLM call to a list of Pydantic objects.
340
306
 
341
307
  Args:
@@ -373,19 +339,12 @@ class PydanticToolsParser(JsonOutputToolsParser):
373
339
  except (ValidationError, ValueError):
374
340
  if partial:
375
341
  continue
376
- has_max_tokens_stop_reason = False
377
- if isinstance(result, list):
378
- has_max_tokens_stop_reason = any(
379
- generation.message.response_metadata.get("stop_reason")
380
- == "max_tokens"
381
- for generation in result
382
- if isinstance(generation, ChatGeneration)
383
- )
384
- else:
385
- # v1 message
386
- has_max_tokens_stop_reason = (
387
- result.response_metadata.get("stop_reason") == "max_tokens"
388
- )
342
+ has_max_tokens_stop_reason = any(
343
+ generation.message.response_metadata.get("stop_reason")
344
+ == "max_tokens"
345
+ for generation in result
346
+ if isinstance(generation, ChatGeneration)
347
+ )
389
348
  if has_max_tokens_stop_reason:
390
349
  logger.exception(_MAX_TOKENS_ERROR)
391
350
  raise
@@ -1,7 +1,7 @@
1
1
  """Output parsers using Pydantic."""
2
2
 
3
3
  import json
4
- from typing import Annotated, Generic, Optional, Union
4
+ from typing import Annotated, Generic, Optional
5
5
 
6
6
  import pydantic
7
7
  from pydantic import SkipValidation
@@ -14,7 +14,6 @@ from langchain_core.utils.pydantic import (
14
14
  PydanticBaseModel,
15
15
  TBaseModel,
16
16
  )
17
- from langchain_core.v1.messages import AIMessage
18
17
 
19
18
 
20
19
  class PydanticOutputParser(JsonOutputParser, Generic[TBaseModel]):
@@ -44,7 +43,7 @@ class PydanticOutputParser(JsonOutputParser, Generic[TBaseModel]):
44
43
  return OutputParserException(msg, llm_output=json_string)
45
44
 
46
45
  def parse_result(
47
- self, result: Union[list[Generation], AIMessage], *, partial: bool = False
46
+ self, result: list[Generation], *, partial: bool = False
48
47
  ) -> Optional[TBaseModel]:
49
48
  """Parse the result of an LLM call to a pydantic object.
50
49