package executor import ( "encoding/json" "fmt" "strings" agentcontext "github.com/yaoapp/yao/agent/context" "github.com/yaoapp/yao/agent/robot/types" ) // InputFormatter provides methods to format input data for assistant prompts // Each phase has specific input requirements: // - P0 (Inspiration): ClockContext + Robot identity // - P1 (Goals): InspirationReport (Clock) or TriggerInput (Human/Event) // - P2 (Tasks): Goals + Available tools // - P3 (Run): Tasks // - P4 (Delivery): Task results // - P5 (Learning): Execution summary type InputFormatter struct{} // NewInputFormatter creates a new InputFormatter func NewInputFormatter() *InputFormatter { return &InputFormatter{} } // FormatClockContext formats ClockContext as user message content // Used by P0 (Inspiration) phase func (f *InputFormatter) FormatClockContext(clock *types.ClockContext, robot *types.Robot) string { if clock == nil { return "" } var sb strings.Builder // Time context section sb.WriteString("## Current Time Context\n\n") sb.WriteString(fmt.Sprintf("- **Now**: %s\n", clock.Now.Format("2006-01-02 15:04:05"))) sb.WriteString(fmt.Sprintf("- **Day**: %s\n", clock.DayOfWeek)) sb.WriteString(fmt.Sprintf("- **Date**: %d/%d/%d\n", clock.Year, clock.Month, clock.DayOfMonth)) sb.WriteString(fmt.Sprintf("- **Week**: %d of year\n", clock.WeekOfYear)) sb.WriteString(fmt.Sprintf("- **Timezone**: %s\n", clock.TZ)) // Time markers sb.WriteString("\n### Time Markers\n") if clock.IsWeekend { sb.WriteString("- ✓ Weekend\n") } if clock.IsMonthStart { sb.WriteString("- ✓ Month Start (1st-3rd)\n") } if clock.IsMonthEnd { sb.WriteString("- ✓ Month End (last 3 days)\n") } if clock.IsQuarterEnd { sb.WriteString("- ✓ Quarter End\n") } if clock.IsYearEnd { sb.WriteString("- ✓ Year End\n") } // Robot identity section (if available) if robot != nil && robot.Config != nil && robot.Config.Identity != nil { sb.WriteString("\n## Robot Identity\n\n") sb.WriteString(fmt.Sprintf("- **Role**: %s\n", robot.Config.Identity.Role)) if len(robot.Config.Identity.Duties) > 0 { sb.WriteString("- **Duties**:\n") for _, duty := range robot.Config.Identity.Duties { sb.WriteString(fmt.Sprintf(" - %s\n", duty)) } } if len(robot.Config.Identity.Rules) > 0 { sb.WriteString("- **Rules**:\n") for _, rule := range robot.Config.Identity.Rules { sb.WriteString(fmt.Sprintf(" - %s\n", rule)) } } } return sb.String() } // FormatInspirationReport formats InspirationReport as user message content // Used by P1 (Goals) phase when trigger is Clock func (f *InputFormatter) FormatInspirationReport(report *types.InspirationReport) string { if report == nil { return "" } var sb strings.Builder // Clock context summary (if available) if report.Clock != nil { sb.WriteString("## Time Context\n\n") sb.WriteString(fmt.Sprintf("- **Time**: %s %s\n", report.Clock.DayOfWeek, report.Clock.Now.Format("15:04"))) sb.WriteString(fmt.Sprintf("- **Date**: %d/%d/%d\n", report.Clock.Year, report.Clock.Month, report.Clock.DayOfMonth)) // Add relevant time markers var markers []string if report.Clock.IsWeekend { markers = append(markers, "Weekend") } if report.Clock.IsMonthStart { markers = append(markers, "Month Start") } if report.Clock.IsMonthEnd { markers = append(markers, "Month End") } if report.Clock.IsQuarterEnd { markers = append(markers, "Quarter End") } if len(markers) > 0 { sb.WriteString(fmt.Sprintf("- **Markers**: %s\n", strings.Join(markers, ", "))) } sb.WriteString("\n") } // Inspiration content if report.Content != "" { sb.WriteString("## Inspiration Report\n\n") sb.WriteString(report.Content) sb.WriteString("\n") } return sb.String() } // FormatTriggerInput formats TriggerInput as user message content // Used by P1 (Goals) phase when trigger is Human or Event func (f *InputFormatter) FormatTriggerInput(input *types.TriggerInput) string { if input == nil { return "" } var sb strings.Builder // Human intervention if input.Action != "" { sb.WriteString("## Human Intervention\n\n") sb.WriteString(fmt.Sprintf("- **Action**: %s\n", input.Action)) if input.UserID != "" { sb.WriteString(fmt.Sprintf("- **User**: %s\n", input.UserID)) } // Messages if len(input.Messages) > 0 { sb.WriteString("\n### User Input\n\n") for _, msg := range input.Messages { if content, ok := msg.Content.(string); ok { sb.WriteString(content) sb.WriteString("\n") } } } return sb.String() } // Event trigger if input.Source != "" { sb.WriteString("## Event Trigger\n\n") sb.WriteString(fmt.Sprintf("- **Source**: %s\n", input.Source)) sb.WriteString(fmt.Sprintf("- **Event Type**: %s\n", input.EventType)) // Event data if input.Data != nil { sb.WriteString("\n### Event Data\n\n") sb.WriteString("```json\n") if data, err := json.MarshalIndent(input.Data, "", " "); err == nil { sb.WriteString(string(data)) } sb.WriteString("\n```\n") } return sb.String() } return "" } // FormatGoals formats Goals as user message content // Used by P2 (Tasks) phase func (f *InputFormatter) FormatGoals(goals *types.Goals, robot *types.Robot) string { if goals == nil { return "" } var sb strings.Builder // Goals content sb.WriteString("## Goals\n\n") sb.WriteString(goals.Content) sb.WriteString("\n") // Available resources (if robot config available) if robot != nil && robot.Config != nil && robot.Config.Resources != nil { sb.WriteString("\n## Available Resources\n\n") // Agents if len(robot.Config.Resources.Agents) > 0 { sb.WriteString("### Agents\n") for _, agent := range robot.Config.Resources.Agents { sb.WriteString(fmt.Sprintf("- %s\n", agent)) } } // MCP tools if len(robot.Config.Resources.MCP) > 0 { sb.WriteString("\n### MCP Tools\n") for _, mcp := range robot.Config.Resources.MCP { if len(mcp.Tools) > 0 { sb.WriteString(fmt.Sprintf("- %s: %s\n", mcp.ID, strings.Join(mcp.Tools, ", "))) } else { sb.WriteString(fmt.Sprintf("- %s: all tools\n", mcp.ID)) } } } } return sb.String() } // FormatTasks formats Tasks as user message content // Used by P3 (Run) phase func (f *InputFormatter) FormatTasks(tasks []types.Task) string { if len(tasks) == 0 { return "No tasks to execute." } var sb strings.Builder sb.WriteString("## Tasks to Execute\n\n") for i, task := range tasks { sb.WriteString(fmt.Sprintf("### Task %d: %s\n\n", i+1, task.ID)) sb.WriteString(fmt.Sprintf("- **Goal Reference**: %s\n", task.GoalRef)) sb.WriteString(fmt.Sprintf("- **Source**: %s\n", task.Source)) sb.WriteString(fmt.Sprintf("- **Executor**: %s (%s)\n", task.ExecutorID, task.ExecutorType)) // Task content if len(task.Messages) > 0 { sb.WriteString("\n**Instructions**:\n") for _, msg := range task.Messages { if content, ok := msg.Content.(string); ok { sb.WriteString(content) sb.WriteString("\n") } } } // Arguments if len(task.Args) > 0 { sb.WriteString("\n**Arguments**:\n") if args, err := json.MarshalIndent(task.Args, "", " "); err == nil { sb.WriteString("```json\n") sb.WriteString(string(args)) sb.WriteString("\n```\n") } } sb.WriteString("\n") } return sb.String() } // FormatTaskResults formats TaskResults as user message content // Used by P4 (Delivery) and P5 (Learning) phases func (f *InputFormatter) FormatTaskResults(results []types.TaskResult) string { if len(results) == 0 { return "No task results." } var sb strings.Builder sb.WriteString("## Task Results\n\n") successCount := 0 failCount := 0 for _, result := range results { if result.Success { successCount++ } else { failCount++ } sb.WriteString(fmt.Sprintf("### Task: %s\n\n", result.TaskID)) if result.Success { sb.WriteString("- **Status**: ✓ Success\n") } else { sb.WriteString("- **Status**: ✗ Failed\n") } sb.WriteString(fmt.Sprintf("- **Duration**: %dms\n", result.Duration)) sb.WriteString(fmt.Sprintf("- **Validated**: %t\n", result.Validated)) // Output if result.Output != nil { sb.WriteString("\n**Output**:\n") if output, err := json.MarshalIndent(result.Output, "", " "); err == nil { sb.WriteString("```json\n") sb.WriteString(string(output)) sb.WriteString("\n```\n") } else { sb.WriteString(fmt.Sprintf("%v\n", result.Output)) } } // Error if result.Error != "" { sb.WriteString(fmt.Sprintf("\n**Error**: %s\n", result.Error)) } sb.WriteString("\n") } // Summary sb.WriteString(fmt.Sprintf("## Summary\n\n- Total: %d tasks\n- Success: %d\n- Failed: %d\n", len(results), successCount, failCount)) return sb.String() } // FormatExecutionSummary formats the entire execution for P5 (Learning) phase func (f *InputFormatter) FormatExecutionSummary(exec *types.Execution) string { if exec == nil { return "" } var sb strings.Builder // Execution metadata sb.WriteString("## Execution Summary\n\n") sb.WriteString(fmt.Sprintf("- **ID**: %s\n", exec.ID)) sb.WriteString(fmt.Sprintf("- **Trigger**: %s\n", exec.TriggerType)) sb.WriteString(fmt.Sprintf("- **Status**: %s\n", exec.Status)) sb.WriteString(fmt.Sprintf("- **Start Time**: %s\n", exec.StartTime.Format("2006-01-02 15:04:05"))) if exec.EndTime != nil { sb.WriteString(fmt.Sprintf("- **End Time**: %s\n", exec.EndTime.Format("2006-01-02 15:04:05"))) duration := exec.EndTime.Sub(exec.StartTime) sb.WriteString(fmt.Sprintf("- **Duration**: %s\n", duration.String())) } if exec.Error != "" { sb.WriteString(fmt.Sprintf("- **Error**: %s\n", exec.Error)) } sb.WriteString("\n") // Inspiration (P0) if exec.Inspiration != nil && exec.Inspiration.Content != "" { sb.WriteString("## Inspiration (P0)\n\n") sb.WriteString(exec.Inspiration.Content) sb.WriteString("\n\n") } // Goals (P1) if exec.Goals != nil && exec.Goals.Content != "" { sb.WriteString("## Goals (P1)\n\n") sb.WriteString(exec.Goals.Content) sb.WriteString("\n\n") } // Tasks (P2) if len(exec.Tasks) > 0 { sb.WriteString("## Tasks (P2)\n\n") for i, task := range exec.Tasks { sb.WriteString(fmt.Sprintf("%d. [%s] %s (executor: %s)\n", i+1, task.Status, task.ID, task.ExecutorID)) } sb.WriteString("\n") } // Results (P3) if len(exec.Results) > 0 { sb.WriteString("## Results (P3)\n\n") for _, result := range exec.Results { status := "✓" if !result.Success { status = "✗" } sb.WriteString(fmt.Sprintf("- %s %s (%dms)\n", status, result.TaskID, result.Duration)) } sb.WriteString("\n") } // Delivery (P4) if exec.Delivery != nil { sb.WriteString("## Delivery (P4)\n\n") sb.WriteString(fmt.Sprintf("- **Type**: %s\n", exec.Delivery.Type)) if exec.Delivery.Success { sb.WriteString("- **Status**: ✓ Success\n") } else { sb.WriteString(fmt.Sprintf("- **Status**: ✗ Failed (%s)\n", exec.Delivery.Error)) } sb.WriteString("\n") } return sb.String() } // BuildMessages is a convenience method to build messages array from content func (f *InputFormatter) BuildMessages(userContent string) []agentcontext.Message { return []agentcontext.Message{ { Role: agentcontext.RoleUser, Content: userContent, }, } } // BuildMessagesWithSystem builds messages array with system and user content func (f *InputFormatter) BuildMessagesWithSystem(systemContent, userContent string) []agentcontext.Message { return []agentcontext.Message{ { Role: agentcontext.RoleSystem, Content: systemContent, }, { Role: agentcontext.RoleUser, Content: userContent, }, } }