Merge pull request #402 from trheyi/main

[add] Neo (100%)
This commit is contained in:
Max 2023-05-06 00:14:54 +08:00 committed by GitHub
commit 569108ebce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 160 additions and 49 deletions

View file

@ -65,7 +65,7 @@ func LoadSource(data []byte, file, id string) (*Command, error) {
}, },
Optional: Optional{ Optional: Optional{
Autopilot: false, Autopilot: false,
Confirm: false, Confirm: "",
MaxAttempts: 10, MaxAttempts: 10,
}, },
} }

View file

@ -5,44 +5,128 @@ import (
"strings" "strings"
"github.com/google/uuid" "github.com/google/uuid"
jsoniter "github.com/json-iterator/go"
v8 "github.com/yaoapp/gou/runtime/v8" v8 "github.com/yaoapp/gou/runtime/v8"
"github.com/yaoapp/kun/log" "github.com/yaoapp/kun/log"
"github.com/yaoapp/kun/maps" "github.com/yaoapp/kun/maps"
"github.com/yaoapp/kun/utils"
"github.com/yaoapp/yao/neo/conversation" "github.com/yaoapp/yao/neo/conversation"
"github.com/yaoapp/yao/neo/message" "github.com/yaoapp/yao/neo/message"
"rogchap.com/v8go" "rogchap.com/v8go"
) )
// Run the command // Run the command
func (req *Request) Run(conversation conversation.Conversation, messages []map[string]interface{}, cb func(msg *message.JSON) int) (interface{}, error) { func (req *Request) Run(messages []map[string]interface{}, cb func(msg *message.JSON) int) error {
content, err := req.prepare(conversation, messages, cb) input, err := req.prepare(messages, cb)
if err != nil { if err != nil {
req.error(err, cb) req.error(err, cb)
return nil, err return err
} }
fmt.Println(string(content)) args, err := req.parseArgs(input, cb)
if err != nil {
cb(req.msg().Done())
return nil
}
if req.Command.Optional.Confirm != "" {
req.confirm(args, cb)
return nil
}
utils.Dump(args)
// DONE // DONE
cb(req.msg().Done()) cb(req.msg().Done())
// cb(req.msg().Text(fmt.Sprintf("- Command: %s\n", req.Command.Name))) return nil
// time.Sleep(200 * time.Millisecond) }
// cb(req.msg().Text(fmt.Sprintf("- Session: %s\n", req.sid))) // confirm the command
// time.Sleep(200 * time.Millisecond) func (req *Request) confirm(args []interface{}, cb func(msg *message.JSON) int) {
// cb(req.msg().Text(fmt.Sprintf("- Request: %s\n", req.sid))) service := strings.Split(req.Command.Optional.Confirm, ".")
// time.Sleep(200 * time.Millisecond) if len(service) == 0 {
service = []string{"neo", "Exec"}
} else if len(service) == 1 {
service = []string{"neo", service[0]}
}
// cb(req.msg().Done()) payload := map[string]interface{}{
return nil, nil "method": service[1],
"args": []interface{}{
req.id,
req.Command.Process,
args,
},
}
msg := req.msg().
Action("ExecCommand", fmt.Sprintf("Service.%s", service[0]), payload, "").
Confirm().
Done()
if req.Actions != nil && len(req.Actions) > 0 {
for _, action := range req.Actions {
msg.Action(action.Name, action.Type, action.Payload, action.Next)
}
}
cb(msg)
}
// validate the command
func (req *Request) parseArgs(input interface{}, cb func(msg *message.JSON) int) ([]interface{}, error) {
args := []interface{}{}
data := map[string]interface{}{}
switch v := input.(type) {
case string:
err := jsoniter.Unmarshal([]byte(v), &data)
if err != nil {
return nil, err
}
break
case []byte:
err := jsoniter.Unmarshal(v, &data)
if err != nil {
return nil, err
}
break
case map[string]interface{}:
data = v
break
default:
err := fmt.Errorf("\nInvalid input type: %T", v)
req.error(err, cb)
return nil, err
}
// validate the args
if req.Command.Args != nil && len(req.Command.Args) > 0 {
for _, arg := range req.Command.Args {
v, ok := data[arg.Name]
if arg.Required && !ok {
err := fmt.Errorf("\nMissing required argument: %s", arg.Name)
return nil, err
}
// @todo: validate the type
args = append(args, v)
}
}
return args, nil
} }
// RunPrepare the command // RunPrepare the command
func (req *Request) prepare(conversation conversation.Conversation, messages []map[string]interface{}, cb func(msg *message.JSON) int) ([]byte, error) { func (req *Request) prepare(messages []map[string]interface{}, cb func(msg *message.JSON) int) (interface{}, error) {
// Before hook
data, err := req.prepareBefore(messages, cb) data, err := req.prepareBefore(messages, cb)
if err != nil { if err != nil {
return nil, err return nil, err
@ -67,7 +151,7 @@ func (req *Request) prepare(conversation conversation.Conversation, messages []m
return nil, err return nil, err
} }
chatMessages, err := req.messages(conversation, prompts, question) chatMessages, err := req.messages(prompts, question)
if err != nil { if err != nil {
req.error(err, cb) req.error(err, cb)
return nil, err return nil, err
@ -91,9 +175,17 @@ func (req *Request) prepare(conversation conversation.Conversation, messages []m
req.error(fmt.Errorf(ex.Message), cb) req.error(fmt.Errorf(ex.Message), cb)
return nil, fmt.Errorf("Chat error: %s", ex.Message) return nil, fmt.Errorf("Chat error: %s", ex.Message)
} }
defer req.saveHistory(conversation, content, chatMessages) defer req.saveHistory(content, chatMessages)
// After hook
args, err := req.prepareAfter(string(content), cb)
if err != nil {
log.Error("Prepare after error: %s", err.Error())
fmt.Println(err)
return content, nil return content, nil
}
return args, nil
} }
// prepareBefore hook // prepareBefore hook
@ -112,11 +204,24 @@ func (req *Request) prepareBefore(messages []map[string]interface{}, cb func(msg
return req.runScript(req.Prepare.Before, args, cb) return req.runScript(req.Prepare.Before, args, cb)
} }
// prepareAfter hook
func (req *Request) prepareAfter(content string, cb func(msg *message.JSON) int) (interface{}, error) {
if req.Prepare.After == "" {
return content, nil
}
// prepare the args
args := []interface{}{content}
return req.runScript(req.Prepare.After, args, cb)
}
// saveHistory save the history // saveHistory save the history
func (req *Request) saveHistory(conversation conversation.Conversation, content []byte, messages []map[string]interface{}) { func (req *Request) saveHistory(content []byte, messages []map[string]interface{}) {
if len(content) > 0 && req.sid != "" && len(messages) > 0 { if len(content) > 0 && req.sid != "" && len(messages) > 0 {
err := conversation.SaveRequest( err := req.conversation.SaveRequest(
req.sid, req.sid,
req.id, req.id,
req.Command.ID, req.Command.ID,
@ -151,7 +256,7 @@ func (req *Request) question(messages []map[string]interface{}) (string, error)
return question, nil return question, nil
} }
func (req *Request) messages(conversation conversation.Conversation, prompts []Prompt, question string) ([]map[string]interface{}, error) { func (req *Request) messages(prompts []Prompt, question string) ([]map[string]interface{}, error) {
messages := []map[string]interface{}{} messages := []map[string]interface{}{}
for _, prompt := range prompts { for _, prompt := range prompts {
message := map[string]interface{}{"role": prompt.Role, "content": prompt.Content} message := map[string]interface{}{"role": prompt.Role, "content": prompt.Content}
@ -161,7 +266,7 @@ func (req *Request) messages(conversation conversation.Conversation, prompts []P
messages = append(messages, message) messages = append(messages, message)
} }
history, err := conversation.GetRequest(req.sid, req.id) history, err := req.conversation.GetRequest(req.sid, req.id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -240,7 +345,7 @@ func (req *Request) msg() *message.JSON {
} }
// NewRequest create a new request // NewRequest create a new request
func (cmd *Command) NewRequest(ctx Context) (*Request, error) { func (cmd *Command) NewRequest(ctx Context, conversation conversation.Conversation) (*Request, error) {
if DefaultStore == nil { if DefaultStore == nil {
return nil, fmt.Errorf("command store is not set") return nil, fmt.Errorf("command store is not set")
@ -261,6 +366,7 @@ func (cmd *Command) NewRequest(ctx Context) (*Request, error) {
sid: ctx.Sid, sid: ctx.Sid,
id: id, id: id,
ctx: ctx, ctx: ctx,
conversation: conversation,
}, nil }, nil
} }
@ -276,6 +382,7 @@ func (cmd *Command) NewRequest(ctx Context) (*Request, error) {
sid: ctx.Sid, sid: ctx.Sid,
id: id, id: id,
ctx: ctx, ctx: ctx,
conversation: conversation,
}, nil }, nil
} }

View file

@ -6,6 +6,8 @@ import (
"github.com/yaoapp/yao/aigc" "github.com/yaoapp/yao/aigc"
"github.com/yaoapp/yao/neo/command/driver" "github.com/yaoapp/yao/neo/command/driver"
"github.com/yaoapp/yao/neo/command/query" "github.com/yaoapp/yao/neo/command/query"
"github.com/yaoapp/yao/neo/conversation"
"github.com/yaoapp/yao/neo/message"
) )
// Request the command request // Request the command request
@ -13,6 +15,7 @@ type Request struct {
id string id string
sid string sid string
ctx Context ctx Context
conversation conversation.Conversation
*Command *Command
} }
@ -26,6 +29,7 @@ type Command struct {
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
Optional Optional `json:"optional,omitempty"` Optional Optional `json:"optional,omitempty"`
Args []Arg `json:"args,omitempty"` Args []Arg `json:"args,omitempty"`
Actions []message.Action `json:"actions,omitempty"`
Stack string `json:"stack,omitempty"` // query stack Stack string `json:"stack,omitempty"` // query stack
Path string `json:"path,omitempty"` // query path Path string `json:"path,omitempty"` // query path
AI aigc.AI `json:"-" yaml:"-"` AI aigc.AI `json:"-" yaml:"-"`
@ -58,7 +62,7 @@ type Prompt struct {
// Optional optional // Optional optional
type Optional struct { type Optional struct {
Autopilot bool `json:"autopilot,omitempty"` Autopilot bool `json:"autopilot,omitempty"`
Confirm bool `json:"confirm,omitempty"` Confirm string `json:"confirm,omitempty"`
MaxAttempts int `json:"maxAttempts,omitempty"` // default 10 MaxAttempts int `json:"maxAttempts,omitempty"` // default 10
} }

View file

@ -145,13 +145,13 @@ func (neo *DSL) Answer(ctx command.Context, question string, answer Answer) erro
// execute the command // execute the command
if isCommand { if isCommand {
req, err := cmd.NewRequest(ctx) req, err := cmd.NewRequest(ctx, neo.Conversation)
if err != nil { if err != nil {
chanError <- err chanError <- err
return return
} }
_, err = req.Run(neo.Conversation, messages, func(msg *message.JSON) int { err = req.Run(messages, func(msg *message.JSON) int {
chanStream <- msg chanStream <- msg
return 1 return 1
}) })