diff --git a/neo/assistant/api.go b/neo/assistant/api.go index e6fa0298..142e86ee 100644 --- a/neo/assistant/api.go +++ b/neo/assistant/api.go @@ -387,7 +387,11 @@ func (ast *Assistant) withHistory(ctx chatctx.Context, input string) ([]chatMess // Add history messages 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) { + newMessages := []map[string]interface{}{} length := len(messages) @@ -425,7 +430,7 @@ func (ast *Assistant) requestMessages(ctx context.Context, messages []chatMessag return nil, fmt.Errorf("role must be string") } - content := message.Text + content := message.String() if content == "" { return nil, fmt.Errorf("content must be string") } diff --git a/neo/assistant/hooks.go b/neo/assistant/hooks.go index dcfa2c67..fa8171cd 100644 --- a/neo/assistant/hooks.go +++ b/neo/assistant/hooks.go @@ -276,6 +276,12 @@ func (ast *Assistant) call(ctx context.Context, method string, c *gin.Context, c 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) { case string: // 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 - msg.AppendTo(contents) + if saveHistory { + msg.AppendTo(contents) + } msg.Write(c.Writer) return nil case map[string]interface{}: msg := message.New().Map(v) - msg.AppendTo(contents) + if saveHistory { + msg.AppendTo(contents) + } msg.Write(c.Writer) return nil diff --git a/neo/message/message.go b/neo/message/message.go index a659d7a6..a820f65f 100644 --- a/neo/message/message.go +++ b/neo/message/message.go @@ -65,6 +65,73 @@ func New() *Message { 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 func NewString(content string) (*Message, error) { if strings.HasPrefix(content, "{") && strings.HasSuffix(content, "}") { @@ -86,10 +153,6 @@ func NewOpenAI(data []byte) *Message { msg := New() text := string(data) data = []byte(strings.TrimPrefix(text, "data: ")) - // fmt.Println("--------------------------------") - // fmt.Println(string(data)) - // fmt.Println("--------------------------------") - switch { case strings.Contains(text, `"delta":{`) && strings.Contains(text, `"tool_calls"`): @@ -138,10 +201,18 @@ func NewOpenAI(data []byte) *Message { // String returns the string representation func (m *Message) String() string { - if m.Text != "" { - return m.Text + typ := m.Type + 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 @@ -222,7 +293,7 @@ func (m *Message) AppendTo(contents *Contents) *Message { contents.AppendFunction([]byte(m.Text)) return m - case "loading": + case "loading", "error": // Ignore loading and error messages return m default: @@ -307,6 +378,11 @@ func (m *Message) Map(msg map[string]interface{}) *Message { if assistantID, ok := msg["assistant_id"].(string); ok { m.AssistantID = assistantID + + // Set name + if m.Role == "assistant" { + m.Name = m.AssistantID + } } if assistantName, ok := msg["assistant_name"].(string); ok { diff --git a/neo/neo.go b/neo/neo.go index 89cae336..5d64ffa6 100644 --- a/neo/neo.go +++ b/neo/neo.go @@ -104,10 +104,25 @@ func (neo *DSL) GenerateWithAI(ctx chatctx.Context, input string, messageType st // Chat with AI in background go func() { - msgList := make([]message.Message, len(messages)) - for i, msg := range messages { - msgList[i] = *message.New().Map(msg) + msgList := []message.Message{} + for _, vv := range messages { + 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 { select { case <-clientBreak: