Repository: langchain
Issue: StructuredTool.from_function does not support functools.partial
Labels: 🤖:bug

### Checked other resources

- [X] I added a very descriptive title to this issue.
- [X] I searched the LangChain documentation with the integrated search.
- [X] I used the GitHub search to find a similar question and didn't find it.
- [X] I am sure that this is a bug in LangChain rather than my code.
- [X] The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).

### Example Code

```python
def test_structured_tool_from_function_partial() -> None:
    """Test that structured tools can be created from a partial function."""

    def func(bar: str, baz: str=None) -> str:
        """Docstring

        Args:
            bar: str
            baz: str
        """
        return bar + baz

    structured_tool = StructuredTool.from_function(
        name="tool",
        description="A tool",
        func=partial(func, baz="foo"),
    )
    assert structured_tool.invoke({"bar": "bar"}) == "barfoo"
```

### Error Message and Stack Trace (if applicable)

```
tests/unit_tests/test_tools.py:484 (test_structured_tool_from_function_partial)
def test_structured_tool_from_function_partial() -> None:
        """Test that structured tools can be created from a partial function."""
    
        def func(bar: str, baz: str=None) -> str:
            """Docstring
    
            Args:
                bar: str
                baz: str
            """
            return bar + baz
    
>       structured_tool = StructuredTool.from_function(
            name="tool",
            description="A tool",
            func=partial(func, baz="foo"),
        )

test_tools.py:497: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../langchain_core/tools/structured.py:181: in from_function
    args_schema = create_schema_from_function(
../../langchain_core/tools/base.py:249: in create_schema_from_function
    validated = validate_arguments(func, config=_SchemaConfig)  # type: ignore
../../../../venv/lib/python3.11/site-packages/pydantic/deprecated/decorator.py:64: in validate_arguments
    return validate(func)
../../../../venv/lib/python3.11/site-packages/pydantic/deprecated/decorator.py:51: in validate
    vd = ValidatedFunction(_func, config)
../../../../venv/lib/python3.11/site-packages/pydantic/deprecated/decorator.py:94: in __init__
    type_hints = _typing_extra.get_type_hints(function, include_extras=True)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

obj = functools.partial(<function test_structured_tool_from_function_partial.<locals>.func at 0x119bd04a0>, baz='foo')
globalns = {}, localns = {}, include_extras = True

    def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
        """Return type hints for an object.
    
        This is often the same as obj.__annotations__, but it handles
        forward references encoded as string literals and recursively replaces all
        'Annotated[T, ...]' with 'T' (unless 'include_extras=True').
    
        The argument may be a module, class, method, or function. The annotations
        are returned as a dictionary. For classes, annotations include also
        inherited members.
    
        TypeError is raised if the argument is not of a type that can contain
        annotations, and an empty dictionary is returned if no annotations are
        present.
    
        BEWARE -- the behavior of globalns and localns is counterintuitive
        (unless you are familiar with how eval() and exec() work).  The
        search order is locals first, then globals.
    
        - If no dict arguments are passed, an attempt is made to use the
          globals from obj (or the respective module's globals for classes),
          and these are also used as the locals.  If the object does not appear
          to have globals, an empty dictionary is used.  For classes, the search
          order is globals first then locals.
    
        - If one dict argument is passed, it is used for both globals and
          locals.
    
        - If two dict arguments are passed, they specify globals and
          locals, respectively.
        """
        if getattr(obj, '__no_type_check__', None):
            return {}
        # Classes require a special treatment.
        if isinstance(obj, type):
            hints = {}
            for base in reversed(obj.__mro__):
                if globalns is None:
                    base_globals = getattr(sys.modules.get(base.__module__, None), '__dict__', {})
                else:
                    base_globals = globalns
                ann = base.__dict__.get('__annotations__', {})
                if isinstance(ann, types.GetSetDescriptorType):
                    ann = {}
                base_locals = dict(vars(base)) if localns is None else localns
                if localns is None and globalns is None:
                    # This is surprising, but required.  Before Python 3.10,
                    # get_type_hints only evaluated the globalns of
                    # a class.  To maintain backwards compatibility, we reverse
                    # the globalns and localns order so that eval() looks into
                    # *base_globals* first rather than *base_locals*.
                    # This only affects ForwardRefs.
                    base_globals, base_locals = base_locals, base_globals
                for name, value in ann.items():
                    if value is None:
                        value = type(None)
                    if isinstance(value, str):
                        value = ForwardRef(value, is_argument=False, is_class=True)
                    value = _eval_type(value, base_globals, base_locals)
                    hints[name] = value
            return hints if include_extras else {k: _strip_annotations(t) for k, t in hints.items()}
    
        if globalns is None:
            if isinstance(obj, types.ModuleType):
                globalns = obj.__dict__
            else:
                nsobj = obj
                # Find globalns for the unwrapped object.
                while hasattr(nsobj, '__wrapped__'):
                    nsobj = nsobj.__wrapped__
                globalns = getattr(nsobj, '__globals__', {})
            if localns is None:
                localns = globalns
        elif localns is None:
            localns = globalns
        hints = getattr(obj, '__annotations__', None)
        if hints is None:
            # Return empty annotations for something that _could_ have them.
            if isinstance(obj, _allowed_types):
                return {}
            else:
>               raise TypeError('{!r} is not a module, class, method, '
                                'or function.'.format(obj))
E               TypeError: functools.partial(<function test_structured_tool_from_function_partial.<locals>.func at 0x119bd04a0>, baz='foo') is not a module, class, method, or function.

/Users/xxx/.pyenv/versions/3.11.4/lib/python3.11/typing.py:2359: TypeError
```

### Description

A value to source_function must be assigned based on the type of the func object in the StructuredTool.from_function method.

### System Info

System Information
------------------
> OS:  Darwin
> OS Version:  Darwin Kernel Version 23.3.0: Wed Dec 20 21:30:44 PST 2023; root:xnu-10002.81.5~7/RELEASE_ARM64_T6000
> Python Version:  3.11.4 (main, Sep  6 2023, 14:18:32) [Clang 13.1.6 (clang-1316.0.21.2.5)]

Package Information
-------------------
> langchain_core: 0.3.11
> langchain: 0.1.16
> langchain_community: 0.0.34
> langsmith: 0.1.128
> langchain_openai: 0.1.3
> langchain_text_splitters: 0.3.0

Optional packages not installed
-------------------------------
> langgraph
> langserve

Other Dependencies
------------------
> aiohttp: 3.10.6
> aiosqlite: Installed. No version info available.
> aleph-alpha-client: Installed. No version info available.
> anthropic: 0.3.11
> arxiv: Installed. No version info available.
> assemblyai: Installed. No version info available.
> async-timeout: Installed. No version info available.
> atlassian-python-api: Installed. No version info available.
> azure-ai-documentintelligence: Installed. No version info available.
> azure-ai-formrecognizer: Installed. No version info available.
> azure-ai-textanalytics: Installed. No version info available.
> azure-cognitiveservices-speech: Installed. No version info available.
> azure-core: Installed. No version info available.
> azure-cosmos: Installed. No version info available.
> azure-identity: Installed. No version info available.
> azure-search-documents: Installed. No version info available.
> beautifulsoup4: 4.12.3
> bibtexparser: Installed. No version info available.
> cassio: 0.1.6
> chardet: Installed. No version info available.
> clarifai: Installed. No version info available.
> cloudpickle: Installed. No version info available.
> cohere: Installed. No version info available.
> couchbase: Installed. No version info available.
> dashvector: Installed. No version info available.
> databricks-vectorsearch: Installed. No version info available.
> dataclasses-json: 0.6.4
> datasets: Installed. No version info available.
> dgml-utils: Installed. No version info available.
> docarray[hnswlib]: Installed. No version info available.
> elasticsearch: Installed. No version info available.
> esprima: Installed. No version info available.
> faiss-cpu: Installed. No version info available.
> feedparser: Installed. No version info available.
> fireworks-ai: 0.9.0
> friendli-client: Installed. No version info available.
> geopandas: Installed. No version info available.
> gitpython: Installed. No version info available.
> google-cloud-documentai: Installed. No version info available.
> gql: Installed. No version info available.
> gradientai: Installed. No version info available.
> hdbcli: Installed. No version info available.
> hologres-vector: Installed. No version info available.
> html2text: Installed. No version info available.
> httpx: 0.27.2
> httpx-sse: 0.4.0
> huggingface_hub: 0.25.2
> javelin-sdk: Installed. No version info available.
> jinja2: 3.1.4
> jq: Installed. No version info available.
> jsonpatch: 1.33
> jsonschema: 4.23.0
> lxml: Installed. No version info available.
> manifest-ml: Installed. No version info available.
> markdownify: Installed. No version info available.
> motor: Installed. No version info available.
> msal: Installed. No version info available.
> mwparserfromhell: Installed. No version info available.
> mwxml: Installed. No version info available.
> newspaper3k: Installed. No version info available.
> nlpcloud: Installed. No version info available.
> numexpr: Installed. No version info available.
> numpy: 1.26.4
> nvidia-riva-client: Installed. No version info available.
> oci: Installed. No version info available.
> openai: 1.13.3
> openapi-pydantic: Installed. No version info available.
> openlm: Installed. No version info available.
> oracle-ads: Installed. No version info available.
> orjson: 3.10.7
> packaging: 24.1
> pandas: 2.0.3
> pdfminer-six: Installed. No version info available.
> pgvector: Installed. No version info available.
> praw: Installed. No version info available.
> premai: Installed. No version info available.
> psychicapi: Installed. No version info available.
> py-trello: Installed. No version info available.
> pydantic: 2.9.2
> pyjwt: Installed. No version info available.
> pymupdf: Installed. No version info available.
> pypdf: Installed. No version info available.
> pypdfium2: Installed. No version info available.
> pyspark: Installed. No version info available.
> PyYAML: 6.0.2
> qdrant-client: Installed. No version info available.
> rank-bm25: Installed. No version info available.
> rapidfuzz: Installed. No version info available.
> rapidocr-onnxruntime: Installed. No version info available.
> rdflib: Installed. No version info available.
> requests: 2.32.3
> requests-toolbelt: Installed. No version info available.
> rspace_client: Installed. No version info available.
> scikit-learn: Installed. No version info available.
> sentence-transformers: Installed. No version info available.
> SQLAlchemy: 2.0.35
> sqlite-vss: Installed. No version info available.
> streamlit: Installed. No version info available.
> sympy: 1.13.3
> telethon: Installed. No version info available.
> tenacity: 8.5.0
> tidb-vector: Installed. No version info available.
> tiktoken: 0.5.2
> timescale-vector: Installed. No version info available.
> torch: Installed. No version info available.
> tqdm: 4.66.5
> transformers: Installed. No version info available.
> tree-sitter: Installed. No version info available.
> tree-sitter-languages: Installed. No version info available.
> typer: Installed. No version info available.
> typing-extensions: 4.12.2
> upstash-redis: Installed. No version info available.
> vdms: 0.0.20
> xata: Installed. No version info available.
> xmltodict: Installed. No version info available.