From 4bbc7a2f55f0383479508093ab796cab89aa43b8 Mon Sep 17 00:00:00 2001 From: Sharan Shirodkar <91109427+sharanshirodkar7@users.noreply.github.com> Date: Mon, 23 Sep 2024 04:14:57 -0400 Subject: [PATCH] Prediction Guard Guardrails components (#677) * added files for PG guardrails components Signed-off-by: sharanshirodkar7 * Fix pre-commit issues: end-of-file, requirements.txt, trailing whitespace, imports, and formatting Signed-off-by: sharanshirodkar7 * added package Signed-off-by: sharanshirodkar7 * added package Signed-off-by: sharanshirodkar7 * added package Signed-off-by: sharanshirodkar7 * file structure updated to latest Signed-off-by: sharanshirodkar7 * Fix pre-commit issues: end-of-file, requirements.txt, trailing whitespace, imports, and formatting Signed-off-by: sharanshirodkar7 * Add .DS_Store to .gitignore Signed-off-by: sharanshirodkar7 * updated readme,requirements+changes based on feedback Signed-off-by: sharanshirodkar7 * references classes in init.py Signed-off-by: sharanshirodkar7 * fix readme link error Signed-off-by: sharanshirodkar7 * fix readme link error Signed-off-by: sharanshirodkar7 * fix readme link error Signed-off-by: sharanshirodkar7 * fix readme link error Signed-off-by: sharanshirodkar7 * fix readme link error Signed-off-by: sharanshirodkar7 * fix readme link error Signed-off-by: sharanshirodkar7 * Fix pre-commit issues: end-of-file, requirements.txt, trailing whitespace, imports, and formatting Signed-off-by: sharanshirodkar7 * removed duplicatesa Signed-off-by: sharanshirodkar7 * removed added readme content Signed-off-by: sharanshirodkar7 --------- Signed-off-by: sharanshirodkar7 --- .../docker/compose/guardrails-compose-cd.yaml | 16 +++++ .gitignore | 1 + comps/__init__.py | 4 ++ comps/cores/proto/docarray.py | 20 ++++++ .../factuality/predictionguard/Dockerfile | 15 ++++ .../factuality/predictionguard/README.md | 39 +++++++++++ .../factuality/predictionguard/__init__.py | 2 + .../docker_compose_factuality.yaml | 20 ++++++ .../factuality_predictionguard.py | 47 +++++++++++++ .../predictionguard/requirements.txt | 13 ++++ .../pii_detection/predictionguard/Dockerfile | 16 +++++ .../pii_detection/predictionguard/README.md | 50 +++++++++++++ .../pii_detection/predictionguard/__init__.py | 2 + .../predictionguard/docker_compose_pii.yaml | 20 ++++++ .../predictionguard/pii_predictionguard.py | 54 ++++++++++++++ .../predictionguard/requirements.txt | 13 ++++ .../predictionguard/Dockerfile | 16 +++++ .../predictionguard/README.md | 38 ++++++++++ .../predictionguard/__init__.py | 2 + .../docker_compose_injection.yaml | 20 ++++++ .../injection_predictionguard.py | 42 +++++++++++ .../predictionguard/requirements.txt | 13 ++++ .../predictionguard/Dockerfile | 16 +++++ .../predictionguard/README.md | 38 ++++++++++ .../predictionguard/__init__.py | 2 + .../docker_compose_toxicity.yaml | 20 ++++++ .../predictionguard/requirements.txt | 13 ++++ .../toxicity_predictionguard.py | 42 +++++++++++ ...t_guardrails_factuality_predictionguard.sh | 70 +++++++++++++++++++ ...uardrails_pii_detection_predictionguard.sh | 68 ++++++++++++++++++ ...drails_prompt_injection_predictionguard.sh | 68 ++++++++++++++++++ ...ails_toxicity_detection_predictionguard.sh | 68 ++++++++++++++++++ 32 files changed, 868 insertions(+) create mode 100644 comps/guardrails/factuality/predictionguard/Dockerfile create mode 100644 comps/guardrails/factuality/predictionguard/README.md create mode 100644 comps/guardrails/factuality/predictionguard/__init__.py create mode 100644 comps/guardrails/factuality/predictionguard/docker_compose_factuality.yaml create mode 100644 comps/guardrails/factuality/predictionguard/factuality_predictionguard.py create mode 100644 comps/guardrails/factuality/predictionguard/requirements.txt create mode 100644 comps/guardrails/pii_detection/predictionguard/Dockerfile create mode 100644 comps/guardrails/pii_detection/predictionguard/README.md create mode 100644 comps/guardrails/pii_detection/predictionguard/__init__.py create mode 100644 comps/guardrails/pii_detection/predictionguard/docker_compose_pii.yaml create mode 100644 comps/guardrails/pii_detection/predictionguard/pii_predictionguard.py create mode 100644 comps/guardrails/pii_detection/predictionguard/requirements.txt create mode 100644 comps/guardrails/prompt_injection/predictionguard/Dockerfile create mode 100644 comps/guardrails/prompt_injection/predictionguard/README.md create mode 100644 comps/guardrails/prompt_injection/predictionguard/__init__.py create mode 100644 comps/guardrails/prompt_injection/predictionguard/docker_compose_injection.yaml create mode 100644 comps/guardrails/prompt_injection/predictionguard/injection_predictionguard.py create mode 100644 comps/guardrails/prompt_injection/predictionguard/requirements.txt create mode 100644 comps/guardrails/toxicity_detection/predictionguard/Dockerfile create mode 100644 comps/guardrails/toxicity_detection/predictionguard/README.md create mode 100644 comps/guardrails/toxicity_detection/predictionguard/__init__.py create mode 100644 comps/guardrails/toxicity_detection/predictionguard/docker_compose_toxicity.yaml create mode 100644 comps/guardrails/toxicity_detection/predictionguard/requirements.txt create mode 100644 comps/guardrails/toxicity_detection/predictionguard/toxicity_predictionguard.py create mode 100644 tests/guardrails/test_guardrails_factuality_predictionguard.sh create mode 100644 tests/guardrails/test_guardrails_pii_detection_predictionguard.sh create mode 100644 tests/guardrails/test_guardrails_prompt_injection_predictionguard.sh create mode 100644 tests/guardrails/test_guardrails_toxicity_detection_predictionguard.sh diff --git a/.github/workflows/docker/compose/guardrails-compose-cd.yaml b/.github/workflows/docker/compose/guardrails-compose-cd.yaml index 86b906d48..5146db9fb 100644 --- a/.github/workflows/docker/compose/guardrails-compose-cd.yaml +++ b/.github/workflows/docker/compose/guardrails-compose-cd.yaml @@ -14,3 +14,19 @@ services: build: dockerfile: comps/guardrails/toxicity_detection/Dockerfile image: ${REGISTRY:-opea}/guardrails-toxicity-detection:${TAG:-latest} + guardrails-pii-detection-predictionguard: + build: + dockerfile: comps/guardrails/pii_detection/predictionguard/Dockerfile + image: ${REGISTRY:-opea}/guardrails-pii-predictionguard:${TAG:-latest} + guardrails-toxicity-detection-predictionguard: + build: + dockerfile: comps/guardrails/toxicity_detection/predictionguard/Dockerfile + image: ${REGISTRY:-opea}/guardrails-toxicity-predictionguard:${TAG:-latest} + guardrails-factuality-predictionguard: + build: + dockerfile: comps/guardrails/factuality/predictionguard/Dockerfile + image: ${REGISTRY:-opea}/guardrails-factuality-predictionguard:${TAG:-latest} + guardrails-injection-predictionguard: + build: + dockerfile: comps/guardrails/prompt_injection/predictionguard/Dockerfile + image: ${REGISTRY:-opea}/guardrails-injection-predictionguard:${TAG:-latest} diff --git a/.gitignore b/.gitignore index 567994ceb..1d1e0a389 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ __pycache__ *.egg-info/ +.DS_Store diff --git a/comps/__init__.py b/comps/__init__.py index 2a65ce4fa..753ac3598 100644 --- a/comps/__init__.py +++ b/comps/__init__.py @@ -29,6 +29,10 @@ TextImageDoc, MultimodalDoc, EmbedMultimodalDoc, + FactualityDoc, + ScoreDoc, + PIIRequestDoc, + PIIResponseDoc, ) # Constants diff --git a/comps/cores/proto/docarray.py b/comps/cores/proto/docarray.py index 1776e35ae..3e4b2ed92 100644 --- a/comps/cores/proto/docarray.py +++ b/comps/cores/proto/docarray.py @@ -20,6 +20,26 @@ class TextDoc(BaseDoc, TopologyInfo): text: str = None +class FactualityDoc(BaseDoc): + reference: str + text: str + + +class ScoreDoc(BaseDoc): + score: float + + +class PIIRequestDoc(BaseDoc): + prompt: str + replace: Optional[bool] = False + replace_method: Optional[str] = "random" + + +class PIIResponseDoc(BaseDoc): + detected_pii: Optional[List[dict]] = None + new_prompt: Optional[str] = None + + class MetadataTextDoc(TextDoc): metadata: Optional[Dict[str, Any]] = Field( description="This encloses all metadata associated with the textdoc.", diff --git a/comps/guardrails/factuality/predictionguard/Dockerfile b/comps/guardrails/factuality/predictionguard/Dockerfile new file mode 100644 index 000000000..309b198c5 --- /dev/null +++ b/comps/guardrails/factuality/predictionguard/Dockerfile @@ -0,0 +1,15 @@ +# Copyright (C) 2024 Prediction Guard, Inc. +# SPDX-License-Identitier: Apache-2.0 + +FROM python:3.11-slim + +COPY comps /home/comps + +RUN pip install --no-cache-dir --upgrade pip && \ + pip install --no-cache-dir -r /home/comps/guardrails/factuality/predictionguard/requirements.txt + +ENV PYTHONPATH=$PYTHONPATH:/home + +WORKDIR /home/comps/guardrails/factuality/predictionguard + +ENTRYPOINT ["python", "factuality_predictionguard.py" ] \ No newline at end of file diff --git a/comps/guardrails/factuality/predictionguard/README.md b/comps/guardrails/factuality/predictionguard/README.md new file mode 100644 index 000000000..e0cc8202a --- /dev/null +++ b/comps/guardrails/factuality/predictionguard/README.md @@ -0,0 +1,39 @@ +# Factuality Check Prediction Guard Microservice + +[Prediction Guard](https://docs.predictionguard.com) allows you to utilize hosted open access LLMs, LVMs, and embedding functionality with seamlessly integrated safeguards. In addition to providing a scalable access to open models, Prediction Guard allows you to configure factual consistency checks, toxicity filters, PII filters, and prompt injection blocking. Join the [Prediction Guard Discord channel](https://discord.gg/TFHgnhAFKd) and request an API key to get started. + +Checking for factual consistency can help to ensure that any LLM hallucinations are being found before being returned to a user. This microservice allows the user to compare two text passages (`reference` and `text`). The output will be a float number from 0.0 to 1.0 (with closer to 1.0 indicating more factual consistency between `reference` and `text`). + +# πŸš€ Start Microservice with Docker + +## Setup Environment Variables + +Setup the following environment variables first + +```bash +export PREDICTIONGUARD_API_KEY=${your_predictionguard_api_key} +``` + +## Build Docker Images + +```bash +cd ../../../../ +docker build -t opea/factuality-predictionguard:latest -f comps/guardrails/factuality/predictionguard/Dockerfile . +``` + +## Start Service + +```bash +docker run -d --name="guardrails-factuality-predictionguard" -p 9075:9075 -e PREDICTIONGUARD_API_KEY=$PREDICTIONGUARD_API_KEY opea/guardrails-factuality-predictionguard:latest +``` + +# πŸš€ Consume Factuality Check Service + +```bash +curl -X POST http://localhost:9075/v1/factuality \ + -H 'Content-Type: application/json' \ + -d '{ + "reference": "The sky is blue.", + "text": "The sky is green." + }' +``` diff --git a/comps/guardrails/factuality/predictionguard/__init__.py b/comps/guardrails/factuality/predictionguard/__init__.py new file mode 100644 index 000000000..a246c95e7 --- /dev/null +++ b/comps/guardrails/factuality/predictionguard/__init__.py @@ -0,0 +1,2 @@ +# Copyright (C) 2024 Prediction Guard, Inc. +# SPDX-License-Identifier: Apache-2.0 diff --git a/comps/guardrails/factuality/predictionguard/docker_compose_factuality.yaml b/comps/guardrails/factuality/predictionguard/docker_compose_factuality.yaml new file mode 100644 index 000000000..7fe32fc2a --- /dev/null +++ b/comps/guardrails/factuality/predictionguard/docker_compose_factuality.yaml @@ -0,0 +1,20 @@ +# Copyright (C) 2024 Prediction Guard, Inc +# SPDX-License-Identifier: Apache-2.0 + +services: + factuality: + image: opea/guardrails-factuality-predictionguard:latest + container_name: guardrails-factuality-predictionguard + ports: + - "9075:9075" + ipc: host + environment: + no_proxy: ${no_proxy} + http_proxy: ${http_proxy} + https_proxy: ${https_proxy} + PREDICTIONGUARD_API_KEY: ${PREDICTIONGUARD_API_KEY} + restart: unless-stopped + +networks: + default: + driver: bridge diff --git a/comps/guardrails/factuality/predictionguard/factuality_predictionguard.py b/comps/guardrails/factuality/predictionguard/factuality_predictionguard.py new file mode 100644 index 000000000..170e2c44b --- /dev/null +++ b/comps/guardrails/factuality/predictionguard/factuality_predictionguard.py @@ -0,0 +1,47 @@ +# Copyright (C) 2024 Prediction Guard, Inc. +# SPDX-License-Identified: Apache-2.0 + + +import time + +from docarray import BaseDoc +from predictionguard import PredictionGuard + +from comps import ( + FactualityDoc, + ScoreDoc, + ServiceType, + opea_microservices, + register_microservice, + register_statistics, + statistics_dict, +) + + +@register_microservice( + name="opea_service@factuality_predictionguard", + service_type=ServiceType.GUARDRAIL, + endpoint="/v1/factuality", + host="0.0.0.0", + port=9075, + input_datatype=FactualityDoc, + output_datatype=ScoreDoc, +) +@register_statistics(names=["opea_service@factuality_predictionguard"]) +def factuality_guard(input: FactualityDoc) -> ScoreDoc: + start = time.time() + + client = PredictionGuard() + + reference = input.reference + text = input.text + + result = client.factuality.check(reference=reference, text=text) + + statistics_dict["opea_service@factuality_predictionguard"].append_latency(time.time() - start, None) + return ScoreDoc(score=result["checks"][0]["score"]) + + +if __name__ == "__main__": + print("Prediction Guard Factuality initialized.") + opea_microservices["opea_service@factuality_predictionguard"].start() diff --git a/comps/guardrails/factuality/predictionguard/requirements.txt b/comps/guardrails/factuality/predictionguard/requirements.txt new file mode 100644 index 000000000..17af64b43 --- /dev/null +++ b/comps/guardrails/factuality/predictionguard/requirements.txt @@ -0,0 +1,13 @@ +aiohttp +docarray +fastapi +huggingface_hub +opentelemetry-api +opentelemetry-exporter-otlp +opentelemetry-sdk +Pillow +predictionguard +prometheus-fastapi-instrumentator +shortuuid +transformers +uvicorn diff --git a/comps/guardrails/pii_detection/predictionguard/Dockerfile b/comps/guardrails/pii_detection/predictionguard/Dockerfile new file mode 100644 index 000000000..f9f47ca29 --- /dev/null +++ b/comps/guardrails/pii_detection/predictionguard/Dockerfile @@ -0,0 +1,16 @@ + +# Copyright (C) 2024 Prediction Guard, Inc. +# SPDX-License-Identitier: Apache-2.0 + +FROM python:3.11-slim + +COPY comps /home/comps + +RUN pip install --no-cache-dir --upgrade pip && \ + pip install --no-cache-dir -r /home/comps/guardrails/pii_detection/predictionguard/requirements.txt + +ENV PYTHONPATH=$PYTHONPATH:/home + +WORKDIR /home/comps/guardrails/pii_detection/predictionguard + +ENTRYPOINT ["python", "pii_predictionguard.py" ] \ No newline at end of file diff --git a/comps/guardrails/pii_detection/predictionguard/README.md b/comps/guardrails/pii_detection/predictionguard/README.md new file mode 100644 index 000000000..e9393b8e5 --- /dev/null +++ b/comps/guardrails/pii_detection/predictionguard/README.md @@ -0,0 +1,50 @@ +# PII Detection Prediction Guard Microservice + +[Prediction Guard](https://docs.predictionguard.com) allows you to utilize hosted open access LLMs, LVMs, and embedding functionality with seamlessly integrated safeguards. In addition to providing a scalable access to open models, Prediction Guard allows you to configure factual consistency checks, toxicity filters, PII filters, and prompt injection blocking. Join the [Prediction Guard Discord channel](https://discord.gg/TFHgnhAFKd) and request an API key to get started. + +Detecting Personal Identifiable Information (PII) is important in ensuring that users aren't sending out private data to LLMs. This service allows you to configurably: + +1. Detect PII +2. Replace PII (with "faked" information) +3. Mask PII (with placeholders) + +# πŸš€ Start Microservice with Docker + +## Setup Environment Variables + +Setup the following environment variables first + +```bash +export PREDICTIONGUARD_API_KEY=${your_predictionguard_api_key} +``` + +## Build Docker Images + +```bash +cd ../../../../ +docker build -t opea/guardrails-pii-predictionguard:latest -f comps/guardrails/pii_detection/predictionguard/Dockerfile . +``` + +## Start Service + +```bash +docker run -d --name="guardrails-pii-predictionguard" -p 9080:9080 -e PREDICTIONGUARD_API_KEY=$PREDICTIONGUARD_API_KEY opea/guardrails-pii-predictionguard:latest +``` + +# πŸš€ Consume PII Detection Service + +```bash +curl -X POST http://localhost:9080/v1/pii \ + -H 'Content-Type: application/json' \ + -d '{ + "prompt": "My name is John Doe and my phone number is 555-555-5555.", + "replace": true, + "replace_method": "random" + }' +``` + +API parameters: + +- `prompt` (string, required): The text in which you want to detect PII (typically the prompt that you anticipate sending to an LLM) +- `replace` (boolean, optional, default is `false`): `true` if you want to replace the detected PII in the `prompt` +- `replace_method` (string, optional, default is `random`): The method you want to use to replace PII (set to either `random`, `fake`, `category`, `mask`) diff --git a/comps/guardrails/pii_detection/predictionguard/__init__.py b/comps/guardrails/pii_detection/predictionguard/__init__.py new file mode 100644 index 000000000..a246c95e7 --- /dev/null +++ b/comps/guardrails/pii_detection/predictionguard/__init__.py @@ -0,0 +1,2 @@ +# Copyright (C) 2024 Prediction Guard, Inc. +# SPDX-License-Identifier: Apache-2.0 diff --git a/comps/guardrails/pii_detection/predictionguard/docker_compose_pii.yaml b/comps/guardrails/pii_detection/predictionguard/docker_compose_pii.yaml new file mode 100644 index 000000000..ba646d5be --- /dev/null +++ b/comps/guardrails/pii_detection/predictionguard/docker_compose_pii.yaml @@ -0,0 +1,20 @@ +# Copyright (C) 2024 Prediction Guard, Inc +# SPDX-License-Identifier: Apache-2.0 + +services: + pii: + image: opea/guardrails-pii-predictionguard:latest + container_name: pii-predictionguard + ports: + - "9080:9080" + ipc: host + environment: + no_proxy: ${no_proxy} + http_proxy: ${http_proxy} + https_proxy: ${https_proxy} + PREDICTIONGUARD_API_KEY: ${PREDICTIONGUARD_API_KEY} + restart: unless-stopped + +networks: + default: + driver: bridge diff --git a/comps/guardrails/pii_detection/predictionguard/pii_predictionguard.py b/comps/guardrails/pii_detection/predictionguard/pii_predictionguard.py new file mode 100644 index 000000000..ac89d05fe --- /dev/null +++ b/comps/guardrails/pii_detection/predictionguard/pii_predictionguard.py @@ -0,0 +1,54 @@ +# Copyright (C) 2024 Prediction Guard, Inc. +# SPDX-License-Identified: Apache-2.0 + + +import json +import time +from typing import List, Optional + +from docarray import BaseDoc +from predictionguard import PredictionGuard + +from comps import ( + PIIRequestDoc, + PIIResponseDoc, + ServiceType, + opea_microservices, + register_microservice, + register_statistics, + statistics_dict, +) + + +@register_microservice( + name="opea_service@pii_predictionguard", + service_type=ServiceType.GUARDRAIL, + endpoint="/v1/pii", + host="0.0.0.0", + port=9080, + input_datatype=PIIRequestDoc, + output_datatype=PIIResponseDoc, +) +@register_statistics(names=["opea_service@pii_predictionguard"]) +def pii_guard(input: PIIRequestDoc) -> PIIResponseDoc: + start = time.time() + + client = PredictionGuard() + + prompt = input.prompt + replace = input.replace + replace_method = input.replace_method + + result = client.pii.check(prompt=prompt, replace=replace, replace_method=replace_method) + + statistics_dict["opea_service@pii_predictionguard"].append_latency(time.time() - start, None) + if "new_prompt" in result["checks"][0].keys(): + return PIIResponseDoc(new_prompt=result["checks"][0]["new_prompt"]) + elif "pii_types_and_positions" in result["checks"][0].keys(): + detected_pii = json.loads(result["checks"][0]["pii_types_and_positions"]) + return PIIResponseDoc(detected_pii=detected_pii) + + +if __name__ == "__main__": + print("Prediction Guard PII Detection initialized.") + opea_microservices["opea_service@pii_predictionguard"].start() diff --git a/comps/guardrails/pii_detection/predictionguard/requirements.txt b/comps/guardrails/pii_detection/predictionguard/requirements.txt new file mode 100644 index 000000000..17af64b43 --- /dev/null +++ b/comps/guardrails/pii_detection/predictionguard/requirements.txt @@ -0,0 +1,13 @@ +aiohttp +docarray +fastapi +huggingface_hub +opentelemetry-api +opentelemetry-exporter-otlp +opentelemetry-sdk +Pillow +predictionguard +prometheus-fastapi-instrumentator +shortuuid +transformers +uvicorn diff --git a/comps/guardrails/prompt_injection/predictionguard/Dockerfile b/comps/guardrails/prompt_injection/predictionguard/Dockerfile new file mode 100644 index 000000000..c3f59efe3 --- /dev/null +++ b/comps/guardrails/prompt_injection/predictionguard/Dockerfile @@ -0,0 +1,16 @@ + +# Copyright (C) 2024 Prediction Guard, Inc. +# SPDX-License-Identitier: Apache-2.0 + +FROM python:3.11-slim + +COPY comps /home/comps + +RUN pip install --no-cache-dir --upgrade pip && \ + pip install --no-cache-dir -r /home/comps/guardrails/prompt_injection/predictionguard/requirements.txt + +ENV PYTHONPATH=$PYTHONPATH:/home + +WORKDIR /home/comps/guardrails/prompt_injection/predictionguard + +ENTRYPOINT ["python", "injection_predictionguard.py" ] \ No newline at end of file diff --git a/comps/guardrails/prompt_injection/predictionguard/README.md b/comps/guardrails/prompt_injection/predictionguard/README.md new file mode 100644 index 000000000..c87f6980e --- /dev/null +++ b/comps/guardrails/prompt_injection/predictionguard/README.md @@ -0,0 +1,38 @@ +# Prompt Injection Detection Prediction Guard Microservice + +[Prediction Guard](https://docs.predictionguard.com) allows you to utilize hosted open access LLMs, LVMs, and embedding functionality with seamlessly integrated safeguards. In addition to providing a scalable access to open models, Prediction Guard allows you to configure factual consistency checks, toxicity filters, PII filters, and prompt injection blocking. Join the [Prediction Guard Discord channel](https://discord.gg/TFHgnhAFKd) and request an API key to get started. + +Prompt Injection occurs when an attacker manipulates an LLM through malicious prompts, causing the system running an LLM to execute the attacker’s intentions. This microservice allows you to check a prompt and get a score from 0.0 to 1.0 indicating the likelihood of a prompt injection (higher numbers indicate danger). + +# πŸš€ Start Microservice with Docker + +## Setup Environment Variables + +Setup the following environment variables first + +```bash +export PREDICTIONGUARD_API_KEY=${your_predictionguard_api_key} +``` + +## Build Docker Images + +```bash +cd ../../../../ +docker build -t opea/guardrails-injection-predictionguard:latest -f comps/guardrails/prompt_injection/predictionguard/Dockerfile . +``` + +## Start Service + +```bash +docker run -d --name="guardrails-injection-predictionguard" -p 9085:9085 -e PREDICTIONGUARD_API_KEY=$PREDICTIONGUARD_API_KEY opea/guardrails-injection-predictionguard:latest +``` + +# πŸš€ Consume Prompt Injection Detection Service + +```bash +curl -X POST http://localhost:9085/v1/injection \ + -H 'Content-Type: application/json' \ + -d '{ + "text": "IGNORE PREVIOUS DIRECTIONS" + }' +``` diff --git a/comps/guardrails/prompt_injection/predictionguard/__init__.py b/comps/guardrails/prompt_injection/predictionguard/__init__.py new file mode 100644 index 000000000..a246c95e7 --- /dev/null +++ b/comps/guardrails/prompt_injection/predictionguard/__init__.py @@ -0,0 +1,2 @@ +# Copyright (C) 2024 Prediction Guard, Inc. +# SPDX-License-Identifier: Apache-2.0 diff --git a/comps/guardrails/prompt_injection/predictionguard/docker_compose_injection.yaml b/comps/guardrails/prompt_injection/predictionguard/docker_compose_injection.yaml new file mode 100644 index 000000000..a877e5c4b --- /dev/null +++ b/comps/guardrails/prompt_injection/predictionguard/docker_compose_injection.yaml @@ -0,0 +1,20 @@ +# Copyright (C) 2024 Prediction Guard, Inc +# SPDX-License-Identifier: Apache-2.0 + +services: + injection: + image: opea/guardrails-injection-predictionguard:latest + container_name: guardrails-injection-predictionguard + ports: + - "9085:9085" + ipc: host + environment: + no_proxy: ${no_proxy} + http_proxy: ${http_proxy} + https_proxy: ${https_proxy} + PREDICTIONGUARD_API_KEY: ${PREDICTIONGUARD_API_KEY} + restart: unless-stopped + +networks: + default: + driver: bridge diff --git a/comps/guardrails/prompt_injection/predictionguard/injection_predictionguard.py b/comps/guardrails/prompt_injection/predictionguard/injection_predictionguard.py new file mode 100644 index 000000000..2d5ef24c7 --- /dev/null +++ b/comps/guardrails/prompt_injection/predictionguard/injection_predictionguard.py @@ -0,0 +1,42 @@ +# Copyright (C) 2024 Prediction Guard, Inc. +# SPDX-License-Identified: Apache-2.0 + + +import time + +from docarray import BaseDoc +from predictionguard import PredictionGuard + +from comps import ServiceType, TextDoc, opea_microservices, register_microservice, register_statistics, statistics_dict + + +class ScoreDoc(BaseDoc): + score: float + + +@register_microservice( + name="opea_service@injection_predictionguard", + service_type=ServiceType.GUARDRAIL, + endpoint="/v1/injection", + host="0.0.0.0", + port=9085, + input_datatype=TextDoc, + output_datatype=ScoreDoc, +) +@register_statistics(names=["opea_service@injection_predictionguard"]) +def injection_guard(input: TextDoc) -> ScoreDoc: + start = time.time() + + client = PredictionGuard() + + text = input.text + + result = client.injection.check(prompt=text) + + statistics_dict["opea_service@injection_predictionguard"].append_latency(time.time() - start, None) + return ScoreDoc(score=result["checks"][0]["probability"]) + + +if __name__ == "__main__": + print("Prediction Guard Injection Detection initialized.") + opea_microservices["opea_service@injection_predictionguard"].start() diff --git a/comps/guardrails/prompt_injection/predictionguard/requirements.txt b/comps/guardrails/prompt_injection/predictionguard/requirements.txt new file mode 100644 index 000000000..17af64b43 --- /dev/null +++ b/comps/guardrails/prompt_injection/predictionguard/requirements.txt @@ -0,0 +1,13 @@ +aiohttp +docarray +fastapi +huggingface_hub +opentelemetry-api +opentelemetry-exporter-otlp +opentelemetry-sdk +Pillow +predictionguard +prometheus-fastapi-instrumentator +shortuuid +transformers +uvicorn diff --git a/comps/guardrails/toxicity_detection/predictionguard/Dockerfile b/comps/guardrails/toxicity_detection/predictionguard/Dockerfile new file mode 100644 index 000000000..c97b3ccda --- /dev/null +++ b/comps/guardrails/toxicity_detection/predictionguard/Dockerfile @@ -0,0 +1,16 @@ + +# Copyright (C) 2024 Prediction Guard, Inc. +# SPDX-License-Identitier: Apache-2.0 + +FROM python:3.11-slim + +COPY comps /home/comps + +RUN pip install --no-cache-dir --upgrade pip && \ + pip install --no-cache-dir -r /home/comps/guardrails/toxicity_detection/predictionguard/requirements.txt + +ENV PYTHONPATH=$PYTHONPATH:/home + +WORKDIR /home/comps/guardrails/toxicity_detection/predictionguard + +ENTRYPOINT ["python", "toxicity_predictionguard.py" ] \ No newline at end of file diff --git a/comps/guardrails/toxicity_detection/predictionguard/README.md b/comps/guardrails/toxicity_detection/predictionguard/README.md new file mode 100644 index 000000000..a4e2615b7 --- /dev/null +++ b/comps/guardrails/toxicity_detection/predictionguard/README.md @@ -0,0 +1,38 @@ +# Toxicity Checking Prediction Guard Microservice + +[Prediction Guard](https://docs.predictionguard.com) allows you to utilize hosted open access LLMs, LVMs, and embedding functionality with seamlessly integrated safeguards. In addition to providing a scalable access to open models, Prediction Guard allows you to configure factual consistency checks, toxicity filters, PII filters, and prompt injection blocking. Join the [Prediction Guard Discord channel](https://discord.gg/TFHgnhAFKd) and request an API key to get started. + +Checking text for toxicity allows you to prevent toxic prompts from being sent to your LLM and toxic LLM outputs from being sent to your users (especially for open access models that might have unclear alignment). This microservice analyzes input text and returns a float number (from 0.0 to 1.0) indicating a level of toxicity (with closer to 1.0 being more toxic). + +# πŸš€ Start Microservice with Docker + +## Setup Environment Variables + +Setup the following environment variables first + +```bash +export PREDICTIONGUARD_API_KEY=${your_predictionguard_api_key} +``` + +## Build Docker Images + +```bash +cd ../../../.. +docker build -t opea/guardrails-toxicity-predictionguard:latest -f comps/guardrails/toxicity_detection/predictionguard/Dockerfile . +``` + +## Start Service + +```bash +docker run -d --name="guardrails-toxicity-predictionguard" -p 9090:9090 -e PREDICTIONGUARD_API_KEY=$PREDICTIONGUARD_API_KEY opea/guardrails-toxicity-predictionguard:latest +``` + +# πŸš€ Consume Toxicity Check Service + +```bash +curl -X POST http://localhost:9090/v1/toxicity \ + -H 'Content-Type: application/json' \ + -d '{ + "text": "I hate you!!" + }' +``` diff --git a/comps/guardrails/toxicity_detection/predictionguard/__init__.py b/comps/guardrails/toxicity_detection/predictionguard/__init__.py new file mode 100644 index 000000000..a246c95e7 --- /dev/null +++ b/comps/guardrails/toxicity_detection/predictionguard/__init__.py @@ -0,0 +1,2 @@ +# Copyright (C) 2024 Prediction Guard, Inc. +# SPDX-License-Identifier: Apache-2.0 diff --git a/comps/guardrails/toxicity_detection/predictionguard/docker_compose_toxicity.yaml b/comps/guardrails/toxicity_detection/predictionguard/docker_compose_toxicity.yaml new file mode 100644 index 000000000..fb8818cec --- /dev/null +++ b/comps/guardrails/toxicity_detection/predictionguard/docker_compose_toxicity.yaml @@ -0,0 +1,20 @@ +# Copyright (C) 2024 Prediction Guard, Inc +# SPDX-License-Identifier: Apache-2.0 + +services: + toxicity: + image: opea/guardrails-toxicity-predictionguard:latest + container_name: guardrails-toxicity-predictionguard + ports: + - "9090:9090" + ipc: host + environment: + no_proxy: ${no_proxy} + http_proxy: ${http_proxy} + https_proxy: ${https_proxy} + PREDICTIONGUARD_API_KEY: ${PREDICTIONGUARD_API_KEY} + restart: unless-stopped + +networks: + default: + driver: bridge diff --git a/comps/guardrails/toxicity_detection/predictionguard/requirements.txt b/comps/guardrails/toxicity_detection/predictionguard/requirements.txt new file mode 100644 index 000000000..17af64b43 --- /dev/null +++ b/comps/guardrails/toxicity_detection/predictionguard/requirements.txt @@ -0,0 +1,13 @@ +aiohttp +docarray +fastapi +huggingface_hub +opentelemetry-api +opentelemetry-exporter-otlp +opentelemetry-sdk +Pillow +predictionguard +prometheus-fastapi-instrumentator +shortuuid +transformers +uvicorn diff --git a/comps/guardrails/toxicity_detection/predictionguard/toxicity_predictionguard.py b/comps/guardrails/toxicity_detection/predictionguard/toxicity_predictionguard.py new file mode 100644 index 000000000..7a0cec2fc --- /dev/null +++ b/comps/guardrails/toxicity_detection/predictionguard/toxicity_predictionguard.py @@ -0,0 +1,42 @@ +# Copyright (C) 2024 Prediction Guard, Inc. +# SPDX-License-Identified: Apache-2.0 + + +import time + +from docarray import BaseDoc +from predictionguard import PredictionGuard + +from comps import ServiceType, TextDoc, opea_microservices, register_microservice, register_statistics, statistics_dict + + +class ScoreDoc(BaseDoc): + score: float + + +@register_microservice( + name="opea_service@toxicity_predictionguard", + service_type=ServiceType.GUARDRAIL, + endpoint="/v1/toxicity", + host="0.0.0.0", + port=9090, + input_datatype=TextDoc, + output_datatype=ScoreDoc, +) +@register_statistics(names=["opea_service@toxicity_predictionguard"]) +def toxicity_guard(input: TextDoc) -> ScoreDoc: + start = time.time() + + client = PredictionGuard() + + text = input.text + + result = client.toxicity.check(text=text) + + statistics_dict["opea_service@toxicity_predictionguard"].append_latency(time.time() - start, None) + return ScoreDoc(score=result["checks"][0]["score"]) + + +if __name__ == "__main__": + print("Prediction Guard Toxicity initialized.") + opea_microservices["opea_service@toxicity_predictionguard"].start() diff --git a/tests/guardrails/test_guardrails_factuality_predictionguard.sh b/tests/guardrails/test_guardrails_factuality_predictionguard.sh new file mode 100644 index 000000000..94234e5a1 --- /dev/null +++ b/tests/guardrails/test_guardrails_factuality_predictionguard.sh @@ -0,0 +1,70 @@ +#!/bin/bash +# Copyright (C) 2024 Prediction Guard, Inc. +# SPDX-License-Identifier: Apache-2.0 + +set -x # Print commands and their arguments as they are executed + +WORKPATH=$(dirname "$PWD") +ip_address=$(hostname -I | awk '{print $1}') # Adjust to a more reliable command +if [ -z "$ip_address" ]; then + ip_address="localhost" # Default to localhost if IP address is empty +fi + +function build_docker_images() { + cd $WORKPATH + echo $(pwd) + docker build --no-cache -t opea/factuality-pg:comps -f comps/guardrails/factuality/predictionguard/Dockerfile . + if [ $? -ne 0 ]; then + echo "opea/factuality-pg build failed" + exit 1 + else + echo "opea/factuality-pg built successfully" + fi +} + +function start_service() { + factuality_service_port=9075 + unset http_proxy + + # Set your API key here (ensure this environment variable is set) + docker run -d --name=test-comps-factuality-pg-server \ + -e http_proxy= -e https_proxy= \ + -e PREDICTIONGUARD_API_KEY=${PREDICTIONGUARD_API_KEY} \ + -p 9075:9075 --ipc=host opea/factuality-pg:comps + sleep 60 # Sleep for 3 minutes to allow the service to start +} + +function validate_microservice() { + factuality_service_port=9075 + result=$(http_proxy="" curl http://${ip_address}:${factuality_service_port}/v1/factuality \ + -X POST \ + -d '{"reference": "The Eiffel Tower is in Paris.", "text": "The Eiffel Tower is in Berlin."}' \ + -H 'Content-Type: application/json') + + if [[ $result == *"score"* ]]; then + echo "Service response is correct." + else + echo "Result wrong. Received was $result" + docker logs test-comps-factuality-pg-server + exit 1 + fi +} + +function stop_docker() { + cid=$(docker ps -aq --filter "name=test-comps-factuality-pg-*") + if [[ ! -z "$cid" ]]; then docker stop $cid && docker rm $cid && sleep 1s; fi +} + +function main() { + stop_docker + + build_docker_images + start_service + + validate_microservice + + stop_docker + echo y | docker system prune +} + +main diff --git a/tests/guardrails/test_guardrails_pii_detection_predictionguard.sh b/tests/guardrails/test_guardrails_pii_detection_predictionguard.sh new file mode 100644 index 000000000..176b7606b --- /dev/null +++ b/tests/guardrails/test_guardrails_pii_detection_predictionguard.sh @@ -0,0 +1,68 @@ +#!/bin/bash +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +set -x # Print commands and their arguments as they are executed + +WORKPATH=$(dirname "$PWD") +ip_address=$(hostname -I | awk '{print $1}') # Adjust to a more reliable command +if [ -z "$ip_address" ]; then + ip_address="localhost" # Default to localhost if IP address is empty +fi + +function build_docker_images() { + cd $WORKPATH + echo $(pwd) + docker build --no-cache -t opea/pii-pg:comps -f comps/guardrails/pii_detection/predictionguard/Dockerfile . + if [ $? -ne 0 ]; then + echo "opea/pii-pg build failed" + exit 1 + else + echo "opea/pii-pg built successfully" + fi +} + +function start_service() { + pii_service_port=9080 + unset http_proxy + docker run -d --name=test-comps-pii-pg-server \ + -e http_proxy= -e https_proxy= \ + -e PREDICTIONGUARD_API_KEY=${PREDICTIONGUARD_API_KEY} \ + -p 9080:9080 --ipc=host opea/pii-pg:comps + sleep 60 # Sleep for 1 minute to allow the service to start +} + +function validate_microservice() { + pii_service_port=9080 + result=$(http_proxy="" curl http://${ip_address}:${pii_service_port}/v1/pii \ + -X POST \ + -d '{"prompt": "My name is John Doe and my phone number is 123-456-7890.", "replace": true, "replace_method": "mask"}' \ + -H 'Content-Type: application/json') + + if [[ $result == *"new_prompt"* || $result == *"detected_pii"* ]]; then + echo "Service response is correct." + else + echo "Result wrong. Received was $result" + docker logs test-comps-pii-pg-server + exit 1 + fi +} + +function stop_docker() { + cid=$(docker ps -aq --filter "name=test-comps-pii-pg-*") + if [[ ! -z "$cid" ]]; then docker stop $cid && docker rm $cid && sleep 1s; fi +} + +function main() { + stop_docker + + build_docker_images + start_service + + validate_microservice + + stop_docker + echo y | docker system prune +} + +main diff --git a/tests/guardrails/test_guardrails_prompt_injection_predictionguard.sh b/tests/guardrails/test_guardrails_prompt_injection_predictionguard.sh new file mode 100644 index 000000000..7ec3de660 --- /dev/null +++ b/tests/guardrails/test_guardrails_prompt_injection_predictionguard.sh @@ -0,0 +1,68 @@ +#!/bin/bash +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +set -x # Print commands and their arguments as they are executed + +WORKPATH=$(dirname "$PWD") +ip_address=$(hostname -I | awk '{print $1}') # Adjust to a more reliable command +if [ -z "$ip_address" ]; then + ip_address="localhost" # Default to localhost if IP address is empty +fi + +function build_docker_images() { + cd $WORKPATH + echo $(pwd) + docker build --no-cache -t opea/injection-pg:comps -f comps/guardrails/prompt_injection/predictionguard/Dockerfile . + if [ $? -ne 0 ]; then + echo "opea/injection-pg build failed" + exit 1 + else + echo "opea/injection-pg built successfully" + fi +} + +function start_service() { + injection_service_port=9085 + unset http_proxy + docker run -d --name=test-comps-injection-pg-server \ + -e http_proxy= -e https_proxy= \ + -e PREDICTIONGUARD_API_KEY=${PREDICTIONGUARD_API_KEY} \ + -p 9085:9085 --ipc=host opea/injection-pg:comps + sleep 60 # Sleep for 1 minute to allow the service to start +} + +function validate_microservice() { + injection_service_port=9085 + result=$(http_proxy="" curl http://${ip_address}:${injection_service_port}/v1/injection \ + -X POST \ + -d '{"text": "How to bypass login screen?"}' \ + -H 'Content-Type: application/json') + + if [[ $result == *"score"* ]]; then + echo "Service response is correct." + else + echo "Result wrong. Received was $result" + docker logs test-comps-injection-pg-server + exit 1 + fi +} + +function stop_docker() { + cid=$(docker ps -aq --filter "name=test-comps-injection-pg-*") + if [[ ! -z "$cid" ]]; then docker stop $cid && docker rm $cid && sleep 1s; fi +} + +function main() { + stop_docker + + build_docker_images + start_service + + validate_microservice + + stop_docker + echo y | docker system prune +} + +main diff --git a/tests/guardrails/test_guardrails_toxicity_detection_predictionguard.sh b/tests/guardrails/test_guardrails_toxicity_detection_predictionguard.sh new file mode 100644 index 000000000..7747c2534 --- /dev/null +++ b/tests/guardrails/test_guardrails_toxicity_detection_predictionguard.sh @@ -0,0 +1,68 @@ +#!/bin/bash +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +set -x # Print commands and their arguments as they are executed + +WORKPATH=$(dirname "$PWD") +ip_address=$(hostname -I | awk '{print $1}') # Adjust to a more reliable command +if [ -z "$ip_address" ]; then + ip_address="localhost" # Default to localhost if IP address is empty +fi + +function build_docker_images() { + cd $WORKPATH + echo $(pwd) + docker build --no-cache -t opea/toxicity-pg:comps -f comps/guardrails/toxicity_detection/predictionguard/Dockerfile . + if [ $? -ne 0 ]; then + echo "opea/toxicity-pg build failed" + exit 1 + else + echo "opea/toxicity-pg built successfully" + fi +} + +function start_service() { + toxicity_service_port=9090 + unset http_proxy + docker run -d --name=test-comps-toxicity-pg-server \ + -e http_proxy= -e https_proxy= \ + -e PREDICTIONGUARD_API_KEY=${PREDICTIONGUARD_API_KEY} \ + -p 9090:9090 --ipc=host opea/toxicity-pg:comps + sleep 60 # Sleep for 1 minute to allow the service to start +} + +function validate_microservice() { + toxicity_service_port=9090 + result=$(http_proxy="" curl http://${ip_address}:${toxicity_service_port}/v1/toxicity \ + -X POST \ + -d '{"text": "I hate you."}' \ + -H 'Content-Type: application/json') + + if [[ $result == *"score"* ]]; then + echo "Service response is correct." + else + echo "Result wrong. Received was $result" + docker logs test-comps-toxicity-pg-server + exit 1 + fi +} + +function stop_docker() { + cid=$(docker ps -aq --filter "name=test-comps-toxicity-pg-*") + if [[ ! -z "$cid" ]]; then docker stop $cid && docker rm $cid && sleep 1s; fi +} + +function main() { + stop_docker + + build_docker_images + start_service + + validate_microservice + + stop_docker + echo y | docker system prune +} + +main