46 lines
1.7 KiB
Go
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."
|
|
}
|