Skip to content

Commit 801884a

Browse files
committed
Support private deployment for OpenGuardrails Dify Plugin
1 parent f7870dd commit 801884a

File tree

12 files changed

+144
-70
lines changed

12 files changed

+144
-70
lines changed

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# OpenGuardrails
22

33
**Author:** openguardrails
4-
**Version:** 0.0.3
4+
**Version:** 0.0.4
55
**Type:** tool
66

77
## Description
@@ -17,7 +17,7 @@ OpenGuardrails focuses on:
1717
- **Content Safety & Compliance** (based on **Rich risk categories**)
1818
- **Sensitive Data Leakage Prevention** PII, Business Secrets
1919
- **Contextual Semantic Understanding** for precise detection
20-
- **thomas Protection** with dual deployment modes:
20+
- **Runtime Protection** with dual deployment modes:
2121
- **API Detection Mode** – flexible integration and precise control
2222
- **Security Gateway Mode** – WAF-like transparent proxy, zero code change
2323

@@ -112,12 +112,13 @@ score:
112112
To use OpenGuardrails, you need an **API Key**:
113113

114114
1. Register at the OpenGuardrails Platform: [https://OpenGuardrails.com/platform/](https://OpenGuardrails.com/platform/)
115-
2. Log in and go to **Account Management** → Get your API Key
115+
2. Log in and go to **Application Management** → Get your API Key for free
116+
![API Key](_assets/account.png)
116117
3. Add the API Key to your Dify plugin configuration
117118

118119
## Example Usage
119120

120-
Use OpenGuardrails’ **check_prompt** and **check_response_ctx** tools to protect the input and output of large language models.
121+
Use OpenGuardrails’ **OGR Check Prompt** and **OGR Check Response Contextual** tools to protect the input and output of large language models.
121122

122123
![workflow](_assets/workflow.png)
123124

_assets/account.jpg

-227 KB
Binary file not shown.

_assets/account.png

129 KB
Loading

_assets/workflow.png

-102 KB
Loading

main.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
11
from dify_plugin import Plugin, DifyPluginEnv
2+
import logging
3+
4+
# Configure logging to show detailed debug information
5+
logging.basicConfig(
6+
level=logging.INFO,
7+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
8+
handlers=[
9+
logging.StreamHandler()
10+
]
11+
)
212

313
plugin = Plugin(DifyPluginEnv(MAX_REQUEST_TIMEOUT=120))
414

manifest.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
version: 0.0.3
1+
version: 0.0.4
22
type: plugin
33
author: openguardrails
44
name: openguardrails
55
label:
6-
en_US: OpenGuardrails - Open Source, Free, Contextual
6+
en_US: OpenGuardrails - Free Open-source Contextual
77
description:
8-
en_US: Open source and free AI guardrails for contextually protecting against prompt attacks, content safety and sensitive data leakage.
8+
en_US: Free Open-source AI guardrails for contextually protecting against prompt attacks, content safety and sensitive data leakage.
99
icon: icon.png
1010
icon_dark: icon-dark.png
1111
resource:

provider/openguardrails.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,42 @@
11
from typing import Any
2+
import logging
23

34
from dify_plugin import ToolProvider
45
from dify_plugin.errors.tool import ToolProviderCredentialValidationError
56
from openguardrails import OpenGuardrails
67

8+
logger = logging.getLogger(__name__)
9+
710

811
class OpenGuardrailsProvider(ToolProvider):
912

1013
def _validate_credentials(self, credentials: dict[str, Any]) -> None:
1114
try:
12-
# 获取API密钥
15+
# Get API key
1316
api_key = credentials.get("api_key")
1417
if not api_key:
1518
raise ToolProviderCredentialValidationError("API key is required")
1619

17-
# 验证API密钥有效性,通过一个简单的测试调用
18-
client = OpenGuardrails(api_key)
19-
# 使用一个简单的测试来验证API密钥
20+
# Get base_url, if not provided, use default value
21+
base_url = credentials.get("base_url")
22+
23+
if base_url:
24+
client = OpenGuardrails(api_key, base_url=base_url)
25+
else:
26+
client = OpenGuardrails(api_key)
27+
28+
# Use a simple test to validate API key
2029
test_result = client.check_prompt("test")
2130

22-
# 如果调用成功并返回了结果,则验证通过
31+
# If the call is successful and returns a result, the validation is successful
2332
if hasattr(test_result, 'suggest_action'):
24-
return # 验证成功
33+
return # Validate Successfully
2534
else:
35+
logger.error(f"Invalid response format, missing suggest_action attribute")
2636
raise ToolProviderCredentialValidationError("Invalid API key response format")
2737

2838
except Exception as e:
39+
logger.error(f"Credential validation error: {str(e)}", exc_info=True)
2940
if "API key" in str(e).lower() or "auth" in str(e).lower():
3041
raise ToolProviderCredentialValidationError("Invalid API key")
3142
else:

provider/openguardrails.yaml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ identity:
22
author: "openguardrails"
33
name: "openguardrails"
44
label:
5-
en_US: "OpenGuardrails - Open Source, Free, Contextual"
5+
en_US: "OpenGuardrails - Free Open-source Contextual"
66
description:
7-
en_US: "Open source and free contextual AI guardrails for protecting against prompt attacks, content safety and sensitive data leakage."
7+
en_US: "Free Open source contextual AI guardrails for protecting against prompt attacks, content safety and sensitive data leakage."
88
icon: "icon.png"
99
icon_dark: "icon-dark.png"
1010

@@ -19,6 +19,16 @@ credentials_for_provider:
1919
help:
2020
en_US: "Get your free API key from OpenGuardrails management console: https://OpenGuardrails.com/platform/"
2121
url: https://OpenGuardrails.com/platform/
22+
base_url:
23+
type: text-input
24+
required: false
25+
label:
26+
en_US: Base URL
27+
placeholder:
28+
en_US: Leave empty to use the free cloud API (https://api.openguardrails.com/v1).
29+
help:
30+
en_US: "If you have a private deployment, enter your private OpenGuardrails server URL (e.g., http://localhost:5001/v1)."
31+
url: https://OpenGuardrails.com/platform/
2232

2333
tools:
2434
- tools/check_prompt.yaml

tools/check_prompt.py

Lines changed: 49 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
from collections.abc import Generator
22
from typing import Any
3+
import logging
34

45
from dify_plugin import Tool
56
from dify_plugin.entities.tool import ToolInvokeMessage
67
from openguardrails import OpenGuardrails
78

9+
logger = logging.getLogger(__name__)
10+
811
class CheckPromptTool(Tool):
912
def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]:
1013
try:
@@ -24,39 +27,57 @@ def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessag
2427
yield self.create_text_message("Error: API key is required.")
2528
return
2629

30+
# Get base_url from credentials
31+
base_url = self.runtime.credentials.get("base_url")
32+
2733
# Create OpenGuardrails client and check user input
28-
client = OpenGuardrails(api_key)
29-
result = client.check_prompt(prompt, user_id=user_id)
34+
try:
35+
if base_url:
36+
client = OpenGuardrails(api_key, base_url=base_url)
37+
else:
38+
client = OpenGuardrails(api_key)
39+
40+
result = client.check_prompt(prompt, user_id=user_id)
41+
except Exception as client_error:
42+
logger.error(f"OpenGuardrails client error: {str(client_error)}", exc_info=True)
43+
raise
3044

3145
# Extract categories field: from compliance and security not equal to "no_risk" categories list first item
32-
categories = []
33-
if result.result.compliance.risk_level != "no_risk" and result.result.compliance.categories:
34-
categories.append(result.result.compliance.categories[0])
35-
elif result.result.security.risk_level != "no_risk" and result.result.security.categories:
36-
categories.append(result.result.security.categories[0])
37-
elif result.result.data.risk_level != "no_risk" and result.result.data.categories:
38-
categories.append(result.result.data.categories[0])
39-
40-
categories_str = ", ".join(categories)
41-
if categories_str:
42-
categories_str = f"{categories_str}"
43-
else:
44-
categories_str = ""
45-
46-
# Process suggest_answer field, if not exist, set to empty string
47-
suggest_answer = ""
48-
if result.suggest_answer:
49-
suggest_answer = result.suggest_answer
50-
51-
# Use custom variable to return result
52-
yield self.create_variable_message("id", result.id)
53-
yield self.create_variable_message("overall_risk_level", result.overall_risk_level)
54-
yield self.create_variable_message("suggest_action", result.suggest_action)
55-
yield self.create_variable_message("suggest_answer", suggest_answer)
56-
yield self.create_variable_message("categories", categories_str)
57-
yield self.create_variable_message("score", result.score)
46+
try:
47+
categories = []
48+
if result.result.compliance.risk_level != "no_risk" and result.result.compliance.categories:
49+
categories.append(result.result.compliance.categories[0])
50+
elif result.result.security.risk_level != "no_risk" and result.result.security.categories:
51+
categories.append(result.result.security.categories[0])
52+
elif result.result.data.risk_level != "no_risk" and result.result.data.categories:
53+
categories.append(result.result.data.categories[0])
54+
55+
categories_str = ", ".join(categories)
56+
if categories_str:
57+
categories_str = f"{categories_str}"
58+
else:
59+
categories_str = ""
60+
61+
# Process suggest_answer field, if not exist, set to empty string
62+
suggest_answer = ""
63+
if result.suggest_answer:
64+
suggest_answer = result.suggest_answer
65+
66+
# Use custom variable to return result
67+
yield self.create_variable_message("id", result.id)
68+
yield self.create_variable_message("overall_risk_level", result.overall_risk_level)
69+
yield self.create_variable_message("suggest_action", result.suggest_action)
70+
yield self.create_variable_message("suggest_answer", suggest_answer)
71+
yield self.create_variable_message("categories", categories_str)
72+
# Ensure score is a number, not None
73+
score_value = result.score if result.score is not None else 0.0
74+
yield self.create_variable_message("score", score_value)
75+
except Exception as result_error:
76+
logger.error(f"Error processing result: {str(result_error)}", exc_info=True)
77+
raise
5878

5979
except Exception as e:
6080
# 错误处理
81+
logger.error(f"CheckPromptTool error: {str(e)}", exc_info=True)
6182
yield self.create_text_message(f"Error: {str(e)}")
6283
yield self.create_json_message({"error": str(e)})

tools/check_prompt.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ identity:
22
name: "check_prompt"
33
author: "openguardrails"
44
label:
5-
en_US: "OpenGuardrails - Check Prompt"
5+
en_US: "OGR Check Prompt"
66
description:
77
human:
88
en_US: "Detect user input for prompt attacks, jailbreaks, malicious operations and content safety issues based on OWASP TOP 10 LLM Applications and GB/T45654-2025 standards"

0 commit comments

Comments
 (0)