yao/agent/output/output.go
Max 21c0ec5034 Enhance Assistant's Stream method and context handling for improved traceability and output management
- Refactored the Stream method to retrieve connector capabilities early, allowing output adapters to utilize them effectively.
- Introduced a new Info method in the Assistant to provide structured assistant information, enhancing context accessibility.
- Updated StreamStartData to include additional fields such as ChatID and Assistant info for better event tracking.
- Improved error handling in the Stream method to ensure robust management of connector retrieval failures.
- Enhanced internationalization support for stream event messages, providing localized output for different clients.
2025-11-22 11:14:49 +08:00

149 lines
3.4 KiB
Go

package output
import (
"fmt"
"sync"
"github.com/yaoapp/yao/agent/context"
"github.com/yaoapp/yao/agent/output/adapters/cui"
"github.com/yaoapp/yao/agent/output/adapters/openai"
"github.com/yaoapp/yao/agent/output/message"
)
var (
writerCache = make(map[*context.Context]message.Writer)
writerMutex sync.RWMutex
globalFactory message.WriterFactory
)
// Send sends a single message using the appropriate writer for the context
func Send(ctx *context.Context, msg *message.Message) error {
writer, err := GetWriter(ctx)
if err != nil {
return err
}
return writer.Write(msg)
}
// SendGroup sends a message group using the appropriate writer for the context
func SendGroup(ctx *context.Context, group *message.Group) error {
writer, err := GetWriter(ctx)
if err != nil {
return err
}
return writer.WriteGroup(group)
}
// GetWriter gets or creates a writer for the given context
// Writers are cached per context to avoid recreating them
func GetWriter(ctx *context.Context) (message.Writer, error) {
// Try to get cached writer
writerMutex.RLock()
writer, exists := writerCache[ctx]
writerMutex.RUnlock()
if exists {
return writer, nil
}
// Create new writer
writerMutex.Lock()
defer writerMutex.Unlock()
// Double-check after acquiring write lock
if writer, exists := writerCache[ctx]; exists {
return writer, nil
}
// Create writer based on context.Accept
writer, err := createWriter(ctx)
if err != nil {
return nil, err
}
// Cache the writer
writerCache[ctx] = writer
return writer, nil
}
// createWriter creates a writer based on context.Accept
func createWriter(ctx *context.Context) (message.Writer, error) {
// If global factory is set, use it
if globalFactory != nil {
return globalFactory.NewWriter(ctx, nil)
}
// Default: create based on Accept type
switch ctx.Accept {
case context.AcceptStandard:
// OpenAI-compatible format
return openai.NewWriter(ctx)
case context.AcceptWebCUI, context.AccepNativeCUI, context.AcceptDesktopCUI:
// CUI format
return cui.NewWriter(ctx)
default:
// Default to Standard
return openai.NewWriter(ctx)
}
}
// SetWriterFactory sets a custom writer factory
// This allows applications to provide their own writer implementations
func SetWriterFactory(factory message.WriterFactory) {
globalFactory = factory
}
// ClearWriterCache clears the writer cache
// Should be called when contexts are cleaned up
func ClearWriterCache(ctx *context.Context) {
writerMutex.Lock()
defer writerMutex.Unlock()
delete(writerCache, ctx)
}
// ClearAllWriterCache clears all cached writers
func ClearAllWriterCache() {
writerMutex.Lock()
defer writerMutex.Unlock()
writerCache = make(map[*context.Context]message.Writer)
}
// Flush flushes the writer for the given context
func Flush(ctx *context.Context) error {
writer, err := GetWriter(ctx)
if err != nil {
return err
}
return writer.Flush()
}
// Close closes the writer for the given context and removes it from cache
func Close(ctx *context.Context) error {
writer, err := GetWriter(ctx)
if err != nil {
return err
}
err = writer.Close()
ClearWriterCache(ctx)
return err
}
// SendMulti is a convenience function to send multiple messages
func SendMulti(ctx *context.Context, messages ...*message.Message) error {
writer, err := GetWriter(ctx)
if err != nil {
return err
}
for _, msg := range messages {
if err := writer.Write(msg); err != nil {
return fmt.Errorf("failed to send message: %w", err)
}
}
return nil
}