feat(companion): add personality, relationship, and mood engines
This commit is contained in:
121
internal/companion/relationship.go
Normal file
121
internal/companion/relationship.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package companion
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"sync"
|
||||
|
||||
"git.db123.ir/db123/divacode/internal/storage"
|
||||
"git.db123.ir/db123/divacode/pkg/personality"
|
||||
)
|
||||
|
||||
type RelationshipEngine struct {
|
||||
mu sync.Mutex
|
||||
db *storage.DB
|
||||
}
|
||||
|
||||
func NewRelationshipEngine(db *storage.DB) *RelationshipEngine {
|
||||
e := &RelationshipEngine{db: db}
|
||||
e.seed()
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *RelationshipEngine) seed() {
|
||||
defaults := map[string]float64{
|
||||
"familiarity": 0.0,
|
||||
"trust": 0.3,
|
||||
}
|
||||
for metric, val := range defaults {
|
||||
err := e.db.Exec(
|
||||
"INSERT OR IGNORE INTO relationship_metrics (id, metric, value) VALUES (?, ?, ?)",
|
||||
"rel_"+metric, metric, val,
|
||||
)
|
||||
if err != nil {
|
||||
slog.Warn("seed metric failed", "metric", metric, "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *RelationshipEngine) Current() *personality.RelationshipMetrics {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
rm := &personality.RelationshipMetrics{}
|
||||
|
||||
row := e.db.QueryRow("SELECT value FROM relationship_metrics WHERE metric = 'familiarity'")
|
||||
row.Scan(&rm.Familiarity)
|
||||
|
||||
row = e.db.QueryRow("SELECT value FROM relationship_metrics WHERE metric = 'trust'")
|
||||
row.Scan(&rm.Trust)
|
||||
|
||||
topics, _ := e.db.Query("SELECT topic FROM shared_topics ORDER BY count DESC LIMIT 20")
|
||||
if topics != nil {
|
||||
defer topics.Close()
|
||||
for topics.Next() {
|
||||
var t string
|
||||
topics.Scan(&t)
|
||||
rm.SharedTopics = append(rm.SharedTopics, t)
|
||||
}
|
||||
}
|
||||
|
||||
jokes, _ := e.db.Query("SELECT joke FROM inside_jokes LIMIT 20")
|
||||
if jokes != nil {
|
||||
defer jokes.Close()
|
||||
for jokes.Next() {
|
||||
var j string
|
||||
jokes.Scan(&j)
|
||||
rm.InsideJokes = append(rm.InsideJokes, j)
|
||||
}
|
||||
}
|
||||
|
||||
return rm
|
||||
}
|
||||
|
||||
func (e *RelationshipEngine) Trust() float64 {
|
||||
var v float64
|
||||
err := e.db.QueryRow("SELECT value FROM relationship_metrics WHERE metric = 'trust'").Scan(&v)
|
||||
if err != nil {
|
||||
return 0.3
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (e *RelationshipEngine) UpdateFamiliarity(delta float64) error {
|
||||
return e.adjust("familiarity", delta)
|
||||
}
|
||||
|
||||
func (e *RelationshipEngine) UpdateTrust(delta float64) error {
|
||||
return e.adjust("trust", delta)
|
||||
}
|
||||
|
||||
func (e *RelationshipEngine) AddSharedTopic(topic string) error {
|
||||
return e.db.Exec(
|
||||
`INSERT INTO shared_topics (id, topic, count, last_discussed)
|
||||
VALUES (?, ?, 1, datetime('now'))
|
||||
ON CONFLICT(topic) DO UPDATE SET count = count + 1, last_discussed = datetime('now')`,
|
||||
"topic_"+topic, topic,
|
||||
)
|
||||
}
|
||||
|
||||
func (e *RelationshipEngine) adjust(metric string, delta float64) error {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
var current float64
|
||||
row := e.db.QueryRow("SELECT value FROM relationship_metrics WHERE metric = ?", metric)
|
||||
if err := row.Scan(¤t); err != nil {
|
||||
current = 0
|
||||
}
|
||||
|
||||
newVal := current + delta
|
||||
if newVal < 0 {
|
||||
newVal = 0
|
||||
}
|
||||
if newVal > 1 {
|
||||
newVal = 1
|
||||
}
|
||||
|
||||
return e.db.Exec(
|
||||
"UPDATE relationship_metrics SET value = ?, updated_at = datetime('now') WHERE metric = ?",
|
||||
newVal, metric,
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user