Preserve message resource origins and profile credential lookup names
This commit is contained in:
@@ -275,9 +275,6 @@ func validateEffective(config *document, validation *configkit.Validation) {
|
||||
if profile.Model != nil && *profile.Model != "" && strings.TrimSpace(*profile.Model) == "" {
|
||||
validation.Error(base.Key("model"), "model must not be blank")
|
||||
}
|
||||
if profile.APIKeyEnv != nil && *profile.APIKeyEnv != "" && !envName.MatchString(*profile.APIKeyEnv) {
|
||||
validation.Error(base.Key("api_key_env"), "environment variable name is invalid")
|
||||
}
|
||||
if profile.Temperature != nil {
|
||||
if err := domain.ValidateExecutionTargetSettings(domain.ExecutionTarget{Temperature: *profile.Temperature}); err != nil {
|
||||
validation.Error(base.Key("temperature"), err.Error())
|
||||
|
||||
@@ -66,37 +66,9 @@ func resolvePrompts(prompts map[string]map[string]promptConfig, resolution *conf
|
||||
if inputsChanged {
|
||||
resolution.Record(base.Key("inputs"), configkit.ChangeNormalization, "normalize input metadata")
|
||||
}
|
||||
messagesChanged := false
|
||||
for index := range definition.Messages {
|
||||
message := &definition.Messages[index]
|
||||
messagePath := base.Key("messages").Index(index)
|
||||
if message.Role != nil {
|
||||
role, err := domain.NormalizeMessageRole(*message.Role)
|
||||
if err != nil {
|
||||
resolution.Error(messagePath.Key("role"), err.Error())
|
||||
} else {
|
||||
messagesChanged = normalizeString(&message.Role, messagePath.Key("role"), resolution, role) || messagesChanged
|
||||
}
|
||||
}
|
||||
if control := message.CacheControl; control != nil && control.Type != nil {
|
||||
value := &domain.CacheControl{Type: domain.CacheControlType(*control.Type)}
|
||||
if control.TTL != nil {
|
||||
value.TTL = *control.TTL
|
||||
}
|
||||
normalized, err := domain.NormalizeCacheControl(value)
|
||||
if err != nil {
|
||||
resolution.Error(messagePath.Key("cache_control"), err.Error())
|
||||
} else {
|
||||
messagesChanged = normalizeString(&control.Type, messagePath.Key("cache_control").Key("type"), resolution, string(normalized.Type)) || messagesChanged
|
||||
if control.TTL != nil {
|
||||
messagesChanged = normalizeString(&control.TTL, messagePath.Key("cache_control").Key("ttl"), resolution, normalized.TTL) || messagesChanged
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if messagesChanged {
|
||||
resolution.Record(base.Key("messages"), configkit.ChangeNormalization, "normalize message metadata")
|
||||
}
|
||||
// Normalize message metadata on execution-owned copies below. ConfigKit
|
||||
// records sequence mutations as whole-value replacements, which would
|
||||
// discard the effective source contexts of unchanged content_file fields.
|
||||
if definition.Output != nil && definition.Output.SchemaPath != nil {
|
||||
normalizeString(&definition.Output.SchemaPath, base.Key("output").Key("schema_path"), resolution, strings.TrimSpace(*definition.Output.SchemaPath))
|
||||
}
|
||||
@@ -180,6 +152,10 @@ func (state *loaded) materializePrompt(id, version string, raw promptConfig, loo
|
||||
if message.Role == nil {
|
||||
return nil, fmt.Errorf("%s: role is required", path.Key("role"))
|
||||
}
|
||||
role, err := domain.NormalizeMessageRole(*message.Role)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %w", path.Key("role"), err)
|
||||
}
|
||||
if (message.Content != nil) == (message.ContentFile != nil) {
|
||||
return nil, fmt.Errorf("%s: exactly one of content or content_file is required", path)
|
||||
}
|
||||
@@ -214,7 +190,7 @@ func (state *loaded) materializePrompt(id, version string, raw promptConfig, loo
|
||||
}
|
||||
entry.messages = append(entry.messages, compiled)
|
||||
definition.Templates = append(definition.Templates, domain.PromptMessageTemplate{
|
||||
Role: *message.Role, Content: content, CacheControl: control,
|
||||
Role: role, Content: content, CacheControl: control,
|
||||
})
|
||||
}
|
||||
if raw.Output == nil {
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
func TestLoadCapturesInheritedImportedContent(t *testing.T) {
|
||||
files := fstest.MapFS{
|
||||
"app/main.yaml": {Data: []byte("configkit:\n imports:\n - path: ../catalog/base.yaml\nprompts:\n child:\n extends: base\n")},
|
||||
"catalog/base.yaml": {Data: []byte("prompts:\n base:\n v1:\n messages:\n - role: user\n content_file: ./body.md\n output:\n format: text\n validation_mode: basic\n")},
|
||||
"catalog/base.yaml": {Data: []byte("prompts:\n base:\n v1:\n messages:\n - role: ' USER '\n cache_control:\n type: ' ephemeral '\n ttl: ' 1h '\n content_file: ./body.md\n output:\n format: text\n validation_mode: basic\n")},
|
||||
"catalog/body.md": {Data: []byte("Captured {{.name}}")},
|
||||
}
|
||||
namespace := configkit.NewFilesystem("embedded", files)
|
||||
@@ -37,6 +37,23 @@ func TestLoadCapturesInheritedImportedContent(t *testing.T) {
|
||||
if definition.Templates[0].Content != "Captured {{.name}}" || inspection.PromptHash == "" {
|
||||
t.Fatalf("inherited file content was not captured: %+v, %+v", definition, inspection)
|
||||
}
|
||||
message := definition.Templates[0]
|
||||
if message.Role != "user" || message.CacheControl == nil || message.CacheControl.Type != "ephemeral" || message.CacheControl.TTL != "1h" {
|
||||
t.Fatalf("message metadata was not normalized: %+v", message)
|
||||
}
|
||||
field, ok := state.result.Metadata().Lookup(configkit.Keys("prompts", "child", "v1", "messages").Index(0).Key("content_file"))
|
||||
if !ok || len(field.Origin.Locations) != 1 {
|
||||
t.Fatalf("content origin was lost during normalization: %+v", field)
|
||||
}
|
||||
var originPath string
|
||||
for _, source := range state.result.Sources() {
|
||||
if source.ID == field.Origin.Locations[0].SourceID {
|
||||
originPath = source.Path
|
||||
}
|
||||
}
|
||||
if originPath != "catalog/base.yaml" {
|
||||
t.Fatalf("content origin = %q, want catalog/base.yaml", originPath)
|
||||
}
|
||||
files["catalog/body.md"].Data = []byte("Changed")
|
||||
again, againInspection, err := state.prompt("child", "v1")
|
||||
if err != nil || again.Templates[0].Content != "Captured {{.name}}" || againInspection.PromptHash != inspection.PromptHash {
|
||||
|
||||
Reference in New Issue
Block a user