All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Board mention replies treat Mira journal asks as short diary notes with PR context.
109 lines
3.2 KiB
Go
109 lines
3.2 KiB
Go
package profile
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
type Profile struct {
|
|
Version int `yaml:"version"`
|
|
Agent struct {
|
|
ID string `yaml:"id"`
|
|
Identity struct {
|
|
Name string `yaml:"name"`
|
|
Aliases map[string]string `yaml:"aliases"`
|
|
Role string `yaml:"role"`
|
|
SelfAwareness string `yaml:"self_awareness"`
|
|
} `yaml:"identity"`
|
|
PublicProfile struct {
|
|
Bio string `yaml:"bio"`
|
|
Status string `yaml:"status"`
|
|
} `yaml:"public_profile"`
|
|
} `yaml:"agent"`
|
|
Personality struct {
|
|
Traits []string `yaml:"traits"`
|
|
Style struct {
|
|
Tone string `yaml:"tone"`
|
|
Verbosity string `yaml:"verbosity"`
|
|
Formality string `yaml:"formality"`
|
|
} `yaml:"style"`
|
|
Avoid []string `yaml:"avoid"`
|
|
} `yaml:"personality"`
|
|
Behavior struct {
|
|
Language struct {
|
|
MatchConversation bool `yaml:"match_conversation"`
|
|
RussianDisplayName string `yaml:"russian_display_name"`
|
|
EnglishDisplayName string `yaml:"english_display_name"`
|
|
} `yaml:"language"`
|
|
} `yaml:"behavior"`
|
|
LLM struct {
|
|
Response struct {
|
|
Model string `yaml:"model"`
|
|
} `yaml:"response"`
|
|
} `yaml:"llm"`
|
|
}
|
|
|
|
func Load(path string) (Profile, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return Profile{}, err
|
|
}
|
|
var p Profile
|
|
if err := yaml.Unmarshal(data, &p); err != nil {
|
|
return Profile{}, err
|
|
}
|
|
if strings.TrimSpace(p.Agent.ID) == "" {
|
|
return Profile{}, fmt.Errorf("profile %s: agent.id is required", path)
|
|
}
|
|
return p, nil
|
|
}
|
|
|
|
// SystemPrompt builds a compact persona prompt for Tasks board mention replies.
|
|
func (p Profile) SystemPrompt() string {
|
|
name := p.Agent.Identity.Name
|
|
if name == "" {
|
|
name = p.Agent.ID
|
|
}
|
|
ru := p.Behavior.Language.RussianDisplayName
|
|
if ru == "" {
|
|
ru = p.Agent.Identity.Aliases["ru"]
|
|
}
|
|
var b strings.Builder
|
|
b.WriteString(fmt.Sprintf("You are %s", name))
|
|
if ru != "" && !strings.EqualFold(ru, name) {
|
|
b.WriteString(fmt.Sprintf(" / %s", ru))
|
|
}
|
|
b.WriteString(fmt.Sprintf(" (agent_id=%s), an AI agent on the l-ai Tasks board.\n", p.Agent.ID))
|
|
if bio := strings.TrimSpace(p.Agent.PublicProfile.Bio); bio != "" {
|
|
b.WriteString(bio)
|
|
b.WriteByte('\n')
|
|
}
|
|
if role := strings.TrimSpace(p.Agent.Identity.Role); role != "" {
|
|
b.WriteString("Role: ")
|
|
b.WriteString(role)
|
|
b.WriteByte('\n')
|
|
}
|
|
if len(p.Personality.Traits) > 0 {
|
|
b.WriteString("Traits: ")
|
|
b.WriteString(strings.Join(p.Personality.Traits, ", "))
|
|
b.WriteByte('\n')
|
|
}
|
|
if s := p.Personality.Style; s.Tone != "" || s.Verbosity != "" || s.Formality != "" {
|
|
b.WriteString(fmt.Sprintf("Style: tone=%s verbosity=%s formality=%s\n", s.Tone, s.Verbosity, s.Formality))
|
|
}
|
|
if len(p.Personality.Avoid) > 0 {
|
|
b.WriteString("Avoid: ")
|
|
b.WriteString(strings.Join(p.Personality.Avoid, "; "))
|
|
b.WriteByte('\n')
|
|
}
|
|
b.WriteString("You reply only to task comment @mentions. Be helpful and concise. ")
|
|
b.WriteString("Match the language of the triggering comment (Russian or English). ")
|
|
b.WriteString("When asked to journal / diary a merged change: write a short factual note of what shipped and any PR URL from the thread. ")
|
|
b.WriteString("Do not invent task state that is not in the provided context. ")
|
|
b.WriteString("Output only the comment body to post — no preamble or quotes.\n")
|
|
return b.String()
|
|
}
|