package consumers import ( "errors" "fmt" "io/fs" "path" configkit "gitea.maximumdirect.net/eric/ConfigKit" ) // CollectionBundleConfig is a representative application-owned collection // shape. Collection and prompt version labels remain ordinary domain fields. type CollectionBundleConfig struct { Version string `yaml:"version"` Collections map[string]CollectionDefinition `yaml:"collections"` } type CollectionDefinition struct { Name string `yaml:"name"` Version string `yaml:"version"` Description string `yaml:"description"` Prompts map[string]CollectionPrompt `yaml:"prompts"` Profiles map[string]CollectionProfile `yaml:"profiles"` } type CollectionPrompt struct { Version string `yaml:"version"` DefaultProfile string `yaml:"default_profile"` ContentFile string `yaml:"content_file"` ResourceText string `yaml:"resource_text"` Messages []CollectionMessage `yaml:"messages"` } type CollectionMessage struct { Role string `yaml:"role"` Content string `yaml:"content"` } type CollectionProfile struct { Format string `yaml:"format"` Temperature float64 `yaml:"temperature"` } // LoadCollectionBundle demonstrates one layered collection load. Its main // document owns the application schema version; the mounted entry declares // relative destinations for collection metadata, prompt/profile peers, and // their nested imports. The operator version override remains a separate tier. func LoadCollectionBundle() (*configkit.Result[CollectionBundleConfig], error) { files, err := fs.Sub(bundledFiles, "testdata/collection-bundle") if err != nil { return nil, err } bundle := configkit.NewFilesystem("collection bundle", files) collectionDestination := configkit.Keys("collections", "weatherreporter") promptDestination := collectionDestination.Key("prompts") return configkit.LoadLayered( configkit.Input{Source: configkit.Bytes("application main", []byte("version: 1\n"))}, configkit.LayeredOptions[CollectionBundleConfig]{ Definition: configkit.Definition[CollectionBundleConfig]{ Version: &configkit.VersionRule{Path: configkit.Keys("version"), Check: func(version configkit.Version, _ *configkit.Validation) error { if !version.Present || version.Text != "1" { return errors.New("unsupported application schema version") } return nil }}, Resolve: resolveCollectionResources, Validate: validateCollectionBundle, }, Underlays: []configkit.Underlay{ {Inputs: []configkit.Input{{ Source: bundle.File("collections/weather/collection.yml"), Imports: true, Destination: collectionDestination, }}}, {Inputs: []configkit.Input{{ Source: bundle.File("operators/weather-prompt-version.yml"), Destination: promptDestination, }}}, }, }, ) } func resolveCollectionResources(configuration *CollectionBundleConfig, resolution *configkit.Resolution) error { for collectionID, collection := range configuration.Collections { for promptID, prompt := range collection.Prompts { if prompt.ContentFile == "" { continue } contentPath := configkit.Keys("collections", collectionID, "prompts", promptID, "content_file") info, found := resolution.InputInfo(contentPath) if !found || len(info.Origin.Locations) != 1 { return fmt.Errorf("prompt resource has no single declaring source") } resourceSource, found := resolution.Source(info.Origin.Locations[0].SourceID) if !found { return fmt.Errorf("prompt resource source is unavailable") } context, found := resolution.SourceContext(resourceSource.ID) if !found { return fmt.Errorf("prompt resource context is unavailable") } data, err := fs.ReadFile(context.FS, path.Join(context.BaseDir, prompt.ContentFile)) if err != nil { return fmt.Errorf("read prompt resource: %w", err) } prompt.ResourceText = string(data) collection.Prompts[promptID] = prompt resolution.Record( configkit.Keys("collections", collectionID, "prompts", promptID, "resource_text"), configkit.ChangeNormalization, "read source-relative prompt content", contentPath, ) } configuration.Collections[collectionID] = collection } return nil } func validateCollectionBundle(configuration *CollectionBundleConfig, validation *configkit.Validation) { for collectionID, collection := range configuration.Collections { for promptID, prompt := range collection.Prompts { if prompt.DefaultProfile == "" { continue } if _, found := collection.Profiles[prompt.DefaultProfile]; !found { validation.Error(configkit.Keys("collections", collectionID, "prompts", promptID, "default_profile"), "must name a profile in this collection") } } } } // ReadCollectionPromptContent follows the effective content field's physical // source, including when it was inherited from a peer prompt. func ReadCollectionPromptContent(result *configkit.Result[CollectionBundleConfig], collectionID, promptID string) (string, error) { if result == nil { return "", errors.New("collection result is nil") } contentPath := configkit.Keys("collections", collectionID, "prompts", promptID, "content_file") info, found := result.Metadata().Lookup(contentPath) if !found || len(info.Origin.Locations) != 1 { return "", errors.New("prompt resource has no single declaring source") } context, found := result.SourceContext(info.Origin.Locations[0].SourceID) if !found { return "", errors.New("prompt resource context is unavailable") } prompt, found := result.Config.Collections[collectionID].Prompts[promptID] if !found { return "", errors.New("prompt is unavailable") } data, err := fs.ReadFile(context.FS, path.Join(context.BaseDir, prompt.ContentFile)) if err != nil { return "", err } return string(data), nil }