Adopt PromptKit layered configuration sources
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user