- Implement V2 execution model in the standard executor, simplifying task execution to a single call without validation loops. - Introduce support for resuming suspended executions, allowing for human input during task processing. - Enhance event handling by pushing task completion and failure events to the event bus for better tracking and integration. - Update tests to reflect changes in execution flow and ensure robust handling of task statuses and results.
131 lines
4.1 KiB
Go
131 lines
4.1 KiB
Go
package api
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/yaoapp/yao/agent/robot/executor/standard"
|
|
"github.com/yaoapp/yao/agent/robot/manager"
|
|
"github.com/yaoapp/yao/agent/robot/types"
|
|
)
|
|
|
|
// InteractRequest represents a unified interaction with a robot.
|
|
type InteractRequest struct {
|
|
ExecutionID string `json:"execution_id,omitempty"`
|
|
TaskID string `json:"task_id,omitempty"`
|
|
Source types.InteractSource `json:"source,omitempty"`
|
|
Message string `json:"message"`
|
|
Action string `json:"action,omitempty"`
|
|
}
|
|
|
|
// InteractResult is the response from an interaction.
|
|
type InteractResult struct {
|
|
ExecutionID string `json:"execution_id,omitempty"`
|
|
Status string `json:"status"`
|
|
Message string `json:"message,omitempty"`
|
|
ChatID string `json:"chat_id,omitempty"`
|
|
Reply string `json:"reply,omitempty"`
|
|
WaitForMore bool `json:"wait_for_more,omitempty"`
|
|
}
|
|
|
|
// Interact handles all human-robot interactions through a unified entry point.
|
|
//
|
|
// Routing logic:
|
|
// - If manager is running, delegate to Manager.HandleInteract (full V2 flow with Host Agent)
|
|
// - Otherwise, use legacy direct-executor path for backward compatibility
|
|
func Interact(ctx *types.Context, memberID string, req *InteractRequest) (*InteractResult, error) {
|
|
if memberID == "" {
|
|
return nil, fmt.Errorf("member_id is required")
|
|
}
|
|
if req == nil {
|
|
return nil, fmt.Errorf("interact request is required")
|
|
}
|
|
|
|
// Try V2 path via manager
|
|
mgr, err := getManager()
|
|
if err == nil && mgr != nil {
|
|
return managerInteract(ctx, mgr, memberID, req)
|
|
}
|
|
|
|
// V1 fallback: require execution_id for direct resume
|
|
if req.ExecutionID == "" {
|
|
return nil, fmt.Errorf("execution_id is required for current version (Host Agent deferred)")
|
|
}
|
|
|
|
return legacyResume(ctx, req)
|
|
}
|
|
|
|
// managerInteract delegates to the manager's HandleInteract.
|
|
func managerInteract(ctx *types.Context, mgr *manager.Manager, memberID string, req *InteractRequest) (*InteractResult, error) {
|
|
mgrReq := &manager.InteractRequest{
|
|
ExecutionID: req.ExecutionID,
|
|
TaskID: req.TaskID,
|
|
Source: req.Source,
|
|
Message: req.Message,
|
|
Action: req.Action,
|
|
}
|
|
|
|
resp, err := mgr.HandleInteract(ctx, memberID, mgrReq)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &InteractResult{
|
|
ExecutionID: resp.ExecutionID,
|
|
Status: resp.Status,
|
|
Message: resp.Message,
|
|
ChatID: resp.ChatID,
|
|
Reply: resp.Reply,
|
|
WaitForMore: resp.WaitForMore,
|
|
}, nil
|
|
}
|
|
|
|
// legacyResume handles the direct executor resume path (backward compatible).
|
|
func legacyResume(ctx *types.Context, req *InteractRequest) (*InteractResult, error) {
|
|
executor := standard.New()
|
|
err := executor.Resume(ctx, req.ExecutionID, req.Message)
|
|
if err != nil {
|
|
if err == types.ErrExecutionSuspended {
|
|
return &InteractResult{
|
|
ExecutionID: req.ExecutionID,
|
|
Status: "waiting",
|
|
Message: "Execution suspended again: needs more input",
|
|
}, nil
|
|
}
|
|
return nil, fmt.Errorf("failed to resume execution: %w", err)
|
|
}
|
|
|
|
return &InteractResult{
|
|
ExecutionID: req.ExecutionID,
|
|
Status: "resumed",
|
|
Message: "Execution resumed and completed successfully",
|
|
}, nil
|
|
}
|
|
|
|
// Reply is a semantic shortcut for replying to a specific waiting task.
|
|
func Reply(ctx *types.Context, memberID string, execID string, taskID string, message string) (*InteractResult, error) {
|
|
return Interact(ctx, memberID, &InteractRequest{
|
|
ExecutionID: execID,
|
|
TaskID: taskID,
|
|
Source: types.InteractSourceUI,
|
|
Message: message,
|
|
})
|
|
}
|
|
|
|
// Confirm is a semantic shortcut for confirming a pending execution.
|
|
func Confirm(ctx *types.Context, memberID string, execID string, message string) (*InteractResult, error) {
|
|
return Interact(ctx, memberID, &InteractRequest{
|
|
ExecutionID: execID,
|
|
Source: types.InteractSourceUI,
|
|
Message: message,
|
|
Action: "confirm",
|
|
})
|
|
}
|
|
|
|
// CancelExecution cancels a waiting/confirming execution via the manager.
|
|
func CancelExecution(ctx *types.Context, execID string) error {
|
|
mgr, err := getManager()
|
|
if err != nil {
|
|
return fmt.Errorf("cancel not available: %w", err)
|
|
}
|
|
return mgr.CancelExecution(ctx, execID)
|
|
}
|