Repository navigation
feat(tools): add opt-in jev noul task guardrail - #8095
Prince Maurya (PRINCE2-AI) wants to merge 2 commits into
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HyD8jEWKJEV4qoziJRYLmM
Strix Security ReviewWarning This pull request has 20 commits after the last Strix review ( No security issues found. Review summaryReviewed the four added files in this PR, which introduce an opt-in, callable task guardrail backed by TypeSafe's Jev Noul model. The factory validates all configuration (threshold is a finite non-bool value in [0,1]; question and question_name are non-empty strings), and the guardrail validates the SDK response probability before use, so malformed or out-of-range answers can never pass. The only external call sends the task's raw output to the TypeSafe SDK, which is the documented, opt-in purpose of the module and not a code-level vulnerability. Rejection reasons only incorporate developer-supplied configuration and validated numeric probabilities, so no untrusted input is reflected back into the agent. Static analysis (bandit, semgrep) returned no findings, and no hardcoded secrets, unsafe deserialization, or injection sinks were found. No security issues require changes. Updated for Reviewed by Strix |
|
Important Review skippedReview was skipped as selected files did not have any reviewable changes. ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Priority: ⬇️ Low Merge Risk: ⚪ Minimal · up to
|
Closes #8068 (part of #8065).
What this adds
create_jev_noul_guardrail(question, threshold, *, client=None, model=None, question_name="output_passes")increwai_tools.tools.jev_noul_guardrail. It returns a callable task guardrail that asks TypeSafe's Jev model one Noul question about the task output (state=output.raw) and compares Jev's probability with the threshold.>= thresholdreturns(True, output.raw), so the output is kept unchanged. A probability equal to the threshold passes; this is documented.(False, reason). The reason includes the question, the probability and the threshold, and it goes back to the agent through the task's existingguardrail_max_retriesloop. The guardrail adds no retry loop of its own.system_one(authentication, network, API, response validation) propagate unchanged. They never count as a pass and are not turned into a retry.ValueErrorwhen the guardrail is created.system_onemethod (aSystemOneClientprotocol;TypeSafeClientsatisfies it under mypy). With no client, atypesafe_sdk.TypeSafeClientis created from theTYPESAFE_*env vars for each check and closed in afinallyblock.Design notes
LLMGuardrailStartedEventcallsinspect.getsource()on callable guardrails that are notLLMGuardrail/HallucinationGuardrail. That raisesTypeErrorfor an instance of a class with__call__, so the factory returns a closure. A test runs it throughTask(guardrail=...)andprocess_guardrail.from __future__ import annotationsin the module.Task.validate_guardrail_functionchecks the return annotation at runtime, and a string annotation would be rejected.importlib, socrewai_toolsimports without it and mypy passes whether or nottypesafe-sdkis installed. I didn't add atypesafe-sdkextra or touchpyproject.toml/uv.lock, to avoid conflicting with feat(tools): add jev decision tool #8076, which adds that extra for [FEATURE] Add a minimal Jev Choice decision tool using the TypeSafe Python SDK #8066. Questions are sent in the SDK's raw-dict form ({"type": "noul", "instructions": ...}), so tests need neither the SDK nor credentials. Once feat(tools): add jev decision tool #8076 lands, I'm happy to move this next tojev_decision_toolor share a package if maintainers prefer.crewai_tools.__init__for now, so it doesn't conflict with feat(tools): add jev decision tool #8076's export lines. I can add the export if wanted.Tests
lib/crewai-tools/tests/tools/test_jev_noul_guardrail.py: 41 tests with a fake client, no API keys or paid calls.ValueError.LiteAgentOutputworks, and the guardrail reads realtypesafe_sdk.SystemOneResponseobjects. That last test is skipped when the SDK isn't installed.Task(guardrail=...)accepts it, andprocess_guardrailand its events work. ThroughTask._invoke_guardrail_function, a rejection retries the agent once and then passes, and the task fails afterguardrail_max_retriesare used up.Local checks on Python 3.12:
pyteston the new test file: 41 passed withtypesafe-sdk0.7.3 installed, and 38 passed and 2 skipped without it.ruff checkandruff format --check(0.15.1) are clean on the new files.mypy(strict, 1.19.1) is clean on the new module, with and without the SDK installed.banditreports no issues.I have not run it against the live TypeSafe API, because I don't have a key.
AI disclosure
I prepared this with an AI coding assistant (Claude Code), as stated in my comment on the issue, and I reviewed and tested it. I can't apply the
llm-generatedlabel myself; could a maintainer please add it?🤖 Generated with Claude Code
https://claude.ai/code/session_01HyD8jEWKJEV4qoziJRYLmM
Note
Medium Risk
New task guardrail path can block or retry agent output and may call an external TypeSafe API when enabled, though behavior is opt-in and SDK failures fail loudly rather than silently passing.
Overview
Adds an opt-in
create_jev_noul_guardrailfactory undercrewai_tools.tools.jev_noul_guardrail, plus README and broad tests. Tasks can attach a guardrail that sendsoutput.rawto TypeSafe Jev viasystem_onewith one Noul question and passes when the returned probability is ≥ a configured threshold (inclusive); otherwise it returns(False, reason)and uses CrewAI’s existingguardrail_max_retries.Rejections also cover missing or invalid Noul answers; SDK errors propagate instead of being treated as pass or soft retry. Optional
client/model/question_nameare supported; without a client,typesafe_sdkis lazy-imported and a per-checkTypeSafeClientis opened and closed. The factory returns a plain closure (not a callable class) for guardrail introspection compatibility, and the module avoids postponed annotations soTask’s runtime return-type check acceptstuple[bool, Any]. Not exported from top-levelcrewai_toolsyet.Reviewed by Cursor Bugbot for commit 90f79ed. Bugbot is set up for automated code reviews on this repo. Configure here.