diff --git a/libs/core/langchain_core/embeddings/fake.py b/libs/core/langchain_core/embeddings/fake.py index b5286a9a09ce8..6f7c4241d54b2 100644 --- a/libs/core/langchain_core/embeddings/fake.py +++ b/libs/core/langchain_core/embeddings/fake.py @@ -53,7 +53,7 @@ class FakeEmbeddings(Embeddings, BaseModel): def _get_embedding(self) -> list[float]: import numpy as np # type: ignore[import-not-found, import-untyped] - return list(np.random.normal(size=self.size)) + return list(np.random.default_rng().normal(size=self.size)) def embed_documents(self, texts: list[str]) -> list[list[float]]: return [self._get_embedding() for _ in texts] @@ -109,8 +109,8 @@ def _get_embedding(self, seed: int) -> list[float]: import numpy as np # type: ignore[import-not-found, import-untyped] # set the seed for the random generator - np.random.seed(seed) - return list(np.random.normal(size=self.size)) + rng = np.random.default_rng(seed) + return list(rng.normal(size=self.size)) def _get_seed(self, text: str) -> int: """Get a seed for the random generator, using the hash of the text.""" diff --git a/libs/core/langchain_core/language_models/base.py b/libs/core/langchain_core/language_models/base.py index d1ac7891d85b0..5e60c890a4708 100644 --- a/libs/core/langchain_core/language_models/base.py +++ b/libs/core/langchain_core/language_models/base.py @@ -237,7 +237,7 @@ def with_structured_output( """Not implemented on this class.""" # Implement this on child class if there is a way of steering the model to # generate responses that match a given schema. - raise NotImplementedError() + raise NotImplementedError @deprecated("0.1.7", alternative="invoke", removal="1.0") @abstractmethod diff --git a/libs/core/langchain_core/language_models/chat_models.py b/libs/core/langchain_core/language_models/chat_models.py index ad238eda45940..39fd11c247f9f 100644 --- a/libs/core/langchain_core/language_models/chat_models.py +++ b/libs/core/langchain_core/language_models/chat_models.py @@ -977,7 +977,7 @@ def _stream( run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> Iterator[ChatGenerationChunk]: - raise NotImplementedError() + raise NotImplementedError async def _astream( self, @@ -1112,7 +1112,7 @@ def bind_tools( ], **kwargs: Any, ) -> Runnable[LanguageModelInput, BaseMessage]: - raise NotImplementedError() + raise NotImplementedError def with_structured_output( self, diff --git a/libs/core/langchain_core/language_models/llms.py b/libs/core/langchain_core/language_models/llms.py index c247078a3a832..7fd47e627d739 100644 --- a/libs/core/langchain_core/language_models/llms.py +++ b/libs/core/langchain_core/language_models/llms.py @@ -698,7 +698,7 @@ def _stream( Returns: An iterator of GenerationChunks. """ - raise NotImplementedError() + raise NotImplementedError async def _astream( self, diff --git a/libs/core/langchain_core/output_parsers/openai_functions.py b/libs/core/langchain_core/output_parsers/openai_functions.py index d61533855fd7c..66b8865a1ea2b 100644 --- a/libs/core/langchain_core/output_parsers/openai_functions.py +++ b/libs/core/langchain_core/output_parsers/openai_functions.py @@ -151,7 +151,7 @@ def parse(self, text: str) -> Any: Returns: The parsed JSON object. """ - raise NotImplementedError() + raise NotImplementedError class JsonKeyOutputFunctionsParser(JsonOutputFunctionsParser): diff --git a/libs/core/langchain_core/output_parsers/openai_tools.py b/libs/core/langchain_core/output_parsers/openai_tools.py index 9e768e5da44e4..2944eff4223eb 100644 --- a/libs/core/langchain_core/output_parsers/openai_tools.py +++ b/libs/core/langchain_core/output_parsers/openai_tools.py @@ -207,7 +207,7 @@ def parse(self, text: str) -> Any: Returns: The parsed tool calls. """ - raise NotImplementedError() + raise NotImplementedError class JsonOutputKeyToolsParser(JsonOutputToolsParser): diff --git a/libs/core/langchain_core/output_parsers/transform.py b/libs/core/langchain_core/output_parsers/transform.py index 8367681d56ba5..0636d2d661b2b 100644 --- a/libs/core/langchain_core/output_parsers/transform.py +++ b/libs/core/langchain_core/output_parsers/transform.py @@ -106,7 +106,7 @@ def _diff(self, prev: Optional[T], next: T) -> T: Returns: The diff between the previous and current parsed output. """ - raise NotImplementedError() + raise NotImplementedError def _transform(self, input: Iterator[Union[str, BaseMessage]]) -> Iterator[Any]: prev_parsed = None diff --git a/libs/core/langchain_core/prompts/chat.py b/libs/core/langchain_core/prompts/chat.py index e3771ab40a6f5..111aed89b3c97 100644 --- a/libs/core/langchain_core/prompts/chat.py +++ b/libs/core/langchain_core/prompts/chat.py @@ -1336,7 +1336,7 @@ def save(self, file_path: Union[Path, str]) -> None: Args: file_path: path to file. """ - raise NotImplementedError() + raise NotImplementedError def pretty_repr(self, html: bool = False) -> str: """Human-readable representation. diff --git a/libs/core/langchain_core/prompts/few_shot.py b/libs/core/langchain_core/prompts/few_shot.py index 54c004c23d662..96e70c17d95cb 100644 --- a/libs/core/langchain_core/prompts/few_shot.py +++ b/libs/core/langchain_core/prompts/few_shot.py @@ -464,4 +464,4 @@ def pretty_repr(self, html: bool = False) -> str: Returns: A pretty representation of the prompt template. """ - raise NotImplementedError() + raise NotImplementedError diff --git a/libs/core/langchain_core/prompts/image.py b/libs/core/langchain_core/prompts/image.py index 5e76cea59debb..a75a5eece0f91 100644 --- a/libs/core/langchain_core/prompts/image.py +++ b/libs/core/langchain_core/prompts/image.py @@ -132,4 +132,4 @@ def pretty_repr(self, html: bool = False) -> str: Returns: A pretty representation of the prompt. """ - raise NotImplementedError() + raise NotImplementedError diff --git a/libs/core/langchain_core/runnables/graph_mermaid.py b/libs/core/langchain_core/runnables/graph_mermaid.py index 70b8e4865b5a6..2c8319f027bf0 100644 --- a/libs/core/langchain_core/runnables/graph_mermaid.py +++ b/libs/core/langchain_core/runnables/graph_mermaid.py @@ -1,3 +1,4 @@ +import asyncio import base64 import re from dataclasses import asdict @@ -289,9 +290,14 @@ async def _render_mermaid_using_pyppeteer( img_bytes = await page.screenshot({"fullPage": False}) await browser.close() + def write_to_file(path: str, bytes: bytes) -> None: + with open(path, "wb") as file: + file.write(bytes) + if output_file_path is not None: - with open(output_file_path, "wb") as file: - file.write(img_bytes) + await asyncio.get_event_loop().run_in_executor( + None, write_to_file, output_file_path, img_bytes + ) return img_bytes diff --git a/libs/core/langchain_core/utils/utils.py b/libs/core/langchain_core/utils/utils.py index 94f356da33116..c66ed52228c42 100644 --- a/libs/core/langchain_core/utils/utils.py +++ b/libs/core/langchain_core/utils/utils.py @@ -453,7 +453,7 @@ def get_secret_from_env() -> Optional[SecretStr]: return SecretStr(os.environ[key]) if isinstance(default, str): return SecretStr(default) - elif isinstance(default, type(None)): + elif default is None: return None else: if error_message: diff --git a/libs/core/pyproject.toml b/libs/core/pyproject.toml index 365db50c53be1..80ef45805d26d 100644 --- a/libs/core/pyproject.toml +++ b/libs/core/pyproject.toml @@ -44,8 +44,42 @@ python = ">=3.12.4" [tool.poetry.extras] [tool.ruff.lint] -select = [ "B", "C4", "E", "EM", "F", "I", "N", "PIE", "SIM", "T201", "UP", "W",] -ignore = [ "UP007", "W293",] +select = [ + "ASYNC", + "B", + "C4", + "COM", + "DJ", + "E", + "EM", + "EXE", + "F", + "FLY", + "FURB", + "I", + "ICN", + "INT", + "LOG", + "N", + "NPY", + "PD", + "PIE", + "Q", + "RSE", + "SIM", + "SLOT", + "T10", + "T201", + "TID", + "UP", + "W", + "YTT" +] +ignore = [ + "COM812", # Messes with the formatter + "UP007", # Incompatible with pydantic + Python 3.9 + "W293", # +] [tool.coverage.run] omit = [ "tests/*",] diff --git a/libs/core/tests/unit_tests/chat_history/test_chat_history.py b/libs/core/tests/unit_tests/chat_history/test_chat_history.py index 729f15381c175..eb7690335f974 100644 --- a/libs/core/tests/unit_tests/chat_history/test_chat_history.py +++ b/libs/core/tests/unit_tests/chat_history/test_chat_history.py @@ -17,7 +17,7 @@ def add_message(self, message: BaseMessage) -> None: def clear(self) -> None: """Clear the store.""" - raise NotImplementedError() + raise NotImplementedError store: list[BaseMessage] = [] chat_history = SampleChatHistory(store=store) @@ -50,7 +50,7 @@ def add_messages(self, message: Sequence[BaseMessage]) -> None: def clear(self) -> None: """Clear the store.""" - raise NotImplementedError() + raise NotImplementedError chat_history = BulkAddHistory(store=store) chat_history.add_message(HumanMessage(content="Hello")) diff --git a/libs/core/tests/unit_tests/language_models/chat_models/test_base.py b/libs/core/tests/unit_tests/language_models/chat_models/test_base.py index 85f6d5bee1d91..2cd08a27d0383 100644 --- a/libs/core/tests/unit_tests/language_models/chat_models/test_base.py +++ b/libs/core/tests/unit_tests/language_models/chat_models/test_base.py @@ -165,7 +165,7 @@ def _generate( **kwargs: Any, ) -> ChatResult: """Top Level call""" - raise NotImplementedError() + raise NotImplementedError def _stream( self, @@ -210,7 +210,7 @@ def _generate( **kwargs: Any, ) -> ChatResult: """Top Level call""" - raise NotImplementedError() + raise NotImplementedError async def _astream( # type: ignore self, diff --git a/libs/core/tests/unit_tests/language_models/llms/test_base.py b/libs/core/tests/unit_tests/language_models/llms/test_base.py index 9c995f924ce57..f8ff2fdc4f2a6 100644 --- a/libs/core/tests/unit_tests/language_models/llms/test_base.py +++ b/libs/core/tests/unit_tests/language_models/llms/test_base.py @@ -161,7 +161,7 @@ def _generate( **kwargs: Any, ) -> LLMResult: """Top Level call""" - raise NotImplementedError() + raise NotImplementedError def _stream( self, @@ -198,7 +198,7 @@ def _generate( **kwargs: Any, ) -> LLMResult: """Top Level call""" - raise NotImplementedError() + raise NotImplementedError async def _astream( self, diff --git a/libs/core/tests/unit_tests/output_parsers/test_base_parsers.py b/libs/core/tests/unit_tests/output_parsers/test_base_parsers.py index c4c97292b0197..49f04911265cc 100644 --- a/libs/core/tests/unit_tests/output_parsers/test_base_parsers.py +++ b/libs/core/tests/unit_tests/output_parsers/test_base_parsers.py @@ -59,7 +59,7 @@ class StrInvertCase(BaseTransformOutputParser[str]): def parse(self, text: str) -> str: """Parse a single string into a specific format.""" - raise NotImplementedError() + raise NotImplementedError def parse_result( self, result: list[Generation], *, partial: bool = False diff --git a/libs/core/tests/unit_tests/runnables/test_fallbacks.py b/libs/core/tests/unit_tests/runnables/test_fallbacks.py index 69ea68ba9af16..731b3ddaa62aa 100644 --- a/libs/core/tests/unit_tests/runnables/test_fallbacks.py +++ b/libs/core/tests/unit_tests/runnables/test_fallbacks.py @@ -61,13 +61,13 @@ def chain() -> Runnable: def _raise_error(inputs: dict) -> str: - raise ValueError() + raise ValueError def _dont_raise_error(inputs: dict) -> str: if "exception" in inputs: return "bar" - raise ValueError() + raise ValueError @pytest.fixture() @@ -99,11 +99,11 @@ def _runnable(inputs: dict) -> str: if inputs["text"] == "foo": return "first" if "exception" not in inputs: - raise ValueError() + raise ValueError if inputs["text"] == "bar": return "second" if isinstance(inputs["exception"], ValueError): - raise RuntimeError() + raise RuntimeError return "third" @@ -251,13 +251,13 @@ def _generate(input: Iterator) -> Iterator[str]: def _generate_immediate_error(input: Iterator) -> Iterator[str]: - raise ValueError() + raise ValueError yield "" def _generate_delayed_error(input: Iterator) -> Iterator[str]: yield "" - raise ValueError() + raise ValueError def test_fallbacks_stream() -> None: @@ -279,13 +279,13 @@ async def _agenerate(input: AsyncIterator) -> AsyncIterator[str]: async def _agenerate_immediate_error(input: AsyncIterator) -> AsyncIterator[str]: - raise ValueError() + raise ValueError yield "" async def _agenerate_delayed_error(input: AsyncIterator) -> AsyncIterator[str]: yield "" - raise ValueError() + raise ValueError async def test_fallbacks_astream() -> None: diff --git a/libs/core/tests/unit_tests/runnables/test_graph.py b/libs/core/tests/unit_tests/runnables/test_graph.py index 870842bcc49e9..8898b64c01fae 100644 --- a/libs/core/tests/unit_tests/runnables/test_graph.py +++ b/libs/core/tests/unit_tests/runnables/test_graph.py @@ -356,7 +356,7 @@ class InvalidInputTypeRunnable(Runnable[int, int]): @property @override def InputType(self) -> type: - raise TypeError() + raise TypeError @override def invoke( @@ -381,7 +381,7 @@ class InvalidOutputTypeRunnable(Runnable[int, int]): @property @override def OutputType(self) -> type: - raise TypeError() + raise TypeError @override def invoke( diff --git a/libs/core/tests/unit_tests/runnables/test_runnable.py b/libs/core/tests/unit_tests/runnables/test_runnable.py index 6fdf76bf03f4f..a58e81dc36275 100644 --- a/libs/core/tests/unit_tests/runnables/test_runnable.py +++ b/libs/core/tests/unit_tests/runnables/test_runnable.py @@ -653,7 +653,7 @@ def test_with_types_with_type_generics() -> None: def foo(x: int) -> None: """Add one to the input.""" - raise NotImplementedError() + raise NotImplementedError # Try specifying some RunnableLambda(foo).with_types( @@ -3980,7 +3980,7 @@ def __init__(self, fail_starts_with: str) -> None: def invoke( self, input: Any, config: Optional[RunnableConfig] = None, **kwargs: Any ) -> Any: - raise NotImplementedError() + raise NotImplementedError def _batch( self, @@ -4101,7 +4101,7 @@ def __init__(self, fail_starts_with: str) -> None: def invoke( self, input: Any, config: Optional[RunnableConfig] = None, **kwargs: Any ) -> Any: - raise NotImplementedError() + raise NotImplementedError async def _abatch( self, @@ -5352,7 +5352,7 @@ def on_end(run: Run) -> None: assert value2 in shared_state.values(), "Value not found in the dictionary." -async def test_closing_iterator_doesnt_raise_error() -> None: +def test_closing_iterator_doesnt_raise_error() -> None: """Test that closing an iterator calls on_chain_end rather than on_chain_error.""" import time @@ -5361,9 +5361,10 @@ async def test_closing_iterator_doesnt_raise_error() -> None: from langchain_core.output_parsers import StrOutputParser on_chain_error_triggered = False + on_chain_end_triggered = False class MyHandler(BaseCallbackHandler): - async def on_chain_error( + def on_chain_error( self, error: BaseException, *, @@ -5376,6 +5377,17 @@ async def on_chain_error( nonlocal on_chain_error_triggered on_chain_error_triggered = True + def on_chain_end( + self, + outputs: dict[str, Any], + *, + run_id: UUID, + parent_run_id: Optional[UUID] = None, + **kwargs: Any, + ) -> None: + nonlocal on_chain_end_triggered + on_chain_end_triggered = True + llm = GenericFakeChatModel(messages=iter(["hi there"])) chain = llm | StrOutputParser() chain_ = chain.with_config({"callbacks": [MyHandler()]}) @@ -5386,6 +5398,7 @@ async def on_chain_error( # Wait for a bit to make sure that the callback is called. time.sleep(0.05) assert on_chain_error_triggered is False + assert on_chain_end_triggered is True def test_pydantic_protected_namespaces() -> None: diff --git a/libs/core/tests/unit_tests/runnables/test_runnable_events_v2.py b/libs/core/tests/unit_tests/runnables/test_runnable_events_v2.py index afc05ae39f61d..8ceb4bf38b5f1 100644 --- a/libs/core/tests/unit_tests/runnables/test_runnable_events_v2.py +++ b/libs/core/tests/unit_tests/runnables/test_runnable_events_v2.py @@ -2067,7 +2067,7 @@ def stream( config: Optional[RunnableConfig] = None, **kwargs: Optional[Any], ) -> Iterator[Output]: - raise NotImplementedError() + raise NotImplementedError async def astream( self, diff --git a/libs/core/tests/unit_tests/runnables/test_utils.py b/libs/core/tests/unit_tests/runnables/test_utils.py index c07a33da3d335..27d1272ee27ef 100644 --- a/libs/core/tests/unit_tests/runnables/test_utils.py +++ b/libs/core/tests/unit_tests/runnables/test_utils.py @@ -19,7 +19,7 @@ [ (lambda x: x * 2, "lambda x: x * 2"), (lambda a, b: a + b, "lambda a, b: a + b"), - (lambda x: x if x > 0 else 0, "lambda x: x if x > 0 else 0"), + (lambda x: x if x > 0 else 0, "lambda x: x if x > 0 else 0"), # noqa: FURB136 ], ) def test_get_lambda_source(func: Callable, expected_source: str) -> None: diff --git a/libs/core/tests/unit_tests/stubs.py b/libs/core/tests/unit_tests/stubs.py index 95f36b72b0fbf..8fdeccfc7d8b3 100644 --- a/libs/core/tests/unit_tests/stubs.py +++ b/libs/core/tests/unit_tests/stubs.py @@ -5,6 +5,8 @@ class AnyStr(str): + __slots__ = () + def __eq__(self, other: Any) -> bool: return isinstance(other, str) diff --git a/libs/core/tests/unit_tests/test_messages.py b/libs/core/tests/unit_tests/test_messages.py index 1a6e0f8f9ed01..aafcd15e1bb81 100644 --- a/libs/core/tests/unit_tests/test_messages.py +++ b/libs/core/tests/unit_tests/test_messages.py @@ -346,7 +346,7 @@ def test_multiple_msg(self) -> None: self.chat_msg, self.tool_calls_msg, ] - expected_output = "\n".join( + expected_output = "\n".join( # noqa: FLY002 [ "Human: human", "AI: ai", diff --git a/libs/core/tests/unit_tests/test_tools.py b/libs/core/tests/unit_tests/test_tools.py index 3eae40ede1e82..f77b473d39fee 100644 --- a/libs/core/tests/unit_tests/test_tools.py +++ b/libs/core/tests/unit_tests/test_tools.py @@ -401,7 +401,7 @@ def foo(bar: int, baz: str) -> str: bar: the bar value baz: the baz value """ - raise NotImplementedError() + raise NotImplementedError structured_tool = StructuredTool.from_function(foo) assert structured_tool.name == "foo" @@ -435,7 +435,7 @@ def foo(bar: int, baz: list[str]) -> str: bar: int baz: List[str] """ - raise NotImplementedError() + raise NotImplementedError structured_tool = StructuredTool.from_function(foo) assert structured_tool.name == "foo" @@ -781,7 +781,7 @@ def foo(bar: int, baz: str) -> str: bar: the bar value baz: the baz value """ - raise NotImplementedError() + raise NotImplementedError structured_tool = StructuredTool.from_function(foo) assert structured_tool.name == "foo" @@ -854,7 +854,7 @@ def _parse_input( self, tool_input: Union[str, dict], ) -> Union[str, dict[str, Any]]: - raise NotImplementedError() + raise NotImplementedError def _run(self) -> str: return "dummy" @@ -916,7 +916,7 @@ def _parse_input( self, tool_input: Union[str, dict], ) -> Union[str, dict[str, Any]]: - raise NotImplementedError() + raise NotImplementedError def _run(self) -> str: return "dummy" diff --git a/libs/core/tests/unit_tests/vectorstores/test_in_memory.py b/libs/core/tests/unit_tests/vectorstores/test_in_memory.py index 5373d022dbf6d..1ab6186882639 100644 --- a/libs/core/tests/unit_tests/vectorstores/test_in_memory.py +++ b/libs/core/tests/unit_tests/vectorstores/test_in_memory.py @@ -39,7 +39,7 @@ async def test_inmemory_similarity_search() -> None: output = await store.asimilarity_search("bar", k=2) assert output == [ _any_id_document(page_content="bar"), - _any_id_document(page_content="baz"), + _any_id_document(page_content="foo"), ] @@ -81,7 +81,7 @@ async def test_inmemory_mmr() -> None: output = docsearch.max_marginal_relevance_search("foo", k=10, lambda_mult=0.1) assert len(output) == len(texts) assert output[0] == _any_id_document(page_content="foo") - assert output[1] == _any_id_document(page_content="foy") + assert output[1] == _any_id_document(page_content="fou") # Check async version output = await docsearch.amax_marginal_relevance_search( @@ -89,7 +89,7 @@ async def test_inmemory_mmr() -> None: ) assert len(output) == len(texts) assert output[0] == _any_id_document(page_content="foo") - assert output[1] == _any_id_document(page_content="foy") + assert output[1] == _any_id_document(page_content="fou") async def test_inmemory_dump_load(tmp_path: Path) -> None: diff --git a/libs/core/tests/unit_tests/vectorstores/test_vectorstore.py b/libs/core/tests/unit_tests/vectorstores/test_vectorstore.py index aba1481c62338..a0472b70cdaae 100644 --- a/libs/core/tests/unit_tests/vectorstores/test_vectorstore.py +++ b/libs/core/tests/unit_tests/vectorstores/test_vectorstore.py @@ -63,7 +63,7 @@ def from_texts( # type: ignore def similarity_search( self, query: str, k: int = 4, **kwargs: Any ) -> list[Document]: - raise NotImplementedError() + raise NotImplementedError class CustomAddDocumentsVectorstore(VectorStore): @@ -107,7 +107,7 @@ def from_texts( # type: ignore def similarity_search( self, query: str, k: int = 4, **kwargs: Any ) -> list[Document]: - raise NotImplementedError() + raise NotImplementedError @pytest.mark.parametrize(