Files
DivaCode/internal/companion/engine.go
db123-ai 3895ca68c0 feat: Phase 2 — chat templates, reasoning parser, collapsible TUI, mouse support
- internal/llama/template.go: ChatML/Llama3 template engine with stop tokens
- internal/agent/reasoning.go: parse <think>/<reasoning> tags from LLM output
- companion/promptbuilder.go: output structured BuildResult, add date/time context
- agent/agent.go: wire templates + reasoning split into message loop
- storage: add reasoning TEXT column to messages table + migration
- channel/tui.go: collapsible reasoning (tab toggle), mouse scroll, polished dimmed style
- config: CHAT_TEMPLATE env var (chatml|llama3|none)
- plan.md: mark Phase 2 complete, add themes TODO
2026-06-10 09:51:24 +03:30

124 lines
2.8 KiB
Go

package companion
import (
"context"
"sync"
"time"
"git.db123.ir/db123/divacode/internal/config"
"git.db123.ir/db123/divacode/internal/storage"
"git.db123.ir/db123/divacode/pkg/personality"
"git.db123.ir/db123/divacode/pkg/types"
)
type PromptInput struct {
UserMessage string
RecentMessages []types.Message
Memories []string
}
type Engine struct {
mu sync.Mutex
cfg *config.Config
db *storage.DB
personality *PersonalityEngine
relationship *RelationshipEngine
reflection *ReflectionEngine
promptBuilder *PromptBuilder
mood *MoodEngine
lastContact time.Time
pendingAutoMsg string
}
func NewEngine(cfg *config.Config, db *storage.DB) *Engine {
return &Engine{
cfg: cfg,
db: db,
personality: NewPersonalityEngine(db),
relationship: NewRelationshipEngine(db),
reflection: NewReflectionEngine(db),
promptBuilder: NewPromptBuilder(cfg),
mood: NewMoodEngine(),
lastContact: time.Now(),
}
}
func (e *Engine) BuildPrompt(ctx context.Context, input *PromptInput) (*BuildResult, error) {
e.mu.Lock()
defer e.mu.Unlock()
traits := e.personality.Current()
rel := e.relationship.Current()
mood := e.mood.Current()
observations := e.reflection.RecentObservations(5)
result := e.promptBuilder.Build(&BuildInput{
SystemPrompt: e.cfg.SystemPrompt,
Traits: traits,
Relationship: rel,
Mood: mood,
Observations: observations,
UserMessage: input.UserMessage,
Memories: input.Memories,
Messages: input.RecentMessages,
})
e.lastContact = time.Now()
return result, nil
}
func (e *Engine) WriteDiary(ctx context.Context) error {
e.mu.Lock()
defer e.mu.Unlock()
return e.reflection.GenerateDiary(ctx, e.lastContact)
}
func (e *Engine) Mood() types.Mood {
e.mu.Lock()
defer e.mu.Unlock()
return e.mood.Current()
}
func (e *Engine) Trust() float64 {
e.mu.Lock()
defer e.mu.Unlock()
return e.relationship.Trust()
}
func (e *Engine) LastContactDuration() time.Duration {
e.mu.Lock()
defer e.mu.Unlock()
return time.Since(e.lastContact)
}
func (e *Engine) QueueAutonomousMessage(msg string) {
e.mu.Lock()
defer e.mu.Unlock()
e.pendingAutoMsg = msg
}
func (e *Engine) PopAutonomousMessage() string {
e.mu.Lock()
defer e.mu.Unlock()
msg := e.pendingAutoMsg
e.pendingAutoMsg = ""
return msg
}
func (e *Engine) Personality() *PersonalityEngine { return e.personality }
func (e *Engine) Relationship() *RelationshipEngine { return e.relationship }
func (e *Engine) Reflection() *ReflectionEngine { return e.reflection }
type BuildInput struct {
SystemPrompt string
Traits []personality.TraitValue
Relationship *personality.RelationshipMetrics
Mood types.Mood
Observations []personality.Observation
UserMessage string
Memories []string
Messages []types.Message
}