package context import ( "github.com/yaoapp/yao/agent/output" "github.com/yaoapp/yao/agent/output/message" ) // Send sends a message via the output module // Automatically manages BlockID, ThreadID, and metadata for delta operations // - For delta operations: inherits BlockID and ThreadID from original message // - For new messages: auto-generates BlockID if not specified, sets ThreadID from Stack // - Records metadata for all sent messages to enable delta inheritance func (ctx *Context) Send(msg *message.Message) error { output, err := ctx.getOutput() if err != nil { return err } // === Delta operations: Auto-inherit BlockID and ThreadID === if msg.Delta && msg.MessageID != "" { if metadata := ctx.getMessageMetadata(msg.MessageID); metadata != nil { // Inherit BlockID if not specified if msg.BlockID == "" { msg.BlockID = metadata.BlockID } // Inherit ThreadID if not specified if msg.ThreadID == "" { msg.ThreadID = metadata.ThreadID } } } // === Non-delta operations: Auto-set fields === if !msg.Delta { // Auto-set ThreadID for non-root Stack (nested agent calls) if msg.ThreadID == "" && ctx.Stack != nil && !ctx.Stack.IsRoot() { msg.ThreadID = ctx.Stack.ID } // BlockID is NOT auto-generated by default (only manually specified in special cases) // Example: Send a web card after LLM output, group them in the same Block // Developers can specify via ctx.Send(message, blockId) or message.block_id } // === Record metadata for subsequent delta operations === ctx.recordMessageMetadata(msg) // === Actually send the message === return output.Send(msg) } // SendGroup sends a group of messages via the output module // Deprecated: This method is deprecated and will be removed in future versions func (ctx *Context) SendGroup(group *message.Group) error { output, err := ctx.getOutput() if err != nil { return err } return output.SendGroup(group) } // Flush flushes the output writer func (ctx *Context) Flush() error { output, err := ctx.getOutput() if err != nil { return err } return output.Flush() } // CloseOutput closes the output writer func (ctx *Context) CloseOutput() error { output, err := ctx.getOutput() if err != nil { return err } return output.Close() } // getOutput gets the output writer for the context func (ctx *Context) getOutput() (*output.Output, error) { if ctx.output != nil { return ctx.output, nil } trace, _ := ctx.Trace() var options message.Options = message.Options{ BaseURL: "/", Writer: ctx.Writer, Trace: trace, Locale: ctx.Locale, Accept: string(ctx.Accept), } // Convert ModelCapabilities to message.ModelCapabilities if ctx.Capabilities != nil { options.Capabilities = &message.ModelCapabilities{ Vision: ctx.Capabilities.Vision, ToolCalls: ctx.Capabilities.ToolCalls, Audio: ctx.Capabilities.Audio, Reasoning: ctx.Capabilities.Reasoning, Streaming: ctx.Capabilities.Streaming, JSON: ctx.Capabilities.JSON, Multimodal: ctx.Capabilities.Multimodal, TemperatureAdjustable: ctx.Capabilities.TemperatureAdjustable, } } var err error ctx.output, err = output.NewOutput(options) if err != nil { return nil, err } return ctx.output, nil }