• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

daisytuner / docc / 22020750556

14 Feb 2026 04:38PM UTC coverage: 64.828% (-1.5%) from 66.315%
22020750556

Pull #524

github

web-flow
Merge 2784aa264 into 9d01cacd5
Pull Request #524: Native Tensor Support - Step 2: Use tensor types on memlets of tensor nodes

245 of 570 new or added lines in 24 files covered. (42.98%)

458 existing lines in 18 files now uncovered.

23080 of 35602 relevant lines covered (64.83%)

371.57 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/python/docc/python/onnx_model_builder.py
1
"""
2
ONNX Model Builder
3

4
Converts JSON graph descriptions emitted by the ONNX tensor dispatchers
5
into valid ONNX model files that can be loaded by ONNX Runtime.
6
"""
7

UNCOV
8
import json
×
UNCOV
9
import os
×
UNCOV
10
from pathlib import Path
×
UNCOV
11
from typing import List, Dict, Any, Set, Tuple
×
12

13

UNCOV
14
def build_onnx_model(json_path: str, output_path: str) -> str:
×
15
    """
16
    Build an ONNX model from a JSON graph description.
17

18
    Args:
19
        json_path: Path to the JSON file containing node definitions
20
        output_path: Path where the .onnx model should be written
21

22
    Returns:
23
        Path to the generated ONNX model file
24
    """
UNCOV
25
    try:
×
UNCOV
26
        import onnx
×
UNCOV
27
        from onnx import helper, TensorProto
×
28
    except ImportError:
×
29
        raise ImportError(
×
30
            "The 'onnx' package is required for ONNX model generation. "
31
            "Install it with: pip install onnx"
32
        )
33

34
    # Read the JSON file
UNCOV
35
    with open(json_path, "r") as f:
×
UNCOV
36
        content = f.read().strip()
×
37

38
    # The JSON file contains comma-separated node definitions
39
    # Wrap in array brackets to make it valid JSON
UNCOV
40
    if content.endswith(","):
×
UNCOV
41
        content = content[:-1]  # Remove trailing comma
×
UNCOV
42
    json_content = f"[{content}]"
×
43

UNCOV
44
    try:
×
UNCOV
45
        nodes_data = json.loads(json_content)
×
46
    except json.JSONDecodeError as e:
×
47
        raise ValueError(
×
48
            f"Failed to parse ONNX graph JSON: {e}\nContent: {json_content[:500]}"
49
        )
50

51
    # Collect all inputs and outputs
UNCOV
52
    all_inputs: Set[str] = set()
×
UNCOV
53
    all_outputs: Set[str] = set()
×
UNCOV
54
    intermediate: Set[str] = set()
×
UNCOV
55
    elem_type = TensorProto.FLOAT  # Default
×
56

57
    # Build ONNX nodes
UNCOV
58
    onnx_nodes = []
×
UNCOV
59
    for node_data in nodes_data:
×
UNCOV
60
        op_type = node_data.get("op_type", "Unknown")
×
UNCOV
61
        name = node_data.get("name", "unnamed")
×
UNCOV
62
        inputs = node_data.get("inputs", [])
×
UNCOV
63
        outputs = node_data.get("outputs", [])
×
UNCOV
64
        attributes = node_data.get("attributes", {})
×
65

UNCOV
66
        if "elem_type" in node_data:
×
UNCOV
67
            elem_type = node_data["elem_type"]
×
68

69
        # Track inputs/outputs
UNCOV
70
        for inp in inputs:
×
UNCOV
71
            if inp not in intermediate:
×
UNCOV
72
                all_inputs.add(inp)
×
UNCOV
73
        for out in outputs:
×
UNCOV
74
            intermediate.add(out)
×
UNCOV
75
            all_outputs.add(out)
×
76

77
        # Build attribute list
UNCOV
78
        onnx_attrs = []
×
UNCOV
79
        for attr_name, attr_value in attributes.items():
×
UNCOV
80
            if isinstance(attr_value, list):
×
UNCOV
81
                if all(isinstance(x, int) for x in attr_value):
×
UNCOV
82
                    onnx_attrs.append(helper.make_attribute(attr_name, attr_value))
×
83
                elif all(isinstance(x, float) for x in attr_value):
×
84
                    onnx_attrs.append(helper.make_attribute(attr_name, attr_value))
×
UNCOV
85
            elif isinstance(attr_value, int):
×
UNCOV
86
                onnx_attrs.append(helper.make_attribute(attr_name, attr_value))
×
UNCOV
87
            elif isinstance(attr_value, float):
×
UNCOV
88
                onnx_attrs.append(helper.make_attribute(attr_name, attr_value))
×
89
            elif isinstance(attr_value, str):
×
90
                onnx_attrs.append(helper.make_attribute(attr_name, attr_value))
×
91

92
        # Create the ONNX node
UNCOV
93
        node = helper.make_node(
×
94
            op_type,
95
            inputs=inputs,
96
            outputs=outputs,
97
            name=name,
98
        )
99
        # Add attributes
UNCOV
100
        node.attribute.extend(onnx_attrs)
×
UNCOV
101
        onnx_nodes.append(node)
×
102

103
    # Remove intermediate values from outputs (only keep final outputs)
104
    # Final outputs are those not consumed by any other node
UNCOV
105
    consumed = set()
×
UNCOV
106
    for node_data in nodes_data:
×
UNCOV
107
        for inp in node_data.get("inputs", []):
×
UNCOV
108
            consumed.add(inp)
×
109

UNCOV
110
    final_outputs = all_outputs - consumed
×
UNCOV
111
    graph_inputs = all_inputs - intermediate
×
112

113
    # If no final outputs, use all outputs
UNCOV
114
    if not final_outputs:
×
115
        final_outputs = all_outputs
×
116

117
    # Create graph inputs (with dynamic shapes using dim_param)
UNCOV
118
    input_tensors = []
×
UNCOV
119
    for inp_name in sorted(graph_inputs):
×
120
        # Use dynamic shape with symbolic dimension
UNCOV
121
        input_tensor = helper.make_tensor_value_info(
×
122
            inp_name, elem_type, None  # Dynamic shape
123
        )
UNCOV
124
        input_tensors.append(input_tensor)
×
125

126
    # Create graph outputs
UNCOV
127
    output_tensors = []
×
UNCOV
128
    for out_name in sorted(final_outputs):
×
UNCOV
129
        output_tensor = helper.make_tensor_value_info(
×
130
            out_name, elem_type, None  # Dynamic shape
131
        )
UNCOV
132
        output_tensors.append(output_tensor)
×
133

134
    # Create the graph
UNCOV
135
    graph = helper.make_graph(
×
136
        onnx_nodes,
137
        "docc_onnx_graph",
138
        input_tensors,
139
        output_tensors,
140
    )
141

142
    # Create the model
UNCOV
143
    model = helper.make_model(
×
144
        graph, opset_imports=[helper.make_opsetid("", 13)]  # ONNX opset 13
145
    )
146

147
    # Set IR version
UNCOV
148
    model.ir_version = 7
×
149

150
    # Validate the model
UNCOV
151
    try:
×
UNCOV
152
        onnx.checker.check_model(model)
×
UNCOV
153
    except onnx.checker.ValidationError as e:
×
154
        # Log warning but continue - dynamic shapes may cause validation issues
UNCOV
155
        print(f"ONNX validation warning: {e}")
×
156

157
    # Save the model
UNCOV
158
    onnx.save(model, output_path)
×
159

UNCOV
160
    return output_path
×
161

162

UNCOV
163
def convert_json_to_onnx(build_path: str) -> str:
×
164
    """
165
    Find and convert ONNX JSON files in the build directory.
166

167
    Args:
168
        build_path: Path to the build directory
169

170
    Returns:
171
        Path to the generated ONNX model, or None if no JSON found
172
    """
UNCOV
173
    json_paths = Path(build_path).glob("*.onnx.json")
×
UNCOV
174
    models = []
×
UNCOV
175
    for json_path in json_paths:
×
UNCOV
176
        onnx_path = json_path.parent / (json_path.name.replace(".onnx.json", ".onnx"))
×
UNCOV
177
        build_onnx_model(str(json_path), str(onnx_path))
×
UNCOV
178
        models.append(str(onnx_path))
×
179

UNCOV
180
    return models
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc