From 5648159d27549e3f546c071df172a6b30dd19d86 Mon Sep 17 00:00:00 2001 From: Ankur-singh Date: Thu, 23 Jan 2025 23:29:36 +0000 Subject: [PATCH 1/4] integrate polite guard component Signed-off-by: Ankur-singh --- comps/guardrails/src/polite_guard/Dockerfile | 31 +++++++++ comps/guardrails/src/polite_guard/__init__.py | 2 + .../src/polite_guard/integrations/__init__.py | 2 + .../polite_guard/integrations/politeguard.py | 48 +++++++++++++ .../opea_polite_guard_microservice.py | 68 +++++++++++++++++++ .../src/polite_guard/requirements.txt | 16 +++++ 6 files changed, 167 insertions(+) create mode 100644 comps/guardrails/src/polite_guard/Dockerfile create mode 100644 comps/guardrails/src/polite_guard/__init__.py create mode 100644 comps/guardrails/src/polite_guard/integrations/__init__.py create mode 100644 comps/guardrails/src/polite_guard/integrations/politeguard.py create mode 100644 comps/guardrails/src/polite_guard/opea_polite_guard_microservice.py create mode 100644 comps/guardrails/src/polite_guard/requirements.txt diff --git a/comps/guardrails/src/polite_guard/Dockerfile b/comps/guardrails/src/polite_guard/Dockerfile new file mode 100644 index 0000000000..f3d197a802 --- /dev/null +++ b/comps/guardrails/src/polite_guard/Dockerfile @@ -0,0 +1,31 @@ +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +FROM python:3.11-slim + +ENV LANG=C.UTF-8 + +ARG ARCH="cpu" + +RUN apt-get update -y && apt-get install -y --no-install-recommends --fix-missing \ + libgl1-mesa-glx \ + libjemalloc-dev + + +RUN useradd -m -s /bin/bash user && \ + mkdir -p /home/user && \ + chown -R user /home/user/ + +USER user + +COPY comps /home/user/comps + +RUN pip install --no-cache-dir --upgrade pip && \ + if [ ${ARCH} = "cpu" ]; then pip install --no-cache-dir torch --index-url https://download.pytorch.org/whl/cpu; fi && \ + pip install --no-cache-dir -r /home/user/comps/guardrails/src/polite_guard/requirements.txt + +ENV PYTHONPATH=$PYTHONPATH:/home/user + +WORKDIR /home/user/comps/guardrails/src/polite_guard/ + +ENTRYPOINT ["python", "opea_polite_guard_microservice.py"] diff --git a/comps/guardrails/src/polite_guard/__init__.py b/comps/guardrails/src/polite_guard/__init__.py new file mode 100644 index 0000000000..916f3a44b2 --- /dev/null +++ b/comps/guardrails/src/polite_guard/__init__.py @@ -0,0 +1,2 @@ +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 diff --git a/comps/guardrails/src/polite_guard/integrations/__init__.py b/comps/guardrails/src/polite_guard/integrations/__init__.py new file mode 100644 index 0000000000..4057dc0163 --- /dev/null +++ b/comps/guardrails/src/polite_guard/integrations/__init__.py @@ -0,0 +1,2 @@ +# Copyright (C) 2025 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 diff --git a/comps/guardrails/src/polite_guard/integrations/politeguard.py b/comps/guardrails/src/polite_guard/integrations/politeguard.py new file mode 100644 index 0000000000..d8b7ba21d7 --- /dev/null +++ b/comps/guardrails/src/polite_guard/integrations/politeguard.py @@ -0,0 +1,48 @@ +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import asyncio +import os + +from transformers import pipeline + +from comps import CustomLogger, OpeaComponent, OpeaComponentRegistry, ServiceType, TextDoc + +logger = CustomLogger("opea_polite_guard") +logflag = os.getenv("LOGFLAG", False) + + +@OpeaComponentRegistry.register("OPEA_POLITE_GUARD") +class OpeaPoliteGuard(OpeaComponent): + """A specialized politeness detection component derived from OpeaComponent.""" + + def __init__(self, name: str, description: str, config: dict = None): + super().__init__(name, ServiceType.GUARDRAIL.name.lower(), description, config) + self.model = os.getenv("POLITE_GUARD_MODEL", "Intel/polite-guard") + self.polite_pipeline = pipeline("text-classification", model=self.model, tokenizer=self.model) + health_status = self.check_health() + if not health_status: + logger.error("OpeaPoliteGuard health check failed.") + + async def invoke(self, input: str): + """Invokes the polite guard for the input. + + Args: + input (Input str) + """ + response = await asyncio.to_thread(self.polite_pipeline, input) + if response[0]["label"] == "impolite": + return TextDoc(text=f"Violated policies: Impolite (score: {response[0]['score']:0.2f}), please check your input.", downstream_black_list=[".*"]) + else: + return TextDoc(text=input) + + def check_health(self) -> bool: + """Checks the health of the animation service. + + Returns: + bool: True if the service is reachable and healthy, False otherwise. + """ + if self.polite_pipeline: + return True + else: + return False diff --git a/comps/guardrails/src/polite_guard/opea_polite_guard_microservice.py b/comps/guardrails/src/polite_guard/opea_polite_guard_microservice.py new file mode 100644 index 0000000000..f5fe1149ef --- /dev/null +++ b/comps/guardrails/src/polite_guard/opea_polite_guard_microservice.py @@ -0,0 +1,68 @@ +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import os +import time + +from integrations.politeguard import OpeaPoliteGuard + +from comps import ( + CustomLogger, + OpeaComponentLoader, + ServiceType, + TextDoc, + opea_microservices, + register_microservice, + register_statistics, + statistics_dict, +) + +logger = CustomLogger("opea_polite_guard_microservice") +logflag = os.getenv("LOGFLAG", False) + +polite_guard_component_name = os.getenv("POLITE_GUARD_COMPONENT_NAME", "OPEA_POLITE_GUARD") +# Initialize OpeaComponentLoader +loader = OpeaComponentLoader( + polite_guard_component_name, + name=polite_guard_component_name, + description=f"OPEA Polite Guard Component: {polite_guard_component_name}", +) + + +@register_microservice( + name="opea_service@polite_guard", + service_type=ServiceType.GUARDRAIL, + endpoint="/v1/polite", + host="0.0.0.0", + port=9092, + input_datatype=TextDoc, + output_datatype=TextDoc, +) +@register_statistics(names=["opea_service@polite_guard"]) +async def llm_generate(input: TextDoc): + start = time.time() + + # Log the input if logging is enabled + if logflag: + logger.info(f"Input received: {input}") + + try: + # Use the loader to invoke the component + bias_response = await loader.invoke(input.text) + + # Log the result if logging is enabled + if logflag: + logger.info(f"Output received: {bias_response}") + + # Record statistics + statistics_dict["opea_service@polite_guard"].append_latency(time.time() - start, None) + return bias_response + + except Exception as e: + logger.error(f"Error during polite guard invocation: {e}") + raise + + +if __name__ == "__main__": + opea_microservices["opea_service@polite_guard"].start() + logger.info("OPEA Polite Guard Microservice is up and running successfully...") diff --git a/comps/guardrails/src/polite_guard/requirements.txt b/comps/guardrails/src/polite_guard/requirements.txt new file mode 100644 index 0000000000..abca61e88c --- /dev/null +++ b/comps/guardrails/src/polite_guard/requirements.txt @@ -0,0 +1,16 @@ +aiohttp +docarray[full] +fastapi +httpx +huggingface_hub +langchain-community +langchain-huggingface +opentelemetry-api +opentelemetry-exporter-otlp +opentelemetry-sdk +prometheus-fastapi-instrumentator +pyyaml +requests +shortuuid +transformers +uvicorn From 2f31a4f5872ade88157bf52f50b7196391e10266 Mon Sep 17 00:00:00 2001 From: Ankur-singh Date: Thu, 23 Jan 2025 23:49:45 +0000 Subject: [PATCH 2/4] add polite guard microservice readme Signed-off-by: Ankur-singh --- comps/guardrails/src/polite_guard/README.md | 90 +++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 comps/guardrails/src/polite_guard/README.md diff --git a/comps/guardrails/src/polite_guard/README.md b/comps/guardrails/src/polite_guard/README.md new file mode 100644 index 0000000000..977375ba6e --- /dev/null +++ b/comps/guardrails/src/polite_guard/README.md @@ -0,0 +1,90 @@ +# Politeness Guard Microservice + +## Introduction + +The Polite Guard Microservice allows AI application developers to ensure that user input and Large Language Model (LLM) outputs remain polite and respectful. By leveraging, [Polite Guard](https://huggingface.co/Intel/polite-guard), a fine-tuned Transformer model for politeness classification, this lightweight guardrails microservice ensures courteous interactions without significantly sacrificing performance, making it suitable for deployment on both Intel Gaudi and Xeon. + +Politeness plays a crucial role in creating a positive and respectful environment. The service classifies text into four categories: *polite*, *somewhat polite*, *neutral*, and *impolite*. Any *impolite* text is rejected, along with a score, ensuring that systems maintain a courteous tone. + +More details about the Polite Guard model can be found [here](https://github.com/intel/polite-guard). + + +## 🚀1. Start Microservice with Python(Option 1) + +### 1.1 Install Requirements + +```bash +pip install -r requirements.txt +``` + +### 1.2 Start Politeness Detection Microservice with Python Script + +```bash +python opea_polite_guard_microservice.py +``` + +## 🚀2. Start Microservice with Docker (Option 2) + +### 2.1 Prepare bias detection model + +```bash +export HUGGINGFACEHUB_API_TOKEN=${YOUR_HF_TOKEN} +``` + +### 2.2 Build Docker Image + +```bash +cd ../../../ # back to GenAIComps/ folder +docker build -t opea/guardrails-politeness-detection:latest --build-arg https_proxy=$https_proxy --build-arg http_proxy=$http_proxy -f comps/guardrails/src/polite_guard/Dockerfile . +``` + +### 2.3 Run Docker Container with Microservice + +```bash +docker run -d --rm --runtime=runc --name="guardrails-politeness-detection" -p 9092:9092 --ipc=host -e http_proxy=$http_proxy -e https_proxy=$https_proxy -e HUGGINGFACEHUB_API_TOKEN=${HUGGINGFACEHUB_API_TOKEN} -e HF_TOKEN=${HUGGINGFACEHUB_API_TOKEN} opea/guardrails-politeness-detection:latest +``` + +### 2.4 Get Status of Microservice + +```bash +docker container logs -f guardrails-politeness-detection +``` + +### 2.5 Consume Microservice Pre-LLM/Post-LLM + +Once microservice starts, users can use examples (bash or python) below to apply bias detection for both user's query (Pre-LLM) or LLM's response (Post-LLM) + +**Bash:** + +```bash +curl localhost:9092/v1/polite + -X POST + -d '{"text":"He is stupid"}' + -H 'Content-Type: application/json' +``` + +Example Output: + +```bash +"\nViolated policies: Impolite (score: 1.00), please check your input.\n" +``` + +**Python Script:** + +```python +import requests +import json + +proxies = {"http": ""} +url = "http://localhost:9092/v1/polite" +data = {"text": "He is stupid"} + + +try: + resp = requests.post(url=url, data=data, proxies=proxies) + print(resp.text) + resp.raise_for_status() # Raise an exception for unsuccessful HTTP status codes + print("Request successful!") +except requests.exceptions.RequestException as e: + print("An error occurred:", e) +``` From 5e638de88a978cb3728ee0bfa5ef889c108bf394 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 23:58:41 +0000 Subject: [PATCH 3/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Signed-off-by: Ankur-singh --- comps/guardrails/src/polite_guard/README.md | 5 ++--- .../guardrails/src/polite_guard/integrations/politeguard.py | 5 ++++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/comps/guardrails/src/polite_guard/README.md b/comps/guardrails/src/polite_guard/README.md index 977375ba6e..f7658a143c 100644 --- a/comps/guardrails/src/polite_guard/README.md +++ b/comps/guardrails/src/polite_guard/README.md @@ -2,13 +2,12 @@ ## Introduction -The Polite Guard Microservice allows AI application developers to ensure that user input and Large Language Model (LLM) outputs remain polite and respectful. By leveraging, [Polite Guard](https://huggingface.co/Intel/polite-guard), a fine-tuned Transformer model for politeness classification, this lightweight guardrails microservice ensures courteous interactions without significantly sacrificing performance, making it suitable for deployment on both Intel Gaudi and Xeon. +The Polite Guard Microservice allows AI application developers to ensure that user input and Large Language Model (LLM) outputs remain polite and respectful. By leveraging, [Polite Guard](https://huggingface.co/Intel/polite-guard), a fine-tuned Transformer model for politeness classification, this lightweight guardrails microservice ensures courteous interactions without significantly sacrificing performance, making it suitable for deployment on both Intel Gaudi and Xeon. -Politeness plays a crucial role in creating a positive and respectful environment. The service classifies text into four categories: *polite*, *somewhat polite*, *neutral*, and *impolite*. Any *impolite* text is rejected, along with a score, ensuring that systems maintain a courteous tone. +Politeness plays a crucial role in creating a positive and respectful environment. The service classifies text into four categories: _polite_, _somewhat polite_, _neutral_, and _impolite_. Any _impolite_ text is rejected, along with a score, ensuring that systems maintain a courteous tone. More details about the Polite Guard model can be found [here](https://github.com/intel/polite-guard). - ## 🚀1. Start Microservice with Python(Option 1) ### 1.1 Install Requirements diff --git a/comps/guardrails/src/polite_guard/integrations/politeguard.py b/comps/guardrails/src/polite_guard/integrations/politeguard.py index d8b7ba21d7..dd96260552 100644 --- a/comps/guardrails/src/polite_guard/integrations/politeguard.py +++ b/comps/guardrails/src/polite_guard/integrations/politeguard.py @@ -32,7 +32,10 @@ async def invoke(self, input: str): """ response = await asyncio.to_thread(self.polite_pipeline, input) if response[0]["label"] == "impolite": - return TextDoc(text=f"Violated policies: Impolite (score: {response[0]['score']:0.2f}), please check your input.", downstream_black_list=[".*"]) + return TextDoc( + text=f"Violated policies: Impolite (score: {response[0]['score']:0.2f}), please check your input.", + downstream_black_list=[".*"], + ) else: return TextDoc(text=input) From 6b591e7de5baa1f94cee050e9d3abfb54e0981da Mon Sep 17 00:00:00 2001 From: Ankur-singh Date: Mon, 3 Feb 2025 07:54:36 -0800 Subject: [PATCH 4/4] Update README.md with corrected curl command and improved Python example Signed-off-by: Ankur-singh --- comps/guardrails/src/polite_guard/README.md | 24 ++++++++++----------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/comps/guardrails/src/polite_guard/README.md b/comps/guardrails/src/polite_guard/README.md index f7658a143c..db86722716 100644 --- a/comps/guardrails/src/polite_guard/README.md +++ b/comps/guardrails/src/polite_guard/README.md @@ -56,9 +56,9 @@ Once microservice starts, users can use examples (bash or python) below to apply **Bash:** ```bash -curl localhost:9092/v1/polite - -X POST - -d '{"text":"He is stupid"}' +curl localhost:9092/v1/polite \ + -X POST \ + -d '{"text":"He is stupid"}' \ -H 'Content-Type: application/json' ``` @@ -72,18 +72,16 @@ Example Output: ```python import requests -import json -proxies = {"http": ""} +# Define the URL and payload url = "http://localhost:9092/v1/polite" -data = {"text": "He is stupid"} +payload = {"text": "He is stupid"} +headers = {"Content-Type": "application/json"} +# Send a POST request +response = requests.post(url, json=payload, headers=headers) -try: - resp = requests.post(url=url, data=data, proxies=proxies) - print(resp.text) - resp.raise_for_status() # Raise an exception for unsuccessful HTTP status codes - print("Request successful!") -except requests.exceptions.RequestException as e: - print("An error occurred:", e) +# Print the response +print("Status Code:", response.status_code) +print("Response Body:", response.json()) ```