Include local backend target in checkpoint identity
This commit is contained in:
@@ -41,6 +41,7 @@ type PromptKitClient struct {
|
|||||||
recorder *LLMProfileRecorder
|
recorder *LLMProfileRecorder
|
||||||
profileDir string
|
profileDir string
|
||||||
profileFile string
|
profileFile string
|
||||||
|
localEndpoint string
|
||||||
reasoningEffort *string
|
reasoningEffort *string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,9 +71,11 @@ func NewPromptKitClient(cfg PromptKitClientConfig) (*PromptKitClient, error) {
|
|||||||
if profileFile := strings.TrimSpace(cfg.ProfileFile); profileFile != "" {
|
if profileFile := strings.TrimSpace(cfg.ProfileFile); profileFile != "" {
|
||||||
options = append(options, promptkit.WithProfileFile(profileFile))
|
options = append(options, promptkit.WithProfileFile(profileFile))
|
||||||
}
|
}
|
||||||
|
var localEndpoint string
|
||||||
if cfg.LocalBackend != nil {
|
if cfg.LocalBackend != nil {
|
||||||
localBackend := *cfg.LocalBackend
|
localBackend := *cfg.LocalBackend
|
||||||
localBackend.Endpoint = strings.TrimSpace(localBackend.Endpoint)
|
localBackend.Endpoint = strings.TrimSpace(localBackend.Endpoint)
|
||||||
|
localEndpoint = localBackend.Endpoint
|
||||||
options = append(options, PromptKitLocalBackendOption(localBackend))
|
options = append(options, PromptKitLocalBackendOption(localBackend))
|
||||||
}
|
}
|
||||||
options = append(options, cfg.EngineOptions...)
|
options = append(options, cfg.EngineOptions...)
|
||||||
@@ -99,6 +102,7 @@ func NewPromptKitClient(cfg PromptKitClientConfig) (*PromptKitClient, error) {
|
|||||||
recorder: recorder,
|
recorder: recorder,
|
||||||
profileDir: strings.TrimSpace(cfg.ProfileDir),
|
profileDir: strings.TrimSpace(cfg.ProfileDir),
|
||||||
profileFile: strings.TrimSpace(cfg.ProfileFile),
|
profileFile: strings.TrimSpace(cfg.ProfileFile),
|
||||||
|
localEndpoint: localEndpoint,
|
||||||
reasoningEffort: reasoningEffort,
|
reasoningEffort: reasoningEffort,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -317,7 +321,11 @@ func (c *PromptKitClient) LLMCheckpointFingerprints() ([]CheckpointFingerprint,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return []CheckpointFingerprint{fingerprint}, nil
|
fingerprints := []CheckpointFingerprint{fingerprint}
|
||||||
|
if c.localEndpoint != "" {
|
||||||
|
fingerprints = append(fingerprints, promptKitLocalBackendFingerprint(c.localEndpoint))
|
||||||
|
}
|
||||||
|
return fingerprints, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLLMProfileRecorder() *LLMProfileRecorder {
|
func NewLLMProfileRecorder() *LLMProfileRecorder {
|
||||||
|
|||||||
@@ -312,6 +312,83 @@ func TestPromptKitClientCheckpointFingerprintTracksProfileSource(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPromptKitClientCheckpointFingerprintTracksLocalBackendTarget(t *testing.T) {
|
||||||
|
const (
|
||||||
|
firstEndpoint = "http://localhost:8000/v1"
|
||||||
|
secondEndpoint = "https://inference.example.test/v1"
|
||||||
|
)
|
||||||
|
fingerprintsFor := func(endpoint string, concurrencyLimit int) []CheckpointFingerprint {
|
||||||
|
t.Helper()
|
||||||
|
client, err := NewPromptKitClient(PromptKitClientConfig{
|
||||||
|
Assets: newTestPromptKitAssets(t),
|
||||||
|
LocalBackend: &PromptKitLocalBackendConfig{
|
||||||
|
Endpoint: endpoint,
|
||||||
|
ConcurrencyLimit: concurrencyLimit,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
values, err := client.LLMCheckpointFingerprints()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
baseline := fingerprintsFor(firstEndpoint, 0)
|
||||||
|
if len(baseline) != 2 ||
|
||||||
|
baseline[0].Name != promptKitProfileFingerprintName ||
|
||||||
|
baseline[1].Name != promptKitLocalBackendFingerprintName {
|
||||||
|
t.Fatalf("checkpoint fingerprints = %#v, want profile source then local backend target", baseline)
|
||||||
|
}
|
||||||
|
endpointChanged := fingerprintsFor(secondEndpoint, 0)
|
||||||
|
if baseline[0] != endpointChanged[0] || baseline[1] == endpointChanged[1] {
|
||||||
|
t.Fatalf("endpoint fingerprints = %#v and %#v, want only local target to change", baseline, endpointChanged)
|
||||||
|
}
|
||||||
|
concurrencyChanged := fingerprintsFor(firstEndpoint, 4)
|
||||||
|
if !reflect.DeepEqual(baseline, concurrencyChanged) {
|
||||||
|
t.Fatalf("concurrency fingerprints = %#v, want %#v", concurrencyChanged, baseline)
|
||||||
|
}
|
||||||
|
for _, values := range [][]CheckpointFingerprint{baseline, endpointChanged} {
|
||||||
|
for _, fingerprint := range values {
|
||||||
|
if strings.Contains(fingerprint.Value, firstEndpoint) ||
|
||||||
|
strings.Contains(fingerprint.Value, secondEndpoint) {
|
||||||
|
t.Fatalf("checkpoint fingerprint exposes endpoint: %#v", fingerprint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
localBackend := &PromptKitLocalBackendConfig{
|
||||||
|
Endpoint: " " + firstEndpoint + " ",
|
||||||
|
ConcurrencyLimit: 0,
|
||||||
|
}
|
||||||
|
client, err := NewPromptKitClient(PromptKitClientConfig{
|
||||||
|
Assets: newTestPromptKitAssets(t),
|
||||||
|
LocalBackend: localBackend,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
localBackend.Endpoint = secondEndpoint
|
||||||
|
copy, err := client.LLMCheckpointFingerprints()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(copy, baseline) {
|
||||||
|
t.Fatalf("fingerprints after input mutation = %#v, want retained target %#v", copy, baseline)
|
||||||
|
}
|
||||||
|
copy[0].Value = "mutated-profile"
|
||||||
|
copy[1].Value = "mutated-target"
|
||||||
|
fresh, err := client.LLMCheckpointFingerprints()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(fresh, baseline) {
|
||||||
|
t.Fatalf("fingerprints after returned-slice mutation = %#v, want %#v", fresh, baseline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestPromptKitClientUsesPromptDefaultProfileWhenRequestProfileEmpty(t *testing.T) {
|
func TestPromptKitClientUsesPromptDefaultProfileWhenRequestProfileEmpty(t *testing.T) {
|
||||||
fake := &fakePromptKitLLM{content: `{"ok":true}`}
|
fake := &fakePromptKitLLM{content: `{"ok":true}`}
|
||||||
client := newTestPromptKitClient(t, fake)
|
client := newTestPromptKitClient(t, fake)
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
promptKitProfileFingerprintName = "promptkit_profile_source"
|
promptKitProfileFingerprintName = "promptkit_profile_source"
|
||||||
|
promptKitLocalBackendFingerprintName = "promptkit_local_backend_target"
|
||||||
|
promptKitLocalBackendMarker = "notarius:promptkit-local-backend:v1"
|
||||||
// The built-in profile catalog is compiled into this pinned PromptKit
|
// The built-in profile catalog is compiled into this pinned PromptKit
|
||||||
// release. Update this identity when the dependency is upgraded.
|
// release. Update this identity when the dependency is upgraded.
|
||||||
promptKitBuiltinProfileCatalogID = "promptkit:v0.3.0:builtin-profiles"
|
promptKitBuiltinProfileCatalogID = "promptkit:v0.3.0:builtin-profiles"
|
||||||
@@ -45,6 +47,16 @@ func promptKitProfileFingerprint(profileDir, profileFile string) (CheckpointFing
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func promptKitLocalBackendFingerprint(endpoint string) CheckpointFingerprint {
|
||||||
|
hasher := sha256.New()
|
||||||
|
writeFingerprintPart(hasher, []byte(promptKitLocalBackendMarker))
|
||||||
|
writeFingerprintPart(hasher, []byte(strings.TrimSpace(endpoint)))
|
||||||
|
return CheckpointFingerprint{
|
||||||
|
Name: promptKitLocalBackendFingerprintName,
|
||||||
|
Value: "sha256:" + hex.EncodeToString(hasher.Sum(nil)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func promptKitProfileFileDigests(root string) ([][]byte, error) {
|
func promptKitProfileFileDigests(root string) ([][]byte, error) {
|
||||||
var digests [][]byte
|
var digests [][]byte
|
||||||
err := filepath.WalkDir(root, func(name string, entry fs.DirEntry, walkErr error) error {
|
err := filepath.WalkDir(root, func(name string, entry fs.DirEntry, walkErr error) error {
|
||||||
|
|||||||
@@ -81,7 +81,10 @@ func TestScheduledClientPreservesCheckpointFingerprints(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
inner := &fingerprintedStructuredClient{
|
inner := &fingerprintedStructuredClient{
|
||||||
fingerprints: []CheckpointFingerprint{{Name: "profile_source", Value: "sha256:one"}},
|
fingerprints: []CheckpointFingerprint{
|
||||||
|
{Name: "profile_source", Value: "sha256:one"},
|
||||||
|
{Name: "backend_target", Value: "sha256:two"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
client := NewScheduledClient(inner, scheduler)
|
client := NewScheduledClient(inner, scheduler)
|
||||||
provider, ok := client.(CheckpointFingerprintProvider)
|
provider, ok := client.(CheckpointFingerprintProvider)
|
||||||
@@ -92,7 +95,9 @@ func TestScheduledClientPreservesCheckpointFingerprints(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if len(got) != 1 || got[0] != inner.fingerprints[0] {
|
if len(got) != len(inner.fingerprints) ||
|
||||||
|
got[0] != inner.fingerprints[0] ||
|
||||||
|
got[1] != inner.fingerprints[1] {
|
||||||
t.Fatalf("checkpoint fingerprints = %#v, want %#v", got, inner.fingerprints)
|
t.Fatalf("checkpoint fingerprints = %#v, want %#v", got, inner.fingerprints)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user