Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
self_hosted.py237 linesDownload Raw Back to llms
1import importlib.util2import logging3import pickle4from typing import Any, Callable, List, Mapping, Optional5 6from langchain_core.callbacks import CallbackManagerForLLMRun7from langchain_core.language_models.llms import LLM8from pydantic import ConfigDict9 10from langchain_community.llms.utils import enforce_stop_tokens11 12logger = logging.getLogger(__name__)13 14 15def _generate_text(16    pipeline: Any,17    prompt: str,18    *args: Any,19    stop: Optional[List[str]] = None,20    **kwargs: Any,21) -> str:22    """Inference function to send to the remote hardware.23 24    Accepts a pipeline callable (or, more likely,25    a key pointing to the model on the cluster's object store)26    and returns text predictions for each document27    in the batch.28    """29    text = pipeline(prompt, *args, **kwargs)30    if stop is not None:31        text = enforce_stop_tokens(text, stop)32    return text33 34 35def _send_pipeline_to_device(pipeline: Any, device: int) -> Any:36    """Send a pipeline to a device on the cluster."""37    if isinstance(pipeline, str):38        with open(pipeline, "rb") as f:39            # This code path can only be triggered if the user40            # passed allow_dangerous_deserialization=True41            pipeline = pickle.load(f)  # ignore[pickle]: explicit-opt-in42 43    if importlib.util.find_spec("torch") is not None:44        import torch45 46        cuda_device_count = torch.cuda.device_count()47        if device < -1 or (device >= cuda_device_count):48            raise ValueError(49                f"Got device=={device}, "50                f"device is required to be within [-1, {cuda_device_count})"51            )52        if device < 0 and cuda_device_count > 0:53            logger.warning(54                "Device has %d GPUs available. "55                "Provide device={deviceId} to `from_model_id` to use available"56                "GPUs for execution. deviceId is -1 for CPU and "57                "can be a positive integer associated with CUDA device id.",58                cuda_device_count,59            )60 61        pipeline.device = torch.device(device)62        pipeline.model = pipeline.model.to(pipeline.device)63    return pipeline64 65 66class SelfHostedPipeline(LLM):67    """Model inference on self-hosted remote hardware.68 69    Supported hardware includes auto-launched instances on AWS, GCP, Azure,70    and Lambda, as well as servers specified71    by IP address and SSH credentials (such as on-prem, or another72    cloud like Paperspace, Coreweave, etc.).73 74    To use, you should have the ``runhouse`` python package installed.75 76    Example for custom pipeline and inference functions:77        .. code-block:: python78 79            from langchain_community.llms import SelfHostedPipeline80            from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline81            import runhouse as rh82 83            def load_pipeline():84                tokenizer = AutoTokenizer.from_pretrained("gpt2")85                model = AutoModelForCausalLM.from_pretrained("gpt2")86                return pipeline(87                    "text-generation", model=model, tokenizer=tokenizer,88                    max_new_tokens=1089                )90            def inference_fn(pipeline, prompt, stop = None):91                return pipeline(prompt)[0]["generated_text"]92 93            gpu = rh.cluster(name="rh-a10x", instance_type="A100:1")94            llm = SelfHostedPipeline(95                model_load_fn=load_pipeline,96                hardware=gpu,97                model_reqs=model_reqs, inference_fn=inference_fn98            )99    Example for <2GB model (can be serialized and sent directly to the server):100        .. code-block:: python101 102            from langchain_community.llms import SelfHostedPipeline103            import runhouse as rh104            gpu = rh.cluster(name="rh-a10x", instance_type="A100:1")105            my_model = ...106            llm = SelfHostedPipeline.from_pipeline(107                pipeline=my_model,108                hardware=gpu,109                model_reqs=["./", "torch", "transformers"],110            )111    Example passing model path for larger models:112        .. code-block:: python113 114            from langchain_community.llms import SelfHostedPipeline115            import runhouse as rh116            import pickle117            from transformers import pipeline118 119            generator = pipeline(model="gpt2")120            rh.blob(pickle.dumps(generator), path="models/pipeline.pkl"121                ).save().to(gpu, path="models")122            llm = SelfHostedPipeline.from_pipeline(123                pipeline="models/pipeline.pkl",124                hardware=gpu,125                model_reqs=["./", "torch", "transformers"],126            )127    """128 129    pipeline_ref: Any = None  #: :meta private:130    client: Any = None  #: :meta private:131    inference_fn: Callable = _generate_text  #: :meta private:132    """Inference function to send to the remote hardware."""133    hardware: Any = None134    """Remote hardware to send the inference function to."""135    model_load_fn: Callable136    """Function to load the model remotely on the server."""137    load_fn_kwargs: Optional[dict] = None138    """Keyword arguments to pass to the model load function."""139    model_reqs: List[str] = ["./", "torch"]140    """Requirements to install on hardware to inference the model."""141 142    allow_dangerous_deserialization: bool = False143    """Allow deserialization using pickle which can be dangerous if 144    loading compromised data.145    """146 147    model_config = ConfigDict(148        extra="forbid",149    )150 151    def __init__(self, **kwargs: Any):152        """Init the pipeline with an auxiliary function.153 154        The load function must be in global scope to be imported155        and run on the server, i.e. in a module and not a REPL or closure.156        Then, initialize the remote inference function.157        """158        if not kwargs.get("allow_dangerous_deserialization"):159            raise ValueError(160                "SelfHostedPipeline relies on the pickle module. "161                "You will need to set allow_dangerous_deserialization=True "162                "if you want to opt-in to allow deserialization of data using pickle."163                "Data can be compromised by a malicious actor if "164                "not handled properly to include "165                "a malicious payload that when deserialized with "166                "pickle can execute arbitrary code. "167            )168        super().__init__(**kwargs)169        try:170            import runhouse as rh171 172        except ImportError:173            raise ImportError(174                "Could not import runhouse python package. "175                "Please install it with `pip install runhouse`."176            )177 178        remote_load_fn = rh.function(fn=self.model_load_fn).to(179            self.hardware, reqs=self.model_reqs180        )181        _load_fn_kwargs = self.load_fn_kwargs or {}182        self.pipeline_ref = remote_load_fn.remote(**_load_fn_kwargs)183 184        self.client = rh.function(fn=self.inference_fn).to(185            self.hardware, reqs=self.model_reqs186        )187 188    @classmethod189    def from_pipeline(190        cls,191        pipeline: Any,192        hardware: Any,193        model_reqs: Optional[List[str]] = None,194        device: int = 0,195        **kwargs: Any,196    ) -> LLM:197        """Init the SelfHostedPipeline from a pipeline object or string."""198        if not isinstance(pipeline, str):199            logger.warning(200                "Serializing pipeline to send to remote hardware. "201                "Note, it can be quite slow"202                "to serialize and send large models with each execution. "203                "Consider sending the pipeline"204                "to the cluster and passing the path to the pipeline instead."205            )206 207        load_fn_kwargs = {"pipeline": pipeline, "device": device}208        return cls(209            load_fn_kwargs=load_fn_kwargs,210            model_load_fn=_send_pipeline_to_device,211            hardware=hardware,212            model_reqs=["transformers", "torch"] + (model_reqs or []),213            **kwargs,214        )215 216    @property217    def _identifying_params(self) -> Mapping[str, Any]:218        """Get the identifying parameters."""219        return {220            **{"hardware": self.hardware},221        }222 223    @property224    def _llm_type(self) -> str:225        return "self_hosted_llm"226 227    def _call(228        self,229        prompt: str,230        stop: Optional[List[str]] = None,231        run_manager: Optional[CallbackManagerForLLMRun] = None,232        **kwargs: Any,233    ) -> str:234        return self.client(235            pipeline=self.pipeline_ref, prompt=prompt, stop=stop, **kwargs236        )237 
codekingpro/portable-devtools · Team Ai