categorize emoji reactions as positive/negative/confused with tracked counters

This commit is contained in:
ollie 2026-07-27 18:30:01 +02:00
parent 96a140a83c
commit c656846cce
2 changed files with 50 additions and 12 deletions

View File

@ -607,12 +607,41 @@ func (s *agent) InjectSystemEvent(content string) {
s.Queue("<detached-process-result>\n" + content + "\n</detached-process-result>")
}
// classifyReaction returns a category and description for a reaction emoji.
func classifyReaction(emoji string) (category, description string, positive bool) {
switch emoji {
case "👍", "✅":
return "positive", "The response was good. Keep doing what you're doing.", true
case "🚀", "🎉":
return "excellent", "The response was exactly what was wanted.", true
case "👎", "❌":
return "negative", "The response was wrong or unhelpful.", false
case "💩", "🤬":
return "terrible", "The response was fundamentally wrong. Stop this approach entirely and reassess from scratch.", false
case "🤔":
return "confused", "The response was unclear or confusing.", false
default:
return "unknown", "", false
}
}
func (s *agent) React(emoji string) {
if s.session == nil {
return
}
msg := "[reacted " + emoji + " to your previous response]"
category, desc, positive := classifyReaction(emoji)
var msg string
if desc != "" {
msg = "[reaction: " + category + " (" + emoji + ")] " + desc
} else {
msg = "[reacted " + emoji + " to your previous response]"
}
s.session.appendUserMessage(msg)
if positive {
s.session.PositiveReactions++
} else if category == "negative" || category == "terrible" {
s.session.NegativeReactions++
}
s.emit(Event{Role: "user", Content: msg})
}
@ -773,7 +802,6 @@ func (s *agent) SetSessionID(newID string) error {
const defaultContextLength = 128000
const defaultToolResultMaxBytes = 131072
// autoCompactLimit returns the token threshold for auto-compaction (75%).
func (s *agent) autoCompactLimit(ctx context.Context) int {
ctxLen := s.runtime.Backend.ContextLength(ctx)
@ -1204,16 +1232,16 @@ func (s *agent) executeTurn(ctx context.Context, input string) string {
// Build per-turn agentConfig from the current runtime.
s.cfg = agentConfig{
Backend: s.runtime.Backend,
preamble: s.runtime.Preamble,
Tools: s.runtime.Tools,
Exec: s.runtime.Exec,
ClassifyTool: s.runtime.ClassifyTool,
ClassifyTier: s.runtime.ClassifyTier,
GenerationParams: s.runtime.GenParams,
MaxSteps: s.runtime.MaxSteps,
ReadPlanStep: s.readPlanStep,
TurnError: s.turnError,
Backend: s.runtime.Backend,
preamble: s.runtime.Preamble,
Tools: s.runtime.Tools,
Exec: s.runtime.Exec,
ClassifyTool: s.runtime.ClassifyTool,
ClassifyTier: s.runtime.ClassifyTier,
GenerationParams: s.runtime.GenParams,
MaxSteps: s.runtime.MaxSteps,
ReadPlanStep: s.readPlanStep,
TurnError: s.turnError,
}
var replyBuf strings.Builder

View File

@ -52,6 +52,9 @@ type PersistedSession struct {
Estimated bool `json:"estimated,omitempty"`
LastTurnCostUSD float64 `json:"lastTurnCostUSD,omitempty"`
SessionCostUSD float64 `json:"sessionCostUSD,omitempty"`
// Reaction tracking.
PositiveReactions int `json:"positiveReactions,omitempty"`
NegativeReactions int `json:"negativeReactions,omitempty"`
}
// TaskState is a compact structured overlay that summarizes the agent's
@ -117,6 +120,8 @@ func (s *Session) saveToFull(path, id, agentName, backendName, modelName, cwd, r
Estimated: s.Estimated,
LastTurnCostUSD: s.LastTurnCostUSD,
SessionCostUSD: s.SessionCostUSD,
PositiveReactions: s.PositiveReactions,
NegativeReactions: s.NegativeReactions,
}
data, err := json.Marshal(ps)
if err != nil {
@ -151,6 +156,8 @@ func RestoreSession(ps *PersistedSession) *Session {
Estimated: ps.Estimated,
LastTurnCostUSD: ps.LastTurnCostUSD,
SessionCostUSD: ps.SessionCostUSD,
PositiveReactions: ps.PositiveReactions,
NegativeReactions: ps.NegativeReactions,
}
for _, m := range ps.Messages {
if m.Role == "user" {
@ -181,6 +188,9 @@ type Session struct {
turnCreationTokens int
turnOutputTokens int
turnCostUSD float64 // >0 when backend reported cost directly (e.g. OpenRouter)
// Reaction tracking.
PositiveReactions int
NegativeReactions int
}
// newSession creates a new empty Session. The caller is responsible for