Refactor conversation management and migrate to store-based architecture in Neo API

- Replaced the conversation handling logic with a new store-based approach, enhancing data management and retrieval capabilities.
- Updated all relevant methods to utilize the new store interface, including GetChats, GetChat, GetHistory, and SaveAssistant, ensuring consistent functionality across the API.
- Removed deprecated conversation-related files and structures, streamlining the codebase and improving maintainability.
- Enhanced the AssistantFilter and AssistantResponse types to support the new store architecture, improving filtering and pagination capabilities.
- Updated tests to cover the new store-based methods and ensure robust functionality across different storage backends.
This commit is contained in:
Max 2025-01-01 10:43:05 +08:00
parent 238347c834
commit e530ccc1cd
12 changed files with 222 additions and 214 deletions

View file

@ -15,8 +15,8 @@ import (
"github.com/yaoapp/gou/connector" "github.com/yaoapp/gou/connector"
"github.com/yaoapp/gou/process" "github.com/yaoapp/gou/process"
"github.com/yaoapp/yao/helper" "github.com/yaoapp/yao/helper"
"github.com/yaoapp/yao/neo/conversation"
"github.com/yaoapp/yao/neo/message" "github.com/yaoapp/yao/neo/message"
"github.com/yaoapp/yao/neo/store"
) )
// API registers the Neo API endpoints // API registers the Neo API endpoints
@ -227,7 +227,7 @@ func (neo *DSL) handleChatList(c *gin.Context) {
} }
// Create filter from query parameters // Create filter from query parameters
filter := conversation.ChatFilter{ filter := store.ChatFilter{
Keywords: c.Query("keywords"), Keywords: c.Query("keywords"),
Order: c.Query("order"), Order: c.Query("order"),
} }
@ -245,7 +245,7 @@ func (neo *DSL) handleChatList(c *gin.Context) {
} }
} }
response, err := neo.Conversation.GetChats(sid, filter) response, err := neo.Store.GetChats(sid, filter)
if err != nil { if err != nil {
c.JSON(500, gin.H{"message": err.Error(), "code": 500}) c.JSON(500, gin.H{"message": err.Error(), "code": 500})
c.Done() c.Done()
@ -266,7 +266,7 @@ func (neo *DSL) handleChatHistory(c *gin.Context) {
} }
cid := c.Query("chat_id") cid := c.Query("chat_id")
history, err := neo.Conversation.GetHistory(sid, cid) history, err := neo.Store.GetHistory(sid, cid)
if err != nil { if err != nil {
c.JSON(500, gin.H{"message": err.Error(), "code": 500}) c.JSON(500, gin.H{"message": err.Error(), "code": 500})
c.Done() c.Done()
@ -450,7 +450,7 @@ func (neo *DSL) handleChatDetail(c *gin.Context) {
return return
} }
chat, err := neo.Conversation.GetChat(sid, chatID) chat, err := neo.Store.GetChat(sid, chatID)
if err != nil { if err != nil {
c.JSON(500, gin.H{"message": err.Error(), "code": 500}) c.JSON(500, gin.H{"message": err.Error(), "code": 500})
c.Done() c.Done()
@ -475,14 +475,14 @@ func (neo *DSL) handleMentions(c *gin.Context) {
mentionable := true mentionable := true
// Query mentionable assistants // Query mentionable assistants
filter := conversation.AssistantFilter{ filter := store.AssistantFilter{
Keywords: keywords, Keywords: keywords,
Mentionable: &mentionable, Mentionable: &mentionable,
Page: 1, Page: 1,
PageSize: 20, PageSize: 20,
} }
response, err := neo.Conversation.GetAssistants(filter) response, err := neo.Store.GetAssistants(filter)
if err != nil { if err != nil {
c.JSON(500, gin.H{"message": err.Error(), "code": 500}) c.JSON(500, gin.H{"message": err.Error(), "code": 500})
c.Done() c.Done()
@ -552,7 +552,7 @@ func (neo *DSL) handleChatUpdate(c *gin.Context) {
return return
} }
err := neo.Conversation.UpdateChatTitle(sid, chatID, body.Title) err := neo.Store.UpdateChatTitle(sid, chatID, body.Title)
if err != nil { if err != nil {
c.JSON(500, gin.H{"message": err.Error(), "code": 500}) c.JSON(500, gin.H{"message": err.Error(), "code": 500})
c.Done() c.Done()
@ -579,7 +579,7 @@ func (neo *DSL) handleChatDelete(c *gin.Context) {
return return
} }
err := neo.Conversation.DeleteChat(sid, chatID) err := neo.Store.DeleteChat(sid, chatID)
if err != nil { if err != nil {
c.JSON(500, gin.H{"message": err.Error(), "code": 500}) c.JSON(500, gin.H{"message": err.Error(), "code": 500})
c.Done() c.Done()
@ -599,7 +599,7 @@ func (neo *DSL) handleChatsDeleteAll(c *gin.Context) {
return return
} }
err := neo.Conversation.DeleteAllChats(sid) err := neo.Store.DeleteAllChats(sid)
if err != nil { if err != nil {
c.JSON(500, gin.H{"message": err.Error(), "code": 500}) c.JSON(500, gin.H{"message": err.Error(), "code": 500})
c.Done() c.Done()
@ -840,7 +840,7 @@ func (neo *DSL) handleGenerateCustom(c *gin.Context) {
// handleAssistantList handles listing assistants // handleAssistantList handles listing assistants
func (neo *DSL) handleAssistantList(c *gin.Context) { func (neo *DSL) handleAssistantList(c *gin.Context) {
// Parse filter parameters // Parse filter parameters
filter := conversation.AssistantFilter{ filter := store.AssistantFilter{
Page: 1, Page: 1,
PageSize: 20, PageSize: 20,
} }
@ -894,7 +894,7 @@ func (neo *DSL) handleAssistantList(c *gin.Context) {
} }
} }
response, err := neo.Conversation.GetAssistants(filter) response, err := neo.Store.GetAssistants(filter)
if err != nil { if err != nil {
c.JSON(500, gin.H{"message": err.Error(), "code": 500}) c.JSON(500, gin.H{"message": err.Error(), "code": 500})
c.Done() c.Done()
@ -930,13 +930,13 @@ func (neo *DSL) handleAssistantDetail(c *gin.Context) {
return return
} }
filter := conversation.AssistantFilter{ filter := store.AssistantFilter{
AssistantID: assistantID, AssistantID: assistantID,
Page: 1, Page: 1,
PageSize: 1, PageSize: 1,
} }
response, err := neo.Conversation.GetAssistants(filter) response, err := neo.Store.GetAssistants(filter)
if err != nil { if err != nil {
c.JSON(500, gin.H{"message": err.Error(), "code": 500}) c.JSON(500, gin.H{"message": err.Error(), "code": 500})
c.Done() c.Done()
@ -962,7 +962,7 @@ func (neo *DSL) handleAssistantSave(c *gin.Context) {
return return
} }
id, err := neo.Conversation.SaveAssistant(assistant) id, err := neo.Store.SaveAssistant(assistant)
if err != nil { if err != nil {
c.JSON(500, gin.H{"message": err.Error(), "code": 500}) c.JSON(500, gin.H{"message": err.Error(), "code": 500})
c.Done() c.Done()
@ -987,7 +987,7 @@ func (neo *DSL) handleAssistantDelete(c *gin.Context) {
return return
} }
err := neo.Conversation.DeleteAssistant(assistantID) err := neo.Store.DeleteAssistant(assistantID)
if err != nil { if err != nil {
c.JSON(500, gin.H{"message": err.Error(), "code": 500}) c.JSON(500, gin.H{"message": err.Error(), "code": 500})
c.Done() c.Done()

View file

@ -0,0 +1,26 @@
package assistant
import "github.com/yaoapp/yao/neo/store"
// loadedAssistant the loaded assistant
var loadedAssistant = map[string]*Assistant{}
// LoadLocal create a new assistant from local
func LoadLocal(path string) *Assistant {
return nil
}
// LoadZip create a new assistant from zip
func LoadZip(zip string) *Assistant {
return nil
}
// LoadRemote create a new assistant from remote
func LoadRemote(url string) *Assistant {
return nil
}
// LoadStore create a new assistant from store
func LoadStore(store store.Store) *Assistant {
return nil
}

View file

@ -9,7 +9,7 @@ import (
"github.com/yaoapp/gou/application" "github.com/yaoapp/gou/application"
"github.com/yaoapp/yao/config" "github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/neo/assistant" "github.com/yaoapp/yao/neo/assistant"
"github.com/yaoapp/yao/neo/conversation" "github.com/yaoapp/yao/neo/store"
) )
// Neo the neo AI assistant // Neo the neo AI assistant
@ -23,7 +23,7 @@ func Load(cfg config.Config) error {
Prompts: []assistant.Prompt{}, Prompts: []assistant.Prompt{},
Option: map[string]interface{}{}, Option: map[string]interface{}{},
Allows: []string{}, Allows: []string{},
ConversationSetting: conversation.Setting{ StoreSetting: store.Setting{
Table: "yao_neo_conversation", Table: "yao_neo_conversation",
Connector: "default", Connector: "default",
}, },
@ -39,14 +39,14 @@ func Load(cfg config.Config) error {
return err return err
} }
if setting.ConversationSetting.MaxSize == 0 { if setting.StoreSetting.MaxSize == 0 {
setting.ConversationSetting.MaxSize = 100 setting.StoreSetting.MaxSize = 100
} }
Neo = &setting Neo = &setting
// Conversation Setting // Store Setting
err = Neo.createConversation() err = Neo.createStore()
if err != nil { if err != nil {
return err return err
} }

View file

@ -13,8 +13,8 @@ import (
"github.com/yaoapp/yao/neo/assistant" "github.com/yaoapp/yao/neo/assistant"
"github.com/yaoapp/yao/neo/assistant/local" "github.com/yaoapp/yao/neo/assistant/local"
"github.com/yaoapp/yao/neo/assistant/openai" "github.com/yaoapp/yao/neo/assistant/openai"
"github.com/yaoapp/yao/neo/conversation"
"github.com/yaoapp/yao/neo/message" "github.com/yaoapp/yao/neo/message"
"github.com/yaoapp/yao/neo/store"
"github.com/yaoapp/yao/share" "github.com/yaoapp/yao/share"
) )
@ -473,7 +473,7 @@ func (neo *DSL) createDefaultAssistant() (assistant.API, error) {
// chatMessages get the chat messages // chatMessages get the chat messages
func (neo *DSL) chatMessages(ctx Context, content ...string) ([]map[string]interface{}, error) { func (neo *DSL) chatMessages(ctx Context, content ...string) ([]map[string]interface{}, error) {
history, err := neo.Conversation.GetHistory(ctx.Sid, ctx.ChatID) history, err := neo.Store.GetHistory(ctx.Sid, ctx.ChatID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -493,7 +493,7 @@ func (neo *DSL) chatMessages(ctx Context, content ...string) ([]map[string]inter
func (neo *DSL) saveHistory(sid string, chatID string, content []byte, messages []map[string]interface{}) { func (neo *DSL) saveHistory(sid string, chatID string, content []byte, messages []map[string]interface{}) {
if len(content) > 0 && sid != "" && len(messages) > 0 { if len(content) > 0 && sid != "" && len(messages) > 0 {
err := neo.Conversation.SaveHistory( err := neo.Store.SaveHistory(
sid, sid,
[]map[string]interface{}{ []map[string]interface{}{
{"role": "user", "content": messages[len(messages)-1]["content"], "name": sid}, {"role": "user", "content": messages[len(messages)-1]["content"], "name": sid},
@ -509,39 +509,39 @@ func (neo *DSL) saveHistory(sid string, chatID string, content []byte, messages
} }
} }
// createConversation create a new conversation // createStore create a new store
func (neo *DSL) createConversation() error { func (neo *DSL) createStore() error {
var err error var err error
if neo.ConversationSetting.Connector == "default" || neo.ConversationSetting.Connector == "" { if neo.StoreSetting.Connector == "default" || neo.StoreSetting.Connector == "" {
neo.Conversation, err = conversation.NewXun(neo.ConversationSetting) neo.Store, err = store.NewXun(neo.StoreSetting)
return err return err
} }
// other connector // other connector
conn, err := connector.Select(neo.ConversationSetting.Connector) conn, err := connector.Select(neo.StoreSetting.Connector)
if err != nil { if err != nil {
return err return err
} }
if conn.Is(connector.DATABASE) { if conn.Is(connector.DATABASE) {
neo.Conversation, err = conversation.NewXun(neo.ConversationSetting) neo.Store, err = store.NewXun(neo.StoreSetting)
return err return err
} else if conn.Is(connector.REDIS) { } else if conn.Is(connector.REDIS) {
neo.Conversation = conversation.NewRedis() neo.Store = store.NewRedis()
return nil return nil
} else if conn.Is(connector.MONGO) { } else if conn.Is(connector.MONGO) {
neo.Conversation = conversation.NewMongo() neo.Store = store.NewMongo()
return nil return nil
} else if conn.Is(connector.WEAVIATE) { } else if conn.Is(connector.WEAVIATE) {
neo.Conversation = conversation.NewWeaviate() neo.Store = store.NewWeaviate()
return nil return nil
} }
return fmt.Errorf("%s conversation connector %s not support", neo.ID, neo.ConversationSetting.Connector) return fmt.Errorf("%s store connector %s not support", neo.ID, neo.StoreSetting.Connector)
} }
// sendMessage sends a message to the client // sendMessage sends a message to the client

View file

@ -7,8 +7,8 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/yaoapp/gou/process" "github.com/yaoapp/gou/process"
"github.com/yaoapp/kun/exception" "github.com/yaoapp/kun/exception"
"github.com/yaoapp/yao/neo/conversation"
"github.com/yaoapp/yao/neo/message" "github.com/yaoapp/yao/neo/message"
"github.com/yaoapp/yao/neo/store"
) )
// GetNeo returns the Neo instance // GetNeo returns the Neo instance
@ -32,7 +32,6 @@ func init() {
// ProcessWrite process the write request // ProcessWrite process the write request
func ProcessWrite(process *process.Process) interface{} { func ProcessWrite(process *process.Process) interface{} {
process.ValidateArgNums(2) process.ValidateArgNums(2)
w, ok := process.Args[0].(gin.ResponseWriter) w, ok := process.Args[0].(gin.ResponseWriter)
@ -63,11 +62,11 @@ func processAssistantCreate(process *process.Process) interface{} {
data := process.ArgsMap(0) data := process.ArgsMap(0)
neo := GetNeo() neo := GetNeo()
if neo.Conversation == nil { if neo.Store == nil {
exception.New("Neo conversation is not initialized", 500).Throw() exception.New("Neo store is not initialized", 500).Throw()
} }
id, err := neo.Conversation.SaveAssistant(data) id, err := neo.Store.SaveAssistant(data)
if err != nil { if err != nil {
exception.New("Failed to create assistant: %s", 500, err.Error()).Throw() exception.New("Failed to create assistant: %s", 500, err.Error()).Throw()
} }
@ -81,11 +80,11 @@ func processAssistantSave(process *process.Process) interface{} {
data := process.ArgsMap(0) data := process.ArgsMap(0)
neo := GetNeo() neo := GetNeo()
if neo.Conversation == nil { if neo.Store == nil {
exception.New("Neo conversation is not initialized", 500).Throw() exception.New("Neo store is not initialized", 500).Throw()
} }
id, err := neo.Conversation.SaveAssistant(data) id, err := neo.Store.SaveAssistant(data)
if err != nil { if err != nil {
exception.New("Failed to save assistant: %s", 500, err.Error()).Throw() exception.New("Failed to save assistant: %s", 500, err.Error()).Throw()
} }
@ -99,11 +98,11 @@ func processAssistantDelete(process *process.Process) interface{} {
assistantID := process.ArgsString(0) assistantID := process.ArgsString(0)
neo := GetNeo() neo := GetNeo()
if neo.Conversation == nil { if neo.Store == nil {
exception.New("Neo conversation is not initialized", 500).Throw() exception.New("Neo store is not initialized", 500).Throw()
} }
err := neo.Conversation.DeleteAssistant(assistantID) err := neo.Store.DeleteAssistant(assistantID)
if err != nil { if err != nil {
exception.New("Failed to delete assistant: %s", 500, err.Error()).Throw() exception.New("Failed to delete assistant: %s", 500, err.Error()).Throw()
} }
@ -114,7 +113,7 @@ func processAssistantDelete(process *process.Process) interface{} {
// processAssistantSearch process the assistant search request // processAssistantSearch process the assistant search request
func processAssistantSearch(process *process.Process) interface{} { func processAssistantSearch(process *process.Process) interface{} {
params := process.ArgsMap(0) params := process.ArgsMap(0)
filter := conversation.AssistantFilter{} filter := store.AssistantFilter{}
// Parse page and pagesize // Parse page and pagesize
if page, ok := params["page"]; ok { if page, ok := params["page"]; ok {
@ -165,11 +164,11 @@ func processAssistantSearch(process *process.Process) interface{} {
// Get assistants // Get assistants
neo := GetNeo() neo := GetNeo()
if neo.Conversation == nil { if neo.Store == nil {
exception.New("Neo conversation is not initialized", 500).Throw() exception.New("Neo store is not initialized", 500).Throw()
} }
res, err := neo.Conversation.GetAssistants(filter) res, err := neo.Store.GetAssistants(filter)
if err != nil { if err != nil {
exception.New("get assistants error: %s", 500, err).Throw() exception.New("get assistants error: %s", 500, err).Throw()
} }
@ -183,17 +182,17 @@ func processAssistantFind(process *process.Process) interface{} {
assistantID := process.ArgsString(0) assistantID := process.ArgsString(0)
neo := GetNeo() neo := GetNeo()
if neo.Conversation == nil { if neo.Store == nil {
exception.New("Neo conversation is not initialized", 500).Throw() exception.New("Neo store is not initialized", 500).Throw()
} }
filter := conversation.AssistantFilter{ filter := store.AssistantFilter{
AssistantID: assistantID, AssistantID: assistantID,
Page: 1, Page: 1,
PageSize: 1, PageSize: 1,
} }
res, err := neo.Conversation.GetAssistants(filter) res, err := neo.Store.GetAssistants(filter)
if err != nil { if err != nil {
exception.New("Failed to find assistant: %s", 500, err.Error()).Throw() exception.New("Failed to find assistant: %s", 500, err.Error()).Throw()
} }

View file

@ -1,10 +1,10 @@
package conversation package store
// Mongo represents a MongoDB-based conversation storage // Mongo represents a MongoDB-based conversation storage
type Mongo struct{} type Mongo struct{}
// NewMongo creates a new MongoDB conversation storage // NewMongo create a new mongo store
func NewMongo() *Mongo { func NewMongo() Store {
return &Mongo{} return &Mongo{}
} }

View file

@ -1,10 +1,10 @@
package conversation package store
// Redis represents a Redis-based conversation storage // Redis represents a Redis-based conversation storage
type Redis struct{} type Redis struct{}
// NewRedis creates a new Redis conversation storage // NewRedis create a new redis store
func NewRedis() *Redis { func NewRedis() Store {
return &Redis{} return &Redis{}
} }

View file

@ -1,4 +1,4 @@
package conversation package store
// Setting represents the conversation configuration structure // Setting represents the conversation configuration structure
// Used to configure basic conversation parameters including connector, user field, table name, etc. // Used to configure basic conversation parameters including connector, user field, table name, etc.
@ -69,9 +69,9 @@ type AssistantResponse struct {
Total int64 `json:"total"` // Total number of items Total int64 `json:"total"` // Total number of items
} }
// Conversation defines the conversation storage interface // Store defines the conversation storage interface
// Provides basic operations required for conversation management // Provides basic operations required for conversation management
type Conversation interface { type Store interface {
// GetChats retrieves a list of chats // GetChats retrieves a list of chats
// sid: Session ID // sid: Session ID
// filter: Filter conditions // filter: Filter conditions

View file

@ -1,10 +1,10 @@
package conversation package store
// Weaviate represents a Weaviate-based conversation storage // Weaviate represents a Weaviate-based conversation storage
type Weaviate struct{} type Weaviate struct{}
// NewWeaviate creates a new Weaviate conversation storage // NewWeaviate create a new weaviate store
func NewWeaviate() *Weaviate { func NewWeaviate() Store {
return &Weaviate{} return &Weaviate{}
} }

View file

@ -1,4 +1,4 @@
package conversation package store
import ( import (
"fmt" "fmt"
@ -45,8 +45,8 @@ type Xun struct {
// DeleteAssistant deletes an assistant by assistant_id // DeleteAssistant deletes an assistant by assistant_id
// GetAssistants retrieves a paginated list of assistants with filtering // GetAssistants retrieves a paginated list of assistants with filtering
// NewXun create a new conversation // NewXun create a new xun store
func NewXun(setting Setting) (*Xun, error) { func NewXun(setting Setting) (Store, error) {
conv := &Xun{setting: setting} conv := &Xun{setting: setting}
if setting.Connector == "default" { if setting.Connector == "default" {
conv.query = capsule.Global.Query() conv.query = capsule.Global.Query()

View file

@ -1,4 +1,4 @@
package conversation package store
import ( import (
"fmt" "fmt"
@ -38,7 +38,7 @@ func TestNewXunDefault(t *testing.T) {
// Add a small delay to ensure table is created // Add a small delay to ensure table is created
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
conv, err := NewXun(Setting{ store, err := NewXun(Setting{
Connector: "default", Connector: "default",
Table: "__unit_test_conversation", Table: "__unit_test_conversation",
}) })
@ -69,38 +69,47 @@ func TestNewXunDefault(t *testing.T) {
} }
assert.Equal(t, true, has) assert.Equal(t, true, has)
// validate the history table // Validate table structure by attempting operations
tab, err := conv.schema.GetTable(conv.getHistoryTable()) // Test history operations
if err != nil { messages := []map[string]interface{}{
t.Fatal(err) {"role": "user", "content": "test message"},
}
err = store.SaveHistory("test_user", messages, "test_chat", nil)
assert.Nil(t, err)
history, err := store.GetHistory("test_user", "test_chat")
assert.Nil(t, err)
assert.NotEmpty(t, history)
// Test chat operations
err = store.UpdateChatTitle("test_user", "test_chat", "Test Chat")
assert.Nil(t, err)
chat, err := store.GetChat("test_user", "test_chat")
assert.Nil(t, err)
assert.NotNil(t, chat)
// Test assistant operations
assistant := map[string]interface{}{
"name": "Test Assistant",
"type": "assistant",
"connector": "test",
"description": "Test Description",
"tags": []string{"test"},
"mentionable": true,
"automated": true,
} }
fields := []string{"id", "sid", "cid", "uid", "role", "name", "content", "context", "created_at", "updated_at", "expired_at"} id, err := store.SaveAssistant(assistant)
for _, field := range fields { assert.Nil(t, err)
assert.Equal(t, true, tab.HasColumn(field)) assert.NotNil(t, id)
}
// validate the chat table // Clean up test data
tab, err = conv.schema.GetTable(conv.getChatTable()) err = store.DeleteChat("test_user", "test_chat")
if err != nil { assert.Nil(t, err)
t.Fatal(err)
}
chatFields := []string{"id", "chat_id", "title", "sid", "created_at", "updated_at"} err = store.DeleteAssistant(id.(string))
for _, field := range chatFields { assert.Nil(t, err)
assert.Equal(t, true, tab.HasColumn(field))
}
// validate the assistant table
tab, err = conv.schema.GetTable(conv.getAssistantTable())
if err != nil {
t.Fatal(err)
}
assistantFields := []string{"id", "assistant_id", "type", "name", "avatar", "connector", "description", "options", "prompts", "flows", "files", "functions", "tags", "readonly", "permissions", "automated", "mentionable", "created_at", "updated_at"}
for _, field := range assistantFields {
assert.Equal(t, true, tab.HasColumn(field))
}
} }
func TestNewXunConnector(t *testing.T) { func TestNewXunConnector(t *testing.T) {
@ -128,7 +137,7 @@ func TestNewXunConnector(t *testing.T) {
// Add a small delay to ensure table is created // Add a small delay to ensure table is created
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
conv, err := NewXun(Setting{ store, err := NewXun(Setting{
Connector: "mysql", Connector: "mysql",
Table: "__unit_test_conversation", Table: "__unit_test_conversation",
}) })
@ -159,38 +168,19 @@ func TestNewXunConnector(t *testing.T) {
} }
assert.Equal(t, true, has) assert.Equal(t, true, has)
// validate the history table // Test basic operations
tab, err := conv.schema.GetTable(conv.getHistoryTable()) messages := []map[string]interface{}{
if err != nil { {"role": "user", "content": "test message"},
t.Fatal(err)
} }
err = store.SaveHistory("test_user", messages, "test_chat", nil)
assert.Nil(t, err)
fields := []string{"id", "sid", "cid", "uid", "role", "name", "content", "context", "created_at", "updated_at", "expired_at"} history, err := store.GetHistory("test_user", "test_chat")
for _, field := range fields { assert.Nil(t, err)
assert.Equal(t, true, tab.HasColumn(field)) assert.NotEmpty(t, history)
}
// validate the chat table err = store.DeleteChat("test_user", "test_chat")
tab, err = conv.schema.GetTable(conv.getChatTable()) assert.Nil(t, err)
if err != nil {
t.Fatal(err)
}
chatFields := []string{"id", "chat_id", "title", "sid", "created_at", "updated_at"}
for _, field := range chatFields {
assert.Equal(t, true, tab.HasColumn(field))
}
// validate the assistant table
tab, err = conv.schema.GetTable(conv.getAssistantTable())
if err != nil {
t.Fatal(err)
}
assistantFields := []string{"id", "assistant_id", "type", "name", "avatar", "connector", "description", "options", "prompts", "flows", "files", "functions", "tags", "readonly", "permissions", "automated", "mentionable", "created_at", "updated_at"}
for _, field := range assistantFields {
assert.Equal(t, true, tab.HasColumn(field))
}
} }
func TestXunSaveAndGetHistory(t *testing.T) { func TestXunSaveAndGetHistory(t *testing.T) {
@ -209,7 +199,7 @@ func TestXunSaveAndGetHistory(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
conv, err := NewXun(Setting{ store, err := NewXun(Setting{
Connector: "default", Connector: "default",
Table: "__unit_test_conversation", Table: "__unit_test_conversation",
TTL: 3600, TTL: 3600,
@ -217,14 +207,14 @@ func TestXunSaveAndGetHistory(t *testing.T) {
// save the history // save the history
cid := "123456" cid := "123456"
err = conv.SaveHistory("123456", []map[string]interface{}{ err = store.SaveHistory("123456", []map[string]interface{}{
{"role": "user", "name": "user1", "content": "hello"}, {"role": "user", "name": "user1", "content": "hello"},
{"role": "assistant", "name": "user1", "content": "Hello there, how"}, {"role": "assistant", "name": "user1", "content": "Hello there, how"},
}, cid, nil) }, cid, nil)
assert.Nil(t, err) assert.Nil(t, err)
// get the history // get the history
data, err := conv.GetHistory("123456", cid) data, err := store.GetHistory("123456", cid)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -247,7 +237,7 @@ func TestXunSaveAndGetHistoryWithCID(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
conv, err := NewXun(Setting{ store, err := NewXun(Setting{
Connector: "default", Connector: "default",
Table: "__unit_test_conversation", Table: "__unit_test_conversation",
TTL: 3600, TTL: 3600,
@ -260,11 +250,11 @@ func TestXunSaveAndGetHistoryWithCID(t *testing.T) {
{"role": "user", "name": "user1", "content": "hello"}, {"role": "user", "name": "user1", "content": "hello"},
{"role": "assistant", "name": "assistant1", "content": "Hi! How can I help you?"}, {"role": "assistant", "name": "assistant1", "content": "Hi! How can I help you?"},
} }
err = conv.SaveHistory(sid, messages, cid, nil) err = store.SaveHistory(sid, messages, cid, nil)
assert.Nil(t, err) assert.Nil(t, err)
// get the history for specific cid // get the history for specific cid
data, err := conv.GetHistory(sid, cid) data, err := store.GetHistory(sid, cid)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -275,25 +265,25 @@ func TestXunSaveAndGetHistoryWithCID(t *testing.T) {
moreMessages := []map[string]interface{}{ moreMessages := []map[string]interface{}{
{"role": "user", "name": "user1", "content": "another message"}, {"role": "user", "name": "user1", "content": "another message"},
} }
err = conv.SaveHistory(sid, moreMessages, anotherCID, nil) err = store.SaveHistory(sid, moreMessages, anotherCID, nil)
assert.Nil(t, err) assert.Nil(t, err)
// get history for the first cid - should still be 2 messages // get history for the first cid - should still be 2 messages
data, err = conv.GetHistory(sid, cid) data, err = store.GetHistory(sid, cid)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
assert.Equal(t, 2, len(data)) assert.Equal(t, 2, len(data))
// get history for the second cid - should be 1 message // get history for the second cid - should be 1 message
data, err = conv.GetHistory(sid, anotherCID) data, err = store.GetHistory(sid, anotherCID)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
assert.Equal(t, 1, len(data)) assert.Equal(t, 1, len(data))
// get all history for the sid without specifying cid // get all history for the sid without specifying cid
allData, err := conv.GetHistory(sid, cid) allData, err := store.GetHistory(sid, cid)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -316,7 +306,7 @@ func TestXunGetChats(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
conv, err := NewXun(Setting{ store, err := NewXun(Setting{
Connector: "default", Connector: "default",
Table: "__unit_test_conversation", Table: "__unit_test_conversation",
}) })
@ -333,22 +323,15 @@ func TestXunGetChats(t *testing.T) {
// Create chats with different dates // Create chats with different dates
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
chatID := fmt.Sprintf("chat_%d", i) chatID := fmt.Sprintf("chat_%d", i)
// First create the chat with a title title := fmt.Sprintf("Test Chat %d", i)
err = conv.newQueryChat().Insert(map[string]interface{}{
"chat_id": chatID,
"title": fmt.Sprintf("Test Chat %d", i),
"sid": sid,
"created_at": time.Now(),
})
if err != nil {
t.Fatal(err)
}
// Then save the history // Save history first to create the chat
err = conv.SaveHistory(sid, messages, chatID, nil) err = store.SaveHistory(sid, messages, chatID, nil)
if err != nil { assert.Nil(t, err)
t.Fatal(err)
} // Update the chat title
err = store.UpdateChatTitle(sid, chatID, title)
assert.Nil(t, err)
} }
// Test getting chats with default filter // Test getting chats with default filter
@ -356,7 +339,7 @@ func TestXunGetChats(t *testing.T) {
PageSize: 10, PageSize: 10,
Order: "desc", Order: "desc",
} }
groups, err := conv.GetChats(sid, filter) groups, err := store.GetChats(sid, filter)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -365,7 +348,7 @@ func TestXunGetChats(t *testing.T) {
// Test with keywords // Test with keywords
filter.Keywords = "test" filter.Keywords = "test"
groups, err = conv.GetChats(sid, filter) groups, err = store.GetChats(sid, filter)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -379,7 +362,7 @@ func TestXunDeleteChat(t *testing.T) {
defer capsule.Schema().DropTableIfExists("__unit_test_conversation_history") defer capsule.Schema().DropTableIfExists("__unit_test_conversation_history")
defer capsule.Schema().DropTableIfExists("__unit_test_conversation_chat") defer capsule.Schema().DropTableIfExists("__unit_test_conversation_chat")
conv, err := NewXun(Setting{ store, err := NewXun(Setting{
Connector: "default", Connector: "default",
Table: "__unit_test_conversation", Table: "__unit_test_conversation",
}) })
@ -395,20 +378,20 @@ func TestXunDeleteChat(t *testing.T) {
} }
// Save the chat and history // Save the chat and history
err = conv.SaveHistory(sid, messages, cid, nil) err = store.SaveHistory(sid, messages, cid, nil)
assert.Nil(t, err) assert.Nil(t, err)
// Verify chat exists // Verify chat exists
chat, err := conv.GetChat(sid, cid) chat, err := store.GetChat(sid, cid)
assert.Nil(t, err) assert.Nil(t, err)
assert.NotNil(t, chat) assert.NotNil(t, chat)
// Delete the chat // Delete the chat
err = conv.DeleteChat(sid, cid) err = store.DeleteChat(sid, cid)
assert.Nil(t, err) assert.Nil(t, err)
// Verify chat is deleted // Verify chat is deleted
chat, err = conv.GetChat(sid, cid) chat, err = store.GetChat(sid, cid)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, (*ChatInfo)(nil), chat) assert.Equal(t, (*ChatInfo)(nil), chat)
} }
@ -419,7 +402,7 @@ func TestXunDeleteAllChats(t *testing.T) {
defer capsule.Schema().DropTableIfExists("__unit_test_conversation_history") defer capsule.Schema().DropTableIfExists("__unit_test_conversation_history")
defer capsule.Schema().DropTableIfExists("__unit_test_conversation_chat") defer capsule.Schema().DropTableIfExists("__unit_test_conversation_chat")
conv, err := NewXun(Setting{ store, err := NewXun(Setting{
Connector: "default", Connector: "default",
Table: "__unit_test_conversation", Table: "__unit_test_conversation",
}) })
@ -436,21 +419,21 @@ func TestXunDeleteAllChats(t *testing.T) {
// Save multiple chats // Save multiple chats
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
cid := fmt.Sprintf("test_chat_%d", i) cid := fmt.Sprintf("test_chat_%d", i)
err = conv.SaveHistory(sid, messages, cid, nil) err = store.SaveHistory(sid, messages, cid, nil)
assert.Nil(t, err) assert.Nil(t, err)
} }
// Verify chats exist // Verify chats exist
response, err := conv.GetChats(sid, ChatFilter{}) response, err := store.GetChats(sid, ChatFilter{})
assert.Nil(t, err) assert.Nil(t, err)
assert.Greater(t, response.Total, int64(0)) assert.Greater(t, response.Total, int64(0))
// Delete all chats // Delete all chats
err = conv.DeleteAllChats(sid) err = store.DeleteAllChats(sid)
assert.Nil(t, err) assert.Nil(t, err)
// Verify all chats are deleted // Verify all chats are deleted
response, err = conv.GetChats(sid, ChatFilter{}) response, err = store.GetChats(sid, ChatFilter{})
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, int64(0), response.Total) assert.Equal(t, int64(0), response.Total)
} }
@ -470,7 +453,7 @@ func TestXunAssistantCRUD(t *testing.T) {
// Add a small delay to ensure table is created // Add a small delay to ensure table is created
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
conv, err := NewXun(Setting{ store, err := NewXun(Setting{
Connector: "default", Connector: "default",
Table: "__unit_test_conversation", Table: "__unit_test_conversation",
}) })
@ -495,7 +478,7 @@ func TestXunAssistantCRUD(t *testing.T) {
} }
// Test SaveAssistant (Create) with string JSON // Test SaveAssistant (Create) with string JSON
v, err := conv.SaveAssistant(assistant) v, err := store.SaveAssistant(assistant)
assert.Nil(t, err) assert.Nil(t, err)
assistantID := v.(string) assistantID := v.(string)
assert.NotEmpty(t, assistantID) assert.NotEmpty(t, assistantID)
@ -519,7 +502,7 @@ func TestXunAssistantCRUD(t *testing.T) {
} }
// Test SaveAssistant (Create) with native types // Test SaveAssistant (Create) with native types
v, err = conv.SaveAssistant(assistant2) v, err = store.SaveAssistant(assistant2)
assert.Nil(t, err) assert.Nil(t, err)
assistant2ID := v.(string) assistant2ID := v.(string)
assert.NotEmpty(t, assistant2ID) assert.NotEmpty(t, assistant2ID)
@ -542,13 +525,13 @@ func TestXunAssistantCRUD(t *testing.T) {
} }
// Test SaveAssistant (Create) with nil fields // Test SaveAssistant (Create) with nil fields
v, err = conv.SaveAssistant(assistant3) v, err = store.SaveAssistant(assistant3)
assert.Nil(t, err) assert.Nil(t, err)
assistant3ID := v.(string) assistant3ID := v.(string)
assert.NotEmpty(t, assistant3ID) assert.NotEmpty(t, assistant3ID)
// Test GetAssistants to verify JSON fields are properly stored // Test GetAssistants to verify JSON fields are properly stored
resp, err := conv.GetAssistants(AssistantFilter{}) resp, err := store.GetAssistants(AssistantFilter{})
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 3, len(resp.Data)) assert.Equal(t, 3, len(resp.Data))
@ -614,11 +597,11 @@ func TestXunAssistantCRUD(t *testing.T) {
// Test updating with mixed JSON formats // Test updating with mixed JSON formats
assistant2["assistant_id"] = assistant2ID assistant2["assistant_id"] = assistant2ID
_, err = conv.SaveAssistant(assistant2) _, err = store.SaveAssistant(assistant2)
assert.Nil(t, err) assert.Nil(t, err)
// Verify update // Verify update
resp, err = conv.GetAssistants(AssistantFilter{}) resp, err = store.GetAssistants(AssistantFilter{})
assert.Nil(t, err) assert.Nil(t, err)
for _, item := range resp.Data { for _, item := range resp.Data {
if item["assistant_id"].(string) == assistant2ID { if item["assistant_id"].(string) == assistant2ID {
@ -630,14 +613,14 @@ func TestXunAssistantCRUD(t *testing.T) {
} }
// Test DeleteAssistant // Test DeleteAssistant
err = conv.DeleteAssistant(assistantID) err = store.DeleteAssistant(assistantID)
assert.Nil(t, err) assert.Nil(t, err)
err = conv.DeleteAssistant(assistant2ID) err = store.DeleteAssistant(assistant2ID)
assert.Nil(t, err) assert.Nil(t, err)
err = conv.DeleteAssistant(assistant3ID) err = store.DeleteAssistant(assistant3ID)
assert.Nil(t, err) assert.Nil(t, err)
resp, err = conv.GetAssistants(AssistantFilter{}) resp, err = store.GetAssistants(AssistantFilter{})
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 0, len(resp.Data)) assert.Equal(t, 0, len(resp.Data))
} }
@ -658,7 +641,7 @@ func TestXunAssistantPagination(t *testing.T) {
// Add a small delay to ensure table is created // Add a small delay to ensure table is created
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
conv, err := NewXun(Setting{ store, err := NewXun(Setting{
Connector: "default", Connector: "default",
Table: "__unit_test_conversation", Table: "__unit_test_conversation",
}) })
@ -692,12 +675,12 @@ func TestXunAssistantPagination(t *testing.T) {
"mentionable": mentionable, "mentionable": mentionable,
"automated": automated, "automated": automated,
} }
_, err = conv.SaveAssistant(assistant) _, err = store.SaveAssistant(assistant)
assert.Nil(t, err) assert.Nil(t, err)
} }
// Test first page // Test first page
resp, err := conv.GetAssistants(AssistantFilter{ resp, err := store.GetAssistants(AssistantFilter{
Page: 1, Page: 1,
PageSize: 10, PageSize: 10,
}) })
@ -709,7 +692,7 @@ func TestXunAssistantPagination(t *testing.T) {
assert.Equal(t, 0, resp.Prev) assert.Equal(t, 0, resp.Prev)
// Test second page // Test second page
resp, err = conv.GetAssistants(AssistantFilter{ resp, err = store.GetAssistants(AssistantFilter{
Page: 2, Page: 2,
PageSize: 10, PageSize: 10,
}) })
@ -719,7 +702,7 @@ func TestXunAssistantPagination(t *testing.T) {
assert.Equal(t, 1, resp.Prev) assert.Equal(t, 1, resp.Prev)
// Test last page // Test last page
resp, err = conv.GetAssistants(AssistantFilter{ resp, err = store.GetAssistants(AssistantFilter{
Page: 3, Page: 3,
PageSize: 10, PageSize: 10,
}) })
@ -729,7 +712,7 @@ func TestXunAssistantPagination(t *testing.T) {
assert.Equal(t, 2, resp.Prev) assert.Equal(t, 2, resp.Prev)
// Test filtering with tags // Test filtering with tags
resp, err = conv.GetAssistants(AssistantFilter{ resp, err = store.GetAssistants(AssistantFilter{
Tags: []string{"tag0"}, Tags: []string{"tag0"},
Page: 1, Page: 1,
PageSize: 10, PageSize: 10,
@ -738,7 +721,7 @@ func TestXunAssistantPagination(t *testing.T) {
assert.Equal(t, 5, len(resp.Data)) assert.Equal(t, 5, len(resp.Data))
// Test filtering with keywords // Test filtering with keywords
resp, err = conv.GetAssistants(AssistantFilter{ resp, err = store.GetAssistants(AssistantFilter{
Keywords: "Assistant 1", Keywords: "Assistant 1",
Page: 1, Page: 1,
PageSize: 10, PageSize: 10,
@ -747,7 +730,7 @@ func TestXunAssistantPagination(t *testing.T) {
assert.Greater(t, len(resp.Data), 0) assert.Greater(t, len(resp.Data), 0)
// Test filtering with connector // Test filtering with connector
resp, err = conv.GetAssistants(AssistantFilter{ resp, err = store.GetAssistants(AssistantFilter{
Connector: "connector0", Connector: "connector0",
Page: 1, Page: 1,
PageSize: 10, PageSize: 10,
@ -757,7 +740,7 @@ func TestXunAssistantPagination(t *testing.T) {
// Test filtering with mentionable // Test filtering with mentionable
mentionableTrue := true mentionableTrue := true
resp, err = conv.GetAssistants(AssistantFilter{ resp, err = store.GetAssistants(AssistantFilter{
Mentionable: &mentionableTrue, Mentionable: &mentionableTrue,
Page: 1, Page: 1,
PageSize: 10, PageSize: 10,
@ -767,7 +750,7 @@ func TestXunAssistantPagination(t *testing.T) {
// Test filtering with automated // Test filtering with automated
automatedTrue := true automatedTrue := true
resp, err = conv.GetAssistants(AssistantFilter{ resp, err = store.GetAssistants(AssistantFilter{
Automated: &automatedTrue, Automated: &automatedTrue,
Page: 1, Page: 1,
PageSize: 10, PageSize: 10,
@ -780,7 +763,7 @@ func TestXunAssistantPagination(t *testing.T) {
firstAssistantID := resp.Data[0]["assistant_id"].(string) firstAssistantID := resp.Data[0]["assistant_id"].(string)
// Test exact match with assistant_id // Test exact match with assistant_id
resp, err = conv.GetAssistants(AssistantFilter{ resp, err = store.GetAssistants(AssistantFilter{
AssistantID: firstAssistantID, AssistantID: firstAssistantID,
Page: 1, Page: 1,
PageSize: 10, PageSize: 10,
@ -790,7 +773,7 @@ func TestXunAssistantPagination(t *testing.T) {
assert.Equal(t, firstAssistantID, resp.Data[0]["assistant_id"]) assert.Equal(t, firstAssistantID, resp.Data[0]["assistant_id"])
// Test assistant_id with other filters // Test assistant_id with other filters
resp, err = conv.GetAssistants(AssistantFilter{ resp, err = store.GetAssistants(AssistantFilter{
AssistantID: firstAssistantID, AssistantID: firstAssistantID,
Select: []string{"name", "assistant_id", "description"}, Select: []string{"name", "assistant_id", "description"},
Page: 1, Page: 1,
@ -807,7 +790,7 @@ func TestXunAssistantPagination(t *testing.T) {
assert.NotContains(t, resp.Data[0], "options") assert.NotContains(t, resp.Data[0], "options")
// Test non-existent assistant_id // Test non-existent assistant_id
resp, err = conv.GetAssistants(AssistantFilter{ resp, err = store.GetAssistants(AssistantFilter{
AssistantID: "non-existent-id", AssistantID: "non-existent-id",
Page: 1, Page: 1,
PageSize: 10, PageSize: 10,
@ -816,7 +799,7 @@ func TestXunAssistantPagination(t *testing.T) {
assert.Equal(t, 0, len(resp.Data)) assert.Equal(t, 0, len(resp.Data))
// Test combined filters // Test combined filters
resp, err = conv.GetAssistants(AssistantFilter{ resp, err = store.GetAssistants(AssistantFilter{
Tags: []string{"tag0"}, Tags: []string{"tag0"},
Keywords: "Assistant", Keywords: "Assistant",
Connector: "connector0", Connector: "connector0",
@ -828,7 +811,7 @@ func TestXunAssistantPagination(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
// Test filtering with select fields // Test filtering with select fields
resp, err = conv.GetAssistants(AssistantFilter{ resp, err = store.GetAssistants(AssistantFilter{
Select: []string{"name", "description", "tags"}, Select: []string{"name", "description", "tags"},
Page: 1, Page: 1,
PageSize: 10, PageSize: 10,
@ -851,7 +834,7 @@ func TestXunAssistantPagination(t *testing.T) {
} }
// Test filtering with select fields and other filters combined // Test filtering with select fields and other filters combined
resp, err = conv.GetAssistants(AssistantFilter{ resp, err = store.GetAssistants(AssistantFilter{
Tags: []string{"tag0"}, Tags: []string{"tag0"},
Keywords: "Assistant", Keywords: "Assistant",
Select: []string{"name", "tags"}, Select: []string{"name", "tags"},

View file

@ -5,30 +5,30 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/yaoapp/yao/neo/assistant" "github.com/yaoapp/yao/neo/assistant"
"github.com/yaoapp/yao/neo/conversation" "github.com/yaoapp/yao/neo/store"
) )
// DSL AI assistant // DSL AI assistant
type DSL struct { type DSL struct {
ID string `json:"-" yaml:"-"` ID string `json:"-" yaml:"-"`
Name string `json:"name,omitempty" yaml:"name,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"`
Use string `json:"use,omitempty" yaml:"use,omitempty"` // Which assistant to use default Use string `json:"use,omitempty" yaml:"use,omitempty"` // Which assistant to use default
Guard string `json:"guard,omitempty" yaml:"guard,omitempty"` Guard string `json:"guard,omitempty" yaml:"guard,omitempty"`
Connector string `json:"connector" yaml:"connector"` Connector string `json:"connector" yaml:"connector"`
ConversationSetting conversation.Setting `json:"conversation" yaml:"conversation"` StoreSetting store.Setting `json:"store" yaml:"store"`
Option map[string]interface{} `json:"option" yaml:"option"` Option map[string]interface{} `json:"option" yaml:"option"`
Prepare string `json:"prepare,omitempty" yaml:"prepare,omitempty"` Prepare string `json:"prepare,omitempty" yaml:"prepare,omitempty"`
Create string `json:"create,omitempty" yaml:"create,omitempty"` Create string `json:"create,omitempty" yaml:"create,omitempty"`
Write string `json:"write,omitempty" yaml:"write,omitempty"` Write string `json:"write,omitempty" yaml:"write,omitempty"`
AssistantListHook string `json:"assistants,omitempty" yaml:"assistants,omitempty"` // Get the assistant list from the hook AssistantListHook string `json:"assistants,omitempty" yaml:"assistants,omitempty"` // Get the assistant list from the hook
MentionHook string `json:"mentions,omitempty"` // Get the mention list from the hook MentionHook string `json:"mentions,omitempty"` // Get the mention list from the hook
Prompts []assistant.Prompt `json:"prompts,omitempty" yaml:"prompts,omitempty"` Prompts []assistant.Prompt `json:"prompts,omitempty" yaml:"prompts,omitempty"`
Allows []string `json:"allows,omitempty" yaml:"allows,omitempty"` Allows []string `json:"allows,omitempty" yaml:"allows,omitempty"`
Assistant assistant.API `json:"-" yaml:"-"` // The default assistant Assistant assistant.API `json:"-" yaml:"-"` // The default assistant
Conversation conversation.Conversation `json:"-" yaml:"-"` Store store.Store `json:"-" yaml:"-"`
GuardHandlers []gin.HandlerFunc `json:"-" yaml:"-"` GuardHandlers []gin.HandlerFunc `json:"-" yaml:"-"`
AssistantList []assistant.Assistant `json:"-" yaml:"-"` AssistantList []assistant.Assistant `json:"-" yaml:"-"`
AssistantMaps map[string]assistant.Assistant `json:"-" yaml:"-"` AssistantMaps map[string]assistant.Assistant `json:"-" yaml:"-"`
} }
// Mention list // Mention list