[add] Neo matching command
This commit is contained in:
parent
1f51bdaf8f
commit
7b8459b688
9 changed files with 121 additions and 89 deletions
|
|
@ -17,6 +17,23 @@ func SetStore(store Store) {
|
||||||
DefaultStore = store
|
DefaultStore = store
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Match the command from the content
|
||||||
|
func Match(sid string, query driver.Query, input string) (string, error) {
|
||||||
|
|
||||||
|
if DefaultStore == nil {
|
||||||
|
return "", fmt.Errorf("command store is not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the command from the store
|
||||||
|
if id, cid, has := DefaultStore.GetRequest(sid); has {
|
||||||
|
fmt.Println("Match Requst:", id)
|
||||||
|
return cid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefaultStore.Match(query, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the command to the store
|
||||||
func (cmd *Command) save() error {
|
func (cmd *Command) save() error {
|
||||||
if DefaultStore == nil {
|
if DefaultStore == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -51,13 +51,13 @@ func TestMemoryMatch(t *testing.T) {
|
||||||
defer test.Clean()
|
defer test.Clean()
|
||||||
|
|
||||||
mem := prepare(t)
|
mem := prepare(t)
|
||||||
id, err := mem.Match(Query{}, "Generate table test data")
|
id, err := mem.Match(Query{Stack: "Table.Page.pet"}, "Generate table test data")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
assert.Equal(t, "table.data", id)
|
assert.Equal(t, "table.data", id)
|
||||||
|
|
||||||
id, err = mem.Match(Query{Stack: "Form"}, "Generate table test data")
|
id, err = mem.Match(Query{Stack: "Form.Page.pet", Path: "/Form/pet"}, "Generate table test data")
|
||||||
assert.ErrorContains(t, err, "no related command found")
|
assert.ErrorContains(t, err, "no related command found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,7 +72,7 @@ func prepare(t *testing.T) *Memory {
|
||||||
Name: "Generate test data for the table",
|
Name: "Generate test data for the table",
|
||||||
Description: "Generate test data for the table",
|
Description: "Generate test data for the table",
|
||||||
Stack: "Table.*",
|
Stack: "Table.*",
|
||||||
Path: "*",
|
Path: "Table.*",
|
||||||
Args: []map[string]interface{}{
|
Args: []map[string]interface{}{
|
||||||
{
|
{
|
||||||
"name": "data",
|
"name": "data",
|
||||||
|
|
|
||||||
|
|
@ -8,39 +8,40 @@ import (
|
||||||
// MatchStack match the stack
|
// MatchStack match the stack
|
||||||
func (query Query) MatchStack(stack string) bool {
|
func (query Query) MatchStack(stack string) bool {
|
||||||
|
|
||||||
if query.Stack == "" || query.Stack == "*" || stack == "" {
|
if stack == "" || stack == "*" || query.Stack == "" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if query.Stack == stack {
|
if stack == query.Stack {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
matched, _ := regexp.MatchString(strings.ReplaceAll(query.Stack, "*", ".*"), stack)
|
matched, _ := regexp.MatchString(strings.ReplaceAll(stack, "*", ".*"), query.Stack)
|
||||||
return matched
|
return matched
|
||||||
}
|
}
|
||||||
|
|
||||||
// MatchPath match the path
|
// MatchPath match the path
|
||||||
func (query Query) MatchPath(path string) bool {
|
func (query Query) MatchPath(path string) bool {
|
||||||
if query.Path == "" || query.Path == "*" || path == "" {
|
if path == "" || path == "*" || query.Path == "" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if query.Path == path {
|
if path == query.Path {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
matched, _ := regexp.MatchString(strings.ReplaceAll(query.Path, "*", ".*"), path)
|
matched, _ := regexp.MatchString(strings.ReplaceAll(path, "*", ".*"), query.Path)
|
||||||
return matched
|
return matched
|
||||||
}
|
}
|
||||||
|
|
||||||
// MatchAny match the stack or path
|
// MatchAny match the stack or path
|
||||||
func (query Query) MatchAny(stack, path string) bool {
|
func (query Query) MatchAny(stack, path string) bool {
|
||||||
if query.Path == "" || query.Path == "-" {
|
|
||||||
|
if path == "" || path == "-" {
|
||||||
return query.MatchStack(stack)
|
return query.MatchStack(stack)
|
||||||
}
|
}
|
||||||
|
|
||||||
if query.Stack == "" || query.Stack == "-" {
|
if stack == "" || stack == "-" {
|
||||||
return query.MatchPath(path)
|
return query.MatchPath(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,84 +1,76 @@
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/yaoapp/kun/exception"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var requests = sync.Map{}
|
func output(format string, args ...interface{}) []byte {
|
||||||
|
content := fmt.Sprintf(format, args...)
|
||||||
|
return []byte(fmt.Sprintf(`{"id":"chatcmpl-7Atx502nGBuYcvoZfIaWU4FREI1mT","object":"chat.completion.chunk","created":1682832715,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"%s"},"index":0,"finish_reason":null}]}`, content))
|
||||||
|
}
|
||||||
|
|
||||||
// Run the command
|
// Run the command
|
||||||
func (req *Request) Run(cb func(data []byte) int) (interface{}, error) {
|
func (req *Request) Run(messages []map[string]interface{}, cb func(data []byte) int) (interface{}, error) {
|
||||||
|
|
||||||
|
cb(output("- Command: %s\\n", req.Command.ID))
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
|
||||||
|
cb(output("- Session: %s\\n", req.sid))
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
|
||||||
|
cb(output("- Request: %s\\n", req.id))
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
|
||||||
|
cb([]byte(`[DONE]`))
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRequest create a new request
|
// NewRequest create a new request
|
||||||
func (cmd *Command) NewRequest(ctx Context, messages []map[string]interface{}) (*Request, error) {
|
func (cmd *Command) NewRequest(ctx Context) (*Request, error) {
|
||||||
|
|
||||||
v, ok := requests.Load(ctx.Sid)
|
if DefaultStore == nil {
|
||||||
if !ok {
|
return nil, fmt.Errorf("command store is not set")
|
||||||
v = map[string]string{
|
|
||||||
"id": uuid.New().String(),
|
|
||||||
"cmd": cmd.ID,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
req, ok := v.(map[string]string)
|
if ctx.Sid == "" {
|
||||||
if !ok {
|
return nil, fmt.Errorf("context sid is request")
|
||||||
return nil, fmt.Errorf("request id is not string")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if req["id"] == "" {
|
// continue the request
|
||||||
return nil, fmt.Errorf("request id is request")
|
id, cid, has := DefaultStore.GetRequest(ctx.Sid)
|
||||||
}
|
if has {
|
||||||
|
if cid != cmd.ID {
|
||||||
if req["cmd"] != cmd.ID {
|
|
||||||
defer requests.Delete(ctx.Sid)
|
|
||||||
return nil, fmt.Errorf("request id is not match")
|
return nil, fmt.Errorf("request id is not match")
|
||||||
}
|
}
|
||||||
|
return &Request{
|
||||||
|
Command: cmd,
|
||||||
|
sid: ctx.Sid,
|
||||||
|
id: id,
|
||||||
|
ctx: ctx,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new request
|
||||||
|
id = uuid.New().String()
|
||||||
|
err := DefaultStore.SetRequest(ctx.Sid, id, cmd.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &Request{
|
return &Request{
|
||||||
Command: cmd,
|
Command: cmd,
|
||||||
messages: messages,
|
|
||||||
sid: ctx.Sid,
|
sid: ctx.Sid,
|
||||||
id: req["id"],
|
id: id,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Done the request done
|
// Done the request done
|
||||||
func (req *Request) Done() {
|
func (req *Request) Done() {
|
||||||
requests.Delete(req.sid)
|
if DefaultStore == nil {
|
||||||
}
|
DefaultStore.DelRequest(req.sid)
|
||||||
|
}
|
||||||
// prepare the command
|
|
||||||
func (req *Request) prepare(ctx context.Context, data []map[string]interface{}, messages []map[string]interface{}, option map[string]interface{}, cb func(data []byte) int) (int, *exception.Exception) {
|
|
||||||
return 1, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// before the process
|
|
||||||
func (req *Request) before(ctx context.Context, data []map[string]interface{}, messages []map[string]interface{}, option map[string]interface{}, cb func(data []byte) int) (interface{}, *exception.Exception) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// after the process
|
|
||||||
func (req *Request) after(ctx context.Context, data []map[string]interface{}, messages []map[string]interface{}, option map[string]interface{}, cb func(data []byte) int) (interface{}, *exception.Exception) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// run the process
|
|
||||||
func (req *Request) process(ctx context.Context, data []map[string]interface{}, messages []map[string]interface{}, option map[string]interface{}, cb func(data []byte) int) (interface{}, *exception.Exception) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (req *Request) saveConversation(ctx context.Context, data []map[string]interface{}, messages []map[string]interface{}, option map[string]interface{}, cb func(data []byte) int) (interface{}, *exception.Exception) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (req *Request) saveData(ctx context.Context, data []map[string]interface{}, messages []map[string]interface{}, option map[string]interface{}, cb func(data []byte) int) (interface{}, *exception.Exception) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ type Request struct {
|
||||||
id string
|
id string
|
||||||
sid string
|
sid string
|
||||||
ctx Context
|
ctx Context
|
||||||
messages []map[string]interface{}
|
|
||||||
*Command
|
*Command
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,7 +65,7 @@ type Optional struct {
|
||||||
type Context struct {
|
type Context struct {
|
||||||
Sid string `json:"-" yaml:"-"`
|
Sid string `json:"-" yaml:"-"`
|
||||||
Stack string `json:"stack,omitempty"`
|
Stack string `json:"stack,omitempty"`
|
||||||
Path string `json:"path,omitempty"`
|
Path string `json:"pathname,omitempty"`
|
||||||
context.Context `json:"-" yaml:"-"`
|
context.Context `json:"-" yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
22
neo/load.go
22
neo/load.go
|
|
@ -4,8 +4,11 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/yaoapp/gou/application"
|
"github.com/yaoapp/gou/application"
|
||||||
|
"github.com/yaoapp/kun/log"
|
||||||
"github.com/yaoapp/yao/aigc"
|
"github.com/yaoapp/yao/aigc"
|
||||||
"github.com/yaoapp/yao/config"
|
"github.com/yaoapp/yao/config"
|
||||||
|
"github.com/yaoapp/yao/neo/command"
|
||||||
|
"github.com/yaoapp/yao/neo/command/driver"
|
||||||
"github.com/yaoapp/yao/neo/conversation"
|
"github.com/yaoapp/yao/neo/conversation"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -41,18 +44,31 @@ func Load(cfg config.Config) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
Neo = &setting
|
Neo = &setting
|
||||||
|
|
||||||
|
// AI Setting
|
||||||
err = Neo.newAI()
|
err = Neo.newAI()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Conversation Setting
|
||||||
err = Neo.newConversation()
|
err = Neo.newConversation()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Command Setting
|
||||||
|
store, err := driver.NewMemory("gpt-3_5-turbo", nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
command.SetStore(store)
|
||||||
|
|
||||||
|
// Load the commands
|
||||||
|
err = command.Load(cfg)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Command Load Error: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadCommands load the commands
|
|
||||||
func (neo *DSL) LoadCommands() {}
|
|
||||||
|
|
|
||||||
18
neo/neo.go
18
neo/neo.go
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/yaoapp/kun/log"
|
"github.com/yaoapp/kun/log"
|
||||||
"github.com/yaoapp/yao/helper"
|
"github.com/yaoapp/yao/helper"
|
||||||
"github.com/yaoapp/yao/neo/command"
|
"github.com/yaoapp/yao/neo/command"
|
||||||
|
"github.com/yaoapp/yao/neo/command/driver"
|
||||||
"github.com/yaoapp/yao/neo/conversation"
|
"github.com/yaoapp/yao/neo/conversation"
|
||||||
"github.com/yaoapp/yao/openai"
|
"github.com/yaoapp/yao/openai"
|
||||||
)
|
)
|
||||||
|
|
@ -66,7 +67,7 @@ func (neo *DSL) API(router *gin.Engine, path string) error {
|
||||||
// utils.Dump(messages)
|
// utils.Dump(messages)
|
||||||
|
|
||||||
// set the context
|
// set the context
|
||||||
ctx, cancel := command.NewContextWithCancel(sid, c.GetString("context"))
|
ctx, cancel := command.NewContextWithCancel(sid, c.Query("context"))
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
err = neo.Answer(ctx, c, messages)
|
err = neo.Answer(ctx, c, messages)
|
||||||
|
|
@ -87,9 +88,13 @@ func (neo *DSL) Answer(ctx command.Context, answer Answer, messages []map[string
|
||||||
chanError := make(chan error, 1)
|
chanError := make(chan error, 1)
|
||||||
|
|
||||||
// check the command
|
// check the command
|
||||||
// cmd, isCommand := neo.Command.Match(ctx, messages)
|
var cmd *command.Command
|
||||||
isCommand := false
|
var isCommand = false
|
||||||
cmd := command.Command{}
|
input := messages[len(messages)-1]["content"].(string)
|
||||||
|
name, err := command.Match(ctx.Sid, driver.Query{Stack: ctx.Stack, Path: ctx.Path}, input)
|
||||||
|
if err == nil && name != "" {
|
||||||
|
cmd, isCommand = command.Commands[name]
|
||||||
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
@ -100,13 +105,13 @@ func (neo *DSL) Answer(ctx command.Context, answer Answer, messages []map[string
|
||||||
// execute the command
|
// execute the command
|
||||||
if isCommand {
|
if isCommand {
|
||||||
|
|
||||||
req, err := cmd.NewRequest(ctx, messages)
|
req, err := cmd.NewRequest(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
chanError <- err
|
chanError <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = req.Run(func(data []byte) int {
|
_, err = req.Run(messages, func(data []byte) int {
|
||||||
chanStream <- data
|
chanStream <- data
|
||||||
return 1
|
return 1
|
||||||
})
|
})
|
||||||
|
|
@ -114,6 +119,7 @@ func (neo *DSL) Answer(ctx command.Context, answer Answer, messages []map[string
|
||||||
if err != nil {
|
if err != nil {
|
||||||
chanError <- err
|
chanError <- err
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import (
|
||||||
httpTest "github.com/yaoapp/gou/http"
|
httpTest "github.com/yaoapp/gou/http"
|
||||||
"github.com/yaoapp/yao/config"
|
"github.com/yaoapp/yao/config"
|
||||||
"github.com/yaoapp/yao/helper"
|
"github.com/yaoapp/yao/helper"
|
||||||
|
"github.com/yaoapp/yao/neo/command"
|
||||||
"github.com/yaoapp/yao/test"
|
"github.com/yaoapp/yao/test"
|
||||||
_ "github.com/yaoapp/yao/utils"
|
_ "github.com/yaoapp/yao/utils"
|
||||||
)
|
)
|
||||||
|
|
@ -104,11 +105,18 @@ func testServer(t *testing.T, router *gin.Engine) (string, func()) {
|
||||||
|
|
||||||
func testRouter(t *testing.T) *gin.Engine {
|
func testRouter(t *testing.T) *gin.Engine {
|
||||||
|
|
||||||
|
// Load Config
|
||||||
err := Load(config.Conf)
|
err := Load(config.Conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load Commands
|
||||||
|
err = command.Load(config.Conf)
|
||||||
|
// if err != nil {
|
||||||
|
// t.Fatal(err)
|
||||||
|
// }
|
||||||
|
|
||||||
router := gin.New()
|
router := gin.New()
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
return router
|
return router
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/yaoapp/yao/aigc"
|
"github.com/yaoapp/yao/aigc"
|
||||||
"github.com/yaoapp/yao/neo/command"
|
|
||||||
"github.com/yaoapp/yao/neo/conversation"
|
"github.com/yaoapp/yao/neo/conversation"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -20,7 +19,6 @@ type DSL struct {
|
||||||
Allows []string `json:"allows,omitempty"`
|
Allows []string `json:"allows,omitempty"`
|
||||||
AI aigc.AI `json:"-" yaml:"-"`
|
AI aigc.AI `json:"-" yaml:"-"`
|
||||||
Conversation Conversation `json:"-" yaml:"-"`
|
Conversation Conversation `json:"-" yaml:"-"`
|
||||||
Command Command `json:"-" yaml:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Conversation the store interface
|
// Conversation the store interface
|
||||||
|
|
@ -29,11 +27,6 @@ type Conversation interface {
|
||||||
SaveHistory(sid string, messages []map[string]interface{}) error
|
SaveHistory(sid string, messages []map[string]interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command the command interface
|
|
||||||
type Command interface {
|
|
||||||
Match(ctx command.Context, messages []map[string]interface{}) (*command.Command, bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Answer the answer interface
|
// Answer the answer interface
|
||||||
type Answer interface {
|
type Answer interface {
|
||||||
GetString(key string) (s string)
|
GetString(key string) (s string)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue