Refactor Neo API assistant message handling and content processing

- Enhanced message generation by introducing NewHistory and NewContent methods for improved handling of message content and history.
- Updated the withHistory method to utilize the new NewHistory method, streamlining the addition of historical messages.
- Improved the call method to conditionally save message history based on input parameters, enhancing flexibility in message management.
- Refactored the String method in the Message struct to better represent message types and properties, improving serialization consistency.

These changes enhance the maintainability and robustness of the Neo API assistant, paving the way for improved message handling and content processing.
This commit is contained in:
Max 2025-01-22 16:09:01 +08:00
parent 1eba627e38
commit 1404c549a7
4 changed files with 121 additions and 15 deletions

View file

@ -387,7 +387,11 @@ func (ast *Assistant) withHistory(ctx chatctx.Context, input string) ([]chatMess
// Add history messages // Add history messages
for _, h := range history { for _, h := range history {
messages = append(messages, *chatMessage.New().Map(h)) msgs, err := chatMessage.NewHistory(h)
if err != nil {
return nil, err
}
messages = append(messages, msgs...)
} }
} }
@ -416,6 +420,7 @@ func (ast *Assistant) Chat(ctx context.Context, messages []chatMessage.Message,
} }
func (ast *Assistant) requestMessages(ctx context.Context, messages []chatMessage.Message) ([]map[string]interface{}, error) { func (ast *Assistant) requestMessages(ctx context.Context, messages []chatMessage.Message) ([]map[string]interface{}, error) {
newMessages := []map[string]interface{}{} newMessages := []map[string]interface{}{}
length := len(messages) length := len(messages)
@ -425,7 +430,7 @@ func (ast *Assistant) requestMessages(ctx context.Context, messages []chatMessag
return nil, fmt.Errorf("role must be string") return nil, fmt.Errorf("role must be string")
} }
content := message.Text content := message.String()
if content == "" { if content == "" {
return nil, fmt.Errorf("content must be string") return nil, fmt.Errorf("content must be string")
} }

View file

@ -276,6 +276,12 @@ func (ast *Assistant) call(ctx context.Context, method string, c *gin.Context, c
return bridge.JsException(info.Context(), err.Error()) return bridge.JsException(info.Context(), err.Error())
} }
// Save history by default
saveHistory := true
if len(args) > 1 && args[1].IsBoolean() {
saveHistory = args[1].Boolean()
}
switch v := input.(type) { switch v := input.(type) {
case string: case string:
// Check if the message is json // Check if the message is json
@ -285,13 +291,17 @@ func (ast *Assistant) call(ctx context.Context, method string, c *gin.Context, c
} }
// Append the message to the contents // Append the message to the contents
msg.AppendTo(contents) if saveHistory {
msg.AppendTo(contents)
}
msg.Write(c.Writer) msg.Write(c.Writer)
return nil return nil
case map[string]interface{}: case map[string]interface{}:
msg := message.New().Map(v) msg := message.New().Map(v)
msg.AppendTo(contents) if saveHistory {
msg.AppendTo(contents)
}
msg.Write(c.Writer) msg.Write(c.Writer)
return nil return nil

View file

@ -65,6 +65,73 @@ func New() *Message {
return &Message{Actions: []Action{}, Props: map[string]interface{}{}} return &Message{Actions: []Action{}, Props: map[string]interface{}{}}
} }
// NewHistory create a new message from history
func NewHistory(history map[string]interface{}) ([]Message, error) {
if history == nil {
return []Message{}, nil
}
var copy map[string]interface{} = map[string]interface{}{}
for key, value := range history {
if key != "content" {
copy[key] = value
}
}
globalMessage := New().Map(copy)
messages := []Message{}
if content, ok := history["content"].(string); ok {
if strings.HasPrefix(content, "{") && strings.HasSuffix(content, "}") {
var msg Message = *globalMessage
if err := jsoniter.UnmarshalFromString(content, &msg); err != nil {
return nil, err
}
messages = append(messages, msg)
} else if strings.HasPrefix(content, "[") && strings.HasSuffix(content, "]") {
var msgs []Message
if err := jsoniter.UnmarshalFromString(content, &msgs); err != nil {
return nil, err
}
for _, msg := range msgs {
msg.AssistantID = globalMessage.AssistantID
msg.AssistantName = globalMessage.AssistantName
msg.AssistantAvatar = globalMessage.AssistantAvatar
msg.Role = globalMessage.Role
msg.Name = globalMessage.Name
msg.Mentions = globalMessage.Mentions
messages = append(messages, msg)
}
} else {
messages = append(messages, Message{Text: content})
}
}
return messages, nil
}
// NewContent create a new message from content
func NewContent(content string) ([]Message, error) {
messages := []Message{}
if strings.HasPrefix(content, "{") && strings.HasSuffix(content, "}") {
var msg Message
if err := jsoniter.UnmarshalFromString(content, &msg); err != nil {
return nil, err
}
messages = append(messages, msg)
} else if strings.HasPrefix(content, "[") && strings.HasSuffix(content, "]") {
var msgs []Message
if err := jsoniter.UnmarshalFromString(content, &msgs); err != nil {
return nil, err
}
for _, msg := range msgs {
messages = append(messages, msg)
}
} else {
messages = append(messages, Message{Text: content})
}
return messages, nil
}
// NewString create a new message from string // NewString create a new message from string
func NewString(content string) (*Message, error) { func NewString(content string) (*Message, error) {
if strings.HasPrefix(content, "{") && strings.HasSuffix(content, "}") { if strings.HasPrefix(content, "{") && strings.HasSuffix(content, "}") {
@ -86,10 +153,6 @@ func NewOpenAI(data []byte) *Message {
msg := New() msg := New()
text := string(data) text := string(data)
data = []byte(strings.TrimPrefix(text, "data: ")) data = []byte(strings.TrimPrefix(text, "data: "))
// fmt.Println("--------------------------------")
// fmt.Println(string(data))
// fmt.Println("--------------------------------")
switch { switch {
case strings.Contains(text, `"delta":{`) && strings.Contains(text, `"tool_calls"`): case strings.Contains(text, `"delta":{`) && strings.Contains(text, `"tool_calls"`):
@ -138,10 +201,18 @@ func NewOpenAI(data []byte) *Message {
// String returns the string representation // String returns the string representation
func (m *Message) String() string { func (m *Message) String() string {
if m.Text != "" { typ := m.Type
return m.Text if typ == "" {
typ = "text"
}
switch typ {
case "text":
return m.Text
default:
raw, _ := jsoniter.MarshalToString(map[string]interface{}{"type": m.Type, "props": m.Props})
return raw
} }
return ""
} }
// SetText set the text // SetText set the text
@ -222,7 +293,7 @@ func (m *Message) AppendTo(contents *Contents) *Message {
contents.AppendFunction([]byte(m.Text)) contents.AppendFunction([]byte(m.Text))
return m return m
case "loading": case "loading", "error": // Ignore loading and error messages
return m return m
default: default:
@ -307,6 +378,11 @@ func (m *Message) Map(msg map[string]interface{}) *Message {
if assistantID, ok := msg["assistant_id"].(string); ok { if assistantID, ok := msg["assistant_id"].(string); ok {
m.AssistantID = assistantID m.AssistantID = assistantID
// Set name
if m.Role == "assistant" {
m.Name = m.AssistantID
}
} }
if assistantName, ok := msg["assistant_name"].(string); ok { if assistantName, ok := msg["assistant_name"].(string); ok {

View file

@ -104,10 +104,25 @@ func (neo *DSL) GenerateWithAI(ctx chatctx.Context, input string, messageType st
// Chat with AI in background // Chat with AI in background
go func() { go func() {
msgList := make([]message.Message, len(messages)) msgList := []message.Message{}
for i, msg := range messages { for _, vv := range messages {
msgList[i] = *message.New().Map(msg) msg := message.New().Map(vv)
if content, ok := vv["content"].(string); ok {
msgs, err := message.NewContent(content)
if err == nil {
for _, v := range msgs {
v.AssistantID = msg.AssistantID
v.AssistantName = msg.AssistantName
v.AssistantAvatar = msg.AssistantAvatar
v.Role = msg.Role
v.Name = msg.Name
v.Mentions = msg.Mentions
msgList = append(msgList, v)
}
}
}
} }
err := ast.Chat(c.Request.Context(), msgList, neo.Option, func(data []byte) int { err := ast.Chat(c.Request.Context(), msgList, neo.Option, func(data []byte) int {
select { select {
case <-clientBreak: case <-clientBreak: