parser_test.go 14.8 KB
Newer Older
1
package parser
2
3

import (
Michael Yang's avatar
Michael Yang committed
4
	"bytes"
5
	"encoding/binary"
6
	"errors"
Michael Yang's avatar
Michael Yang committed
7
8
	"fmt"
	"io"
9
10
	"strings"
	"testing"
11
	"unicode/utf16"
12
13

	"github.com/stretchr/testify/assert"
Michael Yang's avatar
lint  
Michael Yang committed
14
	"github.com/stretchr/testify/require"
15
16
	"golang.org/x/text/encoding"
	"golang.org/x/text/encoding/unicode"
17
18
)

Michael Yang's avatar
Michael Yang committed
19
func TestParseFileFile(t *testing.T) {
20
21
22
23
24
25
	input := `
FROM model1
ADAPTER adapter1
LICENSE MIT
PARAMETER param1 value1
PARAMETER param2 value2
Josh Yan's avatar
Josh Yan committed
26
27
28
29
30
31
32
TEMPLATE """{{ if .System }}<|start_header_id|>system<|end_header_id|>

{{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|>

{{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|>

{{ .Response }}<|eot_id|>"""    
33
34
35
36
`

	reader := strings.NewReader(input)

Michael Yang's avatar
Michael Yang committed
37
	modelfile, err := ParseFile(reader)
Michael Yang's avatar
lint  
Michael Yang committed
38
	require.NoError(t, err)
39
40
41
42
43
44
45

	expectedCommands := []Command{
		{Name: "model", Args: "model1"},
		{Name: "adapter", Args: "adapter1"},
		{Name: "license", Args: "MIT"},
		{Name: "param1", Args: "value1"},
		{Name: "param2", Args: "value2"},
Josh Yan's avatar
Josh Yan committed
46
		{Name: "template", Args: "{{ if .System }}<|start_header_id|>system<|end_header_id|>\n\n{{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|>\n\n{{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|>\n\n{{ .Response }}<|eot_id|>"},
47
48
	}

Michael Yang's avatar
Michael Yang committed
49
	assert.Equal(t, expectedCommands, modelfile.Commands)
50
51
}

Josh Yan's avatar
Josh Yan committed
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
func TestParseFileTrimSpace(t *testing.T) {
	input := `
FROM "     model 1"
ADAPTER      adapter3
LICENSE "MIT       "
PARAMETER param1        value1
PARAMETER param2    value2
TEMPLATE """   {{ if .System }}<|start_header_id|>system<|end_header_id|>

{{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|>

{{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|>

{{ .Response }}<|eot_id|>   """    
`

	reader := strings.NewReader(input)

	modelfile, err := ParseFile(reader)
	require.NoError(t, err)

	expectedCommands := []Command{
		{Name: "model", Args: "     model 1"},
		{Name: "adapter", Args: "adapter3"},
		{Name: "license", Args: "MIT       "},
		{Name: "param1", Args: "value1"},
		{Name: "param2", Args: "value2"},
		{Name: "template", Args: "   {{ if .System }}<|start_header_id|>system<|end_header_id|>\n\n{{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|>\n\n{{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|>\n\n{{ .Response }}<|eot_id|>   "},
	}

	assert.Equal(t, expectedCommands, modelfile.Commands)
}

