Files
Cyrene_v2/internal/services/backthink/thinker.go
T

46 lines
1.7 KiB
Go

package backthink
import (
"context"
"fmt"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/loop"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
)
// LoopThinker adapts a *loop.Loop into a Thinker. It is the production wiring
// that lets a background pass drive the full tool loop (memory injection, model
// steps, tool calls such as IoT/calendar/reminders) without the service knowing
// the loop's concrete shape.
type LoopThinker struct {
loop *loop.Loop
}
// NewLoopThinker wraps l as a Thinker.
func NewLoopThinker(l *loop.Loop) *LoopThinker { return &LoopThinker{loop: l} }
// Think runs one unattended think through the wrapped loop and returns the
// conclusion text. It returns ErrNoLoop when no loop was injected.
func (t *LoopThinker) Think(ctx context.Context, sessionID, trigger string) (string, error) {
if t.loop == nil {
return "", ErrNoLoop
}
messages := []model.Message{model.TextMessage(model.RoleUser, backgroundPrompt(trigger))}
res, err := t.loop.Run(ctx, sessionID, messages)
if err != nil {
return "", fmt.Errorf("backthink: think: %w", err)
}
return res.Text, nil
}
// backgroundPrompt is the instruction handed to the runtime for one unattended
// thought. It explicitly avoids asking for a chat-style message to the user.
func backgroundPrompt(trigger string) string {
return "[background autonomous reflection]\n" +
"Trigger: " + trigger + "\n" +
"Review recent context, device states, calendar and reminders. Decide " +
"whether anything is worth proactively telling the owner. If nothing " +
"matters, conclude \"nothing to report\". Keep the conclusion to one or " +
"two sentences. Do not produce a chat-style message to the user."
}