langchain-core 0.4.0.dev0__py3-none-any.whl → 1.0.0__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.
Potentially problematic release.
This version of langchain-core might be problematic. Click here for more details.
- langchain_core/__init__.py +1 -1
- langchain_core/_api/__init__.py +3 -4
- langchain_core/_api/beta_decorator.py +45 -70
- langchain_core/_api/deprecation.py +80 -80
- langchain_core/_api/path.py +22 -8
- langchain_core/_import_utils.py +10 -4
- langchain_core/agents.py +25 -21
- langchain_core/caches.py +53 -63
- langchain_core/callbacks/__init__.py +1 -8
- langchain_core/callbacks/base.py +341 -348
- langchain_core/callbacks/file.py +55 -44
- langchain_core/callbacks/manager.py +546 -683
- langchain_core/callbacks/stdout.py +29 -30
- langchain_core/callbacks/streaming_stdout.py +35 -36
- langchain_core/callbacks/usage.py +65 -70
- langchain_core/chat_history.py +48 -55
- langchain_core/document_loaders/base.py +46 -21
- langchain_core/document_loaders/langsmith.py +39 -36
- langchain_core/documents/__init__.py +0 -1
- langchain_core/documents/base.py +96 -74
- langchain_core/documents/compressor.py +12 -9
- langchain_core/documents/transformers.py +29 -28
- langchain_core/embeddings/fake.py +56 -57
- langchain_core/env.py +2 -3
- langchain_core/example_selectors/base.py +12 -0
- langchain_core/example_selectors/length_based.py +1 -1
- langchain_core/example_selectors/semantic_similarity.py +21 -25
- langchain_core/exceptions.py +15 -9
- langchain_core/globals.py +4 -163
- langchain_core/indexing/api.py +132 -125
- langchain_core/indexing/base.py +64 -67
- langchain_core/indexing/in_memory.py +26 -6
- langchain_core/language_models/__init__.py +15 -27
- langchain_core/language_models/_utils.py +267 -117
- langchain_core/language_models/base.py +92 -177
- langchain_core/language_models/chat_models.py +547 -407
- langchain_core/language_models/fake.py +11 -11
- langchain_core/language_models/fake_chat_models.py +72 -118
- langchain_core/language_models/llms.py +168 -242
- langchain_core/load/dump.py +8 -11
- langchain_core/load/load.py +32 -28
- langchain_core/load/mapping.py +2 -4
- langchain_core/load/serializable.py +50 -56
- langchain_core/messages/__init__.py +36 -51
- langchain_core/messages/ai.py +377 -150
- langchain_core/messages/base.py +239 -47
- langchain_core/messages/block_translators/__init__.py +111 -0
- langchain_core/messages/block_translators/anthropic.py +470 -0
- langchain_core/messages/block_translators/bedrock.py +94 -0
- langchain_core/messages/block_translators/bedrock_converse.py +297 -0
- langchain_core/messages/block_translators/google_genai.py +530 -0
- langchain_core/messages/block_translators/google_vertexai.py +21 -0
- langchain_core/messages/block_translators/groq.py +143 -0
- langchain_core/messages/block_translators/langchain_v0.py +301 -0
- langchain_core/messages/block_translators/openai.py +1010 -0
- langchain_core/messages/chat.py +2 -3
- langchain_core/messages/content.py +1423 -0
- langchain_core/messages/function.py +7 -7
- langchain_core/messages/human.py +44 -38
- langchain_core/messages/modifier.py +3 -2
- langchain_core/messages/system.py +40 -27
- langchain_core/messages/tool.py +160 -58
- langchain_core/messages/utils.py +527 -638
- langchain_core/output_parsers/__init__.py +1 -14
- langchain_core/output_parsers/base.py +68 -104
- langchain_core/output_parsers/json.py +13 -17
- langchain_core/output_parsers/list.py +11 -33
- langchain_core/output_parsers/openai_functions.py +56 -74
- langchain_core/output_parsers/openai_tools.py +68 -109
- langchain_core/output_parsers/pydantic.py +15 -13
- langchain_core/output_parsers/string.py +6 -2
- langchain_core/output_parsers/transform.py +17 -60
- langchain_core/output_parsers/xml.py +34 -44
- langchain_core/outputs/__init__.py +1 -1
- langchain_core/outputs/chat_generation.py +26 -11
- langchain_core/outputs/chat_result.py +1 -3
- langchain_core/outputs/generation.py +17 -6
- langchain_core/outputs/llm_result.py +15 -8
- langchain_core/prompt_values.py +29 -123
- langchain_core/prompts/__init__.py +3 -27
- langchain_core/prompts/base.py +48 -63
- langchain_core/prompts/chat.py +259 -288
- langchain_core/prompts/dict.py +19 -11
- langchain_core/prompts/few_shot.py +84 -90
- langchain_core/prompts/few_shot_with_templates.py +14 -12
- langchain_core/prompts/image.py +19 -14
- langchain_core/prompts/loading.py +6 -8
- langchain_core/prompts/message.py +7 -8
- langchain_core/prompts/prompt.py +42 -43
- langchain_core/prompts/string.py +37 -16
- langchain_core/prompts/structured.py +43 -46
- langchain_core/rate_limiters.py +51 -60
- langchain_core/retrievers.py +52 -192
- langchain_core/runnables/base.py +1727 -1683
- langchain_core/runnables/branch.py +52 -73
- langchain_core/runnables/config.py +89 -103
- langchain_core/runnables/configurable.py +128 -130
- langchain_core/runnables/fallbacks.py +93 -82
- langchain_core/runnables/graph.py +127 -127
- langchain_core/runnables/graph_ascii.py +63 -41
- langchain_core/runnables/graph_mermaid.py +87 -70
- langchain_core/runnables/graph_png.py +31 -36
- langchain_core/runnables/history.py +145 -161
- langchain_core/runnables/passthrough.py +141 -144
- langchain_core/runnables/retry.py +84 -68
- langchain_core/runnables/router.py +33 -37
- langchain_core/runnables/schema.py +79 -72
- langchain_core/runnables/utils.py +95 -139
- langchain_core/stores.py +85 -131
- langchain_core/structured_query.py +11 -15
- langchain_core/sys_info.py +31 -32
- langchain_core/tools/__init__.py +1 -14
- langchain_core/tools/base.py +221 -247
- langchain_core/tools/convert.py +144 -161
- langchain_core/tools/render.py +10 -10
- langchain_core/tools/retriever.py +12 -19
- langchain_core/tools/simple.py +52 -29
- langchain_core/tools/structured.py +56 -60
- langchain_core/tracers/__init__.py +1 -9
- langchain_core/tracers/_streaming.py +6 -7
- langchain_core/tracers/base.py +103 -112
- langchain_core/tracers/context.py +29 -48
- langchain_core/tracers/core.py +142 -105
- langchain_core/tracers/evaluation.py +30 -34
- langchain_core/tracers/event_stream.py +162 -117
- langchain_core/tracers/langchain.py +34 -36
- langchain_core/tracers/log_stream.py +87 -49
- langchain_core/tracers/memory_stream.py +3 -3
- langchain_core/tracers/root_listeners.py +18 -34
- langchain_core/tracers/run_collector.py +8 -20
- langchain_core/tracers/schemas.py +0 -125
- langchain_core/tracers/stdout.py +3 -3
- langchain_core/utils/__init__.py +1 -4
- langchain_core/utils/_merge.py +47 -9
- langchain_core/utils/aiter.py +70 -66
- langchain_core/utils/env.py +12 -9
- langchain_core/utils/function_calling.py +139 -206
- langchain_core/utils/html.py +7 -8
- langchain_core/utils/input.py +6 -6
- langchain_core/utils/interactive_env.py +6 -2
- langchain_core/utils/iter.py +48 -45
- langchain_core/utils/json.py +14 -4
- langchain_core/utils/json_schema.py +159 -43
- langchain_core/utils/mustache.py +32 -25
- langchain_core/utils/pydantic.py +67 -40
- langchain_core/utils/strings.py +5 -5
- langchain_core/utils/usage.py +1 -1
- langchain_core/utils/utils.py +104 -62
- langchain_core/vectorstores/base.py +131 -179
- langchain_core/vectorstores/in_memory.py +113 -182
- langchain_core/vectorstores/utils.py +23 -17
- langchain_core/version.py +1 -1
- langchain_core-1.0.0.dist-info/METADATA +68 -0
- langchain_core-1.0.0.dist-info/RECORD +172 -0
- {langchain_core-0.4.0.dev0.dist-info → langchain_core-1.0.0.dist-info}/WHEEL +1 -1
- langchain_core/beta/__init__.py +0 -1
- langchain_core/beta/runnables/__init__.py +0 -1
- langchain_core/beta/runnables/context.py +0 -448
- langchain_core/memory.py +0 -116
- langchain_core/messages/content_blocks.py +0 -1435
- langchain_core/prompts/pipeline.py +0 -133
- langchain_core/pydantic_v1/__init__.py +0 -30
- langchain_core/pydantic_v1/dataclasses.py +0 -23
- langchain_core/pydantic_v1/main.py +0 -23
- langchain_core/tracers/langchain_v1.py +0 -23
- langchain_core/utils/loading.py +0 -31
- langchain_core/v1/__init__.py +0 -1
- langchain_core/v1/chat_models.py +0 -1047
- langchain_core/v1/messages.py +0 -755
- langchain_core-0.4.0.dev0.dist-info/METADATA +0 -108
- langchain_core-0.4.0.dev0.dist-info/RECORD +0 -177
- langchain_core-0.4.0.dev0.dist-info/entry_points.txt +0 -4
|
@@ -5,8 +5,6 @@ from __future__ import annotations
|
|
|
5
5
|
from typing import (
|
|
6
6
|
TYPE_CHECKING,
|
|
7
7
|
Any,
|
|
8
|
-
Optional,
|
|
9
|
-
Union,
|
|
10
8
|
)
|
|
11
9
|
|
|
12
10
|
from typing_extensions import override
|
|
@@ -20,7 +18,6 @@ from langchain_core.outputs import (
|
|
|
20
18
|
GenerationChunk,
|
|
21
19
|
)
|
|
22
20
|
from langchain_core.runnables.config import run_in_executor
|
|
23
|
-
from langchain_core.v1.messages import AIMessage, AIMessageChunk
|
|
24
21
|
|
|
25
22
|
if TYPE_CHECKING:
|
|
26
23
|
from collections.abc import AsyncIterator, Iterator
|
|
@@ -33,27 +30,23 @@ class BaseTransformOutputParser(BaseOutputParser[T]):
|
|
|
33
30
|
|
|
34
31
|
def _transform(
|
|
35
32
|
self,
|
|
36
|
-
input: Iterator[
|
|
33
|
+
input: Iterator[str | BaseMessage],
|
|
37
34
|
) -> Iterator[T]:
|
|
38
35
|
for chunk in input:
|
|
39
36
|
if isinstance(chunk, BaseMessage):
|
|
40
37
|
yield self.parse_result([ChatGeneration(message=chunk)])
|
|
41
|
-
elif isinstance(chunk, AIMessage):
|
|
42
|
-
yield self.parse_result(chunk)
|
|
43
38
|
else:
|
|
44
39
|
yield self.parse_result([Generation(text=chunk)])
|
|
45
40
|
|
|
46
41
|
async def _atransform(
|
|
47
42
|
self,
|
|
48
|
-
input: AsyncIterator[
|
|
43
|
+
input: AsyncIterator[str | BaseMessage],
|
|
49
44
|
) -> AsyncIterator[T]:
|
|
50
45
|
async for chunk in input:
|
|
51
46
|
if isinstance(chunk, BaseMessage):
|
|
52
47
|
yield await run_in_executor(
|
|
53
48
|
None, self.parse_result, [ChatGeneration(message=chunk)]
|
|
54
49
|
)
|
|
55
|
-
elif isinstance(chunk, AIMessage):
|
|
56
|
-
yield await run_in_executor(None, self.parse_result, chunk)
|
|
57
50
|
else:
|
|
58
51
|
yield await run_in_executor(
|
|
59
52
|
None, self.parse_result, [Generation(text=chunk)]
|
|
@@ -62,8 +55,8 @@ class BaseTransformOutputParser(BaseOutputParser[T]):
|
|
|
62
55
|
@override
|
|
63
56
|
def transform(
|
|
64
57
|
self,
|
|
65
|
-
input: Iterator[
|
|
66
|
-
config:
|
|
58
|
+
input: Iterator[str | BaseMessage],
|
|
59
|
+
config: RunnableConfig | None = None,
|
|
67
60
|
**kwargs: Any,
|
|
68
61
|
) -> Iterator[T]:
|
|
69
62
|
"""Transform the input into the output format.
|
|
@@ -71,7 +64,7 @@ class BaseTransformOutputParser(BaseOutputParser[T]):
|
|
|
71
64
|
Args:
|
|
72
65
|
input: The input to transform.
|
|
73
66
|
config: The configuration to use for the transformation.
|
|
74
|
-
kwargs: Additional keyword arguments.
|
|
67
|
+
**kwargs: Additional keyword arguments.
|
|
75
68
|
|
|
76
69
|
Yields:
|
|
77
70
|
The transformed output.
|
|
@@ -83,8 +76,8 @@ class BaseTransformOutputParser(BaseOutputParser[T]):
|
|
|
83
76
|
@override
|
|
84
77
|
async def atransform(
|
|
85
78
|
self,
|
|
86
|
-
input: AsyncIterator[
|
|
87
|
-
config:
|
|
79
|
+
input: AsyncIterator[str | BaseMessage],
|
|
80
|
+
config: RunnableConfig | None = None,
|
|
88
81
|
**kwargs: Any,
|
|
89
82
|
) -> AsyncIterator[T]:
|
|
90
83
|
"""Async transform the input into the output format.
|
|
@@ -92,7 +85,7 @@ class BaseTransformOutputParser(BaseOutputParser[T]):
|
|
|
92
85
|
Args:
|
|
93
86
|
input: The input to transform.
|
|
94
87
|
config: The configuration to use for the transformation.
|
|
95
|
-
kwargs: Additional keyword arguments.
|
|
88
|
+
**kwargs: Additional keyword arguments.
|
|
96
89
|
|
|
97
90
|
Yields:
|
|
98
91
|
The transformed output.
|
|
@@ -113,7 +106,7 @@ class BaseCumulativeTransformOutputParser(BaseTransformOutputParser[T]):
|
|
|
113
106
|
|
|
114
107
|
def _diff(
|
|
115
108
|
self,
|
|
116
|
-
prev:
|
|
109
|
+
prev: T | None,
|
|
117
110
|
next: T, # noqa: A002
|
|
118
111
|
) -> T:
|
|
119
112
|
"""Convert parsed outputs into a diff format.
|
|
@@ -130,42 +123,23 @@ class BaseCumulativeTransformOutputParser(BaseTransformOutputParser[T]):
|
|
|
130
123
|
raise NotImplementedError
|
|
131
124
|
|
|
132
125
|
@override
|
|
133
|
-
def _transform(
|
|
134
|
-
self, input: Iterator[Union[str, BaseMessage, AIMessage]]
|
|
135
|
-
) -> Iterator[Any]:
|
|
126
|
+
def _transform(self, input: Iterator[str | BaseMessage]) -> Iterator[Any]:
|
|
136
127
|
prev_parsed = None
|
|
137
|
-
acc_gen:
|
|
138
|
-
None
|
|
139
|
-
)
|
|
128
|
+
acc_gen: GenerationChunk | ChatGenerationChunk | None = None
|
|
140
129
|
for chunk in input:
|
|
141
|
-
chunk_gen:
|
|
130
|
+
chunk_gen: GenerationChunk | ChatGenerationChunk
|
|
142
131
|
if isinstance(chunk, BaseMessageChunk):
|
|
143
132
|
chunk_gen = ChatGenerationChunk(message=chunk)
|
|
144
133
|
elif isinstance(chunk, BaseMessage):
|
|
145
134
|
chunk_gen = ChatGenerationChunk(
|
|
146
135
|
message=BaseMessageChunk(**chunk.model_dump())
|
|
147
136
|
)
|
|
148
|
-
elif isinstance(chunk, AIMessageChunk):
|
|
149
|
-
chunk_gen = chunk
|
|
150
|
-
elif isinstance(chunk, AIMessage):
|
|
151
|
-
chunk_gen = AIMessageChunk(
|
|
152
|
-
content=chunk.content,
|
|
153
|
-
id=chunk.id,
|
|
154
|
-
name=chunk.name,
|
|
155
|
-
lc_version=chunk.lc_version,
|
|
156
|
-
response_metadata=chunk.response_metadata,
|
|
157
|
-
usage_metadata=chunk.usage_metadata,
|
|
158
|
-
parsed=chunk.parsed,
|
|
159
|
-
)
|
|
160
137
|
else:
|
|
161
138
|
chunk_gen = GenerationChunk(text=chunk)
|
|
162
139
|
|
|
163
140
|
acc_gen = chunk_gen if acc_gen is None else acc_gen + chunk_gen # type: ignore[operator]
|
|
164
141
|
|
|
165
|
-
|
|
166
|
-
parsed = self.parse_result(acc_gen, partial=True)
|
|
167
|
-
else:
|
|
168
|
-
parsed = self.parse_result([acc_gen], partial=True)
|
|
142
|
+
parsed = self.parse_result([acc_gen], partial=True)
|
|
169
143
|
if parsed is not None and parsed != prev_parsed:
|
|
170
144
|
if self.diff:
|
|
171
145
|
yield self._diff(prev_parsed, parsed)
|
|
@@ -175,41 +149,24 @@ class BaseCumulativeTransformOutputParser(BaseTransformOutputParser[T]):
|
|
|
175
149
|
|
|
176
150
|
@override
|
|
177
151
|
async def _atransform(
|
|
178
|
-
self, input: AsyncIterator[
|
|
152
|
+
self, input: AsyncIterator[str | BaseMessage]
|
|
179
153
|
) -> AsyncIterator[T]:
|
|
180
154
|
prev_parsed = None
|
|
181
|
-
acc_gen:
|
|
182
|
-
None
|
|
183
|
-
)
|
|
155
|
+
acc_gen: GenerationChunk | ChatGenerationChunk | None = None
|
|
184
156
|
async for chunk in input:
|
|
185
|
-
chunk_gen:
|
|
157
|
+
chunk_gen: GenerationChunk | ChatGenerationChunk
|
|
186
158
|
if isinstance(chunk, BaseMessageChunk):
|
|
187
159
|
chunk_gen = ChatGenerationChunk(message=chunk)
|
|
188
160
|
elif isinstance(chunk, BaseMessage):
|
|
189
161
|
chunk_gen = ChatGenerationChunk(
|
|
190
162
|
message=BaseMessageChunk(**chunk.model_dump())
|
|
191
163
|
)
|
|
192
|
-
elif isinstance(chunk, AIMessageChunk):
|
|
193
|
-
chunk_gen = chunk
|
|
194
|
-
elif isinstance(chunk, AIMessage):
|
|
195
|
-
chunk_gen = AIMessageChunk(
|
|
196
|
-
content=chunk.content,
|
|
197
|
-
id=chunk.id,
|
|
198
|
-
name=chunk.name,
|
|
199
|
-
lc_version=chunk.lc_version,
|
|
200
|
-
response_metadata=chunk.response_metadata,
|
|
201
|
-
usage_metadata=chunk.usage_metadata,
|
|
202
|
-
parsed=chunk.parsed,
|
|
203
|
-
)
|
|
204
164
|
else:
|
|
205
165
|
chunk_gen = GenerationChunk(text=chunk)
|
|
206
166
|
|
|
207
167
|
acc_gen = chunk_gen if acc_gen is None else acc_gen + chunk_gen # type: ignore[operator]
|
|
208
168
|
|
|
209
|
-
|
|
210
|
-
parsed = await self.aparse_result(acc_gen, partial=True)
|
|
211
|
-
else:
|
|
212
|
-
parsed = await self.aparse_result([acc_gen], partial=True)
|
|
169
|
+
parsed = await self.aparse_result([acc_gen], partial=True)
|
|
213
170
|
if parsed is not None and parsed != prev_parsed:
|
|
214
171
|
if self.diff:
|
|
215
172
|
yield await run_in_executor(None, self._diff, prev_parsed, parsed)
|
|
@@ -5,17 +5,23 @@ import re
|
|
|
5
5
|
import xml
|
|
6
6
|
import xml.etree.ElementTree as ET
|
|
7
7
|
from collections.abc import AsyncIterator, Iterator
|
|
8
|
-
from typing import Any, Literal
|
|
8
|
+
from typing import Any, Literal
|
|
9
9
|
from xml.etree.ElementTree import TreeBuilder
|
|
10
10
|
|
|
11
11
|
from typing_extensions import override
|
|
12
12
|
|
|
13
13
|
from langchain_core.exceptions import OutputParserException
|
|
14
14
|
from langchain_core.messages import BaseMessage
|
|
15
|
-
from langchain_core.messages.utils import convert_from_v1_message
|
|
16
15
|
from langchain_core.output_parsers.transform import BaseTransformOutputParser
|
|
17
16
|
from langchain_core.runnables.utils import AddableDict
|
|
18
|
-
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
from defusedxml import ElementTree # type: ignore[import-untyped]
|
|
20
|
+
from defusedxml.ElementTree import XMLParser # type: ignore[import-untyped]
|
|
21
|
+
|
|
22
|
+
_HAS_DEFUSEDXML = True
|
|
23
|
+
except ImportError:
|
|
24
|
+
_HAS_DEFUSEDXML = False
|
|
19
25
|
|
|
20
26
|
XML_FORMAT_INSTRUCTIONS = """The output should be formatted as a XML file.
|
|
21
27
|
1. Output should conform to the tags below.
|
|
@@ -52,17 +58,13 @@ class _StreamingParser:
|
|
|
52
58
|
parser is requested.
|
|
53
59
|
"""
|
|
54
60
|
if parser == "defusedxml":
|
|
55
|
-
|
|
56
|
-
from defusedxml.ElementTree import ( # type: ignore[import-untyped]
|
|
57
|
-
XMLParser,
|
|
58
|
-
)
|
|
59
|
-
except ImportError as e:
|
|
61
|
+
if not _HAS_DEFUSEDXML:
|
|
60
62
|
msg = (
|
|
61
63
|
"defusedxml is not installed. "
|
|
62
64
|
"Please install it to use the defusedxml parser."
|
|
63
65
|
"You can install it with `pip install defusedxml` "
|
|
64
66
|
)
|
|
65
|
-
raise ImportError(msg)
|
|
67
|
+
raise ImportError(msg)
|
|
66
68
|
parser_ = XMLParser(target=TreeBuilder())
|
|
67
69
|
else:
|
|
68
70
|
parser_ = None
|
|
@@ -73,14 +75,14 @@ class _StreamingParser:
|
|
|
73
75
|
self.buffer = ""
|
|
74
76
|
self.xml_started = False
|
|
75
77
|
|
|
76
|
-
def parse(self, chunk:
|
|
78
|
+
def parse(self, chunk: str | BaseMessage) -> Iterator[AddableDict]:
|
|
77
79
|
"""Parse a chunk of text.
|
|
78
80
|
|
|
79
81
|
Args:
|
|
80
82
|
chunk: A chunk of text to parse. This can be a string or a BaseMessage.
|
|
81
83
|
|
|
82
84
|
Yields:
|
|
83
|
-
|
|
85
|
+
A dictionary representing the parsed XML element.
|
|
84
86
|
|
|
85
87
|
Raises:
|
|
86
88
|
xml.etree.ElementTree.ParseError: If the XML is not well-formed.
|
|
@@ -107,10 +109,11 @@ class _StreamingParser:
|
|
|
107
109
|
self.buffer = ""
|
|
108
110
|
# yield all events
|
|
109
111
|
try:
|
|
110
|
-
|
|
112
|
+
events = self.pull_parser.read_events()
|
|
113
|
+
for event, elem in events: # type: ignore[misc]
|
|
111
114
|
if event == "start":
|
|
112
115
|
# update current path
|
|
113
|
-
self.current_path.append(elem.tag)
|
|
116
|
+
self.current_path.append(elem.tag) # type: ignore[union-attr]
|
|
114
117
|
self.current_path_has_children = False
|
|
115
118
|
elif event == "end":
|
|
116
119
|
# remove last element from current path
|
|
@@ -118,7 +121,7 @@ class _StreamingParser:
|
|
|
118
121
|
self.current_path.pop()
|
|
119
122
|
# yield element
|
|
120
123
|
if not self.current_path_has_children:
|
|
121
|
-
yield nested_element(self.current_path, elem)
|
|
124
|
+
yield nested_element(self.current_path, elem) # type: ignore[arg-type]
|
|
122
125
|
# prevent yielding of parent element
|
|
123
126
|
if self.current_path:
|
|
124
127
|
self.current_path_has_children = True
|
|
@@ -137,9 +140,6 @@ class _StreamingParser:
|
|
|
137
140
|
"""Close the parser.
|
|
138
141
|
|
|
139
142
|
This should be called after all chunks have been parsed.
|
|
140
|
-
|
|
141
|
-
Raises:
|
|
142
|
-
xml.etree.ElementTree.ParseError: If the XML is not well-formed.
|
|
143
143
|
"""
|
|
144
144
|
# Ignore ParseError. This will ignore any incomplete XML at the end of the input
|
|
145
145
|
with contextlib.suppress(xml.etree.ElementTree.ParseError):
|
|
@@ -149,20 +149,21 @@ class _StreamingParser:
|
|
|
149
149
|
class XMLOutputParser(BaseTransformOutputParser):
|
|
150
150
|
"""Parse an output using xml format."""
|
|
151
151
|
|
|
152
|
-
tags:
|
|
152
|
+
tags: list[str] | None = None
|
|
153
153
|
"""Tags to tell the LLM to expect in the XML output.
|
|
154
154
|
|
|
155
155
|
Note this may not be perfect depending on the LLM implementation.
|
|
156
156
|
|
|
157
157
|
For example, with tags=["foo", "bar", "baz"]:
|
|
158
|
-
1. A well-formatted XML instance:
|
|
159
|
-
"<foo>\n <bar>\n <baz></baz>\n </bar>\n</foo>"
|
|
160
158
|
|
|
161
|
-
|
|
162
|
-
|
|
159
|
+
1. A well-formatted XML instance:
|
|
160
|
+
"<foo>\n <bar>\n <baz></baz>\n </bar>\n</foo>"
|
|
161
|
+
|
|
162
|
+
2. A badly-formatted XML instance (missing closing tag for 'bar'):
|
|
163
|
+
"<foo>\n <bar>\n </foo>"
|
|
163
164
|
|
|
164
|
-
|
|
165
|
-
|
|
165
|
+
3. A badly-formatted XML instance (unexpected 'tag' element):
|
|
166
|
+
"<foo>\n <tag>\n </tag>\n</foo>"
|
|
166
167
|
"""
|
|
167
168
|
encoding_matcher: re.Pattern = re.compile(
|
|
168
169
|
r"<([^>]*encoding[^>]*)>\n(.*)", re.MULTILINE | re.DOTALL
|
|
@@ -192,7 +193,7 @@ class XMLOutputParser(BaseTransformOutputParser):
|
|
|
192
193
|
"""Return the format instructions for the XML output."""
|
|
193
194
|
return XML_FORMAT_INSTRUCTIONS.format(tags=self.tags)
|
|
194
195
|
|
|
195
|
-
def parse(self, text: str) -> dict[str,
|
|
196
|
+
def parse(self, text: str) -> dict[str, str | list[Any]]:
|
|
196
197
|
"""Parse the output of an LLM call.
|
|
197
198
|
|
|
198
199
|
Args:
|
|
@@ -210,16 +211,14 @@ class XMLOutputParser(BaseTransformOutputParser):
|
|
|
210
211
|
# Imports are temporarily placed here to avoid issue with caching on CI
|
|
211
212
|
# likely if you're reading this you can move them to the top of the file
|
|
212
213
|
if self.parser == "defusedxml":
|
|
213
|
-
|
|
214
|
-
from defusedxml import ElementTree # type: ignore[import-untyped]
|
|
215
|
-
except ImportError as e:
|
|
214
|
+
if not _HAS_DEFUSEDXML:
|
|
216
215
|
msg = (
|
|
217
216
|
"defusedxml is not installed. "
|
|
218
217
|
"Please install it to use the defusedxml parser."
|
|
219
218
|
"You can install it with `pip install defusedxml`"
|
|
220
219
|
"See https://github.com/tiran/defusedxml for more details"
|
|
221
220
|
)
|
|
222
|
-
raise ImportError(msg)
|
|
221
|
+
raise ImportError(msg)
|
|
223
222
|
et = ElementTree # Use the defusedxml parser
|
|
224
223
|
else:
|
|
225
224
|
et = ET # Use the standard library parser
|
|
@@ -241,32 +240,23 @@ class XMLOutputParser(BaseTransformOutputParser):
|
|
|
241
240
|
raise OutputParserException(msg, llm_output=text) from e
|
|
242
241
|
|
|
243
242
|
@override
|
|
244
|
-
def _transform(
|
|
245
|
-
self, input: Iterator[Union[str, BaseMessage, AIMessage]]
|
|
246
|
-
) -> Iterator[AddableDict]:
|
|
243
|
+
def _transform(self, input: Iterator[str | BaseMessage]) -> Iterator[AddableDict]:
|
|
247
244
|
streaming_parser = _StreamingParser(self.parser)
|
|
248
245
|
for chunk in input:
|
|
249
|
-
|
|
250
|
-
yield from streaming_parser.parse(convert_from_v1_message(chunk))
|
|
251
|
-
else:
|
|
252
|
-
yield from streaming_parser.parse(chunk)
|
|
246
|
+
yield from streaming_parser.parse(chunk)
|
|
253
247
|
streaming_parser.close()
|
|
254
248
|
|
|
255
249
|
@override
|
|
256
250
|
async def _atransform(
|
|
257
|
-
self, input: AsyncIterator[
|
|
251
|
+
self, input: AsyncIterator[str | BaseMessage]
|
|
258
252
|
) -> AsyncIterator[AddableDict]:
|
|
259
253
|
streaming_parser = _StreamingParser(self.parser)
|
|
260
254
|
async for chunk in input:
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
yield output
|
|
264
|
-
else:
|
|
265
|
-
for output in streaming_parser.parse(chunk):
|
|
266
|
-
yield output
|
|
255
|
+
for output in streaming_parser.parse(chunk):
|
|
256
|
+
yield output
|
|
267
257
|
streaming_parser.close()
|
|
268
258
|
|
|
269
|
-
def _root_to_dict(self, root: ET.Element) -> dict[str,
|
|
259
|
+
def _root_to_dict(self, root: ET.Element) -> dict[str, str | list[Any]]:
|
|
270
260
|
"""Converts xml tree to python dictionary."""
|
|
271
261
|
if root.text and bool(re.search(r"\S", root.text)):
|
|
272
262
|
# If root text contains any non-whitespace character it
|
|
@@ -12,7 +12,7 @@ When invoking models via the standard runnable methods (e.g. invoke, batch, etc.
|
|
|
12
12
|
- LLMs will return regular text strings.
|
|
13
13
|
|
|
14
14
|
In addition, users can access the raw output of either LLMs or chat models via
|
|
15
|
-
callbacks. The
|
|
15
|
+
callbacks. The `on_chat_model_end` and `on_llm_end` callbacks will return an
|
|
16
16
|
LLMResult object containing the generated outputs and any additional information
|
|
17
17
|
returned by the model provider.
|
|
18
18
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from typing import Literal
|
|
5
|
+
from typing import Literal
|
|
6
6
|
|
|
7
7
|
from pydantic import model_validator
|
|
8
8
|
from typing_extensions import Self
|
|
@@ -15,7 +15,7 @@ from langchain_core.utils._merge import merge_dicts
|
|
|
15
15
|
class ChatGeneration(Generation):
|
|
16
16
|
"""A single chat generation output.
|
|
17
17
|
|
|
18
|
-
A subclass of Generation that represents the response from a chat model
|
|
18
|
+
A subclass of `Generation` that represents the response from a chat model
|
|
19
19
|
that generates chat messages.
|
|
20
20
|
|
|
21
21
|
The `message` attribute is a structured representation of the chat message.
|
|
@@ -29,8 +29,9 @@ class ChatGeneration(Generation):
|
|
|
29
29
|
text: str = ""
|
|
30
30
|
"""The text contents of the output message.
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
!!! warning
|
|
33
33
|
SHOULD NOT BE SET DIRECTLY!
|
|
34
|
+
|
|
34
35
|
"""
|
|
35
36
|
message: BaseMessage
|
|
36
37
|
"""The message output by the chat model."""
|
|
@@ -69,9 +70,9 @@ class ChatGeneration(Generation):
|
|
|
69
70
|
|
|
70
71
|
|
|
71
72
|
class ChatGenerationChunk(ChatGeneration):
|
|
72
|
-
"""ChatGeneration chunk.
|
|
73
|
+
"""`ChatGeneration` chunk.
|
|
73
74
|
|
|
74
|
-
ChatGeneration chunks can be concatenated with other ChatGeneration chunks.
|
|
75
|
+
`ChatGeneration` chunks can be concatenated with other `ChatGeneration` chunks.
|
|
75
76
|
"""
|
|
76
77
|
|
|
77
78
|
message: BaseMessageChunk
|
|
@@ -81,13 +82,20 @@ class ChatGenerationChunk(ChatGeneration):
|
|
|
81
82
|
"""Type is used exclusively for serialization purposes."""
|
|
82
83
|
|
|
83
84
|
def __add__(
|
|
84
|
-
self, other:
|
|
85
|
+
self, other: ChatGenerationChunk | list[ChatGenerationChunk]
|
|
85
86
|
) -> ChatGenerationChunk:
|
|
86
|
-
"""Concatenate two
|
|
87
|
+
"""Concatenate two `ChatGenerationChunk`s.
|
|
87
88
|
|
|
88
89
|
Args:
|
|
89
|
-
other: The other ChatGenerationChunk or list of
|
|
90
|
-
concatenate.
|
|
90
|
+
other: The other `ChatGenerationChunk` or list of `ChatGenerationChunk`
|
|
91
|
+
to concatenate.
|
|
92
|
+
|
|
93
|
+
Raises:
|
|
94
|
+
TypeError: If other is not a `ChatGenerationChunk` or list of
|
|
95
|
+
`ChatGenerationChunk`.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
A new `ChatGenerationChunk` concatenated from self and other.
|
|
91
99
|
"""
|
|
92
100
|
if isinstance(other, ChatGenerationChunk):
|
|
93
101
|
generation_info = merge_dicts(
|
|
@@ -115,8 +123,15 @@ class ChatGenerationChunk(ChatGeneration):
|
|
|
115
123
|
|
|
116
124
|
def merge_chat_generation_chunks(
|
|
117
125
|
chunks: list[ChatGenerationChunk],
|
|
118
|
-
) ->
|
|
119
|
-
"""Merge a list of
|
|
126
|
+
) -> ChatGenerationChunk | None:
|
|
127
|
+
"""Merge a list of `ChatGenerationChunk`s into a single `ChatGenerationChunk`.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
chunks: A list of `ChatGenerationChunk` to merge.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
A merged `ChatGenerationChunk`, or None if the input list is empty.
|
|
134
|
+
"""
|
|
120
135
|
if not chunks:
|
|
121
136
|
return None
|
|
122
137
|
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"""Chat result schema."""
|
|
2
2
|
|
|
3
|
-
from typing import Optional
|
|
4
|
-
|
|
5
3
|
from pydantic import BaseModel
|
|
6
4
|
|
|
7
5
|
from langchain_core.outputs.chat_generation import ChatGeneration
|
|
@@ -26,7 +24,7 @@ class ChatResult(BaseModel):
|
|
|
26
24
|
Generations is a list to allow for multiple candidate generations for a single
|
|
27
25
|
input prompt.
|
|
28
26
|
"""
|
|
29
|
-
llm_output:
|
|
27
|
+
llm_output: dict | None = None
|
|
30
28
|
"""For arbitrary LLM provider specific output.
|
|
31
29
|
|
|
32
30
|
This dictionary is a free-form dictionary that can contain any information that the
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from typing import Any, Literal
|
|
5
|
+
from typing import Any, Literal
|
|
6
6
|
|
|
7
7
|
from langchain_core.load import Serializable
|
|
8
8
|
from langchain_core.utils._merge import merge_dicts
|
|
@@ -28,7 +28,7 @@ class Generation(Serializable):
|
|
|
28
28
|
text: str
|
|
29
29
|
"""Generated text output."""
|
|
30
30
|
|
|
31
|
-
generation_info:
|
|
31
|
+
generation_info: dict[str, Any] | None = None
|
|
32
32
|
"""Raw response from the provider.
|
|
33
33
|
|
|
34
34
|
May include things like the reason for finishing or token log probabilities.
|
|
@@ -39,14 +39,15 @@ class Generation(Serializable):
|
|
|
39
39
|
|
|
40
40
|
@classmethod
|
|
41
41
|
def is_lc_serializable(cls) -> bool:
|
|
42
|
-
"""Return
|
|
42
|
+
"""Return True as this class is serializable."""
|
|
43
43
|
return True
|
|
44
44
|
|
|
45
45
|
@classmethod
|
|
46
46
|
def get_lc_namespace(cls) -> list[str]:
|
|
47
|
-
"""Get the namespace of the
|
|
47
|
+
"""Get the namespace of the LangChain object.
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
Returns:
|
|
50
|
+
`["langchain", "schema", "output"]`
|
|
50
51
|
"""
|
|
51
52
|
return ["langchain", "schema", "output"]
|
|
52
53
|
|
|
@@ -55,7 +56,17 @@ class GenerationChunk(Generation):
|
|
|
55
56
|
"""Generation chunk, which can be concatenated with other Generation chunks."""
|
|
56
57
|
|
|
57
58
|
def __add__(self, other: GenerationChunk) -> GenerationChunk:
|
|
58
|
-
"""Concatenate two
|
|
59
|
+
"""Concatenate two `GenerationChunk`s.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
other: Another `GenerationChunk` to concatenate with.
|
|
63
|
+
|
|
64
|
+
Raises:
|
|
65
|
+
TypeError: If other is not a `GenerationChunk`.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
A new `GenerationChunk` concatenated from self and other.
|
|
69
|
+
"""
|
|
59
70
|
if isinstance(other, GenerationChunk):
|
|
60
71
|
generation_info = merge_dicts(
|
|
61
72
|
self.generation_info or {},
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from copy import deepcopy
|
|
6
|
-
from typing import Literal
|
|
6
|
+
from typing import Literal
|
|
7
7
|
|
|
8
8
|
from pydantic import BaseModel
|
|
9
9
|
|
|
@@ -21,7 +21,7 @@ class LLMResult(BaseModel):
|
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
23
|
generations: list[
|
|
24
|
-
list[
|
|
24
|
+
list[Generation | ChatGeneration | GenerationChunk | ChatGenerationChunk]
|
|
25
25
|
]
|
|
26
26
|
"""Generated outputs.
|
|
27
27
|
|
|
@@ -30,13 +30,13 @@ class LLMResult(BaseModel):
|
|
|
30
30
|
The second dimension of the list represents different candidate generations for a
|
|
31
31
|
given prompt.
|
|
32
32
|
|
|
33
|
-
- When returned from **an LLM**, the type is
|
|
34
|
-
- When returned from a **chat model**, the type is
|
|
33
|
+
- When returned from **an LLM**, the type is `list[list[Generation]]`.
|
|
34
|
+
- When returned from a **chat model**, the type is `list[list[ChatGeneration]]`.
|
|
35
35
|
|
|
36
36
|
ChatGeneration is a subclass of Generation that has a field for a structured chat
|
|
37
37
|
message.
|
|
38
38
|
"""
|
|
39
|
-
llm_output:
|
|
39
|
+
llm_output: dict | None = None
|
|
40
40
|
"""For arbitrary LLM provider specific output.
|
|
41
41
|
|
|
42
42
|
This dictionary is a free-form dictionary that can contain any information that the
|
|
@@ -45,10 +45,10 @@ class LLMResult(BaseModel):
|
|
|
45
45
|
Users should generally avoid relying on this field and instead rely on accessing
|
|
46
46
|
relevant information from standardized fields present in AIMessage.
|
|
47
47
|
"""
|
|
48
|
-
run:
|
|
48
|
+
run: list[RunInfo] | None = None
|
|
49
49
|
"""List of metadata info for model call for each input.
|
|
50
50
|
|
|
51
|
-
See
|
|
51
|
+
See `langchain_core.outputs.run_info.RunInfo` for details.
|
|
52
52
|
"""
|
|
53
53
|
|
|
54
54
|
type: Literal["LLMResult"] = "LLMResult"
|
|
@@ -91,7 +91,14 @@ class LLMResult(BaseModel):
|
|
|
91
91
|
return llm_results
|
|
92
92
|
|
|
93
93
|
def __eq__(self, other: object) -> bool:
|
|
94
|
-
"""Check for LLMResult equality by ignoring any metadata related to runs.
|
|
94
|
+
"""Check for `LLMResult` equality by ignoring any metadata related to runs.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
other: Another `LLMResult` object to compare against.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
`True` if the generations and `llm_output` are equal, `False` otherwise.
|
|
101
|
+
"""
|
|
95
102
|
if not isinstance(other, LLMResult):
|
|
96
103
|
return NotImplemented
|
|
97
104
|
return (
|