feat: [Anchor Engine] Add real-time intent preservation integration#1796
feat: [Anchor Engine] Add real-time intent preservation integration#1796HasRahm wants to merge 14 commits into
Conversation
Greptile SummaryThis PR adds
|
| Filename | Overview |
|---|---|
| nemoguardrails/library/hallucination/anchor/actions.py | New async action using httpx; previous blocking requests/third-party URL issues resolved; minor: relevant_chunks type not normalised before JSON serialisation |
| nemoguardrails/library/hallucination/anchor/flows.co | Colang 2.0 flow wiring added; mirrors self check hallucination pattern correctly; $anchor_threshold has no default/documentation |
| nemoguardrails/library/hallucination/anchor/flows.v1.co | Colang 1.0 compatibility flow; mirrors v2 flow structure correctly |
| nemoguardrails/library/hallucination/anchor/init.py | Standard module init with SPDX header; correctly exports check_anchor_drift |
| tests/test_anchor_check.py | Good coverage of key paths: missing key, empty context, allow/block responses, non-200, and exception handling |
| nemoguardrails/utils.py | Minor reformatting of a lambda expression for PEP8 line-length compliance; no functional change |
| tests/guardrails/test_iorails_streaming.py | Updated noqa comment from bare string to F704 code; no functional change |
Sequence Diagram
sequenceDiagram
participant Colang as Colang Runtime
participant Flow as anchor check hallucination (flows.co)
participant Action as check_anchor_drift (actions.py)
participant Env as Environment
participant API as Anchor Engine API (ANCHOR_BASE_URL)
Colang->>Flow: output rail triggered ($check_hallucination==True)
Flow->>Action: await check_anchor_drift(threshold=$anchor_threshold)
Action->>Env: os.environ.get("ANCHOR_API_KEY")
alt API key missing
Action-->>Flow: raise ValueError
end
Action->>Env: os.environ.get("ANCHOR_BASE_URL", "http://localhost:3000")
Action->>Action: extract last_bot_message, relevant_chunks from context
alt message or context empty
Action-->>Flow: return True (pass-through)
end
Action->>API: POST {base_url}/api/score Bearer token + payload
alt HTTP 200
API-->>Action: {allow: true/false}
Action-->>Flow: return bool(allow)
else non-200 or exception
Action-->>Flow: return True (fail-open) + log warning
end
alt is_safe == False
Flow->>Colang: SelfCheckHallucinationRailException / bot inform answer unknown + abort
else is_safe == True
Flow->>Colang: continue normally
end
Prompt To Fix All With AI
This is a comment left during a code review.
Path: nemoguardrails/library/hallucination/anchor/flows.co
Line: 3-4
Comment:
**`$anchor_threshold` undefined raises instead of defaulting**
The flow invokes `check_anchor_drift(threshold=$anchor_threshold)` but never initialises `$anchor_threshold`. If a caller hasn't set this variable, Colang will pass `None` (or raise a runtime error depending on the runtime version). The Python side does coerce `None` gracefully via `try: float(threshold) except TypeError → 0.95`, so it silently works in practice — but the intent is opaque and the default threshold (0.95) is invisible to the person writing the config.
Adding a default assignment at the top of the flow or documenting the required variable in a docstring would make this explicit.
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: nemoguardrails/library/hallucination/anchor/actions.py
Line: 56
Comment:
**`relevant_chunks` type not normalised before JSON serialisation**
`source_context` is passed directly as the `"context"` field in the JSON payload. In many RAG pipelines `relevant_chunks` is stored as a list of strings rather than a single string. A list value serialises to a JSON array, which the API endpoint may not handle. Adding an explicit conversion ensures consistent behaviour:
```suggestion
source_context = context.get("relevant_chunks", "")
if isinstance(source_context, list):
source_context = "\n".join(source_context)
```
How can I resolve this? If you propose a fix, please make it concise.Reviews (10): Last reviewed commit: "fix(pipeline): revert to raw colang assi..." | Re-trigger Greptile
📝 WalkthroughWalkthroughA new async action Changes
Sequence DiagramsequenceDiagram
participant Guardrails as Guardrails<br/>System
participant Action as check_anchor_drift<br/>Action
participant Env as Environment
participant API as Anchor API<br/>Endpoint
Guardrails->>Action: check_anchor_drift(context, threshold)
Action->>Env: read ANCHOR_API_KEY
Env-->>Action: API key
Action->>Action: extract last_bot_message,<br/>relevant_chunks
alt Missing Context
Action-->>Guardrails: return True
else Context Available
Action->>API: POST /api/score<br/>(message, context, threshold)
API-->>Action: JSON response
Action->>Action: parse response.allow
Action-->>Guardrails: return boolean
end
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@nemoguardrails/library/hallucination/anchor/actions.py`:
- Around line 23-24: The code assumes context is a dict and calls
context.get(...) which raises AttributeError if context is None; update the
access in the anchor actions to handle a missing context by using a safe
fallback (e.g., replace context.get(...) with (context or {}).get(...) or
explicitly set context = context or {} at the start of the function), ensuring
variables like last_bot_message and source_context are retrieved from a non-None
mapping; locate these accesses in actions.py around the last_bot_message and
source_context assignments and apply the safe fallback consistently.
- Around line 29-50: The async action currently calls the blocking
requests.post() directly and catches all exceptions; change the code to offload
the blocking HTTP call to a thread using asyncio.to_thread when invoking
requests.post (and response.json()), restrict exception handling to
requests.RequestException, ValueError, and TypeError, and log the caught
exception before returning the safe default; locate the async function that
calls requests.post, the requests.post invocation and the response.json() usage,
and replace direct calls with asyncio.to_thread wrappers while adding logging
for errors and narrowing the except clause to those specific exception types,
then return the same default (True) on handled errors.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: fb30f843-5adc-4d22-98d1-bc1d273dfbeb
📒 Files selected for processing (2)
nemoguardrails/library/hallucination/anchor/__init__.pynemoguardrails/library/hallucination/anchor/actions.py
…nc I/O, and SPDX licensing
…ase, insert NVIDIA license header
…nd use native async
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
…e 100% code coverage
…eypatch in tests to ensure deterministic execution
…ings for both v1 and v2 runtimes
…ime colang configuration of anchor thresholds
…ine streaming test noqa suppression
…lure and handle literal string coercion natively in python controller
a6be550 to
c69efe5
Compare
This PR adds real-time drift monitoring utilizing the @anchor-engine/sdk, providing high-fidelity grounding scores (HHEM-2.1). It includes a +9.2% High-Fidelity Lift benchmark result on CNN/DailyMail.
Summary by CodeRabbit
Release Notes