langchain-core 0.3.79__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 +23 -26
- langchain_core/_api/deprecation.py +52 -65
- langchain_core/_api/path.py +3 -6
- langchain_core/_import_utils.py +3 -4
- langchain_core/agents.py +19 -19
- langchain_core/caches.py +53 -63
- langchain_core/callbacks/__init__.py +1 -8
- langchain_core/callbacks/base.py +323 -334
- langchain_core/callbacks/file.py +44 -44
- langchain_core/callbacks/manager.py +441 -507
- langchain_core/callbacks/stdout.py +29 -30
- langchain_core/callbacks/streaming_stdout.py +32 -32
- langchain_core/callbacks/usage.py +60 -57
- langchain_core/chat_history.py +48 -63
- langchain_core/document_loaders/base.py +23 -23
- langchain_core/document_loaders/langsmith.py +37 -37
- langchain_core/documents/__init__.py +0 -1
- langchain_core/documents/base.py +62 -65
- langchain_core/documents/compressor.py +4 -4
- langchain_core/documents/transformers.py +28 -29
- langchain_core/embeddings/fake.py +50 -54
- langchain_core/example_selectors/length_based.py +1 -1
- langchain_core/example_selectors/semantic_similarity.py +21 -25
- langchain_core/exceptions.py +10 -11
- langchain_core/globals.py +3 -151
- langchain_core/indexing/api.py +61 -66
- langchain_core/indexing/base.py +58 -58
- langchain_core/indexing/in_memory.py +3 -3
- langchain_core/language_models/__init__.py +14 -27
- langchain_core/language_models/_utils.py +270 -84
- langchain_core/language_models/base.py +55 -162
- langchain_core/language_models/chat_models.py +442 -402
- langchain_core/language_models/fake.py +11 -11
- langchain_core/language_models/fake_chat_models.py +61 -39
- langchain_core/language_models/llms.py +123 -231
- langchain_core/load/dump.py +4 -5
- langchain_core/load/load.py +18 -28
- langchain_core/load/mapping.py +2 -4
- langchain_core/load/serializable.py +39 -40
- langchain_core/messages/__init__.py +61 -22
- langchain_core/messages/ai.py +368 -163
- langchain_core/messages/base.py +214 -43
- 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 -6
- langchain_core/messages/content.py +1423 -0
- langchain_core/messages/function.py +6 -10
- langchain_core/messages/human.py +41 -38
- langchain_core/messages/modifier.py +2 -2
- langchain_core/messages/system.py +38 -28
- langchain_core/messages/tool.py +96 -103
- langchain_core/messages/utils.py +478 -504
- langchain_core/output_parsers/__init__.py +1 -14
- langchain_core/output_parsers/base.py +58 -61
- langchain_core/output_parsers/json.py +7 -8
- langchain_core/output_parsers/list.py +5 -7
- langchain_core/output_parsers/openai_functions.py +49 -47
- langchain_core/output_parsers/openai_tools.py +14 -19
- langchain_core/output_parsers/pydantic.py +12 -13
- langchain_core/output_parsers/string.py +2 -2
- langchain_core/output_parsers/transform.py +15 -17
- langchain_core/output_parsers/xml.py +8 -10
- langchain_core/outputs/__init__.py +1 -1
- langchain_core/outputs/chat_generation.py +18 -18
- langchain_core/outputs/chat_result.py +1 -3
- langchain_core/outputs/generation.py +8 -8
- langchain_core/outputs/llm_result.py +10 -10
- langchain_core/prompt_values.py +12 -12
- langchain_core/prompts/__init__.py +3 -27
- langchain_core/prompts/base.py +45 -55
- langchain_core/prompts/chat.py +254 -313
- langchain_core/prompts/dict.py +5 -5
- langchain_core/prompts/few_shot.py +81 -88
- langchain_core/prompts/few_shot_with_templates.py +11 -13
- langchain_core/prompts/image.py +12 -14
- langchain_core/prompts/loading.py +6 -8
- langchain_core/prompts/message.py +3 -3
- langchain_core/prompts/prompt.py +24 -39
- langchain_core/prompts/string.py +4 -4
- langchain_core/prompts/structured.py +42 -50
- langchain_core/rate_limiters.py +51 -60
- langchain_core/retrievers.py +49 -190
- langchain_core/runnables/base.py +1484 -1709
- langchain_core/runnables/branch.py +45 -61
- langchain_core/runnables/config.py +80 -88
- langchain_core/runnables/configurable.py +117 -134
- langchain_core/runnables/fallbacks.py +83 -79
- langchain_core/runnables/graph.py +85 -95
- langchain_core/runnables/graph_ascii.py +27 -28
- langchain_core/runnables/graph_mermaid.py +38 -50
- langchain_core/runnables/graph_png.py +15 -16
- langchain_core/runnables/history.py +135 -148
- langchain_core/runnables/passthrough.py +124 -150
- langchain_core/runnables/retry.py +46 -51
- langchain_core/runnables/router.py +25 -30
- langchain_core/runnables/schema.py +79 -74
- langchain_core/runnables/utils.py +62 -68
- langchain_core/stores.py +81 -115
- langchain_core/structured_query.py +8 -8
- langchain_core/sys_info.py +27 -29
- langchain_core/tools/__init__.py +1 -14
- langchain_core/tools/base.py +179 -187
- langchain_core/tools/convert.py +131 -139
- langchain_core/tools/render.py +10 -10
- langchain_core/tools/retriever.py +11 -11
- langchain_core/tools/simple.py +19 -24
- langchain_core/tools/structured.py +30 -39
- langchain_core/tracers/__init__.py +1 -9
- langchain_core/tracers/base.py +97 -99
- langchain_core/tracers/context.py +29 -52
- langchain_core/tracers/core.py +50 -60
- langchain_core/tracers/evaluation.py +11 -11
- langchain_core/tracers/event_stream.py +115 -70
- langchain_core/tracers/langchain.py +21 -21
- langchain_core/tracers/log_stream.py +43 -43
- langchain_core/tracers/memory_stream.py +3 -3
- langchain_core/tracers/root_listeners.py +16 -16
- langchain_core/tracers/run_collector.py +2 -4
- langchain_core/tracers/schemas.py +0 -129
- langchain_core/tracers/stdout.py +3 -3
- langchain_core/utils/__init__.py +1 -4
- langchain_core/utils/_merge.py +46 -8
- langchain_core/utils/aiter.py +57 -61
- langchain_core/utils/env.py +9 -9
- langchain_core/utils/function_calling.py +89 -191
- langchain_core/utils/html.py +7 -8
- langchain_core/utils/input.py +6 -6
- langchain_core/utils/interactive_env.py +1 -1
- langchain_core/utils/iter.py +37 -42
- langchain_core/utils/json.py +4 -3
- langchain_core/utils/json_schema.py +8 -8
- langchain_core/utils/mustache.py +9 -11
- langchain_core/utils/pydantic.py +33 -35
- langchain_core/utils/strings.py +5 -5
- langchain_core/utils/usage.py +1 -1
- langchain_core/utils/utils.py +80 -54
- langchain_core/vectorstores/base.py +129 -164
- langchain_core/vectorstores/in_memory.py +99 -174
- langchain_core/vectorstores/utils.py +5 -5
- langchain_core/version.py +1 -1
- {langchain_core-0.3.79.dist-info → langchain_core-1.0.0.dist-info}/METADATA +28 -27
- langchain_core-1.0.0.dist-info/RECORD +172 -0
- {langchain_core-0.3.79.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 -447
- langchain_core/memory.py +0 -120
- langchain_core/messages/content_blocks.py +0 -176
- langchain_core/prompts/pipeline.py +0 -138
- 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 -31
- langchain_core/utils/loading.py +0 -35
- langchain_core-0.3.79.dist-info/RECORD +0 -174
- langchain_core-0.3.79.dist-info/entry_points.txt +0 -4
|
@@ -6,28 +6,28 @@ import asyncio
|
|
|
6
6
|
import inspect
|
|
7
7
|
import json
|
|
8
8
|
import typing
|
|
9
|
-
import warnings
|
|
10
9
|
from abc import ABC, abstractmethod
|
|
11
|
-
from collections.abc import AsyncIterator, Iterator, Sequence
|
|
10
|
+
from collections.abc import AsyncIterator, Callable, Iterator, Sequence
|
|
12
11
|
from functools import cached_property
|
|
13
12
|
from operator import itemgetter
|
|
14
|
-
from typing import TYPE_CHECKING, Any,
|
|
13
|
+
from typing import TYPE_CHECKING, Any, Literal, cast
|
|
15
14
|
|
|
16
|
-
from pydantic import BaseModel, ConfigDict, Field
|
|
15
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
17
16
|
from typing_extensions import override
|
|
18
17
|
|
|
19
|
-
from langchain_core._api import deprecated
|
|
20
18
|
from langchain_core.caches import BaseCache
|
|
21
19
|
from langchain_core.callbacks import (
|
|
22
20
|
AsyncCallbackManager,
|
|
23
21
|
AsyncCallbackManagerForLLMRun,
|
|
24
|
-
BaseCallbackManager,
|
|
25
22
|
CallbackManager,
|
|
26
23
|
CallbackManagerForLLMRun,
|
|
27
24
|
Callbacks,
|
|
28
25
|
)
|
|
29
26
|
from langchain_core.globals import get_llm_cache
|
|
30
|
-
from langchain_core.language_models._utils import
|
|
27
|
+
from langchain_core.language_models._utils import (
|
|
28
|
+
_normalize_messages,
|
|
29
|
+
_update_message_content_to_blocks,
|
|
30
|
+
)
|
|
31
31
|
from langchain_core.language_models.base import (
|
|
32
32
|
BaseLanguageModel,
|
|
33
33
|
LangSmithParams,
|
|
@@ -36,16 +36,17 @@ from langchain_core.language_models.base import (
|
|
|
36
36
|
from langchain_core.load import dumpd, dumps
|
|
37
37
|
from langchain_core.messages import (
|
|
38
38
|
AIMessage,
|
|
39
|
+
AIMessageChunk,
|
|
39
40
|
AnyMessage,
|
|
40
41
|
BaseMessage,
|
|
41
|
-
BaseMessageChunk,
|
|
42
|
-
HumanMessage,
|
|
43
42
|
convert_to_messages,
|
|
44
|
-
convert_to_openai_image_block,
|
|
45
43
|
is_data_content_block,
|
|
46
44
|
message_chunk_to_message,
|
|
47
45
|
)
|
|
48
|
-
from langchain_core.messages
|
|
46
|
+
from langchain_core.messages import content as types
|
|
47
|
+
from langchain_core.messages.block_translators.openai import (
|
|
48
|
+
convert_to_openai_image_block,
|
|
49
|
+
)
|
|
49
50
|
from langchain_core.output_parsers.openai_tools import (
|
|
50
51
|
JsonOutputKeyToolsParser,
|
|
51
52
|
PydanticToolsParser,
|
|
@@ -69,6 +70,7 @@ from langchain_core.utils.function_calling import (
|
|
|
69
70
|
convert_to_openai_tool,
|
|
70
71
|
)
|
|
71
72
|
from langchain_core.utils.pydantic import TypeBaseModel, is_basemodel_subclass
|
|
73
|
+
from langchain_core.utils.utils import LC_ID_PREFIX, from_env
|
|
72
74
|
|
|
73
75
|
if TYPE_CHECKING:
|
|
74
76
|
import uuid
|
|
@@ -106,11 +108,11 @@ def _generate_response_from_error(error: BaseException) -> list[ChatGeneration]:
|
|
|
106
108
|
|
|
107
109
|
|
|
108
110
|
def _format_for_tracing(messages: list[BaseMessage]) -> list[BaseMessage]:
|
|
109
|
-
"""Format messages for tracing in
|
|
111
|
+
"""Format messages for tracing in `on_chat_model_start`.
|
|
110
112
|
|
|
111
113
|
- Update image content blocks to OpenAI Chat Completions format (backward
|
|
112
114
|
compatibility).
|
|
113
|
-
- Add
|
|
115
|
+
- Add `type` key to content blocks that have a single key.
|
|
114
116
|
|
|
115
117
|
Args:
|
|
116
118
|
messages: List of messages to format.
|
|
@@ -129,7 +131,7 @@ def _format_for_tracing(messages: list[BaseMessage]) -> list[BaseMessage]:
|
|
|
129
131
|
if (
|
|
130
132
|
block.get("type") == "image"
|
|
131
133
|
and is_data_content_block(block)
|
|
132
|
-
and block.get("source_type")
|
|
134
|
+
and not ("file_id" in block or block.get("source_type") == "id")
|
|
133
135
|
):
|
|
134
136
|
if message_to_trace is message:
|
|
135
137
|
# Shallow copy
|
|
@@ -139,6 +141,22 @@ def _format_for_tracing(messages: list[BaseMessage]) -> list[BaseMessage]:
|
|
|
139
141
|
message_to_trace.content[idx] = ( # type: ignore[index] # mypy confused by .model_copy
|
|
140
142
|
convert_to_openai_image_block(block)
|
|
141
143
|
)
|
|
144
|
+
elif (
|
|
145
|
+
block.get("type") == "file"
|
|
146
|
+
and is_data_content_block(block) # v0 (image/audio/file) or v1
|
|
147
|
+
and "base64" in block
|
|
148
|
+
# Backward compat: convert v1 base64 blocks to v0
|
|
149
|
+
):
|
|
150
|
+
if message_to_trace is message:
|
|
151
|
+
# Shallow copy
|
|
152
|
+
message_to_trace = message.model_copy()
|
|
153
|
+
message_to_trace.content = list(message_to_trace.content)
|
|
154
|
+
|
|
155
|
+
message_to_trace.content[idx] = { # type: ignore[index]
|
|
156
|
+
**{k: v for k, v in block.items() if k != "base64"},
|
|
157
|
+
"data": block["base64"],
|
|
158
|
+
"source_type": "base64",
|
|
159
|
+
}
|
|
142
160
|
elif len(block) == 1 and "type" not in block:
|
|
143
161
|
# Tracing assumes all content blocks have a "type" key. Here
|
|
144
162
|
# we add this key if it is missing, and there's an obvious
|
|
@@ -161,13 +179,13 @@ def generate_from_stream(stream: Iterator[ChatGenerationChunk]) -> ChatResult:
|
|
|
161
179
|
"""Generate from a stream.
|
|
162
180
|
|
|
163
181
|
Args:
|
|
164
|
-
stream: Iterator of
|
|
182
|
+
stream: Iterator of `ChatGenerationChunk`.
|
|
165
183
|
|
|
166
184
|
Raises:
|
|
167
185
|
ValueError: If no generations are found in the stream.
|
|
168
186
|
|
|
169
187
|
Returns:
|
|
170
|
-
|
|
188
|
+
Chat result.
|
|
171
189
|
|
|
172
190
|
"""
|
|
173
191
|
generation = next(stream, None)
|
|
@@ -192,17 +210,17 @@ async def agenerate_from_stream(
|
|
|
192
210
|
"""Async generate from a stream.
|
|
193
211
|
|
|
194
212
|
Args:
|
|
195
|
-
stream: Iterator of
|
|
213
|
+
stream: Iterator of `ChatGenerationChunk`.
|
|
196
214
|
|
|
197
215
|
Returns:
|
|
198
|
-
|
|
216
|
+
Chat result.
|
|
199
217
|
|
|
200
218
|
"""
|
|
201
219
|
chunks = [chunk async for chunk in stream]
|
|
202
220
|
return await run_in_executor(None, generate_from_stream, iter(chunks))
|
|
203
221
|
|
|
204
222
|
|
|
205
|
-
def _format_ls_structured_output(ls_structured_output_format:
|
|
223
|
+
def _format_ls_structured_output(ls_structured_output_format: dict | None) -> dict:
|
|
206
224
|
if ls_structured_output_format:
|
|
207
225
|
try:
|
|
208
226
|
ls_structured_output_format_dict = {
|
|
@@ -221,135 +239,99 @@ def _format_ls_structured_output(ls_structured_output_format: Optional[dict]) ->
|
|
|
221
239
|
return ls_structured_output_format_dict
|
|
222
240
|
|
|
223
241
|
|
|
224
|
-
class BaseChatModel(BaseLanguageModel[
|
|
225
|
-
"""Base class for chat models.
|
|
242
|
+
class BaseChatModel(BaseLanguageModel[AIMessage], ABC):
|
|
243
|
+
r"""Base class for chat models.
|
|
226
244
|
|
|
227
245
|
Key imperative methods:
|
|
228
246
|
Methods that actually call the underlying model.
|
|
229
247
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
|
234
|
-
|
|
235
|
-
| `ainvoke`
|
|
236
|
-
|
|
237
|
-
| `
|
|
238
|
-
|
|
239
|
-
| `
|
|
240
|
-
|
|
241
|
-
| `
|
|
242
|
-
|
|
243
|
-
| `batch` | list['''] | list[BaseMessage] | Defaults to running invoke in concurrent threads. |
|
|
244
|
-
+---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
|
|
245
|
-
| `abatch` | list['''] | list[BaseMessage] | Defaults to running ainvoke in concurrent threads. |
|
|
246
|
-
+---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
|
|
247
|
-
| `batch_as_completed` | list['''] | Iterator[tuple[int, Union[BaseMessage, Exception]]] | Defaults to running invoke in concurrent threads. |
|
|
248
|
-
+---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
|
|
249
|
-
| `abatch_as_completed` | list['''] | AsyncIterator[tuple[int, Union[BaseMessage, Exception]]] | Defaults to running ainvoke in concurrent threads. |
|
|
250
|
-
+---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
|
|
251
|
-
|
|
252
|
-
This table provides a brief overview of the main imperative methods. Please see the base Runnable reference for full documentation.
|
|
248
|
+
This table provides a brief overview of the main imperative methods. Please see the base `Runnable` reference for full documentation.
|
|
249
|
+
|
|
250
|
+
| Method | Input | Output | Description |
|
|
251
|
+
| ---------------------- | ------------------------------------------------------------ | ---------------------------------------------------------- | -------------------------------------------------------------------------------- |
|
|
252
|
+
| `invoke` | `str` \| `list[dict | tuple | BaseMessage]` \| `PromptValue` | `BaseMessage` | A single chat model call. |
|
|
253
|
+
| `ainvoke` | `'''` | `BaseMessage` | Defaults to running `invoke` in an async executor. |
|
|
254
|
+
| `stream` | `'''` | `Iterator[BaseMessageChunk]` | Defaults to yielding output of `invoke`. |
|
|
255
|
+
| `astream` | `'''` | `AsyncIterator[BaseMessageChunk]` | Defaults to yielding output of `ainvoke`. |
|
|
256
|
+
| `astream_events` | `'''` | `AsyncIterator[StreamEvent]` | Event types: `on_chat_model_start`, `on_chat_model_stream`, `on_chat_model_end`. |
|
|
257
|
+
| `batch` | `list[''']` | `list[BaseMessage]` | Defaults to running `invoke` in concurrent threads. |
|
|
258
|
+
| `abatch` | `list[''']` | `list[BaseMessage]` | Defaults to running `ainvoke` in concurrent threads. |
|
|
259
|
+
| `batch_as_completed` | `list[''']` | `Iterator[tuple[int, Union[BaseMessage, Exception]]]` | Defaults to running `invoke` in concurrent threads. |
|
|
260
|
+
| `abatch_as_completed` | `list[''']` | `AsyncIterator[tuple[int, Union[BaseMessage, Exception]]]` | Defaults to running `ainvoke` in concurrent threads. |
|
|
253
261
|
|
|
254
262
|
Key declarative methods:
|
|
255
|
-
Methods for creating another Runnable using the
|
|
256
|
-
|
|
257
|
-
+----------------------------------+-----------------------------------------------------------------------------------------------------------+
|
|
258
|
-
| Method | Description |
|
|
259
|
-
+==================================+===========================================================================================================+
|
|
260
|
-
| `bind_tools` | Create ChatModel that can call tools. |
|
|
261
|
-
+----------------------------------+-----------------------------------------------------------------------------------------------------------+
|
|
262
|
-
| `with_structured_output` | Create wrapper that structures model output using schema. |
|
|
263
|
-
+----------------------------------+-----------------------------------------------------------------------------------------------------------+
|
|
264
|
-
| `with_retry` | Create wrapper that retries model calls on failure. |
|
|
265
|
-
+----------------------------------+-----------------------------------------------------------------------------------------------------------+
|
|
266
|
-
| `with_fallbacks` | Create wrapper that falls back to other models on failure. |
|
|
267
|
-
+----------------------------------+-----------------------------------------------------------------------------------------------------------+
|
|
268
|
-
| `configurable_fields` | Specify init args of the model that can be configured at runtime via the RunnableConfig. |
|
|
269
|
-
+----------------------------------+-----------------------------------------------------------------------------------------------------------+
|
|
270
|
-
| `configurable_alternatives` | Specify alternative models which can be swapped in at runtime via the RunnableConfig. |
|
|
271
|
-
+----------------------------------+-----------------------------------------------------------------------------------------------------------+
|
|
263
|
+
Methods for creating another `Runnable` using the chat model.
|
|
272
264
|
|
|
273
265
|
This table provides a brief overview of the main declarative methods. Please see the reference for each method for full documentation.
|
|
274
266
|
|
|
267
|
+
| Method | Description |
|
|
268
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------ |
|
|
269
|
+
| `bind_tools` | Create chat model that can call tools. |
|
|
270
|
+
| `with_structured_output` | Create wrapper that structures model output using schema. |
|
|
271
|
+
| `with_retry` | Create wrapper that retries model calls on failure. |
|
|
272
|
+
| `with_fallbacks` | Create wrapper that falls back to other models on failure. |
|
|
273
|
+
| `configurable_fields` | Specify init args of the model that can be configured at runtime via the `RunnableConfig`. |
|
|
274
|
+
| `configurable_alternatives` | Specify alternative models which can be swapped in at runtime via the `RunnableConfig`. |
|
|
275
|
+
|
|
275
276
|
Creating custom chat model:
|
|
276
277
|
Custom chat model implementations should inherit from this class.
|
|
277
278
|
Please reference the table below for information about which
|
|
278
279
|
methods and properties are required or optional for implementations.
|
|
279
280
|
|
|
280
|
-
|
|
281
|
-
|
|
|
282
|
-
+==================================+====================================================================+===================+
|
|
281
|
+
| Method/Property | Description | Required |
|
|
282
|
+
| -------------------------------- | ------------------------------------------------------------------ | ----------------- |
|
|
283
283
|
| `_generate` | Use to generate a chat result from a prompt | Required |
|
|
284
|
-
+----------------------------------+--------------------------------------------------------------------+-------------------+
|
|
285
284
|
| `_llm_type` (property) | Used to uniquely identify the type of the model. Used for logging. | Required |
|
|
286
|
-
+----------------------------------+--------------------------------------------------------------------+-------------------+
|
|
287
285
|
| `_identifying_params` (property) | Represent model parameterization for tracing purposes. | Optional |
|
|
288
|
-
+----------------------------------+--------------------------------------------------------------------+-------------------+
|
|
289
286
|
| `_stream` | Use to implement streaming | Optional |
|
|
290
|
-
+----------------------------------+--------------------------------------------------------------------+-------------------+
|
|
291
287
|
| `_agenerate` | Use to implement a native async method | Optional |
|
|
292
|
-
+----------------------------------+--------------------------------------------------------------------+-------------------+
|
|
293
288
|
| `_astream` | Use to implement async version of `_stream` | Optional |
|
|
294
|
-
+----------------------------------+--------------------------------------------------------------------+-------------------+
|
|
295
|
-
|
|
296
|
-
Follow the guide for more information on how to implement a custom Chat Model:
|
|
297
|
-
[Guide](https://python.langchain.com/docs/how_to/custom_chat_model/).
|
|
298
289
|
|
|
299
290
|
""" # noqa: E501
|
|
300
291
|
|
|
301
|
-
|
|
302
|
-
name="callback_manager", since="0.1.7", removal="1.0", alternative="callbacks"
|
|
303
|
-
)(
|
|
304
|
-
Field(
|
|
305
|
-
default=None,
|
|
306
|
-
exclude=True,
|
|
307
|
-
description="Callback manager to add to the run trace.",
|
|
308
|
-
)
|
|
309
|
-
)
|
|
310
|
-
|
|
311
|
-
rate_limiter: Optional[BaseRateLimiter] = Field(default=None, exclude=True)
|
|
292
|
+
rate_limiter: BaseRateLimiter | None = Field(default=None, exclude=True)
|
|
312
293
|
"An optional rate limiter to use for limiting the number of requests."
|
|
313
294
|
|
|
314
|
-
disable_streaming:
|
|
295
|
+
disable_streaming: bool | Literal["tool_calling"] = False
|
|
315
296
|
"""Whether to disable streaming for this model.
|
|
316
297
|
|
|
317
|
-
If streaming is bypassed, then
|
|
318
|
-
defer to
|
|
298
|
+
If streaming is bypassed, then `stream`/`astream`/`astream_events` will
|
|
299
|
+
defer to `invoke`/`ainvoke`.
|
|
319
300
|
|
|
320
|
-
- If True
|
|
321
|
-
- If
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
- If False (
|
|
301
|
+
- If `True`, will always bypass streaming case.
|
|
302
|
+
- If `'tool_calling'`, will bypass streaming case only when the model is called
|
|
303
|
+
with a `tools` keyword argument. In other words, LangChain will automatically
|
|
304
|
+
switch to non-streaming behavior (`invoke`) only when the tools argument is
|
|
305
|
+
provided. This offers the best of both worlds.
|
|
306
|
+
- If `False` (Default), will always use streaming case if available.
|
|
326
307
|
|
|
327
|
-
The main reason for this flag is that code might be written using
|
|
308
|
+
The main reason for this flag is that code might be written using `stream` and
|
|
328
309
|
a user may want to swap out a given model for another model whose the implementation
|
|
329
310
|
does not properly support streaming.
|
|
330
|
-
|
|
331
311
|
"""
|
|
332
312
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
313
|
+
output_version: str | None = Field(
|
|
314
|
+
default_factory=from_env("LC_OUTPUT_VERSION", default=None)
|
|
315
|
+
)
|
|
316
|
+
"""Version of `AIMessage` output format to store in message content.
|
|
337
317
|
|
|
338
|
-
|
|
339
|
-
|
|
318
|
+
`AIMessage.content_blocks` will lazily parse the contents of `content` into a
|
|
319
|
+
standard format. This flag can be used to additionally store the standard format
|
|
320
|
+
in message content, e.g., for serialization purposes.
|
|
340
321
|
|
|
341
|
-
|
|
342
|
-
Dict: Validated values.
|
|
322
|
+
Supported values:
|
|
343
323
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
324
|
+
- `'v0'`: provider-specific format in content (can lazily-parse with
|
|
325
|
+
`content_blocks`)
|
|
326
|
+
- `'v1'`: standardized format in content (consistent with `content_blocks`)
|
|
327
|
+
|
|
328
|
+
Partner packages (e.g.,
|
|
329
|
+
[`langchain-openai`](https://pypi.org/project/langchain-openai)) can also use this
|
|
330
|
+
field to roll out new content formats in a backward-compatible way.
|
|
331
|
+
|
|
332
|
+
!!! version-added "Added in version 1.0"
|
|
333
|
+
|
|
334
|
+
"""
|
|
353
335
|
|
|
354
336
|
model_config = ConfigDict(
|
|
355
337
|
arbitrary_types_allowed=True,
|
|
@@ -364,7 +346,7 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
364
346
|
@property
|
|
365
347
|
@override
|
|
366
348
|
def OutputType(self) -> Any:
|
|
367
|
-
"""Get the output type for this
|
|
349
|
+
"""Get the output type for this `Runnable`."""
|
|
368
350
|
return AnyMessage
|
|
369
351
|
|
|
370
352
|
def _convert_input(self, model_input: LanguageModelInput) -> PromptValue:
|
|
@@ -384,35 +366,38 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
384
366
|
def invoke(
|
|
385
367
|
self,
|
|
386
368
|
input: LanguageModelInput,
|
|
387
|
-
config:
|
|
369
|
+
config: RunnableConfig | None = None,
|
|
388
370
|
*,
|
|
389
|
-
stop:
|
|
371
|
+
stop: list[str] | None = None,
|
|
390
372
|
**kwargs: Any,
|
|
391
|
-
) ->
|
|
373
|
+
) -> AIMessage:
|
|
392
374
|
config = ensure_config(config)
|
|
393
375
|
return cast(
|
|
394
|
-
"
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
376
|
+
"AIMessage",
|
|
377
|
+
cast(
|
|
378
|
+
"ChatGeneration",
|
|
379
|
+
self.generate_prompt(
|
|
380
|
+
[self._convert_input(input)],
|
|
381
|
+
stop=stop,
|
|
382
|
+
callbacks=config.get("callbacks"),
|
|
383
|
+
tags=config.get("tags"),
|
|
384
|
+
metadata=config.get("metadata"),
|
|
385
|
+
run_name=config.get("run_name"),
|
|
386
|
+
run_id=config.pop("run_id", None),
|
|
387
|
+
**kwargs,
|
|
388
|
+
).generations[0][0],
|
|
389
|
+
).message,
|
|
390
|
+
)
|
|
406
391
|
|
|
407
392
|
@override
|
|
408
393
|
async def ainvoke(
|
|
409
394
|
self,
|
|
410
395
|
input: LanguageModelInput,
|
|
411
|
-
config:
|
|
396
|
+
config: RunnableConfig | None = None,
|
|
412
397
|
*,
|
|
413
|
-
stop:
|
|
398
|
+
stop: list[str] | None = None,
|
|
414
399
|
**kwargs: Any,
|
|
415
|
-
) ->
|
|
400
|
+
) -> AIMessage:
|
|
416
401
|
config = ensure_config(config)
|
|
417
402
|
llm_result = await self.agenerate_prompt(
|
|
418
403
|
[self._convert_input(input)],
|
|
@@ -424,15 +409,17 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
424
409
|
run_id=config.pop("run_id", None),
|
|
425
410
|
**kwargs,
|
|
426
411
|
)
|
|
427
|
-
return cast(
|
|
412
|
+
return cast(
|
|
413
|
+
"AIMessage", cast("ChatGeneration", llm_result.generations[0][0]).message
|
|
414
|
+
)
|
|
428
415
|
|
|
429
416
|
def _should_stream(
|
|
430
417
|
self,
|
|
431
418
|
*,
|
|
432
419
|
async_api: bool,
|
|
433
|
-
run_manager:
|
|
434
|
-
|
|
435
|
-
|
|
420
|
+
run_manager: CallbackManagerForLLMRun
|
|
421
|
+
| AsyncCallbackManagerForLLMRun
|
|
422
|
+
| None = None,
|
|
436
423
|
**kwargs: Any,
|
|
437
424
|
) -> bool:
|
|
438
425
|
"""Determine if a given model call should hit the streaming API."""
|
|
@@ -457,6 +444,11 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
457
444
|
if "stream" in kwargs:
|
|
458
445
|
return kwargs["stream"]
|
|
459
446
|
|
|
447
|
+
if "streaming" in self.model_fields_set:
|
|
448
|
+
streaming_value = getattr(self, "streaming", None)
|
|
449
|
+
if isinstance(streaming_value, bool):
|
|
450
|
+
return streaming_value
|
|
451
|
+
|
|
460
452
|
# Check if any streaming callback handlers have been passed in.
|
|
461
453
|
handlers = run_manager.handlers if run_manager else []
|
|
462
454
|
return any(isinstance(h, _StreamingCallbackHandler) for h in handlers)
|
|
@@ -465,15 +457,15 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
465
457
|
def stream(
|
|
466
458
|
self,
|
|
467
459
|
input: LanguageModelInput,
|
|
468
|
-
config:
|
|
460
|
+
config: RunnableConfig | None = None,
|
|
469
461
|
*,
|
|
470
|
-
stop:
|
|
462
|
+
stop: list[str] | None = None,
|
|
471
463
|
**kwargs: Any,
|
|
472
|
-
) -> Iterator[
|
|
464
|
+
) -> Iterator[AIMessageChunk]:
|
|
473
465
|
if not self._should_stream(async_api=False, **{**kwargs, "stream": True}):
|
|
474
466
|
# Model doesn't implement streaming, so use default implementation
|
|
475
467
|
yield cast(
|
|
476
|
-
"
|
|
468
|
+
"AIMessageChunk",
|
|
477
469
|
self.invoke(input, config=config, stop=stop, **kwargs),
|
|
478
470
|
)
|
|
479
471
|
else:
|
|
@@ -518,16 +510,51 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
518
510
|
|
|
519
511
|
try:
|
|
520
512
|
input_messages = _normalize_messages(messages)
|
|
521
|
-
run_id = "-".join((
|
|
513
|
+
run_id = "-".join((LC_ID_PREFIX, str(run_manager.run_id)))
|
|
514
|
+
yielded = False
|
|
515
|
+
index = -1
|
|
516
|
+
index_type = ""
|
|
522
517
|
for chunk in self._stream(input_messages, stop=stop, **kwargs):
|
|
523
518
|
if chunk.message.id is None:
|
|
524
519
|
chunk.message.id = run_id
|
|
525
520
|
chunk.message.response_metadata = _gen_info_and_msg_metadata(chunk)
|
|
521
|
+
if self.output_version == "v1":
|
|
522
|
+
# Overwrite .content with .content_blocks
|
|
523
|
+
chunk.message = _update_message_content_to_blocks(
|
|
524
|
+
chunk.message, "v1"
|
|
525
|
+
)
|
|
526
|
+
for block in cast(
|
|
527
|
+
"list[types.ContentBlock]", chunk.message.content
|
|
528
|
+
):
|
|
529
|
+
if block["type"] != index_type:
|
|
530
|
+
index_type = block["type"]
|
|
531
|
+
index = index + 1
|
|
532
|
+
if "index" not in block:
|
|
533
|
+
block["index"] = index
|
|
526
534
|
run_manager.on_llm_new_token(
|
|
527
535
|
cast("str", chunk.message.content), chunk=chunk
|
|
528
536
|
)
|
|
529
537
|
chunks.append(chunk)
|
|
530
|
-
yield chunk.message
|
|
538
|
+
yield cast("AIMessageChunk", chunk.message)
|
|
539
|
+
yielded = True
|
|
540
|
+
|
|
541
|
+
# Yield a final empty chunk with chunk_position="last" if not yet
|
|
542
|
+
# yielded
|
|
543
|
+
if (
|
|
544
|
+
yielded
|
|
545
|
+
and isinstance(chunk.message, AIMessageChunk)
|
|
546
|
+
and not chunk.message.chunk_position
|
|
547
|
+
):
|
|
548
|
+
empty_content: str | list = (
|
|
549
|
+
"" if isinstance(chunk.message.content, str) else []
|
|
550
|
+
)
|
|
551
|
+
msg_chunk = AIMessageChunk(
|
|
552
|
+
content=empty_content, chunk_position="last", id=run_id
|
|
553
|
+
)
|
|
554
|
+
run_manager.on_llm_new_token(
|
|
555
|
+
"", chunk=ChatGenerationChunk(message=msg_chunk)
|
|
556
|
+
)
|
|
557
|
+
yield msg_chunk
|
|
531
558
|
except BaseException as e:
|
|
532
559
|
generations_with_error_metadata = _generate_response_from_error(e)
|
|
533
560
|
chat_generation_chunk = merge_chat_generation_chunks(chunks)
|
|
@@ -556,15 +583,15 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
556
583
|
async def astream(
|
|
557
584
|
self,
|
|
558
585
|
input: LanguageModelInput,
|
|
559
|
-
config:
|
|
586
|
+
config: RunnableConfig | None = None,
|
|
560
587
|
*,
|
|
561
|
-
stop:
|
|
588
|
+
stop: list[str] | None = None,
|
|
562
589
|
**kwargs: Any,
|
|
563
|
-
) -> AsyncIterator[
|
|
590
|
+
) -> AsyncIterator[AIMessageChunk]:
|
|
564
591
|
if not self._should_stream(async_api=True, **{**kwargs, "stream": True}):
|
|
565
592
|
# No async or sync stream is implemented, so fall back to ainvoke
|
|
566
593
|
yield cast(
|
|
567
|
-
"
|
|
594
|
+
"AIMessageChunk",
|
|
568
595
|
await self.ainvoke(input, config=config, stop=stop, **kwargs),
|
|
569
596
|
)
|
|
570
597
|
return
|
|
@@ -611,7 +638,10 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
611
638
|
|
|
612
639
|
try:
|
|
613
640
|
input_messages = _normalize_messages(messages)
|
|
614
|
-
run_id = "-".join((
|
|
641
|
+
run_id = "-".join((LC_ID_PREFIX, str(run_manager.run_id)))
|
|
642
|
+
yielded = False
|
|
643
|
+
index = -1
|
|
644
|
+
index_type = ""
|
|
615
645
|
async for chunk in self._astream(
|
|
616
646
|
input_messages,
|
|
617
647
|
stop=stop,
|
|
@@ -620,11 +650,42 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
620
650
|
if chunk.message.id is None:
|
|
621
651
|
chunk.message.id = run_id
|
|
622
652
|
chunk.message.response_metadata = _gen_info_and_msg_metadata(chunk)
|
|
653
|
+
if self.output_version == "v1":
|
|
654
|
+
# Overwrite .content with .content_blocks
|
|
655
|
+
chunk.message = _update_message_content_to_blocks(
|
|
656
|
+
chunk.message, "v1"
|
|
657
|
+
)
|
|
658
|
+
for block in cast(
|
|
659
|
+
"list[types.ContentBlock]", chunk.message.content
|
|
660
|
+
):
|
|
661
|
+
if block["type"] != index_type:
|
|
662
|
+
index_type = block["type"]
|
|
663
|
+
index = index + 1
|
|
664
|
+
if "index" not in block:
|
|
665
|
+
block["index"] = index
|
|
623
666
|
await run_manager.on_llm_new_token(
|
|
624
667
|
cast("str", chunk.message.content), chunk=chunk
|
|
625
668
|
)
|
|
626
669
|
chunks.append(chunk)
|
|
627
|
-
yield chunk.message
|
|
670
|
+
yield cast("AIMessageChunk", chunk.message)
|
|
671
|
+
yielded = True
|
|
672
|
+
|
|
673
|
+
# Yield a final empty chunk with chunk_position="last" if not yet yielded
|
|
674
|
+
if (
|
|
675
|
+
yielded
|
|
676
|
+
and isinstance(chunk.message, AIMessageChunk)
|
|
677
|
+
and not chunk.message.chunk_position
|
|
678
|
+
):
|
|
679
|
+
empty_content: str | list = (
|
|
680
|
+
"" if isinstance(chunk.message.content, str) else []
|
|
681
|
+
)
|
|
682
|
+
msg_chunk = AIMessageChunk(
|
|
683
|
+
content=empty_content, chunk_position="last", id=run_id
|
|
684
|
+
)
|
|
685
|
+
await run_manager.on_llm_new_token(
|
|
686
|
+
"", chunk=ChatGenerationChunk(message=msg_chunk)
|
|
687
|
+
)
|
|
688
|
+
yield msg_chunk
|
|
628
689
|
except BaseException as e:
|
|
629
690
|
generations_with_error_metadata = _generate_response_from_error(e)
|
|
630
691
|
chat_generation_chunk = merge_chat_generation_chunks(chunks)
|
|
@@ -650,7 +711,7 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
650
711
|
|
|
651
712
|
# --- Custom methods ---
|
|
652
713
|
|
|
653
|
-
def _combine_llm_outputs(self, llm_outputs: list[
|
|
714
|
+
def _combine_llm_outputs(self, llm_outputs: list[dict | None]) -> dict: # noqa: ARG002
|
|
654
715
|
return {}
|
|
655
716
|
|
|
656
717
|
def _convert_cached_generations(self, cache_val: list) -> list[ChatGeneration]:
|
|
@@ -694,7 +755,7 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
694
755
|
|
|
695
756
|
def _get_invocation_params(
|
|
696
757
|
self,
|
|
697
|
-
stop:
|
|
758
|
+
stop: list[str] | None = None,
|
|
698
759
|
**kwargs: Any,
|
|
699
760
|
) -> dict:
|
|
700
761
|
params = self.dict()
|
|
@@ -703,7 +764,7 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
703
764
|
|
|
704
765
|
def _get_ls_params(
|
|
705
766
|
self,
|
|
706
|
-
stop:
|
|
767
|
+
stop: list[str] | None = None,
|
|
707
768
|
**kwargs: Any,
|
|
708
769
|
) -> LangSmithParams:
|
|
709
770
|
"""Get standard params for tracing."""
|
|
@@ -741,7 +802,7 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
741
802
|
|
|
742
803
|
return ls_params
|
|
743
804
|
|
|
744
|
-
def _get_llm_string(self, stop:
|
|
805
|
+
def _get_llm_string(self, stop: list[str] | None = None, **kwargs: Any) -> str:
|
|
745
806
|
if self.is_lc_serializable():
|
|
746
807
|
params = {**kwargs, "stop": stop}
|
|
747
808
|
param_string = str(sorted(params.items()))
|
|
@@ -758,13 +819,13 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
758
819
|
def generate(
|
|
759
820
|
self,
|
|
760
821
|
messages: list[list[BaseMessage]],
|
|
761
|
-
stop:
|
|
822
|
+
stop: list[str] | None = None,
|
|
762
823
|
callbacks: Callbacks = None,
|
|
763
824
|
*,
|
|
764
|
-
tags:
|
|
765
|
-
metadata:
|
|
766
|
-
run_name:
|
|
767
|
-
run_id:
|
|
825
|
+
tags: list[str] | None = None,
|
|
826
|
+
metadata: dict[str, Any] | None = None,
|
|
827
|
+
run_name: str | None = None,
|
|
828
|
+
run_id: uuid.UUID | None = None,
|
|
768
829
|
**kwargs: Any,
|
|
769
830
|
) -> LLMResult:
|
|
770
831
|
"""Pass a sequence of prompts to the model and return model generations.
|
|
@@ -777,13 +838,13 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
777
838
|
1. Take advantage of batched calls,
|
|
778
839
|
2. Need more output from the model than just the top generated value,
|
|
779
840
|
3. Are building chains that are agnostic to the underlying language model
|
|
780
|
-
|
|
841
|
+
type (e.g., pure text completion models vs chat models).
|
|
781
842
|
|
|
782
843
|
Args:
|
|
783
844
|
messages: List of list of messages.
|
|
784
845
|
stop: Stop words to use when generating. Model output is cut off at the
|
|
785
846
|
first occurrence of any of these substrings.
|
|
786
|
-
callbacks: Callbacks to pass through. Used for executing additional
|
|
847
|
+
callbacks: `Callbacks` to pass through. Used for executing additional
|
|
787
848
|
functionality, such as logging or streaming, throughout generation.
|
|
788
849
|
tags: The tags to apply.
|
|
789
850
|
metadata: The metadata to apply.
|
|
@@ -793,8 +854,8 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
793
854
|
to the model provider API call.
|
|
794
855
|
|
|
795
856
|
Returns:
|
|
796
|
-
An LLMResult
|
|
797
|
-
|
|
857
|
+
An `LLMResult`, which contains a list of candidate `Generations` for each
|
|
858
|
+
input prompt and additional model provider-specific output.
|
|
798
859
|
|
|
799
860
|
"""
|
|
800
861
|
ls_structured_output_format = kwargs.pop(
|
|
@@ -865,7 +926,9 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
865
926
|
output = LLMResult(generations=generations, llm_output=llm_output)
|
|
866
927
|
if run_managers:
|
|
867
928
|
run_infos = []
|
|
868
|
-
for manager, flattened_output in zip(
|
|
929
|
+
for manager, flattened_output in zip(
|
|
930
|
+
run_managers, flattened_outputs, strict=False
|
|
931
|
+
):
|
|
869
932
|
manager.on_llm_end(flattened_output)
|
|
870
933
|
run_infos.append(RunInfo(run_id=manager.run_id))
|
|
871
934
|
output.run = run_infos
|
|
@@ -874,13 +937,13 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
874
937
|
async def agenerate(
|
|
875
938
|
self,
|
|
876
939
|
messages: list[list[BaseMessage]],
|
|
877
|
-
stop:
|
|
940
|
+
stop: list[str] | None = None,
|
|
878
941
|
callbacks: Callbacks = None,
|
|
879
942
|
*,
|
|
880
|
-
tags:
|
|
881
|
-
metadata:
|
|
882
|
-
run_name:
|
|
883
|
-
run_id:
|
|
943
|
+
tags: list[str] | None = None,
|
|
944
|
+
metadata: dict[str, Any] | None = None,
|
|
945
|
+
run_name: str | None = None,
|
|
946
|
+
run_id: uuid.UUID | None = None,
|
|
884
947
|
**kwargs: Any,
|
|
885
948
|
) -> LLMResult:
|
|
886
949
|
"""Asynchronously pass a sequence of prompts to a model and return generations.
|
|
@@ -893,13 +956,13 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
893
956
|
1. Take advantage of batched calls,
|
|
894
957
|
2. Need more output from the model than just the top generated value,
|
|
895
958
|
3. Are building chains that are agnostic to the underlying language model
|
|
896
|
-
|
|
959
|
+
type (e.g., pure text completion models vs chat models).
|
|
897
960
|
|
|
898
961
|
Args:
|
|
899
962
|
messages: List of list of messages.
|
|
900
963
|
stop: Stop words to use when generating. Model output is cut off at the
|
|
901
964
|
first occurrence of any of these substrings.
|
|
902
|
-
callbacks: Callbacks to pass through. Used for executing additional
|
|
965
|
+
callbacks: `Callbacks` to pass through. Used for executing additional
|
|
903
966
|
functionality, such as logging or streaming, throughout generation.
|
|
904
967
|
tags: The tags to apply.
|
|
905
968
|
metadata: The metadata to apply.
|
|
@@ -909,8 +972,8 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
909
972
|
to the model provider API call.
|
|
910
973
|
|
|
911
974
|
Returns:
|
|
912
|
-
An LLMResult
|
|
913
|
-
|
|
975
|
+
An `LLMResult`, which contains a list of candidate `Generations` for each
|
|
976
|
+
input prompt and additional model provider-specific output.
|
|
914
977
|
|
|
915
978
|
"""
|
|
916
979
|
ls_structured_output_format = kwargs.pop(
|
|
@@ -987,7 +1050,7 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
987
1050
|
llm_output=res.llm_output, # type: ignore[union-attr]
|
|
988
1051
|
)
|
|
989
1052
|
)
|
|
990
|
-
for run_manager, res in zip(run_managers, results)
|
|
1053
|
+
for run_manager, res in zip(run_managers, results, strict=False)
|
|
991
1054
|
if not isinstance(res, Exception)
|
|
992
1055
|
]
|
|
993
1056
|
)
|
|
@@ -1003,7 +1066,7 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1003
1066
|
*[
|
|
1004
1067
|
run_manager.on_llm_end(flattened_output)
|
|
1005
1068
|
for run_manager, flattened_output in zip(
|
|
1006
|
-
run_managers, flattened_outputs
|
|
1069
|
+
run_managers, flattened_outputs, strict=False
|
|
1007
1070
|
)
|
|
1008
1071
|
]
|
|
1009
1072
|
)
|
|
@@ -1017,7 +1080,7 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1017
1080
|
def generate_prompt(
|
|
1018
1081
|
self,
|
|
1019
1082
|
prompts: list[PromptValue],
|
|
1020
|
-
stop:
|
|
1083
|
+
stop: list[str] | None = None,
|
|
1021
1084
|
callbacks: Callbacks = None,
|
|
1022
1085
|
**kwargs: Any,
|
|
1023
1086
|
) -> LLMResult:
|
|
@@ -1028,7 +1091,7 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1028
1091
|
async def agenerate_prompt(
|
|
1029
1092
|
self,
|
|
1030
1093
|
prompts: list[PromptValue],
|
|
1031
|
-
stop:
|
|
1094
|
+
stop: list[str] | None = None,
|
|
1032
1095
|
callbacks: Callbacks = None,
|
|
1033
1096
|
**kwargs: Any,
|
|
1034
1097
|
) -> LLMResult:
|
|
@@ -1040,8 +1103,8 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1040
1103
|
def _generate_with_cache(
|
|
1041
1104
|
self,
|
|
1042
1105
|
messages: list[BaseMessage],
|
|
1043
|
-
stop:
|
|
1044
|
-
run_manager:
|
|
1106
|
+
stop: list[str] | None = None,
|
|
1107
|
+
run_manager: CallbackManagerForLLMRun | None = None,
|
|
1045
1108
|
**kwargs: Any,
|
|
1046
1109
|
) -> ChatResult:
|
|
1047
1110
|
llm_cache = self.cache if isinstance(self.cache, BaseCache) else get_llm_cache()
|
|
@@ -1077,15 +1140,53 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1077
1140
|
**kwargs,
|
|
1078
1141
|
):
|
|
1079
1142
|
chunks: list[ChatGenerationChunk] = []
|
|
1143
|
+
run_id: str | None = (
|
|
1144
|
+
f"{LC_ID_PREFIX}-{run_manager.run_id}" if run_manager else None
|
|
1145
|
+
)
|
|
1146
|
+
yielded = False
|
|
1147
|
+
index = -1
|
|
1148
|
+
index_type = ""
|
|
1080
1149
|
for chunk in self._stream(messages, stop=stop, **kwargs):
|
|
1081
1150
|
chunk.message.response_metadata = _gen_info_and_msg_metadata(chunk)
|
|
1151
|
+
if self.output_version == "v1":
|
|
1152
|
+
# Overwrite .content with .content_blocks
|
|
1153
|
+
chunk.message = _update_message_content_to_blocks(
|
|
1154
|
+
chunk.message, "v1"
|
|
1155
|
+
)
|
|
1156
|
+
for block in cast(
|
|
1157
|
+
"list[types.ContentBlock]", chunk.message.content
|
|
1158
|
+
):
|
|
1159
|
+
if block["type"] != index_type:
|
|
1160
|
+
index_type = block["type"]
|
|
1161
|
+
index = index + 1
|
|
1162
|
+
if "index" not in block:
|
|
1163
|
+
block["index"] = index
|
|
1082
1164
|
if run_manager:
|
|
1083
1165
|
if chunk.message.id is None:
|
|
1084
|
-
chunk.message.id =
|
|
1166
|
+
chunk.message.id = run_id
|
|
1085
1167
|
run_manager.on_llm_new_token(
|
|
1086
1168
|
cast("str", chunk.message.content), chunk=chunk
|
|
1087
1169
|
)
|
|
1088
1170
|
chunks.append(chunk)
|
|
1171
|
+
yielded = True
|
|
1172
|
+
|
|
1173
|
+
# Yield a final empty chunk with chunk_position="last" if not yet yielded
|
|
1174
|
+
if (
|
|
1175
|
+
yielded
|
|
1176
|
+
and isinstance(chunk.message, AIMessageChunk)
|
|
1177
|
+
and not chunk.message.chunk_position
|
|
1178
|
+
):
|
|
1179
|
+
empty_content: str | list = (
|
|
1180
|
+
"" if isinstance(chunk.message.content, str) else []
|
|
1181
|
+
)
|
|
1182
|
+
chunk = ChatGenerationChunk(
|
|
1183
|
+
message=AIMessageChunk(
|
|
1184
|
+
content=empty_content, chunk_position="last", id=run_id
|
|
1185
|
+
)
|
|
1186
|
+
)
|
|
1187
|
+
if run_manager:
|
|
1188
|
+
run_manager.on_llm_new_token("", chunk=chunk)
|
|
1189
|
+
chunks.append(chunk)
|
|
1089
1190
|
result = generate_from_stream(iter(chunks))
|
|
1090
1191
|
elif inspect.signature(self._generate).parameters.get("run_manager"):
|
|
1091
1192
|
result = self._generate(
|
|
@@ -1094,10 +1195,17 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1094
1195
|
else:
|
|
1095
1196
|
result = self._generate(messages, stop=stop, **kwargs)
|
|
1096
1197
|
|
|
1198
|
+
if self.output_version == "v1":
|
|
1199
|
+
# Overwrite .content with .content_blocks
|
|
1200
|
+
for generation in result.generations:
|
|
1201
|
+
generation.message = _update_message_content_to_blocks(
|
|
1202
|
+
generation.message, "v1"
|
|
1203
|
+
)
|
|
1204
|
+
|
|
1097
1205
|
# Add response metadata to each generation
|
|
1098
1206
|
for idx, generation in enumerate(result.generations):
|
|
1099
1207
|
if run_manager and generation.message.id is None:
|
|
1100
|
-
generation.message.id = f"{
|
|
1208
|
+
generation.message.id = f"{LC_ID_PREFIX}-{run_manager.run_id}-{idx}"
|
|
1101
1209
|
generation.message.response_metadata = _gen_info_and_msg_metadata(
|
|
1102
1210
|
generation
|
|
1103
1211
|
)
|
|
@@ -1113,8 +1221,8 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1113
1221
|
async def _agenerate_with_cache(
|
|
1114
1222
|
self,
|
|
1115
1223
|
messages: list[BaseMessage],
|
|
1116
|
-
stop:
|
|
1117
|
-
run_manager:
|
|
1224
|
+
stop: list[str] | None = None,
|
|
1225
|
+
run_manager: AsyncCallbackManagerForLLMRun | None = None,
|
|
1118
1226
|
**kwargs: Any,
|
|
1119
1227
|
) -> ChatResult:
|
|
1120
1228
|
llm_cache = self.cache if isinstance(self.cache, BaseCache) else get_llm_cache()
|
|
@@ -1150,15 +1258,53 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1150
1258
|
**kwargs,
|
|
1151
1259
|
):
|
|
1152
1260
|
chunks: list[ChatGenerationChunk] = []
|
|
1261
|
+
run_id: str | None = (
|
|
1262
|
+
f"{LC_ID_PREFIX}-{run_manager.run_id}" if run_manager else None
|
|
1263
|
+
)
|
|
1264
|
+
yielded = False
|
|
1265
|
+
index = -1
|
|
1266
|
+
index_type = ""
|
|
1153
1267
|
async for chunk in self._astream(messages, stop=stop, **kwargs):
|
|
1154
1268
|
chunk.message.response_metadata = _gen_info_and_msg_metadata(chunk)
|
|
1269
|
+
if self.output_version == "v1":
|
|
1270
|
+
# Overwrite .content with .content_blocks
|
|
1271
|
+
chunk.message = _update_message_content_to_blocks(
|
|
1272
|
+
chunk.message, "v1"
|
|
1273
|
+
)
|
|
1274
|
+
for block in cast(
|
|
1275
|
+
"list[types.ContentBlock]", chunk.message.content
|
|
1276
|
+
):
|
|
1277
|
+
if block["type"] != index_type:
|
|
1278
|
+
index_type = block["type"]
|
|
1279
|
+
index = index + 1
|
|
1280
|
+
if "index" not in block:
|
|
1281
|
+
block["index"] = index
|
|
1155
1282
|
if run_manager:
|
|
1156
1283
|
if chunk.message.id is None:
|
|
1157
|
-
chunk.message.id =
|
|
1284
|
+
chunk.message.id = run_id
|
|
1158
1285
|
await run_manager.on_llm_new_token(
|
|
1159
1286
|
cast("str", chunk.message.content), chunk=chunk
|
|
1160
1287
|
)
|
|
1161
1288
|
chunks.append(chunk)
|
|
1289
|
+
yielded = True
|
|
1290
|
+
|
|
1291
|
+
# Yield a final empty chunk with chunk_position="last" if not yet yielded
|
|
1292
|
+
if (
|
|
1293
|
+
yielded
|
|
1294
|
+
and isinstance(chunk.message, AIMessageChunk)
|
|
1295
|
+
and not chunk.message.chunk_position
|
|
1296
|
+
):
|
|
1297
|
+
empty_content: str | list = (
|
|
1298
|
+
"" if isinstance(chunk.message.content, str) else []
|
|
1299
|
+
)
|
|
1300
|
+
chunk = ChatGenerationChunk(
|
|
1301
|
+
message=AIMessageChunk(
|
|
1302
|
+
content=empty_content, chunk_position="last", id=run_id
|
|
1303
|
+
)
|
|
1304
|
+
)
|
|
1305
|
+
if run_manager:
|
|
1306
|
+
await run_manager.on_llm_new_token("", chunk=chunk)
|
|
1307
|
+
chunks.append(chunk)
|
|
1162
1308
|
result = generate_from_stream(iter(chunks))
|
|
1163
1309
|
elif inspect.signature(self._agenerate).parameters.get("run_manager"):
|
|
1164
1310
|
result = await self._agenerate(
|
|
@@ -1167,10 +1313,17 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1167
1313
|
else:
|
|
1168
1314
|
result = await self._agenerate(messages, stop=stop, **kwargs)
|
|
1169
1315
|
|
|
1316
|
+
if self.output_version == "v1":
|
|
1317
|
+
# Overwrite .content with .content_blocks
|
|
1318
|
+
for generation in result.generations:
|
|
1319
|
+
generation.message = _update_message_content_to_blocks(
|
|
1320
|
+
generation.message, "v1"
|
|
1321
|
+
)
|
|
1322
|
+
|
|
1170
1323
|
# Add response metadata to each generation
|
|
1171
1324
|
for idx, generation in enumerate(result.generations):
|
|
1172
1325
|
if run_manager and generation.message.id is None:
|
|
1173
|
-
generation.message.id = f"{
|
|
1326
|
+
generation.message.id = f"{LC_ID_PREFIX}-{run_manager.run_id}-{idx}"
|
|
1174
1327
|
generation.message.response_metadata = _gen_info_and_msg_metadata(
|
|
1175
1328
|
generation
|
|
1176
1329
|
)
|
|
@@ -1187,8 +1340,8 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1187
1340
|
def _generate(
|
|
1188
1341
|
self,
|
|
1189
1342
|
messages: list[BaseMessage],
|
|
1190
|
-
stop:
|
|
1191
|
-
run_manager:
|
|
1343
|
+
stop: list[str] | None = None,
|
|
1344
|
+
run_manager: CallbackManagerForLLMRun | None = None,
|
|
1192
1345
|
**kwargs: Any,
|
|
1193
1346
|
) -> ChatResult:
|
|
1194
1347
|
"""Generate the result.
|
|
@@ -1206,8 +1359,8 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1206
1359
|
async def _agenerate(
|
|
1207
1360
|
self,
|
|
1208
1361
|
messages: list[BaseMessage],
|
|
1209
|
-
stop:
|
|
1210
|
-
run_manager:
|
|
1362
|
+
stop: list[str] | None = None,
|
|
1363
|
+
run_manager: AsyncCallbackManagerForLLMRun | None = None,
|
|
1211
1364
|
**kwargs: Any,
|
|
1212
1365
|
) -> ChatResult:
|
|
1213
1366
|
"""Generate the result.
|
|
@@ -1233,8 +1386,8 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1233
1386
|
def _stream(
|
|
1234
1387
|
self,
|
|
1235
1388
|
messages: list[BaseMessage],
|
|
1236
|
-
stop:
|
|
1237
|
-
run_manager:
|
|
1389
|
+
stop: list[str] | None = None,
|
|
1390
|
+
run_manager: CallbackManagerForLLMRun | None = None,
|
|
1238
1391
|
**kwargs: Any,
|
|
1239
1392
|
) -> Iterator[ChatGenerationChunk]:
|
|
1240
1393
|
"""Stream the output of the model.
|
|
@@ -1253,8 +1406,8 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1253
1406
|
async def _astream(
|
|
1254
1407
|
self,
|
|
1255
1408
|
messages: list[BaseMessage],
|
|
1256
|
-
stop:
|
|
1257
|
-
run_manager:
|
|
1409
|
+
stop: list[str] | None = None,
|
|
1410
|
+
run_manager: AsyncCallbackManagerForLLMRun | None = None,
|
|
1258
1411
|
**kwargs: Any,
|
|
1259
1412
|
) -> AsyncIterator[ChatGenerationChunk]:
|
|
1260
1413
|
"""Stream the output of the model.
|
|
@@ -1288,44 +1441,10 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1288
1441
|
break
|
|
1289
1442
|
yield item # type: ignore[misc]
|
|
1290
1443
|
|
|
1291
|
-
@deprecated("0.1.7", alternative="invoke", removal="1.0")
|
|
1292
|
-
def __call__(
|
|
1293
|
-
self,
|
|
1294
|
-
messages: list[BaseMessage],
|
|
1295
|
-
stop: Optional[list[str]] = None,
|
|
1296
|
-
callbacks: Callbacks = None,
|
|
1297
|
-
**kwargs: Any,
|
|
1298
|
-
) -> BaseMessage:
|
|
1299
|
-
"""Call the model.
|
|
1300
|
-
|
|
1301
|
-
Args:
|
|
1302
|
-
messages: List of messages.
|
|
1303
|
-
stop: Stop words to use when generating. Model output is cut off at the
|
|
1304
|
-
first occurrence of any of these substrings.
|
|
1305
|
-
callbacks: Callbacks to pass through. Used for executing additional
|
|
1306
|
-
functionality, such as logging or streaming, throughout generation.
|
|
1307
|
-
**kwargs: Arbitrary additional keyword arguments. These are usually passed
|
|
1308
|
-
to the model provider API call.
|
|
1309
|
-
|
|
1310
|
-
Raises:
|
|
1311
|
-
ValueError: If the generation is not a chat generation.
|
|
1312
|
-
|
|
1313
|
-
Returns:
|
|
1314
|
-
The model output message.
|
|
1315
|
-
|
|
1316
|
-
"""
|
|
1317
|
-
generation = self.generate(
|
|
1318
|
-
[messages], stop=stop, callbacks=callbacks, **kwargs
|
|
1319
|
-
).generations[0][0]
|
|
1320
|
-
if isinstance(generation, ChatGeneration):
|
|
1321
|
-
return generation.message
|
|
1322
|
-
msg = "Unexpected generation type"
|
|
1323
|
-
raise ValueError(msg)
|
|
1324
|
-
|
|
1325
1444
|
async def _call_async(
|
|
1326
1445
|
self,
|
|
1327
1446
|
messages: list[BaseMessage],
|
|
1328
|
-
stop:
|
|
1447
|
+
stop: list[str] | None = None,
|
|
1329
1448
|
callbacks: Callbacks = None,
|
|
1330
1449
|
**kwargs: Any,
|
|
1331
1450
|
) -> BaseMessage:
|
|
@@ -1338,91 +1457,6 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1338
1457
|
msg = "Unexpected generation type"
|
|
1339
1458
|
raise ValueError(msg)
|
|
1340
1459
|
|
|
1341
|
-
@deprecated("0.1.7", alternative="invoke", removal="1.0")
|
|
1342
|
-
def call_as_llm(
|
|
1343
|
-
self, message: str, stop: Optional[list[str]] = None, **kwargs: Any
|
|
1344
|
-
) -> str:
|
|
1345
|
-
"""Call the model.
|
|
1346
|
-
|
|
1347
|
-
Args:
|
|
1348
|
-
message: The input message.
|
|
1349
|
-
stop: Stop words to use when generating. Model output is cut off at the
|
|
1350
|
-
first occurrence of any of these substrings.
|
|
1351
|
-
**kwargs: Arbitrary additional keyword arguments. These are usually passed
|
|
1352
|
-
to the model provider API call.
|
|
1353
|
-
|
|
1354
|
-
Returns:
|
|
1355
|
-
The model output string.
|
|
1356
|
-
|
|
1357
|
-
"""
|
|
1358
|
-
return self.predict(message, stop=stop, **kwargs)
|
|
1359
|
-
|
|
1360
|
-
@deprecated("0.1.7", alternative="invoke", removal="1.0")
|
|
1361
|
-
@override
|
|
1362
|
-
def predict(
|
|
1363
|
-
self, text: str, *, stop: Optional[Sequence[str]] = None, **kwargs: Any
|
|
1364
|
-
) -> str:
|
|
1365
|
-
"""Predict the next message.
|
|
1366
|
-
|
|
1367
|
-
Args:
|
|
1368
|
-
text: The input message.
|
|
1369
|
-
stop: Stop words to use when generating. Model output is cut off at the
|
|
1370
|
-
first occurrence of any of these substrings.
|
|
1371
|
-
**kwargs: Arbitrary additional keyword arguments. These are usually passed
|
|
1372
|
-
to the model provider API call.
|
|
1373
|
-
|
|
1374
|
-
Raises:
|
|
1375
|
-
ValueError: If the output is not a string.
|
|
1376
|
-
|
|
1377
|
-
Returns:
|
|
1378
|
-
The predicted output string.
|
|
1379
|
-
|
|
1380
|
-
"""
|
|
1381
|
-
stop_ = None if stop is None else list(stop)
|
|
1382
|
-
result = self([HumanMessage(content=text)], stop=stop_, **kwargs)
|
|
1383
|
-
if isinstance(result.content, str):
|
|
1384
|
-
return result.content
|
|
1385
|
-
msg = "Cannot use predict when output is not a string."
|
|
1386
|
-
raise ValueError(msg)
|
|
1387
|
-
|
|
1388
|
-
@deprecated("0.1.7", alternative="invoke", removal="1.0")
|
|
1389
|
-
@override
|
|
1390
|
-
def predict_messages(
|
|
1391
|
-
self,
|
|
1392
|
-
messages: list[BaseMessage],
|
|
1393
|
-
*,
|
|
1394
|
-
stop: Optional[Sequence[str]] = None,
|
|
1395
|
-
**kwargs: Any,
|
|
1396
|
-
) -> BaseMessage:
|
|
1397
|
-
stop_ = None if stop is None else list(stop)
|
|
1398
|
-
return self(messages, stop=stop_, **kwargs)
|
|
1399
|
-
|
|
1400
|
-
@deprecated("0.1.7", alternative="ainvoke", removal="1.0")
|
|
1401
|
-
@override
|
|
1402
|
-
async def apredict(
|
|
1403
|
-
self, text: str, *, stop: Optional[Sequence[str]] = None, **kwargs: Any
|
|
1404
|
-
) -> str:
|
|
1405
|
-
stop_ = None if stop is None else list(stop)
|
|
1406
|
-
result = await self._call_async(
|
|
1407
|
-
[HumanMessage(content=text)], stop=stop_, **kwargs
|
|
1408
|
-
)
|
|
1409
|
-
if isinstance(result.content, str):
|
|
1410
|
-
return result.content
|
|
1411
|
-
msg = "Cannot use predict when output is not a string."
|
|
1412
|
-
raise ValueError(msg)
|
|
1413
|
-
|
|
1414
|
-
@deprecated("0.1.7", alternative="ainvoke", removal="1.0")
|
|
1415
|
-
@override
|
|
1416
|
-
async def apredict_messages(
|
|
1417
|
-
self,
|
|
1418
|
-
messages: list[BaseMessage],
|
|
1419
|
-
*,
|
|
1420
|
-
stop: Optional[Sequence[str]] = None,
|
|
1421
|
-
**kwargs: Any,
|
|
1422
|
-
) -> BaseMessage:
|
|
1423
|
-
stop_ = None if stop is None else list(stop)
|
|
1424
|
-
return await self._call_async(messages, stop=stop_, **kwargs)
|
|
1425
|
-
|
|
1426
1460
|
@property
|
|
1427
1461
|
@abstractmethod
|
|
1428
1462
|
def _llm_type(self) -> str:
|
|
@@ -1438,12 +1472,12 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1438
1472
|
def bind_tools(
|
|
1439
1473
|
self,
|
|
1440
1474
|
tools: Sequence[
|
|
1441
|
-
|
|
1475
|
+
typing.Dict[str, Any] | type | Callable | BaseTool # noqa: UP006
|
|
1442
1476
|
],
|
|
1443
1477
|
*,
|
|
1444
|
-
tool_choice:
|
|
1478
|
+
tool_choice: str | None = None,
|
|
1445
1479
|
**kwargs: Any,
|
|
1446
|
-
) -> Runnable[LanguageModelInput,
|
|
1480
|
+
) -> Runnable[LanguageModelInput, AIMessage]:
|
|
1447
1481
|
"""Bind tools to the model.
|
|
1448
1482
|
|
|
1449
1483
|
Args:
|
|
@@ -1458,11 +1492,11 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1458
1492
|
|
|
1459
1493
|
def with_structured_output(
|
|
1460
1494
|
self,
|
|
1461
|
-
schema:
|
|
1495
|
+
schema: typing.Dict | type, # noqa: UP006
|
|
1462
1496
|
*,
|
|
1463
1497
|
include_raw: bool = False,
|
|
1464
1498
|
**kwargs: Any,
|
|
1465
|
-
) -> Runnable[LanguageModelInput,
|
|
1499
|
+
) -> Runnable[LanguageModelInput, typing.Dict | BaseModel]: # noqa: UP006
|
|
1466
1500
|
"""Model wrapper that returns outputs formatted to match the given schema.
|
|
1467
1501
|
|
|
1468
1502
|
Args:
|
|
@@ -1470,124 +1504,130 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1470
1504
|
|
|
1471
1505
|
- an OpenAI function/tool schema,
|
|
1472
1506
|
- a JSON Schema,
|
|
1473
|
-
- a TypedDict class,
|
|
1507
|
+
- a `TypedDict` class,
|
|
1474
1508
|
- or a Pydantic class.
|
|
1475
1509
|
|
|
1476
|
-
If
|
|
1510
|
+
If `schema` is a Pydantic class then the model output will be a
|
|
1477
1511
|
Pydantic instance of that class, and the model-generated fields will be
|
|
1478
1512
|
validated by the Pydantic class. Otherwise the model output will be a
|
|
1479
|
-
dict and will not be validated.
|
|
1480
|
-
|
|
1481
|
-
|
|
1513
|
+
dict and will not be validated.
|
|
1514
|
+
|
|
1515
|
+
See `langchain_core.utils.function_calling.convert_to_openai_tool` for
|
|
1516
|
+
more on how to properly specify types and descriptions of schema fields
|
|
1517
|
+
when specifying a Pydantic or `TypedDict` class.
|
|
1482
1518
|
|
|
1483
1519
|
include_raw:
|
|
1484
|
-
If False then only the parsed structured output is returned. If
|
|
1485
|
-
an error occurs during model output parsing it will be raised. If True
|
|
1486
|
-
then both the raw model response (a BaseMessage) and the parsed model
|
|
1520
|
+
If `False` then only the parsed structured output is returned. If
|
|
1521
|
+
an error occurs during model output parsing it will be raised. If `True`
|
|
1522
|
+
then both the raw model response (a `BaseMessage`) and the parsed model
|
|
1487
1523
|
response will be returned. If an error occurs during output parsing it
|
|
1488
|
-
will be caught and returned as well.
|
|
1489
|
-
|
|
1524
|
+
will be caught and returned as well.
|
|
1525
|
+
|
|
1526
|
+
The final output is always a `dict` with keys `'raw'`, `'parsed'`, and
|
|
1527
|
+
`'parsing_error'`.
|
|
1490
1528
|
|
|
1491
1529
|
Raises:
|
|
1492
|
-
ValueError: If there are any unsupported
|
|
1530
|
+
ValueError: If there are any unsupported `kwargs`.
|
|
1493
1531
|
NotImplementedError: If the model does not implement
|
|
1494
|
-
|
|
1532
|
+
`with_structured_output()`.
|
|
1495
1533
|
|
|
1496
1534
|
Returns:
|
|
1497
|
-
A Runnable that takes same inputs as a
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1535
|
+
A `Runnable` that takes same inputs as a
|
|
1536
|
+
`langchain_core.language_models.chat.BaseChatModel`. If `include_raw` is
|
|
1537
|
+
`False` and `schema` is a Pydantic class, `Runnable` outputs an instance
|
|
1538
|
+
of `schema` (i.e., a Pydantic object). Otherwise, if `include_raw` is
|
|
1539
|
+
`False` then `Runnable` outputs a `dict`.
|
|
1501
1540
|
|
|
1502
|
-
|
|
1541
|
+
If `include_raw` is `True`, then `Runnable` outputs a `dict` with keys:
|
|
1503
1542
|
|
|
1504
|
-
|
|
1543
|
+
- `'raw'`: `BaseMessage`
|
|
1544
|
+
- `'parsed'`: `None` if there was a parsing error, otherwise the type
|
|
1545
|
+
depends on the `schema` as described above.
|
|
1546
|
+
- `'parsing_error'`: `BaseException | None`
|
|
1505
1547
|
|
|
1506
|
-
|
|
1507
|
-
- ``'parsed'``: None if there was a parsing error, otherwise the type depends on the ``schema`` as described above.
|
|
1508
|
-
- ``'parsing_error'``: Optional[BaseException]
|
|
1548
|
+
Example: Pydantic schema (`include_raw=False`):
|
|
1509
1549
|
|
|
1510
|
-
|
|
1511
|
-
|
|
1550
|
+
```python
|
|
1551
|
+
from pydantic import BaseModel
|
|
1512
1552
|
|
|
1513
|
-
from pydantic import BaseModel
|
|
1514
1553
|
|
|
1554
|
+
class AnswerWithJustification(BaseModel):
|
|
1555
|
+
'''An answer to the user question along with justification for the answer.'''
|
|
1515
1556
|
|
|
1516
|
-
|
|
1517
|
-
|
|
1557
|
+
answer: str
|
|
1558
|
+
justification: str
|
|
1518
1559
|
|
|
1519
|
-
answer: str
|
|
1520
|
-
justification: str
|
|
1521
1560
|
|
|
1561
|
+
model = ChatModel(model="model-name", temperature=0)
|
|
1562
|
+
structured_model = model.with_structured_output(AnswerWithJustification)
|
|
1522
1563
|
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
structured_llm.invoke(
|
|
1527
|
-
"What weighs more a pound of bricks or a pound of feathers"
|
|
1528
|
-
)
|
|
1529
|
-
|
|
1530
|
-
# -> AnswerWithJustification(
|
|
1531
|
-
# answer='They weigh the same',
|
|
1532
|
-
# justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'
|
|
1533
|
-
# )
|
|
1564
|
+
structured_model.invoke(
|
|
1565
|
+
"What weighs more a pound of bricks or a pound of feathers"
|
|
1566
|
+
)
|
|
1534
1567
|
|
|
1535
|
-
|
|
1536
|
-
|
|
1568
|
+
# -> AnswerWithJustification(
|
|
1569
|
+
# answer='They weigh the same',
|
|
1570
|
+
# justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'
|
|
1571
|
+
# )
|
|
1572
|
+
```
|
|
1537
1573
|
|
|
1538
|
-
|
|
1574
|
+
Example: Pydantic schema (`include_raw=True`):
|
|
1539
1575
|
|
|
1576
|
+
```python
|
|
1577
|
+
from pydantic import BaseModel
|
|
1540
1578
|
|
|
1541
|
-
class AnswerWithJustification(BaseModel):
|
|
1542
|
-
'''An answer to the user question along with justification for the answer.'''
|
|
1543
1579
|
|
|
1544
|
-
|
|
1545
|
-
|
|
1580
|
+
class AnswerWithJustification(BaseModel):
|
|
1581
|
+
'''An answer to the user question along with justification for the answer.'''
|
|
1546
1582
|
|
|
1583
|
+
answer: str
|
|
1584
|
+
justification: str
|
|
1547
1585
|
|
|
1548
|
-
llm = ChatModel(model="model-name", temperature=0)
|
|
1549
|
-
structured_llm = llm.with_structured_output(
|
|
1550
|
-
AnswerWithJustification, include_raw=True
|
|
1551
|
-
)
|
|
1552
1586
|
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
# 'raw': AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_Ao02pnFYXD6GN1yzc0uXPsvF', 'function': {'arguments': '{"answer":"They weigh the same.","justification":"Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ."}', 'name': 'AnswerWithJustification'}, 'type': 'function'}]}),
|
|
1558
|
-
# 'parsed': AnswerWithJustification(answer='They weigh the same.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'),
|
|
1559
|
-
# 'parsing_error': None
|
|
1560
|
-
# }
|
|
1587
|
+
model = ChatModel(model="model-name", temperature=0)
|
|
1588
|
+
structured_model = model.with_structured_output(
|
|
1589
|
+
AnswerWithJustification, include_raw=True
|
|
1590
|
+
)
|
|
1561
1591
|
|
|
1562
|
-
|
|
1563
|
-
|
|
1592
|
+
structured_model.invoke(
|
|
1593
|
+
"What weighs more a pound of bricks or a pound of feathers"
|
|
1594
|
+
)
|
|
1595
|
+
# -> {
|
|
1596
|
+
# 'raw': AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_Ao02pnFYXD6GN1yzc0uXPsvF', 'function': {'arguments': '{"answer":"They weigh the same.","justification":"Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ."}', 'name': 'AnswerWithJustification'}, 'type': 'function'}]}),
|
|
1597
|
+
# 'parsed': AnswerWithJustification(answer='They weigh the same.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'),
|
|
1598
|
+
# 'parsing_error': None
|
|
1599
|
+
# }
|
|
1600
|
+
```
|
|
1564
1601
|
|
|
1565
|
-
|
|
1566
|
-
from langchain_core.utils.function_calling import convert_to_openai_tool
|
|
1602
|
+
Example: `dict` schema (`include_raw=False`):
|
|
1567
1603
|
|
|
1604
|
+
```python
|
|
1605
|
+
from pydantic import BaseModel
|
|
1606
|
+
from langchain_core.utils.function_calling import convert_to_openai_tool
|
|
1568
1607
|
|
|
1569
|
-
class AnswerWithJustification(BaseModel):
|
|
1570
|
-
'''An answer to the user question along with justification for the answer.'''
|
|
1571
1608
|
|
|
1572
|
-
|
|
1573
|
-
|
|
1609
|
+
class AnswerWithJustification(BaseModel):
|
|
1610
|
+
'''An answer to the user question along with justification for the answer.'''
|
|
1574
1611
|
|
|
1612
|
+
answer: str
|
|
1613
|
+
justification: str
|
|
1575
1614
|
|
|
1576
|
-
dict_schema = convert_to_openai_tool(AnswerWithJustification)
|
|
1577
|
-
llm = ChatModel(model="model-name", temperature=0)
|
|
1578
|
-
structured_llm = llm.with_structured_output(dict_schema)
|
|
1579
1615
|
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
# -> {
|
|
1584
|
-
# 'answer': 'They weigh the same',
|
|
1585
|
-
# 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.'
|
|
1586
|
-
# }
|
|
1616
|
+
dict_schema = convert_to_openai_tool(AnswerWithJustification)
|
|
1617
|
+
model = ChatModel(model="model-name", temperature=0)
|
|
1618
|
+
structured_model = model.with_structured_output(dict_schema)
|
|
1587
1619
|
|
|
1588
|
-
|
|
1620
|
+
structured_model.invoke(
|
|
1621
|
+
"What weighs more a pound of bricks or a pound of feathers"
|
|
1622
|
+
)
|
|
1623
|
+
# -> {
|
|
1624
|
+
# 'answer': 'They weigh the same',
|
|
1625
|
+
# 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.'
|
|
1626
|
+
# }
|
|
1627
|
+
```
|
|
1589
1628
|
|
|
1590
|
-
|
|
1629
|
+
!!! warning "Behavior changed in 0.2.26"
|
|
1630
|
+
Added support for TypedDict class.
|
|
1591
1631
|
|
|
1592
1632
|
""" # noqa: E501
|
|
1593
1633
|
_ = kwargs.pop("method", None)
|
|
@@ -1632,17 +1672,17 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1632
1672
|
class SimpleChatModel(BaseChatModel):
|
|
1633
1673
|
"""Simplified implementation for a chat model to inherit from.
|
|
1634
1674
|
|
|
1635
|
-
|
|
1675
|
+
!!! note
|
|
1636
1676
|
This implementation is primarily here for backwards compatibility. For new
|
|
1637
|
-
implementations, please use
|
|
1677
|
+
implementations, please use `BaseChatModel` directly.
|
|
1638
1678
|
|
|
1639
1679
|
"""
|
|
1640
1680
|
|
|
1641
1681
|
def _generate(
|
|
1642
1682
|
self,
|
|
1643
1683
|
messages: list[BaseMessage],
|
|
1644
|
-
stop:
|
|
1645
|
-
run_manager:
|
|
1684
|
+
stop: list[str] | None = None,
|
|
1685
|
+
run_manager: CallbackManagerForLLMRun | None = None,
|
|
1646
1686
|
**kwargs: Any,
|
|
1647
1687
|
) -> ChatResult:
|
|
1648
1688
|
output_str = self._call(messages, stop=stop, run_manager=run_manager, **kwargs)
|
|
@@ -1654,8 +1694,8 @@ class SimpleChatModel(BaseChatModel):
|
|
|
1654
1694
|
def _call(
|
|
1655
1695
|
self,
|
|
1656
1696
|
messages: list[BaseMessage],
|
|
1657
|
-
stop:
|
|
1658
|
-
run_manager:
|
|
1697
|
+
stop: list[str] | None = None,
|
|
1698
|
+
run_manager: CallbackManagerForLLMRun | None = None,
|
|
1659
1699
|
**kwargs: Any,
|
|
1660
1700
|
) -> str:
|
|
1661
1701
|
"""Simpler interface."""
|
|
@@ -1663,8 +1703,8 @@ class SimpleChatModel(BaseChatModel):
|
|
|
1663
1703
|
async def _agenerate(
|
|
1664
1704
|
self,
|
|
1665
1705
|
messages: list[BaseMessage],
|
|
1666
|
-
stop:
|
|
1667
|
-
run_manager:
|
|
1706
|
+
stop: list[str] | None = None,
|
|
1707
|
+
run_manager: AsyncCallbackManagerForLLMRun | None = None,
|
|
1668
1708
|
**kwargs: Any,
|
|
1669
1709
|
) -> ChatResult:
|
|
1670
1710
|
return await run_in_executor(
|
|
@@ -1678,7 +1718,7 @@ class SimpleChatModel(BaseChatModel):
|
|
|
1678
1718
|
|
|
1679
1719
|
|
|
1680
1720
|
def _gen_info_and_msg_metadata(
|
|
1681
|
-
generation:
|
|
1721
|
+
generation: ChatGeneration | ChatGenerationChunk,
|
|
1682
1722
|
) -> dict:
|
|
1683
1723
|
return {
|
|
1684
1724
|
**(generation.generation_info or {}),
|