Merge pull request #883 from trheyi/main
Enhance input handling and message processing in assistant methods
This commit is contained in:
commit
2c35cbe507
5 changed files with 580 additions and 210 deletions
|
|
@ -46,7 +46,7 @@ func GetByConnector(connector string, name string) (*Assistant, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute implements the execute functionality
|
// Execute implements the execute functionality
|
||||||
func (ast *Assistant) Execute(c *gin.Context, ctx chatctx.Context, input string, options map[string]interface{}, callback ...interface{}) error {
|
func (ast *Assistant) Execute(c *gin.Context, ctx chatctx.Context, input interface{}, options map[string]interface{}, callback ...interface{}) error {
|
||||||
contents := chatMessage.NewContents()
|
contents := chatMessage.NewContents()
|
||||||
messages, err := ast.withHistory(ctx, input)
|
messages, err := ast.withHistory(ctx, input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -56,7 +56,27 @@ func (ast *Assistant) Execute(c *gin.Context, ctx chatctx.Context, input string,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute implements the execute functionality
|
// Execute implements the execute functionality
|
||||||
func (ast *Assistant) execute(c *gin.Context, ctx chatctx.Context, input []chatMessage.Message, userOptions map[string]interface{}, contents *chatMessage.Contents, callback ...interface{}) error {
|
func (ast *Assistant) execute(c *gin.Context, ctx chatctx.Context, userInput interface{}, userOptions map[string]interface{}, contents *chatMessage.Contents, callback ...interface{}) error {
|
||||||
|
|
||||||
|
var input []chatMessage.Message
|
||||||
|
|
||||||
|
switch v := userInput.(type) {
|
||||||
|
case string:
|
||||||
|
input = []chatMessage.Message{{Role: "user", Text: v}}
|
||||||
|
|
||||||
|
case []interface{}:
|
||||||
|
raw, err := jsoniter.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("marshal input error: %s", err.Error())
|
||||||
|
}
|
||||||
|
err = jsoniter.Unmarshal(raw, &input)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unmarshal input error: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
case []chatMessage.Message:
|
||||||
|
input = v
|
||||||
|
}
|
||||||
|
|
||||||
if contents == nil {
|
if contents == nil {
|
||||||
contents = chatMessage.NewContents()
|
contents = chatMessage.NewContents()
|
||||||
|
|
@ -742,12 +762,25 @@ func (ast *Assistant) withPrompts(messages []chatMessage.Message) []chatMessage.
|
||||||
|
|
||||||
func (ast *Assistant) withHistory(ctx chatctx.Context, input interface{}) ([]chatMessage.Message, error) {
|
func (ast *Assistant) withHistory(ctx chatctx.Context, input interface{}) ([]chatMessage.Message, error) {
|
||||||
|
|
||||||
var userMessage *chatMessage.Message = chatMessage.New()
|
var userMessage *chatMessage.Message
|
||||||
|
var inputMessages []*chatMessage.Message
|
||||||
switch v := input.(type) {
|
switch v := input.(type) {
|
||||||
case string:
|
case string:
|
||||||
userMessage.Map(map[string]interface{}{"role": "user", "content": v})
|
userMessage = chatMessage.New().Map(map[string]interface{}{"role": "user", "content": v})
|
||||||
|
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
userMessage.Map(v)
|
userMessage = chatMessage.New().Map(v)
|
||||||
|
|
||||||
|
case []interface{}:
|
||||||
|
raw, err := jsoniter.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("marshal input error: %s", err.Error())
|
||||||
|
}
|
||||||
|
err = jsoniter.Unmarshal(raw, &inputMessages)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unmarshal input error: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
case chatMessage.Message:
|
case chatMessage.Message:
|
||||||
userMessage = &v
|
userMessage = &v
|
||||||
case *chatMessage.Message:
|
case *chatMessage.Message:
|
||||||
|
|
@ -757,7 +790,6 @@ func (ast *Assistant) withHistory(ctx chatctx.Context, input interface{}) ([]cha
|
||||||
}
|
}
|
||||||
|
|
||||||
messages := []chatMessage.Message{}
|
messages := []chatMessage.Message{}
|
||||||
|
|
||||||
if storage != nil {
|
if storage != nil {
|
||||||
history, err := storage.GetHistory(ctx.Sid, ctx.ChatID)
|
history, err := storage.GetHistory(ctx.Sid, ctx.ChatID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -778,7 +810,19 @@ func (ast *Assistant) withHistory(ctx chatctx.Context, input interface{}) ([]cha
|
||||||
messages = ast.withPrompts(messages)
|
messages = ast.withPrompts(messages)
|
||||||
|
|
||||||
// Add user message
|
// Add user message
|
||||||
messages = append(messages, *userMessage)
|
if userMessage != nil {
|
||||||
|
messages = append(messages, *userMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add input messages
|
||||||
|
if len(inputMessages) > 0 {
|
||||||
|
for _, msg := range inputMessages {
|
||||||
|
if msg == nil || msg.Role == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
messages = append(messages, *msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
return messages, nil
|
return messages, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
502
neo/assistant/call.go
Normal file
502
neo/assistant/call.go
Normal file
|
|
@ -0,0 +1,502 @@
|
||||||
|
package assistant
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/yaoapp/gou/runtime/v8/bridge"
|
||||||
|
"github.com/yaoapp/kun/log"
|
||||||
|
chatctx "github.com/yaoapp/yao/neo/context"
|
||||||
|
chatMessage "github.com/yaoapp/yao/neo/message"
|
||||||
|
"rogchap.com/v8go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// objectCall is the object for the call function
|
||||||
|
type objectCall struct{}
|
||||||
|
|
||||||
|
// OptionsCall is the options for the call function
|
||||||
|
type OptionsCall struct {
|
||||||
|
Retry OptionsCallRetry `json:"retry,omitempty"`
|
||||||
|
Options map[string]interface{} `json:"options,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionsCallRetry is the retry options for the call function
|
||||||
|
type OptionsCallRetry struct {
|
||||||
|
Times int `json:"times,omitempty"`
|
||||||
|
Delay int `json:"delay,omitempty"`
|
||||||
|
DelayMax int `json:"delay_max,omitempty"`
|
||||||
|
Prompt string `json:"prompt,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// allowedEvents is the allowed events for the call function
|
||||||
|
var allowedEvents = map[string]bool{
|
||||||
|
"done": true,
|
||||||
|
"retry": true,
|
||||||
|
"error": true,
|
||||||
|
"message": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var callProps = []string{
|
||||||
|
"assistant_id",
|
||||||
|
"input",
|
||||||
|
"options",
|
||||||
|
}
|
||||||
|
|
||||||
|
// jsNewPlan create a plan object and return it
|
||||||
|
func jsCall(info *v8go.FunctionCallbackInfo) *v8go.Value {
|
||||||
|
|
||||||
|
args := info.Args()
|
||||||
|
if len(args) < 2 {
|
||||||
|
return bridge.JsException(info.Context(), "Run requires at least two arguments")
|
||||||
|
}
|
||||||
|
|
||||||
|
options := v8go.Undefined(info.Context().Isolate())
|
||||||
|
if len(args) > 2 {
|
||||||
|
options = args[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export the object
|
||||||
|
obj := &objectCall{}
|
||||||
|
objectTmpl := obj.ExportObject(info)
|
||||||
|
this, err := objectTmpl.NewInstance(info.Context())
|
||||||
|
if err != nil {
|
||||||
|
return bridge.JsException(info.Context(), err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy global properties
|
||||||
|
global := info.This()
|
||||||
|
for _, prop := range objectProperties {
|
||||||
|
if !global.Has(prop) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
value, err := global.Get(prop)
|
||||||
|
if err != nil {
|
||||||
|
return bridge.JsException(info.Context(), fmt.Sprintf("Failed to get property %s: %s", prop, err.Error()))
|
||||||
|
}
|
||||||
|
this.Set(prop, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Set("assistant_id", args[0])
|
||||||
|
this.Set("input", args[1])
|
||||||
|
this.Set("options", options)
|
||||||
|
return this.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportObject Export as a FS Object
|
||||||
|
func (obj *objectCall) ExportObject(info *v8go.FunctionCallbackInfo) *v8go.ObjectTemplate {
|
||||||
|
tmpl := v8go.NewObjectTemplate(info.Context().Isolate())
|
||||||
|
tmpl.Set("On", v8go.NewFunctionTemplate(info.Context().Isolate(), obj.on)) // On the call
|
||||||
|
tmpl.Set("Run", v8go.NewFunctionTemplate(info.Context().Isolate(), obj.run)) // Run the call
|
||||||
|
return tmpl
|
||||||
|
}
|
||||||
|
|
||||||
|
// on bind the callback to the call object
|
||||||
|
func (obj *objectCall) on(info *v8go.FunctionCallbackInfo) *v8go.Value {
|
||||||
|
|
||||||
|
args := info.Args()
|
||||||
|
if len(args) < 2 {
|
||||||
|
return bridge.JsException(info.Context(), "On requires at least one argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !args[0].IsString() {
|
||||||
|
return bridge.JsException(info.Context(), "The first argument should be a string")
|
||||||
|
}
|
||||||
|
|
||||||
|
name := args[0].String()
|
||||||
|
if !allowedEvents[name] {
|
||||||
|
return bridge.JsException(info.Context(), fmt.Sprintf("Invalid event %s", name))
|
||||||
|
}
|
||||||
|
|
||||||
|
cb := args[1]
|
||||||
|
if !cb.IsFunction() {
|
||||||
|
return bridge.JsException(info.Context(), fmt.Sprintf("The second argument should be a function for event %s", name))
|
||||||
|
}
|
||||||
|
|
||||||
|
this := info.This()
|
||||||
|
this.Set(fmt.Sprintf("on_%s", name), cb)
|
||||||
|
return this.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// run run the call
|
||||||
|
func (obj *objectCall) run(info *v8go.FunctionCallbackInfo) *v8go.Value {
|
||||||
|
|
||||||
|
this := info.This()
|
||||||
|
args := info.Args()
|
||||||
|
|
||||||
|
global, err := getGlobal(info.Context(), this)
|
||||||
|
if err != nil {
|
||||||
|
return bridge.JsException(info.Context(), err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
goArgs := []interface{}{}
|
||||||
|
jsArgs := []v8go.Valuer{}
|
||||||
|
if len(args) > 0 {
|
||||||
|
for _, arg := range args {
|
||||||
|
v, err := bridge.GoValue(arg, info.Context())
|
||||||
|
if err != nil {
|
||||||
|
return bridge.JsException(info.Context(), err.Error())
|
||||||
|
}
|
||||||
|
goArgs = append(goArgs, v)
|
||||||
|
jsArgs = append(jsArgs, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the assistant id
|
||||||
|
jsAssistantID, err := this.Get("assistant_id")
|
||||||
|
if err != nil {
|
||||||
|
return bridge.JsException(info.Context(), err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
assistantID := jsAssistantID.String()
|
||||||
|
|
||||||
|
// Get the input
|
||||||
|
jsInput, err := this.Get("input")
|
||||||
|
if err != nil {
|
||||||
|
return bridge.JsException(info.Context(), fmt.Sprintf("Failed to get the input: %s", err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
input, err := bridge.GoValue(jsInput, info.Context())
|
||||||
|
if err != nil {
|
||||||
|
return bridge.JsException(info.Context(), fmt.Sprintf("Failed to unmarshal the input: %s", err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options
|
||||||
|
options := OptionsCall{
|
||||||
|
Retry: OptionsCallRetry{
|
||||||
|
Times: 3,
|
||||||
|
Delay: 200,
|
||||||
|
DelayMax: 5000,
|
||||||
|
Prompt: "Please fix the error. \n {{ error }}",
|
||||||
|
},
|
||||||
|
Options: map[string]interface{}{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the options
|
||||||
|
if this.Has("options") {
|
||||||
|
jsOptions, err := this.Get("options")
|
||||||
|
if err != nil {
|
||||||
|
return bridge.JsException(info.Context(), fmt.Sprintf("Failed to get the options: %s", err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bridge.Unmarshal(jsOptions, &options)
|
||||||
|
if err != nil {
|
||||||
|
return bridge.JsException(info.Context(), fmt.Sprintf("Failed to unmarshal the options: %s", err.Error()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the assistant
|
||||||
|
newAst, err := Get(assistantID)
|
||||||
|
if err != nil {
|
||||||
|
return bridge.JsException(info.Context(), fmt.Sprintf("Failed to get the assistant: %s", err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the message event ( it will be used for the message event )
|
||||||
|
eventMessage := ""
|
||||||
|
goCallProps := map[string]interface{}{}
|
||||||
|
if this.Has("on_message") {
|
||||||
|
jsEventMessage, err := this.Get("on_message")
|
||||||
|
if err != nil {
|
||||||
|
return bridge.JsException(info.Context(), fmt.Sprintf("Failed to get the message: %s", err.Error()))
|
||||||
|
}
|
||||||
|
eventMessage = jsEventMessage.String()
|
||||||
|
|
||||||
|
for _, prop := range callProps {
|
||||||
|
if this.Has(prop) {
|
||||||
|
value, err := this.Get(prop)
|
||||||
|
if err != nil {
|
||||||
|
return bridge.JsException(info.Context(), fmt.Sprintf("Failed to get the %s property: %s", prop, err.Error()))
|
||||||
|
}
|
||||||
|
goValue, err := bridge.GoValue(value, info.Context())
|
||||||
|
if err != nil {
|
||||||
|
return bridge.JsException(info.Context(), fmt.Sprintf("Failed to get the %s property: %s", prop, err.Error()))
|
||||||
|
}
|
||||||
|
goCallProps[prop] = goValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the chat context
|
||||||
|
var chatCtx chatctx.Context = global.ChatContext
|
||||||
|
chatCtx.AssistantID = assistantID
|
||||||
|
chatCtx.ChatID = fmt.Sprintf("chat_%s", uuid.New().String()) // New chat id
|
||||||
|
chatCtx.Silent = true
|
||||||
|
|
||||||
|
// Define the callback function
|
||||||
|
var cb func(msg *chatMessage.Message) = nil
|
||||||
|
var output = []chatMessage.Message{}
|
||||||
|
cb = func(msg *chatMessage.Message) {
|
||||||
|
output = append(output, *msg)
|
||||||
|
if eventMessage != "" {
|
||||||
|
err := obj.triggerAnonymous(chatCtx, global, goCallProps, eventMessage, goArgs, msg)
|
||||||
|
if err != nil {
|
||||||
|
color.Red("Failed to trigger the message event: %s", err.Error())
|
||||||
|
log.Error("Failed to trigger the message event: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the assistant
|
||||||
|
err = newAst.Execute(global.GinContext, chatCtx, input, options.Options, cb) // Execute the assistant
|
||||||
|
if err != nil {
|
||||||
|
return bridge.JsException(info.Context(), err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy props
|
||||||
|
for name, value := range goCallProps {
|
||||||
|
info.Context().Global().Set(name, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger the done event
|
||||||
|
exception := obj.trigger(info, "done", jsArgs...)
|
||||||
|
if exception != nil {
|
||||||
|
return exception
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *objectCall) triggerAnonymous(chatCtx chatctx.Context, global *GlobalVariables, goCallProps map[string]interface{}, source string, bindArgs []interface{}, fnArgs ...interface{}) error {
|
||||||
|
|
||||||
|
ctx, err := global.Assistant.Script.NewContext(global.ChatContext.Sid, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer ctx.Close()
|
||||||
|
|
||||||
|
// Update Context
|
||||||
|
global.Assistant.InitObject(ctx, global.GinContext, chatCtx, global.Contents)
|
||||||
|
|
||||||
|
// Copy props
|
||||||
|
for k, v := range goCallProps {
|
||||||
|
ctx.WithGlobal(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the args
|
||||||
|
ctx.WithGlobal("args", bindArgs)
|
||||||
|
_, err = ctx.CallAnonymousWith(context.Background(), source, fnArgs...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// trigger trigger the callback
|
||||||
|
func (obj *objectCall) trigger(info *v8go.FunctionCallbackInfo, name string, fnArgs ...v8go.Valuer) *v8go.Value {
|
||||||
|
// Try to get the callback
|
||||||
|
this := info.This()
|
||||||
|
if this.Has(fmt.Sprintf("on_%s", name)) {
|
||||||
|
event, err := this.Get(fmt.Sprintf("on_%s", name))
|
||||||
|
if err != nil {
|
||||||
|
return bridge.JsException(info.Context(), fmt.Sprintf("Failed to get the %s callback: %s ", name, err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if event.IsFunction() {
|
||||||
|
|
||||||
|
cb, err := event.AsFunction()
|
||||||
|
if err != nil {
|
||||||
|
return bridge.JsException(info.Context(), fmt.Sprintf("Failed to get the %s callback: %s", name, err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := cb.Call(this, fnArgs...)
|
||||||
|
if err != nil {
|
||||||
|
return bridge.JsException(info.Context(), fmt.Sprintf("Failed to trigger the %s callback: %s", name, err.Error()))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// jsCallBackup is the backup function for the call function
|
||||||
|
// func jsCallBackup(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
|
||||||
|
// newAst, err := Get(assistantID)
|
||||||
|
// if err != nil {
|
||||||
|
// return bridge.JsException(info.Context(), err.Error())
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Get the input
|
||||||
|
// input := args[1].String()
|
||||||
|
|
||||||
|
// // Get the global variables
|
||||||
|
// global, err := global(info)
|
||||||
|
// if err != nil {
|
||||||
|
// return bridge.JsException(info.Context(), err.Error())
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Update Context
|
||||||
|
// chatContext := global.ChatContext
|
||||||
|
// chatContext.AssistantID = assistantID
|
||||||
|
// chatContext.ChatID = fmt.Sprintf("chat_%s", uuid.New().String()) // New chat id
|
||||||
|
// chatContext.Silent = true // Silent mode
|
||||||
|
|
||||||
|
// var cb func(msg *chatMessage.Message) = nil
|
||||||
|
// if len(args) > 2 {
|
||||||
|
|
||||||
|
// // Rest args
|
||||||
|
// var jsArgs *v8go.Value
|
||||||
|
// goArgs := []interface{}{}
|
||||||
|
// if len(args) > 3 {
|
||||||
|
// jsArgs = args[3]
|
||||||
|
// if jsArgs != nil {
|
||||||
|
// if jsArgs.IsArray() {
|
||||||
|
// v, err := bridge.GoValue(jsArgs, info.Context())
|
||||||
|
// if err != nil {
|
||||||
|
// return bridge.JsException(info.Context(), err.Error())
|
||||||
|
// }
|
||||||
|
// arr, ok := v.([]interface{})
|
||||||
|
// if !ok {
|
||||||
|
// return bridge.JsException(info.Context(), "Invalid arguments")
|
||||||
|
// }
|
||||||
|
// goArgs = arr
|
||||||
|
// } else {
|
||||||
|
// v, err := bridge.GoValue(jsArgs, info.Context())
|
||||||
|
// if err != nil {
|
||||||
|
// return bridge.JsException(info.Context(), err.Error())
|
||||||
|
// }
|
||||||
|
// goArgs = []interface{}{v}
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Parse the callback
|
||||||
|
// funcType := "method"
|
||||||
|
// name := ""
|
||||||
|
// userArgs := []interface{}{}
|
||||||
|
// if args[2].IsFunction() {
|
||||||
|
// funcType = "anonymous"
|
||||||
|
// } else {
|
||||||
|
// goValue, err := bridge.GoValue(args[2], info.Context())
|
||||||
|
// if err != nil {
|
||||||
|
// return bridge.JsException(info.Context(), err.Error())
|
||||||
|
// }
|
||||||
|
// switch v := goValue.(type) {
|
||||||
|
// case string:
|
||||||
|
// name = v
|
||||||
|
// case map[string]interface{}:
|
||||||
|
// if fname, ok := v["name"].(string); ok {
|
||||||
|
// name = fname
|
||||||
|
// }
|
||||||
|
// if args, ok := v["args"].([]interface{}); ok {
|
||||||
|
// userArgs = args
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if strings.Contains(name, ".") {
|
||||||
|
// funcType = "process"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// switch funcType {
|
||||||
|
// case "anonymous":
|
||||||
|
// source := args[2].String()
|
||||||
|
// cb = func(msg *chatMessage.Message) {
|
||||||
|
// cbArgs := []interface{}{msg}
|
||||||
|
// cbArgs = append(cbArgs, goArgs...)
|
||||||
|
// ctx, err := global.Assistant.Script.NewContext(global.ChatContext.Sid, nil)
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Println("Failed to create context", err.Error())
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// defer ctx.Close()
|
||||||
|
|
||||||
|
// global.Assistant.InitObject(ctx, global.GinContext, chatContext, global.Contents)
|
||||||
|
// _, err = ctx.CallAnonymousWith(context.Background(), source, cbArgs...)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Error("Failed to call the method: %s", err.Error())
|
||||||
|
// color.Red("Failed to call the method: %s", err.Error())
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// break
|
||||||
|
|
||||||
|
// case "process":
|
||||||
|
|
||||||
|
// cb = func(msg *chatMessage.Message) {
|
||||||
|
// cbArgs := []interface{}{}
|
||||||
|
// cbArgs = append(cbArgs, msg)
|
||||||
|
// cbArgs = append(cbArgs, userArgs...)
|
||||||
|
// p, err := process.Of(name, cbArgs...)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Error("Failed to get the process: %s", err.Error())
|
||||||
|
// color.Red("Failed to get the process: %s", err.Error())
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// err = p.Execute()
|
||||||
|
// if err != nil {
|
||||||
|
// log.Error("Failed to execute the process: %s", err.Error())
|
||||||
|
// color.Red("Failed to execute the process: %s", err.Error())
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// defer p.Release()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// case "method":
|
||||||
|
|
||||||
|
// cb = func(msg *chatMessage.Message) {
|
||||||
|
// cbArgs := []interface{}{}
|
||||||
|
// cbArgs = append(cbArgs, msg)
|
||||||
|
// cbArgs = append(cbArgs, userArgs...)
|
||||||
|
// ctx, err := global.Assistant.Script.NewContext(global.ChatContext.Sid, nil)
|
||||||
|
// if err != nil {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// defer ctx.Close()
|
||||||
|
|
||||||
|
// global.Assistant.InitObject(ctx, global.GinContext, global.ChatContext, global.Contents)
|
||||||
|
// _, err = ctx.CallWith(context.Background(), name, cbArgs...)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Error("Failed to call the method: %s", err.Error())
|
||||||
|
// color.Red("Failed to call the method: %s", err.Error())
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Parse the options
|
||||||
|
// options := map[string]interface{}{}
|
||||||
|
// if len(args) > 4 {
|
||||||
|
// optionsRaw, err := bridge.GoValue(args[4], 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")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// err = newAst.Execute(global.GinContext, chatContext, input, options, cb) // Execute the assistant
|
||||||
|
// if err != nil {
|
||||||
|
// return bridge.JsException(info.Context(), err.Error())
|
||||||
|
// }
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
@ -1,24 +1,32 @@
|
||||||
package assistant
|
package assistant
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/fatih/color"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/uuid"
|
|
||||||
jsoniter "github.com/json-iterator/go"
|
|
||||||
"github.com/yaoapp/gou/process"
|
|
||||||
v8 "github.com/yaoapp/gou/runtime/v8"
|
v8 "github.com/yaoapp/gou/runtime/v8"
|
||||||
"github.com/yaoapp/gou/runtime/v8/bridge"
|
"github.com/yaoapp/gou/runtime/v8/bridge"
|
||||||
"github.com/yaoapp/kun/log"
|
|
||||||
chatctx "github.com/yaoapp/yao/neo/context"
|
chatctx "github.com/yaoapp/yao/neo/context"
|
||||||
"github.com/yaoapp/yao/neo/message"
|
"github.com/yaoapp/yao/neo/message"
|
||||||
chatMessage "github.com/yaoapp/yao/neo/message"
|
chatMessage "github.com/yaoapp/yao/neo/message"
|
||||||
"rogchap.com/v8go"
|
"rogchap.com/v8go"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// objectProperties is the properties of the assistant object
|
||||||
|
var objectProperties = []string{
|
||||||
|
"__yao_agent_global",
|
||||||
|
"assistant",
|
||||||
|
"context",
|
||||||
|
"Plan",
|
||||||
|
"Send",
|
||||||
|
"Call",
|
||||||
|
"Assets",
|
||||||
|
"Set",
|
||||||
|
"Get",
|
||||||
|
"Del",
|
||||||
|
"Clear",
|
||||||
|
}
|
||||||
|
|
||||||
// GlobalVariables is the global variables for the assistant
|
// GlobalVariables is the global variables for the assistant
|
||||||
type GlobalVariables struct {
|
type GlobalVariables struct {
|
||||||
Assistant *Assistant
|
Assistant *Assistant
|
||||||
|
|
@ -36,12 +44,15 @@ func (global *GlobalVariables) JsValue(ctx *v8go.Context) (*v8go.Value, error) {
|
||||||
func (ast *Assistant) InitObject(v8ctx *v8.Context, c *gin.Context, context chatctx.Context, contents *chatMessage.Contents) {
|
func (ast *Assistant) InitObject(v8ctx *v8.Context, c *gin.Context, context chatctx.Context, contents *chatMessage.Contents) {
|
||||||
|
|
||||||
// Add global variables to the script context
|
// Add global variables to the script context
|
||||||
v8ctx.WithGlobal("__yao_agent_global", &GlobalVariables{
|
global := &GlobalVariables{
|
||||||
Assistant: ast,
|
Assistant: ast,
|
||||||
Contents: contents,
|
Contents: contents,
|
||||||
GinContext: c,
|
GinContext: c,
|
||||||
ChatContext: context,
|
ChatContext: context,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
// Add global variables to the script context
|
||||||
|
v8ctx.WithGlobal("__yao_agent_global", global)
|
||||||
|
|
||||||
// Add assistant to the script context
|
// Add assistant to the script context
|
||||||
v8ctx.WithGlobal("assistant", ast.Map())
|
v8ctx.WithGlobal("assistant", ast.Map())
|
||||||
|
|
@ -290,204 +301,19 @@ func jsSend(info *v8go.FunctionCallbackInfo) *v8go.Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func jsCall(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
|
|
||||||
newAst, err := Get(assistantID)
|
|
||||||
if err != nil {
|
|
||||||
return bridge.JsException(info.Context(), err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the input
|
|
||||||
input := args[1].String()
|
|
||||||
|
|
||||||
// Get the global variables
|
|
||||||
global, err := global(info)
|
|
||||||
if err != nil {
|
|
||||||
return bridge.JsException(info.Context(), err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update Context
|
|
||||||
chatContext := global.ChatContext
|
|
||||||
chatContext.AssistantID = assistantID
|
|
||||||
chatContext.ChatID = fmt.Sprintf("chat_%s", uuid.New().String()) // New chat id
|
|
||||||
chatContext.Silent = true // Silent mode
|
|
||||||
|
|
||||||
var cb func(msg *chatMessage.Message) = nil
|
|
||||||
if len(args) > 2 {
|
|
||||||
|
|
||||||
// Rest args
|
|
||||||
var jsArgs *v8go.Value
|
|
||||||
goArgs := []interface{}{}
|
|
||||||
if len(args) > 3 {
|
|
||||||
jsArgs = args[3]
|
|
||||||
if jsArgs != nil {
|
|
||||||
if jsArgs.IsArray() {
|
|
||||||
v, err := bridge.GoValue(jsArgs, info.Context())
|
|
||||||
if err != nil {
|
|
||||||
return bridge.JsException(info.Context(), err.Error())
|
|
||||||
}
|
|
||||||
arr, ok := v.([]interface{})
|
|
||||||
if !ok {
|
|
||||||
return bridge.JsException(info.Context(), "Invalid arguments")
|
|
||||||
}
|
|
||||||
goArgs = arr
|
|
||||||
} else {
|
|
||||||
v, err := bridge.GoValue(jsArgs, info.Context())
|
|
||||||
if err != nil {
|
|
||||||
return bridge.JsException(info.Context(), err.Error())
|
|
||||||
}
|
|
||||||
goArgs = []interface{}{v}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the callback
|
|
||||||
funcType := "method"
|
|
||||||
name := ""
|
|
||||||
userArgs := []interface{}{}
|
|
||||||
if args[2].IsFunction() {
|
|
||||||
funcType = "anonymous"
|
|
||||||
} else {
|
|
||||||
goValue, err := bridge.GoValue(args[2], info.Context())
|
|
||||||
if err != nil {
|
|
||||||
return bridge.JsException(info.Context(), err.Error())
|
|
||||||
}
|
|
||||||
switch v := goValue.(type) {
|
|
||||||
case string:
|
|
||||||
name = v
|
|
||||||
case map[string]interface{}:
|
|
||||||
if fname, ok := v["name"].(string); ok {
|
|
||||||
name = fname
|
|
||||||
}
|
|
||||||
if args, ok := v["args"].([]interface{}); ok {
|
|
||||||
userArgs = args
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Contains(name, ".") {
|
|
||||||
funcType = "process"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch funcType {
|
|
||||||
case "anonymous":
|
|
||||||
source := args[2].String()
|
|
||||||
cb = func(msg *chatMessage.Message) {
|
|
||||||
cbArgs := []interface{}{msg}
|
|
||||||
cbArgs = append(cbArgs, goArgs...)
|
|
||||||
ctx, err := global.Assistant.Script.NewContext(global.ChatContext.Sid, nil)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Failed to create context", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer ctx.Close()
|
|
||||||
|
|
||||||
global.Assistant.InitObject(ctx, global.GinContext, chatContext, global.Contents)
|
|
||||||
_, err = ctx.CallAnonymousWith(context.Background(), source, cbArgs...)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to call the method: %s", err.Error())
|
|
||||||
color.Red("Failed to call the method: %s", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
case "process":
|
|
||||||
|
|
||||||
cb = func(msg *chatMessage.Message) {
|
|
||||||
cbArgs := []interface{}{}
|
|
||||||
cbArgs = append(cbArgs, msg)
|
|
||||||
cbArgs = append(cbArgs, userArgs...)
|
|
||||||
p, err := process.Of(name, cbArgs...)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to get the process: %s", err.Error())
|
|
||||||
color.Red("Failed to get the process: %s", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = p.Execute()
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to execute the process: %s", err.Error())
|
|
||||||
color.Red("Failed to execute the process: %s", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer p.Release()
|
|
||||||
}
|
|
||||||
|
|
||||||
case "method":
|
|
||||||
|
|
||||||
cb = func(msg *chatMessage.Message) {
|
|
||||||
cbArgs := []interface{}{}
|
|
||||||
cbArgs = append(cbArgs, msg)
|
|
||||||
cbArgs = append(cbArgs, userArgs...)
|
|
||||||
ctx, err := global.Assistant.Script.NewContext(global.ChatContext.Sid, nil)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer ctx.Close()
|
|
||||||
|
|
||||||
global.Assistant.InitObject(ctx, global.GinContext, global.ChatContext, global.Contents)
|
|
||||||
_, err = ctx.CallWith(context.Background(), name, cbArgs...)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Failed to call the method: %s", err.Error())
|
|
||||||
color.Red("Failed to call the method: %s", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the options
|
|
||||||
options := map[string]interface{}{}
|
|
||||||
if len(args) > 4 {
|
|
||||||
optionsRaw, err := bridge.GoValue(args[4], 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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = newAst.Execute(global.GinContext, chatContext, input, options, cb) // Execute the assistant
|
|
||||||
if err != nil {
|
|
||||||
return bridge.JsException(info.Context(), err.Error())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// global get the global variables
|
// global get the global variables
|
||||||
func global(info *v8go.FunctionCallbackInfo) (global *GlobalVariables, err error) {
|
func global(info *v8go.FunctionCallbackInfo) (global *GlobalVariables, err error) {
|
||||||
|
return getGlobal(info.Context(), info.This())
|
||||||
|
}
|
||||||
|
|
||||||
jsGlobal, err := info.This().Get("__yao_agent_global")
|
func getGlobal(ctx *v8go.Context, obj *v8go.Object) (global *GlobalVariables, err error) {
|
||||||
|
jsGlobal, err := obj.Get("__yao_agent_global")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to go interface
|
// Convert to go interface
|
||||||
goGlobal, err := bridge.GoValue(jsGlobal, info.Context())
|
goGlobal, err := bridge.GoValue(jsGlobal, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -112,9 +112,6 @@ func jsPlan(info *v8go.FunctionCallbackInfo) *v8go.Value {
|
||||||
|
|
||||||
obj := newPlanObject()
|
obj := newPlanObject()
|
||||||
|
|
||||||
// Export the object
|
|
||||||
objectTmpl := obj.ExportObject(info.Context().Isolate())
|
|
||||||
|
|
||||||
args := info.Args()
|
args := info.Args()
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return bridge.JsException(info.Context(), "the first parameter should be a string")
|
return bridge.JsException(info.Context(), "the first parameter should be a string")
|
||||||
|
|
@ -125,6 +122,7 @@ func jsPlan(info *v8go.FunctionCallbackInfo) *v8go.Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
id := args[0].String()
|
id := args[0].String()
|
||||||
|
objectTmpl := obj.ExportObject(info.Context().Isolate())
|
||||||
plan, err := objectTmpl.NewInstance(info.Context())
|
plan, err := objectTmpl.NewInstance(info.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return bridge.JsException(info.Context(), fmt.Sprintf("failed to create plan object %s", err.Error()))
|
return bridge.JsException(info.Context(), fmt.Sprintf("failed to create plan object %s", err.Error()))
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ type API interface {
|
||||||
ReadBase64(ctx context.Context, fileID string) (string, error)
|
ReadBase64(ctx context.Context, fileID string) (string, error)
|
||||||
|
|
||||||
GetPlaceholder() *Placeholder
|
GetPlaceholder() *Placeholder
|
||||||
Execute(c *gin.Context, ctx chatctx.Context, input string, options map[string]interface{}, callback ...interface{}) error
|
Execute(c *gin.Context, ctx chatctx.Context, input interface{}, options map[string]interface{}, callback ...interface{}) error
|
||||||
Call(c *gin.Context, payload APIPayload) (interface{}, error)
|
Call(c *gin.Context, payload APIPayload) (interface{}, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue