chore(core): bump mypy version to 1.17 (#32390)

Co-authored-by: Mason Daugherty <mason@langchain.dev>
This commit is contained in:
Christophe Bornet 2025-08-07 19:26:29 +02:00 committed by GitHub
parent 42c1159991
commit 499dc35cfb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 676 additions and 566 deletions

View File

@ -144,7 +144,7 @@ def beta(
obj.__init__ = functools.wraps(obj.__init__)( # type: ignore[misc] obj.__init__ = functools.wraps(obj.__init__)( # type: ignore[misc]
warn_if_direct_instance warn_if_direct_instance
) )
return cast("T", obj) return obj
elif isinstance(obj, property): elif isinstance(obj, property):
# note(erick): this block doesn't seem to be used? # note(erick): this block doesn't seem to be used?

View File

@ -225,7 +225,7 @@ def deprecated(
obj.__init__ = functools.wraps(obj.__init__)( # type: ignore[misc] obj.__init__ = functools.wraps(obj.__init__)( # type: ignore[misc]
warn_if_direct_instance warn_if_direct_instance
) )
return cast("T", obj) return obj
elif isinstance(obj, FieldInfoV1): elif isinstance(obj, FieldInfoV1):
wrapped = None wrapped = None

View File

@ -107,7 +107,11 @@ class _StreamingParser:
self.buffer = "" self.buffer = ""
# yield all events # yield all events
try: try:
for event, elem in self.pull_parser.read_events(): for raw_event in self.pull_parser.read_events():
if len(raw_event) <= 1:
continue
event, elem = raw_event
if isinstance(elem, ET.Element):
if event == "start": if event == "start":
# update current path # update current path
self.current_path.append(elem.tag) self.current_path.append(elem.tag)

View File

@ -5300,7 +5300,7 @@ class RunnableBindingBase(RunnableSerializable[Input, Output]):
kwargs. kwargs.
""" """
config: RunnableConfig = Field(default_factory=RunnableConfig) # type: ignore[arg-type] config: RunnableConfig = Field(default_factory=RunnableConfig)
"""The config to bind to the underlying Runnable.""" """The config to bind to the underlying Runnable."""
config_factories: list[Callable[[RunnableConfig], RunnableConfig]] = Field( config_factories: list[Callable[[RunnableConfig], RunnableConfig]] = Field(

View File

@ -5,7 +5,7 @@ import inspect
import typing import typing
from collections.abc import AsyncIterator, Iterator, Sequence from collections.abc import AsyncIterator, Iterator, Sequence
from functools import wraps from functools import wraps
from typing import TYPE_CHECKING, Any, Optional, Union from typing import TYPE_CHECKING, Any, Optional, Union, cast
from pydantic import BaseModel, ConfigDict from pydantic import BaseModel, ConfigDict
from typing_extensions import override from typing_extensions import override
@ -397,7 +397,7 @@ class RunnableWithFallbacks(RunnableSerializable[Input, Output]):
) )
) )
to_return = {} to_return: dict[int, Union[Output, BaseException]] = {}
run_again = dict(enumerate(inputs)) run_again = dict(enumerate(inputs))
handled_exceptions: dict[int, BaseException] = {} handled_exceptions: dict[int, BaseException] = {}
first_to_raise = None first_to_raise = None
@ -447,7 +447,7 @@ class RunnableWithFallbacks(RunnableSerializable[Input, Output]):
if not return_exceptions and sorted_handled_exceptions: if not return_exceptions and sorted_handled_exceptions:
raise sorted_handled_exceptions[0][1] raise sorted_handled_exceptions[0][1]
to_return.update(handled_exceptions) to_return.update(handled_exceptions)
return [output for _, output in sorted(to_return.items())] # type: ignore[misc] return [cast("Output", output) for _, output in sorted(to_return.items())]
@override @override
def stream( def stream(
@ -569,7 +569,7 @@ class RunnableWithFallbacks(RunnableSerializable[Input, Output]):
async for chunk in stream: async for chunk in stream:
yield chunk yield chunk
try: try:
output = output + chunk output = output + chunk # type: ignore[operator]
except TypeError: except TypeError:
output = None output = None
except BaseException as e: except BaseException as e:

View File

@ -177,7 +177,7 @@ class RunLog(RunLogPatch):
# Then compare that the ops are the same # Then compare that the ops are the same
return super().__eq__(other) return super().__eq__(other)
__hash__ = None # type: ignore[assignment] __hash__ = None
T = TypeVar("T") T = TypeVar("T")

View File

@ -277,7 +277,7 @@ def _convert_any_typed_dicts_to_pydantic(
) )
fields: dict = {} fields: dict = {}
for arg, arg_type in annotations_.items(): for arg, arg_type in annotations_.items():
if get_origin(arg_type) is Annotated: if get_origin(arg_type) is Annotated: # type: ignore[comparison-overlap]
annotated_args = get_args(arg_type) annotated_args = get_args(arg_type)
new_arg_type = _convert_any_typed_dicts_to_pydantic( new_arg_type = _convert_any_typed_dicts_to_pydantic(
annotated_args[0], depth=depth + 1, visited=visited annotated_args[0], depth=depth + 1, visited=visited

View File

@ -417,7 +417,7 @@ class AIMessageChunk(AIMessage):
self._tool_call_chunks = [ self._tool_call_chunks = [
block for block in self.content if types.is_tool_call_chunk(block) block for block in self.content if types.is_tool_call_chunk(block)
] ]
return cast("list[types.ToolCallChunk]", self._tool_call_chunks) return self._tool_call_chunks
@property @property
def tool_calls(self) -> list[types.ToolCall]: def tool_calls(self) -> list[types.ToolCall]:

View File

@ -28,7 +28,7 @@ repository = "https://github.com/langchain-ai/langchain"
[dependency-groups] [dependency-groups]
lint = ["ruff<0.13,>=0.12.2"] lint = ["ruff<0.13,>=0.12.2"]
typing = [ typing = [
"mypy<1.16,>=1.15", "mypy<1.18,>=1.17.1",
"types-pyyaml<7.0.0.0,>=6.0.12.2", "types-pyyaml<7.0.0.0,>=6.0.12.2",
"types-requests<3.0.0.0,>=2.28.11.5", "types-requests<3.0.0.0,>=2.28.11.5",
"langchain-text-splitters", "langchain-text-splitters",

View File

@ -479,28 +479,12 @@ class TestFactoryTypeConsistency:
class TestExtraItems: class TestExtraItems:
"""Test that content blocks support extra items via __extra_items__ field.""" """Test that content blocks support extra items."""
def test_text_block_extra_items(self) -> None:
"""Test that TextContentBlock can store extra provider-specific fields."""
block = create_text_block("Hello world")
block["openai_metadata"] = {"model": "gpt-4", "temperature": 0.7} # type: ignore[typeddict-unknown-key]
block["anthropic_usage"] = {"input_tokens": 10, "output_tokens": 20} # type: ignore[typeddict-unknown-key]
block["custom_field"] = "any value" # type: ignore[typeddict-unknown-key]
assert block["type"] == "text"
assert block["text"] == "Hello world"
assert "id" in block
assert block.get("openai_metadata") == {"model": "gpt-4", "temperature": 0.7}
assert block.get("anthropic_usage") == {"input_tokens": 10, "output_tokens": 20}
assert block.get("custom_field") == "any value"
def test_text_block_extras_field(self) -> None: def test_text_block_extras_field(self) -> None:
"""Test that TextContentBlock properly supports the explicit extras field.""" """Test that TextContentBlock properly supports the extras field."""
block = create_text_block("Hello world") block = create_text_block("Hello world")
# Test direct assignment to extras field
block["extras"] = { block["extras"] = {
"openai_metadata": {"model": "gpt-4", "temperature": 0.7}, "openai_metadata": {"model": "gpt-4", "temperature": 0.7},
"anthropic_usage": {"input_tokens": 10, "output_tokens": 20}, "anthropic_usage": {"input_tokens": 10, "output_tokens": 20},
@ -518,32 +502,6 @@ class TestExtraItems:
assert extras.get("anthropic_usage") == expected_usage assert extras.get("anthropic_usage") == expected_usage
assert extras.get("custom_field") == "any value" assert extras.get("custom_field") == "any value"
def test_mixed_extra_items_types(self) -> None:
"""Test that extra items can be various types (str, int, bool, dict, list)."""
block = create_text_block("Test content")
# Add various types of extra fields
block["string_field"] = "string value" # type: ignore[typeddict-unknown-key]
block["int_field"] = 42 # type: ignore[typeddict-unknown-key]
block["float_field"] = 3.14 # type: ignore[typeddict-unknown-key]
block["bool_field"] = True # type: ignore[typeddict-unknown-key]
block["list_field"] = ["item1", "item2", "item3"] # type: ignore[typeddict-unknown-key]
block["dict_field"] = {"nested": {"deeply": "nested value"}} # type: ignore[typeddict-unknown-key]
block["none_field"] = None # type: ignore[typeddict-unknown-key]
# Verify all types are preserved
assert block.get("string_field") == "string value"
assert block.get("int_field") == 42
assert block.get("float_field") == 3.14
assert block.get("bool_field") is True
assert block.get("list_field") == ["item1", "item2", "item3"]
dict_field = block.get("dict_field", {})
assert isinstance(dict_field, dict)
nested = dict_field.get("nested", {})
assert isinstance(nested, dict)
assert nested.get("deeply") == "nested value"
assert block.get("none_field") is None
def test_extra_items_do_not_interfere_with_standard_fields(self) -> None: def test_extra_items_do_not_interfere_with_standard_fields(self) -> None:
"""Test that extra items don't interfere with standard field access.""" """Test that extra items don't interfere with standard field access."""
block = create_text_block("Original text", index=1) block = create_text_block("Original text", index=1)
@ -567,22 +525,21 @@ class TestExtraItems:
block = create_image_block(url="https://example.com/image.jpg") block = create_image_block(url="https://example.com/image.jpg")
# Add an extra field # Add an extra field
block["status"] = "pending" # type: ignore[typeddict-unknown-key] block["extras"] = {"status": "pending"}
assert block.get("status") == "pending" assert block["extras"].get("status") == "pending"
# Modify the extra field # Modify the extra field
block["status"] = "processed" # type: ignore[typeddict-unknown-key] block["extras"] = {"status": "processed"}
assert block.get("status") == "processed" assert block["extras"].get("status") == "processed"
# Add more fields # Add more fields
block["metadata"] = {"version": 1} # type: ignore[typeddict-unknown-key] block["extras"] = {"metadata": {"version": 1}}
metadata = block.get("metadata", {}) metadata = block["extras"].get("metadata", {})
assert isinstance(metadata, dict) assert isinstance(metadata, dict)
assert metadata.get("version") == 1 assert metadata.get("version") == 1
# Modify nested extra field # Modify nested extra field
block["metadata"]["version"] = 2 # type: ignore[typeddict-item] metadata["version"] = 2
metadata = block.get("metadata", {})
assert isinstance(metadata, dict) assert isinstance(metadata, dict)
assert metadata.get("version") == 2 assert metadata.get("version") == 2
@ -590,40 +547,36 @@ class TestExtraItems:
"""Test that all content block types support extra items.""" """Test that all content block types support extra items."""
# Test each content block type # Test each content block type
text_block = create_text_block("test") text_block = create_text_block("test")
text_block["extra"] = "text_extra" # type: ignore[typeddict-unknown-key] text_block["extras"] = {"text_extra": "a"}
assert text_block.get("extra") == "text_extra" assert text_block.get("extras") == {"text_extra": "a"}
image_block = create_image_block(url="https://example.com/image.jpg") image_block = create_image_block(url="https://example.com/image.jpg")
image_block["extra"] = "image_extra" # type: ignore[typeddict-unknown-key] image_block["extras"] = {"image_extra": "a"}
assert image_block.get("extra") == "image_extra" assert image_block.get("extras") == {"image_extra": "a"}
video_block = create_video_block(url="https://example.com/video.mp4") video_block = create_video_block(url="https://example.com/video.mp4")
video_block["extra"] = "video_extra" # type: ignore[typeddict-unknown-key] video_block["extras"] = {"video_extra": "a"}
assert video_block.get("extra") == "video_extra" assert video_block.get("extras") == {"video_extra": "a"}
audio_block = create_audio_block(url="https://example.com/audio.mp3") audio_block = create_audio_block(url="https://example.com/audio.mp3")
audio_block["extra"] = "audio_extra" # type: ignore[typeddict-unknown-key] audio_block["extras"] = {"audio_extra": "a"}
assert audio_block.get("extra") == "audio_extra" assert audio_block.get("extras") == {"audio_extra": "a"}
file_block = create_file_block(url="https://example.com/file.pdf") file_block = create_file_block(url="https://example.com/file.pdf")
file_block["extra"] = "file_extra" # type: ignore[typeddict-unknown-key] file_block["extras"] = {"file_extra": "a"}
assert file_block.get("extra") == "file_extra" assert file_block.get("extras") == {"file_extra": "a"}
plain_text_block = create_plaintext_block("content") plain_text_block = create_plaintext_block("content")
plain_text_block["extra"] = "plaintext_extra" # type: ignore[typeddict-unknown-key] plain_text_block["extras"] = {"plaintext_extra": "a"}
assert plain_text_block.get("extra") == "plaintext_extra" assert plain_text_block.get("extras") == {"plaintext_extra": "a"}
tool_call = create_tool_call("tool", {"arg": "value"}) tool_call = create_tool_call("tool", {"arg": "value"})
tool_call["extra"] = "tool_extra" # type: ignore[typeddict-unknown-key] tool_call["extras"] = {"tool_extra": "a"}
assert tool_call.get("extra") == "tool_extra" assert tool_call.get("extras") == {"tool_extra": "a"}
reasoning_block = create_reasoning_block("reasoning") reasoning_block = create_reasoning_block("reasoning")
reasoning_block["extra"] = "reasoning_extra" # type: ignore[typeddict-unknown-key] reasoning_block["extras"] = {"reasoning_extra": "a"}
assert reasoning_block.get("extra") == "reasoning_extra" assert reasoning_block.get("extras") == {"reasoning_extra": "a"}
non_standard_block = create_non_standard_block({"data": "value"})
non_standard_block["extra"] = "non_standard_extra" # type: ignore[typeddict-unknown-key]
assert non_standard_block.get("extra") == "non_standard_extra"
class TestExtrasField: class TestExtrasField:

View File

@ -81,7 +81,7 @@ def test_structured_prompt_dict() -> None:
chain = loads(dumps(prompt)) | model chain = loads(dumps(prompt)) | model
assert chain.invoke({"hello": "there"}) == {"name": 1, "value": 42} # type: ignore[comparison-overlap] assert chain.invoke({"hello": "there"}) == {"name": 1, "value": 42}
def test_structured_prompt_kwargs() -> None: def test_structured_prompt_kwargs() -> None:
@ -104,7 +104,7 @@ def test_structured_prompt_kwargs() -> None:
assert chain.invoke({"hello": "there"}) == {"name": 1, "value": 7} # type: ignore[comparison-overlap] assert chain.invoke({"hello": "there"}) == {"name": 1, "value": 7} # type: ignore[comparison-overlap]
assert loads(dumps(prompt)).model_dump() == prompt.model_dump() assert loads(dumps(prompt)).model_dump() == prompt.model_dump()
chain = loads(dumps(prompt)) | model chain = loads(dumps(prompt)) | model
assert chain.invoke({"hello": "there"}) == {"name": 1, "value": 7} # type: ignore[comparison-overlap] assert chain.invoke({"hello": "there"}) == {"name": 1, "value": 7}
class OutputSchema(BaseModel): class OutputSchema(BaseModel):
name: str name: str

File diff suppressed because it is too large Load Diff