Merge pull request #921 from trheyi/main

Refactor: Remove deprecated retry logic from objectCall, use the retr…
This commit is contained in:
Max 2025-04-08 12:30:24 +08:00 committed by GitHub
commit 8551bdfdf6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -3,18 +3,13 @@ package assistant
import ( import (
"context" "context"
"fmt" "fmt"
"strings"
"time"
"github.com/fatih/color" "github.com/fatih/color"
"github.com/google/uuid" "github.com/google/uuid"
jsoniter "github.com/json-iterator/go"
"github.com/yaoapp/gou/runtime/v8/bridge" "github.com/yaoapp/gou/runtime/v8/bridge"
"github.com/yaoapp/kun/exception"
"github.com/yaoapp/kun/log" "github.com/yaoapp/kun/log"
chatctx "github.com/yaoapp/yao/neo/context" chatctx "github.com/yaoapp/yao/neo/context"
chatMessage "github.com/yaoapp/yao/neo/message" chatMessage "github.com/yaoapp/yao/neo/message"
sui "github.com/yaoapp/yao/sui/core"
"rogchap.com/v8go" "rogchap.com/v8go"
) )
@ -185,12 +180,12 @@ func (obj *objectCall) run(info *v8go.FunctionCallbackInfo) *v8go.Value {
// Options // Options
options := OptionsCall{ options := OptionsCall{
Retry: OptionsCallRetry{ // Retry: OptionsCallRetry{
Times: 3, // Times: 3,
Delay: 200, // Delay: 200,
DelayMax: 1000, // DelayMax: 1000,
Prompt: "{{ input }}\n**Answer is not correct, please try again.**\nError:\n{{ error }} \nAssistant's last answer:\n{{ output }}", // Prompt: "{{ input }}\n**Answer is not correct, please try again.**\nError:\n{{ error }} \nAssistant's last answer:\n{{ output }}",
}, // },
Silent: true, Silent: true,
Options: map[string]interface{}{}, // LLM API options Options: map[string]interface{}{}, // LLM API options
} }
@ -267,11 +262,12 @@ func (obj *objectCall) run(info *v8go.FunctionCallbackInfo) *v8go.Value {
// Execute the assistant // Execute the assistant
result, err := newAst.Execute(global.GinContext, chatCtx, input, options.Options, cb) // Execute the assistant result, err := newAst.Execute(global.GinContext, chatCtx, input, options.Options, cb) // Execute the assistant
if err != nil { if err != nil {
result, err = obj.retry(jsArgs, err, input, output, info, options) // result, err = obj.retry(jsArgs, err, input, output, info, options)
if err != nil { // if err != nil {
// return bridge.JsException(info.Context(), err.Error())
// }
return bridge.JsException(info.Context(), err.Error()) return bridge.JsException(info.Context(), err.Error())
} }
}
// Copy props // Copy props
for name, value := range goCallProps { for name, value := range goCallProps {
@ -281,11 +277,12 @@ func (obj *objectCall) run(info *v8go.FunctionCallbackInfo) *v8go.Value {
// Trigger the done event // Trigger the done event
doneResult, err := obj.trigger(info, "done", jsArgs...) doneResult, err := obj.trigger(info, "done", jsArgs...)
if err != nil { if err != nil {
result, err = obj.retry(jsArgs, err, input, output, info, options) // result, err = obj.retry(jsArgs, err, input, output, info, options)
if err != nil { // if err != nil {
// return bridge.JsException(info.Context(), err.Error())
// }
return bridge.JsException(info.Context(), err.Error()) return bridge.JsException(info.Context(), err.Error())
} }
}
// Return the done result // Return the done result
if doneResult != nil && !doneResult.IsUndefined() { if doneResult != nil && !doneResult.IsUndefined() {
@ -308,154 +305,156 @@ func (obj *objectCall) run(info *v8go.FunctionCallbackInfo) *v8go.Value {
return jsResult return jsResult
} }
func (obj *objectCall) retry(jsArgs []v8go.Valuer, err error, input interface{}, output []chatMessage.Message, info *v8go.FunctionCallbackInfo, options OptionsCall) (*v8go.Value, error) { // Deprecated this function is not used anymore
// Use the hook retry instead
// func (obj *objectCall) retry(jsArgs []v8go.Valuer, err error, input interface{}, output []chatMessage.Message, info *v8go.FunctionCallbackInfo, options OptionsCall) (*v8go.Value, error) {
// Retry times, if not set, return the error // // Retry times, if not set, return the error
if options.Retry.Times <= 0 { // if options.Retry.Times <= 0 {
return nil, err // return nil, err
} // }
this := info.This() // this := info.This()
errmsg := exception.Trim(err) // errmsg := exception.Trim(err)
// Get current retry times // // Get current retry times
jsTimes, retryErr := this.Get("retry_times") // jsTimes, retryErr := this.Get("retry_times")
if retryErr != nil { // if retryErr != nil {
return nil, fmt.Errorf("%s occurred but failed to get the retry times: %s", errmsg, retryErr.Error()) // return nil, fmt.Errorf("%s occurred but failed to get the retry times: %s", errmsg, retryErr.Error())
} // }
times := int(jsTimes.Int32()) // times := int(jsTimes.Int32())
if times > options.Retry.Times { // if times > options.Retry.Times {
return nil, fmt.Errorf("%s occurred, max retry times reached", errmsg) // return nil, fmt.Errorf("%s occurred, max retry times reached", errmsg)
} // }
// Update the retry times // // Update the retry times
times = times + 1 // times = times + 1
this.Set("retry_times", int32(times)) // this.Set("retry_times", int32(times))
// Message content // // Message content
content := "" // content := ""
for _, msg := range output { // for _, msg := range output {
if msg.Type == "text" && msg.IsDelta { // if msg.Type == "text" && msg.IsDelta {
content += msg.Text // content += msg.Text
} // }
} // }
// Delay // // Delay
delay := options.Retry.Delay * int(times) // delay := options.Retry.Delay * int(times)
if delay > options.Retry.DelayMax { // if delay > options.Retry.DelayMax {
delay = options.Retry.DelayMax // delay = options.Retry.DelayMax
} // }
// Wait for the delay // // Wait for the delay
if delay > 0 { // if delay > 0 {
time.Sleep(time.Duration(delay) * time.Millisecond) // time.Sleep(time.Duration(delay) * time.Millisecond)
} // }
// Format the input // // Format the input
var lastUserMessage *chatMessage.Message = nil // var lastUserMessage *chatMessage.Message = nil
var inputMessages []*chatMessage.Message = nil // var inputMessages []*chatMessage.Message = nil
var lastUserMessageIndex int = 0 // var lastUserMessageIndex int = 0
switch v := input.(type) { // switch v := input.(type) {
case string: // case string:
lastUserMessage = &chatMessage.Message{Type: "text", Text: v, Role: "user"} // lastUserMessage = &chatMessage.Message{Type: "text", Text: v, Role: "user"}
inputMessages = []*chatMessage.Message{lastUserMessage} // inputMessages = []*chatMessage.Message{lastUserMessage}
case []interface{}: // case []interface{}:
// Get the last user message // // Get the last user message
raw, parseErr := jsoniter.Marshal(v) // raw, parseErr := jsoniter.Marshal(v)
if parseErr != nil { // if parseErr != nil {
return nil, fmt.Errorf("%s occurred but failed to marshal the input: %s", errmsg, parseErr.Error()) // return nil, fmt.Errorf("%s occurred but failed to marshal the input: %s", errmsg, parseErr.Error())
} // }
parseErr = jsoniter.Unmarshal(raw, &inputMessages) // parseErr = jsoniter.Unmarshal(raw, &inputMessages)
if parseErr != nil { // if parseErr != nil {
return nil, fmt.Errorf("%s occurred but failed to unmarshal the input: %s", errmsg, parseErr.Error()) // return nil, fmt.Errorf("%s occurred but failed to unmarshal the input: %s", errmsg, parseErr.Error())
} // }
// Get the last user message // // Get the last user message
for i := len(inputMessages) - 1; i >= 0; i-- { // for i := len(inputMessages) - 1; i >= 0; i-- {
if inputMessages[i].Type == "text" && inputMessages[i].Role == "user" { // if inputMessages[i].Type == "text" && inputMessages[i].Role == "user" {
lastUserMessage = inputMessages[i] // lastUserMessage = inputMessages[i]
lastUserMessageIndex = i // lastUserMessageIndex = i
break // break
} // }
} // }
case *chatMessage.Message: // case *chatMessage.Message:
lastUserMessage = v // lastUserMessage = v
inputMessages = []*chatMessage.Message{lastUserMessage} // inputMessages = []*chatMessage.Message{lastUserMessage}
case map[string]interface{}: // case map[string]interface{}:
text, ok := v["text"].(string) // text, ok := v["text"].(string)
if !ok { // if !ok {
return nil, fmt.Errorf("%s occurred but failed to get the text", errmsg) // return nil, fmt.Errorf("%s occurred but failed to get the text", errmsg)
} // }
if v["role"] != "user" { // if v["role"] != "user" {
return nil, fmt.Errorf("%s occurred but the role is not user", errmsg) // return nil, fmt.Errorf("%s occurred but the role is not user", errmsg)
} // }
lastUserMessage = &chatMessage.Message{Type: "text", Text: text, Role: "user"} // lastUserMessage = &chatMessage.Message{Type: "text", Text: text, Role: "user"}
inputMessages = []*chatMessage.Message{lastUserMessage} // inputMessages = []*chatMessage.Message{lastUserMessage}
} // }
// Get the prompt from the options // // Get the prompt from the options
promptTmpl := options.Retry.Prompt // promptTmpl := options.Retry.Prompt
data := sui.Data{ // data := sui.Data{
"error": errmsg, // "error": errmsg,
"output": strings.TrimSpace(content), // "output": strings.TrimSpace(content),
"input": lastUserMessage.Text, // "input": lastUserMessage.Text,
} // }
prompt, _ := data.Replace(promptTmpl) // prompt, _ := data.Replace(promptTmpl)
// Custom retry prompt by hooking the retry event // // Custom retry prompt by hooking the retry event
if this.Has("on_retry") { // if this.Has("on_retry") {
info.Context().Global().Set("error", errmsg) // Set error // info.Context().Global().Set("error", errmsg) // Set error
jsDelay, _ := bridge.JsValue(info.Context(), delay) // jsDelay, _ := bridge.JsValue(info.Context(), delay)
jsPrompt, _ := bridge.JsValue(info.Context(), prompt) // jsPrompt, _ := bridge.JsValue(info.Context(), prompt)
newPrompt, retryErr := obj.trigger(info, "retry", jsTimes, jsDelay, jsPrompt) // newPrompt, retryErr := obj.trigger(info, "retry", jsTimes, jsDelay, jsPrompt)
if retryErr != nil { // if retryErr != nil {
return nil, fmt.Errorf("%s occurred but failed to trigger the retry event: %s", errmsg, retryErr.Error()) // return nil, fmt.Errorf("%s occurred but failed to trigger the retry event: %s", errmsg, retryErr.Error())
} // }
// Update the prompt // // Update the prompt
if newPrompt.IsString() { // if newPrompt.IsString() {
prompt = newPrompt.String() // prompt = newPrompt.String()
} // }
} // }
// Generate the new input with the prompt // // Generate the new input with the prompt
// Update the input // // Update the input
inputMessages[lastUserMessageIndex].Text = prompt // inputMessages[lastUserMessageIndex].Text = prompt
jsInput, inputErr := bridge.JsValue(info.Context(), inputMessages) // jsInput, inputErr := bridge.JsValue(info.Context(), inputMessages)
if inputErr != nil { // if inputErr != nil {
return nil, fmt.Errorf("%s occurred but failed to update the input: %s", errmsg, inputErr.Error()) // return nil, fmt.Errorf("%s occurred but failed to update the input: %s", errmsg, inputErr.Error())
} // }
// Update the input // // Update the input
this.Set("retry_input", jsInput) // this.Set("retry_input", jsInput)
// Call the run function // // Call the run function
run, funcErr := this.Get("Run") // run, funcErr := this.Get("Run")
if funcErr != nil { // if funcErr != nil {
return nil, fmt.Errorf("%s occurred but failed to get the run function: %s", errmsg, funcErr.Error()) // return nil, fmt.Errorf("%s occurred but failed to get the run function: %s", errmsg, funcErr.Error())
} // }
if !run.IsFunction() { // if !run.IsFunction() {
return nil, fmt.Errorf("%s occurred but the run function is not a function", errmsg) // return nil, fmt.Errorf("%s occurred but the run function is not a function", errmsg)
} // }
fn, fnErr := run.AsFunction() // fn, fnErr := run.AsFunction()
if fnErr != nil { // if fnErr != nil {
return nil, fmt.Errorf("%s occurred but failed to get the run function: %s", errmsg, fnErr.Error()) // return nil, fmt.Errorf("%s occurred but failed to get the run function: %s", errmsg, fnErr.Error())
} // }
result, resErr := fn.Call(this, jsArgs...) // result, resErr := fn.Call(this, jsArgs...)
if resErr != nil { // if resErr != nil {
return nil, fmt.Errorf("%s (%d)", exception.Trim(resErr), times-1) // return nil, fmt.Errorf("%s (%d)", exception.Trim(resErr), times-1)
} // }
return result, nil // return result, nil
} // }
func (obj *objectCall) triggerAnonymous(chatCtx chatctx.Context, global *GlobalVariables, goCallProps map[string]interface{}, source string, bindArgs []interface{}, fnArgs ...interface{}) error { func (obj *objectCall) triggerAnonymous(chatCtx chatctx.Context, global *GlobalVariables, goCallProps map[string]interface{}, source string, bindArgs []interface{}, fnArgs ...interface{}) error {