Register configured local PromptKit backend
This commit is contained in:
@@ -19,9 +19,15 @@ import (
|
||||
|
||||
const promptKitProviderName = "promptkit"
|
||||
|
||||
type PromptKitLocalBackendConfig struct {
|
||||
Endpoint string
|
||||
ConcurrencyLimit int
|
||||
}
|
||||
|
||||
type PromptKitClientConfig struct {
|
||||
ProfileDir string
|
||||
ProfileFile string
|
||||
LocalBackend *PromptKitLocalBackendConfig
|
||||
Assets *AssetRegistry
|
||||
Timeout time.Duration
|
||||
HTTPClient *http.Client
|
||||
@@ -46,6 +52,10 @@ type LLMProfileRecorder struct {
|
||||
var _ contracts.StructuredLLMClient = (*PromptKitClient)(nil)
|
||||
var _ contracts.LLMProfileManifestProvider = (*PromptKitClient)(nil)
|
||||
|
||||
func PromptKitLocalBackendOption(cfg PromptKitLocalBackendConfig) promptkit.Option {
|
||||
return promptkit.WithBackend(promptkit.LocalBackend(cfg.Endpoint, cfg.ConcurrencyLimit))
|
||||
}
|
||||
|
||||
func NewPromptKitClient(cfg PromptKitClientConfig) (*PromptKitClient, error) {
|
||||
if cfg.Assets == nil {
|
||||
return nil, fmt.Errorf("PromptKit client assets must not be nil")
|
||||
@@ -60,6 +70,11 @@ func NewPromptKitClient(cfg PromptKitClientConfig) (*PromptKitClient, error) {
|
||||
if profileFile := strings.TrimSpace(cfg.ProfileFile); profileFile != "" {
|
||||
options = append(options, promptkit.WithProfileFile(profileFile))
|
||||
}
|
||||
if cfg.LocalBackend != nil {
|
||||
localBackend := *cfg.LocalBackend
|
||||
localBackend.Endpoint = strings.TrimSpace(localBackend.Endpoint)
|
||||
options = append(options, PromptKitLocalBackendOption(localBackend))
|
||||
}
|
||||
options = append(options, cfg.EngineOptions...)
|
||||
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
@@ -334,6 +335,88 @@ func TestPromptKitClientUsesPromptDefaultProfileWhenRequestProfileEmpty(t *testi
|
||||
}
|
||||
}
|
||||
|
||||
func TestPromptKitClientUsesConfiguredLocalBackend(t *testing.T) {
|
||||
var providerCalls atomic.Int32
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
providerCalls.Add(1)
|
||||
if r.URL.Path != "/v1/chat/completions" {
|
||||
t.Errorf("provider path = %q, want /v1/chat/completions", r.URL.Path)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{
|
||||
"choices": [{"message": {"role": "assistant", "content": "{\"ok\":true}"}}],
|
||||
"usage": {"prompt_tokens": 3, "completion_tokens": 4, "total_tokens": 7}
|
||||
}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
profilePath := filepath.Join(t.TempDir(), "profiles.yml")
|
||||
if err := os.WriteFile(profilePath, []byte(`id: local-profile
|
||||
backend: local
|
||||
model: local-model
|
||||
`), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
localBackend := &PromptKitLocalBackendConfig{
|
||||
Endpoint: " " + server.URL + "/v1 ",
|
||||
ConcurrencyLimit: 2,
|
||||
}
|
||||
client, err := NewPromptKitClient(PromptKitClientConfig{
|
||||
Assets: newTestPromptKitAssets(t),
|
||||
ProfileFile: profilePath,
|
||||
LocalBackend: localBackend,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("NewPromptKitClient() error = %v, want nil", err)
|
||||
}
|
||||
localBackend.Endpoint = "http://127.0.0.1:1/v1"
|
||||
|
||||
request := contracts.StructuredCompletionRequest{
|
||||
PromptID: "adapter.direct-session",
|
||||
ProfileID: "local-profile",
|
||||
Inputs: contracts.LLMInputSet{
|
||||
"transcript": contracts.NewLLMInputMaterial("transcript", "application/json", []byte(`{"source":true}`), "", ""),
|
||||
},
|
||||
Vars: map[string]any{"custom": "value"},
|
||||
}
|
||||
var out map[string]any
|
||||
response, err := client.CompleteStructured(context.Background(), request, &out)
|
||||
if err != nil {
|
||||
t.Fatalf("CompleteStructured() error = %v, want nil", err)
|
||||
}
|
||||
if providerCalls.Load() != 1 {
|
||||
t.Fatalf("provider calls = %d, want 1", providerCalls.Load())
|
||||
}
|
||||
if response.ProfileID != "local-profile" || response.Model != "local-model" {
|
||||
t.Fatalf("response metadata = %#v", response)
|
||||
}
|
||||
if response.Debug == nil || response.Debug.Prompt == nil ||
|
||||
response.Debug.Prompt.SelectedBackendID != promptkit.BackendLocal {
|
||||
t.Fatalf("response debug prompt = %#v, want local backend", response.Debug)
|
||||
}
|
||||
manifests := client.LLMProfileManifests()
|
||||
if len(manifests) != 1 || manifests[0].BackendID != promptkit.BackendLocal {
|
||||
t.Fatalf("profile manifests = %#v, want local backend", manifests)
|
||||
}
|
||||
|
||||
clientWithoutRegistration, err := NewPromptKitClient(PromptKitClientConfig{
|
||||
Assets: newTestPromptKitAssets(t),
|
||||
ProfileFile: profilePath,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("NewPromptKitClient() without local registration error = %v, want nil", err)
|
||||
}
|
||||
_, err = clientWithoutRegistration.CompleteStructured(context.Background(), request, &out)
|
||||
if err == nil ||
|
||||
!strings.Contains(err.Error(), "prepare PromptKit prompt") ||
|
||||
!strings.Contains(err.Error(), promptkit.BackendLocal) {
|
||||
t.Fatalf("CompleteStructured() without registration error = %v, want preparation failure with local backend context", err)
|
||||
}
|
||||
if providerCalls.Load() != 1 {
|
||||
t.Fatalf("provider calls after missing-registration failure = %d, want 1", providerCalls.Load())
|
||||
}
|
||||
}
|
||||
|
||||
func TestLLMProfileRecorderDistinguishesEffectiveTargets(t *testing.T) {
|
||||
recorder := NewLLMProfileRecorder()
|
||||
for _, profile := range []artifacts.LLMProfileManifest{
|
||||
|
||||
Reference in New Issue
Block a user