AI & Agent Dev Bug Sandbox logo
AI & Agent Dev Bug Sandbox
Back to Radar

Postponed Callable Annotations Break Runnable Schemas And Tool Conversion

When `from __future__ import annotations` is used, stringized type annotations are not resolved in the correct namespace for `RunnableLambda` and `RunnableGenerator`, causing `PydanticUserError` or empty tool argument schemas during `as_tool()` conversion, even though direct invocation works.

highConfidence 89%LangChainAffected V1.6.6

Origin Analysis

`RunnableGenerator.InputType` and `OutputType` inspect raw annotation `__args__` directly; with postponed annotations, annotations are strings, so the raw string becomes `Any`. `RunnableLambda.InputType` returns the raw string annotation, and the Pydantic model is built in the Runnable class's module (where the referenced type may not be available) instead of the callable's defining namespace. Partial functions also lose the proper module for output-schema resolution because `functools.partial` objects do not carry `__globals__` of the underlying function.
Enable `from __future__ import annotations` in Python. Define a `TypedDict` class `Query` with a `query: str` field. Define a sync function `lookup(request: Query) -> str` and wrap it in `RunnableLambda`. Call `runnable.as_tool()` and then `tool.invoke({'query': 'hello'})`. The expected tool should have a required string `query` argument and return `HELLO`. Instead, `PydanticUserError` is raised. A `RunnableGenerator` with an `Iterator[Query]` input produces a tool with empty `args` and then raises `KeyError: 'query'`.

Fixing Code Block

Edge Case Audit

Resolving types eagerly may fail for forward references inside function bodies or for dynamically defined types not present in `__globals__`; the fallback to `Any` could silently change schema generation. The fix may introduce a performance overhead for every Runnable creation due to `get_type_hints` calls. It should be gated behind a flag or cached to avoid regression. If the callable's module imports change or types are deleted, resolution may raise and fall back, losing precise types. Rollback: revert the patch and users can manually call `.with_types()` or avoid postponed annotations. Extensive regression testing is needed across Python versions (3.9-3.14) and edge cases like partial with keywords, async generators, and callable objects.

Ecosystem Topology