Files
troupe-standup/wizard-work/spark/snippet.py
T

157 lines
6.0 KiB
Python
Executable File

#!/usr/bin/env python3
"""Verify the Hermes Agent environment this tree ships into.
Exit 0: every required directory and every required tool is present.
Exit 1: at least one of them is genuinely absent; the line naming it is printed.
Exit 2: usage error.
What "available" means. A tool is available only if `hermes_tools` imports and
exposes a *callable* of that name. Nothing else counts: a name in a list is not
a measurement, which is what this file used to print (`available[tool] = True`
in a loop over a hard-coded list, so run under a bare python3 with no Hermes
session it reported four tools and "verified successfully", rc 0 -- Q109).
Resolution. The profile home is --home, else $HERMES_HOME (which in this
install names the active profile's home, e.g. ~/.hermes/profiles/<profile>),
else ~/.hermes/profiles/$HERMES_PROFILE (default profile: spark). The Hermes
root is --root, else the parent of the profile home's `profiles` directory,
else the profile home's own parent. Every path graded is derived from those two,
so the check can be pointed at a synthetic tree and be seen to fail. The
env-first pattern is the lane's own: hermes_agent_verify.py:27.
Graded by test_env_verifiers.py (black box -- runs this file as a subprocess and
imports nothing from it) and by CHECK-env-verifiers.
Run: python3 snippet.py [--home DIR] [--root DIR] [--profile NAME] [--require TOOL]
"""
import argparse
import importlib
import os
import sys
from pathlib import Path
TOOLS = ["web_search", "web_extract", "read_file", "write_file"]
def profile_name(override=None):
"""The profile named in the reading: --profile, else $HERMES_PROFILE, else spark."""
if override:
return override
return os.environ.get("HERMES_PROFILE") or "spark"
def profile_home(override=None, profile="spark"):
"""The profile home this check grades."""
if override:
return Path(override).expanduser()
env = os.environ.get("HERMES_HOME", "")
if env:
return Path(env).expanduser()
return Path.home() / ".hermes" / "profiles" / profile
def hermes_root(profile_home_dir, override=None):
"""The Hermes root: --root, else the parent of `profiles`, else the parent."""
if override:
return Path(override).expanduser()
profile_home_dir = Path(profile_home_dir)
if profile_home_dir.parent.name == "profiles":
return profile_home_dir.parent.parent
return profile_home_dir.parent
def required_dirs(profile_home_dir, root):
"""Directories whose absence makes the reading fail."""
profile_home_dir = Path(profile_home_dir)
return [
Path(root),
profile_home_dir,
profile_home_dir / "cache" / "scratch",
]
def optional_dirs(root):
"""Directories this lane does not depend on; a miss is noted, not failed."""
root = Path(root)
return [root / "skills", root / "plugins"]
def check_environment(profile_home_dir, root):
"""Return (missing_required, present_optional, missing_optional) as strings."""
missing = [str(p) for p in required_dirs(profile_home_dir, root) if not p.is_dir()]
present_opt = [str(p) for p in optional_dirs(root) if p.is_dir()]
missing_opt = [str(p) for p in optional_dirs(root) if not p.is_dir()]
return missing, present_opt, missing_opt
def check_tools(names=None):
"""Import hermes_tools and ask it for each named tool.
Returns (available: dict[str, bool], error: str). `error` is the import
failure when the package cannot be imported at all, "" otherwise. A name
that resolves to a non-callable is unavailable, not available-but-odd.
"""
names = list(names or TOOLS)
try:
module = importlib.import_module("hermes_tools")
except Exception as exc: # ImportError, or a module that raises on import
return {name: False for name in names}, "%s: %s" % (type(exc).__name__, exc)
available = {}
for name in names:
try:
candidate = getattr(module, name, None)
except Exception:
candidate = None
available[name] = callable(candidate)
return available, ""
def build_parser():
parser = argparse.ArgumentParser(
description="Verify Hermes directories and tools; 0 pass, 1 fail, 2 usage.",
)
parser.add_argument("--home", default=None,
help="profile home to grade (default: $HERMES_HOME or ~/.hermes/profiles/<profile>)")
parser.add_argument("--root", default=None,
help="Hermes root to grade (default: derived from the profile home)")
parser.add_argument("--profile", default=None,
help="profile name (default: $HERMES_PROFILE or spark)")
parser.add_argument("--require", action="append", default=[], metavar="TOOL",
help="also require this tool (repeatable); the negative-control hook")
return parser
def main(argv=None):
args = build_parser().parse_args(argv) # exits 2 on a usage error
profile = profile_name(args.profile)
home = profile_home(args.home, profile)
root = hermes_root(home, args.root)
names = list(TOOLS) + [n for n in args.require if n not in TOOLS]
missing_dirs, present_opt, missing_opt = check_environment(home, root)
available, error = check_tools(names)
unavailable = [n for n in names if not available.get(n)]
print("HERMES_ROOT: %s" % root)
print("HERMES_HOME: %s" % home)
print("HERMES_PROFILE: %s" % profile)
for path in [str(p) for p in required_dirs(home, root)]:
print("DIR %s: %s" % ("MISSING" if path in missing_dirs else "OK", path))
for path in present_opt:
print("DIR OPTIONAL-OK: %s" % path)
for path in missing_opt:
print("DIR OPTIONAL-MISSING: %s" % path)
if error:
print("TOOLS_IMPORT_FAILED: %s" % error)
print("TOOLS_AVAILABLE: %s" % sorted(n for n in names if available.get(n)))
print("TOOLS_MISSING: %s" % unavailable)
ok = not missing_dirs and not unavailable
print("VERDICT: %s" % ("PASS" if ok else "FAIL"))
return 0 if ok else 1
if __name__ == "__main__":
sys.exit(main())