operations / scripts /config_navigator.py
jbbove's picture
Fix task output and test infrastructure
8bd860c
#!/usr/bin/env python3
"""
Configuration Navigator for Operations System
This script helps you navigate and understand your config-driven architecture.
Use it to explore, validate, and modify configurations at both global and tool levels.
Usage:
python scripts/config_navigator.py --help
python scripts/config_navigator.py --list-all
python scripts/config_navigator.py --show agent.response_templates
python scripts/config_navigator.py --show omirl.tasks
"""
import os
import sys
import yaml
import argparse
from pathlib import Path
from typing import Dict, Any, List
class ConfigNavigator:
"""Navigate the config-driven architecture"""
def __init__(self, project_root: Path = None):
if project_root is None:
project_root = Path(__file__).parent.parent
self.project_root = project_root
self.agent_config_dir = project_root / "agent" / "config"
self.tools_dir = project_root / "tools"
def list_all_configs(self) -> Dict[str, List[str]]:
"""List all available configuration files"""
configs = {
"🌐 Global Agent Configs": [],
"πŸ”§ Tool-Specific Configs": []
}
# Global configs
if self.agent_config_dir.exists():
for config_file in self.agent_config_dir.glob("*.yaml"):
configs["🌐 Global Agent Configs"].append(f"agent.{config_file.stem}")
# Tool configs
if self.tools_dir.exists():
for tool_dir in self.tools_dir.iterdir():
if tool_dir.is_dir():
tool_config_dir = tool_dir / "config"
if tool_config_dir.exists():
for config_file in tool_config_dir.glob("*.yaml"):
configs["πŸ”§ Tool-Specific Configs"].append(f"{tool_dir.name}.{config_file.stem}")
return configs
def load_config(self, config_path: str) -> Dict[str, Any]:
"""Load a specific configuration file"""
try:
if "." not in config_path:
raise ValueError("Config path must be in format 'namespace.config_name'")
namespace, config_name = config_path.split(".", 1)
if namespace == "agent":
file_path = self.agent_config_dir / f"{config_name}.yaml"
else:
# Assume it's a tool name
file_path = self.tools_dir / namespace / "config" / f"{config_name}.yaml"
if not file_path.exists():
raise FileNotFoundError(f"Config file not found: {file_path}")
with open(file_path, 'r', encoding='utf-8') as f:
return yaml.safe_load(f)
except Exception as e:
print(f"❌ Error loading config '{config_path}': {e}")
return {}
def show_config_structure(self, config_path: str, max_depth: int = 3):
"""Show the structure of a configuration file"""
config = self.load_config(config_path)
if not config:
return
print(f"\nπŸ“‹ **Configuration: {config_path}**")
print("=" * 60)
self._print_dict_structure(config, max_depth=max_depth)
def _print_dict_structure(self, obj: Any, indent: int = 0, max_depth: int = 3, current_depth: int = 0):
"""Recursively print dictionary structure"""
if current_depth >= max_depth:
print(" " * indent + "...")
return
if isinstance(obj, dict):
for key, value in obj.items():
if isinstance(value, dict):
print(" " * indent + f"πŸ“ {key}:")
self._print_dict_structure(value, indent + 1, max_depth, current_depth + 1)
elif isinstance(value, list):
print(" " * indent + f"πŸ“ {key}: [{len(value)} items]")
if value and current_depth < max_depth - 1:
if isinstance(value[0], str):
# Show first few string items
sample = value[:3]
print(" " * (indent + 1) + f"↳ {sample}{'...' if len(value) > 3 else ''}")
else:
self._print_dict_structure(value[0], indent + 1, max_depth, current_depth + 1)
else:
value_str = str(value)
if len(value_str) > 50:
value_str = value_str[:47] + "..."
print(" " * indent + f"πŸ“„ {key}: {value_str}")
elif isinstance(obj, list) and obj:
for i, item in enumerate(obj[:3]): # Show first 3 items
print(" " * indent + f"[{i}]:")
self._print_dict_structure(item, indent + 1, max_depth, current_depth + 1)
if len(obj) > 3:
print(" " * indent + f"... and {len(obj) - 3} more items")
def find_in_configs(self, search_term: str) -> List[tuple]:
"""Search for a term across all configurations"""
results = []
all_configs = self.list_all_configs()
for category, configs in all_configs.items():
for config_path in configs:
config = self.load_config(config_path)
if self._search_in_dict(config, search_term.lower()):
results.append((config_path, category))
return results
def _search_in_dict(self, obj: Any, search_term: str) -> bool:
"""Recursively search for a term in a dictionary"""
if isinstance(obj, dict):
for key, value in obj.items():
if search_term in key.lower() or self._search_in_dict(value, search_term):
return True
elif isinstance(obj, list):
for item in obj:
if self._search_in_dict(item, search_term):
return True
elif isinstance(obj, str):
return search_term in obj.lower()
return False
def validate_configs(self) -> Dict[str, List[str]]:
"""Validate all configuration files"""
results = {"βœ… Valid": [], "❌ Invalid": []}
all_configs = self.list_all_configs()
for category, configs in all_configs.items():
for config_path in configs:
try:
config = self.load_config(config_path)
if config:
results["βœ… Valid"].append(config_path)
else:
results["❌ Invalid"].append(f"{config_path} (empty)")
except Exception as e:
results["❌ Invalid"].append(f"{config_path} ({str(e)})")
return results
def show_config_relationships(self):
"""Show how configurations relate to each other"""
print("\nπŸ”— **Configuration Relationships**")
print("=" * 60)
print("\nπŸ“Š **Control Flow:**")
print("1. πŸ—£οΈ User Request")
print("2. 🧠 llm_router_config.yaml β†’ Route to tool")
print("3. πŸ”§ tool_registry.yaml β†’ Find tool capabilities")
print("4. βš™οΈ tool/config/*.yaml β†’ Execute with parameters")
print("5. πŸ“ response_templates.yaml β†’ Format response")
print("\n🌍 **Shared Data:**")
print("β€’ geography.yaml β†’ Used by all tools for validation")
print("β€’ response_templates.yaml β†’ Used by all tools for responses")
print("\nπŸ”§ **Tool-Specific:**")
print("β€’ tools/{tool}/config/tasks.yaml β†’ What the tool can do")
print("β€’ tools/{tool}/config/parameters.yaml β†’ Tool parameters")
print("β€’ tools/{tool}/config/validation_rules.yaml β†’ Input validation")
def main():
parser = argparse.ArgumentParser(
description="Navigate the config-driven architecture",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s --list-all # List all configs
%(prog)s --show agent.response_templates # Show agent response templates
%(prog)s --show omirl.tasks # Show OMIRL tasks config
%(prog)s --search "precipitazione" # Search for term in all configs
%(prog)s --validate # Validate all configs
%(prog)s --relationships # Show config relationships
"""
)
parser.add_argument("--list-all", action="store_true",
help="List all available configuration files")
parser.add_argument("--show", metavar="CONFIG_PATH",
help="Show structure of specific config (e.g., agent.response_templates)")
parser.add_argument("--search", metavar="TERM",
help="Search for a term across all configurations")
parser.add_argument("--validate", action="store_true",
help="Validate all configuration files")
parser.add_argument("--relationships", action="store_true",
help="Show configuration relationships")
parser.add_argument("--depth", type=int, default=3,
help="Maximum depth for structure display (default: 3)")
args = parser.parse_args()
navigator = ConfigNavigator()
if args.list_all:
print("\nπŸ—ΊοΈ **Available Configurations**")
print("=" * 60)
configs = navigator.list_all_configs()
for category, config_list in configs.items():
print(f"\n{category}:")
for config in config_list:
print(f" β€’ {config}")
elif args.show:
navigator.show_config_structure(args.show, max_depth=args.depth)
elif args.search:
print(f"\nπŸ” **Search Results for '{args.search}'**")
print("=" * 60)
results = navigator.find_in_configs(args.search)
if results:
for config_path, category in results:
print(f"πŸ“„ {config_path} ({category})")
else:
print("No results found.")
elif args.validate:
print("\nβœ… **Configuration Validation**")
print("=" * 60)
results = navigator.validate_configs()
for status, configs in results.items():
print(f"\n{status}:")
for config in configs:
print(f" β€’ {config}")
elif args.relationships:
navigator.show_config_relationships()
else:
parser.print_help()
if __name__ == "__main__":
main()