commit
569108ebce
4 changed files with 160 additions and 49 deletions
|
|
@ -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,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
return content, nil
|
// 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 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")
|
||||||
|
|
@ -257,10 +362,11 @@ func (cmd *Command) NewRequest(ctx Context) (*Request, error) {
|
||||||
return nil, fmt.Errorf("request id is not match")
|
return nil, fmt.Errorf("request id is not match")
|
||||||
}
|
}
|
||||||
return &Request{
|
return &Request{
|
||||||
Command: cmd,
|
Command: cmd,
|
||||||
sid: ctx.Sid,
|
sid: ctx.Sid,
|
||||||
id: id,
|
id: id,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
|
conversation: conversation,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -272,10 +378,11 @@ func (cmd *Command) NewRequest(ctx Context) (*Request, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Request{
|
return &Request{
|
||||||
Command: cmd,
|
Command: cmd,
|
||||||
sid: ctx.Sid,
|
sid: ctx.Sid,
|
||||||
id: id,
|
id: id,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
|
conversation: conversation,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,29 +6,33 @@ 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
|
||||||
type Request struct {
|
type Request struct {
|
||||||
id string
|
id string
|
||||||
sid string
|
sid string
|
||||||
ctx Context
|
ctx Context
|
||||||
|
conversation conversation.Conversation
|
||||||
*Command
|
*Command
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command the command struct
|
// Command the command struct
|
||||||
type Command struct {
|
type Command struct {
|
||||||
ID string `json:"-" yaml:"-"`
|
ID string `json:"-" yaml:"-"`
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
Connector string `json:"connector"`
|
Connector string `json:"connector"`
|
||||||
Process string `json:"process"`
|
Process string `json:"process"`
|
||||||
Prepare Prepare `json:"prepare"`
|
Prepare Prepare `json:"prepare"`
|
||||||
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"`
|
||||||
Stack string `json:"stack,omitempty"` // query stack
|
Actions []message.Action `json:"actions,omitempty"`
|
||||||
Path string `json:"path,omitempty"` // query path
|
Stack string `json:"stack,omitempty"` // query stack
|
||||||
AI aigc.AI `json:"-" yaml:"-"`
|
Path string `json:"path,omitempty"` // query path
|
||||||
|
AI aigc.AI `json:"-" yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Arg the argument
|
// Arg the argument
|
||||||
|
|
@ -57,9 +61,9 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context the context
|
// Context the context
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue