feat(agent): stop command
This commit is contained in:
parent
f3ef7090c5
commit
a0245c7b02
2 changed files with 155 additions and 0 deletions
103
pkg/agent/agent_stop.go
Normal file
103
pkg/agent/agent_stop.go
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sipeed/picoclaw/pkg/bus"
|
||||||
|
"github.com/sipeed/picoclaw/pkg/commands"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (al *AgentLoop) tryHandleStopCommand(
|
||||||
|
ctx context.Context,
|
||||||
|
msg bus.InboundMessage,
|
||||||
|
sessionKey string,
|
||||||
|
) bool {
|
||||||
|
cmdName, ok := commands.CommandName(msg.Content)
|
||||||
|
if !ok || cmdName != "stop" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := al.stopActiveTurnForSession(sessionKey)
|
||||||
|
reply := commands.FormatStopReply(result)
|
||||||
|
if err != nil {
|
||||||
|
reply = "Failed to stop task: " + err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
if al.channelManager != nil {
|
||||||
|
al.channelManager.InvokeTypingStop(msg.Channel, msg.ChatID)
|
||||||
|
}
|
||||||
|
al.resetMessageToolRound(sessionKey)
|
||||||
|
al.PublishResponseIfNeeded(ctx, msg.Channel, msg.ChatID, sessionKey, reply)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (al *AgentLoop) stopActiveTurnForSession(sessionKey string) (commands.StopResult, error) {
|
||||||
|
sessionKey = strings.TrimSpace(sessionKey)
|
||||||
|
if sessionKey == "" {
|
||||||
|
return commands.StopResult{}, fmt.Errorf("session key is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
result := commands.StopResult{}
|
||||||
|
cleared := al.clearSteeringMessagesForScope(sessionKey)
|
||||||
|
al.clearPendingSkills(sessionKey)
|
||||||
|
|
||||||
|
ts := al.getActiveTurnState(sessionKey)
|
||||||
|
if ts == nil {
|
||||||
|
result.Stopped = cleared > 0
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
snap := ts.snapshot()
|
||||||
|
result.TaskName = snap.UserMessage
|
||||||
|
|
||||||
|
if strings.HasPrefix(snap.TurnID, pendingTurnPrefix) {
|
||||||
|
al.markPendingStop(sessionKey)
|
||||||
|
result.Stopped = true
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := al.HardAbort(sessionKey); err != nil {
|
||||||
|
if al.getActiveTurnState(sessionKey) == nil {
|
||||||
|
result.Stopped = cleared > 0
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
return commands.StopResult{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Stopped = true
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (al *AgentLoop) markPendingStop(sessionKey string) {
|
||||||
|
sessionKey = strings.TrimSpace(sessionKey)
|
||||||
|
if sessionKey == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
al.pendingStops.Store(sessionKey, struct{}{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (al *AgentLoop) takePendingStop(sessionKey string) bool {
|
||||||
|
sessionKey = strings.TrimSpace(sessionKey)
|
||||||
|
if sessionKey == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, ok := al.pendingStops.LoadAndDelete(sessionKey)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (al *AgentLoop) resetMessageToolRound(sessionKey string) {
|
||||||
|
if strings.TrimSpace(sessionKey) == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if registry := al.GetRegistry(); registry != nil {
|
||||||
|
if agent := registry.GetDefaultAgent(); agent != nil {
|
||||||
|
if tool, ok := agent.Tools.Get("message"); ok {
|
||||||
|
if resetter, ok := tool.(interface{ ResetSentInRound(sessionKey string) }); ok {
|
||||||
|
resetter.ResetSentInRound(sessionKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
52
pkg/commands/cmd_stop.go
Normal file
52
pkg/commands/cmd_stop.go
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func stopCommand() Definition {
|
||||||
|
return Definition{
|
||||||
|
Name: "stop",
|
||||||
|
Description: "Stop the current task",
|
||||||
|
Usage: "/stop",
|
||||||
|
Handler: func(_ context.Context, req Request, rt *Runtime) error {
|
||||||
|
if rt == nil || rt.StopActiveTurn == nil {
|
||||||
|
return req.Reply(unavailableMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := rt.StopActiveTurn()
|
||||||
|
if err != nil {
|
||||||
|
return req.Reply("Failed to stop task: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return req.Reply(FormatStopReply(result))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatStopReply renders a user-facing reply for a stop request.
|
||||||
|
func FormatStopReply(result StopResult) string {
|
||||||
|
if !result.Stopped {
|
||||||
|
return "No active task to stop."
|
||||||
|
}
|
||||||
|
|
||||||
|
taskName := compactStopTaskName(result.TaskName)
|
||||||
|
if taskName == "" {
|
||||||
|
return "Task stopped. Current task was canceled."
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("Task stopped. %q was canceled.", taskName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func compactStopTaskName(taskName string) string {
|
||||||
|
taskName = strings.Join(strings.Fields(strings.TrimSpace(taskName)), " ")
|
||||||
|
if taskName == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if len(taskName) > 80 {
|
||||||
|
return taskName[:77] + "..."
|
||||||
|
}
|
||||||
|
return taskName
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue