Skip to content

Commit 95ab402

Browse files
feat: Refactor enrichment command to make viper enrichment file work with file strategy mode
1 parent 857424e commit 95ab402

File tree

3 files changed

+51
-44
lines changed

3 files changed

+51
-44
lines changed

cmd/enrich.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,12 @@ from Hugging Face API and README before enrichment.`,
8787
}
8888

8989
// Load config file values if using file strategy
90-
var fileValues map[string]interface{}
90+
var configViper *viper.Viper
9191
if strategy == "file" {
9292
if enrichConfigFile == "" {
9393
return fmt.Errorf("--file is required when using --strategy file")
9494
}
95-
fileValues, err = loadEnrichmentConfig(enrichConfigFile)
95+
configViper, err = loadEnrichmentConfig(enrichConfigFile)
9696
if err != nil {
9797
return fmt.Errorf("failed to load config file: %w", err)
9898
}
@@ -106,7 +106,7 @@ from Hugging Face API and README before enrichment.`,
106106
})
107107

108108
// Run enrichment
109-
enriched, err := e.Enrich(bom, fileValues)
109+
enriched, err := e.Enrich(bom, configViper)
110110
if err != nil {
111111
return fmt.Errorf("enrichment failed: %w", err)
112112
}
@@ -150,7 +150,7 @@ func init() {
150150
enrichCmd.Flags().StringVar(&enrichSpecVersion, "spec", "", "CycloneDX spec version for output (default: same as input)")
151151

152152
enrichCmd.Flags().StringVar(&enrichStrategy, "strategy", "interactive", "Enrichment strategy: interactive|file")
153-
enrichCmd.Flags().StringVar(&enrichConfigFile, "file", "/config/enrichment.yaml", "Path to enrichment config file (YAML)")
153+
enrichCmd.Flags().StringVar(&enrichConfigFile, "file", "./config/enrichment.yaml", "Path to enrichment config file (YAML)")
154154
enrichCmd.Flags().BoolVar(&enrichRequiredOnly, "required-only", false, "Only prompt for required fields")
155155
enrichCmd.Flags().Float64Var(&enrichMinWeight, "min-weight", 0.0, "Only prompt for fields with weight >= this value")
156156
enrichCmd.Flags().BoolVar(&enrichRefetch, "refetch", false, "Refetch model metadata from Hugging Face before enrichment")
@@ -165,7 +165,7 @@ func init() {
165165
}
166166

167167
// loadEnrichmentConfig loads enrichment values from a YAML config file
168-
func loadEnrichmentConfig(path string) (map[string]interface{}, error) {
168+
func loadEnrichmentConfig(path string) (*viper.Viper, error) {
169169
v := viper.New()
170170
v.SetConfigFile(path)
171171
v.SetConfigType("yaml")
@@ -174,5 +174,5 @@ func loadEnrichmentConfig(path string) (map[string]interface{}, error) {
174174
return nil, err
175175
}
176176

177-
return v.AllSettings(), nil
177+
return v, nil
178178
}

config/enrichment.yaml

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,27 @@
1-
# Component-level fields
2-
licenses: Apache-2.0
3-
tags:
1+
# AIBoMGen Enrichment Configuration
2+
# This file provides values to enrich an existing AIBOM when using --strategy file
3+
#
4+
# IMPORTANT: Use the full BOM path as the key to avoid ambiguity
5+
# Keys are case-insensitive (they are automatically lowercased during processing)
6+
7+
BOM.metadata.component.name: "my-model-name"
8+
BOM.metadata.component.licenses: "Apache-2.0"
9+
BOM.metadata.component.manufacturer: "Google"
10+
BOM.metadata.component.group: "google"
11+
BOM.metadata.component.tags:
412
- nlp
513
- transformer
614
- bert
7-
manufacturer: Google
8-
group: google
9-
10-
# Model card fields
11-
task: text-classification
12-
architectureFamily: transformer
13-
modelArchitecture: bert-base
14-
15-
# Hugging Face properties
16-
language: en
17-
libraryName: transformers
1815

19-
# Model card sections
20-
useCases: "Sentiment analysis, text classification"
21-
technicalLimitations: "Limited to English text, max 512 tokens"
22-
ethicalConsiderations: "May reflect biases present in training data"
16+
BOM.metadata.component.properties.huggingface:language: "en"
17+
BOM.metadata.component.properties.huggingface:libraryName: "transformers"
18+
BOM.metadata.component.properties.huggingface:baseModel: "distilbert-base-uncased"
19+
BOM.metadata.component.properties.huggingface:modelCardContact: "[email protected]"
2320

24-
# External references
25-
paperUrl: https://arxiv.org/abs/1810.04805
26-
demoUrl: https://huggingface.co/spaces/demo
21+
BOM.metadata.component.modelCard.modelParameters.task: "text-classification"
22+
BOM.metadata.component.modelCard.modelParameters.architectureFamily: "transformer"
23+
BOM.metadata.component.modelCard.modelParameters.modelArchitecture: "bert-base"
2724

28-
# Environmental impact
29-
hardwareType: "NVIDIA V100"
30-
hoursUsed: "96"
31-
cloudProvider: "Google Cloud"
32-
computeRegion: "us-central1"
25+
BOM.metadata.component.modelCard.considerations.useCases: "Text generation, dialogue systems, creative writing"
26+
BOM.metadata.component.modelCard.considerations.technicalLimitations: "Limited context window of 512 tokens, may generate inconsistent or factually incorrect text"
27+
BOM.metadata.component.modelCard.considerations.ethicalConsiderations: "May generate biased or harmful content based on training data. Should not be used for sensitive applications without additional safeguards."

internal/enricher/enricher.go

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func New(opts Options) *Enricher {
5454
}
5555

5656
// Enrich enriches a BOM with additional metadata
57-
func (e *Enricher) Enrich(bom *cdx.BOM, fileValues map[string]interface{}) (*cdx.BOM, error) {
57+
func (e *Enricher) Enrich(bom *cdx.BOM, configViper interface{}) (*cdx.BOM, error) {
5858
if bom == nil {
5959
return nil, fmt.Errorf("nil BOM")
6060
}
@@ -111,7 +111,7 @@ func (e *Enricher) Enrich(bom *cdx.BOM, fileValues map[string]interface{}) (*cdx
111111

112112
switch e.config.Strategy {
113113
case "file":
114-
value, err = e.getValueFromFile(spec, fileValues)
114+
value, err = e.getValueFromFile(spec, configViper)
115115
case "interactive":
116116
value, err = e.getValueInteractive(spec, src, tgt.BOM)
117117
default:
@@ -209,12 +209,27 @@ func (e *Enricher) refetchMetadata(modelID string) (*fetcher.ModelAPIResponse, *
209209
}
210210

211211
// getValueFromFile extracts a value from the config file
212-
func (e *Enricher) getValueFromFile(spec metadata.FieldSpec, values map[string]interface{}) (interface{}, error) {
213-
// Map field spec key to config file key
214-
// e.g. "BOM.metadata.component.licenses" -> "licenses"
215-
key := configKeyFromSpec(spec.Key)
212+
func (e *Enricher) getValueFromFile(spec metadata.FieldSpec, configViper interface{}) (interface{}, error) {
213+
// If no config provided, return nil
214+
if configViper == nil {
215+
return nil, nil
216+
}
217+
218+
// Type assert to viper.Viper
219+
type viperGetter interface {
220+
Get(key string) interface{}
221+
}
216222

217-
if val, ok := values[key]; ok {
223+
v, ok := configViper.(viperGetter)
224+
if !ok {
225+
return nil, fmt.Errorf("invalid config type")
226+
}
227+
228+
// Use the full key - viper will handle the nested lookup and lowercasing
229+
key := string(spec.Key)
230+
val := v.Get(key)
231+
232+
if val != nil {
218233
logf("", "loaded %s from config file", spec.Key)
219234
return val, nil
220235
}
@@ -349,11 +364,8 @@ func bomModelCard(bom *cdx.BOM) *cdx.MLModelCard {
349364
}
350365

351366
func configKeyFromSpec(key metadata.Key) string {
352-
// Convert "BOM.metadata.component.licenses" to "licenses"
353-
parts := strings.Split(string(key), ".")
354-
if len(parts) > 0 {
355-
return parts[len(parts)-1]
356-
}
367+
// Use the full key to avoid ambiguity
368+
// e.g. "BOM.metadata.component.properties.huggingface:baseModel" stays as-is
357369
return string(key)
358370
}
359371

0 commit comments

Comments
 (0)