feat(companion): add personality, relationship, and mood engines

This commit is contained in:
2026-06-10 00:49:01 +03:30
parent 8d99c1856b
commit 147a51ecba
3 changed files with 307 additions and 0 deletions

View File

@@ -0,0 +1,107 @@
package companion
import (
"fmt"
"log/slog"
"sync"
"git.db123.ir/db123/divacode/internal/storage"
"git.db123.ir/db123/divacode/pkg/personality"
)
type PersonalityEngine struct {
mu sync.Mutex
db *storage.DB
def map[personality.Trait]personality.TraitValue
}
func NewPersonalityEngine(db *storage.DB) *PersonalityEngine {
e := &PersonalityEngine{
db: db,
def: map[personality.Trait]personality.TraitValue{
personality.TraitHumor: {Trait: personality.TraitHumor, Value: 0.5, MinValue: 0.3, MaxValue: 0.7},
personality.TraitCuriosity: {Trait: personality.TraitCuriosity, Value: 0.8, MinValue: 0.7, MaxValue: 0.9},
personality.TraitPlayfulness: {Trait: personality.TraitPlayfulness, Value: 0.5, MinValue: 0.3, MaxValue: 0.7},
personality.TraitFormality: {Trait: personality.TraitFormality, Value: 0.3, MinValue: 0.2, MaxValue: 0.5},
personality.TraitAffection: {Trait: personality.TraitAffection, Value: 0.5, MinValue: 0.3, MaxValue: 0.7},
},
}
e.seed()
return e
}
func (e *PersonalityEngine) seed() {
for _, tv := range e.def {
err := e.db.Exec(
"INSERT OR IGNORE INTO personality_traits (id, trait, value, min_value, max_value) VALUES (?, ?, ?, ?, ?)",
fmt.Sprintf("trait_%s", tv.Trait), string(tv.Trait), tv.Value, tv.MinValue, tv.MaxValue,
)
if err != nil {
slog.Warn("seed trait failed", "trait", tv.Trait, "error", err)
}
}
}
func (e *PersonalityEngine) Current() []personality.TraitValue {
e.mu.Lock()
defer e.mu.Unlock()
rows, err := e.db.Query("SELECT trait, value, min_value, max_value, updated_at FROM personality_traits")
if err != nil {
slog.Warn("read traits failed", "error", err)
return e.defaults()
}
defer rows.Close()
var traits []personality.TraitValue
for rows.Next() {
var tv personality.TraitValue
var t, updated string
if err := rows.Scan(&t, &tv.Value, &tv.MinValue, &tv.MaxValue, &updated); err != nil {
continue
}
tv.Trait = personality.Trait(t)
traits = append(traits, tv)
}
if len(traits) == 0 {
return e.defaults()
}
return traits
}
func (e *PersonalityEngine) Update(trait personality.Trait, delta float64) error {
e.mu.Lock()
defer e.mu.Unlock()
current := e.get(trait)
newVal := current.Value + delta
if newVal < current.MinValue {
newVal = current.MinValue
}
if newVal > current.MaxValue {
newVal = current.MaxValue
}
return e.db.Exec(
"UPDATE personality_traits SET value = ?, updated_at = datetime('now') WHERE trait = ?",
newVal, string(trait),
)
}
func (e *PersonalityEngine) get(trait personality.Trait) personality.TraitValue {
row := e.db.QueryRow("SELECT trait, value, min_value, max_value FROM personality_traits WHERE trait = ?", string(trait))
var tv personality.TraitValue
var t string
if err := row.Scan(&t, &tv.Value, &tv.MinValue, &tv.MaxValue); err != nil {
return e.def[trait]
}
tv.Trait = personality.Trait(t)
return tv
}
func (e *PersonalityEngine) defaults() []personality.TraitValue {
var out []personality.TraitValue
for _, tv := range e.def {
out = append(out, tv)
}
return out
}