Add scoped identity listings and backend inventory

This commit is contained in:
2026-10-17 06:22:22 +00:00
parent 15f79ce7b1
commit c9b623b374
10 changed files with 438 additions and 21 deletions

View File

@@ -3,8 +3,32 @@ 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
}
// PromptIdentity identifies one prompt ID and its optional descriptive version
// label in the effective configuration captured when an Engine is constructed.
// A nil Version means no label was declared; a non-nil empty string is an
@@ -17,43 +41,57 @@ type PromptIdentity struct {
// 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. The returned slice is caller-owned and safe to mutate
// while the engine is used concurrently. Empty inventories succeed.
// 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.
func (e *Engine) ListProfiles(ctx context.Context) ([]string, error) {
// 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)
profiles, err := e.internal.ListProfiles(ctx, internalListingFilters(filters)...)
if err != nil {
return nil, mapPublicError(err)
}
return profiles, nil
}
// ListPrompts returns every prompt ID in the effective configuration captured
// when the engine was constructed, sorted lexically by ID. Each identity may
// include its descriptive version label. The returned slice and each Version
// pointer are caller-owned and safe to mutate while the engine is used
// concurrently. Empty inventories succeed.
// 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.
func (e *Engine) ListPrompts(ctx context.Context) ([]PromptIdentity, error) {
// 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)
identities, err := e.internal.ListPrompts(ctx, internalListingFilters(filters)...)
if err != nil {
return nil, mapPublicError(err)
}
@@ -66,3 +104,40 @@ func (e *Engine) ListPrompts(ctx context.Context) ([]PromptIdentity, error) {
}
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
}