package transcript import ( "bufio" "bytes" "encoding/json" "errors" "io" "os" "strings" "unicode/utf8" ) const tailWindow = 256 * 1024 // LastMessages reads a JSONL transcript and returns the last user and assistant // messages, each truncated to maxLen runes. func LastMessages(path string, maxLen int) (lastUser, lastAgent string, err error) { f, err := os.Open(path) if err != nil { if os.IsNotExist(err) { return "", "", err } return "", "", err } defer f.Close() info, err := f.Stat() if err != nil { return "", "", err } size := info.Size() var r io.Reader = f if size > tailWindow { start := size - tailWindow if _, err := f.Seek(start, io.SeekStart); err != nil { return "", "", err } tail := make([]byte, tailWindow) n, err := io.ReadFull(f, tail) if err != nil && !errors.Is(err, io.ErrUnexpectedEOF) { return "", "", err } tail = tail[:n] if i := bytes.IndexByte(tail, '\n'); i >= 0 { tail = tail[i+1:] } r = bytes.NewReader(tail) } sc := bufio.NewScanner(r) sc.Buffer(make([]byte, 0, 64*1024), 1024*1024) for sc.Scan() { line := strings.TrimSpace(sc.Text()) if line == "" { continue } var row struct { Role string `json:"role"` Type string `json:"type"` Content string `json:"content"` } if err := json.Unmarshal([]byte(line), &row); err != nil { continue } role := row.Role if role == "" { role = row.Type } switch role { case "user": lastUser = row.Content case "assistant": lastAgent = row.Content } } if err := sc.Err(); err != nil { return "", "", err } lastUser = truncateRunes(lastUser, maxLen) lastAgent = truncateRunes(lastAgent, maxLen) return lastUser, lastAgent, nil } func truncateRunes(s string, maxLen int) string { if maxLen <= 0 || utf8.RuneCountInString(s) <= maxLen { return s } runes := []rune(s) return string(runes[:maxLen]) }