package promptkit_test import ( "context" "fmt" "strings" "testing" "go.maximumdirect.net/configkit" "go.maximumdirect.net/promptkit" ) func TestBundledCompositionReplacementAndInheritance(t *testing.T) { underlay := configkit.Input{Source: configkit.Mapping("caller underlay", map[string]any{ "profiles": []any{map[string]any{"id": "gpt-sol-latest", "model": "underlay-model"}}, })} underlayEngine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Underlays: []configkit.Underlay{{Inputs: []configkit.Input{underlay}}}}}) if err != nil { t.Fatal(err) } underlayProfile, err := underlayEngine.InspectProfile(context.Background(), "gpt-sol-latest") if err != nil || underlayProfile.EffectiveModelParams.Model != "underlay-model" || underlayProfile.EffectiveModelParams.BackendID != "" { t.Fatalf("underlay replacement = %+v, %v", underlayProfile, err) } main := configkit.Input{Source: configkit.Mapping("caller main", map[string]any{ "backends": []any{map[string]any{"id": "openrouter", "endpoint": "https://local.invalid/v1"}, map[string]any{"id": "other", "endpoint": "https://other.invalid/v1"}}, "profiles": []any{ map[string]any{"id": "gpt-sol-latest", "model": "main-model"}, map[string]any{"id": "custom", "extends": map[string]any{"id": "gpt-luna-latest"}, "model": "custom-model"}, map[string]any{"id": "auto", "backend": "other", "model": "other-model"}, }, "collections": map[string]any{"openrouter": map[string]any{ "backends": []any{map[string]any{"id": "local", "endpoint": "https://collection.invalid/v1"}}, "profiles": []any{map[string]any{"id": "profile", "backend": "openrouter/local", "model": "collection-model"}}, }}, })} engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Underlays: []configkit.Underlay{{Inputs: []configkit.Input{underlay}}}, Main: &main}}) if err != nil { t.Fatal(err) } inspect := func(id string) promptkit.ExecutionTarget { t.Helper() profile, err := engine.InspectProfile(context.Background(), id) if err != nil { t.Fatal(err) } return profile.EffectiveModelParams } if target := inspect("gpt-sol-latest"); target.Model != "main-model" || target.BackendID != "" { t.Fatalf("replacement = %+v", target) } if target := inspect("custom"); target.Model != "custom-model" || target.BackendID != "openrouter" || target.Endpoint != "https://local.invalid/v1" { t.Fatalf("inherited = %+v", target) } if target := inspect("gpt-luna-latest"); target.Endpoint != "https://local.invalid/v1" || target.APIKeyEnv != "" { t.Fatalf("backend replacement = %+v", target) } if target := inspect("openrouter/profile"); target.BackendID != "openrouter/local" || target.Model != "collection-model" { t.Fatalf("collection = %+v", target) } for _, tier := range []string{"low", "medium", "high", "xhigh", "max"} { target := inspect("auto-" + tier) if target.BackendID != "other" || target.Model != "other-model" || !strings.Contains(fmt.Sprint(target.ExtraParams), "cost_tier:"+tier) || !strings.Contains(fmt.Sprint(target.ExtraParams), "id:auto-router") { t.Errorf("tier %s = %+v", tier, target) } } } func TestBundledCompositionSequenceModes(t *testing.T) { for _, test := range []struct { name, mode, id string wantErr bool count int }{ {"duplicate append", "append", "auto", true, 0}, {"distinct append", "append", "custom", false, 18}, {"replace", "replace", "custom", false, 1}, } { t.Run(test.name, func(t *testing.T) { input := configkit.Input{Source: configkit.Mapping("sequence", map[string]any{ "config": map[string]any{"sequences": []any{map[string]any{"destination": []any{"profiles"}, "mode": test.mode}}}, "profiles": []any{map[string]any{"id": test.id, "model": "custom-model"}}, })} engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &input}}) if test.wantErr { if err == nil { t.Fatal("expected duplicate rejection") } return } if err != nil { t.Fatal(err) } profiles, err := engine.ListProfiles(context.Background()) if err != nil || len(profiles) != test.count { t.Fatalf("profiles = %v, %v", profiles, err) } }) } } func TestBundledCompositionRejectsStrictConflictsAndMissingBackends(t *testing.T) { profile := configkit.Input{Source: configkit.Mapping("profile", map[string]any{"profiles": []any{map[string]any{"id": "auto", "model": "caller"}}})} _, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Underlays: []configkit.Underlay{{Inputs: []configkit.Input{profile, profile}}}}}) if err == nil { t.Fatal("strict group accepted duplicate records") } input := configkit.Input{Source: configkit.Mapping("replace backends", map[string]any{ "config": map[string]any{"sequences": []any{map[string]any{"destination": []any{"backends"}, "mode": "replace"}}}, "backends": []any{map[string]any{"id": "other", "endpoint": "https://other.invalid/v1"}}, })} if _, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &input}}); err == nil { t.Fatal("missing bundled backend was accepted") } }