Update public configuration and inventory contracts

This commit is contained in:
2026-11-02 12:50:58 +00:00
parent 682cb84c82
commit 6b8eefd18b
7 changed files with 89 additions and 98 deletions

View File

@@ -40,7 +40,7 @@ func TestCollectionInputLoadsNativeImportsAndLaterOverride(t *testing.T) {
if err := os.WriteFile(mainPath, []byte("config:\n imports:\n - path: defaults.yaml\n required: true\n"), 0o600); err != nil { if err := os.WriteFile(mainPath, []byte("config:\n imports:\n - path: defaults.yaml\n required: true\n"), 0o600); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := os.WriteFile(defaultsPath, []byte("prompts:\n greeting:\n version: imported\n messages:\n - role: user\n content: Hello\n output:\n format: text\n validation_mode: none\n"), 0o600); err != nil { if err := os.WriteFile(defaultsPath, []byte("prompts:\n - id: greeting\n messages:\n - role: user\n content: Hello\n output:\n format: text\n validation_mode: none\n"), 0o600); err != nil {
t.Fatal(err) t.Fatal(err)
} }
input, err := promptkit.CollectionInput("docs", configkit.File(mainPath)) input, err := promptkit.CollectionInput("docs", configkit.File(mainPath))
@@ -50,7 +50,7 @@ func TestCollectionInputLoadsNativeImportsAndLaterOverride(t *testing.T) {
engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{ engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{
Underlays: []configkit.Underlay{{Inputs: []configkit.Input{input}}}, Underlays: []configkit.Underlay{{Inputs: []configkit.Input{input}}},
Main: &configkit.Input{ Main: &configkit.Input{
Source: configkit.Mapping("later collection override", map[string]any{"prompts": map[string]any{"greeting": map[string]any{"version": "overridden"}}}), Source: configkit.Mapping("later collection override", map[string]any{"prompts": []any{map[string]any{"id": "greeting", "inputs": []any{map[string]any{"name": "overridden"}}, "messages": []any{map[string]any{"role": "user", "content": "Updated"}}, "output": map[string]any{"format": "text", "validation_mode": "none"}}}}),
Destination: configkit.Keys("collections", "docs"), Destination: configkit.Keys("collections", "docs"),
}, },
}}) }})
@@ -58,7 +58,7 @@ func TestCollectionInputLoadsNativeImportsAndLaterOverride(t *testing.T) {
t.Fatalf("construct engine with native collection import: %v", err) t.Fatalf("construct engine with native collection import: %v", err)
} }
inspection, err := engine.InspectPrompt(context.Background(), "docs/greeting") inspection, err := engine.InspectPrompt(context.Background(), "docs/greeting")
if err != nil || inspection.PromptVersion == nil || *inspection.PromptVersion != "overridden" { if err != nil || inspection.PromptID != "docs/greeting" || len(inspection.Inputs) != 1 || inspection.Inputs[0].Name != "overridden" {
t.Fatalf("inspect imported and overlaid prompt = (%+v, %v)", inspection, err) t.Fatalf("inspect imported and overlaid prompt = (%+v, %v)", inspection, err)
} }
} }
@@ -66,7 +66,7 @@ func TestCollectionInputLoadsNativeImportsAndLaterOverride(t *testing.T) {
func TestCollectionInputLoadsEmbeddedImportsAndLaterOverride(t *testing.T) { func TestCollectionInputLoadsEmbeddedImportsAndLaterOverride(t *testing.T) {
files := fstest.MapFS{ files := fstest.MapFS{
"collection.yaml": {Data: []byte("config:\n imports:\n - path: defaults.yaml\n required: true\n")}, "collection.yaml": {Data: []byte("config:\n imports:\n - path: defaults.yaml\n required: true\n")},
"defaults.yaml": {Data: []byte("prompts:\n greeting:\n version: imported\n messages:\n - role: user\n content: Hello\n output:\n format: text\n validation_mode: none\n")}, "defaults.yaml": {Data: []byte("prompts:\n - id: greeting\n messages:\n - role: user\n content: Hello\n output:\n format: text\n validation_mode: none\n")},
} }
filesystem := configkit.NewFilesystem("embedded collection", files) filesystem := configkit.NewFilesystem("embedded collection", files)
input, err := promptkit.CollectionInput("docs", filesystem.File("collection.yaml")) input, err := promptkit.CollectionInput("docs", filesystem.File("collection.yaml"))
@@ -77,7 +77,7 @@ func TestCollectionInputLoadsEmbeddedImportsAndLaterOverride(t *testing.T) {
engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{ engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{
Underlays: []configkit.Underlay{{Inputs: []configkit.Input{input}}}, Underlays: []configkit.Underlay{{Inputs: []configkit.Input{input}}},
Main: &configkit.Input{ Main: &configkit.Input{
Source: configkit.Mapping("later collection override", map[string]any{"prompts": map[string]any{"greeting": map[string]any{"version": "overridden"}}}), Source: configkit.Mapping("later collection override", map[string]any{"prompts": []any{map[string]any{"id": "greeting", "inputs": []any{map[string]any{"name": "overridden"}}, "messages": []any{map[string]any{"role": "user", "content": "Updated"}}, "output": map[string]any{"format": "text", "validation_mode": "none"}}}}),
Destination: configkit.Keys("collections", "docs"), Destination: configkit.Keys("collections", "docs"),
}, },
}}) }})
@@ -85,7 +85,7 @@ func TestCollectionInputLoadsEmbeddedImportsAndLaterOverride(t *testing.T) {
t.Fatalf("construct engine with embedded collection import: %v", err) t.Fatalf("construct engine with embedded collection import: %v", err)
} }
inspection, err := engine.InspectPrompt(context.Background(), "docs/greeting") inspection, err := engine.InspectPrompt(context.Background(), "docs/greeting")
if err != nil || inspection.PromptVersion == nil || *inspection.PromptVersion != "overridden" { if err != nil || inspection.PromptID != "docs/greeting" || len(inspection.Inputs) != 1 || inspection.Inputs[0].Name != "overridden" {
t.Fatalf("inspect imported and overlaid prompt = (%+v, %v)", inspection, err) t.Fatalf("inspect imported and overlaid prompt = (%+v, %v)", inspection, err)
} }
} }

