Cut over public engine construction to ConfigKit sources
This commit is contained in:
@@ -5,7 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -14,33 +13,25 @@ import (
|
||||
"testing/fstest"
|
||||
|
||||
"gitea.maximumdirect.net/eric/promptkit"
|
||||
"gitea.maximumdirect.net/eric/promptkit/testfixture"
|
||||
)
|
||||
|
||||
type inspectionCountingFS struct {
|
||||
opens atomic.Int64
|
||||
}
|
||||
|
||||
func (f *inspectionCountingFS) Open(string) (fs.File, error) {
|
||||
f.opens.Add(1)
|
||||
return nil, fs.ErrNotExist
|
||||
}
|
||||
|
||||
func TestInspectProfileResolvesCredentialStatesWithoutPromptOrGeneration(t *testing.T) {
|
||||
const environmentName = "PROMPTKIT_INSPECTION_ABSENT_KEY"
|
||||
t.Setenv(environmentName, "")
|
||||
client := &fakeLLMClient{response: &promptkit.GenerateResponse{Content: "unexpected"}}
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(fstest.MapFS{}, "."),
|
||||
promptkit.WithProfileFS(fstest.MapFS{
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(fstest.MapFS{}, "."),
|
||||
testfixture.WithProfileFS(fstest.MapFS{
|
||||
"environment.yaml": &fstest.MapFile{Data: []byte(`id: environment
|
||||
endpoint: http://environment.example/v1
|
||||
model: environment-model
|
||||
api_key_env: PROMPTKIT_INSPECTION_ABSENT_KEY
|
||||
`)},
|
||||
}, "."),
|
||||
promptkit.WithProfiles(
|
||||
promptkit.Profile{ID: "direct", Endpoint: "http://direct.example/v1", Model: "direct-model", APIKeyRequired: true},
|
||||
promptkit.Profile{ID: "none", Endpoint: "http://none.example/v1", Model: "none-model"},
|
||||
testfixture.WithProfiles(
|
||||
testfixture.Profile{ID: "direct", Endpoint: "http://direct.example/v1", Model: "direct-model", APIKeyRequired: true},
|
||||
testfixture.Profile{ID: "none", Endpoint: "http://none.example/v1", Model: "none-model"},
|
||||
),
|
||||
promptkit.WithLLMClient(client),
|
||||
)
|
||||
@@ -54,7 +45,7 @@ api_key_env: PROMPTKIT_INSPECTION_ABSENT_KEY
|
||||
wantDirectKey bool
|
||||
wantEndpoint string
|
||||
}{
|
||||
{profileID: " environment ", wantEnv: environmentName, wantEndpoint: "http://environment.example/v1"},
|
||||
{profileID: "environment", wantEnv: environmentName, wantEndpoint: "http://environment.example/v1"},
|
||||
{profileID: "direct", wantDirectKey: true, wantEndpoint: "http://direct.example/v1"},
|
||||
{profileID: "none", wantEndpoint: "http://none.example/v1"},
|
||||
} {
|
||||
@@ -63,7 +54,7 @@ api_key_env: PROMPTKIT_INSPECTION_ABSENT_KEY
|
||||
if err != nil {
|
||||
t.Fatalf("inspect profile: %v", err)
|
||||
}
|
||||
if inspection.ProfileID != strings.TrimSpace(tc.profileID) ||
|
||||
if inspection.ProfileID != tc.profileID ||
|
||||
inspection.EffectiveModelParams.Endpoint != tc.wantEndpoint ||
|
||||
inspection.EffectiveModelParams.BackendID != "" ||
|
||||
inspection.EffectiveModelParams.APIKeyEnv != tc.wantEnv ||
|
||||
@@ -77,10 +68,10 @@ api_key_env: PROMPTKIT_INSPECTION_ABSENT_KEY
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileProfileNormalizedIDMatchesInspectionAndPreparation(t *testing.T) {
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", "normalized-profile", "message"), "."),
|
||||
promptkit.WithProfileFS(fstest.MapFS{
|
||||
func TestFileProfileExactIDMatchesInspectionAndPreparation(t *testing.T) {
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", " normalized-profile ", "message"), "."),
|
||||
testfixture.WithProfileFS(fstest.MapFS{
|
||||
"profile.yaml": &fstest.MapFile{Data: []byte(`
|
||||
id: " normalized-profile "
|
||||
endpoint: http://profile.example/v1
|
||||
@@ -92,19 +83,19 @@ model: normalized-model
|
||||
t.Fatalf("construct engine: %v", err)
|
||||
}
|
||||
|
||||
inspection, err := engine.InspectProfile(context.Background(), " normalized-profile ")
|
||||
inspection, err := engine.InspectProfile(context.Background(), " normalized-profile ")
|
||||
if err != nil {
|
||||
t.Fatalf("inspect normalized profile: %v", err)
|
||||
}
|
||||
prepared, err := engine.Prepare(context.Background(), promptkit.RunRequest{
|
||||
PromptID: "prompt",
|
||||
ProfileID: " normalized-profile ",
|
||||
ProfileID: " normalized-profile ",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("prepare with normalized profile: %v", err)
|
||||
}
|
||||
|
||||
if inspection.ProfileID != "normalized-profile" ||
|
||||
if inspection.ProfileID != " normalized-profile " ||
|
||||
prepared.SelectedProfileID != inspection.ProfileID ||
|
||||
inspection.EffectiveModelParams.Model != "normalized-model" ||
|
||||
prepared.EffectiveModelParams.Model != inspection.EffectiveModelParams.Model {
|
||||
@@ -113,11 +104,11 @@ model: normalized-model
|
||||
}
|
||||
|
||||
func TestInspectProfilePreservesPublicErrorIdentities(t *testing.T) {
|
||||
newEngine := func(t *testing.T, options ...promptkit.Option) *promptkit.Engine {
|
||||
newEngine := func(t *testing.T, options ...any) *promptkit.Engine {
|
||||
t.Helper()
|
||||
engine, err := promptkit.NewEngine(
|
||||
promptkit.Config{},
|
||||
append([]promptkit.Option{promptkit.WithPromptFS(fstest.MapFS{}, ".")}, options...)...,
|
||||
engine, err := testfixture.NewEngine(
|
||||
testfixture.Config{},
|
||||
append([]any{testfixture.WithPromptFS(fstest.MapFS{}, ".")}, options...)...,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("construct inspection engine: %v", err)
|
||||
@@ -131,7 +122,7 @@ func TestInspectProfilePreservesPublicErrorIdentities(t *testing.T) {
|
||||
t.Fatalf("nil engine result=(%#v, %v), want ErrInvalidConfig", result, err)
|
||||
}
|
||||
|
||||
valid := newEngine(t, promptkit.WithProfiles(promptkit.Profile{
|
||||
valid := newEngine(t, testfixture.WithProfiles(testfixture.Profile{
|
||||
ID: "profile", Endpoint: "http://profile.example/v1", Model: "model",
|
||||
}))
|
||||
if result, err := valid.InspectProfile(context.Background(), " \t "); result != nil ||
|
||||
@@ -143,29 +134,25 @@ func TestInspectProfilePreservesPublicErrorIdentities(t *testing.T) {
|
||||
t.Fatalf("missing profile result=(%#v, %v), want only ErrProfileNotFound", result, err)
|
||||
}
|
||||
|
||||
malformed := newEngine(t, promptkit.WithProfileFS(fstest.MapFS{
|
||||
malformed, err := testfixture.NewEngine(testfixture.Config{}, testfixture.WithProfileFS(fstest.MapFS{
|
||||
"broken.yaml": &fstest.MapFile{Data: []byte("id: broken\nendpoint: http://broken.example/v1\nmodel: model\nextra_params:\n invalid: .nan\n")},
|
||||
}, "."))
|
||||
if result, err := malformed.InspectProfile(context.Background(), "broken"); result != nil ||
|
||||
!errors.Is(err, promptkit.ErrProfileLoad) {
|
||||
t.Fatalf("malformed profile result=(%#v, %v), want ErrProfileLoad", result, err)
|
||||
if malformed != nil || !errors.Is(err, promptkit.ErrInvalidConfig) {
|
||||
t.Fatalf("malformed profile construction=(%#v, %v), want ErrInvalidConfig", malformed, err)
|
||||
}
|
||||
|
||||
unknownBackend := newEngine(t, promptkit.WithProfiles(promptkit.Profile{
|
||||
unknownBackend, err := testfixture.NewEngine(testfixture.Config{}, testfixture.WithProfiles(testfixture.Profile{
|
||||
ID: "unknown-backend", BackendID: "unknown", Model: "model",
|
||||
}))
|
||||
if result, err := unknownBackend.InspectProfile(context.Background(), "unknown-backend"); result != nil ||
|
||||
!errors.Is(err, promptkit.ErrProfileLoad) {
|
||||
t.Fatalf("unknown backend result=(%#v, %v), want ErrProfileLoad", result, err)
|
||||
if unknownBackend != nil || !errors.Is(err, promptkit.ErrInvalidConfig) {
|
||||
t.Fatalf("unknown backend construction=(%#v, %v), want ErrInvalidConfig", unknownBackend, err)
|
||||
}
|
||||
|
||||
countingFS := &inspectionCountingFS{}
|
||||
canceled := newEngine(t, promptkit.WithProfileFS(countingFS, "."))
|
||||
canceled := newEngine(t, testfixture.WithProfiles(testfixture.Profile{ID: "profile", Endpoint: "http://profile.example/v1"}))
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
if result, err := canceled.InspectProfile(ctx, "profile"); result != nil ||
|
||||
!errors.Is(err, promptkit.ErrProfileLoad) || !errors.Is(err, context.Canceled) || countingFS.opens.Load() != 0 {
|
||||
t.Fatalf("canceled inspection result=(%#v, %v), opens=%d", result, err, countingFS.opens.Load())
|
||||
if result, err := canceled.InspectProfile(ctx, "profile"); result != nil || !errors.Is(err, context.Canceled) {
|
||||
t.Fatalf("canceled inspection result=(%#v, %v)", result, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,9 +160,9 @@ func TestInspectProfileReturnsIndependentTargetMatchingPreparation(t *testing.T)
|
||||
extraParams := map[string]any{
|
||||
"nested": map[string]any{"value": "original"},
|
||||
}
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
promptkit.WithProfiles(promptkit.Profile{
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
testfixture.WithProfiles(testfixture.Profile{
|
||||
ID: "profile", Endpoint: "http://profile.example/v1", Model: "model", ExtraParams: extraParams,
|
||||
}),
|
||||
)
|
||||
@@ -210,8 +197,8 @@ func TestInspectProfileReturnsIndependentTargetMatchingPreparation(t *testing.T)
|
||||
|
||||
func TestInspectPromptReturnsDeclaredMetadataWithoutExecutionWork(t *testing.T) {
|
||||
client := &fakeLLMClient{response: &promptkit.GenerateResponse{Content: "unexpected"}}
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(fstest.MapFS{
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(fstest.MapFS{
|
||||
"report-v1.yaml": &fstest.MapFile{Data: []byte(`id: report
|
||||
version: "1.0.0"
|
||||
messages:
|
||||
@@ -278,7 +265,7 @@ output:
|
||||
func TestInspectPromptPreservesPublicErrorIdentities(t *testing.T) {
|
||||
newEngine := func(t *testing.T, source fstest.MapFS) *promptkit.Engine {
|
||||
t.Helper()
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{}, promptkit.WithPromptFS(source, "."))
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{}, testfixture.WithPromptFS(source, "."))
|
||||
if err != nil {
|
||||
t.Fatalf("construct prompt-inspection engine: %v", err)
|
||||
}
|
||||
@@ -358,29 +345,25 @@ output:
|
||||
},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
if result, err := newEngine(t, source).InspectPrompt(context.Background(), "broken", "1"); result != nil ||
|
||||
!errors.Is(err, promptkit.ErrPromptLoad) {
|
||||
t.Fatalf("broken prompt result=(%#v, %v), want ErrPromptLoad", result, err)
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{}, testfixture.WithPromptFS(source, "."))
|
||||
if engine != nil || !errors.Is(err, promptkit.ErrInvalidConfig) {
|
||||
t.Fatalf("broken prompt construction=(%#v, %v), want ErrInvalidConfig", engine, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
countingFS := &inspectionCountingFS{}
|
||||
canceled, err := promptkit.NewEngine(promptkit.Config{}, promptkit.WithPromptFS(countingFS, "."))
|
||||
if err != nil {
|
||||
t.Fatalf("construct canceled prompt-inspection engine: %v", err)
|
||||
}
|
||||
canceled := valid
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
if result, err := canceled.InspectPrompt(ctx, "prompt", "1"); result != nil ||
|
||||
!errors.Is(err, promptkit.ErrPromptLoad) || !errors.Is(err, context.Canceled) || countingFS.opens.Load() != 0 {
|
||||
t.Fatalf("canceled inspection result=(%#v, %v), opens=%d", result, err, countingFS.opens.Load())
|
||||
!errors.Is(err, context.Canceled) {
|
||||
t.Fatalf("canceled inspection result=(%#v, %v)", result, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInspectPromptReturnsIndependentMetadataMatchingPreparation(t *testing.T) {
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(fstest.MapFS{
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(fstest.MapFS{
|
||||
"prompt.yaml": &fstest.MapFile{Data: []byte(`id: prompt
|
||||
version: "1"
|
||||
default_profile: profile
|
||||
@@ -396,7 +379,7 @@ output:
|
||||
validation_mode: basic
|
||||
`)},
|
||||
}, "."),
|
||||
promptkit.WithProfiles(promptkit.Profile{
|
||||
testfixture.WithProfiles(testfixture.Profile{
|
||||
ID: "profile", Endpoint: "http://profile.example/v1", Model: "model",
|
||||
}),
|
||||
)
|
||||
@@ -482,9 +465,9 @@ func TestBackendIdentityJSONNamesAndOmission(t *testing.T) {
|
||||
|
||||
func TestEndpointOnlyProfileOmitsBackendIdentityFromStableJSON(t *testing.T) {
|
||||
client := &fakeLLMClient{response: &promptkit.GenerateResponse{Content: "ok"}}
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
promptkit.WithProfiles(promptkit.Profile{
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
testfixture.WithProfiles(testfixture.Profile{
|
||||
ID: "profile", Endpoint: "http://example.test/v1", Model: "model",
|
||||
}),
|
||||
promptkit.WithLLMClient(client),
|
||||
@@ -518,21 +501,13 @@ func TestEndpointOnlyProfileOmitsBackendIdentityFromStableJSON(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnknownProfileBackendHasProfileLoadIdentity(t *testing.T) {
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
promptkit.WithProfiles(promptkit.Profile{ID: "profile", BackendID: "unknown", Model: "model"}),
|
||||
func TestUnknownProfileBackendFailsConstruction(t *testing.T) {
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
testfixture.WithProfiles(testfixture.Profile{ID: "profile", BackendID: "unknown", Model: "model"}),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("construct engine: %v", err)
|
||||
}
|
||||
|
||||
_, err = engine.Prepare(context.Background(), promptkit.RunRequest{PromptID: "prompt"})
|
||||
if !errors.Is(err, promptkit.ErrProfileLoad) {
|
||||
t.Fatalf("expected ErrProfileLoad, got %v", err)
|
||||
}
|
||||
if errors.Is(err, promptkit.ErrInvalidRequest) {
|
||||
t.Fatalf("unknown backend should not have invalid-request identity: %v", err)
|
||||
if engine != nil || !errors.Is(err, promptkit.ErrInvalidConfig) {
|
||||
t.Fatalf("construction=(%#v, %v), want ErrInvalidConfig", engine, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,8 +521,8 @@ func TestLocalBackendConstructsAndRegistersConventionalBackend(t *testing.T) {
|
||||
}
|
||||
|
||||
endpoint := "http://local.example/v1"
|
||||
backend := promptkit.LocalBackend(endpoint, limit)
|
||||
want := promptkit.Backend{
|
||||
backend := testfixture.LocalBackend(endpoint, limit)
|
||||
want := testfixture.Backend{
|
||||
ID: localBackendID,
|
||||
Endpoint: endpoint,
|
||||
ConcurrencyLimit: limit,
|
||||
@@ -556,10 +531,10 @@ func TestLocalBackendConstructsAndRegistersConventionalBackend(t *testing.T) {
|
||||
t.Fatalf("LocalBackend()=%+v, want %+v", backend, want)
|
||||
}
|
||||
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", "local-profile", "message"), "."),
|
||||
promptkit.WithBackend(backend),
|
||||
promptkit.WithProfiles(promptkit.Profile{
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", "local-profile", "message"), "."),
|
||||
testfixture.WithBackend(backend),
|
||||
testfixture.WithProfiles(testfixture.Profile{
|
||||
ID: "local-profile",
|
||||
BackendID: localBackendID,
|
||||
Model: "model",
|
||||
@@ -582,20 +557,20 @@ func TestLocalBackendConstructsAndRegistersConventionalBackend(t *testing.T) {
|
||||
func TestCustomBackendFlowsThroughProfilesOverridesAndInjectedClient(t *testing.T) {
|
||||
t.Setenv("CUSTOM_LLM_KEY", "test-key")
|
||||
client := &fakeLLMClient{response: &promptkit.GenerateResponse{Content: "ok"}}
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", "backend-profile", "message"), "."),
|
||||
promptkit.WithBackend(promptkit.Backend{
|
||||
ID: " custom ",
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", "backend-profile", "message"), "."),
|
||||
testfixture.WithBackend(testfixture.Backend{
|
||||
ID: "custom",
|
||||
Endpoint: " http://backend.example/v1 ",
|
||||
APIKeyEnv: " CUSTOM_LLM_KEY ",
|
||||
ExtraParams: map[string]any{
|
||||
"provider": "custom",
|
||||
},
|
||||
}),
|
||||
promptkit.WithProfiles(
|
||||
promptkit.Profile{ID: "backend-profile", BackendID: "custom", Model: "backend-model"},
|
||||
promptkit.Profile{ID: "profile-endpoint", BackendID: "custom", Endpoint: "http://profile.example/v1", Model: "profile-model"},
|
||||
promptkit.Profile{ID: "blank-profile-endpoint", BackendID: "custom", Endpoint: " \t ", Model: "profile-model"},
|
||||
testfixture.WithProfiles(
|
||||
testfixture.Profile{ID: "backend-profile", BackendID: "custom", Model: "backend-model"},
|
||||
testfixture.Profile{ID: "profile-endpoint", BackendID: "custom", Endpoint: "http://profile.example/v1", Model: "profile-model"},
|
||||
testfixture.Profile{ID: "blank-profile-endpoint", BackendID: "custom", Model: "profile-model"},
|
||||
),
|
||||
promptkit.WithLLMClient(client),
|
||||
)
|
||||
@@ -655,16 +630,15 @@ func TestCustomBackendFlowsThroughProfilesOverridesAndInjectedClient(t *testing.
|
||||
}
|
||||
|
||||
func TestCustomBackendSupportsFileProfileAndBothSelectionPaths(t *testing.T) {
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", "file-profile", "message"), "."),
|
||||
promptkit.WithProfileFS(fstest.MapFS{
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", "file-profile", "message"), "."),
|
||||
testfixture.WithProfileFS(fstest.MapFS{
|
||||
"profile.yaml": &fstest.MapFile{Data: []byte(`id: file-profile
|
||||
backend: file-backend
|
||||
endpoint: " "
|
||||
model: file-model
|
||||
`)},
|
||||
}, "."),
|
||||
promptkit.WithBackend(promptkit.Backend{
|
||||
testfixture.WithBackend(testfixture.Backend{
|
||||
ID: "file-backend",
|
||||
Endpoint: "http://file-backend.example/v1",
|
||||
}),
|
||||
@@ -689,13 +663,13 @@ model: file-model
|
||||
}
|
||||
|
||||
func TestBackendOptionsAccumulateAndRegistrationsAreEngineLocal(t *testing.T) {
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", "first-profile", "message"), "."),
|
||||
promptkit.WithBackend(promptkit.Backend{ID: "first", Endpoint: "http://first.example/v1"}),
|
||||
promptkit.WithBackend(promptkit.Backend{ID: "second", Endpoint: "http://second.example/v1"}),
|
||||
promptkit.WithProfiles(
|
||||
promptkit.Profile{ID: "first-profile", BackendID: "first", Model: "model"},
|
||||
promptkit.Profile{ID: "second-profile", BackendID: "second", Model: "model"},
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", "first-profile", "message"), "."),
|
||||
testfixture.WithBackend(testfixture.Backend{ID: "first", Endpoint: "http://first.example/v1"}),
|
||||
testfixture.WithBackend(testfixture.Backend{ID: "second", Endpoint: "http://second.example/v1"}),
|
||||
testfixture.WithProfiles(
|
||||
testfixture.Profile{ID: "first-profile", BackendID: "first", Model: "model"},
|
||||
testfixture.Profile{ID: "second-profile", BackendID: "second", Model: "model"},
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -718,10 +692,10 @@ func TestBackendOptionsAccumulateAndRegistrationsAreEngineLocal(t *testing.T) {
|
||||
|
||||
newEngine := func(endpoint string) *promptkit.Engine {
|
||||
t.Helper()
|
||||
value, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
promptkit.WithBackend(promptkit.Backend{ID: "same-id", Endpoint: endpoint}),
|
||||
promptkit.WithProfiles(promptkit.Profile{ID: "profile", BackendID: "same-id", Model: "model"}),
|
||||
value, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
testfixture.WithBackend(testfixture.Backend{ID: "same-id", Endpoint: endpoint}),
|
||||
testfixture.WithProfiles(testfixture.Profile{ID: "profile", BackendID: "same-id", Model: "model"}),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("construct isolated engine: %v", err)
|
||||
@@ -746,7 +720,7 @@ func TestBackendOptionsAccumulateAndRegistrationsAreEngineLocal(t *testing.T) {
|
||||
|
||||
func TestWithBackendCopiesQueueCapacity(t *testing.T) {
|
||||
queueCapacity := 4
|
||||
option := promptkit.WithBackend(promptkit.Backend{
|
||||
option := testfixture.WithBackend(testfixture.Backend{
|
||||
ID: "custom",
|
||||
Endpoint: "http://custom.example/v1",
|
||||
ConcurrencyLimit: 1,
|
||||
@@ -754,10 +728,10 @@ func TestWithBackendCopiesQueueCapacity(t *testing.T) {
|
||||
})
|
||||
queueCapacity = -1
|
||||
|
||||
_, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
_, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
option,
|
||||
promptkit.WithProfiles(promptkit.Profile{
|
||||
testfixture.WithProfiles(testfixture.Profile{
|
||||
ID: "profile", BackendID: "custom", Model: "model",
|
||||
}),
|
||||
)
|
||||
@@ -766,41 +740,31 @@ func TestWithBackendCopiesQueueCapacity(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackendRegistrationRejectsInvalidAndDuplicateDefinitions(t *testing.T) {
|
||||
func TestBackendConfigRejectsInvalidDefinitions(t *testing.T) {
|
||||
cycle := map[string]any{}
|
||||
cycle["self"] = cycle
|
||||
tests := []struct {
|
||||
name string
|
||||
backends []promptkit.Backend
|
||||
backends []testfixture.Backend
|
||||
}{
|
||||
{name: "blank id", backends: []promptkit.Backend{{Endpoint: "http://example.test/v1"}}},
|
||||
{name: "invalid endpoint", backends: []promptkit.Backend{{ID: "custom", Endpoint: "ftp://example.test/v1"}}},
|
||||
{name: "invalid environment", backends: []promptkit.Backend{{ID: "custom", Endpoint: "http://example.test/v1", APIKeyEnv: "BAD-NAME"}}},
|
||||
{name: "reserved extra parameter", backends: []promptkit.Backend{{ID: "custom", Endpoint: "http://example.test/v1", ExtraParams: map[string]any{"model": "override"}}}},
|
||||
{name: "cyclic extra parameter", backends: []promptkit.Backend{{ID: "custom", Endpoint: "http://example.test/v1", ExtraParams: cycle}}},
|
||||
{name: "malformed JSON number", backends: []promptkit.Backend{{ID: "custom", Endpoint: "http://example.test/v1", ExtraParams: map[string]any{"value": json.Number("01")}}}},
|
||||
{name: "excessively deep extra parameter", backends: []promptkit.Backend{{ID: "custom", Endpoint: "http://example.test/v1", ExtraParams: map[string]any{"value": excessivelyDeepJSONValue()}}}},
|
||||
{name: "duplicate consumer id", backends: []promptkit.Backend{
|
||||
{ID: " custom ", Endpoint: "http://one.example/v1"},
|
||||
{ID: "custom", Endpoint: "http://two.example/v1"},
|
||||
}},
|
||||
{name: "reserved OpenRouter ID", backends: []promptkit.Backend{{
|
||||
ID: promptkit.BackendOpenRouter, Endpoint: "http://replacement.example/v1",
|
||||
}}},
|
||||
{name: "reserved Rakestrawhome ID", backends: []promptkit.Backend{{
|
||||
ID: promptkit.BackendRakestrawHome, Endpoint: "http://replacement.example/v1",
|
||||
}}},
|
||||
{name: "blank id", backends: []testfixture.Backend{{Endpoint: "http://example.test/v1"}}},
|
||||
{name: "invalid endpoint", backends: []testfixture.Backend{{ID: "custom", Endpoint: "ftp://example.test/v1"}}},
|
||||
{name: "invalid environment", backends: []testfixture.Backend{{ID: "custom", Endpoint: "http://example.test/v1", APIKeyEnv: "BAD-NAME"}}},
|
||||
{name: "reserved extra parameter", backends: []testfixture.Backend{{ID: "custom", Endpoint: "http://example.test/v1", ExtraParams: map[string]any{"model": "override"}}}},
|
||||
{name: "cyclic extra parameter", backends: []testfixture.Backend{{ID: "custom", Endpoint: "http://example.test/v1", ExtraParams: cycle}}},
|
||||
{name: "malformed JSON number", backends: []testfixture.Backend{{ID: "custom", Endpoint: "http://example.test/v1", ExtraParams: map[string]any{"value": json.Number("01")}}}},
|
||||
{name: "excessively deep extra parameter", backends: []testfixture.Backend{{ID: "custom", Endpoint: "http://example.test/v1", ExtraParams: map[string]any{"value": excessivelyDeepJSONValue()}}}},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
options := []promptkit.Option{
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
options := []any{
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
}
|
||||
for _, backend := range tt.backends {
|
||||
options = append(options, promptkit.WithBackend(backend))
|
||||
options = append(options, testfixture.WithBackend(backend))
|
||||
}
|
||||
_, err := promptkit.NewEngine(promptkit.Config{}, options...)
|
||||
_, err := testfixture.NewEngine(testfixture.Config{}, options...)
|
||||
if !errors.Is(err, promptkit.ErrInvalidConfig) {
|
||||
t.Fatalf("expected ErrInvalidConfig, got %v", err)
|
||||
}
|
||||
@@ -811,12 +775,12 @@ func TestBackendRegistrationRejectsInvalidAndDuplicateDefinitions(t *testing.T)
|
||||
func TestBackendExtraParamsAreDeeplyCopiedAtConstructionAndLookup(t *testing.T) {
|
||||
nested := map[string]any{"value": "original"}
|
||||
extraParams := map[string]any{"nested": nested}
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
promptkit.WithBackend(promptkit.Backend{
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
testfixture.WithBackend(testfixture.Backend{
|
||||
ID: "custom", Endpoint: "http://example.test/v1", ExtraParams: extraParams,
|
||||
}),
|
||||
promptkit.WithProfiles(promptkit.Profile{ID: "profile", BackendID: "custom", Model: "model"}),
|
||||
testfixture.WithProfiles(testfixture.Profile{ID: "profile", BackendID: "custom", Model: "model"}),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("construct engine: %v", err)
|
||||
@@ -956,13 +920,13 @@ func TestEngineRunReturnsFinalResultAfterRepairExhaustion(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRepeatedOptionsUseLastValueInEachCategory(t *testing.T) {
|
||||
profile := promptkit.Profile{ID: "profile", Endpoint: "http://example.test/v1", Model: "model"}
|
||||
profile := testfixture.Profile{ID: "profile", Endpoint: "http://example.test/v1", Model: "model"}
|
||||
|
||||
t.Run("prompt source", func(t *testing.T) {
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("first", "profile", "first"), "."),
|
||||
promptkit.WithPromptFS(contractPromptFS("second", "profile", "second"), "."),
|
||||
promptkit.WithProfiles(profile),
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("first", "profile", "first"), "."),
|
||||
testfixture.WithPromptFS(contractPromptFS("second", "profile", "second"), "."),
|
||||
testfixture.WithProfiles(profile),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("construct engine: %v", err)
|
||||
@@ -977,10 +941,10 @@ func TestRepeatedOptionsUseLastValueInEachCategory(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("profile source", func(t *testing.T) {
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
promptkit.WithProfileFS(contractProfileFS("profile", "first-model"), "."),
|
||||
promptkit.WithProfileFS(contractProfileFS("profile", "second-model"), "."),
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
testfixture.WithProfileFS(contractProfileFS("profile", "first-model"), "."),
|
||||
testfixture.WithProfileFS(contractProfileFS("profile", "second-model"), "."),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("construct engine: %v", err)
|
||||
@@ -995,10 +959,10 @@ func TestRepeatedOptionsUseLastValueInEachCategory(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("fallback profile source", func(t *testing.T) {
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
promptkit.WithFallbackProfileFS(contractProfileFS("profile", "first-model"), "."),
|
||||
promptkit.WithFallbackProfileFS(contractProfileFS("profile", "second-model"), "."),
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
testfixture.WithFallbackProfileFS(contractProfileFS("profile", "first-model"), "."),
|
||||
testfixture.WithFallbackProfileFS(contractProfileFS("profile", "second-model"), "."),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("construct engine: %v", err)
|
||||
@@ -1017,10 +981,10 @@ func TestRepeatedOptionsUseLastValueInEachCategory(t *testing.T) {
|
||||
first.Model = "first-model"
|
||||
second := profile
|
||||
second.Model = "second-model"
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
promptkit.WithProfiles(first),
|
||||
promptkit.WithProfiles(second),
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
testfixture.WithProfiles(first),
|
||||
testfixture.WithProfiles(second),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("construct engine: %v", err)
|
||||
@@ -1035,9 +999,9 @@ func TestRepeatedOptionsUseLastValueInEachCategory(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("schema source", func(t *testing.T) {
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractSchemaPromptFS(), "."),
|
||||
promptkit.WithProfiles(profile),
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractSchemaPromptFS(), "."),
|
||||
testfixture.WithProfiles(profile),
|
||||
promptkit.WithSchemaFS(contractSchemaFS("first"), "."),
|
||||
promptkit.WithSchemaFS(contractSchemaFS("second"), "."),
|
||||
)
|
||||
@@ -1056,9 +1020,9 @@ func TestRepeatedOptionsUseLastValueInEachCategory(t *testing.T) {
|
||||
|
||||
t.Run("model client", func(t *testing.T) {
|
||||
var firstCalls, secondCalls atomic.Int64
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
promptkit.WithProfiles(profile),
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
testfixture.WithProfiles(profile),
|
||||
promptkit.WithLLMClient(countingLLMClient{calls: &firstCalls}),
|
||||
promptkit.WithLLMClient(countingLLMClient{calls: &secondCalls}),
|
||||
)
|
||||
@@ -1074,9 +1038,9 @@ func TestRepeatedOptionsUseLastValueInEachCategory(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("artifact reader", func(t *testing.T) {
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractInputPromptFS(), "."),
|
||||
promptkit.WithProfiles(profile),
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractInputPromptFS(), "."),
|
||||
testfixture.WithProfiles(profile),
|
||||
promptkit.WithArtifactReader(fixedArtifactReader("first")),
|
||||
promptkit.WithArtifactReader(fixedArtifactReader("second")),
|
||||
)
|
||||
@@ -1109,11 +1073,11 @@ func TestFallbackProfileSourcePrecedence(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run("in-memory profiles override ordinary and fallback profiles", func(t *testing.T) {
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", profileID, "message"), "."),
|
||||
promptkit.WithProfiles(promptkit.Profile{ID: profileID, Endpoint: "http://example.test/v1", Model: "memory-model"}),
|
||||
promptkit.WithProfileFS(contractProfileFS(profileID, "ordinary-model"), "."),
|
||||
promptkit.WithFallbackProfileFS(contractProfileFS(profileID, "fallback-model"), "."),
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", profileID, "message"), "."),
|
||||
testfixture.WithProfiles(testfixture.Profile{ID: profileID, Endpoint: "http://example.test/v1", Model: "memory-model"}),
|
||||
testfixture.WithProfileFS(contractProfileFS(profileID, "ordinary-model"), "."),
|
||||
testfixture.WithFallbackProfileFS(contractProfileFS(profileID, "fallback-model"), "."),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("construct engine: %v", err)
|
||||
@@ -1124,10 +1088,10 @@ func TestFallbackProfileSourcePrecedence(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("ordinary filesystem source overrides fallback profile", func(t *testing.T) {
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", profileID, "message"), "."),
|
||||
promptkit.WithProfileFS(contractProfileFS(profileID, "ordinary-model"), "."),
|
||||
promptkit.WithFallbackProfileFS(contractProfileFS(profileID, "fallback-model"), "."),
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", profileID, "message"), "."),
|
||||
testfixture.WithProfileFS(contractProfileFS(profileID, "ordinary-model"), "."),
|
||||
testfixture.WithFallbackProfileFS(contractProfileFS(profileID, "fallback-model"), "."),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("construct engine: %v", err)
|
||||
@@ -1140,9 +1104,9 @@ func TestFallbackProfileSourcePrecedence(t *testing.T) {
|
||||
t.Run("ordinary option replaces configured directory", func(t *testing.T) {
|
||||
profileDir := t.TempDir()
|
||||
writePublicProfileFile(t, profileDir, profileID, "http://example.test/v1", "directory-model")
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{ProfileDir: profileDir},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", profileID, "message"), "."),
|
||||
promptkit.WithProfileFS(contractProfileFS(profileID, "option-model"), "."),
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{ProfileDir: profileDir},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", profileID, "message"), "."),
|
||||
testfixture.WithProfileFS(contractProfileFS(profileID, "option-model"), "."),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("construct engine: %v", err)
|
||||
@@ -1155,9 +1119,9 @@ func TestFallbackProfileSourcePrecedence(t *testing.T) {
|
||||
t.Run("configured directory overrides fallback profile", func(t *testing.T) {
|
||||
profileDir := t.TempDir()
|
||||
writePublicProfileFile(t, profileDir, profileID, "http://example.test/v1", "directory-model")
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{ProfileDir: profileDir},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", profileID, "message"), "."),
|
||||
promptkit.WithFallbackProfileFS(contractProfileFS(profileID, "fallback-model"), "."),
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{ProfileDir: profileDir},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", profileID, "message"), "."),
|
||||
testfixture.WithFallbackProfileFS(contractProfileFS(profileID, "fallback-model"), "."),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("construct engine: %v", err)
|
||||
@@ -1168,9 +1132,9 @@ func TestFallbackProfileSourcePrecedence(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("fallback profile overrides built-in profile", func(t *testing.T) {
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", "mistral-small-3", "message"), "."),
|
||||
promptkit.WithFallbackProfileFS(contractProfileFS("mistral-small-3", "fallback-model"), "."),
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", "mistral-small-3", "message"), "."),
|
||||
testfixture.WithFallbackProfileFS(contractProfileFS("mistral-small-3", "fallback-model"), "."),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("construct engine: %v", err)
|
||||
@@ -1182,17 +1146,17 @@ func TestFallbackProfileSourcePrecedence(t *testing.T) {
|
||||
|
||||
t.Run("missing fallback profile uses built-in profile", func(t *testing.T) {
|
||||
t.Setenv("OPENROUTER_API_KEY", "test-key")
|
||||
baseline, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", "mistral-small-3", "message"), "."),
|
||||
baseline, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", "mistral-small-3", "message"), "."),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("construct baseline engine: %v", err)
|
||||
}
|
||||
want := prepareModel(t, baseline, "prompt")
|
||||
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", "mistral-small-3", "message"), "."),
|
||||
promptkit.WithFallbackProfileFS(contractProfileFS(profileID, "fallback-model"), "."),
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", "mistral-small-3", "message"), "."),
|
||||
testfixture.WithFallbackProfileFS(contractProfileFS(profileID, "fallback-model"), "."),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("construct engine: %v", err)
|
||||
@@ -1210,8 +1174,8 @@ func TestPromptkitOwnedProfilesUseExistingProfileWorkflows(t *testing.T) {
|
||||
)
|
||||
|
||||
t.Run("inspection and preparation resolve both names", func(t *testing.T) {
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", standardID, "message"), "."),
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", standardID, "message"), "."),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("construct engine: %v", err)
|
||||
@@ -1241,9 +1205,9 @@ func TestPromptkitOwnedProfilesUseExistingProfileWorkflows(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("consumer definitions override an owned profile and its base", func(t *testing.T) {
|
||||
auto, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("auto", autoID, "message"), "."),
|
||||
promptkit.WithProfiles(promptkit.Profile{
|
||||
auto, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("auto", autoID, "message"), "."),
|
||||
testfixture.WithProfiles(testfixture.Profile{
|
||||
ID: autoID,
|
||||
Endpoint: "http://consumer.example/v1",
|
||||
Model: "consumer-auto-model",
|
||||
@@ -1260,9 +1224,9 @@ func TestPromptkitOwnedProfilesUseExistingProfileWorkflows(t *testing.T) {
|
||||
t.Fatalf("owned profile override model = %q", autoPrepared.EffectiveModelParams.Model)
|
||||
}
|
||||
|
||||
standard, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("standard", standardID, "message"), "."),
|
||||
promptkit.WithProfiles(promptkit.Profile{
|
||||
standard, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("standard", standardID, "message"), "."),
|
||||
testfixture.WithProfiles(testfixture.Profile{
|
||||
ID: "gemini-flash-latest",
|
||||
Endpoint: "http://consumer.example/v1",
|
||||
Model: "consumer-base-model",
|
||||
@@ -1281,9 +1245,9 @@ func TestPromptkitOwnedProfilesUseExistingProfileWorkflows(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("prompt default selection remains explicit", func(t *testing.T) {
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("default", "application-default", "message"), "."),
|
||||
promptkit.WithProfiles(promptkit.Profile{
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("default", "application-default", "message"), "."),
|
||||
testfixture.WithProfiles(testfixture.Profile{
|
||||
ID: "application-default",
|
||||
Endpoint: "http://consumer.example/v1",
|
||||
Model: "consumer-default-model",
|
||||
@@ -1302,69 +1266,56 @@ func TestPromptkitOwnedProfilesUseExistingProfileWorkflows(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestFallbackProfileSourcePreservesLazyLoadingAndErrors(t *testing.T) {
|
||||
func TestInvalidFallbackProfileSourcesFailConstruction(t *testing.T) {
|
||||
const profileID = "application-profile"
|
||||
|
||||
t.Run("construction defers malformed fallback profiles", func(t *testing.T) {
|
||||
_, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(fstest.MapFS{}, "."),
|
||||
promptkit.WithFallbackProfileFS(fstest.MapFS{
|
||||
t.Run("malformed fallback profile", func(t *testing.T) {
|
||||
_, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(fstest.MapFS{}, "."),
|
||||
testfixture.WithFallbackProfileFS(fstest.MapFS{
|
||||
"broken.yaml": &fstest.MapFile{Data: []byte("id: broken\nunknown: value\n")},
|
||||
}, "."),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("construct engine with malformed fallback profile: %v", err)
|
||||
if !errors.Is(err, promptkit.ErrInvalidConfig) {
|
||||
t.Fatalf("expected invalid configuration, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("unrelated malformed fallback profile does not block matching definition", func(t *testing.T) {
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", profileID, "message"), "."),
|
||||
promptkit.WithFallbackProfileFS(fstest.MapFS{
|
||||
t.Run("unrelated malformed fallback profile blocks construction", func(t *testing.T) {
|
||||
_, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", profileID, "message"), "."),
|
||||
testfixture.WithFallbackProfileFS(fstest.MapFS{
|
||||
"broken.yaml": &fstest.MapFile{Data: []byte("id: unrelated\nunknown: value\n")},
|
||||
"valid.yaml": &fstest.MapFile{Data: []byte("id: application-profile\nendpoint: http://example.test/v1\nmodel: fallback-model\n")},
|
||||
}, "."),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("construct engine: %v", err)
|
||||
}
|
||||
prepared, err := engine.Prepare(context.Background(), promptkit.RunRequest{PromptID: "prompt"})
|
||||
if err != nil {
|
||||
t.Fatalf("prepare from valid fallback profile: %v", err)
|
||||
}
|
||||
if prepared.EffectiveModelParams.Model != "fallback-model" {
|
||||
t.Fatalf("unexpected fallback profile model: %q", prepared.EffectiveModelParams.Model)
|
||||
if !errors.Is(err, promptkit.ErrInvalidConfig) {
|
||||
t.Fatalf("expected invalid configuration, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("matching malformed fallback profile does not reach built-in profile", func(t *testing.T) {
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", "mistral-small-3", "message"), "."),
|
||||
promptkit.WithFallbackProfileFS(fstest.MapFS{
|
||||
t.Run("matching malformed fallback profile", func(t *testing.T) {
|
||||
_, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", "mistral-small-3", "message"), "."),
|
||||
testfixture.WithFallbackProfileFS(fstest.MapFS{
|
||||
"mistral-small-3.yaml": &fstest.MapFile{Data: []byte("id: mistral-small-3\nendpoint: http://example.test/v1\nmodel: fallback-model\nunknown: value\n")},
|
||||
}, "."),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("construct engine: %v", err)
|
||||
}
|
||||
if _, err := engine.Prepare(context.Background(), promptkit.RunRequest{PromptID: "prompt"}); !errors.Is(err, promptkit.ErrProfileLoad) {
|
||||
t.Fatalf("expected ErrProfileLoad, got %v", err)
|
||||
if !errors.Is(err, promptkit.ErrInvalidConfig) {
|
||||
t.Fatalf("expected invalid configuration, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("matching malformed ordinary profile does not reach fallback profile", func(t *testing.T) {
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", profileID, "message"), "."),
|
||||
promptkit.WithProfileFS(fstest.MapFS{
|
||||
t.Run("matching malformed ordinary profile", func(t *testing.T) {
|
||||
_, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", profileID, "message"), "."),
|
||||
testfixture.WithProfileFS(fstest.MapFS{
|
||||
"application-profile.yaml": &fstest.MapFile{Data: []byte("id: application-profile\nendpoint: http://example.test/v1\nmodel: ordinary-model\nunknown: value\n")},
|
||||
}, "."),
|
||||
promptkit.WithFallbackProfileFS(contractProfileFS(profileID, "fallback-model"), "."),
|
||||
testfixture.WithFallbackProfileFS(contractProfileFS(profileID, "fallback-model"), "."),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("construct engine: %v", err)
|
||||
}
|
||||
if _, err := engine.Prepare(context.Background(), promptkit.RunRequest{PromptID: "prompt"}); !errors.Is(err, promptkit.ErrProfileLoad) {
|
||||
t.Fatalf("expected ErrProfileLoad, got %v", err)
|
||||
if !errors.Is(err, promptkit.ErrInvalidConfig) {
|
||||
t.Fatalf("expected invalid configuration, got %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1372,9 +1323,9 @@ func TestFallbackProfileSourcePreservesLazyLoadingAndErrors(t *testing.T) {
|
||||
func TestFallbackProfileSourceWorksAcrossWorkflows(t *testing.T) {
|
||||
const profileID = "application-profile"
|
||||
client := &fakeLLMClient{response: &promptkit.GenerateResponse{Content: "ok"}}
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", profileID, "message"), "."),
|
||||
promptkit.WithFallbackProfileFS(contractProfileFS(profileID, "fallback-model"), "."),
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", profileID, "message"), "."),
|
||||
testfixture.WithFallbackProfileFS(contractProfileFS(profileID, "fallback-model"), "."),
|
||||
promptkit.WithLLMClient(client),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -1417,13 +1368,13 @@ func TestFallbackProfileSourceWorksAcrossWorkflows(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEngineSupportsConcurrentPrepareAndRun(t *testing.T) {
|
||||
engine, err := promptkit.NewEngine(promptkit.Config{},
|
||||
promptkit.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
promptkit.WithBackend(promptkit.Backend{
|
||||
engine, err := testfixture.NewEngine(testfixture.Config{},
|
||||
testfixture.WithPromptFS(contractPromptFS("prompt", "profile", "message"), "."),
|
||||
testfixture.WithBackend(testfixture.Backend{
|
||||
ID: "concurrent",
|
||||
Endpoint: "http://example.test/v1",
|
||||
}),
|
||||
promptkit.WithProfiles(promptkit.Profile{
|
||||
testfixture.WithProfiles(testfixture.Profile{
|
||||
ID: "profile",
|
||||
BackendID: "concurrent",
|
||||
Model: "model",
|
||||
|
||||
Reference in New Issue
Block a user