fix: Enhance Kol Variable Rendering and Management
- Updated the regex for placeholder matching to support more flexible key formats. - Refactored variable storage to allow both ID and key lookups in rendering. - Improved schema validation to check for duplicate keys and enforce non-empty keys. - Added new utility functions for variable value lookup and display name generation. - Enhanced tests to cover new key-based variable scenarios. - Refactored KolPrompt repository to simplify prompt storage and management, including the removal of obsolete revision handling. - Introduced new API endpoints for saving, activating, and archiving prompts. - Added new utility functions for handling Kol placeholders and platform options in the admin web. - Implemented database migrations to simplify prompt storage structure and ensure data integrity.
This commit is contained in:
@@ -33,14 +33,21 @@ type KolSchemaJSON struct {
|
||||
Variables []KolVariableDefinition `json:"variables"`
|
||||
}
|
||||
|
||||
var kolPlaceholderRE = regexp.MustCompile(`\{\{\s*([^}\s]+)\s*\}\}`)
|
||||
var kolPlaceholderRE = regexp.MustCompile(`\{\{\s*([^{}]+?)\s*\}\}`)
|
||||
|
||||
func RenderPrompt(content string, schema KolSchemaJSON, values map[string]any) (string, error) {
|
||||
schema = normalizeKolSchema(schema)
|
||||
|
||||
byID := make(map[string]KolVariableDefinition, len(schema.Variables))
|
||||
byToken := make(map[string]KolVariableDefinition, len(schema.Variables)*2)
|
||||
for _, variable := range schema.Variables {
|
||||
byID[variable.ID] = variable
|
||||
id := strings.TrimSpace(variable.ID)
|
||||
key := strings.TrimSpace(variable.Key)
|
||||
if id != "" {
|
||||
byToken[id] = variable
|
||||
}
|
||||
if key != "" {
|
||||
byToken[key] = variable
|
||||
}
|
||||
}
|
||||
|
||||
missingSet := make(map[string]struct{})
|
||||
@@ -62,19 +69,19 @@ func RenderPrompt(content string, schema KolSchemaJSON, values map[string]any) (
|
||||
return match
|
||||
}
|
||||
|
||||
id := submatches[1]
|
||||
variable, exists := byID[id]
|
||||
token := strings.TrimSpace(submatches[1])
|
||||
variable, exists := byToken[token]
|
||||
if !exists {
|
||||
addMissing(id)
|
||||
addMissing(token)
|
||||
return match
|
||||
}
|
||||
|
||||
value, hasValue := values[id]
|
||||
value, hasValue := lookupKolVariableValue(values, variable, token)
|
||||
if hasValue {
|
||||
return fmt.Sprint(value)
|
||||
}
|
||||
if variable.Required {
|
||||
addMissing(id)
|
||||
addMissing(kolVariableDisplayName(variable))
|
||||
}
|
||||
return ""
|
||||
})
|
||||
@@ -94,16 +101,26 @@ func HashPrompt(rendered string) string {
|
||||
func ValidateSchema(schema KolSchemaJSON) error {
|
||||
schema = normalizeKolSchema(schema)
|
||||
|
||||
seen := make(map[string]struct{}, len(schema.Variables))
|
||||
seenIDs := make(map[string]struct{}, len(schema.Variables))
|
||||
seenKeys := make(map[string]struct{}, len(schema.Variables))
|
||||
for _, variable := range schema.Variables {
|
||||
id := strings.TrimSpace(variable.ID)
|
||||
if id == "" || !strings.HasPrefix(id, "var_") {
|
||||
return fmt.Errorf("variable id must be non-empty and start with var_")
|
||||
}
|
||||
if _, exists := seen[id]; exists {
|
||||
if _, exists := seenIDs[id]; exists {
|
||||
return fmt.Errorf("duplicate variable id: %s", id)
|
||||
}
|
||||
seen[id] = struct{}{}
|
||||
seenIDs[id] = struct{}{}
|
||||
|
||||
key := strings.TrimSpace(variable.Key)
|
||||
if key == "" {
|
||||
return fmt.Errorf("variable key must be non-empty")
|
||||
}
|
||||
if _, exists := seenKeys[key]; exists {
|
||||
return fmt.Errorf("duplicate variable key: %s", key)
|
||||
}
|
||||
seenKeys[key] = struct{}{}
|
||||
|
||||
switch KolVariableType(strings.TrimSpace(string(variable.Type))) {
|
||||
case KolVariableTypeInput, KolVariableTypeTextarea, KolVariableTypeSelect, KolVariableTypeNumber, KolVariableTypeCheckbox:
|
||||
@@ -125,3 +142,38 @@ func normalizeKolSchema(schema KolSchemaJSON) KolSchemaJSON {
|
||||
}
|
||||
return schema
|
||||
}
|
||||
|
||||
func lookupKolVariableValue(values map[string]any, variable KolVariableDefinition, token string) (any, bool) {
|
||||
candidates := []string{
|
||||
strings.TrimSpace(token),
|
||||
strings.TrimSpace(variable.Key),
|
||||
strings.TrimSpace(variable.ID),
|
||||
}
|
||||
|
||||
seen := make(map[string]struct{}, len(candidates))
|
||||
for _, candidate := range candidates {
|
||||
if candidate == "" {
|
||||
continue
|
||||
}
|
||||
if _, exists := seen[candidate]; exists {
|
||||
continue
|
||||
}
|
||||
seen[candidate] = struct{}{}
|
||||
|
||||
if value, ok := values[candidate]; ok {
|
||||
return value, true
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func kolVariableDisplayName(variable KolVariableDefinition) string {
|
||||
if label := strings.TrimSpace(variable.Label); label != "" {
|
||||
return label
|
||||
}
|
||||
if key := strings.TrimSpace(variable.Key); key != "" {
|
||||
return key
|
||||
}
|
||||
return strings.TrimSpace(variable.ID)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user