From 1c095a537ab2aefe7e8f942c00e38bda7c6e49b1 Mon Sep 17 00:00:00 2001 From: Raja Sekhar Rao Dheekonda Date: Mon, 11 May 2026 20:12:18 -0700 Subject: [PATCH 1/3] fix(ai-red-teaming): use UV tool Python executable for workflow execution Fixes workflow execution failures when running under uv tool environments. Changes from sys.executable (system Python) to resolve_python_executable() which properly detects and uses the UV tool Python that has all required Dreadnode SDK dependencies. Resolves Python path issues causing: - ModuleNotFoundError for dreadnode modules - Missing litellm and AIRT dependencies - Workflow subprocess execution failures This ensures workflows use the same Python environment as the agent runtime. --- capabilities/ai-red-teaming/tools/workflows.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/capabilities/ai-red-teaming/tools/workflows.py b/capabilities/ai-red-teaming/tools/workflows.py index 5c39d50..b62034b 100644 --- a/capabilities/ai-red-teaming/tools/workflows.py +++ b/capabilities/ai-red-teaming/tools/workflows.py @@ -15,6 +15,7 @@ from pathlib import Path from dreadnode.agents.tools import tool +from dreadnode.app.env import resolve_python_executable WORKFLOWS_DIR = Path( os.environ.get( @@ -122,8 +123,9 @@ def execute_workflow( timeout = min(timeout, 600) try: + python_executable = resolve_python_executable() result = subprocess.run( - [sys.executable, str(filepath)], + [python_executable, str(filepath)], capture_output=True, text=True, timeout=timeout, From 51bc5cd076da8d8005b2bf5b1c503f9459fffa8f Mon Sep 17 00:00:00 2001 From: Raja Sekhar Rao Dheekonda Date: Mon, 11 May 2026 20:15:31 -0700 Subject: [PATCH 2/3] fix(ai-red-teaming): comprehensive Python executable path resolution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Version bump to 1.2.1 and complete fix for sys.executable issues across ALL files in the ai-red-teaming capability. Fixed 5 total files: - ✅ tools/workflows.py (already committed) - ✅ tools/attacks.py - ✅ scripts/workflow_helper.py - ✅ scripts/attack_runner.py - ✅ tests/test_attack_runner.py - ✅ capability.yaml (version: 1.2.0 → 1.2.1) All subprocess calls now use resolve_python_executable() instead of sys.executable to ensure proper UV tool Python environment execution. This comprehensive fix ensures that every part of the ai-red-teaming capability uses the correct Python executable with full SDK dependencies. --- capabilities/ai-red-teaming/capability.yaml | 2 +- capabilities/ai-red-teaming/scripts/attack_runner.py | 5 ++++- capabilities/ai-red-teaming/scripts/workflow_helper.py | 5 ++++- capabilities/ai-red-teaming/tests/test_attack_runner.py | 5 ++++- capabilities/ai-red-teaming/tools/attacks.py | 4 +++- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/capabilities/ai-red-teaming/capability.yaml b/capabilities/ai-red-teaming/capability.yaml index 21cebdb..65fd2ef 100644 --- a/capabilities/ai-red-teaming/capability.yaml +++ b/capabilities/ai-red-teaming/capability.yaml @@ -1,6 +1,6 @@ schema: 1 name: ai-red-teaming -version: "1.2.0" +version: "1.2.1" description: > Probe the security and safety of AI applications, agents, and foundation models. Orchestrates adversarial attack workflows to discover vulnerabilities in LLMs, diff --git a/capabilities/ai-red-teaming/scripts/attack_runner.py b/capabilities/ai-red-teaming/scripts/attack_runner.py index 29c6d34..9f64280 100644 --- a/capabilities/ai-red-teaming/scripts/attack_runner.py +++ b/capabilities/ai-red-teaming/scripts/attack_runner.py @@ -20,6 +20,8 @@ import time from pathlib import Path +from dreadnode.app.env import resolve_python_executable + WORKFLOWS_DIR = Path( os.environ.get( "AIRT_WORKFLOWS_DIR", @@ -79,8 +81,9 @@ def _auto_execute_workflow(filename: str, timeout: int = 540) -> str: return "\n[AUTO-EXECUTE] Syntax error in generated script: {} (line {})".format(e.msg, e.lineno) try: + python_executable = resolve_python_executable() result = subprocess.run( - [sys.executable, str(filepath)], + [python_executable, str(filepath)], cwd=str(WORKFLOWS_DIR.parent), capture_output=True, text=True, diff --git a/capabilities/ai-red-teaming/scripts/workflow_helper.py b/capabilities/ai-red-teaming/scripts/workflow_helper.py index d787ef2..0f254b5 100644 --- a/capabilities/ai-red-teaming/scripts/workflow_helper.py +++ b/capabilities/ai-red-teaming/scripts/workflow_helper.py @@ -13,6 +13,8 @@ import time from pathlib import Path +from dreadnode.app.env import resolve_python_executable + WORKFLOWS_DIR = Path( os.environ.get( "AIRT_WORKFLOWS_DIR", @@ -118,8 +120,9 @@ def execute_workflow(params: dict) -> dict: timeout = min(timeout, 600) # Max 10 minutes try: + python_executable = resolve_python_executable() result = subprocess.run( - [sys.executable, str(filepath)], + [python_executable, str(filepath)], cwd=str(WORKFLOWS_DIR.parent), capture_output=True, text=True, diff --git a/capabilities/ai-red-teaming/tests/test_attack_runner.py b/capabilities/ai-red-teaming/tests/test_attack_runner.py index 8570da7..c35c178 100644 --- a/capabilities/ai-red-teaming/tests/test_attack_runner.py +++ b/capabilities/ai-red-teaming/tests/test_attack_runner.py @@ -16,6 +16,8 @@ import pytest +from dreadnode.app.env import resolve_python_executable + # --------------------------------------------------------------------------- # Load attack_runner as a module (it's not a package, just a script) # --------------------------------------------------------------------------- @@ -39,8 +41,9 @@ def _generate(params: dict) -> dict: """Call attack_runner via subprocess and return JSON result.""" payload = json.dumps({"name": "generate_attack", "parameters": params}) env = {**os.environ, "DREADNODE_WORKSPACE_DIR": "/tmp/airt_test"} + python_executable = resolve_python_executable() result = subprocess.run( - [sys.executable, str(RUNNER_PATH)], + [python_executable, str(RUNNER_PATH)], input=payload, capture_output=True, text=True, diff --git a/capabilities/ai-red-teaming/tools/attacks.py b/capabilities/ai-red-teaming/tools/attacks.py index 93b61c0..0655d3c 100644 --- a/capabilities/ai-red-teaming/tools/attacks.py +++ b/capabilities/ai-red-teaming/tools/attacks.py @@ -18,6 +18,7 @@ from pathlib import Path from dreadnode.agents.tools import tool +from dreadnode.app.env import resolve_python_executable _RUNNER_SCRIPT = Path(__file__).parent.parent / "scripts" / "attack_runner.py" @@ -26,8 +27,9 @@ def _call_runner(name: str, params: dict) -> str: """Call attack_runner.py via subprocess with JSON dispatch.""" payload = json.dumps({"name": name, "parameters": params}) try: + python_executable = resolve_python_executable() result = subprocess.run( - [sys.executable, str(_RUNNER_SCRIPT)], + [python_executable, str(_RUNNER_SCRIPT)], input=payload, capture_output=True, text=True, From 23556b899993ca2d21e455cfe981b3d483b512cd Mon Sep 17 00:00:00 2001 From: Raja Sekhar Rao Dheekonda Date: Mon, 11 May 2026 20:50:51 -0700 Subject: [PATCH 3/3] fix(ai-red-teaming): add Python path logging for workflow execution visibility - Added Python executable logging to all subprocess calls - Users now see exactly which Python is executing their workflows - Format: '[INFO] Executing workflow with Python: /path/to/python' - Helps debug environment issues and ensures correct Python is used Files updated: - tools/attacks.py - attack runner subprocess - tools/workflows.py - workflow execution subprocess - scripts/workflow_helper.py - workflow helper subprocess - scripts/attack_runner.py - generated workflow subprocess - tests/test_attack_runner.py - test subprocess Complete fix for Python executable resolution + visibility. --- capabilities/ai-red-teaming/scripts/attack_runner.py | 1 + capabilities/ai-red-teaming/scripts/workflow_helper.py | 1 + capabilities/ai-red-teaming/tests/test_attack_runner.py | 1 + capabilities/ai-red-teaming/tools/attacks.py | 1 + capabilities/ai-red-teaming/tools/workflows.py | 1 + 5 files changed, 5 insertions(+) diff --git a/capabilities/ai-red-teaming/scripts/attack_runner.py b/capabilities/ai-red-teaming/scripts/attack_runner.py index 9f64280..632590a 100644 --- a/capabilities/ai-red-teaming/scripts/attack_runner.py +++ b/capabilities/ai-red-teaming/scripts/attack_runner.py @@ -82,6 +82,7 @@ def _auto_execute_workflow(filename: str, timeout: int = 540) -> str: try: python_executable = resolve_python_executable() + print(f"[INFO] Executing workflow with Python: {python_executable}", file=sys.stderr) result = subprocess.run( [python_executable, str(filepath)], cwd=str(WORKFLOWS_DIR.parent), diff --git a/capabilities/ai-red-teaming/scripts/workflow_helper.py b/capabilities/ai-red-teaming/scripts/workflow_helper.py index 0f254b5..bc42b64 100644 --- a/capabilities/ai-red-teaming/scripts/workflow_helper.py +++ b/capabilities/ai-red-teaming/scripts/workflow_helper.py @@ -121,6 +121,7 @@ def execute_workflow(params: dict) -> dict: try: python_executable = resolve_python_executable() + print(f"[INFO] Executing workflow with Python: {python_executable}", file=sys.stderr) result = subprocess.run( [python_executable, str(filepath)], cwd=str(WORKFLOWS_DIR.parent), diff --git a/capabilities/ai-red-teaming/tests/test_attack_runner.py b/capabilities/ai-red-teaming/tests/test_attack_runner.py index c35c178..bf8b6e0 100644 --- a/capabilities/ai-red-teaming/tests/test_attack_runner.py +++ b/capabilities/ai-red-teaming/tests/test_attack_runner.py @@ -42,6 +42,7 @@ def _generate(params: dict) -> dict: payload = json.dumps({"name": "generate_attack", "parameters": params}) env = {**os.environ, "DREADNODE_WORKSPACE_DIR": "/tmp/airt_test"} python_executable = resolve_python_executable() + print(f"[INFO] Running test with Python: {python_executable}", file=sys.stderr) result = subprocess.run( [python_executable, str(RUNNER_PATH)], input=payload, diff --git a/capabilities/ai-red-teaming/tools/attacks.py b/capabilities/ai-red-teaming/tools/attacks.py index 0655d3c..9601fbd 100644 --- a/capabilities/ai-red-teaming/tools/attacks.py +++ b/capabilities/ai-red-teaming/tools/attacks.py @@ -28,6 +28,7 @@ def _call_runner(name: str, params: dict) -> str: payload = json.dumps({"name": name, "parameters": params}) try: python_executable = resolve_python_executable() + print(f"[INFO] Executing attack runner with Python: {python_executable}", file=sys.stderr) result = subprocess.run( [python_executable, str(_RUNNER_SCRIPT)], input=payload, diff --git a/capabilities/ai-red-teaming/tools/workflows.py b/capabilities/ai-red-teaming/tools/workflows.py index b62034b..b1061bf 100644 --- a/capabilities/ai-red-teaming/tools/workflows.py +++ b/capabilities/ai-red-teaming/tools/workflows.py @@ -124,6 +124,7 @@ def execute_workflow( try: python_executable = resolve_python_executable() + print(f"[INFO] Executing workflow with Python: {python_executable}", file=sys.stderr) result = subprocess.run( [python_executable, str(filepath)], capture_output=True,