Block request and response content using Azure Content Safety
Configure the policy to block request and response content matching the Hate and Violence categories defined by Azure.
In this example, we create a category_names function to parse the category object, and a check_response function to parse the response returned by Azure Content Safety.
Prerequisites
-
You have an Azure subscription and access to Azure AI Content Safety.
-
An existing AI Model.
Environment variables
-
CONTENT_SAFETY_URL: The full URL of the Azure AI Content Safety instance. -
CONTENT_SAFETY_KEY: The API key to access the Azure AI Content Safety instance.
ai_gateway_policies:
- ref: custom-guardrail-azure-content-safety
ai_gateway: !lookup {id: !env AI_GATEWAY_ID}
name: custom-guardrail-azure-content-safety
display_name: Block request and response content using Azure Content Safety
type: ai-custom-guardrail
enabled: true
config:
guarding_mode: BOTH
text_source: concatenate_all_content
params:
content_safety_url: !env CONTENT_SAFETY_URL
content_safety_key: !env CONTENT_SAFETY_KEY
categories: '[{ "name": "Hate", "rejection_level": 2}, { "name": "Violence",
"rejection_level": 2}]'
output_type: FourSeverityLevels
azure_api_version: '2023-10-01'
request:
url: "$(conf.params.content_safety_url)"
headers:
Ocp-Apim-Subscription-Key: "$(conf.params.content_safety_key)"
body:
categories: "$(category_names)"
text: "$(content)"
outputType: "$(conf.params.output_type)"
queries:
api-version: "$(conf.params.azure_api_version)"
response:
block: "$(check_response.block)"
block_message: "$(check_response.block_message)"
functions:
category_names: |
return function(conf)
local category_names = {}
for _, v in ipairs(conf.params.categories or {}) do
if v.rejection_level >= 2 then
table.insert(category_names, v.name)
end
end
return category_names
end
check_response: |
return function(resp, conf)
local res = {
block = true,
block_message = "content passed safety check",
}
if
((type(resp.categoriesAnalysis) ~= "table") or (#resp.categoriesAnalysis == 0))
and ((type(resp.blocklistsMatch) ~= "table") or (#resp.blocklistsMatch == 0))
then
return res, "content safety introspection is invalid"
end
local reason = ""
local ok = true
for _, v in ipairs(resp.categoriesAnalysis or EMPTY_T) do
local category_name = v.category
local failure_level = nil
for _, category in ipairs(conf.params.categories or {}) do
if category.name == category_name then
failure_level = category.rejection_level
break
end
end
if failure_level and (v.severity >= failure_level) then
if not ok then
reason = reason .. "; "
end
ok = false
reason = reason .. "breached category [" .. category_name .. "] at level " .. failure_level
end
end
for _, v in ipairs(resp.blocklistsMatch or EMPTY_T) do
local blocklist_name = v.blocklistName or "unknown_name"
ok = false
reason = reason .. "breached blocklist [" .. blocklist_name .. "]"
end
return {
block = not ok,
block_message = reason,
}
end
global: falseMake sure to replace the following placeholders with your own values:
-
AI_GATEWAY_ID: Theidof your AI Gateway. -
!env CONTENT_SAFETY_URL: The full URL of the Azure AI Content Safety instance. -
!env CONTENT_SAFETY_KEY: The API key to access the Azure AI Content Safety instance.
curl -X POST https://{region}.api.konghq.com/v1/ai-gateways/{AIGatewayId}/policies \
--header "accept: application/json" \
--header "Content-Type: application/json" \
--header "Authorization: Bearer $KONNECT_TOKEN" \
--data '
{
"name": "custom-guardrail-azure-content-safety",
"display_name": "Block request and response content using Azure Content Safety",
"type": "ai-custom-guardrail",
"enabled": true,
"config": {
"guarding_mode": "BOTH",
"text_source": "concatenate_all_content",
"params": {
"content_safety_url": "'$CONTENT_SAFETY_URL'",
"content_safety_key": "'$CONTENT_SAFETY_KEY'",
"categories": "[{ \"name\": \"Hate\", \"rejection_level\": 2}, { \"name\": \"Violence\", \"rejection_level\": 2}]",
"output_type": "FourSeverityLevels",
"azure_api_version": "2023-10-01"
},
"request": {
"url": "$(conf.params.content_safety_url)",
"headers": {
"Ocp-Apim-Subscription-Key": "$(conf.params.content_safety_key)"
},
"body": {
"categories": "$(category_names)",
"text": "$(content)",
"outputType": "$(conf.params.output_type)"
},
"queries": {
"api-version": "$(conf.params.azure_api_version)"
}
},
"response": {
"block": "$(check_response.block)",
"block_message": "$(check_response.block_message)"
},
"functions": {
"category_names": "return function(conf)\n local category_names = {}\n for _, v in ipairs(conf.params.categories or {}) do\n if v.rejection_level >= 2 then\n table.insert(category_names, v.name)\n end\n end\n return category_names\nend\n",
"check_response": "return function(resp, conf)\n local res = {\n block = true,\n block_message = \"content passed safety check\",\n }\n\n if\n ((type(resp.categoriesAnalysis) ~= \"table\") or (#resp.categoriesAnalysis == 0))\n and ((type(resp.blocklistsMatch) ~= \"table\") or (#resp.blocklistsMatch == 0))\n then\n return res, \"content safety introspection is invalid\"\n end\n\n local reason = \"\"\n local ok = true\n\n for _, v in ipairs(resp.categoriesAnalysis or EMPTY_T) do\n local category_name = v.category\n local failure_level = nil\n for _, category in ipairs(conf.params.categories or {}) do\n if category.name == category_name then\n failure_level = category.rejection_level\n break\n end\n end\n\n if failure_level and (v.severity >= failure_level) then\n if not ok then\n reason = reason .. \"; \"\n end\n\n ok = false\n reason = reason .. \"breached category [\" .. category_name .. \"] at level \" .. failure_level\n end\n end\n\n for _, v in ipairs(resp.blocklistsMatch or EMPTY_T) do\n local blocklist_name = v.blocklistName or \"unknown_name\"\n\n ok = false\n reason = reason .. \"breached blocklist [\" .. blocklist_name .. \"]\"\n end\n\n return {\n block = not ok,\n block_message = reason,\n }\nend\n"
}
},
"global": false
}
'Make sure to replace the following placeholders with your own values:
-
region: Geographic region where your Kong Konnect is hosted and operates. -
KONNECT_TOKEN: Your Personal Access Token (PAT) associated with your Konnect account. -
AIGatewayId: Theidof the AI Gateway.
See the Konnect AI Gateway API reference to learn about region-specific URLs and personal access tokens.
Prerequisite: Configure your Personal Access Token
terraform {
required_providers {
konnect = {
source = "kong/konnect"
}
}
}
provider "konnect" {
personal_access_token = "$KONNECT_TOKEN"
server_url = "https://us.api.konghq.com/"
}Add the following to your Terraform configuration:
resource "konnect_ai_gateway_policy" "my_policy" {
name = "custom-guardrail-azure-content-safety"
display_name = "Block request and response content using Azure Content Safety"
type = "ai-custom-guardrail"
enabled = true
global = false
config = jsonencode({
guarding_mode = "BOTH"
text_source = "concatenate_all_content"
params = {
content_safety_url = var.content_safety_url
content_safety_key = var.content_safety_key
categories = "[{ \"name\": \"Hate\", \"rejection_level\": 2}, { \"name\": \"Violence\", \"rejection_level\": 2}]"
output_type = "FourSeverityLevels"
azure_api_version = "2023-10-01"
}
request = {
url = "$(conf.params.content_safety_url)"
headers = {
Ocp-Apim-Subscription-Key = "$(conf.params.content_safety_key)"
}
body = {
categories = "$(category_names)"
text = "$(content)"
outputType = "$(conf.params.output_type)"
}
queries = {
api-version = "$(conf.params.azure_api_version)"
}
}
response = {
block = "$(check_response.block)"
block_message = "$(check_response.block_message)"
}
functions = {
category_names = <<EOF
return function(conf)
local category_names = {}
for _, v in ipairs(conf.params.categories or {}) do
if v.rejection_level >= 2 then
table.insert(category_names, v.name)
end
end
return category_names
end
EOF
check_response = <<EOF
return function(resp, conf)
local res = {
block = true,
block_message = "content passed safety check",
}
if
((type(resp.categoriesAnalysis) ~= "table") or (#resp.categoriesAnalysis == 0))
and ((type(resp.blocklistsMatch) ~= "table") or (#resp.blocklistsMatch == 0))
then
return res, "content safety introspection is invalid"
end
local reason = ""
local ok = true
for _, v in ipairs(resp.categoriesAnalysis or EMPTY_T) do
local category_name = v.category
local failure_level = nil
for _, category in ipairs(conf.params.categories or {}) do
if category.name == category_name then
failure_level = category.rejection_level
break
end
end
if failure_level and (v.severity >= failure_level) then
if not ok then
reason = reason .. "; "
end
ok = false
reason = reason .. "breached category [" .. category_name .. "] at level " .. failure_level
end
end
for _, v in ipairs(resp.blocklistsMatch or EMPTY_T) do
local blocklist_name = v.blocklistName or "unknown_name"
ok = false
reason = reason .. "breached blocklist [" .. blocklist_name .. "]"
end
return {
block = not ok,
block_message = reason,
}
end
EOF
}
})
gateway_id = konnect_ai_gateway.my_ai_gateway.id
}This example requires the following variables to be added to your manifest. You can specify values at runtime by setting TF_VAR_name=value.
variable "content_safety_url" {
type = string
}
variable "content_safety_key" {
type = string
}ai_gateway_policies:
- ref: custom-guardrail-azure-content-safety
ai_gateway: !lookup {id: !env AI_GATEWAY_ID}
name: custom-guardrail-azure-content-safety
display_name: Block request and response content using Azure Content Safety
type: ai-custom-guardrail
enabled: true
config:
guarding_mode: BOTH
text_source: concatenate_all_content
params:
content_safety_url: !env CONTENT_SAFETY_URL
content_safety_key: !env CONTENT_SAFETY_KEY
categories: '[{ "name": "Hate", "rejection_level": 2}, { "name": "Violence",
"rejection_level": 2}]'
output_type: FourSeverityLevels
azure_api_version: '2023-10-01'
request:
url: "$(conf.params.content_safety_url)"
headers:
Ocp-Apim-Subscription-Key: "$(conf.params.content_safety_key)"
body:
categories: "$(category_names)"
text: "$(content)"
outputType: "$(conf.params.output_type)"
queries:
api-version: "$(conf.params.azure_api_version)"
response:
block: "$(check_response.block)"
block_message: "$(check_response.block_message)"
functions:
category_names: |
return function(conf)
local category_names = {}
for _, v in ipairs(conf.params.categories or {}) do
if v.rejection_level >= 2 then
table.insert(category_names, v.name)
end
end
return category_names
end
check_response: |
return function(resp, conf)
local res = {
block = true,
block_message = "content passed safety check",
}
if
((type(resp.categoriesAnalysis) ~= "table") or (#resp.categoriesAnalysis == 0))
and ((type(resp.blocklistsMatch) ~= "table") or (#resp.blocklistsMatch == 0))
then
return res, "content safety introspection is invalid"
end
local reason = ""
local ok = true
for _, v in ipairs(resp.categoriesAnalysis or EMPTY_T) do
local category_name = v.category
local failure_level = nil
for _, category in ipairs(conf.params.categories or {}) do
if category.name == category_name then
failure_level = category.rejection_level
break
end
end
if failure_level and (v.severity >= failure_level) then
if not ok then
reason = reason .. "; "
end
ok = false
reason = reason .. "breached category [" .. category_name .. "] at level " .. failure_level
end
end
for _, v in ipairs(resp.blocklistsMatch or EMPTY_T) do
local blocklist_name = v.blocklistName or "unknown_name"
ok = false
reason = reason .. "breached blocklist [" .. blocklist_name .. "]"
end
return {
block = not ok,
block_message = reason,
}
end
global: falseMake sure to replace the following placeholders with your own values:
-
AI_GATEWAY_ID: Theidof your AI Gateway. -
!env CONTENT_SAFETY_URL: The full URL of the Azure AI Content Safety instance. -
!env CONTENT_SAFETY_KEY: The API key to access the Azure AI Content Safety instance.
curl -X POST https://{region}.api.konghq.com/v1/ai-gateways/{AIGatewayId}/policies \
--header "accept: application/json" \
--header "Content-Type: application/json" \
--header "Authorization: Bearer $KONNECT_TOKEN" \
--data '
{
"name": "custom-guardrail-azure-content-safety",
"display_name": "Block request and response content using Azure Content Safety",
"type": "ai-custom-guardrail",
"enabled": true,
"config": {
"guarding_mode": "BOTH",
"text_source": "concatenate_all_content",
"params": {
"content_safety_url": "'$CONTENT_SAFETY_URL'",
"content_safety_key": "'$CONTENT_SAFETY_KEY'",
"categories": "[{ \"name\": \"Hate\", \"rejection_level\": 2}, { \"name\": \"Violence\", \"rejection_level\": 2}]",
"output_type": "FourSeverityLevels",
"azure_api_version": "2023-10-01"
},
"request": {
"url": "$(conf.params.content_safety_url)",
"headers": {
"Ocp-Apim-Subscription-Key": "$(conf.params.content_safety_key)"
},
"body": {
"categories": "$(category_names)",
"text": "$(content)",
"outputType": "$(conf.params.output_type)"
},
"queries": {
"api-version": "$(conf.params.azure_api_version)"
}
},
"response": {
"block": "$(check_response.block)",
"block_message": "$(check_response.block_message)"
},
"functions": {
"category_names": "return function(conf)\n local category_names = {}\n for _, v in ipairs(conf.params.categories or {}) do\n if v.rejection_level >= 2 then\n table.insert(category_names, v.name)\n end\n end\n return category_names\nend\n",
"check_response": "return function(resp, conf)\n local res = {\n block = true,\n block_message = \"content passed safety check\",\n }\n\n if\n ((type(resp.categoriesAnalysis) ~= \"table\") or (#resp.categoriesAnalysis == 0))\n and ((type(resp.blocklistsMatch) ~= \"table\") or (#resp.blocklistsMatch == 0))\n then\n return res, \"content safety introspection is invalid\"\n end\n\n local reason = \"\"\n local ok = true\n\n for _, v in ipairs(resp.categoriesAnalysis or EMPTY_T) do\n local category_name = v.category\n local failure_level = nil\n for _, category in ipairs(conf.params.categories or {}) do\n if category.name == category_name then\n failure_level = category.rejection_level\n break\n end\n end\n\n if failure_level and (v.severity >= failure_level) then\n if not ok then\n reason = reason .. \"; \"\n end\n\n ok = false\n reason = reason .. \"breached category [\" .. category_name .. \"] at level \" .. failure_level\n end\n end\n\n for _, v in ipairs(resp.blocklistsMatch or EMPTY_T) do\n local blocklist_name = v.blocklistName or \"unknown_name\"\n\n ok = false\n reason = reason .. \"breached blocklist [\" .. blocklist_name .. \"]\"\n end\n\n return {\n block = not ok,\n block_message = reason,\n }\nend\n"
}
},
"global": false
}
'Make sure to replace the following placeholders with your own values:
-
region: Geographic region where your Kong Konnect is hosted and operates. -
KONNECT_TOKEN: Your Personal Access Token (PAT) associated with your Konnect account. -
AIGatewayId: Theidof the AI Gateway.
See the Konnect AI Gateway API reference to learn about region-specific URLs and personal access tokens.
Prerequisite: Configure your Personal Access Token
terraform {
required_providers {
konnect = {
source = "kong/konnect"
}
}
}
provider "konnect" {
personal_access_token = "$KONNECT_TOKEN"
server_url = "https://us.api.konghq.com/"
}Add the following to your Terraform configuration:
resource "konnect_ai_gateway_policy" "my_policy" {
name = "custom-guardrail-azure-content-safety"
display_name = "Block request and response content using Azure Content Safety"
type = "ai-custom-guardrail"
enabled = true
global = false
config = jsonencode({
guarding_mode = "BOTH"
text_source = "concatenate_all_content"
params = {
content_safety_url = var.content_safety_url
content_safety_key = var.content_safety_key
categories = "[{ \"name\": \"Hate\", \"rejection_level\": 2}, { \"name\": \"Violence\", \"rejection_level\": 2}]"
output_type = "FourSeverityLevels"
azure_api_version = "2023-10-01"
}
request = {
url = "$(conf.params.content_safety_url)"
headers = {
Ocp-Apim-Subscription-Key = "$(conf.params.content_safety_key)"
}
body = {
categories = "$(category_names)"
text = "$(content)"
outputType = "$(conf.params.output_type)"
}
queries = {
api-version = "$(conf.params.azure_api_version)"
}
}
response = {
block = "$(check_response.block)"
block_message = "$(check_response.block_message)"
}
functions = {
category_names = <<EOF
return function(conf)
local category_names = {}
for _, v in ipairs(conf.params.categories or {}) do
if v.rejection_level >= 2 then
table.insert(category_names, v.name)
end
end
return category_names
end
EOF
check_response = <<EOF
return function(resp, conf)
local res = {
block = true,
block_message = "content passed safety check",
}
if
((type(resp.categoriesAnalysis) ~= "table") or (#resp.categoriesAnalysis == 0))
and ((type(resp.blocklistsMatch) ~= "table") or (#resp.blocklistsMatch == 0))
then
return res, "content safety introspection is invalid"
end
local reason = ""
local ok = true
for _, v in ipairs(resp.categoriesAnalysis or EMPTY_T) do
local category_name = v.category
local failure_level = nil
for _, category in ipairs(conf.params.categories or {}) do
if category.name == category_name then
failure_level = category.rejection_level
break
end
end
if failure_level and (v.severity >= failure_level) then
if not ok then
reason = reason .. "; "
end
ok = false
reason = reason .. "breached category [" .. category_name .. "] at level " .. failure_level
end
end
for _, v in ipairs(resp.blocklistsMatch or EMPTY_T) do
local blocklist_name = v.blocklistName or "unknown_name"
ok = false
reason = reason .. "breached blocklist [" .. blocklist_name .. "]"
end
return {
block = not ok,
block_message = reason,
}
end
EOF
}
})
gateway_id = konnect_ai_gateway.my_ai_gateway.id
}This example requires the following variables to be added to your manifest. You can specify values at runtime by setting TF_VAR_name=value.
variable "content_safety_url" {
type = string
}
variable "content_safety_key" {
type = string
}ai_gateway_policies:
- ref: custom-guardrail-azure-content-safety
ai_gateway: !lookup {id: !env AI_GATEWAY_ID}
name: custom-guardrail-azure-content-safety
display_name: Block request and response content using Azure Content Safety
type: ai-custom-guardrail
enabled: true
config:
guarding_mode: BOTH
text_source: concatenate_all_content
params:
content_safety_url: !env CONTENT_SAFETY_URL
content_safety_key: !env CONTENT_SAFETY_KEY
categories: '[{ "name": "Hate", "rejection_level": 2}, { "name": "Violence",
"rejection_level": 2}]'
output_type: FourSeverityLevels
azure_api_version: '2023-10-01'
request:
url: "$(conf.params.content_safety_url)"
headers:
Ocp-Apim-Subscription-Key: "$(conf.params.content_safety_key)"
body:
categories: "$(category_names)"
text: "$(content)"
outputType: "$(conf.params.output_type)"
queries:
api-version: "$(conf.params.azure_api_version)"
response:
block: "$(check_response.block)"
block_message: "$(check_response.block_message)"
functions:
category_names: |
return function(conf)
local category_names = {}
for _, v in ipairs(conf.params.categories or {}) do
if v.rejection_level >= 2 then
table.insert(category_names, v.name)
end
end
return category_names
end
check_response: |
return function(resp, conf)
local res = {
block = true,
block_message = "content passed safety check",
}
if
((type(resp.categoriesAnalysis) ~= "table") or (#resp.categoriesAnalysis == 0))
and ((type(resp.blocklistsMatch) ~= "table") or (#resp.blocklistsMatch == 0))
then
return res, "content safety introspection is invalid"
end
local reason = ""
local ok = true
for _, v in ipairs(resp.categoriesAnalysis or EMPTY_T) do
local category_name = v.category
local failure_level = nil
for _, category in ipairs(conf.params.categories or {}) do
if category.name == category_name then
failure_level = category.rejection_level
break
end
end
if failure_level and (v.severity >= failure_level) then
if not ok then
reason = reason .. "; "
end
ok = false
reason = reason .. "breached category [" .. category_name .. "] at level " .. failure_level
end
end
for _, v in ipairs(resp.blocklistsMatch or EMPTY_T) do
local blocklist_name = v.blocklistName or "unknown_name"
ok = false
reason = reason .. "breached blocklist [" .. blocklist_name .. "]"
end
return {
block = not ok,
block_message = reason,
}
end
global: falseMake sure to replace the following placeholders with your own values:
-
AI_GATEWAY_ID: Theidof your AI Gateway. -
!env CONTENT_SAFETY_URL: The full URL of the Azure AI Content Safety instance. -
!env CONTENT_SAFETY_KEY: The API key to access the Azure AI Content Safety instance.
curl -X POST https://{region}.api.konghq.com/v1/ai-gateways/{AIGatewayId}/policies \
--header "accept: application/json" \
--header "Content-Type: application/json" \
--header "Authorization: Bearer $KONNECT_TOKEN" \
--data '
{
"name": "custom-guardrail-azure-content-safety",
"display_name": "Block request and response content using Azure Content Safety",
"type": "ai-custom-guardrail",
"enabled": true,
"config": {
"guarding_mode": "BOTH",
"text_source": "concatenate_all_content",
"params": {
"content_safety_url": "'$CONTENT_SAFETY_URL'",
"content_safety_key": "'$CONTENT_SAFETY_KEY'",
"categories": "[{ \"name\": \"Hate\", \"rejection_level\": 2}, { \"name\": \"Violence\", \"rejection_level\": 2}]",
"output_type": "FourSeverityLevels",
"azure_api_version": "2023-10-01"
},
"request": {
"url": "$(conf.params.content_safety_url)",
"headers": {
"Ocp-Apim-Subscription-Key": "$(conf.params.content_safety_key)"
},
"body": {
"categories": "$(category_names)",
"text": "$(content)",
"outputType": "$(conf.params.output_type)"
},
"queries": {
"api-version": "$(conf.params.azure_api_version)"
}
},
"response": {
"block": "$(check_response.block)",
"block_message": "$(check_response.block_message)"
},
"functions": {
"category_names": "return function(conf)\n local category_names = {}\n for _, v in ipairs(conf.params.categories or {}) do\n if v.rejection_level >= 2 then\n table.insert(category_names, v.name)\n end\n end\n return category_names\nend\n",
"check_response": "return function(resp, conf)\n local res = {\n block = true,\n block_message = \"content passed safety check\",\n }\n\n if\n ((type(resp.categoriesAnalysis) ~= \"table\") or (#resp.categoriesAnalysis == 0))\n and ((type(resp.blocklistsMatch) ~= \"table\") or (#resp.blocklistsMatch == 0))\n then\n return res, \"content safety introspection is invalid\"\n end\n\n local reason = \"\"\n local ok = true\n\n for _, v in ipairs(resp.categoriesAnalysis or EMPTY_T) do\n local category_name = v.category\n local failure_level = nil\n for _, category in ipairs(conf.params.categories or {}) do\n if category.name == category_name then\n failure_level = category.rejection_level\n break\n end\n end\n\n if failure_level and (v.severity >= failure_level) then\n if not ok then\n reason = reason .. \"; \"\n end\n\n ok = false\n reason = reason .. \"breached category [\" .. category_name .. \"] at level \" .. failure_level\n end\n end\n\n for _, v in ipairs(resp.blocklistsMatch or EMPTY_T) do\n local blocklist_name = v.blocklistName or \"unknown_name\"\n\n ok = false\n reason = reason .. \"breached blocklist [\" .. blocklist_name .. \"]\"\n end\n\n return {\n block = not ok,\n block_message = reason,\n }\nend\n"
}
},
"global": false
}
'Make sure to replace the following placeholders with your own values:
-
region: Geographic region where your Kong Konnect is hosted and operates. -
KONNECT_TOKEN: Your Personal Access Token (PAT) associated with your Konnect account. -
AIGatewayId: Theidof the AI Gateway.
See the Konnect AI Gateway API reference to learn about region-specific URLs and personal access tokens.
Prerequisite: Configure your Personal Access Token
terraform {
required_providers {
konnect = {
source = "kong/konnect"
}
}
}
provider "konnect" {
personal_access_token = "$KONNECT_TOKEN"
server_url = "https://us.api.konghq.com/"
}Add the following to your Terraform configuration:
resource "konnect_ai_gateway_policy" "my_policy" {
name = "custom-guardrail-azure-content-safety"
display_name = "Block request and response content using Azure Content Safety"
type = "ai-custom-guardrail"
enabled = true
global = false
config = jsonencode({
guarding_mode = "BOTH"
text_source = "concatenate_all_content"
params = {
content_safety_url = var.content_safety_url
content_safety_key = var.content_safety_key
categories = "[{ \"name\": \"Hate\", \"rejection_level\": 2}, { \"name\": \"Violence\", \"rejection_level\": 2}]"
output_type = "FourSeverityLevels"
azure_api_version = "2023-10-01"
}
request = {
url = "$(conf.params.content_safety_url)"
headers = {
Ocp-Apim-Subscription-Key = "$(conf.params.content_safety_key)"
}
body = {
categories = "$(category_names)"
text = "$(content)"
outputType = "$(conf.params.output_type)"
}
queries = {
api-version = "$(conf.params.azure_api_version)"
}
}
response = {
block = "$(check_response.block)"
block_message = "$(check_response.block_message)"
}
functions = {
category_names = <<EOF
return function(conf)
local category_names = {}
for _, v in ipairs(conf.params.categories or {}) do
if v.rejection_level >= 2 then
table.insert(category_names, v.name)
end
end
return category_names
end
EOF
check_response = <<EOF
return function(resp, conf)
local res = {
block = true,
block_message = "content passed safety check",
}
if
((type(resp.categoriesAnalysis) ~= "table") or (#resp.categoriesAnalysis == 0))
and ((type(resp.blocklistsMatch) ~= "table") or (#resp.blocklistsMatch == 0))
then
return res, "content safety introspection is invalid"
end
local reason = ""
local ok = true
for _, v in ipairs(resp.categoriesAnalysis or EMPTY_T) do
local category_name = v.category
local failure_level = nil
for _, category in ipairs(conf.params.categories or {}) do
if category.name == category_name then
failure_level = category.rejection_level
break
end
end
if failure_level and (v.severity >= failure_level) then
if not ok then
reason = reason .. "; "
end
ok = false
reason = reason .. "breached category [" .. category_name .. "] at level " .. failure_level
end
end
for _, v in ipairs(resp.blocklistsMatch or EMPTY_T) do
local blocklist_name = v.blocklistName or "unknown_name"
ok = false
reason = reason .. "breached blocklist [" .. blocklist_name .. "]"
end
return {
block = not ok,
block_message = reason,
}
end
EOF
}
})
gateway_id = konnect_ai_gateway.my_ai_gateway.id
}This example requires the following variables to be added to your manifest. You can specify values at runtime by setting TF_VAR_name=value.
variable "content_safety_url" {
type = string
}
variable "content_safety_key" {
type = string
}ai_gateway_policies:
- ref: custom-guardrail-azure-content-safety
ai_gateway: !lookup {id: !env AI_GATEWAY_ID}
name: custom-guardrail-azure-content-safety
display_name: Block request and response content using Azure Content Safety
type: ai-custom-guardrail
enabled: true
config:
guarding_mode: BOTH
text_source: concatenate_all_content
params:
content_safety_url: !env CONTENT_SAFETY_URL
content_safety_key: !env CONTENT_SAFETY_KEY
categories: '[{ "name": "Hate", "rejection_level": 2}, { "name": "Violence",
"rejection_level": 2}]'
output_type: FourSeverityLevels
azure_api_version: '2023-10-01'
request:
url: "$(conf.params.content_safety_url)"
headers:
Ocp-Apim-Subscription-Key: "$(conf.params.content_safety_key)"
body:
categories: "$(category_names)"
text: "$(content)"
outputType: "$(conf.params.output_type)"
queries:
api-version: "$(conf.params.azure_api_version)"
response:
block: "$(check_response.block)"
block_message: "$(check_response.block_message)"
functions:
category_names: |
return function(conf)
local category_names = {}
for _, v in ipairs(conf.params.categories or {}) do
if v.rejection_level >= 2 then
table.insert(category_names, v.name)
end
end
return category_names
end
check_response: |
return function(resp, conf)
local res = {
block = true,
block_message = "content passed safety check",
}
if
((type(resp.categoriesAnalysis) ~= "table") or (#resp.categoriesAnalysis == 0))
and ((type(resp.blocklistsMatch) ~= "table") or (#resp.blocklistsMatch == 0))
then
return res, "content safety introspection is invalid"
end
local reason = ""
local ok = true
for _, v in ipairs(resp.categoriesAnalysis or EMPTY_T) do
local category_name = v.category
local failure_level = nil
for _, category in ipairs(conf.params.categories or {}) do
if category.name == category_name then
failure_level = category.rejection_level
break
end
end
if failure_level and (v.severity >= failure_level) then
if not ok then
reason = reason .. "; "
end
ok = false
reason = reason .. "breached category [" .. category_name .. "] at level " .. failure_level
end
end
for _, v in ipairs(resp.blocklistsMatch or EMPTY_T) do
local blocklist_name = v.blocklistName or "unknown_name"
ok = false
reason = reason .. "breached blocklist [" .. blocklist_name .. "]"
end
return {
block = not ok,
block_message = reason,
}
end
global: trueMake sure to replace the following placeholders with your own values:
-
AI_GATEWAY_ID: Theidof your AI Gateway. -
!env CONTENT_SAFETY_URL: The full URL of the Azure AI Content Safety instance. -
!env CONTENT_SAFETY_KEY: The API key to access the Azure AI Content Safety instance.
curl -X POST https://{region}.api.konghq.com/v1/ai-gateways/{AIGatewayId}/policies \
--header "accept: application/json" \
--header "Content-Type: application/json" \
--header "Authorization: Bearer $KONNECT_TOKEN" \
--data '
{
"name": "custom-guardrail-azure-content-safety",
"display_name": "Block request and response content using Azure Content Safety",
"type": "ai-custom-guardrail",
"enabled": true,
"config": {
"guarding_mode": "BOTH",
"text_source": "concatenate_all_content",
"params": {
"content_safety_url": "'$CONTENT_SAFETY_URL'",
"content_safety_key": "'$CONTENT_SAFETY_KEY'",
"categories": "[{ \"name\": \"Hate\", \"rejection_level\": 2}, { \"name\": \"Violence\", \"rejection_level\": 2}]",
"output_type": "FourSeverityLevels",
"azure_api_version": "2023-10-01"
},
"request": {
"url": "$(conf.params.content_safety_url)",
"headers": {
"Ocp-Apim-Subscription-Key": "$(conf.params.content_safety_key)"
},
"body": {
"categories": "$(category_names)",
"text": "$(content)",
"outputType": "$(conf.params.output_type)"
},
"queries": {
"api-version": "$(conf.params.azure_api_version)"
}
},
"response": {
"block": "$(check_response.block)",
"block_message": "$(check_response.block_message)"
},
"functions": {
"category_names": "return function(conf)\n local category_names = {}\n for _, v in ipairs(conf.params.categories or {}) do\n if v.rejection_level >= 2 then\n table.insert(category_names, v.name)\n end\n end\n return category_names\nend\n",
"check_response": "return function(resp, conf)\n local res = {\n block = true,\n block_message = \"content passed safety check\",\n }\n\n if\n ((type(resp.categoriesAnalysis) ~= \"table\") or (#resp.categoriesAnalysis == 0))\n and ((type(resp.blocklistsMatch) ~= \"table\") or (#resp.blocklistsMatch == 0))\n then\n return res, \"content safety introspection is invalid\"\n end\n\n local reason = \"\"\n local ok = true\n\n for _, v in ipairs(resp.categoriesAnalysis or EMPTY_T) do\n local category_name = v.category\n local failure_level = nil\n for _, category in ipairs(conf.params.categories or {}) do\n if category.name == category_name then\n failure_level = category.rejection_level\n break\n end\n end\n\n if failure_level and (v.severity >= failure_level) then\n if not ok then\n reason = reason .. \"; \"\n end\n\n ok = false\n reason = reason .. \"breached category [\" .. category_name .. \"] at level \" .. failure_level\n end\n end\n\n for _, v in ipairs(resp.blocklistsMatch or EMPTY_T) do\n local blocklist_name = v.blocklistName or \"unknown_name\"\n\n ok = false\n reason = reason .. \"breached blocklist [\" .. blocklist_name .. \"]\"\n end\n\n return {\n block = not ok,\n block_message = reason,\n }\nend\n"
}
},
"global": true
}
'Make sure to replace the following placeholders with your own values:
-
region: Geographic region where your Kong Konnect is hosted and operates. -
KONNECT_TOKEN: Your Personal Access Token (PAT) associated with your Konnect account. -
AIGatewayId: Theidof the AI Gateway.
See the Konnect AI Gateway API reference to learn about region-specific URLs and personal access tokens.
Prerequisite: Configure your Personal Access Token
terraform {
required_providers {
konnect = {
source = "kong/konnect"
}
}
}
provider "konnect" {
personal_access_token = "$KONNECT_TOKEN"
server_url = "https://us.api.konghq.com/"
}Add the following to your Terraform configuration:
resource "konnect_ai_gateway_policy" "my_policy" {
name = "custom-guardrail-azure-content-safety"
display_name = "Block request and response content using Azure Content Safety"
type = "ai-custom-guardrail"
enabled = true
global = true
config = jsonencode({
guarding_mode = "BOTH"
text_source = "concatenate_all_content"
params = {
content_safety_url = var.content_safety_url
content_safety_key = var.content_safety_key
categories = "[{ \"name\": \"Hate\", \"rejection_level\": 2}, { \"name\": \"Violence\", \"rejection_level\": 2}]"
output_type = "FourSeverityLevels"
azure_api_version = "2023-10-01"
}
request = {
url = "$(conf.params.content_safety_url)"
headers = {
Ocp-Apim-Subscription-Key = "$(conf.params.content_safety_key)"
}
body = {
categories = "$(category_names)"
text = "$(content)"
outputType = "$(conf.params.output_type)"
}
queries = {
api-version = "$(conf.params.azure_api_version)"
}
}
response = {
block = "$(check_response.block)"
block_message = "$(check_response.block_message)"
}
functions = {
category_names = <<EOF
return function(conf)
local category_names = {}
for _, v in ipairs(conf.params.categories or {}) do
if v.rejection_level >= 2 then
table.insert(category_names, v.name)
end
end
return category_names
end
EOF
check_response = <<EOF
return function(resp, conf)
local res = {
block = true,
block_message = "content passed safety check",
}
if
((type(resp.categoriesAnalysis) ~= "table") or (#resp.categoriesAnalysis == 0))
and ((type(resp.blocklistsMatch) ~= "table") or (#resp.blocklistsMatch == 0))
then
return res, "content safety introspection is invalid"
end
local reason = ""
local ok = true
for _, v in ipairs(resp.categoriesAnalysis or EMPTY_T) do
local category_name = v.category
local failure_level = nil
for _, category in ipairs(conf.params.categories or {}) do
if category.name == category_name then
failure_level = category.rejection_level
break
end
end
if failure_level and (v.severity >= failure_level) then
if not ok then
reason = reason .. "; "
end
ok = false
reason = reason .. "breached category [" .. category_name .. "] at level " .. failure_level
end
end
for _, v in ipairs(resp.blocklistsMatch or EMPTY_T) do
local blocklist_name = v.blocklistName or "unknown_name"
ok = false
reason = reason .. "breached blocklist [" .. blocklist_name .. "]"
end
return {
block = not ok,
block_message = reason,
}
end
EOF
}
})
gateway_id = konnect_ai_gateway.my_ai_gateway.id
}This example requires the following variables to be added to your manifest. You can specify values at runtime by setting TF_VAR_name=value.
variable "content_safety_url" {
type = string
}
variable "content_safety_key" {
type = string
}After creating the policy, add custom-guardrail-azure-content-safety to the policies array of your AI Model. The tool examples update an existing AI Model with the policy attached: replace the entity configuration in the example with your own, as the update replaces the entire AI Model.
ai_gateway_models:
- ref: my-gpt-4o
ai_gateway: !lookup {id: !env AI_GATEWAY_ID}
display_name: my-gpt-4o
name: my-gpt-4o
type: model
capabilities:
- generate
formats:
- type: openai
targets:
- name: gpt-4o
provider: my-openai-account
config:
type: openai
config:
route:
paths:
- "/v1"
model:
body_param: model
values:
- my-gpt-4o
logging:
payloads: false
policies:
- !ref custom-guardrail-azure-content-safety#nameAfter creating the policy, add custom-guardrail-azure-content-safety to the policies array of your AI Model. The tool examples update an existing AI Model with the policy attached: replace the entity configuration in the example with your own, as the update replaces the entire AI Model.
curl -X PUT https://{region}.api.konghq.com/v1/ai-gateways/{AIGatewayId}/models/my-gpt-4o \
--header "accept: application/json" \
--header "Content-Type: application/json" \
--header "Authorization: Bearer $KONNECT_TOKEN" \
--data '
{
"display_name": "my-gpt-4o",
"name": "my-gpt-4o",
"type": "model",
"capabilities": [
"generate"
],
"formats": [
{
"type": "openai"
}
],
"policies": [
"custom-guardrail-azure-content-safety"
],
"targets": [
{
"name": "gpt-4o",
"provider": "my-openai-account",
"config": {
"type": "openai"
}
}
],
"config": {
"route": {
"paths": [
"/v1"
],
"model": {
"body_param": "model",
"values": [
"my-gpt-4o"
]
}
},
"logging": {
"payloads": false
}
}
}
'After creating the policy, add custom-guardrail-azure-content-safety to the policies array of your AI Model. The tool examples update an existing AI Model with the policy attached: replace the entity configuration in the example with your own, as the update replaces the entire AI Model.
resource "konnect_ai_gateway_model" "my_model" {
display_name = "my-gpt-4o"
name = "my-gpt-4o"
type = "model"
capabilities = ["generate"]
formats = [
{
type = "openai"
} ]
targets = [
{
name = "gpt-4o"
provider = "my-openai-account"
config = {
type = "openai"
}
} ]
config = jsonencode({
route = {
paths = ["/v1"]
model = {
body_param = "model"
values = ["my-gpt-4o"]
}
}
logging = {
payloads = false
}
})
policies = [konnect_ai_gateway_policy.my_policy.name]
gateway_id = konnect_ai_gateway.my_ai_gateway.id
}This example requires the following variables to be added to your manifest. You can specify values at runtime by setting TF_VAR_name=value.
variable "content_safety_url" {
type = string
}
variable "content_safety_key" {
type = string
}After creating the policy, add custom-guardrail-azure-content-safety to the policies array of your AI Consumer. The tool examples update an existing AI Consumer with the policy attached: replace the entity configuration in the example with your own, as the update replaces the entire AI Consumer.
ai_gateway_consumers:
- ref: mobile-app-production
ai_gateway: !lookup {id: !env AI_GATEWAY_ID}
display_name: Mobile App - Production
name: mobile-app-production
type: api-key
policies:
- !ref custom-guardrail-azure-content-safety#nameAfter creating the policy, add custom-guardrail-azure-content-safety to the policies array of your AI Consumer. The tool examples update an existing AI Consumer with the policy attached: replace the entity configuration in the example with your own, as the update replaces the entire AI Consumer.
curl -X PUT https://{region}.api.konghq.com/v1/ai-gateways/{AIGatewayId}/consumers/mobile-app-production \
--header "accept: application/json" \
--header "Content-Type: application/json" \
--header "Authorization: Bearer $KONNECT_TOKEN" \
--data '
{
"display_name": "Mobile App - Production",
"name": "mobile-app-production",
"type": "api-key",
"policies": [
"custom-guardrail-azure-content-safety"
]
}
'After creating the policy, add custom-guardrail-azure-content-safety to the policies array of your AI Consumer. The tool examples update an existing AI Consumer with the policy attached: replace the entity configuration in the example with your own, as the update replaces the entire AI Consumer.
resource "konnect_ai_gateway_consumer" "my_consumer" {
display_name = "Mobile App - Production"
name = "mobile-app-production"
type = "api-key"
policies = [konnect_ai_gateway_policy.my_policy.name]
gateway_id = konnect_ai_gateway.my_ai_gateway.id
}This example requires the following variables to be added to your manifest. You can specify values at runtime by setting TF_VAR_name=value.
variable "content_safety_url" {
type = string
}
variable "content_safety_key" {
type = string
}After creating the policy, add custom-guardrail-azure-content-safety to the policies array of your AI Consumer Group. The tool examples update an existing AI Consumer Group with the policy attached: replace the entity configuration in the example with your own, as the update replaces the entire AI Consumer Group.
ai_gateway_consumer_groups:
- ref: internal-teams
ai_gateway: !lookup {id: !env AI_GATEWAY_ID}
display_name: Internal Teams
name: internal-teams
policies:
- !ref custom-guardrail-azure-content-safety#nameAfter creating the policy, add custom-guardrail-azure-content-safety to the policies array of your AI Consumer Group. The tool examples update an existing AI Consumer Group with the policy attached: replace the entity configuration in the example with your own, as the update replaces the entire AI Consumer Group.
curl -X PUT https://{region}.api.konghq.com/v1/ai-gateways/{AIGatewayId}/consumer-groups/internal-teams \
--header "accept: application/json" \
--header "Content-Type: application/json" \
--header "Authorization: Bearer $KONNECT_TOKEN" \
--data '
{
"display_name": "Internal Teams",
"name": "internal-teams",
"policies": [
"custom-guardrail-azure-content-safety"
]
}
'After creating the policy, add custom-guardrail-azure-content-safety to the policies array of your AI Consumer Group. The tool examples update an existing AI Consumer Group with the policy attached: replace the entity configuration in the example with your own, as the update replaces the entire AI Consumer Group.
resource "konnect_ai_gateway_consumer_group" "my_consumer_group" {
display_name = "Internal Teams"
name = "internal-teams"
policies = [konnect_ai_gateway_policy.my_policy.name]
gateway_id = konnect_ai_gateway.my_ai_gateway.id
}This example requires the following variables to be added to your manifest. You can specify values at runtime by setting TF_VAR_name=value.
variable "content_safety_url" {
type = string
}
variable "content_safety_key" {
type = string
}