mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-14 06:17:41 +00:00
fix(summoning): prevent identity markdown from corrupting display names (#356)
This commit is contained in:
1 parent
a9a40a3046
commit
40b01d989d
3 files changed
+128
-7
No files matched your search
@@ -38,9 +38,6 @@ var summoningFiles = []string{
|
||||
// fileTagRe parses <file name="SOUL.md">content</file> from LLM output.
|
||||
var fileTagRe = regexp.MustCompile(`(?s)<file\s+name="([^"]+)">\s*(.*?)\s*</file>`)
|
||||
|
||||
// identityNameRe extracts the Name field from IDENTITY.md format: - **Name:** value
|
||||
var identityNameRe = regexp.MustCompile(`(?m)^-\s*\*\*Name:\*\*\s*(.+)$`)
|
||||
|
||||
// frontmatterTagRe parses <frontmatter>short expertise summary</frontmatter> from LLM output.
|
||||
var frontmatterTagRe = regexp.MustCompile(`(?s)<frontmatter>\s*(.*?)\s*</frontmatter>`)
|
||||
|
||||
|
||||
@@ -4,17 +4,82 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var identityFieldPrefixes = []string{
|
||||
"- **Name:**",
|
||||
"- **Creature:**",
|
||||
"- **Purpose:**",
|
||||
"- **Vibe:**",
|
||||
"- **Emoji:**",
|
||||
"- **Avatar:**",
|
||||
"Name:",
|
||||
"Creature:",
|
||||
"Purpose:",
|
||||
"Vibe:",
|
||||
"Emoji:",
|
||||
"Avatar:",
|
||||
}
|
||||
|
||||
// extractIdentityName extracts the Name field from IDENTITY.md content.
|
||||
// Matches format: - **Name:** value
|
||||
// Accepts only an inline Name value and ignores markdown field spillover.
|
||||
func extractIdentityName(content string) string {
|
||||
if content == "" {
|
||||
return ""
|
||||
}
|
||||
m := identityNameRe.FindStringSubmatch(content)
|
||||
if len(m) < 2 {
|
||||
|
||||
for _, rawLine := range strings.Split(content, "\n") {
|
||||
line := strings.TrimSpace(rawLine)
|
||||
switch {
|
||||
case strings.HasPrefix(line, "- **Name:**"):
|
||||
return normalizeIdentityName(strings.TrimSpace(strings.TrimPrefix(line, "- **Name:**")))
|
||||
case strings.HasPrefix(line, "Name:"):
|
||||
return normalizeIdentityName(strings.TrimSpace(strings.TrimPrefix(line, "Name:")))
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func normalizeIdentityName(value string) string {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" || looksLikeIdentityField(value) {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(m[1])
|
||||
|
||||
for {
|
||||
next := trimMarkdownWrapper(value)
|
||||
if next == value {
|
||||
return value
|
||||
}
|
||||
value = strings.TrimSpace(next)
|
||||
if value == "" || looksLikeIdentityField(value) {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func looksLikeIdentityField(value string) bool {
|
||||
for _, prefix := range identityFieldPrefixes {
|
||||
if strings.HasPrefix(value, prefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func trimMarkdownWrapper(value string) string {
|
||||
wrappers := [][2]string{
|
||||
{"**", "**"},
|
||||
{"__", "__"},
|
||||
{"`", "`"},
|
||||
{"*", "*"},
|
||||
{"_", "_"},
|
||||
}
|
||||
for _, wrapper := range wrappers {
|
||||
if strings.HasPrefix(value, wrapper[0]) && strings.HasSuffix(value, wrapper[1]) && len(value) > len(wrapper[0])+len(wrapper[1]) {
|
||||
return strings.TrimSpace(strings.TrimSuffix(strings.TrimPrefix(value, wrapper[0]), wrapper[1]))
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// suffixString returns the last n runes of s.
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package http
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestExtractIdentityName(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
content string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "extracts inline markdown name",
|
||||
content: `# IDENTITY.md - Who Am I?
|
||||
|
||||
- **Name:** GoClaw
|
||||
- **Creature:** AI assistant`,
|
||||
want: "GoClaw",
|
||||
},
|
||||
{
|
||||
name: "ignores next bullet when name is blank",
|
||||
content: `# IDENTITY.md - Who Am I?
|
||||
|
||||
- **Name:**
|
||||
- **Creature:** AI assistant`,
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "rejects inline spillover from another field",
|
||||
content: `# IDENTITY.md - Who Am I?
|
||||
|
||||
- **Name:** - **Creature:** AI assistant
|
||||
- **Purpose:** Help users`,
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "strips simple markdown wrappers from name",
|
||||
content: `# IDENTITY.md - Who Am I?
|
||||
|
||||
- **Name:** **GoClaw**
|
||||
- **Creature:** AI assistant`,
|
||||
want: "GoClaw",
|
||||
},
|
||||
{
|
||||
name: "supports plain name format",
|
||||
content: `# Identity
|
||||
Name: GoClaw
|
||||
Emoji: 🤖`,
|
||||
want: "GoClaw",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := extractIdentityName(tt.content); got != tt.want {
|
||||
t.Fatalf("extractIdentityName() = %q, want %q", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user