Refactor assistant types and tools support
- Renamed `Functions` to `Tools` across multiple files - Added `ConnectorSetting` to support vision and tools flags - Updated loading and serialization logic for tools - Modified message contents handling for better JSON parsing - Adjusted test cases to reflect tool-related changes
This commit is contained in:
parent
6ac0406ad4
commit
455e39fa54
8 changed files with 133 additions and 88 deletions
|
|
@ -441,7 +441,13 @@ func (ast *Assistant) saveChatHistory(ctx chatctx.Context, messages []chatMessag
|
||||||
|
|
||||||
// contents
|
// contents
|
||||||
fmt.Println("---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 ---")
|
fmt.Println("---contents end ---")
|
||||||
|
|
||||||
// Add mentions
|
// Add mentions
|
||||||
|
|
@ -465,9 +471,9 @@ func (ast *Assistant) withOptions(options map[string]interface{}) map[string]int
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add functions
|
// Add tools
|
||||||
if ast.Functions != nil && len(ast.Functions) > 0 {
|
if ast.Tools != nil && len(ast.Tools) > 0 {
|
||||||
options["tools"] = ast.Functions
|
options["tools"] = ast.Tools
|
||||||
if options["tool_choice"] == nil {
|
if options["tool_choice"] == nil {
|
||||||
options["tool_choice"] = "auto"
|
options["tool_choice"] = "auto"
|
||||||
}
|
}
|
||||||
|
|
@ -554,8 +560,8 @@ func (ast *Assistant) requestMessages(ctx context.Context, messages []chatMessag
|
||||||
|
|
||||||
for index, message := range messages {
|
for index, message := range messages {
|
||||||
|
|
||||||
// Ignore the function call message
|
// Ignore the tool call message
|
||||||
if message.Type == "function" {
|
if message.Type == "tool_calls" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ func (ast *Assistant) Map() map[string]interface{} {
|
||||||
"description": ast.Description,
|
"description": ast.Description,
|
||||||
"options": ast.Options,
|
"options": ast.Options,
|
||||||
"prompts": ast.Prompts,
|
"prompts": ast.Prompts,
|
||||||
"functions": ast.Functions,
|
"tools": ast.Tools,
|
||||||
"tags": ast.Tags,
|
"tags": ast.Tags,
|
||||||
"mentionable": ast.Mentionable,
|
"mentionable": ast.Mentionable,
|
||||||
"automated": ast.Automated,
|
"automated": ast.Automated,
|
||||||
|
|
@ -199,6 +199,12 @@ func (ast *Assistant) Clone() *Assistant {
|
||||||
copy(clone.Prompts, ast.Prompts)
|
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
|
// Deep copy flows
|
||||||
if ast.Flows != nil {
|
if ast.Flows != nil {
|
||||||
clone.Flows = make([]map[string]interface{}, len(ast.Flows))
|
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 {
|
if v, ok := data["connector"].(string); ok {
|
||||||
ast.Connector = v
|
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 {
|
if v, ok := data["type"].(string); ok {
|
||||||
ast.Type = v
|
ast.Type = v
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -276,16 +276,15 @@ func LoadPath(path string) (*Assistant, error) {
|
||||||
data["updated_at"] = max(updatedAt, ts)
|
data["updated_at"] = max(updatedAt, ts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// load functions
|
// load tools
|
||||||
functionsfile := filepath.Join(path, "functions.json")
|
toolsfile := filepath.Join(path, "tools.yao")
|
||||||
if has, _ := app.Exists(functionsfile); has {
|
if has, _ := app.Exists(toolsfile); has {
|
||||||
functions, ts, err := loadFunctions(functionsfile)
|
tools, ts, err := loadTools(toolsfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
data["functions"] = functions
|
data["tools"] = tools
|
||||||
updatedAt = max(updatedAt, ts)
|
updatedAt = max(updatedAt, ts)
|
||||||
data["updated_at"] = updatedAt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// load flow
|
// load flow
|
||||||
|
|
@ -440,22 +439,24 @@ func loadMap(data map[string]interface{}) (*Assistant, error) {
|
||||||
assistant.Prompts = prompts
|
assistant.Prompts = prompts
|
||||||
}
|
}
|
||||||
|
|
||||||
// functions
|
// tools
|
||||||
if funcs, has := data["functions"]; has {
|
if tools, has := data["tools"]; has {
|
||||||
switch vv := funcs.(type) {
|
switch vv := tools.(type) {
|
||||||
case []Function:
|
case []Tool:
|
||||||
assistant.Functions = vv
|
assistant.Tools = vv
|
||||||
|
|
||||||
default:
|
default:
|
||||||
raw, err := jsoniter.Marshal(vv)
|
raw, err := jsoniter.Marshal(tools)
|
||||||
if err != nil {
|
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 {
|
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
|
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) {
|
func loadPrompts(file string, root string) (string, int64, error) {
|
||||||
|
|
||||||
app, err := fs.Get("app")
|
app, err := fs.Get("app")
|
||||||
|
|
@ -629,3 +604,33 @@ func (ast *Assistant) initialize() error {
|
||||||
|
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,8 +94,8 @@ type Prompt struct {
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function a function
|
// Tool represents a tool
|
||||||
type Function struct {
|
type Tool struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Function struct {
|
Function struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
@ -129,7 +129,7 @@ type Assistant struct {
|
||||||
Automated bool `json:"automated,omitempty"` // Whether this assistant is automated
|
Automated bool `json:"automated,omitempty"` // Whether this assistant is automated
|
||||||
Options map[string]interface{} `json:"options,omitempty"` // AI Options
|
Options map[string]interface{} `json:"options,omitempty"` // AI Options
|
||||||
Prompts []Prompt `json:"prompts,omitempty"` // AI Prompts
|
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
|
Flows []map[string]interface{} `json:"flows,omitempty"` // Assistant Flows
|
||||||
Placeholder *Placeholder `json:"placeholder,omitempty"` // Assistant Placeholder
|
Placeholder *Placeholder `json:"placeholder,omitempty"` // Assistant Placeholder
|
||||||
Script *v8.Script `json:"-" yaml:"-"` // Assistant Script
|
Script *v8.Script `json:"-" yaml:"-"` // Assistant Script
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package message
|
package message
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -25,7 +27,7 @@ type Data struct {
|
||||||
ID string `json:"id"` // the id of the content
|
ID string `json:"id"` // the id of the content
|
||||||
Function string `json:"function"` // the function name
|
Function string `json:"function"` // the function name
|
||||||
Bytes []byte `json:"bytes"` // the content bytes
|
Bytes []byte `json:"bytes"` // the content bytes
|
||||||
Arguments []byte `json:"arguments"` // the function arguments
|
Arguments []byte `json:"arguments,omitempty"` // the function arguments
|
||||||
Props map[string]interface{} `json:"props"` // the props
|
Props map[string]interface{} `json:"props"` // the props
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -159,7 +161,8 @@ func (data *Data) Map() (map[string]interface{}, error) {
|
||||||
v["props"] = data.Props
|
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
|
var vv interface{} = nil
|
||||||
err := jsoniter.Unmarshal(data.Arguments, &vv)
|
err := jsoniter.Unmarshal(data.Arguments, &vv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -192,7 +195,7 @@ func (data *Data) MarshalJSON() ([]byte, error) {
|
||||||
v["props"] = data.Props
|
v["props"] = data.Props
|
||||||
}
|
}
|
||||||
|
|
||||||
if data.Arguments != nil {
|
if data.Arguments != nil && len(data.Arguments) > 0 {
|
||||||
var vv interface{} = nil
|
var vv interface{} = nil
|
||||||
err := jsoniter.Unmarshal(data.Arguments, &vv)
|
err := jsoniter.Unmarshal(data.Arguments, &vv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -237,7 +237,7 @@ func (conv *Xun) initAssistantTable() error {
|
||||||
table.JSON("prompts").Null() // assistant prompts
|
table.JSON("prompts").Null() // assistant prompts
|
||||||
table.JSON("flows").Null() // assistant flows
|
table.JSON("flows").Null() // assistant flows
|
||||||
table.JSON("files").Null() // assistant files
|
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.JSON("tags").Null() // assistant tags
|
||||||
table.Boolean("readonly").SetDefault(false).Index() // assistant readonly
|
table.Boolean("readonly").SetDefault(false).Index() // assistant readonly
|
||||||
table.JSON("permissions").Null() // assistant permissions
|
table.JSON("permissions").Null() // assistant permissions
|
||||||
|
|
@ -259,7 +259,7 @@ func (conv *Xun) initAssistantTable() error {
|
||||||
return err
|
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 {
|
for _, field := range fields {
|
||||||
if !tab.HasColumn(field) {
|
if !tab.HasColumn(field) {
|
||||||
return fmt.Errorf("%s is required", 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
|
// 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 {
|
for _, field := range jsonFields {
|
||||||
if val, ok := assistantCopy[field]; ok && val != nil {
|
if val, ok := assistantCopy[field]; ok && val != nil {
|
||||||
// If it's a string, try to parse it first
|
// 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
|
// Convert rows to map slice and parse JSON fields
|
||||||
data := make([]map[string]interface{}, len(rows))
|
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 {
|
for i, row := range rows {
|
||||||
data[i] = row
|
data[i] = row
|
||||||
// Only parse JSON fields if they are selected or no select filter is provided
|
// 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
|
// 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)
|
conv.parseJSONFields(data, jsonFields)
|
||||||
|
|
||||||
return data, nil
|
return data, nil
|
||||||
|
|
|
||||||
|
|
@ -526,7 +526,7 @@ func TestXunAssistantCRUD(t *testing.T) {
|
||||||
"prompts": []string{"prompt1", "prompt2"},
|
"prompts": []string{"prompt1", "prompt2"},
|
||||||
"flows": []string{"flow1", "flow2"},
|
"flows": []string{"flow1", "flow2"},
|
||||||
"files": []string{"file1", "file2"},
|
"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},
|
"permissions": map[string]interface{}{"read": true, "write": true},
|
||||||
"placeholder": map[string]interface{}{
|
"placeholder": map[string]interface{}{
|
||||||
"title": "Test Title 2",
|
"title": "Test Title 2",
|
||||||
|
|
@ -557,7 +557,7 @@ func TestXunAssistantCRUD(t *testing.T) {
|
||||||
"prompts": nil,
|
"prompts": nil,
|
||||||
"flows": nil,
|
"flows": nil,
|
||||||
"files": nil,
|
"files": nil,
|
||||||
"functions": nil,
|
"tools": nil,
|
||||||
"permissions": nil,
|
"permissions": nil,
|
||||||
"placeholder": nil,
|
"placeholder": nil,
|
||||||
"mentionable": true,
|
"mentionable": true,
|
||||||
|
|
@ -580,7 +580,7 @@ func TestXunAssistantCRUD(t *testing.T) {
|
||||||
assert.Nil(t, assistant3Data["prompts"])
|
assert.Nil(t, assistant3Data["prompts"])
|
||||||
assert.Nil(t, assistant3Data["flows"])
|
assert.Nil(t, assistant3Data["flows"])
|
||||||
assert.Nil(t, assistant3Data["files"])
|
assert.Nil(t, assistant3Data["files"])
|
||||||
assert.Nil(t, assistant3Data["functions"])
|
assert.Nil(t, assistant3Data["tools"])
|
||||||
assert.Nil(t, assistant3Data["permissions"])
|
assert.Nil(t, assistant3Data["permissions"])
|
||||||
assert.Nil(t, assistant3Data["placeholder"])
|
assert.Nil(t, assistant3Data["placeholder"])
|
||||||
assert.Equal(t, int64(1), assistant3Data["mentionable"])
|
assert.Equal(t, int64(1), assistant3Data["mentionable"])
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,13 @@ type DSL struct {
|
||||||
RAG *rag.RAG `json:"-" yaml:"-"`
|
RAG *rag.RAG `json:"-" yaml:"-"`
|
||||||
Vision *vision.Vision `json:"-" yaml:"-"`
|
Vision *vision.Vision `json:"-" yaml:"-"`
|
||||||
GuardHandlers []gin.HandlerFunc `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
|
// VisionSetting the vision setting
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue