langchain-core 0.3.72__py3-none-any.whl → 0.4.0.dev0__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 +1 -0
- langchain_core/_api/deprecation.py +2 -0
- langchain_core/beta/runnables/context.py +1 -0
- langchain_core/callbacks/base.py +23 -14
- langchain_core/callbacks/file.py +1 -0
- langchain_core/callbacks/manager.py +145 -19
- langchain_core/callbacks/streaming_stdout.py +4 -3
- langchain_core/callbacks/usage.py +15 -3
- langchain_core/chat_history.py +1 -0
- langchain_core/document_loaders/langsmith.py +2 -1
- langchain_core/documents/base.py +2 -0
- langchain_core/embeddings/fake.py +2 -0
- langchain_core/indexing/api.py +10 -0
- langchain_core/language_models/_utils.py +37 -0
- langchain_core/language_models/base.py +4 -1
- langchain_core/language_models/chat_models.py +48 -27
- langchain_core/language_models/fake_chat_models.py +71 -1
- langchain_core/language_models/llms.py +1 -0
- langchain_core/memory.py +1 -0
- langchain_core/messages/__init__.py +54 -0
- langchain_core/messages/ai.py +31 -18
- langchain_core/messages/content_blocks.py +1349 -69
- langchain_core/messages/human.py +1 -0
- langchain_core/messages/modifier.py +1 -1
- langchain_core/messages/tool.py +8 -83
- langchain_core/messages/utils.py +221 -6
- langchain_core/output_parsers/base.py +51 -14
- langchain_core/output_parsers/json.py +5 -2
- langchain_core/output_parsers/list.py +7 -2
- langchain_core/output_parsers/openai_functions.py +29 -5
- langchain_core/output_parsers/openai_tools.py +90 -47
- langchain_core/output_parsers/pydantic.py +3 -2
- langchain_core/output_parsers/transform.py +53 -12
- langchain_core/output_parsers/xml.py +14 -5
- langchain_core/outputs/llm_result.py +4 -1
- langchain_core/prompt_values.py +111 -7
- langchain_core/prompts/base.py +4 -0
- langchain_core/prompts/chat.py +3 -0
- langchain_core/prompts/few_shot.py +1 -0
- langchain_core/prompts/few_shot_with_templates.py +1 -0
- langchain_core/prompts/image.py +1 -0
- langchain_core/prompts/pipeline.py +1 -0
- langchain_core/prompts/prompt.py +1 -0
- langchain_core/prompts/structured.py +1 -0
- langchain_core/rate_limiters.py +1 -0
- langchain_core/retrievers.py +3 -0
- langchain_core/runnables/base.py +75 -57
- langchain_core/runnables/branch.py +1 -0
- langchain_core/runnables/config.py +2 -2
- langchain_core/runnables/configurable.py +2 -1
- langchain_core/runnables/fallbacks.py +3 -7
- langchain_core/runnables/graph.py +5 -3
- langchain_core/runnables/graph_ascii.py +1 -0
- langchain_core/runnables/graph_mermaid.py +1 -0
- langchain_core/runnables/history.py +1 -0
- langchain_core/runnables/passthrough.py +3 -0
- langchain_core/runnables/retry.py +1 -0
- langchain_core/runnables/router.py +1 -0
- langchain_core/runnables/schema.py +1 -0
- langchain_core/stores.py +3 -0
- langchain_core/tools/base.py +43 -11
- langchain_core/tools/convert.py +25 -3
- langchain_core/tools/retriever.py +8 -1
- langchain_core/tools/structured.py +10 -1
- langchain_core/tracers/base.py +14 -7
- langchain_core/tracers/context.py +1 -1
- langchain_core/tracers/core.py +27 -4
- langchain_core/tracers/event_stream.py +14 -3
- langchain_core/tracers/langchain.py +14 -3
- langchain_core/tracers/log_stream.py +4 -1
- langchain_core/utils/aiter.py +5 -0
- langchain_core/utils/function_calling.py +2 -1
- langchain_core/utils/iter.py +1 -0
- langchain_core/v1/__init__.py +1 -0
- langchain_core/v1/chat_models.py +1047 -0
- langchain_core/v1/messages.py +755 -0
- langchain_core/vectorstores/base.py +1 -0
- langchain_core/version.py +1 -1
- {langchain_core-0.3.72.dist-info → langchain_core-0.4.0.dev0.dist-info}/METADATA +1 -1
- {langchain_core-0.3.72.dist-info → langchain_core-0.4.0.dev0.dist-info}/RECORD +82 -79
- {langchain_core-0.3.72.dist-info → langchain_core-0.4.0.dev0.dist-info}/WHEEL +0 -0
- {langchain_core-0.3.72.dist-info → langchain_core-0.4.0.dev0.dist-info}/entry_points.txt +0 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import copy
|
|
1
2
|
import re
|
|
2
3
|
from collections.abc import Sequence
|
|
3
4
|
from typing import Optional
|
|
4
5
|
|
|
5
6
|
from langchain_core.messages import BaseMessage
|
|
7
|
+
from langchain_core.v1.messages import MessageV1
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
def _is_openai_data_block(block: dict) -> bool:
|
|
@@ -51,6 +53,7 @@ def _parse_data_uri(uri: str) -> Optional[dict]:
|
|
|
51
53
|
"mime_type": "image/jpeg",
|
|
52
54
|
"data": "/9j/4AAQSkZJRg...",
|
|
53
55
|
}
|
|
56
|
+
|
|
54
57
|
"""
|
|
55
58
|
regex = r"^data:(?P<mime_type>[^;]+);base64,(?P<data>.+)$"
|
|
56
59
|
match = re.match(regex, uri)
|
|
@@ -137,3 +140,37 @@ def _normalize_messages(messages: Sequence[BaseMessage]) -> list[BaseMessage]:
|
|
|
137
140
|
formatted_messages.append(formatted_message)
|
|
138
141
|
|
|
139
142
|
return formatted_messages
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _normalize_messages_v1(messages: Sequence[MessageV1]) -> list[MessageV1]:
|
|
146
|
+
"""Extend support for message formats.
|
|
147
|
+
|
|
148
|
+
Chat models implement support for images in OpenAI Chat Completions format, as well
|
|
149
|
+
as other multimodal data as standard data blocks. This function extends support to
|
|
150
|
+
audio and file data in OpenAI Chat Completions format by converting them to standard
|
|
151
|
+
data blocks.
|
|
152
|
+
"""
|
|
153
|
+
formatted_messages = []
|
|
154
|
+
for message in messages:
|
|
155
|
+
formatted_message = message
|
|
156
|
+
if isinstance(message.content, list):
|
|
157
|
+
for idx, block in enumerate(message.content):
|
|
158
|
+
if (
|
|
159
|
+
isinstance(block, dict)
|
|
160
|
+
# Subset to (PDF) files and audio, as most relevant chat models
|
|
161
|
+
# support images in OAI format (and some may not yet support the
|
|
162
|
+
# standard data block format)
|
|
163
|
+
and block.get("type") in {"file", "input_audio"}
|
|
164
|
+
and _is_openai_data_block(block) # type: ignore[arg-type]
|
|
165
|
+
):
|
|
166
|
+
if formatted_message is message:
|
|
167
|
+
formatted_message = copy.copy(message)
|
|
168
|
+
# Also shallow-copy content
|
|
169
|
+
formatted_message.content = list(formatted_message.content)
|
|
170
|
+
|
|
171
|
+
formatted_message.content[idx] = ( # type: ignore[call-overload]
|
|
172
|
+
_convert_openai_format_to_data_block(block) # type: ignore[arg-type]
|
|
173
|
+
)
|
|
174
|
+
formatted_messages.append(formatted_message)
|
|
175
|
+
|
|
176
|
+
return formatted_messages
|
|
@@ -31,6 +31,7 @@ from langchain_core.messages import (
|
|
|
31
31
|
from langchain_core.prompt_values import PromptValue
|
|
32
32
|
from langchain_core.runnables import Runnable, RunnableSerializable
|
|
33
33
|
from langchain_core.utils import get_pydantic_field_names
|
|
34
|
+
from langchain_core.v1.messages import AIMessage as AIMessageV1
|
|
34
35
|
|
|
35
36
|
if TYPE_CHECKING:
|
|
36
37
|
from langchain_core.outputs import LLMResult
|
|
@@ -85,7 +86,9 @@ def _get_token_ids_default_method(text: str) -> list[int]:
|
|
|
85
86
|
LanguageModelInput = Union[PromptValue, str, Sequence[MessageLikeRepresentation]]
|
|
86
87
|
LanguageModelOutput = Union[BaseMessage, str]
|
|
87
88
|
LanguageModelLike = Runnable[LanguageModelInput, LanguageModelOutput]
|
|
88
|
-
LanguageModelOutputVar = TypeVar(
|
|
89
|
+
LanguageModelOutputVar = TypeVar(
|
|
90
|
+
"LanguageModelOutputVar", BaseMessage, str, AIMessageV1
|
|
91
|
+
)
|
|
89
92
|
|
|
90
93
|
|
|
91
94
|
def _get_verbosity() -> bool:
|
|
@@ -11,22 +11,9 @@ from abc import ABC, abstractmethod
|
|
|
11
11
|
from collections.abc import AsyncIterator, Iterator, Sequence
|
|
12
12
|
from functools import cached_property
|
|
13
13
|
from operator import itemgetter
|
|
14
|
-
from typing import
|
|
15
|
-
TYPE_CHECKING,
|
|
16
|
-
Any,
|
|
17
|
-
Callable,
|
|
18
|
-
Literal,
|
|
19
|
-
Optional,
|
|
20
|
-
Union,
|
|
21
|
-
cast,
|
|
22
|
-
)
|
|
14
|
+
from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, Union, cast
|
|
23
15
|
|
|
24
|
-
from pydantic import
|
|
25
|
-
BaseModel,
|
|
26
|
-
ConfigDict,
|
|
27
|
-
Field,
|
|
28
|
-
model_validator,
|
|
29
|
-
)
|
|
16
|
+
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
30
17
|
from typing_extensions import override
|
|
31
18
|
|
|
32
19
|
from langchain_core._api import deprecated
|
|
@@ -63,6 +50,7 @@ from langchain_core.outputs import (
|
|
|
63
50
|
ChatGeneration,
|
|
64
51
|
ChatGenerationChunk,
|
|
65
52
|
ChatResult,
|
|
53
|
+
Generation,
|
|
66
54
|
LLMResult,
|
|
67
55
|
RunInfo,
|
|
68
56
|
)
|
|
@@ -653,6 +641,34 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
653
641
|
def _combine_llm_outputs(self, llm_outputs: list[Optional[dict]]) -> dict: # noqa: ARG002
|
|
654
642
|
return {}
|
|
655
643
|
|
|
644
|
+
def _convert_cached_generations(self, cache_val: list) -> list[ChatGeneration]:
|
|
645
|
+
"""Convert cached Generation objects to ChatGeneration objects.
|
|
646
|
+
|
|
647
|
+
Handle case where cache contains Generation objects instead of
|
|
648
|
+
ChatGeneration objects. This can happen due to serialization/deserialization
|
|
649
|
+
issues or legacy cache data (see #22389).
|
|
650
|
+
|
|
651
|
+
Args:
|
|
652
|
+
cache_val: List of cached generation objects.
|
|
653
|
+
|
|
654
|
+
Returns:
|
|
655
|
+
List of ChatGeneration objects.
|
|
656
|
+
"""
|
|
657
|
+
converted_generations = []
|
|
658
|
+
for gen in cache_val:
|
|
659
|
+
if isinstance(gen, Generation) and not isinstance(gen, ChatGeneration):
|
|
660
|
+
# Convert Generation to ChatGeneration by creating AIMessage
|
|
661
|
+
# from the text content
|
|
662
|
+
chat_gen = ChatGeneration(
|
|
663
|
+
message=AIMessage(content=gen.text),
|
|
664
|
+
generation_info=gen.generation_info,
|
|
665
|
+
)
|
|
666
|
+
converted_generations.append(chat_gen)
|
|
667
|
+
else:
|
|
668
|
+
# Already a ChatGeneration or other expected type
|
|
669
|
+
converted_generations.append(gen)
|
|
670
|
+
return converted_generations
|
|
671
|
+
|
|
656
672
|
def _get_invocation_params(
|
|
657
673
|
self,
|
|
658
674
|
stop: Optional[list[str]] = None,
|
|
@@ -1010,7 +1026,8 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1010
1026
|
prompt = dumps(messages)
|
|
1011
1027
|
cache_val = llm_cache.lookup(prompt, llm_string)
|
|
1012
1028
|
if isinstance(cache_val, list):
|
|
1013
|
-
|
|
1029
|
+
converted_generations = self._convert_cached_generations(cache_val)
|
|
1030
|
+
return ChatResult(generations=converted_generations)
|
|
1014
1031
|
elif self.cache is None:
|
|
1015
1032
|
pass
|
|
1016
1033
|
else:
|
|
@@ -1082,7 +1099,8 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1082
1099
|
prompt = dumps(messages)
|
|
1083
1100
|
cache_val = await llm_cache.alookup(prompt, llm_string)
|
|
1084
1101
|
if isinstance(cache_val, list):
|
|
1085
|
-
|
|
1102
|
+
converted_generations = self._convert_cached_generations(cache_val)
|
|
1103
|
+
return ChatResult(generations=converted_generations)
|
|
1086
1104
|
elif self.cache is None:
|
|
1087
1105
|
pass
|
|
1088
1106
|
else:
|
|
@@ -1367,12 +1385,13 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1367
1385
|
"""Model wrapper that returns outputs formatted to match the given schema.
|
|
1368
1386
|
|
|
1369
1387
|
Args:
|
|
1370
|
-
schema:
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1388
|
+
schema: The output schema. Can be passed in as:
|
|
1389
|
+
|
|
1390
|
+
- an OpenAI function/tool schema,
|
|
1391
|
+
- a JSON Schema,
|
|
1392
|
+
- a TypedDict class,
|
|
1393
|
+
- or a Pydantic class.
|
|
1394
|
+
|
|
1376
1395
|
If ``schema`` is a Pydantic class then the model output will be a
|
|
1377
1396
|
Pydantic instance of that class, and the model-generated fields will be
|
|
1378
1397
|
validated by the Pydantic class. Otherwise the model output will be a
|
|
@@ -1386,7 +1405,7 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1386
1405
|
then both the raw model response (a BaseMessage) and the parsed model
|
|
1387
1406
|
response will be returned. If an error occurs during output parsing it
|
|
1388
1407
|
will be caught and returned as well. The final output is always a dict
|
|
1389
|
-
with keys
|
|
1408
|
+
with keys ``'raw'``, ``'parsed'``, and ``'parsing_error'``.
|
|
1390
1409
|
|
|
1391
1410
|
Returns:
|
|
1392
1411
|
A Runnable that takes same inputs as a :class:`langchain_core.language_models.chat.BaseChatModel`.
|
|
@@ -1397,9 +1416,10 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1397
1416
|
Otherwise, if ``include_raw`` is False then Runnable outputs a dict.
|
|
1398
1417
|
|
|
1399
1418
|
If ``include_raw`` is True, then Runnable outputs a dict with keys:
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1419
|
+
|
|
1420
|
+
- ``'raw'``: BaseMessage
|
|
1421
|
+
- ``'parsed'``: None if there was a parsing error, otherwise the type depends on the ``schema`` as described above.
|
|
1422
|
+
- ``'parsing_error'``: Optional[BaseException]
|
|
1403
1423
|
|
|
1404
1424
|
Example: Pydantic schema (include_raw=False):
|
|
1405
1425
|
.. code-block:: python
|
|
@@ -1465,6 +1485,7 @@ class BaseChatModel(BaseLanguageModel[BaseMessage], ABC):
|
|
|
1465
1485
|
.. versionchanged:: 0.2.26
|
|
1466
1486
|
|
|
1467
1487
|
Added support for TypedDict class.
|
|
1488
|
+
|
|
1468
1489
|
""" # noqa: E501
|
|
1469
1490
|
_ = kwargs.pop("method", None)
|
|
1470
1491
|
_ = kwargs.pop("strict", None)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import re
|
|
5
5
|
import time
|
|
6
|
-
from collections.abc import AsyncIterator, Iterator
|
|
6
|
+
from collections.abc import AsyncIterator, Iterable, Iterator
|
|
7
7
|
from typing import Any, Optional, Union, cast
|
|
8
8
|
|
|
9
9
|
from typing_extensions import override
|
|
@@ -16,6 +16,10 @@ from langchain_core.language_models.chat_models import BaseChatModel, SimpleChat
|
|
|
16
16
|
from langchain_core.messages import AIMessage, AIMessageChunk, BaseMessage
|
|
17
17
|
from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult
|
|
18
18
|
from langchain_core.runnables import RunnableConfig
|
|
19
|
+
from langchain_core.v1.chat_models import BaseChatModel as BaseChatModelV1
|
|
20
|
+
from langchain_core.v1.messages import AIMessage as AIMessageV1
|
|
21
|
+
from langchain_core.v1.messages import AIMessageChunk as AIMessageChunkV1
|
|
22
|
+
from langchain_core.v1.messages import MessageV1
|
|
19
23
|
|
|
20
24
|
|
|
21
25
|
class FakeMessagesListChatModel(BaseChatModel):
|
|
@@ -367,3 +371,69 @@ class ParrotFakeChatModel(BaseChatModel):
|
|
|
367
371
|
@property
|
|
368
372
|
def _llm_type(self) -> str:
|
|
369
373
|
return "parrot-fake-chat-model"
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
class GenericFakeChatModelV1(BaseChatModelV1):
|
|
377
|
+
"""Generic fake chat model that can be used to test the chat model interface."""
|
|
378
|
+
|
|
379
|
+
messages: Optional[Iterator[Union[AIMessageV1, str]]] = None
|
|
380
|
+
message_chunks: Optional[Iterable[Union[AIMessageChunkV1, str]]] = None
|
|
381
|
+
|
|
382
|
+
@override
|
|
383
|
+
def _invoke(
|
|
384
|
+
self,
|
|
385
|
+
messages: list[MessageV1],
|
|
386
|
+
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
|
387
|
+
**kwargs: Any,
|
|
388
|
+
) -> AIMessageV1:
|
|
389
|
+
"""Top Level call."""
|
|
390
|
+
if self.messages is None:
|
|
391
|
+
error_msg = "Messages iterator is not set."
|
|
392
|
+
raise ValueError(error_msg)
|
|
393
|
+
message = next(self.messages)
|
|
394
|
+
return AIMessageV1(content=message) if isinstance(message, str) else message
|
|
395
|
+
|
|
396
|
+
@override
|
|
397
|
+
def _stream(
|
|
398
|
+
self,
|
|
399
|
+
messages: list[MessageV1],
|
|
400
|
+
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
|
401
|
+
**kwargs: Any,
|
|
402
|
+
) -> Iterator[AIMessageChunkV1]:
|
|
403
|
+
"""Top Level call."""
|
|
404
|
+
if self.message_chunks is None:
|
|
405
|
+
error_msg = "Message chunks iterator is not set."
|
|
406
|
+
raise ValueError(error_msg)
|
|
407
|
+
for chunk in self.message_chunks:
|
|
408
|
+
if isinstance(chunk, str):
|
|
409
|
+
yield AIMessageChunkV1(chunk)
|
|
410
|
+
else:
|
|
411
|
+
yield chunk
|
|
412
|
+
|
|
413
|
+
@property
|
|
414
|
+
def _llm_type(self) -> str:
|
|
415
|
+
return "generic-fake-chat-model"
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
class ParrotFakeChatModelV1(BaseChatModelV1):
|
|
419
|
+
"""Generic fake chat model that can be used to test the chat model interface.
|
|
420
|
+
|
|
421
|
+
* Chat model should be usable in both sync and async tests
|
|
422
|
+
"""
|
|
423
|
+
|
|
424
|
+
@override
|
|
425
|
+
def _invoke(
|
|
426
|
+
self,
|
|
427
|
+
messages: list[MessageV1],
|
|
428
|
+
stop: Optional[list[str]] = None,
|
|
429
|
+
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
|
430
|
+
**kwargs: Any,
|
|
431
|
+
) -> AIMessageV1:
|
|
432
|
+
"""Top Level call."""
|
|
433
|
+
if isinstance(messages[-1], AIMessageV1):
|
|
434
|
+
return messages[-1]
|
|
435
|
+
return AIMessageV1(content=messages[-1].content)
|
|
436
|
+
|
|
437
|
+
@property
|
|
438
|
+
def _llm_type(self) -> str:
|
|
439
|
+
return "parrot-fake-chat-model"
|
langchain_core/memory.py
CHANGED
|
@@ -33,6 +33,24 @@ if TYPE_CHECKING:
|
|
|
33
33
|
)
|
|
34
34
|
from langchain_core.messages.chat import ChatMessage, ChatMessageChunk
|
|
35
35
|
from langchain_core.messages.content_blocks import (
|
|
36
|
+
Annotation,
|
|
37
|
+
AudioContentBlock,
|
|
38
|
+
Citation,
|
|
39
|
+
CodeInterpreterCall,
|
|
40
|
+
CodeInterpreterOutput,
|
|
41
|
+
CodeInterpreterResult,
|
|
42
|
+
ContentBlock,
|
|
43
|
+
DataContentBlock,
|
|
44
|
+
FileContentBlock,
|
|
45
|
+
ImageContentBlock,
|
|
46
|
+
NonStandardAnnotation,
|
|
47
|
+
NonStandardContentBlock,
|
|
48
|
+
PlainTextContentBlock,
|
|
49
|
+
ReasoningContentBlock,
|
|
50
|
+
TextContentBlock,
|
|
51
|
+
VideoContentBlock,
|
|
52
|
+
WebSearchCall,
|
|
53
|
+
WebSearchResult,
|
|
36
54
|
convert_to_openai_data_block,
|
|
37
55
|
convert_to_openai_image_block,
|
|
38
56
|
is_data_content_block,
|
|
@@ -65,24 +83,42 @@ if TYPE_CHECKING:
|
|
|
65
83
|
__all__ = (
|
|
66
84
|
"AIMessage",
|
|
67
85
|
"AIMessageChunk",
|
|
86
|
+
"Annotation",
|
|
68
87
|
"AnyMessage",
|
|
88
|
+
"AudioContentBlock",
|
|
69
89
|
"BaseMessage",
|
|
70
90
|
"BaseMessageChunk",
|
|
71
91
|
"ChatMessage",
|
|
72
92
|
"ChatMessageChunk",
|
|
93
|
+
"Citation",
|
|
94
|
+
"CodeInterpreterCall",
|
|
95
|
+
"CodeInterpreterOutput",
|
|
96
|
+
"CodeInterpreterResult",
|
|
97
|
+
"ContentBlock",
|
|
98
|
+
"DataContentBlock",
|
|
99
|
+
"FileContentBlock",
|
|
73
100
|
"FunctionMessage",
|
|
74
101
|
"FunctionMessageChunk",
|
|
75
102
|
"HumanMessage",
|
|
76
103
|
"HumanMessageChunk",
|
|
104
|
+
"ImageContentBlock",
|
|
77
105
|
"InvalidToolCall",
|
|
78
106
|
"MessageLikeRepresentation",
|
|
107
|
+
"NonStandardAnnotation",
|
|
108
|
+
"NonStandardContentBlock",
|
|
109
|
+
"PlainTextContentBlock",
|
|
110
|
+
"ReasoningContentBlock",
|
|
79
111
|
"RemoveMessage",
|
|
80
112
|
"SystemMessage",
|
|
81
113
|
"SystemMessageChunk",
|
|
114
|
+
"TextContentBlock",
|
|
82
115
|
"ToolCall",
|
|
83
116
|
"ToolCallChunk",
|
|
84
117
|
"ToolMessage",
|
|
85
118
|
"ToolMessageChunk",
|
|
119
|
+
"VideoContentBlock",
|
|
120
|
+
"WebSearchCall",
|
|
121
|
+
"WebSearchResult",
|
|
86
122
|
"_message_from_dict",
|
|
87
123
|
"convert_to_messages",
|
|
88
124
|
"convert_to_openai_data_block",
|
|
@@ -103,25 +139,43 @@ __all__ = (
|
|
|
103
139
|
_dynamic_imports = {
|
|
104
140
|
"AIMessage": "ai",
|
|
105
141
|
"AIMessageChunk": "ai",
|
|
142
|
+
"Annotation": "content_blocks",
|
|
143
|
+
"AudioContentBlock": "content_blocks",
|
|
106
144
|
"BaseMessage": "base",
|
|
107
145
|
"BaseMessageChunk": "base",
|
|
108
146
|
"merge_content": "base",
|
|
109
147
|
"message_to_dict": "base",
|
|
110
148
|
"messages_to_dict": "base",
|
|
149
|
+
"Citation": "content_blocks",
|
|
150
|
+
"ContentBlock": "content_blocks",
|
|
111
151
|
"ChatMessage": "chat",
|
|
112
152
|
"ChatMessageChunk": "chat",
|
|
153
|
+
"CodeInterpreterCall": "content_blocks",
|
|
154
|
+
"CodeInterpreterOutput": "content_blocks",
|
|
155
|
+
"CodeInterpreterResult": "content_blocks",
|
|
156
|
+
"DataContentBlock": "content_blocks",
|
|
157
|
+
"FileContentBlock": "content_blocks",
|
|
113
158
|
"FunctionMessage": "function",
|
|
114
159
|
"FunctionMessageChunk": "function",
|
|
115
160
|
"HumanMessage": "human",
|
|
116
161
|
"HumanMessageChunk": "human",
|
|
162
|
+
"NonStandardAnnotation": "content_blocks",
|
|
163
|
+
"NonStandardContentBlock": "content_blocks",
|
|
164
|
+
"PlainTextContentBlock": "content_blocks",
|
|
165
|
+
"ReasoningContentBlock": "content_blocks",
|
|
117
166
|
"RemoveMessage": "modifier",
|
|
118
167
|
"SystemMessage": "system",
|
|
119
168
|
"SystemMessageChunk": "system",
|
|
169
|
+
"WebSearchCall": "content_blocks",
|
|
170
|
+
"WebSearchResult": "content_blocks",
|
|
171
|
+
"ImageContentBlock": "content_blocks",
|
|
120
172
|
"InvalidToolCall": "tool",
|
|
173
|
+
"TextContentBlock": "content_blocks",
|
|
121
174
|
"ToolCall": "tool",
|
|
122
175
|
"ToolCallChunk": "tool",
|
|
123
176
|
"ToolMessage": "tool",
|
|
124
177
|
"ToolMessageChunk": "tool",
|
|
178
|
+
"VideoContentBlock": "content_blocks",
|
|
125
179
|
"AnyMessage": "utils",
|
|
126
180
|
"MessageLikeRepresentation": "utils",
|
|
127
181
|
"_message_from_dict": "utils",
|
langchain_core/messages/ai.py
CHANGED
|
@@ -8,11 +8,7 @@ from typing import Any, Literal, Optional, Union, cast
|
|
|
8
8
|
from pydantic import model_validator
|
|
9
9
|
from typing_extensions import NotRequired, Self, TypedDict, override
|
|
10
10
|
|
|
11
|
-
from langchain_core.messages.base import
|
|
12
|
-
BaseMessage,
|
|
13
|
-
BaseMessageChunk,
|
|
14
|
-
merge_content,
|
|
15
|
-
)
|
|
11
|
+
from langchain_core.messages.base import BaseMessage, BaseMessageChunk, merge_content
|
|
16
12
|
from langchain_core.messages.tool import (
|
|
17
13
|
InvalidToolCall,
|
|
18
14
|
ToolCall,
|
|
@@ -20,15 +16,9 @@ from langchain_core.messages.tool import (
|
|
|
20
16
|
default_tool_chunk_parser,
|
|
21
17
|
default_tool_parser,
|
|
22
18
|
)
|
|
23
|
-
from langchain_core.messages.tool import
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
from langchain_core.messages.tool import (
|
|
27
|
-
tool_call as create_tool_call,
|
|
28
|
-
)
|
|
29
|
-
from langchain_core.messages.tool import (
|
|
30
|
-
tool_call_chunk as create_tool_call_chunk,
|
|
31
|
-
)
|
|
19
|
+
from langchain_core.messages.tool import invalid_tool_call as create_invalid_tool_call
|
|
20
|
+
from langchain_core.messages.tool import tool_call as create_tool_call
|
|
21
|
+
from langchain_core.messages.tool import tool_call_chunk as create_tool_call_chunk
|
|
32
22
|
from langchain_core.utils._merge import merge_dicts, merge_lists
|
|
33
23
|
from langchain_core.utils.json import parse_partial_json
|
|
34
24
|
from langchain_core.utils.usage import _dict_int_op
|
|
@@ -37,6 +27,16 @@ logger = logging.getLogger(__name__)
|
|
|
37
27
|
|
|
38
28
|
|
|
39
29
|
_LC_ID_PREFIX = "run-"
|
|
30
|
+
"""Internal tracing/callback system identifier.
|
|
31
|
+
|
|
32
|
+
Used for:
|
|
33
|
+
- Tracing. Every LangChain operation (LLM call, chain execution, tool use, etc.)
|
|
34
|
+
gets a unique run_id (UUID)
|
|
35
|
+
- Enables tracking parent-child relationships between operations
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
_LC_AUTO_PREFIX = "lc_"
|
|
39
|
+
"""LangChain auto-generated ID prefix for messages and content blocks."""
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
class InputTokenDetails(TypedDict, total=False):
|
|
@@ -57,6 +57,7 @@ class InputTokenDetails(TypedDict, total=False):
|
|
|
57
57
|
.. versionadded:: 0.3.9
|
|
58
58
|
|
|
59
59
|
May also hold extra provider-specific keys.
|
|
60
|
+
|
|
60
61
|
"""
|
|
61
62
|
|
|
62
63
|
audio: int
|
|
@@ -89,6 +90,7 @@ class OutputTokenDetails(TypedDict, total=False):
|
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
.. versionadded:: 0.3.9
|
|
93
|
+
|
|
92
94
|
"""
|
|
93
95
|
|
|
94
96
|
audio: int
|
|
@@ -128,6 +130,7 @@ class UsageMetadata(TypedDict):
|
|
|
128
130
|
.. versionchanged:: 0.3.9
|
|
129
131
|
|
|
130
132
|
Added ``input_token_details`` and ``output_token_details``.
|
|
133
|
+
|
|
131
134
|
"""
|
|
132
135
|
|
|
133
136
|
input_tokens: int
|
|
@@ -425,17 +428,27 @@ def add_ai_message_chunks(
|
|
|
425
428
|
|
|
426
429
|
chunk_id = None
|
|
427
430
|
candidates = [left.id] + [o.id for o in others]
|
|
428
|
-
# first pass: pick the first non-run-*
|
|
431
|
+
# first pass: pick the first provider-assigned id (non-run-* and non-lc_*)
|
|
429
432
|
for id_ in candidates:
|
|
430
|
-
if
|
|
433
|
+
if (
|
|
434
|
+
id_
|
|
435
|
+
and not id_.startswith(_LC_ID_PREFIX)
|
|
436
|
+
and not id_.startswith(_LC_AUTO_PREFIX)
|
|
437
|
+
):
|
|
431
438
|
chunk_id = id_
|
|
432
439
|
break
|
|
433
440
|
else:
|
|
434
|
-
# second pass:
|
|
441
|
+
# second pass: prefer lc_* ids over run-* ids
|
|
435
442
|
for id_ in candidates:
|
|
436
|
-
if id_:
|
|
443
|
+
if id_ and id_.startswith(_LC_AUTO_PREFIX):
|
|
437
444
|
chunk_id = id_
|
|
438
445
|
break
|
|
446
|
+
else:
|
|
447
|
+
# third pass: take any remaining id (run-* ids)
|
|
448
|
+
for id_ in candidates:
|
|
449
|
+
if id_:
|
|
450
|
+
chunk_id = id_
|
|
451
|
+
break
|
|
439
452
|
|
|
440
453
|
return left.__class__(
|
|
441
454
|
example=left.example,
|