mirror of
https://github.com/huggingface/transformers.git
synced 2025-08-01 10:41:07 +06:00

Some checks are pending
Self-hosted runner (benchmark) / Benchmark (aws-g5-4xlarge-cache) (push) Waiting to run
Build documentation / build (push) Waiting to run
New model PR merged notification / Notify new model (push) Waiting to run
Slow tests on important models (on Push - A10) / Get all modified files (push) Waiting to run
Slow tests on important models (on Push - A10) / Slow & FA2 tests (push) Blocked by required conditions
Self-hosted runner (push-caller) / Check if setup was changed (push) Waiting to run
Self-hosted runner (push-caller) / build-docker-containers (push) Blocked by required conditions
Self-hosted runner (push-caller) / Trigger Push CI (push) Blocked by required conditions
Secret Leaks / trufflehog (push) Waiting to run
Update Transformers metadata / build_and_package (push) Waiting to run
* Change topological sort to return level-based output (lists of lists) * Update main for modular converter * Update test * update check_modular_conversion * Update gitignore * Fix missing conversion for glm4 * Update * Fix error msg * Fixup * fix docstring * update docs * Add comment * delete qwen3_moe
92 lines
3.8 KiB
Python
92 lines
3.8 KiB
Python
import ast
|
|
from collections import defaultdict
|
|
|
|
|
|
# Function to perform topological sorting
|
|
def topological_sort(dependencies: dict) -> list[list[str]]:
|
|
"""Given the dependencies graph construct sorted list of list of modular files
|
|
|
|
For example, returned list of lists might be:
|
|
[
|
|
["../modular_llama.py", "../modular_gemma.py"], # level 0
|
|
["../modular_llama4.py", "../modular_gemma2.py"], # level 1
|
|
["../modular_glm4.py"], # level 2
|
|
]
|
|
which means llama and gemma do not depend on any other modular models, while llama4 and gemma2
|
|
depend on the models in the first list, and glm4 depends on the models in the second and (optionally) in the first list.
|
|
"""
|
|
|
|
# Nodes are the name of the models to convert (we only add those to the graph)
|
|
nodes = {node.rsplit("modular_", 1)[1].replace(".py", "") for node in dependencies.keys()}
|
|
# This will be a graph from models to convert, to models to convert that should be converted before (as they are a dependency)
|
|
graph = {}
|
|
name_mapping = {}
|
|
for node, deps in dependencies.items():
|
|
node_name = node.rsplit("modular_", 1)[1].replace(".py", "")
|
|
dep_names = {dep.split(".")[-2] for dep in deps}
|
|
dependencies = {dep for dep in dep_names if dep in nodes and dep != node_name}
|
|
graph[node_name] = dependencies
|
|
name_mapping[node_name] = node
|
|
|
|
sorting_list = []
|
|
while len(graph) > 0:
|
|
# Find the nodes with 0 out-degree
|
|
leaf_nodes = {node for node in graph if len(graph[node]) == 0}
|
|
# Add them to the list as next level
|
|
sorting_list.append([name_mapping[node] for node in leaf_nodes])
|
|
# Remove the leafs from the graph (and from the deps of other nodes)
|
|
graph = {node: deps - leaf_nodes for node, deps in graph.items() if node not in leaf_nodes}
|
|
|
|
return sorting_list
|
|
|
|
|
|
# Function to extract class and import info from a file
|
|
def extract_classes_and_imports(file_path):
|
|
with open(file_path, "r", encoding="utf-8") as file:
|
|
tree = ast.parse(file.read(), filename=file_path)
|
|
imports = set()
|
|
|
|
for node in ast.walk(tree):
|
|
if isinstance(node, (ast.Import, ast.ImportFrom)):
|
|
module = node.module if isinstance(node, ast.ImportFrom) else None
|
|
if module and (".modeling_" in module or "transformers.models" in module):
|
|
imports.add(module)
|
|
return imports
|
|
|
|
|
|
# Function to map dependencies between classes
|
|
def map_dependencies(py_files):
|
|
dependencies = defaultdict(set)
|
|
# First pass: Extract all classes and map to files
|
|
for file_path in py_files:
|
|
# dependencies[file_path].add(None)
|
|
class_to_file = extract_classes_and_imports(file_path)
|
|
for module in class_to_file:
|
|
dependencies[file_path].add(module)
|
|
return dependencies
|
|
|
|
|
|
def find_priority_list(py_files):
|
|
"""
|
|
Given a list of modular files, sorts them by topological order. Modular models that DON'T depend on other modular
|
|
models will be higher in the topological order.
|
|
|
|
Args:
|
|
py_files: List of paths to the modular files
|
|
|
|
Returns:
|
|
Ordered list of lists of files and their dependencies (dict)
|
|
|
|
For example, ordered_files might be:
|
|
[
|
|
["../modular_llama.py", "../modular_gemma.py"], # level 0
|
|
["../modular_llama4.py", "../modular_gemma2.py"], # level 1
|
|
["../modular_glm4.py"], # level 2
|
|
]
|
|
which means llama and gemma do not depend on any other modular models, while llama4 and gemma2
|
|
depend on the models in the first list, and glm4 depends on the models in the second and (optionally) in the first list.
|
|
"""
|
|
dependencies = map_dependencies(py_files)
|
|
ordered_files = topological_sort(dependencies)
|
|
return ordered_files, dependencies
|