langchain-core 0.4.0.dev0__py3-none-any.whl → 1.0.0a2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- langchain_core/_api/beta_decorator.py +2 -2
- langchain_core/_api/deprecation.py +1 -1
- langchain_core/beta/runnables/context.py +1 -1
- langchain_core/callbacks/base.py +14 -23
- langchain_core/callbacks/file.py +13 -2
- langchain_core/callbacks/manager.py +74 -157
- langchain_core/callbacks/streaming_stdout.py +3 -4
- langchain_core/callbacks/usage.py +2 -12
- langchain_core/chat_history.py +6 -6
- langchain_core/documents/base.py +1 -1
- langchain_core/documents/compressor.py +9 -6
- langchain_core/indexing/base.py +2 -2
- langchain_core/language_models/_utils.py +232 -101
- langchain_core/language_models/base.py +35 -23
- langchain_core/language_models/chat_models.py +248 -54
- langchain_core/language_models/fake_chat_models.py +28 -81
- langchain_core/load/dump.py +3 -4
- langchain_core/messages/__init__.py +30 -24
- langchain_core/messages/ai.py +188 -30
- langchain_core/messages/base.py +164 -25
- langchain_core/messages/block_translators/__init__.py +89 -0
- langchain_core/messages/block_translators/anthropic.py +451 -0
- langchain_core/messages/block_translators/bedrock.py +45 -0
- langchain_core/messages/block_translators/bedrock_converse.py +47 -0
- langchain_core/messages/block_translators/google_genai.py +45 -0
- langchain_core/messages/block_translators/google_vertexai.py +47 -0
- langchain_core/messages/block_translators/groq.py +45 -0
- langchain_core/messages/block_translators/langchain_v0.py +164 -0
- langchain_core/messages/block_translators/ollama.py +45 -0
- langchain_core/messages/block_translators/openai.py +798 -0
- langchain_core/messages/{content_blocks.py → content.py} +303 -278
- langchain_core/messages/human.py +29 -9
- langchain_core/messages/system.py +29 -9
- langchain_core/messages/tool.py +94 -13
- langchain_core/messages/utils.py +34 -234
- langchain_core/output_parsers/base.py +14 -50
- langchain_core/output_parsers/json.py +2 -5
- langchain_core/output_parsers/list.py +2 -7
- langchain_core/output_parsers/openai_functions.py +5 -28
- langchain_core/output_parsers/openai_tools.py +49 -90
- langchain_core/output_parsers/pydantic.py +2 -3
- langchain_core/output_parsers/transform.py +12 -53
- langchain_core/output_parsers/xml.py +9 -17
- langchain_core/prompt_values.py +8 -112
- langchain_core/prompts/chat.py +1 -3
- langchain_core/runnables/base.py +500 -451
- langchain_core/runnables/branch.py +1 -1
- langchain_core/runnables/fallbacks.py +4 -4
- langchain_core/runnables/history.py +1 -1
- langchain_core/runnables/passthrough.py +3 -3
- langchain_core/runnables/retry.py +1 -1
- langchain_core/runnables/router.py +1 -1
- langchain_core/structured_query.py +3 -7
- langchain_core/tools/base.py +14 -41
- langchain_core/tools/convert.py +2 -22
- langchain_core/tools/retriever.py +1 -8
- langchain_core/tools/structured.py +2 -10
- langchain_core/tracers/_streaming.py +6 -7
- langchain_core/tracers/base.py +7 -14
- langchain_core/tracers/core.py +4 -27
- langchain_core/tracers/event_stream.py +4 -15
- langchain_core/tracers/langchain.py +3 -14
- langchain_core/tracers/log_stream.py +2 -3
- langchain_core/utils/_merge.py +45 -7
- langchain_core/utils/function_calling.py +22 -9
- langchain_core/utils/utils.py +29 -0
- langchain_core/version.py +1 -1
- {langchain_core-0.4.0.dev0.dist-info → langchain_core-1.0.0a2.dist-info}/METADATA +7 -9
- {langchain_core-0.4.0.dev0.dist-info → langchain_core-1.0.0a2.dist-info}/RECORD +71 -64
- 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 → langchain_core-1.0.0a2.dist-info}/WHEEL +0 -0
- {langchain_core-0.4.0.dev0.dist-info → langchain_core-1.0.0a2.dist-info}/entry_points.txt +0 -0
langchain_core/v1/chat_models.py
DELETED
|
@@ -1,1047 +0,0 @@
|
|
|
1
|
-
"""Chat models for conversational AI."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import copy
|
|
6
|
-
import typing
|
|
7
|
-
import warnings
|
|
8
|
-
from abc import ABC, abstractmethod
|
|
9
|
-
from collections.abc import AsyncIterator, Iterator, Mapping, Sequence
|
|
10
|
-
from functools import cached_property
|
|
11
|
-
from operator import itemgetter
|
|
12
|
-
from typing import (
|
|
13
|
-
TYPE_CHECKING,
|
|
14
|
-
Any,
|
|
15
|
-
Callable,
|
|
16
|
-
Literal,
|
|
17
|
-
Optional,
|
|
18
|
-
Union,
|
|
19
|
-
cast,
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
from pydantic import (
|
|
23
|
-
BaseModel,
|
|
24
|
-
ConfigDict,
|
|
25
|
-
Field,
|
|
26
|
-
field_validator,
|
|
27
|
-
)
|
|
28
|
-
from typing_extensions import TypeAlias, override
|
|
29
|
-
|
|
30
|
-
from langchain_core.caches import BaseCache
|
|
31
|
-
from langchain_core.callbacks import (
|
|
32
|
-
AsyncCallbackManager,
|
|
33
|
-
AsyncCallbackManagerForLLMRun,
|
|
34
|
-
CallbackManager,
|
|
35
|
-
CallbackManagerForLLMRun,
|
|
36
|
-
Callbacks,
|
|
37
|
-
)
|
|
38
|
-
from langchain_core.language_models._utils import _normalize_messages_v1
|
|
39
|
-
from langchain_core.language_models.base import (
|
|
40
|
-
LangSmithParams,
|
|
41
|
-
LanguageModelInput,
|
|
42
|
-
_get_token_ids_default_method,
|
|
43
|
-
_get_verbosity,
|
|
44
|
-
)
|
|
45
|
-
from langchain_core.load import dumpd
|
|
46
|
-
from langchain_core.messages import (
|
|
47
|
-
convert_to_openai_image_block,
|
|
48
|
-
get_buffer_string,
|
|
49
|
-
is_data_content_block,
|
|
50
|
-
)
|
|
51
|
-
from langchain_core.messages.utils import (
|
|
52
|
-
convert_from_v1_message,
|
|
53
|
-
convert_to_messages_v1,
|
|
54
|
-
)
|
|
55
|
-
from langchain_core.outputs import (
|
|
56
|
-
ChatGeneration,
|
|
57
|
-
ChatGenerationChunk,
|
|
58
|
-
)
|
|
59
|
-
from langchain_core.prompt_values import PromptValue
|
|
60
|
-
from langchain_core.rate_limiters import BaseRateLimiter
|
|
61
|
-
from langchain_core.runnables import RunnableMap, RunnablePassthrough
|
|
62
|
-
from langchain_core.runnables.base import RunnableSerializable
|
|
63
|
-
from langchain_core.runnables.config import ensure_config, run_in_executor
|
|
64
|
-
from langchain_core.tracers._streaming import _StreamingCallbackHandler
|
|
65
|
-
from langchain_core.utils.function_calling import (
|
|
66
|
-
convert_to_json_schema,
|
|
67
|
-
convert_to_openai_tool,
|
|
68
|
-
)
|
|
69
|
-
from langchain_core.utils.pydantic import TypeBaseModel, is_basemodel_subclass
|
|
70
|
-
from langchain_core.v1.messages import AIMessage as AIMessageV1
|
|
71
|
-
from langchain_core.v1.messages import AIMessageChunk as AIMessageChunkV1
|
|
72
|
-
from langchain_core.v1.messages import HumanMessage as HumanMessageV1
|
|
73
|
-
from langchain_core.v1.messages import MessageV1, add_ai_message_chunks
|
|
74
|
-
|
|
75
|
-
if TYPE_CHECKING:
|
|
76
|
-
from langchain_core.output_parsers.base import OutputParserLike
|
|
77
|
-
from langchain_core.runnables import Runnable, RunnableConfig
|
|
78
|
-
from langchain_core.tools import BaseTool
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def _generate_response_from_error(error: BaseException) -> list[AIMessageV1]:
|
|
82
|
-
if hasattr(error, "response"):
|
|
83
|
-
response = error.response
|
|
84
|
-
metadata: dict = {}
|
|
85
|
-
if hasattr(response, "headers"):
|
|
86
|
-
try:
|
|
87
|
-
metadata["headers"] = dict(response.headers)
|
|
88
|
-
except Exception:
|
|
89
|
-
metadata["headers"] = None
|
|
90
|
-
if hasattr(response, "status_code"):
|
|
91
|
-
metadata["status_code"] = response.status_code
|
|
92
|
-
if hasattr(error, "request_id"):
|
|
93
|
-
metadata["request_id"] = error.request_id
|
|
94
|
-
# Permit response_metadata without model_name, model_provider fields
|
|
95
|
-
generations = [AIMessageV1(content=[], response_metadata=metadata)] # type: ignore[arg-type]
|
|
96
|
-
else:
|
|
97
|
-
generations = []
|
|
98
|
-
|
|
99
|
-
return generations
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def _format_for_tracing(messages: Sequence[MessageV1]) -> list[MessageV1]:
|
|
103
|
-
"""Format messages for tracing in on_chat_model_start.
|
|
104
|
-
|
|
105
|
-
- Update image content blocks to OpenAI Chat Completions format (backward
|
|
106
|
-
compatibility).
|
|
107
|
-
- Add "type" key to content blocks that have a single key.
|
|
108
|
-
|
|
109
|
-
Args:
|
|
110
|
-
messages: List of messages to format.
|
|
111
|
-
|
|
112
|
-
Returns:
|
|
113
|
-
List of messages formatted for tracing.
|
|
114
|
-
"""
|
|
115
|
-
messages_to_trace = []
|
|
116
|
-
for message in messages:
|
|
117
|
-
message_to_trace = message
|
|
118
|
-
for idx, block in enumerate(message.content):
|
|
119
|
-
# Update image content blocks to OpenAI # Chat Completions format.
|
|
120
|
-
if (
|
|
121
|
-
block["type"] == "image"
|
|
122
|
-
and is_data_content_block(block) # type: ignore[arg-type] # permit unnecessary runtime check
|
|
123
|
-
and block.get("source_type") != "id"
|
|
124
|
-
):
|
|
125
|
-
if message_to_trace is message:
|
|
126
|
-
# Shallow copy
|
|
127
|
-
message_to_trace = copy.copy(message)
|
|
128
|
-
message_to_trace.content = list(message_to_trace.content)
|
|
129
|
-
|
|
130
|
-
# TODO: for tracing purposes we store non-standard types (OpenAI format)
|
|
131
|
-
# in message content. Consider typing these block formats.
|
|
132
|
-
message_to_trace.content[idx] = convert_to_openai_image_block(block) # type: ignore[arg-type, call-overload]
|
|
133
|
-
else:
|
|
134
|
-
pass
|
|
135
|
-
messages_to_trace.append(message_to_trace)
|
|
136
|
-
|
|
137
|
-
return messages_to_trace
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
def generate_from_stream(stream: Iterator[AIMessageChunkV1]) -> AIMessageV1:
|
|
141
|
-
"""Generate from a stream.
|
|
142
|
-
|
|
143
|
-
Args:
|
|
144
|
-
stream: Iterator of AIMessageChunkV1.
|
|
145
|
-
|
|
146
|
-
Returns:
|
|
147
|
-
AIMessageV1: aggregated message.
|
|
148
|
-
"""
|
|
149
|
-
generation = next(stream, None)
|
|
150
|
-
if generation:
|
|
151
|
-
generation += list(stream)
|
|
152
|
-
if generation is None:
|
|
153
|
-
msg = "No generations found in stream."
|
|
154
|
-
raise ValueError(msg)
|
|
155
|
-
return generation.to_message()
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
async def agenerate_from_stream(
|
|
159
|
-
stream: AsyncIterator[AIMessageChunkV1],
|
|
160
|
-
) -> AIMessageV1:
|
|
161
|
-
"""Async generate from a stream.
|
|
162
|
-
|
|
163
|
-
Args:
|
|
164
|
-
stream: Iterator of AIMessageChunkV1.
|
|
165
|
-
|
|
166
|
-
Returns:
|
|
167
|
-
AIMessageV1: aggregated message.
|
|
168
|
-
"""
|
|
169
|
-
chunks = [chunk async for chunk in stream]
|
|
170
|
-
return await run_in_executor(None, generate_from_stream, iter(chunks))
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
def _format_ls_structured_output(ls_structured_output_format: Optional[dict]) -> dict:
|
|
174
|
-
if ls_structured_output_format:
|
|
175
|
-
try:
|
|
176
|
-
ls_structured_output_format_dict = {
|
|
177
|
-
"ls_structured_output_format": {
|
|
178
|
-
"kwargs": ls_structured_output_format.get("kwargs", {}),
|
|
179
|
-
"schema": convert_to_json_schema(
|
|
180
|
-
ls_structured_output_format["schema"]
|
|
181
|
-
),
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
except ValueError:
|
|
185
|
-
ls_structured_output_format_dict = {}
|
|
186
|
-
else:
|
|
187
|
-
ls_structured_output_format_dict = {}
|
|
188
|
-
|
|
189
|
-
return ls_structured_output_format_dict
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
class BaseChatModel(RunnableSerializable[LanguageModelInput, AIMessageV1], ABC):
|
|
193
|
-
"""Base class for chat models.
|
|
194
|
-
|
|
195
|
-
Key imperative methods:
|
|
196
|
-
Methods that actually call the underlying model.
|
|
197
|
-
|
|
198
|
-
+---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
|
|
199
|
-
| Method | Input | Output | Description |
|
|
200
|
-
+===========================+================================================================+=====================================================================+==================================================================================================+
|
|
201
|
-
| `invoke` | str | list[dict | tuple | BaseMessage] | PromptValue | BaseMessage | A single chat model call. |
|
|
202
|
-
+---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
|
|
203
|
-
| `ainvoke` | ''' | BaseMessage | Defaults to running invoke in an async executor. |
|
|
204
|
-
+---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
|
|
205
|
-
| `stream` | ''' | Iterator[BaseMessageChunk] | Defaults to yielding output of invoke. |
|
|
206
|
-
+---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
|
|
207
|
-
| `astream` | ''' | AsyncIterator[BaseMessageChunk] | Defaults to yielding output of ainvoke. |
|
|
208
|
-
+---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
|
|
209
|
-
| `astream_events` | ''' | AsyncIterator[StreamEvent] | Event types: 'on_chat_model_start', 'on_chat_model_stream', 'on_chat_model_end'. |
|
|
210
|
-
+---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
|
|
211
|
-
| `batch` | list['''] | list[BaseMessage] | Defaults to running invoke in concurrent threads. |
|
|
212
|
-
+---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
|
|
213
|
-
| `abatch` | list['''] | list[BaseMessage] | Defaults to running ainvoke in concurrent threads. |
|
|
214
|
-
+---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
|
|
215
|
-
| `batch_as_completed` | list['''] | Iterator[tuple[int, Union[BaseMessage, Exception]]] | Defaults to running invoke in concurrent threads. |
|
|
216
|
-
+---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
|
|
217
|
-
| `abatch_as_completed` | list['''] | AsyncIterator[tuple[int, Union[BaseMessage, Exception]]] | Defaults to running ainvoke in concurrent threads. |
|
|
218
|
-
+---------------------------+----------------------------------------------------------------+---------------------------------------------------------------------+--------------------------------------------------------------------------------------------------+
|
|
219
|
-
|
|
220
|
-
This table provides a brief overview of the main imperative methods. Please see the base Runnable reference for full documentation.
|
|
221
|
-
|
|
222
|
-
Key declarative methods:
|
|
223
|
-
Methods for creating another Runnable using the ChatModel.
|
|
224
|
-
|
|
225
|
-
+----------------------------------+-----------------------------------------------------------------------------------------------------------+
|
|
226
|
-
| Method | Description |
|
|
227
|
-
+==================================+===========================================================================================================+
|
|
228
|
-
| `bind_tools` | Create ChatModel that can call tools. |
|
|
229
|
-
+----------------------------------+-----------------------------------------------------------------------------------------------------------+
|
|
230
|
-
| `with_structured_output` | Create wrapper that structures model output using schema. |
|
|
231
|
-
+----------------------------------+-----------------------------------------------------------------------------------------------------------+
|
|
232
|
-
| `with_retry` | Create wrapper that retries model calls on failure. |
|
|
233
|
-
+----------------------------------+-----------------------------------------------------------------------------------------------------------+
|
|
234
|
-
| `with_fallbacks` | Create wrapper that falls back to other models on failure. |
|
|
235
|
-
+----------------------------------+-----------------------------------------------------------------------------------------------------------+
|
|
236
|
-
| `configurable_fields` | Specify init args of the model that can be configured at runtime via the RunnableConfig. |
|
|
237
|
-
+----------------------------------+-----------------------------------------------------------------------------------------------------------+
|
|
238
|
-
| `configurable_alternatives` | Specify alternative models which can be swapped in at runtime via the RunnableConfig. |
|
|
239
|
-
+----------------------------------+-----------------------------------------------------------------------------------------------------------+
|
|
240
|
-
|
|
241
|
-
This table provides a brief overview of the main declarative methods. Please see the reference for each method for full documentation.
|
|
242
|
-
|
|
243
|
-
Creating custom chat model:
|
|
244
|
-
Custom chat model implementations should inherit from this class.
|
|
245
|
-
Please reference the table below for information about which
|
|
246
|
-
methods and properties are required or optional for implementations.
|
|
247
|
-
|
|
248
|
-
+----------------------------------+--------------------------------------------------------------------+-------------------+
|
|
249
|
-
| Method/Property | Description | Required/Optional |
|
|
250
|
-
+==================================+====================================================================+===================+
|
|
251
|
-
| `_generate` | Use to generate a chat result from a prompt | Required |
|
|
252
|
-
+----------------------------------+--------------------------------------------------------------------+-------------------+
|
|
253
|
-
| `_llm_type` (property) | Used to uniquely identify the type of the model. Used for logging. | Required |
|
|
254
|
-
+----------------------------------+--------------------------------------------------------------------+-------------------+
|
|
255
|
-
| `_identifying_params` (property) | Represent model parameterization for tracing purposes. | Optional |
|
|
256
|
-
+----------------------------------+--------------------------------------------------------------------+-------------------+
|
|
257
|
-
| `_stream` | Use to implement streaming | Optional |
|
|
258
|
-
+----------------------------------+--------------------------------------------------------------------+-------------------+
|
|
259
|
-
| `_agenerate` | Use to implement a native async method | Optional |
|
|
260
|
-
+----------------------------------+--------------------------------------------------------------------+-------------------+
|
|
261
|
-
| `_astream` | Use to implement async version of `_stream` | Optional |
|
|
262
|
-
+----------------------------------+--------------------------------------------------------------------+-------------------+
|
|
263
|
-
|
|
264
|
-
Follow the guide for more information on how to implement a custom Chat Model:
|
|
265
|
-
[Guide](https://python.langchain.com/docs/how_to/custom_chat_model/).
|
|
266
|
-
|
|
267
|
-
""" # noqa: E501
|
|
268
|
-
|
|
269
|
-
rate_limiter: Optional[BaseRateLimiter] = Field(default=None, exclude=True)
|
|
270
|
-
"An optional rate limiter to use for limiting the number of requests."
|
|
271
|
-
|
|
272
|
-
disable_streaming: Union[bool, Literal["tool_calling"]] = False
|
|
273
|
-
"""Whether to disable streaming for this model.
|
|
274
|
-
|
|
275
|
-
If streaming is bypassed, then ``stream()``/``astream()``/``astream_events()`` will
|
|
276
|
-
defer to ``invoke()``/``ainvoke()``.
|
|
277
|
-
|
|
278
|
-
- If True, will always bypass streaming case.
|
|
279
|
-
- If ``'tool_calling'``, will bypass streaming case only when the model is called
|
|
280
|
-
with a ``tools`` keyword argument. In other words, LangChain will automatically
|
|
281
|
-
switch to non-streaming behavior (``invoke()``) only when the tools argument is
|
|
282
|
-
provided. This offers the best of both worlds.
|
|
283
|
-
- If False (default), will always use streaming case if available.
|
|
284
|
-
|
|
285
|
-
The main reason for this flag is that code might be written using ``.stream()`` and
|
|
286
|
-
a user may want to swap out a given model for another model whose the implementation
|
|
287
|
-
does not properly support streaming.
|
|
288
|
-
"""
|
|
289
|
-
|
|
290
|
-
cache: Union[BaseCache, bool, None] = Field(default=None, exclude=True)
|
|
291
|
-
"""Whether to cache the response.
|
|
292
|
-
|
|
293
|
-
* If true, will use the global cache.
|
|
294
|
-
* If false, will not use a cache
|
|
295
|
-
* If None, will use the global cache if it's set, otherwise no cache.
|
|
296
|
-
* If instance of BaseCache, will use the provided cache.
|
|
297
|
-
|
|
298
|
-
Caching is not currently supported for streaming methods of models.
|
|
299
|
-
"""
|
|
300
|
-
verbose: bool = Field(default_factory=_get_verbosity, exclude=True, repr=False)
|
|
301
|
-
"""Whether to print out response text."""
|
|
302
|
-
callbacks: Callbacks = Field(default=None, exclude=True)
|
|
303
|
-
"""Callbacks to add to the run trace."""
|
|
304
|
-
tags: Optional[list[str]] = Field(default=None, exclude=True)
|
|
305
|
-
"""Tags to add to the run trace."""
|
|
306
|
-
metadata: Optional[dict[str, Any]] = Field(default=None, exclude=True)
|
|
307
|
-
"""Metadata to add to the run trace."""
|
|
308
|
-
custom_get_token_ids: Optional[Callable[[str], list[int]]] = Field(
|
|
309
|
-
default=None, exclude=True
|
|
310
|
-
)
|
|
311
|
-
"""Optional encoder to use for counting tokens."""
|
|
312
|
-
|
|
313
|
-
model_config = ConfigDict(
|
|
314
|
-
arbitrary_types_allowed=True,
|
|
315
|
-
)
|
|
316
|
-
|
|
317
|
-
@cached_property
|
|
318
|
-
def _serialized(self) -> dict[str, Any]:
|
|
319
|
-
return dumpd(self)
|
|
320
|
-
|
|
321
|
-
# --- Runnable methods ---
|
|
322
|
-
|
|
323
|
-
@field_validator("verbose", mode="before")
|
|
324
|
-
def set_verbose(cls, verbose: Optional[bool]) -> bool: # noqa: FBT001
|
|
325
|
-
"""If verbose is None, set it.
|
|
326
|
-
|
|
327
|
-
This allows users to pass in None as verbose to access the global setting.
|
|
328
|
-
|
|
329
|
-
Args:
|
|
330
|
-
verbose: The verbosity setting to use.
|
|
331
|
-
|
|
332
|
-
Returns:
|
|
333
|
-
The verbosity setting to use.
|
|
334
|
-
"""
|
|
335
|
-
if verbose is None:
|
|
336
|
-
return _get_verbosity()
|
|
337
|
-
return verbose
|
|
338
|
-
|
|
339
|
-
@property
|
|
340
|
-
@override
|
|
341
|
-
def InputType(self) -> TypeAlias:
|
|
342
|
-
"""Get the input type for this runnable."""
|
|
343
|
-
from langchain_core.prompt_values import (
|
|
344
|
-
ChatPromptValueConcrete,
|
|
345
|
-
StringPromptValue,
|
|
346
|
-
)
|
|
347
|
-
|
|
348
|
-
# This is a version of LanguageModelInput which replaces the abstract
|
|
349
|
-
# base class BaseMessage with a union of its subclasses, which makes
|
|
350
|
-
# for a much better schema.
|
|
351
|
-
return Union[
|
|
352
|
-
str,
|
|
353
|
-
Union[StringPromptValue, ChatPromptValueConcrete],
|
|
354
|
-
list[MessageV1],
|
|
355
|
-
]
|
|
356
|
-
|
|
357
|
-
@property
|
|
358
|
-
@override
|
|
359
|
-
def OutputType(self) -> Any:
|
|
360
|
-
"""Get the output type for this runnable."""
|
|
361
|
-
return AIMessageV1
|
|
362
|
-
|
|
363
|
-
def _convert_input(self, model_input: LanguageModelInput) -> list[MessageV1]:
|
|
364
|
-
if isinstance(model_input, PromptValue):
|
|
365
|
-
return model_input.to_messages(message_version="v1")
|
|
366
|
-
if isinstance(model_input, str):
|
|
367
|
-
return [HumanMessageV1(content=model_input)]
|
|
368
|
-
if isinstance(model_input, Sequence):
|
|
369
|
-
return convert_to_messages_v1(model_input)
|
|
370
|
-
msg = (
|
|
371
|
-
f"Invalid input type {type(model_input)}. "
|
|
372
|
-
"Must be a PromptValue, str, or list of Messages."
|
|
373
|
-
)
|
|
374
|
-
raise ValueError(msg)
|
|
375
|
-
|
|
376
|
-
def _should_stream(
|
|
377
|
-
self,
|
|
378
|
-
*,
|
|
379
|
-
async_api: bool,
|
|
380
|
-
run_manager: Optional[
|
|
381
|
-
Union[CallbackManagerForLLMRun, AsyncCallbackManagerForLLMRun]
|
|
382
|
-
] = None,
|
|
383
|
-
**kwargs: Any,
|
|
384
|
-
) -> bool:
|
|
385
|
-
"""Determine if a given model call should hit the streaming API."""
|
|
386
|
-
sync_not_implemented = type(self)._stream == BaseChatModel._stream # noqa: SLF001
|
|
387
|
-
async_not_implemented = type(self)._astream == BaseChatModel._astream # noqa: SLF001
|
|
388
|
-
|
|
389
|
-
# Check if streaming is implemented.
|
|
390
|
-
if (not async_api) and sync_not_implemented:
|
|
391
|
-
return False
|
|
392
|
-
# Note, since async falls back to sync we check both here.
|
|
393
|
-
if async_api and async_not_implemented and sync_not_implemented:
|
|
394
|
-
return False
|
|
395
|
-
|
|
396
|
-
# Check if streaming has been disabled on this instance.
|
|
397
|
-
if self.disable_streaming is True:
|
|
398
|
-
return False
|
|
399
|
-
# We assume tools are passed in via "tools" kwarg in all models.
|
|
400
|
-
if self.disable_streaming == "tool_calling" and kwargs.get("tools"):
|
|
401
|
-
return False
|
|
402
|
-
|
|
403
|
-
# Check if a runtime streaming flag has been passed in.
|
|
404
|
-
if "stream" in kwargs:
|
|
405
|
-
return kwargs["stream"]
|
|
406
|
-
|
|
407
|
-
# Check if any streaming callback handlers have been passed in.
|
|
408
|
-
handlers = run_manager.handlers if run_manager else []
|
|
409
|
-
return any(isinstance(h, _StreamingCallbackHandler) for h in handlers)
|
|
410
|
-
|
|
411
|
-
@override
|
|
412
|
-
def invoke(
|
|
413
|
-
self,
|
|
414
|
-
input: LanguageModelInput,
|
|
415
|
-
config: Optional[RunnableConfig] = None,
|
|
416
|
-
**kwargs: Any,
|
|
417
|
-
) -> AIMessageV1:
|
|
418
|
-
config = ensure_config(config)
|
|
419
|
-
messages = self._convert_input(input)
|
|
420
|
-
ls_structured_output_format = kwargs.pop(
|
|
421
|
-
"ls_structured_output_format", None
|
|
422
|
-
) or kwargs.pop("structured_output_format", None)
|
|
423
|
-
ls_structured_output_format_dict = _format_ls_structured_output(
|
|
424
|
-
ls_structured_output_format
|
|
425
|
-
)
|
|
426
|
-
|
|
427
|
-
params = self._get_invocation_params(**kwargs)
|
|
428
|
-
options = {**kwargs, **ls_structured_output_format_dict}
|
|
429
|
-
inheritable_metadata = {
|
|
430
|
-
**(config.get("metadata") or {}),
|
|
431
|
-
**self._get_ls_params(**kwargs),
|
|
432
|
-
}
|
|
433
|
-
callback_manager = CallbackManager.configure(
|
|
434
|
-
config.get("callbacks"),
|
|
435
|
-
self.callbacks,
|
|
436
|
-
self.verbose,
|
|
437
|
-
config.get("tags"),
|
|
438
|
-
self.tags,
|
|
439
|
-
inheritable_metadata,
|
|
440
|
-
self.metadata,
|
|
441
|
-
)
|
|
442
|
-
(run_manager,) = callback_manager.on_chat_model_start(
|
|
443
|
-
self._serialized,
|
|
444
|
-
_format_for_tracing(messages),
|
|
445
|
-
invocation_params=params,
|
|
446
|
-
options=options,
|
|
447
|
-
name=config.get("run_name"),
|
|
448
|
-
run_id=config.pop("run_id", None),
|
|
449
|
-
batch_size=1,
|
|
450
|
-
)
|
|
451
|
-
|
|
452
|
-
if self.rate_limiter:
|
|
453
|
-
self.rate_limiter.acquire(blocking=True)
|
|
454
|
-
|
|
455
|
-
input_messages = _normalize_messages_v1(messages)
|
|
456
|
-
|
|
457
|
-
if self._should_stream(async_api=False, **kwargs):
|
|
458
|
-
chunks: list[AIMessageChunkV1] = []
|
|
459
|
-
try:
|
|
460
|
-
for msg in self._stream(input_messages, **kwargs):
|
|
461
|
-
run_manager.on_llm_new_token(msg.text)
|
|
462
|
-
chunks.append(msg)
|
|
463
|
-
except BaseException as e:
|
|
464
|
-
run_manager.on_llm_error(e, response=_generate_response_from_error(e))
|
|
465
|
-
raise
|
|
466
|
-
full_message = add_ai_message_chunks(chunks[0], *chunks[1:]).to_message()
|
|
467
|
-
else:
|
|
468
|
-
try:
|
|
469
|
-
full_message = self._invoke(input_messages, **kwargs)
|
|
470
|
-
except BaseException as e:
|
|
471
|
-
run_manager.on_llm_error(e)
|
|
472
|
-
raise
|
|
473
|
-
|
|
474
|
-
run_manager.on_llm_end(full_message)
|
|
475
|
-
return full_message
|
|
476
|
-
|
|
477
|
-
@override
|
|
478
|
-
async def ainvoke(
|
|
479
|
-
self,
|
|
480
|
-
input: LanguageModelInput,
|
|
481
|
-
config: Optional[RunnableConfig] = None,
|
|
482
|
-
**kwargs: Any,
|
|
483
|
-
) -> AIMessageV1:
|
|
484
|
-
config = ensure_config(config)
|
|
485
|
-
messages = self._convert_input(input)
|
|
486
|
-
ls_structured_output_format = kwargs.pop(
|
|
487
|
-
"ls_structured_output_format", None
|
|
488
|
-
) or kwargs.pop("structured_output_format", None)
|
|
489
|
-
ls_structured_output_format_dict = _format_ls_structured_output(
|
|
490
|
-
ls_structured_output_format
|
|
491
|
-
)
|
|
492
|
-
|
|
493
|
-
params = self._get_invocation_params(**kwargs)
|
|
494
|
-
options = {**kwargs, **ls_structured_output_format_dict}
|
|
495
|
-
inheritable_metadata = {
|
|
496
|
-
**(config.get("metadata") or {}),
|
|
497
|
-
**self._get_ls_params(**kwargs),
|
|
498
|
-
}
|
|
499
|
-
callback_manager = AsyncCallbackManager.configure(
|
|
500
|
-
config.get("callbacks"),
|
|
501
|
-
self.callbacks,
|
|
502
|
-
self.verbose,
|
|
503
|
-
config.get("tags"),
|
|
504
|
-
self.tags,
|
|
505
|
-
inheritable_metadata,
|
|
506
|
-
self.metadata,
|
|
507
|
-
)
|
|
508
|
-
(run_manager,) = await callback_manager.on_chat_model_start(
|
|
509
|
-
self._serialized,
|
|
510
|
-
_format_for_tracing(messages),
|
|
511
|
-
invocation_params=params,
|
|
512
|
-
options=options,
|
|
513
|
-
name=config.get("run_name"),
|
|
514
|
-
run_id=config.pop("run_id", None),
|
|
515
|
-
batch_size=1,
|
|
516
|
-
)
|
|
517
|
-
|
|
518
|
-
if self.rate_limiter:
|
|
519
|
-
await self.rate_limiter.aacquire(blocking=True)
|
|
520
|
-
|
|
521
|
-
# TODO: type openai image, audio, file types and permit in MessageV1
|
|
522
|
-
input_messages = _normalize_messages_v1(messages)
|
|
523
|
-
|
|
524
|
-
if self._should_stream(async_api=True, **kwargs):
|
|
525
|
-
chunks: list[AIMessageChunkV1] = []
|
|
526
|
-
try:
|
|
527
|
-
async for msg in self._astream(input_messages, **kwargs):
|
|
528
|
-
await run_manager.on_llm_new_token(msg.text)
|
|
529
|
-
chunks.append(msg)
|
|
530
|
-
except BaseException as e:
|
|
531
|
-
await run_manager.on_llm_error(
|
|
532
|
-
e, response=_generate_response_from_error(e)
|
|
533
|
-
)
|
|
534
|
-
raise
|
|
535
|
-
full_message = add_ai_message_chunks(chunks[0], *chunks[1:]).to_message()
|
|
536
|
-
else:
|
|
537
|
-
try:
|
|
538
|
-
full_message = await self._ainvoke(input_messages, **kwargs)
|
|
539
|
-
except BaseException as e:
|
|
540
|
-
await run_manager.on_llm_error(
|
|
541
|
-
e, response=_generate_response_from_error(e)
|
|
542
|
-
)
|
|
543
|
-
raise
|
|
544
|
-
|
|
545
|
-
await run_manager.on_llm_end(full_message)
|
|
546
|
-
return full_message
|
|
547
|
-
|
|
548
|
-
@override
|
|
549
|
-
def stream(
|
|
550
|
-
self,
|
|
551
|
-
input: LanguageModelInput,
|
|
552
|
-
config: Optional[RunnableConfig] = None,
|
|
553
|
-
**kwargs: Any,
|
|
554
|
-
) -> Iterator[AIMessageChunkV1]:
|
|
555
|
-
if not self._should_stream(async_api=False, **{**kwargs, "stream": True}):
|
|
556
|
-
# model doesn't implement streaming, so use default implementation
|
|
557
|
-
yield cast(
|
|
558
|
-
"AIMessageChunkV1",
|
|
559
|
-
self.invoke(input, config=config, **kwargs),
|
|
560
|
-
)
|
|
561
|
-
else:
|
|
562
|
-
config = ensure_config(config)
|
|
563
|
-
messages = self._convert_input(input)
|
|
564
|
-
ls_structured_output_format = kwargs.pop(
|
|
565
|
-
"ls_structured_output_format", None
|
|
566
|
-
) or kwargs.pop("structured_output_format", None)
|
|
567
|
-
ls_structured_output_format_dict = _format_ls_structured_output(
|
|
568
|
-
ls_structured_output_format
|
|
569
|
-
)
|
|
570
|
-
|
|
571
|
-
params = self._get_invocation_params(**kwargs)
|
|
572
|
-
options = {**kwargs, **ls_structured_output_format_dict}
|
|
573
|
-
inheritable_metadata = {
|
|
574
|
-
**(config.get("metadata") or {}),
|
|
575
|
-
**self._get_ls_params(**kwargs),
|
|
576
|
-
}
|
|
577
|
-
callback_manager = CallbackManager.configure(
|
|
578
|
-
config.get("callbacks"),
|
|
579
|
-
self.callbacks,
|
|
580
|
-
self.verbose,
|
|
581
|
-
config.get("tags"),
|
|
582
|
-
self.tags,
|
|
583
|
-
inheritable_metadata,
|
|
584
|
-
self.metadata,
|
|
585
|
-
)
|
|
586
|
-
(run_manager,) = callback_manager.on_chat_model_start(
|
|
587
|
-
self._serialized,
|
|
588
|
-
_format_for_tracing(messages),
|
|
589
|
-
invocation_params=params,
|
|
590
|
-
options=options,
|
|
591
|
-
name=config.get("run_name"),
|
|
592
|
-
run_id=config.pop("run_id", None),
|
|
593
|
-
batch_size=1,
|
|
594
|
-
)
|
|
595
|
-
|
|
596
|
-
chunks: list[AIMessageChunkV1] = []
|
|
597
|
-
|
|
598
|
-
if self.rate_limiter:
|
|
599
|
-
self.rate_limiter.acquire(blocking=True)
|
|
600
|
-
|
|
601
|
-
try:
|
|
602
|
-
# TODO: replace this with something for new messages
|
|
603
|
-
input_messages = _normalize_messages_v1(messages)
|
|
604
|
-
for msg in self._stream(input_messages, **kwargs):
|
|
605
|
-
run_manager.on_llm_new_token(msg.text)
|
|
606
|
-
chunks.append(msg)
|
|
607
|
-
yield msg
|
|
608
|
-
|
|
609
|
-
if msg.chunk_position != "last":
|
|
610
|
-
yield (AIMessageChunkV1([], chunk_position="last"))
|
|
611
|
-
except BaseException as e:
|
|
612
|
-
run_manager.on_llm_error(e, response=_generate_response_from_error(e))
|
|
613
|
-
raise
|
|
614
|
-
|
|
615
|
-
msg = add_ai_message_chunks(chunks[0], *chunks[1:])
|
|
616
|
-
run_manager.on_llm_end(msg)
|
|
617
|
-
|
|
618
|
-
@override
|
|
619
|
-
async def astream(
|
|
620
|
-
self,
|
|
621
|
-
input: LanguageModelInput,
|
|
622
|
-
config: Optional[RunnableConfig] = None,
|
|
623
|
-
**kwargs: Any,
|
|
624
|
-
) -> AsyncIterator[AIMessageChunkV1]:
|
|
625
|
-
if not self._should_stream(async_api=True, **{**kwargs, "stream": True}):
|
|
626
|
-
# No async or sync stream is implemented, so fall back to ainvoke
|
|
627
|
-
yield cast(
|
|
628
|
-
"AIMessageChunkV1",
|
|
629
|
-
await self.ainvoke(input, config=config, **kwargs),
|
|
630
|
-
)
|
|
631
|
-
return
|
|
632
|
-
|
|
633
|
-
config = ensure_config(config)
|
|
634
|
-
messages = self._convert_input(input)
|
|
635
|
-
|
|
636
|
-
ls_structured_output_format = kwargs.pop(
|
|
637
|
-
"ls_structured_output_format", None
|
|
638
|
-
) or kwargs.pop("structured_output_format", None)
|
|
639
|
-
ls_structured_output_format_dict = _format_ls_structured_output(
|
|
640
|
-
ls_structured_output_format
|
|
641
|
-
)
|
|
642
|
-
|
|
643
|
-
params = self._get_invocation_params(**kwargs)
|
|
644
|
-
options = {**kwargs, **ls_structured_output_format_dict}
|
|
645
|
-
inheritable_metadata = {
|
|
646
|
-
**(config.get("metadata") or {}),
|
|
647
|
-
**self._get_ls_params(**kwargs),
|
|
648
|
-
}
|
|
649
|
-
callback_manager = AsyncCallbackManager.configure(
|
|
650
|
-
config.get("callbacks"),
|
|
651
|
-
self.callbacks,
|
|
652
|
-
self.verbose,
|
|
653
|
-
config.get("tags"),
|
|
654
|
-
self.tags,
|
|
655
|
-
inheritable_metadata,
|
|
656
|
-
self.metadata,
|
|
657
|
-
)
|
|
658
|
-
(run_manager,) = await callback_manager.on_chat_model_start(
|
|
659
|
-
self._serialized,
|
|
660
|
-
_format_for_tracing(messages),
|
|
661
|
-
invocation_params=params,
|
|
662
|
-
options=options,
|
|
663
|
-
name=config.get("run_name"),
|
|
664
|
-
run_id=config.pop("run_id", None),
|
|
665
|
-
batch_size=1,
|
|
666
|
-
)
|
|
667
|
-
|
|
668
|
-
if self.rate_limiter:
|
|
669
|
-
await self.rate_limiter.aacquire(blocking=True)
|
|
670
|
-
|
|
671
|
-
chunks: list[AIMessageChunkV1] = []
|
|
672
|
-
|
|
673
|
-
try:
|
|
674
|
-
input_messages = _normalize_messages_v1(messages)
|
|
675
|
-
async for msg in self._astream(
|
|
676
|
-
input_messages,
|
|
677
|
-
**kwargs,
|
|
678
|
-
):
|
|
679
|
-
await run_manager.on_llm_new_token(msg.text)
|
|
680
|
-
chunks.append(msg)
|
|
681
|
-
yield msg
|
|
682
|
-
if msg.chunk_position != "last":
|
|
683
|
-
yield (AIMessageChunkV1([], chunk_position="last"))
|
|
684
|
-
except BaseException as e:
|
|
685
|
-
await run_manager.on_llm_error(e, response=_generate_response_from_error(e))
|
|
686
|
-
raise
|
|
687
|
-
|
|
688
|
-
msg = add_ai_message_chunks(chunks[0], *chunks[1:])
|
|
689
|
-
await run_manager.on_llm_end(msg)
|
|
690
|
-
|
|
691
|
-
# --- Custom methods ---
|
|
692
|
-
|
|
693
|
-
def _combine_llm_outputs(self, llm_outputs: list[Optional[dict]]) -> dict: # noqa: ARG002
|
|
694
|
-
return {}
|
|
695
|
-
|
|
696
|
-
def _get_invocation_params(
|
|
697
|
-
self,
|
|
698
|
-
stop: Optional[list[str]] = None,
|
|
699
|
-
**kwargs: Any,
|
|
700
|
-
) -> dict:
|
|
701
|
-
params = self.dump()
|
|
702
|
-
params["stop"] = stop
|
|
703
|
-
return {**params, **kwargs}
|
|
704
|
-
|
|
705
|
-
def _get_ls_params(
|
|
706
|
-
self,
|
|
707
|
-
stop: Optional[list[str]] = None,
|
|
708
|
-
**kwargs: Any,
|
|
709
|
-
) -> LangSmithParams:
|
|
710
|
-
"""Get standard params for tracing."""
|
|
711
|
-
# get default provider from class name
|
|
712
|
-
default_provider = self.__class__.__name__
|
|
713
|
-
if default_provider.startswith("Chat"):
|
|
714
|
-
default_provider = default_provider[4:].lower()
|
|
715
|
-
elif default_provider.endswith("Chat"):
|
|
716
|
-
default_provider = default_provider[:-4]
|
|
717
|
-
default_provider = default_provider.lower()
|
|
718
|
-
|
|
719
|
-
ls_params = LangSmithParams(ls_provider=default_provider, ls_model_type="chat")
|
|
720
|
-
if stop:
|
|
721
|
-
ls_params["ls_stop"] = stop
|
|
722
|
-
|
|
723
|
-
# model
|
|
724
|
-
if hasattr(self, "model") and isinstance(self.model, str):
|
|
725
|
-
ls_params["ls_model_name"] = self.model
|
|
726
|
-
elif hasattr(self, "model_name") and isinstance(self.model_name, str):
|
|
727
|
-
ls_params["ls_model_name"] = self.model_name
|
|
728
|
-
|
|
729
|
-
# temperature
|
|
730
|
-
if "temperature" in kwargs and isinstance(kwargs["temperature"], float):
|
|
731
|
-
ls_params["ls_temperature"] = kwargs["temperature"]
|
|
732
|
-
elif hasattr(self, "temperature") and isinstance(self.temperature, float):
|
|
733
|
-
ls_params["ls_temperature"] = self.temperature
|
|
734
|
-
|
|
735
|
-
# max_tokens
|
|
736
|
-
if "max_tokens" in kwargs and isinstance(kwargs["max_tokens"], int):
|
|
737
|
-
ls_params["ls_max_tokens"] = kwargs["max_tokens"]
|
|
738
|
-
elif hasattr(self, "max_tokens") and isinstance(self.max_tokens, int):
|
|
739
|
-
ls_params["ls_max_tokens"] = self.max_tokens
|
|
740
|
-
|
|
741
|
-
return ls_params
|
|
742
|
-
|
|
743
|
-
def _get_llm_string(self, stop: Optional[list[str]] = None, **kwargs: Any) -> str:
|
|
744
|
-
params = self._get_invocation_params(stop=stop, **kwargs)
|
|
745
|
-
params = {**params, **kwargs}
|
|
746
|
-
return str(sorted(params.items()))
|
|
747
|
-
|
|
748
|
-
def _invoke(
|
|
749
|
-
self,
|
|
750
|
-
messages: list[MessageV1],
|
|
751
|
-
**kwargs: Any,
|
|
752
|
-
) -> AIMessageV1:
|
|
753
|
-
raise NotImplementedError
|
|
754
|
-
|
|
755
|
-
async def _ainvoke(
|
|
756
|
-
self,
|
|
757
|
-
messages: list[MessageV1],
|
|
758
|
-
**kwargs: Any,
|
|
759
|
-
) -> AIMessageV1:
|
|
760
|
-
return await run_in_executor(
|
|
761
|
-
None,
|
|
762
|
-
self._invoke,
|
|
763
|
-
messages,
|
|
764
|
-
**kwargs,
|
|
765
|
-
)
|
|
766
|
-
|
|
767
|
-
def _stream(
|
|
768
|
-
self,
|
|
769
|
-
messages: list[MessageV1],
|
|
770
|
-
**kwargs: Any,
|
|
771
|
-
) -> Iterator[AIMessageChunkV1]:
|
|
772
|
-
raise NotImplementedError
|
|
773
|
-
|
|
774
|
-
async def _astream(
|
|
775
|
-
self,
|
|
776
|
-
messages: list[MessageV1],
|
|
777
|
-
**kwargs: Any,
|
|
778
|
-
) -> AsyncIterator[AIMessageChunkV1]:
|
|
779
|
-
iterator = await run_in_executor(
|
|
780
|
-
None,
|
|
781
|
-
self._stream,
|
|
782
|
-
messages,
|
|
783
|
-
**kwargs,
|
|
784
|
-
)
|
|
785
|
-
done = object()
|
|
786
|
-
while True:
|
|
787
|
-
item = await run_in_executor(
|
|
788
|
-
None,
|
|
789
|
-
next,
|
|
790
|
-
iterator,
|
|
791
|
-
done,
|
|
792
|
-
)
|
|
793
|
-
if item is done:
|
|
794
|
-
break
|
|
795
|
-
yield item # type: ignore[misc]
|
|
796
|
-
|
|
797
|
-
@property
|
|
798
|
-
@abstractmethod
|
|
799
|
-
def _llm_type(self) -> str:
|
|
800
|
-
"""Return type of chat model."""
|
|
801
|
-
|
|
802
|
-
def dump(self, **kwargs: Any) -> dict: # noqa: ARG002
|
|
803
|
-
"""Return a dictionary of the LLM."""
|
|
804
|
-
starter_dict = dict(self._identifying_params)
|
|
805
|
-
starter_dict["_type"] = self._llm_type
|
|
806
|
-
return starter_dict
|
|
807
|
-
|
|
808
|
-
def bind_tools(
|
|
809
|
-
self,
|
|
810
|
-
tools: Sequence[
|
|
811
|
-
Union[typing.Dict[str, Any], type, Callable, BaseTool] # noqa: UP006
|
|
812
|
-
],
|
|
813
|
-
*,
|
|
814
|
-
tool_choice: Optional[Union[str]] = None,
|
|
815
|
-
**kwargs: Any,
|
|
816
|
-
) -> Runnable[LanguageModelInput, AIMessageV1]:
|
|
817
|
-
"""Bind tools to the model.
|
|
818
|
-
|
|
819
|
-
Args:
|
|
820
|
-
tools: Sequence of tools to bind to the model.
|
|
821
|
-
tool_choice: The tool to use. If "any" then any tool can be used.
|
|
822
|
-
|
|
823
|
-
Returns:
|
|
824
|
-
A Runnable that returns a message.
|
|
825
|
-
"""
|
|
826
|
-
raise NotImplementedError
|
|
827
|
-
|
|
828
|
-
def with_structured_output(
|
|
829
|
-
self,
|
|
830
|
-
schema: Union[typing.Dict, type], # noqa: UP006
|
|
831
|
-
*,
|
|
832
|
-
include_raw: bool = False,
|
|
833
|
-
**kwargs: Any,
|
|
834
|
-
) -> Runnable[LanguageModelInput, Union[typing.Dict, BaseModel]]: # noqa: UP006
|
|
835
|
-
"""Model wrapper that returns outputs formatted to match the given schema.
|
|
836
|
-
|
|
837
|
-
Args:
|
|
838
|
-
schema:
|
|
839
|
-
The output schema. Can be passed in as:
|
|
840
|
-
- an OpenAI function/tool schema,
|
|
841
|
-
- a JSON Schema,
|
|
842
|
-
- a TypedDict class,
|
|
843
|
-
- or a Pydantic class.
|
|
844
|
-
If ``schema`` is a Pydantic class then the model output will be a
|
|
845
|
-
Pydantic instance of that class, and the model-generated fields will be
|
|
846
|
-
validated by the Pydantic class. Otherwise the model output will be a
|
|
847
|
-
dict and will not be validated. See :meth:`langchain_core.utils.function_calling.convert_to_openai_tool`
|
|
848
|
-
for more on how to properly specify types and descriptions of
|
|
849
|
-
schema fields when specifying a Pydantic or TypedDict class.
|
|
850
|
-
|
|
851
|
-
include_raw:
|
|
852
|
-
If False then only the parsed structured output is returned. If
|
|
853
|
-
an error occurs during model output parsing it will be raised. If True
|
|
854
|
-
then both the raw model response (a BaseMessage) and the parsed model
|
|
855
|
-
response will be returned. If an error occurs during output parsing it
|
|
856
|
-
will be caught and returned as well. The final output is always a dict
|
|
857
|
-
with keys "raw", "parsed", and "parsing_error".
|
|
858
|
-
|
|
859
|
-
Returns:
|
|
860
|
-
A Runnable that takes same inputs as a :class:`langchain_core.language_models.chat.BaseChatModel`.
|
|
861
|
-
|
|
862
|
-
If ``include_raw`` is False and ``schema`` is a Pydantic class, Runnable outputs
|
|
863
|
-
an instance of ``schema`` (i.e., a Pydantic object).
|
|
864
|
-
|
|
865
|
-
Otherwise, if ``include_raw`` is False then Runnable outputs a dict.
|
|
866
|
-
|
|
867
|
-
If ``include_raw`` is True, then Runnable outputs a dict with keys:
|
|
868
|
-
- ``"raw"``: BaseMessage
|
|
869
|
-
- ``"parsed"``: None if there was a parsing error, otherwise the type depends on the ``schema`` as described above.
|
|
870
|
-
- ``"parsing_error"``: Optional[BaseException]
|
|
871
|
-
|
|
872
|
-
Example: Pydantic schema (include_raw=False):
|
|
873
|
-
.. code-block:: python
|
|
874
|
-
|
|
875
|
-
from pydantic import BaseModel
|
|
876
|
-
|
|
877
|
-
class AnswerWithJustification(BaseModel):
|
|
878
|
-
'''An answer to the user question along with justification for the answer.'''
|
|
879
|
-
answer: str
|
|
880
|
-
justification: str
|
|
881
|
-
|
|
882
|
-
llm = ChatModel(model="model-name", temperature=0)
|
|
883
|
-
structured_llm = llm.with_structured_output(AnswerWithJustification)
|
|
884
|
-
|
|
885
|
-
structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers")
|
|
886
|
-
|
|
887
|
-
# -> AnswerWithJustification(
|
|
888
|
-
# answer='They weigh the same',
|
|
889
|
-
# 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.'
|
|
890
|
-
# )
|
|
891
|
-
|
|
892
|
-
Example: Pydantic schema (include_raw=True):
|
|
893
|
-
.. code-block:: python
|
|
894
|
-
|
|
895
|
-
from pydantic import BaseModel
|
|
896
|
-
|
|
897
|
-
class AnswerWithJustification(BaseModel):
|
|
898
|
-
'''An answer to the user question along with justification for the answer.'''
|
|
899
|
-
answer: str
|
|
900
|
-
justification: str
|
|
901
|
-
|
|
902
|
-
llm = ChatModel(model="model-name", temperature=0)
|
|
903
|
-
structured_llm = llm.with_structured_output(AnswerWithJustification, include_raw=True)
|
|
904
|
-
|
|
905
|
-
structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers")
|
|
906
|
-
# -> {
|
|
907
|
-
# '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'}]}),
|
|
908
|
-
# '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.'),
|
|
909
|
-
# 'parsing_error': None
|
|
910
|
-
# }
|
|
911
|
-
|
|
912
|
-
Example: Dict schema (include_raw=False):
|
|
913
|
-
.. code-block:: python
|
|
914
|
-
|
|
915
|
-
from pydantic import BaseModel
|
|
916
|
-
from langchain_core.utils.function_calling import convert_to_openai_tool
|
|
917
|
-
|
|
918
|
-
class AnswerWithJustification(BaseModel):
|
|
919
|
-
'''An answer to the user question along with justification for the answer.'''
|
|
920
|
-
answer: str
|
|
921
|
-
justification: str
|
|
922
|
-
|
|
923
|
-
dict_schema = convert_to_openai_tool(AnswerWithJustification)
|
|
924
|
-
llm = ChatModel(model="model-name", temperature=0)
|
|
925
|
-
structured_llm = llm.with_structured_output(dict_schema)
|
|
926
|
-
|
|
927
|
-
structured_llm.invoke("What weighs more a pound of bricks or a pound of feathers")
|
|
928
|
-
# -> {
|
|
929
|
-
# 'answer': 'They weigh the same',
|
|
930
|
-
# '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.'
|
|
931
|
-
# }
|
|
932
|
-
|
|
933
|
-
.. versionchanged:: 0.2.26
|
|
934
|
-
|
|
935
|
-
Added support for TypedDict class.
|
|
936
|
-
""" # noqa: E501
|
|
937
|
-
_ = kwargs.pop("method", None)
|
|
938
|
-
_ = kwargs.pop("strict", None)
|
|
939
|
-
if kwargs:
|
|
940
|
-
msg = f"Received unsupported arguments {kwargs}"
|
|
941
|
-
raise ValueError(msg)
|
|
942
|
-
|
|
943
|
-
from langchain_core.output_parsers.openai_tools import (
|
|
944
|
-
JsonOutputKeyToolsParser,
|
|
945
|
-
PydanticToolsParser,
|
|
946
|
-
)
|
|
947
|
-
|
|
948
|
-
if type(self).bind_tools is BaseChatModel.bind_tools:
|
|
949
|
-
msg = "with_structured_output is not implemented for this model."
|
|
950
|
-
raise NotImplementedError(msg)
|
|
951
|
-
|
|
952
|
-
llm = self.bind_tools(
|
|
953
|
-
[schema],
|
|
954
|
-
tool_choice="any",
|
|
955
|
-
ls_structured_output_format={
|
|
956
|
-
"kwargs": {"method": "function_calling"},
|
|
957
|
-
"schema": schema,
|
|
958
|
-
},
|
|
959
|
-
)
|
|
960
|
-
if isinstance(schema, type) and is_basemodel_subclass(schema):
|
|
961
|
-
output_parser: OutputParserLike = PydanticToolsParser(
|
|
962
|
-
tools=[cast("TypeBaseModel", schema)], first_tool_only=True
|
|
963
|
-
)
|
|
964
|
-
else:
|
|
965
|
-
key_name = convert_to_openai_tool(schema)["function"]["name"]
|
|
966
|
-
output_parser = JsonOutputKeyToolsParser(
|
|
967
|
-
key_name=key_name, first_tool_only=True
|
|
968
|
-
)
|
|
969
|
-
if include_raw:
|
|
970
|
-
parser_assign = RunnablePassthrough.assign(
|
|
971
|
-
parsed=itemgetter("raw") | output_parser, parsing_error=lambda _: None
|
|
972
|
-
)
|
|
973
|
-
parser_none = RunnablePassthrough.assign(parsed=lambda _: None)
|
|
974
|
-
parser_with_fallback = parser_assign.with_fallbacks(
|
|
975
|
-
[parser_none], exception_key="parsing_error"
|
|
976
|
-
)
|
|
977
|
-
return RunnableMap(raw=llm) | parser_with_fallback
|
|
978
|
-
return llm | output_parser
|
|
979
|
-
|
|
980
|
-
@property
|
|
981
|
-
def _identifying_params(self) -> Mapping[str, Any]:
|
|
982
|
-
"""Get the identifying parameters."""
|
|
983
|
-
return self.lc_attributes
|
|
984
|
-
|
|
985
|
-
def get_token_ids(self, text: str) -> list[int]:
|
|
986
|
-
"""Return the ordered ids of the tokens in a text.
|
|
987
|
-
|
|
988
|
-
Args:
|
|
989
|
-
text: The string input to tokenize.
|
|
990
|
-
|
|
991
|
-
Returns:
|
|
992
|
-
A list of ids corresponding to the tokens in the text, in order they occur
|
|
993
|
-
in the text.
|
|
994
|
-
"""
|
|
995
|
-
if self.custom_get_token_ids is not None:
|
|
996
|
-
return self.custom_get_token_ids(text)
|
|
997
|
-
return _get_token_ids_default_method(text)
|
|
998
|
-
|
|
999
|
-
def get_num_tokens(self, text: str) -> int:
|
|
1000
|
-
"""Get the number of tokens present in the text.
|
|
1001
|
-
|
|
1002
|
-
Useful for checking if an input fits in a model's context window.
|
|
1003
|
-
|
|
1004
|
-
Args:
|
|
1005
|
-
text: The string input to tokenize.
|
|
1006
|
-
|
|
1007
|
-
Returns:
|
|
1008
|
-
The integer number of tokens in the text.
|
|
1009
|
-
"""
|
|
1010
|
-
return len(self.get_token_ids(text))
|
|
1011
|
-
|
|
1012
|
-
def get_num_tokens_from_messages(
|
|
1013
|
-
self,
|
|
1014
|
-
messages: list[MessageV1],
|
|
1015
|
-
tools: Optional[Sequence] = None,
|
|
1016
|
-
) -> int:
|
|
1017
|
-
"""Get the number of tokens in the messages.
|
|
1018
|
-
|
|
1019
|
-
Useful for checking if an input fits in a model's context window.
|
|
1020
|
-
|
|
1021
|
-
**Note**: the base implementation of get_num_tokens_from_messages ignores
|
|
1022
|
-
tool schemas.
|
|
1023
|
-
|
|
1024
|
-
Args:
|
|
1025
|
-
messages: The message inputs to tokenize.
|
|
1026
|
-
tools: If provided, sequence of dict, BaseModel, function, or BaseTools
|
|
1027
|
-
to be converted to tool schemas.
|
|
1028
|
-
|
|
1029
|
-
Returns:
|
|
1030
|
-
The sum of the number of tokens across the messages.
|
|
1031
|
-
"""
|
|
1032
|
-
messages_v0 = [convert_from_v1_message(message) for message in messages]
|
|
1033
|
-
if tools is not None:
|
|
1034
|
-
warnings.warn(
|
|
1035
|
-
"Counting tokens in tool schemas is not yet supported. Ignoring tools.",
|
|
1036
|
-
stacklevel=2,
|
|
1037
|
-
)
|
|
1038
|
-
return sum(self.get_num_tokens(get_buffer_string([m])) for m in messages_v0)
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
def _gen_info_and_msg_metadata(
|
|
1042
|
-
generation: Union[ChatGeneration, ChatGenerationChunk],
|
|
1043
|
-
) -> dict:
|
|
1044
|
-
return {
|
|
1045
|
-
**(generation.generation_info or {}),
|
|
1046
|
-
**generation.message.response_metadata,
|
|
1047
|
-
}
|