View File

@@ -73,8 +73,8 @@ func TestProfileEnvironmentLookupAcceptsNonShellNames(t *testing.T) {
func TestPublicConstructionWithLocalBackendAndProgrammaticProfile(t *testing.T) { func TestPublicConstructionWithLocalBackendAndProgrammaticProfile(t *testing.T) {
backendID, model := "workstation", "offline-model" backendID, model := "workstation", "offline-model"
prompt := configkit.Input{Source: configkit.Mapping("prompt", map[string]any{ prompt := configkit.Input{Source: configkit.Mapping("prompt", map[string]any{
"prompts": map[string]any{"hello": map[string]any{ "prompts": []any{map[string]any{
"version": "1", "id": "hello",
"default_profile": "offline", "default_profile": "offline",
"messages": []any{map[string]any{"role": "user", "content": "Hello"}}, "messages": []any{map[string]any{"role": "user", "content": "Hello"}},
"output": map[string]any{"format": "text", "validation_mode": "none"}, "output": map[string]any{"format": "text", "validation_mode": "none"},
@@ -143,12 +143,11 @@ func TestPublicConstructionCombinesEmbeddedOperatorAndProgrammaticInputs(t *test
embedded, err := promptkit.FilesystemInputs("embedded defaults", fstest.MapFS{ embedded, err := promptkit.FilesystemInputs("embedded defaults", fstest.MapFS{
"config/default.yaml": &fstest.MapFile{Data: []byte(` "config/default.yaml": &fstest.MapFile{Data: []byte(`
profiles: profiles:
application: - id: application
endpoint: https://example.invalid/v1 endpoint: https://example.invalid/v1
model: default-model model: default-model
prompts: prompts:
hello: - id: hello
version: "1"
default_profile: application default_profile: application
messages: messages:
- role: user - role: user
@@ -162,16 +161,16 @@ prompts:
t.Fatalf("select embedded defaults: %v", err) t.Fatalf("select embedded defaults: %v", err)
} }
operatorDirectory := t.TempDir() operatorDirectory := t.TempDir()
if err := os.WriteFile(filepath.Join(operatorDirectory, "override.yaml"), []byte("profiles:\n application:\n model: operator-model\n"), 0o644); err != nil { if err := os.WriteFile(filepath.Join(operatorDirectory, "override.yaml"), []byte("profiles:\n - id: application\n endpoint: https://example.invalid/v1\n model: operator-model\n"), 0o644); err != nil {
t.Fatalf("write operator input: %v", err) t.Fatalf("write operator input: %v", err)
} }
operator, err := promptkit.DirectoryInputs(operatorDirectory) operator, err := promptkit.DirectoryInputs(operatorDirectory)
if err != nil { if err != nil {
t.Fatalf("select operator inputs: %v", err) t.Fatalf("select operator inputs: %v", err)
} }
programmaticModel := "programmatic-model" programmaticModel, programmaticEndpoint := "programmatic-model", "https://example.invalid/v1"
programmatic := configkit.Input{Source: promptkit.ProfileSource("programmatic settings", []promptkit.ProfileConfig{ programmatic := configkit.Input{Source: promptkit.ProfileSource("programmatic settings", []promptkit.ProfileConfig{
{ID: "application", Model: &programmaticModel}, {ID: "application", Endpoint: &programmaticEndpoint, Model: &programmaticModel},
})} })}
for _, test := range []struct { for _, test := range []struct {
name string name string
@@ -207,7 +206,7 @@ prompts:
func TestPublicConstructionPreservesConfigKitLoadError(t *testing.T) { func TestPublicConstructionPreservesConfigKitLoadError(t *testing.T) {
engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &configkit.Input{ engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &configkit.Input{
Source: configkit.Mapping("invalid", map[string]any{"profiles": map[string]any{"broken": map[string]any{"unknown": true}}}), Source: configkit.Mapping("invalid", map[string]any{"profiles": []any{map[string]any{"id": "broken", "unknown": true}}}),
}}}) }}})
var loadErr *configkit.LoadError var loadErr *configkit.LoadError
if engine != nil || !errors.Is(err, promptkit.ErrInvalidConfig) || !errors.As(err, &loadErr) { if engine != nil || !errors.Is(err, promptkit.ErrInvalidConfig) || !errors.As(err, &loadErr) {
@@ -291,7 +290,7 @@ func TestBackendSourceOwnsSmokeTestValuesAndDefersDefaultLookup(t *testing.T) {
"https://backend.example/v1", "https://backend.example/v1",
"not-yet-listed", "not-yet-listed",
"https://probe.example/ready?check=1", "https://probe.example/ready?check=1",
"openrouter_key", " OPENROUTER_KEY ",
"https://probe.example/key?scope=current", "https://probe.example/key?scope=current",
} { } {
if !strings.Contains(contents, want) { if !strings.Contains(contents, want) {

View File

@@ -701,6 +701,13 @@ inspection, listing, and namespace contract tests.
Validation: run the affected external-package contract tests using real ConfigKit Validation: run the affected external-package contract tests using real ConfigKit
composition and offline sources. Do not add index/list-position guarantees. composition and offline sources. Do not add index/list-position guarantees.
Completion: public source, construction, profile inheritance/inspection,
namespace, and inventory fixtures now use explicit-ID lists and identity
inheritance. Layered replacements supply complete records, and configuration
inspection assertions reflect composed values. Focused offline contract tests
passed on Go 1.27.0. Tests depending on the shared legacy execution fixture and
the full root package remain assigned to subsequent fixture migrations.
## Stage 16: Migrate Internal Execution And Rendering Fixtures ## Stage 16: Migrate Internal Execution And Rendering Fixtures
Scope: remaining `internal/usecase`, `internal/prompt`, `internal/domain`, and Scope: remaining `internal/usecase`, `internal/prompt`, `internal/domain`, and

View File

@@ -17,8 +17,8 @@ import (
func TestListIdentitiesBasicContract(t *testing.T) { func TestListIdentitiesBasicContract(t *testing.T) {
engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &configkit.Input{ engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &configkit.Input{
Source: configkit.Mapping("listing", map[string]any{ Source: configkit.Mapping("listing", map[string]any{
"profiles": map[string]any{"configured-profile": map[string]any{"model": "example-model"}}, "profiles": []any{map[string]any{"id": "configured-profile", "model": "example-model"}},
"prompts": map[string]any{"example-prompt": promptDefinitionWithVersion("10", listingContractPrompt())}, "prompts": []any{listingContractPrompt("example-prompt")},
}), }),
}}}) }}})
if err != nil { if err != nil {
@@ -38,13 +38,13 @@ func TestListIdentitiesBasicContract(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
want := []promptkit.PromptIdentity{ want := []promptkit.PromptIdentity{
{ID: "example-prompt", Version: stringPointerForTest("10")}, {ID: "example-prompt"},
} }
if len(prompts) != len(want) { if len(prompts) != len(want) {
t.Fatalf("prompt listing = %#v, want %#v", prompts, want) t.Fatalf("prompt listing = %#v, want %#v", prompts, want)
} }
for i := range want { for i := range want {
if prompts[i].ID != want[i].ID || prompts[i].Version == nil || *prompts[i].Version != *want[i].Version { if prompts[i].ID != want[i].ID {
t.Fatalf("prompt listing = %#v, want %#v", prompts, want) t.Fatalf("prompt listing = %#v, want %#v", prompts, want)
} }
} }
@@ -54,8 +54,7 @@ func TestListIdentitiesBasicContract(t *testing.T) {
t.Fatalf("listed profile could not be inspected: (%+v, %v)", profile, err) t.Fatalf("listed profile could not be inspected: (%+v, %v)", profile, err)
} }
prompt, err := engine.InspectPrompt(context.Background(), prompts[0].ID) prompt, err := engine.InspectPrompt(context.Background(), prompts[0].ID)
if err != nil || prompt == nil || prompt.PromptID != prompts[0].ID || prompt.PromptVersion == nil || if err != nil || prompt == nil || prompt.PromptID != prompts[0].ID {
*prompt.PromptVersion != *prompts[0].Version {
t.Fatalf("listed prompt could not be inspected: (%+v, %v)", prompt, err) t.Fatalf("listed prompt could not be inspected: (%+v, %v)", prompt, err)
} }
} }
@@ -135,16 +134,15 @@ func TestScopedIdentityListingsApplyNamespaceFilters(t *testing.T) {
} }
prompts, err := scoped.ListPrompts(context.Background(), promptkit.NamespaceFilter{Mode: promptkit.NamespaceNamed, Name: "team"}) prompts, err := scoped.ListPrompts(context.Background(), promptkit.NamespaceFilter{Mode: promptkit.NamespaceNamed, Name: "team"})
if err != nil || len(prompts) != 1 || prompts[0].ID != "team/prompt" || prompts[0].Version == nil || *prompts[0].Version != "team-v1" { if err != nil || len(prompts) != 1 || prompts[0].ID != "team/prompt" {
t.Fatalf("named prompt identity = %#v, %v", prompts, err) t.Fatalf("named prompt identity = %#v, %v", prompts, err)
} }
if _, err := scoped.InspectPrompt(context.Background(), prompts[0].ID); err != nil { if _, err := scoped.InspectPrompt(context.Background(), prompts[0].ID); err != nil {
t.Fatalf("listed full prompt ID did not round trip: %v", err) t.Fatalf("listed full prompt ID did not round trip: %v", err)
} }
*prompts[0].Version = "caller change"
prompts[0].ID = "caller change" prompts[0].ID = "caller change"
prompts, err = scoped.ListPrompts(context.Background(), promptkit.NamespaceFilter{Mode: promptkit.NamespaceNamed, Name: "team"}) prompts, err = scoped.ListPrompts(context.Background(), promptkit.NamespaceFilter{Mode: promptkit.NamespaceNamed, Name: "team"})
if err != nil || len(prompts) != 1 || prompts[0].ID != "team/prompt" || prompts[0].Version == nil || *prompts[0].Version != "team-v1" { if err != nil || len(prompts) != 1 || prompts[0].ID != "team/prompt" {
t.Fatalf("mutating filtered prompt inventory escaped: %#v, %v", prompts, err) t.Fatalf("mutating filtered prompt inventory escaped: %#v, %v", prompts, err)
} }
profiles, err := scoped.ListProfiles(context.Background(), promptkit.NamespaceFilter{Mode: promptkit.NamespaceNamed, Name: "team"}) profiles, err := scoped.ListProfiles(context.Background(), promptkit.NamespaceFilter{Mode: promptkit.NamespaceNamed, Name: "team"})
@@ -214,9 +212,9 @@ func newNamespaceListingEngine(t *testing.T) *promptkit.Engine {
t.Helper() t.Helper()
collection := func(namespace string) map[string]any { collection := func(namespace string) map[string]any {
return map[string]any{ return map[string]any{
"backends": map[string]any{"backend": map[string]any{"endpoint": "https://example.test/v1"}}, "backends": []any{map[string]any{"id": "backend", "endpoint": "https://example.test/v1"}},
"profiles": map[string]any{"profile": map[string]any{"backend": namespace + "/backend", "model": "example-model"}}, "profiles": []any{map[string]any{"id": "profile", "backend": namespace + "/backend", "model": "example-model"}},
"prompts": map[string]any{"prompt": promptDefinitionWithVersion("team-v1", listingContractPrompt())}, "prompts": []any{listingContractPrompt("prompt")},
} }
} }
input := configkit.Input{Source: configkit.Mapping("namespaced listing", map[string]any{ input := configkit.Input{Source: configkit.Mapping("namespaced listing", map[string]any{
@@ -224,9 +222,9 @@ func newNamespaceListingEngine(t *testing.T) *promptkit.Engine {
"team": collection("team"), "team": collection("team"),
"team-blue": collection("team-blue"), "team-blue": collection("team-blue"),
}, },
"backends": map[string]any{"root-backend": map[string]any{"endpoint": "https://root.example.test/v1"}}, "backends": []any{map[string]any{"id": "root-backend", "endpoint": "https://root.example.test/v1"}},
"profiles": map[string]any{"root-profile": map[string]any{"backend": "root-backend", "model": "root-model"}}, "profiles": []any{map[string]any{"id": "root-profile", "backend": "root-backend", "model": "root-model"}},
"prompts": map[string]any{"root-prompt": promptDefinitionWithVersion("root-v1", listingContractPrompt())}, "prompts": []any{listingContractPrompt("root-prompt")},
})} })}
engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &input}}) engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &input}})
if err != nil { if err != nil {
@@ -241,18 +239,18 @@ func newNamespaceListingEngine(t *testing.T) *promptkit.Engine {
func TestResourceAndCollectionInventories(t *testing.T) { func TestResourceAndCollectionInventories(t *testing.T) {
underlay := configkit.Input{Source: configkit.Mapping("inventory defaults", map[string]any{ underlay := configkit.Input{Source: configkit.Mapping("inventory defaults", map[string]any{
"backends": map[string]any{"root-backend": map[string]any{"endpoint": "https://root.example.test/v1"}}, "backends": []any{map[string]any{"id": "root-backend", "endpoint": "https://root.example.test/v1"}},
"profiles": map[string]any{"root-profile": map[string]any{"endpoint": "https://root.example.test/v1", "model": "root-model"}}, "profiles": []any{map[string]any{"id": "root-profile", "endpoint": "https://root.example.test/v1", "model": "root-model"}},
"prompts": map[string]any{"root-prompt": listingContractPrompt()}, "prompts": []any{listingContractPrompt("root-prompt")},
"collections": map[string]any{ "collections": map[string]any{
"alpha": map[string]any{ "alpha": map[string]any{
"backends": map[string]any{"api": map[string]any{"endpoint": "https://alpha.example.test/v1"}}, "backends": []any{map[string]any{"id": "api", "endpoint": "https://alpha.example.test/v1"}},
"profiles": map[string]any{"profile": map[string]any{"backend": "alpha/api", "model": "alpha-model"}}, "profiles": []any{map[string]any{"id": "profile", "backend": "alpha/api", "model": "alpha-model"}},
"prompts": map[string]any{"prompt": listingContractPrompt()}, "prompts": []any{listingContractPrompt("prompt")},
}, },
"alpha-beta": map[string]any{"prompts": map[string]any{"prompt": listingContractPrompt()}}, "alpha-beta": map[string]any{"prompts": []any{listingContractPrompt("prompt")}},
"beta": map[string]any{"backends": map[string]any{"api": map[string]any{"endpoint": "https://beta.example.test/v1"}}}, "beta": map[string]any{"backends": []any{map[string]any{"id": "api", "endpoint": "https://beta.example.test/v1"}}},
"resource-only": map[string]any{"profiles": map[string]any{"profile": map[string]any{"endpoint": "https://resource.example.test/v1", "model": "resource-model"}}}, "resource-only": map[string]any{"profiles": []any{map[string]any{"id": "profile", "endpoint": "https://resource.example.test/v1", "model": "resource-model"}}},
"metadata-only": map[string]any{"release_version": "v1"}, "metadata-only": map[string]any{"release_version": "v1"},
"empty-label": map[string]any{"release_version": ""}, "empty-label": map[string]any{"release_version": ""},
"null-label": map[string]any{"release_version": nil}, "null-label": map[string]any{"release_version": nil},
@@ -363,8 +361,8 @@ func TestListIdentitiesInvalidEngine(t *testing.T) {
func TestListIdentitiesPreserveContextErrorsWithoutPartialResults(t *testing.T) { func TestListIdentitiesPreserveContextErrorsWithoutPartialResults(t *testing.T) {
engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &configkit.Input{ engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &configkit.Input{
Source: configkit.Mapping("listing", map[string]any{ Source: configkit.Mapping("listing", map[string]any{
"profiles": map[string]any{"profile": map[string]any{}}, "profiles": []any{map[string]any{"id": "profile"}},
"prompts": map[string]any{"example": promptDefinitionWithVersion("v1", listingContractPrompt())}, "prompts": []any{listingContractPrompt("example")},
}), }),
}}}) }}})
if err != nil { if err != nil {
@@ -399,26 +397,20 @@ func TestListIdentitiesPreserveContextErrorsWithoutPartialResults(t *testing.T)
func TestListIdentitiesUseEffectiveLayeredMembership(t *testing.T) { func TestListIdentitiesUseEffectiveLayeredMembership(t *testing.T) {
underlay := configkit.Input{Source: configkit.Mapping("consumer defaults", map[string]any{ underlay := configkit.Input{Source: configkit.Mapping("consumer defaults", map[string]any{
"collections": map[string]any{"shared": map[string]any{ "collections": map[string]any{"shared": map[string]any{
"profiles": map[string]any{ "profiles": []any{
"base": map[string]any{"endpoint": "https://example.test/v1", "model": "base-model"}, map[string]any{"id": "base", "endpoint": "https://example.test/v1", "model": "base-model"},
"child": map[string]any{"extends": "base"}, map[string]any{"id": "child", "extends": map[string]any{"id": "base"}},
"overlay": map[string]any{"endpoint": "https://example.test/v1", "model": "old-model"}, map[string]any{"id": "overlay", "endpoint": "https://example.test/v1", "model": "old-model"},
},
"prompts": map[string]any{
"prompt": promptDefinitionWithVersion("v1", listingContractPrompt()),
}, },
"prompts": []any{listingContractPrompt("prompt")},
}}, }},
"prompts": map[string]any{ "prompts": []any{listingContractPrompt("underlay-only")},
"underlay-only": promptDefinitionWithVersion("v1", listingContractPrompt()),
},
})} })}
main := configkit.Input{Source: configkit.Mapping("application overlay", map[string]any{ main := configkit.Input{Source: configkit.Mapping("application overlay", map[string]any{
"collections": map[string]any{"shared": map[string]any{"profiles": map[string]any{ "collections": map[string]any{"shared": map[string]any{"profiles": []any{
"overlay": map[string]any{"model": "effective-model"}, map[string]any{"id": "overlay", "model": "effective-model"},
}}}, }}},
"prompts": map[string]any{ "prompts": []any{listingContractPrompt("main-only")},
"main-only": promptDefinitionWithVersion("v3", listingContractPrompt()),
},
})} })}
engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{ engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{
Underlays: []configkit.Underlay{{Inputs: []configkit.Input{underlay}}}, Underlays: []configkit.Underlay{{Inputs: []configkit.Input{underlay}}},
@@ -459,14 +451,14 @@ func TestListIdentitiesUseEffectiveLayeredMembership(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
wantPrompts := []promptkit.PromptIdentity{ wantPrompts := []promptkit.PromptIdentity{
{ID: "main-only", Version: stringPointerForTest("v3")}, {ID: "main-only"},
{ID: "shared/prompt", Version: stringPointerForTest("v1")}, {ID: "shared/prompt"},
{ID: "underlay-only", Version: stringPointerForTest("v1")}, {ID: "underlay-only"},
} }
if !reflect.DeepEqual(prompts, wantPrompts) { if !reflect.DeepEqual(prompts, wantPrompts) {
t.Fatalf("effective prompt identities = %#v, want %#v", prompts, wantPrompts) t.Fatalf("effective prompt identities = %#v, want %#v", prompts, wantPrompts)
} }
if got := countListingPrompt(prompts, "shared/prompt", "v1"); got != 1 { if got := countListingPrompt(prompts, "shared/prompt"); got != 1 {
t.Fatalf("overlaid prompt identity appears %d times, want 1", got) t.Fatalf("overlaid prompt identity appears %d times, want 1", got)
} }
} }
@@ -488,8 +480,8 @@ func TestListIdentitiesEmptyEffectiveConfiguration(t *testing.T) {
func TestListIdentitiesAreOwnedAndSafeForConcurrentUse(t *testing.T) { func TestListIdentitiesAreOwnedAndSafeForConcurrentUse(t *testing.T) {
engine, err := newListingContractEngine(t, map[string]any{ engine, err := newListingContractEngine(t, map[string]any{
"profiles": map[string]any{"profile": map[string]any{"model": "partial-model"}}, "profiles": []any{map[string]any{"id": "profile", "model": "partial-model"}},
"prompts": map[string]any{"prompt": promptDefinitionWithVersion("v1", listingContractPrompt())}, "prompts": []any{listingContractPrompt("prompt")},
}) })
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -511,9 +503,6 @@ func TestListIdentitiesAreOwnedAndSafeForConcurrentUse(t *testing.T) {
for i := range prompts { for i := range prompts {
if prompts[i].ID == "prompt" { if prompts[i].ID == "prompt" {
prompts[i].ID = "caller mutation" prompts[i].ID = "caller mutation"
if prompts[i].Version != nil {
*prompts[i].Version = "caller mutation"
}
} }
} }
profiles, err = engine.ListProfiles(context.Background()) profiles, err = engine.ListProfiles(context.Background())
@@ -521,7 +510,7 @@ func TestListIdentitiesAreOwnedAndSafeForConcurrentUse(t *testing.T) {
t.Fatalf("profile result mutation escaped into engine: %#v, %v", profiles, err) t.Fatalf("profile result mutation escaped into engine: %#v, %v", profiles, err)
} }
prompts, err = engine.ListPrompts(context.Background()) prompts, err = engine.ListPrompts(context.Background())
if err != nil || countListingPrompt(prompts, "prompt", "v1") != 1 { if err != nil || countListingPrompt(prompts, "prompt") != 1 {
t.Fatalf("prompt result mutation escaped into engine: %#v, %v", prompts, err) t.Fatalf("prompt result mutation escaped into engine: %#v, %v", prompts, err)
} }
@@ -557,9 +546,6 @@ func TestListIdentitiesAreOwnedAndSafeForConcurrentUse(t *testing.T) {
return return
} }
prompts[0].ID = "worker mutation" prompts[0].ID = "worker mutation"
if prompts[0].Version != nil {
*prompts[0].Version = "worker mutation"
}
if _, err := engine.InspectProfile(context.Background(), "profile"); err != nil { if _, err := engine.InspectProfile(context.Background(), "profile"); err != nil {
results <- fmt.Errorf("inspect profile: %w", err) results <- fmt.Errorf("inspect profile: %w", err)
@@ -582,7 +568,7 @@ func TestListIdentitiesAreOwnedAndSafeForConcurrentUse(t *testing.T) {
func TestListIdentitiesUseCapturedSourcesWithoutExecutionWork(t *testing.T) { func TestListIdentitiesUseCapturedSourcesWithoutExecutionWork(t *testing.T) {
files := fstest.MapFS{ files := fstest.MapFS{
"config/listing.yaml": &fstest.MapFile{Data: []byte("profiles:\n captured-partial:\n model: partial-model\nprompts:\n captured-prompt:\n version: v1\n messages:\n - role: user\n content: Hello.\n output:\n format: text\n validation_mode: basic\n")}, "config/listing.yaml": &fstest.MapFile{Data: []byte("profiles:\n - id: captured-partial\n model: partial-model\nprompts:\n - id: captured-prompt\n messages:\n - role: user\n content: Hello.\n output:\n format: text\n validation_mode: basic\n")},
} }
inputs, err := promptkit.FilesystemInputs("application listing", files, "config") inputs, err := promptkit.FilesystemInputs("application listing", files, "config")
if err != nil { if err != nil {
@@ -594,20 +580,21 @@ func TestListIdentitiesUseCapturedSourcesWithoutExecutionWork(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
files["config/listing.yaml"].Data = []byte("profiles:\n replacement-profile: {}\nprompts:\n replacement-prompt:\n version: v2\n messages:\n - role: user\n content: Changed.\n output:\n format: text\n validation_mode: basic\n") files["config/listing.yaml"].Data = []byte("profiles:\n - id: replacement-profile\nprompts:\n - id: replacement-prompt\n messages:\n - role: user\n content: Changed.\n output:\n format: text\n validation_mode: basic\n")
profiles, err := engine.ListProfiles(context.Background()) profiles, err := engine.ListProfiles(context.Background())
if err != nil || !containsListingProfile(profiles, "captured-partial") || containsListingProfile(profiles, "replacement-profile") { if err != nil || !containsListingProfile(profiles, "captured-partial") || containsListingProfile(profiles, "replacement-profile") {
t.Fatalf("profile listing did not use captured source: %#v, %v", profiles, err) t.Fatalf("profile listing did not use captured source: %#v, %v", profiles, err)
} }
prompts, err := engine.ListPrompts(context.Background()) prompts, err := engine.ListPrompts(context.Background())
if err != nil || countListingPrompt(prompts, "captured-prompt", "v1") != 1 || countListingPrompt(prompts, "replacement-prompt", "v2") != 0 { if err != nil || countListingPrompt(prompts, "captured-prompt") != 1 || countListingPrompt(prompts, "replacement-prompt") != 0 {
t.Fatalf("prompt listing did not use captured source: %#v, %v", prompts, err) t.Fatalf("prompt listing did not use captured source: %#v, %v", prompts, err)
} }
} }
func listingContractPrompt() map[string]any { func listingContractPrompt(id string) map[string]any {
return map[string]any{ return map[string]any{
"id": id,
"messages": []any{map[string]any{"role": "user", "content": "Hello."}}, "messages": []any{map[string]any{"role": "user", "content": "Hello."}},
"output": map[string]any{"format": "text", "validation_mode": "basic"}, "output": map[string]any{"format": "text", "validation_mode": "basic"},
} }
@@ -632,10 +619,10 @@ func countListingProfile(profiles []string, id string) int {
return count return count
} }
func countListingPrompt(prompts []promptkit.PromptIdentity, id, version string) int { func countListingPrompt(prompts []promptkit.PromptIdentity, id string) int {
count := 0 count := 0
for _, prompt := range prompts { for _, prompt := range prompts {
if prompt.ID == id && prompt.Version != nil && *prompt.Version == version { if prompt.ID == id {
count++ count++
} }
} }

View File

@@ -268,12 +268,11 @@ func newNamespacedContractEngine(t *testing.T, client promptkit.LLMClient) *prom
for _, namespace := range []string{"alpha", "beta"} { for _, namespace := range []string{"alpha", "beta"} {
endpoint := "https://" + namespace + ".example/v1" endpoint := "https://" + namespace + ".example/v1"
input, err := promptkit.CollectionInput(namespace, configkit.Mapping(namespace+" resources", map[string]any{ input, err := promptkit.CollectionInput(namespace, configkit.Mapping(namespace+" resources", map[string]any{
"backends": map[string]any{"backend": map[string]any{"endpoint": endpoint}}, "backends": []any{map[string]any{"id": "backend", "endpoint": endpoint}},
"profiles": map[string]any{"shared": map[string]any{ "profiles": []any{map[string]any{"id": "shared",
"backend": namespace + "/backend", "model": namespace + "-model", "backend": namespace + "/backend", "model": namespace + "-model",
}}, }},
"prompts": map[string]any{"shared": map[string]any{ "prompts": []any{map[string]any{"id": "shared",
"version": "1",
"default_profile": "root-only", "default_profile": "root-only",
"messages": []any{map[string]any{"role": "user", "content": "Hello"}}, "messages": []any{map[string]any{"role": "user", "content": "Hello"}},
"output": map[string]any{"format": "text", "validation_mode": "none"}, "output": map[string]any{"format": "text", "validation_mode": "none"},
@@ -286,11 +285,10 @@ func newNamespacedContractEngine(t *testing.T, client promptkit.LLMClient) *prom
inputs = append(inputs, input) inputs = append(inputs, input)
} }
inputs = append(inputs, configkit.Input{Source: configkit.Mapping("root resources", map[string]any{ inputs = append(inputs, configkit.Input{Source: configkit.Mapping("root resources", map[string]any{
"profiles": map[string]any{"root-only": map[string]any{ "profiles": []any{map[string]any{"id": "root-only",
"endpoint": "https://root.example/v1", "model": "root-model", "endpoint": "https://root.example/v1", "model": "root-model",
}}, }},
"prompts": map[string]any{"root-only": map[string]any{ "prompts": []any{map[string]any{"id": "root-only",
"version": "1",
"messages": []any{map[string]any{"role": "user", "content": "Root prompt"}}, "messages": []any{map[string]any{"role": "user", "content": "Root prompt"}},
"output": map[string]any{"format": "text", "validation_mode": "none"}, "output": map[string]any{"format": "text", "validation_mode": "none"},
}}, }},

View File

@@ -61,7 +61,7 @@ func TestProfileInheritanceConfiguredBaseAliasWorkflow(t *testing.T) {
func TestProfileInheritanceYAMLAliasOfConfiguredProfile(t *testing.T) { func TestProfileInheritanceYAMLAliasOfConfiguredProfile(t *testing.T) {
inputs, err := promptkit.FilesystemInputs("profile alias", fstest.MapFS{ inputs, err := promptkit.FilesystemInputs("profile alias", fstest.MapFS{
"config/alias.yaml": &fstest.MapFile{Data: []byte("profiles:\n base:\n model: synthetic-model\n yaml-alias:\n extends: base\n")}, "config/alias.yaml": &fstest.MapFile{Data: []byte("profiles:\n - id: base\n model: synthetic-model\n - id: yaml-alias\n extends: {id: base}\n")},
}, "config") }, "config")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -79,12 +79,12 @@ func TestProfileInheritanceYAMLAliasOfConfiguredProfile(t *testing.T) {
func TestProfileInheritanceRetainsRequiredCredentialBehavior(t *testing.T) { func TestProfileInheritanceRetainsRequiredCredentialBehavior(t *testing.T) {
engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &configkit.Input{ engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &configkit.Input{
Source: configkit.Mapping("credentials", map[string]any{ Source: configkit.Mapping("credentials", map[string]any{
"backends": map[string]any{"credential-backend": map[string]any{ "backends": []any{map[string]any{"id": "credential-backend",
"endpoint": "https://credential.example/v1", "api_key_env": "OPTIONAL_BACKEND_KEY", "endpoint": "https://credential.example/v1", "api_key_env": "OPTIONAL_BACKEND_KEY",
}}, }},
"profiles": map[string]any{ "profiles": []any{
"credential-base": map[string]any{"backend": "credential-backend", "model": "model", "api_key_required": true}, map[string]any{"id": "credential-base", "backend": "credential-backend", "model": "model", "api_key_required": true},
"credential-child": map[string]any{"extends": "credential-base"}, map[string]any{"id": "credential-child", "extends": map[string]any{"id": "credential-base"}},
}, },
}), }),
}}}) }}})
@@ -106,8 +106,8 @@ func TestProfileInheritancePreservesPublicErrorIdentities(t *testing.T) {
t.Fatalf("missing profile = (%+v, %v)", inspection, err) t.Fatalf("missing profile = (%+v, %v)", inspection, err)
} }
for _, document := range []map[string]any{ for _, document := range []map[string]any{
{"profiles": map[string]any{"child": map[string]any{"extends": "missing"}}}, {"profiles": []any{map[string]any{"id": "child", "extends": map[string]any{"id": "missing"}}}},
{"profiles": map[string]any{"a": map[string]any{"extends": "b"}, "b": map[string]any{"extends": "a"}}}, {"profiles": []any{map[string]any{"id": "a", "extends": map[string]any{"id": "b"}}, map[string]any{"id": "b", "extends": map[string]any{"id": "a"}}}},
} { } {
invalid, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &configkit.Input{ invalid, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &configkit.Input{
Source: configkit.Mapping("invalid inheritance", document), Source: configkit.Mapping("invalid inheritance", document),
@@ -120,7 +120,7 @@ func TestProfileInheritancePreservesPublicErrorIdentities(t *testing.T) {
func TestProfileInheritanceFreezesPreparedExecution(t *testing.T) { func TestProfileInheritanceFreezesPreparedExecution(t *testing.T) {
profiles := &mutableInheritanceProfileFS{files: fstest.MapFS{ profiles := &mutableInheritanceProfileFS{files: fstest.MapFS{
"config/profiles.yaml": &fstest.MapFile{Data: []byte("profiles:\n base:\n endpoint: https://base.example/v1\n model: first-model\n child:\n extends: base\n")}, "config/profiles.yaml": &fstest.MapFile{Data: []byte("profiles:\n - id: base\n endpoint: https://base.example/v1\n model: first-model\n - id: child\n extends: {id: base}\n")},
}} }}
inputs, err := promptkit.FilesystemInputs("profiles", profiles, "config") inputs, err := promptkit.FilesystemInputs("profiles", profiles, "config")
if err != nil { if err != nil {
@@ -138,7 +138,7 @@ func TestProfileInheritanceFreezesPreparedExecution(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
profiles.set("config/profiles.yaml", "profiles:\n base:\n endpoint: https://base.example/v1\n model: second-model\n child:\n extends: base\n") profiles.set("config/profiles.yaml", "profiles:\n - id: base\n endpoint: https://base.example/v1\n model: second-model\n - id: child\n extends: {id: base}\n")
result, err := engine.RunPrepared(context.Background(), prepared) result, err := engine.RunPrepared(context.Background(), prepared)
if err != nil || result == nil || client.model != "first-model" { if err != nil || result == nil || client.model != "first-model" {
t.Fatalf("prepared execution = (%+v, %v), model %q", result, err, client.model) t.Fatalf("prepared execution = (%+v, %v), model %q", result, err, client.model)

View File

@@ -12,7 +12,7 @@ import (
func TestInspectProfileUsesLiteralFileKeyForPreparation(t *testing.T) { func TestInspectProfileUsesLiteralFileKeyForPreparation(t *testing.T) {
profiles, err := promptkit.FilesystemInputs("literal profile", fstest.MapFS{ profiles, err := promptkit.FilesystemInputs("literal profile", fstest.MapFS{
"config/profile.yaml": &fstest.MapFile{Data: []byte("profiles:\n ' literal ':\n endpoint: https://example.test/v1\n model: model\n")}, "config/profile.yaml": &fstest.MapFile{Data: []byte("profiles:\n - id: ' literal '\n endpoint: https://example.test/v1\n model: model\n")},
}, "config") }, "config")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -20,8 +20,8 @@ func TestInspectProfileUsesLiteralFileKeyForPreparation(t *testing.T) {
engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{ engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{
Underlays: []configkit.Underlay{{Inputs: profiles}}, Underlays: []configkit.Underlay{{Inputs: profiles}},
Main: &configkit.Input{Source: configkit.Mapping("prompt", map[string]any{ Main: &configkit.Input{Source: configkit.Mapping("prompt", map[string]any{
"prompts": map[string]any{"prompt": map[string]any{ "prompts": []any{map[string]any{
"version": "1", "id": "prompt",
"default_profile": " literal ", "default_profile": " literal ",
"messages": []any{map[string]any{"role": "user", "content": "hello"}}, "messages": []any{map[string]any{"role": "user", "content": "hello"}},
"output": map[string]any{"format": "text", "validation_mode": "none"}, "output": map[string]any{"format": "text", "validation_mode": "none"},
@@ -50,8 +50,8 @@ func TestInspectProfileUsesLiteralFileKeyForPreparation(t *testing.T) {
func TestInspectProfileIsIndependentAndDoesNotInvokeProvider(t *testing.T) { func TestInspectProfileIsIndependentAndDoesNotInvokeProvider(t *testing.T) {
client := &fakeLLMClient{response: &promptkit.GenerateResponse{Content: "unexpected"}} client := &fakeLLMClient{response: &promptkit.GenerateResponse{Content: "unexpected"}}
engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &configkit.Input{ engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &configkit.Input{
Source: configkit.Mapping("inspection", map[string]any{"profiles": map[string]any{ Source: configkit.Mapping("inspection", map[string]any{"profiles": []any{
"profile": map[string]any{ map[string]any{"id": "profile",
"endpoint": "https://example.test/v1", "model": "model", "api_key_required": true, "endpoint": "https://example.test/v1", "model": "model", "api_key_required": true,
"extra_params": map[string]any{"nested": map[string]any{"value": "original"}}, "extra_params": map[string]any{"nested": map[string]any{"value": "original"}},
}, },
@@ -77,7 +77,7 @@ func TestInspectProfilePublicErrorsAndPartialTarget(t *testing.T) {
t.Fatalf("nil engine: %v", err) t.Fatalf("nil engine: %v", err)
} }
engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &configkit.Input{ engine, err := promptkit.NewEngine(promptkit.Config{Sources: promptkit.ConfigurationSources{Main: &configkit.Input{
Source: configkit.Mapping("partial", map[string]any{"profiles": map[string]any{"profile": map[string]any{"model": "model"}}}), Source: configkit.Mapping("partial", map[string]any{"profiles": []any{map[string]any{"id": "profile", "model": "model"}}}),
}}}) }}})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)