fix(chatpb): Update AddFile to support nested chat entries

- The AddFile function now correctly parses both the old flat log
  format and the new format where chats are grouped by topic.
- It flattens the new structure by iterating through the 'Entries'
  of a chat group and appending them to the main chat list.
- This allows the application to transparently load both old and
  new log files.
This commit is contained in:
Castor Gemini 2025-08-22 02:14:17 -05:00 committed by Jeff Carr
parent 80090152a1
commit 3b6b85200d
1 changed files with 84 additions and 22 deletions

View File

@ -48,6 +48,14 @@ func UnmarshalChatsTEXT(data []byte) (*Chats, error) {
} }
func (all *Chats) AddFile(filename string) error { func (all *Chats) AddFile(filename string) error {
// Nil checks for safety.
if all == nil {
return fmt.Errorf("cannot call AddFile on a nil *Chats object")
}
if all.Chats == nil {
all.Chats = make([]*Chat, 0)
}
data, err := os.ReadFile(filename) data, err := os.ReadFile(filename)
if err != nil { if err != nil {
log.Fatalf("Error reading file %s: %v", filename, err) log.Fatalf("Error reading file %s: %v", filename, err)
@ -60,17 +68,74 @@ func (all *Chats) AddFile(filename string) error {
return err return err
} }
for _, chat := range logData.GetChats() { // Iterate through the top-level messages from the source file.
// make a copy for _, chatGroup := range logData.GetChats() {
newc := proto.Clone(chat).(*Chat) if len(chatGroup.GetEntries()) > 0 {
// NEW FORMAT: This is a group, process its entries.
for _, entry := range chatGroup.GetEntries() {
// Convert the ChatEntry into a new Chat object for the flat list.
newChat := convertEntryToChat(entry, filename)
if newChat != nil {
newChat.VerifyUuid()
all.AppendByUuid(newChat)
}
}
} else {
// OLD FORMAT: This is a single, flat Chat message.
// We still process it to handle its external content file correctly.
newChat := convertChatToChat(chatGroup, filename)
if newChat != nil {
newChat.VerifyUuid()
all.AppendByUuid(newChat)
}
}
}
return nil
}
// convertChatToChat handles an old-style Chat message. It creates a clean
// copy and resolves its external content file.
func convertChatToChat(chat *Chat, filename string) *Chat {
if chat == nil {
return nil
}
// Manually create a ChatEntry from the Chat fields to reuse the logic.
entry := &ChatEntry{
From: chat.GetFrom(),
Ctime: chat.GetCtime(),
Content: chat.GetContent(),
Table: chat.GetTable(),
ToolCalls: chat.GetToolCalls(),
ContentFile: chat.GetContentFile(),
Uuid: chat.GetUuid(),
Snippets: chat.GetSnippets(),
}
return convertEntryToChat(entry, filename)
}
// convertEntryToChat creates a new Chat object from a ChatEntry's data
// and resolves its external content file.
func convertEntryToChat(entry *ChatEntry, filename string) *Chat {
if entry == nil {
return nil
}
// Create a new Chat object and copy the fields.
newChat := &Chat{
From: entry.GetFrom(),
Ctime: entry.GetCtime(),
Table: entry.GetTable(),
ToolCalls: entry.GetToolCalls(),
Snippets: entry.GetSnippets(),
Uuid: entry.GetUuid(),
}
// Handle content: prefer content_file, fallback to content. // Handle content: prefer content_file, fallback to content.
var content string var content string
if contentFile := chat.GetContentFile(); contentFile != "" { if contentFile := entry.GetContentFile(); contentFile != "" {
// Construct the full path relative to the log file's directory.
logDir := filepath.Dir(filename) logDir := filepath.Dir(filename)
contentPath := filepath.Join(logDir, contentFile) contentPath := filepath.Join(logDir, contentFile)
contentBytes, err := os.ReadFile(contentPath) contentBytes, err := os.ReadFile(contentPath)
if err != nil { if err != nil {
content = fmt.Sprintf("--- ERROR: Could not read content file %s: %v ---", contentPath, err) content = fmt.Sprintf("--- ERROR: Could not read content file %s: %v ---", contentPath, err)
@ -78,17 +143,14 @@ func (all *Chats) AddFile(filename string) error {
content = string(contentBytes) content = string(contentBytes)
} }
} else { } else {
// Fallback for older log formats. content = entry.GetContent()
content = chat.GetContent()
}
newc.Content = content
newc.VerifyUuid()
all.AppendNew(newc)
} }
newChat.Content = content
return nil return newChat
} }
func (chats *Chats) VerifyUuids() bool { func (chats *Chats) VerifyUuids() bool {
var changed bool var changed bool