Merge pull request #826 from trheyi/main

Refactor Neo API assistant message handling and content processing
This commit is contained in:
Max 2025-01-22 16:10:06 +08:00 committed by GitHub
commit a8b82248a2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
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: