File size: 4,872 Bytes
9b13a24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!/usr/bin/env python3
"""
generate_toolbox.py

Reads registry/*.yaml and regenerates derived artifacts:
  - .env.example (root, derived from providers)
  - directory scaffold (ai-platform/ tree)
  - clients/python + clients/typescript stub stubs
  - mcp/servers manifests stub
  - terraform/datarobot scaffold (kept as committed templates)

Run:  python codegen/generate_toolbox.py   (or: make generate)
"""
from __future__ import annotations

import os
import sys
from pathlib import Path

try:
    import yaml
except ImportError:
    print("PyYAML required:  pip install pyyaml", file=sys.stderr)
    sys.exit(1)

ROOT = Path(__file__).resolve().parent.parent
REGISTRY = ROOT / "registry"

# --- Tree derived from capabilities + providers -------------------------------
BASE_TREE = {
    "00-core": ["inference", "agents", "workflows", "memory", "planning", "evaluation", "orchestration"],
    "10-data": ["ingestion", "connectors", "datasets", "wrangling", "sql", "feature-engineering", "unstructured"],
    "20-predictive-ai": ["projects", "experiments", "autopilot", "time-series", "blueprints", "custom-models"],
    "30-generative-ai": ["llm", "prompts", "rag", "vector-databases", "embeddings", "guardrails"],
    "40-agentic-ai": ["agents", "agent-tools", "mcp", "tracing", "evaluation", "deployment"],
    "50-web-intelligence": ["search", "crawling", "scraping", "extraction", "citations"],
    "60-browser": ["browseruse", "navigation", "interaction", "extraction"],
    "70-tools": ["composio", "custom-tools", "tool-registry"],
    "80-mcp": ["client", "servers", "discovery", "authentication", "permissions"],
    "90-execution": ["sandbox", "containers", "python", "javascript", "distributed-inference"],
    "100-mlops": ["datarobot", "mlflow", "arixe", "langsmith"],
    "110-workloads": ["datarobot"],
    "120-governance": ["authentication", "authorization", "audit", "moderation", "guardrails", "compliance"],
    "130-deployment": ["datarobot", "kubernetes", "docker", "cloud", "ci-cd"],
    "140-apis": ["datarobot", "groq", "openai", "anthropic", "openapi", "mcp"],
    "150-sdks": ["python", "r", "typescript", "go", "rust", "java"],
    "160-cli": ["datarobot"],
    "170-ui": ["gradio", "vercel-ai", "datarobot-apps"],
    "180-voice": ["livekit"],
    "190-hardware": ["echokit"],
    "200-observability": ["opentelemetry", "datadog", "langsmith", "logs", "metrics", "traces"],
    "210-financial-data": ["filings", "companies", "webhooks", "schemas"],
    "220-ai-infrastructure": ["coreweave", "nvidia", "kubernetes", "gpu"],
    "230-geo": ["geocoding", "routing", "geofencing", "gnss", "ins", "sensor-fusion", "maps", "places", "schemas"],
    "900-registry": [],
}


def load_registry(name: str) -> dict:
    path = REGISTRY / name
    if not path.exists():
        return {}
    with open(path) as f:
        return yaml.safe_load(f) or {}


def gen_env(providers: dict) -> str:
    lines = ["# Auto-generated from registry/providers.yaml by codegen/generate_toolbox.py", ""]
    seen = set()
    for name, prov in providers.items():
        apis = prov.get("apis", {})
        vars_ = []
        for api, spec in apis.items():
            if isinstance(spec, dict):
                for key in ("base_env", "token_env", "enabled_flag"):
                    val = spec.get(key)
                    if val and val not in seen:
                        seen.add(val)
                        vars_.append(val)
        if vars_:
            lines.append(f"# ===== {prov.get('label', name)} ===== ")
            for v in vars_:
                lines.append(f'{v}="YOUR_{v}"')
            lines.append("")
    return "\n".join(lines)


def scaffold_tree() -> None:
    base = ROOT / "ai-platform"
    for folder, subs in BASE_TREE.items():
        (base / folder).mkdir(parents=True, exist_ok=True)
        for sub in subs:
            (base / folder / sub).mkdir(parents=True, exist_ok=True)
    # placeholder to keep git-ignorable dirs meaningful
    (base / "900-registry" / ".gitkeep").write_text("")
    print("Scaffolded ai-platform/ tree")


def main() -> int:
    providers = load_registry("providers.yaml").get("providers", {})
    env_path = ROOT / ".env.example"
    env_path.write_text(gen_env(providers))
    print(f"Wrote {env_path}")

    # Keep committed scaffolding in sync
    clients_py = ROOT / "clients/python"
    clients_py.mkdir(parents=True, exist_ok=True)
    for name in providers:
        (clients_py / f"{name}.py").touch(exist_ok=True)

    clients_ts = ROOT / "clients/typescript/src"
    clients_ts.mkdir(parents=True, exist_ok=True)
    for name in providers:
        (clients_ts / f"{name}.ts").touch(exist_ok=True)

    mcp_dir = ROOT / "mcp/servers"
    mcp_dir.mkdir(parents=True, exist_ok=True)

    scaffold_tree()
    print("Done.")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())