ryanoakley commited on
Commit
ce9c8dc
·
verified ·
1 Parent(s): 18d7601

Update templates/result.html

Browse files
Files changed (1) hide show
  1. templates/result.html +773 -586
templates/result.html CHANGED
@@ -4,7 +4,7 @@
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>AI SBOM Generated</title>
7
- <style>
8
  body {
9
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
10
  margin: 0;
@@ -27,36 +27,71 @@
27
  box-shadow: 0 2px 5px rgba(0,0,0,0.05);
28
  display: flex;
29
  align-items: center;
 
30
  margin-bottom: 30px;
31
  }
 
 
 
 
32
  .header img {
33
  height: 60px;
34
  margin-right: 15px;
35
  }
 
 
 
 
36
  .header h1 {
37
  margin: 0;
38
  font-size: 28px;
39
  color: #2c3e50;
40
  font-weight: 600;
 
41
  }
42
-
43
- /* header-content div for layout */
44
- .header .header-content {
45
  display: flex;
46
- flex-direction: column; /* Stack title and count */
47
  }
48
- .header h1 {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  margin: 0;
50
- font-size: 28px;
51
- color: #2c3e50;
 
 
 
52
  font-weight: 600;
53
- margin-bottom: 5px; /* Space between title and count */
54
  }
55
- /* Added style for sbom-count */
56
- .header .sbom-count {
 
 
 
57
  font-size: 14px;
58
- color: #555;
59
- font-weight: 500;
60
  }
61
 
62
  /* Content styling */
@@ -326,6 +361,19 @@
326
  .score-table td {
327
  padding: 12px;
328
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
329
  .score-weight {
330
  font-size: 0.9em;
331
  color: #666;
@@ -426,6 +474,10 @@
426
  color: #2e7d32;
427
  margin-bottom: 15px;
428
  }
 
 
 
 
429
  .importance-indicator {
430
  display: inline-block;
431
  margin-left: 5px;
@@ -478,11 +530,11 @@
478
 
479
  /* Styles for completeness profile */
480
  .completeness-profile {
481
- background-color: #e8f5e9;
482
  border-radius: 8px;
483
  padding: 20px;
484
  margin: 20px 0;
485
- border-left: 4px solid #4caf50;
486
  }
487
  .profile-badge {
488
  display: inline-block;
@@ -547,656 +599,790 @@
547
  margin-bottom: 15px;
548
  }
549
 
550
- /* Section for score calculation explanation */
551
- .score-calculation {
552
- margin-top: 30px;
553
- padding: 25px;
554
- background-color: #ffffff;
555
  border-radius: 8px;
556
- box-shadow: 0 2px 10px rgba(0,0,0,0.05);
 
 
557
  }
558
- .score-calculation h3 {
559
  margin-top: 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
560
  color: #2c3e50;
561
- border-bottom: 2px solid #f0f0f0;
562
- padding-bottom: 10px;
563
- margin-bottom: 20px;
564
  }
565
- .calculation-section {
566
- margin-bottom: 25px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
567
  }
568
 
569
- /* Footer styling */
570
- .footer {
571
- text-align: center;
 
572
  padding: 20px;
573
- color: #7f8c8d;
574
- font-size: 14px;
575
- margin-top: 30px;
576
  }
577
-
578
- /* Responsive adjustments */
579
- @media (max-width: 768px) {
580
- .aibom-property {
581
- flex-direction: column;
582
- }
583
- .property-name {
584
- width: 100%;
585
- margin-bottom: 5px;
586
- }
587
- .total-score-container {
588
- flex-direction: column;
589
- align-items: flex-start;
590
- }
591
- .total-score {
592
- margin-bottom: 10px;
593
- }
594
- .aibom-tabs {
595
- flex-wrap: wrap;
596
- }
597
- .aibom-tab {
598
- margin-bottom: 5px;
599
- }
600
  }
601
  </style>
602
  </head>
603
  <body>
604
- <!-- Header with logo, title, and SBOM count -->
605
- <div class="header">
606
- <a href="https://aetheris.ai/" target="_blank">
607
- <img src="https://huggingface.co/spaces/aetheris-ai/aibom-generator/resolve/main/templates/images/AetherisAI-logo.png" alt="Aetheris AI Logo">
608
- </a>
609
- <!-- Header-content div -->
610
- <div class="header-content">
611
- <h1>AI SBOM Generator</h1>
612
- </div>
613
- </div>
614
-
615
-
616
  <div class="container">
617
- <div class="content-section">
618
- <h2>AI SBOM Generated for {{ model_id }}</h2>
619
-
620
- <a href="/" class="button">Generate another AI SBOM</a>
621
-
622
- <div class="download-section">
623
- <p>Download generated AI SBOM in CycloneDX format</p>
624
- <button onclick="downloadJSON()">Download JSON</button>
 
625
  </div>
 
 
 
 
 
 
 
 
 
 
626
 
627
- {% if enhancement_report and enhancement_report.ai_enhanced %}
628
- <div class="improvement">
629
- <h3>AI Enhancement Results</h3>
630
- <p>This AI SBOM was enhanced using <strong>{{ enhancement_report.ai_model }}</strong></p>
631
- <p>Original Score: {{ enhancement_report.original_score.total_score|round(1) }}/100</p>
632
- <p>Enhanced Score: {{ enhancement_report.final_score.total_score|round(1) }}/100</p>
633
- <p>Improvement: <span class="improvement-value">+{{ enhancement_report.improvement|round(1) }} points</span></p>
 
 
 
 
 
 
 
 
 
 
 
634
  </div>
635
- {% endif %}
636
  </div>
637
 
638
- <!-- Human-friendly AI SBOM Viewer -->
639
- <div class="note-box">
640
- <p><strong>Note:</strong> This page displays the AI SBOM in a human-friendly format for easier readability.
641
- The downloaded JSON file follows the standard CycloneDX format required for interoperability with other tools.</p>
642
  </div>
643
-
644
- <div class="aibom-tabs">
645
- <div class="aibom-tab active" onclick="switchTab('human-view')">Human-Friendly View</div>
646
- <div class="aibom-tab" onclick="switchTab('json-view')">JSON View</div>
647
- <div class="aibom-tab" onclick="switchTab('field-checklist')">Field Checklist</div>
648
- <div class="aibom-tab" onclick="switchTab('score-view')">Score Report</div>
 
 
 
 
 
649
  </div>
 
 
 
 
 
 
 
 
 
 
650
 
651
- <div id="human-view" class="tab-content active">
652
- <div class="aibom-viewer">
653
- <!-- Key Information Section -->
654
- <div class="aibom-section key-info">
655
- <h4>Key Information</h4>
 
 
 
656
  <div class="aibom-property">
657
- <div class="property-name">Model Name:</div>
658
- <div class="property-value">{{ aibom.components[0].name if aibom.components and aibom.components[0].name else 'Not specified' }}</div>
659
  </div>
660
  <div class="aibom-property">
661
- <div class="property-name">Type:</div>
662
- <div class="property-value">{{ aibom.components[0].type if aibom.components and aibom.components[0].type else 'Not specified' }}</div>
663
  </div>
664
  <div class="aibom-property">
665
- <div class="property-name">Version:</div>
666
- <div class="property-value">{{ aibom.components[0].version if aibom.components and aibom.components[0].version else 'Not specified' }}</div>
667
  </div>
668
  <div class="aibom-property">
669
- <div class="property-name">PURL:</div>
670
- <div class="property-value">{{ aibom.components[0].purl if aibom.components and aibom.components[0].purl else 'Not specified' }}</div>
671
  </div>
672
- {% if aibom.components and aibom.components[0].description %}
673
  <div class="aibom-property">
674
- <div class="property-name">Description:</div>
675
- <div class="property-value">{{ aibom.components[0].description }}</div>
 
 
 
 
676
  </div>
677
  {% endif %}
678
  </div>
679
 
680
- <!-- Model Card Section -->
681
  {% if aibom.components and aibom.components[0].modelCard %}
682
  <div class="aibom-section">
683
- <h4 class="collapsible" onclick="toggleCollapsible(this)">Model Card</h4>
684
- <div class="collapsible-content">
685
- {% if aibom.components[0].modelCard.modelParameters %}
686
- <div class="aibom-property">
687
- <div class="property-name">Model Parameters:</div>
688
- <div class="property-value">
689
- <ul>
690
- {% for key, value in aibom.components[0].modelCard.modelParameters.items() %}
691
- <li><strong>{{ key }}:</strong> {{ value }}</li>
692
- {% endfor %}
693
- </ul>
694
- </div>
695
- </div>
696
- {% endif %}
697
-
698
- {% if aibom.components[0].modelCard.considerations %}
699
- <div class="aibom-property">
700
- <div class="property-name">Considerations:</div>
701
- <div class="property-value">
702
- <ul>
703
- {% for key, value in aibom.components[0].modelCard.considerations.items() %}
704
- <li><strong>{{ key }}:</strong> {{ value }}</li>
705
- {% endfor %}
706
- </ul>
707
- </div>
708
- </div>
709
- {% endif %}
710
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
711
  </div>
712
  {% endif %}
713
-
714
- <!-- External References Section -->
715
- {% if aibom.components and aibom.components[0].externalReferences %}
716
  <div class="aibom-section">
717
- <h4 class="collapsible" onclick="toggleCollapsible(this)">External References</h4>
718
- <div class="collapsible-content">
719
- <ul>
720
- {% for ref in aibom.components[0].externalReferences %}
721
- <li>
722
- <strong>{{ ref.type }}:</strong>
723
- <a href="{{ ref.url }}" target="_blank">{{ ref.url }}</a>
724
- {% if ref.comment %}
725
- <br><em>{{ ref.comment }}</em>
726
- {% endif %}
727
- </li>
728
- {% endfor %}
729
- </ul>
730
  </div>
 
731
  </div>
732
  {% endif %}
733
- </div>
734
- </div>
735
 
736
- <div id="json-view" class="tab-content">
737
- <div class="json-view">
738
- <pre>{{ aibom | tojson(indent=2) }}</pre>
739
- </div>
740
- </div>
741
-
742
- <div id="field-checklist" class="tab-content">
743
- <div class="content-section">
744
- <h3>Field Checklist & Mapping</h3>
745
-
746
- <!-- Field Tier Legend -->
747
- <div class="tier-legend">
748
- <div class="tier-legend-item">
749
- <span class="field-tier tier-critical"></span>
750
- <span>Critical</span>
751
  </div>
752
- <div class="tier-legend-item">
753
- <span class="field-tier tier-important"></span>
754
- <span>Important</span>
755
  </div>
756
- <div class="tier-legend-item">
757
- <span class="field-tier tier-supplementary"></span>
758
- <span>Supplementary</span>
 
759
  </div>
 
760
  </div>
761
-
762
- <p>This table shows how fields map to the CycloneDX specification and their status in your AI SBOM.</p>
763
-
764
- <div class="field-mapping-container">
765
- <h4>Standard CycloneDX Fields</h4>
766
- <p>These fields are part of the official CycloneDX specification and are used in all SBOMs:</p>
767
- <table class="field-mapping-table">
768
- <thead>
769
- <tr>
770
- <th>Status</th>
771
- <th>Field Name</th>
772
- <th>CycloneDX JSON Path</th>
773
- <th>Info</th>
774
- <th>Importance</th>
775
- </tr>
776
- </thead>
777
- <tbody>
778
- {% for field_key, field_data in completeness_score.field_categorization.standard_cyclonedx_fields.items() %}
779
- <tr class="{% if field_data.status == '✔' %}present-field{% else %}missing-field{% endif %}">
780
- <td class="status-cell">
781
- {% if field_data.status == "✔" %}
782
- <span class="check-mark">✔</span>
783
- {% else %}
784
- <span class="x-mark">✘</span>
785
- {% endif %}
786
- </td>
787
- <td>{{ field_data.field_name }}</td>
788
- <td>{{ field_data.json_path }}</td>
789
- <td>
790
- <span class="tooltip">(?)
791
- <span class="tooltiptext">{{ field_data.field_name }} field information.</span>
792
- </span>
793
- </td>
794
- <td>
795
- <span class="field-tier tier-{{ field_data.importance|lower }}"></span>
796
- {{ field_data.importance }}
797
- </td>
798
- </tr>
799
- {% endfor %}
800
- </tbody>
801
- </table>
802
 
803
-
804
- <h4>AI-Specific Extension Fields</h4>
805
- <p>These fields extend the CycloneDX specification specifically for AI models:</p>
806
- <table class="field-mapping-table">
807
- <thead>
808
- <tr>
809
- <th>Status</th>
810
- <th>Field Name</th>
811
- <th>CycloneDX JSON Path</th>
812
- <th>Info</th>
813
- <th>Importance</th>
814
- </tr>
815
- </thead>
816
- <tbody>
817
- {% for field_key, field_data in completeness_score.field_categorization.ai_specific_extension_fields.items() %}
818
- <tr class="{% if field_data.status == '✔' %}present-field{% else %}missing-field{% endif %}">
819
- <td class="status-cell">
820
- {% if field_data.status == "✔" %}
821
- <span class="check-mark">✔</span>
822
- {% else %}
823
- <span class="x-mark">✘</span>
824
- {% endif %}
825
- </td>
826
- <td>{{ field_data.field_name }}</td>
827
- <td>{{ field_data.json_path }}</td>
828
- <td>
829
- <span class="tooltip">(?)
830
- <span class="tooltiptext">{{ field_data.field_name }} field information.</span>
831
- </span>
832
- </td>
833
- <td>
834
- <span class="field-tier tier-{{ field_data.importance|lower }}"></span>
835
- {{ field_data.importance }}
836
- </td>
837
- </tr>
838
- {% endfor %}
839
- </tbody>
840
- </table>
841
- </div>
842
-
843
- <style>
844
- .field-mapping-container {
845
- margin-top: 20px;
846
- max-width: 100%;
847
- overflow-x: auto;
848
- }
849
- .field-mapping-table {
850
- width: 100%;
851
- border-collapse: collapse;
852
- margin-bottom: 30px;
853
- box-shadow: 0 2px 8px rgba(0,0,0,0.1);
854
- border-radius: 8px;
855
- overflow: hidden;
856
- table-layout: fixed;
857
- }
858
- .field-mapping-table th {
859
- background-color: #f5f5f5;
860
- padding: 12px 15px;
861
- text-align: left;
862
- font-weight: 600;
863
- color: #333;
864
- border-bottom: 2px solid #ddd;
865
- }
866
- .field-mapping-table td {
867
- padding: 10px 15px;
868
- border-bottom: 1px solid #eee;
869
- vertical-align: middle;
870
- word-wrap: break-word;
871
- word-break: break-word;
872
- max-width: 250px;
873
- }
874
- .field-mapping-table tr:last-child td {
875
- border-bottom: none;
876
- }
877
- .field-mapping-table tr:hover {
878
- background-color: #f9f9f9;
879
- }
880
- .status-cell {
881
- text-align: center;
882
- width: 60px;
883
- }
884
- .present-field {
885
- background-color: #f0f7f0;
886
- }
887
- .missing-field {
888
- background-color: #fff7f7;
889
- }
890
- .check-mark {
891
- color: #4caf50;
892
- font-weight: bold;
893
- font-size: 18px;
894
- }
895
- .x-mark {
896
- color: #f44336;
897
- font-weight: bold;
898
- font-size: 18px;
899
- }
900
- </style>
901
  </div>
902
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
903
 
904
- <div id="score-view" class="tab-content">
905
- <div class="content-section">
906
- <h3>AI SBOM Completeness Score</h3>
907
-
908
- <!-- Completeness Profile Section -->
909
- {% if completeness_score.completeness_profile %}
910
- <div class="completeness-profile">
911
- <h4>Completeness Profile:
912
- <span class="profile-badge profile-{{ completeness_score.completeness_profile.name|lower }}">
913
- {{ completeness_score.completeness_profile.name }}
914
- </span>
915
- </h4>
916
- <p>{{ completeness_score.completeness_profile.description }}</p>
917
-
918
- {% if completeness_score.completeness_profile.next_level %}
919
- <p><strong>Next level:</strong> {{ completeness_score.completeness_profile.next_level.name }}
920
- ({{ completeness_score.completeness_profile.next_level.missing_fields_count }} fields to add)</p>
921
- {% endif %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
922
  </div>
923
- {% endif %}
924
-
925
- <!-- Total Score with Progress Bar -->
926
- <div class="total-score-container">
927
- <div class="total-score">{{ completeness_score.total_score|round(1) }}/100</div>
928
- <div class="total-progress">
929
- <div class="progress-container">
930
- {% set score_percent = (completeness_score.total_score / 100) * 100 %}
931
- {% set score_class = 'progress-poor' %}
932
- {% set score_label = 'Poor' %}
933
-
934
- {% if score_percent >= 90 %}
935
- {% set score_class = 'progress-excellent' %}
936
- {% set score_label = 'Excellent' %}
937
- {% elif score_percent >= 70 %}
938
- {% set score_class = 'progress-good' %}
939
- {% set score_label = 'Good' %}
940
- {% elif score_percent >= 50 %}
941
- {% set score_class = 'progress-fair' %}
942
- {% set score_label = 'Fair' %}
943
- {% endif %}
944
-
945
- <div class="progress-bar {{ score_class }}" style="width: {{ score_percent }}%">
946
- {{ score_percent|int }}% {{ score_label }}
947
- </div>
948
- </div>
949
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
950
  </div>
951
-
952
- <!-- Validation Penalty Explanation -->
953
- {% if completeness_score.validation_penalty %}
954
- <div class="validation-penalty-info">
955
- <h4>About the Validation Penalty</h4>
956
- <p>Your score includes a penalty because the AIBOM has schema validation issues. These are structural problems that don't comply with the CycloneDX specification requirements.</p>
957
- <p><strong>How to fix this:</strong> Look at the "Fix Validation Issues" section in the recommendations below. Fixing these issues will remove the penalty and improve your overall score.</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
958
  </div>
959
- {% endif %}
960
-
961
- <!-- Section Scores with Progress Bars and Tooltips -->
962
- <table class="score-table">
 
 
963
  <thead>
964
  <tr>
965
- <th>Section</th>
966
- <th>Score</th>
967
- <th>Weight</th>
968
- <th>Progress</th>
 
969
  </tr>
970
  </thead>
971
  <tbody>
972
- {% set weights = {'required_fields': 20, 'metadata': 20, 'component_basic': 20, 'component_model_card': 30, 'external_references': 10} %}
973
- {% set tooltips = {
974
- 'required_fields': 'Basic SBOM fields required by the CycloneDX specification: bomFormat, specVersion, serialNumber, and version.',
975
- 'metadata': 'Information about the AI SBOM itself: timestamp, tools used to generate it, authors, and component metadata.',
976
- 'component_basic': 'Basic information about the AI model: type, name, bom-ref, PURL, description, and licenses.',
977
- 'component_model_card': 'Detailed information about the model: parameters, quantitative analysis, and ethical considerations.',
978
- 'external_references': 'Links to external resources like model cards, repositories, and datasets.'
979
- } %}
980
- {% set display_names = {
981
- 'required_fields': 'Required Fields',
982
- 'metadata': 'Metadata',
983
- 'component_basic': 'Component Basic',
984
- 'component_model_card': 'Model Card',
985
- 'external_references': 'External References'
986
- } %}
987
- {% for section, score in completeness_score.section_scores.items() %}
988
- <tr>
989
- <td>
990
- {{ display_names[section] }}
991
- <span class="tooltip">(?)
992
- <span class="tooltiptext">{{ tooltips[section] }}</span>
993
- </span>
994
- </td>
995
- <td>{{ score|round(1) }}/{{ completeness_score.max_scores[section] }}</td>
996
- <td>{{ weights[section] }}%</td>
997
- <td style="width: 50%;">
998
- <div class="progress-container">
999
- {% set percent = (score / completeness_score.max_scores[section]) * 100 %}
1000
- {% set class = 'progress-poor' %}
1001
-
1002
- {% if percent >= 90 %}
1003
- {% set class = 'progress-excellent' %}
1004
- {% elif percent >= 70 %}
1005
- {% set class = 'progress-good' %}
1006
- {% elif percent >= 50 %}
1007
- {% set class = 'progress-fair' %}
1008
- {% endif %}
1009
-
1010
- <div class="progress-bar {{ class }}" style="width: {{ percent }}%">
1011
- {{ percent|int }}%
1012
- </div>
1013
- </div>
1014
- </td>
1015
- </tr>
1016
  {% endfor %}
1017
  </tbody>
1018
  </table>
1019
-
1020
- <!-- How the Overall Score is Calculated Section -->
1021
- <div class="score-calculation">
1022
- <h3>How the Overall Score is Calculated</h3>
 
 
 
 
 
 
 
 
 
 
 
 
 
1023
 
1024
- <!-- Missing Fields Section -->
1025
- <div class="calculation-section missing-fields">
1026
- <h4>Critical Missing Fields</h4>
1027
- <p>The following fields are missing or incomplete and have the biggest impact on your score:</p>
1028
- <ul>
1029
- {% set missing_critical = [] %}
1030
- {% for field, status in completeness_score.field_checklist.items() %}
1031
- {% if "✘" in status %}
1032
- {% if completeness_score.field_tiers and field in completeness_score.field_tiers and completeness_score.field_tiers[field] == 'critical' %}
1033
- {% set _ = missing_critical.append(field) %}
1034
- <li>
1035
- <strong>{{ field }}</strong>
1036
- <span class="field-tier tier-critical"></span>
1037
- {% if field == "component.description" %}
1038
- - Add a detailed description of the model (at least 20 characters)
1039
- {% elif field == "component.purl" %}
1040
- - Add a valid PURL in the format pkg:huggingface/[owner]/[name]@[version]
1041
- {% elif field == "modelCard.modelParameters" %}
1042
- - Add model parameters section with architecture, size, and training details
1043
- {% elif field == "primaryPurpose" %}
1044
- - Add primary purpose information (what the model is designed for)
1045
- {% else %}
1046
- - This field is required for comprehensive documentation
1047
- {% endif %}
1048
- </li>
1049
- {% endif %}
1050
  {% endif %}
1051
- {% endfor %}
1052
- {% if missing_critical|length == 0 %}
1053
- <li>No critical fields are missing. Great job!</li>
1054
- {% endif %}
1055
- </ul>
1056
- </div>
1057
-
1058
- <!-- Recommendations Section -->
1059
- <div class="calculation-section recommendations">
1060
- <h4>Recommendations to Improve Your Score</h4>
1061
- <ol>
1062
- {% if completeness_score.section_scores.component_model_card < completeness_score.max_scores.component_model_card %}
1063
- <li>
1064
- <strong>Enhance Model Card</strong> (+{{ ((completeness_score.max_scores.component_model_card - completeness_score.section_scores.component_model_card) * 0.3)|round(1) }} points):
1065
- <ul>
1066
- {% if completeness_score.missing_fields.critical %}
1067
- {% for field in completeness_score.missing_fields.critical %}
1068
- {% if field == "modelCard.modelParameters" or field == "modelCard.considerations" %}
1069
- <li>Add {{ field }} information</li>
1070
- {% endif %}
1071
- {% endfor %}
1072
- {% endif %}
1073
- </ul>
1074
- </li>
1075
- {% endif %}
1076
-
1077
- {% if completeness_score.section_scores.component_basic < completeness_score.max_scores.component_basic %}
1078
- <li>
1079
- <strong>Add Basic Component Information</strong> (+{{ ((completeness_score.max_scores.component_basic - completeness_score.section_scores.component_basic) * 0.2)|round(1) }} points):
1080
- <ul>
1081
- {% if completeness_score.missing_fields.critical %}
1082
- {% for field in completeness_score.missing_fields.critical %}
1083
- {% if field == "name" or field == "description" or field == "purl" %}
1084
- <li>Add {{ field }} information</li>
1085
- {% endif %}
1086
- {% endfor %}
1087
- {% endif %}
1088
- {% if completeness_score.missing_fields.important %}
1089
- {% for field in completeness_score.missing_fields.important %}
1090
- {% if field == "type" or field == "licenses" %}
1091
- <li>Add {{ field }} information</li>
1092
- {% endif %}
1093
- {% endfor %}
1094
- {% endif %}
1095
- </ul>
1096
- </li>
1097
- {% endif %}
1098
-
1099
- {% if completeness_score.section_scores.metadata < completeness_score.max_scores.metadata %}
1100
- <li>
1101
- <strong>Add Metadata</strong> (+{{ ((completeness_score.max_scores.metadata - completeness_score.section_scores.metadata) * 0.2)|round(1) }} points):
1102
- <ul>
1103
- {% if completeness_score.missing_fields.critical %}
1104
- {% for field in completeness_score.missing_fields.critical %}
1105
- {% if field == "primaryPurpose" or field == "suppliedBy" %}
1106
- <li>Add {{ field }} information</li>
1107
- {% endif %}
1108
- {% endfor %}
1109
- {% endif %}
1110
- {% if completeness_score.missing_fields.supplementary %}
1111
- {% for field in completeness_score.missing_fields.supplementary %}
1112
- {% if field == "standardCompliance" or field == "domain" or field == "autonomyType" %}
1113
- <li>Add {{ field }} information</li>
1114
- {% endif %}
1115
- {% endfor %}
1116
- {% endif %}
1117
- </ul>
1118
- </li>
1119
- {% endif %}
1120
-
1121
- {% if completeness_score.section_scores.external_references < completeness_score.max_scores.external_references %}
1122
- <li>
1123
- <strong>Add External References</strong> (+{{ ((completeness_score.max_scores.external_references - completeness_score.section_scores.external_references) * 0.1)|round(1) }} points):
1124
- <ul>
1125
- {% if completeness_score.missing_fields.critical %}
1126
- {% for field in completeness_score.missing_fields.critical %}
1127
- {% if field == "downloadLocation" %}
1128
- <li>Add download location reference</li>
1129
- {% endif %}
1130
- {% endfor %}
1131
- {% endif %}
1132
- <li>Add links to model card, repository, and dataset</li>
1133
- </ul>
1134
- </li>
1135
- {% endif %}
1136
-
1137
- {% if completeness_score.validation and not completeness_score.validation.valid %}
1138
- <li>
1139
- <strong>Fix Validation Issues</strong> (remove validation penalty):
1140
- <ul>
1141
- {% for recommendation in completeness_score.validation.recommendations %}
1142
- <li>{{ recommendation }}</li>
1143
- {% endfor %}
1144
- </ul>
1145
- </li>
1146
- {% endif %}
1147
- </ol>
1148
  </div>
1149
-
1150
- <!-- Scoring Rubric Section -->
1151
- <div class="calculation-section scoring-rubric">
1152
- <h4>Scoring Rubric</h4>
1153
- <p>The overall score is calculated using a <strong>weighted normalization</strong> approach:</p>
1154
- <p><strong>Total Score = Sum of (Section Score × Section Weight)</strong></p>
1155
- <p>Where:</p>
1156
  <ul>
1157
- <li>Section Score = Points earned in that section</li>
1158
- <li>Section Weight = Section's maximum points ÷ Total possible points (100)</li>
 
 
 
1159
  </ul>
1160
 
1161
- <div class="note-box">
1162
- <p><strong>Example calculation:</strong> If your SBOM has these section scores:</p>
1163
- <ul>
1164
- <li>Required Fields: 20 points × 0.20 weight = 4.0 points</li>
1165
- <li>Metadata: 15 points × 0.20 weight = 3.0 points</li>
1166
- <li>Component Basic: 10 points × 0.20 weight = 2.0 points</li>
1167
- <li>Model Card: 10 points × 0.30 weight = 3.0 points</li>
1168
- <li>External References: 5 points × 0.10 weight = 0.5 points</li>
1169
- </ul>
1170
- <p>The total score would be 12.5 points, even though the raw section scores sum to 60 points.</p>
1171
- <p><strong>Note:</strong> The total score is <em>not</em> the sum of section scores. Each section contributes proportionally to its weight in the final score.</p>
1172
- </div>
1173
-
1174
- <p>Fields are classified into three tiers based on importance:</p>
1175
  <ul>
1176
- <li><span class="field-tier tier-critical"></span> <strong>Critical fields</strong>: Highest weight (3-4 points each)</li>
1177
- <li><span class="field-tier tier-important"></span> <strong>Important fields</strong>: Medium weight (2-4 points each)</li>
1178
- <li><span class="field-tier tier-supplementary"></span> <strong>Supplementary fields</strong>: Lower weight (1-2 points each)</li>
1179
  </ul>
1180
 
1181
- <p>Penalties are applied for missing critical fields:</p>
1182
  <ul>
1183
- <li>Missing >3 critical fields: 20% penalty (score × 0.8)</li>
1184
- <li>Missing 1-3 critical fields: 10% penalty (score × 0.9)</li>
1185
- <li>Missing >5 important fields: 5% penalty (score × 0.95)</li>
1186
  </ul>
1187
 
1188
- {% if completeness_score.validation_penalty %}
1189
- <p>Additional penalties are applied based on validation results:</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1190
  <ul>
1191
- <li>Schema errors: Up to 50% reduction (10% per error)</li>
1192
- <li>Schema warnings: Up to 20% reduction (5% per warning)</li>
 
1193
  </ul>
 
 
 
1194
  {% endif %}
1195
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1196
  </div>
1197
  </div>
1198
  </div>
1199
 
 
 
 
 
 
 
 
 
 
 
1200
  <div class="content-section" style="text-align: center;">
1201
  <h3>🗣️ Help Us Spread the Word</h3>
1202
  <p>If you find this tool useful, share it with your network! <a href="https://sbom.aetheris.ai" target="_blank" rel="noopener noreferrer">https://sbom.aetheris.ai</a></p>
@@ -1209,17 +1395,17 @@
1209
  </p>
1210
  </div>
1211
 
1212
- <!-- Info Section -->
1213
- <div class="content-section" style="text-align: center;>
1214
  <!-- Display the SBOM count -->
1215
  <div class="sbom-count">🚀 Generated AI SBOMs using this tool: <strong>{{ sbom_count if sbom_count else 'N/A' }}</strong></div>
1216
  </div>
1217
-
1218
  <!-- Footer -->
1219
  <div class="footer">
1220
  <p>© 2025 AI SBOM Generator | Powered by Aetheris AI</p>
1221
  </div>
1222
- </div>
1223
 
1224
  <script>
1225
  function switchTab(tabId) {
@@ -1272,4 +1458,5 @@
1272
  });
1273
  </script>
1274
  </body>
1275
- </html>
 
 
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>AI SBOM Generated</title>
7
+ <script type="text/javascript" src="https://gc.kis.v2.scr.kaspersky-labs.com/FD126C42-EBFA-4E12-B309-BB3FDD723AC1/main.js?attr=tvQoyfSXq4JVDJvyoOAfThXMw9mgZRducLzTjUOnVphMAP2MBTXzDKF8WIUJETdEqCHXbUfgy-ihVF7QjtQfVIhhPZixhTUykm7ogEKcAY8g8XaCJjgLJ20ey7kvNW5yNFjtTgdjbiRHA4-4FfeZelCLWCOSbbpWpDquPezYp8V_SOTuYVK6EghRcNZgchGXWWoSOoga9DiwoB_4FVDoLZLAdtsEBybX-rcletazWXVi9vxFurtEiSBk3ewixUb_l1FR4W7mdhQXaDegmrEu__o1qNa73vqLlLY22wmjB5rhQnOItFstTw-BRsUtYRbY7d-jjDlvtLjvqrwMm8rYZlFzpvbgjkeQzbbyXQy0Wd0YNm11gR1z-q0xI-BTRItFmStcjfEWzrsnhOWvBeHrGvEpX8yWcLTOAOHXrE6Cp-i3mThNUiOErT99ZiPPmhieQZl6oq1Ed3sSldNMH1hsMsKuGxs-FhkmukdqA7jGa4gLoH44TqKyiB9xYz9SYuz02ngy3Z_jH7YuKbVCIqs3_GoXzOcbdzr0fRyP6CTomcBrIoSZQEx7ViPv0Lp4wf3pPt_zyOSVCeQ5kf3vjTsS-_0fs5KrbHNv79lNkBALSrNDDJTBFTo7RboIfhfsRdK0mZXAwzUgMuQVQSTVfe38SAk81t5hep59Nt9DAPECVEYO1j8YUeSPMHfFOcMXU8Jf2pWawwZYjHry6zfQ9Lf7gr-4QPIIvNjN_0kEGt6x-V8_QriVH4z1LkeLkLsy8a_i95Z5CadplFml5XZnt_MAi9rOfF1SVGfUjKYelOg1hUTRfDmFyHR4V1RCVJvdXCY17EHPfT7r0V6TUWFNhWAK2dkA8Be-CH_sb0LcyK1nb4zXrMlOQIB-XMsPxVl_tE3jLRTn9DREQDPnQ2G1-KeyjdI9zyutUVukG3EAdmJ-mH-9ATWfGuZQpRxblkXPiq4Ssi6jYLmE9yLcJyyZ3IfDsEMq4ZF7hNIutja3YPlCNUnE1U1yxqcNZvodlEKoTBpE0eZmq1_VVvZZmADz2CY6ZGrMMLb4-JPFw6mY8Y6sWSIuVok5nId4MmVrvMjf_M0DdPkRyCsqNBfw89fUFjUGWe6N0TTU4r595mfdPEg4HJJsq7qRlMCp3n1K8xMbz3zJDHe0OW-9_TcA7dSK7WpxkX8gaXemJddZjRuqJEIo4U5ortUQxCgYREMUAHYnWNZzy0qiyKYR17bA4apHq40wF3L-D_NUKkCP_i08dsCEabPqDHWM2HGW04kchwOlVcZeMKM592clkn4Fv6eIioqQIbFhFjAdnPiqSm4xWNtQxVn6VusRr4Sah0Vw5WYWaBqcZ4sJiQ6OslO0HKY8rg4MO6xo8niPr2tn9V_qwCIPLfYXfgub5tEofKtQkChKgIhtY8HIkFF25ZRyQf-MBu-bOfEG6WSBiuAqm25MqU00kqMHmv-y8tORjPhwHfl78Z1U0w3bz1NUoWtzB_qKFL1aCR6K0B2I59gS2ZvIj5BBM0rz0_u2QuogHNq1HluUEq6GkefCTZI8ovLHPFSVqWkQxqmvs7w3l2P3b9wXGzvnTR01bEVDNUETDHsDO9faiPkKoBS_vOwHNlJEJdPhfIpULJ0VgULPBrfN3XtHk14dcEGcXaFwIfXjeDMs5RXb38hs0wCmXcFQlr0TxIQ5t8jPMef3zCKlqqHm2l1HtJQJfzAOxbxZ2DQ6-LaeGBgHE6iW" nonce="508f6bedf744be51dd051f51433cdbea" charset="UTF-8"></script><link rel="stylesheet" crossorigin="anonymous" href="https://gc.kis.v2.scr.kaspersky-labs.com/E3E8934C-235A-4B0E-825A-35A08381A191/abn/main.css?attr=aHR0cHM6Ly9tYWlsLWF0dGFjaG1lbnQuZ29vZ2xldXNlcmNvbnRlbnQuY29tL2F0dGFjaG1lbnQvdS8wLz91aT0yJmlrPTI5MzQwOTYwNGImYXR0aWQ9MC4xJnBlcm1tc2dpZD1tc2ctZjoxODM1MTQzNjcwMjEyNTQ2MDM4JnRoPTE5NzdiZDcxZWVkMzRkZjYmdmlldz1hdHQmZGlzcD1zYWZlJnJlYWxhdHRpZD1mX21ienh0cTdrMCZ6dyZzYWRkYmF0PUFOR2pkSjlXOXNjTFh3Q0hueU9Bd0VFcFlrYUNSSDd4dFlPUFBYd0p6VndUbHZuLUhxTjMtSTFwOVk0d0tCMU10YWxVcTBWeW03TUJKWGVfcGNwa1kxVkowTjFmQTZqM2xUSm9nZlNPQ0FqcGtzSHROS0tBTXRtQ1ZqYTFnWkhPZUNfNWU0T3ExcFQxdUF2WE9kb19neDBxMlFMQnJMSnNobWQxZGd4RjY1MjdRRUxkUUlWRm0xY3V3Q1RlVGREWnlxYjZtdDdBcUI4UTJhdlJGaFRkUTE5eEx2QlFzOFJ2cXgxeDY5Y09mUjFmYlJpb2s3VzQ5TFp4eVY5T05pbVdyZHRnZ3d5Y1NfY2I0SkNveXRhXzd5d2NDamVpQmxaTGlMLUg1LU5DNTQ1TjhSZVNZVWRkeTJCSExzaFg4a2xLejdwaG04SVBPTlB4YzhsTk5TVjN1TkZlTTVrdWpSNUYxM0RJMjVWZ184ZEY3eGtDeENKYnk4QVBiTkpYVW5sOHlmZWR3b0FqTmtOU0taZTRRaHRsMnBFbWhBNU9DQTM1eUpKcGxFMEEzM1VweE41WFNxM2pRdFZrZDlFckllNnNWcUEtWjRSbGVuaEFJNG03Q3J3ZXctSjhjbllXN2NuZVJuaHoySWtkNkdhdVFubzRmOUxYV25wUnZsTkpVdTVTV2VDanZtRmtZOUJ0UVFjNGFCZHBiMEdIMkNIOXFZM1JxOEhOVTltN040ZUJKdUJLT2lIMUJEaVFWNFdHa052YW9INEdJYXRSRmFWYzBXcGFubERWblJnZS1GNzdvMkJSY1FpQkIzMVdFM0pxN3ZDRDlYSVoxckJNZnNzdGhnNEcxUGliNC1jSk1FakNJUWE0SDVQZUFCYUt6R0xpRVRNRWp1cFFXVWJJV3ZJMktOdHpEd04wQkpWcjVrM20zQ19qMl9ady1Qd296VG1qbjA5X1ZOdzlOSERyeGd2VnZxR01GQ3JDRHpQX1lNNnhhRlM4Tk82bDc0LVByNVZUYlIzQTJGdlhlVTJSRWIzMGV3NDlYUGM0VDYyLTUwRkstM3dySFlNNmtTQ3l4ZXJYNXBZbmxpOWZlRlU1blpzTFQ0dENyOGx5R0FReVJ1ZEpBTVl6YnNzai1pdjZXMk5wNmFRWDZZNktfZ1l4VmtPTWU1bzctMWdiTTFfWG5lMklNZl83dE5oTERLNTFPMTBNWHZYTWJOblhaNjhEOGpCMmFhdHhfaHlFbEtSb0RMU05KTHJuMVJlY0xGMy1Nb1hMLVNQVFM2MlNhdUV5RGxoNDZ0T3p2TUJPeG95UjF4TEI0OWVOSWJkODBidVprY1BLQ1ZNUmI2OTNGVUQ3eHowZ3h3c1lWRTZsOTI3MXlOUml6MHRBX1BuSk4zbEZkTUkzNy1pQjJSTlFDV0ZYVHc"/><style>
8
  body {
9
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
10
  margin: 0;
 
27
  box-shadow: 0 2px 5px rgba(0,0,0,0.05);
28
  display: flex;
29
  align-items: center;
30
+ justify-content: space-between;
31
  margin-bottom: 30px;
32
  }
33
+ .header-left {
34
+ display: flex;
35
+ align-items: center;
36
+ }
37
  .header img {
38
  height: 60px;
39
  margin-right: 15px;
40
  }
41
+ .header-content {
42
+ display: flex;
43
+ flex-direction: column;
44
+ }
45
  .header h1 {
46
  margin: 0;
47
  font-size: 28px;
48
  color: #2c3e50;
49
  font-weight: 600;
50
+ margin-bottom: 5px;
51
  }
52
+ .header-right {
 
 
53
  display: flex;
54
+ gap: 10px;
55
  }
56
+ .generate-another-btn {
57
+ padding: 12px 20px;
58
+ background-color: #3498db;
59
+ color: white;
60
+ text-decoration: none;
61
+ border-radius: 6px;
62
+ font-weight: 500;
63
+ font-size: 15px;
64
+ transition: background-color 0.3s;
65
+ }
66
+ .generate-another-btn:hover {
67
+ background-color: #2980b9;
68
+ text-decoration: none;
69
+ }
70
+ .success-message {
71
+ text-align: left;
72
+ padding: 15px;
73
+ background-color: #d4edda;
74
+ border: 1px solid #c3e6cb;
75
+ border-radius: 8px;
76
+ margin-bottom: 20px;
77
+ }
78
+ .success-message h2 {
79
  margin: 0;
80
+ font-size: 18px;
81
+ color: #155724;
82
+ font-weight: 500;
83
+ }
84
+ .model-name {
85
  font-weight: 600;
86
+ color: #2c3e50;
87
  }
88
+ /* Footer styling */
89
+ .footer {
90
+ text-align: center;
91
+ padding: 20px;
92
+ color: #7f8c8d;
93
  font-size: 14px;
94
+ margin-top: 30px;
 
95
  }
96
 
97
  /* Content styling */
 
361
  .score-table td {
362
  padding: 12px;
363
  }
364
+ /* Adjust column widths for better progress bar display */
365
+ .score-table th:nth-child(1), .score-table td:nth-child(1) {
366
+ width: 25%; /* Category column */
367
+ }
368
+ .score-table th:nth-child(2), .score-table td:nth-child(2) {
369
+ width: 20%; /* Fields Present column */
370
+ }
371
+ .score-table th:nth-child(3), .score-table td:nth-child(3) {
372
+ width: 15%; /* Score column */
373
+ }
374
+ .score-table th:nth-child(4), .score-table td:nth-child(4) {
375
+ width: 40%; /* Progress column - wider for better display */
376
+ }
377
  .score-weight {
378
  font-size: 0.9em;
379
  color: #666;
 
474
  color: #2e7d32;
475
  margin-bottom: 15px;
476
  }
477
+ .recommendations ul {
478
+ margin-bottom: 0;
479
+ padding-left: 20px;
480
+ }
481
  .importance-indicator {
482
  display: inline-block;
483
  margin-left: 5px;
 
530
 
531
  /* Styles for completeness profile */
532
  .completeness-profile {
533
+ background-color: #f6f5f5;
534
  border-radius: 8px;
535
  padding: 20px;
536
  margin: 20px 0;
537
+ border-left: 4px solid #7f7c7c;
538
  }
539
  .profile-badge {
540
  display: inline-block;
 
599
  margin-bottom: 15px;
600
  }
601
 
602
+ /* Validation warning box styling */
603
+ .validation-warning-box {
604
+ background-color: #fff3e0;
605
+ border: 1px solid #ff9800;
606
+ border-left: 4px solid #ff9800;
607
  border-radius: 8px;
608
+ padding: 20px;
609
+ margin: 20px 0;
610
+ box-shadow: 0 2px 10px rgba(255, 152, 0, 0.1);
611
  }
612
+ .validation-warning-box h4 {
613
  margin-top: 0;
614
+ color: #e65100;
615
+ margin-bottom: 15px;
616
+ display: flex;
617
+ align-items: center;
618
+ }
619
+ .validation-warning-box .warning-icon {
620
+ margin-right: 10px;
621
+ font-size: 1.2em;
622
+ }
623
+ .validation-warning-box .issue-summary {
624
+ margin-bottom: 15px;
625
+ line-height: 1.6;
626
+ }
627
+ .validation-warning-box .issue-details {
628
+ margin-bottom: 15px;
629
+ }
630
+ .validation-warning-box .issue-list {
631
+ margin: 10px 0;
632
+ padding-left: 20px;
633
+ }
634
+ .validation-warning-box .issue-list li {
635
+ margin-bottom: 8px;
636
+ line-height: 1.5;
637
+ }
638
+ .validation-warning-box .call-to-action {
639
+ margin-top: 15px;
640
+ padding-top: 15px;
641
+ border-top: 1px solid #ffcc80;
642
+ }
643
+ .validation-warning-box .call-to-action p {
644
+ margin-bottom: 10px;
645
+ }
646
+ .issue-tracker-link {
647
+ display: inline-block;
648
+ padding: 8px 16px;
649
+ background-color: #3498db;
650
+ color: white;
651
+ text-decoration: none;
652
+ border-radius: 4px;
653
+ font-weight: 500;
654
+ transition: background-color 0.3s;
655
+ }
656
+ .issue-tracker-link:hover {
657
+ background-color: #2980b9;
658
+ text-decoration: none;
659
+ }
660
+
661
+ /* Category table styling */
662
+ .category-table {
663
+ margin-bottom: 30px;
664
+ }
665
+ .category-table h4 {
666
  color: #2c3e50;
667
+ margin-bottom: 10px;
668
+ font-size: 18px;
 
669
  }
670
+ .category-result {
671
+ background-color: #f8f9fa;
672
+ padding: 10px;
673
+ border-radius: 4px;
674
+ margin-top: 10px;
675
+ font-weight: bold;
676
+ }
677
+ .field-type-legend {
678
+ background-color: #e3f2fd;
679
+ border-left: 4px solid #2196f3;
680
+ padding: 15px;
681
+ margin: 20px 0;
682
+ border-radius: 8px;
683
+ font-size: 0.9em;
684
+ }
685
+ .field-type-legend h4 {
686
+ margin-top: 0;
687
+ color: #1565c0;
688
+ margin-bottom: 10px;
689
+ }
690
+ .legend-item {
691
+ display: inline-block;
692
+ margin-right: 20px;
693
+ margin-bottom: 5px;
694
  }
695
 
696
+ /* Support section styling */
697
+ .support-section {
698
+ background-color: #f8f9fa;
699
+ border-left: 4px solid #6c757d;
700
  padding: 20px;
701
+ margin: 30px 0;
702
+ border-radius: 8px;
703
+ text-align: center;
704
  }
705
+ .support-section h3 {
706
+ margin-top: 0;
707
+ color: #495057;
708
+ margin-bottom: 15px;
709
+ }
710
+ .support-section p {
711
+ margin-bottom: 15px;
712
+ color: #6c757d;
713
+ }
714
+ .github-button {
715
+ display: inline-block;
716
+ padding: 12px 20px;
717
+ background-color: #3498db;
718
+ color: white;
719
+ text-decoration: none;
720
+ border-radius: 6px;
721
+ font-weight: 500;
722
+ transition: background-color 0.3s;
723
+ }
724
+ .github-button:hover {
725
+ background-color: #2980b9;
726
+ text-decoration: none;
 
727
  }
728
  </style>
729
  </head>
730
  <body>
 
 
 
 
 
 
 
 
 
 
 
 
731
  <div class="container">
732
+ <!-- Header -->
733
+ <div class="header">
734
+ <div class="header-left">
735
+ <a href="https://aetheris.ai/" target="_blank">
736
+ <img src="https://huggingface.co/spaces/aetheris-ai/aibom-generator/resolve/main/templates/images/AetherisAI-logo.png" alt="Aetheris AI Logo">
737
+ </a>
738
+ <div class="header-content">
739
+ <h1>AI SBOM Generator</h1>
740
+ </div>
741
  </div>
742
+ <div class="header-right">
743
+ <!-- <a href="https://aetheris-ai-aibom-generator.hf.space/" class="generate-another-btn">🚀 Generate Another AI SBOM</a> -->
744
+ <a href="/" class="generate-another-btn">🚀 Generate Another AI SBOM</a>
745
+ </div>
746
+ </div>
747
+
748
+ <!-- Success Message
749
+ <div class="success-message">
750
+ <h2>✅ AI SBOM is Generated Successfully for <span class="model-name">{{ model_id }}</span></h2>
751
+ </div> -->
752
 
753
+ <!-- Key Information -->
754
+ <div class="key-info">
755
+ <h3>📋 AI SBOM Summary for model <span class="model-name">{{ model_id }}</span></h3>
756
+ <div class="aibom-property">
757
+ <span class="property-name">Model:</span>
758
+ <span class="property-value">{{ model_id }}</span>
759
+ </div>
760
+ <div class="aibom-property">
761
+ <span class="property-name">Generated:</span>
762
+ <span class="property-value">{{ aibom.metadata.timestamp }}</span>
763
+ </div>
764
+ <div class="aibom-property">
765
+ <span class="property-name">SBOM Format:</span>
766
+ <span class="property-value">{{ aibom.bomFormat }} {{ aibom.specVersion }}</span>
767
+ </div>
768
+ <div class="aibom-property">
769
+ <span class="property-name">Serial Number:</span>
770
+ <span class="property-value">{{ aibom.serialNumber }}</span>
771
  </div>
 
772
  </div>
773
 
774
+ <!-- Download Section -->
775
+ <div class="download-section">
776
+ <p><strong>Download your AI SBOM:</strong></p>
777
+ <button onclick="downloadJSON()">📥 Download JSON</button>
778
  </div>
779
+
780
+ <!-- Completeness Profile -->
781
+ {% if completeness_score.completeness_profile %}
782
+ <div class="completeness-profile">
783
+ <h3>📊 Completeness Assessment</h3>
784
+ <div>
785
+ <span class="profile-badge profile-{{ completeness_score.completeness_profile.name|lower }}">
786
+ {{ completeness_score.completeness_profile.name }}
787
+ </span>
788
+ <span>{{ completeness_score.completeness_profile.description }}</span>
789
+ </div>
790
  </div>
791
+ {% endif %}
792
+
793
+ <!-- Tabbed Content -->
794
+ <div class="aibom-viewer">
795
+ <div class="aibom-tabs">
796
+ <div class="aibom-tab active" onclick="switchTab('human-view')">Human-Friendly View</div>
797
+ <div class="aibom-tab" onclick="switchTab('field-checklist')">Field Checklist</div>
798
+ <div class="aibom-tab" onclick="switchTab('score-view')">Score Report</div>
799
+ <div class="aibom-tab" onclick="switchTab('json-view')">JSON View</div>
800
+ </div>
801
 
802
+ <!-- Human-Friendly View Tab -->
803
+ <div id="human-view" class="tab-content active">
804
+ <div class="aibom-section">
805
+ <h4>🤖 AI Model Information</h4>
806
+ <div class="aibom-property">
807
+ <span class="property-name">Name:</span>
808
+ <span class="property-value">{{ aibom.components[0].name if aibom.components else 'Not specified' }}</span>
809
+ </div>
810
  <div class="aibom-property">
811
+ <span class="property-name">Type:</span>
812
+ <span class="property-value">{{ aibom.components[0].type if aibom.components else 'Not specified' }}</span>
813
  </div>
814
  <div class="aibom-property">
815
+ <span class="property-name">Version:</span>
816
+ <span class="property-value">{{ aibom.components[0].version if aibom.components else 'Not specified' }}</span>
817
  </div>
818
  <div class="aibom-property">
819
+ <span class="property-name">Description:</span>
820
+ <span class="property-value">{{ aibom.components[0].description if aibom.components and aibom.components[0].description else 'Not specified' }}</span>
821
  </div>
822
  <div class="aibom-property">
823
+ <span class="property-name">PURL:</span>
824
+ <span class="property-value">{{ aibom.components[0].purl if aibom.components and aibom.components[0].purl else 'Not specified' }}</span>
825
  </div>
826
+ {% if aibom.components and aibom.components[0].licenses %}
827
  <div class="aibom-property">
828
+ <span class="property-name">Licenses:</span>
829
+ <span class="property-value">
830
+ {% for license in aibom.components[0].licenses %}
831
+ <span class="tag">{{ license.license.id if license.license else 'Unknown' }}</span>
832
+ {% endfor %}
833
+ </span>
834
  </div>
835
  {% endif %}
836
  </div>
837
 
 
838
  {% if aibom.components and aibom.components[0].modelCard %}
839
  <div class="aibom-section">
840
+ <h4>📊 Model Card</h4>
841
+ {% if aibom.components[0].modelCard.modelParameters %}
842
+ <div class="aibom-property">
843
+ <span class="property-name">Architecture:</span>
844
+ <span class="property-value">{{ aibom.components[0].modelCard.modelParameters.modelArchitecture if aibom.components[0].modelCard.modelParameters.modelArchitecture else 'Not specified' }}</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
845
  </div>
846
+ <div class="aibom-property">
847
+ <span class="property-name">Task:</span>
848
+ <span class="property-value">{{ aibom.components[0].modelCard.modelParameters.task if aibom.components[0].modelCard.modelParameters.task else 'Not specified' }}</span>
849
+ </div>
850
+ {% endif %}
851
+ {% if aibom.components[0].modelCard.properties %}
852
+ <div class="aibom-property">
853
+ <span class="property-name">Additional Properties:</span>
854
+ <span class="property-value">
855
+ {% for prop in aibom.components[0].modelCard.properties %}
856
+ <span class="tag">{{ prop.name }}: {{ prop.value }}</span>
857
+ {% endfor %}
858
+ </span>
859
+ </div>
860
+ {% endif %}
861
  </div>
862
  {% endif %}
863
+
864
+ {% if aibom.externalReferences %}
 
865
  <div class="aibom-section">
866
+ <h4>🔗 External References</h4>
867
+ {% for ref in aibom.externalReferences %}
868
+ <div class="aibom-property">
869
+ <span class="property-name">{{ ref.type|title }}:</span>
870
+ <span class="property-value"><a href="{{ ref.url }}" target="_blank">{{ ref.url }}</a></span>
 
 
 
 
 
 
 
 
871
  </div>
872
+ {% endfor %}
873
  </div>
874
  {% endif %}
 
 
875
 
876
+ <div class="aibom-section">
877
+ <h4>🛠️ Generation Metadata</h4>
878
+ <div class="aibom-property">
879
+ <span class="property-name">Generated by:</span>
880
+ <span class="property-value">{{ aibom.metadata.tools.components[0].name if aibom.metadata.tools and aibom.metadata.tools.components else 'Unknown' }}</span>
 
 
 
 
 
 
 
 
 
 
881
  </div>
882
+ <div class="aibom-property">
883
+ <span class="property-name">Timestamp:</span>
884
+ <span class="property-value">{{ aibom.metadata.timestamp }}</span>
885
  </div>
886
+ {% if aibom.metadata.component %}
887
+ <div class="aibom-property">
888
+ <span class="property-name">Component Ref:</span>
889
+ <span class="property-value">{{ aibom.metadata.component['bom-ref'] }}</span>
890
  </div>
891
+ {% endif %}
892
  </div>
893
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
894
 
895
+ <!-- Field Checklist Tab -->
896
+ <div id="field-checklist" class="tab-content">
897
+ <div class="content-section">
898
+ <h3>Field Checklist & Mapping</h3>
899
+
900
+ <!-- Field Type Legend -->
901
+ <div class="field-type-legend">
902
+ <h4>Legend</h4>
903
+ <div class="legend-item">
904
+ <span class="field-tier tier-critical"></span>
905
+ <span>Critical</span>
906
+ </div>
907
+ <div class="legend-item">
908
+ <span class="field-tier tier-important"></span>
909
+ <span>Important</span>
910
+ </div>
911
+ <div class="legend-item">
912
+ <span class="field-tier tier-supplementary"></span>
913
+ <span>Supplementary</span>
914
+ </div>
915
+ <div class="legend-item">
916
+ <strong>CDX</strong> = CycloneDX Standard
917
+ </div>
918
+ <div class="legend-item">
919
+ <strong>AI</strong> = AI-Specific Extension
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
920
  </div>
921
  </div>
922
+
923
+ <p>This breakdown outlines field categories and statuses in the AI SBOM generated for model <strong>{{ model_id }}</strong>, showing how each field impacts the completeness score.</p>
924
+
925
+ {% if completeness_score.field_checklist %}
926
+ <!-- Required Fields Category -->
927
+ <div class="category-table">
928
+ <h4>Required Fields Category</h4>
929
+ <table>
930
+ <thead>
931
+ <tr>
932
+ <th>Status</th>
933
+ <th>Field Name</th>
934
+ <th>Actual Location</th>
935
+ <th>Tier</th>
936
+ <th>Type</th>
937
+ </tr>
938
+ </thead>
939
+ <tbody>
940
+ {% set required_fields = ['bomFormat', 'specVersion', 'serialNumber', 'version'] %}
941
+ {% for field in required_fields %}
942
+ <tr>
943
+ <td>
944
+ {% if completeness_score.field_checklist.get(field, '').startswith('✔') %}
945
+ <span class="check-mark">✔</span>
946
+ {% else %}
947
+ <span class="x-mark">✘</span>
948
+ {% endif %}
949
+ </td>
950
+ <td>{{ field }}</td>
951
+ <td>
952
+ {% if completeness_score.field_checklist.get(field, '').startswith('✔') %}
953
+ $.{{ field }}
954
+ {% else %}
955
+ Not found
956
+ {% endif %}
957
+ </td>
958
+ <td><span class="field-tier tier-critical"></span> Critical</td>
959
+ <td>CDX</td>
960
+ </tr>
961
+ {% endfor %}
962
+ </tbody>
963
+ </table>
964
+ <div class="category-result">
965
+ 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
966
+ ({{ completeness_score.category_details.required_fields.percentage if completeness_score.category_details else 'N/A' }}%) =
967
+ {{ completeness_score.section_scores.required_fields if completeness_score.section_scores else 'N/A' }}/20 points
968
+ </div>
969
+ </div>
970
 
971
+ <!-- Metadata Category -->
972
+ <div class="category-table">
973
+ <h4>Metadata Category</h4>
974
+ <table>
975
+ <thead>
976
+ <tr>
977
+ <th>Status</th>
978
+ <th>Field Name</th>
979
+ <th>Actual Location</th>
980
+ <th>Tier</th>
981
+ <th>Type</th>
982
+ </tr>
983
+ </thead>
984
+ <tbody>
985
+ {% set metadata_fields = [
986
+ ('primaryPurpose', 'Critical'),
987
+ ('suppliedBy', 'Critical'),
988
+ ('standardCompliance', 'Supplementary'),
989
+ ('domain', 'Supplementary'),
990
+ ('autonomyType', 'Supplementary')
991
+ ] %}
992
+ {% for field, tier in metadata_fields %}
993
+ <tr>
994
+ <td>
995
+ {% if completeness_score.field_checklist.get(field, '').startswith('✔') %}
996
+ <span class="check-mark">✔</span>
997
+ {% else %}
998
+ <span class="x-mark">✘</span>
999
+ {% endif %}
1000
+ </td>
1001
+ <td>{{ field }}</td>
1002
+ <td>
1003
+ {% if completeness_score.field_checklist.get(field, '').startswith('✔') %}
1004
+ $.metadata.properties[name="{{ field }}"]
1005
+ {% else %}
1006
+ Not found
1007
+ {% endif %}
1008
+ </td>
1009
+ <td><span class="field-tier tier-{{ tier.lower() }}"></span> {{ tier }}</td>
1010
+ <td>AI</td>
1011
+ </tr>
1012
+ {% endfor %}
1013
+ </tbody>
1014
+ </table>
1015
+ <div class="category-result">
1016
+ 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
1017
+ ({{ completeness_score.category_details.metadata.percentage if completeness_score.category_details else 'N/A' }}%) =
1018
+ {{ completeness_score.section_scores.metadata if completeness_score.section_scores else 'N/A' }}/20 points
1019
  </div>
1020
+ </div>
1021
+
1022
+ <!-- Component Basic Category -->
1023
+ <div class="category-table">
1024
+ <h4>Component Basic Category</h4>
1025
+ <table>
1026
+ <thead>
1027
+ <tr>
1028
+ <th>Status</th>
1029
+ <th>Field Name</th>
1030
+ <th>Actual Location</th>
1031
+ <th>Tier</th>
1032
+ <th>Type</th>
1033
+ </tr>
1034
+ </thead>
1035
+ <tbody>
1036
+ {% set component_basic_fields = [
1037
+ ('name', 'Critical'),
1038
+ ('type', 'Important'),
1039
+ ('purl', 'Important'),
1040
+ ('description', 'Important'),
1041
+ ('licenses', 'Important')
1042
+ ] %}
1043
+ {% for field, tier in component_basic_fields %}
1044
+ <tr>
1045
+ <td>
1046
+ {% if completeness_score.field_checklist.get(field, '').startswith('✔') %}
1047
+ <span class="check-mark">✔</span>
1048
+ {% else %}
1049
+ <span class="x-mark">✘</span>
1050
+ {% endif %}
1051
+ </td>
1052
+ <td>{{ field }}</td>
1053
+ <td>
1054
+ {% if completeness_score.field_checklist.get(field, '').startswith('✔') %}
1055
+ $.components[0].{{ field }}
1056
+ {% else %}
1057
+ {% if field == 'description' %}
1058
+ Not found in component level
1059
+ {% else %}
1060
+ Not found
1061
+ {% endif %}
1062
+ {% endif %}
1063
+ </td>
1064
+ <td><span class="field-tier tier-{{ tier.lower() }}"></span> {{ tier }}</td>
1065
+ <td>CDX</td>
1066
+ </tr>
1067
+ {% endfor %}
1068
+ </tbody>
1069
+ </table>
1070
+ <div class="category-result">
1071
+ 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
1072
+ ({{ completeness_score.category_details.component_basic.percentage if completeness_score.category_details else 'N/A' }}%) =
1073
+ {{ completeness_score.section_scores.component_basic if completeness_score.section_scores else 'N/A' }}/20 points
1074
  </div>
1075
+ </div>
1076
+
1077
+ <!-- Component Model Card Category -->
1078
+ <div class="category-table">
1079
+ <h4>Component Model Card Category</h4>
1080
+ <table>
1081
+ <thead>
1082
+ <tr>
1083
+ <th>Status</th>
1084
+ <th>Field Name</th>
1085
+ <th>Actual Location</th>
1086
+ <th>Tier</th>
1087
+ <th>Type</th>
1088
+ </tr>
1089
+ </thead>
1090
+ <tbody>
1091
+ {% set model_card_fields = [
1092
+ ('energyConsumption', 'Important'),
1093
+ ('hyperparameter', 'Important'),
1094
+ ('limitation', 'Important'),
1095
+ ('safetyRiskAssessment', 'Important'),
1096
+ ('typeOfModel', 'Important'),
1097
+ ('modelExplainability', 'Supplementary'),
1098
+ ('energyQuantity', 'Supplementary'),
1099
+ ('energyUnit', 'Supplementary'),
1100
+ ('informationAboutTraining', 'Supplementary'),
1101
+ ('informationAboutApplication', 'Supplementary'),
1102
+ ('metric', 'Supplementary'),
1103
+ ('metricDecisionThreshold', 'Supplementary'),
1104
+ ('modelDataPreprocessing', 'Supplementary'),
1105
+ ('useSensitivePersonalInformation', 'Supplementary')
1106
+ ] %}
1107
+ {% for field, tier in model_card_fields %}
1108
+ <tr>
1109
+ <td>
1110
+ {% if completeness_score.field_checklist.get(field, '').startswith('✔') %}
1111
+ <span class="check-mark">✔</span>
1112
+ {% else %}
1113
+ <span class="x-mark">✘</span>
1114
+ {% endif %}
1115
+ </td>
1116
+ <td>{{ field }}</td>
1117
+ <td>
1118
+ {% if completeness_score.field_checklist.get(field, '').startswith('✔') %}
1119
+ {% if field == 'typeOfModel' %}
1120
+ $.metadata.properties[name="{{ field }}"]
1121
+ {% else %}
1122
+ $.components[0].modelCard.{{ field }}
1123
+ {% endif %}
1124
+ {% else %}
1125
+ Not found
1126
+ {% endif %}
1127
+ </td>
1128
+ <td><span class="field-tier tier-{{ tier.lower() }}"></span> {{ tier }}</td>
1129
+ <td>AI</td>
1130
+ </tr>
1131
+ {% endfor %}
1132
+ </tbody>
1133
+ </table>
1134
+ <div class="category-result">
1135
+ 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
1136
+ ({{ completeness_score.category_details.component_model_card.percentage if completeness_score.category_details else 'N/A' }}%) =
1137
+ {{ completeness_score.section_scores.component_model_card if completeness_score.section_scores else 'N/A' }}/30 points
1138
  </div>
1139
+ </div>
1140
+
1141
+ <!-- External References Category -->
1142
+ <div class="category-table">
1143
+ <h4>External References Category</h4>
1144
+ <table>
1145
  <thead>
1146
  <tr>
1147
+ <th>Status</th>
1148
+ <th>Field Name</th>
1149
+ <th>Actual Location</th>
1150
+ <th>Tier</th>
1151
+ <th>Type</th>
1152
  </tr>
1153
  </thead>
1154
  <tbody>
1155
+ {% set external_ref_fields = [('downloadLocation', 'Critical')] %}
1156
+ {% for field, tier in external_ref_fields %}
1157
+ <tr>
1158
+ <td>
1159
+ {% if completeness_score.field_checklist.get(field, '').startswith('✔') %}
1160
+ <span class="check-mark">✔</span>
1161
+ {% else %}
1162
+ <span class="x-mark">✘</span>
1163
+ {% endif %}
1164
+ </td>
1165
+ <td>{{ field }}</td>
1166
+ <td>
1167
+ {% if completeness_score.field_checklist.get(field, '').startswith('✔') %}
1168
+ $.externalReferences[type="distribution"]
1169
+ {% else %}
1170
+ Not found
1171
+ {% endif %}
1172
+ </td>
1173
+ <td><span class="field-tier tier-{{ tier.lower() }}"></span> {{ tier }}</td>
1174
+ <td>CDX</td>
1175
+ </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1176
  {% endfor %}
1177
  </tbody>
1178
  </table>
1179
+ <div class="category-result">
1180
+ 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
1181
+ ({{ completeness_score.category_details.external_references.percentage if completeness_score.category_details else 'N/A' }}%) =
1182
+ {{ completeness_score.section_scores.external_references if completeness_score.section_scores else 'N/A' }}/10 points
1183
+ </div>
1184
+ </div>
1185
+
1186
+ {% else %}
1187
+ <p>Field checklist data not available.</p>
1188
+ {% endif %}
1189
+ </div>
1190
+ </div>
1191
+
1192
+ <!-- Score Report Tab -->
1193
+ <div id="score-view" class="tab-content">
1194
+ <div class="content-section">
1195
+ <h3>📊 Completeness Score Report</h3>
1196
 
1197
+ <!-- Total Score Display -->
1198
+ <div class="total-score-container">
1199
+ <div class="total-score">{{ (completeness_score.total_score if completeness_score.total_score != "Undefined" else 0)|round(1) }}/100</div>
1200
+ <div class="total-progress">
1201
+ <div class="progress-container">
1202
+ {% set score_percent = completeness_score.total_score %}
1203
+ {% if score_percent >= 90 %}
1204
+ {% set score_class = 'progress-excellent' %}
1205
+ {% set score_label = 'Excellent' %}
1206
+ {% elif score_percent >= 70 %}
1207
+ {% set score_class = 'progress-good' %}
1208
+ {% set score_label = 'Good' %}
1209
+ {% elif score_percent >= 50 %}
1210
+ {% set score_class = 'progress-fair' %}
1211
+ {% set score_label = 'Fair' %}
1212
+ {% else %}
1213
+ {% set score_class = 'progress-poor' %}
1214
+ {% set score_label = 'Poor' %}
 
 
 
 
 
 
 
 
1215
  {% endif %}
1216
+
1217
+ <div class="progress-bar {{ score_class }}" style="width: {{ score_percent }}%">
1218
+ {{ score_percent|int }}% {{ score_label }}
1219
+ </div>
1220
+ </div>
1221
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1222
  </div>
1223
+
1224
+ <!-- Generic Scoring Explanation -->
1225
+ <div class="scoring-rubric">
1226
+ <h4>How AI SBOM Completeness is Scored</h4>
1227
+ <p>The completeness score evaluates how well your AI SBOM documents the model across five key categories:</p>
 
 
1228
  <ul>
1229
+ <li><strong>Required Fields (20 points):</strong> Basic SBOM structure mandated by CycloneDX</li>
1230
+ <li><strong>Metadata (20 points):</strong> Information about the SBOM generation and model purpose</li>
1231
+ <li><strong>Component Basic (20 points):</strong> Essential model identification and licensing</li>
1232
+ <li><strong>Model Card (30 points):</strong> Detailed AI-specific documentation for transparency</li>
1233
+ <li><strong>External References (10 points):</strong> Links to model resources and documentation</li>
1234
  </ul>
1235
 
1236
+ <p><strong>Calculation Method:</strong></p>
1237
+ <p>Each category score = (Present Fields ÷ Total Fields) × Maximum Points</p>
1238
+ <p>Subtotal = Sum of all category scores</p>
1239
+ <p>Final Score = Subtotal × Penalty Factor (if applicable)</p>
1240
+ <h4>Penalty Structure:</h4>
1241
+ <p><strong>Critical Fields Missing:</strong></p>
 
 
 
 
 
 
 
 
1242
  <ul>
1243
+ <li>0-1 missing: No penalty</li>
1244
+ <li>2-3 missing: 10% penalty (×0.9)</li>
1245
+ <li>4+ missing: 20% penalty (×0.8)</li>
1246
  </ul>
1247
 
1248
+ <p><strong>Important Fields Missing:</strong></p>
1249
  <ul>
1250
+ <li>0-4 missing: No penalty</li>
1251
+ <li>5+ missing: 5% penalty (×0.95)</li>
 
1252
  </ul>
1253
 
1254
+ <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>
1255
+
1256
+ </div>
1257
+
1258
+ <!-- Specific Breakdown for This SBOM -->
1259
+ <div class="note-box">
1260
+ <h4>Your AI SBOM Breakdown</h4>
1261
+ <p><strong>Model:</strong> {{ model_id }}</p>
1262
+
1263
+ <table class="score-table">
1264
+ <thead>
1265
+ <tr>
1266
+ <th>Category</th>
1267
+ <th>Fields Present</th>
1268
+ <th>Score</th>
1269
+ <th>Progress</th>
1270
+ </tr>
1271
+ </thead>
1272
+ <tbody>
1273
+ {% if completeness_score.category_details and completeness_score.section_scores %}
1274
+ {% set categories = [
1275
+ ('Required Fields', 'required_fields', 20),
1276
+ ('Metadata', 'metadata', 20),
1277
+ ('Component Basic', 'component_basic', 20),
1278
+ ('Model Card', 'component_model_card', 30),
1279
+ ('External References', 'external_references', 10)
1280
+ ] %}
1281
+ {% for display_name, key, max_score in categories %}
1282
+ <tr>
1283
+ <td>{{ display_name }}</td>
1284
+ <td>{{ completeness_score.category_details[key].present_fields }}/{{ completeness_score.category_details[key].total_fields }}</td>
1285
+ <td>{{ completeness_score.section_scores[key]|round(1) }}/{{ max_score }}</td>
1286
+ <td>
1287
+ <div class="progress-container">
1288
+ {% set percentage = completeness_score.category_details[key].percentage %}
1289
+ {% if percentage >= 80 %}
1290
+ {% set progress_class = "progress-excellent" %}
1291
+ {% elif percentage >= 60 %}
1292
+ {% set progress_class = "progress-good" %}
1293
+ {% elif percentage >= 40 %}
1294
+ {% set progress_class = "progress-fair" %}
1295
+ {% else %}
1296
+ {% set progress_class = "progress-poor" %}
1297
+ {% endif %}
1298
+ <div class="progress-bar {{ progress_class }}" style="width: {{ percentage }}%">{{ percentage|round(0) }}%</div>
1299
+ </div>
1300
+ </td>
1301
+ </tr>
1302
+ {% endfor %}
1303
+ {% else %}
1304
+ <tr><td colspan="4">Breakdown data not available</td></tr>
1305
+ {% endif %}
1306
+ </tbody>
1307
+ </table>
1308
+ </div>
1309
+
1310
+
1311
+ <p><strong>Calculation:</strong></p>
1312
+ <p>Subtotal:
1313
+ {% if completeness_score.section_scores %}
1314
+ {% for category, score in completeness_score.section_scores.items() %}
1315
+ {{ score|round(1) }}{% if not loop.last %} + {% endif %}
1316
+ {% endfor %}
1317
+ = <strong>{{ completeness_score.subtotal_score|round(1) }}/100</strong>
1318
+ {% else %}
1319
+ <strong>{{ completeness_score.subtotal_score|round(1) }}/100</strong>
1320
+ {% endif %}
1321
+ </p>
1322
+
1323
+ {% if completeness_score.penalty_applied %}
1324
+ <p>Penalty Applied: <strong>-{{ completeness_score.penalty_percentage }}%</strong> ({{ completeness_score.penalty_reason }})</p>
1325
+ <p>Final Score: {{ completeness_score.subtotal_score|round(1) }} × {{ completeness_score.penalty_factor }} = <strong>{{ completeness_score.total_score|round(1) }}/100</strong></p>
1326
+ {% else %}
1327
+ <p>No penalties applied</p>
1328
+ <p>Final Score: <strong>{{ completeness_score.total_score|round(1) }}/100</strong></p>
1329
+ {% endif %}
1330
+ </div>
1331
+
1332
+ <!-- Missing Fields Analysis -->
1333
+ {% if completeness_score.missing_counts %}
1334
+ <div class="missing-fields">
1335
+ <h4>Missing Fields Summary</h4>
1336
  <ul>
1337
+ <li><strong>Critical:</strong> {{ completeness_score.missing_counts.critical }} missing</li>
1338
+ <li><strong>Important:</strong> {{ completeness_score.missing_counts.important }} missing</li>
1339
+ <li><strong>Supplementary:</strong> {{ completeness_score.missing_counts.supplementary }} missing</li>
1340
  </ul>
1341
+
1342
+ {% if completeness_score.missing_counts.important >= 5 %}
1343
+ <p><strong>Impact:</strong> Missing multiple critical and/or important fields will incur penalties according to the Penalty Structure.</p>
1344
  {% endif %}
1345
  </div>
1346
+ {% endif %}
1347
+
1348
+ <!-- Recommendations -->
1349
+ {% if completeness_score.recommendations %}
1350
+ <div class="recommendations">
1351
+ <h4>General Recommendations to Improve AI SBOM Completeness</h4>
1352
+ <ul>
1353
+ <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>
1354
+ <li><strong>Metadata:</strong> Include author or organization name, purpose of the model, and relevant timestamps in the model repository or card.</li>
1355
+ <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>
1356
+ <li><strong>Model Card:</strong> Fill out structured sections for model parameters, evaluation metrics, limitations, and ethical considerations to enable full transparency.</li>
1357
+ <li><strong>External References:</strong> Add links to source code, datasets, documentation, and versioned download locations to support traceability and reproducibility.</li>
1358
+ </ul>
1359
+ </div>
1360
+ {% endif %}
1361
+ </div>
1362
+ </div>
1363
+
1364
+ <!-- JSON View Tab -->
1365
+ <div id="json-view" class="tab-content">
1366
+ <div class="content-section">
1367
+ <h3>📄 Raw JSON View</h3>
1368
+ <p>This is the complete AI SBOM in CycloneDX JSON format:</p>
1369
+ <div class="json-view">
1370
+ <pre>{{ aibom|tojson(indent=2) }}</pre>
1371
+ </div>
1372
  </div>
1373
  </div>
1374
  </div>
1375
 
1376
+ <!-- Support Section -->
1377
+ <div class="support-section">
1378
+ <h3>🛠️ Need Help or Found an Issue?</h3>
1379
+ <p>If you encountered any problems, found a bug, or have suggestions for improvement, we'd love to hear from you!</p>
1380
+ <a href="https://github.com/aetheris-ai/aibom-generator/issues" target="_blank" class="github-button">
1381
+ Report Issue on GitHub
1382
+ </a>
1383
+ </div>
1384
+
1385
+ <!-- Help us spread the word section -->
1386
  <div class="content-section" style="text-align: center;">
1387
  <h3>🗣️ Help Us Spread the Word</h3>
1388
  <p>If you find this tool useful, share it with your network! <a href="https://sbom.aetheris.ai" target="_blank" rel="noopener noreferrer">https://sbom.aetheris.ai</a></p>
 
1395
  </p>
1396
  </div>
1397
 
1398
+ <!-- Info Section -->
1399
+ <div class="content-section" style="text-align: center;">
1400
  <!-- Display the SBOM count -->
1401
  <div class="sbom-count">🚀 Generated AI SBOMs using this tool: <strong>{{ sbom_count if sbom_count else 'N/A' }}</strong></div>
1402
  </div>
1403
+
1404
  <!-- Footer -->
1405
  <div class="footer">
1406
  <p>© 2025 AI SBOM Generator | Powered by Aetheris AI</p>
1407
  </div>
1408
+ </div>
1409
 
1410
  <script>
1411
  function switchTab(tabId) {
 
1458
  });
1459
  </script>
1460
  </body>
1461
+ </html>
1462
+