remove emoji reaction feature (core, dbus, KDE GUI)
This commit is contained in:
parent
cfdedb2e5e
commit
f37e81d8cc
|
|
@ -9,7 +9,6 @@ import (
|
|||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/simonfxr/pubsub"
|
||||
"ollie/backend"
|
||||
|
|
@ -439,68 +438,6 @@ func (ag *Agent) ListModels() []string {
|
|||
return ag.runtime.Backend.Models(context.Background())
|
||||
}
|
||||
|
||||
// Reactions returns a map of response ID → emoji for all recorded reactions.
|
||||
func (ag *Agent) Reactions() map[string]string {
|
||||
result := make(map[string]string)
|
||||
if ag.history == nil {
|
||||
return result
|
||||
}
|
||||
for _, reaction := range ag.history.Reactions {
|
||||
result[reaction.ResponseID] = reaction.Emoji
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// React records a reaction emoji against the most recent (or specified) response.
|
||||
func (ag *Agent) React(responseID, emoji string) error {
|
||||
if ag.history == nil {
|
||||
return fmt.Errorf("no active session")
|
||||
}
|
||||
category, _, _ := classifyReaction(emoji)
|
||||
if category == "unknown" {
|
||||
return fmt.Errorf("unsupported reaction: %s", emoji)
|
||||
}
|
||||
if responseID == "" {
|
||||
for i := len(ag.history.messages) - 1; i >= 0; i-- {
|
||||
if ag.history.messages[i].Role == "assistant" {
|
||||
responseID = ag.history.messages[i].ID
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if responseID == "" {
|
||||
return fmt.Errorf("no assistant response to react to")
|
||||
}
|
||||
found := false
|
||||
for i := range ag.history.messages {
|
||||
if ag.history.messages[i].Role == "assistant" && ag.history.messages[i].ID == responseID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("assistant response not found: %s", responseID)
|
||||
}
|
||||
reaction := Reaction{ID: NewReactionID(), ResponseID: responseID, Emoji: emoji, Category: category, CreatedAt: time.Now()}
|
||||
replaced := false
|
||||
for i := range ag.history.Reactions {
|
||||
if ag.history.Reactions[i].ResponseID == responseID {
|
||||
if ag.history.Reactions[i].Emoji == emoji {
|
||||
return nil
|
||||
}
|
||||
ag.history.Reactions[i] = reaction
|
||||
replaced = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !replaced {
|
||||
ag.history.Reactions = append(ag.history.Reactions, reaction)
|
||||
}
|
||||
ag.history.recomputeReactionCounts()
|
||||
ag.saveSession()
|
||||
return nil
|
||||
}
|
||||
|
||||
// execServer returns the execute server, or nil if unavailable.
|
||||
func (ag *Agent) execServer() toolsrv.Runner {
|
||||
return ag.runtime.ToolServer
|
||||
|
|
|
|||
|
|
@ -36,14 +36,6 @@ Be concise. Capture what another LLM needs to seamlessly continue.`
|
|||
warmIndexSize = 10
|
||||
)
|
||||
|
||||
type Reaction struct {
|
||||
ID string `json:"id"`
|
||||
ResponseID string `json:"responseId"`
|
||||
Emoji string `json:"emoji"`
|
||||
Category string `json:"category"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
}
|
||||
|
||||
// PersistedAgent is the on-disk format for a saved session.
|
||||
type PersistedAgent struct {
|
||||
ID string `json:"id"`
|
||||
|
|
@ -63,10 +55,6 @@ type PersistedAgent 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"`
|
||||
Reactions []Reaction `json:"reactions,omitempty"`
|
||||
}
|
||||
|
||||
// TaskState is a compact structured overlay that summarizes the agent's
|
||||
|
|
@ -149,9 +137,6 @@ func (s *History) saveToFull(path, id, agentName, backendName, modelName, cwd, r
|
|||
Estimated: s.Estimated,
|
||||
LastTurnCostUSD: s.LastTurnCostUSD,
|
||||
SessionCostUSD: s.SessionCostUSD,
|
||||
PositiveReactions: s.PositiveReactions,
|
||||
NegativeReactions: s.NegativeReactions,
|
||||
Reactions: s.Reactions,
|
||||
}
|
||||
data, err := json.Marshal(ps)
|
||||
if err != nil {
|
||||
|
|
@ -191,9 +176,6 @@ func RestoreHistory(ps *PersistedAgent) *History {
|
|||
Estimated: ps.Estimated,
|
||||
LastTurnCostUSD: ps.LastTurnCostUSD,
|
||||
SessionCostUSD: ps.SessionCostUSD,
|
||||
PositiveReactions: ps.PositiveReactions,
|
||||
NegativeReactions: ps.NegativeReactions,
|
||||
Reactions: ps.Reactions,
|
||||
}
|
||||
for i := range s.messages {
|
||||
if s.messages[i].Role == "assistant" && s.messages[i].ID == "" {
|
||||
|
|
@ -229,10 +211,6 @@ type History struct {
|
|||
turnCreationTokens int
|
||||
turnOutputTokens int
|
||||
turnCostUSD float64 // >0 when backend reported cost directly (e.g. OpenRouter)
|
||||
// Reaction tracking.
|
||||
PositiveReactions int
|
||||
NegativeReactions int
|
||||
Reactions []Reaction
|
||||
}
|
||||
|
||||
// newHistory creates a new empty Session. The caller is responsible for
|
||||
|
|
@ -258,18 +236,7 @@ func (s *History) Checkpoint(ts TaskState) *History {
|
|||
}
|
||||
|
||||
func (s *History) history() []backend.Message {
|
||||
if len(s.Reactions) == 0 {
|
||||
return s.messages
|
||||
}
|
||||
// Append feedback after the conversation so assistant tool-call messages stay
|
||||
// adjacent to their tool results, as required by provider APIs.
|
||||
out := make([]backend.Message, 0, len(s.messages)+len(s.Reactions))
|
||||
out = append(out, s.messages...)
|
||||
for _, r := range s.Reactions {
|
||||
_, desc, _ := classifyReaction(r.Emoji)
|
||||
out = append(out, backend.Message{Role: "user", Content: "[reaction to assistant response " + r.ResponseID + ": " + r.Category + " (" + r.Emoji + ")] " + desc})
|
||||
}
|
||||
return out
|
||||
return s.messages
|
||||
}
|
||||
|
||||
func (s *History) taskState() *TaskState {
|
||||
|
|
@ -296,18 +263,7 @@ func (s *History) addUsage(u backend.Usage, estimated bool) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *History) recomputeReactionCounts() {
|
||||
s.PositiveReactions = 0
|
||||
s.NegativeReactions = 0
|
||||
for _, r := range s.Reactions {
|
||||
switch r.Category {
|
||||
case "positive", "excellent":
|
||||
s.PositiveReactions++
|
||||
case "negative", "terrible":
|
||||
s.NegativeReactions++
|
||||
}
|
||||
}
|
||||
}
|
||||
func (s *History) recomputeReactionCounts() {}
|
||||
|
||||
func (s *History) resetTurnAccumulators() {
|
||||
s.turnInputTokens = 0
|
||||
|
|
@ -701,30 +657,5 @@ func (s *History) contextDebug() string {
|
|||
func NewResponseID() string {
|
||||
b := make([]byte, 3)
|
||||
rand.Read(b) //nolint:errcheck
|
||||
return "resp_" + strconv.FormatInt(time.Now().UnixNano(), 10) + "_" + fmt.Sprintf("%06x", b)
|
||||
}
|
||||
|
||||
// NewReactionID generates a unique identifier for a user reaction.
|
||||
func NewReactionID() string {
|
||||
b := make([]byte, 3)
|
||||
rand.Read(b) //nolint:errcheck
|
||||
return "react_" + strconv.FormatInt(time.Now().UnixNano(), 10) + "_" + fmt.Sprintf("%06x", b)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
return "resp_" + strconv.FormatInt(time.Now().UnixNano(), 10) + "_" + fmt.Sprintf("%06x", b)
|
||||
}
|
||||
|
|
|
|||
30
dbus/dbus.go
30
dbus/dbus.go
|
|
@ -297,21 +297,6 @@ func (a *DBusAdapter) Submit(sessionID, prompt string) (bool, *dbus.Error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
func (a *DBusAdapter) React(sessionID, emoji string) (bool, *dbus.Error) {
|
||||
return a.ReactTo(sessionID, "", emoji)
|
||||
}
|
||||
|
||||
func (a *DBusAdapter) ReactTo(sessionID, responseID, emoji string) (bool, *dbus.Error) {
|
||||
sess := fs.Lookup(a.tree, sessionID)
|
||||
if sess == nil {
|
||||
return false, nil
|
||||
}
|
||||
if err := sess.Core.Agent().React(responseID, emoji); err != nil {
|
||||
return false, dbus.MakeFailedError(err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (a *DBusAdapter) Interrupt(sessionID string) (bool, *dbus.Error) {
|
||||
sess := fs.Lookup(a.tree, sessionID)
|
||||
if sess == nil {
|
||||
|
|
@ -396,18 +381,6 @@ func (a *DBusAdapter) SetConfig(sessionID, key, value string) (bool, *dbus.Error
|
|||
return true, nil
|
||||
}
|
||||
|
||||
func (a *DBusAdapter) GetReactions(sessionID string) (string, *dbus.Error) {
|
||||
sess := fs.Lookup(a.tree, sessionID)
|
||||
if sess == nil {
|
||||
return "{}", nil
|
||||
}
|
||||
data, err := json.Marshal(sess.Core.Agent().Reactions())
|
||||
if err != nil {
|
||||
return "", dbus.MakeFailedError(err)
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
func (a *DBusAdapter) GetContext(sessionID string) (string, *dbus.Error) {
|
||||
sess := fs.Lookup(a.tree, sessionID)
|
||||
if sess == nil {
|
||||
|
|
@ -684,8 +657,6 @@ var introspectXML = `<node>
|
|||
<method name="KillSession"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="b"/></method>
|
||||
<method name="RenameSession"><arg direction="in" type="s" name="sessionID"/><arg direction="in" type="s" name="newName"/><arg direction="out" type="b"/></method>
|
||||
<method name="Submit"><arg direction="in" type="s" name="sessionID"/><arg direction="in" type="s" name="prompt"/><arg direction="out" type="b"/></method>
|
||||
<method name="React"><arg direction="in" type="s" name="sessionID"/><arg direction="in" type="s" name="emoji"/><arg direction="out" type="b"/></method>
|
||||
<method name="ReactTo"><arg direction="in" type="s" name="sessionID"/><arg direction="in" type="s" name="responseID"/><arg direction="in" type="s" name="emoji"/><arg direction="out" type="b"/></method>
|
||||
<method name="Interrupt"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="b"/></method>
|
||||
<method name="GetState"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="s"/></method>
|
||||
<method name="GetChat"><arg direction="in" type="s" name="sessionID"/><arg direction="in" type="x" name="offset"/><arg direction="out" type="s"/><arg direction="out" type="x"/></method>
|
||||
|
|
@ -694,7 +665,6 @@ var introspectXML = `<node>
|
|||
<method name="GetConfig"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="s"/></method>
|
||||
<method name="SetConfig"><arg direction="in" type="s" name="sessionID"/><arg direction="in" type="s" name="key"/><arg direction="in" type="s" name="value"/><arg direction="out" type="b"/></method>
|
||||
<method name="GetContext"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="s"/></method>
|
||||
<method name="GetReactions"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="s"/></method>
|
||||
<method name="ListBackends"><arg direction="out" type="as"/></method>
|
||||
<method name="ListModelsByBackend"><arg direction="in" type="s" name="backend"/><arg direction="out" type="as"/></method>
|
||||
<method name="ListModels"><arg direction="in" type="s" name="sessionID"/><arg direction="out" type="as"/></method>
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ import (
|
|||
|
||||
"ollie/agent"
|
||||
"ollie/backend"
|
||||
"ollie/toolsrv"
|
||||
olog "ollie/log"
|
||||
"ollie/toolsrv"
|
||||
)
|
||||
|
||||
// SessionFileList defines session-level files (shared across agents).
|
||||
|
|
@ -47,8 +47,6 @@ var AgentFileList = []struct {
|
|||
{"tail", false, false},
|
||||
{"prompt.prev", false, false},
|
||||
{"context", false, false},
|
||||
{"react", false, true},
|
||||
{"reactions", false, false},
|
||||
{"toolsrv.loaded", false, false},
|
||||
{"toolsrv.rev", false, false},
|
||||
{"tools", false, false},
|
||||
|
|
@ -125,14 +123,6 @@ func (h *sessionHelper) fileSpec(name string, mode os.FileMode) FileSpec {
|
|||
h.sess.mu.RUnlock()
|
||||
return data, nil
|
||||
}
|
||||
case "reactions":
|
||||
fs.Read = func() ([]byte, error) {
|
||||
data, err := json.Marshal(h.sess.Core.Agent().Reactions())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append(data, '\n'), nil
|
||||
}
|
||||
case "chat":
|
||||
fs.Read = func() ([]byte, error) {
|
||||
h.sess.mu.RLock()
|
||||
|
|
@ -238,28 +228,6 @@ func (h *sessionHelper) fileSpec(name string, mode os.FileMode) FileSpec {
|
|||
}
|
||||
return h.toolRegistry.Load(h.sess.id, input)
|
||||
}
|
||||
case "react":
|
||||
fs.Write = func(data []byte) error {
|
||||
input := strings.TrimSpace(string(data))
|
||||
if input == "" {
|
||||
return nil
|
||||
}
|
||||
var req struct {
|
||||
ResponseID string `json:"responseId"`
|
||||
Emoji string `json:"emoji"`
|
||||
}
|
||||
if strings.HasPrefix(input, "{") {
|
||||
if err := json.Unmarshal(data, &req); err != nil {
|
||||
return fmt.Errorf("react: invalid JSON: %w", err)
|
||||
}
|
||||
} else {
|
||||
req.Emoji = input
|
||||
}
|
||||
if req.Emoji == "" {
|
||||
return fmt.Errorf("react: emoji is required")
|
||||
}
|
||||
return h.sess.Core.Agent().React(req.ResponseID, req.Emoji)
|
||||
}
|
||||
}
|
||||
|
||||
// Wait
|
||||
|
|
|
|||
Loading…
Reference in New Issue