Spaces:
Running
Running
| /** | |
| * 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; | |