package promptkit import ( "context" "fmt" internalengine "gitea.maximumdirect.net/eric/promptkit/internal/engine" ) // NamespaceFilterMode selects namespace membership for an identity listing. type NamespaceFilterMode uint8 const ( // NamespaceDefault uses the engine view's scope, or all IDs if it is unset. NamespaceDefault NamespaceFilterMode = iota // NamespaceNamed selects IDs whose exact namespace matches Name. NamespaceNamed // NamespaceUnqualified selects IDs without a namespace. NamespaceUnqualified // NamespaceAll selects IDs from every namespace, overriding the view scope. NamespaceAll ) // NamespaceFilter optionally overrides the namespace used for one listing. // NamespaceNamed requires a nonempty valid namespace in Name; other modes // ignore Name. type NamespaceFilter struct { Mode NamespaceFilterMode Name string } // ResourceKind selects one identity type for [Engine.ListNamespaces]. type ResourceKind string const ( // ResourcePrompt selects namespaces containing prompt IDs. ResourcePrompt ResourceKind = "prompt" // ResourceProfile selects namespaces containing profile IDs. ResourceProfile ResourceKind = "profile" // ResourceBackend selects namespaces containing backend IDs. ResourceBackend ResourceKind = "backend" ) // CollectionInfo describes one effective collections declaration. type CollectionInfo struct { // Namespace is the exact collection key. Namespace string // ReleaseVersion is nil when absent or null and points to the exact value // when present, including an explicitly empty string. ReleaseVersion *string } // PromptIdentity identifies one prompt ID and its optional descriptive version // label in the effective configuration captured when an Engine is constructed. // Version is metadata only and never participates in prompt selection. A nil // Version means no label was declared; a non-nil empty string is an explicit // label. It has no stable JSON representation. type PromptIdentity struct { ID string Version *string } // ListProfiles returns profile IDs in the effective configuration captured // when the engine was constructed. Maintained and consumer profiles are // included; an inherited profile retains its own ID. IDs are preserved exactly // and sorted lexically. An omitted filter or NamespaceDefault uses the view's // profile namespace, or all profiles when unset. NamespaceNamed selects the // exact named namespace, NamespaceUnqualified selects IDs without a slash, and // NamespaceAll selects every profile. An explicit filter replaces the view's // default for this call. At most one filter is accepted. // // The returned slice is caller-owned and safe to mutate while the engine is // used concurrently. Empty inventories succeed. // // Listing reports membership, not execution readiness. It does not reload // configuration or perform resource, credential, provider, or execution work. // Profile IDs are application data and may be sensitive. A nil or zero-value // engine returns an error matching ErrInvalidConfig. Cancellation or deadline // expiration returns the context error and no partial result. Multiple // filters, an unknown mode, or an invalid named namespace returns an error // matching ErrInvalidRequest. func (e *Engine) ListProfiles(ctx context.Context, filters ...NamespaceFilter) ([]string, error) { if e == nil || e.internal == nil { return nil, fmt.Errorf("%w: engine is nil", ErrInvalidConfig) } profiles, err := e.internal.ListProfiles(ctx, internalListingFilters(filters)...) if err != nil { return nil, mapPublicError(err) } return profiles, nil } // ListPrompts returns prompt IDs in the effective configuration captured when // the engine was constructed, sorted lexically by ID. Each identity may include // its descriptive version label. Its optional filter follows the same // NamespaceDefault, NamespaceNamed, NamespaceUnqualified, and NamespaceAll // behavior as [Engine.ListProfiles], using the prompt namespace by default. // At most one filter is accepted. // // The returned slice and each Version pointer are caller-owned and safe to // mutate while the engine is used concurrently. Empty inventories succeed. // // Listing reports membership, not execution readiness. It does not reload // configuration or perform resource, credential, provider, or execution work. // Prompt identities are application data and may be sensitive. A nil or // zero-value engine returns an error matching ErrInvalidConfig. Cancellation // or deadline expiration returns the context error and no partial result. // Multiple filters, an unknown mode, or an invalid named namespace returns an // error matching ErrInvalidRequest. func (e *Engine) ListPrompts(ctx context.Context, filters ...NamespaceFilter) ([]PromptIdentity, error) { if e == nil || e.internal == nil { return nil, fmt.Errorf("%w: engine is nil", ErrInvalidConfig) } identities, err := e.internal.ListPrompts(ctx, internalListingFilters(filters)...) if err != nil { return nil, mapPublicError(err) } public := make([]PromptIdentity, len(identities)) for i, identity := range identities { public[i] = PromptIdentity{ID: identity.ID, Version: copyStringPtr(identity.Version)} } if err := ctx.Err(); err != nil { return nil, mapPublicError(err) } return public, nil } // ListBackends returns backend IDs in the effective configuration captured // when the engine was constructed, sorted lexically by full ID. Its optional // filter follows the same namespace behavior as [Engine.ListProfiles], using // the backend namespace by default. At most one filter is accepted. The // returned slice is caller-owned and safe to mutate while the engine is used // concurrently. Empty inventories succeed. // // Listing reports membership, not execution readiness, and performs no // provider or execution work. A nil or zero-value engine returns an error // matching ErrInvalidConfig. Cancellation or deadline expiration returns the // context error and no partial result. Multiple filters, an unknown mode, or // an invalid named namespace returns an error matching ErrInvalidRequest. func (e *Engine) ListBackends(ctx context.Context, filters ...NamespaceFilter) ([]string, error) { if e == nil || e.internal == nil { return nil, fmt.Errorf("%w: engine is nil", ErrInvalidConfig) } backends, err := e.internal.ListBackends(ctx, internalListingFilters(filters)...) if err != nil { return nil, mapPublicError(err) } return backends, nil } func internalListingFilters(filters []NamespaceFilter) []internalengine.ListingFilter { if filters == nil { return nil } internal := make([]internalengine.ListingFilter, len(filters)) for index, filter := range filters { internal[index] = internalengine.ListingFilter{ Mode: internalengine.ListingFilterMode(filter.Mode), Name: filter.Name, } } return internal } // ListNamespaces returns the sorted, distinct namespaces that contain the // selected resource kinds. With no kinds, it returns the union across prompts, // profiles, and backends. Repeated kinds have no effect; an unknown kind // returns an error matching ErrInvalidRequest. The inventory is global and is // not restricted by this engine view's scopes. Unqualified resource IDs do // not contribute a namespace. // // The returned slice is caller-owned. A nil or zero-value engine returns an // error matching ErrInvalidConfig. Cancellation or deadline expiration // returns the context error and no partial result. func (e *Engine) ListNamespaces(ctx context.Context, kinds ...ResourceKind) ([]string, error) { if e == nil || e.internal == nil { return nil, fmt.Errorf("%w: engine is nil", ErrInvalidConfig) } internalKinds := make([]string, len(kinds)) for index, kind := range kinds { internalKinds[index] = string(kind) } namespaces, err := e.internal.ListNamespaces(ctx, internalKinds) if err != nil { return nil, mapPublicError(err) } return namespaces, nil } // ListCollections returns the sorted collection declarations from the // effective configuration captured when the engine was constructed. It // includes metadata-only and resource-only declarations and is independent of // namespace view scopes. It does not infer collections from resource IDs or // configuration source history. // // The returned slice and each ReleaseVersion pointer are caller-owned. Nil // means the label was absent or null; a non-nil empty string is preserved. A // nil or zero-value engine returns an error matching ErrInvalidConfig. // Cancellation or deadline expiration returns the context error and no partial // result. func (e *Engine) ListCollections(ctx context.Context) ([]CollectionInfo, error) { if e == nil || e.internal == nil { return nil, fmt.Errorf("%w: engine is nil", ErrInvalidConfig) } collections, err := e.internal.ListCollections(ctx) if err != nil { return nil, mapPublicError(err) } public := make([]CollectionInfo, len(collections)) for index, collection := range collections { public[index] = CollectionInfo{ Namespace: collection.Namespace, ReleaseVersion: copyStringPtr(collection.ReleaseVersion), } } if err := ctx.Err(); err != nil { return nil, mapPublicError(err) } return public, nil }