diff --git a/neo/assistant/api.go b/neo/assistant/api.go index 46617319..b90d73a9 100644 --- a/neo/assistant/api.go +++ b/neo/assistant/api.go @@ -441,7 +441,13 @@ func (ast *Assistant) saveChatHistory(ctx chatctx.Context, messages []chatMessag // contents fmt.Println("---contents ---") - utils.Dump(contents) + if contents.Data != nil { + fmt.Println("---contents.Data ---") + for _, content := range contents.Data { + fmt.Println(content.Map()) + } + fmt.Println("---contents.Data end ---") + } fmt.Println("---contents end ---") // Add mentions @@ -465,9 +471,9 @@ func (ast *Assistant) withOptions(options map[string]interface{}) map[string]int } } - // Add functions - if ast.Functions != nil && len(ast.Functions) > 0 { - options["tools"] = ast.Functions + // Add tools + if ast.Tools != nil && len(ast.Tools) > 0 { + options["tools"] = ast.Tools if options["tool_choice"] == nil { options["tool_choice"] = "auto" } @@ -554,8 +560,8 @@ func (ast *Assistant) requestMessages(ctx context.Context, messages []chatMessag for index, message := range messages { - // Ignore the function call message - if message.Type == "function" { + // Ignore the tool call message + if message.Type == "tool_calls" { continue } diff --git a/neo/assistant/assistant.go b/neo/assistant/assistant.go index b5244035..75ac2db6 100644 --- a/neo/assistant/assistant.go +++ b/neo/assistant/assistant.go @@ -132,7 +132,7 @@ func (ast *Assistant) Map() map[string]interface{} { "description": ast.Description, "options": ast.Options, "prompts": ast.Prompts, - "functions": ast.Functions, + "tools": ast.Tools, "tags": ast.Tags, "mentionable": ast.Mentionable, "automated": ast.Automated, @@ -199,6 +199,12 @@ func (ast *Assistant) Clone() *Assistant { copy(clone.Prompts, ast.Prompts) } + // Deep copy tools + if ast.Tools != nil { + clone.Tools = make([]Tool, len(ast.Tools)) + copy(clone.Tools, ast.Tools) + } + // Deep copy flows if ast.Flows != nil { clone.Flows = make([]map[string]interface{}, len(ast.Flows)) @@ -232,6 +238,24 @@ func (ast *Assistant) Update(data map[string]interface{}) error { if v, ok := data["connector"].(string); ok { ast.Connector = v } + + if v, has := data["tools"]; has { + switch tools := v.(type) { + case []Tool: + ast.Tools = tools + default: + raw, err := jsoniter.Marshal(tools) + if err != nil { + return err + } + ast.Tools = []Tool{} + err = jsoniter.Unmarshal(raw, &ast.Tools) + if err != nil { + return err + } + } + } + if v, ok := data["type"].(string); ok { ast.Type = v } diff --git a/neo/assistant/load.go b/neo/assistant/load.go index 2811fa82..d9f9aad4 100644 --- a/neo/assistant/load.go +++ b/neo/assistant/load.go @@ -276,16 +276,15 @@ func LoadPath(path string) (*Assistant, error) { data["updated_at"] = max(updatedAt, ts) } - // load functions - functionsfile := filepath.Join(path, "functions.json") - if has, _ := app.Exists(functionsfile); has { - functions, ts, err := loadFunctions(functionsfile) + // load tools + toolsfile := filepath.Join(path, "tools.yao") + if has, _ := app.Exists(toolsfile); has { + tools, ts, err := loadTools(toolsfile) if err != nil { return nil, err } - data["functions"] = functions + data["tools"] = tools updatedAt = max(updatedAt, ts) - data["updated_at"] = updatedAt } // load flow @@ -440,22 +439,24 @@ func loadMap(data map[string]interface{}) (*Assistant, error) { assistant.Prompts = prompts } - // functions - if funcs, has := data["functions"]; has { - switch vv := funcs.(type) { - case []Function: - assistant.Functions = vv + // tools + if tools, has := data["tools"]; has { + switch vv := tools.(type) { + case []Tool: + assistant.Tools = vv + default: - raw, err := jsoniter.Marshal(vv) + raw, err := jsoniter.Marshal(tools) if err != nil { - return nil, err + return nil, fmt.Errorf("tools format error %s", err.Error()) } - var functions []Function - err = jsoniter.Unmarshal(raw, &functions) + + var tools []Tool + err = jsoniter.Unmarshal(raw, &tools) if err != nil { - return nil, err + return nil, fmt.Errorf("tools format error %s", err.Error()) } - assistant.Functions = functions + assistant.Tools = tools } } @@ -501,32 +502,6 @@ func loadMap(data map[string]interface{}) (*Assistant, error) { return assistant, nil } -func loadFunctions(file string) ([]Function, int64, error) { - - app, err := fs.Get("app") - if err != nil { - return nil, 0, err - } - - ts, err := app.ModTime(file) - if err != nil { - return nil, 0, err - } - - raw, err := app.ReadFile(file) - if err != nil { - return nil, 0, err - } - - var functions []Function - err = jsoniter.Unmarshal(raw, &functions) - if err != nil { - return nil, 0, err - } - - return functions, ts.UnixNano(), nil -} - func loadPrompts(file string, root string) (string, int64, error) { app, err := fs.Get("app") @@ -629,3 +604,33 @@ func (ast *Assistant) initialize() error { return nil } + +func loadTools(file string) ([]Tool, int64, error) { + + app, err := fs.Get("app") + if err != nil { + return nil, 0, err + } + + content, err := app.ReadFile(file) + if err != nil { + return nil, 0, err + } + + ts, err := app.ModTime(file) + if err != nil { + return nil, 0, err + } + + if len(content) == 0 { + return []Tool{}, ts.UnixNano(), nil + } + + var tools []Tool + err = jsoniter.Unmarshal(content, &tools) + if err != nil { + return nil, 0, err + } + + return tools, ts.UnixNano(), nil +} diff --git a/neo/assistant/types.go b/neo/assistant/types.go index 76cc9158..d4fa7fcc 100644 --- a/neo/assistant/types.go +++ b/neo/assistant/types.go @@ -94,8 +94,8 @@ type Prompt struct { Name string `json:"name,omitempty"` } -// Function a function -type Function struct { +// Tool represents a tool +type Tool struct { Type string `json:"type"` Function struct { Name string `json:"name"` @@ -129,7 +129,7 @@ type Assistant struct { Automated bool `json:"automated,omitempty"` // Whether this assistant is automated Options map[string]interface{} `json:"options,omitempty"` // AI Options Prompts []Prompt `json:"prompts,omitempty"` // AI Prompts - Functions []Function `json:"functions,omitempty"` // Assistant Functions + Tools []Tool `json:"tools,omitempty"` // Assistant Tools Flows []map[string]interface{} `json:"flows,omitempty"` // Assistant Flows Placeholder *Placeholder `json:"placeholder,omitempty"` // Assistant Placeholder Script *v8.Script `json:"-" yaml:"-"` // Assistant Script diff --git a/neo/message/contents.go b/neo/message/contents.go index aa5ca064..04b30229 100644 --- a/neo/message/contents.go +++ b/neo/message/contents.go @@ -1,6 +1,8 @@ package message import ( + "fmt" + jsoniter "github.com/json-iterator/go" ) @@ -21,12 +23,12 @@ type Contents struct { // Data the data of the content type Data struct { - Type string `json:"type"` // text, function, error, ... - ID string `json:"id"` // the id of the content - Function string `json:"function"` // the function name - Bytes []byte `json:"bytes"` // the content bytes - Arguments []byte `json:"arguments"` // the function arguments - Props map[string]interface{} `json:"props"` // the props + Type string `json:"type"` // text, function, error, ... + ID string `json:"id"` // the id of the content + Function string `json:"function"` // the function name + Bytes []byte `json:"bytes"` // the content bytes + Arguments []byte `json:"arguments,omitempty"` // the function arguments + Props map[string]interface{} `json:"props"` // the props } // NewContents create a new contents @@ -159,7 +161,8 @@ func (data *Data) Map() (map[string]interface{}, error) { v["props"] = data.Props } - if data.Arguments != nil { + if data.Arguments != nil && len(data.Arguments) > 0 { + fmt.Println("data.Arguments", string(data.Arguments)) var vv interface{} = nil err := jsoniter.Unmarshal(data.Arguments, &vv) if err != nil { @@ -192,7 +195,7 @@ func (data *Data) MarshalJSON() ([]byte, error) { v["props"] = data.Props } - if data.Arguments != nil { + if data.Arguments != nil && len(data.Arguments) > 0 { var vv interface{} = nil err := jsoniter.Unmarshal(data.Arguments, &vv) if err != nil { diff --git a/neo/store/xun.go b/neo/store/xun.go index 80563925..ae8ac824 100644 --- a/neo/store/xun.go +++ b/neo/store/xun.go @@ -237,7 +237,7 @@ func (conv *Xun) initAssistantTable() error { table.JSON("prompts").Null() // assistant prompts table.JSON("flows").Null() // assistant flows table.JSON("files").Null() // assistant files - table.JSON("functions").Null() // assistant functions + table.JSON("tools").Null() // assistant tools table.JSON("tags").Null() // assistant tags table.Boolean("readonly").SetDefault(false).Index() // assistant readonly table.JSON("permissions").Null() // assistant permissions @@ -259,7 +259,7 @@ func (conv *Xun) initAssistantTable() error { return err } - fields := []string{"id", "assistant_id", "type", "name", "avatar", "connector", "description", "path", "sort", "built_in", "placeholder", "options", "prompts", "flows", "files", "functions", "tags", "mentionable", "created_at", "updated_at"} + fields := []string{"id", "assistant_id", "type", "name", "avatar", "connector", "description", "path", "sort", "built_in", "placeholder", "options", "prompts", "flows", "files", "tools", "tags", "mentionable", "created_at", "updated_at"} for _, field := range fields { if !tab.HasColumn(field) { return fmt.Errorf("%s is required", field) @@ -767,7 +767,7 @@ func (conv *Xun) SaveAssistant(assistant map[string]interface{}) (interface{}, e } // Process JSON fields - jsonFields := []string{"tags", "options", "prompts", "flows", "files", "functions", "permissions", "placeholder"} + jsonFields := []string{"tags", "options", "prompts", "flows", "files", "tools", "permissions", "placeholder"} for _, field := range jsonFields { if val, ok := assistantCopy[field]; ok && val != nil { // If it's a string, try to parse it first @@ -954,7 +954,7 @@ func (conv *Xun) GetAssistants(filter AssistantFilter) (*AssistantResponse, erro // Convert rows to map slice and parse JSON fields data := make([]map[string]interface{}, len(rows)) - jsonFields := []string{"tags", "options", "prompts", "flows", "files", "functions", "permissions", "placeholder"} + jsonFields := []string{"tags", "options", "prompts", "flows", "files", "tools", "permissions", "placeholder"} for i, row := range rows { data[i] = row // Only parse JSON fields if they are selected or no select filter is provided @@ -1008,7 +1008,7 @@ func (conv *Xun) GetAssistant(assistantID string) (map[string]interface{}, error } // Parse JSON fields - jsonFields := []string{"tags", "options", "prompts", "flows", "files", "functions", "permissions", "placeholder"} + jsonFields := []string{"tags", "options", "prompts", "flows", "files", "tools", "permissions", "placeholder"} conv.parseJSONFields(data, jsonFields) return data, nil diff --git a/neo/store/xun_test.go b/neo/store/xun_test.go index 74277676..0eef1794 100644 --- a/neo/store/xun_test.go +++ b/neo/store/xun_test.go @@ -526,7 +526,7 @@ func TestXunAssistantCRUD(t *testing.T) { "prompts": []string{"prompt1", "prompt2"}, "flows": []string{"flow1", "flow2"}, "files": []string{"file1", "file2"}, - "functions": []map[string]interface{}{{"name": "func1"}, {"name": "func2"}}, + "tools": []map[string]interface{}{{"name": "tool1"}, {"name": "tool2"}}, "permissions": map[string]interface{}{"read": true, "write": true}, "placeholder": map[string]interface{}{ "title": "Test Title 2", @@ -557,7 +557,7 @@ func TestXunAssistantCRUD(t *testing.T) { "prompts": nil, "flows": nil, "files": nil, - "functions": nil, + "tools": nil, "permissions": nil, "placeholder": nil, "mentionable": true, @@ -580,7 +580,7 @@ func TestXunAssistantCRUD(t *testing.T) { assert.Nil(t, assistant3Data["prompts"]) assert.Nil(t, assistant3Data["flows"]) assert.Nil(t, assistant3Data["files"]) - assert.Nil(t, assistant3Data["functions"]) + assert.Nil(t, assistant3Data["tools"]) assert.Nil(t, assistant3Data["permissions"]) assert.Nil(t, assistant3Data["placeholder"]) assert.Equal(t, int64(1), assistant3Data["mentionable"]) diff --git a/neo/types.go b/neo/types.go index e02de507..06e3ed22 100644 --- a/neo/types.go +++ b/neo/types.go @@ -12,25 +12,32 @@ import ( // DSL AI assistant type DSL struct { - ID string `json:"-" yaml:"-"` - Name string `json:"name,omitempty" yaml:"name,omitempty"` - Use string `json:"use,omitempty" yaml:"use,omitempty"` // Which assistant to use default - Guard string `json:"guard,omitempty" yaml:"guard,omitempty"` - Connector string `json:"connector" yaml:"connector"` - StoreSetting store.Setting `json:"store" yaml:"store"` - RAGSetting rag.Setting `json:"rag" yaml:"rag"` - VisionSetting VisionSetting `json:"vision" yaml:"vision"` - Option map[string]interface{} `json:"option" yaml:"option"` - Prepare string `json:"prepare,omitempty" yaml:"prepare,omitempty"` - Create string `json:"create,omitempty" yaml:"create,omitempty"` - Write string `json:"write,omitempty" yaml:"write,omitempty"` - Prompts []assistant.Prompt `json:"prompts,omitempty" yaml:"prompts,omitempty"` - Allows []string `json:"allows,omitempty" yaml:"allows,omitempty"` - Assistant assistant.API `json:"-" yaml:"-"` // The default assistant - Store store.Store `json:"-" yaml:"-"` - RAG *rag.RAG `json:"-" yaml:"-"` - Vision *vision.Vision `json:"-" yaml:"-"` - GuardHandlers []gin.HandlerFunc `json:"-" yaml:"-"` + ID string `json:"-" yaml:"-"` + Name string `json:"name,omitempty" yaml:"name,omitempty"` + Use string `json:"use,omitempty" yaml:"use,omitempty"` // Which assistant to use default + Guard string `json:"guard,omitempty" yaml:"guard,omitempty"` + Connector string `json:"connector" yaml:"connector"` + StoreSetting store.Setting `json:"store" yaml:"store"` + RAGSetting rag.Setting `json:"rag" yaml:"rag"` + VisionSetting VisionSetting `json:"vision" yaml:"vision"` + Option map[string]interface{} `json:"option" yaml:"option"` + Prepare string `json:"prepare,omitempty" yaml:"prepare,omitempty"` + Create string `json:"create,omitempty" yaml:"create,omitempty"` + Write string `json:"write,omitempty" yaml:"write,omitempty"` + Prompts []assistant.Prompt `json:"prompts,omitempty" yaml:"prompts,omitempty"` + Allows []string `json:"allows,omitempty" yaml:"allows,omitempty"` + Assistant assistant.API `json:"-" yaml:"-"` // The default assistant + Store store.Store `json:"-" yaml:"-"` + RAG *rag.RAG `json:"-" yaml:"-"` + Vision *vision.Vision `json:"-" yaml:"-"` + GuardHandlers []gin.HandlerFunc `json:"-" yaml:"-"` + Connectors map[string]ConnectorSetting `json:"-" yaml:"-"` +} + +// ConnectorSetting the connector setting +type ConnectorSetting struct { + Vision bool `json:"vision,omitempty" yaml:"vision,omitempty"` + Tools bool `json:"tools,omitempty" yaml:"tools,omitempty"` } // VisionSetting the vision setting