Compare commits

...

2 Commits

Author SHA1 Message Date
Eugene Yurtsev
ee6369c26c x 2025-09-11 14:15:19 -04:00
Eugene Yurtsev
454cda8a24 x 2025-09-11 13:50:04 -04:00
5 changed files with 22 additions and 23 deletions

View File

@@ -434,7 +434,7 @@ class ChildTool(BaseTool):
name: str
"""The unique name of the tool that clearly communicates its purpose."""
description: str
description: Optional[str] = None
"""Used to tell the model how/when/why to use the tool.
You can provide few-shot examples as a part of the description.

View File

@@ -39,7 +39,7 @@ if TYPE_CHECKING:
class StructuredTool(BaseTool):
"""Tool that can operate on any number of inputs."""
description: str = ""
description: Optional[str] = None
args_schema: Annotated[ArgsSchema, SkipValidation()] = Field(
..., description="The tool schema."
)
@@ -237,21 +237,19 @@ class StructuredTool(BaseTool):
f"got {args_schema}"
)
raise TypeError(msg)
if description_ is None:
msg = "Function must have a docstring if description not provided."
raise ValueError(msg)
if description is None:
if description is None and description_ is not None:
# Only apply if using the function's docstring
description_ = textwrap.dedent(description_).strip()
# Description example:
# search_api(query: str) - Searches the API for the query.
description_ = f"{description_.strip()}"
if description_:
# Always strip description if it exists
description_ = description_.strip()
return cls(
name=name,
func=func,
coroutine=coroutine,
args_schema=args_schema,
args_schema=args_schema, # type: ignore[arg-type]
description=description_,
return_direct=return_direct,
response_format=response_format,

View File

@@ -22,7 +22,7 @@ from typing import (
from pydantic import BaseModel
from pydantic.v1 import BaseModel as BaseModelV1
from pydantic.v1 import Field, create_model
from typing_extensions import TypedDict, get_args, get_origin, is_typeddict
from typing_extensions import NotRequired, TypedDict, get_args, get_origin, is_typeddict
import langchain_core
from langchain_core._api import beta, deprecated
@@ -48,7 +48,7 @@ class FunctionDescription(TypedDict):
name: str
"""The name of the function."""
description: str
description: NotRequired[str]
"""A description of the function."""
parameters: dict
"""The parameters of the function."""

View File

@@ -702,19 +702,21 @@ def test_tool_with_kwargs() -> None:
def test_missing_docstring() -> None:
"""Test error is raised when docstring is missing."""
# expect to throw a value error if there's no docstring
with pytest.raises(ValueError, match="Function must have a docstring"):
@tool
def search_api(query: str) -> str:
return "API result"
"""Test error is not raised when docstring is missing."""
@tool
class MyTool(BaseModel):
foo: str
def search_api(query: str) -> str:
return "API result"
assert not MyTool.description # type: ignore[attr-defined]
assert search_api.name == "search_api"
assert search_api.description is None
assert search_api.args_schema
assert search_api.args_schema.model_json_schema() == {
"properties": {"query": {"title": "Query", "type": "string"}},
"required": ["query"],
"title": "search_api",
"type": "object",
}
def test_create_tool_positional_args() -> None:

View File

@@ -93,7 +93,6 @@ class _MagicFunctionSchema(BaseModel):
@tool(args_schema=_MagicFunctionSchema)
def magic_function(_input: int) -> int:
"""Apply a magic function to an input."""
return _input + 2