RenderTranscript renders a previous session as a readable transcript. When the rendered text would exceed maxChars, the oldest messages are dropped (the most recent ones are the most useful for continuing work) and a note records how many were omitted. maxChars <= 0 selects DefaultMaxChars.
(h Header, msgs []chat.Message, maxChars int)
| 133 | // (the most recent ones are the most useful for continuing work) and a note |
| 134 | // records how many were omitted. maxChars <= 0 selects DefaultMaxChars. |
| 135 | func RenderTranscript(h Header, msgs []chat.Message, maxChars int) string { |
| 136 | if maxChars <= 0 { |
| 137 | maxChars = DefaultMaxChars |
| 138 | } |
| 139 | |
| 140 | var head strings.Builder |
| 141 | title := strings.TrimSpace(h.Title) |
| 142 | if title == "" { |
| 143 | title = "(untitled)" |
| 144 | } |
| 145 | fmt.Fprintf(&head, "# Session %s — %s\n", h.ID, title) |
| 146 | if !h.CreatedAt.IsZero() { |
| 147 | fmt.Fprintf(&head, "Created: %s\n", h.CreatedAt.Format(time.RFC3339)) |
| 148 | } |
| 149 | fmt.Fprintf(&head, "Messages: %d\n", h.NumMessages) |
| 150 | |
| 151 | blocks := make([]string, 0, len(msgs)) |
| 152 | for i := range msgs { |
| 153 | if b := renderMessage(msgs[i]); b != "" { |
| 154 | blocks = append(blocks, b) |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | // Keep the most recent blocks that fit in the remaining budget. The header |
| 159 | // always stays; the omission note (when needed) is counted against the |
| 160 | // budget so the final string respects maxChars. |
| 161 | budget := maxChars - head.Len() |
| 162 | kept := 0 |
| 163 | used := 0 |
| 164 | for _, b := range slices.Backward(blocks) { |
| 165 | cost := len(b) + 1 // +1 for the joining newline |
| 166 | if used+cost > budget && kept > 0 { |
| 167 | break |
| 168 | } |
| 169 | used += cost |
| 170 | kept++ |
| 171 | } |
| 172 | omitted := len(blocks) - kept |
| 173 | |
| 174 | var out strings.Builder |
| 175 | out.WriteString(head.String()) |
| 176 | if omitted > 0 { |
| 177 | fmt.Fprintf(&out, "\n[%d earlier message(s) omitted to fit the context budget; showing the most recent %d]\n", omitted, kept) |
| 178 | } |
| 179 | for _, b := range blocks[len(blocks)-kept:] { |
| 180 | out.WriteString("\n") |
| 181 | out.WriteString(b) |
| 182 | out.WriteString("\n") |
| 183 | } |
| 184 | return out.String() |
| 185 | } |
| 186 | |
| 187 | // renderMessage turns one chat message into a transcript block, or "" when it |
| 188 | // carries nothing worth showing (e.g. an empty implicit message). |