From 8c6734325b84f7c2f28ce5379838d7d2c82a54d3 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Fri, 11 Apr 2025 07:18:44 -0400 Subject: [PATCH] partners[lint]: run `pyupgrade` to get code in line with 3.9 standards (#30781) Using `pyupgrade` to get all `partners` code up to 3.9 standards (mostly, fixing old `typing` imports). --- .../langchain_anthropic/chat_models.py | 95 ++++++------- .../langchain_anthropic/experimental.py | 16 +-- .../anthropic/langchain_anthropic/llms.py | 28 ++-- .../langchain_anthropic/output_parsers.py | 12 +- libs/partners/anthropic/pyproject.toml | 6 +- .../integration_tests/test_chat_models.py | 8 +- .../integration_tests/test_experimental.py | 4 +- .../tests/integration_tests/test_llms.py | 2 +- .../tests/integration_tests/test_standard.py | 12 +- .../tests/unit_tests/test_chat_models.py | 16 +-- .../tests/unit_tests/test_output_parsers.py | 6 +- .../tests/unit_tests/test_standard.py | 4 +- .../chroma/langchain_chroma/vectorstores.py | 123 ++++++++--------- libs/partners/chroma/pyproject.toml | 6 +- .../integration_tests/fake_embeddings.py | 25 ++-- .../integration_tests/test_vectorstores.py | 6 +- .../chroma/tests/unit_tests/test_standard.py | 2 +- .../langchain_deepseek/chat_models.py | 23 ++-- libs/partners/deepseek/pyproject.toml | 6 +- .../integration_tests/test_chat_models.py | 4 +- .../tests/unit_tests/test_chat_models.py | 16 +-- libs/partners/exa/langchain_exa/_utilities.py | 3 +- libs/partners/exa/langchain_exa/retrievers.py | 12 +- libs/partners/exa/langchain_exa/tools.py | 18 +-- libs/partners/exa/pyproject.toml | 6 +- .../langchain_fireworks/chat_models.py | 79 +++++------ .../langchain_fireworks/embeddings.py | 6 +- .../fireworks/langchain_fireworks/llms.py | 16 +-- libs/partners/fireworks/pyproject.toml | 6 +- .../integration_tests/test_chat_models.py | 4 +- .../tests/integration_tests/test_standard.py | 4 +- .../unit_tests/test_embeddings_standard.py | 6 +- .../tests/unit_tests/test_standard.py | 6 +- .../groq/langchain_groq/chat_models.py | 71 +++++----- libs/partners/groq/pyproject.toml | 6 +- .../tests/integration_tests/test_standard.py | 4 +- .../groq/tests/unit_tests/fake/callbacks.py | 8 +- .../groq/tests/unit_tests/test_standard.py | 4 +- .../chat_models/huggingface.py | 31 ++--- .../embeddings/huggingface.py | 16 +-- .../embeddings/huggingface_endpoint.py | 10 +- .../llms/huggingface_endpoint.py | 25 ++-- .../llms/huggingface_pipeline.py | 13 +- libs/partners/huggingface/pyproject.toml | 6 +- .../test_embeddings_standard.py | 6 +- .../tests/integration_tests/test_llms.py | 2 +- .../tests/integration_tests/test_standard.py | 4 +- .../tests/unit_tests/test_chat_models.py | 12 +- .../langchain_mistralai/chat_models.py | 92 ++++++------- .../langchain_mistralai/embeddings.py | 20 +-- libs/partners/mistralai/pyproject.toml | 6 +- .../tests/integration_tests/test_standard.py | 4 +- .../tests/unit_tests/test_chat_models.py | 9 +- .../tests/unit_tests/test_standard.py | 4 +- .../nomic/langchain_nomic/embeddings.py | 10 +- libs/partners/nomic/pyproject.toml | 5 + .../ollama/langchain_ollama/__init__.py | 1 - .../ollama/langchain_ollama/chat_models.py | 79 +++++------ .../ollama/langchain_ollama/embeddings.py | 14 +- libs/partners/ollama/langchain_ollama/llms.py | 34 ++--- libs/partners/ollama/pyproject.toml | 6 +- libs/partners/ollama/scripts/check_imports.py | 1 + .../chat_models/test_chat_models.py | 6 +- .../chat_models/test_chat_models_standard.py | 4 +- .../integration_tests/test_embeddings.py | 4 +- .../tests/unit_tests/test_chat_models.py | 6 +- .../langchain_openai/chat_models/azure.py | 32 ++--- .../langchain_openai/chat_models/base.py | 129 +++++++++--------- .../langchain_openai/embeddings/azure.py | 5 +- .../langchain_openai/embeddings/base.py | 96 ++++++------- .../openai/langchain_openai/llms/azure.py | 15 +- .../openai/langchain_openai/llms/base.py | 79 +++++------ libs/partners/openai/pyproject.toml | 6 +- .../chat_models/test_azure_standard.py | 5 +- .../chat_models/test_base.py | 13 +- .../chat_models/test_base_standard.py | 10 +- .../chat_models/test_responses_api.py | 4 +- .../chat_models/test_responses_standard.py | 4 +- .../embeddings/test_base_standard.py | 4 +- .../integration_tests/llms/test_azure.py | 3 +- .../tests/integration_tests/llms/test_base.py | 2 +- .../chat_models/test_azure_standard.py | 6 +- .../tests/unit_tests/chat_models/test_base.py | 20 +-- .../chat_models/test_base_standard.py | 6 +- .../chat_models/test_responses_standard.py | 6 +- .../embeddings/test_azure_standard.py | 6 +- .../embeddings/test_base_standard.py | 6 +- .../openai/tests/unit_tests/fake/callbacks.py | 8 +- .../openai/tests/unit_tests/llms/test_base.py | 3 +- .../openai/tests/unit_tests/test_secrets.py | 20 +-- .../langchain_perplexity/chat_models.py | 49 +++---- libs/partners/perplexity/pyproject.toml | 6 +- .../test_chat_models_standard.py | 4 +- .../tests/unit_tests/test_chat_models.py | 8 +- .../unit_tests/test_chat_models_standard.py | 6 +- .../prompty/langchain_prompty/core.py | 36 ++--- .../prompty/langchain_prompty/langchain.py | 6 +- .../prompty/langchain_prompty/parsers.py | 8 +- .../prompty/langchain_prompty/utils.py | 16 +-- libs/partners/prompty/pyproject.toml | 7 +- .../partners/prompty/scripts/check_imports.py | 2 - .../tests/unit_tests/fake_callback_handler.py | 12 +- .../tests/unit_tests/fake_chat_model.py | 12 +- .../tests/unit_tests/fake_output_parser.py | 5 +- .../unit_tests/test_prompty_serialization.py | 10 +- .../qdrant/langchain_qdrant/_utils.py | 6 +- .../langchain_qdrant/fastembed_sparse.py | 5 +- .../qdrant/langchain_qdrant/qdrant.py | 82 ++++++----- .../langchain_qdrant/sparse_embeddings.py | 9 +- .../qdrant/langchain_qdrant/vectorstores.py | 117 ++++++++-------- libs/partners/qdrant/pyproject.toml | 6 +- libs/partners/qdrant/scripts/check_imports.py | 2 - .../qdrant/tests/integration_tests/common.py | 16 +-- .../tests/integration_tests/fixtures.py | 5 +- .../qdrant_vector_store/test_add_texts.py | 4 +- .../qdrant_vector_store/test_from_texts.py | 4 +- .../voyageai/langchain_voyageai/embeddings.py | 25 ++-- .../voyageai/langchain_voyageai/rerank.py | 5 +- libs/partners/voyageai/pyproject.toml | 6 +- .../partners/xai/langchain_xai/chat_models.py | 17 +-- libs/partners/xai/pyproject.toml | 6 +- .../test_chat_models_standard.py | 4 +- .../unit_tests/test_chat_models_standard.py | 6 +- 123 files changed, 1000 insertions(+), 1109 deletions(-) diff --git a/libs/partners/anthropic/langchain_anthropic/chat_models.py b/libs/partners/anthropic/langchain_anthropic/chat_models.py index 02c21741a8c..d998f94c5da 100644 --- a/libs/partners/anthropic/langchain_anthropic/chat_models.py +++ b/libs/partners/anthropic/langchain_anthropic/chat_models.py @@ -1,21 +1,14 @@ import copy import re import warnings +from collections.abc import AsyncIterator, Iterator, Mapping, Sequence from functools import cached_property from operator import itemgetter from typing import ( Any, - AsyncIterator, Callable, - Dict, - Iterator, - List, Literal, - Mapping, Optional, - Sequence, - Tuple, - Type, Union, cast, ) @@ -89,8 +82,8 @@ class AnthropicTool(TypedDict): name: str description: str - input_schema: Dict[str, Any] - cache_control: NotRequired[Dict[str, str]] + input_schema: dict[str, Any] + cache_control: NotRequired[dict[str, str]] def _is_builtin_tool(tool: Any) -> bool: @@ -109,7 +102,7 @@ def _is_builtin_tool(tool: Any) -> bool: return any(tool_type.startswith(prefix) for prefix in _builtin_tool_prefixes) -def _format_image(image_url: str) -> Dict: +def _format_image(image_url: str) -> dict: """ Formats an image of format data:image/jpeg;base64,{b64_string} to a dict for anthropic api @@ -138,7 +131,7 @@ def _format_image(image_url: str) -> Dict: def _merge_messages( messages: Sequence[BaseMessage], -) -> List[Union[SystemMessage, AIMessage, HumanMessage]]: +) -> list[Union[SystemMessage, AIMessage, HumanMessage]]: """Merge runs of human/tool messages into single human messages with content blocks.""" # noqa: E501 merged: list = [] for curr in messages: @@ -169,7 +162,7 @@ def _merge_messages( for c in (SystemMessage, HumanMessage) ): if isinstance(cast(BaseMessage, last).content, str): - new_content: List = [ + new_content: list = [ {"type": "text", "text": cast(BaseMessage, last).content} ] else: @@ -185,8 +178,8 @@ def _merge_messages( def _format_messages( - messages: List[BaseMessage], -) -> Tuple[Union[str, List[Dict], None], List[Dict]]: + messages: list[BaseMessage], +) -> tuple[Union[str, list[dict], None], list[dict]]: """Format messages for anthropic.""" """ @@ -198,8 +191,8 @@ def _format_messages( for m in messages ] """ - system: Union[str, List[Dict], None] = None - formatted_messages: List[Dict] = [] + system: Union[str, list[dict], None] = None + formatted_messages: list[dict] = [] merged_messages = _merge_messages(messages) for i, message in enumerate(merged_messages): @@ -220,7 +213,7 @@ def _format_messages( continue role = _message_type_lookups[message.type] - content: Union[str, List] + content: Union[str, list] if not isinstance(message.content, str): # parse as dict @@ -830,7 +823,7 @@ class ChatAnthropic(BaseChatModel): max_retries: int = 2 """Number of retries allowed for requests sent to the Anthropic Completion API.""" - stop_sequences: Optional[List[str]] = Field(None, alias="stop") + stop_sequences: Optional[list[str]] = Field(None, alias="stop") """Default stop sequences.""" anthropic_api_url: Optional[str] = Field( @@ -858,7 +851,7 @@ class ChatAnthropic(BaseChatModel): default_headers: Optional[Mapping[str, str]] = None """Headers to pass to the Anthropic clients, will be used for every API call.""" - model_kwargs: Dict[str, Any] = Field(default_factory=dict) + model_kwargs: dict[str, Any] = Field(default_factory=dict) streaming: bool = False """Whether to use streaming or not.""" @@ -868,7 +861,7 @@ class ChatAnthropic(BaseChatModel): message chunks will be generated during the stream including usage metadata. """ - thinking: Optional[Dict[str, Any]] = Field(default=None) + thinking: Optional[dict[str, Any]] = Field(default=None) """Parameters for Claude reasoning, e.g., ``{"type": "enabled", "budget_tokens": 10_000}``""" @@ -878,7 +871,7 @@ class ChatAnthropic(BaseChatModel): return "anthropic-chat" @property - def lc_secrets(self) -> Dict[str, str]: + def lc_secrets(self) -> dict[str, str]: return {"anthropic_api_key": "ANTHROPIC_API_KEY"} @classmethod @@ -886,12 +879,12 @@ class ChatAnthropic(BaseChatModel): return True @classmethod - def get_lc_namespace(cls) -> List[str]: + def get_lc_namespace(cls) -> list[str]: """Get the namespace of the langchain object.""" return ["langchain", "chat_models", "anthropic"] @property - def _identifying_params(self) -> Dict[str, Any]: + def _identifying_params(self) -> dict[str, Any]: """Get the identifying parameters.""" return { "model": self.model, @@ -907,7 +900,7 @@ class ChatAnthropic(BaseChatModel): } def _get_ls_params( - self, stop: Optional[List[str]] = None, **kwargs: Any + self, stop: Optional[list[str]] = None, **kwargs: Any ) -> LangSmithParams: """Get standard params for tracing.""" params = self._get_invocation_params(stop=stop, **kwargs) @@ -925,14 +918,14 @@ class ChatAnthropic(BaseChatModel): @model_validator(mode="before") @classmethod - def build_extra(cls, values: Dict) -> Any: + def build_extra(cls, values: dict) -> Any: all_required_field_names = get_pydantic_field_names(cls) values = _build_model_kwargs(values, all_required_field_names) return values @cached_property - def _client_params(self) -> Dict[str, Any]: - client_params: Dict[str, Any] = { + def _client_params(self) -> dict[str, Any]: + client_params: dict[str, Any] = { "api_key": self.anthropic_api_key.get_secret_value(), "base_url": self.anthropic_api_url, "max_retries": self.max_retries, @@ -958,9 +951,9 @@ class ChatAnthropic(BaseChatModel): self, input_: LanguageModelInput, *, - stop: Optional[List[str]] = None, - **kwargs: Dict, - ) -> Dict: + stop: Optional[list[str]] = None, + **kwargs: dict, + ) -> dict: messages = self._convert_input(input_).to_messages() system, formatted_messages = _format_messages(messages) payload = { @@ -981,8 +974,8 @@ class ChatAnthropic(BaseChatModel): def _stream( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, *, stream_usage: Optional[bool] = None, @@ -1012,8 +1005,8 @@ class ChatAnthropic(BaseChatModel): async def _astream( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, *, stream_usage: Optional[bool] = None, @@ -1088,8 +1081,8 @@ class ChatAnthropic(BaseChatModel): def _generate( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> ChatResult: @@ -1104,8 +1097,8 @@ class ChatAnthropic(BaseChatModel): async def _agenerate( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, **kwargs: Any, ) -> ChatResult: @@ -1120,7 +1113,7 @@ class ChatAnthropic(BaseChatModel): def _get_llm_for_structured_output_when_thinking_is_enabled( self, - schema: Union[Dict, type], + schema: Union[dict, type], formatted_tool: AnthropicTool, ) -> Runnable[LanguageModelInput, BaseMessage]: thinking_admonition = ( @@ -1148,10 +1141,10 @@ class ChatAnthropic(BaseChatModel): def bind_tools( self, - tools: Sequence[Union[Dict[str, Any], Type, Callable, BaseTool]], + tools: Sequence[Union[dict[str, Any], type, Callable, BaseTool]], *, tool_choice: Optional[ - Union[Dict[str, str], Literal["any", "auto"], str] + Union[dict[str, str], Literal["any", "auto"], str] ] = None, parallel_tool_calls: Optional[bool] = None, **kwargs: Any, @@ -1326,11 +1319,11 @@ class ChatAnthropic(BaseChatModel): def with_structured_output( self, - schema: Union[Dict, type], + schema: Union[dict, type], *, include_raw: bool = False, **kwargs: Any, - ) -> Runnable[LanguageModelInput, Union[Dict, BaseModel]]: + ) -> Runnable[LanguageModelInput, Union[dict, BaseModel]]: """Model wrapper that returns outputs formatted to match the given schema. Args: @@ -1483,9 +1476,9 @@ class ChatAnthropic(BaseChatModel): @beta() def get_num_tokens_from_messages( self, - messages: List[BaseMessage], + messages: list[BaseMessage], tools: Optional[ - Sequence[Union[Dict[str, Any], Type, Callable, BaseTool]] + Sequence[Union[dict[str, Any], type, Callable, BaseTool]] ] = None, ) -> int: """Count tokens in a sequence of input messages. @@ -1546,7 +1539,7 @@ class ChatAnthropic(BaseChatModel): https://docs.anthropic.com/en/docs/build-with-claude/token-counting """ formatted_system, formatted_messages = _format_messages(messages) - kwargs: Dict[str, Any] = {} + kwargs: dict[str, Any] = {} if isinstance(formatted_system, str): kwargs["system"] = formatted_system if tools: @@ -1562,7 +1555,7 @@ class ChatAnthropic(BaseChatModel): def convert_to_anthropic_tool( - tool: Union[Dict[str, Any], Type, Callable, BaseTool], + tool: Union[dict[str, Any], type, Callable, BaseTool], ) -> AnthropicTool: """Convert a tool-like object to an Anthropic tool definition.""" # already in Anthropic tool format @@ -1611,8 +1604,8 @@ class _AnthropicToolUse(TypedDict): def _lc_tool_calls_to_anthropic_tool_use_blocks( - tool_calls: List[ToolCall], -) -> List[_AnthropicToolUse]: + tool_calls: list[ToolCall], +) -> list[_AnthropicToolUse]: blocks = [] for tool_call in tool_calls: blocks.append( @@ -1735,7 +1728,7 @@ class ChatAnthropicMessages(ChatAnthropic): def _create_usage_metadata(anthropic_usage: BaseModel) -> UsageMetadata: - input_token_details: Dict = { + input_token_details: dict = { "cache_read": getattr(anthropic_usage, "cache_read_input_tokens", None), "cache_creation": getattr(anthropic_usage, "cache_creation_input_tokens", None), } diff --git a/libs/partners/anthropic/langchain_anthropic/experimental.py b/libs/partners/anthropic/langchain_anthropic/experimental.py index 2d2e35fae24..5f4c8a4d23c 100644 --- a/libs/partners/anthropic/langchain_anthropic/experimental.py +++ b/libs/partners/anthropic/langchain_anthropic/experimental.py @@ -1,8 +1,6 @@ import json from typing import ( Any, - Dict, - List, Union, ) @@ -44,7 +42,7 @@ TOOL_PARAMETER_FORMAT = """ """ -def _get_type(parameter: Dict[str, Any]) -> str: +def _get_type(parameter: dict[str, Any]) -> str: if "type" in parameter: return parameter["type"] if "anyOf" in parameter: @@ -54,9 +52,9 @@ def _get_type(parameter: Dict[str, Any]) -> str: return json.dumps(parameter) -def get_system_message(tools: List[Dict]) -> str: +def get_system_message(tools: list[dict]) -> str: """Generate a system message that describes the available tools.""" - tools_data: List[Dict] = [ + tools_data: list[dict] = [ { "tool_name": tool["name"], "tool_description": tool["description"], @@ -86,13 +84,13 @@ def get_system_message(tools: List[Dict]) -> str: return SYSTEM_PROMPT_FORMAT.format(formatted_tools=tools_formatted) -def _xml_to_dict(t: Any) -> Union[str, Dict[str, Any]]: +def _xml_to_dict(t: Any) -> Union[str, dict[str, Any]]: # Base case: If the element has no children, return its text or an empty string. if len(t) == 0: return t.text or "" # Recursive case: The element has children. Convert them into a dictionary. - d: Dict[str, Any] = {} + d: dict[str, Any] = {} for child in t: if child.tag not in d: d[child.tag] = _xml_to_dict(child) @@ -104,7 +102,7 @@ def _xml_to_dict(t: Any) -> Union[str, Dict[str, Any]]: return d -def _xml_to_function_call(invoke: Any, tools: List[Dict]) -> Dict[str, Any]: +def _xml_to_function_call(invoke: Any, tools: list[dict]) -> dict[str, Any]: name = invoke.find("tool_name").text arguments = _xml_to_dict(invoke.find("parameters")) @@ -135,7 +133,7 @@ def _xml_to_function_call(invoke: Any, tools: List[Dict]) -> Dict[str, Any]: } -def _xml_to_tool_calls(elem: Any, tools: List[Dict]) -> List[Dict[str, Any]]: +def _xml_to_tool_calls(elem: Any, tools: list[dict]) -> list[dict[str, Any]]: """ Convert an XML element and its children into a dictionary of dictionaries. """ diff --git a/libs/partners/anthropic/langchain_anthropic/llms.py b/libs/partners/anthropic/langchain_anthropic/llms.py index 0b2579946d7..e6b94ed95bc 100644 --- a/libs/partners/anthropic/langchain_anthropic/llms.py +++ b/libs/partners/anthropic/langchain_anthropic/llms.py @@ -1,13 +1,9 @@ import re import warnings +from collections.abc import AsyncIterator, Iterator, Mapping from typing import ( Any, - AsyncIterator, Callable, - Dict, - Iterator, - List, - Mapping, Optional, ) @@ -83,11 +79,11 @@ class _AnthropicCommon(BaseLanguageModel): HUMAN_PROMPT: Optional[str] = None AI_PROMPT: Optional[str] = None count_tokens: Optional[Callable[[str], int]] = None - model_kwargs: Dict[str, Any] = Field(default_factory=dict) + model_kwargs: dict[str, Any] = Field(default_factory=dict) @model_validator(mode="before") @classmethod - def build_extra(cls, values: Dict) -> Any: + def build_extra(cls, values: dict) -> Any: all_required_field_names = get_pydantic_field_names(cls) values = _build_model_kwargs(values, all_required_field_names) return values @@ -131,7 +127,7 @@ class _AnthropicCommon(BaseLanguageModel): """Get the identifying parameters.""" return {**{}, **self._default_params} - def _get_anthropic_stop(self, stop: Optional[List[str]] = None) -> List[str]: + def _get_anthropic_stop(self, stop: Optional[list[str]] = None) -> list[str]: if not self.HUMAN_PROMPT or not self.AI_PROMPT: raise NameError("Please ensure the anthropic package is loaded") @@ -165,7 +161,7 @@ class AnthropicLLM(LLM, _AnthropicCommon): @model_validator(mode="before") @classmethod - def raise_warning(cls, values: Dict) -> Any: + def raise_warning(cls, values: dict) -> Any: """Raise warning that this class is deprecated.""" warnings.warn( "This Anthropic LLM is deprecated. " @@ -180,7 +176,7 @@ class AnthropicLLM(LLM, _AnthropicCommon): return "anthropic-llm" @property - def lc_secrets(self) -> Dict[str, str]: + def lc_secrets(self) -> dict[str, str]: return {"anthropic_api_key": "ANTHROPIC_API_KEY"} @classmethod @@ -188,7 +184,7 @@ class AnthropicLLM(LLM, _AnthropicCommon): return True @property - def _identifying_params(self) -> Dict[str, Any]: + def _identifying_params(self) -> dict[str, Any]: """Get the identifying parameters.""" return { "model": self.model, @@ -203,7 +199,7 @@ class AnthropicLLM(LLM, _AnthropicCommon): } def _get_ls_params( - self, stop: Optional[List[str]] = None, **kwargs: Any + self, stop: Optional[list[str]] = None, **kwargs: Any ) -> LangSmithParams: """Get standard params for tracing.""" params = super()._get_ls_params(stop=stop, **kwargs) @@ -233,7 +229,7 @@ class AnthropicLLM(LLM, _AnthropicCommon): def _call( self, prompt: str, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> str: @@ -277,7 +273,7 @@ class AnthropicLLM(LLM, _AnthropicCommon): async def _acall( self, prompt: str, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, **kwargs: Any, ) -> str: @@ -303,7 +299,7 @@ class AnthropicLLM(LLM, _AnthropicCommon): def _stream( self, prompt: str, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> Iterator[GenerationChunk]: @@ -338,7 +334,7 @@ class AnthropicLLM(LLM, _AnthropicCommon): async def _astream( self, prompt: str, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, **kwargs: Any, ) -> AsyncIterator[GenerationChunk]: diff --git a/libs/partners/anthropic/langchain_anthropic/output_parsers.py b/libs/partners/anthropic/langchain_anthropic/output_parsers.py index c30f7ebc665..a521822f507 100644 --- a/libs/partners/anthropic/langchain_anthropic/output_parsers.py +++ b/libs/partners/anthropic/langchain_anthropic/output_parsers.py @@ -1,4 +1,4 @@ -from typing import Any, List, Optional, Type, Union, cast +from typing import Any, Optional, Union, cast from langchain_core.messages import AIMessage, ToolCall from langchain_core.messages.tool import tool_call @@ -14,14 +14,14 @@ class ToolsOutputParser(BaseGenerationOutputParser): """Whether to return only the first tool call.""" args_only: bool = False """Whether to return only the arguments of the tool calls.""" - pydantic_schemas: Optional[List[Type[BaseModel]]] = None + pydantic_schemas: Optional[list[type[BaseModel]]] = None """Pydantic schemas to parse tool calls into.""" model_config = ConfigDict( extra="forbid", ) - def parse_result(self, result: List[Generation], *, partial: bool = False) -> Any: + def parse_result(self, result: list[Generation], *, partial: bool = False) -> Any: """Parse a list of candidate model Generations into a specific format. Args: @@ -34,7 +34,7 @@ class ToolsOutputParser(BaseGenerationOutputParser): if not result or not isinstance(result[0], ChatGeneration): return None if self.first_tool_only else [] message = cast(AIMessage, result[0].message) - tool_calls: List = [ + tool_calls: list = [ dict(tc) for tc in _extract_tool_calls_from_message(message) ] if isinstance(message.content, list): @@ -64,14 +64,14 @@ class ToolsOutputParser(BaseGenerationOutputParser): return cls_(**tool_call["args"]) -def _extract_tool_calls_from_message(message: AIMessage) -> List[ToolCall]: +def _extract_tool_calls_from_message(message: AIMessage) -> list[ToolCall]: """Extract tool calls from a list of content blocks.""" if message.tool_calls: return message.tool_calls return extract_tool_calls(message.content) -def extract_tool_calls(content: Union[str, List[Union[str, dict]]]) -> List[ToolCall]: +def extract_tool_calls(content: Union[str, list[Union[str, dict]]]) -> list[ToolCall]: """Extract tool calls from a list of content blocks.""" if isinstance(content, list): tool_calls = [] diff --git a/libs/partners/anthropic/pyproject.toml b/libs/partners/anthropic/pyproject.toml index 8be13a8e10b..fa0262ebd5c 100644 --- a/libs/partners/anthropic/pyproject.toml +++ b/libs/partners/anthropic/pyproject.toml @@ -55,8 +55,12 @@ langchain-tests = { path = "../../standard-tests", editable = true } disallow_untyped_defs = "True" plugins = ['pydantic.mypy'] +[tool.ruff] +target-version = "py39" + [tool.ruff.lint] -select = ["E", "F", "I", "T201"] +select = ["E", "F", "I", "T201", "UP"] +ignore = [ "UP007", ] [tool.coverage.run] omit = ["tests/*"] diff --git a/libs/partners/anthropic/tests/integration_tests/test_chat_models.py b/libs/partners/anthropic/tests/integration_tests/test_chat_models.py index fc24c7d515c..5c41c3f878e 100644 --- a/libs/partners/anthropic/tests/integration_tests/test_chat_models.py +++ b/libs/partners/anthropic/tests/integration_tests/test_chat_models.py @@ -2,7 +2,7 @@ import json from base64 import b64encode -from typing import List, Optional +from typing import Optional import httpx import pytest @@ -270,7 +270,7 @@ def test_anthropic_call() -> None: def test_anthropic_generate() -> None: """Test generate method of anthropic.""" chat = ChatAnthropic(model=MODEL_NAME) - chat_messages: List[List[BaseMessage]] = [ + chat_messages: list[list[BaseMessage]] = [ [HumanMessage(content="How many toes do dogs have?")] ] messages_copy = [messages.copy() for messages in chat_messages] @@ -318,7 +318,7 @@ async def test_anthropic_async_streaming_callback() -> None: callback_manager=callback_manager, verbose=True, ) - chat_messages: List[BaseMessage] = [ + chat_messages: list[BaseMessage] = [ HumanMessage(content="How many toes do dogs have?") ] async for token in chat.astream(chat_messages): @@ -809,7 +809,7 @@ def test_image_tool_calling() -> None: fav_color: str - human_content: List[dict] = [ + human_content: list[dict] = [ { "type": "text", "text": "what's your favorite color in this image", diff --git a/libs/partners/anthropic/tests/integration_tests/test_experimental.py b/libs/partners/anthropic/tests/integration_tests/test_experimental.py index 175fa60abe3..67f5cd23ae8 100644 --- a/libs/partners/anthropic/tests/integration_tests/test_experimental.py +++ b/libs/partners/anthropic/tests/integration_tests/test_experimental.py @@ -1,7 +1,7 @@ """Test ChatAnthropic chat model.""" from enum import Enum -from typing import List, Optional +from typing import Optional from langchain_core.prompts import ChatPromptTemplate from pydantic import BaseModel, Field @@ -136,7 +136,7 @@ def test_anthropic_complex_structured_output() -> None: sender_address: Optional[str] = Field( None, description="The sender's address, if available" ) - action_items: List[str] = Field( + action_items: list[str] = Field( ..., description="A list of action items requested by the email" ) topic: str = Field( diff --git a/libs/partners/anthropic/tests/integration_tests/test_llms.py b/libs/partners/anthropic/tests/integration_tests/test_llms.py index b3018c192f4..9bf2900ba35 100644 --- a/libs/partners/anthropic/tests/integration_tests/test_llms.py +++ b/libs/partners/anthropic/tests/integration_tests/test_llms.py @@ -1,6 +1,6 @@ """Test Anthropic API wrapper.""" -from typing import Generator +from collections.abc import Generator import pytest from langchain_core.callbacks import CallbackManager diff --git a/libs/partners/anthropic/tests/integration_tests/test_standard.py b/libs/partners/anthropic/tests/integration_tests/test_standard.py index c4f3805ae97..1a11a4850af 100644 --- a/libs/partners/anthropic/tests/integration_tests/test_standard.py +++ b/libs/partners/anthropic/tests/integration_tests/test_standard.py @@ -1,7 +1,7 @@ """Standard LangChain interface tests""" from pathlib import Path -from typing import Dict, List, Literal, Type, cast +from typing import Literal, cast from langchain_core.language_models import BaseChatModel from langchain_core.messages import AIMessage @@ -14,7 +14,7 @@ REPO_ROOT_DIR = Path(__file__).parents[5] class TestAnthropicStandard(ChatModelIntegrationTests): @property - def chat_model_class(self) -> Type[BaseChatModel]: + def chat_model_class(self) -> type[BaseChatModel]: return ChatAnthropic @property @@ -36,9 +36,9 @@ class TestAnthropicStandard(ChatModelIntegrationTests): @property def supported_usage_metadata_details( self, - ) -> Dict[ + ) -> dict[ Literal["invoke", "stream"], - List[ + list[ Literal[ "audio_input", "audio_output", @@ -58,7 +58,7 @@ class TestAnthropicStandard(ChatModelIntegrationTests): model="claude-3-5-sonnet-20240620", # type: ignore[call-arg] extra_headers={"anthropic-beta": "prompt-caching-2024-07-31"}, # type: ignore[call-arg] ) - with open(REPO_ROOT_DIR / "README.md", "r") as f: + with open(REPO_ROOT_DIR / "README.md") as f: readme = f.read() input_ = f"""What's langchain? Here's the langchain README: @@ -87,7 +87,7 @@ class TestAnthropicStandard(ChatModelIntegrationTests): model="claude-3-5-sonnet-20240620", # type: ignore[call-arg] extra_headers={"anthropic-beta": "prompt-caching-2024-07-31"}, # type: ignore[call-arg] ) - with open(REPO_ROOT_DIR / "README.md", "r") as f: + with open(REPO_ROOT_DIR / "README.md") as f: readme = f.read() input_ = f"""What's langchain? Here's the langchain README: diff --git a/libs/partners/anthropic/tests/unit_tests/test_chat_models.py b/libs/partners/anthropic/tests/unit_tests/test_chat_models.py index 452788a301e..7281a104cbb 100644 --- a/libs/partners/anthropic/tests/unit_tests/test_chat_models.py +++ b/libs/partners/anthropic/tests/unit_tests/test_chat_models.py @@ -1,7 +1,7 @@ """Test chat model integration.""" import os -from typing import Any, Callable, Dict, Literal, Type, cast +from typing import Any, Callable, Literal, cast import pytest from anthropic.types import Message, TextBlock, Usage @@ -297,7 +297,7 @@ def test__merge_messages_mutation() -> None: @pytest.fixture() -def pydantic() -> Type[BaseModel]: +def pydantic() -> type[BaseModel]: class dummy_function(BaseModel): """dummy function""" @@ -328,7 +328,7 @@ def dummy_tool() -> BaseTool: arg2: Literal["bar", "baz"] = Field(..., description="one of 'bar', 'baz'") class DummyFunction(BaseTool): # type: ignore[override] - args_schema: Type[BaseModel] = Schema + args_schema: type[BaseModel] = Schema name: str = "dummy_function" description: str = "dummy function" @@ -339,7 +339,7 @@ def dummy_tool() -> BaseTool: @pytest.fixture() -def json_schema() -> Dict: +def json_schema() -> dict: return { "title": "dummy_function", "description": "dummy function", @@ -357,7 +357,7 @@ def json_schema() -> Dict: @pytest.fixture() -def openai_function() -> Dict: +def openai_function() -> dict: return { "name": "dummy_function", "description": "dummy function", @@ -377,11 +377,11 @@ def openai_function() -> Dict: def test_convert_to_anthropic_tool( - pydantic: Type[BaseModel], + pydantic: type[BaseModel], function: Callable, dummy_tool: BaseTool, - json_schema: Dict, - openai_function: Dict, + json_schema: dict, + openai_function: dict, ) -> None: expected = { "name": "dummy_function", diff --git a/libs/partners/anthropic/tests/unit_tests/test_output_parsers.py b/libs/partners/anthropic/tests/unit_tests/test_output_parsers.py index af560eac0fa..7bbb517c00c 100644 --- a/libs/partners/anthropic/tests/unit_tests/test_output_parsers.py +++ b/libs/partners/anthropic/tests/unit_tests/test_output_parsers.py @@ -1,4 +1,4 @@ -from typing import Any, List, Literal +from typing import Any, Literal from langchain_core.messages import AIMessage from langchain_core.outputs import ChatGeneration @@ -6,7 +6,7 @@ from pydantic import BaseModel from langchain_anthropic.output_parsers import ToolsOutputParser -_CONTENT: List = [ +_CONTENT: list = [ { "type": "text", "text": "thought", @@ -19,7 +19,7 @@ _CONTENT: List = [ {"type": "tool_use", "input": {"baz": "a"}, "id": "2", "name": "_Foo2"}, ] -_RESULT: List = [ChatGeneration(message=AIMessage(_CONTENT))] # type: ignore[misc] +_RESULT: list = [ChatGeneration(message=AIMessage(_CONTENT))] # type: ignore[misc] class _Foo1(BaseModel): diff --git a/libs/partners/anthropic/tests/unit_tests/test_standard.py b/libs/partners/anthropic/tests/unit_tests/test_standard.py index 7879e2995bb..cb963c90b94 100644 --- a/libs/partners/anthropic/tests/unit_tests/test_standard.py +++ b/libs/partners/anthropic/tests/unit_tests/test_standard.py @@ -1,7 +1,5 @@ """Standard LangChain interface tests""" -from typing import Type - from langchain_core.language_models import BaseChatModel from langchain_tests.unit_tests import ChatModelUnitTests @@ -10,7 +8,7 @@ from langchain_anthropic import ChatAnthropic class TestAnthropicStandard(ChatModelUnitTests): @property - def chat_model_class(self) -> Type[BaseChatModel]: + def chat_model_class(self) -> type[BaseChatModel]: return ChatAnthropic @property diff --git a/libs/partners/chroma/langchain_chroma/vectorstores.py b/libs/partners/chroma/langchain_chroma/vectorstores.py index 12deb3830be..c09fdf0f655 100644 --- a/libs/partners/chroma/langchain_chroma/vectorstores.py +++ b/libs/partners/chroma/langchain_chroma/vectorstores.py @@ -8,17 +8,12 @@ from __future__ import annotations import base64 import logging import uuid +from collections.abc import Iterable, Sequence from typing import ( TYPE_CHECKING, Any, Callable, - Dict, - Iterable, - List, Optional, - Sequence, - Tuple, - Type, Union, ) @@ -37,11 +32,11 @@ logger = logging.getLogger() DEFAULT_K = 4 # Number of Documents to return. -def _results_to_docs(results: Any) -> List[Document]: +def _results_to_docs(results: Any) -> list[Document]: return [doc for doc, _ in _results_to_docs_and_scores(results)] -def _results_to_docs_and_scores(results: Any) -> List[Tuple[Document, float]]: +def _results_to_docs_and_scores(results: Any) -> list[tuple[Document, float]]: return [ # TODO: Chroma can do batch querying, # we shouldn't hard code to the 1st result @@ -58,7 +53,7 @@ def _results_to_docs_and_scores(results: Any) -> List[Tuple[Document, float]]: ] -def _results_to_docs_and_vectors(results: Any) -> List[Tuple[Document, np.ndarray]]: +def _results_to_docs_and_vectors(results: Any) -> list[tuple[Document, np.ndarray]]: return [ (Document(page_content=result[0], metadata=result[1] or {}), result[2]) for result in zip( @@ -69,7 +64,7 @@ def _results_to_docs_and_vectors(results: Any) -> List[Tuple[Document, np.ndarra ] -Matrix = Union[List[List[float]], List[np.ndarray], np.ndarray] +Matrix = Union[list[list[float]], list[np.ndarray], np.ndarray] def cosine_similarity(X: Matrix, Y: Matrix) -> np.ndarray: @@ -104,7 +99,7 @@ def maximal_marginal_relevance( embedding_list: list, lambda_mult: float = 0.5, k: int = 4, -) -> List[int]: +) -> list[int]: """Calculate maximal marginal relevance. Args: @@ -287,7 +282,7 @@ class Chroma(VectorStore): embedding_function: Optional[Embeddings] = None, persist_directory: Optional[str] = None, client_settings: Optional[chromadb.config.Settings] = None, - collection_metadata: Optional[Dict] = None, + collection_metadata: Optional[dict] = None, client: Optional[chromadb.ClientAPI] = None, relevance_score_fn: Optional[Callable[[float], float]] = None, create_collection_if_not_exists: Optional[bool] = True, @@ -370,13 +365,13 @@ class Chroma(VectorStore): @xor_args(("query_texts", "query_embeddings")) def __query_collection( self, - query_texts: Optional[List[str]] = None, - query_embeddings: Optional[List[List[float]]] = None, + query_texts: Optional[list[str]] = None, + query_embeddings: Optional[list[list[float]]] = None, n_results: int = 4, - where: Optional[Dict[str, str]] = None, - where_document: Optional[Dict[str, str]] = None, + where: Optional[dict[str, str]] = None, + where_document: Optional[dict[str, str]] = None, **kwargs: Any, - ) -> Union[List[Document], chromadb.QueryResult]: + ) -> Union[list[Document], chromadb.QueryResult]: """Query the chroma collection. Args: @@ -411,11 +406,11 @@ class Chroma(VectorStore): def add_images( self, - uris: List[str], - metadatas: Optional[List[dict]] = None, - ids: Optional[List[str]] = None, + uris: list[str], + metadatas: Optional[list[dict]] = None, + ids: Optional[list[str]] = None, **kwargs: Any, - ) -> List[str]: + ) -> list[str]: """Run more images through the embeddings and add to the vectorstore. Args: @@ -502,10 +497,10 @@ class Chroma(VectorStore): def add_texts( self, texts: Iterable[str], - metadatas: Optional[List[dict]] = None, - ids: Optional[List[str]] = None, + metadatas: Optional[list[dict]] = None, + ids: Optional[list[str]] = None, **kwargs: Any, - ) -> List[str]: + ) -> list[str]: """Run more texts through the embeddings and add to the vectorstore. Args: @@ -591,9 +586,9 @@ class Chroma(VectorStore): self, query: str, k: int = DEFAULT_K, - filter: Optional[Dict[str, str]] = None, + filter: Optional[dict[str, str]] = None, **kwargs: Any, - ) -> List[Document]: + ) -> list[Document]: """Run similarity search with Chroma. Args: @@ -612,12 +607,12 @@ class Chroma(VectorStore): def similarity_search_by_vector( self, - embedding: List[float], + embedding: list[float], k: int = DEFAULT_K, - filter: Optional[Dict[str, str]] = None, - where_document: Optional[Dict[str, str]] = None, + filter: Optional[dict[str, str]] = None, + where_document: Optional[dict[str, str]] = None, **kwargs: Any, - ) -> List[Document]: + ) -> list[Document]: """Return docs most similar to embedding vector. Args: @@ -642,12 +637,12 @@ class Chroma(VectorStore): def similarity_search_by_vector_with_relevance_scores( self, - embedding: List[float], + embedding: list[float], k: int = DEFAULT_K, - filter: Optional[Dict[str, str]] = None, - where_document: Optional[Dict[str, str]] = None, + filter: Optional[dict[str, str]] = None, + where_document: Optional[dict[str, str]] = None, **kwargs: Any, - ) -> List[Tuple[Document, float]]: + ) -> list[tuple[Document, float]]: """Return docs most similar to embedding vector and similarity score. Args: @@ -675,10 +670,10 @@ class Chroma(VectorStore): self, query: str, k: int = DEFAULT_K, - filter: Optional[Dict[str, str]] = None, - where_document: Optional[Dict[str, str]] = None, + filter: Optional[dict[str, str]] = None, + where_document: Optional[dict[str, str]] = None, **kwargs: Any, - ) -> List[Tuple[Document, float]]: + ) -> list[tuple[Document, float]]: """Run similarity search with Chroma with distance. Args: @@ -717,10 +712,10 @@ class Chroma(VectorStore): self, query: str, k: int = DEFAULT_K, - filter: Optional[Dict[str, str]] = None, - where_document: Optional[Dict[str, str]] = None, + filter: Optional[dict[str, str]] = None, + where_document: Optional[dict[str, str]] = None, **kwargs: Any, - ) -> List[Tuple[Document, np.ndarray]]: + ) -> list[tuple[Document, np.ndarray]]: """Run similarity search with Chroma with vectors. Args: @@ -800,9 +795,9 @@ class Chroma(VectorStore): self, uri: str, k: int = DEFAULT_K, - filter: Optional[Dict[str, str]] = None, + filter: Optional[dict[str, str]] = None, **kwargs: Any, - ) -> List[Document]: + ) -> list[Document]: """Search for similar images based on the given image URI. Args: @@ -844,9 +839,9 @@ class Chroma(VectorStore): self, uri: str, k: int = DEFAULT_K, - filter: Optional[Dict[str, str]] = None, + filter: Optional[dict[str, str]] = None, **kwargs: Any, - ) -> List[Tuple[Document, float]]: + ) -> list[tuple[Document, float]]: """Search for similar images based on the given image URI. Args: @@ -886,14 +881,14 @@ class Chroma(VectorStore): def max_marginal_relevance_search_by_vector( self, - embedding: List[float], + embedding: list[float], k: int = DEFAULT_K, fetch_k: int = 20, lambda_mult: float = 0.5, - filter: Optional[Dict[str, str]] = None, - where_document: Optional[Dict[str, str]] = None, + filter: Optional[dict[str, str]] = None, + where_document: Optional[dict[str, str]] = None, **kwargs: Any, - ) -> List[Document]: + ) -> list[Document]: """Return docs selected using the maximal marginal relevance. Maximal marginal relevance optimizes for similarity to query AND diversity @@ -942,10 +937,10 @@ class Chroma(VectorStore): k: int = DEFAULT_K, fetch_k: int = 20, lambda_mult: float = 0.5, - filter: Optional[Dict[str, str]] = None, - where_document: Optional[Dict[str, str]] = None, + filter: Optional[dict[str, str]] = None, + where_document: Optional[dict[str, str]] = None, **kwargs: Any, - ) -> List[Document]: + ) -> list[Document]: """Return docs selected using the maximal marginal relevance. Maximal marginal relevance optimizes for similarity to query AND diversity @@ -1005,8 +1000,8 @@ class Chroma(VectorStore): limit: Optional[int] = None, offset: Optional[int] = None, where_document: Optional[WhereDocument] = None, - include: Optional[List[str]] = None, - ) -> Dict[str, Any]: + include: Optional[list[str]] = None, + ) -> dict[str, Any]: """Gets the collection. Args: @@ -1081,7 +1076,7 @@ class Chroma(VectorStore): return self.update_documents([document_id], [document]) # type: ignore - def update_documents(self, ids: List[str], documents: List[Document]) -> None: + def update_documents(self, ids: list[str], documents: list[Document]) -> None: """Update a document in the collection. Args: @@ -1129,16 +1124,16 @@ class Chroma(VectorStore): @classmethod def from_texts( - cls: Type[Chroma], - texts: List[str], + cls: type[Chroma], + texts: list[str], embedding: Optional[Embeddings] = None, - metadatas: Optional[List[dict]] = None, - ids: Optional[List[str]] = None, + metadatas: Optional[list[dict]] = None, + ids: Optional[list[str]] = None, collection_name: str = _LANGCHAIN_DEFAULT_COLLECTION_NAME, persist_directory: Optional[str] = None, client_settings: Optional[chromadb.config.Settings] = None, client: Optional[chromadb.ClientAPI] = None, - collection_metadata: Optional[Dict] = None, + collection_metadata: Optional[dict] = None, **kwargs: Any, ) -> Chroma: """Create a Chroma vectorstore from a raw documents. @@ -1200,15 +1195,15 @@ class Chroma(VectorStore): @classmethod def from_documents( - cls: Type[Chroma], - documents: List[Document], + cls: type[Chroma], + documents: list[Document], embedding: Optional[Embeddings] = None, - ids: Optional[List[str]] = None, + ids: Optional[list[str]] = None, collection_name: str = _LANGCHAIN_DEFAULT_COLLECTION_NAME, persist_directory: Optional[str] = None, client_settings: Optional[chromadb.config.Settings] = None, client: Optional[chromadb.ClientAPI] = None, # Add this line - collection_metadata: Optional[Dict] = None, + collection_metadata: Optional[dict] = None, **kwargs: Any, ) -> Chroma: """Create a Chroma vectorstore from a list of documents. @@ -1249,7 +1244,7 @@ class Chroma(VectorStore): **kwargs, ) - def delete(self, ids: Optional[List[str]] = None, **kwargs: Any) -> None: + def delete(self, ids: Optional[list[str]] = None, **kwargs: Any) -> None: """Delete by vector IDs. Args: diff --git a/libs/partners/chroma/pyproject.toml b/libs/partners/chroma/pyproject.toml index 906cfb6b222..2964a6c34f2 100644 --- a/libs/partners/chroma/pyproject.toml +++ b/libs/partners/chroma/pyproject.toml @@ -58,8 +58,12 @@ langchain-tests = { path = "../../standard-tests", editable = true } [tool.mypy] disallow_untyped_defs = true +[tool.ruff] +target-version = "py39" + [tool.ruff.lint] -select = ["E", "F", "I", "T201", "D"] +select = ["E", "F", "I", "T201", "D", "UP"] +ignore = [ "UP007", ] [tool.coverage.run] omit = ["tests/*"] diff --git a/libs/partners/chroma/tests/integration_tests/fake_embeddings.py b/libs/partners/chroma/tests/integration_tests/fake_embeddings.py index 63394e78cbe..e04f295782b 100644 --- a/libs/partners/chroma/tests/integration_tests/fake_embeddings.py +++ b/libs/partners/chroma/tests/integration_tests/fake_embeddings.py @@ -1,7 +1,6 @@ """Fake Embedding class for testing purposes.""" import math -from typing import List from langchain_core.embeddings import Embeddings @@ -11,22 +10,22 @@ fake_texts = ["foo", "bar", "baz"] class FakeEmbeddings(Embeddings): """Fake embeddings functionality for testing.""" - def embed_documents(self, texts: List[str]) -> List[List[float]]: + def embed_documents(self, texts: list[str]) -> list[list[float]]: """Return simple embeddings. Embeddings encode each text as its index.""" - return [[float(1.0)] * 9 + [float(i)] for i in range(len(texts))] + return [[1.0] * 9 + [float(i)] for i in range(len(texts))] - async def aembed_documents(self, texts: List[str]) -> List[List[float]]: + async def aembed_documents(self, texts: list[str]) -> list[list[float]]: return self.embed_documents(texts) - def embed_query(self, text: str) -> List[float]: + def embed_query(self, text: str) -> list[float]: """Return constant query embeddings. Embeddings are identical to embed_documents(texts)[0]. Distance to each text will be that text's index, as it was passed to embed_documents.""" - return [float(1.0)] * 9 + [float(0.0)] + return [1.0] * 9 + [0.0] - async def aembed_query(self, text: str) -> List[float]: + async def aembed_query(self, text: str) -> list[float]: return self.embed_query(text) @@ -35,22 +34,22 @@ class ConsistentFakeEmbeddings(FakeEmbeddings): vectors for the same texts.""" def __init__(self, dimensionality: int = 10) -> None: - self.known_texts: List[str] = [] + self.known_texts: list[str] = [] self.dimensionality = dimensionality - def embed_documents(self, texts: List[str]) -> List[List[float]]: + def embed_documents(self, texts: list[str]) -> list[list[float]]: """Return consistent embeddings for each text seen so far.""" out_vectors = [] for text in texts: if text not in self.known_texts: self.known_texts.append(text) - vector = [float(1.0)] * (self.dimensionality - 1) + [ + vector = [1.0] * (self.dimensionality - 1) + [ float(self.known_texts.index(text)) ] out_vectors.append(vector) return out_vectors - def embed_query(self, text: str) -> List[float]: + def embed_query(self, text: str) -> list[float]: """Return consistent embeddings for the text, if seen before, or a constant one if the text is unknown.""" return self.embed_documents([text])[0] @@ -61,13 +60,13 @@ class AngularTwoDimensionalEmbeddings(Embeddings): From angles (as strings in units of pi) to unit embedding vectors on a circle. """ - def embed_documents(self, texts: List[str]) -> List[List[float]]: + def embed_documents(self, texts: list[str]) -> list[list[float]]: """ Make a list of texts into a list of embedding vectors. """ return [self.embed_query(text) for text in texts] - def embed_query(self, text: str) -> List[float]: + def embed_query(self, text: str) -> list[float]: """ Convert input text to a 'vector' (list of floats). If the text is a number, use it as the angle for the diff --git a/libs/partners/chroma/tests/integration_tests/test_vectorstores.py b/libs/partners/chroma/tests/integration_tests/test_vectorstores.py index 7420a99ec5e..cbf2554b61e 100644 --- a/libs/partners/chroma/tests/integration_tests/test_vectorstores.py +++ b/libs/partners/chroma/tests/integration_tests/test_vectorstores.py @@ -3,8 +3,8 @@ import os.path import tempfile import uuid +from collections.abc import Generator from typing import ( - Generator, cast, ) @@ -222,7 +222,7 @@ def test_chroma_with_metadatas_with_scores_using_vector() -> None: def test_chroma_search_filter() -> None: """Test end to end construction and search with metadata filtering.""" texts = ["far", "bar", "baz"] - metadatas = [{"first_letter": "{}".format(text[0])} for text in texts] + metadatas = [{"first_letter": f"{text[0]}"} for text in texts] ids = [f"id_{i}" for i in range(len(texts))] docsearch = Chroma.from_texts( collection_name="test_collection", @@ -245,7 +245,7 @@ def test_chroma_search_filter() -> None: def test_chroma_search_filter_with_scores() -> None: """Test end to end construction and scored search with metadata filtering.""" texts = ["far", "bar", "baz"] - metadatas = [{"first_letter": "{}".format(text[0])} for text in texts] + metadatas = [{"first_letter": f"{text[0]}"} for text in texts] ids = [f"id_{i}" for i in range(len(texts))] docsearch = Chroma.from_texts( collection_name="test_collection", diff --git a/libs/partners/chroma/tests/unit_tests/test_standard.py b/libs/partners/chroma/tests/unit_tests/test_standard.py index 3e2945cb7fa..e933339a453 100644 --- a/libs/partners/chroma/tests/unit_tests/test_standard.py +++ b/libs/partners/chroma/tests/unit_tests/test_standard.py @@ -1,4 +1,4 @@ -from typing import Generator +from collections.abc import Generator import pytest from langchain_core.vectorstores import VectorStore diff --git a/libs/partners/deepseek/langchain_deepseek/chat_models.py b/libs/partners/deepseek/langchain_deepseek/chat_models.py index be52b82285d..9166e61cfa0 100644 --- a/libs/partners/deepseek/langchain_deepseek/chat_models.py +++ b/libs/partners/deepseek/langchain_deepseek/chat_models.py @@ -1,7 +1,8 @@ """DeepSeek chat models.""" +from collections.abc import Iterator from json import JSONDecodeError -from typing import Any, Dict, Iterator, List, Literal, Optional, Type, TypeVar, Union +from typing import Any, Literal, Optional, TypeVar, Union import openai from langchain_core.callbacks import ( @@ -19,8 +20,8 @@ from typing_extensions import Self DEFAULT_API_BASE = "https://api.deepseek.com/v1" _BM = TypeVar("_BM", bound=BaseModel) -_DictOrPydanticClass = Union[Dict[str, Any], Type[_BM], Type] -_DictOrPydantic = Union[Dict, _BM] +_DictOrPydanticClass = Union[dict[str, Any], type[_BM], type] +_DictOrPydantic = Union[dict, _BM] class ChatDeepSeek(BaseChatOpenAI): @@ -178,7 +179,7 @@ class ChatDeepSeek(BaseChatOpenAI): return "chat-deepseek" @property - def lc_secrets(self) -> Dict[str, str]: + def lc_secrets(self) -> dict[str, str]: """A map of constructor argument names to secret ids.""" return {"api_key": "DEEPSEEK_API_KEY"} @@ -217,7 +218,7 @@ class ChatDeepSeek(BaseChatOpenAI): def _create_chat_result( self, response: Union[dict, openai.BaseModel], - generation_info: Optional[Dict] = None, + generation_info: Optional[dict] = None, ) -> ChatResult: rtn = super()._create_chat_result(response, generation_info) @@ -243,8 +244,8 @@ class ChatDeepSeek(BaseChatOpenAI): def _convert_chunk_to_generation_chunk( self, chunk: dict, - default_chunk_class: Type, - base_generation_info: Optional[Dict], + default_chunk_class: type, + base_generation_info: Optional[dict], ) -> Optional[ChatGenerationChunk]: generation_chunk = super()._convert_chunk_to_generation_chunk( chunk, @@ -268,8 +269,8 @@ class ChatDeepSeek(BaseChatOpenAI): def _stream( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> Iterator[ChatGenerationChunk]: @@ -287,8 +288,8 @@ class ChatDeepSeek(BaseChatOpenAI): def _generate( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> ChatResult: diff --git a/libs/partners/deepseek/pyproject.toml b/libs/partners/deepseek/pyproject.toml index cf1eeddbedd..e9d7b32dbb9 100644 --- a/libs/partners/deepseek/pyproject.toml +++ b/libs/partners/deepseek/pyproject.toml @@ -45,8 +45,12 @@ langchain-tests = { path = "../../standard-tests", editable = true } [tool.mypy] disallow_untyped_defs = "True" +[tool.ruff] +target-version = "py39" + [tool.ruff.lint] -select = ["E", "F", "I", "T201"] +select = ["E", "F", "I", "T201", "UP"] +ignore = [ "UP007", ] [tool.coverage.run] omit = ["tests/*"] diff --git a/libs/partners/deepseek/tests/integration_tests/test_chat_models.py b/libs/partners/deepseek/tests/integration_tests/test_chat_models.py index 43dc2de4047..eca16830ed1 100644 --- a/libs/partners/deepseek/tests/integration_tests/test_chat_models.py +++ b/libs/partners/deepseek/tests/integration_tests/test_chat_models.py @@ -1,6 +1,6 @@ """Test ChatDeepSeek chat model.""" -from typing import Optional, Type +from typing import Optional import pytest from langchain_core.language_models import BaseChatModel @@ -13,7 +13,7 @@ from langchain_deepseek.chat_models import ChatDeepSeek class TestChatDeepSeek(ChatModelIntegrationTests): @property - def chat_model_class(self) -> Type[ChatDeepSeek]: + def chat_model_class(self) -> type[ChatDeepSeek]: return ChatDeepSeek @property diff --git a/libs/partners/deepseek/tests/unit_tests/test_chat_models.py b/libs/partners/deepseek/tests/unit_tests/test_chat_models.py index 94135a14a1f..14834fe3b37 100644 --- a/libs/partners/deepseek/tests/unit_tests/test_chat_models.py +++ b/libs/partners/deepseek/tests/unit_tests/test_chat_models.py @@ -1,6 +1,6 @@ """Test chat model integration.""" -from typing import Any, Dict, Literal, Type, Union +from typing import Any, Literal, Union from unittest.mock import MagicMock from langchain_core.messages import AIMessageChunk @@ -28,9 +28,9 @@ class MockOpenAIResponse(BaseModel): exclude_none: bool = False, round_trip: bool = False, warnings: Union[Literal["none", "warn", "error"], bool] = True, - context: Union[Dict[str, Any], None] = None, + context: Union[dict[str, Any], None] = None, serialize_as_any: bool = False, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: choices_list = [] for choice in self.choices: if isinstance(choice.message, ChatCompletionMessage): @@ -57,7 +57,7 @@ class MockOpenAIResponse(BaseModel): class TestChatDeepSeekUnit(ChatModelUnitTests): @property - def chat_model_class(self) -> Type[ChatDeepSeek]: + def chat_model_class(self) -> type[ChatDeepSeek]: return ChatDeepSeek @property @@ -134,7 +134,7 @@ class TestChatDeepSeekCustomUnit: def test_convert_chunk_with_reasoning_content(self) -> None: """Test that reasoning_content is properly extracted from streaming chunk.""" chat_model = ChatDeepSeek(model="deepseek-chat", api_key=SecretStr("api_key")) - chunk: Dict[str, Any] = { + chunk: dict[str, Any] = { "choices": [ { "delta": { @@ -158,7 +158,7 @@ class TestChatDeepSeekCustomUnit: def test_convert_chunk_with_reasoning(self) -> None: """Test that reasoning is properly extracted from streaming chunk.""" chat_model = ChatDeepSeek(model="deepseek-chat", api_key=SecretStr("api_key")) - chunk: Dict[str, Any] = { + chunk: dict[str, Any] = { "choices": [ { "delta": { @@ -182,7 +182,7 @@ class TestChatDeepSeekCustomUnit: def test_convert_chunk_without_reasoning(self) -> None: """Test that chunk without reasoning fields works correctly.""" chat_model = ChatDeepSeek(model="deepseek-chat", api_key=SecretStr("api_key")) - chunk: Dict[str, Any] = {"choices": [{"delta": {"content": "Main content"}}]} + chunk: dict[str, Any] = {"choices": [{"delta": {"content": "Main content"}}]} chunk_result = chat_model._convert_chunk_to_generation_chunk( chunk, AIMessageChunk, None @@ -194,7 +194,7 @@ class TestChatDeepSeekCustomUnit: def test_convert_chunk_with_empty_delta(self) -> None: """Test that chunk with empty delta works correctly.""" chat_model = ChatDeepSeek(model="deepseek-chat", api_key=SecretStr("api_key")) - chunk: Dict[str, Any] = {"choices": [{"delta": {}}]} + chunk: dict[str, Any] = {"choices": [{"delta": {}}]} chunk_result = chat_model._convert_chunk_to_generation_chunk( chunk, AIMessageChunk, None diff --git a/libs/partners/exa/langchain_exa/_utilities.py b/libs/partners/exa/langchain_exa/_utilities.py index bb9d02fc8e9..5e0dac692aa 100644 --- a/libs/partners/exa/langchain_exa/_utilities.py +++ b/libs/partners/exa/langchain_exa/_utilities.py @@ -1,11 +1,10 @@ import os # type: ignore[import-not-found] -from typing import Dict from exa_py import Exa # type: ignore from langchain_core.utils import convert_to_secret_str -def initialize_client(values: Dict) -> Dict: +def initialize_client(values: dict) -> dict: """Initialize the client.""" exa_api_key = values.get("exa_api_key") or os.environ.get("EXA_API_KEY") or "" values["exa_api_key"] = convert_to_secret_str(exa_api_key) diff --git a/libs/partners/exa/langchain_exa/retrievers.py b/libs/partners/exa/langchain_exa/retrievers.py index b5da661c7b7..1585e6a5796 100644 --- a/libs/partners/exa/langchain_exa/retrievers.py +++ b/libs/partners/exa/langchain_exa/retrievers.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Literal, Optional, Union +from typing import Any, Literal, Optional, Union from exa_py import Exa # type: ignore[untyped-import] from exa_py.api import ( @@ -13,7 +13,7 @@ from pydantic import Field, SecretStr, model_validator from langchain_exa._utilities import initialize_client -def _get_metadata(result: Any) -> Dict[str, Any]: +def _get_metadata(result: Any) -> dict[str, Any]: """Get the metadata from a result object.""" metadata = { "title": result.title, @@ -35,9 +35,9 @@ class ExaSearchRetriever(BaseRetriever): k: int = 10 # num_results """The number of search results to return.""" - include_domains: Optional[List[str]] = None + include_domains: Optional[list[str]] = None """A list of domains to include in the search.""" - exclude_domains: Optional[List[str]] = None + exclude_domains: Optional[list[str]] = None """A list of domains to exclude from the search.""" start_crawl_date: Optional[str] = None """The start date for the crawl (in YYYY-MM-DD format).""" @@ -62,14 +62,14 @@ class ExaSearchRetriever(BaseRetriever): @model_validator(mode="before") @classmethod - def validate_environment(cls, values: Dict) -> Any: + def validate_environment(cls, values: dict) -> Any: """Validate the environment.""" values = initialize_client(values) return values def _get_relevant_documents( self, query: str, *, run_manager: CallbackManagerForRetrieverRun - ) -> List[Document]: + ) -> list[Document]: response = self.client.search_and_contents( # type: ignore[misc] query, num_results=self.k, diff --git a/libs/partners/exa/langchain_exa/tools.py b/libs/partners/exa/langchain_exa/tools.py index 172064ef3ae..e22e4e709d8 100644 --- a/libs/partners/exa/langchain_exa/tools.py +++ b/libs/partners/exa/langchain_exa/tools.py @@ -1,6 +1,6 @@ """Tool for the Exa Search API.""" -from typing import Any, Dict, List, Optional, Union +from typing import Any, Optional, Union from exa_py import Exa # type: ignore[untyped-import] from exa_py.api import ( @@ -66,7 +66,7 @@ class ExaSearchResults(BaseTool): # type: ignore[override] @model_validator(mode="before") @classmethod - def validate_environment(cls, values: Dict) -> Any: + def validate_environment(cls, values: dict) -> Any: """Validate the environment.""" values = initialize_client(values) return values @@ -77,15 +77,15 @@ class ExaSearchResults(BaseTool): # type: ignore[override] num_results: int, text_contents_options: Optional[Union[TextContentsOptions, bool]] = None, highlights: Optional[Union[HighlightsContentsOptions, bool]] = None, - include_domains: Optional[List[str]] = None, - exclude_domains: Optional[List[str]] = None, + include_domains: Optional[list[str]] = None, + exclude_domains: Optional[list[str]] = None, start_crawl_date: Optional[str] = None, end_crawl_date: Optional[str] = None, start_published_date: Optional[str] = None, end_published_date: Optional[str] = None, use_autoprompt: Optional[bool] = None, run_manager: Optional[CallbackManagerForToolRun] = None, - ) -> Union[List[Dict], str]: + ) -> Union[list[dict], str]: """Use the tool.""" try: return self.client.search_and_contents( @@ -120,7 +120,7 @@ class ExaFindSimilarResults(BaseTool): # type: ignore[override] @model_validator(mode="before") @classmethod - def validate_environment(cls, values: Dict) -> Any: + def validate_environment(cls, values: dict) -> Any: """Validate the environment.""" values = initialize_client(values) return values @@ -131,8 +131,8 @@ class ExaFindSimilarResults(BaseTool): # type: ignore[override] num_results: int, text_contents_options: Optional[Union[TextContentsOptions, bool]] = None, highlights: Optional[Union[HighlightsContentsOptions, bool]] = None, - include_domains: Optional[List[str]] = None, - exclude_domains: Optional[List[str]] = None, + include_domains: Optional[list[str]] = None, + exclude_domains: Optional[list[str]] = None, start_crawl_date: Optional[str] = None, end_crawl_date: Optional[str] = None, start_published_date: Optional[str] = None, @@ -140,7 +140,7 @@ class ExaFindSimilarResults(BaseTool): # type: ignore[override] exclude_source_domain: Optional[bool] = None, category: Optional[str] = None, run_manager: Optional[CallbackManagerForToolRun] = None, - ) -> Union[List[Dict], str]: + ) -> Union[list[dict], str]: """Use the tool.""" try: return self.client.find_similar_and_contents( diff --git a/libs/partners/exa/pyproject.toml b/libs/partners/exa/pyproject.toml index c135e8e3e5f..e9e44980154 100644 --- a/libs/partners/exa/pyproject.toml +++ b/libs/partners/exa/pyproject.toml @@ -45,8 +45,12 @@ langchain-core = { path = "../../core", editable = true } [tool.mypy] disallow_untyped_defs = "True" +[tool.ruff] +target-version = "py39" + [tool.ruff.lint] -select = ["E", "F", "I", "T201"] +select = ["E", "F", "I", "T201", "UP"] +ignore = [ "UP007", ] [tool.coverage.run] omit = ["tests/*"] diff --git a/libs/partners/fireworks/langchain_fireworks/chat_models.py b/libs/partners/fireworks/langchain_fireworks/chat_models.py index e2953eab7fe..70dd14684d4 100644 --- a/libs/partners/fireworks/langchain_fireworks/chat_models.py +++ b/libs/partners/fireworks/langchain_fireworks/chat_models.py @@ -4,20 +4,13 @@ from __future__ import annotations import json import logging +from collections.abc import AsyncIterator, Iterator, Mapping, Sequence from operator import itemgetter from typing import ( Any, - AsyncIterator, Callable, - Dict, - Iterator, - List, Literal, - Mapping, Optional, - Sequence, - Tuple, - Type, TypedDict, Union, cast, @@ -109,7 +102,7 @@ def _convert_dict_to_message(_dict: Mapping[str, Any]) -> BaseMessage: # Fix for azure # Also Fireworks returns None for tool invocations content = _dict.get("content", "") or "" - additional_kwargs: Dict = {} + additional_kwargs: dict = {} if function_call := _dict.get("function_call"): additional_kwargs["function_call"] = dict(function_call) tool_calls = [] @@ -157,7 +150,7 @@ def _convert_message_to_dict(message: BaseMessage) -> dict: Returns: The dictionary. """ - message_dict: Dict[str, Any] + message_dict: dict[str, Any] if isinstance(message, ChatMessage): message_dict = {"role": message.role, "content": message.content} elif isinstance(message, HumanMessage): @@ -205,14 +198,14 @@ def _convert_message_to_dict(message: BaseMessage) -> dict: def _convert_chunk_to_message_chunk( - chunk: Mapping[str, Any], default_class: Type[BaseMessageChunk] + chunk: Mapping[str, Any], default_class: type[BaseMessageChunk] ) -> BaseMessageChunk: choice = chunk["choices"][0] _dict = choice["delta"] role = cast(str, _dict.get("role")) content = cast(str, _dict.get("content") or "") - additional_kwargs: Dict = {} - tool_call_chunks: List[ToolCallChunk] = [] + additional_kwargs: dict = {} + tool_call_chunks: list[ToolCallChunk] = [] if _dict.get("function_call"): function_call = dict(_dict["function_call"]) if "name" in function_call and function_call["name"] is None: @@ -290,17 +283,17 @@ class ChatFireworks(BaseChatModel): """ @property - def lc_secrets(self) -> Dict[str, str]: + def lc_secrets(self) -> dict[str, str]: return {"fireworks_api_key": "FIREWORKS_API_KEY"} @classmethod - def get_lc_namespace(cls) -> List[str]: + def get_lc_namespace(cls) -> list[str]: """Get the namespace of the langchain object.""" return ["langchain", "chat_models", "fireworks"] @property - def lc_attributes(self) -> Dict[str, Any]: - attributes: Dict[str, Any] = {} + def lc_attributes(self) -> dict[str, Any]: + attributes: dict[str, Any] = {} if self.fireworks_api_base: attributes["fireworks_api_base"] = self.fireworks_api_base @@ -319,9 +312,9 @@ class ChatFireworks(BaseChatModel): """Model name to use.""" temperature: float = 0.0 """What sampling temperature to use.""" - stop: Optional[Union[str, List[str]]] = Field(default=None, alias="stop_sequences") + stop: Optional[Union[str, list[str]]] = Field(default=None, alias="stop_sequences") """Default stop sequences.""" - model_kwargs: Dict[str, Any] = Field(default_factory=dict) + model_kwargs: dict[str, Any] = Field(default_factory=dict) """Holds any model parameters valid for `create` call not explicitly specified.""" fireworks_api_key: SecretStr = Field( alias="api_key", @@ -344,7 +337,7 @@ class ChatFireworks(BaseChatModel): ) """Base URL path for API requests, leave blank if not using a proxy or service emulator.""" - request_timeout: Union[float, Tuple[float, float], Any, None] = Field( + request_timeout: Union[float, tuple[float, float], Any, None] = Field( default=None, alias="timeout" ) """Timeout for requests to Fireworks completion API. Can be float, httpx.Timeout or @@ -364,7 +357,7 @@ class ChatFireworks(BaseChatModel): @model_validator(mode="before") @classmethod - def build_extra(cls, values: Dict[str, Any]) -> Any: + def build_extra(cls, values: dict[str, Any]) -> Any: """Build extra kwargs from additional params that were passed in.""" all_required_field_names = get_pydantic_field_names(cls) values = _build_model_kwargs(values, all_required_field_names) @@ -398,7 +391,7 @@ class ChatFireworks(BaseChatModel): return self @property - def _default_params(self) -> Dict[str, Any]: + def _default_params(self) -> dict[str, Any]: """Get the default parameters for calling Fireworks API.""" params = { "model": self.model_name, @@ -413,7 +406,7 @@ class ChatFireworks(BaseChatModel): return params def _get_ls_params( - self, stop: Optional[List[str]] = None, **kwargs: Any + self, stop: Optional[list[str]] = None, **kwargs: Any ) -> LangSmithParams: """Get standard params for tracing.""" params = self._get_invocation_params(stop=stop, **kwargs) @@ -429,7 +422,7 @@ class ChatFireworks(BaseChatModel): ls_params["ls_stop"] = ls_stop return ls_params - def _combine_llm_outputs(self, llm_outputs: List[Optional[dict]]) -> dict: + def _combine_llm_outputs(self, llm_outputs: list[Optional[dict]]) -> dict: overall_token_usage: dict = {} system_fingerprint = None for output in llm_outputs: @@ -452,15 +445,15 @@ class ChatFireworks(BaseChatModel): def _stream( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> Iterator[ChatGenerationChunk]: message_dicts, params = self._create_message_dicts(messages, stop) params = {**params, **kwargs, "stream": True} - default_chunk_class: Type[BaseMessageChunk] = AIMessageChunk + default_chunk_class: type[BaseMessageChunk] = AIMessageChunk for chunk in self.client.create(messages=message_dicts, **params): if not isinstance(chunk, dict): chunk = chunk.model_dump() @@ -487,8 +480,8 @@ class ChatFireworks(BaseChatModel): def _generate( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, stream: Optional[bool] = None, **kwargs: Any, @@ -509,8 +502,8 @@ class ChatFireworks(BaseChatModel): return self._create_chat_result(response) def _create_message_dicts( - self, messages: List[BaseMessage], stop: Optional[List[str]] - ) -> Tuple[List[Dict[str, Any]], Dict[str, Any]]: + self, messages: list[BaseMessage], stop: Optional[list[str]] + ) -> tuple[list[dict[str, Any]], dict[str, Any]]: params = self._default_params if stop is not None: params["stop"] = stop @@ -547,15 +540,15 @@ class ChatFireworks(BaseChatModel): async def _astream( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, **kwargs: Any, ) -> AsyncIterator[ChatGenerationChunk]: message_dicts, params = self._create_message_dicts(messages, stop) params = {**params, **kwargs, "stream": True} - default_chunk_class: Type[BaseMessageChunk] = AIMessageChunk + default_chunk_class: type[BaseMessageChunk] = AIMessageChunk async for chunk in self.async_client.acreate(messages=message_dicts, **params): if not isinstance(chunk, dict): chunk = chunk.model_dump() @@ -584,8 +577,8 @@ class ChatFireworks(BaseChatModel): async def _agenerate( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, stream: Optional[bool] = None, **kwargs: Any, @@ -607,13 +600,13 @@ class ChatFireworks(BaseChatModel): return self._create_chat_result(response) @property - def _identifying_params(self) -> Dict[str, Any]: + def _identifying_params(self) -> dict[str, Any]: """Get the identifying parameters.""" return {"model_name": self.model_name, **self._default_params} def _get_invocation_params( - self, stop: Optional[List[str]] = None, **kwargs: Any - ) -> Dict[str, Any]: + self, stop: Optional[list[str]] = None, **kwargs: Any + ) -> dict[str, Any]: """Get the parameters used to invoke the model.""" return { "model": self.model_name, @@ -634,7 +627,7 @@ class ChatFireworks(BaseChatModel): ) def bind_functions( self, - functions: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]], + functions: Sequence[Union[dict[str, Any], type[BaseModel], Callable, BaseTool]], function_call: Optional[ Union[_FunctionCall, str, Literal["auto", "none"]] ] = None, @@ -690,7 +683,7 @@ class ChatFireworks(BaseChatModel): def bind_tools( self, - tools: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]], + tools: Sequence[Union[dict[str, Any], type[BaseModel], Callable, BaseTool]], *, tool_choice: Optional[ Union[dict, str, Literal["auto", "any", "none"], bool] @@ -738,14 +731,14 @@ class ChatFireworks(BaseChatModel): def with_structured_output( self, - schema: Optional[Union[Dict, Type[BaseModel]]] = None, + schema: Optional[Union[dict, type[BaseModel]]] = None, *, method: Literal[ "function_calling", "json_mode", "json_schema" ] = "function_calling", include_raw: bool = False, **kwargs: Any, - ) -> Runnable[LanguageModelInput, Union[Dict, BaseModel]]: + ) -> Runnable[LanguageModelInput, Union[dict, BaseModel]]: """Model wrapper that returns outputs formatted to match the given schema. Args: diff --git a/libs/partners/fireworks/langchain_fireworks/embeddings.py b/libs/partners/fireworks/langchain_fireworks/embeddings.py index 55d363bc162..ce9995823b9 100644 --- a/libs/partners/fireworks/langchain_fireworks/embeddings.py +++ b/libs/partners/fireworks/langchain_fireworks/embeddings.py @@ -1,5 +1,3 @@ -from typing import List - from langchain_core.embeddings import Embeddings from langchain_core.utils import secret_from_env from openai import OpenAI @@ -96,13 +94,13 @@ class FireworksEmbeddings(BaseModel, Embeddings): ) return self - def embed_documents(self, texts: List[str]) -> List[List[float]]: + def embed_documents(self, texts: list[str]) -> list[list[float]]: """Embed search docs.""" return [ i.embedding for i in self.client.embeddings.create(input=texts, model=self.model).data ] - def embed_query(self, text: str) -> List[float]: + def embed_query(self, text: str) -> list[float]: """Embed query text.""" return self.embed_documents([text])[0] diff --git a/libs/partners/fireworks/langchain_fireworks/llms.py b/libs/partners/fireworks/langchain_fireworks/llms.py index fad10e9039f..43dfcedc1bc 100644 --- a/libs/partners/fireworks/langchain_fireworks/llms.py +++ b/libs/partners/fireworks/langchain_fireworks/llms.py @@ -1,7 +1,7 @@ """Wrapper around Fireworks AI's Completion API.""" import logging -from typing import Any, Dict, List, Optional +from typing import Any, Optional import requests from aiohttp import ClientSession @@ -63,7 +63,7 @@ class Fireworks(LLM): for question answering or summarization. A value greater than 1 introduces more randomness in the output. """ - model_kwargs: Dict[str, Any] = Field(default_factory=dict) + model_kwargs: dict[str, Any] = Field(default_factory=dict) """Holds any model parameters valid for `create` call not explicitly specified.""" top_k: Optional[int] = None """Used to limit the number of choices for the next predicted word or token. It @@ -90,7 +90,7 @@ class Fireworks(LLM): @model_validator(mode="before") @classmethod - def build_extra(cls, values: Dict[str, Any]) -> Any: + def build_extra(cls, values: dict[str, Any]) -> Any: """Build extra kwargs from additional params that were passed in.""" all_required_field_names = get_pydantic_field_names(cls) values = _build_model_kwargs(values, all_required_field_names) @@ -109,7 +109,7 @@ class Fireworks(LLM): return f"langchain-fireworks/{__version__}" @property - def default_params(self) -> Dict[str, Any]: + def default_params(self) -> dict[str, Any]: return { "model": self.model, "temperature": self.temperature, @@ -122,7 +122,7 @@ class Fireworks(LLM): def _call( self, prompt: str, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> str: @@ -139,7 +139,7 @@ class Fireworks(LLM): "Content-Type": "application/json", } stop_to_use = stop[0] if stop and len(stop) == 1 else stop - payload: Dict[str, Any] = { + payload: dict[str, Any] = { **self.default_params, "prompt": prompt, "stop": stop_to_use, @@ -168,7 +168,7 @@ class Fireworks(LLM): async def _acall( self, prompt: str, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, **kwargs: Any, ) -> str: @@ -185,7 +185,7 @@ class Fireworks(LLM): "Content-Type": "application/json", } stop_to_use = stop[0] if stop and len(stop) == 1 else stop - payload: Dict[str, Any] = { + payload: dict[str, Any] = { **self.default_params, "prompt": prompt, "stop": stop_to_use, diff --git a/libs/partners/fireworks/pyproject.toml b/libs/partners/fireworks/pyproject.toml index e3468ef5f4a..cb111c14522 100644 --- a/libs/partners/fireworks/pyproject.toml +++ b/libs/partners/fireworks/pyproject.toml @@ -48,8 +48,12 @@ langchain-tests = { path = "../../standard-tests", editable = true } [tool.mypy] disallow_untyped_defs = "True" +[tool.ruff] +target-version = "py39" + [tool.ruff.lint] -select = ["E", "F", "I", "T201"] +select = ["E", "F", "I", "T201", "UP"] +ignore = [ "UP007", ] [tool.coverage.run] omit = ["tests/*"] diff --git a/libs/partners/fireworks/tests/integration_tests/test_chat_models.py b/libs/partners/fireworks/tests/integration_tests/test_chat_models.py index 6a019bd38b7..0209aa9b15d 100644 --- a/libs/partners/fireworks/tests/integration_tests/test_chat_models.py +++ b/libs/partners/fireworks/tests/integration_tests/test_chat_models.py @@ -4,12 +4,12 @@ You will need FIREWORKS_API_KEY set in your environment to run these tests. """ import json -from typing import Any, Literal, Optional +from typing import Annotated, Any, Literal, Optional import pytest from langchain_core.messages import AIMessage, AIMessageChunk, BaseMessageChunk from pydantic import BaseModel, Field -from typing_extensions import Annotated, TypedDict +from typing_extensions import TypedDict from langchain_fireworks import ChatFireworks diff --git a/libs/partners/fireworks/tests/integration_tests/test_standard.py b/libs/partners/fireworks/tests/integration_tests/test_standard.py index 024a5f0e787..5c467a01649 100644 --- a/libs/partners/fireworks/tests/integration_tests/test_standard.py +++ b/libs/partners/fireworks/tests/integration_tests/test_standard.py @@ -1,7 +1,5 @@ """Standard LangChain interface tests""" -from typing import Type - import pytest from langchain_core.language_models import BaseChatModel from langchain_core.tools import BaseTool @@ -14,7 +12,7 @@ from langchain_fireworks import ChatFireworks class TestFireworksStandard(ChatModelIntegrationTests): @property - def chat_model_class(self) -> Type[BaseChatModel]: + def chat_model_class(self) -> type[BaseChatModel]: return ChatFireworks @property diff --git a/libs/partners/fireworks/tests/unit_tests/test_embeddings_standard.py b/libs/partners/fireworks/tests/unit_tests/test_embeddings_standard.py index 44b6197a391..916f9bf2ca7 100644 --- a/libs/partners/fireworks/tests/unit_tests/test_embeddings_standard.py +++ b/libs/partners/fireworks/tests/unit_tests/test_embeddings_standard.py @@ -1,7 +1,5 @@ """Standard LangChain interface tests""" -from typing import Tuple, Type - from langchain_core.embeddings import Embeddings from langchain_tests.unit_tests.embeddings import EmbeddingsUnitTests @@ -10,7 +8,7 @@ from langchain_fireworks import FireworksEmbeddings class TestFireworksStandard(EmbeddingsUnitTests): @property - def embeddings_class(self) -> Type[Embeddings]: + def embeddings_class(self) -> type[Embeddings]: return FireworksEmbeddings @property @@ -18,7 +16,7 @@ class TestFireworksStandard(EmbeddingsUnitTests): return {"api_key": "test_api_key"} @property - def init_from_env_params(self) -> Tuple[dict, dict, dict]: + def init_from_env_params(self) -> tuple[dict, dict, dict]: return ( { "FIREWORKS_API_KEY": "api_key", diff --git a/libs/partners/fireworks/tests/unit_tests/test_standard.py b/libs/partners/fireworks/tests/unit_tests/test_standard.py index 4f444897154..25cf0b6f3e4 100644 --- a/libs/partners/fireworks/tests/unit_tests/test_standard.py +++ b/libs/partners/fireworks/tests/unit_tests/test_standard.py @@ -1,7 +1,5 @@ """Standard LangChain interface tests""" -from typing import Tuple, Type - from langchain_core.language_models import BaseChatModel from langchain_tests.unit_tests import ( # type: ignore[import-not-found] ChatModelUnitTests, # type: ignore[import-not-found] @@ -12,7 +10,7 @@ from langchain_fireworks import ChatFireworks class TestFireworksStandard(ChatModelUnitTests): @property - def chat_model_class(self) -> Type[BaseChatModel]: + def chat_model_class(self) -> type[BaseChatModel]: return ChatFireworks @property @@ -20,7 +18,7 @@ class TestFireworksStandard(ChatModelUnitTests): return {"api_key": "test_api_key"} @property - def init_from_env_params(self) -> Tuple[dict, dict, dict]: + def init_from_env_params(self) -> tuple[dict, dict, dict]: return ( { "FIREWORKS_API_KEY": "api_key", diff --git a/libs/partners/groq/langchain_groq/chat_models.py b/libs/partners/groq/langchain_groq/chat_models.py index 82b69076466..240c1ae2d0b 100644 --- a/libs/partners/groq/langchain_groq/chat_models.py +++ b/libs/partners/groq/langchain_groq/chat_models.py @@ -4,20 +4,13 @@ from __future__ import annotations import json import warnings +from collections.abc import AsyncIterator, Iterator, Mapping, Sequence from operator import itemgetter from typing import ( Any, - AsyncIterator, Callable, - Dict, - Iterator, - List, Literal, - Mapping, Optional, - Sequence, - Tuple, - Type, TypedDict, Union, cast, @@ -307,9 +300,9 @@ class ChatGroq(BaseChatModel): """Model name to use.""" temperature: float = 0.7 """What sampling temperature to use.""" - stop: Optional[Union[List[str], str]] = Field(default=None, alias="stop_sequences") + stop: Optional[Union[list[str], str]] = Field(default=None, alias="stop_sequences") """Default stop sequences.""" - model_kwargs: Dict[str, Any] = Field(default_factory=dict) + model_kwargs: dict[str, Any] = Field(default_factory=dict) """Holds any model parameters valid for `create` call not explicitly specified.""" groq_api_key: Optional[SecretStr] = Field( alias="api_key", default_factory=secret_from_env("GROQ_API_KEY", default=None) @@ -324,7 +317,7 @@ class ChatGroq(BaseChatModel): groq_proxy: Optional[str] = Field( default_factory=from_env("GROQ_PROXY", default=None) ) - request_timeout: Union[float, Tuple[float, float], Any, None] = Field( + request_timeout: Union[float, tuple[float, float], Any, None] = Field( default=None, alias="timeout" ) """Timeout for requests to Groq completion API. Can be float, httpx.Timeout or @@ -353,7 +346,7 @@ class ChatGroq(BaseChatModel): @model_validator(mode="before") @classmethod - def build_extra(cls, values: Dict[str, Any]) -> Any: + def build_extra(cls, values: dict[str, Any]) -> Any: """Build extra kwargs from additional params that were passed in.""" all_required_field_names = get_pydantic_field_names(cls) extra = values.get("model_kwargs", {}) @@ -392,7 +385,7 @@ class ChatGroq(BaseChatModel): self.default_headers or {} ) - client_params: Dict[str, Any] = { + client_params: dict[str, Any] = { "api_key": ( self.groq_api_key.get_secret_value() if self.groq_api_key else None ), @@ -406,13 +399,13 @@ class ChatGroq(BaseChatModel): try: import groq - sync_specific: Dict[str, Any] = {"http_client": self.http_client} + sync_specific: dict[str, Any] = {"http_client": self.http_client} if not self.client: self.client = groq.Groq( **client_params, **sync_specific ).chat.completions if not self.async_client: - async_specific: Dict[str, Any] = {"http_client": self.http_async_client} + async_specific: dict[str, Any] = {"http_client": self.http_async_client} self.async_client = groq.AsyncGroq( **client_params, **async_specific ).chat.completions @@ -427,7 +420,7 @@ class ChatGroq(BaseChatModel): # Serializable class method overrides # @property - def lc_secrets(self) -> Dict[str, str]: + def lc_secrets(self) -> dict[str, str]: return {"groq_api_key": "GROQ_API_KEY"} @classmethod @@ -444,7 +437,7 @@ class ChatGroq(BaseChatModel): return "groq-chat" def _get_ls_params( - self, stop: Optional[List[str]] = None, **kwargs: Any + self, stop: Optional[list[str]] = None, **kwargs: Any ) -> LangSmithParams: """Get standard params for tracing.""" params = self._get_invocation_params(stop=stop, **kwargs) @@ -480,8 +473,8 @@ class ChatGroq(BaseChatModel): def _generate( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> ChatResult: @@ -500,8 +493,8 @@ class ChatGroq(BaseChatModel): async def _agenerate( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, **kwargs: Any, ) -> ChatResult: @@ -521,8 +514,8 @@ class ChatGroq(BaseChatModel): def _stream( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> Iterator[ChatGenerationChunk]: @@ -530,7 +523,7 @@ class ChatGroq(BaseChatModel): params = {**params, **kwargs, "stream": True} - default_chunk_class: Type[BaseMessageChunk] = AIMessageChunk + default_chunk_class: type[BaseMessageChunk] = AIMessageChunk for chunk in self.client.create(messages=message_dicts, **params): if not isinstance(chunk, dict): chunk = chunk.model_dump() @@ -560,8 +553,8 @@ class ChatGroq(BaseChatModel): async def _astream( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, **kwargs: Any, ) -> AsyncIterator[ChatGenerationChunk]: @@ -569,7 +562,7 @@ class ChatGroq(BaseChatModel): params = {**params, **kwargs, "stream": True} - default_chunk_class: Type[BaseMessageChunk] = AIMessageChunk + default_chunk_class: type[BaseMessageChunk] = AIMessageChunk async for chunk in await self.async_client.create( messages=message_dicts, **params ): @@ -605,7 +598,7 @@ class ChatGroq(BaseChatModel): # Internal methods # @property - def _default_params(self) -> Dict[str, Any]: + def _default_params(self) -> dict[str, Any]: """Get the default parameters for calling Groq API.""" params = { "model": self.model_name, @@ -652,15 +645,15 @@ class ChatGroq(BaseChatModel): return ChatResult(generations=generations, llm_output=llm_output) def _create_message_dicts( - self, messages: List[BaseMessage], stop: Optional[List[str]] - ) -> Tuple[List[Dict[str, Any]], Dict[str, Any]]: + self, messages: list[BaseMessage], stop: Optional[list[str]] + ) -> tuple[list[dict[str, Any]], dict[str, Any]]: params = self._default_params if stop is not None: params["stop"] = stop message_dicts = [_convert_message_to_dict(m) for m in messages] return message_dicts, params - def _combine_llm_outputs(self, llm_outputs: List[Optional[dict]]) -> dict: + def _combine_llm_outputs(self, llm_outputs: list[Optional[dict]]) -> dict: overall_token_usage: dict = {} system_fingerprint = None for output in llm_outputs: @@ -688,7 +681,7 @@ class ChatGroq(BaseChatModel): ) def bind_functions( self, - functions: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]], + functions: Sequence[Union[dict[str, Any], type[BaseModel], Callable, BaseTool]], function_call: Optional[ Union[_FunctionCall, str, Literal["auto", "none"]] ] = None, @@ -743,7 +736,7 @@ class ChatGroq(BaseChatModel): def bind_tools( self, - tools: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]], + tools: Sequence[Union[dict[str, Any], type[BaseModel], Callable, BaseTool]], *, tool_choice: Optional[ Union[dict, str, Literal["auto", "any", "none"], bool] @@ -791,12 +784,12 @@ class ChatGroq(BaseChatModel): def with_structured_output( self, - schema: Optional[Union[Dict, Type[BaseModel]]] = None, + schema: Optional[Union[dict, type[BaseModel]]] = None, *, method: Literal["function_calling", "json_mode"] = "function_calling", include_raw: bool = False, **kwargs: Any, - ) -> Runnable[LanguageModelInput, Union[Dict, BaseModel]]: + ) -> Runnable[LanguageModelInput, Union[dict, BaseModel]]: """Model wrapper that returns outputs formatted to match the given schema. Args: @@ -1096,7 +1089,7 @@ def _convert_message_to_dict(message: BaseMessage) -> dict: Returns: The dictionary. """ - message_dict: Dict[str, Any] + message_dict: dict[str, Any] if isinstance(message, ChatMessage): message_dict = {"role": message.role, "content": message.content} elif isinstance(message, HumanMessage): @@ -1142,13 +1135,13 @@ def _convert_message_to_dict(message: BaseMessage) -> dict: def _convert_chunk_to_message_chunk( - chunk: Mapping[str, Any], default_class: Type[BaseMessageChunk] + chunk: Mapping[str, Any], default_class: type[BaseMessageChunk] ) -> BaseMessageChunk: choice = chunk["choices"][0] _dict = choice["delta"] role = cast(str, _dict.get("role")) content = cast(str, _dict.get("content") or "") - additional_kwargs: Dict = {} + additional_kwargs: dict = {} if _dict.get("function_call"): function_call = dict(_dict["function_call"]) if "name" in function_call and function_call["name"] is None: @@ -1202,7 +1195,7 @@ def _convert_dict_to_message(_dict: Mapping[str, Any]) -> BaseMessage: return HumanMessage(content=_dict.get("content", "")) elif role == "assistant": content = _dict.get("content", "") or "" - additional_kwargs: Dict = {} + additional_kwargs: dict = {} if function_call := _dict.get("function_call"): additional_kwargs["function_call"] = dict(function_call) tool_calls = [] diff --git a/libs/partners/groq/pyproject.toml b/libs/partners/groq/pyproject.toml index df11456bfba..ac534088465 100644 --- a/libs/partners/groq/pyproject.toml +++ b/libs/partners/groq/pyproject.toml @@ -40,8 +40,12 @@ langchain-tests = { path = "../../standard-tests", editable = true } [tool.mypy] disallow_untyped_defs = "True" +[tool.ruff] +target-version = "py39" + [tool.ruff.lint] -select = ["E", "F", "I", "W"] +select = ["E", "F", "I", "W", "UP"] +ignore = [ "UP007", ] [tool.coverage.run] omit = ["tests/*"] diff --git a/libs/partners/groq/tests/integration_tests/test_standard.py b/libs/partners/groq/tests/integration_tests/test_standard.py index 0820faf77c6..04e68fa567f 100644 --- a/libs/partners/groq/tests/integration_tests/test_standard.py +++ b/libs/partners/groq/tests/integration_tests/test_standard.py @@ -1,7 +1,5 @@ """Standard LangChain interface tests""" -from typing import Type - import pytest from langchain_core.language_models import BaseChatModel from langchain_core.rate_limiters import InMemoryRateLimiter @@ -17,7 +15,7 @@ rate_limiter = InMemoryRateLimiter(requests_per_second=0.2) class BaseTestGroq(ChatModelIntegrationTests): @property - def chat_model_class(self) -> Type[BaseChatModel]: + def chat_model_class(self) -> type[BaseChatModel]: return ChatGroq @pytest.mark.xfail(reason="Not yet implemented.") diff --git a/libs/partners/groq/tests/unit_tests/fake/callbacks.py b/libs/partners/groq/tests/unit_tests/fake/callbacks.py index 34f825e16a5..8b25a400b43 100644 --- a/libs/partners/groq/tests/unit_tests/fake/callbacks.py +++ b/libs/partners/groq/tests/unit_tests/fake/callbacks.py @@ -1,7 +1,7 @@ """A fake callback handler for testing purposes.""" from itertools import chain -from typing import Any, Dict, List, Optional, Union +from typing import Any, Optional, Union from uuid import UUID from langchain_core.callbacks.base import AsyncCallbackHandler, BaseCallbackHandler @@ -15,7 +15,7 @@ class BaseFakeCallbackHandler(BaseModel): starts: int = 0 ends: int = 0 errors: int = 0 - errors_args: List[Any] = [] + errors_args: list[Any] = [] text: int = 0 ignore_llm_: bool = False ignore_chain_: bool = False @@ -264,8 +264,8 @@ class FakeCallbackHandler(BaseCallbackHandler, BaseFakeCallbackHandlerMixin): class FakeCallbackHandlerWithChatStart(FakeCallbackHandler): def on_chat_model_start( self, - serialized: Dict[str, Any], - messages: List[List[BaseMessage]], + serialized: dict[str, Any], + messages: list[list[BaseMessage]], *, run_id: UUID, parent_run_id: Optional[UUID] = None, diff --git a/libs/partners/groq/tests/unit_tests/test_standard.py b/libs/partners/groq/tests/unit_tests/test_standard.py index f04d13b703f..4ab0bf4fedd 100644 --- a/libs/partners/groq/tests/unit_tests/test_standard.py +++ b/libs/partners/groq/tests/unit_tests/test_standard.py @@ -1,7 +1,5 @@ """Standard LangChain interface tests""" -from typing import Type - from langchain_core.language_models import BaseChatModel from langchain_tests.unit_tests.chat_models import ( ChatModelUnitTests, @@ -12,7 +10,7 @@ from langchain_groq import ChatGroq class TestGroqStandard(ChatModelUnitTests): @property - def chat_model_class(self) -> Type[BaseChatModel]: + def chat_model_class(self) -> type[BaseChatModel]: return ChatGroq @property diff --git a/libs/partners/huggingface/langchain_huggingface/chat_models/huggingface.py b/libs/partners/huggingface/langchain_huggingface/chat_models/huggingface.py index 30510ca10fe..9d20b8b87ce 100644 --- a/libs/partners/huggingface/langchain_huggingface/chat_models/huggingface.py +++ b/libs/partners/huggingface/langchain_huggingface/chat_models/huggingface.py @@ -1,16 +1,13 @@ """Hugging Face Chat Wrapper.""" import json +from collections.abc import Sequence from dataclasses import dataclass from typing import ( Any, Callable, - Dict, - List, Literal, Optional, - Sequence, - Type, Union, cast, ) @@ -46,8 +43,8 @@ DEFAULT_SYSTEM_PROMPT = """You are a helpful, respectful, and honest assistant." class TGI_RESPONSE: """Response from the TextGenInference API.""" - choices: List[Any] - usage: Dict + choices: list[Any] + usage: dict @dataclass @@ -56,12 +53,12 @@ class TGI_MESSAGE: role: str content: str - tool_calls: List[Dict] + tool_calls: list[dict] def _convert_message_to_chat_message( message: BaseMessage, -) -> Dict: +) -> dict: if isinstance(message, ChatMessage): return dict(role=message.role, content=message.content) elif isinstance(message, HumanMessage): @@ -104,7 +101,7 @@ def _convert_TGI_message_to_LC_message( content = cast(str, _message.content) if content is None: content = "" - additional_kwargs: Dict = {} + additional_kwargs: dict = {} if tool_calls := _message.tool_calls: if "arguments" in tool_calls[0]["function"]: functions = tool_calls[0]["function"].pop("arguments") @@ -358,8 +355,8 @@ class ChatHuggingFace(BaseChatModel): def _generate( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> ChatResult: @@ -380,8 +377,8 @@ class ChatHuggingFace(BaseChatModel): async def _agenerate( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, **kwargs: Any, ) -> ChatResult: @@ -398,7 +395,7 @@ class ChatHuggingFace(BaseChatModel): def _to_chat_prompt( self, - messages: List[BaseMessage], + messages: list[BaseMessage], ) -> str: """Convert a list of messages into a prompt format expected by wrapped LLM.""" if not messages: @@ -472,7 +469,7 @@ class ChatHuggingFace(BaseChatModel): def bind_tools( self, - tools: Sequence[Union[Dict[str, Any], Type, Callable, BaseTool]], + tools: Sequence[Union[dict[str, Any], type, Callable, BaseTool]], *, tool_choice: Optional[ Union[dict, str, Literal["auto", "none", "required"], bool] @@ -529,8 +526,8 @@ class ChatHuggingFace(BaseChatModel): return super().bind(tools=formatted_tools, **kwargs) def _create_message_dicts( - self, messages: List[BaseMessage], stop: Optional[List[str]] - ) -> List[Dict[Any, Any]]: + self, messages: list[BaseMessage], stop: Optional[list[str]] + ) -> list[dict[Any, Any]]: message_dicts = [_convert_message_to_chat_message(m) for m in messages] return message_dicts diff --git a/libs/partners/huggingface/langchain_huggingface/embeddings/huggingface.py b/libs/partners/huggingface/langchain_huggingface/embeddings/huggingface.py index a15cd56fa0d..c092eee5730 100644 --- a/libs/partners/huggingface/langchain_huggingface/embeddings/huggingface.py +++ b/libs/partners/huggingface/langchain_huggingface/embeddings/huggingface.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional +from typing import Any, Optional from langchain_core.embeddings import Embeddings from pydantic import BaseModel, ConfigDict, Field @@ -40,16 +40,16 @@ class HuggingFaceEmbeddings(BaseModel, Embeddings): cache_folder: Optional[str] = None """Path to store models. Can be also set by SENTENCE_TRANSFORMERS_HOME environment variable.""" - model_kwargs: Dict[str, Any] = Field(default_factory=dict) + model_kwargs: dict[str, Any] = Field(default_factory=dict) """Keyword arguments to pass to the Sentence Transformer model, such as `device`, `prompts`, `default_prompt_name`, `revision`, `trust_remote_code`, or `token`. See also the Sentence Transformer documentation: https://sbert.net/docs/package_reference/SentenceTransformer.html#sentence_transformers.SentenceTransformer""" - encode_kwargs: Dict[str, Any] = Field(default_factory=dict) + encode_kwargs: dict[str, Any] = Field(default_factory=dict) """Keyword arguments to pass when calling the `encode` method for the documents of the Sentence Transformer model, such as `prompt_name`, `prompt`, `batch_size`, `precision`, `normalize_embeddings`, and more. See also the Sentence Transformer documentation: https://sbert.net/docs/package_reference/SentenceTransformer.html#sentence_transformers.SentenceTransformer.encode""" - query_encode_kwargs: Dict[str, Any] = Field(default_factory=dict) + query_encode_kwargs: dict[str, Any] = Field(default_factory=dict) """Keyword arguments to pass when calling the `encode` method for the query of the Sentence Transformer model, such as `prompt_name`, `prompt`, `batch_size`, `precision`, `normalize_embeddings`, and more. @@ -102,8 +102,8 @@ class HuggingFaceEmbeddings(BaseModel, Embeddings): ) def _embed( - self, texts: list[str], encode_kwargs: Dict[str, Any] - ) -> List[List[float]]: + self, texts: list[str], encode_kwargs: dict[str, Any] + ) -> list[list[float]]: """ Embed a text using the HuggingFace transformer model. @@ -138,7 +138,7 @@ class HuggingFaceEmbeddings(BaseModel, Embeddings): return embeddings.tolist() - def embed_documents(self, texts: List[str]) -> List[List[float]]: + def embed_documents(self, texts: list[str]) -> list[list[float]]: """Compute doc embeddings using a HuggingFace transformer model. Args: @@ -149,7 +149,7 @@ class HuggingFaceEmbeddings(BaseModel, Embeddings): """ return self._embed(texts, self.encode_kwargs) - def embed_query(self, text: str) -> List[float]: + def embed_query(self, text: str) -> list[float]: """Compute query embeddings using a HuggingFace transformer model. Args: diff --git a/libs/partners/huggingface/langchain_huggingface/embeddings/huggingface_endpoint.py b/libs/partners/huggingface/langchain_huggingface/embeddings/huggingface_endpoint.py index 896e0fde3b8..91cb01a469d 100644 --- a/libs/partners/huggingface/langchain_huggingface/embeddings/huggingface_endpoint.py +++ b/libs/partners/huggingface/langchain_huggingface/embeddings/huggingface_endpoint.py @@ -1,5 +1,5 @@ import os -from typing import Any, List, Optional +from typing import Any, Optional from langchain_core.embeddings import Embeddings from langchain_core.utils import from_env @@ -101,7 +101,7 @@ class HuggingFaceEndpointEmbeddings(BaseModel, Embeddings): ) return self - def embed_documents(self, texts: List[str]) -> List[List[float]]: + def embed_documents(self, texts: list[str]) -> list[list[float]]: """Call out to HuggingFaceHub's embedding endpoint for embedding search docs. Args: @@ -117,7 +117,7 @@ class HuggingFaceEndpointEmbeddings(BaseModel, Embeddings): responses = self.client.feature_extraction(text=texts, **_model_kwargs) return responses - async def aembed_documents(self, texts: List[str]) -> List[List[float]]: + async def aembed_documents(self, texts: list[str]) -> list[list[float]]: """Async Call to HuggingFaceHub's embedding endpoint for embedding search docs. Args: @@ -134,7 +134,7 @@ class HuggingFaceEndpointEmbeddings(BaseModel, Embeddings): ) return responses - def embed_query(self, text: str) -> List[float]: + def embed_query(self, text: str) -> list[float]: """Call out to HuggingFaceHub's embedding endpoint for embedding query text. Args: @@ -146,7 +146,7 @@ class HuggingFaceEndpointEmbeddings(BaseModel, Embeddings): response = self.embed_documents([text])[0] return response - async def aembed_query(self, text: str) -> List[float]: + async def aembed_query(self, text: str) -> list[float]: """Async Call to HuggingFaceHub's embedding endpoint for embedding query text. Args: diff --git a/libs/partners/huggingface/langchain_huggingface/llms/huggingface_endpoint.py b/libs/partners/huggingface/langchain_huggingface/llms/huggingface_endpoint.py index 076cafd4de6..c44122a42b2 100644 --- a/libs/partners/huggingface/langchain_huggingface/llms/huggingface_endpoint.py +++ b/libs/partners/huggingface/langchain_huggingface/llms/huggingface_endpoint.py @@ -2,7 +2,8 @@ import inspect import json # type: ignore[import-not-found] import logging import os -from typing import Any, AsyncIterator, Dict, Iterator, List, Mapping, Optional +from collections.abc import AsyncIterator, Iterator, Mapping +from typing import Any, Optional from langchain_core.callbacks import ( AsyncCallbackManagerForLLMRun, @@ -96,7 +97,7 @@ class HuggingFaceEndpoint(LLM): """Whether to prepend the prompt to the generated text""" truncate: Optional[int] = None """Truncate inputs tokens to the given size""" - stop_sequences: List[str] = Field(default_factory=list) + stop_sequences: list[str] = Field(default_factory=list) """Stop generating tokens if a member of `stop_sequences` is generated""" seed: Optional[int] = None """Random sampling seed""" @@ -111,9 +112,9 @@ class HuggingFaceEndpoint(LLM): watermark: bool = False """Watermarking with [A Watermark for Large Language Models] (https://arxiv.org/abs/2301.10226)""" - server_kwargs: Dict[str, Any] = Field(default_factory=dict) + server_kwargs: dict[str, Any] = Field(default_factory=dict) """Holds any text-generation-inference server parameters not explicitly specified""" - model_kwargs: Dict[str, Any] = Field(default_factory=dict) + model_kwargs: dict[str, Any] = Field(default_factory=dict) """Holds any model parameters valid for `call` not explicitly specified""" model: str client: Any = None #: :meta private: @@ -128,7 +129,7 @@ class HuggingFaceEndpoint(LLM): @model_validator(mode="before") @classmethod - def build_extra(cls, values: Dict[str, Any]) -> Any: + def build_extra(cls, values: dict[str, Any]) -> Any: """Build extra kwargs from additional params that were passed in.""" all_required_field_names = get_pydantic_field_names(cls) extra = values.get("model_kwargs", {}) @@ -252,7 +253,7 @@ class HuggingFaceEndpoint(LLM): return self @property - def _default_params(self) -> Dict[str, Any]: + def _default_params(self) -> dict[str, Any]: """Get the default parameters for calling text generation inference API.""" return { "max_new_tokens": self.max_new_tokens, @@ -285,8 +286,8 @@ class HuggingFaceEndpoint(LLM): return "huggingface_endpoint" def _invocation_params( - self, runtime_stop: Optional[List[str]], **kwargs: Any - ) -> Dict[str, Any]: + self, runtime_stop: Optional[list[str]], **kwargs: Any + ) -> dict[str, Any]: params = {**self._default_params, **kwargs} params["stop_sequences"] = params["stop_sequences"] + (runtime_stop or []) return params @@ -294,7 +295,7 @@ class HuggingFaceEndpoint(LLM): def _call( self, prompt: str, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> str: @@ -326,7 +327,7 @@ class HuggingFaceEndpoint(LLM): async def _acall( self, prompt: str, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, **kwargs: Any, ) -> str: @@ -357,7 +358,7 @@ class HuggingFaceEndpoint(LLM): def _stream( self, prompt: str, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> Iterator[GenerationChunk]: @@ -394,7 +395,7 @@ class HuggingFaceEndpoint(LLM): async def _astream( self, prompt: str, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, **kwargs: Any, ) -> AsyncIterator[GenerationChunk]: diff --git a/libs/partners/huggingface/langchain_huggingface/llms/huggingface_pipeline.py b/libs/partners/huggingface/langchain_huggingface/llms/huggingface_pipeline.py index 4314b906aba..96e408bb7a4 100644 --- a/libs/partners/huggingface/langchain_huggingface/llms/huggingface_pipeline.py +++ b/libs/partners/huggingface/langchain_huggingface/llms/huggingface_pipeline.py @@ -2,7 +2,8 @@ from __future__ import annotations # type: ignore[import-not-found] import importlib.util import logging -from typing import Any, Dict, Iterator, List, Mapping, Optional +from collections.abc import Iterator, Mapping +from typing import Any, Optional from langchain_core.callbacks import CallbackManagerForLLMRun from langchain_core.language_models.llms import BaseLLM @@ -82,7 +83,7 @@ class HuggingFacePipeline(BaseLLM): @model_validator(mode="before") @classmethod - def pre_init_validator(cls, values: Dict[str, Any]) -> Dict[str, Any]: + def pre_init_validator(cls, values: dict[str, Any]) -> dict[str, Any]: """Ensure model_id is set either by pipeline or user input.""" if "model_id" not in values: if "pipeline" in values and values["pipeline"]: @@ -297,13 +298,13 @@ class HuggingFacePipeline(BaseLLM): def _generate( self, - prompts: List[str], - stop: Optional[List[str]] = None, + prompts: list[str], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> LLMResult: # List to hold all results - text_generations: List[str] = [] + text_generations: list[str] = [] pipeline_kwargs = kwargs.get("pipeline_kwargs", {}) skip_prompt = kwargs.get("skip_prompt", False) @@ -347,7 +348,7 @@ class HuggingFacePipeline(BaseLLM): def _stream( self, prompt: str, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> Iterator[GenerationChunk]: diff --git a/libs/partners/huggingface/pyproject.toml b/libs/partners/huggingface/pyproject.toml index 92d267ea1ec..be42f640bc8 100644 --- a/libs/partners/huggingface/pyproject.toml +++ b/libs/partners/huggingface/pyproject.toml @@ -49,8 +49,12 @@ langchain-community = { path = "../../community", editable = true } [tool.mypy] disallow_untyped_defs = "True" +[tool.ruff] +target-version = "py39" + [tool.ruff.lint] -select = ["E", "F", "I", "T201"] +select = ["E", "F", "I", "T201", "UP"] +ignore = [ "UP007", ] [tool.coverage.run] omit = ["tests/*"] diff --git a/libs/partners/huggingface/tests/integration_tests/test_embeddings_standard.py b/libs/partners/huggingface/tests/integration_tests/test_embeddings_standard.py index a878bea8cfa..bd93baf8d29 100644 --- a/libs/partners/huggingface/tests/integration_tests/test_embeddings_standard.py +++ b/libs/partners/huggingface/tests/integration_tests/test_embeddings_standard.py @@ -1,7 +1,5 @@ """Test HuggingFace embeddings.""" -from typing import Type - from langchain_tests.integration_tests import EmbeddingsIntegrationTests from langchain_huggingface.embeddings import ( @@ -12,7 +10,7 @@ from langchain_huggingface.embeddings import ( class TestHuggingFaceEmbeddings(EmbeddingsIntegrationTests): @property - def embeddings_class(self) -> Type[HuggingFaceEmbeddings]: + def embeddings_class(self) -> type[HuggingFaceEmbeddings]: return HuggingFaceEmbeddings @property @@ -22,7 +20,7 @@ class TestHuggingFaceEmbeddings(EmbeddingsIntegrationTests): class TestHuggingFaceEndpointEmbeddings(EmbeddingsIntegrationTests): @property - def embeddings_class(self) -> Type[HuggingFaceEndpointEmbeddings]: + def embeddings_class(self) -> type[HuggingFaceEndpointEmbeddings]: return HuggingFaceEndpointEmbeddings @property diff --git a/libs/partners/huggingface/tests/integration_tests/test_llms.py b/libs/partners/huggingface/tests/integration_tests/test_llms.py index e251c5bdb67..55bd5502c0d 100644 --- a/libs/partners/huggingface/tests/integration_tests/test_llms.py +++ b/libs/partners/huggingface/tests/integration_tests/test_llms.py @@ -1,4 +1,4 @@ -from typing import Generator +from collections.abc import Generator from langchain_huggingface.llms import HuggingFacePipeline diff --git a/libs/partners/huggingface/tests/integration_tests/test_standard.py b/libs/partners/huggingface/tests/integration_tests/test_standard.py index ca2e930c93c..eca81e2f65f 100644 --- a/libs/partners/huggingface/tests/integration_tests/test_standard.py +++ b/libs/partners/huggingface/tests/integration_tests/test_standard.py @@ -1,7 +1,5 @@ """Standard LangChain interface tests""" -from typing import Type - import pytest from langchain_core.language_models import BaseChatModel from langchain_core.tools import BaseTool @@ -12,7 +10,7 @@ from langchain_huggingface import ChatHuggingFace, HuggingFaceEndpoint class TestHuggingFaceEndpoint(ChatModelIntegrationTests): @property - def chat_model_class(self) -> Type[BaseChatModel]: + def chat_model_class(self) -> type[BaseChatModel]: return ChatHuggingFace @property diff --git a/libs/partners/huggingface/tests/unit_tests/test_chat_models.py b/libs/partners/huggingface/tests/unit_tests/test_chat_models.py index 10b67c93e89..00f267b9d70 100644 --- a/libs/partners/huggingface/tests/unit_tests/test_chat_models.py +++ b/libs/partners/huggingface/tests/unit_tests/test_chat_models.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List # type: ignore[import-not-found] +from typing import Any # type: ignore[import-not-found] from unittest.mock import MagicMock, Mock, patch import pytest # type: ignore[import-not-found] @@ -45,7 +45,7 @@ from langchain_huggingface.llms.huggingface_endpoint import ( ], ) def test_convert_message_to_chat_message( - message: BaseMessage, expected: Dict[str, str] + message: BaseMessage, expected: dict[str, str] ) -> None: result = _convert_message_to_chat_message(message) assert result == expected @@ -150,7 +150,7 @@ def test_create_chat_result(chat_hugging_face: Any) -> None: ], ) def test_to_chat_prompt_errors( - chat_hugging_face: Any, messages: List[BaseMessage], expected_error: str + chat_hugging_face: Any, messages: list[BaseMessage], expected_error: str ) -> None: with pytest.raises(ValueError) as e: chat_hugging_face._to_chat_prompt(messages) @@ -194,7 +194,7 @@ def test_to_chat_prompt_valid_messages(chat_hugging_face: Any) -> None: ], ) def test_to_chatml_format( - chat_hugging_face: Any, message: BaseMessage, expected: Dict[str, str] + chat_hugging_face: Any, message: BaseMessage, expected: dict[str, str] ) -> None: result = chat_hugging_face._to_chatml_format(message) assert result == expected @@ -207,7 +207,7 @@ def test_to_chatml_format_with_invalid_type(chat_hugging_face: Any) -> None: assert "Unknown message type:" in str(e.value) -def tool_mock() -> Dict: +def tool_mock() -> dict: return {"function": {"name": "test_tool"}} @@ -232,7 +232,7 @@ def tool_mock() -> Dict: ) def test_bind_tools_errors( chat_hugging_face: Any, - tools: Dict[str, str], + tools: dict[str, str], tool_choice: Any, expected_exception: Any, expected_message: str, diff --git a/libs/partners/mistralai/langchain_mistralai/chat_models.py b/libs/partners/mistralai/langchain_mistralai/chat_models.py index bc761d2bb6c..442364e1c72 100644 --- a/libs/partners/mistralai/langchain_mistralai/chat_models.py +++ b/libs/partners/mistralai/langchain_mistralai/chat_models.py @@ -7,20 +7,14 @@ import os import re import ssl import uuid +from collections.abc import AsyncIterator, Iterator, Sequence +from contextlib import AbstractAsyncContextManager from operator import itemgetter from typing import ( Any, - AsyncContextManager, - AsyncIterator, Callable, - Dict, - Iterator, - List, Literal, Optional, - Sequence, - Tuple, - Type, Union, cast, ) @@ -142,13 +136,13 @@ def _convert_tool_call_id_to_mistral_compatible(tool_call_id: str) -> str: def _convert_mistral_chat_message_to_message( - _message: Dict, + _message: dict, ) -> BaseMessage: role = _message["role"] assert role == "assistant", f"Expected role to be 'assistant', got {role}" content = cast(str, _message["content"]) - additional_kwargs: Dict = {} + additional_kwargs: dict = {} tool_calls = [] invalid_tool_calls = [] if raw_tool_calls := _message.get("tool_calls"): @@ -196,8 +190,8 @@ async def _araise_on_error(response: httpx.Response) -> None: async def _aiter_sse( - event_source_mgr: AsyncContextManager[EventSource], -) -> AsyncIterator[Dict]: + event_source_mgr: AbstractAsyncContextManager[EventSource], +) -> AsyncIterator[dict]: """Iterate over the server-sent events.""" async with event_source_mgr as event_source: await _araise_on_error(event_source.response) @@ -234,7 +228,7 @@ async def acompletion_with_retry( def _convert_chunk_to_message_chunk( - chunk: Dict, default_class: Type[BaseMessageChunk] + chunk: dict, default_class: type[BaseMessageChunk] ) -> BaseMessageChunk: _choice = chunk["choices"][0] _delta = _choice["delta"] @@ -243,7 +237,7 @@ def _convert_chunk_to_message_chunk( if role == "user" or default_class == HumanMessageChunk: return HumanMessageChunk(content=content) elif role == "assistant" or default_class == AIMessageChunk: - additional_kwargs: Dict = {} + additional_kwargs: dict = {} response_metadata = {} if raw_tool_calls := _delta.get("tool_calls"): additional_kwargs["tool_calls"] = raw_tool_calls @@ -295,7 +289,7 @@ def _convert_chunk_to_message_chunk( def _format_tool_call_for_mistral(tool_call: ToolCall) -> dict: """Format Langchain ToolCall to dict expected by Mistral.""" - result: Dict[str, Any] = { + result: dict[str, Any] = { "function": { "name": tool_call["name"], "arguments": json.dumps(tool_call["args"]), @@ -309,7 +303,7 @@ def _format_tool_call_for_mistral(tool_call: ToolCall) -> dict: def _format_invalid_tool_call_for_mistral(invalid_tool_call: InvalidToolCall) -> dict: """Format Langchain InvalidToolCall to dict expected by Mistral.""" - result: Dict[str, Any] = { + result: dict[str, Any] = { "function": { "name": invalid_tool_call["name"], "arguments": invalid_tool_call["args"], @@ -323,13 +317,13 @@ def _format_invalid_tool_call_for_mistral(invalid_tool_call: InvalidToolCall) -> def _convert_message_to_mistral_chat_message( message: BaseMessage, -) -> Dict: +) -> dict: if isinstance(message, ChatMessage): return dict(role=message.role, content=message.content) elif isinstance(message, HumanMessage): return dict(role="user", content=message.content) elif isinstance(message, AIMessage): - message_dict: Dict[str, Any] = {"role": "assistant"} + message_dict: dict[str, Any] = {"role": "assistant"} tool_calls = [] if message.tool_calls or message.invalid_tool_calls: for tool_call in message.tool_calls: @@ -407,7 +401,7 @@ class ChatMistralAI(BaseChatModel): random_seed: Optional[int] = None safe_mode: Optional[bool] = None streaming: bool = False - model_kwargs: Dict[str, Any] = Field(default_factory=dict) + model_kwargs: dict[str, Any] = Field(default_factory=dict) """Holds any invocation parameters not explicitly specified.""" model_config = ConfigDict( @@ -417,14 +411,14 @@ class ChatMistralAI(BaseChatModel): @model_validator(mode="before") @classmethod - def build_extra(cls, values: Dict[str, Any]) -> Any: + def build_extra(cls, values: dict[str, Any]) -> Any: """Build extra kwargs from additional params that were passed in.""" all_required_field_names = get_pydantic_field_names(cls) values = _build_model_kwargs(values, all_required_field_names) return values @property - def _default_params(self) -> Dict[str, Any]: + def _default_params(self) -> dict[str, Any]: """Get the default parameters for calling the API.""" defaults = { "model": self.model, @@ -439,7 +433,7 @@ class ChatMistralAI(BaseChatModel): return filtered def _get_ls_params( - self, stop: Optional[List[str]] = None, **kwargs: Any + self, stop: Optional[list[str]] = None, **kwargs: Any ) -> LangSmithParams: """Get standard params for tracing.""" params = self._get_invocation_params(stop=stop, **kwargs) @@ -456,7 +450,7 @@ class ChatMistralAI(BaseChatModel): return ls_params @property - def _client_params(self) -> Dict[str, Any]: + def _client_params(self) -> dict[str, Any]: """Get the parameters used for the client.""" return self._default_params @@ -473,7 +467,7 @@ class ChatMistralAI(BaseChatModel): stream = kwargs["stream"] if stream: - def iter_sse() -> Iterator[Dict]: + def iter_sse() -> Iterator[dict]: with connect_sse( self.client, "POST", "/chat/completions", json=kwargs ) as event_source: @@ -492,7 +486,7 @@ class ChatMistralAI(BaseChatModel): rtn = _completion_with_retry(**kwargs) return rtn - def _combine_llm_outputs(self, llm_outputs: List[Optional[dict]]) -> dict: + def _combine_llm_outputs(self, llm_outputs: list[Optional[dict]]) -> dict: overall_token_usage: dict = {} for output in llm_outputs: if output is None: @@ -557,8 +551,8 @@ class ChatMistralAI(BaseChatModel): def _generate( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, stream: Optional[bool] = None, **kwargs: Any, @@ -577,7 +571,7 @@ class ChatMistralAI(BaseChatModel): ) return self._create_chat_result(response) - def _create_chat_result(self, response: Dict) -> ChatResult: + def _create_chat_result(self, response: dict) -> ChatResult: generations = [] token_usage = response.get("usage", {}) for res in response["choices"]: @@ -603,8 +597,8 @@ class ChatMistralAI(BaseChatModel): return ChatResult(generations=generations, llm_output=llm_output) def _create_message_dicts( - self, messages: List[BaseMessage], stop: Optional[List[str]] - ) -> Tuple[List[Dict], Dict[str, Any]]: + self, messages: list[BaseMessage], stop: Optional[list[str]] + ) -> tuple[list[dict], dict[str, Any]]: params = self._client_params if stop is not None or "stop" in params: if "stop" in params: @@ -617,15 +611,15 @@ class ChatMistralAI(BaseChatModel): def _stream( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> Iterator[ChatGenerationChunk]: message_dicts, params = self._create_message_dicts(messages, stop) params = {**params, **kwargs, "stream": True} - default_chunk_class: Type[BaseMessageChunk] = AIMessageChunk + default_chunk_class: type[BaseMessageChunk] = AIMessageChunk for chunk in self.completion_with_retry( messages=message_dicts, run_manager=run_manager, **params ): @@ -643,15 +637,15 @@ class ChatMistralAI(BaseChatModel): async def _astream( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, **kwargs: Any, ) -> AsyncIterator[ChatGenerationChunk]: message_dicts, params = self._create_message_dicts(messages, stop) params = {**params, **kwargs, "stream": True} - default_chunk_class: Type[BaseMessageChunk] = AIMessageChunk + default_chunk_class: type[BaseMessageChunk] = AIMessageChunk async for chunk in await acompletion_with_retry( self, messages=message_dicts, run_manager=run_manager, **params ): @@ -669,8 +663,8 @@ class ChatMistralAI(BaseChatModel): async def _agenerate( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, stream: Optional[bool] = None, **kwargs: Any, @@ -691,7 +685,7 @@ class ChatMistralAI(BaseChatModel): def bind_tools( self, - tools: Sequence[Union[Dict[str, Any], Type, Callable, BaseTool]], + tools: Sequence[Union[dict[str, Any], type, Callable, BaseTool]], tool_choice: Optional[Union[dict, str, Literal["auto", "any"]]] = None, **kwargs: Any, ) -> Runnable[LanguageModelInput, BaseMessage]: @@ -733,14 +727,14 @@ class ChatMistralAI(BaseChatModel): def with_structured_output( self, - schema: Optional[Union[Dict, Type]] = None, + schema: Optional[Union[dict, type]] = None, *, method: Literal[ "function_calling", "json_mode", "json_schema" ] = "function_calling", include_raw: bool = False, **kwargs: Any, - ) -> Runnable[LanguageModelInput, Union[Dict, BaseModel]]: + ) -> Runnable[LanguageModelInput, Union[dict, BaseModel]]: """Model wrapper that returns outputs formatted to match the given schema. Args: @@ -1048,7 +1042,7 @@ class ChatMistralAI(BaseChatModel): return llm | output_parser @property - def _identifying_params(self) -> Dict[str, Any]: + def _identifying_params(self) -> dict[str, Any]: """Get the identifying parameters.""" return self._default_params @@ -1058,7 +1052,7 @@ class ChatMistralAI(BaseChatModel): return "mistralai-chat" @property - def lc_secrets(self) -> Dict[str, str]: + def lc_secrets(self) -> dict[str, str]: return {"mistral_api_key": "MISTRAL_API_KEY"} @classmethod @@ -1067,14 +1061,14 @@ class ChatMistralAI(BaseChatModel): return True @classmethod - def get_lc_namespace(cls) -> List[str]: + def get_lc_namespace(cls) -> list[str]: """Get the namespace of the langchain object.""" return ["langchain", "chat_models", "mistralai"] def _convert_to_openai_response_format( - schema: Union[Dict[str, Any], Type], *, strict: Optional[bool] = None -) -> Dict: + schema: Union[dict[str, Any], type], *, strict: Optional[bool] = None +) -> dict: """Same as in ChatOpenAI, but don't pass through Pydantic BaseModels.""" if ( isinstance(schema, dict) @@ -1094,8 +1088,10 @@ def _convert_to_openai_response_format( function["schema"] = function.pop("parameters") response_format = {"type": "json_schema", "json_schema": function} - if strict is not None and strict is not response_format["json_schema"].get( - "strict" + if ( + strict is not None + and strict is not response_format["json_schema"].get("strict") + and isinstance(schema, dict) ): msg = ( f"Output schema already has 'strict' value set to " diff --git a/libs/partners/mistralai/langchain_mistralai/embeddings.py b/libs/partners/mistralai/langchain_mistralai/embeddings.py index 85e4d4289b7..72f692a0aed 100644 --- a/libs/partners/mistralai/langchain_mistralai/embeddings.py +++ b/libs/partners/mistralai/langchain_mistralai/embeddings.py @@ -1,7 +1,7 @@ import asyncio import logging import warnings -from typing import Iterable, List +from collections.abc import Iterable import httpx from httpx import Response @@ -33,7 +33,7 @@ class DummyTokenizer: """Dummy tokenizer for when tokenizer cannot be accessed (e.g., via Huggingface)""" @staticmethod - def encode_batch(texts: List[str]) -> List[List[str]]: + def encode_batch(texts: list[str]) -> list[list[str]]: return [list(text) for text in texts] @@ -177,7 +177,7 @@ class MistralAIEmbeddings(BaseModel, Embeddings): self.tokenizer = Tokenizer.from_pretrained( "mistralai/Mixtral-8x7B-v0.1" ) - except IOError: # huggingface_hub GatedRepoError + except OSError: # huggingface_hub GatedRepoError warnings.warn( "Could not download mistral tokenizer from Huggingface for " "calculating batch sizes. Set a Huggingface token via the " @@ -187,10 +187,10 @@ class MistralAIEmbeddings(BaseModel, Embeddings): self.tokenizer = DummyTokenizer() return self - def _get_batches(self, texts: List[str]) -> Iterable[List[str]]: + def _get_batches(self, texts: list[str]) -> Iterable[list[str]]: """Split a list of texts into batches of less than 16k tokens for Mistral API.""" - batch: List[str] = [] + batch: list[str] = [] batch_tokens = 0 text_token_lengths = [ @@ -211,7 +211,7 @@ class MistralAIEmbeddings(BaseModel, Embeddings): if batch: yield batch - def embed_documents(self, texts: List[str]) -> List[List[float]]: + def embed_documents(self, texts: list[str]) -> list[list[float]]: """Embed a list of document texts. Args: @@ -230,7 +230,7 @@ class MistralAIEmbeddings(BaseModel, Embeddings): wait=wait_fixed(self.wait_time), stop=stop_after_attempt(self.max_retries), ) - def _embed_batch(batch: List[str]) -> Response: + def _embed_batch(batch: list[str]) -> Response: response = self.client.post( url="/embeddings", json=dict( @@ -252,7 +252,7 @@ class MistralAIEmbeddings(BaseModel, Embeddings): logger.error(f"An error occurred with MistralAI: {e}") raise - async def aembed_documents(self, texts: List[str]) -> List[List[float]]: + async def aembed_documents(self, texts: list[str]) -> list[list[float]]: """Embed a list of document texts. Args: @@ -283,7 +283,7 @@ class MistralAIEmbeddings(BaseModel, Embeddings): logger.error(f"An error occurred with MistralAI: {e}") raise - def embed_query(self, text: str) -> List[float]: + def embed_query(self, text: str) -> list[float]: """Embed a single query text. Args: @@ -294,7 +294,7 @@ class MistralAIEmbeddings(BaseModel, Embeddings): """ return self.embed_documents([text])[0] - async def aembed_query(self, text: str) -> List[float]: + async def aembed_query(self, text: str) -> list[float]: """Embed a single query text. Args: diff --git a/libs/partners/mistralai/pyproject.toml b/libs/partners/mistralai/pyproject.toml index e9aa41d2bc4..f5b1d2efc5e 100644 --- a/libs/partners/mistralai/pyproject.toml +++ b/libs/partners/mistralai/pyproject.toml @@ -44,8 +44,12 @@ langchain-tests = { path = "../../standard-tests", editable = true } [tool.mypy] disallow_untyped_defs = "True" +[tool.ruff] +target-version = "py39" + [tool.ruff.lint] -select = ["E", "F", "I", "T201"] +select = ["E", "F", "I", "T201", "UP"] +ignore = [ "UP007", ] [tool.coverage.run] omit = ["tests/*"] diff --git a/libs/partners/mistralai/tests/integration_tests/test_standard.py b/libs/partners/mistralai/tests/integration_tests/test_standard.py index 24ca4f2a071..af51d28f98d 100644 --- a/libs/partners/mistralai/tests/integration_tests/test_standard.py +++ b/libs/partners/mistralai/tests/integration_tests/test_standard.py @@ -1,7 +1,5 @@ """Standard LangChain interface tests""" -from typing import Type - from langchain_core.language_models import BaseChatModel from langchain_tests.integration_tests import ( # type: ignore[import-not-found] ChatModelIntegrationTests, # type: ignore[import-not-found] @@ -12,7 +10,7 @@ from langchain_mistralai import ChatMistralAI class TestMistralStandard(ChatModelIntegrationTests): @property - def chat_model_class(self) -> Type[BaseChatModel]: + def chat_model_class(self) -> type[BaseChatModel]: return ChatMistralAI @property diff --git a/libs/partners/mistralai/tests/unit_tests/test_chat_models.py b/libs/partners/mistralai/tests/unit_tests/test_chat_models.py index 6a94f431cfd..22dd3fa3e72 100644 --- a/libs/partners/mistralai/tests/unit_tests/test_chat_models.py +++ b/libs/partners/mistralai/tests/unit_tests/test_chat_models.py @@ -1,7 +1,8 @@ """Test MistralAI Chat API wrapper.""" import os -from typing import Any, AsyncGenerator, Dict, Generator, List, cast +from collections.abc import AsyncGenerator, Generator +from typing import Any, cast from unittest.mock import MagicMock, patch import httpx @@ -104,13 +105,13 @@ def test_mistralai_initialization_baseurl_env(env_var_name: str) -> None: ], ) def test_convert_message_to_mistral_chat_message( - message: BaseMessage, expected: Dict + message: BaseMessage, expected: dict ) -> None: result = _convert_message_to_mistral_chat_message(message) assert result == expected -def _make_completion_response_from_token(token: str) -> Dict: +def _make_completion_response_from_token(token: str) -> dict: return dict( id="abc123", model="fake_model", @@ -236,7 +237,7 @@ def test__convert_dict_to_message_tool_call() -> None: def test_custom_token_counting() -> None: - def token_encoder(text: str) -> List[int]: + def token_encoder(text: str) -> list[int]: return [1, 2, 3] llm = ChatMistralAI(custom_get_token_ids=token_encoder) diff --git a/libs/partners/mistralai/tests/unit_tests/test_standard.py b/libs/partners/mistralai/tests/unit_tests/test_standard.py index fde82de2d50..4ba75d610ba 100644 --- a/libs/partners/mistralai/tests/unit_tests/test_standard.py +++ b/libs/partners/mistralai/tests/unit_tests/test_standard.py @@ -1,7 +1,5 @@ """Standard LangChain interface tests""" -from typing import Type - from langchain_core.language_models import BaseChatModel from langchain_tests.unit_tests import ( # type: ignore[import-not-found] ChatModelUnitTests, # type: ignore[import-not-found] @@ -12,5 +10,5 @@ from langchain_mistralai import ChatMistralAI class TestMistralStandard(ChatModelUnitTests): @property - def chat_model_class(self) -> Type[BaseChatModel]: + def chat_model_class(self) -> type[BaseChatModel]: return ChatMistralAI diff --git a/libs/partners/nomic/langchain_nomic/embeddings.py b/libs/partners/nomic/langchain_nomic/embeddings.py index 1dd2fa60fc2..6b0b3210115 100644 --- a/libs/partners/nomic/langchain_nomic/embeddings.py +++ b/libs/partners/nomic/langchain_nomic/embeddings.py @@ -1,5 +1,5 @@ import os -from typing import List, Literal, Optional, overload +from typing import Literal, Optional, overload import nomic # type: ignore[import] from langchain_core.embeddings import Embeddings @@ -86,7 +86,7 @@ class NomicEmbeddings(Embeddings): self.device = device self.vision_model = vision_model - def embed(self, texts: List[str], *, task_type: str) -> List[List[float]]: + def embed(self, texts: list[str], *, task_type: str) -> list[list[float]]: """Embed texts. Args: @@ -105,7 +105,7 @@ class NomicEmbeddings(Embeddings): ) return output["embeddings"] - def embed_documents(self, texts: List[str]) -> List[List[float]]: + def embed_documents(self, texts: list[str]) -> list[list[float]]: """Embed search docs. Args: @@ -116,7 +116,7 @@ class NomicEmbeddings(Embeddings): task_type="search_document", ) - def embed_query(self, text: str) -> List[float]: + def embed_query(self, text: str) -> list[float]: """Embed query text. Args: @@ -127,7 +127,7 @@ class NomicEmbeddings(Embeddings): task_type="search_query", )[0] - def embed_image(self, uris: List[str]) -> List[List[float]]: + def embed_image(self, uris: list[str]) -> list[list[float]]: return embed.image( images=uris, model=self.vision_model, diff --git a/libs/partners/nomic/pyproject.toml b/libs/partners/nomic/pyproject.toml index 72e00e1b9d3..d3e1f67df0d 100644 --- a/libs/partners/nomic/pyproject.toml +++ b/libs/partners/nomic/pyproject.toml @@ -40,13 +40,18 @@ dev = ["langchain-core"] [tool.uv.sources] langchain-core = { path = "../../core", editable = true } +[tool.ruff] +target-version = "py39" + [tool.ruff.lint] select = [ "E", # pycodestyle "F", # pyflakes "I", # isort "T201", # print + "UP", # pyupgrade ] +ignore = [ "UP007", ] [tool.mypy] disallow_untyped_defs = "True" diff --git a/libs/partners/ollama/langchain_ollama/__init__.py b/libs/partners/ollama/langchain_ollama/__init__.py index add2685a5b1..5edbd2236be 100644 --- a/libs/partners/ollama/langchain_ollama/__init__.py +++ b/libs/partners/ollama/langchain_ollama/__init__.py @@ -3,7 +3,6 @@ It provides infrastructure for interacting with the Ollama service. """ - from importlib import metadata from langchain_ollama.chat_models import ChatOllama diff --git a/libs/partners/ollama/langchain_ollama/chat_models.py b/libs/partners/ollama/langchain_ollama/chat_models.py index a163ce82f80..a0b47bb5b33 100644 --- a/libs/partners/ollama/langchain_ollama/chat_models.py +++ b/libs/partners/ollama/langchain_ollama/chat_models.py @@ -1,21 +1,14 @@ """Ollama chat models.""" import json +from collections.abc import AsyncIterator, Iterator, Mapping, Sequence from operator import itemgetter from typing import ( Any, - AsyncIterator, Callable, - Dict, Final, - Iterator, - List, Literal, - Mapping, Optional, - Sequence, - Tuple, - Type, Union, cast, ) @@ -153,7 +146,7 @@ def _parse_arguments_from_tool_call( def _get_tool_calls_from_response( response: Mapping[str, Any], -) -> List[ToolCall]: +) -> list[ToolCall]: """Get tool calls from ollama response.""" tool_calls = [] if "message" in response: @@ -341,7 +334,7 @@ class ChatOllama(BaseChatModel): model: str """Model name to use.""" - extract_reasoning: Optional[Union[bool, Tuple[str, str]]] = False + extract_reasoning: Optional[Union[bool, tuple[str, str]]] = False """Whether to extract the reasoning tokens in think blocks. Extracts `chunk.content` to `chunk.additional_kwargs.reasoning_content`. If a tuple is supplied, they are assumed to be the (start, end) tokens. @@ -399,7 +392,7 @@ class ChatOllama(BaseChatModel): to a specific number will make the model generate the same text for the same prompt.""" - stop: Optional[List[str]] = None + stop: Optional[list[str]] = None """Sets the stop tokens to use.""" tfs_z: Optional[float] = None @@ -443,10 +436,10 @@ class ChatOllama(BaseChatModel): def _chat_params( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, **kwargs: Any, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: ollama_messages = self._convert_messages_to_ollama_messages(messages) if self.stop is not None and stop is not None: @@ -499,13 +492,13 @@ class ChatOllama(BaseChatModel): return self def _convert_messages_to_ollama_messages( - self, messages: List[BaseMessage] + self, messages: list[BaseMessage] ) -> Sequence[Message]: - ollama_messages: List = [] + ollama_messages: list = [] for message in messages: role: Literal["user", "assistant", "system", "tool"] tool_call_id: Optional[str] = None - tool_calls: Optional[List[Dict[str, Any]]] = None + tool_calls: Optional[list[dict[str, Any]]] = None if isinstance(message, HumanMessage): role = "user" elif isinstance(message, AIMessage): @@ -531,7 +524,7 @@ class ChatOllama(BaseChatModel): if isinstance(message.content, str): content = message.content else: - for content_part in cast(List[Dict], message.content): + for content_part in cast(list[dict], message.content): if content_part.get("type") == "text": content += f"\n{content_part['text']}" elif content_part.get("type") == "tool_use": @@ -583,7 +576,7 @@ class ChatOllama(BaseChatModel): def _extract_reasoning( self, message_chunk: BaseMessageChunk, is_thinking: bool - ) -> Tuple[BaseMessageChunk, bool]: + ) -> tuple[BaseMessageChunk, bool]: """Mutate a message chunk to extract reasoning content.""" if not self.extract_reasoning: return message_chunk, is_thinking @@ -605,8 +598,8 @@ class ChatOllama(BaseChatModel): async def _acreate_chat_stream( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, **kwargs: Any, ) -> AsyncIterator[Union[Mapping[str, Any], str]]: chat_params = self._chat_params(messages, stop, **kwargs) @@ -619,8 +612,8 @@ class ChatOllama(BaseChatModel): def _create_chat_stream( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, **kwargs: Any, ) -> Iterator[Union[Mapping[str, Any], str]]: chat_params = self._chat_params(messages, stop, **kwargs) @@ -632,8 +625,8 @@ class ChatOllama(BaseChatModel): def _chat_stream_with_aggregation( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, verbose: bool = False, **kwargs: Any, @@ -657,8 +650,8 @@ class ChatOllama(BaseChatModel): async def _achat_stream_with_aggregation( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, verbose: bool = False, **kwargs: Any, @@ -681,7 +674,7 @@ class ChatOllama(BaseChatModel): return final_chunk def _get_ls_params( - self, stop: Optional[List[str]] = None, **kwargs: Any + self, stop: Optional[list[str]] = None, **kwargs: Any ) -> LangSmithParams: """Get standard params for tracing.""" params = self._get_invocation_params(stop=stop, **kwargs) @@ -697,8 +690,8 @@ class ChatOllama(BaseChatModel): def _generate( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> ChatResult: @@ -719,8 +712,8 @@ class ChatOllama(BaseChatModel): def _iterate_over_stream( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, **kwargs: Any, ) -> Iterator[ChatGenerationChunk]: is_thinking = False @@ -758,8 +751,8 @@ class ChatOllama(BaseChatModel): def _stream( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> Iterator[ChatGenerationChunk]: @@ -773,8 +766,8 @@ class ChatOllama(BaseChatModel): async def _aiterate_over_stream( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, **kwargs: Any, ) -> AsyncIterator[ChatGenerationChunk]: is_thinking = False @@ -812,8 +805,8 @@ class ChatOllama(BaseChatModel): async def _astream( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, **kwargs: Any, ) -> AsyncIterator[ChatGenerationChunk]: @@ -827,8 +820,8 @@ class ChatOllama(BaseChatModel): async def _agenerate( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, **kwargs: Any, ) -> ChatResult: @@ -854,7 +847,7 @@ class ChatOllama(BaseChatModel): def bind_tools( self, - tools: Sequence[Union[Dict[str, Any], Type, Callable, BaseTool]], + tools: Sequence[Union[dict[str, Any], type, Callable, BaseTool]], *, tool_choice: Optional[Union[dict, str, Literal["auto", "any"], bool]] = None, **kwargs: Any, @@ -877,12 +870,12 @@ class ChatOllama(BaseChatModel): def with_structured_output( self, - schema: Union[Dict, type], + schema: Union[dict, type], *, method: Literal["function_calling", "json_mode", "json_schema"] = "json_schema", include_raw: bool = False, **kwargs: Any, - ) -> Runnable[LanguageModelInput, Union[Dict, BaseModel]]: + ) -> Runnable[LanguageModelInput, Union[dict, BaseModel]]: """Model wrapper that returns outputs formatted to match the given schema. Args: diff --git a/libs/partners/ollama/langchain_ollama/embeddings.py b/libs/partners/ollama/langchain_ollama/embeddings.py index 35b2e095ac6..c6327343d48 100644 --- a/libs/partners/ollama/langchain_ollama/embeddings.py +++ b/libs/partners/ollama/langchain_ollama/embeddings.py @@ -1,6 +1,6 @@ """Ollama embeddings models.""" -from typing import Any, Dict, List, Optional +from typing import Any, Optional from langchain_core.embeddings import Embeddings from ollama import AsyncClient, Client @@ -188,7 +188,7 @@ class OllamaEmbeddings(BaseModel, Embeddings): """The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)""" - stop: Optional[List[str]] = None + stop: Optional[list[str]] = None """Sets the stop tokens to use.""" tfs_z: Optional[float] = None @@ -211,7 +211,7 @@ class OllamaEmbeddings(BaseModel, Embeddings): ) @property - def _default_params(self) -> Dict[str, Any]: + def _default_params(self) -> dict[str, Any]: """Get the default parameters for calling Ollama.""" return { "mirostat": self.mirostat, @@ -237,18 +237,18 @@ class OllamaEmbeddings(BaseModel, Embeddings): self._async_client = AsyncClient(host=self.base_url, **client_kwargs) return self - def embed_documents(self, texts: List[str]) -> List[List[float]]: + def embed_documents(self, texts: list[str]) -> list[list[float]]: """Embed search docs.""" embedded_docs = self._client.embed( self.model, texts, options=self._default_params, keep_alive=self.keep_alive )["embeddings"] return embedded_docs - def embed_query(self, text: str) -> List[float]: + def embed_query(self, text: str) -> list[float]: """Embed query text.""" return self.embed_documents([text])[0] - async def aembed_documents(self, texts: List[str]) -> List[List[float]]: + async def aembed_documents(self, texts: list[str]) -> list[list[float]]: """Embed search docs.""" embedded_docs = ( await self._async_client.embed( @@ -257,6 +257,6 @@ class OllamaEmbeddings(BaseModel, Embeddings): )["embeddings"] return embedded_docs - async def aembed_query(self, text: str) -> List[float]: + async def aembed_query(self, text: str) -> list[float]: """Embed query text.""" return (await self.aembed_documents([text]))[0] diff --git a/libs/partners/ollama/langchain_ollama/llms.py b/libs/partners/ollama/langchain_ollama/llms.py index 24708e88b64..5298fec0e3c 100644 --- a/libs/partners/ollama/langchain_ollama/llms.py +++ b/libs/partners/ollama/langchain_ollama/llms.py @@ -1,13 +1,9 @@ """Ollama large language models.""" +from collections.abc import AsyncIterator, Iterator, Mapping from typing import ( Any, - AsyncIterator, - Dict, - Iterator, - List, Literal, - Mapping, Optional, Union, ) @@ -89,7 +85,7 @@ class OllamaLLM(BaseLLM): to a specific number will make the model generate the same text for the same prompt.""" - stop: Optional[List[str]] = None + stop: Optional[list[str]] = None """Sets the stop tokens to use.""" tfs_z: Optional[float] = None @@ -134,9 +130,9 @@ class OllamaLLM(BaseLLM): def _generate_params( self, prompt: str, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, **kwargs: Any, - ) -> Dict[str, Any]: + ) -> dict[str, Any]: if self.stop is not None and stop is not None: raise ValueError("`stop` found in both the input and default params.") elif self.stop is not None: @@ -181,7 +177,7 @@ class OllamaLLM(BaseLLM): return "ollama-llm" def _get_ls_params( - self, stop: Optional[List[str]] = None, **kwargs: Any + self, stop: Optional[list[str]] = None, **kwargs: Any ) -> LangSmithParams: """Get standard params for tracing.""" params = super()._get_ls_params(stop=stop, **kwargs) @@ -200,7 +196,7 @@ class OllamaLLM(BaseLLM): async def _acreate_generate_stream( self, prompt: str, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, **kwargs: Any, ) -> AsyncIterator[Union[Mapping[str, Any], str]]: async for part in await self._async_client.generate( @@ -211,7 +207,7 @@ class OllamaLLM(BaseLLM): def _create_generate_stream( self, prompt: str, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, **kwargs: Any, ) -> Iterator[Union[Mapping[str, Any], str]]: yield from self._client.generate( @@ -221,7 +217,7 @@ class OllamaLLM(BaseLLM): async def _astream_with_aggregation( self, prompt: str, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, verbose: bool = False, **kwargs: Any, @@ -253,7 +249,7 @@ class OllamaLLM(BaseLLM): def _stream_with_aggregation( self, prompt: str, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, verbose: bool = False, **kwargs: Any, @@ -284,8 +280,8 @@ class OllamaLLM(BaseLLM): def _generate( self, - prompts: List[str], - stop: Optional[List[str]] = None, + prompts: list[str], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> LLMResult: @@ -303,8 +299,8 @@ class OllamaLLM(BaseLLM): async def _agenerate( self, - prompts: List[str], - stop: Optional[List[str]] = None, + prompts: list[str], + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, **kwargs: Any, ) -> LLMResult: @@ -323,7 +319,7 @@ class OllamaLLM(BaseLLM): def _stream( self, prompt: str, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> Iterator[GenerationChunk]: @@ -345,7 +341,7 @@ class OllamaLLM(BaseLLM): async def _astream( self, prompt: str, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, **kwargs: Any, ) -> AsyncIterator[GenerationChunk]: diff --git a/libs/partners/ollama/pyproject.toml b/libs/partners/ollama/pyproject.toml index a3f32aba500..6d9a54af8ad 100644 --- a/libs/partners/ollama/pyproject.toml +++ b/libs/partners/ollama/pyproject.toml @@ -40,6 +40,9 @@ langchain-tests = { path = "../../standard-tests", editable = true } [tool.mypy] disallow_untyped_defs = "True" +[tool.ruff] +target-version = "py39" + [tool.ruff.lint] select = [ "E", # pycodestyle @@ -47,8 +50,9 @@ select = [ "I", # isort "T201", # print "D", # pydocstyle - + "UP", # pyupgrade ] +ignore = [ "UP007", ] [tool.ruff.lint.pydocstyle] convention = "google" diff --git a/libs/partners/ollama/scripts/check_imports.py b/libs/partners/ollama/scripts/check_imports.py index acf6128ab95..64b1b383092 100644 --- a/libs/partners/ollama/scripts/check_imports.py +++ b/libs/partners/ollama/scripts/check_imports.py @@ -1,4 +1,5 @@ """load multiple Python files specified as command line arguments.""" + import sys import traceback from importlib.machinery import SourceFileLoader diff --git a/libs/partners/ollama/tests/integration_tests/chat_models/test_chat_models.py b/libs/partners/ollama/tests/integration_tests/chat_models/test_chat_models.py index 61190f73f80..4ec3f91b779 100644 --- a/libs/partners/ollama/tests/integration_tests/chat_models/test_chat_models.py +++ b/libs/partners/ollama/tests/integration_tests/chat_models/test_chat_models.py @@ -1,10 +1,10 @@ """Ollama specific chat model integration tests""" -from typing import List, Optional +from typing import Annotated, Optional import pytest from pydantic import BaseModel, Field -from typing_extensions import Annotated, TypedDict +from typing_extensions import TypedDict from langchain_ollama import ChatOllama @@ -78,7 +78,7 @@ def test_structured_output_deeply_nested(model: str) -> None: class Data(BaseModel): """Extracted data about people.""" - people: List[Person] + people: list[Person] chat = llm.with_structured_output(Data) # type: ignore[arg-type] text = ( diff --git a/libs/partners/ollama/tests/integration_tests/chat_models/test_chat_models_standard.py b/libs/partners/ollama/tests/integration_tests/chat_models/test_chat_models_standard.py index cccda2cc186..341af1e3847 100644 --- a/libs/partners/ollama/tests/integration_tests/chat_models/test_chat_models_standard.py +++ b/libs/partners/ollama/tests/integration_tests/chat_models/test_chat_models_standard.py @@ -1,7 +1,5 @@ """Test chat model integration using standard integration tests.""" -from typing import Type - from langchain_tests.integration_tests import ChatModelIntegrationTests from langchain_ollama.chat_models import ChatOllama @@ -9,7 +7,7 @@ from langchain_ollama.chat_models import ChatOllama class TestChatOllama(ChatModelIntegrationTests): @property - def chat_model_class(self) -> Type[ChatOllama]: + def chat_model_class(self) -> type[ChatOllama]: return ChatOllama @property diff --git a/libs/partners/ollama/tests/integration_tests/test_embeddings.py b/libs/partners/ollama/tests/integration_tests/test_embeddings.py index 0953510c901..5d30bb212fa 100644 --- a/libs/partners/ollama/tests/integration_tests/test_embeddings.py +++ b/libs/partners/ollama/tests/integration_tests/test_embeddings.py @@ -1,7 +1,5 @@ """Test Ollama embeddings.""" -from typing import Type - from langchain_tests.integration_tests import EmbeddingsIntegrationTests from langchain_ollama.embeddings import OllamaEmbeddings @@ -9,7 +7,7 @@ from langchain_ollama.embeddings import OllamaEmbeddings class TestOllamaEmbeddings(EmbeddingsIntegrationTests): @property - def embeddings_class(self) -> Type[OllamaEmbeddings]: + def embeddings_class(self) -> type[OllamaEmbeddings]: return OllamaEmbeddings @property diff --git a/libs/partners/ollama/tests/unit_tests/test_chat_models.py b/libs/partners/ollama/tests/unit_tests/test_chat_models.py index 01abf25c460..95d32b180c9 100644 --- a/libs/partners/ollama/tests/unit_tests/test_chat_models.py +++ b/libs/partners/ollama/tests/unit_tests/test_chat_models.py @@ -1,6 +1,6 @@ """Test chat model integration.""" + import json -from typing import Dict, Type from langchain_tests.unit_tests import ChatModelUnitTests @@ -9,11 +9,11 @@ from langchain_ollama.chat_models import ChatOllama, _parse_arguments_from_tool_ class TestChatOllama(ChatModelUnitTests): @property - def chat_model_class(self) -> Type[ChatOllama]: + def chat_model_class(self) -> type[ChatOllama]: return ChatOllama @property - def chat_model_params(self) -> Dict: + def chat_model_params(self) -> dict: return {"model": "llama3-groq-tool-use"} diff --git a/libs/partners/openai/langchain_openai/chat_models/azure.py b/libs/partners/openai/langchain_openai/chat_models/azure.py index 04c5e4e5c1d..58170a6d147 100644 --- a/libs/partners/openai/langchain_openai/chat_models/azure.py +++ b/libs/partners/openai/langchain_openai/chat_models/azure.py @@ -4,18 +4,8 @@ from __future__ import annotations import logging import os -from typing import ( - Any, - Awaitable, - Callable, - Dict, - List, - Optional, - Type, - TypedDict, - TypeVar, - Union, -) +from collections.abc import Awaitable +from typing import Any, Callable, Optional, TypedDict, TypeVar, Union import openai from langchain_core.language_models import LanguageModelInput @@ -34,8 +24,8 @@ logger = logging.getLogger(__name__) _BM = TypeVar("_BM", bound=BaseModel) -_DictOrPydanticClass = Union[Dict[str, Any], Type[_BM]] -_DictOrPydantic = Union[Dict, _BM] +_DictOrPydanticClass = Union[dict[str, Any], type[_BM]] +_DictOrPydantic = Union[dict, _BM] class _AllReturnType(TypedDict): @@ -547,7 +537,7 @@ class AzureChatOpenAI(BaseChatOpenAI): Used for tracing and token counting. Does NOT affect completion. """ - disabled_params: Optional[Dict[str, Any]] = Field(default=None) + disabled_params: Optional[dict[str, Any]] = Field(default=None) """Parameters of the OpenAI client or chat.completions endpoint that should be disabled for the given model. @@ -570,12 +560,12 @@ class AzureChatOpenAI(BaseChatOpenAI): """ @classmethod - def get_lc_namespace(cls) -> List[str]: + def get_lc_namespace(cls) -> list[str]: """Get the namespace of the langchain object.""" return ["langchain", "chat_models", "azure_openai"] @property - def lc_secrets(self) -> Dict[str, str]: + def lc_secrets(self) -> dict[str, str]: return { "openai_api_key": "AZURE_OPENAI_API_KEY", "azure_ad_token": "AZURE_OPENAI_AD_TOKEN", @@ -672,7 +662,7 @@ class AzureChatOpenAI(BaseChatOpenAI): return self @property - def _identifying_params(self) -> Dict[str, Any]: + def _identifying_params(self) -> dict[str, Any]: """Get the identifying parameters.""" return { **{"azure_deployment": self.deployment_name}, @@ -684,14 +674,14 @@ class AzureChatOpenAI(BaseChatOpenAI): return "azure-openai-chat" @property - def lc_attributes(self) -> Dict[str, Any]: + def lc_attributes(self) -> dict[str, Any]: return { "openai_api_type": self.openai_api_type, "openai_api_version": self.openai_api_version, } def _get_ls_params( - self, stop: Optional[List[str]] = None, **kwargs: Any + self, stop: Optional[list[str]] = None, **kwargs: Any ) -> LangSmithParams: """Get the parameters used to invoke the model.""" params = super()._get_ls_params(stop=stop, **kwargs) @@ -710,7 +700,7 @@ class AzureChatOpenAI(BaseChatOpenAI): def _create_chat_result( self, response: Union[dict, openai.BaseModel], - generation_info: Optional[Dict] = None, + generation_info: Optional[dict] = None, ) -> ChatResult: chat_result = super()._create_chat_result(response, generation_info) diff --git a/libs/partners/openai/langchain_openai/chat_models/base.py b/libs/partners/openai/langchain_openai/chat_models/base.py index 8eb36f2bbd8..c3d5fa4bc04 100644 --- a/libs/partners/openai/langchain_openai/chat_models/base.py +++ b/libs/partners/openai/langchain_openai/chat_models/base.py @@ -10,6 +10,7 @@ import re import ssl import sys import warnings +from collections.abc import AsyncIterator, Iterator, Mapping, Sequence from functools import partial from io import BytesIO from json import JSONDecodeError @@ -18,17 +19,9 @@ from operator import itemgetter from typing import ( TYPE_CHECKING, Any, - AsyncIterator, Callable, - Dict, - Iterator, - List, Literal, - Mapping, Optional, - Sequence, - Tuple, - Type, TypedDict, TypeVar, Union, @@ -137,7 +130,7 @@ def _convert_dict_to_message(_dict: Mapping[str, Any]) -> BaseMessage: # Fix for azure # Also OpenAI returns None for tool invocations content = _dict.get("content", "") or "" - additional_kwargs: Dict = {} + additional_kwargs: dict = {} if function_call := _dict.get("function_call"): additional_kwargs["function_call"] = dict(function_call) tool_calls = [] @@ -243,7 +236,7 @@ def _convert_message_to_dict(message: BaseMessage) -> dict: Returns: The dictionary. """ - message_dict: Dict[str, Any] = {"content": _format_message_content(message.content)} + message_dict: dict[str, Any] = {"content": _format_message_content(message.content)} if (name := message.name or message.additional_kwargs.get("name")) is not None: message_dict["name"] = name @@ -304,12 +297,12 @@ def _convert_message_to_dict(message: BaseMessage) -> dict: def _convert_delta_to_message_chunk( - _dict: Mapping[str, Any], default_class: Type[BaseMessageChunk] + _dict: Mapping[str, Any], default_class: type[BaseMessageChunk] ) -> BaseMessageChunk: id_ = _dict.get("id") role = cast(str, _dict.get("role")) content = cast(str, _dict.get("content") or "") - additional_kwargs: Dict = {} + additional_kwargs: dict = {} if _dict.get("function_call"): function_call = dict(_dict["function_call"]) if "name" in function_call and function_call["name"] is None: @@ -418,8 +411,8 @@ class _FunctionCall(TypedDict): _BM = TypeVar("_BM", bound=BaseModel) -_DictOrPydanticClass = Union[Dict[str, Any], Type[_BM], Type] -_DictOrPydantic = Union[Dict, _BM] +_DictOrPydanticClass = Union[dict[str, Any], type[_BM], type] +_DictOrPydantic = Union[dict, _BM] class _AllReturnType(TypedDict): @@ -437,7 +430,7 @@ class BaseChatOpenAI(BaseChatModel): """Model name to use.""" temperature: Optional[float] = None """What sampling temperature to use.""" - model_kwargs: Dict[str, Any] = Field(default_factory=dict) + model_kwargs: dict[str, Any] = Field(default_factory=dict) """Holds any model parameters valid for `create` call not explicitly specified.""" openai_api_key: Optional[SecretStr] = Field( alias="api_key", default_factory=secret_from_env("OPENAI_API_KEY", default=None) @@ -451,7 +444,7 @@ class BaseChatOpenAI(BaseChatModel): openai_proxy: Optional[str] = Field( default_factory=from_env("OPENAI_PROXY", default=None) ) - request_timeout: Union[float, Tuple[float, float], Any, None] = Field( + request_timeout: Union[float, tuple[float, float], Any, None] = Field( default=None, alias="timeout" ) """Timeout for requests to OpenAI completion API. Can be float, httpx.Timeout or @@ -476,7 +469,7 @@ class BaseChatOpenAI(BaseChatModel): """Number of most likely tokens to return at each token position, each with an associated log probability. `logprobs` must be set to true if this parameter is used.""" - logit_bias: Optional[Dict[int, int]] = None + logit_bias: Optional[dict[int, int]] = None """Modify the likelihood of specified tokens appearing in the completion.""" streaming: bool = False """Whether to stream the results or not.""" @@ -517,14 +510,14 @@ class BaseChatOpenAI(BaseChatModel): http_async_client: Union[Any, None] = Field(default=None, exclude=True) """Optional httpx.AsyncClient. Only used for async invocations. Must specify http_client as well if you'd like a custom client for sync invocations.""" - stop: Optional[Union[List[str], str]] = Field(default=None, alias="stop_sequences") + stop: Optional[Union[list[str], str]] = Field(default=None, alias="stop_sequences") """Default stop sequences.""" extra_body: Optional[Mapping[str, Any]] = None """Optional additional JSON properties to include in the request parameters when making requests to OpenAI compatible APIs, such as vLLM.""" include_response_headers: bool = False """Whether to include response headers in the output message response_metadata.""" - disabled_params: Optional[Dict[str, Any]] = Field(default=None) + disabled_params: Optional[dict[str, Any]] = Field(default=None) """Parameters of the OpenAI client or chat.completions endpoint that should be disabled for the given model. @@ -554,7 +547,7 @@ class BaseChatOpenAI(BaseChatModel): @model_validator(mode="before") @classmethod - def build_extra(cls, values: Dict[str, Any]) -> Any: + def build_extra(cls, values: dict[str, Any]) -> Any: """Build extra kwargs from additional params that were passed in.""" all_required_field_names = get_pydantic_field_names(cls) values = _build_model_kwargs(values, all_required_field_names) @@ -562,7 +555,7 @@ class BaseChatOpenAI(BaseChatModel): @model_validator(mode="before") @classmethod - def validate_temperature(cls, values: Dict[str, Any]) -> Any: + def validate_temperature(cls, values: dict[str, Any]) -> Any: """Currently o1 models only allow temperature=1.""" model = values.get("model_name") or values.get("model") or "" if model.startswith("o1") and "temperature" not in values: @@ -642,7 +635,7 @@ class BaseChatOpenAI(BaseChatModel): return self @property - def _default_params(self) -> Dict[str, Any]: + def _default_params(self) -> dict[str, Any]: """Get the default parameters for calling OpenAI API.""" exclude_if_none = { "presence_penalty": self.presence_penalty, @@ -669,7 +662,7 @@ class BaseChatOpenAI(BaseChatModel): return params - def _combine_llm_outputs(self, llm_outputs: List[Optional[dict]]) -> dict: + def _combine_llm_outputs(self, llm_outputs: list[Optional[dict]]) -> dict: overall_token_usage: dict = {} system_fingerprint = None for output in llm_outputs: @@ -697,8 +690,8 @@ class BaseChatOpenAI(BaseChatModel): def _convert_chunk_to_generation_chunk( self, chunk: dict, - default_chunk_class: Type, - base_generation_info: Optional[Dict], + default_chunk_class: type, + base_generation_info: Optional[dict], ) -> Optional[ChatGenerationChunk]: if chunk.get("type") == "content.delta": # from beta.chat.completions.stream return None @@ -749,8 +742,8 @@ class BaseChatOpenAI(BaseChatModel): def _stream_responses( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> Iterator[ChatGenerationChunk]: @@ -783,8 +776,8 @@ class BaseChatOpenAI(BaseChatModel): async def _astream_responses( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, **kwargs: Any, ) -> AsyncIterator[ChatGenerationChunk]: @@ -838,8 +831,8 @@ class BaseChatOpenAI(BaseChatModel): def _stream( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, *, stream_usage: Optional[bool] = None, @@ -850,7 +843,7 @@ class BaseChatOpenAI(BaseChatModel): if stream_usage: kwargs["stream_options"] = {"include_usage": stream_usage} payload = self._get_request_payload(messages, stop=stop, **kwargs) - default_chunk_class: Type[BaseMessageChunk] = AIMessageChunk + default_chunk_class: type[BaseMessageChunk] = AIMessageChunk base_generation_info = {} if "response_format" in payload: @@ -908,8 +901,8 @@ class BaseChatOpenAI(BaseChatModel): def _generate( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> ChatResult: @@ -965,7 +958,7 @@ class BaseChatOpenAI(BaseChatModel): self, input_: LanguageModelInput, *, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, **kwargs: Any, ) -> dict: messages = self._convert_input(input_).to_messages() @@ -982,7 +975,7 @@ class BaseChatOpenAI(BaseChatModel): def _create_chat_result( self, response: Union[dict, openai.BaseModel], - generation_info: Optional[Dict] = None, + generation_info: Optional[dict] = None, ) -> ChatResult: generations = [] @@ -1032,8 +1025,8 @@ class BaseChatOpenAI(BaseChatModel): async def _astream( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, *, stream_usage: Optional[bool] = None, @@ -1044,7 +1037,7 @@ class BaseChatOpenAI(BaseChatModel): if stream_usage: kwargs["stream_options"] = {"include_usage": stream_usage} payload = self._get_request_payload(messages, stop=stop, **kwargs) - default_chunk_class: Type[BaseMessageChunk] = AIMessageChunk + default_chunk_class: type[BaseMessageChunk] = AIMessageChunk base_generation_info = {} if "response_format" in payload: @@ -1106,8 +1099,8 @@ class BaseChatOpenAI(BaseChatModel): async def _agenerate( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, **kwargs: Any, ) -> ChatResult: @@ -1160,13 +1153,13 @@ class BaseChatOpenAI(BaseChatModel): ) @property - def _identifying_params(self) -> Dict[str, Any]: + def _identifying_params(self) -> dict[str, Any]: """Get the identifying parameters.""" return {"model_name": self.model_name, **self._default_params} def _get_invocation_params( - self, stop: Optional[List[str]] = None, **kwargs: Any - ) -> Dict[str, Any]: + self, stop: Optional[list[str]] = None, **kwargs: Any + ) -> dict[str, Any]: """Get the parameters used to invoke the model.""" return { "model": self.model_name, @@ -1176,7 +1169,7 @@ class BaseChatOpenAI(BaseChatModel): } def _get_ls_params( - self, stop: Optional[List[str]] = None, **kwargs: Any + self, stop: Optional[list[str]] = None, **kwargs: Any ) -> LangSmithParams: """Get standard params for tracing.""" params = self._get_invocation_params(stop=stop, **kwargs) @@ -1199,7 +1192,7 @@ class BaseChatOpenAI(BaseChatModel): """Return type of chat model.""" return "openai-chat" - def _get_encoding_model(self) -> Tuple[str, tiktoken.Encoding]: + def _get_encoding_model(self) -> tuple[str, tiktoken.Encoding]: if self.tiktoken_model_name is not None: model = self.tiktoken_model_name else: @@ -1211,7 +1204,7 @@ class BaseChatOpenAI(BaseChatModel): encoding = tiktoken.get_encoding(model) return model, encoding - def get_token_ids(self, text: str) -> List[int]: + def get_token_ids(self, text: str) -> list[int]: """Get the tokens present in the text with tiktoken package.""" if self.custom_get_token_ids is not None: return self.custom_get_token_ids(text) @@ -1223,9 +1216,9 @@ class BaseChatOpenAI(BaseChatModel): def get_num_tokens_from_messages( self, - messages: List[BaseMessage], + messages: list[BaseMessage], tools: Optional[ - Sequence[Union[Dict[str, Any], Type, Callable, BaseTool]] + Sequence[Union[dict[str, Any], type, Callable, BaseTool]] ] = None, ) -> int: """Calculate num tokens for gpt-3.5-turbo and gpt-4 with tiktoken package. @@ -1327,7 +1320,7 @@ class BaseChatOpenAI(BaseChatModel): ) def bind_functions( self, - functions: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]], + functions: Sequence[Union[dict[str, Any], type[BaseModel], Callable, BaseTool]], function_call: Optional[ Union[_FunctionCall, str, Literal["auto", "none"]] ] = None, @@ -1380,7 +1373,7 @@ class BaseChatOpenAI(BaseChatModel): def bind_tools( self, - tools: Sequence[Union[Dict[str, Any], Type, Callable, BaseTool]], + tools: Sequence[Union[dict[str, Any], type, Callable, BaseTool]], *, tool_choice: Optional[ Union[dict, str, Literal["auto", "none", "required", "any"], bool] @@ -1727,7 +1720,7 @@ class BaseChatOpenAI(BaseChatModel): else: return llm | output_parser - def _filter_disabled_params(self, **kwargs: Any) -> Dict[str, Any]: + def _filter_disabled_params(self, **kwargs: Any) -> dict[str, Any]: if not self.disabled_params: return kwargs filtered = {} @@ -2301,17 +2294,17 @@ class ChatOpenAI(BaseChatOpenAI): # type: ignore[override] """Maximum number of tokens to generate.""" @property - def lc_secrets(self) -> Dict[str, str]: + def lc_secrets(self) -> dict[str, str]: return {"openai_api_key": "OPENAI_API_KEY"} @classmethod - def get_lc_namespace(cls) -> List[str]: + def get_lc_namespace(cls) -> list[str]: """Get the namespace of the langchain object.""" return ["langchain", "chat_models", "openai"] @property - def lc_attributes(self) -> Dict[str, Any]: - attributes: Dict[str, Any] = {} + def lc_attributes(self) -> dict[str, Any]: + attributes: dict[str, Any] = {} if self.openai_organization: attributes["openai_organization"] = self.openai_organization @@ -2330,7 +2323,7 @@ class ChatOpenAI(BaseChatOpenAI): # type: ignore[override] return True @property - def _default_params(self) -> Dict[str, Any]: + def _default_params(self) -> dict[str, Any]: """Get the default parameters for calling OpenAI API.""" params = super()._default_params if "max_tokens" in params: @@ -2342,7 +2335,7 @@ class ChatOpenAI(BaseChatOpenAI): # type: ignore[override] self, input_: LanguageModelInput, *, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, **kwargs: Any, ) -> dict: payload = super()._get_request_payload(input_, stop=stop, **kwargs) @@ -2719,7 +2712,7 @@ def _lc_invalid_tool_call_to_openai_tool_call( } -def _url_to_size(image_source: str) -> Optional[Tuple[int, int]]: +def _url_to_size(image_source: str) -> Optional[tuple[int, int]]: try: from PIL import Image # type: ignore[import] except ImportError: @@ -2771,7 +2764,7 @@ def _is_b64(s: str) -> bool: return s.startswith("data:image") -def _resize(width: int, height: int) -> Tuple[int, int]: +def _resize(width: int, height: int) -> tuple[int, int]: # larger side must be <= 2048 if width > 2048 or height > 2048: if width > height: @@ -2792,8 +2785,8 @@ def _resize(width: int, height: int) -> Tuple[int, int]: def _convert_to_openai_response_format( - schema: Union[Dict[str, Any], Type], *, strict: Optional[bool] = None -) -> Union[Dict, TypeBaseModel]: + schema: Union[dict[str, Any], type], *, strict: Optional[bool] = None +) -> Union[dict, TypeBaseModel]: if isinstance(schema, type) and is_basemodel_subclass(schema): return schema @@ -2815,8 +2808,10 @@ def _convert_to_openai_response_format( function["schema"] = function.pop("parameters") response_format = {"type": "json_schema", "json_schema": function} - if strict is not None and strict is not response_format["json_schema"].get( - "strict" + if ( + strict is not None + and strict is not response_format["json_schema"].get("strict") + and isinstance(schema, dict) ): msg = ( f"Output schema already has 'strict' value set to " @@ -2829,7 +2824,7 @@ def _convert_to_openai_response_format( def _oai_structured_outputs_parser( - ai_msg: AIMessage, schema: Type[_BM] + ai_msg: AIMessage, schema: type[_BM] ) -> Optional[PydanticBaseModel]: if parsed := ai_msg.additional_kwargs.get("parsed"): if isinstance(parsed, dict): @@ -3141,7 +3136,7 @@ def _construct_responses_api_input(messages: Sequence[BaseMessage]) -> list: def _construct_lc_result_from_responses_api( response: Response, - schema: Optional[Type[_BM]] = None, + schema: Optional[type[_BM]] = None, metadata: Optional[dict] = None, ) -> ChatResult: """Construct ChatResponse from OpenAI Response API response.""" @@ -3278,7 +3273,7 @@ def _construct_lc_result_from_responses_api( def _convert_responses_chunk_to_generation_chunk( - chunk: Any, schema: Optional[Type[_BM]] = None, metadata: Optional[dict] = None + chunk: Any, schema: Optional[type[_BM]] = None, metadata: Optional[dict] = None ) -> Optional[ChatGenerationChunk]: content = [] tool_call_chunks: list = [] diff --git a/libs/partners/openai/langchain_openai/embeddings/azure.py b/libs/partners/openai/langchain_openai/embeddings/azure.py index decc33e9639..9be5c7937ab 100644 --- a/libs/partners/openai/langchain_openai/embeddings/azure.py +++ b/libs/partners/openai/langchain_openai/embeddings/azure.py @@ -2,12 +2,13 @@ from __future__ import annotations -from typing import Awaitable, Callable, Optional, Union +from collections.abc import Awaitable +from typing import Callable, Optional, Union, cast import openai from langchain_core.utils import from_env, secret_from_env from pydantic import Field, SecretStr, model_validator -from typing_extensions import Self, cast +from typing_extensions import Self from langchain_openai.embeddings.base import OpenAIEmbeddings diff --git a/libs/partners/openai/langchain_openai/embeddings/base.py b/libs/partners/openai/langchain_openai/embeddings/base.py index 4e2f7c74cfb..e3d03f942b8 100644 --- a/libs/partners/openai/langchain_openai/embeddings/base.py +++ b/libs/partners/openai/langchain_openai/embeddings/base.py @@ -2,20 +2,8 @@ from __future__ import annotations import logging import warnings -from typing import ( - Any, - Dict, - Iterable, - List, - Literal, - Mapping, - Optional, - Sequence, - Set, - Tuple, - Union, - cast, -) +from collections.abc import Iterable, Mapping, Sequence +from typing import Any, Literal, Optional, Union, cast import openai import tiktoken @@ -29,19 +17,19 @@ logger = logging.getLogger(__name__) def _process_batched_chunked_embeddings( num_texts: int, - tokens: List[Union[List[int], str]], - batched_embeddings: List[List[float]], - indices: List[int], + tokens: list[Union[list[int], str]], + batched_embeddings: list[list[float]], + indices: list[int], skip_empty: bool, -) -> List[Optional[List[float]]]: +) -> list[Optional[list[float]]]: # for each text, this is the list of embeddings (list of list of floats) # corresponding to the chunks of the text - results: List[List[List[float]]] = [[] for _ in range(num_texts)] + results: list[list[list[float]]] = [[] for _ in range(num_texts)] # for each text, this is the token length of each chunk # for transformers tokenization, this is the string length # for tiktoken, this is the number of tokens - num_tokens_in_batch: List[List[int]] = [[] for _ in range(num_texts)] + num_tokens_in_batch: list[list[int]] = [[] for _ in range(num_texts)] for i in range(len(indices)): if skip_empty and len(batched_embeddings[i]) == 1: @@ -50,10 +38,10 @@ def _process_batched_chunked_embeddings( num_tokens_in_batch[indices[i]].append(len(tokens[i])) # for each text, this is the final embedding - embeddings: List[Optional[List[float]]] = [] + embeddings: list[Optional[list[float]]] = [] for i in range(num_texts): # an embedding for each chunk - _result: List[List[float]] = results[i] + _result: list[list[float]] = results[i] if len(_result) == 0: # this will be populated with the embedding of an empty string @@ -213,13 +201,13 @@ class OpenAIEmbeddings(BaseModel, Embeddings): ), ) """Automatically inferred from env var `OPENAI_ORG_ID` if not provided.""" - allowed_special: Union[Literal["all"], Set[str], None] = None - disallowed_special: Union[Literal["all"], Set[str], Sequence[str], None] = None + allowed_special: Union[Literal["all"], set[str], None] = None + disallowed_special: Union[Literal["all"], set[str], Sequence[str], None] = None chunk_size: int = 1000 """Maximum number of texts to embed in each batch""" max_retries: int = 2 """Maximum number of retries to make when generating.""" - request_timeout: Optional[Union[float, Tuple[float, float], Any]] = Field( + request_timeout: Optional[Union[float, tuple[float, float], Any]] = Field( default=None, alias="timeout" ) """Timeout for requests to OpenAI completion API. Can be float, httpx.Timeout or @@ -240,7 +228,7 @@ class OpenAIEmbeddings(BaseModel, Embeddings): when tiktoken is called, you can specify a model name to use here.""" show_progress_bar: bool = False """Whether to show a progress bar when embedding.""" - model_kwargs: Dict[str, Any] = Field(default_factory=dict) + model_kwargs: dict[str, Any] = Field(default_factory=dict) """Holds any model parameters valid for `create` call not explicitly specified.""" skip_empty: bool = False """Whether to skip empty strings when embedding or raise an error. @@ -270,7 +258,7 @@ class OpenAIEmbeddings(BaseModel, Embeddings): @model_validator(mode="before") @classmethod - def build_extra(cls, values: Dict[str, Any]) -> Any: + def build_extra(cls, values: dict[str, Any]) -> Any: """Build extra kwargs from additional params that were passed in.""" all_required_field_names = get_pydantic_field_names(cls) extra = values.get("model_kwargs", {}) @@ -354,15 +342,15 @@ class OpenAIEmbeddings(BaseModel, Embeddings): return self @property - def _invocation_params(self) -> Dict[str, Any]: - params: Dict = {"model": self.model, **self.model_kwargs} + def _invocation_params(self) -> dict[str, Any]: + params: dict = {"model": self.model, **self.model_kwargs} if self.dimensions is not None: params["dimensions"] = self.dimensions return params def _tokenize( - self, texts: List[str], chunk_size: int - ) -> Tuple[Iterable[int], List[Union[List[int], str]], List[int]]: + self, texts: list[str], chunk_size: int + ) -> tuple[Iterable[int], list[Union[list[int], str]], list[int]]: """ Take the input `texts` and `chunk_size` and return 3 iterables as a tuple: @@ -383,8 +371,8 @@ class OpenAIEmbeddings(BaseModel, Embeddings): indices: An iterable of the same length as `tokens` that maps each token-array to the index of the original text in `texts`. """ - tokens: List[Union[List[int], str]] = [] - indices: List[int] = [] + tokens: list[Union[list[int], str]] = [] + indices: list[int] = [] model_name = self.tiktoken_model_name or self.model # If tiktoken flag set to False @@ -403,11 +391,11 @@ class OpenAIEmbeddings(BaseModel, Embeddings): ) for i, text in enumerate(texts): # Tokenize the text using HuggingFace transformers - tokenized: List[int] = tokenizer.encode(text, add_special_tokens=False) + tokenized: list[int] = tokenizer.encode(text, add_special_tokens=False) # Split tokens into chunks respecting the embedding_ctx_length for j in range(0, len(tokenized), self.embedding_ctx_length): - token_chunk: List[int] = tokenized[ + token_chunk: list[int] = tokenized[ j : j + self.embedding_ctx_length ] @@ -420,7 +408,7 @@ class OpenAIEmbeddings(BaseModel, Embeddings): encoding = tiktoken.encoding_for_model(model_name) except KeyError: encoding = tiktoken.get_encoding("cl100k_base") - encoder_kwargs: Dict[str, Any] = { + encoder_kwargs: dict[str, Any] = { k: v for k, v in { "allowed_special": self.allowed_special, @@ -459,8 +447,8 @@ class OpenAIEmbeddings(BaseModel, Embeddings): # please refer to # https://github.com/openai/openai-cookbook/blob/main/examples/Embedding_long_inputs.ipynb def _get_len_safe_embeddings( - self, texts: List[str], *, engine: str, chunk_size: Optional[int] = None - ) -> List[List[float]]: + self, texts: list[str], *, engine: str, chunk_size: Optional[int] = None + ) -> list[list[float]]: """ Generate length-safe embeddings for a list of texts. @@ -478,7 +466,7 @@ class OpenAIEmbeddings(BaseModel, Embeddings): """ _chunk_size = chunk_size or self.chunk_size _iter, tokens, indices = self._tokenize(texts, _chunk_size) - batched_embeddings: List[List[float]] = [] + batched_embeddings: list[list[float]] = [] for i in _iter: response = self.client.create( input=tokens[i : i + _chunk_size], **self._invocation_params @@ -490,9 +478,9 @@ class OpenAIEmbeddings(BaseModel, Embeddings): embeddings = _process_batched_chunked_embeddings( len(texts), tokens, batched_embeddings, indices, self.skip_empty ) - _cached_empty_embedding: Optional[List[float]] = None + _cached_empty_embedding: Optional[list[float]] = None - def empty_embedding() -> List[float]: + def empty_embedding() -> list[float]: nonlocal _cached_empty_embedding if _cached_empty_embedding is None: average_embedded = self.client.create( @@ -508,8 +496,8 @@ class OpenAIEmbeddings(BaseModel, Embeddings): # please refer to # https://github.com/openai/openai-cookbook/blob/main/examples/Embedding_long_inputs.ipynb async def _aget_len_safe_embeddings( - self, texts: List[str], *, engine: str, chunk_size: Optional[int] = None - ) -> List[List[float]]: + self, texts: list[str], *, engine: str, chunk_size: Optional[int] = None + ) -> list[list[float]]: """ Asynchronously generate length-safe embeddings for a list of texts. @@ -528,7 +516,7 @@ class OpenAIEmbeddings(BaseModel, Embeddings): _chunk_size = chunk_size or self.chunk_size _iter, tokens, indices = self._tokenize(texts, _chunk_size) - batched_embeddings: List[List[float]] = [] + batched_embeddings: list[list[float]] = [] _chunk_size = chunk_size or self.chunk_size for i in range(0, len(tokens), _chunk_size): response = await self.async_client.create( @@ -542,9 +530,9 @@ class OpenAIEmbeddings(BaseModel, Embeddings): embeddings = _process_batched_chunked_embeddings( len(texts), tokens, batched_embeddings, indices, self.skip_empty ) - _cached_empty_embedding: Optional[List[float]] = None + _cached_empty_embedding: Optional[list[float]] = None - async def empty_embedding() -> List[float]: + async def empty_embedding() -> list[float]: nonlocal _cached_empty_embedding if _cached_empty_embedding is None: average_embedded = await self.async_client.create( @@ -558,8 +546,8 @@ class OpenAIEmbeddings(BaseModel, Embeddings): return [e if e is not None else await empty_embedding() for e in embeddings] def embed_documents( - self, texts: List[str], chunk_size: int | None = None - ) -> List[List[float]]: + self, texts: list[str], chunk_size: int | None = None + ) -> list[list[float]]: """Call out to OpenAI's embedding endpoint for embedding search docs. Args: @@ -572,7 +560,7 @@ class OpenAIEmbeddings(BaseModel, Embeddings): """ chunk_size_ = chunk_size or self.chunk_size if not self.check_embedding_ctx_length: - embeddings: List[List[float]] = [] + embeddings: list[list[float]] = [] for i in range(0, len(texts), chunk_size_): response = self.client.create( input=texts[i : i + chunk_size_], **self._invocation_params @@ -588,8 +576,8 @@ class OpenAIEmbeddings(BaseModel, Embeddings): return self._get_len_safe_embeddings(texts, engine=engine) async def aembed_documents( - self, texts: List[str], chunk_size: int | None = None - ) -> List[List[float]]: + self, texts: list[str], chunk_size: int | None = None + ) -> list[list[float]]: """Call out to OpenAI's embedding endpoint async for embedding search docs. Args: @@ -602,7 +590,7 @@ class OpenAIEmbeddings(BaseModel, Embeddings): """ chunk_size_ = chunk_size or self.chunk_size if not self.check_embedding_ctx_length: - embeddings: List[List[float]] = [] + embeddings: list[list[float]] = [] for i in range(0, len(texts), chunk_size_): response = await self.async_client.create( input=texts[i : i + chunk_size_], **self._invocation_params @@ -617,7 +605,7 @@ class OpenAIEmbeddings(BaseModel, Embeddings): engine = cast(str, self.deployment) return await self._aget_len_safe_embeddings(texts, engine=engine) - def embed_query(self, text: str) -> List[float]: + def embed_query(self, text: str) -> list[float]: """Call out to OpenAI's embedding endpoint for embedding query text. Args: @@ -628,7 +616,7 @@ class OpenAIEmbeddings(BaseModel, Embeddings): """ return self.embed_documents([text])[0] - async def aembed_query(self, text: str) -> List[float]: + async def aembed_query(self, text: str) -> list[float]: """Call out to OpenAI's embedding endpoint async for embedding query text. Args: diff --git a/libs/partners/openai/langchain_openai/llms/azure.py b/libs/partners/openai/langchain_openai/llms/azure.py index 05e90d0b975..7b51d79576f 100644 --- a/libs/partners/openai/langchain_openai/llms/azure.py +++ b/libs/partners/openai/langchain_openai/llms/azure.py @@ -1,13 +1,14 @@ from __future__ import annotations import logging -from typing import Any, Awaitable, Callable, Dict, List, Mapping, Optional, Union +from collections.abc import Awaitable, Mapping +from typing import Any, Callable, Optional, Union, cast import openai from langchain_core.language_models import LangSmithParams from langchain_core.utils import from_env, secret_from_env from pydantic import Field, SecretStr, model_validator -from typing_extensions import Self, cast +from typing_extensions import Self from langchain_openai.llms.base import BaseOpenAI @@ -91,12 +92,12 @@ class AzureOpenAI(BaseOpenAI): """ @classmethod - def get_lc_namespace(cls) -> List[str]: + def get_lc_namespace(cls) -> list[str]: """Get the namespace of the langchain object.""" return ["langchain", "llms", "openai"] @property - def lc_secrets(self) -> Dict[str, str]: + def lc_secrets(self) -> dict[str, str]: return { "openai_api_key": "AZURE_OPENAI_API_KEY", "azure_ad_token": "AZURE_OPENAI_AD_TOKEN", @@ -188,12 +189,12 @@ class AzureOpenAI(BaseOpenAI): } @property - def _invocation_params(self) -> Dict[str, Any]: + def _invocation_params(self) -> dict[str, Any]: openai_params = {"model": self.deployment_name} return {**openai_params, **super()._invocation_params} def _get_ls_params( - self, stop: Optional[List[str]] = None, **kwargs: Any + self, stop: Optional[list[str]] = None, **kwargs: Any ) -> LangSmithParams: """Get standard params for tracing.""" params = super()._get_ls_params(stop=stop, **kwargs) @@ -209,7 +210,7 @@ class AzureOpenAI(BaseOpenAI): return "azure" @property - def lc_attributes(self) -> Dict[str, Any]: + def lc_attributes(self) -> dict[str, Any]: return { "openai_api_type": self.openai_api_type, "openai_api_version": self.openai_api_version, diff --git a/libs/partners/openai/langchain_openai/llms/base.py b/libs/partners/openai/langchain_openai/llms/base.py index ec23efb84be..4228bfcc9d8 100644 --- a/libs/partners/openai/langchain_openai/llms/base.py +++ b/libs/partners/openai/langchain_openai/llms/base.py @@ -2,21 +2,8 @@ from __future__ import annotations import logging import sys -from typing import ( - AbstractSet, - Any, - AsyncIterator, - Collection, - Dict, - Iterator, - List, - Literal, - Mapping, - Optional, - Set, - Tuple, - Union, -) +from collections.abc import AsyncIterator, Collection, Iterator, Mapping +from typing import Any, Literal, Optional, Union import openai import tiktoken @@ -35,7 +22,7 @@ logger = logging.getLogger(__name__) def _update_token_usage( - keys: Set[str], response: Dict[str, Any], token_usage: Dict[str, Any] + keys: set[str], response: dict[str, Any], token_usage: dict[str, Any] ) -> None: """Update token usage.""" _keys_to_use = keys.intersection(response["usage"]) @@ -47,7 +34,7 @@ def _update_token_usage( def _stream_response_to_generation_chunk( - stream_response: Dict[str, Any], + stream_response: dict[str, Any], ) -> GenerationChunk: """Convert a stream response to a generation chunk.""" if not stream_response["choices"]: @@ -84,7 +71,7 @@ class BaseOpenAI(BaseLLM): """How many completions to generate for each prompt.""" best_of: int = 1 """Generates best_of completions server-side and returns the "best".""" - model_kwargs: Dict[str, Any] = Field(default_factory=dict) + model_kwargs: dict[str, Any] = Field(default_factory=dict) """Holds any model parameters valid for `create` call not explicitly specified.""" openai_api_key: Optional[SecretStr] = Field( alias="api_key", default_factory=secret_from_env("OPENAI_API_KEY", default=None) @@ -108,12 +95,12 @@ class BaseOpenAI(BaseLLM): ) batch_size: int = 20 """Batch size to use when passing multiple documents to generate.""" - request_timeout: Union[float, Tuple[float, float], Any, None] = Field( + request_timeout: Union[float, tuple[float, float], Any, None] = Field( default=None, alias="timeout" ) """Timeout for requests to OpenAI completion API. Can be float, httpx.Timeout or None.""" - logit_bias: Optional[Dict[str, float]] = None + logit_bias: Optional[dict[str, float]] = None """Adjust the probability of specific tokens being generated.""" max_retries: int = 2 """Maximum number of retries to make when generating.""" @@ -124,7 +111,7 @@ class BaseOpenAI(BaseLLM): as well the chosen tokens.""" streaming: bool = False """Whether to stream the results or not.""" - allowed_special: Union[Literal["all"], AbstractSet[str]] = set() + allowed_special: Union[Literal["all"], set[str]] = set() """Set of special tokens that are allowed。""" disallowed_special: Union[Literal["all"], Collection[str]] = "all" """Set of special tokens that are not allowed。""" @@ -157,7 +144,7 @@ class BaseOpenAI(BaseLLM): @model_validator(mode="before") @classmethod - def build_extra(cls, values: Dict[str, Any]) -> Any: + def build_extra(cls, values: dict[str, Any]) -> Any: """Build extra kwargs from additional params that were passed in.""" all_required_field_names = get_pydantic_field_names(cls) values = _build_model_kwargs(values, all_required_field_names) @@ -197,9 +184,9 @@ class BaseOpenAI(BaseLLM): return self @property - def _default_params(self) -> Dict[str, Any]: + def _default_params(self) -> dict[str, Any]: """Get the default parameters for calling OpenAI API.""" - normal_params: Dict[str, Any] = { + normal_params: dict[str, Any] = { "temperature": self.temperature, "top_p": self.top_p, "frequency_penalty": self.frequency_penalty, @@ -228,7 +215,7 @@ class BaseOpenAI(BaseLLM): def _stream( self, prompt: str, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> Iterator[GenerationChunk]: @@ -255,7 +242,7 @@ class BaseOpenAI(BaseLLM): async def _astream( self, prompt: str, - stop: Optional[List[str]] = None, + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, **kwargs: Any, ) -> AsyncIterator[GenerationChunk]: @@ -283,8 +270,8 @@ class BaseOpenAI(BaseLLM): def _generate( self, - prompts: List[str], - stop: Optional[List[str]] = None, + prompts: list[str], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> LLMResult: @@ -307,7 +294,7 @@ class BaseOpenAI(BaseLLM): params = {**params, **kwargs} sub_prompts = self.get_sub_prompts(params, prompts, stop) choices = [] - token_usage: Dict[str, int] = {} + token_usage: dict[str, int] = {} # Get the token usage from the response. # Includes prompt, completion, and total tokens used. _keys = {"completion_tokens", "prompt_tokens", "total_tokens"} @@ -363,8 +350,8 @@ class BaseOpenAI(BaseLLM): async def _agenerate( self, - prompts: List[str], - stop: Optional[List[str]] = None, + prompts: list[str], + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, **kwargs: Any, ) -> LLMResult: @@ -373,7 +360,7 @@ class BaseOpenAI(BaseLLM): params = {**params, **kwargs} sub_prompts = self.get_sub_prompts(params, prompts, stop) choices = [] - token_usage: Dict[str, int] = {} + token_usage: dict[str, int] = {} # Get the token usage from the response. # Includes prompt, completion, and total tokens used. _keys = {"completion_tokens", "prompt_tokens", "total_tokens"} @@ -419,10 +406,10 @@ class BaseOpenAI(BaseLLM): def get_sub_prompts( self, - params: Dict[str, Any], - prompts: List[str], - stop: Optional[List[str]] = None, - ) -> List[List[str]]: + params: dict[str, Any], + prompts: list[str], + stop: Optional[list[str]] = None, + ) -> list[list[str]]: """Get the sub prompts for llm call.""" if stop is not None: params["stop"] = stop @@ -441,9 +428,9 @@ class BaseOpenAI(BaseLLM): def create_llm_result( self, choices: Any, - prompts: List[str], - params: Dict[str, Any], - token_usage: Dict[str, int], + prompts: list[str], + params: dict[str, Any], + token_usage: dict[str, int], *, system_fingerprint: Optional[str] = None, ) -> LLMResult: @@ -470,7 +457,7 @@ class BaseOpenAI(BaseLLM): return LLMResult(generations=generations, llm_output=llm_output) @property - def _invocation_params(self) -> Dict[str, Any]: + def _invocation_params(self) -> dict[str, Any]: """Get the parameters used to invoke the model.""" return self._default_params @@ -484,7 +471,7 @@ class BaseOpenAI(BaseLLM): """Return type of llm.""" return "openai" - def get_token_ids(self, text: str) -> List[int]: + def get_token_ids(self, text: str) -> list[int]: """Get the token IDs using the tiktoken package.""" if self.custom_get_token_ids is not None: return self.custom_get_token_ids(text) @@ -689,7 +676,7 @@ class OpenAI(BaseOpenAI): """ # noqa: E501 @classmethod - def get_lc_namespace(cls) -> List[str]: + def get_lc_namespace(cls) -> list[str]: """Get the namespace of the langchain object.""" return ["langchain", "llms", "openai"] @@ -699,16 +686,16 @@ class OpenAI(BaseOpenAI): return True @property - def _invocation_params(self) -> Dict[str, Any]: + def _invocation_params(self) -> dict[str, Any]: return {**{"model": self.model_name}, **super()._invocation_params} @property - def lc_secrets(self) -> Dict[str, str]: + def lc_secrets(self) -> dict[str, str]: return {"openai_api_key": "OPENAI_API_KEY"} @property - def lc_attributes(self) -> Dict[str, Any]: - attributes: Dict[str, Any] = {} + def lc_attributes(self) -> dict[str, Any]: + attributes: dict[str, Any] = {} if self.openai_api_base: attributes["openai_api_base"] = self.openai_api_base diff --git a/libs/partners/openai/pyproject.toml b/libs/partners/openai/pyproject.toml index 26bb92c126d..d691069ce91 100644 --- a/libs/partners/openai/pyproject.toml +++ b/libs/partners/openai/pyproject.toml @@ -59,8 +59,12 @@ disallow_untyped_defs = "True" module = "transformers" ignore_missing_imports = true +[tool.ruff] +target-version = "py39" + [tool.ruff.lint] -select = ["E", "F", "I", "T201"] +select = ["E", "F", "I", "T201", "UP"] +ignore = [ "UP007", ] [tool.ruff.format] docstring-code-format = true diff --git a/libs/partners/openai/tests/integration_tests/chat_models/test_azure_standard.py b/libs/partners/openai/tests/integration_tests/chat_models/test_azure_standard.py index b87be33b30e..1e7d6f0cdcf 100644 --- a/libs/partners/openai/tests/integration_tests/chat_models/test_azure_standard.py +++ b/libs/partners/openai/tests/integration_tests/chat_models/test_azure_standard.py @@ -1,7 +1,6 @@ """Standard LangChain interface tests""" import os -from typing import Type from langchain_core.language_models import BaseChatModel from langchain_tests.integration_tests import ChatModelIntegrationTests @@ -14,7 +13,7 @@ OPENAI_API_BASE = os.environ.get("AZURE_OPENAI_API_BASE", "") class TestAzureOpenAIStandard(ChatModelIntegrationTests): @property - def chat_model_class(self) -> Type[BaseChatModel]: + def chat_model_class(self) -> type[BaseChatModel]: return AzureChatOpenAI @property @@ -40,7 +39,7 @@ class TestAzureOpenAIStandardLegacy(ChatModelIntegrationTests): """Test a legacy model.""" @property - def chat_model_class(self) -> Type[BaseChatModel]: + def chat_model_class(self) -> type[BaseChatModel]: return AzureChatOpenAI @property diff --git a/libs/partners/openai/tests/integration_tests/chat_models/test_base.py b/libs/partners/openai/tests/integration_tests/chat_models/test_base.py index 8015eda5ab5..369dbd5d2c7 100644 --- a/libs/partners/openai/tests/integration_tests/chat_models/test_base.py +++ b/libs/partners/openai/tests/integration_tests/chat_models/test_base.py @@ -2,9 +2,10 @@ import base64 import json +from collections.abc import AsyncIterator from pathlib import Path from textwrap import dedent -from typing import Any, AsyncIterator, List, Literal, Optional, cast +from typing import Any, Literal, Optional, cast import httpx import openai @@ -531,14 +532,14 @@ class MakeASandwich(BaseModel): bread_type: str cheese_type: str - condiments: List[str] - vegetables: List[str] + condiments: list[str] + vegetables: list[str] def test_tool_use() -> None: llm = ChatOpenAI(model="gpt-4-turbo", temperature=0) llm_with_tool = llm.bind_tools(tools=[GenerateUsername], tool_choice=True) - msgs: List = [HumanMessage("Sally has green hair, what would her username be?")] + msgs: list = [HumanMessage("Sally has green hair, what would her username be?")] ai_msg = llm_with_tool.invoke(msgs) assert isinstance(ai_msg, AIMessage) @@ -583,7 +584,7 @@ def test_manual_tool_call_msg(use_responses_api: bool) -> None: model="gpt-3.5-turbo-0125", temperature=0, use_responses_api=use_responses_api ) llm_with_tool = llm.bind_tools(tools=[GenerateUsername]) - msgs: List = [ + msgs: list = [ HumanMessage("Sally has green hair, what would her username be?"), AIMessage( content="", @@ -1045,7 +1046,7 @@ def test_audio_output_modality() -> None: }, ) - history: List[BaseMessage] = [ + history: list[BaseMessage] = [ HumanMessage("Make me a short audio clip of you yelling") ] diff --git a/libs/partners/openai/tests/integration_tests/chat_models/test_base_standard.py b/libs/partners/openai/tests/integration_tests/chat_models/test_base_standard.py index 53528808c2a..e23bbffa7c5 100644 --- a/libs/partners/openai/tests/integration_tests/chat_models/test_base_standard.py +++ b/libs/partners/openai/tests/integration_tests/chat_models/test_base_standard.py @@ -1,7 +1,7 @@ """Standard LangChain interface tests""" from pathlib import Path -from typing import Dict, List, Literal, Type, cast +from typing import Literal, cast from langchain_core.language_models import BaseChatModel from langchain_core.messages import AIMessage @@ -14,7 +14,7 @@ REPO_ROOT_DIR = Path(__file__).parents[6] class TestOpenAIStandard(ChatModelIntegrationTests): @property - def chat_model_class(self) -> Type[BaseChatModel]: + def chat_model_class(self) -> type[BaseChatModel]: return ChatOpenAI @property @@ -36,9 +36,9 @@ class TestOpenAIStandard(ChatModelIntegrationTests): @property def supported_usage_metadata_details( self, - ) -> Dict[ + ) -> dict[ Literal["invoke", "stream"], - List[ + list[ Literal[ "audio_input", "audio_output", @@ -51,7 +51,7 @@ class TestOpenAIStandard(ChatModelIntegrationTests): return {"invoke": ["reasoning_output", "cache_read_input"], "stream": []} def invoke_with_cache_read_input(self, *, stream: bool = False) -> AIMessage: - with open(REPO_ROOT_DIR / "README.md", "r") as f: + with open(REPO_ROOT_DIR / "README.md") as f: readme = f.read() input_ = f"""What's langchain? Here's the langchain README: diff --git a/libs/partners/openai/tests/integration_tests/chat_models/test_responses_api.py b/libs/partners/openai/tests/integration_tests/chat_models/test_responses_api.py index 5abe11cfe20..7c7b95011e3 100644 --- a/libs/partners/openai/tests/integration_tests/chat_models/test_responses_api.py +++ b/libs/partners/openai/tests/integration_tests/chat_models/test_responses_api.py @@ -2,7 +2,7 @@ import json import os -from typing import Any, Optional, cast +from typing import Annotated, Any, Optional, cast import openai import pytest @@ -13,7 +13,7 @@ from langchain_core.messages import ( BaseMessageChunk, ) from pydantic import BaseModel -from typing_extensions import Annotated, TypedDict +from typing_extensions import TypedDict from langchain_openai import ChatOpenAI diff --git a/libs/partners/openai/tests/integration_tests/chat_models/test_responses_standard.py b/libs/partners/openai/tests/integration_tests/chat_models/test_responses_standard.py index d2cd27f3dbf..4bd6c8128e9 100644 --- a/libs/partners/openai/tests/integration_tests/chat_models/test_responses_standard.py +++ b/libs/partners/openai/tests/integration_tests/chat_models/test_responses_standard.py @@ -1,7 +1,5 @@ """Standard LangChain interface tests for Responses API""" -from typing import Type - import pytest from langchain_core.language_models import BaseChatModel @@ -11,7 +9,7 @@ from tests.integration_tests.chat_models.test_base_standard import TestOpenAISta class TestOpenAIResponses(TestOpenAIStandard): @property - def chat_model_class(self) -> Type[BaseChatModel]: + def chat_model_class(self) -> type[BaseChatModel]: return ChatOpenAI @property diff --git a/libs/partners/openai/tests/integration_tests/embeddings/test_base_standard.py b/libs/partners/openai/tests/integration_tests/embeddings/test_base_standard.py index 66f74f1687f..a7b09cf34e6 100644 --- a/libs/partners/openai/tests/integration_tests/embeddings/test_base_standard.py +++ b/libs/partners/openai/tests/integration_tests/embeddings/test_base_standard.py @@ -1,7 +1,5 @@ """Standard LangChain interface tests""" -from typing import Type - from langchain_core.embeddings import Embeddings from langchain_tests.integration_tests.embeddings import EmbeddingsIntegrationTests @@ -10,7 +8,7 @@ from langchain_openai import OpenAIEmbeddings class TestOpenAIStandard(EmbeddingsIntegrationTests): @property - def embeddings_class(self) -> Type[Embeddings]: + def embeddings_class(self) -> type[Embeddings]: return OpenAIEmbeddings @property diff --git a/libs/partners/openai/tests/integration_tests/llms/test_azure.py b/libs/partners/openai/tests/integration_tests/llms/test_azure.py index fcbc6b3b0aa..3af71e31d55 100644 --- a/libs/partners/openai/tests/integration_tests/llms/test_azure.py +++ b/libs/partners/openai/tests/integration_tests/llms/test_azure.py @@ -1,7 +1,8 @@ """Test AzureOpenAI wrapper.""" import os -from typing import Any, Generator +from collections.abc import Generator +from typing import Any import pytest from langchain_core.callbacks import CallbackManager diff --git a/libs/partners/openai/tests/integration_tests/llms/test_base.py b/libs/partners/openai/tests/integration_tests/llms/test_base.py index c320a1db913..703391a50ac 100644 --- a/libs/partners/openai/tests/integration_tests/llms/test_base.py +++ b/libs/partners/openai/tests/integration_tests/llms/test_base.py @@ -1,6 +1,6 @@ """Test OpenAI llm.""" -from typing import Generator +from collections.abc import Generator import pytest from langchain_core.callbacks import CallbackManager diff --git a/libs/partners/openai/tests/unit_tests/chat_models/test_azure_standard.py b/libs/partners/openai/tests/unit_tests/chat_models/test_azure_standard.py index f74f1c6804f..cdb8289da12 100644 --- a/libs/partners/openai/tests/unit_tests/chat_models/test_azure_standard.py +++ b/libs/partners/openai/tests/unit_tests/chat_models/test_azure_standard.py @@ -1,7 +1,5 @@ """Standard LangChain interface tests""" -from typing import Tuple, Type - import pytest from langchain_core.language_models import BaseChatModel from langchain_core.tools import BaseTool @@ -12,7 +10,7 @@ from langchain_openai import AzureChatOpenAI class TestOpenAIStandard(ChatModelUnitTests): @property - def chat_model_class(self) -> Type[BaseChatModel]: + def chat_model_class(self) -> type[BaseChatModel]: return AzureChatOpenAI @property @@ -30,7 +28,7 @@ class TestOpenAIStandard(ChatModelUnitTests): super().test_bind_tool_pydantic(model, my_adder_tool) @property - def init_from_env_params(self) -> Tuple[dict, dict, dict]: + def init_from_env_params(self) -> tuple[dict, dict, dict]: return ( { "AZURE_OPENAI_API_KEY": "api_key", diff --git a/libs/partners/openai/tests/unit_tests/chat_models/test_base.py b/libs/partners/openai/tests/unit_tests/chat_models/test_base.py index fc4666c10c6..5b8d219d923 100644 --- a/libs/partners/openai/tests/unit_tests/chat_models/test_base.py +++ b/libs/partners/openai/tests/unit_tests/chat_models/test_base.py @@ -3,7 +3,7 @@ import json from functools import partial from types import TracebackType -from typing import Any, Dict, List, Literal, Optional, Type, Union, cast +from typing import Any, Literal, Optional, Union, cast from unittest.mock import AsyncMock, MagicMock, patch import pytest @@ -241,7 +241,7 @@ class MockAsyncContextManager: async def __aexit__( self, - exc_type: Optional[Type[BaseException]], + exc_type: Optional[type[BaseException]], exc: Optional[BaseException], tb: Optional[TracebackType], ) -> None: @@ -270,7 +270,7 @@ class MockSyncContextManager: def __exit__( self, - exc_type: Optional[Type[BaseException]], + exc_type: Optional[type[BaseException]], exc: Optional[BaseException], tb: Optional[TracebackType], ) -> None: @@ -382,7 +382,7 @@ DEEPSEEK_STREAM_DATA = """{"id":"d3610c24e6b42518a7883ea57c3ea2c3","choices":[{" @pytest.fixture -def mock_deepseek_completion() -> List[Dict]: +def mock_deepseek_completion() -> list[dict]: list_chunk_data = DEEPSEEK_STREAM_DATA.split("\n") result_list = [] for msg in list_chunk_data: @@ -450,7 +450,7 @@ OPENAI_STREAM_DATA = """{"id":"chatcmpl-9nhARrdUiJWEMd5plwV1Gc9NCjb9M","object": @pytest.fixture -def mock_openai_completion() -> List[Dict]: +def mock_openai_completion() -> list[dict]: list_chunk_data = OPENAI_STREAM_DATA.split("\n") result_list = [] for msg in list_chunk_data: @@ -615,7 +615,7 @@ def test_openai_invoke_name(mock_client: MagicMock) -> None: def test_custom_token_counting() -> None: - def token_encoder(text: str) -> List[int]: + def token_encoder(text: str) -> list[int]: return [1, 2, 3] llm = ChatOpenAI(custom_get_token_ids=token_encoder) @@ -662,8 +662,8 @@ class MakeASandwich(BaseModel): bread_type: str cheese_type: str - condiments: List[str] - vegetables: List[str] + condiments: list[str] + vegetables: list[str] @pytest.mark.parametrize( @@ -695,7 +695,7 @@ def test_bind_tools_tool_choice(tool_choice: Any, strict: Optional[bool]) -> Non @pytest.mark.parametrize("include_raw", [True, False]) @pytest.mark.parametrize("strict", [True, False, None]) def test_with_structured_output( - schema: Union[Type, Dict[str, Any], None], + schema: Union[type, dict[str, Any], None], method: Literal["function_calling", "json_mode", "json_schema"], include_raw: bool, strict: Optional[bool], @@ -787,7 +787,7 @@ class Foo(BaseModel): # FooV1 ], ) -def test_schema_from_with_structured_output(schema: Type) -> None: +def test_schema_from_with_structured_output(schema: type) -> None: """Test schema from with_structured_output.""" llm = ChatOpenAI(model="gpt-4o") diff --git a/libs/partners/openai/tests/unit_tests/chat_models/test_base_standard.py b/libs/partners/openai/tests/unit_tests/chat_models/test_base_standard.py index d24ddd1e71f..cec64d9f590 100644 --- a/libs/partners/openai/tests/unit_tests/chat_models/test_base_standard.py +++ b/libs/partners/openai/tests/unit_tests/chat_models/test_base_standard.py @@ -1,7 +1,5 @@ """Standard LangChain interface tests""" -from typing import Tuple, Type - from langchain_core.language_models import BaseChatModel from langchain_tests.unit_tests import ChatModelUnitTests @@ -10,11 +8,11 @@ from langchain_openai import ChatOpenAI class TestOpenAIStandard(ChatModelUnitTests): @property - def chat_model_class(self) -> Type[BaseChatModel]: + def chat_model_class(self) -> type[BaseChatModel]: return ChatOpenAI @property - def init_from_env_params(self) -> Tuple[dict, dict, dict]: + def init_from_env_params(self) -> tuple[dict, dict, dict]: return ( { "OPENAI_API_KEY": "api_key", diff --git a/libs/partners/openai/tests/unit_tests/chat_models/test_responses_standard.py b/libs/partners/openai/tests/unit_tests/chat_models/test_responses_standard.py index 2d835e57295..0f460a4d249 100644 --- a/libs/partners/openai/tests/unit_tests/chat_models/test_responses_standard.py +++ b/libs/partners/openai/tests/unit_tests/chat_models/test_responses_standard.py @@ -1,7 +1,5 @@ """Standard LangChain interface tests""" -from typing import Tuple, Type - from langchain_core.language_models import BaseChatModel from langchain_tests.unit_tests import ChatModelUnitTests @@ -10,7 +8,7 @@ from langchain_openai import ChatOpenAI class TestOpenAIResponses(ChatModelUnitTests): @property - def chat_model_class(self) -> Type[BaseChatModel]: + def chat_model_class(self) -> type[BaseChatModel]: return ChatOpenAI @property @@ -18,7 +16,7 @@ class TestOpenAIResponses(ChatModelUnitTests): return {"use_responses_api": True} @property - def init_from_env_params(self) -> Tuple[dict, dict, dict]: + def init_from_env_params(self) -> tuple[dict, dict, dict]: return ( { "OPENAI_API_KEY": "api_key", diff --git a/libs/partners/openai/tests/unit_tests/embeddings/test_azure_standard.py b/libs/partners/openai/tests/unit_tests/embeddings/test_azure_standard.py index a972fe5e385..b7fdeaa8181 100644 --- a/libs/partners/openai/tests/unit_tests/embeddings/test_azure_standard.py +++ b/libs/partners/openai/tests/unit_tests/embeddings/test_azure_standard.py @@ -1,5 +1,3 @@ -from typing import Tuple, Type - from langchain_core.embeddings import Embeddings from langchain_tests.unit_tests.embeddings import EmbeddingsUnitTests @@ -8,7 +6,7 @@ from langchain_openai import AzureOpenAIEmbeddings class TestAzureOpenAIStandard(EmbeddingsUnitTests): @property - def embeddings_class(self) -> Type[Embeddings]: + def embeddings_class(self) -> type[Embeddings]: return AzureOpenAIEmbeddings @property @@ -16,7 +14,7 @@ class TestAzureOpenAIStandard(EmbeddingsUnitTests): return {"api_key": "api_key", "azure_endpoint": "https://endpoint.com"} @property - def init_from_env_params(self) -> Tuple[dict, dict, dict]: + def init_from_env_params(self) -> tuple[dict, dict, dict]: return ( { "AZURE_OPENAI_API_KEY": "api_key", diff --git a/libs/partners/openai/tests/unit_tests/embeddings/test_base_standard.py b/libs/partners/openai/tests/unit_tests/embeddings/test_base_standard.py index b1b2f2d368f..0faabb8bddc 100644 --- a/libs/partners/openai/tests/unit_tests/embeddings/test_base_standard.py +++ b/libs/partners/openai/tests/unit_tests/embeddings/test_base_standard.py @@ -1,7 +1,5 @@ """Standard LangChain interface tests""" -from typing import Tuple, Type - from langchain_core.embeddings import Embeddings from langchain_tests.unit_tests.embeddings import EmbeddingsUnitTests @@ -10,11 +8,11 @@ from langchain_openai import OpenAIEmbeddings class TestOpenAIStandard(EmbeddingsUnitTests): @property - def embeddings_class(self) -> Type[Embeddings]: + def embeddings_class(self) -> type[Embeddings]: return OpenAIEmbeddings @property - def init_from_env_params(self) -> Tuple[dict, dict, dict]: + def init_from_env_params(self) -> tuple[dict, dict, dict]: return ( { "OPENAI_API_KEY": "api_key", diff --git a/libs/partners/openai/tests/unit_tests/fake/callbacks.py b/libs/partners/openai/tests/unit_tests/fake/callbacks.py index d4b8d4b2c25..da3fa0664a0 100644 --- a/libs/partners/openai/tests/unit_tests/fake/callbacks.py +++ b/libs/partners/openai/tests/unit_tests/fake/callbacks.py @@ -1,7 +1,7 @@ """A fake callback handler for testing purposes.""" from itertools import chain -from typing import Any, Dict, List, Optional, Union +from typing import Any, Optional, Union from uuid import UUID from langchain_core.callbacks.base import AsyncCallbackHandler, BaseCallbackHandler @@ -15,7 +15,7 @@ class BaseFakeCallbackHandler(BaseModel): starts: int = 0 ends: int = 0 errors: int = 0 - errors_args: List[Any] = [] + errors_args: list[Any] = [] text: int = 0 ignore_llm_: bool = False ignore_chain_: bool = False @@ -195,8 +195,8 @@ class FakeCallbackHandler(BaseCallbackHandler, BaseFakeCallbackHandlerMixin): class FakeCallbackHandlerWithChatStart(FakeCallbackHandler): def on_chat_model_start( self, - serialized: Dict[str, Any], - messages: List[List[BaseMessage]], + serialized: dict[str, Any], + messages: list[list[BaseMessage]], *, run_id: UUID, parent_run_id: Optional[UUID] = None, diff --git a/libs/partners/openai/tests/unit_tests/llms/test_base.py b/libs/partners/openai/tests/unit_tests/llms/test_base.py index e6b281324f9..7d130ab276a 100644 --- a/libs/partners/openai/tests/unit_tests/llms/test_base.py +++ b/libs/partners/openai/tests/unit_tests/llms/test_base.py @@ -1,5 +1,4 @@ import os -from typing import List import pytest @@ -65,7 +64,7 @@ def test_get_token_ids(model: str) -> None: def test_custom_token_counting() -> None: - def token_encoder(text: str) -> List[int]: + def token_encoder(text: str) -> list[int]: return [1, 2, 3] llm = OpenAI(custom_get_token_ids=token_encoder) diff --git a/libs/partners/openai/tests/unit_tests/test_secrets.py b/libs/partners/openai/tests/unit_tests/test_secrets.py index 2c88858056c..f0540997fd8 100644 --- a/libs/partners/openai/tests/unit_tests/test_secrets.py +++ b/libs/partners/openai/tests/unit_tests/test_secrets.py @@ -1,4 +1,4 @@ -from typing import Type, cast +from typing import cast import pytest from langchain_core.load import dumpd @@ -72,7 +72,7 @@ def test_azure_openai_embeddings_secrets() -> None: @pytest.mark.parametrize( "model_class", [AzureChatOpenAI, AzureOpenAI, AzureOpenAIEmbeddings] ) -def test_azure_openai_api_key_is_secret_string(model_class: Type) -> None: +def test_azure_openai_api_key_is_secret_string(model_class: type) -> None: """Test that the API key is stored as a SecretStr.""" model = model_class( openai_api_key="secret-api-key", @@ -88,7 +88,7 @@ def test_azure_openai_api_key_is_secret_string(model_class: Type) -> None: "model_class", [AzureChatOpenAI, AzureOpenAI, AzureOpenAIEmbeddings] ) def test_azure_openai_api_key_masked_when_passed_from_env( - model_class: Type, monkeypatch: MonkeyPatch, capsys: CaptureFixture + model_class: type, monkeypatch: MonkeyPatch, capsys: CaptureFixture ) -> None: """Test that the API key is masked when passed from an environment variable.""" monkeypatch.setenv("AZURE_OPENAI_API_KEY", "secret-api-key") @@ -109,7 +109,7 @@ def test_azure_openai_api_key_masked_when_passed_from_env( "model_class", [AzureChatOpenAI, AzureOpenAI, AzureOpenAIEmbeddings] ) def test_azure_openai_api_key_masked_when_passed_via_constructor( - model_class: Type, capsys: CaptureFixture + model_class: type, capsys: CaptureFixture ) -> None: """Test that the API key is masked when passed via the constructor.""" model = model_class( @@ -133,7 +133,7 @@ def test_azure_openai_api_key_masked_when_passed_via_constructor( "model_class", [AzureChatOpenAI, AzureOpenAI, AzureOpenAIEmbeddings] ) def test_azure_openai_uses_actual_secret_value_from_secretstr( - model_class: Type, + model_class: type, ) -> None: """Test that the actual secret value is correctly retrieved.""" model = model_class( @@ -147,7 +147,7 @@ def test_azure_openai_uses_actual_secret_value_from_secretstr( @pytest.mark.parametrize("model_class", [ChatOpenAI, OpenAI, OpenAIEmbeddings]) -def test_openai_api_key_is_secret_string(model_class: Type) -> None: +def test_openai_api_key_is_secret_string(model_class: type) -> None: """Test that the API key is stored as a SecretStr.""" model = model_class(openai_api_key="secret-api-key") assert isinstance(model.openai_api_key, SecretStr) @@ -155,7 +155,7 @@ def test_openai_api_key_is_secret_string(model_class: Type) -> None: @pytest.mark.parametrize("model_class", [ChatOpenAI, OpenAI, OpenAIEmbeddings]) def test_openai_api_key_masked_when_passed_from_env( - model_class: Type, monkeypatch: MonkeyPatch, capsys: CaptureFixture + model_class: type, monkeypatch: MonkeyPatch, capsys: CaptureFixture ) -> None: """Test that the API key is masked when passed from an environment variable.""" monkeypatch.setenv("OPENAI_API_KEY", "secret-api-key") @@ -168,7 +168,7 @@ def test_openai_api_key_masked_when_passed_from_env( @pytest.mark.parametrize("model_class", [ChatOpenAI, OpenAI, OpenAIEmbeddings]) def test_openai_api_key_masked_when_passed_via_constructor( - model_class: Type, capsys: CaptureFixture + model_class: type, capsys: CaptureFixture ) -> None: """Test that the API key is masked when passed via the constructor.""" model = model_class(openai_api_key="secret-api-key") @@ -179,14 +179,14 @@ def test_openai_api_key_masked_when_passed_via_constructor( @pytest.mark.parametrize("model_class", [ChatOpenAI, OpenAI, OpenAIEmbeddings]) -def test_openai_uses_actual_secret_value_from_secretstr(model_class: Type) -> None: +def test_openai_uses_actual_secret_value_from_secretstr(model_class: type) -> None: """Test that the actual secret value is correctly retrieved.""" model = model_class(openai_api_key="secret-api-key") assert cast(SecretStr, model.openai_api_key).get_secret_value() == "secret-api-key" @pytest.mark.parametrize("model_class", [AzureChatOpenAI, AzureOpenAI]) -def test_azure_serialized_secrets(model_class: Type) -> None: +def test_azure_serialized_secrets(model_class: type) -> None: """Test that the actual secret value is correctly retrieved.""" model = model_class( openai_api_key="secret-api-key", api_version="foo", azure_endpoint="foo" diff --git a/libs/partners/perplexity/langchain_perplexity/chat_models.py b/libs/partners/perplexity/langchain_perplexity/chat_models.py index 3387705fe4c..69ff64696f1 100644 --- a/libs/partners/perplexity/langchain_perplexity/chat_models.py +++ b/libs/partners/perplexity/langchain_perplexity/chat_models.py @@ -3,20 +3,9 @@ from __future__ import annotations import logging +from collections.abc import Iterator, Mapping from operator import itemgetter -from typing import ( - Any, - Dict, - Iterator, - List, - Literal, - Mapping, - Optional, - Tuple, - Type, - TypeVar, - Union, -) +from typing import Any, Literal, Optional, TypeVar, Union import openai from langchain_core.callbacks import CallbackManagerForLLMRun @@ -50,8 +39,8 @@ from pydantic import BaseModel, ConfigDict, Field, SecretStr, model_validator from typing_extensions import Self _BM = TypeVar("_BM", bound=BaseModel) -_DictOrPydanticClass = Union[Dict[str, Any], Type[_BM], Type] -_DictOrPydantic = Union[Dict, _BM] +_DictOrPydanticClass = Union[dict[str, Any], type[_BM], type] +_DictOrPydantic = Union[dict, _BM] logger = logging.getLogger(__name__) @@ -162,14 +151,14 @@ class ChatPerplexity(BaseChatModel): """Model name.""" temperature: float = 0.7 """What sampling temperature to use.""" - model_kwargs: Dict[str, Any] = Field(default_factory=dict) + model_kwargs: dict[str, Any] = Field(default_factory=dict) """Holds any model parameters valid for `create` call not explicitly specified.""" pplx_api_key: Optional[SecretStr] = Field( default_factory=secret_from_env("PPLX_API_KEY", default=None), alias="api_key" ) """Base URL path for API requests, leave blank if not using a proxy or service emulator.""" - request_timeout: Optional[Union[float, Tuple[float, float]]] = Field( + request_timeout: Optional[Union[float, tuple[float, float]]] = Field( None, alias="timeout" ) """Timeout for requests to PerplexityChat completion API. Default is None.""" @@ -183,12 +172,12 @@ class ChatPerplexity(BaseChatModel): model_config = ConfigDict(populate_by_name=True) @property - def lc_secrets(self) -> Dict[str, str]: + def lc_secrets(self) -> dict[str, str]: return {"pplx_api_key": "PPLX_API_KEY"} @model_validator(mode="before") @classmethod - def build_extra(cls, values: Dict[str, Any]) -> Any: + def build_extra(cls, values: dict[str, Any]) -> Any: """Build extra kwargs from additional params that were passed in.""" all_required_field_names = get_pydantic_field_names(cls) extra = values.get("model_kwargs", {}) @@ -232,7 +221,7 @@ class ChatPerplexity(BaseChatModel): return self @property - def _default_params(self) -> Dict[str, Any]: + def _default_params(self) -> dict[str, Any]: """Get the default parameters for calling PerplexityChat API.""" return { "max_tokens": self.max_tokens, @@ -241,7 +230,7 @@ class ChatPerplexity(BaseChatModel): **self.model_kwargs, } - def _convert_message_to_dict(self, message: BaseMessage) -> Dict[str, Any]: + def _convert_message_to_dict(self, message: BaseMessage) -> dict[str, Any]: if isinstance(message, ChatMessage): message_dict = {"role": message.role, "content": message.content} elif isinstance(message, SystemMessage): @@ -255,8 +244,8 @@ class ChatPerplexity(BaseChatModel): return message_dict def _create_message_dicts( - self, messages: List[BaseMessage], stop: Optional[List[str]] - ) -> Tuple[List[Dict[str, Any]], Dict[str, Any]]: + self, messages: list[BaseMessage], stop: Optional[list[str]] + ) -> tuple[list[dict[str, Any]], dict[str, Any]]: params = dict(self._invocation_params) if stop is not None: if "stop" in params: @@ -266,11 +255,11 @@ class ChatPerplexity(BaseChatModel): return message_dicts, params def _convert_delta_to_message_chunk( - self, _dict: Mapping[str, Any], default_class: Type[BaseMessageChunk] + self, _dict: Mapping[str, Any], default_class: type[BaseMessageChunk] ) -> BaseMessageChunk: role = _dict.get("role") content = _dict.get("content") or "" - additional_kwargs: Dict = {} + additional_kwargs: dict = {} if _dict.get("function_call"): function_call = dict(_dict["function_call"]) if "name" in function_call and function_call["name"] is None: @@ -296,8 +285,8 @@ class ChatPerplexity(BaseChatModel): def _stream( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> Iterator[ChatGenerationChunk]: @@ -367,8 +356,8 @@ class ChatPerplexity(BaseChatModel): def _generate( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> ChatResult: @@ -402,7 +391,7 @@ class ChatPerplexity(BaseChatModel): @property def _invocation_params(self) -> Mapping[str, Any]: """Get the parameters used to invoke the model.""" - pplx_creds: Dict[str, Any] = {"model": self.model} + pplx_creds: dict[str, Any] = {"model": self.model} return {**pplx_creds, **self._default_params} @property diff --git a/libs/partners/perplexity/pyproject.toml b/libs/partners/perplexity/pyproject.toml index 048488168e5..1f86e7f5649 100644 --- a/libs/partners/perplexity/pyproject.toml +++ b/libs/partners/perplexity/pyproject.toml @@ -55,8 +55,12 @@ plugins = ['pydantic.mypy'] module = "transformers" ignore_missing_imports = true +[tool.ruff] +target-version = "py39" + [tool.ruff.lint] -select = ["E", "F", "I", "T201"] +select = ["E", "F", "I", "T201", "UP"] +ignore = [ "UP007", ] [tool.ruff.format] docstring-code-format = true diff --git a/libs/partners/perplexity/tests/integration_tests/test_chat_models_standard.py b/libs/partners/perplexity/tests/integration_tests/test_chat_models_standard.py index bf14f35c33f..ec94533bdc7 100644 --- a/libs/partners/perplexity/tests/integration_tests/test_chat_models_standard.py +++ b/libs/partners/perplexity/tests/integration_tests/test_chat_models_standard.py @@ -1,7 +1,5 @@ """Standard LangChain interface tests.""" -from typing import Type - import pytest from langchain_core.language_models import BaseChatModel from langchain_tests.integration_tests import ChatModelIntegrationTests @@ -11,7 +9,7 @@ from langchain_perplexity import ChatPerplexity class TestPerplexityStandard(ChatModelIntegrationTests): @property - def chat_model_class(self) -> Type[BaseChatModel]: + def chat_model_class(self) -> type[BaseChatModel]: return ChatPerplexity @property diff --git a/libs/partners/perplexity/tests/unit_tests/test_chat_models.py b/libs/partners/perplexity/tests/unit_tests/test_chat_models.py index b5daab5cacc..a160c5f24c9 100644 --- a/libs/partners/perplexity/tests/unit_tests/test_chat_models.py +++ b/libs/partners/perplexity/tests/unit_tests/test_chat_models.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional +from typing import Any, Optional from unittest.mock import MagicMock from langchain_core.messages import AIMessageChunk, BaseMessageChunk @@ -51,7 +51,7 @@ def test_perplexity_stream_includes_citations(mocker: MockerFixture) -> None: "choices": [{"delta": {"content": "Perplexity"}, "finish_reason": None}], "citations": ["example.com", "example2.com"], } - mock_chunks: List[Dict[str, Any]] = [mock_chunk_0, mock_chunk_1] + mock_chunks: list[dict[str, Any]] = [mock_chunk_0, mock_chunk_1] mock_stream = MagicMock() mock_stream.__iter__.return_value = mock_chunks patcher = mocker.patch.object( @@ -103,7 +103,7 @@ def test_perplexity_stream_includes_citations_and_images(mocker: MockerFixture) } ], } - mock_chunks: List[Dict[str, Any]] = [mock_chunk_0, mock_chunk_1] + mock_chunks: list[dict[str, Any]] = [mock_chunk_0, mock_chunk_1] mock_stream = MagicMock() mock_stream.__iter__.return_value = mock_chunks patcher = mocker.patch.object( @@ -162,7 +162,7 @@ def test_perplexity_stream_includes_citations_and_related_questions( "citations": ["example.com", "example2.com"], "related_questions": ["example_question_1", "example_question_2"], } - mock_chunks: List[Dict[str, Any]] = [mock_chunk_0, mock_chunk_1] + mock_chunks: list[dict[str, Any]] = [mock_chunk_0, mock_chunk_1] mock_stream = MagicMock() mock_stream.__iter__.return_value = mock_chunks patcher = mocker.patch.object( diff --git a/libs/partners/perplexity/tests/unit_tests/test_chat_models_standard.py b/libs/partners/perplexity/tests/unit_tests/test_chat_models_standard.py index 4e08418845f..09f40e93900 100644 --- a/libs/partners/perplexity/tests/unit_tests/test_chat_models_standard.py +++ b/libs/partners/perplexity/tests/unit_tests/test_chat_models_standard.py @@ -1,7 +1,5 @@ """Test Perplexity Chat API wrapper.""" -from typing import Tuple, Type - from langchain_core.language_models import BaseChatModel from langchain_tests.unit_tests import ChatModelUnitTests @@ -10,9 +8,9 @@ from langchain_perplexity import ChatPerplexity class TestPerplexityStandard(ChatModelUnitTests): @property - def chat_model_class(self) -> Type[BaseChatModel]: + def chat_model_class(self) -> type[BaseChatModel]: return ChatPerplexity @property - def init_from_env_params(self) -> Tuple[dict, dict, dict]: + def init_from_env_params(self) -> tuple[dict, dict, dict]: return ({"PPLX_API_KEY": "api_key"}, {}, {"pplx_api_key": "api_key"}) diff --git a/libs/partners/prompty/langchain_prompty/core.py b/libs/partners/prompty/langchain_prompty/core.py index bbce07fa4fc..7dc1910e1a4 100644 --- a/libs/partners/prompty/langchain_prompty/core.py +++ b/libs/partners/prompty/langchain_prompty/core.py @@ -5,7 +5,7 @@ import json import os import re from pathlib import Path -from typing import Any, Dict, Generic, List, Literal, Optional, Type, TypeVar, Union +from typing import Any, Generic, Literal, Optional, TypeVar, Union import yaml from pydantic import BaseModel, ConfigDict, Field, FilePath @@ -24,7 +24,7 @@ class PropertySettings(BaseModel): model_config = ConfigDict(arbitrary_types_allowed=True) type: Literal["string", "number", "array", "object", "boolean"] - default: Union[str, int, float, List, Dict, bool, None] = Field(default=None) + default: Union[str, int, float, list, dict, bool, None] = Field(default=None) description: str = Field(default="") @@ -58,8 +58,8 @@ class Prompty(BaseModel): # metadata name: str = Field(default="") description: str = Field(default="") - authors: List[str] = Field(default=[]) - tags: List[str] = Field(default=[]) + authors: list[str] = Field(default=[]) + tags: list[str] = Field(default=[]) version: str = Field(default="") base: str = Field(default="") basePrompty: Optional[Prompty] = Field(default=None) @@ -70,8 +70,8 @@ class Prompty(BaseModel): sample: dict = Field(default={}) # input / output - inputs: Dict[str, PropertySettings] = Field(default={}) - outputs: Dict[str, PropertySettings] = Field(default={}) + inputs: dict[str, PropertySettings] = Field(default={}) + outputs: dict[str, PropertySettings] = Field(default={}) # template template: TemplateSettings @@ -79,7 +79,7 @@ class Prompty(BaseModel): file: FilePath = Field(default="") # type: ignore[assignment] content: str = Field(default="") - def to_safe_dict(self) -> Dict[str, Any]: + def to_safe_dict(self) -> dict[str, Any]: d = {} for k, v in self: if v != "" and v != {} and v != [] and v is not None: @@ -130,7 +130,7 @@ class Prompty(BaseModel): attribute.startswith("file:") and Path(parent / attribute.split(":")[1]).exists() ): - with open(parent / attribute.split(":")[1], "r") as f: + with open(parent / attribute.split(":")[1]) as f: items = json.load(f) if isinstance(items, list): return [Prompty.normalize(value, parent) for value in items] @@ -155,8 +155,8 @@ class Prompty(BaseModel): def param_hoisting( - top: Dict[str, Any], bottom: Dict[str, Any], top_key: Any = None -) -> Dict[str, Any]: + top: dict[str, Any], bottom: dict[str, Any], top_key: Any = None +) -> dict[str, Any]: """Merge two dictionaries with hoisting of parameters from bottom to top. Args: @@ -198,18 +198,18 @@ class NoOpParser(Invoker): return data -class InvokerFactory(object): +class InvokerFactory: """Factory for creating invokers.""" _instance = None - _renderers: Dict[str, Type[Invoker]] = {} - _parsers: Dict[str, Type[Invoker]] = {} - _executors: Dict[str, Type[Invoker]] = {} - _processors: Dict[str, Type[Invoker]] = {} + _renderers: dict[str, type[Invoker]] = {} + _parsers: dict[str, type[Invoker]] = {} + _executors: dict[str, type[Invoker]] = {} + _processors: dict[str, type[Invoker]] = {} def __new__(cls) -> InvokerFactory: if cls._instance is None: - cls._instance = super(InvokerFactory, cls).__new__(cls) + cls._instance = super().__new__(cls) # Add NOOP invokers cls._renderers["NOOP"] = NoOpParser cls._parsers["NOOP"] = NoOpParser @@ -221,7 +221,7 @@ class InvokerFactory(object): self, type: Literal["renderer", "parser", "executor", "processor"], name: str, - invoker: Type[Invoker], + invoker: type[Invoker], ) -> None: if type == "renderer": self._renderers[name] = invoker @@ -264,7 +264,7 @@ class InvokerFactory(object): else: raise ValueError(f"Invalid type {type}") - def to_dict(self) -> Dict[str, Any]: + def to_dict(self) -> dict[str, Any]: return { "renderers": { k: f"{v.__module__}.{v.__name__}" for k, v in self._renderers.items() diff --git a/libs/partners/prompty/langchain_prompty/langchain.py b/libs/partners/prompty/langchain_prompty/langchain.py index 52eac52c0da..3246d3aab93 100644 --- a/libs/partners/prompty/langchain_prompty/langchain.py +++ b/libs/partners/prompty/langchain_prompty/langchain.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain_core.runnables import Runnable, RunnableLambda @@ -10,10 +10,10 @@ from .utils import load, prepare def create_chat_prompt( path: str, input_name_agent_scratchpad: str = "agent_scratchpad", -) -> Runnable[Dict[str, Any], ChatPromptTemplate]: +) -> Runnable[dict[str, Any], ChatPromptTemplate]: """Create a chat prompt from a Langchain schema.""" - def runnable_chat_lambda(inputs: Dict[str, Any]) -> ChatPromptTemplate: + def runnable_chat_lambda(inputs: dict[str, Any]) -> ChatPromptTemplate: p = load(path) parsed = prepare(p, inputs) # Parsed messages have been templated diff --git a/libs/partners/prompty/langchain_prompty/parsers.py b/libs/partners/prompty/langchain_prompty/parsers.py index 592ddd80136..ec94d4ac015 100644 --- a/libs/partners/prompty/langchain_prompty/parsers.py +++ b/libs/partners/prompty/langchain_prompty/parsers.py @@ -1,6 +1,6 @@ import base64 import re -from typing import Dict, List, Type, Union +from typing import Union from langchain_core.messages import ( AIMessage, @@ -15,7 +15,7 @@ from .core import Invoker, Prompty, SimpleModel class RoleMap: - _ROLE_MAP: Dict[str, Type[BaseMessage]] = { + _ROLE_MAP: dict[str, type[BaseMessage]] = { "system": SystemMessage, "user": HumanMessage, "human": HumanMessage, @@ -26,7 +26,7 @@ class RoleMap: ROLES = _ROLE_MAP.keys() @classmethod - def get_message_class(cls, role: str) -> Type[BaseMessage]: + def get_message_class(cls, role: str) -> type[BaseMessage]: return cls._ROLE_MAP[role] @@ -60,7 +60,7 @@ class PromptyChatParser(Invoker): "and .jpg / .jpeg are supported." ) - def parse_content(self, content: str) -> Union[str, List]: + def parse_content(self, content: str) -> Union[str, list]: """for parsing inline images""" # regular expression to parse markdown images image = r"(?P!\[[^\]]*\])\((?P.*?)(?=\"|\))\)" diff --git a/libs/partners/prompty/langchain_prompty/utils.py b/libs/partners/prompty/langchain_prompty/utils.py index d790f27e905..bbee4140996 100644 --- a/libs/partners/prompty/langchain_prompty/utils.py +++ b/libs/partners/prompty/langchain_prompty/utils.py @@ -1,6 +1,6 @@ import traceback from pathlib import Path -from typing import Any, Dict, List, Union +from typing import Any, Union from .core import ( Frontmatter, @@ -120,7 +120,7 @@ def load(prompt_path: str, configuration: str = "default") -> Prompty: def prepare( prompt: Prompty, - inputs: Dict[str, Any] = {}, + inputs: dict[str, Any] = {}, ) -> Any: """Prepare the inputs for the prompty. @@ -166,9 +166,9 @@ def prepare( def run( prompt: Prompty, - content: Union[Dict, List, str], - configuration: Dict[str, Any] = {}, - parameters: Dict[str, Any] = {}, + content: Union[dict, list, str], + configuration: dict[str, Any] = {}, + parameters: dict[str, Any] = {}, raw: bool = False, ) -> Any: """Run the prompty. @@ -219,9 +219,9 @@ def run( def execute( prompt: Union[str, Prompty], - configuration: Dict[str, Any] = {}, - parameters: Dict[str, Any] = {}, - inputs: Dict[str, Any] = {}, + configuration: dict[str, Any] = {}, + parameters: dict[str, Any] = {}, + inputs: dict[str, Any] = {}, raw: bool = False, connection: str = "default", ) -> Any: diff --git a/libs/partners/prompty/pyproject.toml b/libs/partners/prompty/pyproject.toml index f47596855a5..971f81577a6 100644 --- a/libs/partners/prompty/pyproject.toml +++ b/libs/partners/prompty/pyproject.toml @@ -45,9 +45,12 @@ langchain-core = { path = "../../core", editable = true } langchain-text-splitters = { path = "../../text-splitters", editable = true } langchain = { path = "../../langchain", editable = true } - [tool.ruff] -select = ["E", "F", "I"] +target-version = "py39" + +[tool.ruff.lint] +select = ["E", "F", "I", "T201", "UP"] +ignore = [ "UP007", ] [tool.mypy] disallow_untyped_defs = "True" diff --git a/libs/partners/prompty/scripts/check_imports.py b/libs/partners/prompty/scripts/check_imports.py index fd21a4975b7..dabb5ba8ed8 100644 --- a/libs/partners/prompty/scripts/check_imports.py +++ b/libs/partners/prompty/scripts/check_imports.py @@ -10,8 +10,6 @@ if __name__ == "__main__": SourceFileLoader("x", file).load_module() except Exception: has_faillure = True - print(file) traceback.print_exc() - print() sys.exit(1 if has_failure else 0) diff --git a/libs/partners/prompty/tests/unit_tests/fake_callback_handler.py b/libs/partners/prompty/tests/unit_tests/fake_callback_handler.py index cfbfa4f6f81..bcff90185aa 100644 --- a/libs/partners/prompty/tests/unit_tests/fake_callback_handler.py +++ b/libs/partners/prompty/tests/unit_tests/fake_callback_handler.py @@ -1,7 +1,7 @@ """A fake callback handler for testing purposes.""" from itertools import chain -from typing import Any, Dict, List, Optional, Union +from typing import Any, Optional, Union from uuid import UUID from langchain_core.callbacks import AsyncCallbackHandler, BaseCallbackHandler @@ -41,7 +41,7 @@ class BaseFakeCallbackHandler(BaseModel): retriever_errors: int = 0 retries: int = 0 - input_prompts: List[str] = [] + input_prompts: list[str] = [] class BaseFakeCallbackHandlerMixin(BaseFakeCallbackHandler): @@ -142,7 +142,7 @@ class FakeCallbackHandler(BaseCallbackHandler, BaseFakeCallbackHandlerMixin): return self.ignore_retriever_ def on_llm_start( - self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any + self, serialized: dict[str, Any], prompts: list[str], **kwargs: Any ) -> Any: self.input_prompts = prompts self.on_llm_start_common() @@ -267,8 +267,8 @@ class FakeCallbackHandler(BaseCallbackHandler, BaseFakeCallbackHandlerMixin): class FakeCallbackHandlerWithChatStart(FakeCallbackHandler): def on_chat_model_start( self, - serialized: Dict[str, Any], - messages: List[List[BaseMessage]], + serialized: dict[str, Any], + messages: list[list[BaseMessage]], *, run_id: UUID, parent_run_id: Optional[UUID] = None, @@ -304,7 +304,7 @@ class FakeAsyncCallbackHandler(AsyncCallbackHandler, BaseFakeCallbackHandlerMixi self.on_retry_common() async def on_llm_start( - self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any + self, serialized: dict[str, Any], prompts: list[str], **kwargs: Any ) -> None: self.on_llm_start_common() diff --git a/libs/partners/prompty/tests/unit_tests/fake_chat_model.py b/libs/partners/prompty/tests/unit_tests/fake_chat_model.py index 6047a79a33a..c709f561f7e 100644 --- a/libs/partners/prompty/tests/unit_tests/fake_chat_model.py +++ b/libs/partners/prompty/tests/unit_tests/fake_chat_model.py @@ -1,7 +1,7 @@ """Fake Chat Model wrapper for testing purposes.""" import json -from typing import Any, Dict, List, Optional +from typing import Any, Optional from langchain_core.callbacks import ( AsyncCallbackManagerForLLMRun, @@ -17,8 +17,8 @@ class FakeEchoPromptChatModel(SimpleChatModel): def _call( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> str: @@ -26,8 +26,8 @@ class FakeEchoPromptChatModel(SimpleChatModel): async def _agenerate( self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, + messages: list[BaseMessage], + stop: Optional[list[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, **kwargs: Any, ) -> ChatResult: @@ -41,5 +41,5 @@ class FakeEchoPromptChatModel(SimpleChatModel): return "fake-echo-prompt-chat-model" @property - def _identifying_params(self) -> Dict[str, Any]: + def _identifying_params(self) -> dict[str, Any]: return {"key": "fake"} diff --git a/libs/partners/prompty/tests/unit_tests/fake_output_parser.py b/libs/partners/prompty/tests/unit_tests/fake_output_parser.py index 33e400aedc6..fcec4d4c04e 100644 --- a/libs/partners/prompty/tests/unit_tests/fake_output_parser.py +++ b/libs/partners/prompty/tests/unit_tests/fake_output_parser.py @@ -1,10 +1,10 @@ -from typing import Optional, Tuple, Union +from typing import Optional, Union from langchain.agents import AgentOutputParser from langchain_core.agents import AgentAction, AgentFinish -def extract_action_details(text: str) -> Tuple[Optional[str], Optional[str]]: +def extract_action_details(text: str) -> tuple[Optional[str], Optional[str]]: # Split the text into lines and strip whitespace lines = [line.strip() for line in text.strip().split("\n")] @@ -24,7 +24,6 @@ def extract_action_details(text: str) -> Tuple[Optional[str], Optional[str]]: class FakeOutputParser(AgentOutputParser): def parse(self, text: str) -> Union[AgentAction, AgentFinish]: - print("FakeOutputParser", text) action, input = extract_action_details(text) if action: diff --git a/libs/partners/prompty/tests/unit_tests/test_prompty_serialization.py b/libs/partners/prompty/tests/unit_tests/test_prompty_serialization.py index 9233de36653..c68b8238dc4 100644 --- a/libs/partners/prompty/tests/unit_tests/test_prompty_serialization.py +++ b/libs/partners/prompty/tests/unit_tests/test_prompty_serialization.py @@ -1,6 +1,5 @@ import json import os -from typing import List, Tuple from langchain.agents.format_scratchpad import format_to_openai_function_messages from langchain.tools import tool @@ -43,8 +42,6 @@ def test_prompty_basic_chain() -> None: else: msgs = parsed_prompts.content - print(msgs) - assert len(msgs) == 2 # Test for system and user entries system_message = msgs[0] @@ -111,7 +108,7 @@ def test_prompty_used_in_agent() -> None: class AgentInput(BaseModel): input: str - chat_history: List[Tuple[str, str]] = Field( + chat_history: list[tuple[str, str]] = Field( ..., extra={"widget": {"type": "chat", "input": "input", "output": "output"}}, ) @@ -131,7 +128,6 @@ def test_prompty_used_in_agent() -> None: ], } ) - print(callbackHandler) input_prompt = callbackHandler.input_prompts[0] # Test for existence of fakeFirstName and fakeLastName in the system message @@ -157,9 +153,7 @@ def test_all_prompty_can_run() -> None: for file in prompty_files: file_path = os.path.join(prompty_folder, file) - print(f"==========\nTesting Prompty file: {file_path}") prompt = langchain_prompty.create_chat_prompt(file_path) model = FakeEchoPromptChatModel() chain = prompt | model - output = chain.invoke({}) - print(f"{file_path}, {output}") + chain.invoke({}) diff --git a/libs/partners/qdrant/langchain_qdrant/_utils.py b/libs/partners/qdrant/langchain_qdrant/_utils.py index 95abe264eea..df2c5d6039e 100644 --- a/libs/partners/qdrant/langchain_qdrant/_utils.py +++ b/libs/partners/qdrant/langchain_qdrant/_utils.py @@ -1,8 +1,8 @@ -from typing import List, Union +from typing import Union import numpy as np -Matrix = Union[List[List[float]], List[np.ndarray], np.ndarray] +Matrix = Union[list[list[float]], list[np.ndarray], np.ndarray] def maximal_marginal_relevance( @@ -10,7 +10,7 @@ def maximal_marginal_relevance( embedding_list: list, lambda_mult: float = 0.5, k: int = 4, -) -> List[int]: +) -> list[int]: """Calculate maximal marginal relevance.""" if min(k, len(embedding_list)) <= 0: return [] diff --git a/libs/partners/qdrant/langchain_qdrant/fastembed_sparse.py b/libs/partners/qdrant/langchain_qdrant/fastembed_sparse.py index c521540965e..84371986395 100644 --- a/libs/partners/qdrant/langchain_qdrant/fastembed_sparse.py +++ b/libs/partners/qdrant/langchain_qdrant/fastembed_sparse.py @@ -1,4 +1,5 @@ -from typing import Any, List, Optional, Sequence +from collections.abc import Sequence +from typing import Any, Optional from langchain_qdrant.sparse_embeddings import SparseEmbeddings, SparseVector @@ -56,7 +57,7 @@ class FastEmbedSparse(SparseEmbeddings): **kwargs, ) - def embed_documents(self, texts: List[str]) -> List[SparseVector]: + def embed_documents(self, texts: list[str]) -> list[SparseVector]: results = self._model.embed( texts, batch_size=self._batch_size, parallel=self._parallel ) diff --git a/libs/partners/qdrant/langchain_qdrant/qdrant.py b/libs/partners/qdrant/langchain_qdrant/qdrant.py index 495294024b5..80f3bf57a4a 100644 --- a/libs/partners/qdrant/langchain_qdrant/qdrant.py +++ b/libs/partners/qdrant/langchain_qdrant/qdrant.py @@ -1,20 +1,14 @@ from __future__ import annotations import uuid +from collections.abc import Generator, Iterable, Sequence from enum import Enum from itertools import islice from operator import itemgetter from typing import ( Any, Callable, - Dict, - Generator, - Iterable, - List, Optional, - Sequence, - Tuple, - Type, Union, ) @@ -275,10 +269,10 @@ class QdrantVectorStore(VectorStore): @classmethod def from_texts( - cls: Type[QdrantVectorStore], - texts: List[str], + cls: type[QdrantVectorStore], + texts: list[str], embedding: Optional[Embeddings] = None, - metadatas: Optional[List[dict]] = None, + metadatas: Optional[list[dict]] = None, ids: Optional[Sequence[str | int]] = None, collection_name: Optional[str] = None, location: Optional[str] = None, @@ -299,9 +293,9 @@ class QdrantVectorStore(VectorStore): retrieval_mode: RetrievalMode = RetrievalMode.DENSE, sparse_embedding: Optional[SparseEmbeddings] = None, sparse_vector_name: str = SPARSE_VECTOR_NAME, - collection_create_options: Dict[str, Any] = {}, - vector_params: Dict[str, Any] = {}, - sparse_vector_params: Dict[str, Any] = {}, + collection_create_options: dict[str, Any] = {}, + vector_params: dict[str, Any] = {}, + sparse_vector_params: dict[str, Any] = {}, batch_size: int = 64, force_recreate: bool = False, validate_embeddings: bool = True, @@ -363,7 +357,7 @@ class QdrantVectorStore(VectorStore): @classmethod def from_existing_collection( - cls: Type[QdrantVectorStore], + cls: type[QdrantVectorStore], collection_name: str, embedding: Optional[Embeddings] = None, retrieval_mode: RetrievalMode = RetrievalMode.DENSE, @@ -427,11 +421,11 @@ class QdrantVectorStore(VectorStore): def add_texts( # type: ignore self, texts: Iterable[str], - metadatas: Optional[List[dict]] = None, + metadatas: Optional[list[dict]] = None, ids: Optional[Sequence[str | int]] = None, batch_size: int = 64, **kwargs: Any, - ) -> List[str | int]: + ) -> list[str | int]: """Add texts with embeddings to the vectorstore. Returns: @@ -459,7 +453,7 @@ class QdrantVectorStore(VectorStore): consistency: Optional[models.ReadConsistency] = None, hybrid_fusion: Optional[models.FusionQuery] = None, **kwargs: Any, - ) -> List[Document]: + ) -> list[Document]: """Return docs most similar to query. Returns: @@ -489,7 +483,7 @@ class QdrantVectorStore(VectorStore): consistency: Optional[models.ReadConsistency] = None, hybrid_fusion: Optional[models.FusionQuery] = None, **kwargs: Any, - ) -> List[Tuple[Document, float]]: + ) -> list[tuple[Document, float]]: """Return docs most similar to query. Returns: @@ -570,7 +564,7 @@ class QdrantVectorStore(VectorStore): def similarity_search_with_score_by_vector( self, - embedding: List[float], + embedding: list[float], k: int = 4, filter: Optional[models.Filter] = None, search_params: Optional[models.SearchParams] = None, @@ -578,7 +572,7 @@ class QdrantVectorStore(VectorStore): score_threshold: Optional[float] = None, consistency: Optional[models.ReadConsistency] = None, **kwargs: Any, - ) -> List[tuple[Document, float]]: + ) -> list[tuple[Document, float]]: """Return docs most similar to embedding vector. Returns: @@ -623,7 +617,7 @@ class QdrantVectorStore(VectorStore): def similarity_search_by_vector( self, - embedding: List[float], + embedding: list[float], k: int = 4, filter: Optional[models.Filter] = None, search_params: Optional[models.SearchParams] = None, @@ -631,7 +625,7 @@ class QdrantVectorStore(VectorStore): score_threshold: Optional[float] = None, consistency: Optional[models.ReadConsistency] = None, **kwargs: Any, - ) -> List[Document]: + ) -> list[Document]: """Return docs most similar to embedding vector. Returns: @@ -660,7 +654,7 @@ class QdrantVectorStore(VectorStore): score_threshold: Optional[float] = None, consistency: Optional[models.ReadConsistency] = None, **kwargs: Any, - ) -> List[Document]: + ) -> list[Document]: """Return docs selected using the maximal marginal relevance with dense vectors. Maximal marginal relevance optimizes for similarity to query AND diversity @@ -693,7 +687,7 @@ class QdrantVectorStore(VectorStore): def max_marginal_relevance_search_by_vector( self, - embedding: List[float], + embedding: list[float], k: int = 4, fetch_k: int = 20, lambda_mult: float = 0.5, @@ -702,7 +696,7 @@ class QdrantVectorStore(VectorStore): score_threshold: Optional[float] = None, consistency: Optional[models.ReadConsistency] = None, **kwargs: Any, - ) -> List[Document]: + ) -> list[Document]: """Return docs selected using the maximal marginal relevance with dense vectors. Maximal marginal relevance optimizes for similarity to query AND diversity @@ -726,7 +720,7 @@ class QdrantVectorStore(VectorStore): def max_marginal_relevance_search_with_score_by_vector( self, - embedding: List[float], + embedding: list[float], k: int = 4, fetch_k: int = 20, lambda_mult: float = 0.5, @@ -735,7 +729,7 @@ class QdrantVectorStore(VectorStore): score_threshold: Optional[float] = None, consistency: Optional[models.ReadConsistency] = None, **kwargs: Any, - ) -> List[Tuple[Document, float]]: + ) -> list[tuple[Document, float]]: """Return docs selected using the maximal marginal relevance. Maximal marginal relevance optimizes for similarity to query AND diversity among selected documents. @@ -782,7 +776,7 @@ class QdrantVectorStore(VectorStore): def delete( # type: ignore self, - ids: Optional[List[str | int]] = None, + ids: Optional[list[str | int]] = None, **kwargs: Any, ) -> Optional[bool]: """Delete documents by their ids. @@ -800,7 +794,7 @@ class QdrantVectorStore(VectorStore): ) return result.status == models.UpdateStatus.COMPLETED - def get_by_ids(self, ids: Sequence[str | int], /) -> List[Document]: + def get_by_ids(self, ids: Sequence[str | int], /) -> list[Document]: results = self.client.retrieve(self.collection_name, ids, with_payload=True) return [ @@ -815,11 +809,11 @@ class QdrantVectorStore(VectorStore): @classmethod def construct_instance( - cls: Type[QdrantVectorStore], + cls: type[QdrantVectorStore], embedding: Optional[Embeddings] = None, retrieval_mode: RetrievalMode = RetrievalMode.DENSE, sparse_embedding: Optional[SparseEmbeddings] = None, - client_options: Dict[str, Any] = {}, + client_options: dict[str, Any] = {}, collection_name: Optional[str] = None, distance: models.Distance = models.Distance.COSINE, content_payload_key: str = CONTENT_KEY, @@ -827,9 +821,9 @@ class QdrantVectorStore(VectorStore): vector_name: str = VECTOR_NAME, sparse_vector_name: str = SPARSE_VECTOR_NAME, force_recreate: bool = False, - collection_create_options: Dict[str, Any] = {}, - vector_params: Dict[str, Any] = {}, - sparse_vector_params: Dict[str, Any] = {}, + collection_create_options: dict[str, Any] = {}, + vector_params: dict[str, Any] = {}, + sparse_vector_params: dict[str, Any] = {}, validate_embeddings: bool = True, validate_collection_config: bool = True, ) -> QdrantVectorStore: @@ -960,7 +954,7 @@ class QdrantVectorStore(VectorStore): def _generate_batches( self, texts: Iterable[str], - metadatas: Optional[List[dict]] = None, + metadatas: Optional[list[dict]] = None, ids: Optional[Sequence[str | int]] = None, batch_size: int = 64, ) -> Generator[tuple[list[str | int], list[models.PointStruct]], Any, None]: @@ -994,10 +988,10 @@ class QdrantVectorStore(VectorStore): @staticmethod def _build_payloads( texts: Iterable[str], - metadatas: Optional[List[dict]], + metadatas: Optional[list[dict]], content_payload_key: str, metadata_payload_key: str, - ) -> List[dict]: + ) -> list[dict]: payloads = [] for i, text in enumerate(texts): if text is None: @@ -1018,7 +1012,7 @@ class QdrantVectorStore(VectorStore): def _build_vectors( self, texts: Iterable[str], - ) -> List[models.VectorStruct]: + ) -> list[models.VectorStruct]: if self.retrieval_mode == RetrievalMode.DENSE: batch_embeddings = self.embeddings.embed_documents(list(texts)) return [ @@ -1068,7 +1062,7 @@ class QdrantVectorStore(VectorStore): @classmethod def _validate_collection_config( - cls: Type[QdrantVectorStore], + cls: type[QdrantVectorStore], client: QdrantClient, collection_name: str, retrieval_mode: RetrievalMode, @@ -1097,17 +1091,17 @@ class QdrantVectorStore(VectorStore): @classmethod def _validate_collection_for_dense( - cls: Type[QdrantVectorStore], + cls: type[QdrantVectorStore], client: QdrantClient, collection_name: str, vector_name: str, distance: models.Distance, - dense_embeddings: Union[Embeddings, List[float], None], + dense_embeddings: Union[Embeddings, list[float], None], ) -> None: collection_info = client.get_collection(collection_name=collection_name) vector_config = collection_info.config.params.vectors - if isinstance(vector_config, Dict): + if isinstance(vector_config, dict): # vector_config is a Dict[str, VectorParams] if vector_name not in vector_config: raise QdrantVectorStoreError( @@ -1164,7 +1158,7 @@ class QdrantVectorStore(VectorStore): @classmethod def _validate_collection_for_sparse( - cls: Type[QdrantVectorStore], + cls: type[QdrantVectorStore], client: QdrantClient, collection_name: str, sparse_vector_name: str, @@ -1185,7 +1179,7 @@ class QdrantVectorStore(VectorStore): @classmethod def _validate_embeddings( - cls: Type[QdrantVectorStore], + cls: type[QdrantVectorStore], retrieval_mode: RetrievalMode, embedding: Optional[Embeddings], sparse_embedding: Optional[SparseEmbeddings], diff --git a/libs/partners/qdrant/langchain_qdrant/sparse_embeddings.py b/libs/partners/qdrant/langchain_qdrant/sparse_embeddings.py index 49551403609..15a8002e501 100644 --- a/libs/partners/qdrant/langchain_qdrant/sparse_embeddings.py +++ b/libs/partners/qdrant/langchain_qdrant/sparse_embeddings.py @@ -1,5 +1,4 @@ from abc import ABC, abstractmethod -from typing import List from langchain_core.runnables.config import run_in_executor from pydantic import BaseModel, Field @@ -10,8 +9,8 @@ class SparseVector(BaseModel, extra="forbid"): Sparse vector structure """ - indices: List[int] = Field(..., description="indices must be unique") - values: List[float] = Field( + indices: list[int] = Field(..., description="indices must be unique") + values: list[float] = Field( ..., description="values and indices must be the same length" ) @@ -20,14 +19,14 @@ class SparseEmbeddings(ABC): """An interface for sparse embedding models to use with Qdrant.""" @abstractmethod - def embed_documents(self, texts: List[str]) -> List[SparseVector]: + def embed_documents(self, texts: list[str]) -> list[SparseVector]: """Embed search docs.""" @abstractmethod def embed_query(self, text: str) -> SparseVector: """Embed query text.""" - async def aembed_documents(self, texts: List[str]) -> List[SparseVector]: + async def aembed_documents(self, texts: list[str]) -> list[SparseVector]: """Asynchronous Embed search docs.""" return await run_in_executor(None, self.embed_documents, texts) diff --git a/libs/partners/qdrant/langchain_qdrant/vectorstores.py b/libs/partners/qdrant/langchain_qdrant/vectorstores.py index 1b4941604f6..001d9a45cc5 100644 --- a/libs/partners/qdrant/langchain_qdrant/vectorstores.py +++ b/libs/partners/qdrant/langchain_qdrant/vectorstores.py @@ -4,21 +4,14 @@ import functools import os import uuid import warnings +from collections.abc import AsyncGenerator, Generator, Iterable, Sequence from itertools import islice from operator import itemgetter from typing import ( TYPE_CHECKING, Any, - AsyncGenerator, Callable, - Dict, - Generator, - Iterable, - List, Optional, - Sequence, - Tuple, - Type, Union, ) @@ -35,7 +28,7 @@ from qdrant_client.local.async_qdrant_local import AsyncQdrantLocal from langchain_qdrant._utils import maximal_marginal_relevance if TYPE_CHECKING: - DictFilter = Dict[str, Union[str, int, bool, dict, list]] + DictFilter = dict[str, Union[str, int, bool, dict, list]] MetadataFilter = Union[DictFilter, models.Filter] @@ -153,11 +146,11 @@ class Qdrant(VectorStore): def add_texts( self, texts: Iterable[str], - metadatas: Optional[List[dict]] = None, + metadatas: Optional[list[dict]] = None, ids: Optional[Sequence[str]] = None, batch_size: int = 64, **kwargs: Any, - ) -> List[str]: + ) -> list[str]: """Run more texts through the embeddings and add to the vectorstore. Args: @@ -188,11 +181,11 @@ class Qdrant(VectorStore): async def aadd_texts( self, texts: Iterable[str], - metadatas: Optional[List[dict]] = None, + metadatas: Optional[list[dict]] = None, ids: Optional[Sequence[str]] = None, batch_size: int = 64, **kwargs: Any, - ) -> List[str]: + ) -> list[str]: """Run more texts through the embeddings and add to the vectorstore. Args: @@ -236,7 +229,7 @@ class Qdrant(VectorStore): score_threshold: Optional[float] = None, consistency: Optional[models.ReadConsistency] = None, **kwargs: Any, - ) -> List[Document]: + ) -> list[Document]: """Return docs most similar to query. Args: @@ -290,7 +283,7 @@ class Qdrant(VectorStore): k: int = 4, filter: Optional[MetadataFilter] = None, **kwargs: Any, - ) -> List[Document]: + ) -> list[Document]: """Return docs most similar to query. Args: query: Text to look up documents similar to. @@ -312,7 +305,7 @@ class Qdrant(VectorStore): score_threshold: Optional[float] = None, consistency: Optional[models.ReadConsistency] = None, **kwargs: Any, - ) -> List[Tuple[Document, float]]: + ) -> list[tuple[Document, float]]: """Return docs most similar to query. Args: @@ -369,7 +362,7 @@ class Qdrant(VectorStore): score_threshold: Optional[float] = None, consistency: Optional[models.ReadConsistency] = None, **kwargs: Any, - ) -> List[Tuple[Document, float]]: + ) -> list[tuple[Document, float]]: """Return docs most similar to query. Args: @@ -419,7 +412,7 @@ class Qdrant(VectorStore): def similarity_search_by_vector( self, - embedding: List[float], + embedding: list[float], k: int = 4, filter: Optional[MetadataFilter] = None, search_params: Optional[models.SearchParams] = None, @@ -427,7 +420,7 @@ class Qdrant(VectorStore): score_threshold: Optional[float] = None, consistency: Optional[models.ReadConsistency] = None, **kwargs: Any, - ) -> List[Document]: + ) -> list[Document]: """Return docs most similar to embedding vector. Args: @@ -477,7 +470,7 @@ class Qdrant(VectorStore): @sync_call_fallback async def asimilarity_search_by_vector( self, - embedding: List[float], + embedding: list[float], k: int = 4, filter: Optional[MetadataFilter] = None, search_params: Optional[models.SearchParams] = None, @@ -485,7 +478,7 @@ class Qdrant(VectorStore): score_threshold: Optional[float] = None, consistency: Optional[models.ReadConsistency] = None, **kwargs: Any, - ) -> List[Document]: + ) -> list[Document]: """Return docs most similar to embedding vector. Args: @@ -535,7 +528,7 @@ class Qdrant(VectorStore): def similarity_search_with_score_by_vector( self, - embedding: List[float], + embedding: list[float], k: int = 4, filter: Optional[MetadataFilter] = None, search_params: Optional[models.SearchParams] = None, @@ -543,7 +536,7 @@ class Qdrant(VectorStore): score_threshold: Optional[float] = None, consistency: Optional[models.ReadConsistency] = None, **kwargs: Any, - ) -> List[Tuple[Document, float]]: + ) -> list[tuple[Document, float]]: """Return docs most similar to embedding vector. Args: @@ -622,7 +615,7 @@ class Qdrant(VectorStore): @sync_call_fallback async def asimilarity_search_with_score_by_vector( self, - embedding: List[float], + embedding: list[float], k: int = 4, filter: Optional[MetadataFilter] = None, search_params: Optional[models.SearchParams] = None, @@ -630,7 +623,7 @@ class Qdrant(VectorStore): score_threshold: Optional[float] = None, consistency: Optional[models.ReadConsistency] = None, **kwargs: Any, - ) -> List[Tuple[Document, float]]: + ) -> list[tuple[Document, float]]: """Return docs most similar to embedding vector. Args: @@ -725,7 +718,7 @@ class Qdrant(VectorStore): score_threshold: Optional[float] = None, consistency: Optional[models.ReadConsistency] = None, **kwargs: Any, - ) -> List[Document]: + ) -> list[Document]: """Return docs selected using the maximal marginal relevance. Maximal marginal relevance optimizes for similarity to query AND diversity @@ -789,7 +782,7 @@ class Qdrant(VectorStore): score_threshold: Optional[float] = None, consistency: Optional[models.ReadConsistency] = None, **kwargs: Any, - ) -> List[Document]: + ) -> list[Document]: """Return docs selected using the maximal marginal relevance. Maximal marginal relevance optimizes for similarity to query AND diversity @@ -844,7 +837,7 @@ class Qdrant(VectorStore): def max_marginal_relevance_search_by_vector( self, - embedding: List[float], + embedding: list[float], k: int = 4, fetch_k: int = 20, lambda_mult: float = 0.5, @@ -853,7 +846,7 @@ class Qdrant(VectorStore): score_threshold: Optional[float] = None, consistency: Optional[models.ReadConsistency] = None, **kwargs: Any, - ) -> List[Document]: + ) -> list[Document]: """Return docs selected using the maximal marginal relevance. Maximal marginal relevance optimizes for similarity to query AND diversity @@ -907,7 +900,7 @@ class Qdrant(VectorStore): @sync_call_fallback async def amax_marginal_relevance_search_by_vector( self, - embedding: List[float], + embedding: list[float], k: int = 4, fetch_k: int = 20, lambda_mult: float = 0.5, @@ -916,7 +909,7 @@ class Qdrant(VectorStore): score_threshold: Optional[float] = None, consistency: Optional[models.ReadConsistency] = None, **kwargs: Any, - ) -> List[Document]: + ) -> list[Document]: """Return docs selected using the maximal marginal relevance. Maximal marginal relevance optimizes for similarity to query AND diversity among selected documents. @@ -970,7 +963,7 @@ class Qdrant(VectorStore): def max_marginal_relevance_search_with_score_by_vector( self, - embedding: List[float], + embedding: list[float], k: int = 4, fetch_k: int = 20, lambda_mult: float = 0.5, @@ -979,7 +972,7 @@ class Qdrant(VectorStore): score_threshold: Optional[float] = None, consistency: Optional[models.ReadConsistency] = None, **kwargs: Any, - ) -> List[Tuple[Document, float]]: + ) -> list[tuple[Document, float]]: """Return docs selected using the maximal marginal relevance. Maximal marginal relevance optimizes for similarity to query AND diversity among selected documents. @@ -1058,7 +1051,7 @@ class Qdrant(VectorStore): @sync_call_fallback async def amax_marginal_relevance_search_with_score_by_vector( self, - embedding: List[float], + embedding: list[float], k: int = 4, fetch_k: int = 20, lambda_mult: float = 0.5, @@ -1067,7 +1060,7 @@ class Qdrant(VectorStore): score_threshold: Optional[float] = None, consistency: Optional[models.ReadConsistency] = None, **kwargs: Any, - ) -> List[Tuple[Document, float]]: + ) -> list[tuple[Document, float]]: """Return docs selected using the maximal marginal relevance. Maximal marginal relevance optimizes for similarity to query AND diversity among selected documents. @@ -1128,7 +1121,7 @@ class Qdrant(VectorStore): for i in mmr_selected ] - def delete(self, ids: Optional[List[str]] = None, **kwargs: Any) -> Optional[bool]: + def delete(self, ids: Optional[list[str]] = None, **kwargs: Any) -> Optional[bool]: """Delete by vector ID or other criteria. Args: @@ -1147,7 +1140,7 @@ class Qdrant(VectorStore): @sync_call_fallback async def adelete( - self, ids: Optional[List[str]] = None, **kwargs: Any + self, ids: Optional[list[str]] = None, **kwargs: Any ) -> Optional[bool]: """Delete by vector ID or other criteria. @@ -1174,10 +1167,10 @@ class Qdrant(VectorStore): @classmethod def from_texts( - cls: Type[Qdrant], - texts: List[str], + cls: type[Qdrant], + texts: list[str], embedding: Embeddings, - metadatas: Optional[List[dict]] = None, + metadatas: Optional[list[dict]] = None, ids: Optional[Sequence[str]] = None, location: Optional[str] = None, url: Optional[str] = None, @@ -1351,7 +1344,7 @@ class Qdrant(VectorStore): @classmethod def from_existing_collection( - cls: Type[Qdrant], + cls: type[Qdrant], embedding: Embeddings, path: Optional[str] = None, collection_name: Optional[str] = None, @@ -1408,10 +1401,10 @@ class Qdrant(VectorStore): @classmethod @sync_call_fallback async def afrom_texts( - cls: Type[Qdrant], - texts: List[str], + cls: type[Qdrant], + texts: list[str], embedding: Embeddings, - metadatas: Optional[List[dict]] = None, + metadatas: Optional[list[dict]] = None, ids: Optional[Sequence[str]] = None, location: Optional[str] = None, url: Optional[str] = None, @@ -1585,8 +1578,8 @@ class Qdrant(VectorStore): @classmethod def construct_instance( - cls: Type[Qdrant], - texts: List[str], + cls: type[Qdrant], + texts: list[str], embedding: Embeddings, location: Optional[str] = None, url: Optional[str] = None, @@ -1744,8 +1737,8 @@ class Qdrant(VectorStore): @classmethod async def aconstruct_instance( - cls: Type[Qdrant], - texts: List[str], + cls: type[Qdrant], + texts: list[str], embedding: Embeddings, location: Optional[str] = None, url: Optional[str] = None, @@ -1936,7 +1929,7 @@ class Qdrant(VectorStore): query: str, k: int = 4, **kwargs: Any, - ) -> List[Tuple[Document, float]]: + ) -> list[tuple[Document, float]]: """Return docs and relevance scores in the range [0, 1]. 0 is dissimilar, 1 is most similar. @@ -1959,7 +1952,7 @@ class Qdrant(VectorStore): query: str, k: int = 4, **kwargs: Any, - ) -> List[Tuple[Document, float]]: + ) -> list[tuple[Document, float]]: """Return docs and relevance scores in the range [0, 1]. 0 is dissimilar, 1 is most similar. @@ -1980,10 +1973,10 @@ class Qdrant(VectorStore): def _build_payloads( cls, texts: Iterable[str], - metadatas: Optional[List[dict]], + metadatas: Optional[list[dict]], content_payload_key: str, metadata_payload_key: str, - ) -> List[dict]: + ) -> list[dict]: payloads = [] for i, text in enumerate(texts): if text is None: @@ -2017,7 +2010,7 @@ class Qdrant(VectorStore): metadata=metadata, ) - def _build_condition(self, key: str, value: Any) -> List[models.FieldCondition]: + def _build_condition(self, key: str, value: Any) -> list[models.FieldCondition]: out = [] if isinstance(value, dict): @@ -2053,7 +2046,7 @@ class Qdrant(VectorStore): ] ) - def _embed_query(self, query: str) -> List[float]: + def _embed_query(self, query: str) -> list[float]: """Embed query text. Used to provide backward compatibility with `embedding_function` argument. @@ -2073,7 +2066,7 @@ class Qdrant(VectorStore): raise ValueError("Neither of embeddings or embedding_function is set") return embedding.tolist() if hasattr(embedding, "tolist") else embedding - async def _aembed_query(self, query: str) -> List[float]: + async def _aembed_query(self, query: str) -> list[float]: """Embed query text asynchronously. Used to provide backward compatibility with `embedding_function` argument. @@ -2093,7 +2086,7 @@ class Qdrant(VectorStore): raise ValueError("Neither of embeddings or embedding_function is set") return embedding.tolist() if hasattr(embedding, "tolist") else embedding - def _embed_texts(self, texts: Iterable[str]) -> List[List[float]]: + def _embed_texts(self, texts: Iterable[str]) -> list[list[float]]: """Embed search texts. Used to provide backward compatibility with `embedding_function` argument. @@ -2120,7 +2113,7 @@ class Qdrant(VectorStore): return embeddings - async def _aembed_texts(self, texts: Iterable[str]) -> List[List[float]]: + async def _aembed_texts(self, texts: Iterable[str]) -> list[list[float]]: """Embed search texts. Used to provide backward compatibility with `embedding_function` argument. @@ -2150,10 +2143,10 @@ class Qdrant(VectorStore): def _generate_rest_batches( self, texts: Iterable[str], - metadatas: Optional[List[dict]] = None, + metadatas: Optional[list[dict]] = None, ids: Optional[Sequence[str]] = None, batch_size: int = 64, - ) -> Generator[Tuple[List[str], List[models.PointStruct]], None, None]: + ) -> Generator[tuple[list[str], list[models.PointStruct]], None, None]: texts_iterator = iter(texts) metadatas_iterator = iter(metadatas or []) ids_iterator = iter(ids or [uuid.uuid4().hex for _ in iter(texts)]) @@ -2190,10 +2183,10 @@ class Qdrant(VectorStore): async def _agenerate_rest_batches( self, texts: Iterable[str], - metadatas: Optional[List[dict]] = None, + metadatas: Optional[list[dict]] = None, ids: Optional[Sequence[str]] = None, batch_size: int = 64, - ) -> AsyncGenerator[Tuple[List[str], List[models.PointStruct]], None]: + ) -> AsyncGenerator[tuple[list[str], list[models.PointStruct]], None]: texts_iterator = iter(texts) metadatas_iterator = iter(metadatas or []) ids_iterator = iter(ids or [uuid.uuid4().hex for _ in iter(texts)]) @@ -2241,7 +2234,7 @@ class Qdrant(VectorStore): host: Optional[str] = None, path: Optional[str] = None, **kwargs: Any, - ) -> Tuple[QdrantClient, Optional[AsyncQdrantClient]]: + ) -> tuple[QdrantClient, Optional[AsyncQdrantClient]]: if api_key is None: api_key = os.getenv("QDRANT_API_KEY") diff --git a/libs/partners/qdrant/pyproject.toml b/libs/partners/qdrant/pyproject.toml index ba2f28f29af..fca522417d9 100644 --- a/libs/partners/qdrant/pyproject.toml +++ b/libs/partners/qdrant/pyproject.toml @@ -48,7 +48,11 @@ typing = ["mypy<2.0,>=1.10", "simsimd<7.0.0,>=6.0.0", "langchain-core"] langchain-core = { path = "../../core", editable = true } [tool.ruff] -select = ["E", "F", "I"] +target-version = "py39" + +[tool.ruff.lint] +select = ["E", "F", "I", "T201", "UP"] +ignore = [ "UP007", ] [tool.mypy] disallow_untyped_defs = true diff --git a/libs/partners/qdrant/scripts/check_imports.py b/libs/partners/qdrant/scripts/check_imports.py index ba8de50118c..ec3fc6e95f5 100644 --- a/libs/partners/qdrant/scripts/check_imports.py +++ b/libs/partners/qdrant/scripts/check_imports.py @@ -10,8 +10,6 @@ if __name__ == "__main__": SourceFileLoader("x", file).load_module() except Exception: has_failure = True - print(file) traceback.print_exc() - print() sys.exit(1 if has_failure else 0) diff --git a/libs/partners/qdrant/tests/integration_tests/common.py b/libs/partners/qdrant/tests/integration_tests/common.py index 3c06cc9019e..b63a6e18618 100644 --- a/libs/partners/qdrant/tests/integration_tests/common.py +++ b/libs/partners/qdrant/tests/integration_tests/common.py @@ -1,5 +1,3 @@ -from typing import List - import requests # type: ignore from langchain_core.documents import Document from langchain_core.embeddings import Embeddings @@ -18,7 +16,7 @@ def qdrant_running_locally() -> bool: return False -def assert_documents_equals(actual: List[Document], expected: List[Document]): # type: ignore[no-untyped-def] +def assert_documents_equals(actual: list[Document], expected: list[Document]): # type: ignore[no-untyped-def] assert len(actual) == len(expected) for actual_doc, expected_doc in zip(actual, expected): @@ -38,22 +36,22 @@ class ConsistentFakeEmbeddings(Embeddings): vectors for the same texts.""" def __init__(self, dimensionality: int = 10) -> None: - self.known_texts: List[str] = [] + self.known_texts: list[str] = [] self.dimensionality = dimensionality - def embed_documents(self, texts: List[str]) -> List[List[float]]: + def embed_documents(self, texts: list[str]) -> list[list[float]]: """Return consistent embeddings for each text seen so far.""" out_vectors = [] for text in texts: if text not in self.known_texts: self.known_texts.append(text) - vector = [float(1.0)] * (self.dimensionality - 1) + [ + vector = [1.0] * (self.dimensionality - 1) + [ float(self.known_texts.index(text)) ] out_vectors.append(vector) return out_vectors - def embed_query(self, text: str) -> List[float]: + def embed_query(self, text: str) -> list[float]: """Return consistent embeddings for the text, if seen before, or a constant one if the text is unknown.""" return self.embed_documents([text])[0] @@ -64,10 +62,10 @@ class ConsistentFakeSparseEmbeddings(SparseEmbeddings): "to return consistent vectors for the same texts.""" def __init__(self, dimensionality: int = 25) -> None: - self.known_texts: List[str] = [] + self.known_texts: list[str] = [] self.dimensionality = 25 - def embed_documents(self, texts: List[str]) -> List[SparseVector]: + def embed_documents(self, texts: list[str]) -> list[SparseVector]: """Return consistent embeddings for each text seen so far.""" out_vectors = [] for text in texts: diff --git a/libs/partners/qdrant/tests/integration_tests/fixtures.py b/libs/partners/qdrant/tests/integration_tests/fixtures.py index 7a92abe475d..6e059cbc671 100644 --- a/libs/partners/qdrant/tests/integration_tests/fixtures.py +++ b/libs/partners/qdrant/tests/integration_tests/fixtures.py @@ -1,6 +1,5 @@ import logging import os -from typing import List from langchain_qdrant.qdrant import RetrievalMode from tests.integration_tests.common import qdrant_running_locally @@ -8,7 +7,7 @@ from tests.integration_tests.common import qdrant_running_locally logger = logging.getLogger(__name__) -def qdrant_locations(use_in_memory: bool = True) -> List[str]: +def qdrant_locations(use_in_memory: bool = True) -> list[str]: locations = [] if use_in_memory: @@ -28,7 +27,7 @@ def qdrant_locations(use_in_memory: bool = True) -> List[str]: def retrieval_modes( *, dense: bool = True, sparse: bool = True, hybrid: bool = True -) -> List[RetrievalMode]: +) -> list[RetrievalMode]: modes = [] if dense: diff --git a/libs/partners/qdrant/tests/integration_tests/qdrant_vector_store/test_add_texts.py b/libs/partners/qdrant/tests/integration_tests/qdrant_vector_store/test_add_texts.py index 08c006a596f..5908ba4b7e2 100644 --- a/libs/partners/qdrant/tests/integration_tests/qdrant_vector_store/test_add_texts.py +++ b/libs/partners/qdrant/tests/integration_tests/qdrant_vector_store/test_add_texts.py @@ -1,5 +1,5 @@ import uuid -from typing import List, Union +from typing import Union import pytest from langchain_core.documents import Document @@ -118,7 +118,7 @@ def test_qdrant_add_texts_stores_ids( batch_size: int, ) -> None: """Test end to end Qdrant.add_texts stores provided ids.""" - ids: List[Union[str, int]] = [ + ids: list[Union[str, int]] = [ "fa38d572-4c31-4579-aedc-1960d79df6df", 432, 432145435, diff --git a/libs/partners/qdrant/tests/integration_tests/qdrant_vector_store/test_from_texts.py b/libs/partners/qdrant/tests/integration_tests/qdrant_vector_store/test_from_texts.py index 65f76dc9ebe..437d9525e4e 100644 --- a/libs/partners/qdrant/tests/integration_tests/qdrant_vector_store/test_from_texts.py +++ b/libs/partners/qdrant/tests/integration_tests/qdrant_vector_store/test_from_texts.py @@ -1,5 +1,5 @@ import uuid -from typing import List, Union +from typing import Union import pytest from langchain_core.documents import Document @@ -49,7 +49,7 @@ def test_qdrant_from_texts_stores_ids( ) -> None: """Test end to end Qdrant.from_texts stores provided ids.""" collection_name = uuid.uuid4().hex - ids: List[Union[str, int]] = [ + ids: list[Union[str, int]] = [ "fa38d572-4c31-4579-aedc-1960d79df6df", 786, ] diff --git a/libs/partners/voyageai/langchain_voyageai/embeddings.py b/libs/partners/voyageai/langchain_voyageai/embeddings.py index c5d8efcc320..1accc645c1d 100644 --- a/libs/partners/voyageai/langchain_voyageai/embeddings.py +++ b/libs/partners/voyageai/langchain_voyageai/embeddings.py @@ -1,5 +1,6 @@ import logging -from typing import Any, Iterable, List, Literal, Optional, cast +from collections.abc import Iterable +from typing import Any, Literal, Optional, cast import voyageai # type: ignore from langchain_core.embeddings import Embeddings @@ -85,7 +86,7 @@ class VoyageAIEmbeddings(BaseModel, Embeddings): self._aclient = voyageai.client_async.AsyncClient(api_key=api_key_str) return self - def _get_batch_iterator(self, texts: List[str]) -> Iterable: + def _get_batch_iterator(self, texts: list[str]) -> Iterable: if self.show_progress_bar: try: from tqdm.auto import tqdm # type: ignore @@ -101,9 +102,9 @@ class VoyageAIEmbeddings(BaseModel, Embeddings): return _iter - def embed_documents(self, texts: List[str]) -> List[List[float]]: + def embed_documents(self, texts: list[str]) -> list[list[float]]: """Embed search docs.""" - embeddings: List[List[float]] = [] + embeddings: list[list[float]] = [] _iter = self._get_batch_iterator(texts) for i in _iter: @@ -114,10 +115,10 @@ class VoyageAIEmbeddings(BaseModel, Embeddings): truncation=self.truncation, output_dimension=self.output_dimension, ).embeddings - embeddings.extend(cast(Iterable[List[float]], r)) + embeddings.extend(cast(Iterable[list[float]], r)) return embeddings - def embed_query(self, text: str) -> List[float]: + def embed_query(self, text: str) -> list[float]: """Embed query text.""" r = self._client.embed( [text], @@ -126,10 +127,10 @@ class VoyageAIEmbeddings(BaseModel, Embeddings): truncation=self.truncation, output_dimension=self.output_dimension, ).embeddings[0] - return cast(List[float], r) + return cast(list[float], r) - async def aembed_documents(self, texts: List[str]) -> List[List[float]]: - embeddings: List[List[float]] = [] + async def aembed_documents(self, texts: list[str]) -> list[list[float]]: + embeddings: list[list[float]] = [] _iter = self._get_batch_iterator(texts) for i in _iter: @@ -140,11 +141,11 @@ class VoyageAIEmbeddings(BaseModel, Embeddings): truncation=self.truncation, output_dimension=self.output_dimension, ) - embeddings.extend(cast(Iterable[List[float]], r.embeddings)) + embeddings.extend(cast(Iterable[list[float]], r.embeddings)) return embeddings - async def aembed_query(self, text: str) -> List[float]: + async def aembed_query(self, text: str) -> list[float]: r = await self._aclient.embed( [text], model=self.model, @@ -152,4 +153,4 @@ class VoyageAIEmbeddings(BaseModel, Embeddings): truncation=self.truncation, output_dimension=self.output_dimension, ) - return cast(List[float], r.embeddings[0]) + return cast(list[float], r.embeddings[0]) diff --git a/libs/partners/voyageai/langchain_voyageai/rerank.py b/libs/partners/voyageai/langchain_voyageai/rerank.py index 4be3d8f7a2d..8a9e4f06973 100644 --- a/libs/partners/voyageai/langchain_voyageai/rerank.py +++ b/libs/partners/voyageai/langchain_voyageai/rerank.py @@ -1,8 +1,9 @@ from __future__ import annotations import os +from collections.abc import Sequence from copy import deepcopy -from typing import Any, Dict, Optional, Sequence, Union +from typing import Any, Optional, Union import voyageai # type: ignore from langchain_core.callbacks.manager import Callbacks @@ -34,7 +35,7 @@ class VoyageAIRerank(BaseDocumentCompressor): @model_validator(mode="before") @classmethod - def validate_environment(cls, values: Dict) -> Any: + def validate_environment(cls, values: dict) -> Any: """Validate that api key exists in environment.""" voyage_api_key = values.get("voyage_api_key") or os.getenv( "VOYAGE_API_KEY", None diff --git a/libs/partners/voyageai/pyproject.toml b/libs/partners/voyageai/pyproject.toml index d043227de21..76ffed4207f 100644 --- a/libs/partners/voyageai/pyproject.toml +++ b/libs/partners/voyageai/pyproject.toml @@ -47,8 +47,12 @@ langchain-core = { path = "../../core", editable = true } [tool.mypy] disallow_untyped_defs = "True" +[tool.ruff] +target-version = "py39" + [tool.ruff.lint] -select = ["E", "F", "I"] +select = ["E", "F", "I", "UP"] +ignore = [ "UP007", ] [tool.coverage.run] omit = ["tests/*"] diff --git a/libs/partners/xai/langchain_xai/chat_models.py b/libs/partners/xai/langchain_xai/chat_models.py index f3f8553551d..5690843f261 100644 --- a/libs/partners/xai/langchain_xai/chat_models.py +++ b/libs/partners/xai/langchain_xai/chat_models.py @@ -2,11 +2,8 @@ from typing import ( Any, - Dict, - List, Literal, Optional, - Type, TypeVar, Union, ) @@ -23,8 +20,8 @@ from pydantic import BaseModel, ConfigDict, Field, SecretStr, model_validator from typing_extensions import Self _BM = TypeVar("_BM", bound=BaseModel) -_DictOrPydanticClass = Union[Dict[str, Any], Type[_BM], Type] -_DictOrPydantic = Union[Dict, _BM] +_DictOrPydanticClass = Union[dict[str, Any], type[_BM], type] +_DictOrPydantic = Union[dict, _BM] class ChatXAI(BaseChatOpenAI): # type: ignore[override] @@ -285,7 +282,7 @@ class ChatXAI(BaseChatOpenAI): # type: ignore[override] ) @property - def lc_secrets(self) -> Dict[str, str]: + def lc_secrets(self) -> dict[str, str]: """A map of constructor argument names to secret ids. For example, @@ -294,17 +291,17 @@ class ChatXAI(BaseChatOpenAI): # type: ignore[override] return {"xai_api_key": "XAI_API_KEY"} @classmethod - def get_lc_namespace(cls) -> List[str]: + def get_lc_namespace(cls) -> list[str]: """Get the namespace of the langchain object.""" return ["langchain_xai", "chat_models"] @property - def lc_attributes(self) -> Dict[str, Any]: + def lc_attributes(self) -> dict[str, Any]: """List of attribute names that should be included in the serialized kwargs. These attributes must be accepted by the constructor. """ - attributes: Dict[str, Any] = {} + attributes: dict[str, Any] = {} if self.xai_api_base: attributes["xai_api_base"] = self.xai_api_base @@ -322,7 +319,7 @@ class ChatXAI(BaseChatOpenAI): # type: ignore[override] return "xai-chat" def _get_ls_params( - self, stop: Optional[List[str]] = None, **kwargs: Any + self, stop: Optional[list[str]] = None, **kwargs: Any ) -> LangSmithParams: """Get the parameters used to invoke the model.""" params = super()._get_ls_params(stop=stop, **kwargs) diff --git a/libs/partners/xai/pyproject.toml b/libs/partners/xai/pyproject.toml index 141eb1fae47..a82b353c51b 100644 --- a/libs/partners/xai/pyproject.toml +++ b/libs/partners/xai/pyproject.toml @@ -50,8 +50,12 @@ langchain-openai = { path = "../openai", editable = true } [tool.mypy] disallow_untyped_defs = "True" +[tool.ruff] +target-version = "py39" + [tool.ruff.lint] -select = ["E", "F", "I", "D"] +select = ["E", "F", "I", "D", "UP"] +ignore = [ "UP007", ] [tool.coverage.run] omit = ["tests/*"] diff --git a/libs/partners/xai/tests/integration_tests/test_chat_models_standard.py b/libs/partners/xai/tests/integration_tests/test_chat_models_standard.py index c4952a50663..632b518f71d 100644 --- a/libs/partners/xai/tests/integration_tests/test_chat_models_standard.py +++ b/libs/partners/xai/tests/integration_tests/test_chat_models_standard.py @@ -1,7 +1,5 @@ """Standard LangChain interface tests""" -from typing import Type - import pytest # type: ignore[import-not-found] from langchain_core.language_models import BaseChatModel from langchain_core.rate_limiters import InMemoryRateLimiter @@ -20,7 +18,7 @@ rate_limiter = InMemoryRateLimiter( class TestXAIStandard(ChatModelIntegrationTests): @property - def chat_model_class(self) -> Type[BaseChatModel]: + def chat_model_class(self) -> type[BaseChatModel]: return ChatXAI @property diff --git a/libs/partners/xai/tests/unit_tests/test_chat_models_standard.py b/libs/partners/xai/tests/unit_tests/test_chat_models_standard.py index a70718f3224..7c9a947a913 100644 --- a/libs/partners/xai/tests/unit_tests/test_chat_models_standard.py +++ b/libs/partners/xai/tests/unit_tests/test_chat_models_standard.py @@ -1,7 +1,5 @@ """Standard LangChain interface tests""" -from typing import Tuple, Type - from langchain_core.language_models import BaseChatModel from langchain_tests.unit_tests import ( # type: ignore[import-not-found] ChatModelUnitTests, # type: ignore[import-not-found] @@ -12,7 +10,7 @@ from langchain_xai import ChatXAI class TestXAIStandard(ChatModelUnitTests): @property - def chat_model_class(self) -> Type[BaseChatModel]: + def chat_model_class(self) -> type[BaseChatModel]: return ChatXAI @property @@ -20,7 +18,7 @@ class TestXAIStandard(ChatModelUnitTests): return {"model": "grok-beta"} @property - def init_from_env_params(self) -> Tuple[dict, dict, dict]: + def init_from_env_params(self) -> tuple[dict, dict, dict]: return ( { "XAI_API_KEY": "api_key",