content_test.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. package message
  2. import (
  3. "encoding/base64"
  4. "fmt"
  5. "strings"
  6. "testing"
  7. "charm.land/fantasy"
  8. "github.com/stretchr/testify/require"
  9. )
  10. func makeTestAttachments(n int, contentSize int) []Attachment {
  11. attachments := make([]Attachment, n)
  12. content := []byte(strings.Repeat("x", contentSize))
  13. for i := range n {
  14. attachments[i] = Attachment{
  15. FilePath: fmt.Sprintf("/path/to/file%d.txt", i),
  16. MimeType: "text/plain",
  17. Content: content,
  18. }
  19. }
  20. return attachments
  21. }
  22. func TestToAIMessage_CorruptedMediaData(t *testing.T) {
  23. t.Parallel()
  24. msg := &Message{
  25. Role: Tool,
  26. Parts: []ContentPart{
  27. ToolResult{
  28. ToolCallID: "call_123",
  29. Name: "screenshot",
  30. Content: "Loaded image/png content",
  31. Data: "abc\x80def",
  32. MIMEType: "image/png",
  33. },
  34. },
  35. }
  36. messages := msg.ToAIMessage()
  37. require.Len(t, messages, 1)
  38. require.Len(t, messages[0].Content, 1)
  39. part, ok := messages[0].Content[0].(fantasy.ToolResultPart)
  40. require.True(t, ok)
  41. require.Equal(t, "call_123", part.ToolCallID)
  42. textContent, ok := part.Output.(fantasy.ToolResultOutputContentText)
  43. require.True(t, ok, "corrupted media should be downgraded to text")
  44. require.Equal(t, mediaLoadFailedPlaceholder, textContent.Text)
  45. }
  46. func TestToAIMessage_ValidMediaData(t *testing.T) {
  47. t.Parallel()
  48. validBase64 := base64.StdEncoding.EncodeToString([]byte{0x89, 0x50, 0x4E, 0x47})
  49. msg := &Message{
  50. Role: Tool,
  51. Parts: []ContentPart{
  52. ToolResult{
  53. ToolCallID: "call_456",
  54. Name: "screenshot",
  55. Content: "Loaded image/png content",
  56. Data: validBase64,
  57. MIMEType: "image/png",
  58. },
  59. },
  60. }
  61. messages := msg.ToAIMessage()
  62. require.Len(t, messages, 1)
  63. require.Len(t, messages[0].Content, 1)
  64. part, ok := messages[0].Content[0].(fantasy.ToolResultPart)
  65. require.True(t, ok)
  66. require.Equal(t, "call_456", part.ToolCallID)
  67. mediaContent, ok := part.Output.(fantasy.ToolResultOutputContentMedia)
  68. require.True(t, ok, "valid media should remain as media")
  69. require.Equal(t, validBase64, mediaContent.Data)
  70. require.Equal(t, "image/png", mediaContent.MediaType)
  71. }
  72. func TestToAIMessage_ASCIIButInvalidBase64(t *testing.T) {
  73. t.Parallel()
  74. msg := &Message{
  75. Role: Tool,
  76. Parts: []ContentPart{
  77. ToolResult{
  78. ToolCallID: "call_789",
  79. Name: "screenshot",
  80. Content: "Loaded image/png content",
  81. Data: "not-valid-base64!!!",
  82. MIMEType: "image/png",
  83. },
  84. },
  85. }
  86. messages := msg.ToAIMessage()
  87. require.Len(t, messages, 1)
  88. require.Len(t, messages[0].Content, 1)
  89. part, ok := messages[0].Content[0].(fantasy.ToolResultPart)
  90. require.True(t, ok)
  91. require.Equal(t, "call_789", part.ToolCallID)
  92. textContent, ok := part.Output.(fantasy.ToolResultOutputContentText)
  93. require.True(t, ok, "ASCII but invalid base64 should be downgraded to text")
  94. require.Equal(t, mediaLoadFailedPlaceholder, textContent.Text)
  95. }
  96. func BenchmarkPromptWithTextAttachments(b *testing.B) {
  97. cases := []struct {
  98. name string
  99. numFiles int
  100. contentSize int
  101. }{
  102. {"1file_100bytes", 1, 100},
  103. {"5files_1KB", 5, 1024},
  104. {"10files_10KB", 10, 10 * 1024},
  105. {"20files_50KB", 20, 50 * 1024},
  106. }
  107. for _, tc := range cases {
  108. attachments := makeTestAttachments(tc.numFiles, tc.contentSize)
  109. prompt := "Process these files"
  110. b.Run(tc.name, func(b *testing.B) {
  111. b.ReportAllocs()
  112. for range b.N {
  113. _ = PromptWithTextAttachments(prompt, attachments)
  114. }
  115. })
  116. }
  117. }