ai-station/.venv/lib/python3.12/site-packages/opentelemetry/instrumentation/bedrock/guardrail.py

223 lines
8.5 KiB
Python
Raw Permalink Normal View History

from enum import Enum
from opentelemetry.semconv._incubating.attributes import (
gen_ai_attributes as GenAIAttributes,
)
from opentelemetry.instrumentation.bedrock.span_utils import set_guardrail_attributes
class Type(Enum):
INPUT = "input"
OUTPUT = "output"
class GuardrailAttributes:
GUARDRAIL = "gen_ai.guardrail"
TYPE = "gen_ai.guardrail.type"
PII = "gen_ai.guardrail.pii"
PATTERN = "gen_ai.guardrail.pattern"
TOPIC = "gen_ai.guardrail.topic"
CONTENT = "gen_ai.guardrail.content"
CONFIDENCE = "gen_ai.guardrail.confidence"
MATCH = "gen_ai.guardrail.match"
def is_guardrail_activated(response):
if "results" in response:
for message in response["results"]:
if message.get("completionReason") == "CONTENT_FILTERED":
return True
if response.get("stopReason") == "guardrail_intervened":
return True
return response.get("amazon-bedrock-guardrailAction") != "NONE"
def handle_invoke_metrics(t: Type, guardrail, attrs, metric_params):
if "invocationMetrics" in guardrail:
if "guardrailProcessingLatency" in guardrail["invocationMetrics"]:
input_latency = guardrail["invocationMetrics"]["guardrailProcessingLatency"]
metric_params.guardrail_latency_histogram.record(
input_latency,
attributes={
**attrs,
GenAIAttributes.GEN_AI_TOKEN_TYPE: t.value,
},
)
if "guardrailCoverage" in guardrail["invocationMetrics"]:
coverage = guardrail["invocationMetrics"]["guardrailCoverage"]
char_guarded = coverage["textCharacters"]["guarded"]
metric_params.guardrail_coverage.add(
char_guarded,
attributes={
**attrs,
GenAIAttributes.GEN_AI_TOKEN_TYPE: t.value,
},
)
def handle_sensitive(t: Type, guardrail, attrs, metric_params):
pii = set()
regex = set()
if "sensitiveInformationPolicy" in guardrail:
sensitive_info = guardrail["sensitiveInformationPolicy"]
if "piiEntities" in sensitive_info:
for entry in sensitive_info["piiEntities"]:
pii.add(entry["type"])
metric_params.guardrail_sensitive_info.add(
1,
attributes={
**attrs,
GuardrailAttributes.TYPE: t.value,
GuardrailAttributes.PII: entry["type"],
},
)
if "regexes" in sensitive_info:
for entry in sensitive_info["regexes"]:
regex.add(entry["name"])
metric_params.guardrail_sensitive_info.add(
1,
attributes={
**attrs,
GuardrailAttributes.TYPE: t.value,
GuardrailAttributes.PATTERN: entry["name"],
},
)
return {
"pii": [*pii],
"regex": [*regex],
}
def handle_topic(t: Type, guardrail, attrs, metric_params):
blocked_topics = set()
if "topicPolicy" in guardrail:
topics = guardrail["topicPolicy"]["topics"]
for topic in topics:
blocked_topics.add(topic["name"])
metric_params.guardrail_topic.add(
1,
attributes={
**attrs,
GuardrailAttributes.TYPE: t.value,
GuardrailAttributes.TOPIC: topic["name"],
},
)
return [*blocked_topics]
def handle_content(t: Type, guardrail, attrs, metric_params):
content = set()
if "contentPolicy" in guardrail:
filters = guardrail["contentPolicy"]["filters"]
for filter in filters:
content.add(filter["type"])
metric_params.guardrail_content.add(
1,
attributes={
**attrs,
GuardrailAttributes.TYPE: t.value,
GuardrailAttributes.CONTENT: filter["type"],
GuardrailAttributes.CONFIDENCE: filter["confidence"],
},
)
return [*content]
def handle_words(t: Type, guardrail, attrs, metric_params):
words = set()
if "wordPolicy" in guardrail:
filters = guardrail["wordPolicy"]
if "customWords" in filters:
for filter in filters["customWords"]:
words.add(filter["match"])
metric_params.guardrail_words.add(
1,
attributes={
**attrs,
GuardrailAttributes.TYPE: t.value,
GuardrailAttributes.MATCH: filter["match"],
},
)
if "managedWordLists" in filters:
for filter in filters["managedWordLists"]:
words.add(filter["match"])
metric_params.guardrail_words.add(
1,
attributes={
**attrs,
GuardrailAttributes.TYPE: t.value,
GuardrailAttributes.MATCH: filter["match"],
},
)
return [*words]
def guardrail_converse(span, response, vendor, model, metric_params):
attrs = {
"gen_ai.vendor": vendor,
GenAIAttributes.GEN_AI_RESPONSE_MODEL: model,
GenAIAttributes.GEN_AI_SYSTEM: "bedrock",
}
input_filters = None
output_filters = []
if "trace" in response and "guardrail" in response["trace"]:
guardrail = response["trace"]["guardrail"]
if "inputAssessment" in guardrail:
guardrail_id = next(iter(guardrail["inputAssessment"]))
attrs[GuardrailAttributes.GUARDRAIL] = guardrail_id
guardrail_info = guardrail["inputAssessment"][guardrail_id]
input_filters = _handle(Type.INPUT, guardrail_info, attrs, metric_params)
if "outputAssessments" in guardrail:
guardrail_id = next(iter(guardrail["outputAssessments"]))
attrs[GuardrailAttributes.GUARDRAIL] = guardrail_id
guardrail_infos = guardrail["outputAssessments"][guardrail_id]
for guardrail_info in guardrail_infos:
output_filters.append(_handle(Type.OUTPUT, guardrail_info, attrs, metric_params))
if is_guardrail_activated(response):
metric_params.guardrail_activation.add(1, attrs)
set_guardrail_attributes(span, input_filters, output_filters)
def guardrail_handling(span, response_body, vendor, model, metric_params):
input_filters = None
output_filters = []
if "amazon-bedrock-guardrailAction" in response_body:
attrs = {
"gen_ai.vendor": vendor,
GenAIAttributes.GEN_AI_RESPONSE_MODEL: model,
GenAIAttributes.GEN_AI_SYSTEM: "bedrock",
}
if "amazon-bedrock-trace" in response_body:
bedrock_trace = response_body["amazon-bedrock-trace"]
if "guardrail" in bedrock_trace and "input" in bedrock_trace["guardrail"]:
guardrail_id = next(iter(bedrock_trace["guardrail"]["input"]))
attrs[GuardrailAttributes.GUARDRAIL] = guardrail_id
guardrail_info = bedrock_trace["guardrail"]["input"][guardrail_id]
input_filters = _handle(Type.INPUT, guardrail_info, attrs, metric_params)
if "guardrail" in bedrock_trace and "outputs" in bedrock_trace["guardrail"]:
outputs = bedrock_trace["guardrail"]["outputs"]
for output in outputs:
guardrail_id = next(iter(output))
attrs[GuardrailAttributes.GUARDRAIL] = guardrail_id
guardrail_info = outputs[0][guardrail_id]
output_filters.append(_handle(Type.OUTPUT, guardrail_info, attrs, metric_params))
if is_guardrail_activated(response_body):
metric_params.guardrail_activation.add(1, attrs)
set_guardrail_attributes(span, input_filters, output_filters)
def _handle(t: Type, guardrail_info, attrs, metric_params):
handle_invoke_metrics(t, guardrail_info, attrs, metric_params)
sensitive = handle_sensitive(t, guardrail_info, attrs, metric_params)
topic = handle_topic(t, guardrail_info, attrs, metric_params)
content = handle_content(t, guardrail_info, attrs, metric_params)
words = handle_words(t, guardrail_info, attrs, metric_params)
return {
"sensitive": sensitive,
"topic": topic,
"content": content,
"words": words,
}