feat(kol): KOL workspace services (profile/package/prompt/asset/renderer)
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type KolVariableType string
|
||||
|
||||
const (
|
||||
KolVariableTypeInput KolVariableType = "input"
|
||||
KolVariableTypeTextarea KolVariableType = "textarea"
|
||||
KolVariableTypeSelect KolVariableType = "select"
|
||||
KolVariableTypeNumber KolVariableType = "number"
|
||||
KolVariableTypeCheckbox KolVariableType = "checkbox"
|
||||
)
|
||||
|
||||
type KolVariableDefinition struct {
|
||||
ID string `json:"id"`
|
||||
Key string `json:"key"`
|
||||
Label string `json:"label"`
|
||||
Type KolVariableType `json:"type"`
|
||||
Required bool `json:"required"`
|
||||
Placeholder *string `json:"placeholder,omitempty"`
|
||||
Options []string `json:"options,omitempty"`
|
||||
}
|
||||
|
||||
type KolSchemaJSON struct {
|
||||
Variables []KolVariableDefinition `json:"variables"`
|
||||
}
|
||||
|
||||
var kolPlaceholderRE = regexp.MustCompile(`\{\{\s*([^}\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))
|
||||
for _, variable := range schema.Variables {
|
||||
byID[variable.ID] = variable
|
||||
}
|
||||
|
||||
missingSet := make(map[string]struct{})
|
||||
missing := make([]string, 0)
|
||||
addMissing := func(id string) {
|
||||
if id == "" {
|
||||
return
|
||||
}
|
||||
if _, exists := missingSet[id]; exists {
|
||||
return
|
||||
}
|
||||
missingSet[id] = struct{}{}
|
||||
missing = append(missing, id)
|
||||
}
|
||||
|
||||
rendered := kolPlaceholderRE.ReplaceAllStringFunc(content, func(match string) string {
|
||||
submatches := kolPlaceholderRE.FindStringSubmatch(match)
|
||||
if len(submatches) < 2 {
|
||||
return match
|
||||
}
|
||||
|
||||
id := submatches[1]
|
||||
variable, exists := byID[id]
|
||||
if !exists {
|
||||
addMissing(id)
|
||||
return match
|
||||
}
|
||||
|
||||
value, hasValue := values[id]
|
||||
if hasValue {
|
||||
return fmt.Sprint(value)
|
||||
}
|
||||
if variable.Required {
|
||||
addMissing(id)
|
||||
}
|
||||
return ""
|
||||
})
|
||||
|
||||
if len(missing) > 0 {
|
||||
return "", fmt.Errorf("missing variables: %s", strings.Join(missing, ", "))
|
||||
}
|
||||
|
||||
return rendered, nil
|
||||
}
|
||||
|
||||
func HashPrompt(rendered string) string {
|
||||
sum := sha256.Sum256([]byte(rendered))
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
func ValidateSchema(schema KolSchemaJSON) error {
|
||||
schema = normalizeKolSchema(schema)
|
||||
|
||||
seen := 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 {
|
||||
return fmt.Errorf("duplicate variable id: %s", id)
|
||||
}
|
||||
seen[id] = struct{}{}
|
||||
|
||||
switch KolVariableType(strings.TrimSpace(string(variable.Type))) {
|
||||
case KolVariableTypeInput, KolVariableTypeTextarea, KolVariableTypeSelect, KolVariableTypeNumber, KolVariableTypeCheckbox:
|
||||
default:
|
||||
return fmt.Errorf("unsupported variable type: %s", variable.Type)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func JSONEncodeSchema(schema KolSchemaJSON) ([]byte, error) {
|
||||
return json.Marshal(normalizeKolSchema(schema))
|
||||
}
|
||||
|
||||
func normalizeKolSchema(schema KolSchemaJSON) KolSchemaJSON {
|
||||
if schema.Variables == nil {
|
||||
schema.Variables = []KolVariableDefinition{}
|
||||
}
|
||||
return schema
|
||||
}
|
||||
Reference in New Issue
Block a user