summaryrefslogtreecommitdiff
path: root/format_rich_log.go
blob: ddf75bf6b0701248cd7d8a73f24a537d23b2156d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"go.wit.com/lib/protobuf/chatpb"
	"go.wit.com/log"
)

const termWidth = 120 // The target width for the formatted output boxes.

func formatRichLog(filename string) *chatpb.Chats {
	data, err := os.ReadFile(filename)
	if err != nil {
		log.Fatalf("Error reading file %s: %v", filename, err)
	}

	logData, err := chatpb.UnmarshalChatsTEXT(data)
	if err != nil {
		log.Fatalf("Error unmarshaling log file %s: %v", filename, err)
	}

	for _, chat := range logData.GetChats() {
		author := chat.GetFrom().String()

		// Handle content: prefer content_file, fallback to content.
		var content string
		if contentFile := chat.GetContentFile(); contentFile != "" {
			// Construct the full path relative to the log file's directory.
			logDir := filepath.Dir(filename)
			contentPath := filepath.Join(logDir, contentFile)

			contentBytes, err := os.ReadFile(contentPath)
			if err != nil {
				content = fmt.Sprintf("---	ERROR: Could not read content file %s: %v ---", contentPath, err)
			} else {
				content = string(contentBytes)
			}
		} else {
			// Fallback for older log formats.
			content = chat.GetContent()
		}

		// Print the conversational content first.
		if content != "" {
			// Trim trailing newlines for cleaner output.
			fmt.Printf("✦ %s: %s\n", author, strings.TrimSpace(content))
		}

		// Now, format and print any tool calls.
		for _, toolCall := range chat.GetToolCalls() {
			printToolCallBox(toolCall)
		}
	}

	return logData
}

// printToolCallBox handles the decorative formatting for a single tool call.
func printToolCallBox(tc *chatpb.ToolCall) {
	boxWidth := termWidth - 2 // Account for the side borders.

	// --- Top Border ---
	fmt.Printf(" ╭%s╮\n", strings.Repeat("─", boxWidth))

	// --- Header Line ---
	header := fmt.Sprintf(" ✔  %s %s (%s)", tc.GetName(), tc.GetInput(), tc.GetDescription())
	printWrappedLine(header, boxWidth)
	printEmptyLine(boxWidth)

	// --- Stdout ---
	if stdout := tc.GetOutputStdout(); stdout != "" {
		for _, line := range strings.Split(stdout, "\n") {
			printWrappedLine("    "+line, boxWidth)
		}
	}

	// --- Stderr ---
	if stderr := tc.GetOutputStderr(); stderr != "" {
		for _, line := range strings.Split(stderr, "\n") {
			printWrappedLine("    "+line, boxWidth)
		}
	}

	printEmptyLine(boxWidth)

	// --- Bottom Border ---
	fmt.Printf(" ╰%s╯\n", strings.Repeat("─", boxWidth))
}

// printWrappedLine prints a line of text, wrapping it if it's too long.
func printWrappedLine(text string, width int) {
	if len(text) == 0 {
		printEmptyLine(width)
		return
	}

	// Simple wrapping logic.
	for len(text) > width {
		fmt.Printf(" │ %-*s │\n", width, text[:width])
		text = text[width:]
	}
	fmt.Printf(" │ %-*s │\n", width, text)
}

// printEmptyLine prints a blank line within the box.
func printEmptyLine(width int) {
	fmt.Printf(" │ %*s │\n", width, "")
}