echodict/llama.cpp
version https://git-lfs.github.com/spec/v1 oid sha256:cfc44b7ba25614df70e6b65e3341cae0310163bd32fd31a6b928a542df433faf size 30786
0762
1#!/usr/bin/env python32 3"""4This script parses docs/ops/*.csv and creates the ops.md, which is a table documenting supported operations on various ggml backends.5"""6import csv7import logging8import sys9from pathlib import Path10from collections import defaultdict11 12 13class DocsGenerator:14 def __init__(self, ggml_root: str, output_filename: str = "ops.md"):15 self.ggml_root = Path(ggml_root)16 self.ops_dir = self.ggml_root / "docs" / "ops"17 self.output_filename = output_filename18 self.backend_support: dict[str, dict[str, list[bool]]] = defaultdict(19 lambda: defaultdict(list)20 )21 self.all_operations: set[str] = set()22 self.all_backends: set[str] = set()23 self.logger = logging.getLogger(__name__)24 25 def parse_support_files(self) -> None:26 if not self.ops_dir.exists():27 self.logger.warning(f"ops directory not found: {self.ops_dir}")28 return29 30 self.logger.info(f"Parsing support files from {self.ops_dir}...")31 32 for support_file in self.ops_dir.glob("*.csv"):33 self.logger.info(f" Reading: {support_file.name}")34 self._parse_support_file(support_file)35 36 def _parse_support_file(self, file_path: Path) -> None:37 try:38 with open(file_path, "r", newline='') as f:39 reader = csv.DictReader(f)40 41 for row in reader:42 # Skip rows that don't have support mode43 if row.get('test_mode') != 'support':44 continue45 46 backend_name = row.get('backend_name', '').strip()47 operation = row.get('op_name', '').strip()48 supported_str = row.get('error_message', '').strip() # "yes" or "no"49 backend_reg_name = row.get('backend_reg_name', '').strip()50 51 # Skip invalid or error operations52 if not operation or not backend_name or operation in [53 "CONTEXT_ERROR",54 "BUILD_ERROR",55 ]:56 continue57 58 is_supported = supported_str.lower() == "yes"59 60 # Use backend_reg_name for grouping, fallback to backend_name61 backend_key = backend_reg_name if backend_reg_name else backend_name62 63 self.all_backends.add(backend_key)64 self.backend_support[backend_key][operation].append(is_supported)65 self.all_operations.add(operation)66 67 except Exception as e:68 self.logger.error(f" Error parsing {file_path}: {e}")69 70 def get_backend_support_status(self, backend: str, operation: str) -> str:71 support_list = self.backend_support[backend].get(operation, [])72 73 if not support_list:74 return "unsupported"75 76 all_supported = all(support_list)77 any_supported = any(support_list)78 79 if all_supported:80 return "supported"81 elif any_supported:82 return "partially supported"83 else:84 return "unsupported"85 86 def get_support_status(self, operation: str) -> str:87 if operation not in self.all_operations:88 return "unsupported"89 90 support_count = 091 total_backends = len(self.all_backends)92 93 for backend in self.all_backends:94 if self.backend_support[backend].get(operation, False):95 support_count += 196 97 if support_count == 0:98 return "unsupported"99 elif support_count == total_backends:100 return "supported"101 else:102 return "partially supported"103 104 def get_support_symbol(self, status: str) -> str:105 symbols = {"supported": "โ
", "partially supported": "๐ก", "unsupported": "โ"}106 return symbols.get(status, "โ")107 108 def generate_markdown(self) -> str:109 lines = []110 111 lines.append("# GGML Operations")112 lines.append("")113 lines.append("List of GGML operations and backend support status.")114 lines.append("")115 lines.append("## How to add a backend to this table:")116 lines.append("")117 lines.append("1. Run `test-backend-ops support --output csv` with your backend name and redirect output to a csv file in `docs/ops/` (e.g., `docs/ops/CUDA.csv`)")118 lines.append("2. Regenerate `/docs/ops.md` via `./scripts/create_ops_docs.py`")119 lines.append("")120 lines.append("Legend:")121 lines.append("- โ
Fully supported by this backend")122 lines.append("- ๐ก Partially supported by this backend")123 lines.append("- โ Not supported by this backend")124 lines.append("")125 126 backends = sorted(self.all_backends)127 header = "| Operation |"128 for backend in backends:129 header += f" {backend} |"130 131 separator = "|-----------|"132 for _ in backends:133 separator += "------|"134 135 lines.append(header)136 lines.append(separator)137 138 sorted_operations = sorted(self.all_operations)139 140 for operation in sorted_operations:141 row = f"| {operation:>32} |"142 143 for backend in backends:144 status = self.get_backend_support_status(backend, operation)145 if status == "supported":146 symbol = "โ
"147 elif status == "partially supported":148 symbol = "๐ก"149 else:150 symbol = "โ"151 row += f" {symbol} |"152 153 lines.append(row)154 155 lines.append("")156 157 return "\n".join(lines)158 159 def run(self) -> None:160 self.logger.info("Parsing GGML operation support files...")161 self.parse_support_files()162 163 if not self.all_operations:164 self.logger.error(165 "No operations found. Make sure to run test-backend-ops support --output csv > docs/ops/file.csv first."166 )167 return168 169 self.logger.info(170 f"Found {len(self.all_operations)} operations across {len(self.all_backends)} backends"171 )172 173 self.logger.info("Generating markdown...")174 markdown_content = self.generate_markdown()175 176 docs_dir = self.ggml_root / "docs"177 docs_dir.mkdir(exist_ok=True)178 179 ops_file = docs_dir / self.output_filename180 with open(ops_file, "w") as f:181 f.write(markdown_content)182 183 self.logger.info(f"Generated: {ops_file}")184 self.logger.info(f"Operations: {len(self.all_operations)}")185 self.logger.info(f"Backends: {len(self.all_backends)}")186 187 188def main():189 logging.basicConfig(level=logging.INFO)190 191 if len(sys.argv) > 1:192 output_filename = sys.argv[1]193 else:194 output_filename = "ops.md"195 196 generator = DocsGenerator(".", output_filename)197 generator.run()198 199 200if __name__ == "__main__":201 main()202 