Michael Yang's avatar
Michael Yang committed
85
func TestParseFileFrom(t *testing.T) {
Michael Yang's avatar
lint  
Michael Yang committed
86
	cases := []struct {
Michael Yang's avatar
tests  
Michael Yang committed
87
88
89
90
		input    string
		expected []Command
		err      error
	}{
Josh Yan's avatar
Josh Yan committed
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
		{
			"FROM \"FOO  BAR  \"",
			[]Command{{Name: "model", Args: "FOO  BAR  "}},
			nil,
		},
		{
			"FROM \"FOO BAR\"\nPARAMETER param1 value1",
			[]Command{{Name: "model", Args: "FOO BAR"}, {Name: "param1", Args: "value1"}},
			nil,
		},
		{
			"FROM     FOOO BAR    ",
			[]Command{{Name: "model", Args: "FOOO BAR"}},
			nil,
		},
		{
			"FROM /what/is/the path ",
			[]Command{{Name: "model", Args: "/what/is/the path"}},
			nil,
		},
Michael Yang's avatar
tests  
Michael Yang committed
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
		{
			"FROM foo",
			[]Command{{Name: "model", Args: "foo"}},
			nil,
		},
		{
			"FROM /path/to/model",
			[]Command{{Name: "model", Args: "/path/to/model"}},
			nil,
		},
		{
			"FROM /path/to/model/fp16.bin",
			[]Command{{Name: "model", Args: "/path/to/model/fp16.bin"}},
			nil,
		},
		{
			"FROM llama3:latest",
			[]Command{{Name: "model", Args: "llama3:latest"}},
			nil,
		},
		{
			"FROM llama3:7b-instruct-q4_K_M",
			[]Command{{Name: "model", Args: "llama3:7b-instruct-q4_K_M"}},
			nil,
		},
		{
			"", nil, errMissingFrom,
		},
		{
			"PARAMETER param1 value1",
			nil,
			errMissingFrom,
		},
		{
			"PARAMETER param1 value1\nFROM foo",
			[]Command{{Name: "param1", Args: "value1"}, {Name: "model", Args: "foo"}},
			nil,
		},
Josh Yan's avatar
Josh Yan committed
149
150
		{
			"PARAMETER what the \nFROM lemons make lemonade ",
Josh Yan's avatar
Josh Yan committed
151
			[]Command{{Name: "what", Args: "the"}, {Name: "model", Args: "lemons make lemonade"}},
Josh Yan's avatar
Josh Yan committed
152
153
			nil,
		},
Michael Yang's avatar
tests  
Michael Yang committed
154
	}
155

Michael Yang's avatar
tests  
Michael Yang committed
156
157
	for _, c := range cases {
		t.Run("", func(t *testing.T) {
Michael Yang's avatar
Michael Yang committed
158
			modelfile, err := ParseFile(strings.NewReader(c.input))
Michael Yang's avatar
lint  
Michael Yang committed
159
			require.ErrorIs(t, err, c.err)
Michael Yang's avatar
Michael Yang committed
160
161
162
			if modelfile != nil {
				assert.Equal(t, c.expected, modelfile.Commands)
			}
Michael Yang's avatar
tests  
Michael Yang committed
163
164
		})
	}
165
166
}

Michael Yang's avatar
Michael Yang committed
167
func TestParseFileParametersMissingValue(t *testing.T) {
168
169
170
171
172
173
174
	input := `
FROM foo
PARAMETER param1
`

	reader := strings.NewReader(input)

Michael Yang's avatar
Michael Yang committed
175
	_, err := ParseFile(reader)
Michael Yang's avatar
lint  
Michael Yang committed
176
	require.ErrorIs(t, err, io.ErrUnexpectedEOF)
177
}
178

Michael Yang's avatar
Michael Yang committed
179
func TestParseFileBadCommand(t *testing.T) {
Michael Yang's avatar
Michael Yang committed
180
181
182
183
	input := `
FROM foo
BADCOMMAND param1 value1
`
184
185
186
187
188
	parserError := &ParserError{
		LineNumber: 3,
		Msg:        errInvalidCommand.Error(),
	}

Michael Yang's avatar
Michael Yang committed
189
	_, err := ParseFile(strings.NewReader(input))
190
191
192
	if !errors.As(err, &parserError) {
		t.Errorf("unexpected error: expected: %s, actual: %s", parserError.Error(), err.Error())
	}
Michael Yang's avatar
Michael Yang committed
193
194
}

Michael Yang's avatar
Michael Yang committed
195
func TestParseFileMessages(t *testing.T) {
Michael Yang's avatar
lint  
Michael Yang committed
196
	cases := []struct {
Michael Yang's avatar
Michael Yang committed
197
198
199
200
201
202
203
		input    string
		expected []Command
		err      error
	}{
		{
			`
FROM foo
Michael Yang's avatar
Michael Yang committed
204
MESSAGE system You are a file parser. Always parse things.
Michael Yang's avatar
Michael Yang committed
205
206
207
`,
			[]Command{
				{Name: "model", Args: "foo"},
Michael Yang's avatar
Michael Yang committed
208
				{Name: "message", Args: "system: You are a file parser. Always parse things."},
Michael Yang's avatar
Michael Yang committed
209
210
211
212
213
			},
			nil,
		},
		{
			`
214
FROM foo
Michael Yang's avatar
Michael Yang committed
215
MESSAGE system You are a file parser. Always parse things.`,
Michael Yang's avatar
Michael Yang committed
216
217
			[]Command{
				{Name: "model", Args: "foo"},
Michael Yang's avatar
Michael Yang committed
218
				{Name: "message", Args: "system: You are a file parser. Always parse things."},
Michael Yang's avatar
Michael Yang committed
219
220
221
222
223
224
			},
			nil,
		},
		{
			`
FROM foo
Michael Yang's avatar
Michael Yang committed
225
MESSAGE system You are a file parser. Always parse things.
226
227
MESSAGE user Hey there!
MESSAGE assistant Hello, I want to parse all the things!
Michael Yang's avatar
Michael Yang committed
228
229
230
`,
			[]Command{
				{Name: "model", Args: "foo"},
Michael Yang's avatar
Michael Yang committed
231
				{Name: "message", Args: "system: You are a file parser. Always parse things."},
Michael Yang's avatar
Michael Yang committed
232
233
234
235
236
237
238
239
240
				{Name: "message", Args: "user: Hey there!"},
				{Name: "message", Args: "assistant: Hello, I want to parse all the things!"},
			},
			nil,
		},
		{
			`
FROM foo
MESSAGE system """
Michael Yang's avatar
Michael Yang committed
241
You are a multiline file parser. Always parse things.
Michael Yang's avatar
Michael Yang committed
242
243
244
245
"""
			`,
			[]Command{
				{Name: "model", Args: "foo"},
Michael Yang's avatar
Michael Yang committed
246
				{Name: "message", Args: "system: \nYou are a multiline file parser. Always parse things.\n"},
Michael Yang's avatar
Michael Yang committed
247
248
249
250
251
252
253
254
255
			},
			nil,
		},
		{
			`
FROM foo
MESSAGE badguy I'm a bad guy!
`,
			nil,
256
257
258
259
			&ParserError{
				LineNumber: 3,
				Msg:        errInvalidMessageRole.Error(),
			},
Michael Yang's avatar
Michael Yang committed
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
		},
		{
			`
FROM foo
MESSAGE system
`,
			nil,
			io.ErrUnexpectedEOF,
		},
		{
			`
FROM foo
MESSAGE system`,
			nil,
			io.ErrUnexpectedEOF,
		},
	}
277

278
	for _, tt := range cases {
Michael Yang's avatar
Michael Yang committed
279
		t.Run("", func(t *testing.T) {
280
281
			modelfile, err := ParseFile(strings.NewReader(tt.input))

Michael Yang's avatar
Michael Yang committed
282
			if modelfile != nil {
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
				assert.Equal(t, tt.expected, modelfile.Commands)
			}

			if tt.err == nil {
				if err != nil {
					t.Fatalf("expected no error, but got %v", err)
				}
				return
			}

			switch tt.err.(type) {
			case *ParserError:
				var pErr *ParserError
				if errors.As(err, &pErr) {
					// got the correct type of error
					return
				}
			}

			if errors.Is(err, tt.err) {
				return
Michael Yang's avatar
Michael Yang committed
304
			}
305
306

			t.Fatalf("unexpected error: expected: %v, actual: %v", tt.err, err)
Michael Yang's avatar
Michael Yang committed
307
308
309
		})
	}
}
310

Michael Yang's avatar
Michael Yang committed
311
func TestParseFileQuoted(t *testing.T) {
Michael Yang's avatar
lint  
Michael Yang committed
312
	cases := []struct {
Michael Yang's avatar
Michael Yang committed
313
314
315
316
317
318
319
		multiline string
		expected  []Command
		err       error
	}{
		{
			`
FROM foo
Michael Yang's avatar
Michael Yang committed
320
SYSTEM """
Michael Yang's avatar
Michael Yang committed
321
This is a
Michael Yang's avatar
Michael Yang committed
322
multiline system.
Michael Yang's avatar
Michael Yang committed
323
324
325
326
"""
			`,
			[]Command{
				{Name: "model", Args: "foo"},
Michael Yang's avatar
Michael Yang committed
327
				{Name: "system", Args: "\nThis is a\nmultiline system.\n"},
Michael Yang's avatar
Michael Yang committed
328
329
330
331
332
333
			},
			nil,
		},
		{
			`
FROM foo
Michael Yang's avatar
Michael Yang committed
334
SYSTEM """
Michael Yang's avatar
Michael Yang committed
335
This is a
Michael Yang's avatar
Michael Yang committed
336
multiline system."""
Michael Yang's avatar
Michael Yang committed
337
338
339
			`,
			[]Command{
				{Name: "model", Args: "foo"},
Michael Yang's avatar
Michael Yang committed
340
				{Name: "system", Args: "\nThis is a\nmultiline system."},
Michael Yang's avatar
Michael Yang committed
341
342
343
344
345
346
			},
			nil,
		},
		{
			`
FROM foo
Michael Yang's avatar
Michael Yang committed
347
348
SYSTEM """This is a
multiline system."""
Michael Yang's avatar
Michael Yang committed
349
350
351
			`,
			[]Command{
				{Name: "model", Args: "foo"},
Michael Yang's avatar
Michael Yang committed
352
				{Name: "system", Args: "This is a\nmultiline system."},
Michael Yang's avatar
Michael Yang committed
353
354
355
356
357
358
			},
			nil,
		},
		{
			`
FROM foo
Michael Yang's avatar
Michael Yang committed
359
SYSTEM """This is a multiline system."""
Michael Yang's avatar
Michael Yang committed
360
361
362
			`,
			[]Command{
				{Name: "model", Args: "foo"},
Michael Yang's avatar
Michael Yang committed
363
				{Name: "system", Args: "This is a multiline system."},
Michael Yang's avatar
Michael Yang committed
364
365
366
367
368
369
			},
			nil,
		},
		{
			`
FROM foo
Michael Yang's avatar
Michael Yang committed
370
SYSTEM """This is a multiline system.""
Michael Yang's avatar
Michael Yang committed
371
372
373
374
375
376
377
			`,
			nil,
			io.ErrUnexpectedEOF,
		},
		{
			`
FROM foo
Michael Yang's avatar
Michael Yang committed
378
SYSTEM "
Michael Yang's avatar
Michael Yang committed
379
380
381
382
383
384
385
			`,
			nil,
			io.ErrUnexpectedEOF,
		},
		{
			`
FROM foo
Michael Yang's avatar
Michael Yang committed
386
387
SYSTEM """
This is a multiline system with "quotes".
Michael Yang's avatar
Michael Yang committed
388
389
390
391
"""
`,
			[]Command{
				{Name: "model", Args: "foo"},
Michael Yang's avatar
Michael Yang committed
392
				{Name: "system", Args: "\nThis is a multiline system with \"quotes\".\n"},
Michael Yang's avatar
Michael Yang committed
393
394
395
396
397
398
			},
			nil,
		},
		{
			`
FROM foo
Michael Yang's avatar
Michael Yang committed
399
SYSTEM """"""
Michael Yang's avatar
Michael Yang committed
400
401
402
`,
			[]Command{
				{Name: "model", Args: "foo"},
Michael Yang's avatar
Michael Yang committed
403
				{Name: "system", Args: ""},
Michael Yang's avatar
Michael Yang committed
404
405
406
407
408
409
			},
			nil,
		},
		{
			`
FROM foo
Michael Yang's avatar
Michael Yang committed
410
SYSTEM ""
Michael Yang's avatar
Michael Yang committed
411
412
413
`,
			[]Command{
				{Name: "model", Args: "foo"},
Michael Yang's avatar
Michael Yang committed
414
				{Name: "system", Args: ""},
Michael Yang's avatar
Michael Yang committed
415
416
417
418
419
420
			},
			nil,
		},
		{
			`
FROM foo
Michael Yang's avatar
Michael Yang committed
421
SYSTEM "'"
Michael Yang's avatar
Michael Yang committed
422
423
424
`,
			[]Command{
				{Name: "model", Args: "foo"},
Michael Yang's avatar
Michael Yang committed
425
				{Name: "system", Args: "'"},
Michael Yang's avatar
Michael Yang committed
426
427
428
			},
			nil,
		},
Michael Yang's avatar
tests  
Michael Yang committed
429
430
431
		{
			`
FROM foo
Michael Yang's avatar
Michael Yang committed
432
SYSTEM """''"'""'""'"'''''""'""'"""
Michael Yang's avatar
tests  
Michael Yang committed
433
434
435
`,
			[]Command{
				{Name: "model", Args: "foo"},
Michael Yang's avatar
Michael Yang committed
436
437
438
439
440
441
442
443
444
445
446
447
448
				{Name: "system", Args: `''"'""'""'"'''''""'""'`},
			},
			nil,
		},
		{
			`
FROM foo
TEMPLATE """
{{ .Prompt }}
"""`,
			[]Command{
				{Name: "model", Args: "foo"},
				{Name: "template", Args: "\n{{ .Prompt }}\n"},
Michael Yang's avatar
tests  
Michael Yang committed
449
450
451
			},
			nil,
		},
452
453
	}

Michael Yang's avatar
Michael Yang committed
454
455
	for _, c := range cases {
		t.Run("", func(t *testing.T) {
Michael Yang's avatar
Michael Yang committed
456
			modelfile, err := ParseFile(strings.NewReader(c.multiline))
Michael Yang's avatar
lint  
Michael Yang committed
457
			require.ErrorIs(t, err, c.err)
Michael Yang's avatar
Michael Yang committed
458
459
460
			if modelfile != nil {
				assert.Equal(t, c.expected, modelfile.Commands)
			}
Michael Yang's avatar
Michael Yang committed
461
462
		})
	}
463
464
}

Michael Yang's avatar
Michael Yang committed
465
func TestParseFileParameters(t *testing.T) {
Michael Yang's avatar
lint  
Michael Yang committed
466
	cases := map[string]struct {
Michael Yang's avatar
tests  
Michael Yang committed
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
		name, value string
	}{
		"numa true":                    {"numa", "true"},
		"num_ctx 1":                    {"num_ctx", "1"},
		"num_batch 1":                  {"num_batch", "1"},
		"num_gqa 1":                    {"num_gqa", "1"},
		"num_gpu 1":                    {"num_gpu", "1"},
		"main_gpu 1":                   {"main_gpu", "1"},
		"low_vram true":                {"low_vram", "true"},
		"logits_all true":              {"logits_all", "true"},
		"vocab_only true":              {"vocab_only", "true"},
		"use_mmap true":                {"use_mmap", "true"},
		"use_mlock true":               {"use_mlock", "true"},
		"num_thread 1":                 {"num_thread", "1"},
		"num_keep 1":                   {"num_keep", "1"},
		"seed 1":                       {"seed", "1"},
		"num_predict 1":                {"num_predict", "1"},
		"top_k 1":                      {"top_k", "1"},
		"top_p 1.0":                    {"top_p", "1.0"},
486
		"min_p 0.05":                   {"min_p", "0.05"},
Michael Yang's avatar
tests  
Michael Yang committed
487
488
489
490
491
492
493
494
495
496
497
498
		"tfs_z 1.0":                    {"tfs_z", "1.0"},
		"typical_p 1.0":                {"typical_p", "1.0"},
		"repeat_last_n 1":              {"repeat_last_n", "1"},
		"temperature 1.0":              {"temperature", "1.0"},
		"repeat_penalty 1.0":           {"repeat_penalty", "1.0"},
		"presence_penalty 1.0":         {"presence_penalty", "1.0"},
		"frequency_penalty 1.0":        {"frequency_penalty", "1.0"},
		"mirostat 1":                   {"mirostat", "1"},
		"mirostat_tau 1.0":             {"mirostat_tau", "1.0"},
		"mirostat_eta 1.0":             {"mirostat_eta", "1.0"},
		"penalize_newline true":        {"penalize_newline", "true"},
		"stop ### User:":               {"stop", "### User:"},
Josh Yan's avatar
Josh Yan committed
499
		"stop ### User: ":              {"stop", "### User:"},
Michael Yang's avatar
tests  
Michael Yang committed
500
501
502
503
504
505
506
		"stop \"### User:\"":           {"stop", "### User:"},
		"stop \"### User: \"":          {"stop", "### User: "},
		"stop \"\"\"### User:\"\"\"":   {"stop", "### User:"},
		"stop \"\"\"### User:\n\"\"\"": {"stop", "### User:\n"},
		"stop <|endoftext|>":           {"stop", "<|endoftext|>"},
		"stop <|eot_id|>":              {"stop", "<|eot_id|>"},
		"stop </s>":                    {"stop", "</s>"},
Michael Yang's avatar
Michael Yang committed
507
	}
508

Michael Yang's avatar
tests  
Michael Yang committed
509
510
	for k, v := range cases {
		t.Run(k, func(t *testing.T) {
Michael Yang's avatar
Michael Yang committed
511
512
			var b bytes.Buffer
			fmt.Fprintln(&b, "FROM foo")
Michael Yang's avatar
tests  
Michael Yang committed
513
			fmt.Fprintln(&b, "PARAMETER", k)
Michael Yang's avatar
Michael Yang committed
514
			modelfile, err := ParseFile(&b)
Michael Yang's avatar
lint  
Michael Yang committed
515
			require.NoError(t, err)
Michael Yang's avatar
tests  
Michael Yang committed
516
517
518
519

			assert.Equal(t, []Command{
				{Name: "model", Args: "foo"},
				{Name: v.name, Args: v.value},
Michael Yang's avatar
Michael Yang committed
520
			}, modelfile.Commands)
Michael Yang's avatar
Michael Yang committed
521
522
523
524
		})
	}
}

