file_test.go 10 KB

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