Spaces:
Running
Running
File size: 7,254 Bytes
9bd422a | 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 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | /**
* OpsetChecker - Displays opset compatibility information for a loaded ONNX model
* Shows current opset version, compares against standard versions, lists domains and custom operators.
* Requirements: 22.1, 22.2, 22.3, 22.4, 22.5
*/
class OpsetChecker {
/**
* Standard ONNX opset versions to compare against.
* @type {number[]}
*/
static STANDARD_OPSETS = [7, 9, 11, 13, 15, 17, 18, 19, 20, 21];
/**
* @param {string} containerId - ID of the container element
*/
constructor(containerId) {
this._containerId = containerId;
this._container = document.getElementById(containerId);
this._data = null;
if (!this._container) {
console.warn(`[OpsetChecker] Container #${containerId} not found`);
}
this._setupEventListeners();
}
// βββ Private ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
/**
* Listen for model:loaded events to auto-update.
*/
_setupEventListeners() {
if (window.EventBus && typeof CONFIG !== 'undefined' && CONFIG.EVENTS) {
window.EventBus.on(CONFIG.EVENTS.MODEL_LOADED, (data) => {
if (data && data.model) {
const result = this.compute(data.model);
this.render(result);
}
});
}
}
/**
* Escape HTML special characters.
* @param {string} str
* @returns {string}
*/
_escapeHtml(str) {
const div = document.createElement('div');
div.appendChild(document.createTextNode(String(str)));
return div.innerHTML;
}
// βββ Public API βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
/**
* Compute opset compatibility data from a parsed model.
* @param {object} parsedModel
* @returns {{ modelOpset: number, compatibility: Array<{version: number, compatible: boolean}>, domains: Array<{domain: string, version: number}>, customOperators: Array<{name: string, domain: string}>, allStandard: boolean }}
*/
compute(parsedModel) {
const modelOpset = (parsedModel && parsedModel.metadata && parsedModel.metadata.opsetVersion) || 0;
// Compare against standard opset versions
// Compatible means the standard version >= model's opset (runtime can run the model)
const compatibility = OpsetChecker.STANDARD_OPSETS.map((version) => ({
version,
compatible: version >= modelOpset,
}));
// Extract all domains and versions from opset_import
const opsetImport = (parsedModel && parsedModel.metadata && Array.isArray(parsedModel.metadata.opsetImport))
? parsedModel.metadata.opsetImport
: [];
const domains = opsetImport.map((op) => ({
domain: op.domain || 'ai.onnx (default)',
version: op.version || 0,
}));
// Find custom domain operators from graph nodes
const nodes = (parsedModel && parsedModel.graph && parsedModel.graph.nodes) || [];
const customOperators = [];
const seen = new Set();
for (const node of nodes) {
if (node.domain && node.domain !== '' && node.domain !== 'ai.onnx') {
const key = `${node.domain}::${node.opType}`;
if (!seen.has(key)) {
seen.add(key);
customOperators.push({ name: node.opType || 'Unknown', domain: node.domain });
}
}
}
const allStandard = customOperators.length === 0;
this._data = { modelOpset, compatibility, domains, customOperators, allStandard };
return this._data;
}
/**
* Render the opset compatibility information into the container.
* @param {{ modelOpset: number, compatibility: Array, domains: Array, customOperators: Array, allStandard: boolean }} data
*/
render(data) {
if (!this._container) return;
if (!data) {
this._container.innerHTML = '<p class="text-muted">No opset information available.</p>';
return;
}
const { modelOpset, compatibility, domains, customOperators, allStandard } = data;
let html = '';
// Current opset version
html += `
<div class="mb-3">
<div class="small">
<i class="fas fa-tag me-1"></i>
<strong>Model Opset Version:</strong> ${modelOpset}
</div>
</div>`;
// Standard opset compatibility table
html += `
<div class="mb-3">
<div class="small fw-bold mb-1">Standard Opset Compatibility</div>
<div class="d-flex flex-wrap gap-1">`;
for (const entry of compatibility) {
const icon = entry.compatible ? 'fa-check-circle text-success' : 'fa-times-circle text-danger';
const label = entry.compatible ? 'Compatible' : 'Incompatible';
html += `
<span class="badge ${entry.compatible ? 'bg-success' : 'bg-danger'} bg-opacity-10 text-${entry.compatible ? 'success' : 'danger'} border"
title="Opset ${entry.version}: ${label}"
aria-label="Opset ${entry.version}: ${label}">
<i class="fas ${icon} me-1"></i>${entry.version}
</span>`;
}
html += `
</div>
</div>`;
// Domains from opset_import
if (domains.length > 0) {
html += `
<div class="mb-3">
<div class="small fw-bold mb-1">Opset Imports</div>
<table class="table table-sm table-hover mb-0" role="grid" aria-label="Opset imports">
<thead>
<tr>
<th scope="col">Domain</th>
<th scope="col" class="text-end">Version</th>
</tr>
</thead>
<tbody>`;
for (const d of domains) {
html += `
<tr>
<td>${this._escapeHtml(d.domain)}</td>
<td class="text-end">${d.version}</td>
</tr>`;
}
html += `
</tbody>
</table>
</div>`;
}
// Custom domain operators
if (allStandard) {
html += `
<div class="small text-success">
<i class="fas fa-check me-1"></i>All operators compatible with standard ONNX
</div>`;
} else {
html += `
<div class="mb-2">
<div class="small fw-bold mb-1">Custom Domain Operators</div>
<table class="table table-sm table-hover mb-0" role="grid" aria-label="Custom domain operators">
<thead>
<tr>
<th scope="col">Operator</th>
<th scope="col">Domain</th>
</tr>
</thead>
<tbody>`;
for (const op of customOperators) {
html += `
<tr>
<td>${this._escapeHtml(op.name)}</td>
<td>${this._escapeHtml(op.domain)}</td>
</tr>`;
}
html += `
</tbody>
</table>
</div>`;
}
this._container.innerHTML = html;
}
/**
* Clear the display.
*/
clear() {
this._data = null;
if (!this._container) return;
this._container.innerHTML = '<p class="text-muted">Select a model to view opset compatibility</p>';
}
/**
* Get the last computed data.
* @returns {object|null}
*/
getData() {
return this._data;
}
}
window.OpsetChecker = OpsetChecker;
|