Michael Yang's avatar
Michael Yang committed
525
func TestParseFileComments(t *testing.T) {
Michael Yang's avatar
lint  
Michael Yang committed
526
	cases := []struct {
Michael Yang's avatar
Michael Yang committed
527
528
529
530
531
532
		input    string
		expected []Command
	}{
		{
			`
# comment
533
FROM foo
Michael Yang's avatar
Michael Yang committed
534
535
536
537
538
539
	`,
			[]Command{
				{Name: "model", Args: "foo"},
			},
		},
	}
540

Michael Yang's avatar
Michael Yang committed
541
542
	for _, c := range cases {
		t.Run("", func(t *testing.T) {
Michael Yang's avatar
Michael Yang committed
543
			modelfile, err := ParseFile(strings.NewReader(c.input))
Michael Yang's avatar
lint  
Michael Yang committed
544
			require.NoError(t, err)
Michael Yang's avatar
Michael Yang committed
545
			assert.Equal(t, c.expected, modelfile.Commands)
Michael Yang's avatar
Michael Yang committed
546
547
		})
	}
548
}
Michael Yang's avatar
Michael Yang committed
549

Michael Yang's avatar
Michael Yang committed
550
func TestParseFileFormatParseFile(t *testing.T) {
Michael Yang's avatar
lint  
Michael Yang committed
551
	cases := []string{
Michael Yang's avatar
Michael Yang committed
552
553
554
555
556
557
558
		`
FROM foo
ADAPTER adapter1
LICENSE MIT
PARAMETER param1 value1
PARAMETER param2 value2
TEMPLATE template1
Michael Yang's avatar
Michael Yang committed
559
MESSAGE system You are a file parser. Always parse things.
Michael Yang's avatar
Michael Yang committed
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
MESSAGE user Hey there!
MESSAGE assistant Hello, I want to parse all the things!
`,
		`
FROM foo
ADAPTER adapter1
LICENSE MIT
PARAMETER param1 value1
PARAMETER param2 value2
TEMPLATE template1
MESSAGE system """
You are a store greeter. Always responsed with "Hello!".
"""
MESSAGE user Hey there!
MESSAGE assistant Hello, I want to parse all the things!
`,
		`
FROM foo
ADAPTER adapter1
LICENSE """
Very long and boring legal text.
Blah blah blah.
"Oh look, a quote!"
"""

PARAMETER param1 value1
PARAMETER param2 value2
TEMPLATE template1
MESSAGE system """
You are a store greeter. Always responsed with "Hello!".
"""
MESSAGE user Hey there!
MESSAGE assistant Hello, I want to parse all the things!
593
594
595
596
`,
		`
FROM foo
SYSTEM ""
Michael Yang's avatar
Michael Yang committed
597
598
599
600
601
`,
	}

	for _, c := range cases {
		t.Run("", func(t *testing.T) {
Michael Yang's avatar
Michael Yang committed
602
			modelfile, err := ParseFile(strings.NewReader(c))
Michael Yang's avatar
lint  
Michael Yang committed
603
			require.NoError(t, err)
Michael Yang's avatar
Michael Yang committed
604

Michael Yang's avatar
Michael Yang committed
605
			modelfile2, err := ParseFile(strings.NewReader(modelfile.String()))
Michael Yang's avatar
lint  
Michael Yang committed
606
			require.NoError(t, err)
Michael Yang's avatar
Michael Yang committed
607

Michael Yang's avatar
Michael Yang committed
608
			assert.Equal(t, modelfile, modelfile2)
Michael Yang's avatar
Michael Yang committed
609
610
611
		})
	}
}
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626

func TestParseFileUTF16ParseFile(t *testing.T) {
	data := `FROM bob
PARAMETER param1 1
PARAMETER param2 4096
SYSTEM You are a utf16 file.
`

	expected := []Command{
		{Name: "model", Args: "bob"},
		{Name: "param1", Args: "1"},
		{Name: "param2", Args: "4096"},
		{Name: "system", Args: "You are a utf16 file."},
	}

627
628
629
630
	t.Run("le", func(t *testing.T) {
		var b bytes.Buffer
		require.NoError(t, binary.Write(&b, binary.LittleEndian, []byte{0xff, 0xfe}))
		require.NoError(t, binary.Write(&b, binary.LittleEndian, utf16.Encode([]rune(data))))
631

632
633
		actual, err := ParseFile(&b)
		require.NoError(t, err)
634

635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
		assert.Equal(t, expected, actual.Commands)
	})

	t.Run("be", func(t *testing.T) {
		var b bytes.Buffer
		require.NoError(t, binary.Write(&b, binary.BigEndian, []byte{0xfe, 0xff}))
		require.NoError(t, binary.Write(&b, binary.BigEndian, utf16.Encode([]rune(data))))

		actual, err := ParseFile(&b)
		require.NoError(t, err)
		assert.Equal(t, expected, actual.Commands)
	})
}

func TestParseMultiByte(t *testing.T) {
	input := `FROM test
	SYSTEM 你好👋`

	expect := []Command{
		{Name: "model", Args: "test"},
		{Name: "system", Args: "你好👋"},
	}

	encodings := []encoding.Encoding{
		unicode.UTF8,
		unicode.UTF16(unicode.LittleEndian, unicode.UseBOM),
		unicode.UTF16(unicode.BigEndian, unicode.UseBOM),
	}

	for _, encoding := range encodings {
		t.Run(fmt.Sprintf("%s", encoding), func(t *testing.T) {
			s, err := encoding.NewEncoder().String(input)
			require.NoError(t, err)

			actual, err := ParseFile(strings.NewReader(s))
			require.NoError(t, err)

			assert.Equal(t, expect, actual.Commands)
		})
	}
675
}