| <!DOCTYPE html>
|
| <html lang="en">
|
|
|
| <head>
|
| <meta charset="UTF-8">
|
| <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| <title>AIBOM Generated</title>
|
| <link rel="stylesheet" href="{{ static_root|default('/static') }}/css/style.css?v=2.0">
|
|
|
| </head>
|
|
|
| <body>
|
| <div class="container">
|
|
|
| {% include 'includes/header.html' %}
|
|
|
| |
| |
| |
|
|
|
|
|
|
| <div class="key-info">
|
| <h3>📋 AIBOM Summary</h3>
|
| <div class="aibom-property">
|
| <span class="property-name">Model:</span>
|
| <span class="property-value"><a href="https://huggingface.co/{{ model_id }}" target="_blank">{{ model_id
|
| }}</a></span>
|
| </div>
|
| <div class="aibom-property">
|
| <span class="property-name">Generated:</span>
|
| <span class="property-value">{{ aibom.metadata.timestamp }}</span>
|
| </div>
|
| <div class="aibom-property">
|
| <span class="property-name">SBOM Format:</span>
|
| <span class="property-value">
|
| <a href="https://cyclonedx.org/docs/1.6/json/#components_items_modelCard" target="_blank">{{
|
| aibom.bomFormat }} {{ aibom.specVersion }}</a>,
|
| <a href="https://cyclonedx.org/docs/1.7/json/#components_items_modelCard" target="_blank">{{
|
| aibom.bomFormat }} 1.7</a>
|
| </span>
|
| </div>
|
| <div class="aibom-property">
|
| <span class="property-name">Serial Number:</span>
|
| <span class="property-value">{{ aibom.serialNumber }}</span>
|
| </div>
|
| </div>
|
|
|
|
|
| {% set score_percent = (completeness_score.total_score if completeness_score.total_score != 'Undefined' else 0)
|
| | float %}
|
| {% if score_percent >= 90 %}
|
| {% set score_class = 'progress-excellent' %}
|
| {% set score_label = 'Excellent' %}
|
| {% elif score_percent >= 70 %}
|
| {% set score_class = 'progress-good' %}
|
| {% set score_label = 'Good' %}
|
| {% elif score_percent >= 50 %}
|
| {% set score_class = 'progress-fair' %}
|
| {% set score_label = 'Fair' %}
|
| {% else %}
|
| {% set score_class = 'progress-poor' %}
|
| {% set score_label = 'Poor' %}
|
| {% endif %}
|
|
|
|
|
| <div class="completeness-profile {{ score_class }}-border"
|
| style="display: flex; flex-wrap: wrap; gap: 0; align-items: center;">
|
| <div class="completeness-left"
|
| style="flex: 1 1 50%; min-width: 300px; padding-right: 20px; box-sizing: border-box;">
|
| {% if completeness_score.completeness_profile %}
|
| <h3 style="margin-top:0; margin-bottom:15px;">📊 Completeness Assessment</h3>
|
| <div style="display: flex; gap: 15px; align-items: center;">
|
| <span class="profile-badge profile-{{ completeness_score.completeness_profile.name|lower }}">
|
| {{ completeness_score.completeness_profile.name }}
|
| </span>
|
| <span>{{ completeness_score.completeness_profile.description }}</span>
|
| </div>
|
| {% endif %}
|
| </div>
|
|
|
| <div class="completeness-right" style="flex: 1 1 50%; min-width: 300px; box-sizing: border-box;">
|
| <div class="download-section-inner">
|
| <h3 style="margin-top:0; margin-bottom:15px;">💾 Download your AIBOM</h3>
|
| <div class="download-buttons" style="display: flex; gap: 10px;">
|
| <button onclick="downloadJSON(AIBOM_CDX_JSON_1_6, FILENAME_BASE + '_aibom_1_6.json')">
|
| <img src="{{ static_root|default('/static') }}/images/cdx.webp" alt="CycloneDX Logo"
|
| width="16" height="16" style="filter: brightness(0) invert(1);">
|
| CycloneDX 1.6
|
| </button>
|
| <button onclick="downloadJSON(AIBOM_CDX_JSON_1_7, FILENAME_BASE + '_aibom_1_7.json')">
|
| <img src="{{ static_root|default('/static') }}/images/cdx.webp" alt="CycloneDX Logo"
|
| width="16" height="16" style="filter: brightness(0) invert(1);">
|
| CycloneDX 1.7
|
| </button>
|
| </div>
|
| </div>
|
| </div>
|
| </div>
|
|
|
|
|
| <div class="aibom-viewer">
|
| <div class="aibom-tabs">
|
| <div class="aibom-tab active" onclick="switchTab('human-view')">Human-Friendly View</div>
|
| <div class="aibom-tab" onclick="switchTab('field-checklist')">Field Checklist</div>
|
| <div class="aibom-tab" onclick="switchTab('score-view')">Score Report</div>
|
| <div class="aibom-tab" onclick="switchTab('json-view')">JSON View</div>
|
| </div>
|
|
|
|
|
| <div id="human-view" class="tab-content active">
|
| <div class="aibom-section">
|
| <h4>🤖 AI Model Information</h4>
|
| <div class="aibom-property">
|
| <span class="property-name">Name:</span>
|
| <span class="property-value">
|
| {{ aibom.components[0].name if aibom.components else 'Not specified' }}
|
| </span>
|
| </div>
|
| <div class="aibom-property">
|
| <span class="property-name">Type:</span>
|
| <span class="property-value">
|
| {{ aibom.components[0].type if aibom.components else 'Not specified' }}
|
| </span>
|
| </div>
|
| <div class="aibom-property">
|
| <span class="property-name">Version:</span>
|
| <span class="property-value">{{ aibom.components[0].version if aibom.components else 'Not
|
| specified' }}</span>
|
| </div>
|
| <div class="aibom-property">
|
| <span class="property-name">Description:</span>
|
| <span class="property-value">{{ aibom.components[0].description if aibom.components and
|
| aibom.components[0].description else 'Not specified' }}</span>
|
| </div>
|
| <div class="aibom-property">
|
| <span class="property-name">PURL:</span>
|
| <span class="property-value">{{ aibom.components[0].purl if aibom.components and
|
| aibom.components[0].purl else 'Not specified' }}</span>
|
| </div>
|
| {% if aibom.components and aibom.components[0].licenses %}
|
| <div class="aibom-property">
|
| <span class="property-name">Licenses:</span>
|
| <span class="property-value">
|
| {% for license in aibom.components[0].licenses %}
|
| <span class="tag">
|
| {% if license.license %}
|
| {{ license.license.id if license.license.id else license.license.name }}
|
| {% else %}
|
| Unknown
|
| {% endif %}
|
| </span>
|
| {% endfor %}
|
| </span>
|
| </div>
|
| {% endif %}
|
| </div>
|
|
|
| {% if aibom.components and aibom.components[0].modelCard %}
|
| <div class="aibom-section">
|
| <h4>📊 Model Card</h4>
|
| {% if aibom.components[0].modelCard.modelParameters %}
|
| <div class="aibom-property">
|
| <span class="property-name">Architecture:</span>
|
| <span class="property-value">
|
| {{ aibom.components[0].modelCard.modelParameters.modelArchitecture if
|
| aibom.components[0].modelCard.modelParameters.modelArchitecture else 'Not specified' }}
|
| </span>
|
| </div>
|
| <div class="aibom-property">
|
| <span class="property-name">Task:</span>
|
| <span class="property-value">{{ aibom.components[0].modelCard.modelParameters.task if
|
| aibom.components[0].modelCard.modelParameters.task else 'Not specified' }}</span>
|
| </div>
|
| {% endif %}
|
| {% if aibom.components[0].modelCard.properties %}
|
| <div class="aibom-property">
|
| <span class="property-name">Additional Properties:</span>
|
| <span class="property-value">
|
| {% for prop in aibom.components[0].modelCard.properties %}
|
| <span class="tag">{{ prop.name }}: {{ prop.value }}</span>
|
| {% endfor %}
|
| </span>
|
| </div>
|
| {% endif %}
|
| {% set hp_props = [] %}
|
| {% set quant_props = [] %}
|
| {% if aibom.components[0].properties %}
|
| {% for prop in aibom.components[0].properties %}
|
| {% if prop.name.startswith('hyperparameter:') %}{% if hp_props.append(prop) %}{% endif %}{% endif %}
|
| {% if prop.name.startswith('quantization:') %}{% if quant_props.append(prop) %}{% endif %}{% endif
|
| %}
|
| {% endfor %}
|
| {% endif %}
|
| {% if hp_props %}
|
| <div class="aibom-property">
|
| <span class="property-name">Hyperparameters:</span>
|
| <span class="property-value">
|
| {% for prop in hp_props %}
|
| <span class="tag">{{ prop.name.split(':')[1] | replace('_', ' ') | title }}: {{ prop.value
|
| }}</span>
|
| {% endfor %}
|
| </span>
|
| </div>
|
| {% endif %}
|
| {% if quant_props %}
|
| <div class="aibom-property">
|
| <span class="property-name">Quantization:</span>
|
| <span class="property-value">
|
| {% for prop in quant_props %}
|
| <span class="tag">{{ prop.name.split(':')[1] | replace('_', ' ') | title }}: {{ prop.value
|
| }}</span>
|
| {% endfor %}
|
| </span>
|
| </div>
|
| {% endif %}
|
| </div>
|
| {% endif %}
|
|
|
| {% if aibom.externalReferences %}
|
| <div class="aibom-section">
|
| <h4>🔗 External References</h4>
|
| {% for ref in aibom.externalReferences %}
|
| <div class="aibom-property">
|
| <span class="property-name">{{ ref.type|title }}:</span>
|
| <span class="property-value"><a href="{{ ref.url }}" target="_blank">{{ ref.url }}</a></span>
|
| </div>
|
| {% endfor %}
|
| </div>
|
| {% endif %}
|
|
|
| <div class="aibom-section">
|
| <h4>🛠️ Generation Metadata</h4>
|
| <div class="aibom-property">
|
| <span class="property-name">Generated by:</span>
|
| <span class="property-value">{{ aibom.metadata.tools.components[0].name if aibom.metadata.tools
|
| and aibom.metadata.tools.components else 'Unknown' }}</span>
|
| </div>
|
| <div class="aibom-property">
|
| <span class="property-name">Timestamp:</span>
|
| <span class="property-value">{{ aibom.metadata.timestamp }}</span>
|
| </div>
|
| {% if aibom.components and aibom.components[0].purl %}
|
| <div class="aibom-property">
|
| <span class="property-name">Component PURL:</span>
|
| <span class="property-value"><a href="https://huggingface.co/{{ model_id }}" target="_blank">{{
|
| aibom.components[0].purl }}</a></span>
|
| </div>
|
| {% elif aibom.metadata.component %}
|
| <div class="aibom-property">
|
| <span class="property-name">Component PURL:</span>
|
| <span class="property-value">{{ aibom.metadata.component['bom-ref'] }}</span>
|
| </div>
|
| {% endif %}
|
| </div>
|
| </div>
|
|
|
|
|
| <div id="field-checklist" class="tab-content">
|
| <div class="content-section">
|
| <h3>Field Checklist & Mapping</h3>
|
|
|
|
|
| <div class="field-type-legend">
|
| <h4>Legend</h4>
|
| <div class="legend-item">
|
| <span class="field-tier tier-critical"></span>
|
| <span>Critical</span>
|
| </div>
|
| <div class="legend-item">
|
| <span class="field-tier tier-important"></span>
|
| <span>Important</span>
|
| </div>
|
| <div class="legend-item">
|
| <span class="field-tier tier-supplementary"></span>
|
| <span>Supplementary</span>
|
| </div>
|
| <div class="legend-item">
|
| <strong>CDX</strong> = CycloneDX Standard
|
| </div>
|
| <div class="legend-item">
|
| <strong>AI</strong> = AI-Specific Extension
|
| </div>
|
| </div>
|
|
|
| <p>This breakdown outlines field categories and statuses in the AIBOM generated for model <strong><a
|
| href="https://huggingface.co/{{ model_id }}" target="_blank">{{ model_id
|
| }}</a></strong>, showing how each field impacts the completeness score.</p>
|
|
|
| {% if completeness_score.field_checklist %}
|
|
|
| <div class="category-table">
|
| <h4>Required Fields Category</h4>
|
| <table>
|
| <thead>
|
| <tr>
|
| <th>Status</th>
|
| <th>Field Name</th>
|
| <th>Actual Location</th>
|
| <th>Tier</th>
|
| <th>Type</th>
|
| </tr>
|
| </thead>
|
| <tbody>
|
| {% set required_fields = ['bomFormat', 'specVersion', 'serialNumber', 'version'] %}
|
| {% for field in required_fields %}
|
| <tr>
|
| <td>
|
| {% if completeness_score.field_checklist.get(field, '').startswith('✔') %}
|
| <span class="check-mark">✔</span>
|
| {% else %}
|
| <span class="x-mark">✘</span>
|
| {% endif %}
|
| </td>
|
| <td>{{ field }}</td>
|
| <td>
|
| {% if completeness_score.field_checklist.get(field, '').startswith('✔') %}
|
| $.{{ field }}
|
| {% else %}
|
| Not found
|
| {% endif %}
|
| </td>
|
| <td><span class="field-tier tier-critical"></span> Critical</td>
|
| <td>
|
| {% set f_type = completeness_score.field_types.get(field, 'Unknown') %}
|
| {% set f_url = completeness_score.reference_urls.get(field, '') if
|
| completeness_score.reference_urls else '' %}
|
| {% if f_url %}
|
| <a href="{{ f_url }}" target="_blank">{{ f_type }}</a>
|
| {% else %}
|
| {{ f_type }}
|
| {% endif %}
|
| </td>
|
| </tr>
|
| {% endfor %}
|
| </tbody>
|
| </table>
|
| <div class="category-result">
|
| Result: {{ completeness_score.category_details.required_fields.present_fields if
|
| completeness_score.category_details else 'N/A' }}/{{
|
| completeness_score.category_details.required_fields.total_fields if
|
| completeness_score.category_details else 'N/A' }} present
|
| ({{ completeness_score.category_details.required_fields.percentage if
|
| completeness_score.category_details else 'N/A' }}%) =
|
| {{ completeness_score.section_scores.required_fields if completeness_score.section_scores
|
| else 'N/A' }}/20 points
|
| </div>
|
| </div>
|
|
|
|
|
| <div class="category-table">
|
| <h4>Metadata Category</h4>
|
| <table>
|
| <thead>
|
| <tr>
|
| <th>Status</th>
|
| <th>Field Name</th>
|
| <th>Actual Location</th>
|
| <th>Tier</th>
|
| <th>Type</th>
|
| </tr>
|
| </thead>
|
| <tbody>
|
| {% set metadata_fields = [
|
| ('primaryPurpose', 'Critical'),
|
| ('suppliedBy', 'Critical'),
|
| ('standardCompliance', 'Supplementary'),
|
| ('domain', 'Supplementary'),
|
| ('autonomyType', 'Supplementary')
|
| ] %}
|
| {% for field, tier in metadata_fields %}
|
| <tr>
|
| <td>
|
| {% if completeness_score.field_checklist.get(field, '').startswith('✔') %}
|
| <span class="check-mark">✔</span>
|
| {% else %}
|
| <span class="x-mark">✘</span>
|
| {% endif %}
|
| </td>
|
| <td>{{ field }}</td>
|
| <td>
|
| {% if completeness_score.field_checklist.get(field, '').startswith('✔') %}
|
| {% if field == 'primaryPurpose' %}
|
| $.components[0].modelCard.modelParameters.task
|
| {% elif field == 'suppliedBy' %}
|
| $.components[0].supplier.name
|
| {% else %}
|
| $.components[0].modelCard.properties[name="{{ field }}"]
|
| {% endif %}
|
| {% else %}
|
| Not found
|
| {% endif %}
|
| </td>
|
| <td><span class="field-tier tier-{{ tier|lower }}"></span> {{ tier }}</td>
|
| <td>
|
| {% set f_type = completeness_score.field_types.get(field, 'Unknown') %}
|
| {% set f_url = completeness_score.reference_urls.get(field, '') if
|
| completeness_score.reference_urls else '' %}
|
| {% if f_url %}
|
| <a href="{{ f_url }}" target="_blank">{{ f_type }}</a>
|
| {% else %}
|
| {{ f_type }}
|
| {% endif %}
|
| </td>
|
| </tr>
|
| {% endfor %}
|
| </tbody>
|
| </table>
|
| <div class="category-result">
|
| Result: {{ completeness_score.category_details.metadata.present_fields if
|
| completeness_score.category_details else 'N/A' }}/{{
|
| completeness_score.category_details.metadata.total_fields if
|
| completeness_score.category_details else 'N/A' }} present
|
| ({{ completeness_score.category_details.metadata.percentage if
|
| completeness_score.category_details else 'N/A' }}%) =
|
| {{ completeness_score.section_scores.metadata if completeness_score.section_scores else
|
| 'N/A' }}/20 points
|
| </div>
|
| </div>
|
|
|
|
|
| <div class="category-table">
|
| <h4>Component Basic Category</h4>
|
| <table>
|
| <thead>
|
| <tr>
|
| <th>Status</th>
|
| <th>Field Name</th>
|
| <th>Actual Location</th>
|
| <th>Tier</th>
|
| <th>Type</th>
|
| </tr>
|
| </thead>
|
| <tbody>
|
| {% set component_basic_fields = [
|
| ('name', 'Critical'),
|
| ('type', 'Critical'),
|
| ('component_version', 'Critical'),
|
| ('purl', 'Important'),
|
| ('description', 'Important'),
|
| ('licenses', 'Important')
|
| ] %}
|
| {% for field, tier in component_basic_fields %}
|
| <tr>
|
| <td>
|
| {% if completeness_score.field_checklist.get(field, '').startswith('✔') %}
|
| <span class="check-mark">✔</span>
|
| {% else %}
|
| <span class="x-mark">✘</span>
|
| {% endif %}
|
| </td>
|
| <td>{% if field == 'component_version' %}version{% else %}{{ field }}{% endif %}
|
| </td>
|
| <td>
|
| {% if completeness_score.field_checklist.get(field, '').startswith('✔') %}
|
| {% if field == 'component_version' %}
|
| $.components[0].version
|
| {% else %}
|
| $.components[0].{{ field }}
|
| {% endif %}
|
| {% else %}
|
| {% if field == 'description' %}
|
| Not found in component level
|
| {% else %}
|
| Not found
|
| {% endif %}
|
| {% endif %}
|
| </td>
|
| <td><span class="field-tier tier-{{ tier|lower }}"></span> {{ tier }}</td>
|
| <td>
|
| {% set f_type = completeness_score.field_types.get(field, 'Unknown') %}
|
| {% set f_url = completeness_score.reference_urls.get(field, '') if
|
| completeness_score.reference_urls else '' %}
|
| {% if f_url %}
|
| <a href="{{ f_url }}" target="_blank">{{ f_type }}</a>
|
| {% else %}
|
| {{ f_type }}
|
| {% endif %}
|
| </td>
|
| </tr>
|
| {% endfor %}
|
| </tbody>
|
| </table>
|
| <div class="category-result">
|
| Result: {{ completeness_score.category_details.component_basic.present_fields if
|
| completeness_score.category_details else 'N/A' }}/{{
|
| completeness_score.category_details.component_basic.total_fields if
|
| completeness_score.category_details else 'N/A' }} present
|
| ({{ completeness_score.category_details.component_basic.percentage if
|
| completeness_score.category_details else 'N/A' }}%) =
|
| {{ completeness_score.section_scores.component_basic if completeness_score.section_scores
|
| else 'N/A' }}/20 points
|
| </div>
|
| </div>
|
|
|
|
|
| <div class="category-table">
|
| <h4>Component Model Card Category</h4>
|
| <table>
|
| <thead>
|
| <tr>
|
| <th>Status</th>
|
| <th>Field Name</th>
|
| <th>Actual Location</th>
|
| <th>Tier</th>
|
| <th>Type</th>
|
| </tr>
|
| </thead>
|
| <tbody>
|
| {% set model_card_fields = completeness_score.category_fields_list.component_model_card
|
| if completeness_score and completeness_score.category_fields_list else [] %}
|
| {% for field_item in model_card_fields %}
|
| {% set field = field_item.name %}
|
| {% set tier = field_item.tier %}
|
| <tr>
|
| <td>
|
| {% if completeness_score.field_checklist.get(field, '').startswith('✔') %}
|
| <span class="check-mark">✔</span>
|
| {% else %}
|
| <span class="x-mark">✘</span>
|
| {% endif %}
|
| </td>
|
| <td>{{ field }}</td>
|
| <td>
|
| {% if completeness_score.field_checklist.get(field, '').startswith('✔') %}
|
| {{ field_item.path }}
|
| {% else %}
|
| Not found
|
| {% endif %}
|
| </td>
|
| <td><span class="field-tier tier-{{ tier|lower }}"></span> {{ tier }}</td>
|
| <td>
|
| {% set f_type = completeness_score.field_types.get(field, 'Unknown') %}
|
| {% set f_url = completeness_score.reference_urls.get(field, '') if
|
| completeness_score.reference_urls else '' %}
|
| {% if f_url %}
|
| <a href="{{ f_url }}" target="_blank">{{ f_type }}</a>
|
| {% else %}
|
| {{ f_type }}
|
| {% endif %}
|
| </td>
|
| </tr>
|
| {% endfor %}
|
| </tbody>
|
| </table>
|
| <div class="category-result">
|
| Result: {{ completeness_score.category_details.component_model_card.present_fields if
|
| completeness_score.category_details else 'N/A' }}/{{
|
| completeness_score.category_details.component_model_card.total_fields if
|
| completeness_score.category_details else 'N/A' }} present
|
| ({{ completeness_score.category_details.component_model_card.percentage if
|
| completeness_score.category_details else 'N/A' }}%) =
|
| {{ completeness_score.section_scores.component_model_card if
|
| completeness_score.section_scores else 'N/A' }}/30 points
|
| </div>
|
| </div>
|
|
|
|
|
| <div class="category-table">
|
| <h4>External References Category</h4>
|
| <table>
|
| <thead>
|
| <tr>
|
| <th>Status</th>
|
| <th>Field Name</th>
|
| <th>Actual Location</th>
|
| <th>Tier</th>
|
| <th>Type</th>
|
| </tr>
|
| </thead>
|
| <tbody>
|
| {% set external_ref_fields = completeness_score.category_fields_list.external_references
|
| if completeness_score and completeness_score.category_fields_list else [] %}
|
| {% for field_item in external_ref_fields %}
|
| {% set field = field_item.name %}
|
| {% set tier = field_item.tier %}
|
| <tr>
|
| <td>
|
| {% if completeness_score.field_checklist.get(field, '').startswith('✔') %}
|
| <span class="check-mark">✔</span>
|
| {% else %}
|
| <span class="x-mark">✘</span>
|
| {% endif %}
|
| </td>
|
| <td>{{ field }}</td>
|
| <td>
|
| {% if completeness_score.field_checklist.get(field, '').startswith('✔') %}
|
| {{ field_item.path }}
|
| {% else %}
|
| Not found
|
| {% endif %}
|
| </td>
|
| <td><span class="field-tier tier-{{ tier|lower }}"></span> {{ tier }}</td>
|
| <td>
|
| {% set f_type = completeness_score.field_types.get(field, 'Unknown') %}
|
| {% set f_url = completeness_score.reference_urls.get(field, '') if
|
| completeness_score.reference_urls else '' %}
|
| {% if f_url %}
|
| <a href="{{ f_url }}" target="_blank">{{ f_type }}</a>
|
| {% else %}
|
| {{ f_type }}
|
| {% endif %}
|
| </td>
|
| </tr>
|
| {% endfor %}
|
| </tbody>
|
| </table>
|
| <div class="category-result">
|
| Result: {{ completeness_score.category_details.external_references.present_fields if
|
| completeness_score.category_details else 'N/A' }}/{{
|
| completeness_score.category_details.external_references.total_fields if
|
| completeness_score.category_details else 'N/A' }} present
|
| ({{ completeness_score.category_details.external_references.percentage if
|
| completeness_score.category_details else 'N/A' }}%) =
|
| {{ completeness_score.section_scores.external_references if
|
| completeness_score.section_scores else 'N/A' }}/10 points
|
| </div>
|
| </div>
|
|
|
| {% else %}
|
| <p>Field checklist data not available.</p>
|
| {% endif %}
|
| </div>
|
| </div>
|
|
|
|
|
| <div id="score-view" class="tab-content">
|
| <div class="content-section">
|
| <h3>📊 Completeness Score Report</h3>
|
|
|
|
|
| <div class="total-score-container">
|
| <div class="total-score">{{ (completeness_score.total_score if completeness_score.total_score !=
|
| "Undefined" else 0)|round(1) }}/100</div>
|
| <div class="total-progress">
|
| <div class="progress-container">
|
| <div class="progress-bar {{ score_class }}"
|
| style="width: {{ score_percent|round|int }}%">
|
| {{ score_percent|int }}% {{ score_label }}
|
| </div>
|
| </div>
|
| </div>
|
| </div>
|
|
|
|
|
|
|
|
|
| <div class="note-box">
|
| <h4>Your AIBOM Breakdown</h4>
|
| <p><strong>Model:</strong> <a href="https://huggingface.co/{{ model_id }}" target="_blank">{{
|
| model_id }}</a></p>
|
|
|
| <table class="score-table">
|
| <thead>
|
| <tr>
|
| <th>Category</th>
|
| <th>Fields Present</th>
|
| <th>Score</th>
|
| <th>Progress</th>
|
| </tr>
|
| </thead>
|
| <tbody>
|
| {% if completeness_score.category_details and completeness_score.section_scores %}
|
| {% set categories = [
|
| ('Required Fields', 'required_fields', 20),
|
| ('Metadata', 'metadata', 20),
|
| ('Component Basic', 'component_basic', 20),
|
| ('Model Card', 'component_model_card', 30),
|
| ('External References', 'external_references', 10)
|
| ] %}
|
| {% for display_name, key, max_score in categories %}
|
| <tr>
|
| <td>{{ display_name }}</td>
|
| <td>{{ completeness_score.category_details[key].present_fields }}/{{
|
| completeness_score.category_details[key].total_fields }}</td>
|
| <td>{{ completeness_score.section_scores[key]|round(1) }}/{{ max_score }}</td>
|
| <td>
|
| <div class="progress-container">
|
| {% set percentage = completeness_score.category_details[key].percentage %}
|
| {% if percentage >= 80 %}
|
| {% set progress_class = "progress-excellent" %}
|
| {% elif percentage >= 60 %}
|
| {% set progress_class = "progress-good" %}
|
| {% elif percentage >= 40 %}
|
| {% set progress_class = "progress-fair" %}
|
| {% else %}
|
| {% set progress_class = "progress-poor" %}
|
| {% endif %}
|
| <div class="progress-bar {{ progress_class }}"
|
| style="width: {{ percentage|round|int }}%">{{ percentage|round|int }}%
|
| </div>
|
| </div>
|
| </td>
|
| </tr>
|
| {% endfor %}
|
| {% else %}
|
| <tr>
|
| <td colspan="4">Breakdown data not available</td>
|
| </tr>
|
| {% endif %}
|
| </tbody>
|
| </table>
|
| </div>
|
|
|
| <p><strong>Calculation:</strong></p>
|
| <p>Subtotal:
|
| {% if completeness_score.section_scores %}
|
| {% for category, score in completeness_score.section_scores.items() %}
|
| {{ score|round(1) }}{% if not loop.last %} + {% endif %}
|
| {% endfor %}
|
| = <strong>{{ completeness_score.subtotal_score|round(1) }}/100</strong>
|
| {% else %}
|
| <strong>{{ completeness_score.subtotal_score|round(1) }}/100</strong>
|
| {% endif %}
|
| </p>
|
|
|
| {% if completeness_score.penalty_applied %}
|
| <p>Penalty Applied: <strong>-{{ completeness_score.penalty_percentage }}%</strong> ({{
|
| completeness_score.penalty_reason }})</p>
|
| <p>Final Score: {{ completeness_score.subtotal_score|round(1) }} × {{
|
| completeness_score.penalty_factor }} = <strong>{{ completeness_score.total_score|round(1)
|
| }}/100</strong></p>
|
| {% else %}
|
| <p>No penalties applied</p>
|
| <p>Final Score: <strong>{{ completeness_score.total_score|round(1) }}/100</strong></p>
|
| {% endif %}
|
| </div>
|
|
|
|
|
| {% if completeness_score.missing_counts %}
|
| <div class="missing-fields">
|
| <h4>Missing Fields Summary</h4>
|
| <ul>
|
| <li><strong>Critical:</strong> {{ completeness_score.missing_counts.critical }} missing</li>
|
| <li><strong>Important:</strong> {{ completeness_score.missing_counts.important }} missing</li>
|
| <li><strong>Supplementary:</strong> {{ completeness_score.missing_counts.supplementary }}
|
| missing</li>
|
| </ul>
|
|
|
| {% if completeness_score.missing_counts.important >= 5 %}
|
| <p><strong>Impact:</strong> Missing multiple critical and/or important fields will incur penalties
|
| according to the Penalty Structure.</p>
|
| {% endif %}
|
| </div>
|
| {% endif %}
|
|
|
|
|
| {% if completeness_score.recommendations %}
|
| <div class="recommendations">
|
| <h4>General Recommendations to Improve AIBOM Completeness</h4>
|
| <ul>
|
| <li><strong>Required Fields:</strong> Ensure the model is published with a clear name, version,
|
| and hosting platform information to allow proper SBOM structuring.</li>
|
| <li><strong>Metadata:</strong> Include author or organization name, purpose of the model, and
|
| relevant timestamps in the model repository or card.</li>
|
| <li><strong>Component Basic:</strong> Provide a descriptive model title, a meaningful
|
| description, a valid license, and a consistent version reference (e.g., tags or commits).
|
| </li>
|
| <li><strong>Model Card:</strong> Fill out structured sections for model parameters, evaluation
|
| metrics, limitations, and ethical considerations to enable full transparency.</li>
|
| <li><strong>External References:</strong> Add links to source code, datasets, documentation, and
|
| versioned download locations to support traceability and reproducibility.</li>
|
| </ul>
|
| </div>
|
|
|
|
|
| <div class="scoring-rubric">
|
| <h4>How AIBOM Completeness is Scored</h4>
|
| <p>The completeness score evaluates how well your AIBOM documents the model across five key
|
| categories:</p>
|
| <ul>
|
| <li><strong>Required Fields ({{ completeness_score.category_details.required_fields.max_points
|
| if completeness_score.category_details else 'N/A' }} points):</strong> Basic SBOM
|
| structure mandated by CycloneDX
|
| </li>
|
| <li><strong>Metadata ({{ completeness_score.category_details.metadata.max_points if
|
| completeness_score.category_details else 'N/A' }} points):</strong> Information about
|
| the SBOM generation and model
|
| purpose</li>
|
| <li><strong>Component Basic ({{ completeness_score.category_details.component_basic.max_points
|
| if completeness_score.category_details else 'N/A' }} points):</strong> Essential model
|
| identification and licensing
|
| </li>
|
| <li><strong>Model Card ({{ completeness_score.category_details.component_model_card.max_points
|
| if completeness_score.category_details else 'N/A' }} points):</strong> Detailed
|
| AI-specific documentation for transparency
|
| </li>
|
| <li><strong>External References ({{
|
| completeness_score.category_details.external_references.max_points if
|
| completeness_score.category_details else 'N/A' }} points):</strong> Links to model
|
| resources and documentation
|
| </li>
|
| </ul>
|
|
|
| <p><strong>Calculation Method:</strong></p>
|
| <p>Each category score = (Present Fields ÷ Total Fields) × Maximum Points</p>
|
| <p>Subtotal = Sum of all category scores</p>
|
| <p>Final Score = Subtotal × Penalty Factor (if applicable)</p>
|
| <h4>Penalty Structure:</h4>
|
| <p><strong>Critical Fields Missing:</strong></p>
|
| <ul>
|
| <li>0-1 missing: No penalty</li>
|
| <li>2-3 missing: 10% penalty (×0.9)</li>
|
| <li>4+ missing: 20% penalty (×0.8)</li>
|
| </ul>
|
|
|
| <p><strong>Important Fields Missing:</strong></p>
|
| <ul>
|
| <li>0-4 missing: No penalty</li>
|
| <li>5+ missing: 5% penalty (×0.95)</li>
|
| </ul>
|
|
|
| <p><strong>Note:</strong> Penalties are cumulative and applied to the subtotal. For example, if you
|
| have 3 critical fields missing AND 5 important fields missing, both penalties apply: Subtotal ×
|
| 0.9 × 0.95 = Final Score.</p>
|
|
|
| </div>
|
| {% endif %}
|
| </div>
|
| </div>
|
|
|
|
|
| <div id="json-view" class="tab-content">
|
| <div class="content-section">
|
| <h3>📄 Raw JSON View</h3>
|
| <p>This is the complete AIBOM components array in CycloneDX JSON format:</p>
|
| <div class="json-view">
|
| <pre>{{ components_json }}</pre>
|
| </div>
|
| </div>
|
| </div>
|
|
|
|
|
| {% include 'includes/footer.html' %}
|
| </div>
|
|
|
| <script>
|
| const AIBOM_CDX_JSON_1_6 = {{ aibom_cdx_json_1_6 | safe }};
|
| const AIBOM_CDX_JSON_1_7 = {{ aibom_cdx_json_1_7 | safe }};
|
| const FILENAME_BASE = "{{ model_id|replace('/', '_') }}";
|
| </script>
|
| <script src="{{ static_root|default('/static') }}/js/script.js"></script>
|
| </body>
|
|
|
| </html> |