#!/usr/bin/env python3 """ Promptfoo Python assertions over parsed bash tool calls emitted by pi_provider.py. pi_provider.py enriches every bash tool_execution_start event with BashCommandParser output before storing it in metadata.toolCalls: args.commands -> list of ParsedCommand dicts (raw, binary, flags, words, env_vars, wrappers, redirections, operator_after) args.binaries -> list of executed binary names (pipeline-wide) args.words -> list of literal words args.has_binary -> dict[str, bool], e.g. {"pytest": true} args.has_word -> dict[str, bool] args.has_flag -> dict[str, bool], e.g. {"-q": true, "--tb": true} In a promptfoo python assertion these are reachable at: context["metadata"]["toolCalls"][*]["args"] Usage in promptfooconfig.yaml: - type: python value: "file://tests/assert_bash_calls.py:assert_bash_binary" config: binary: pytest The default get_assert() is equivalent to assert_bash_binary with config.binary. """ from typing import Any, Dict, List PASS = {"pass": True, "score": 1.0, "reason": ""} FAIL = {"pass": False, "score": 0.0, "reason": ""} def _bash_args(context: Dict[str, Any]) -> List[Dict[str, Any]]: """Returns the args dict of every bash tool call in the trajectory.""" tool_calls = (context.get("metadata") or {}).get("toolCalls") or [] return [t.get("args", {}) for t in tool_calls if t.get("tool") == "bash"] def _result(passed: bool, reason: str, score: float = 1.0) -> Dict[str, Any]: if passed: return {"pass": True, "score": score, "reason": reason} return {"pass": False, "score": 0.0, "reason": reason} def assert_exec(output: str, context: Dict[str, Any], expected, result: bool) -> Dict[str, Any]: args_list = _bash_args(context) if not args_list: return _result(not result, "No bash tool calls were made at all") for args in args_list: binaries = args.get("binaries", []) per_command = [c.get("binary") for c in args.get("commands", [])] for entity in expected: if entity in binaries or entity in per_command: return _result(result, f"bash invoked '{entity}'") seen = sorted({b for a in args_list for b in a.get("binaries", [])}) return _result(not result, f"'{expected}' was never invoked. Binaries seen: {seen or 'none'}") def assert_no_compile(output: str, context: Dict[str, Any]) -> Dict[str, Any]: """Asserts a specific binaries was not executed in any bash call (config: binary).""" expected = ["ninja", "autoninja"] return assert_exec(output, context, expected, False) def assert_run_unittests(output: str, context: Dict[str, Any]) -> Dict[str, Any]: """Assert that unittests has started """ expected = ["./out/chrome/unit_tests", "./unit_tests", "unit_tests"] return assert_exec(output, context, expected, True)