Adopt PromptKit layered configuration sources

This commit is contained in:
2026-10-04 00:33:31 +00:00
parent 43a68e97ee
commit f5fa607c35
5 changed files with 66 additions and 55 deletions

View File

@@ -4,15 +4,12 @@ import (
"context"
"errors"
"fmt"
"io/fs"
"net/http"
"os"
"path/filepath"
"sort"
"strings"
"sync"
"sync/atomic"
ck "gitea.maximumdirect.net/eric/ConfigKit"
"gitea.maximumdirect.net/eric/audita/internal/core/config"
"gitea.maximumdirect.net/eric/audita/internal/core/redaction"
"gitea.maximumdirect.net/eric/audita/internal/framework/responseschema"
@@ -43,22 +40,36 @@ func NewRuntime(ctx context.Context, c config.Config, mainPath string, client *h
if !filepath.IsAbs(profileDir) {
profileDir = filepath.Join(filepath.Dir(mainPath), profileDir)
}
profileFS := &profileSourceFS{FS: os.DirFS(profileDir), paths: make(map[string]struct{})}
options := []pk.Option{
pk.WithPromptFS(prompts.Filesystem(), prompts.AssetRoot),
pk.WithSchemaFS(responseschema.Filesystem(), responseschema.AssetRoot),
pk.WithProfileFS(profileFS, "."),
promptInputs, err := pk.FilesystemInputs("Audita embedded prompts", prompts.Filesystem(), prompts.AssetRoot)
if err != nil {
return nil, fmt.Errorf("discover embedded prompts: %w", err)
}
ids := make([]string, 0, len(c.Backends))
profileInputs, err := pk.DirectoryInputs(profileDir)
if err != nil {
return nil, fmt.Errorf("discover operator definitions: %w", err)
}
applicationBackends := make(map[string]any, len(c.Backends))
for id := range c.Backends {
ids = append(ids, id)
}
sort.Strings(ids)
for _, id := range ids {
backend := c.Backends[id]
options = append(options, pk.WithBackend(pk.Backend{ID: id, Endpoint: backend.Endpoint, ConcurrencyLimit: backend.Concurrency, QueueCapacity: &backend.QueueCapacity}))
applicationBackends[id] = map[string]any{
"endpoint": backend.Endpoint,
"concurrency_limit": backend.Concurrency,
"queue_capacity": backend.QueueCapacity,
"api_key_env": nil,
}
}
engine, err := pk.NewEngine(pk.Config{HTTPClient: client, TransientRetries: &c.LLM.TransientRetries}, options...)
sources := pk.ConfigurationSources{
Underlays: []ck.Underlay{
{Inputs: promptInputs},
{Inputs: profileInputs},
},
Main: &ck.Input{Source: ck.Mapping("application backends", map[string]any{"backends": applicationBackends})},
}
engine, err := pk.NewEngine(pk.Config{
Sources: sources,
HTTPClient: client,
TransientRetries: &c.LLM.TransientRetries,
}, pk.WithSchemaFS(responseschema.Filesystem(), responseschema.AssetRoot))
if err != nil {
return nil, fmt.Errorf("assemble LLM runtime: %w", err)
}
@@ -78,6 +89,12 @@ func NewRuntime(ctx context.Context, c config.Config, mainPath string, client *h
if !ok {
return nil, fmt.Errorf("%s profile must select a registered application backend", selection.role)
}
if strings.TrimSpace(target.Model) == "" {
return nil, fmt.Errorf("%s profile must select a nonblank model", selection.role)
}
if strings.TrimSpace(target.Endpoint) == "" {
return nil, fmt.Errorf("%s profile must resolve to a nonblank endpoint", selection.role)
}
endpoint := func(value string) string { return strings.TrimRight(strings.TrimSpace(value), "/") }
if endpoint(target.Endpoint) != endpoint(backend.Endpoint) {
return nil, fmt.Errorf("%s profile endpoint must match its registered backend", selection.role)
@@ -90,12 +107,6 @@ func NewRuntime(ctx context.Context, c config.Config, mainPath string, client *h
}
runtime.Profiles[selection.role] = *profile
}
profileFS.mu.Lock()
for name := range profileFS.paths {
runtime.ProfilePaths = append(runtime.ProfilePaths, filepath.Join(profileDir, filepath.FromSlash(name)))
}
profileFS.mu.Unlock()
sort.Strings(runtime.ProfilePaths)
return runtime, nil
}
@@ -109,21 +120,3 @@ func RedactError(err error, c config.Config) string {
}
return redaction.New(ConfiguredSecrets(&c)).String(err.Error())
}
// Record successful profile reads at the library boundary, including inherited
// and differently named sources, without duplicating PromptKit's discovery rules.
type profileSourceFS struct {
fs.FS
mu sync.Mutex
paths map[string]struct{}
}
func (f *profileSourceFS) ReadFile(name string) ([]byte, error) {
data, err := fs.ReadFile(f.FS, name)
if err == nil {
f.mu.Lock()
f.paths[name] = struct{}{}
f.mu.Unlock()
}
return data, err
}

View File

@@ -5,15 +5,28 @@ import (
"strings"
"testing"
ck "gitea.maximumdirect.net/eric/ConfigKit"
"gitea.maximumdirect.net/eric/audita/internal/framework/responseschema"
pk "gitea.maximumdirect.net/eric/promptkit"
)
func TestEmbeddedDefinitionsPrepareThroughPromptKit(t *testing.T) {
engine, err := pk.NewEngine(pk.Config{},
pk.WithPromptFS(Filesystem(), AssetRoot),
promptInputs, err := pk.FilesystemInputs("embedded prompts", Filesystem(), AssetRoot)
if err != nil {
t.Fatal(err)
}
endpoint := "https://example.invalid/v1"
model := "fixture-model"
sources := pk.ConfigurationSources{
Underlays: []ck.Underlay{
{Inputs: promptInputs},
{Inputs: []ck.Input{{Source: pk.ProfileSource("registry fixture profile", map[string]pk.ProfileConfig{
"fixture": {Endpoint: &endpoint, Model: &model},
})}}},
},
}
engine, err := pk.NewEngine(pk.Config{Sources: sources},
pk.WithSchemaFS(responseschema.Filesystem(), responseschema.AssetRoot),
pk.WithProfiles(pk.Profile{ID: "fixture", Endpoint: "https://example.invalid/v1", Model: "fixture-model"}),
)
if err != nil {
t.Fatal(err)