langchain-core 0.4.0.dev0__py3-none-any.whl → 1.0.0a1__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.

Files changed (74) hide show
  1. langchain_core/_api/beta_decorator.py +2 -2
  2. langchain_core/_api/deprecation.py +1 -1
  3. langchain_core/beta/runnables/context.py +1 -1
  4. langchain_core/callbacks/base.py +14 -23
  5. langchain_core/callbacks/file.py +13 -2
  6. langchain_core/callbacks/manager.py +74 -157
  7. langchain_core/callbacks/streaming_stdout.py +3 -4
  8. langchain_core/callbacks/usage.py +2 -12
  9. langchain_core/chat_history.py +6 -6
  10. langchain_core/documents/base.py +1 -1
  11. langchain_core/documents/compressor.py +9 -6
  12. langchain_core/indexing/base.py +2 -2
  13. langchain_core/language_models/_utils.py +230 -101
  14. langchain_core/language_models/base.py +35 -23
  15. langchain_core/language_models/chat_models.py +245 -53
  16. langchain_core/language_models/fake_chat_models.py +28 -81
  17. langchain_core/load/dump.py +3 -4
  18. langchain_core/messages/__init__.py +38 -22
  19. langchain_core/messages/ai.py +188 -30
  20. langchain_core/messages/base.py +164 -25
  21. langchain_core/messages/block_translators/__init__.py +89 -0
  22. langchain_core/messages/block_translators/anthropic.py +451 -0
  23. langchain_core/messages/block_translators/bedrock.py +45 -0
  24. langchain_core/messages/block_translators/bedrock_converse.py +47 -0
  25. langchain_core/messages/block_translators/google_genai.py +45 -0
  26. langchain_core/messages/block_translators/google_vertexai.py +47 -0
  27. langchain_core/messages/block_translators/groq.py +45 -0
  28. langchain_core/messages/block_translators/langchain_v0.py +297 -0
  29. langchain_core/messages/block_translators/ollama.py +45 -0
  30. langchain_core/messages/block_translators/openai.py +586 -0
  31. langchain_core/messages/{content_blocks.py → content.py} +346 -213
  32. langchain_core/messages/human.py +29 -9
  33. langchain_core/messages/system.py +29 -9
  34. langchain_core/messages/tool.py +94 -13
  35. langchain_core/messages/utils.py +32 -234
  36. langchain_core/output_parsers/base.py +14 -50
  37. langchain_core/output_parsers/json.py +2 -5
  38. langchain_core/output_parsers/list.py +2 -7
  39. langchain_core/output_parsers/openai_functions.py +5 -28
  40. langchain_core/output_parsers/openai_tools.py +49 -90
  41. langchain_core/output_parsers/pydantic.py +2 -3
  42. langchain_core/output_parsers/transform.py +12 -53
  43. langchain_core/output_parsers/xml.py +9 -17
  44. langchain_core/prompt_values.py +8 -112
  45. langchain_core/prompts/chat.py +1 -3
  46. langchain_core/runnables/base.py +500 -451
  47. langchain_core/runnables/branch.py +1 -1
  48. langchain_core/runnables/fallbacks.py +4 -4
  49. langchain_core/runnables/history.py +1 -1
  50. langchain_core/runnables/passthrough.py +3 -3
  51. langchain_core/runnables/retry.py +1 -1
  52. langchain_core/runnables/router.py +1 -1
  53. langchain_core/structured_query.py +3 -7
  54. langchain_core/tools/base.py +14 -41
  55. langchain_core/tools/convert.py +2 -22
  56. langchain_core/tools/retriever.py +1 -8
  57. langchain_core/tools/structured.py +2 -10
  58. langchain_core/tracers/_streaming.py +6 -7
  59. langchain_core/tracers/base.py +7 -14
  60. langchain_core/tracers/core.py +4 -27
  61. langchain_core/tracers/event_stream.py +4 -15
  62. langchain_core/tracers/langchain.py +3 -14
  63. langchain_core/tracers/log_stream.py +2 -3
  64. langchain_core/utils/_merge.py +45 -7
  65. langchain_core/utils/function_calling.py +22 -9
  66. langchain_core/utils/utils.py +29 -0
  67. langchain_core/version.py +1 -1
  68. {langchain_core-0.4.0.dev0.dist-info → langchain_core-1.0.0a1.dist-info}/METADATA +7 -9
  69. {langchain_core-0.4.0.dev0.dist-info → langchain_core-1.0.0a1.dist-info}/RECORD +71 -64
  70. langchain_core/v1/__init__.py +0 -1
  71. langchain_core/v1/chat_models.py +0 -1047
  72. langchain_core/v1/messages.py +0 -755
  73. {langchain_core-0.4.0.dev0.dist-info → langchain_core-1.0.0a1.dist-info}/WHEEL +0 -0
  74. {langchain_core-0.4.0.dev0.dist-info → langchain_core-1.0.0a1.dist-info}/entry_points.txt +0 -0
@@ -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
- }