Add retry and silent modes to assistant chat processing
- Introduce retry and silent mode flags in context and message structures - Modify API and hooks to support new context modes - Update message handling to propagate retry and silent flags - Replace debug print statements with structured logging - Enhance tool call and message processing with new context options
This commit is contained in:
parent
14bcaa0bf4
commit
13c4e65a95
4 changed files with 113 additions and 23 deletions
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/yaoapp/gou/fs"
|
||||
"github.com/yaoapp/kun/utils"
|
||||
"github.com/yaoapp/kun/log"
|
||||
chatctx "github.com/yaoapp/yao/neo/context"
|
||||
chatMessage "github.com/yaoapp/yao/neo/message"
|
||||
)
|
||||
|
|
@ -190,6 +190,14 @@ func (next *NextAction) Execute(c *gin.Context, ctx chatctx.Context, contents *c
|
|||
return fmt.Errorf("input is required")
|
||||
}
|
||||
|
||||
// Retry mode
|
||||
retry := false
|
||||
_, has = next.Payload["retry"]
|
||||
if has {
|
||||
retry = next.Payload["retry"].(bool)
|
||||
ctx.Retry = retry
|
||||
}
|
||||
|
||||
switch v := next.Payload["input"].(type) {
|
||||
case string:
|
||||
messages := chatMessage.Message{}
|
||||
|
|
@ -338,6 +346,10 @@ func (ast *Assistant) streamChat(
|
|||
return 1 // continue
|
||||
}
|
||||
|
||||
// Retry mode
|
||||
msg.Retry = ctx.Retry // Retry mode
|
||||
msg.Silent = ctx.Silent // Silent mode
|
||||
|
||||
// Handle error
|
||||
if msg.Type == "error" {
|
||||
value := msg.String()
|
||||
|
|
@ -348,7 +360,10 @@ func (ast *Assistant) streamChat(
|
|||
value = res.Error
|
||||
}
|
||||
}
|
||||
chatMessage.New().Error(value).Done().Write(c.Writer)
|
||||
newMsg := chatMessage.New().Error(value).Done()
|
||||
newMsg.Retry = ctx.Retry
|
||||
newMsg.Silent = ctx.Silent
|
||||
newMsg.Write(c.Writer)
|
||||
return 0 // break
|
||||
}
|
||||
|
||||
|
|
@ -468,6 +483,9 @@ func (ast *Assistant) streamChat(
|
|||
"delta": true,
|
||||
})
|
||||
|
||||
output.Retry = ctx.Retry // Retry mode
|
||||
output.Silent = ctx.Silent // Silent mode
|
||||
|
||||
if isFirst {
|
||||
output.Assistant(ast.ID, ast.Name, ast.Avatar)
|
||||
isFirst = false
|
||||
|
|
@ -489,6 +507,8 @@ func (ast *Assistant) streamChat(
|
|||
"type": "text",
|
||||
"delta": true,
|
||||
"done": true,
|
||||
"retry": ctx.Retry,
|
||||
"silent": ctx.Silent,
|
||||
}).
|
||||
Write(c.Writer)
|
||||
}
|
||||
|
|
@ -521,6 +541,8 @@ func (ast *Assistant) streamChat(
|
|||
output := chatMessage.New().Done()
|
||||
if res != nil && res.Output != nil {
|
||||
output = chatMessage.New().Map(map[string]interface{}{"text": res.Output, "done": true})
|
||||
output.Retry = ctx.Retry
|
||||
output.Silent = ctx.Silent
|
||||
}
|
||||
output.Write(c.Writer)
|
||||
done <- true
|
||||
|
|
@ -542,6 +564,8 @@ func (ast *Assistant) streamChat(
|
|||
if err != nil {
|
||||
return fmt.Errorf("error: %s", err.Error())
|
||||
}
|
||||
msg.Retry = ctx.Retry
|
||||
msg.Silent = ctx.Silent
|
||||
msg.Done().Write(c.Writer)
|
||||
}
|
||||
|
||||
|
|
@ -826,9 +850,10 @@ func (ast *Assistant) requestMessages(ctx context.Context, messages []chatMessag
|
|||
|
||||
// For debug environment, print the request messages
|
||||
if os.Getenv("YAO_AGENT_PRINT_REQUEST_MESSAGES") == "true" {
|
||||
fmt.Println("--- REQUEST_MESSAGES -----------------------------")
|
||||
utils.Dump(newMessages)
|
||||
fmt.Println("--- END REQUEST_MESSAGES -----------------------------")
|
||||
for _, message := range newMessages {
|
||||
raw, _ := jsoniter.MarshalToString(message)
|
||||
log.Trace("[Request Message] %s", raw)
|
||||
}
|
||||
}
|
||||
|
||||
return newMessages, nil
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/yaoapp/gou/runtime/v8/bridge"
|
||||
"github.com/yaoapp/kun/log"
|
||||
chatctx "github.com/yaoapp/yao/neo/context"
|
||||
"github.com/yaoapp/yao/neo/message"
|
||||
chatMessage "github.com/yaoapp/yao/neo/message"
|
||||
|
|
@ -168,9 +170,7 @@ func (ast *Assistant) HookDone(c *gin.Context, context chatctx.Context, input []
|
|||
text = content[:endIndex]
|
||||
text = strings.TrimSpace(text)
|
||||
if os.Getenv("YAO_AGENT_PRINT_TOOL_CALL") == "true" {
|
||||
fmt.Println("---- EXTRACTED TOOL CALL ----")
|
||||
fmt.Println(text)
|
||||
fmt.Println("---- END EXTRACTED TOOL CALL ----")
|
||||
log.Trace("[TOOL CALL] %s", text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -309,7 +309,81 @@ func (ast *Assistant) call(ctx context.Context, method string, c *gin.Context, c
|
|||
defer scriptCtx.Close()
|
||||
|
||||
// Add sendMessage function to the script context
|
||||
scriptCtx.WithFunction("SendMessage", func(info *v8go.FunctionCallbackInfo) *v8go.Value {
|
||||
scriptCtx.WithFunction("SendMessage", sendMessage(c, contents))
|
||||
scriptCtx.WithFunction("Run", run(c, context))
|
||||
|
||||
// Check if the method exists
|
||||
if !scriptCtx.Global().Has(method) {
|
||||
return nil, fmt.Errorf(HookErrorMethodNotFound)
|
||||
}
|
||||
|
||||
// Call the method directly in the current thread
|
||||
args = append([]interface{}{context.Map()}, args...)
|
||||
if scriptCtx != nil {
|
||||
return scriptCtx.CallWith(ctx, method, args...)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Execute the assistant
|
||||
func run(c *gin.Context, context chatctx.Context) func(info *v8go.FunctionCallbackInfo) *v8go.Value {
|
||||
return func(info *v8go.FunctionCallbackInfo) *v8go.Value {
|
||||
|
||||
// Get the args
|
||||
args := info.Args()
|
||||
if len(args) < 2 {
|
||||
return bridge.JsException(info.Context(), "Run requires at least two arguments")
|
||||
}
|
||||
|
||||
// Get the assistant id
|
||||
assistantID := args[0].String()
|
||||
|
||||
// Get the assistant
|
||||
assistant, err := Get(assistantID)
|
||||
if err != nil {
|
||||
return bridge.JsException(info.Context(), err.Error())
|
||||
}
|
||||
|
||||
// input []chatMessage.Message
|
||||
input := args[1].String()
|
||||
|
||||
options := map[string]interface{}{}
|
||||
if len(args) > 2 {
|
||||
optionsRaw, err := bridge.GoValue(args[2], info.Context())
|
||||
if err != nil {
|
||||
return bridge.JsException(info.Context(), err.Error())
|
||||
}
|
||||
|
||||
// Parse the options
|
||||
if optionsRaw != nil {
|
||||
switch v := optionsRaw.(type) {
|
||||
case string:
|
||||
err := jsoniter.UnmarshalFromString(v, &options)
|
||||
if err != nil {
|
||||
return bridge.JsException(info.Context(), err.Error())
|
||||
}
|
||||
case map[string]interface{}:
|
||||
options = v
|
||||
default:
|
||||
return bridge.JsException(info.Context(), "Invalid options")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the assistant
|
||||
context.AssistantID = assistantID
|
||||
context.ChatID = fmt.Sprintf("chat_%s", uuid.New().String()) // New chat id
|
||||
context.Silent = true // Silent mode
|
||||
err = assistant.Execute(c, context, input, options) // Execute the assistant
|
||||
if err != nil {
|
||||
return bridge.JsException(info.Context(), err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func sendMessage(c *gin.Context, contents *chatMessage.Contents) func(info *v8go.FunctionCallbackInfo) *v8go.Value {
|
||||
return func(info *v8go.FunctionCallbackInfo) *v8go.Value {
|
||||
|
||||
// Get the message
|
||||
args := info.Args()
|
||||
|
|
@ -354,17 +428,5 @@ func (ast *Assistant) call(ctx context.Context, method string, c *gin.Context, c
|
|||
default:
|
||||
return bridge.JsException(info.Context(), "SendMessage requires a string or a map")
|
||||
}
|
||||
})
|
||||
|
||||
// Check if the method exists
|
||||
if !scriptCtx.Global().Has(method) {
|
||||
return nil, fmt.Errorf(HookErrorMethodNotFound)
|
||||
}
|
||||
|
||||
// Call the method directly in the current thread
|
||||
args = append([]interface{}{context.Map()}, args...)
|
||||
if scriptCtx != nil {
|
||||
return scriptCtx.CallWith(ctx, method, args...)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ type Context struct {
|
|||
Namespace string `json:"namespace,omitempty"`
|
||||
Config map[string]interface{} `json:"config,omitempty"`
|
||||
Signal interface{} `json:"signal,omitempty"`
|
||||
Silent bool `json:"silent,omitempty"` // Silent mode
|
||||
Retry bool `json:"retry,omitempty"` // Retry mode
|
||||
Upload *FileUpload `json:"upload,omitempty"`
|
||||
Version bool `json:"version,omitempty"` // Version support
|
||||
RAG bool `json:"rag,omitempty"` // RAG support
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ type Message struct {
|
|||
Data map[string]interface{} `json:"-"` // data for the message
|
||||
Pending bool `json:"-"` // pending for the message
|
||||
Hidden bool `json:"hidden,omitempty"` // hidden for the message (not show in the UI and history)
|
||||
Retry bool `json:"retry,omitempty"` // retry for the message
|
||||
Silent bool `json:"silent,omitempty"` // silent for the message (not show in the UI and history)
|
||||
}
|
||||
|
||||
// Mention represents a mention
|
||||
|
|
@ -187,10 +189,9 @@ func NewAny(content interface{}) (*Message, error) {
|
|||
// NewOpenAI create a new message from OpenAI response
|
||||
func NewOpenAI(data []byte, isThinking bool) *Message {
|
||||
|
||||
// For Debug
|
||||
// For debug environment, print the response data
|
||||
if os.Getenv("YAO_AGENT_PRINT_RESPONSE_DATA") == "true" {
|
||||
fmt.Printf("%s\n", string(data))
|
||||
log.Trace("[Response Data] %s", string(data))
|
||||
}
|
||||
|
||||
if data == nil || len(data) == 0 {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue