prompt_test.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. package server
  2. import (
  3. "bytes"
  4. "context"
  5. "strings"
  6. "testing"
  7. "github.com/ollama/ollama/api"
  8. "github.com/ollama/ollama/template"
  9. )
  10. func tokenize(_ context.Context, s string) (tokens []int, err error) {
  11. for range strings.Fields(s) {
  12. tokens = append(tokens, len(tokens))
  13. }
  14. return
  15. }
  16. func TestChatPrompt(t *testing.T) {
  17. type expect struct {
  18. prompt string
  19. images [][]byte
  20. }
  21. cases := []struct {
  22. name string
  23. limit int
  24. msgs []api.Message
  25. expect
  26. }{
  27. {
  28. name: "messages",
  29. limit: 64,
  30. msgs: []api.Message{
  31. {Role: "user", Content: "You're a test, Harry!"},
  32. {Role: "assistant", Content: "I-I'm a what?"},
  33. {Role: "user", Content: "A test. And a thumping good one at that, I'd wager."},
  34. },
  35. expect: expect{
  36. prompt: "You're a test, Harry! I-I'm a what? A test. And a thumping good one at that, I'd wager. ",
  37. },
  38. },
  39. {
  40. name: "truncate messages",
  41. limit: 1,
  42. msgs: []api.Message{
  43. {Role: "user", Content: "You're a test, Harry!"},
  44. {Role: "assistant", Content: "I-I'm a what?"},
  45. {Role: "user", Content: "A test. And a thumping good one at that, I'd wager."},
  46. },
  47. expect: expect{
  48. prompt: "A test. And a thumping good one at that, I'd wager. ",
  49. },
  50. },
  51. {
  52. name: "truncate messages with image",
  53. limit: 64,
  54. msgs: []api.Message{
  55. {Role: "user", Content: "You're a test, Harry!"},
  56. {Role: "assistant", Content: "I-I'm a what?"},
  57. {Role: "user", Content: "A test. And a thumping good one at that, I'd wager.", Images: []api.ImageData{[]byte("something")}},
  58. },
  59. expect: expect{
  60. prompt: "[img-0] A test. And a thumping good one at that, I'd wager. ",
  61. images: [][]byte{
  62. []byte("something"),
  63. },
  64. },
  65. },
  66. {
  67. name: "truncate messages with images",
  68. limit: 64,
  69. msgs: []api.Message{
  70. {Role: "user", Content: "You're a test, Harry!", Images: []api.ImageData{[]byte("something")}},
  71. {Role: "assistant", Content: "I-I'm a what?"},
  72. {Role: "user", Content: "A test. And a thumping good one at that, I'd wager.", Images: []api.ImageData{[]byte("somethingelse")}},
  73. },
  74. expect: expect{
  75. prompt: "[img-0] A test. And a thumping good one at that, I'd wager. ",
  76. images: [][]byte{
  77. []byte("somethingelse"),
  78. },
  79. },
  80. },
  81. {
  82. name: "messages with images",
  83. limit: 2048,
  84. msgs: []api.Message{
  85. {Role: "user", Content: "You're a test, Harry!", Images: []api.ImageData{[]byte("something")}},
  86. {Role: "assistant", Content: "I-I'm a what?"},
  87. {Role: "user", Content: "A test. And a thumping good one at that, I'd wager.", Images: []api.ImageData{[]byte("somethingelse")}},
  88. },
  89. expect: expect{
  90. prompt: "[img-0] You're a test, Harry! I-I'm a what? [img-1] A test. And a thumping good one at that, I'd wager. ",
  91. images: [][]byte{
  92. []byte("something"),
  93. []byte("somethingelse"),
  94. },
  95. },
  96. },
  97. {
  98. name: "message with image tag",
  99. limit: 2048,
  100. msgs: []api.Message{
  101. {Role: "user", Content: "You're a test, Harry! [img]", Images: []api.ImageData{[]byte("something")}},
  102. {Role: "assistant", Content: "I-I'm a what?"},
  103. {Role: "user", Content: "A test. And a thumping good one at that, I'd wager.", Images: []api.ImageData{[]byte("somethingelse")}},
  104. },
  105. expect: expect{
  106. prompt: "You're a test, Harry! [img-0] I-I'm a what? [img-1] A test. And a thumping good one at that, I'd wager. ",
  107. images: [][]byte{
  108. []byte("something"),
  109. []byte("somethingelse"),
  110. },
  111. },
  112. },
  113. {
  114. name: "messages with interleaved images",
  115. limit: 2048,
  116. msgs: []api.Message{
  117. {Role: "user", Content: "You're a test, Harry!"},
  118. {Role: "user", Images: []api.ImageData{[]byte("something")}},
  119. {Role: "user", Images: []api.ImageData{[]byte("somethingelse")}},
  120. {Role: "assistant", Content: "I-I'm a what?"},
  121. {Role: "user", Content: "A test. And a thumping good one at that, I'd wager."},
  122. },
  123. expect: expect{
  124. prompt: "You're a test, Harry!\n\n[img-0]\n\n[img-1] I-I'm a what? A test. And a thumping good one at that, I'd wager. ",
  125. images: [][]byte{
  126. []byte("something"),
  127. []byte("somethingelse"),
  128. },
  129. },
  130. },
  131. {
  132. name: "truncate message with interleaved images",
  133. limit: 1024,
  134. msgs: []api.Message{
  135. {Role: "user", Content: "You're a test, Harry!"},
  136. {Role: "user", Images: []api.ImageData{[]byte("something")}},
  137. {Role: "user", Images: []api.ImageData{[]byte("somethingelse")}},
  138. {Role: "assistant", Content: "I-I'm a what?"},
  139. {Role: "user", Content: "A test. And a thumping good one at that, I'd wager."},
  140. },
  141. expect: expect{
  142. prompt: "[img-0] I-I'm a what? A test. And a thumping good one at that, I'd wager. ",
  143. images: [][]byte{
  144. []byte("somethingelse"),
  145. },
  146. },
  147. },
  148. {
  149. name: "message with system prompt",
  150. limit: 2048,
  151. msgs: []api.Message{
  152. {Role: "system", Content: "You are the Test Who Lived."},
  153. {Role: "user", Content: "You're a test, Harry!"},
  154. {Role: "assistant", Content: "I-I'm a what?"},
  155. {Role: "user", Content: "A test. And a thumping good one at that, I'd wager."},
  156. },
  157. expect: expect{
  158. prompt: "You are the Test Who Lived. You're a test, Harry! I-I'm a what? A test. And a thumping good one at that, I'd wager. ",
  159. },
  160. },
  161. }
  162. tmpl, err := template.Parse(`
  163. {{- if .System }}{{ .System }} {{ end }}
  164. {{- if .Prompt }}{{ .Prompt }} {{ end }}
  165. {{- if .Response }}{{ .Response }} {{ end }}`)
  166. if err != nil {
  167. t.Fatal(err)
  168. }
  169. for _, tt := range cases {
  170. t.Run(tt.name, func(t *testing.T) {
  171. model := Model{Template: tmpl, ProjectorPaths: []string{"vision"}}
  172. opts := api.Options{Runner: api.Runner{NumCtx: tt.limit}}
  173. prompt, images, err := chatPrompt(context.TODO(), &model, tokenize, &opts, tt.msgs)
  174. if err != nil {
  175. t.Fatal(err)
  176. }
  177. if tt.prompt != prompt {
  178. t.Errorf("expected %q, got %q", tt.prompt, prompt)
  179. }
  180. if len(images) != len(tt.images) {
  181. t.Fatalf("expected %d images, got %d", len(tt.images), len(images))
  182. }
  183. for i := range images {
  184. if images[i].ID != i {
  185. t.Errorf("expected ID %d, got %d", i, images[i].ID)
  186. }
  187. if !bytes.Equal(images[i].Data, tt.images[i]) {
  188. t.Errorf("expected %q, got %q", tt.images[i], images[i])
  189. }
  190. }
  191. })
  192. }
  193. }