Configure PromptKit transient retries
This commit is contained in:
@@ -26,15 +26,16 @@ type PromptKitLocalBackendConfig struct {
|
||||
}
|
||||
|
||||
type PromptKitClientConfig struct {
|
||||
ProfileDir string
|
||||
ProfileFile string
|
||||
LocalBackend *PromptKitLocalBackendConfig
|
||||
Assets *AssetRegistry
|
||||
Timeout time.Duration
|
||||
HTTPClient *http.Client
|
||||
EngineOptions []promptkit.Option
|
||||
Recorder *LLMProfileRecorder
|
||||
ReasoningEffort *string
|
||||
ProfileDir string
|
||||
ProfileFile string
|
||||
TransientRetries *int
|
||||
LocalBackend *PromptKitLocalBackendConfig
|
||||
Assets *AssetRegistry
|
||||
Timeout time.Duration
|
||||
HTTPClient *http.Client
|
||||
EngineOptions []promptkit.Option
|
||||
Recorder *LLMProfileRecorder
|
||||
ReasoningEffort *string
|
||||
}
|
||||
|
||||
type PromptKitClient struct {
|
||||
@@ -79,9 +80,10 @@ func NewPromptKitClient(cfg PromptKitClientConfig) (*PromptKitClient, error) {
|
||||
options = append(options, cfg.EngineOptions...)
|
||||
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{
|
||||
ProfileDir: profileSource.ProfileDir,
|
||||
Timeout: cfg.Timeout,
|
||||
HTTPClient: cfg.HTTPClient,
|
||||
ProfileDir: profileSource.ProfileDir,
|
||||
Timeout: cfg.Timeout,
|
||||
HTTPClient: cfg.HTTPClient,
|
||||
TransientRetries: cfg.TransientRetries,
|
||||
}, options...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create PromptKit engine: %w", err)
|
||||
|
||||
@@ -971,6 +971,70 @@ model: local-model
|
||||
}
|
||||
}
|
||||
|
||||
func TestPromptKitClientAppliesConfiguredBuiltInTransientRetries(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
retries int
|
||||
statuses []int
|
||||
wantCalls int32
|
||||
wantErr bool
|
||||
}{
|
||||
{name: "eligible failure then success", retries: 1, statuses: []int{http.StatusServiceUnavailable, http.StatusOK}, wantCalls: 2},
|
||||
{name: "disabled", retries: 0, statuses: []int{http.StatusServiceUnavailable}, wantCalls: 1, wantErr: true},
|
||||
{name: "exhausted", retries: 1, statuses: []int{http.StatusServiceUnavailable, http.StatusServiceUnavailable}, wantCalls: 2, wantErr: true},
|
||||
{name: "noneligible client error", retries: 2, statuses: []int{http.StatusBadRequest}, wantCalls: 1, wantErr: true},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
var calls atomic.Int32
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
index := calls.Add(1) - 1
|
||||
if int(index) >= len(test.statuses) {
|
||||
t.Errorf("unexpected HTTP attempt %d", index+1)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
status := test.statuses[index]
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if status != http.StatusOK {
|
||||
w.WriteHeader(status)
|
||||
_, _ = w.Write([]byte(`{"error":{"message":"provider failure"}}`))
|
||||
return
|
||||
}
|
||||
_, _ = w.Write([]byte(`{"choices":[{"message":{"content":"{\"ok\":true}"}}]}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
profilePath := filepath.Join(t.TempDir(), "profile.yml")
|
||||
if err := os.WriteFile(profilePath, []byte("id: retry-profile\nendpoint: "+server.URL+"/v1\nmodel: retry-model\n"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
client, err := NewPromptKitClient(PromptKitClientConfig{
|
||||
Assets: newTestPromptKitAssets(t),
|
||||
ProfileFile: profilePath,
|
||||
TransientRetries: &test.retries,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var out map[string]any
|
||||
_, err = client.CompleteStructured(context.Background(), contracts.StructuredCompletionRequest{
|
||||
PromptID: "adapter.test",
|
||||
ProfileID: "retry-profile",
|
||||
SessionID: "retry-test",
|
||||
Inputs: contracts.LLMInputSet{
|
||||
"transcript": contracts.NewLLMInputMaterial("transcript", "application/json", []byte(`{"source":true}`), "", ""),
|
||||
},
|
||||
}, &out)
|
||||
if (err != nil) != test.wantErr {
|
||||
t.Fatalf("CompleteStructured() error = %v, want error=%t", err, test.wantErr)
|
||||
}
|
||||
if got := calls.Load(); got != test.wantCalls {
|
||||
t.Fatalf("HTTP calls = %d, want %d", got, test.wantCalls)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLLMProfileRecorderDistinguishesEffectiveTargets(t *testing.T) {
|
||||
recorder := NewLLMProfileRecorder()
|
||||
for _, profile := range []artifacts.LLMProfileManifest{
|
||||
|
||||
Reference in New Issue
Block a user