Merge pull request #395 from trheyi/main

[add] Neo matching command
This commit is contained in:
Max 2023-05-04 02:17:27 +08:00 committed by GitHub
commit 68d274b93d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 121 additions and 89 deletions

View file

@ -17,6 +17,23 @@ func SetStore(store 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 {
if DefaultStore == nil {
return nil

View file

@ -51,13 +51,13 @@ func TestMemoryMatch(t *testing.T) {
defer test.Clean()
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 {
t.Fatal(err)
}
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")
}
@ -72,7 +72,7 @@ func prepare(t *testing.T) *Memory {
Name: "Generate test data for the table",
Description: "Generate test data for the table",
Stack: "Table.*",
Path: "*",
Path: "Table.*",
Args: []map[string]interface{}{
{
"name": "data",

View file

@ -8,39 +8,40 @@ import (
// MatchStack match the stack
func (query Query) MatchStack(stack string) bool {
if query.Stack == "" || query.Stack == "*" || stack == "" {
if stack == "" || stack == "*" || query.Stack == "" {
return true
}
if query.Stack == stack {
if stack == query.Stack {
return true
}
matched, _ := regexp.MatchString(strings.ReplaceAll(query.Stack, "*", ".*"), stack)
matched, _ := regexp.MatchString(strings.ReplaceAll(stack, "*", ".*"), query.Stack)
return matched
}
// MatchPath match the path
func (query Query) MatchPath(path string) bool {
if query.Path == "" || query.Path == "*" || path == "" {
if path == "" || path == "*" || query.Path == "" {
return true
}
if query.Path == path {
if path == query.Path {
return true
}
matched, _ := regexp.MatchString(strings.ReplaceAll(query.Path, "*", ".*"), path)
matched, _ := regexp.MatchString(strings.ReplaceAll(path, "*", ".*"), query.Path)
return matched
}
// MatchAny match the stack or path
func (query Query) MatchAny(stack, path string) bool {
if query.Path == "" || query.Path == "-" {
if path == "" || path == "-" {
return query.MatchStack(stack)
}
if query.Stack == "" || query.Stack == "-" {
if stack == "" || stack == "-" {
return query.MatchPath(path)
}

View file

@ -1,84 +1,76 @@
package command
import (
"context"
"fmt"
"sync"
"time"
"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
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
}
// 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 !ok {
v = map[string]string{
"id": uuid.New().String(),
"cmd": cmd.ID,
if DefaultStore == nil {
return nil, fmt.Errorf("command store is not set")
}
if ctx.Sid == "" {
return nil, fmt.Errorf("context sid is request")
}
// continue the request
id, cid, has := DefaultStore.GetRequest(ctx.Sid)
if has {
if cid != cmd.ID {
return nil, fmt.Errorf("request id is not match")
}
return &Request{
Command: cmd,
sid: ctx.Sid,
id: id,
ctx: ctx,
}, nil
}
req, ok := v.(map[string]string)
if !ok {
return nil, fmt.Errorf("request id is not string")
}
if req["id"] == "" {
return nil, fmt.Errorf("request id is request")
}
if req["cmd"] != cmd.ID {
defer requests.Delete(ctx.Sid)
return nil, fmt.Errorf("request id is not match")
// create a new request
id = uuid.New().String()
err := DefaultStore.SetRequest(ctx.Sid, id, cmd.ID)
if err != nil {
return nil, err
}
return &Request{
Command: cmd,
messages: messages,
sid: ctx.Sid,
id: req["id"],
ctx: ctx,
Command: cmd,
sid: ctx.Sid,
id: id,
ctx: ctx,
}, nil
}
// Done the request done
func (req *Request) Done() {
requests.Delete(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
if DefaultStore == nil {
DefaultStore.DelRequest(req.sid)
}
}

View file

@ -9,10 +9,9 @@ import (
// Request the command request
type Request struct {
id string
sid string
ctx Context
messages []map[string]interface{}
id string
sid string
ctx Context
*Command
}
@ -66,7 +65,7 @@ type Optional struct {
type Context struct {
Sid string `json:"-" yaml:"-"`
Stack string `json:"stack,omitempty"`
Path string `json:"path,omitempty"`
Path string `json:"pathname,omitempty"`
context.Context `json:"-" yaml:"-"`
}

View file

@ -4,8 +4,11 @@ import (
"path/filepath"
"github.com/yaoapp/gou/application"
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/aigc"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/neo/command"
"github.com/yaoapp/yao/neo/command/driver"
"github.com/yaoapp/yao/neo/conversation"
)
@ -41,18 +44,31 @@ func Load(cfg config.Config) error {
}
Neo = &setting
// AI Setting
err = Neo.newAI()
if err != nil {
return err
}
// Conversation Setting
err = Neo.newConversation()
if err != nil {
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
}
// LoadCommands load the commands
func (neo *DSL) LoadCommands() {}

View file

@ -15,6 +15,7 @@ import (
"github.com/yaoapp/kun/log"
"github.com/yaoapp/yao/helper"
"github.com/yaoapp/yao/neo/command"
"github.com/yaoapp/yao/neo/command/driver"
"github.com/yaoapp/yao/neo/conversation"
"github.com/yaoapp/yao/openai"
)
@ -66,7 +67,7 @@ func (neo *DSL) API(router *gin.Engine, path string) error {
// utils.Dump(messages)
// set the context
ctx, cancel := command.NewContextWithCancel(sid, c.GetString("context"))
ctx, cancel := command.NewContextWithCancel(sid, c.Query("context"))
defer cancel()
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)
// check the command
// cmd, isCommand := neo.Command.Match(ctx, messages)
isCommand := false
cmd := command.Command{}
var cmd *command.Command
var isCommand = false
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() {
defer func() {
@ -100,13 +105,13 @@ func (neo *DSL) Answer(ctx command.Context, answer Answer, messages []map[string
// execute the command
if isCommand {
req, err := cmd.NewRequest(ctx, messages)
req, err := cmd.NewRequest(ctx)
if err != nil {
chanError <- err
return
}
_, err = req.Run(func(data []byte) int {
_, err = req.Run(messages, func(data []byte) int {
chanStream <- data
return 1
})
@ -114,6 +119,7 @@ func (neo *DSL) Answer(ctx command.Context, answer Answer, messages []map[string
if err != nil {
chanError <- err
}
return
}

View file

@ -15,6 +15,7 @@ import (
httpTest "github.com/yaoapp/gou/http"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/helper"
"github.com/yaoapp/yao/neo/command"
"github.com/yaoapp/yao/test"
_ "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 {
// Load Config
err := Load(config.Conf)
if err != nil {
t.Fatal(err)
}
// Load Commands
err = command.Load(config.Conf)
// if err != nil {
// t.Fatal(err)
// }
router := gin.New()
gin.SetMode(gin.ReleaseMode)
return router

View file

@ -4,7 +4,6 @@ import (
"io"
"github.com/yaoapp/yao/aigc"
"github.com/yaoapp/yao/neo/command"
"github.com/yaoapp/yao/neo/conversation"
)
@ -20,7 +19,6 @@ type DSL struct {
Allows []string `json:"allows,omitempty"`
AI aigc.AI `json:"-" yaml:"-"`
Conversation Conversation `json:"-" yaml:"-"`
Command Command `json:"-" yaml:"-"`
}
// Conversation the store interface
@ -29,11 +27,6 @@ type Conversation interface {
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
type Answer interface {
GetString(key string) (s string)