Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add polite guard #1223

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions comps/guardrails/src/polite_guard/Dockerfile
Original file line number Diff line number Diff line change
@@ -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"]
87 changes: 87 additions & 0 deletions comps/guardrails/src/polite_guard/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# 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

# Define the URL and payload
url = "http://localhost:9092/v1/polite"
payload = {"text": "He is stupid"}
headers = {"Content-Type": "application/json"}

# Send a POST request
response = requests.post(url, json=payload, headers=headers)

# Print the response
print("Status Code:", response.status_code)
print("Response Body:", response.json())
```
2 changes: 2 additions & 0 deletions comps/guardrails/src/polite_guard/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Copyright (C) 2024 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
2 changes: 2 additions & 0 deletions comps/guardrails/src/polite_guard/integrations/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Copyright (C) 2025 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
51 changes: 51 additions & 0 deletions comps/guardrails/src/polite_guard/integrations/politeguard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# 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
Original file line number Diff line number Diff line change
@@ -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...")
16 changes: 16 additions & 0 deletions comps/guardrails/src/polite_guard/requirements.txt
Original file line number Diff line number Diff line change
@@ -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
Loading