From 5b0a046fdbe6f602c10c959d6eab5f30c8dc02fe Mon Sep 17 00:00:00 2001 From: sayakpaul Date: Wed, 14 Jan 2026 10:54:52 +0530 Subject: [PATCH 01/11] start kernels card generation. --- example_usage.py | 36 ++++++++ src/kernels/card_template.md | 26 ++++++ src/kernels/kernel_card_utils.py | 136 +++++++++++++++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 example_usage.py create mode 100644 src/kernels/card_template.md create mode 100644 src/kernels/kernel_card_utils.py diff --git a/example_usage.py b/example_usage.py new file mode 100644 index 0000000..afb04dd --- /dev/null +++ b/example_usage.py @@ -0,0 +1,36 @@ +from pathlib import Path +from kernels.kernel_card_utils import _load_or_create_model_card, _update_model_card_usage +import argparse + + +def main(args): + kernel_dir = Path(args.kernels_dir) + + # Repository ID on Hugging Face Hub + repo_id = "your-org/layer_norm" + + model_card = _load_or_create_model_card( + repo_id_or_path=repo_id, + kernel_description="A fast layer normalization kernel implementation.", + license="apache-2.0" + ) + + updated_card = _update_model_card_usage( + model_card=model_card, + local_path=kernel_dir, + repo_id=repo_id + ) + + card_path = args.card_path or "README.md" + updated_card.save(card_path) + print("Model card updated successfully!") + print("\nUpdated content preview:") + print(updated_card.content[:500] + "...") + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--kernels_dir", type=str, required=True, help="Path to the kernels source.") + parser.add_argument("--card_path", type=str, default=None, help="Path to save the card to.") + args = parser.parse_args() + + main(args) diff --git a/src/kernels/card_template.md b/src/kernels/card_template.md new file mode 100644 index 0000000..c139ead --- /dev/null +++ b/src/kernels/card_template.md @@ -0,0 +1,26 @@ +--- +{{ card_data }} +--- + + + +{{ model_description }} + +## How to use + +```python +# TODO: add an example code snippet for running this kernel +``` + +## Benchmarks + +[TODO: provide benchmarks if available] + +## Code source + +[TODO: provide original code source and other relevant citations if available] + +## Notes + +[TODO: provide additional notes about this kernel if needed] diff --git a/src/kernels/kernel_card_utils.py b/src/kernels/kernel_card_utils.py new file mode 100644 index 0000000..25bf5ba --- /dev/null +++ b/src/kernels/kernel_card_utils.py @@ -0,0 +1,136 @@ +import ast +import re +from pathlib import Path + +from huggingface_hub import ModelCard, ModelCardData +from huggingface_hub.errors import EntryNotFoundError, RepositoryNotFoundError + +MODEL_CARD_TEMPLATE_PATH = Path(__file__).parent / "card_template.md" +DESCRIPTION = """ +This is the repository card of {repo_id} that has been pushed on the Hub. It was built to be used with the [`kernels` library](https://github.com/huggingface/kernels). This card was automatically generated. +""" +EXAMPLE_CODE = """```python +# make sure `kernels` is installed: `pip install -U kernels` +from kernels import get_kernel + +kernel_module = get_kernel("{repo_id}") +{func_name} = kernel_module.{func_name} + +{func_name}(...) +```""" + +is_jinja_available = False +try: + import jinja2 + + is_jinja_available = True +except ImportError: + pass + + +def _load_or_create_model_card( + repo_id_or_path: str = None, + token: str | None = None, + kernel_description: str | None = None, + license: str | None = None, +) -> ModelCard: + """TODO""" + if not is_jinja_available: + raise ValueError( + "Modelcard rendering is based on Jinja templates." + " Please make sure to have `jinja` installed before using `load_or_create_model_card`." + " To install it, please run `pip install Jinja2`." + ) + + try: + # Check if the model card is present on the remote repo + model_card = ModelCard.load(repo_id_or_path, token=token) + except (EntryNotFoundError, RepositoryNotFoundError): + # Otherwise create a model card from template + kernel_description = kernel_description or DESCRIPTION + model_card = ModelCard.from_template( + # Card metadata object that will be converted to YAML block + card_data=ModelCardData(license=license, library_name="kernels"), + template_path=MODEL_CARD_TEMPLATE_PATH, + model_description=kernel_description, + ) + + return model_card + + +def _find_torch_ext_init(local_path: str | Path) -> Path | None: + local_path = Path(local_path) + + torch_ext_dirs = list(local_path.rglob("torch-ext")) + + if not torch_ext_dirs: + return None + + for torch_ext_dir in torch_ext_dirs: + init_files = list(torch_ext_dir.rglob("__init__.py")) + # Filter to get the kernel's __init__.py (not nested test files, etc.) + for init_file in init_files: + # Should be directly under torch-ext/kernel_name/__init__.py + if init_file.parent.parent == torch_ext_dir: + return init_file + + return None + + +def _extract_function_from_all(init_file_path: Path) -> str | None: + try: + content = init_file_path.read_text() + + # Parse the file as an AST + tree = ast.parse(content) + + # Find the __all__ assignment + for node in ast.walk(tree): + if isinstance(node, ast.Assign): + for target in node.targets: + if isinstance(target, ast.Name) and target.id == "__all__": + # Extract the list values + if isinstance(node.value, ast.List): + for elt in node.value.elts: + if isinstance(elt, ast.Constant): + func_name = elt.value + # Skip module names, return the first function-like name + if not func_name.endswith("s") or "_" in func_name: + return func_name + # Fallback: return the first item if no function found + if node.value.elts: + first_elt = node.value.elts[0] + if isinstance(first_elt, ast.Constant): + return first_elt.value + return None + except Exception: + return None + + +def _update_model_card_usage( + model_card: ModelCard, + local_path: str | Path, + repo_id: str = "REPO_ID", +) -> ModelCard: + """TODO""" + init_file = _find_torch_ext_init(local_path) + + if not init_file: + return model_card + + func_name = _extract_function_from_all(init_file) + + if not func_name: + return model_card + + example_code = EXAMPLE_CODE.format(repo_id=repo_id, func_name=func_name) + + # Update the model card content + card_content = str(model_card.content) + pattern = r"(## How to use\s*\n\n)```python\n# TODO: add an example code snippet for running this kernel\n```" + + if re.search(pattern, card_content): + updated_content = re.sub(pattern, r"\1" + example_code, card_content) + model_card.content = updated_content + + return model_card From f0c87c258e0db7bf022e13118a3dc75be33cc61e Mon Sep 17 00:00:00 2001 From: sayakpaul Date: Wed, 14 Jan 2026 11:18:52 +0530 Subject: [PATCH 02/11] up --- example_usage.py | 14 +++----------- src/kernels/kernel_card_utils.py | 4 ++-- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/example_usage.py b/example_usage.py index afb04dd..25fd3bb 100644 --- a/example_usage.py +++ b/example_usage.py @@ -6,20 +6,11 @@ def main(args): kernel_dir = Path(args.kernels_dir) - # Repository ID on Hugging Face Hub - repo_id = "your-org/layer_norm" - model_card = _load_or_create_model_card( - repo_id_or_path=repo_id, - kernel_description="A fast layer normalization kernel implementation.", - license="apache-2.0" + kernel_description=args.description, license="apache-2.0" ) - updated_card = _update_model_card_usage( - model_card=model_card, - local_path=kernel_dir, - repo_id=repo_id - ) + updated_card = _update_model_card_usage(model_card=model_card, local_path=kernel_dir) card_path = args.card_path or "README.md" updated_card.save(card_path) @@ -31,6 +22,7 @@ def main(args): parser = argparse.ArgumentParser() parser.add_argument("--kernels_dir", type=str, required=True, help="Path to the kernels source.") parser.add_argument("--card_path", type=str, default=None, help="Path to save the card to.") + parser.add_argument("--description", type=str, default=None) args = parser.parse_args() main(args) diff --git a/src/kernels/kernel_card_utils.py b/src/kernels/kernel_card_utils.py index 25bf5ba..21d1650 100644 --- a/src/kernels/kernel_card_utils.py +++ b/src/kernels/kernel_card_utils.py @@ -13,7 +13,7 @@ # make sure `kernels` is installed: `pip install -U kernels` from kernels import get_kernel -kernel_module = get_kernel("{repo_id}") +kernel_module = get_kernel("{repo_id}") # <- change the ID if needed {func_name} = kernel_module.{func_name} {func_name}(...) @@ -29,7 +29,7 @@ def _load_or_create_model_card( - repo_id_or_path: str = None, + repo_id_or_path: str = "REPO_ID", token: str | None = None, kernel_description: str | None = None, license: str | None = None, From 8b0aa5d3687179e911a9b8fd0c3ba1e8dc28e1db Mon Sep 17 00:00:00 2001 From: sayakpaul Date: Wed, 14 Jan 2026 13:57:19 +0530 Subject: [PATCH 03/11] address review feedback. --- src/kernels/kernel_card_utils.py | 38 ++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/kernels/kernel_card_utils.py b/src/kernels/kernel_card_utils.py index 21d1650..7acf7e2 100644 --- a/src/kernels/kernel_card_utils.py +++ b/src/kernels/kernel_card_utils.py @@ -21,7 +21,7 @@ is_jinja_available = False try: - import jinja2 + import jinja2 # noqa is_jinja_available = True except ImportError: @@ -61,20 +61,34 @@ def _load_or_create_model_card( def _find_torch_ext_init(local_path: str | Path) -> Path | None: local_path = Path(local_path) - torch_ext_dirs = list(local_path.rglob("torch-ext")) - - if not torch_ext_dirs: + build_toml_path = local_path / "build.toml" + if not build_toml_path.exists(): return None - for torch_ext_dir in torch_ext_dirs: - init_files = list(torch_ext_dir.rglob("__init__.py")) - # Filter to get the kernel's __init__.py (not nested test files, etc.) - for init_file in init_files: - # Should be directly under torch-ext/kernel_name/__init__.py - if init_file.parent.parent == torch_ext_dir: - return init_file + try: + # Import tomli for parsing TOML (Python 3.11+ has tomllib in stdlib) + try: + import tomllib + except ImportError: + import tomli as tomllib + + with open(build_toml_path, "rb") as f: + config = tomllib.load(f) + + # Get kernel name from general.name + kernel_name = config.get("general", {}).get("name") + if not kernel_name: + return None + + module_name = kernel_name.replace("-", "_") + init_file = local_path / "torch-ext" / module_name / "__init__.py" - return None + if init_file.exists(): + return init_file + + return None + except Exception: + return None def _extract_function_from_all(init_file_path: Path) -> str | None: From d5ee2e55b0ee5f7dc600b5d7f684bf79e47dc860 Mon Sep 17 00:00:00 2001 From: sayakpaul Date: Wed, 14 Jan 2026 14:32:48 +0530 Subject: [PATCH 04/11] support backend logging. --- example_usage.py | 3 +- src/kernels/card_template.md | 4 +++ src/kernels/kernel_card_utils.py | 50 +++++++++++++++++++++++++++----- 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/example_usage.py b/example_usage.py index 25fd3bb..7905ddc 100644 --- a/example_usage.py +++ b/example_usage.py @@ -1,5 +1,5 @@ from pathlib import Path -from kernels.kernel_card_utils import _load_or_create_model_card, _update_model_card_usage +from kernels.kernel_card_utils import _load_or_create_model_card, _update_model_card_usage, _update_model_card_backends import argparse @@ -11,6 +11,7 @@ def main(args): ) updated_card = _update_model_card_usage(model_card=model_card, local_path=kernel_dir) + updated_card = _update_model_card_backends(model_card=updated_card, local_path=kernel_dir) card_path = args.card_path or "README.md" updated_card.save(card_path) diff --git a/src/kernels/card_template.md b/src/kernels/card_template.md index c139ead..e30b489 100644 --- a/src/kernels/card_template.md +++ b/src/kernels/card_template.md @@ -13,6 +13,10 @@ should probably proofread and complete it, then remove this comment. --> # TODO: add an example code snippet for running this kernel ``` +## Supported backends + +[TODO: add the backends this kernel supports] + ## Benchmarks [TODO: provide benchmarks if available] diff --git a/src/kernels/kernel_card_utils.py b/src/kernels/kernel_card_utils.py index 7acf7e2..a801bd0 100644 --- a/src/kernels/kernel_card_utils.py +++ b/src/kernels/kernel_card_utils.py @@ -34,7 +34,6 @@ def _load_or_create_model_card( kernel_description: str | None = None, license: str | None = None, ) -> ModelCard: - """TODO""" if not is_jinja_available: raise ValueError( "Modelcard rendering is based on Jinja templates." @@ -58,10 +57,10 @@ def _load_or_create_model_card( return model_card -def _find_torch_ext_init(local_path: str | Path) -> Path | None: +def _parse_build_toml(local_path: str | Path) -> dict | None: local_path = Path(local_path) - build_toml_path = local_path / "build.toml" + if not build_toml_path.exists(): return None @@ -73,8 +72,19 @@ def _find_torch_ext_init(local_path: str | Path) -> Path | None: import tomli as tomllib with open(build_toml_path, "rb") as f: - config = tomllib.load(f) + return tomllib.load(f) + except Exception: + return None + +def _find_torch_ext_init(local_path: str | Path) -> Path | None: + local_path = Path(local_path) + + config = _parse_build_toml(local_path) + if not config: + return None + + try: # Get kernel name from general.name kernel_name = config.get("general", {}).get("name") if not kernel_name: @@ -122,11 +132,8 @@ def _extract_function_from_all(init_file_path: Path) -> str | None: def _update_model_card_usage( - model_card: ModelCard, - local_path: str | Path, - repo_id: str = "REPO_ID", + model_card: ModelCard, local_path: str | Path, repo_id: str = "REPO_ID", ) -> ModelCard: - """TODO""" init_file = _find_torch_ext_init(local_path) if not init_file: @@ -148,3 +155,30 @@ def _update_model_card_usage( model_card.content = updated_content return model_card + + +def _update_model_card_backends(model_card: ModelCard, local_path: str | Path) -> ModelCard: + config = _parse_build_toml(local_path).get("general", {}) + if not config: + return model_card + + card_content = str(model_card.content) + + backends = config.get("backends") + if backends: + backends_list = "\n".join(f"- {backend}" for backend in backends) + pattern = r"(## Supported backends\s*\n\n)\[TODO: add the backends this kernel supports\]" + if re.search(pattern, card_content): + card_content = re.sub(pattern, r"\1" + backends_list, card_content) + + # TODO: should we consider making it a separate utility? + cuda_capabilities = config.get("cuda-capabilities") + if cuda_capabilities: + cuda_list = "\n".join(f"- {cap}" for cap in cuda_capabilities) + cuda_section = f"## CUDA Capabilities\n\n{cuda_list}\n\n" + pattern = r"(## Benchmarks)" + if re.search(pattern, card_content): + card_content = re.sub(pattern, cuda_section + r"\1", card_content) + + model_card.content = card_content + return model_card From c5deecd5394b8ad5556449a8ed41df7e3f6c795d Mon Sep 17 00:00:00 2001 From: sayakpaul Date: Wed, 14 Jan 2026 14:36:53 +0530 Subject: [PATCH 05/11] black --- src/kernels/kernel_card_utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/kernels/kernel_card_utils.py b/src/kernels/kernel_card_utils.py index a801bd0..aa7cd3a 100644 --- a/src/kernels/kernel_card_utils.py +++ b/src/kernels/kernel_card_utils.py @@ -21,7 +21,7 @@ is_jinja_available = False try: - import jinja2 # noqa + import jinja2 # noqa is_jinja_available = True except ImportError: @@ -132,7 +132,9 @@ def _extract_function_from_all(init_file_path: Path) -> str | None: def _update_model_card_usage( - model_card: ModelCard, local_path: str | Path, repo_id: str = "REPO_ID", + model_card: ModelCard, + local_path: str | Path, + repo_id: str = "REPO_ID", ) -> ModelCard: init_file = _find_torch_ext_init(local_path) From 00f970ad2ef9525426b1ad6c0a67f8f6ffdb5631 Mon Sep 17 00:00:00 2001 From: sayakpaul Date: Wed, 14 Jan 2026 17:00:31 +0530 Subject: [PATCH 06/11] up --- src/kernels/kernel_card_utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/kernels/kernel_card_utils.py b/src/kernels/kernel_card_utils.py index aa7cd3a..b71c7f6 100644 --- a/src/kernels/kernel_card_utils.py +++ b/src/kernels/kernel_card_utils.py @@ -159,7 +159,9 @@ def _update_model_card_usage( return model_card -def _update_model_card_backends(model_card: ModelCard, local_path: str | Path) -> ModelCard: +def _update_model_card_backends( + model_card: ModelCard, local_path: str | Path +) -> ModelCard: config = _parse_build_toml(local_path).get("general", {}) if not config: return model_card From 23adda1d681657dfd6523286cb4136e200388e05 Mon Sep 17 00:00:00 2001 From: sayakpaul Date: Wed, 14 Jan 2026 17:16:13 +0530 Subject: [PATCH 07/11] make mypy happy. --- src/kernels/kernel_card_utils.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/kernels/kernel_card_utils.py b/src/kernels/kernel_card_utils.py index b71c7f6..71b34ba 100644 --- a/src/kernels/kernel_card_utils.py +++ b/src/kernels/kernel_card_utils.py @@ -50,7 +50,7 @@ def _load_or_create_model_card( model_card = ModelCard.from_template( # Card metadata object that will be converted to YAML block card_data=ModelCardData(license=license, library_name="kernels"), - template_path=MODEL_CARD_TEMPLATE_PATH, + template_path=str(MODEL_CARD_TEMPLATE_PATH), model_description=kernel_description, ) @@ -117,7 +117,7 @@ def _extract_function_from_all(init_file_path: Path) -> str | None: if isinstance(node.value, ast.List): for elt in node.value.elts: if isinstance(elt, ast.Constant): - func_name = elt.value + func_name = str(elt.value) # Skip module names, return the first function-like name if not func_name.endswith("s") or "_" in func_name: return func_name @@ -125,7 +125,7 @@ def _extract_function_from_all(init_file_path: Path) -> str | None: if node.value.elts: first_elt = node.value.elts[0] if isinstance(first_elt, ast.Constant): - return first_elt.value + return str(first_elt.value) return None except Exception: return None @@ -162,10 +162,12 @@ def _update_model_card_usage( def _update_model_card_backends( model_card: ModelCard, local_path: str | Path ) -> ModelCard: - config = _parse_build_toml(local_path).get("general", {}) + config = _parse_build_toml(local_path) if not config: return model_card + config = config.get("general", {}) + card_content = str(model_card.content) backends = config.get("backends") From 15d1382a5fca77a7e3e9967b8189bb9ae9f8a47d Mon Sep 17 00:00:00 2001 From: sayakpaul Date: Wed, 14 Jan 2026 17:18:00 +0530 Subject: [PATCH 08/11] compat tomllib --- src/kernels/kernel_card_utils.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/kernels/kernel_card_utils.py b/src/kernels/kernel_card_utils.py index 71b34ba..f142f76 100644 --- a/src/kernels/kernel_card_utils.py +++ b/src/kernels/kernel_card_utils.py @@ -2,6 +2,7 @@ import re from pathlib import Path +from .compat import tomllib from huggingface_hub import ModelCard, ModelCardData from huggingface_hub.errors import EntryNotFoundError, RepositoryNotFoundError @@ -65,12 +66,6 @@ def _parse_build_toml(local_path: str | Path) -> dict | None: return None try: - # Import tomli for parsing TOML (Python 3.11+ has tomllib in stdlib) - try: - import tomllib - except ImportError: - import tomli as tomllib - with open(build_toml_path, "rb") as f: return tomllib.load(f) except Exception: From 6c2109e53616141e8c25a18377363734f7ad5214 Mon Sep 17 00:00:00 2001 From: sayakpaul Date: Fri, 16 Jan 2026 10:59:47 +0530 Subject: [PATCH 09/11] implement force_update_content flag --- src/kernels/kernel_card_utils.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/kernels/kernel_card_utils.py b/src/kernels/kernel_card_utils.py index f142f76..c5f2352 100644 --- a/src/kernels/kernel_card_utils.py +++ b/src/kernels/kernel_card_utils.py @@ -34,6 +34,7 @@ def _load_or_create_model_card( token: str | None = None, kernel_description: str | None = None, license: str | None = None, + force_update_content: bool = False, ) -> ModelCard: if not is_jinja_available: raise ValueError( @@ -42,11 +43,15 @@ def _load_or_create_model_card( " To install it, please run `pip install Jinja2`." ) - try: - # Check if the model card is present on the remote repo - model_card = ModelCard.load(repo_id_or_path, token=token) - except (EntryNotFoundError, RepositoryNotFoundError): - # Otherwise create a model card from template + model_card = None + + if not force_update_content: + try: + model_card = ModelCard.load(repo_id_or_path, token=token) + except (EntryNotFoundError, RepositoryNotFoundError): + pass # Will create from template below + + if model_card is None: kernel_description = kernel_description or DESCRIPTION model_card = ModelCard.from_template( # Card metadata object that will be converted to YAML block @@ -154,9 +159,7 @@ def _update_model_card_usage( return model_card -def _update_model_card_backends( - model_card: ModelCard, local_path: str | Path -) -> ModelCard: +def _update_model_card_backends(model_card: ModelCard, local_path: str | Path) -> ModelCard: config = _parse_build_toml(local_path) if not config: return model_card From 667c14c0be9b07b40890e603b6dec1d868800739 Mon Sep 17 00:00:00 2001 From: sayakpaul Date: Fri, 16 Jan 2026 11:17:36 +0530 Subject: [PATCH 10/11] implement utility for license update --- example_usage.py | 11 ++++--- src/kernels/kernel_card_utils.py | 55 ++++++++++++++++++++------------ 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/example_usage.py b/example_usage.py index 7905ddc..c935748 100644 --- a/example_usage.py +++ b/example_usage.py @@ -1,21 +1,22 @@ from pathlib import Path -from kernels.kernel_card_utils import _load_or_create_model_card, _update_model_card_usage, _update_model_card_backends +from kernels.kernel_card_utils import _update_kernel_card_license, _load_or_create_kernel_card, _update_kernel_card_usage, _update_kernel_card_backends import argparse def main(args): kernel_dir = Path(args.kernels_dir) - model_card = _load_or_create_model_card( + kernel_card = _load_or_create_kernel_card( kernel_description=args.description, license="apache-2.0" ) - updated_card = _update_model_card_usage(model_card=model_card, local_path=kernel_dir) - updated_card = _update_model_card_backends(model_card=updated_card, local_path=kernel_dir) + updated_card = _update_kernel_card_usage(kernel_card=kernel_card, local_path=kernel_dir) + updated_card = _update_kernel_card_backends(kernel_card=updated_card, local_path=kernel_dir) + updated_card = _update_kernel_card_license(kernel_card, kernel_dir) card_path = args.card_path or "README.md" updated_card.save(card_path) - print("Model card updated successfully!") + print("Kernel card updated successfully!") print("\nUpdated content preview:") print(updated_card.content[:500] + "...") diff --git a/src/kernels/kernel_card_utils.py b/src/kernels/kernel_card_utils.py index c5f2352..272f122 100644 --- a/src/kernels/kernel_card_utils.py +++ b/src/kernels/kernel_card_utils.py @@ -6,7 +6,7 @@ from huggingface_hub import ModelCard, ModelCardData from huggingface_hub.errors import EntryNotFoundError, RepositoryNotFoundError -MODEL_CARD_TEMPLATE_PATH = Path(__file__).parent / "card_template.md" +KERNEL_CARD_TEMPLATE_PATH = Path(__file__).parent / "card_template.md" DESCRIPTION = """ This is the repository card of {repo_id} that has been pushed on the Hub. It was built to be used with the [`kernels` library](https://github.com/huggingface/kernels). This card was automatically generated. """ @@ -19,6 +19,7 @@ {func_name}(...) ```""" +LIBRARY_NAME = "kernels" is_jinja_available = False try: @@ -29,7 +30,7 @@ pass -def _load_or_create_model_card( +def _load_or_create_kernel_card( repo_id_or_path: str = "REPO_ID", token: str | None = None, kernel_description: str | None = None, @@ -43,24 +44,24 @@ def _load_or_create_model_card( " To install it, please run `pip install Jinja2`." ) - model_card = None + kernel_card = None if not force_update_content: try: - model_card = ModelCard.load(repo_id_or_path, token=token) + kernel_card = ModelCard.load(repo_id_or_path, token=token) except (EntryNotFoundError, RepositoryNotFoundError): pass # Will create from template below - if model_card is None: + if kernel_card is None: kernel_description = kernel_description or DESCRIPTION - model_card = ModelCard.from_template( + kernel_card = ModelCard.from_template( # Card metadata object that will be converted to YAML block - card_data=ModelCardData(license=license, library_name="kernels"), - template_path=str(MODEL_CARD_TEMPLATE_PATH), + card_data=ModelCardData(license=license, library_name=LIBRARY_NAME), + template_path=str(KERNEL_CARD_TEMPLATE_PATH), model_description=kernel_description, ) - return model_card + return kernel_card def _parse_build_toml(local_path: str | Path) -> dict | None: @@ -131,42 +132,42 @@ def _extract_function_from_all(init_file_path: Path) -> str | None: return None -def _update_model_card_usage( - model_card: ModelCard, +def _update_kernel_card_usage( + kernel_card: ModelCard, local_path: str | Path, repo_id: str = "REPO_ID", ) -> ModelCard: init_file = _find_torch_ext_init(local_path) if not init_file: - return model_card + return kernel_card func_name = _extract_function_from_all(init_file) if not func_name: - return model_card + return kernel_card example_code = EXAMPLE_CODE.format(repo_id=repo_id, func_name=func_name) # Update the model card content - card_content = str(model_card.content) + card_content = str(kernel_card.content) pattern = r"(## How to use\s*\n\n)```python\n# TODO: add an example code snippet for running this kernel\n```" if re.search(pattern, card_content): updated_content = re.sub(pattern, r"\1" + example_code, card_content) - model_card.content = updated_content + kernel_card.content = updated_content - return model_card + return kernel_card -def _update_model_card_backends(model_card: ModelCard, local_path: str | Path) -> ModelCard: +def _update_kernel_card_backends(kernel_card: ModelCard, local_path: str | Path) -> ModelCard: config = _parse_build_toml(local_path) if not config: - return model_card + return kernel_card config = config.get("general", {}) - card_content = str(model_card.content) + card_content = str(kernel_card.content) backends = config.get("backends") if backends: @@ -184,5 +185,17 @@ def _update_model_card_backends(model_card: ModelCard, local_path: str | Path) - if re.search(pattern, card_content): card_content = re.sub(pattern, cuda_section + r"\1", card_content) - model_card.content = card_content - return model_card + kernel_card.content = card_content + return kernel_card + + +def _update_kernel_card_license(kernel_card: ModelCard, local_path: str | Path) -> ModelCard: + config = _parse_build_toml(local_path) + if not config: + return kernel_card + + existing_license = kernel_card.data.get("license", None) + license_from_config = config.get("license", None) + final_license = license_from_config or existing_license + kernel_card.data["license"] = final_license + return kernel_card From 8988ab6293d53241271a9f4cf1d5ee3e9d4082fc Mon Sep 17 00:00:00 2001 From: sayakpaul Date: Fri, 16 Jan 2026 11:26:12 +0530 Subject: [PATCH 11/11] implement. --- example_usage.py | 14 +++++++--- src/kernels/card_template.md | 4 +++ src/kernels/kernel_card_utils.py | 46 +++++++++++++++++++++++--------- 3 files changed, 48 insertions(+), 16 deletions(-) diff --git a/example_usage.py b/example_usage.py index c935748..5cb41b4 100644 --- a/example_usage.py +++ b/example_usage.py @@ -1,16 +1,21 @@ from pathlib import Path -from kernels.kernel_card_utils import _update_kernel_card_license, _load_or_create_kernel_card, _update_kernel_card_usage, _update_kernel_card_backends +from kernels.kernel_card_utils import ( + _update_kernel_card_available_funcs, + _update_kernel_card_license, + _load_or_create_kernel_card, + _update_kernel_card_usage, + _update_kernel_card_backends, +) import argparse def main(args): kernel_dir = Path(args.kernels_dir) - kernel_card = _load_or_create_kernel_card( - kernel_description=args.description, license="apache-2.0" - ) + kernel_card = _load_or_create_kernel_card(kernel_description=args.description, license="apache-2.0") updated_card = _update_kernel_card_usage(kernel_card=kernel_card, local_path=kernel_dir) + updated_card = _update_kernel_card_available_funcs(kernel_card=kernel_card, local_path=kernel_dir) updated_card = _update_kernel_card_backends(kernel_card=updated_card, local_path=kernel_dir) updated_card = _update_kernel_card_license(kernel_card, kernel_dir) @@ -20,6 +25,7 @@ def main(args): print("\nUpdated content preview:") print(updated_card.content[:500] + "...") + if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--kernels_dir", type=str, required=True, help="Path to the kernels source.") diff --git a/src/kernels/card_template.md b/src/kernels/card_template.md index e30b489..975cab9 100644 --- a/src/kernels/card_template.md +++ b/src/kernels/card_template.md @@ -13,6 +13,10 @@ should probably proofread and complete it, then remove this comment. --> # TODO: add an example code snippet for running this kernel ``` +## Available functions + +[TODO: add the functions available through this kernel] + ## Supported backends [TODO: add the backends this kernel supports] diff --git a/src/kernels/kernel_card_utils.py b/src/kernels/kernel_card_utils.py index 272f122..6efd818 100644 --- a/src/kernels/kernel_card_utils.py +++ b/src/kernels/kernel_card_utils.py @@ -102,7 +102,7 @@ def _find_torch_ext_init(local_path: str | Path) -> Path | None: return None -def _extract_function_from_all(init_file_path: Path) -> str | None: +def _extract_functions_from_all(init_file_path: Path) -> list[str] | None: try: content = init_file_path.read_text() @@ -114,19 +114,14 @@ def _extract_function_from_all(init_file_path: Path) -> str | None: if isinstance(node, ast.Assign): for target in node.targets: if isinstance(target, ast.Name) and target.id == "__all__": - # Extract the list values + # Extract all list values if isinstance(node.value, ast.List): + functions = [] for elt in node.value.elts: if isinstance(elt, ast.Constant): func_name = str(elt.value) - # Skip module names, return the first function-like name - if not func_name.endswith("s") or "_" in func_name: - return func_name - # Fallback: return the first item if no function found - if node.value.elts: - first_elt = node.value.elts[0] - if isinstance(first_elt, ast.Constant): - return str(first_elt.value) + functions.append(func_name) + return functions if functions else None return None except Exception: return None @@ -142,11 +137,13 @@ def _update_kernel_card_usage( if not init_file: return kernel_card - func_name = _extract_function_from_all(init_file) + func_names = _extract_functions_from_all(init_file) - if not func_name: + if not func_names: return kernel_card + # Use the first function as an example + func_name = func_names[0] example_code = EXAMPLE_CODE.format(repo_id=repo_id, func_name=func_name) # Update the model card content @@ -160,6 +157,31 @@ def _update_kernel_card_usage( return kernel_card +def _update_kernel_card_available_funcs(kernel_card: ModelCard, local_path: str | Path) -> ModelCard: + init_file = _find_torch_ext_init(local_path) + + if not init_file: + return kernel_card + + func_names = _extract_functions_from_all(init_file) + + if not func_names: + return kernel_card + + # Format functions as a bulleted list + functions_list = "\n".join(f"- `{func}`" for func in func_names) + + # Update the model card content + card_content = str(kernel_card.content) + pattern = r"(## Available functions\s*\n\n)\[TODO: add the functions available through this kernel\]" + + if re.search(pattern, card_content): + updated_content = re.sub(pattern, r"\1" + functions_list, card_content) + kernel_card.content = updated_content + + return kernel_card + + def _update_kernel_card_backends(kernel_card: ModelCard, local_path: str | Path) -> ModelCard: config = _parse_build_toml(local_path) if not config: