yao/agent/robot/api/trigger.go
Max c6ca68876d Refactor API Structure and Update TODO for MVP Integration
- Updated the API documentation in TODO.md to reflect the completion of the Go API implementation and end-to-end tests for the main flow (P0 → P1 → P2 → P3 → P4).
- Clarified that Process handlers and JSAPI are optional features, moving them to Phase 12, while emphasizing that the Go API is sufficient for MVP integration.
- Removed obsolete API files (`api.go`, `jsapi.go`, `process.go`) as they were stubs and not implemented in Phase 10, streamlining the codebase.
- Revised the structure of the TODO.md to better outline the goals and tasks for Phases 11 and 12, ensuring clear tracking of progress and dependencies.
2026-01-19 09:17:33 +08:00

191 lines
5.2 KiB
Go

package api
import (
"fmt"
"github.com/yaoapp/yao/agent/robot/types"
)
// ==================== Trigger API ====================
// These functions handle robot execution triggers
// Trigger starts a robot execution with the specified trigger type and request
// This is the main entry point for triggering robot execution
func Trigger(ctx *types.Context, memberID string, req *TriggerRequest) (*TriggerResult, error) {
if memberID == "" {
return nil, fmt.Errorf("member_id is required")
}
if req == nil {
return nil, fmt.Errorf("trigger request is required")
}
mgr, err := getManager()
if err != nil {
return nil, err
}
switch req.Type {
case types.TriggerHuman:
return triggerHuman(ctx, mgr, memberID, req)
case types.TriggerEvent:
return triggerEvent(ctx, mgr, memberID, req)
case types.TriggerClock:
return triggerManual(ctx, mgr, memberID, req)
default:
return nil, fmt.Errorf("invalid trigger type: %s", req.Type)
}
}
// TriggerManual manually triggers a robot execution (for testing or debugging)
// This bypasses normal trigger validation and directly submits to the pool
func TriggerManual(ctx *types.Context, memberID string, triggerType types.TriggerType, data interface{}) (*TriggerResult, error) {
if memberID == "" {
return nil, fmt.Errorf("member_id is required")
}
mgr, err := getManager()
if err != nil {
return nil, err
}
execID, err := mgr.TriggerManual(ctx, memberID, triggerType, data)
if err != nil {
return &TriggerResult{
Accepted: false,
Message: err.Error(),
}, nil
}
return &TriggerResult{
Accepted: true,
JobID: execID,
Message: fmt.Sprintf("Manual trigger (%s) submitted", triggerType),
}, nil
}
// Intervene processes a human intervention request
// Human intervention skips P0 (inspiration) and goes directly to P1 (goals)
func Intervene(ctx *types.Context, memberID string, req *TriggerRequest) (*TriggerResult, error) {
if memberID == "" {
return nil, fmt.Errorf("member_id is required")
}
if req == nil {
return nil, fmt.Errorf("intervention request is required")
}
mgr, err := getManager()
if err != nil {
return nil, err
}
return triggerHuman(ctx, mgr, memberID, req)
}
// HandleEvent processes an event trigger request
// Event trigger skips P0 (inspiration) and goes directly to P1 (goals)
func HandleEvent(ctx *types.Context, memberID string, req *TriggerRequest) (*TriggerResult, error) {
if memberID == "" {
return nil, fmt.Errorf("member_id is required")
}
if req == nil {
return nil, fmt.Errorf("event request is required")
}
mgr, err := getManager()
if err != nil {
return nil, err
}
return triggerEvent(ctx, mgr, memberID, req)
}
// ==================== Internal Trigger Functions ====================
// triggerHuman handles human intervention trigger
func triggerHuman(ctx *types.Context, mgr managerInterface, memberID string, req *TriggerRequest) (*TriggerResult, error) {
// Build intervention request
interveneReq := &types.InterveneRequest{
MemberID: memberID,
TeamID: ctx.TeamID(),
Action: req.Action,
Messages: req.Messages,
PlanTime: req.PlanAt,
ExecutorMode: req.ExecutorMode,
}
// Call manager's Intervene
result, err := mgr.Intervene(ctx, interveneReq)
if err != nil {
return &TriggerResult{
Accepted: false,
Message: err.Error(),
}, nil
}
return &TriggerResult{
Accepted: true,
JobID: result.ExecutionID,
Message: result.Message,
}, nil
}
// triggerEvent handles event trigger
func triggerEvent(ctx *types.Context, mgr managerInterface, memberID string, req *TriggerRequest) (*TriggerResult, error) {
// Build event request
eventReq := &types.EventRequest{
MemberID: memberID,
Source: string(req.Source),
EventType: req.EventType,
Data: req.Data,
ExecutorMode: req.ExecutorMode,
}
// Call manager's HandleEvent
result, err := mgr.HandleEvent(ctx, eventReq)
if err != nil {
return &TriggerResult{
Accepted: false,
Message: err.Error(),
}, nil
}
return &TriggerResult{
Accepted: true,
JobID: result.ExecutionID,
Message: result.Message,
}, nil
}
// triggerManual handles manual/clock trigger
func triggerManual(ctx *types.Context, mgr managerInterface, memberID string, req *TriggerRequest) (*TriggerResult, error) {
// For clock trigger, pass clock context if available
var data interface{}
if req.Data != nil {
data = req.Data
}
execID, err := mgr.TriggerManual(ctx, memberID, req.Type, data)
if err != nil {
return &TriggerResult{
Accepted: false,
Message: err.Error(),
}, nil
}
return &TriggerResult{
Accepted: true,
JobID: execID,
Message: fmt.Sprintf("Trigger (%s) submitted", req.Type),
}, nil
}
// managerInterface defines the methods we need from manager
// This allows for easier testing with mocks
type managerInterface interface {
TriggerManual(ctx *types.Context, memberID string, trigger types.TriggerType, data interface{}) (string, error)
Intervene(ctx *types.Context, req *types.InterveneRequest) (*types.ExecutionResult, error)
HandleEvent(ctx *types.Context, req *types.EventRequest) (*types.ExecutionResult, error)
PauseExecution(ctx *types.Context, execID string) error
ResumeExecution(ctx *types.Context, execID string) error
StopExecution(ctx *types.Context, execID string) error
}