parser_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. package parser
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "fmt"
  6. "io"
  7. "strings"
  8. "testing"
  9. "unicode/utf16"
  10. "github.com/stretchr/testify/assert"
  11. "github.com/stretchr/testify/require"
  12. )
  13. func TestParseFileFile(t *testing.T) {
  14. input := `
  15. FROM model1
  16. ADAPTER adapter1
  17. LICENSE MIT
  18. PARAMETER param1 value1
  19. PARAMETER param2 value2
  20. TEMPLATE template1
  21. `
  22. reader := strings.NewReader(input)
  23. modelfile, err := ParseFile(reader)
  24. require.NoError(t, err)
  25. expectedCommands := []Command{
  26. {Name: "model", Args: "model1"},
  27. {Name: "adapter", Args: "adapter1"},
  28. {Name: "license", Args: "MIT"},
  29. {Name: "param1", Args: "value1"},
  30. {Name: "param2", Args: "value2"},
  31. {Name: "template", Args: "template1"},
  32. }
  33. assert.Equal(t, expectedCommands, modelfile.Commands)
  34. }
  35. func TestParseFileFrom(t *testing.T) {
  36. var cases = []struct {
  37. input string
  38. expected []Command
  39. err error
  40. }{
  41. {
  42. "FROM foo",
  43. []Command{{Name: "model", Args: "foo"}},
  44. nil,
  45. },
  46. {
  47. "FROM /path/to/model",
  48. []Command{{Name: "model", Args: "/path/to/model"}},
  49. nil,
  50. },
  51. {
  52. "FROM /path/to/model/fp16.bin",
  53. []Command{{Name: "model", Args: "/path/to/model/fp16.bin"}},
  54. nil,
  55. },
  56. {
  57. "FROM llama3:latest",
  58. []Command{{Name: "model", Args: "llama3:latest"}},
  59. nil,
  60. },
  61. {
  62. "FROM llama3:7b-instruct-q4_K_M",
  63. []Command{{Name: "model", Args: "llama3:7b-instruct-q4_K_M"}},
  64. nil,
  65. },
  66. {
  67. "", nil, errMissingFrom,
  68. },
  69. {
  70. "PARAMETER param1 value1",
  71. nil,
  72. errMissingFrom,
  73. },
  74. {
  75. "PARAMETER param1 value1\nFROM foo",
  76. []Command{{Name: "param1", Args: "value1"}, {Name: "model", Args: "foo"}},
  77. nil,
  78. },
  79. }
  80. for _, c := range cases {
  81. t.Run("", func(t *testing.T) {
  82. modelfile, err := ParseFile(strings.NewReader(c.input))
  83. require.ErrorIs(t, err, c.err)
  84. if modelfile != nil {
  85. assert.Equal(t, c.expected, modelfile.Commands)
  86. }
  87. })
  88. }
  89. }
  90. func TestParseFileParametersMissingValue(t *testing.T) {
  91. input := `
  92. FROM foo
  93. PARAMETER param1
  94. `
  95. reader := strings.NewReader(input)
  96. _, err := ParseFile(reader)
  97. require.ErrorIs(t, err, io.ErrUnexpectedEOF)
  98. }
  99. func TestParseFileBadCommand(t *testing.T) {
  100. input := `
  101. FROM foo
  102. BADCOMMAND param1 value1
  103. `
  104. _, err := ParseFile(strings.NewReader(input))
  105. require.ErrorIs(t, err, errInvalidCommand)
  106. }
  107. func TestParseFileMessages(t *testing.T) {
  108. var cases = []struct {
  109. input string
  110. expected []Command
  111. err error
  112. }{
  113. {
  114. `
  115. FROM foo
  116. MESSAGE system You are a file parser. Always parse things.
  117. `,
  118. []Command{
  119. {Name: "model", Args: "foo"},
  120. {Name: "message", Args: "system: You are a file parser. Always parse things."},
  121. },
  122. nil,
  123. },
  124. {
  125. `
  126. FROM foo
  127. MESSAGE system You are a file parser. Always parse things.`,
  128. []Command{
  129. {Name: "model", Args: "foo"},
  130. {Name: "message", Args: "system: You are a file parser. Always parse things."},
  131. },
  132. nil,
  133. },
  134. {
  135. `
  136. FROM foo
  137. MESSAGE system You are a file parser. Always parse things.
  138. MESSAGE user Hey there!
  139. MESSAGE assistant Hello, I want to parse all the things!
  140. `,
  141. []Command{
  142. {Name: "model", Args: "foo"},
  143. {Name: "message", Args: "system: You are a file parser. Always parse things."},
  144. {Name: "message", Args: "user: Hey there!"},
  145. {Name: "message", Args: "assistant: Hello, I want to parse all the things!"},
  146. },
  147. nil,
  148. },
  149. {
  150. `
  151. FROM foo
  152. MESSAGE system """
  153. You are a multiline file parser. Always parse things.
  154. """
  155. `,
  156. []Command{
  157. {Name: "model", Args: "foo"},
  158. {Name: "message", Args: "system: \nYou are a multiline file parser. Always parse things.\n"},
  159. },
  160. nil,
  161. },
  162. {
  163. `
  164. FROM foo
  165. MESSAGE badguy I'm a bad guy!
  166. `,
  167. nil,
  168. errInvalidMessageRole,
  169. },
  170. {
  171. `
  172. FROM foo
  173. MESSAGE system
  174. `,
  175. nil,
  176. io.ErrUnexpectedEOF,
  177. },
  178. {
  179. `
  180. FROM foo
  181. MESSAGE system`,
  182. nil,
  183. io.ErrUnexpectedEOF,
  184. },
  185. }
  186. for _, c := range cases {
  187. t.Run("", func(t *testing.T) {
  188. modelfile, err := ParseFile(strings.NewReader(c.input))
  189. require.ErrorIs(t, err, c.err)
  190. if modelfile != nil {
  191. assert.Equal(t, c.expected, modelfile.Commands)
  192. }
  193. })
  194. }
  195. }
  196. func TestParseFileQuoted(t *testing.T) {
  197. var cases = []struct {
  198. multiline string
  199. expected []Command
  200. err error
  201. }{
  202. {
  203. `
  204. FROM foo
  205. SYSTEM """
  206. This is a
  207. multiline system.
  208. """
  209. `,
  210. []Command{
  211. {Name: "model", Args: "foo"},
  212. {Name: "system", Args: "\nThis is a\nmultiline system.\n"},
  213. },
  214. nil,
  215. },
  216. {
  217. `
  218. FROM foo
  219. SYSTEM """
  220. This is a
  221. multiline system."""
  222. `,
  223. []Command{
  224. {Name: "model", Args: "foo"},
  225. {Name: "system", Args: "\nThis is a\nmultiline system."},
  226. },
  227. nil,
  228. },
  229. {
  230. `
  231. FROM foo
  232. SYSTEM """This is a
  233. multiline system."""
  234. `,
  235. []Command{
  236. {Name: "model", Args: "foo"},
  237. {Name: "system", Args: "This is a\nmultiline system."},
  238. },
  239. nil,
  240. },
  241. {
  242. `
  243. FROM foo
  244. SYSTEM """This is a multiline system."""
  245. `,
  246. []Command{
  247. {Name: "model", Args: "foo"},
  248. {Name: "system", Args: "This is a multiline system."},
  249. },
  250. nil,
  251. },
  252. {
  253. `
  254. FROM foo
  255. SYSTEM """This is a multiline system.""
  256. `,
  257. nil,
  258. io.ErrUnexpectedEOF,
  259. },
  260. {
  261. `
  262. FROM foo
  263. SYSTEM "
  264. `,
  265. nil,
  266. io.ErrUnexpectedEOF,
  267. },
  268. {
  269. `
  270. FROM foo
  271. SYSTEM """
  272. This is a multiline system with "quotes".
  273. """
  274. `,
  275. []Command{
  276. {Name: "model", Args: "foo"},
  277. {Name: "system", Args: "\nThis is a multiline system with \"quotes\".\n"},
  278. },
  279. nil,
  280. },
  281. {
  282. `
  283. FROM foo
  284. SYSTEM """"""
  285. `,
  286. []Command{
  287. {Name: "model", Args: "foo"},
  288. {Name: "system", Args: ""},
  289. },
  290. nil,
  291. },
  292. {
  293. `
  294. FROM foo
  295. SYSTEM ""
  296. `,
  297. []Command{
  298. {Name: "model", Args: "foo"},
  299. {Name: "system", Args: ""},
  300. },
  301. nil,
  302. },
  303. {
  304. `
  305. FROM foo
  306. SYSTEM "'"
  307. `,
  308. []Command{
  309. {Name: "model", Args: "foo"},
  310. {Name: "system", Args: "'"},
  311. },
  312. nil,
  313. },
  314. {
  315. `
  316. FROM foo
  317. SYSTEM """''"'""'""'"'''''""'""'"""
  318. `,
  319. []Command{
  320. {Name: "model", Args: "foo"},
  321. {Name: "system", Args: `''"'""'""'"'''''""'""'`},
  322. },
  323. nil,
  324. },
  325. {
  326. `
  327. FROM foo
  328. TEMPLATE """
  329. {{ .Prompt }}
  330. """`,
  331. []Command{
  332. {Name: "model", Args: "foo"},
  333. {Name: "template", Args: "\n{{ .Prompt }}\n"},
  334. },
  335. nil,
  336. },
  337. }
  338. for _, c := range cases {
  339. t.Run("", func(t *testing.T) {
  340. modelfile, err := ParseFile(strings.NewReader(c.multiline))
  341. require.ErrorIs(t, err, c.err)
  342. if modelfile != nil {
  343. assert.Equal(t, c.expected, modelfile.Commands)
  344. }
  345. })
  346. }
  347. }
  348. func TestParseFileParameters(t *testing.T) {
  349. var cases = map[string]struct {
  350. name, value string
  351. }{
  352. "numa true": {"numa", "true"},
  353. "num_ctx 1": {"num_ctx", "1"},
  354. "num_batch 1": {"num_batch", "1"},
  355. "num_gqa 1": {"num_gqa", "1"},
  356. "num_gpu 1": {"num_gpu", "1"},
  357. "main_gpu 1": {"main_gpu", "1"},
  358. "low_vram true": {"low_vram", "true"},
  359. "f16_kv true": {"f16_kv", "true"},
  360. "logits_all true": {"logits_all", "true"},
  361. "vocab_only true": {"vocab_only", "true"},
  362. "use_mmap true": {"use_mmap", "true"},
  363. "use_mlock true": {"use_mlock", "true"},
  364. "num_thread 1": {"num_thread", "1"},
  365. "num_keep 1": {"num_keep", "1"},
  366. "seed 1": {"seed", "1"},
  367. "num_predict 1": {"num_predict", "1"},
  368. "top_k 1": {"top_k", "1"},
  369. "top_p 1.0": {"top_p", "1.0"},
  370. "tfs_z 1.0": {"tfs_z", "1.0"},
  371. "typical_p 1.0": {"typical_p", "1.0"},
  372. "repeat_last_n 1": {"repeat_last_n", "1"},
  373. "temperature 1.0": {"temperature", "1.0"},
  374. "repeat_penalty 1.0": {"repeat_penalty", "1.0"},
  375. "presence_penalty 1.0": {"presence_penalty", "1.0"},
  376. "frequency_penalty 1.0": {"frequency_penalty", "1.0"},
  377. "mirostat 1": {"mirostat", "1"},
  378. "mirostat_tau 1.0": {"mirostat_tau", "1.0"},
  379. "mirostat_eta 1.0": {"mirostat_eta", "1.0"},
  380. "penalize_newline true": {"penalize_newline", "true"},
  381. "stop ### User:": {"stop", "### User:"},
  382. "stop ### User: ": {"stop", "### User: "},
  383. "stop \"### User:\"": {"stop", "### User:"},
  384. "stop \"### User: \"": {"stop", "### User: "},
  385. "stop \"\"\"### User:\"\"\"": {"stop", "### User:"},
  386. "stop \"\"\"### User:\n\"\"\"": {"stop", "### User:\n"},
  387. "stop <|endoftext|>": {"stop", "<|endoftext|>"},
  388. "stop <|eot_id|>": {"stop", "<|eot_id|>"},
  389. "stop </s>": {"stop", "</s>"},
  390. }
  391. for k, v := range cases {
  392. t.Run(k, func(t *testing.T) {
  393. var b bytes.Buffer
  394. fmt.Fprintln(&b, "FROM foo")
  395. fmt.Fprintln(&b, "PARAMETER", k)
  396. modelfile, err := ParseFile(&b)
  397. require.NoError(t, err)
  398. assert.Equal(t, []Command{
  399. {Name: "model", Args: "foo"},
  400. {Name: v.name, Args: v.value},
  401. }, modelfile.Commands)
  402. })
  403. }
  404. }
  405. func TestParseFileComments(t *testing.T) {
  406. var cases = []struct {
  407. input string
  408. expected []Command
  409. }{
  410. {
  411. `
  412. # comment
  413. FROM foo
  414. `,
  415. []Command{
  416. {Name: "model", Args: "foo"},
  417. },
  418. },
  419. }
  420. for _, c := range cases {
  421. t.Run("", func(t *testing.T) {
  422. modelfile, err := ParseFile(strings.NewReader(c.input))
  423. require.NoError(t, err)
  424. assert.Equal(t, c.expected, modelfile.Commands)
  425. })
  426. }
  427. }
  428. func TestParseFileFormatParseFile(t *testing.T) {
  429. var cases = []string{
  430. `
  431. FROM foo
  432. ADAPTER adapter1
  433. LICENSE MIT
  434. PARAMETER param1 value1
  435. PARAMETER param2 value2
  436. TEMPLATE template1
  437. MESSAGE system You are a file parser. Always parse things.
  438. MESSAGE user Hey there!
  439. MESSAGE assistant Hello, I want to parse all the things!
  440. `,
  441. `
  442. FROM foo
  443. ADAPTER adapter1
  444. LICENSE MIT
  445. PARAMETER param1 value1
  446. PARAMETER param2 value2
  447. TEMPLATE template1
  448. MESSAGE system """
  449. You are a store greeter. Always responsed with "Hello!".
  450. """
  451. MESSAGE user Hey there!
  452. MESSAGE assistant Hello, I want to parse all the things!
  453. `,
  454. `
  455. FROM foo
  456. ADAPTER adapter1
  457. LICENSE """
  458. Very long and boring legal text.
  459. Blah blah blah.
  460. "Oh look, a quote!"
  461. """
  462. PARAMETER param1 value1
  463. PARAMETER param2 value2
  464. TEMPLATE template1
  465. MESSAGE system """
  466. You are a store greeter. Always responsed with "Hello!".
  467. """
  468. MESSAGE user Hey there!
  469. MESSAGE assistant Hello, I want to parse all the things!
  470. `,
  471. `
  472. FROM foo
  473. SYSTEM ""
  474. `,
  475. }
  476. for _, c := range cases {
  477. t.Run("", func(t *testing.T) {
  478. modelfile, err := ParseFile(strings.NewReader(c))
  479. require.NoError(t, err)
  480. modelfile2, err := ParseFile(strings.NewReader(modelfile.String()))
  481. require.NoError(t, err)
  482. assert.Equal(t, modelfile, modelfile2)
  483. })
  484. }
  485. }
  486. func TestParseFileUTF16ParseFile(t *testing.T) {
  487. data := `FROM bob
  488. PARAMETER param1 1
  489. PARAMETER param2 4096
  490. SYSTEM You are a utf16 file.
  491. `
  492. // simulate a utf16 le file
  493. utf16File := utf16.Encode(append([]rune{'\ufffe'}, []rune(data)...))
  494. buf := new(bytes.Buffer)
  495. err := binary.Write(buf, binary.LittleEndian, utf16File)
  496. require.NoError(t, err)
  497. actual, err := ParseFile(buf)
  498. require.NoError(t, err)
  499. expected := []Command{
  500. {Name: "model", Args: "bob"},
  501. {Name: "param1", Args: "1"},
  502. {Name: "param2", Args: "4096"},
  503. {Name: "system", Args: "You are a utf16 file."},
  504. }
  505. assert.Equal(t, expected, actual.Commands)
  506. // simulate a utf16 be file
  507. buf = new(bytes.Buffer)
  508. err = binary.Write(buf, binary.BigEndian, utf16File)
  509. require.NoError(t, err)
  510. actual, err = ParseFile(buf)
  511. require.NoError(t, err)
  512. assert.Equal(t, expected, actual.Commands)
  513. }