Enha: address template issues

This commit is contained in:
Grail Finder
2025-12-07 14:17:33 +03:00
parent 4d18d6e730
commit 02bf308452
3 changed files with 123 additions and 0 deletions

87
bot.go
View File

@@ -67,6 +67,80 @@ var (
}
)
// cleanNullMessages removes messages with null or empty content to prevent API issues
func cleanNullMessages(messages []models.RoleMsg) []models.RoleMsg {
cleaned := make([]models.RoleMsg, 0, len(messages))
for _, msg := range messages {
// Include message if it has content or if it's a tool response (which might have tool_call_id)
if msg.HasContent() || msg.ToolCallID != "" {
cleaned = append(cleaned, msg)
}
}
return consolidateConsecutiveAssistantMessages(cleaned)
}
// consolidateConsecutiveAssistantMessages merges consecutive assistant messages into a single message
func consolidateConsecutiveAssistantMessages(messages []models.RoleMsg) []models.RoleMsg {
if len(messages) == 0 {
return messages
}
consolidated := make([]models.RoleMsg, 0, len(messages))
currentAssistantMsg := models.RoleMsg{}
isBuildingAssistantMsg := false
for i := 0; i < len(messages); i++ {
msg := messages[i]
if msg.Role == cfg.AssistantRole || msg.Role == cfg.WriteNextMsgAsCompletionAgent {
// If this is an assistant message, start or continue building
if !isBuildingAssistantMsg {
// Start accumulating assistant message
currentAssistantMsg = msg.Copy()
isBuildingAssistantMsg = true
} else {
// Continue accumulating - append content to the current assistant message
if currentAssistantMsg.IsContentParts() || msg.IsContentParts() {
// Handle structured content
if !currentAssistantMsg.IsContentParts() {
// Convert existing content to content parts
currentAssistantMsg = models.NewMultimodalMsg(currentAssistantMsg.Role, []interface{}{models.TextContentPart{Type: "text", Text: currentAssistantMsg.Content}})
currentAssistantMsg.ToolCallID = msg.ToolCallID
}
if msg.IsContentParts() {
currentAssistantMsg.ContentParts = append(currentAssistantMsg.ContentParts, msg.GetContentParts()...)
} else if msg.Content != "" {
currentAssistantMsg.AddTextPart(msg.Content)
}
} else {
// Simple string content
if currentAssistantMsg.Content != "" {
currentAssistantMsg.Content += "\n" + msg.Content
} else {
currentAssistantMsg.Content = msg.Content
}
}
}
} else {
// This is not an assistant message
// If we were building an assistant message, add it to the result
if isBuildingAssistantMsg {
consolidated = append(consolidated, currentAssistantMsg)
isBuildingAssistantMsg = false
}
// Add the non-assistant message
consolidated = append(consolidated, msg)
}
}
// Don't forget the last assistant message if we were building one
if isBuildingAssistantMsg {
consolidated = append(consolidated, currentAssistantMsg)
}
return consolidated
}
// GetLogLevel returns the current log level as a string
func GetLogLevel() string {
level := logLevel.Level()
@@ -481,6 +555,10 @@ out:
Role: botPersona, Content: respText.String(),
})
}
// Clean null/empty messages to prevent API issues with endpoints like llama.cpp jinja template
cleanChatBody()
colorText()
updateStatusLine()
// bot msg is done;
@@ -492,6 +570,15 @@ out:
findCall(respText.String(), toolResp.String(), tv)
}
// cleanChatBody removes messages with null or empty content to prevent API issues
func cleanChatBody() {
if chatBody != nil && chatBody.Messages != nil {
originalLen := len(chatBody.Messages)
chatBody.Messages = cleanNullMessages(chatBody.Messages)
logger.Debug("cleaned chat body", "original_len", originalLen, "new_len", len(chatBody.Messages))
}
}
func findCall(msg, toolCall string, tv *tview.TextView) {
fc := &models.FuncCall{}
if toolCall != "" {

4
llm.go
View File

@@ -236,6 +236,8 @@ func (op LCPChat) FormMsg(msg, role string, resume bool) (io.Reader, error) {
bodyCopy.Messages[i] = msg
}
}
// Clean null/empty messages to prevent API issues
bodyCopy.Messages = cleanNullMessages(bodyCopy.Messages)
req := models.OpenAIReq{
ChatBody: bodyCopy,
Tools: nil,
@@ -385,6 +387,8 @@ func (ds DeepSeekerChat) FormMsg(msg, role string, resume bool) (io.Reader, erro
bodyCopy.Messages[i] = msg
}
}
// Clean null/empty messages to prevent API issues
bodyCopy.Messages = cleanNullMessages(bodyCopy.Messages)
dsBody := models.NewDSChatReq(*bodyCopy)
data, err := json.Marshal(dsBody)
if err != nil {

View File

@@ -230,6 +230,38 @@ func NewMultimodalMsg(role string, contentParts []interface{}) RoleMsg {
}
}
// HasContent returns true if the message has either string content or structured content parts
func (m RoleMsg) HasContent() bool {
if m.Content != "" {
return true
}
if m.hasContentParts && len(m.ContentParts) > 0 {
return true
}
return false
}
// IsContentParts returns true if the message uses structured content parts
func (m RoleMsg) IsContentParts() bool {
return m.hasContentParts
}
// GetContentParts returns the content parts of the message
func (m RoleMsg) GetContentParts() []interface{} {
return m.ContentParts
}
// Copy creates a copy of the RoleMsg with all fields
func (m RoleMsg) Copy() RoleMsg {
return RoleMsg{
Role: m.Role,
Content: m.Content,
ContentParts: m.ContentParts,
ToolCallID: m.ToolCallID,
hasContentParts: m.hasContentParts,
}
}
// AddTextPart adds a text content part to the message
func (m *RoleMsg) AddTextPart(text string) {
if !m.hasContentParts {