Merge pull request #820 from trheyi/main
Enhance Neo API assistant message storage and structure
This commit is contained in:
commit
2726753f54
3 changed files with 128 additions and 50 deletions
|
|
@ -10,7 +10,6 @@ import (
|
|||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/yaoapp/gou/fs"
|
||||
"github.com/yaoapp/gou/process"
|
||||
"github.com/yaoapp/kun/utils"
|
||||
chatctx "github.com/yaoapp/yao/neo/context"
|
||||
chatMessage "github.com/yaoapp/yao/neo/message"
|
||||
)
|
||||
|
|
@ -61,7 +60,7 @@ func (ast *Assistant) Execute(c *gin.Context, ctx chatctx.Context, input string,
|
|||
}
|
||||
|
||||
// Switch to the new assistant if necessary
|
||||
if res.AssistantID != ctx.AssistantID {
|
||||
if res != nil && res.AssistantID != ctx.AssistantID {
|
||||
newAst, err := Get(res.AssistantID)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -70,17 +69,17 @@ func (ast *Assistant) Execute(c *gin.Context, ctx chatctx.Context, input string,
|
|||
}
|
||||
|
||||
// Handle next action
|
||||
if res.Next != nil {
|
||||
if res != nil && res.Next != nil {
|
||||
return res.Next.Execute(c, ctx)
|
||||
}
|
||||
|
||||
// Update options if provided
|
||||
if res.Options != nil {
|
||||
if res != nil && res.Options != nil {
|
||||
options = res.Options
|
||||
}
|
||||
|
||||
// messages
|
||||
if res.Input != nil {
|
||||
if res != nil && res.Input != nil {
|
||||
messages = res.Input
|
||||
}
|
||||
|
||||
|
|
@ -264,17 +263,20 @@ func (ast *Assistant) streamChat(
|
|||
|
||||
chatMessage.New().
|
||||
Map(map[string]interface{}{
|
||||
"text": value,
|
||||
"done": msg.IsDone,
|
||||
"assistant_id": ast.ID,
|
||||
"assistant_name": ast.Name,
|
||||
"assistant_avatar": ast.Avatar,
|
||||
"text": value,
|
||||
"done": msg.IsDone,
|
||||
}).
|
||||
Write(c.Writer)
|
||||
}
|
||||
|
||||
// Complete the stream
|
||||
if msg.IsDone {
|
||||
// if value == "" {
|
||||
// msg.Write(c.Writer)
|
||||
// }
|
||||
if value == "" {
|
||||
msg.Write(c.Writer)
|
||||
}
|
||||
|
||||
// Call HookDone
|
||||
content.SetStatus(chatMessage.ContentStatusDone)
|
||||
|
|
@ -301,8 +303,11 @@ func (ast *Assistant) streamChat(
|
|||
} else if value != "" {
|
||||
chatMessage.New().
|
||||
Map(map[string]interface{}{
|
||||
"text": value,
|
||||
"done": true,
|
||||
"assistant_id": ast.ID,
|
||||
"assistant_name": ast.Name,
|
||||
"assistant_avatar": ast.Avatar,
|
||||
"text": value,
|
||||
"done": true,
|
||||
}).
|
||||
Write(c.Writer)
|
||||
}
|
||||
|
|
@ -319,15 +324,29 @@ func (ast *Assistant) streamChat(
|
|||
// saveChatHistory saves the chat history if storage is available
|
||||
func (ast *Assistant) saveChatHistory(ctx chatctx.Context, messages []chatMessage.Message, content *chatMessage.Content) {
|
||||
if len(content.Bytes) > 0 && ctx.Sid != "" && len(messages) > 0 {
|
||||
storage.SaveHistory(
|
||||
ctx.Sid,
|
||||
[]map[string]interface{}{
|
||||
{"role": "user", "content": messages[len(messages)-1].Content(), "name": ctx.Sid},
|
||||
{"role": "assistant", "content": content.String(), "name": ctx.Sid},
|
||||
userMessage := messages[len(messages)-1]
|
||||
data := []map[string]interface{}{
|
||||
{
|
||||
"role": "user",
|
||||
"content": userMessage.Content(),
|
||||
"name": ctx.Sid,
|
||||
},
|
||||
ctx.ChatID,
|
||||
nil,
|
||||
)
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": content.String(),
|
||||
"name": ctx.Sid,
|
||||
"assistant_id": ast.ID,
|
||||
"assistant_name": ast.Name,
|
||||
"assistant_avatar": ast.Avatar,
|
||||
},
|
||||
}
|
||||
|
||||
// Add mentions
|
||||
if userMessage.Mentions != nil {
|
||||
data[0]["mentions"] = userMessage.Mentions
|
||||
}
|
||||
|
||||
storage.SaveHistory(ctx.Sid, data, ctx.ChatID, ctx.Map())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -521,8 +540,6 @@ func (ast *Assistant) withAttachments(ctx context.Context, msg *chatMessage.Mess
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
utils.Dump(contents)
|
||||
return contents, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,15 +16,27 @@ import (
|
|||
|
||||
// Message the message
|
||||
type Message struct {
|
||||
Text string `json:"text,omitempty"` // text content
|
||||
Type string `json:"type,omitempty"` // error, text, plan, table, form, page, file, video, audio, image, markdown, json ...
|
||||
Props map[string]interface{} `json:"props,omitempty"` // props for the types
|
||||
IsDone bool `json:"done,omitempty"`
|
||||
Actions []Action `json:"actions,omitempty"` // Conversation Actions for frontend
|
||||
Attachments []Attachment `json:"attachments,omitempty"` // File attachments
|
||||
Role string `json:"role,omitempty"` // user, assistant, system ...
|
||||
Name string `json:"name,omitempty"` // name for the message
|
||||
Data map[string]interface{} `json:"-"`
|
||||
Text string `json:"text,omitempty"` // text content
|
||||
Type string `json:"type,omitempty"` // error, text, plan, table, form, page, file, video, audio, image, markdown, json ...
|
||||
Props map[string]interface{} `json:"props,omitempty"` // props for the types
|
||||
IsDone bool `json:"done,omitempty"` // Mark as a done message from neo
|
||||
IsNew bool `json:"is_new,omitempty"` // Mark as a new message from neo
|
||||
Actions []Action `json:"actions,omitempty"` // Conversation Actions for frontend
|
||||
Attachments []Attachment `json:"attachments,omitempty"` // File attachments
|
||||
Role string `json:"role,omitempty"` // user, assistant, system ...
|
||||
Name string `json:"name,omitempty"` // name for the message
|
||||
AssistantID string `json:"assistant_id,omitempty"` // assistant_id (for assistant role = assistant )
|
||||
AssistantName string `json:"assistant_name,omitempty"` // assistant_name (for assistant role = assistant )
|
||||
AssistantAvatar string `json:"assistant_avatar,omitempty"` // assistant_avatar (for assistant role = assistant )
|
||||
Mentions []Mention `json:"menions,omitempty"` // Mentions for the message ( for user role = user )
|
||||
Data map[string]interface{} `json:"-"`
|
||||
}
|
||||
|
||||
// Mention represents a mention
|
||||
type Mention struct {
|
||||
ID string `json:"assistant_id"` // assistant_id
|
||||
Name string `json:"name"` // name
|
||||
Avatar string `json:"avatar,omitempty"` // avatar
|
||||
}
|
||||
|
||||
// Attachment represents a file attachment
|
||||
|
|
@ -233,6 +245,23 @@ func (m *Message) Map(msg map[string]interface{}) *Message {
|
|||
if done, ok := msg["done"].(bool); ok {
|
||||
m.IsDone = done
|
||||
}
|
||||
|
||||
if isNew, ok := msg["is_new"].(bool); ok {
|
||||
m.IsNew = isNew
|
||||
}
|
||||
|
||||
if assistantID, ok := msg["assistant_id"].(string); ok {
|
||||
m.AssistantID = assistantID
|
||||
}
|
||||
|
||||
if assistantName, ok := msg["assistant_name"].(string); ok {
|
||||
m.AssistantName = assistantName
|
||||
}
|
||||
|
||||
if assistantAvatar, ok := msg["assistant_avatar"].(string); ok {
|
||||
m.AssistantAvatar = assistantAvatar
|
||||
}
|
||||
|
||||
if actions, ok := msg["actions"].([]interface{}); ok {
|
||||
for _, action := range actions {
|
||||
if v, ok := action.(map[string]interface{}); ok {
|
||||
|
|
|
|||
|
|
@ -141,6 +141,10 @@ func (conv *Xun) initHistoryTable() error {
|
|||
table.String("name", 200).Null().Index()
|
||||
table.Text("content").Null()
|
||||
table.JSON("context").Null()
|
||||
table.String("assistant_id", 200).Null().Index()
|
||||
table.String("assistant_name", 200).Null()
|
||||
table.String("assistant_avatar", 200).Null()
|
||||
table.JSON("mentions").Null()
|
||||
table.TimestampTz("created_at").SetDefaultRaw("NOW()").Index()
|
||||
table.TimestampTz("updated_at").Null().Index()
|
||||
table.TimestampTz("expired_at").Null().Index()
|
||||
|
|
@ -158,7 +162,7 @@ func (conv *Xun) initHistoryTable() error {
|
|||
return err
|
||||
}
|
||||
|
||||
fields := []string{"id", "sid", "cid", "uid", "role", "name", "content", "context", "created_at", "updated_at", "expired_at"}
|
||||
fields := []string{"id", "sid", "cid", "uid", "role", "name", "content", "context", "assistant_id", "assistant_name", "assistant_avatar", "mentions", "created_at", "updated_at", "expired_at"}
|
||||
for _, field := range fields {
|
||||
if !tab.HasColumn(field) {
|
||||
return fmt.Errorf("%s is required", field)
|
||||
|
|
@ -450,7 +454,7 @@ func (conv *Xun) GetHistory(sid string, cid string) ([]map[string]interface{}, e
|
|||
}
|
||||
|
||||
qb := conv.newQuery().
|
||||
Select("role", "name", "content", "context", "uid", "created_at", "updated_at").
|
||||
Select("role", "name", "content", "context", "assistant_id", "assistant_name", "assistant_avatar", "mentions", "uid", "created_at", "updated_at").
|
||||
Where("sid", userID).
|
||||
Where("cid", cid).
|
||||
OrderBy("id", "desc")
|
||||
|
|
@ -472,13 +476,17 @@ func (conv *Xun) GetHistory(sid string, cid string) ([]map[string]interface{}, e
|
|||
res := []map[string]interface{}{}
|
||||
for _, row := range rows {
|
||||
message := map[string]interface{}{
|
||||
"role": row.Get("role"),
|
||||
"name": row.Get("name"),
|
||||
"content": row.Get("content"),
|
||||
"context": row.Get("context"),
|
||||
"uid": row.Get("uid"),
|
||||
"created_at": row.Get("created_at"),
|
||||
"updated_at": row.Get("updated_at"),
|
||||
"role": row.Get("role"),
|
||||
"name": row.Get("name"),
|
||||
"content": row.Get("content"),
|
||||
"context": row.Get("context"),
|
||||
"assistant_id": row.Get("assistant_id"),
|
||||
"assistant_name": row.Get("assistant_name"),
|
||||
"assistant_avatar": row.Get("assistant_avatar"),
|
||||
"mentions": row.Get("mentions"),
|
||||
"uid": row.Get("uid"),
|
||||
"created_at": row.Get("created_at"),
|
||||
"updated_at": row.Get("updated_at"),
|
||||
}
|
||||
res = append([]map[string]interface{}{message}, res...)
|
||||
}
|
||||
|
|
@ -551,23 +559,47 @@ func (conv *Xun) SaveHistory(sid string, messages []map[string]interface{}, cid
|
|||
}
|
||||
}
|
||||
|
||||
// Process mentions if present
|
||||
var mentionsRaw interface{} = nil
|
||||
if mentions, ok := message["mentions"].([]interface{}); ok && len(mentions) > 0 {
|
||||
mentionsRaw, err = jsoniter.MarshalToString(mentions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
value := map[string]interface{}{
|
||||
"role": role,
|
||||
"name": "",
|
||||
"content": content,
|
||||
"sid": userID,
|
||||
"cid": cid,
|
||||
"uid": userID,
|
||||
"context": contextRaw,
|
||||
"created_at": now,
|
||||
"updated_at": nil,
|
||||
"expired_at": expiredAt,
|
||||
"role": role,
|
||||
"name": "",
|
||||
"content": content,
|
||||
"sid": userID,
|
||||
"cid": cid,
|
||||
"uid": userID,
|
||||
"context": contextRaw,
|
||||
"mentions": mentionsRaw,
|
||||
"assistant_id": nil,
|
||||
"assistant_name": nil,
|
||||
"assistant_avatar": nil,
|
||||
"created_at": now,
|
||||
"updated_at": nil,
|
||||
"expired_at": expiredAt,
|
||||
}
|
||||
|
||||
if name, ok := message["name"].(string); ok {
|
||||
value["name"] = name
|
||||
}
|
||||
|
||||
// Add assistant fields if present
|
||||
if assistantID, ok := message["assistant_id"].(string); ok {
|
||||
value["assistant_id"] = assistantID
|
||||
}
|
||||
if assistantName, ok := message["assistant_name"].(string); ok {
|
||||
value["assistant_name"] = assistantName
|
||||
}
|
||||
if assistantAvatar, ok := message["assistant_avatar"].(string); ok {
|
||||
value["assistant_avatar"] = assistantAvatar
|
||||
}
|
||||
|
||||
values = append(values, value)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue