-
Notifications
You must be signed in to change notification settings - Fork 0
Separated guard and fallback logic into initial and final. #32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
dd8ee5f
28a971f
6cbb41a
6c8a3e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| """Nodo 8: Fallback Final - Stops processing when risky content is detected.""" | ||
|
|
||
| import logging | ||
|
|
||
| from app.agents.state import AgentState | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| def fallback_final(state: AgentState) -> AgentState: | ||
| """ | ||
| Fallback Final node - Stops processing when risky content is detected. | ||
|
|
||
| This node: | ||
| 1. Sets error message indicating that the information requested is classified or not free to know | ||
| 2. Stops the flow by routing to END | ||
|
|
||
| Args: | ||
| state: Agent state containing the response flagged as risky | ||
|
|
||
| Returns: | ||
| Updated state with error_message set, ready to route to END | ||
| """ | ||
| updated_state = state.copy() | ||
|
|
||
| # Set error message for risky content | ||
| updated_state["error_message"] = "The information requested is classified or not free to know." | ||
| logger.warning("Risky content detected. Stopping processing. Response content not logged for security.") | ||
|
|
||
| return updated_state |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| """Nodo 3: Fallback Inicial - Stops processing when malicious content is detected.""" | ||
|
|
||
| import logging | ||
|
|
||
| from app.agents.state import AgentState | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| def fallback_inicial(state: AgentState) -> AgentState: | ||
| """ | ||
| Fallback Inicial node - Stops processing when malicious content is detected. | ||
|
|
||
| This node: | ||
| 1. Sets error message indicating that the user's intentions break the chatbot's rules | ||
| 2. Stops the flow by routing to END | ||
|
|
||
| Args: | ||
| state: Agent state containing the prompt flagged as malicious | ||
|
|
||
| Returns: | ||
| Updated state with error_message set, ready to route to END | ||
| """ | ||
| updated_state = state.copy() | ||
|
|
||
| # Set error message for malicious content | ||
| updated_state["error_message"] = "The user's intentions break the chatbot's rules." | ||
|
Comment on lines
+26
to
+27
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify routing mechanism and error message integration
# First, examine the file in question
echo "=== Content of fallback_inicial.py ==="
cat -n RAGManager/app/agents/nodes/fallback_inicial.pyRepository: ucudal/reto-xmas-2025-goland-ia-backend Length of output: 1280 🏁 Script executed: #!/bin/bash
# Search for routing logic that checks error_message
echo "=== Searching for routing logic that checks error_message ==="
rg -n -C3 'error_message' --type=py | head -100Repository: ucudal/reto-xmas-2025-goland-ia-backend Length of output: 7845 🏁 Script executed: #!/bin/bash
# Search for route_after_guard_inicial implementation
echo "=== Searching for route_after_guard_inicial ==="
rg -n 'route_after_guard_inicial' --type=py -A 20 | head -150Repository: ucudal/reto-xmas-2025-goland-ia-backend Length of output: 4308 🏁 Script executed: #!/bin/bash
# Search for guard_inicial references and how fallback_inicial integrates
echo "=== Searching for guard_inicial integration ==="
rg -n 'guard_inicial|fallback_inicial' --type=py -B 2 -A 2 | head -150Repository: ucudal/reto-xmas-2025-goland-ia-backend Length of output: 6408 🏁 Script executed: #!/bin/bash
# Find the graph/workflow definition to understand routing
echo "=== Searching for graph or workflow setup ==="
fd -e py | xargs rg -l 'add_node.*fallback_inicial|fallback_inicial.*add_node' | head -5Repository: ucudal/reto-xmas-2025-goland-ia-backend Length of output: 159 The error message is overly generic and overwrites more specific violation context. The routing mechanism is explicit and correct— However,
🤖 Prompt for AI Agents |
||
| logger.warning("Malicious content detected. Stopping processing. Prompt content not logged for security.") | ||
|
|
||
| return updated_state | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,74 @@ | ||||||||||||||||||||||||||||||||||||
| """Nodo Guard Final - Validates generated response for PII (risky information detection).""" | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| import logging | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| from guardrails import Guard | ||||||||||||||||||||||||||||||||||||
| from guardrails.hub import DetectPII | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| from app.agents.state import AgentState | ||||||||||||||||||||||||||||||||||||
| from app.core.config import settings | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| logger = logging.getLogger(__name__) | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| # Initialize Guard with DetectPII validator | ||||||||||||||||||||||||||||||||||||
| # Note: The validator must be installed via: guardrails hub install hub://guardrails/detect_pii | ||||||||||||||||||||||||||||||||||||
| _guard_final = Guard().use( | ||||||||||||||||||||||||||||||||||||
| DetectPII( | ||||||||||||||||||||||||||||||||||||
| pii_entities=settings.guardrails_pii_entities, | ||||||||||||||||||||||||||||||||||||
| on_fail="noop", # Don't raise exceptions, handle via state flags | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| def guard_final(state: AgentState) -> AgentState: | ||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||
| Guard final node - Validates generated response for PII using Guardrails DetectPII. | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| This node: | ||||||||||||||||||||||||||||||||||||
| 1. Validates the generated_response using Guardrails DetectPII validator | ||||||||||||||||||||||||||||||||||||
| 2. Sets is_risky flag if PII is detected | ||||||||||||||||||||||||||||||||||||
| 3. Sets error_message if risky content is detected | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| Args: | ||||||||||||||||||||||||||||||||||||
| state: Agent state containing the generated_response | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| Returns: | ||||||||||||||||||||||||||||||||||||
| Updated state with is_risky and error_message set | ||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||
| updated_state = state.copy() | ||||||||||||||||||||||||||||||||||||
| generated_response = state.get("generated_response", "") | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if not generated_response: | ||||||||||||||||||||||||||||||||||||
| # Empty response is considered safe | ||||||||||||||||||||||||||||||||||||
| updated_state["is_risky"] = False | ||||||||||||||||||||||||||||||||||||
| updated_state["error_message"] = None | ||||||||||||||||||||||||||||||||||||
| return updated_state | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||
| # Validate the generated response using Guardrails | ||||||||||||||||||||||||||||||||||||
| validation_result = _guard_final.validate(generated_response) | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| # Check if validation passed | ||||||||||||||||||||||||||||||||||||
| # The validator returns ValidationResult with outcome | ||||||||||||||||||||||||||||||||||||
| # If validation fails, outcome will indicate failure | ||||||||||||||||||||||||||||||||||||
| if validation_result.validation_passed: | ||||||||||||||||||||||||||||||||||||
| updated_state["is_risky"] = False | ||||||||||||||||||||||||||||||||||||
| updated_state["error_message"] = None | ||||||||||||||||||||||||||||||||||||
| logger.debug("Generated response passed PII detection") | ||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||
| # PII detected | ||||||||||||||||||||||||||||||||||||
| updated_state["is_risky"] = True | ||||||||||||||||||||||||||||||||||||
| updated_state["error_message"] = ( | ||||||||||||||||||||||||||||||||||||
| "PII detected in generated response. The information requested is classified or not free to know." | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| logger.warning("PII detected in generated response. Response content not logged for security.") | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| except Exception as e: | ||||||||||||||||||||||||||||||||||||
| # If validation fails due to error, log it but don't block the request | ||||||||||||||||||||||||||||||||||||
| # This is a safety measure - if Guardrails fails, we allow the request | ||||||||||||||||||||||||||||||||||||
| # but log the error for monitoring | ||||||||||||||||||||||||||||||||||||
| logger.error(f"Error during PII detection: {e}") | ||||||||||||||||||||||||||||||||||||
| updated_state["is_risky"] = False | ||||||||||||||||||||||||||||||||||||
| updated_state["error_message"] = None | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+66
to
+72
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reconsider fail-safe behavior for PII detection. The exception handler allows requests to proceed when validation fails due to an error (sets Consider:
This contrasts with except Exception as e:
# If validation fails due to error, log it but don't block the request
- # This is a safety measure - if Guardrails fails, we allow the request
+ # SECURITY: Consider failing closed for PII detection in production
# but log the error for monitoring
logger.error(f"Error during PII detection: {e}")
- updated_state["is_risky"] = False
- updated_state["error_message"] = None
+ # For production: fail closed to prevent PII leakage
+ updated_state["is_risky"] = True
+ updated_state["error_message"] = (
+ "Unable to validate response safety. Please try again later."
+ )📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| return updated_state | ||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,6 +47,19 @@ class Settings(BaseSettings): | |
| default="cpu", | ||
| description="Device for model inference.", | ||
| ) | ||
| guardrails_pii_entities: list[str] = Field( | ||
| default=[ | ||
| "EMAIL_ADDRESS", | ||
| "PHONE_NUMBER", | ||
| "CREDIT_CARD", | ||
| "SSN", | ||
| "US_PASSPORT", | ||
| "US_DRIVER_LICENSE", | ||
| "IBAN_CODE", | ||
| "IP_ADDRESS", | ||
| ], | ||
| description="List of PII entity types to detect using DetectPII validator.", | ||
| ) | ||
|
Comment on lines
+50
to
+62
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: Here are the valid PII entity types supported by the Guardrails DetectPII/GuardrailsPII validator (as exposed in the validator hub / repo documentation). [1][2]
Sources: Guardrails PII validator docs and DetectPII README. 🌐 Web query:
💡 Result: Here are the PII entity types that Presidio Analyzer supports (as listed in the Presidio docs). Global entities, then country/region-specific entities: Global: USA: UK: Spain: Italy: Poland: Singapore: Australia: India: Finland: Korea: Thai: For how to retrieve the runtime list programmatically (AnalyzerEngine.get_supported_entities), see the Analyzer API reference. [2] Sources: Change The entity type Additionally, consider including 🤖 Prompt for AI Agents |
||
| model_config = SettingsConfigDict( | ||
| env_file=".env", | ||
| env_file_encoding="utf-8", | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.