Add chat detail and mentions handling in Neo API
- Introduced new endpoints for retrieving chat details and mentions, enhancing the API's functionality. - Implemented handleChatDetail method to fetch details of a specific chat by ID, including error handling for missing parameters. - Added handleMentions method to retrieve mentions based on keywords, improving user interaction with chat content. - Updated existing GetChats method to support keyword filtering, allowing for more refined chat list retrieval. - Enhanced the DSL structure to include new methods for managing mentions and chat details, improving overall code organization and maintainability.
This commit is contained in:
parent
2da7c30ac5
commit
eadc550980
9 changed files with 373 additions and 94 deletions
58
neo/api.go
58
neo/api.go
|
|
@ -28,18 +28,22 @@ func (neo *DSL) API(router *gin.Engine, path string) error {
|
||||||
router.OPTIONS(path, neo.optionsHandler)
|
router.OPTIONS(path, neo.optionsHandler)
|
||||||
router.OPTIONS(path+"/status", neo.optionsHandler)
|
router.OPTIONS(path+"/status", neo.optionsHandler)
|
||||||
router.OPTIONS(path+"/chats", neo.optionsHandler)
|
router.OPTIONS(path+"/chats", neo.optionsHandler)
|
||||||
|
router.OPTIONS(path+"/chats/:id", neo.optionsHandler)
|
||||||
router.OPTIONS(path+"/history", neo.optionsHandler)
|
router.OPTIONS(path+"/history", neo.optionsHandler)
|
||||||
router.OPTIONS(path+"/upload", neo.optionsHandler)
|
router.OPTIONS(path+"/upload", neo.optionsHandler)
|
||||||
router.OPTIONS(path+"/download", neo.optionsHandler)
|
router.OPTIONS(path+"/download", neo.optionsHandler)
|
||||||
|
router.OPTIONS(path+"/mentions", neo.optionsHandler)
|
||||||
|
|
||||||
// Register endpoints with middlewares
|
// Register endpoints with middlewares
|
||||||
router.GET(path, append(middlewares, neo.handleChat)...)
|
router.GET(path, append(middlewares, neo.handleChat)...)
|
||||||
router.POST(path, append(middlewares, neo.handleChat)...)
|
router.POST(path, append(middlewares, neo.handleChat)...)
|
||||||
router.GET(path+"/status", append(middlewares, neo.handleStatus)...)
|
router.GET(path+"/status", append(middlewares, neo.handleStatus)...)
|
||||||
router.GET(path+"/chats", append(middlewares, neo.handleChatList)...)
|
router.GET(path+"/chats", append(middlewares, neo.handleChatList)...)
|
||||||
|
router.GET(path+"/chats/:id", append(middlewares, neo.handleChatDetail)...)
|
||||||
router.GET(path+"/history", append(middlewares, neo.handleChatHistory)...)
|
router.GET(path+"/history", append(middlewares, neo.handleChatHistory)...)
|
||||||
router.POST(path+"/upload", append(middlewares, neo.handleUpload)...)
|
router.POST(path+"/upload", append(middlewares, neo.handleUpload)...)
|
||||||
router.GET(path+"/download", append(middlewares, neo.handleDownload)...)
|
router.GET(path+"/download", append(middlewares, neo.handleDownload)...)
|
||||||
|
router.GET(path+"/mentions", append(middlewares, neo.handleMentions)...)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,7 +111,10 @@ func (neo *DSL) handleChatList(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
list, err := neo.Conversation.GetChats(sid)
|
// Get keywords from query parameter
|
||||||
|
keywords := c.Query("keywords")
|
||||||
|
|
||||||
|
list, err := neo.Conversation.GetChats(sid, keywords)
|
||||||
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()
|
||||||
|
|
@ -295,3 +302,52 @@ func (neo *DSL) defaultGuard(c *gin.Context) {
|
||||||
c.Set("__sid", user.SID)
|
c.Set("__sid", user.SID)
|
||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleChatDetail handles getting a single chat's details
|
||||||
|
func (neo *DSL) handleChatDetail(c *gin.Context) {
|
||||||
|
sid := c.GetString("__sid")
|
||||||
|
if sid == "" {
|
||||||
|
c.JSON(400, gin.H{"message": "sid is required", "code": 400})
|
||||||
|
c.Done()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
chatID := c.Param("id")
|
||||||
|
if chatID == "" {
|
||||||
|
c.JSON(400, gin.H{"message": "chat id is required", "code": 400})
|
||||||
|
c.Done()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
chat, err := neo.Conversation.GetChat(sid, chatID)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(500, gin.H{"message": err.Error(), "code": 500})
|
||||||
|
c.Done()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, chat)
|
||||||
|
c.Done()
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleMentions handles getting mentions for a chat
|
||||||
|
func (neo *DSL) handleMentions(c *gin.Context) {
|
||||||
|
sid := c.GetString("__sid")
|
||||||
|
if sid == "" {
|
||||||
|
c.JSON(400, gin.H{"message": "sid is required", "code": 400})
|
||||||
|
c.Done()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get keywords from query parameter
|
||||||
|
keywords := c.Query("keywords")
|
||||||
|
mentions, err := neo.GetMentions(keywords)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(500, gin.H{"message": err.Error(), "code": 500})
|
||||||
|
c.Done()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, map[string]interface{}{"data": mentions})
|
||||||
|
c.Done()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ func (conv *Mongo) UpdateChatTitle(sid string, cid string, title string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetChats get the chat list
|
// GetChats get the chat list
|
||||||
func (conv *Mongo) GetChats(sid string) ([]map[string]interface{}, error) {
|
func (conv *Mongo) GetChats(sid string, keywords ...string) ([]map[string]interface{}, error) {
|
||||||
return []map[string]interface{}{}, nil
|
return []map[string]interface{}{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -37,3 +37,8 @@ func (conv *Mongo) GetRequest(sid string, rid string) ([]map[string]interface{},
|
||||||
func (conv *Mongo) SaveRequest(sid string, rid string, cid string, messages []map[string]interface{}) error {
|
func (conv *Mongo) SaveRequest(sid string, rid string, cid string, messages []map[string]interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetChat get the chat info and its history
|
||||||
|
func (conv *Mongo) GetChat(sid string, cid string) (*ChatInfo, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ func (conv *Redis) UpdateChatTitle(sid string, cid string, title string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetChats get the chat list
|
// GetChats get the chat list
|
||||||
func (conv *Redis) GetChats(sid string) ([]map[string]interface{}, error) {
|
func (conv *Redis) GetChats(sid string, keywords ...string) ([]map[string]interface{}, error) {
|
||||||
return []map[string]interface{}{}, nil
|
return []map[string]interface{}{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -37,3 +37,8 @@ func (conv *Redis) GetRequest(sid string, rid string) ([]map[string]interface{},
|
||||||
func (conv *Redis) SaveRequest(sid string, rid string, cid string, messages []map[string]interface{}) error {
|
func (conv *Redis) SaveRequest(sid string, rid string, cid string, messages []map[string]interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetChat get the chat info and its history
|
||||||
|
func (conv *Redis) GetChat(sid string, cid string) (*ChatInfo, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,17 @@ type Setting struct {
|
||||||
TTL int `json:"ttl,omitempty" yaml:"ttl,omitempty"`
|
TTL int `json:"ttl,omitempty" yaml:"ttl,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChatInfo represents the chat information and its history
|
||||||
|
type ChatInfo struct {
|
||||||
|
Chat map[string]interface{} `json:"chat"`
|
||||||
|
History []map[string]interface{} `json:"history"`
|
||||||
|
}
|
||||||
|
|
||||||
// Conversation the store interface
|
// Conversation the store interface
|
||||||
type Conversation interface {
|
type Conversation interface {
|
||||||
UpdateChatTitle(sid string, cid string, title string) error
|
UpdateChatTitle(sid string, cid string, title string) error
|
||||||
GetChats(sid string) ([]map[string]interface{}, error)
|
GetChats(sid string, keywords ...string) ([]map[string]interface{}, error)
|
||||||
|
GetChat(sid string, cid string) (*ChatInfo, error)
|
||||||
GetHistory(sid string, cid string) ([]map[string]interface{}, error)
|
GetHistory(sid string, cid string) ([]map[string]interface{}, error)
|
||||||
SaveHistory(sid string, messages []map[string]interface{}, cid string) error
|
SaveHistory(sid string, messages []map[string]interface{}, cid string) error
|
||||||
GetRequest(sid string, rid string) ([]map[string]interface{}, error)
|
GetRequest(sid string, rid string) ([]map[string]interface{}, error)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ func (conv *Weaviate) UpdateChatTitle(sid string, cid string, title string) erro
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetChats get the chat list
|
// GetChats get the chat list
|
||||||
func (conv *Weaviate) GetChats(sid string) ([]map[string]interface{}, error) {
|
func (conv *Weaviate) GetChats(sid string, keywords ...string) ([]map[string]interface{}, error) {
|
||||||
return []map[string]interface{}{}, nil
|
return []map[string]interface{}{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -37,3 +37,8 @@ func (conv *Weaviate) GetRequest(sid string, rid string) ([]map[string]interface
|
||||||
func (conv *Weaviate) SaveRequest(sid string, rid string, cid string, messages []map[string]interface{}) error {
|
func (conv *Weaviate) SaveRequest(sid string, rid string, cid string, messages []map[string]interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetChat get the chat info and its history
|
||||||
|
func (conv *Weaviate) GetChat(sid string, cid string) (*ChatInfo, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package conversation
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/yaoapp/gou/connector"
|
"github.com/yaoapp/gou/connector"
|
||||||
|
|
@ -29,16 +30,23 @@ type row struct {
|
||||||
ExpiredAt interface{} `json:"expired_at"`
|
ExpiredAt interface{} `json:"expired_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Public interface methods and constructor remain exported:
|
||||||
|
// - NewXun
|
||||||
|
// - UpdateChatTitle
|
||||||
|
// - GetChats
|
||||||
|
// - GetChat
|
||||||
|
// - GetHistory
|
||||||
|
// - SaveHistory
|
||||||
|
// - GetRequest
|
||||||
|
// - SaveRequest
|
||||||
|
|
||||||
// NewXun create a new conversation
|
// NewXun create a new conversation
|
||||||
func NewXun(setting Setting) (*Xun, error) {
|
func NewXun(setting Setting) (*Xun, 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()
|
||||||
conv.schema = capsule.Global.Schema()
|
conv.schema = capsule.Global.Schema()
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
conn, err := connector.Select(setting.Connector)
|
conn, err := connector.Select(setting.Connector)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -55,7 +63,7 @@ func NewXun(setting Setting) (*Xun, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err := conv.Init()
|
err := conv.initialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -63,43 +71,175 @@ func NewXun(setting Setting) (*Xun, error) {
|
||||||
return conv, nil
|
return conv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewQuery create a new query
|
// Rename the following functions to start with lowercase letters to make them private:
|
||||||
func (conv *Xun) NewQuery() query.Query {
|
|
||||||
|
func (conv *Xun) newQuery() query.Query {
|
||||||
qb := conv.query.New()
|
qb := conv.query.New()
|
||||||
qb.Table(conv.setting.Table)
|
qb.Table(conv.getHistoryTable())
|
||||||
return qb
|
return qb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (conv *Xun) newQueryChat() query.Query {
|
||||||
|
qb := conv.query.New()
|
||||||
|
qb.Table(conv.getChatTable())
|
||||||
|
return qb
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conv *Xun) clean() {
|
||||||
|
nums, err := conv.newQuery().Where("expired_at", "<=", time.Now()).Delete()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Clean the conversation table error: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if nums > 0 {
|
||||||
|
log.Trace("Clean the conversation table: %s %d", conv.setting.Table, nums)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename Init to initialize to avoid conflicts
|
||||||
|
func (conv *Xun) initialize() error {
|
||||||
|
// Initialize history table
|
||||||
|
if err := conv.initHistoryTable(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize chat table
|
||||||
|
if err := conv.initChatTable(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conv *Xun) initHistoryTable() error {
|
||||||
|
historyTable := conv.getHistoryTable()
|
||||||
|
has, err := conv.schema.HasTable(historyTable)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the history table
|
||||||
|
if !has {
|
||||||
|
err = conv.schema.CreateTable(historyTable, func(table schema.Blueprint) {
|
||||||
|
table.ID("id")
|
||||||
|
table.String("sid", 255).Index()
|
||||||
|
table.String("rid", 255).Null().Index()
|
||||||
|
table.String("cid", 200).Null().Index()
|
||||||
|
table.String("role", 200).Null().Index()
|
||||||
|
table.String("name", 200).Null().Index()
|
||||||
|
table.Text("content").Null()
|
||||||
|
table.TimestampTz("created_at").SetDefaultRaw("NOW()").Index()
|
||||||
|
table.TimestampTz("updated_at").Null().Index()
|
||||||
|
table.TimestampTz("expired_at").Null().Index()
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Trace("Create the conversation history table: %s", historyTable)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the table
|
||||||
|
tab, err := conv.schema.GetTable(historyTable)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := []string{"id", "sid", "rid", "cid", "role", "name", "content", "created_at", "updated_at", "expired_at"}
|
||||||
|
for _, field := range fields {
|
||||||
|
if !tab.HasColumn(field) {
|
||||||
|
return fmt.Errorf("%s is required", field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conv *Xun) initChatTable() error {
|
||||||
|
chatTable := conv.getChatTable()
|
||||||
|
has, err := conv.schema.HasTable(chatTable)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the chat table
|
||||||
|
if !has {
|
||||||
|
err = conv.schema.CreateTable(chatTable, func(table schema.Blueprint) {
|
||||||
|
table.ID("id")
|
||||||
|
table.String("chat_id", 200).Unique().Index()
|
||||||
|
table.String("title", 200).Null()
|
||||||
|
table.String("sid", 255).Index()
|
||||||
|
table.TimestampTz("created_at").SetDefaultRaw("NOW()").Index()
|
||||||
|
table.TimestampTz("updated_at").Null().Index()
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Trace("Create the chat table: %s", chatTable)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the table
|
||||||
|
tab, err := conv.schema.GetTable(chatTable)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := []string{"id", "chat_id", "title", "sid", "created_at", "updated_at"}
|
||||||
|
for _, field := range fields {
|
||||||
|
if !tab.HasColumn(field) {
|
||||||
|
return fmt.Errorf("%s is required", field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conv *Xun) getHistoryTable() string {
|
||||||
|
return conv.setting.Table
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conv *Xun) getChatTable() string {
|
||||||
|
return conv.setting.Table + "_chat"
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateChatTitle update the chat title
|
// UpdateChatTitle update the chat title
|
||||||
func (conv *Xun) UpdateChatTitle(sid string, cid string, title string) error {
|
func (conv *Xun) UpdateChatTitle(sid string, cid string, title string) error {
|
||||||
_, err := conv.NewQuery().
|
_, err := conv.newQueryChat().
|
||||||
Where("sid", sid).Where("cid", cid).
|
Where("sid", sid).
|
||||||
Update(map[string]interface{}{"title": title})
|
Where("chat_id", cid).
|
||||||
|
Update(map[string]interface{}{
|
||||||
|
"title": title,
|
||||||
|
"updated_at": time.Now(),
|
||||||
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetChats get the chat list
|
// GetChats get the chat list
|
||||||
func (conv *Xun) GetChats(sid string) ([]map[string]interface{}, error) {
|
func (conv *Xun) GetChats(sid string, keywords ...string) ([]map[string]interface{}, error) {
|
||||||
qb := conv.NewQuery().
|
qb := conv.newQueryChat().
|
||||||
Select("cid").
|
Select("chat_id", "title").
|
||||||
Where("sid", sid).
|
Where("sid", sid)
|
||||||
GroupBy("cid")
|
|
||||||
|
|
||||||
if conv.setting.TTL > 0 {
|
// Add title search if keywords provided
|
||||||
qb.Where("expired_at", ">", time.Now())
|
if len(keywords) > 0 && keywords[0] != "" {
|
||||||
|
keyword := strings.TrimSpace(keywords[0]) // Trim whitespace from keyword
|
||||||
|
if keyword != "" {
|
||||||
|
qb.Where("title", "like", "%"+keyword+"%")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res := []map[string]interface{}{}
|
|
||||||
|
|
||||||
rows, err := qb.Get()
|
rows, err := qb.Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res := []map[string]interface{}{}
|
||||||
for _, row := range rows {
|
for _, row := range rows {
|
||||||
res = append(res, map[string]interface{}{
|
res = append(res, map[string]interface{}{
|
||||||
"chat_id": row.Get("cid"),
|
"chat_id": row.Get("chat_id"),
|
||||||
"title": row.Get("cid"),
|
"title": row.Get("title"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,7 +249,7 @@ func (conv *Xun) GetChats(sid string) ([]map[string]interface{}, error) {
|
||||||
// GetHistory get the history
|
// GetHistory get the history
|
||||||
func (conv *Xun) GetHistory(sid string, cid string) ([]map[string]interface{}, error) {
|
func (conv *Xun) GetHistory(sid string, cid string) ([]map[string]interface{}, error) {
|
||||||
|
|
||||||
qb := conv.NewQuery().
|
qb := conv.newQuery().
|
||||||
Select("role", "name", "content").
|
Select("role", "name", "content").
|
||||||
Where("sid", sid).
|
Where("sid", sid).
|
||||||
Where("cid", cid).
|
Where("cid", cid).
|
||||||
|
|
@ -143,7 +283,31 @@ func (conv *Xun) GetHistory(sid string, cid string) ([]map[string]interface{}, e
|
||||||
|
|
||||||
// SaveHistory save the history
|
// SaveHistory save the history
|
||||||
func (conv *Xun) SaveHistory(sid string, messages []map[string]interface{}, cid string) error {
|
func (conv *Xun) SaveHistory(sid string, messages []map[string]interface{}, cid string) error {
|
||||||
|
// First ensure chat record exists
|
||||||
|
exists, err := conv.newQueryChat().
|
||||||
|
Where("chat_id", cid).
|
||||||
|
Where("sid", sid).
|
||||||
|
Exists()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
// Create new chat record
|
||||||
|
err = conv.newQueryChat().
|
||||||
|
Insert(map[string]interface{}{
|
||||||
|
"chat_id": cid,
|
||||||
|
"sid": sid,
|
||||||
|
"created_at": time.Now(),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save message history
|
||||||
defer conv.clean()
|
defer conv.clean()
|
||||||
var expiredAt interface{} = nil
|
var expiredAt interface{} = nil
|
||||||
values := []row{}
|
values := []row{}
|
||||||
|
|
@ -167,13 +331,13 @@ func (conv *Xun) SaveHistory(sid string, messages []map[string]interface{}, cid
|
||||||
values = append(values, value)
|
values = append(values, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return conv.NewQuery().Insert(values)
|
return conv.newQuery().Insert(values)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRequest get the request history
|
// GetRequest get the request history
|
||||||
func (conv *Xun) GetRequest(sid string, rid string) ([]map[string]interface{}, error) {
|
func (conv *Xun) GetRequest(sid string, rid string) ([]map[string]interface{}, error) {
|
||||||
|
|
||||||
qb := conv.NewQuery().
|
qb := conv.newQuery().
|
||||||
Select("role", "name", "content", "sid").
|
Select("role", "name", "content", "sid").
|
||||||
Where("rid", rid).
|
Where("rid", rid).
|
||||||
Where("sid", sid).
|
Where("sid", sid).
|
||||||
|
|
@ -232,75 +396,35 @@ func (conv *Xun) SaveRequest(sid string, rid string, cid string, messages []map[
|
||||||
values = append(values, value)
|
values = append(values, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return conv.NewQuery().Insert(values)
|
return conv.newQuery().Insert(values)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conv *Xun) clean() {
|
// GetChat get the chat info and its history
|
||||||
nums, err := conv.NewQuery().Where("expired_at", "<=", time.Now()).Delete()
|
func (conv *Xun) GetChat(sid string, cid string) (*ChatInfo, error) {
|
||||||
|
// Get chat info
|
||||||
|
qb := conv.newQueryChat().
|
||||||
|
Select("chat_id", "title").
|
||||||
|
Where("sid", sid).
|
||||||
|
Where("chat_id", cid)
|
||||||
|
|
||||||
|
row, err := qb.First()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Clean the conversation table error: %s", err.Error())
|
return nil, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if nums > 0 {
|
chat := map[string]interface{}{
|
||||||
log.Trace("Clean the conversation table: %s %d", conv.setting.Table, nums)
|
"chat_id": row.Get("chat_id"),
|
||||||
|
"title": row.Get("title"),
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Get chat history
|
||||||
// Init init the conversation
|
history, err := conv.GetHistory(sid, cid)
|
||||||
func (conv *Xun) Init() error {
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
has, err := conv.schema.HasTable(conv.setting.Table)
|
}
|
||||||
if err != nil {
|
|
||||||
return err
|
return &ChatInfo{
|
||||||
}
|
Chat: chat,
|
||||||
|
History: history,
|
||||||
// create the table
|
}, nil
|
||||||
if !has {
|
|
||||||
err = conv.schema.CreateTable(conv.setting.Table, func(table schema.Blueprint) {
|
|
||||||
|
|
||||||
table.ID("id") // The ID field
|
|
||||||
table.String("sid", 255).Index() // The Session ID
|
|
||||||
table.String("rid", 255).Null().Index() // The request ID
|
|
||||||
table.String("cid", 200).Null().Index() // The Chat ID
|
|
||||||
table.String("role", 200).Null().Index() // The Message role
|
|
||||||
table.String("name", 200).Null().Index() // The User name
|
|
||||||
table.String("title", 200).Null().Index() // The Chat title
|
|
||||||
table.Text("content").Null()
|
|
||||||
|
|
||||||
table.TimestampTz("created_at").SetDefaultRaw("NOW()").Index()
|
|
||||||
table.TimestampTz("updated_at").Null().Index()
|
|
||||||
table.TimestampTz("expired_at").Null().Index()
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Trace("Create the conversation table: %s", conv.setting.Table)
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate the table
|
|
||||||
tab, err := conv.schema.GetTable(conv.setting.Table)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fields := []string{"id", "sid", "rid", "cid", "role", "name", "content", "created_at", "updated_at", "expired_at"}
|
|
||||||
for _, field := range fields {
|
|
||||||
if !tab.HasColumn(field) {
|
|
||||||
return fmt.Errorf("%s is required", field)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auto update the title
|
|
||||||
if !tab.HasColumn("title") {
|
|
||||||
err = conv.schema.AlterTable(conv.setting.Table, func(table schema.Blueprint) {
|
|
||||||
table.String("title", 200).Null().Index()
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
57
neo/hooks.go
57
neo/hooks.go
|
|
@ -194,3 +194,60 @@ func (neo *DSL) HookWrite(ctx Context, messages []map[string]interface{}, respon
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HookMention query the mention list
|
||||||
|
func (neo *DSL) HookMention(ctx context.Context, keywords string) ([]Mention, error) {
|
||||||
|
|
||||||
|
// Default Get the assistant list
|
||||||
|
if neo.MentionHook == "" {
|
||||||
|
var mentions []Mention
|
||||||
|
assistants := neo.GetAssistants()
|
||||||
|
for _, assistant := range assistants {
|
||||||
|
mentions = append(mentions, Mention{
|
||||||
|
ID: assistant.ID,
|
||||||
|
Name: assistant.Name,
|
||||||
|
Type: "assistant",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return mentions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a context with 10 second timeout
|
||||||
|
timeoutCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
p, err := process.Of(neo.MentionHook, keywords)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = p.WithContext(timeoutCtx).Execute()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer p.Release()
|
||||||
|
|
||||||
|
// Check if context was canceled
|
||||||
|
if timeoutCtx.Err() != nil {
|
||||||
|
return nil, timeoutCtx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
value := p.Value()
|
||||||
|
if value == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var list []Mention
|
||||||
|
bytes, err := jsoniter.Marshal(value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = jsoniter.Unmarshal(bytes, &list)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
11
neo/neo.go
11
neo/neo.go
|
|
@ -1,6 +1,7 @@
|
||||||
package neo
|
package neo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -47,6 +48,16 @@ func (neo *DSL) Answer(ctx Context, question string, c *gin.Context) error {
|
||||||
return neo.chat(ast, ctx, messages, c)
|
return neo.chat(ast, ctx, messages, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAssistants returns the list of assistants
|
||||||
|
func (neo *DSL) GetAssistants() []assistant.Assistant {
|
||||||
|
return neo.AssistantList
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMentions returns the mention list
|
||||||
|
func (neo *DSL) GetMentions(keywords string) ([]Mention, error) {
|
||||||
|
return neo.HookMention(context.Background(), keywords)
|
||||||
|
}
|
||||||
|
|
||||||
// Upload upload a file
|
// Upload upload a file
|
||||||
func (neo *DSL) Upload(ctx Context, c *gin.Context) (*assistant.File, error) {
|
func (neo *DSL) Upload(ctx Context, c *gin.Context) (*assistant.File, error) {
|
||||||
// Get the file
|
// Get the file
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ type DSL struct {
|
||||||
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
|
||||||
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
|
||||||
|
|
@ -30,6 +31,14 @@ type DSL struct {
|
||||||
AssistantMaps map[string]assistant.Assistant `json:"-" yaml:"-"`
|
AssistantMaps map[string]assistant.Assistant `json:"-" yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mention list
|
||||||
|
type Mention struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Avatar string `json:"avatar,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// Context the context
|
// Context the context
|
||||||
type Context struct {
|
type Context struct {
|
||||||
Sid string `json:"sid" yaml:"-"` // Session ID
|
Sid string `json:"sid" yaml:"-"` // Session ID
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue