Configure PromptKit transient retries
This commit is contained in:
@@ -14,9 +14,10 @@ type Config struct {
|
||||
}
|
||||
|
||||
type PromptKitConfig struct {
|
||||
ProfileDir string `json:"profile_dir,omitempty"`
|
||||
ProfileFile string `json:"profile_file,omitempty"`
|
||||
LocalBackend *PromptKitLocalBackendConfig `json:"local_backend,omitempty"`
|
||||
ProfileDir string `json:"profile_dir,omitempty"`
|
||||
ProfileFile string `json:"profile_file,omitempty"`
|
||||
TransientRetries int `json:"transient_retries"`
|
||||
LocalBackend *PromptKitLocalBackendConfig `json:"local_backend,omitempty"`
|
||||
}
|
||||
|
||||
type PromptKitLocalBackendConfig struct {
|
||||
@@ -29,7 +30,10 @@ type ConcurrencyConfig struct {
|
||||
StageWorkers map[string]int `json:"stage_workers"`
|
||||
}
|
||||
|
||||
const defaultLLMConcurrency = 16
|
||||
const (
|
||||
defaultLLMConcurrency = 16
|
||||
defaultTransientRetries = 2
|
||||
)
|
||||
|
||||
type OutputConfig struct {
|
||||
Directory string `json:"directory"`
|
||||
@@ -55,6 +59,7 @@ type DebugConfig struct {
|
||||
|
||||
func Default() Config {
|
||||
return Config{
|
||||
PromptKit: PromptKitConfig{TransientRetries: defaultTransientRetries},
|
||||
Pipelines: map[string]pipeline.PipelineProfile{},
|
||||
Concurrency: ConcurrencyConfig{
|
||||
TotalLLM: defaultLLMConcurrency,
|
||||
|
||||
@@ -16,9 +16,10 @@ type Document struct {
|
||||
}
|
||||
|
||||
type PromptKitDocument struct {
|
||||
ProfileDir *string `yaml:"profile_dir,omitempty"`
|
||||
ProfileFile *string `yaml:"profile_file,omitempty"`
|
||||
LocalBackend *PromptKitLocalBackendDocument `yaml:"local_backend,omitempty"`
|
||||
ProfileDir *string `yaml:"profile_dir,omitempty"`
|
||||
ProfileFile *string `yaml:"profile_file,omitempty"`
|
||||
TransientRetries *int `yaml:"transient_retries,omitempty"`
|
||||
LocalBackend *PromptKitLocalBackendDocument `yaml:"local_backend,omitempty"`
|
||||
}
|
||||
|
||||
type PromptKitLocalBackendDocument struct {
|
||||
|
||||
@@ -63,6 +63,9 @@ func documentDefinition(state *documentLoadState) configkit.Definition[Document]
|
||||
|
||||
func documentDefaults() Document {
|
||||
return Document{
|
||||
PromptKit: &PromptKitDocument{
|
||||
TransientRetries: documentValue(defaultTransientRetries),
|
||||
},
|
||||
Pipelines: map[string]PipelineDocument{},
|
||||
Concurrency: &ConcurrencyDocument{
|
||||
TotalLLM: documentValue(defaultLLMConcurrency),
|
||||
|
||||
@@ -94,6 +94,9 @@ func documentToConfig(document Document) (Config, error) {
|
||||
if document.PromptKit.ProfileFile != nil {
|
||||
config.PromptKit.ProfileFile = *document.PromptKit.ProfileFile
|
||||
}
|
||||
if document.PromptKit.TransientRetries != nil {
|
||||
config.PromptKit.TransientRetries = *document.PromptKit.TransientRetries
|
||||
}
|
||||
if document.PromptKit.LocalBackend != nil {
|
||||
backend := PromptKitLocalBackendConfig{}
|
||||
if document.PromptKit.LocalBackend.Endpoint != nil {
|
||||
|
||||
@@ -2,6 +2,7 @@ package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -58,6 +59,44 @@ pipelines:
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadedConfigResolvesPromptKitTransientRetriesAndRetainsInspection(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
document string
|
||||
want int
|
||||
}{
|
||||
{name: "omitted", document: "version: 5\n", want: defaultTransientRetries},
|
||||
{name: "disabled", document: "version: 5\npromptkit:\n transient_retries: 0\n", want: 0},
|
||||
{name: "configured", document: "version: 5\npromptkit:\n transient_retries: 5\n", want: 5},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
loaded, err := loadConfigSource(configkit.Bytes("document.yml", []byte(test.document)), LoadOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := loaded.Config.PromptKit.TransientRetries; got != test.want {
|
||||
t.Fatalf("transient retries = %d, want %d", got, test.want)
|
||||
}
|
||||
var effective bytes.Buffer
|
||||
if err := loaded.WriteEffective(&effective); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(effective.String(), "transient_retries: "+strconv.Itoa(test.want)) {
|
||||
t.Fatalf("effective configuration = %q", effective.String())
|
||||
}
|
||||
var explanation bytes.Buffer
|
||||
if err := loaded.WriteExplanation(&explanation, "/promptkit/transient_retries"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(explanation.String(), strconv.Itoa(test.want)) {
|
||||
t.Fatalf("explanation = %q", explanation.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadedConfigOwnsRuntimeValuesAndKeepsFrozenInspection(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -167,6 +167,11 @@ func validateDocumentPromptKit(promptKit *PromptKitDocument, validation *configk
|
||||
if err := validatePromptKitProfileSources(profileDir, profileFile); err != nil {
|
||||
validation.Error(configkit.Keys("promptkit"), err.Error())
|
||||
}
|
||||
if promptKit.TransientRetries == nil {
|
||||
validation.Error(configkit.Keys("promptkit", "transient_retries"), "must be an integer")
|
||||
} else if err := validatePromptKitTransientRetries(*promptKit.TransientRetries); err != nil {
|
||||
validation.Error(configkit.Keys("promptkit", "transient_retries"), err.Error())
|
||||
}
|
||||
if promptKit.LocalBackend == nil {
|
||||
return
|
||||
}
|
||||
@@ -283,6 +288,13 @@ func validatePromptKitLocalBackendConcurrencyLimit(concurrencyLimit int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validatePromptKitTransientRetries(transientRetries int) error {
|
||||
if transientRetries < 0 {
|
||||
return fmt.Errorf("promptkit.transient_retries must not be negative")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateRequiredPath(name, value string) error {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
return fmt.Errorf("%s must not be empty", name)
|
||||
|
||||
@@ -223,6 +223,13 @@ cache:
|
||||
assertDocumentDiagnostic(t, err, configkit.CodeValidateError, "/promptkit/local_backend/endpoint")
|
||||
}
|
||||
|
||||
func TestDocumentLoadRejectsNegativePromptKitTransientRetries(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := loadDocumentSource(configkit.Bytes("promptkit.yml", []byte("version: 5\npromptkit:\n transient_retries: -1\n")), LoadOptions{})
|
||||
assertDocumentDiagnostic(t, err, configkit.CodeValidateError, "/promptkit/transient_retries")
|
||||
}
|
||||
|
||||
func assertDocumentDiagnostic(t *testing.T, err error, code configkit.Code, path string) {
|
||||
t.Helper()
|
||||
var loadError *configkit.LoadError
|
||||
|
||||
@@ -46,6 +46,9 @@ func validatePromptKit(cfg PromptKitConfig) error {
|
||||
if err := validatePromptKitProfileSources(cfg.ProfileDir, cfg.ProfileFile); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validatePromptKitTransientRetries(cfg.TransientRetries); err != nil {
|
||||
return err
|
||||
}
|
||||
if cfg.LocalBackend == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -89,6 +89,22 @@ func TestValidatePromptKitSourcesAreMutuallyExclusive(t *testing.T) {
|
||||
assertValidationContains(t, cfg, "promptkit profile_dir and profile_file are mutually exclusive")
|
||||
}
|
||||
|
||||
func TestValidatePromptKitTransientRetries(t *testing.T) {
|
||||
if got := Default().PromptKit.TransientRetries; got != defaultTransientRetries {
|
||||
t.Fatalf("default transient retries = %d, want %d", got, defaultTransientRetries)
|
||||
}
|
||||
for _, retries := range []int{0, 4} {
|
||||
cfg := Default()
|
||||
cfg.PromptKit.TransientRetries = retries
|
||||
if err := cfg.Validate(); err != nil {
|
||||
t.Fatalf("Validate() with transient retries %d error = %v", retries, err)
|
||||
}
|
||||
}
|
||||
negative := Default()
|
||||
negative.PromptKit.TransientRetries = -1
|
||||
assertValidationContains(t, negative, "promptkit.transient_retries must not be negative")
|
||||
}
|
||||
|
||||
func TestValidatePromptKitLocalBackendEndpoints(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
Reference in New Issue
Block a user