(builder *strings.Builder, msg session.Message)
| 34 | } |
| 35 | |
| 36 | func writeUserMessage(builder *strings.Builder, msg session.Message) { |
| 37 | builder.WriteString("\n## User\n") |
| 38 | |
| 39 | // When MultiContent is present, render text and attachment chips. |
| 40 | // The text part reflects what was actually sent to the model (the user's |
| 41 | // original typed text, plus any dimension notes from image resizing and |
| 42 | // inlined file headers from ReadFileForInline). This is intentional: a |
| 43 | // transcript should show what the model received, not just what the user |
| 44 | // typed into the prompt box. Callers wanting just the raw typed text |
| 45 | // should read msg.Message.Content directly. |
| 46 | if len(msg.Message.MultiContent) > 0 { |
| 47 | for _, part := range msg.Message.MultiContent { |
| 48 | switch part.Type { |
| 49 | case chat.MessagePartTypeText: |
| 50 | if part.Text != "" { |
| 51 | fmt.Fprintf(builder, "\n%s\n", part.Text) |
| 52 | } |
| 53 | case chat.MessagePartTypeDocument: |
| 54 | if part.Document != nil { |
| 55 | doc := part.Document |
| 56 | switch { |
| 57 | case chat.IsImageMimeType(doc.MimeType): |
| 58 | size := doc.Size |
| 59 | if size == 0 { |
| 60 | size = int64(len(doc.Source.InlineData)) |
| 61 | } |
| 62 | fmt.Fprintf(builder, "\n[image: %s (%s, %s)]\n", doc.Name, doc.MimeType, formatBytes(size)) |
| 63 | case doc.Source.InlineText != "": |
| 64 | fmt.Fprintf(builder, "\n[attachment: %s (%s)]\n", doc.Name, doc.MimeType) |
| 65 | default: |
| 66 | size := doc.Size |
| 67 | if size == 0 { |
| 68 | size = int64(len(doc.Source.InlineData)) |
| 69 | } |
| 70 | fmt.Fprintf(builder, "\n[attachment: %s (%s, %s)]\n", doc.Name, doc.MimeType, formatBytes(size)) |
| 71 | } |
| 72 | } |
| 73 | // Note: superseded types kept for backward-compat with stored sessions. |
| 74 | case chat.MessagePartTypeImageURL: |
| 75 | if part.ImageURL != nil { |
| 76 | fmt.Fprintf(builder, "\n[image: %s]\n", part.ImageURL.URL[:min(len(part.ImageURL.URL), 60)]) |
| 77 | } |
| 78 | case chat.MessagePartTypeFile: |
| 79 | if part.File != nil { |
| 80 | fmt.Fprintf(builder, "\n[file: %s]\n", part.File.Path) |
| 81 | } |
| 82 | } |
| 83 | } |
| 84 | return |
| 85 | } |
| 86 | fmt.Fprintf(builder, "\n%s\n", msg.Message.Content) |
| 87 | } |
| 88 | |
| 89 | // formatBytes returns a human-readable byte size string (e.g. "1.2 MB"). |
| 90 | func formatBytes(b int64) string { |
no test coverage detected