langchain-core 1.0.0a5__py3-none-any.whl → 1.0.3__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/__init__.py +1 -1
- langchain_core/_api/__init__.py +3 -4
- langchain_core/_api/beta_decorator.py +23 -26
- langchain_core/_api/deprecation.py +51 -64
- langchain_core/_api/path.py +3 -6
- langchain_core/_import_utils.py +3 -4
- langchain_core/agents.py +20 -22
- langchain_core/caches.py +65 -66
- langchain_core/callbacks/__init__.py +1 -8
- langchain_core/callbacks/base.py +321 -336
- langchain_core/callbacks/file.py +44 -44
- langchain_core/callbacks/manager.py +436 -513
- langchain_core/callbacks/stdout.py +29 -30
- langchain_core/callbacks/streaming_stdout.py +32 -32
- langchain_core/callbacks/usage.py +60 -57
- langchain_core/chat_history.py +53 -68
- langchain_core/document_loaders/base.py +27 -25
- langchain_core/document_loaders/blob_loaders.py +1 -1
- langchain_core/document_loaders/langsmith.py +44 -48
- langchain_core/documents/__init__.py +23 -3
- langchain_core/documents/base.py +98 -90
- langchain_core/documents/compressor.py +10 -10
- langchain_core/documents/transformers.py +34 -35
- langchain_core/embeddings/fake.py +50 -54
- langchain_core/example_selectors/length_based.py +1 -1
- langchain_core/example_selectors/semantic_similarity.py +28 -32
- langchain_core/exceptions.py +21 -20
- langchain_core/globals.py +3 -151
- langchain_core/indexing/__init__.py +1 -1
- langchain_core/indexing/api.py +121 -126
- langchain_core/indexing/base.py +73 -75
- langchain_core/indexing/in_memory.py +4 -6
- langchain_core/language_models/__init__.py +14 -29
- langchain_core/language_models/_utils.py +58 -61
- langchain_core/language_models/base.py +53 -162
- langchain_core/language_models/chat_models.py +298 -387
- langchain_core/language_models/fake.py +11 -11
- langchain_core/language_models/fake_chat_models.py +42 -36
- langchain_core/language_models/llms.py +125 -235
- langchain_core/load/dump.py +9 -12
- langchain_core/load/load.py +18 -28
- langchain_core/load/mapping.py +2 -4
- langchain_core/load/serializable.py +42 -40
- langchain_core/messages/__init__.py +10 -16
- langchain_core/messages/ai.py +148 -148
- langchain_core/messages/base.py +58 -52
- langchain_core/messages/block_translators/__init__.py +27 -17
- langchain_core/messages/block_translators/anthropic.py +6 -6
- langchain_core/messages/block_translators/bedrock_converse.py +5 -5
- langchain_core/messages/block_translators/google_genai.py +505 -20
- langchain_core/messages/block_translators/google_vertexai.py +4 -32
- langchain_core/messages/block_translators/groq.py +117 -21
- langchain_core/messages/block_translators/langchain_v0.py +5 -5
- langchain_core/messages/block_translators/openai.py +11 -11
- langchain_core/messages/chat.py +2 -6
- langchain_core/messages/content.py +337 -328
- langchain_core/messages/function.py +6 -10
- langchain_core/messages/human.py +24 -31
- langchain_core/messages/modifier.py +2 -2
- langchain_core/messages/system.py +19 -29
- langchain_core/messages/tool.py +74 -90
- langchain_core/messages/utils.py +474 -504
- langchain_core/output_parsers/__init__.py +13 -10
- langchain_core/output_parsers/base.py +61 -61
- langchain_core/output_parsers/format_instructions.py +9 -4
- langchain_core/output_parsers/json.py +12 -10
- langchain_core/output_parsers/list.py +21 -23
- langchain_core/output_parsers/openai_functions.py +49 -47
- langchain_core/output_parsers/openai_tools.py +16 -21
- langchain_core/output_parsers/pydantic.py +13 -14
- langchain_core/output_parsers/string.py +5 -5
- langchain_core/output_parsers/transform.py +15 -17
- langchain_core/output_parsers/xml.py +35 -34
- langchain_core/outputs/__init__.py +1 -1
- langchain_core/outputs/chat_generation.py +18 -18
- langchain_core/outputs/chat_result.py +1 -3
- langchain_core/outputs/generation.py +10 -11
- langchain_core/outputs/llm_result.py +10 -10
- langchain_core/prompt_values.py +11 -17
- langchain_core/prompts/__init__.py +3 -27
- langchain_core/prompts/base.py +48 -56
- langchain_core/prompts/chat.py +275 -325
- langchain_core/prompts/dict.py +5 -5
- langchain_core/prompts/few_shot.py +81 -88
- langchain_core/prompts/few_shot_with_templates.py +11 -13
- langchain_core/prompts/image.py +12 -14
- langchain_core/prompts/loading.py +4 -6
- langchain_core/prompts/message.py +3 -3
- langchain_core/prompts/prompt.py +24 -39
- langchain_core/prompts/string.py +26 -10
- langchain_core/prompts/structured.py +49 -53
- langchain_core/rate_limiters.py +51 -60
- langchain_core/retrievers.py +61 -198
- langchain_core/runnables/base.py +1478 -1630
- langchain_core/runnables/branch.py +53 -57
- langchain_core/runnables/config.py +72 -89
- langchain_core/runnables/configurable.py +120 -137
- langchain_core/runnables/fallbacks.py +83 -79
- langchain_core/runnables/graph.py +91 -97
- langchain_core/runnables/graph_ascii.py +27 -28
- langchain_core/runnables/graph_mermaid.py +38 -50
- langchain_core/runnables/graph_png.py +15 -16
- langchain_core/runnables/history.py +135 -148
- langchain_core/runnables/passthrough.py +124 -150
- langchain_core/runnables/retry.py +46 -51
- langchain_core/runnables/router.py +25 -30
- langchain_core/runnables/schema.py +75 -80
- langchain_core/runnables/utils.py +60 -67
- langchain_core/stores.py +85 -121
- langchain_core/structured_query.py +8 -8
- langchain_core/sys_info.py +27 -29
- langchain_core/tools/__init__.py +1 -14
- langchain_core/tools/base.py +285 -229
- langchain_core/tools/convert.py +160 -155
- langchain_core/tools/render.py +10 -10
- langchain_core/tools/retriever.py +12 -11
- langchain_core/tools/simple.py +19 -24
- langchain_core/tools/structured.py +32 -39
- langchain_core/tracers/__init__.py +1 -9
- langchain_core/tracers/base.py +97 -99
- langchain_core/tracers/context.py +29 -52
- langchain_core/tracers/core.py +49 -53
- langchain_core/tracers/evaluation.py +11 -11
- langchain_core/tracers/event_stream.py +65 -64
- langchain_core/tracers/langchain.py +21 -21
- langchain_core/tracers/log_stream.py +45 -45
- langchain_core/tracers/memory_stream.py +3 -3
- langchain_core/tracers/root_listeners.py +16 -16
- langchain_core/tracers/run_collector.py +2 -4
- langchain_core/tracers/schemas.py +0 -129
- langchain_core/tracers/stdout.py +3 -3
- langchain_core/utils/__init__.py +1 -4
- langchain_core/utils/_merge.py +2 -2
- langchain_core/utils/aiter.py +57 -61
- langchain_core/utils/env.py +9 -9
- langchain_core/utils/function_calling.py +89 -186
- langchain_core/utils/html.py +7 -8
- langchain_core/utils/input.py +6 -6
- langchain_core/utils/interactive_env.py +1 -1
- langchain_core/utils/iter.py +36 -40
- langchain_core/utils/json.py +4 -3
- langchain_core/utils/json_schema.py +9 -9
- langchain_core/utils/mustache.py +8 -10
- langchain_core/utils/pydantic.py +33 -35
- langchain_core/utils/strings.py +6 -9
- langchain_core/utils/usage.py +1 -1
- langchain_core/utils/utils.py +66 -62
- langchain_core/vectorstores/base.py +182 -216
- langchain_core/vectorstores/in_memory.py +101 -176
- langchain_core/vectorstores/utils.py +5 -5
- langchain_core/version.py +1 -1
- langchain_core-1.0.3.dist-info/METADATA +69 -0
- langchain_core-1.0.3.dist-info/RECORD +172 -0
- {langchain_core-1.0.0a5.dist-info → langchain_core-1.0.3.dist-info}/WHEEL +1 -1
- langchain_core/memory.py +0 -120
- langchain_core/messages/block_translators/ollama.py +0 -47
- langchain_core/prompts/pipeline.py +0 -138
- langchain_core/pydantic_v1/__init__.py +0 -30
- langchain_core/pydantic_v1/dataclasses.py +0 -23
- langchain_core/pydantic_v1/main.py +0 -23
- langchain_core/tracers/langchain_v1.py +0 -31
- langchain_core/utils/loading.py +0 -35
- langchain_core-1.0.0a5.dist-info/METADATA +0 -77
- langchain_core-1.0.0a5.dist-info/RECORD +0 -181
- langchain_core-1.0.0a5.dist-info/entry_points.txt +0 -4
langchain_core/tools/base.py
CHANGED
|
@@ -8,16 +8,14 @@ import json
|
|
|
8
8
|
import typing
|
|
9
9
|
import warnings
|
|
10
10
|
from abc import ABC, abstractmethod
|
|
11
|
+
from collections.abc import Callable
|
|
11
12
|
from inspect import signature
|
|
12
13
|
from typing import (
|
|
13
14
|
TYPE_CHECKING,
|
|
14
15
|
Annotated,
|
|
15
16
|
Any,
|
|
16
|
-
Callable,
|
|
17
17
|
Literal,
|
|
18
|
-
Optional,
|
|
19
18
|
TypeVar,
|
|
20
|
-
Union,
|
|
21
19
|
cast,
|
|
22
20
|
get_args,
|
|
23
21
|
get_origin,
|
|
@@ -31,7 +29,6 @@ from pydantic import (
|
|
|
31
29
|
PydanticDeprecationWarning,
|
|
32
30
|
SkipValidation,
|
|
33
31
|
ValidationError,
|
|
34
|
-
model_validator,
|
|
35
32
|
validate_arguments,
|
|
36
33
|
)
|
|
37
34
|
from pydantic.v1 import BaseModel as BaseModelV1
|
|
@@ -39,10 +36,8 @@ from pydantic.v1 import ValidationError as ValidationErrorV1
|
|
|
39
36
|
from pydantic.v1 import validate_arguments as validate_arguments_v1
|
|
40
37
|
from typing_extensions import override
|
|
41
38
|
|
|
42
|
-
from langchain_core._api import deprecated
|
|
43
39
|
from langchain_core.callbacks import (
|
|
44
40
|
AsyncCallbackManager,
|
|
45
|
-
BaseCallbackManager,
|
|
46
41
|
CallbackManager,
|
|
47
42
|
Callbacks,
|
|
48
43
|
)
|
|
@@ -82,6 +77,7 @@ TOOL_MESSAGE_BLOCK_TYPES = (
|
|
|
82
77
|
"search_result",
|
|
83
78
|
"custom_tool_call_output",
|
|
84
79
|
"document",
|
|
80
|
+
"file",
|
|
85
81
|
)
|
|
86
82
|
|
|
87
83
|
|
|
@@ -96,7 +92,7 @@ def _is_annotated_type(typ: type[Any]) -> bool:
|
|
|
96
92
|
typ: The type to check.
|
|
97
93
|
|
|
98
94
|
Returns:
|
|
99
|
-
True if the type is an Annotated type, False otherwise.
|
|
95
|
+
`True` if the type is an Annotated type, `False` otherwise.
|
|
100
96
|
"""
|
|
101
97
|
return get_origin(typ) is typing.Annotated
|
|
102
98
|
|
|
@@ -230,7 +226,7 @@ def _is_pydantic_annotation(annotation: Any, pydantic_version: str = "v2") -> bo
|
|
|
230
226
|
pydantic_version: The Pydantic version to check against ("v1" or "v2").
|
|
231
227
|
|
|
232
228
|
Returns:
|
|
233
|
-
True if the annotation is a Pydantic model, False otherwise.
|
|
229
|
+
`True` if the annotation is a Pydantic model, `False` otherwise.
|
|
234
230
|
"""
|
|
235
231
|
base_model_class = BaseModelV1 if pydantic_version == "v1" else BaseModel
|
|
236
232
|
try:
|
|
@@ -249,7 +245,7 @@ def _function_annotations_are_pydantic_v1(
|
|
|
249
245
|
func: The function being checked.
|
|
250
246
|
|
|
251
247
|
Returns:
|
|
252
|
-
True if all Pydantic annotations are from V1, False otherwise.
|
|
248
|
+
True if all Pydantic annotations are from V1, `False` otherwise.
|
|
253
249
|
|
|
254
250
|
Raises:
|
|
255
251
|
NotImplementedError: If the function contains mixed V1 and V2 annotations.
|
|
@@ -284,29 +280,28 @@ def create_schema_from_function(
|
|
|
284
280
|
model_name: str,
|
|
285
281
|
func: Callable,
|
|
286
282
|
*,
|
|
287
|
-
filter_args:
|
|
283
|
+
filter_args: Sequence[str] | None = None,
|
|
288
284
|
parse_docstring: bool = False,
|
|
289
285
|
error_on_invalid_docstring: bool = False,
|
|
290
286
|
include_injected: bool = True,
|
|
291
287
|
) -> type[BaseModel]:
|
|
292
|
-
"""Create a
|
|
288
|
+
"""Create a Pydantic schema from a function's signature.
|
|
293
289
|
|
|
294
290
|
Args:
|
|
295
|
-
model_name: Name to assign to the generated
|
|
291
|
+
model_name: Name to assign to the generated Pydantic schema.
|
|
296
292
|
func: Function to generate the schema from.
|
|
297
293
|
filter_args: Optional list of arguments to exclude from the schema.
|
|
298
|
-
Defaults to FILTERED_ARGS
|
|
294
|
+
Defaults to `FILTERED_ARGS`.
|
|
299
295
|
parse_docstring: Whether to parse the function's docstring for descriptions
|
|
300
|
-
for each argument.
|
|
301
|
-
error_on_invalid_docstring: if
|
|
302
|
-
whether to raise ValueError on invalid Google Style docstrings.
|
|
303
|
-
Defaults to False.
|
|
296
|
+
for each argument.
|
|
297
|
+
error_on_invalid_docstring: if `parse_docstring` is provided, configure
|
|
298
|
+
whether to raise `ValueError` on invalid Google Style docstrings.
|
|
304
299
|
include_injected: Whether to include injected arguments in the schema.
|
|
305
|
-
Defaults to True
|
|
300
|
+
Defaults to `True`, since we want to include them in the schema
|
|
306
301
|
when *validating* tool inputs.
|
|
307
302
|
|
|
308
303
|
Returns:
|
|
309
|
-
A
|
|
304
|
+
A Pydantic model with the same arguments as the function.
|
|
310
305
|
"""
|
|
311
306
|
sig = inspect.signature(func)
|
|
312
307
|
|
|
@@ -316,7 +311,7 @@ def create_schema_from_function(
|
|
|
316
311
|
# https://docs.pydantic.dev/latest/usage/validation_decorator/
|
|
317
312
|
with warnings.catch_warnings():
|
|
318
313
|
# We are using deprecated functionality here.
|
|
319
|
-
# This code should be re-written to simply construct a
|
|
314
|
+
# This code should be re-written to simply construct a Pydantic model
|
|
320
315
|
# using inspect.signature and create_model.
|
|
321
316
|
warnings.simplefilter("ignore", category=PydanticDeprecationWarning)
|
|
322
317
|
validated = validate_arguments(func, config=_SchemaConfig) # type: ignore[operator]
|
|
@@ -389,13 +384,14 @@ class ToolException(Exception): # noqa: N818
|
|
|
389
384
|
"""
|
|
390
385
|
|
|
391
386
|
|
|
392
|
-
ArgsSchema =
|
|
387
|
+
ArgsSchema = TypeBaseModel | dict[str, Any]
|
|
393
388
|
|
|
394
389
|
|
|
395
|
-
class BaseTool(RunnableSerializable[
|
|
390
|
+
class BaseTool(RunnableSerializable[str | dict | ToolCall, Any]):
|
|
396
391
|
"""Base class for all LangChain tools.
|
|
397
392
|
|
|
398
393
|
This abstract class defines the interface that all LangChain tools must implement.
|
|
394
|
+
|
|
399
395
|
Tools are components that can be called by agents to perform specific actions.
|
|
400
396
|
"""
|
|
401
397
|
|
|
@@ -406,7 +402,7 @@ class BaseTool(RunnableSerializable[Union[str, dict, ToolCall], Any]):
|
|
|
406
402
|
**kwargs: Additional keyword arguments passed to the parent class.
|
|
407
403
|
|
|
408
404
|
Raises:
|
|
409
|
-
SchemaAnnotationError: If args_schema has incorrect type annotation.
|
|
405
|
+
SchemaAnnotationError: If `args_schema` has incorrect type annotation.
|
|
410
406
|
"""
|
|
411
407
|
super().__init_subclass__(**kwargs)
|
|
412
408
|
|
|
@@ -440,22 +436,22 @@ class ChildTool(BaseTool):
|
|
|
440
436
|
You can provide few-shot examples as a part of the description.
|
|
441
437
|
"""
|
|
442
438
|
|
|
443
|
-
args_schema: Annotated[
|
|
439
|
+
args_schema: Annotated[ArgsSchema | None, SkipValidation()] = Field(
|
|
444
440
|
default=None, description="The tool schema."
|
|
445
441
|
)
|
|
446
442
|
"""Pydantic model class to validate and parse the tool's input arguments.
|
|
447
443
|
|
|
448
444
|
Args schema should be either:
|
|
449
445
|
|
|
450
|
-
- A subclass of pydantic.BaseModel
|
|
451
|
-
- A subclass of pydantic.v1.BaseModel if accessing v1 namespace in pydantic 2
|
|
452
|
-
-
|
|
446
|
+
- A subclass of `pydantic.BaseModel`.
|
|
447
|
+
- A subclass of `pydantic.v1.BaseModel` if accessing v1 namespace in pydantic 2
|
|
448
|
+
- A JSON schema dict
|
|
453
449
|
"""
|
|
454
450
|
return_direct: bool = False
|
|
455
451
|
"""Whether to return the tool's output directly.
|
|
456
452
|
|
|
457
|
-
Setting this to True means
|
|
458
|
-
|
|
453
|
+
Setting this to `True` means that after the tool is called, the `AgentExecutor` will
|
|
454
|
+
stop looping.
|
|
459
455
|
"""
|
|
460
456
|
verbose: bool = False
|
|
461
457
|
"""Whether to log the tool's progress."""
|
|
@@ -463,52 +459,47 @@ class ChildTool(BaseTool):
|
|
|
463
459
|
callbacks: Callbacks = Field(default=None, exclude=True)
|
|
464
460
|
"""Callbacks to be called during tool execution."""
|
|
465
461
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
Field(
|
|
470
|
-
default=None,
|
|
471
|
-
exclude=True,
|
|
472
|
-
description="Callback manager to add to the run trace.",
|
|
473
|
-
)
|
|
474
|
-
)
|
|
475
|
-
tags: Optional[list[str]] = None
|
|
476
|
-
"""Optional list of tags associated with the tool. Defaults to None.
|
|
462
|
+
tags: list[str] | None = None
|
|
463
|
+
"""Optional list of tags associated with the tool.
|
|
464
|
+
|
|
477
465
|
These tags will be associated with each call to this tool,
|
|
478
466
|
and passed as arguments to the handlers defined in `callbacks`.
|
|
479
|
-
|
|
467
|
+
|
|
468
|
+
You can use these to, e.g., identify a specific instance of a tool with its use
|
|
469
|
+
case.
|
|
480
470
|
"""
|
|
481
|
-
metadata:
|
|
482
|
-
"""Optional metadata associated with the tool.
|
|
471
|
+
metadata: dict[str, Any] | None = None
|
|
472
|
+
"""Optional metadata associated with the tool.
|
|
473
|
+
|
|
483
474
|
This metadata will be associated with each call to this tool,
|
|
484
475
|
and passed as arguments to the handlers defined in `callbacks`.
|
|
485
|
-
|
|
476
|
+
|
|
477
|
+
You can use these to, e.g., identify a specific instance of a tool with its use
|
|
478
|
+
case.
|
|
486
479
|
"""
|
|
487
480
|
|
|
488
|
-
handle_tool_error:
|
|
489
|
-
|
|
490
|
-
)
|
|
491
|
-
"""Handle the content of the ToolException thrown."""
|
|
481
|
+
handle_tool_error: bool | str | Callable[[ToolException], str] | None = False
|
|
482
|
+
"""Handle the content of the `ToolException` thrown."""
|
|
492
483
|
|
|
493
|
-
handle_validation_error:
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
"""Handle the content of the ValidationError thrown."""
|
|
484
|
+
handle_validation_error: (
|
|
485
|
+
bool | str | Callable[[ValidationError | ValidationErrorV1], str] | None
|
|
486
|
+
) = False
|
|
487
|
+
"""Handle the content of the `ValidationError` thrown."""
|
|
497
488
|
|
|
498
489
|
response_format: Literal["content", "content_and_artifact"] = "content"
|
|
499
|
-
"""The tool response format.
|
|
490
|
+
"""The tool response format.
|
|
500
491
|
|
|
501
|
-
If
|
|
502
|
-
ToolMessage
|
|
503
|
-
two-tuple corresponding to the (content, artifact) of a ToolMessage
|
|
492
|
+
If `'content'` then the output of the tool is interpreted as the contents of a
|
|
493
|
+
`ToolMessage`. If `'content_and_artifact'` then the output is expected to be a
|
|
494
|
+
two-tuple corresponding to the `(content, artifact)` of a `ToolMessage`.
|
|
504
495
|
"""
|
|
505
496
|
|
|
506
497
|
def __init__(self, **kwargs: Any) -> None:
|
|
507
498
|
"""Initialize the tool.
|
|
508
499
|
|
|
509
500
|
Raises:
|
|
510
|
-
TypeError: If
|
|
511
|
-
dict
|
|
501
|
+
TypeError: If `args_schema` is not a subclass of pydantic `BaseModel` or
|
|
502
|
+
`dict`.
|
|
512
503
|
"""
|
|
513
504
|
if (
|
|
514
505
|
"args_schema" in kwargs
|
|
@@ -532,7 +523,7 @@ class ChildTool(BaseTool):
|
|
|
532
523
|
"""Check if the tool accepts only a single input argument.
|
|
533
524
|
|
|
534
525
|
Returns:
|
|
535
|
-
True if the tool has only one input argument, False otherwise.
|
|
526
|
+
`True` if the tool has only one input argument, `False` otherwise.
|
|
536
527
|
"""
|
|
537
528
|
keys = {k for k in self.args if k != "kwargs"}
|
|
538
529
|
return len(keys) == 1
|
|
@@ -542,7 +533,7 @@ class ChildTool(BaseTool):
|
|
|
542
533
|
"""Get the tool's input arguments schema.
|
|
543
534
|
|
|
544
535
|
Returns:
|
|
545
|
-
|
|
536
|
+
`dict` containing the tool's argument properties.
|
|
546
537
|
"""
|
|
547
538
|
if isinstance(self.args_schema, dict):
|
|
548
539
|
json_schema = self.args_schema
|
|
@@ -581,9 +572,7 @@ class ChildTool(BaseTool):
|
|
|
581
572
|
# --- Runnable ---
|
|
582
573
|
|
|
583
574
|
@override
|
|
584
|
-
def get_input_schema(
|
|
585
|
-
self, config: Optional[RunnableConfig] = None
|
|
586
|
-
) -> type[BaseModel]:
|
|
575
|
+
def get_input_schema(self, config: RunnableConfig | None = None) -> type[BaseModel]:
|
|
587
576
|
"""The tool's input schema.
|
|
588
577
|
|
|
589
578
|
Args:
|
|
@@ -601,8 +590,8 @@ class ChildTool(BaseTool):
|
|
|
601
590
|
@override
|
|
602
591
|
def invoke(
|
|
603
592
|
self,
|
|
604
|
-
input:
|
|
605
|
-
config:
|
|
593
|
+
input: str | dict | ToolCall,
|
|
594
|
+
config: RunnableConfig | None = None,
|
|
606
595
|
**kwargs: Any,
|
|
607
596
|
) -> Any:
|
|
608
597
|
tool_input, kwargs = _prep_run_args(input, config, **kwargs)
|
|
@@ -611,8 +600,8 @@ class ChildTool(BaseTool):
|
|
|
611
600
|
@override
|
|
612
601
|
async def ainvoke(
|
|
613
602
|
self,
|
|
614
|
-
input:
|
|
615
|
-
config:
|
|
603
|
+
input: str | dict | ToolCall,
|
|
604
|
+
config: RunnableConfig | None = None,
|
|
616
605
|
**kwargs: Any,
|
|
617
606
|
) -> Any:
|
|
618
607
|
tool_input, kwargs = _prep_run_args(input, config, **kwargs)
|
|
@@ -621,8 +610,8 @@ class ChildTool(BaseTool):
|
|
|
621
610
|
# --- Tool ---
|
|
622
611
|
|
|
623
612
|
def _parse_input(
|
|
624
|
-
self, tool_input:
|
|
625
|
-
) ->
|
|
613
|
+
self, tool_input: str | dict, tool_call_id: str | None
|
|
614
|
+
) -> str | dict[str, Any]:
|
|
626
615
|
"""Parse and validate tool input using the args schema.
|
|
627
616
|
|
|
628
617
|
Args:
|
|
@@ -633,10 +622,10 @@ class ChildTool(BaseTool):
|
|
|
633
622
|
The parsed and validated input.
|
|
634
623
|
|
|
635
624
|
Raises:
|
|
636
|
-
ValueError: If string input is provided with JSON schema
|
|
637
|
-
ValueError: If InjectedToolCallId is required but
|
|
625
|
+
ValueError: If `string` input is provided with JSON schema `args_schema`.
|
|
626
|
+
ValueError: If `InjectedToolCallId` is required but `tool_call_id` is not
|
|
638
627
|
provided.
|
|
639
|
-
TypeError: If args_schema is not a Pydantic
|
|
628
|
+
TypeError: If `args_schema` is not a Pydantic `BaseModel` or dict.
|
|
640
629
|
"""
|
|
641
630
|
input_args = self.args_schema
|
|
642
631
|
if isinstance(tool_input, str):
|
|
@@ -699,32 +688,12 @@ class ChildTool(BaseTool):
|
|
|
699
688
|
}
|
|
700
689
|
return tool_input
|
|
701
690
|
|
|
702
|
-
@model_validator(mode="before")
|
|
703
|
-
@classmethod
|
|
704
|
-
def raise_deprecation(cls, values: dict) -> Any:
|
|
705
|
-
"""Raise deprecation warning if callback_manager is used.
|
|
706
|
-
|
|
707
|
-
Args:
|
|
708
|
-
values: The values to validate.
|
|
709
|
-
|
|
710
|
-
Returns:
|
|
711
|
-
The validated values.
|
|
712
|
-
"""
|
|
713
|
-
if values.get("callback_manager") is not None:
|
|
714
|
-
warnings.warn(
|
|
715
|
-
"callback_manager is deprecated. Please use callbacks instead.",
|
|
716
|
-
DeprecationWarning,
|
|
717
|
-
stacklevel=6,
|
|
718
|
-
)
|
|
719
|
-
values["callbacks"] = values.pop("callback_manager", None)
|
|
720
|
-
return values
|
|
721
|
-
|
|
722
691
|
@abstractmethod
|
|
723
692
|
def _run(self, *args: Any, **kwargs: Any) -> Any:
|
|
724
693
|
"""Use the tool.
|
|
725
694
|
|
|
726
|
-
Add run_manager:
|
|
727
|
-
|
|
695
|
+
Add `run_manager: CallbackManagerForToolRun | None = None` to child
|
|
696
|
+
implementations to enable tracing.
|
|
728
697
|
|
|
729
698
|
Returns:
|
|
730
699
|
The result of the tool execution.
|
|
@@ -733,8 +702,8 @@ class ChildTool(BaseTool):
|
|
|
733
702
|
async def _arun(self, *args: Any, **kwargs: Any) -> Any:
|
|
734
703
|
"""Use the tool asynchronously.
|
|
735
704
|
|
|
736
|
-
Add run_manager:
|
|
737
|
-
|
|
705
|
+
Add `run_manager: AsyncCallbackManagerForToolRun | None = None` to child
|
|
706
|
+
implementations to enable tracing.
|
|
738
707
|
|
|
739
708
|
Returns:
|
|
740
709
|
The result of the tool execution.
|
|
@@ -745,8 +714,37 @@ class ChildTool(BaseTool):
|
|
|
745
714
|
kwargs["run_manager"] = kwargs["run_manager"].get_sync()
|
|
746
715
|
return await run_in_executor(None, self._run, *args, **kwargs)
|
|
747
716
|
|
|
717
|
+
def _filter_injected_args(self, tool_input: dict) -> dict:
|
|
718
|
+
"""Filter out injected tool arguments from the input dictionary.
|
|
719
|
+
|
|
720
|
+
Injected arguments are those annotated with `InjectedToolArg` or its
|
|
721
|
+
subclasses, or arguments in `FILTERED_ARGS` like `run_manager` and callbacks.
|
|
722
|
+
|
|
723
|
+
Args:
|
|
724
|
+
tool_input: The tool input dictionary to filter.
|
|
725
|
+
|
|
726
|
+
Returns:
|
|
727
|
+
A filtered dictionary with injected arguments removed.
|
|
728
|
+
"""
|
|
729
|
+
# Start with filtered args from the constant
|
|
730
|
+
filtered_keys = set[str](FILTERED_ARGS)
|
|
731
|
+
|
|
732
|
+
# If we have an args_schema, use it to identify injected args
|
|
733
|
+
if self.args_schema is not None:
|
|
734
|
+
try:
|
|
735
|
+
annotations = get_all_basemodel_annotations(self.args_schema)
|
|
736
|
+
for field_name, field_type in annotations.items():
|
|
737
|
+
if _is_injected_arg_type(field_type):
|
|
738
|
+
filtered_keys.add(field_name)
|
|
739
|
+
except Exception: # noqa: S110
|
|
740
|
+
# If we can't get annotations, just use FILTERED_ARGS
|
|
741
|
+
pass
|
|
742
|
+
|
|
743
|
+
# Filter out the injected keys from tool_input
|
|
744
|
+
return {k: v for k, v in tool_input.items() if k not in filtered_keys}
|
|
745
|
+
|
|
748
746
|
def _to_args_and_kwargs(
|
|
749
|
-
self, tool_input:
|
|
747
|
+
self, tool_input: str | dict, tool_call_id: str | None
|
|
750
748
|
) -> tuple[tuple, dict]:
|
|
751
749
|
"""Convert tool input to positional and keyword arguments.
|
|
752
750
|
|
|
@@ -755,7 +753,7 @@ class ChildTool(BaseTool):
|
|
|
755
753
|
tool_call_id: The ID of the tool call, if available.
|
|
756
754
|
|
|
757
755
|
Returns:
|
|
758
|
-
A tuple of (positional_args, keyword_args) for the tool.
|
|
756
|
+
A tuple of `(positional_args, keyword_args)` for the tool.
|
|
759
757
|
|
|
760
758
|
Raises:
|
|
761
759
|
TypeError: If the tool input type is invalid.
|
|
@@ -786,35 +784,35 @@ class ChildTool(BaseTool):
|
|
|
786
784
|
|
|
787
785
|
def run(
|
|
788
786
|
self,
|
|
789
|
-
tool_input:
|
|
790
|
-
verbose:
|
|
791
|
-
start_color:
|
|
792
|
-
color:
|
|
787
|
+
tool_input: str | dict[str, Any],
|
|
788
|
+
verbose: bool | None = None, # noqa: FBT001
|
|
789
|
+
start_color: str | None = "green",
|
|
790
|
+
color: str | None = "green",
|
|
793
791
|
callbacks: Callbacks = None,
|
|
794
792
|
*,
|
|
795
|
-
tags:
|
|
796
|
-
metadata:
|
|
797
|
-
run_name:
|
|
798
|
-
run_id:
|
|
799
|
-
config:
|
|
800
|
-
tool_call_id:
|
|
793
|
+
tags: list[str] | None = None,
|
|
794
|
+
metadata: dict[str, Any] | None = None,
|
|
795
|
+
run_name: str | None = None,
|
|
796
|
+
run_id: uuid.UUID | None = None,
|
|
797
|
+
config: RunnableConfig | None = None,
|
|
798
|
+
tool_call_id: str | None = None,
|
|
801
799
|
**kwargs: Any,
|
|
802
800
|
) -> Any:
|
|
803
801
|
"""Run the tool.
|
|
804
802
|
|
|
805
803
|
Args:
|
|
806
804
|
tool_input: The input to the tool.
|
|
807
|
-
verbose: Whether to log the tool's progress.
|
|
808
|
-
start_color: The color to use when starting the tool.
|
|
809
|
-
color: The color to use when ending the tool.
|
|
810
|
-
callbacks: Callbacks to be called during tool execution.
|
|
811
|
-
tags: Optional list of tags associated with the tool.
|
|
812
|
-
metadata: Optional metadata associated with the tool.
|
|
813
|
-
run_name: The name of the run.
|
|
814
|
-
run_id: The id of the run.
|
|
815
|
-
config: The configuration for the tool.
|
|
816
|
-
tool_call_id: The id of the tool call.
|
|
817
|
-
kwargs: Keyword arguments to be passed to tool callbacks (event handler)
|
|
805
|
+
verbose: Whether to log the tool's progress.
|
|
806
|
+
start_color: The color to use when starting the tool.
|
|
807
|
+
color: The color to use when ending the tool.
|
|
808
|
+
callbacks: Callbacks to be called during tool execution.
|
|
809
|
+
tags: Optional list of tags associated with the tool.
|
|
810
|
+
metadata: Optional metadata associated with the tool.
|
|
811
|
+
run_name: The name of the run.
|
|
812
|
+
run_id: The id of the run.
|
|
813
|
+
config: The configuration for the tool.
|
|
814
|
+
tool_call_id: The id of the tool call.
|
|
815
|
+
**kwargs: Keyword arguments to be passed to tool callbacks (event handler)
|
|
818
816
|
|
|
819
817
|
Returns:
|
|
820
818
|
The output of the tool.
|
|
@@ -832,24 +830,36 @@ class ChildTool(BaseTool):
|
|
|
832
830
|
self.metadata,
|
|
833
831
|
)
|
|
834
832
|
|
|
833
|
+
# Filter out injected arguments from callback inputs
|
|
834
|
+
filtered_tool_input = (
|
|
835
|
+
self._filter_injected_args(tool_input)
|
|
836
|
+
if isinstance(tool_input, dict)
|
|
837
|
+
else None
|
|
838
|
+
)
|
|
839
|
+
|
|
840
|
+
# Use filtered inputs for the input_str parameter as well
|
|
841
|
+
tool_input_str = (
|
|
842
|
+
tool_input
|
|
843
|
+
if isinstance(tool_input, str)
|
|
844
|
+
else str(
|
|
845
|
+
filtered_tool_input if filtered_tool_input is not None else tool_input
|
|
846
|
+
)
|
|
847
|
+
)
|
|
848
|
+
|
|
835
849
|
run_manager = callback_manager.on_tool_start(
|
|
836
850
|
{"name": self.name, "description": self.description},
|
|
837
|
-
|
|
851
|
+
tool_input_str,
|
|
838
852
|
color=start_color,
|
|
839
853
|
name=run_name,
|
|
840
854
|
run_id=run_id,
|
|
841
|
-
|
|
842
|
-
# For now, it's unclear whether this assumption is ever violated,
|
|
843
|
-
# but if it is we will send a `None` value to the callback instead
|
|
844
|
-
# TODO: will need to address issue via a patch.
|
|
845
|
-
inputs=tool_input if isinstance(tool_input, dict) else None,
|
|
855
|
+
inputs=filtered_tool_input,
|
|
846
856
|
**kwargs,
|
|
847
857
|
)
|
|
848
858
|
|
|
849
859
|
content = None
|
|
850
860
|
artifact = None
|
|
851
861
|
status = "success"
|
|
852
|
-
error_to_raise:
|
|
862
|
+
error_to_raise: Exception | KeyboardInterrupt | None = None
|
|
853
863
|
try:
|
|
854
864
|
child_config = patch_config(config, callbacks=run_manager.get_child())
|
|
855
865
|
with set_config_context(child_config) as context:
|
|
@@ -898,35 +908,35 @@ class ChildTool(BaseTool):
|
|
|
898
908
|
|
|
899
909
|
async def arun(
|
|
900
910
|
self,
|
|
901
|
-
tool_input:
|
|
902
|
-
verbose:
|
|
903
|
-
start_color:
|
|
904
|
-
color:
|
|
911
|
+
tool_input: str | dict,
|
|
912
|
+
verbose: bool | None = None, # noqa: FBT001
|
|
913
|
+
start_color: str | None = "green",
|
|
914
|
+
color: str | None = "green",
|
|
905
915
|
callbacks: Callbacks = None,
|
|
906
916
|
*,
|
|
907
|
-
tags:
|
|
908
|
-
metadata:
|
|
909
|
-
run_name:
|
|
910
|
-
run_id:
|
|
911
|
-
config:
|
|
912
|
-
tool_call_id:
|
|
917
|
+
tags: list[str] | None = None,
|
|
918
|
+
metadata: dict[str, Any] | None = None,
|
|
919
|
+
run_name: str | None = None,
|
|
920
|
+
run_id: uuid.UUID | None = None,
|
|
921
|
+
config: RunnableConfig | None = None,
|
|
922
|
+
tool_call_id: str | None = None,
|
|
913
923
|
**kwargs: Any,
|
|
914
924
|
) -> Any:
|
|
915
925
|
"""Run the tool asynchronously.
|
|
916
926
|
|
|
917
927
|
Args:
|
|
918
928
|
tool_input: The input to the tool.
|
|
919
|
-
verbose: Whether to log the tool's progress.
|
|
920
|
-
start_color: The color to use when starting the tool.
|
|
921
|
-
color: The color to use when ending the tool.
|
|
922
|
-
callbacks: Callbacks to be called during tool execution.
|
|
923
|
-
tags: Optional list of tags associated with the tool.
|
|
924
|
-
metadata: Optional metadata associated with the tool.
|
|
925
|
-
run_name: The name of the run.
|
|
926
|
-
run_id: The id of the run.
|
|
927
|
-
config: The configuration for the tool.
|
|
928
|
-
tool_call_id: The id of the tool call.
|
|
929
|
-
kwargs: Keyword arguments to be passed to tool callbacks
|
|
929
|
+
verbose: Whether to log the tool's progress.
|
|
930
|
+
start_color: The color to use when starting the tool.
|
|
931
|
+
color: The color to use when ending the tool.
|
|
932
|
+
callbacks: Callbacks to be called during tool execution.
|
|
933
|
+
tags: Optional list of tags associated with the tool.
|
|
934
|
+
metadata: Optional metadata associated with the tool.
|
|
935
|
+
run_name: The name of the run.
|
|
936
|
+
run_id: The id of the run.
|
|
937
|
+
config: The configuration for the tool.
|
|
938
|
+
tool_call_id: The id of the tool call.
|
|
939
|
+
**kwargs: Keyword arguments to be passed to tool callbacks
|
|
930
940
|
|
|
931
941
|
Returns:
|
|
932
942
|
The output of the tool.
|
|
@@ -943,23 +953,36 @@ class ChildTool(BaseTool):
|
|
|
943
953
|
metadata,
|
|
944
954
|
self.metadata,
|
|
945
955
|
)
|
|
956
|
+
|
|
957
|
+
# Filter out injected arguments from callback inputs
|
|
958
|
+
filtered_tool_input = (
|
|
959
|
+
self._filter_injected_args(tool_input)
|
|
960
|
+
if isinstance(tool_input, dict)
|
|
961
|
+
else None
|
|
962
|
+
)
|
|
963
|
+
|
|
964
|
+
# Use filtered inputs for the input_str parameter as well
|
|
965
|
+
tool_input_str = (
|
|
966
|
+
tool_input
|
|
967
|
+
if isinstance(tool_input, str)
|
|
968
|
+
else str(
|
|
969
|
+
filtered_tool_input if filtered_tool_input is not None else tool_input
|
|
970
|
+
)
|
|
971
|
+
)
|
|
972
|
+
|
|
946
973
|
run_manager = await callback_manager.on_tool_start(
|
|
947
974
|
{"name": self.name, "description": self.description},
|
|
948
|
-
|
|
975
|
+
tool_input_str,
|
|
949
976
|
color=start_color,
|
|
950
977
|
name=run_name,
|
|
951
978
|
run_id=run_id,
|
|
952
|
-
|
|
953
|
-
# For now, it's unclear whether this assumption is ever violated,
|
|
954
|
-
# but if it is we will send a `None` value to the callback instead
|
|
955
|
-
# TODO: will need to address issue via a patch.
|
|
956
|
-
inputs=tool_input if isinstance(tool_input, dict) else None,
|
|
979
|
+
inputs=filtered_tool_input,
|
|
957
980
|
**kwargs,
|
|
958
981
|
)
|
|
959
982
|
content = None
|
|
960
983
|
artifact = None
|
|
961
984
|
status = "success"
|
|
962
|
-
error_to_raise:
|
|
985
|
+
error_to_raise: Exception | KeyboardInterrupt | None = None
|
|
963
986
|
try:
|
|
964
987
|
tool_args, tool_kwargs = self._to_args_and_kwargs(tool_input, tool_call_id)
|
|
965
988
|
child_config = patch_config(config, callbacks=run_manager.get_child())
|
|
@@ -1010,19 +1033,6 @@ class ChildTool(BaseTool):
|
|
|
1010
1033
|
await run_manager.on_tool_end(output, color=color, name=self.name, **kwargs)
|
|
1011
1034
|
return output
|
|
1012
1035
|
|
|
1013
|
-
@deprecated("0.1.47", alternative="invoke", removal="1.0")
|
|
1014
|
-
def __call__(self, tool_input: str, callbacks: Callbacks = None) -> str:
|
|
1015
|
-
"""Make tool callable (deprecated).
|
|
1016
|
-
|
|
1017
|
-
Args:
|
|
1018
|
-
tool_input: The input to the tool.
|
|
1019
|
-
callbacks: Callbacks to use during execution.
|
|
1020
|
-
|
|
1021
|
-
Returns:
|
|
1022
|
-
The tool's output.
|
|
1023
|
-
"""
|
|
1024
|
-
return self.run(tool_input, callbacks=callbacks)
|
|
1025
|
-
|
|
1026
1036
|
|
|
1027
1037
|
def _is_tool_call(x: Any) -> bool:
|
|
1028
1038
|
"""Check if the input is a tool call dictionary.
|
|
@@ -1031,23 +1041,21 @@ def _is_tool_call(x: Any) -> bool:
|
|
|
1031
1041
|
x: The input to check.
|
|
1032
1042
|
|
|
1033
1043
|
Returns:
|
|
1034
|
-
True if the input is a tool call, False otherwise.
|
|
1044
|
+
`True` if the input is a tool call, `False` otherwise.
|
|
1035
1045
|
"""
|
|
1036
1046
|
return isinstance(x, dict) and x.get("type") == "tool_call"
|
|
1037
1047
|
|
|
1038
1048
|
|
|
1039
1049
|
def _handle_validation_error(
|
|
1040
|
-
e:
|
|
1050
|
+
e: ValidationError | ValidationErrorV1,
|
|
1041
1051
|
*,
|
|
1042
|
-
flag:
|
|
1043
|
-
Literal[True], str, Callable[[Union[ValidationError, ValidationErrorV1]], str]
|
|
1044
|
-
],
|
|
1052
|
+
flag: Literal[True] | str | Callable[[ValidationError | ValidationErrorV1], str],
|
|
1045
1053
|
) -> str:
|
|
1046
1054
|
"""Handle validation errors based on the configured flag.
|
|
1047
1055
|
|
|
1048
1056
|
Args:
|
|
1049
1057
|
e: The validation error that occurred.
|
|
1050
|
-
flag: How to handle the error (bool
|
|
1058
|
+
flag: How to handle the error (`bool`, `str`, or `Callable`).
|
|
1051
1059
|
|
|
1052
1060
|
Returns:
|
|
1053
1061
|
The error message to return.
|
|
@@ -1073,13 +1081,13 @@ def _handle_validation_error(
|
|
|
1073
1081
|
def _handle_tool_error(
|
|
1074
1082
|
e: ToolException,
|
|
1075
1083
|
*,
|
|
1076
|
-
flag:
|
|
1084
|
+
flag: Literal[True] | str | Callable[[ToolException], str] | None,
|
|
1077
1085
|
) -> str:
|
|
1078
1086
|
"""Handle tool execution errors based on the configured flag.
|
|
1079
1087
|
|
|
1080
1088
|
Args:
|
|
1081
1089
|
e: The tool exception that occurred.
|
|
1082
|
-
flag: How to handle the error (bool
|
|
1090
|
+
flag: How to handle the error (`bool`, `str`, or `Callable`).
|
|
1083
1091
|
|
|
1084
1092
|
Returns:
|
|
1085
1093
|
The error message to return.
|
|
@@ -1103,27 +1111,27 @@ def _handle_tool_error(
|
|
|
1103
1111
|
|
|
1104
1112
|
|
|
1105
1113
|
def _prep_run_args(
|
|
1106
|
-
value:
|
|
1107
|
-
config:
|
|
1114
|
+
value: str | dict | ToolCall,
|
|
1115
|
+
config: RunnableConfig | None,
|
|
1108
1116
|
**kwargs: Any,
|
|
1109
|
-
) -> tuple[
|
|
1117
|
+
) -> tuple[str | dict, dict]:
|
|
1110
1118
|
"""Prepare arguments for tool execution.
|
|
1111
1119
|
|
|
1112
1120
|
Args:
|
|
1113
|
-
value: The input value (
|
|
1121
|
+
value: The input value (`str`, `dict`, or `ToolCall`).
|
|
1114
1122
|
config: The runnable configuration.
|
|
1115
1123
|
**kwargs: Additional keyword arguments.
|
|
1116
1124
|
|
|
1117
1125
|
Returns:
|
|
1118
|
-
A tuple of (tool_input, run_kwargs)
|
|
1126
|
+
A tuple of `(tool_input, run_kwargs)`.
|
|
1119
1127
|
"""
|
|
1120
1128
|
config = ensure_config(config)
|
|
1121
1129
|
if _is_tool_call(value):
|
|
1122
|
-
tool_call_id:
|
|
1123
|
-
tool_input:
|
|
1130
|
+
tool_call_id: str | None = cast("ToolCall", value)["id"]
|
|
1131
|
+
tool_input: str | dict = cast("ToolCall", value)["args"].copy()
|
|
1124
1132
|
else:
|
|
1125
1133
|
tool_call_id = None
|
|
1126
|
-
tool_input = cast("
|
|
1134
|
+
tool_input = cast("str | dict", value)
|
|
1127
1135
|
return (
|
|
1128
1136
|
tool_input,
|
|
1129
1137
|
dict(
|
|
@@ -1142,11 +1150,11 @@ def _prep_run_args(
|
|
|
1142
1150
|
def _format_output(
|
|
1143
1151
|
content: Any,
|
|
1144
1152
|
artifact: Any,
|
|
1145
|
-
tool_call_id:
|
|
1153
|
+
tool_call_id: str | None,
|
|
1146
1154
|
name: str,
|
|
1147
1155
|
status: str,
|
|
1148
|
-
) ->
|
|
1149
|
-
"""Format tool output as a ToolMessage if appropriate.
|
|
1156
|
+
) -> ToolOutputMixin | Any:
|
|
1157
|
+
"""Format tool output as a `ToolMessage` if appropriate.
|
|
1150
1158
|
|
|
1151
1159
|
Args:
|
|
1152
1160
|
content: The main content of the tool output.
|
|
@@ -1156,7 +1164,7 @@ def _format_output(
|
|
|
1156
1164
|
status: The execution status.
|
|
1157
1165
|
|
|
1158
1166
|
Returns:
|
|
1159
|
-
The formatted output, either as a ToolMessage or the original content.
|
|
1167
|
+
The formatted output, either as a `ToolMessage` or the original content.
|
|
1160
1168
|
"""
|
|
1161
1169
|
if isinstance(content, ToolOutputMixin) or tool_call_id is None:
|
|
1162
1170
|
return content
|
|
@@ -1180,7 +1188,7 @@ def _is_message_content_type(obj: Any) -> bool:
|
|
|
1180
1188
|
obj: The object to check.
|
|
1181
1189
|
|
|
1182
1190
|
Returns:
|
|
1183
|
-
True if the object is valid message content, False otherwise.
|
|
1191
|
+
`True` if the object is valid message content, `False` otherwise.
|
|
1184
1192
|
"""
|
|
1185
1193
|
return isinstance(obj, str) or (
|
|
1186
1194
|
isinstance(obj, list) and all(_is_message_content_block(e) for e in obj)
|
|
@@ -1196,7 +1204,7 @@ def _is_message_content_block(obj: Any) -> bool:
|
|
|
1196
1204
|
obj: The object to check.
|
|
1197
1205
|
|
|
1198
1206
|
Returns:
|
|
1199
|
-
True if the object is a valid content block, False otherwise.
|
|
1207
|
+
`True` if the object is a valid content block, `False` otherwise.
|
|
1200
1208
|
"""
|
|
1201
1209
|
if isinstance(obj, str):
|
|
1202
1210
|
return True
|
|
@@ -1220,14 +1228,14 @@ def _stringify(content: Any) -> str:
|
|
|
1220
1228
|
return str(content)
|
|
1221
1229
|
|
|
1222
1230
|
|
|
1223
|
-
def _get_type_hints(func: Callable) ->
|
|
1231
|
+
def _get_type_hints(func: Callable) -> dict[str, type] | None:
|
|
1224
1232
|
"""Get type hints from a function, handling partial functions.
|
|
1225
1233
|
|
|
1226
1234
|
Args:
|
|
1227
1235
|
func: The function to get type hints from.
|
|
1228
1236
|
|
|
1229
1237
|
Returns:
|
|
1230
|
-
|
|
1238
|
+
`dict` of type hints, or `None` if extraction fails.
|
|
1231
1239
|
"""
|
|
1232
1240
|
if isinstance(func, functools.partial):
|
|
1233
1241
|
func = func.func
|
|
@@ -1237,14 +1245,14 @@ def _get_type_hints(func: Callable) -> Optional[dict[str, type]]:
|
|
|
1237
1245
|
return None
|
|
1238
1246
|
|
|
1239
1247
|
|
|
1240
|
-
def _get_runnable_config_param(func: Callable) ->
|
|
1241
|
-
"""Find the parameter name for RunnableConfig in a function.
|
|
1248
|
+
def _get_runnable_config_param(func: Callable) -> str | None:
|
|
1249
|
+
"""Find the parameter name for `RunnableConfig` in a function.
|
|
1242
1250
|
|
|
1243
1251
|
Args:
|
|
1244
1252
|
func: The function to check.
|
|
1245
1253
|
|
|
1246
1254
|
Returns:
|
|
1247
|
-
The parameter name for RunnableConfig
|
|
1255
|
+
The parameter name for `RunnableConfig`, or `None` if not found.
|
|
1248
1256
|
"""
|
|
1249
1257
|
type_hints = _get_type_hints(func)
|
|
1250
1258
|
if not type_hints:
|
|
@@ -1263,35 +1271,75 @@ class InjectedToolArg:
|
|
|
1263
1271
|
"""
|
|
1264
1272
|
|
|
1265
1273
|
|
|
1274
|
+
class _DirectlyInjectedToolArg:
|
|
1275
|
+
"""Annotation for tool arguments that are injected at runtime.
|
|
1276
|
+
|
|
1277
|
+
Injected via direct type annotation, rather than annotated metadata.
|
|
1278
|
+
|
|
1279
|
+
For example, `ToolRuntime` is a directly injected argument.
|
|
1280
|
+
|
|
1281
|
+
Note the direct annotation rather than the verbose alternative:
|
|
1282
|
+
`Annotated[ToolRuntime, InjectedRuntime]`
|
|
1283
|
+
|
|
1284
|
+
```python
|
|
1285
|
+
from langchain_core.tools import tool, ToolRuntime
|
|
1286
|
+
|
|
1287
|
+
|
|
1288
|
+
@tool
|
|
1289
|
+
def foo(x: int, runtime: ToolRuntime) -> str:
|
|
1290
|
+
# use runtime.state, runtime.context, runtime.store, etc.
|
|
1291
|
+
...
|
|
1292
|
+
```
|
|
1293
|
+
"""
|
|
1294
|
+
|
|
1295
|
+
|
|
1266
1296
|
class InjectedToolCallId(InjectedToolArg):
|
|
1267
1297
|
"""Annotation for injecting the tool call ID.
|
|
1268
1298
|
|
|
1269
1299
|
This annotation is used to mark a tool parameter that should receive
|
|
1270
1300
|
the tool call ID at runtime.
|
|
1271
1301
|
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
)
|
|
1302
|
+
```python
|
|
1303
|
+
from typing import Annotated
|
|
1304
|
+
from langchain_core.messages import ToolMessage
|
|
1305
|
+
from langchain_core.tools import tool, InjectedToolCallId
|
|
1306
|
+
|
|
1307
|
+
@tool
|
|
1308
|
+
def foo(
|
|
1309
|
+
x: int, tool_call_id: Annotated[str, InjectedToolCallId]
|
|
1310
|
+
) -> ToolMessage:
|
|
1311
|
+
\"\"\"Return x.\"\"\"
|
|
1312
|
+
return ToolMessage(
|
|
1313
|
+
str(x),
|
|
1314
|
+
artifact=x,
|
|
1315
|
+
name="foo",
|
|
1316
|
+
tool_call_id=tool_call_id
|
|
1317
|
+
)
|
|
1289
1318
|
|
|
1319
|
+
```
|
|
1290
1320
|
"""
|
|
1291
1321
|
|
|
1292
1322
|
|
|
1323
|
+
def _is_directly_injected_arg_type(type_: Any) -> bool:
|
|
1324
|
+
"""Check if a type annotation indicates a directly injected argument.
|
|
1325
|
+
|
|
1326
|
+
This is currently only used for `ToolRuntime`.
|
|
1327
|
+
Checks if either the annotation itself is a subclass of `_DirectlyInjectedToolArg`
|
|
1328
|
+
or the origin of the annotation is a subclass of `_DirectlyInjectedToolArg`.
|
|
1329
|
+
|
|
1330
|
+
Ex: `ToolRuntime` or `ToolRuntime[ContextT, StateT]` would both return `True`.
|
|
1331
|
+
"""
|
|
1332
|
+
return (
|
|
1333
|
+
isinstance(type_, type) and issubclass(type_, _DirectlyInjectedToolArg)
|
|
1334
|
+
) or (
|
|
1335
|
+
(origin := get_origin(type_)) is not None
|
|
1336
|
+
and isinstance(origin, type)
|
|
1337
|
+
and issubclass(origin, _DirectlyInjectedToolArg)
|
|
1338
|
+
)
|
|
1339
|
+
|
|
1340
|
+
|
|
1293
1341
|
def _is_injected_arg_type(
|
|
1294
|
-
type_:
|
|
1342
|
+
type_: type | TypeVar, injected_type: type[InjectedToolArg] | None = None
|
|
1295
1343
|
) -> bool:
|
|
1296
1344
|
"""Check if a type annotation indicates an injected argument.
|
|
1297
1345
|
|
|
@@ -1300,9 +1348,17 @@ def _is_injected_arg_type(
|
|
|
1300
1348
|
injected_type: The specific injected type to check for.
|
|
1301
1349
|
|
|
1302
1350
|
Returns:
|
|
1303
|
-
True if the type is an injected argument, False otherwise.
|
|
1351
|
+
`True` if the type is an injected argument, `False` otherwise.
|
|
1304
1352
|
"""
|
|
1305
|
-
|
|
1353
|
+
if injected_type is None:
|
|
1354
|
+
# if no injected type is specified,
|
|
1355
|
+
# check if the type is a directly injected argument
|
|
1356
|
+
if _is_directly_injected_arg_type(type_):
|
|
1357
|
+
return True
|
|
1358
|
+
injected_type = InjectedToolArg
|
|
1359
|
+
|
|
1360
|
+
# if the type is an Annotated type, check if annotated metadata
|
|
1361
|
+
# is an intance or subclass of the injected type
|
|
1306
1362
|
return any(
|
|
1307
1363
|
isinstance(arg, injected_type)
|
|
1308
1364
|
or (isinstance(arg, type) and issubclass(arg, injected_type))
|
|
@@ -1311,23 +1367,23 @@ def _is_injected_arg_type(
|
|
|
1311
1367
|
|
|
1312
1368
|
|
|
1313
1369
|
def get_all_basemodel_annotations(
|
|
1314
|
-
cls:
|
|
1315
|
-
) -> dict[str,
|
|
1316
|
-
"""Get all annotations from a Pydantic BaseModel and its parents.
|
|
1370
|
+
cls: TypeBaseModel | Any, *, default_to_bound: bool = True
|
|
1371
|
+
) -> dict[str, type | TypeVar]:
|
|
1372
|
+
"""Get all annotations from a Pydantic `BaseModel` and its parents.
|
|
1317
1373
|
|
|
1318
1374
|
Args:
|
|
1319
|
-
cls: The Pydantic BaseModel class.
|
|
1320
|
-
default_to_bound: Whether to default to the bound of a TypeVar if it exists.
|
|
1375
|
+
cls: The Pydantic `BaseModel` class.
|
|
1376
|
+
default_to_bound: Whether to default to the bound of a `TypeVar` if it exists.
|
|
1321
1377
|
|
|
1322
1378
|
Returns:
|
|
1323
|
-
|
|
1379
|
+
`dict` of field names to their type annotations.
|
|
1324
1380
|
"""
|
|
1325
1381
|
# cls has no subscript: cls = FooBar
|
|
1326
1382
|
if isinstance(cls, type):
|
|
1327
1383
|
fields = get_fields(cls)
|
|
1328
1384
|
alias_map = {field.alias: name for name, field in fields.items() if field.alias}
|
|
1329
1385
|
|
|
1330
|
-
annotations: dict[str,
|
|
1386
|
+
annotations: dict[str, type | TypeVar] = {}
|
|
1331
1387
|
for name, param in inspect.signature(cls).parameters.items():
|
|
1332
1388
|
# Exclude hidden init args added by pydantic Config. For example if
|
|
1333
1389
|
# BaseModel(extra="allow") then "extra_data" will part of init sig.
|
|
@@ -1368,7 +1424,7 @@ def get_all_basemodel_annotations(
|
|
|
1368
1424
|
# generic_type_vars = (type vars in Baz)
|
|
1369
1425
|
# generic_map = {type var in Baz: str}
|
|
1370
1426
|
generic_type_vars: tuple = getattr(parent_origin, "__parameters__", ())
|
|
1371
|
-
generic_map = dict(zip(generic_type_vars, get_args(parent)))
|
|
1427
|
+
generic_map = dict(zip(generic_type_vars, get_args(parent), strict=False))
|
|
1372
1428
|
for field in getattr(parent_origin, "__annotations__", {}):
|
|
1373
1429
|
annotations[field] = _replace_type_vars(
|
|
1374
1430
|
annotations[field], generic_map, default_to_bound=default_to_bound
|
|
@@ -1381,20 +1437,20 @@ def get_all_basemodel_annotations(
|
|
|
1381
1437
|
|
|
1382
1438
|
|
|
1383
1439
|
def _replace_type_vars(
|
|
1384
|
-
type_:
|
|
1385
|
-
generic_map:
|
|
1440
|
+
type_: type | TypeVar,
|
|
1441
|
+
generic_map: dict[TypeVar, type] | None = None,
|
|
1386
1442
|
*,
|
|
1387
1443
|
default_to_bound: bool = True,
|
|
1388
|
-
) ->
|
|
1389
|
-
"""Replace
|
|
1444
|
+
) -> type | TypeVar:
|
|
1445
|
+
"""Replace `TypeVar`s in a type annotation with concrete types.
|
|
1390
1446
|
|
|
1391
1447
|
Args:
|
|
1392
1448
|
type_: The type annotation to process.
|
|
1393
|
-
generic_map: Mapping of
|
|
1394
|
-
default_to_bound: Whether to use TypeVar bounds as defaults.
|
|
1449
|
+
generic_map: Mapping of `TypeVar`s to concrete types.
|
|
1450
|
+
default_to_bound: Whether to use `TypeVar` bounds as defaults.
|
|
1395
1451
|
|
|
1396
1452
|
Returns:
|
|
1397
|
-
The type with
|
|
1453
|
+
The type with `TypeVar`s replaced.
|
|
1398
1454
|
"""
|
|
1399
1455
|
generic_map = generic_map or {}
|
|
1400
1456
|
if isinstance(type_, TypeVar):
|