146 lines
4.6 KiB
Python
146 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Hermes Profile Inspector - Inspects a Hermes profile and outputs structured JSON.
|
|
|
|
Usage: python3 profile-inspector.py <profile_name>
|
|
|
|
Outputs JSON with: model, provider, skill_count, cron_count, verification status.
|
|
"""
|
|
import json
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
def discover_model_provider(profile_name: str) -> tuple[str, str]:
|
|
"""Discover model and provider from profile config.yaml"""
|
|
config_path = Path(f"/root/.hermes/profiles/{profile_name}/config.yaml")
|
|
if not config_path.exists():
|
|
return "unknown", "unknown"
|
|
|
|
try:
|
|
import yaml
|
|
with open(config_path) as f:
|
|
cfg = yaml.safe_load(f)
|
|
|
|
model_config = cfg.get("model", {})
|
|
if isinstance(model_config, dict):
|
|
model = model_config.get("default", "unknown")
|
|
provider = model_config.get("provider", "unknown")
|
|
else:
|
|
model = str(model_config)
|
|
provider = "unknown"
|
|
return str(model), str(provider)
|
|
except Exception:
|
|
return "unknown", "unknown"
|
|
|
|
def discover_skills(profile_path: Path) -> list[str]:
|
|
"""Discover skill names from profile skills directory"""
|
|
skills_dir = profile_path / "skills"
|
|
if not skills_dir.exists():
|
|
return []
|
|
|
|
skills = []
|
|
for skill_dir in skills_dir.rglob("*"):
|
|
if skill_dir.is_dir():
|
|
skill_info = skill_dir / "SKILL.md"
|
|
if skill_info.exists():
|
|
skills.append(skill_dir.name)
|
|
return sorted(set(skills))
|
|
|
|
def discover_crons(profile_path: Path) -> list[str]:
|
|
"""Discover cron job names from profile cron directory"""
|
|
cron_dir = profile_path / "cron"
|
|
if not cron_dir.exists():
|
|
return []
|
|
|
|
crons = []
|
|
for item in cron_dir.iterdir():
|
|
if item.is_file() and item.suffix == ".py":
|
|
crons.append(item.stem)
|
|
return sorted(crons)
|
|
|
|
def _find_in_dict(data, target, depth=0, max_depth=10):
|
|
"""Recursively search for target key in nested dict/list structure"""
|
|
if depth > max_depth:
|
|
return False
|
|
if isinstance(data, dict):
|
|
if target in data:
|
|
return True
|
|
for v in data.values():
|
|
if _find_in_dict(v, target, depth + 1, max_depth):
|
|
return True
|
|
elif isinstance(data, list):
|
|
for item in data:
|
|
if _find_in_dict(item, target, depth + 1, max_depth):
|
|
return True
|
|
return False
|
|
|
|
def verify_model_chain(profile_name: str, model: str, provider: str) -> dict[str, Any]:
|
|
"""Verify the model exists in provider's model cache"""
|
|
models_cache_path = Path(f"/root/.hermes/profiles/{profile_name}/models_dev_cache.json")
|
|
|
|
if not models_cache_path.exists():
|
|
return {"cache_found": False}
|
|
|
|
try:
|
|
with open(models_cache_path) as f:
|
|
cache = json.load(f)
|
|
|
|
# Check if model is in cache (recursively search nested structure)
|
|
model_found = _find_in_dict(cache, model)
|
|
return {
|
|
"cache_found": True,
|
|
"model_in_cache": model_found,
|
|
"model": model,
|
|
"provider": provider
|
|
}
|
|
except Exception as e:
|
|
return {"cache_found": True, "error": str(e)}
|
|
|
|
def build_profile_report(profile_name: str) -> dict[str, Any]:
|
|
"""Build structured report for a Hermes profile"""
|
|
profile_path = Path(f"/root/.hermes/profiles/{profile_name}")
|
|
|
|
if not profile_path.exists():
|
|
return {"error": f"Profile {profile_name} not found", "exists": False}
|
|
|
|
model, provider = discover_model_provider(profile_name)
|
|
skills = discover_skills(profile_path)
|
|
crons = discover_crons(profile_path)
|
|
|
|
# Count files in profile
|
|
stat = profile_path.stat()
|
|
|
|
return {
|
|
"exists": True,
|
|
"profile": profile_name,
|
|
"model": model,
|
|
"provider": provider,
|
|
"skills": {
|
|
"count": len(skills),
|
|
"names": skills
|
|
},
|
|
"crons": {
|
|
"count": len(crons),
|
|
"names": crons
|
|
},
|
|
"profile_path": str(profile_path),
|
|
"memory_kb": stat.st_size // 1024,
|
|
"verified": True
|
|
}
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) < 2:
|
|
print(json.dumps({"error": "Usage: profile-inspector.py <profile_name>"}))
|
|
sys.exit(1)
|
|
|
|
profile = sys.argv[1]
|
|
report = build_profile_report(profile)
|
|
|
|
if report.get("exists"):
|
|
model = report.get("model", "unknown")
|
|
provider = report.get("provider", "unknown")
|
|
report["model_verification"] = verify_model_chain(profile, model, provider)
|
|
|
|
print(json.dumps(report, indent=2)) |