From 791d59a2c8f9dd5968ff1375eb9ca729b3b77f52 Mon Sep 17 00:00:00 2001 From: Leonid Ganeline Date: Tue, 7 May 2024 15:04:54 -0700 Subject: [PATCH] community: `callbacks` guard_imports (#21173) Issue: we have several helper functions to import third-party libraries like import_uptrain in [community.callbacks](https://api.python.langchain.com/en/latest/callbacks/langchain_community.callbacks.uptrain_callback.import_uptrain.html#langchain_community.callbacks.uptrain_callback.import_uptrain). And we have core.utils.utils.guard_import that works exactly for this purpose. The import_ functions work inconsistently and rather be private functions. Change: replaced these functions with the guard_import function. Related to #21133 --- .../callbacks/aim_callback.py | 11 +----- .../callbacks/clearml_callback.py | 10 +---- .../callbacks/comet_ml_callback.py | 11 +----- .../callbacks/context_callback.py | 30 +++++++-------- .../callbacks/fiddler_callback.py | 10 +---- .../callbacks/flyte_callback.py | 19 ++++------ .../callbacks/infino_callback.py | 21 ++--------- .../callbacks/mlflow_callback.py | 11 +----- .../callbacks/tracers/comet.py | 31 +++++----------- .../callbacks/uptrain_callback.py | 23 ++++-------- .../langchain_community/callbacks/utils.py | 29 +++------------ .../callbacks/wandb_callback.py | 10 +---- .../callbacks/whylabs_callback.py | 37 ++++++++----------- 13 files changed, 74 insertions(+), 179 deletions(-) diff --git a/libs/community/langchain_community/callbacks/aim_callback.py b/libs/community/langchain_community/callbacks/aim_callback.py index 02debd8c8d..e5d1aa50fe 100644 --- a/libs/community/langchain_community/callbacks/aim_callback.py +++ b/libs/community/langchain_community/callbacks/aim_callback.py @@ -4,19 +4,12 @@ from typing import Any, Dict, List, Optional from langchain_core.agents import AgentAction, AgentFinish from langchain_core.callbacks import BaseCallbackHandler from langchain_core.outputs import LLMResult +from langchain_core.utils import guard_import def import_aim() -> Any: """Import the aim python package and raise an error if it is not installed.""" - try: - import aim - except ImportError: - raise ImportError( - "To use the Aim callback manager you need to have the" - " `aim` python package installed." - "Please install it with `pip install aim`" - ) - return aim + return guard_import("aim") class BaseMetadataCallbackHandler: diff --git a/libs/community/langchain_community/callbacks/clearml_callback.py b/libs/community/langchain_community/callbacks/clearml_callback.py index 9f32547e29..2a8f49cf01 100644 --- a/libs/community/langchain_community/callbacks/clearml_callback.py +++ b/libs/community/langchain_community/callbacks/clearml_callback.py @@ -8,6 +8,7 @@ from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Optional, Sequence from langchain_core.agents import AgentAction, AgentFinish from langchain_core.callbacks import BaseCallbackHandler from langchain_core.outputs import LLMResult +from langchain_core.utils import guard_import from langchain_community.callbacks.utils import ( BaseMetadataCallbackHandler, @@ -25,14 +26,7 @@ if TYPE_CHECKING: def import_clearml() -> Any: """Import the clearml python package and raise an error if it is not installed.""" - try: - import clearml - except ImportError: - raise ImportError( - "To use the clearml callback manager you need to have the `clearml` python " - "package installed. Please install it with `pip install clearml`" - ) - return clearml + return guard_import("clearml") class ClearMLCallbackHandler(BaseMetadataCallbackHandler, BaseCallbackHandler): diff --git a/libs/community/langchain_community/callbacks/comet_ml_callback.py b/libs/community/langchain_community/callbacks/comet_ml_callback.py index 027602caff..a05ba48a00 100644 --- a/libs/community/langchain_community/callbacks/comet_ml_callback.py +++ b/libs/community/langchain_community/callbacks/comet_ml_callback.py @@ -6,6 +6,7 @@ from typing import Any, Callable, Dict, List, Optional, Sequence from langchain_core.agents import AgentAction, AgentFinish from langchain_core.callbacks import BaseCallbackHandler from langchain_core.outputs import Generation, LLMResult +from langchain_core.utils import guard_import import langchain_community from langchain_community.callbacks.utils import ( @@ -21,15 +22,7 @@ LANGCHAIN_MODEL_NAME = "langchain-model" def import_comet_ml() -> Any: """Import comet_ml and raise an error if it is not installed.""" - try: - import comet_ml - except ImportError: - raise ImportError( - "To use the comet_ml callback manager you need to have the " - "`comet_ml` python package installed. Please install it with" - " `pip install comet_ml`" - ) - return comet_ml + return guard_import("comet_ml") def _get_experiment( diff --git a/libs/community/langchain_community/callbacks/context_callback.py b/libs/community/langchain_community/callbacks/context_callback.py index 7b724a8282..5a23fcf310 100644 --- a/libs/community/langchain_community/callbacks/context_callback.py +++ b/libs/community/langchain_community/callbacks/context_callback.py @@ -1,4 +1,5 @@ """Callback handler for Context AI""" + import os from typing import Any, Dict, List from uuid import UUID @@ -6,26 +7,23 @@ from uuid import UUID from langchain_core.callbacks import BaseCallbackHandler from langchain_core.messages import BaseMessage from langchain_core.outputs import LLMResult +from langchain_core.utils import guard_import def import_context() -> Any: """Import the `getcontext` package.""" - try: - import getcontext - from getcontext.generated.models import ( - Conversation, - Message, - MessageRole, - Rating, - ) - from getcontext.token import Credential - except ImportError: - raise ImportError( - "To use the context callback manager you need to have the " - "`getcontext` python package installed (version >=0.3.0). " - "Please install it with `pip install --upgrade python-context`" - ) - return getcontext, Credential, Conversation, Message, MessageRole, Rating + return ( + guard_import("getcontext", pip_name="python-context"), + guard_import("getcontext.token", pip_name="python-context").Credential, + guard_import( + "getcontext.generated.models", pip_name="python-context" + ).Conversation, + guard_import("getcontext.generated.models", pip_name="python-context").Message, + guard_import( + "getcontext.generated.models", pip_name="python-context" + ).MessageRole, + guard_import("getcontext.generated.models", pip_name="python-context").Rating, + ) class ContextCallbackHandler(BaseCallbackHandler): diff --git a/libs/community/langchain_community/callbacks/fiddler_callback.py b/libs/community/langchain_community/callbacks/fiddler_callback.py index 0b25251cc5..95df0851d5 100644 --- a/libs/community/langchain_community/callbacks/fiddler_callback.py +++ b/libs/community/langchain_community/callbacks/fiddler_callback.py @@ -4,6 +4,7 @@ from uuid import UUID from langchain_core.callbacks import BaseCallbackHandler from langchain_core.outputs import LLMResult +from langchain_core.utils import guard_import from langchain_community.callbacks.utils import import_pandas @@ -54,14 +55,7 @@ _dataset_dict = { def import_fiddler() -> Any: """Import the fiddler python package and raise an error if it is not installed.""" - try: - import fiddler - except ImportError: - raise ImportError( - "To use fiddler callback handler you need to have `fiddler-client`" - "package installed. Please install it with `pip install fiddler-client`" - ) - return fiddler + return guard_import("fiddler", pip_name="fiddler-client") # First, define custom callback handler implementations diff --git a/libs/community/langchain_community/callbacks/flyte_callback.py b/libs/community/langchain_community/callbacks/flyte_callback.py index 6028d070e7..e3743eda80 100644 --- a/libs/community/langchain_community/callbacks/flyte_callback.py +++ b/libs/community/langchain_community/callbacks/flyte_callback.py @@ -1,4 +1,5 @@ """FlyteKit callback handler.""" + from __future__ import annotations import logging @@ -8,6 +9,7 @@ from typing import TYPE_CHECKING, Any, Dict, List, Tuple from langchain_core.agents import AgentAction, AgentFinish from langchain_core.callbacks import BaseCallbackHandler from langchain_core.outputs import LLMResult +from langchain_core.utils import guard_import from langchain_community.callbacks.utils import ( BaseMetadataCallbackHandler, @@ -26,17 +28,12 @@ logger = logging.getLogger(__name__) def import_flytekit() -> Tuple[flytekit, renderer]: """Import flytekit and flytekitplugins-deck-standard.""" - try: - import flytekit - from flytekitplugins.deck import renderer - except ImportError: - raise ImportError( - "To use the flyte callback manager you need" - "to have the `flytekit` and `flytekitplugins-deck-standard`" - "packages installed. Please install them with `pip install flytekit`" - "and `pip install flytekitplugins-deck-standard`." - ) - return flytekit, renderer + return ( + guard_import("flytekit"), + guard_import( + "flytekitplugins.deck", pip_name="flytekitplugins-deck-standard" + ).renderer, + ) def analyze_text( diff --git a/libs/community/langchain_community/callbacks/infino_callback.py b/libs/community/langchain_community/callbacks/infino_callback.py index 0b58b598df..3205737409 100644 --- a/libs/community/langchain_community/callbacks/infino_callback.py +++ b/libs/community/langchain_community/callbacks/infino_callback.py @@ -5,32 +5,17 @@ from langchain_core.agents import AgentAction, AgentFinish from langchain_core.callbacks import BaseCallbackHandler from langchain_core.messages import BaseMessage from langchain_core.outputs import ChatGeneration, LLMResult +from langchain_core.utils import guard_import def import_infino() -> Any: """Import the infino client.""" - try: - from infinopy import InfinoClient - except ImportError: - raise ImportError( - "To use the Infino callbacks manager you need to have the" - " `infinopy` python package installed." - "Please install it with `pip install infinopy`" - ) - return InfinoClient() + return guard_import("infinopy").InfinoClient() def import_tiktoken() -> Any: """Import tiktoken for counting tokens for OpenAI models.""" - try: - import tiktoken - except ImportError: - raise ImportError( - "To use the ChatOpenAI model with Infino callback manager, you need to " - "have the `tiktoken` python package installed." - "Please install it with `pip install tiktoken`" - ) - return tiktoken + return guard_import("tiktoken") def get_num_tokens(string: str, openai_model_name: str) -> int: diff --git a/libs/community/langchain_community/callbacks/mlflow_callback.py b/libs/community/langchain_community/callbacks/mlflow_callback.py index 294c3ec682..2fa02dc6b1 100644 --- a/libs/community/langchain_community/callbacks/mlflow_callback.py +++ b/libs/community/langchain_community/callbacks/mlflow_callback.py @@ -12,7 +12,7 @@ from langchain_core.agents import AgentAction, AgentFinish from langchain_core.callbacks import BaseCallbackHandler from langchain_core.documents import Document from langchain_core.outputs import LLMResult -from langchain_core.utils import get_from_dict_or_env +from langchain_core.utils import get_from_dict_or_env, guard_import from langchain_community.callbacks.utils import ( BaseMetadataCallbackHandler, @@ -28,14 +28,7 @@ logger = logging.getLogger(__name__) def import_mlflow() -> Any: """Import the mlflow python package and raise an error if it is not installed.""" - try: - import mlflow - except ImportError: - raise ImportError( - "To use the mlflow callback manager you need to have the `mlflow` python " - "package installed. Please install it with `pip install mlflow>=2.3.0`" - ) - return mlflow + return guard_import("mlflow") def mlflow_callback_metrics() -> List[str]: diff --git a/libs/community/langchain_community/callbacks/tracers/comet.py b/libs/community/langchain_community/callbacks/tracers/comet.py index 3119674d77..6695db4557 100644 --- a/libs/community/langchain_community/callbacks/tracers/comet.py +++ b/libs/community/langchain_community/callbacks/tracers/comet.py @@ -2,6 +2,7 @@ from types import ModuleType, SimpleNamespace from typing import TYPE_CHECKING, Any, Callable, Dict from langchain_core.tracers import BaseTracer +from langchain_core.utils import guard_import if TYPE_CHECKING: from uuid import UUID @@ -23,29 +24,15 @@ def _get_run_type(run: "Run") -> str: def import_comet_llm_api() -> SimpleNamespace: """Import comet_llm api and raise an error if it is not installed.""" - try: - from comet_llm import ( - experiment_info, - flush, - ) - from comet_llm.chains import api as chain_api - from comet_llm.chains import ( - chain, - span, - ) - - except ImportError: - raise ImportError( - "To use the CometTracer you need to have the " - "`comet_llm>=2.0.0` python package installed. Please install it with" - " `pip install -U comet_llm`" - ) + comet_llm = guard_import("comet_llm") + comet_llm_chains = guard_import("comet_llm.chains") + return SimpleNamespace( - chain=chain, - span=span, - chain_api=chain_api, - experiment_info=experiment_info, - flush=flush, + chain=comet_llm_chains.chain, + span=comet_llm_chains.span, + chain_api=comet_llm_chains.api, + experiment_info=comet_llm.experiment_info, + flush=comet_llm.flush, ) diff --git a/libs/community/langchain_community/callbacks/uptrain_callback.py b/libs/community/langchain_community/callbacks/uptrain_callback.py index 5e89592dec..92f162ff65 100644 --- a/libs/community/langchain_community/callbacks/uptrain_callback.py +++ b/libs/community/langchain_community/callbacks/uptrain_callback.py @@ -2,12 +2,12 @@ UpTrain Callback Handler UpTrain is an open-source platform to evaluate and improve LLM applications. It provides -grades for 20+ preconfigured checks (covering language, code, embedding use cases), -performs root cause analyses on instances of failure cases and provides guidance for +grades for 20+ preconfigured checks (covering language, code, embedding use cases), +performs root cause analyses on instances of failure cases and provides guidance for resolving them. -This module contains a callback handler for integrating UpTrain seamlessly into your -pipeline and facilitating diverse evaluations. The callback handler automates various +This module contains a callback handler for integrating UpTrain seamlessly into your +pipeline and facilitating diverse evaluations. The callback handler automates various evaluations to assess the performance and effectiveness of the components within the pipeline. @@ -29,7 +29,7 @@ The evaluations conducted include: 3. Context Compression and Reranking: Re-ranking involves reordering nodes based on relevance to the query and selecting - top n nodes. + top n nodes. Due to the potential reduction in the number of nodes after re-ranking, the following evaluations are performed in addition to the RAG evaluations: @@ -65,6 +65,7 @@ from uuid import UUID from langchain_core.callbacks.base import BaseCallbackHandler from langchain_core.documents import Document from langchain_core.outputs import LLMResult +from langchain_core.utils import guard_import logger = logging.getLogger(__name__) handler = logging.StreamHandler(sys.stdout) @@ -75,17 +76,7 @@ logger.addHandler(handler) def import_uptrain() -> Any: """Import the `uptrain` package.""" - try: - import uptrain - except ImportError as e: - raise ImportError( - "To use the UpTrainCallbackHandler, you need the" - "`uptrain` package. Please install it with" - "`pip install uptrain`.", - e, - ) - - return uptrain + return guard_import("uptrain") class UpTrainDataSchema: diff --git a/libs/community/langchain_community/callbacks/utils.py b/libs/community/langchain_community/callbacks/utils.py index 5f8b82a127..db5dd37bf0 100644 --- a/libs/community/langchain_community/callbacks/utils.py +++ b/libs/community/langchain_community/callbacks/utils.py @@ -2,41 +2,22 @@ import hashlib from pathlib import Path from typing import Any, Dict, Iterable, Tuple, Union +from langchain_core.utils import guard_import + def import_spacy() -> Any: """Import the spacy python package and raise an error if it is not installed.""" - try: - import spacy - except ImportError: - raise ImportError( - "This callback manager requires the `spacy` python " - "package installed. Please install it with `pip install spacy`" - ) - return spacy + return guard_import("spacy") def import_pandas() -> Any: """Import the pandas python package and raise an error if it is not installed.""" - try: - import pandas - except ImportError: - raise ImportError( - "This callback manager requires the `pandas` python " - "package installed. Please install it with `pip install pandas`" - ) - return pandas + return guard_import("pandas") def import_textstat() -> Any: """Import the textstat python package and raise an error if it is not installed.""" - try: - import textstat - except ImportError: - raise ImportError( - "This callback manager requires the `textstat` python " - "package installed. Please install it with `pip install textstat`" - ) - return textstat + return guard_import("textstat") def _flatten_dict( diff --git a/libs/community/langchain_community/callbacks/wandb_callback.py b/libs/community/langchain_community/callbacks/wandb_callback.py index b57aed386a..2a6c0aa251 100644 --- a/libs/community/langchain_community/callbacks/wandb_callback.py +++ b/libs/community/langchain_community/callbacks/wandb_callback.py @@ -7,6 +7,7 @@ from typing import Any, Dict, List, Optional, Sequence, Union from langchain_core.agents import AgentAction, AgentFinish from langchain_core.callbacks import BaseCallbackHandler from langchain_core.outputs import LLMResult +from langchain_core.utils import guard_import from langchain_community.callbacks.utils import ( BaseMetadataCallbackHandler, @@ -20,14 +21,7 @@ from langchain_community.callbacks.utils import ( def import_wandb() -> Any: """Import the wandb python package and raise an error if it is not installed.""" - try: - import wandb - except ImportError: - raise ImportError( - "To use the wandb callback manager you need to have the `wandb` python " - "package installed. Please install it with `pip install wandb`" - ) - return wandb + return guard_import("wandb") def load_json_to_dict(json_path: Union[str, Path]) -> dict: diff --git a/libs/community/langchain_community/callbacks/whylabs_callback.py b/libs/community/langchain_community/callbacks/whylabs_callback.py index ea79008624..abf2061857 100644 --- a/libs/community/langchain_community/callbacks/whylabs_callback.py +++ b/libs/community/langchain_community/callbacks/whylabs_callback.py @@ -4,7 +4,7 @@ import logging from typing import TYPE_CHECKING, Any, Optional from langchain_core.callbacks import BaseCallbackHandler -from langchain_core.utils import get_from_env +from langchain_core.utils import get_from_env, guard_import if TYPE_CHECKING: from whylogs.api.logger.logger import Logger @@ -27,22 +27,15 @@ def import_langkit( Returns: The imported langkit module. """ - try: - import langkit - import langkit.regexes - import langkit.textstat - - if sentiment: - import langkit.sentiment - if toxicity: - import langkit.toxicity - if themes: - import langkit.themes - except ImportError: - raise ImportError( - "To use the whylabs callback manager you need to have the `langkit` python " - "package installed. Please install it with `pip install langkit`." - ) + langkit = guard_import("langkit") + guard_import("langkit.regexes") + guard_import("langkit.textstat") + if sentiment: + guard_import("langkit.sentiment") + if toxicity: + guard_import("langkit.toxicity") + if themes: + guard_import("langkit.themes") return langkit @@ -161,10 +154,12 @@ class WhyLabsCallbackHandler(BaseCallbackHandler): # langkit library will import necessary whylogs libraries import_langkit(sentiment=sentiment, toxicity=toxicity, themes=themes) - import whylogs as why - from langkit.callback_handler import get_callback_instance - from whylogs.api.writer.whylabs import WhyLabsWriter - from whylogs.experimental.core.udf_schema import udf_schema + why = guard_import("whylogs") + get_callback_instance = guard_import( + "langkit.callback_handler" + ).get_callback_instance + WhyLabsWriter = guard_import("whylogs.api.writer.whylabs").WhyLabsWriter + udf_schema = guard_import("whylogs.experimental.core.udf_schema").udf_schema if logger is None: api_key = api_key or get_from_env("api_key", "WHYLABS_API_KEY")