mirror of https://github.com/ollama/ollama.git
				
				
				
			
		
			
	
	
		
			140 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
		
		
			
		
	
	
			140 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
|  | package tools | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"testing" | ||
|  | 	"text/template" | ||
|  | ) | ||
|  | 
 | ||
|  | func TestParseTag(t *testing.T) { | ||
|  | 	cases := []struct { | ||
|  | 		name     string | ||
|  | 		template string | ||
|  | 		want     string | ||
|  | 	}{ | ||
|  | 		{ | ||
|  | 			name:     "empty", | ||
|  | 			template: "", | ||
|  | 			want:     "{", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			name:     "no tag", | ||
|  | 			template: "{{if .ToolCalls}}{{end}}", | ||
|  | 			want:     "{", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			name:     "no tag with range", | ||
|  | 			template: "{{if .ToolCalls}}{{range .ToolCalls}}{{ . }}{{end}}{{end}}", | ||
|  | 			want:     "{", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			name:     "tool call with json format", | ||
|  | 			template: "{{if .ToolCalls}}```json\n{{end}}", | ||
|  | 			want:     "```json", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			name:     "square brackets", | ||
|  | 			template: "{{if .ToolCalls}}[{{range .ToolCalls}}{{ . }}{{end}}]{{end}}", | ||
|  | 			want:     "[", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			name:     "square brackets with whitespace", | ||
|  | 			template: "{{if .ToolCalls}}\n [ {{range .ToolCalls}}{{ . }}{{end}}]{{end}}", | ||
|  | 			want:     "[", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			name:     "tailing ]", | ||
|  | 			template: "{{if .ToolCalls}}{{range .ToolCalls}}{{ . }}{{end}}]{{end}}", | ||
|  | 			want:     "{", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			name:     "whitespace only", | ||
|  | 			template: "{{if .ToolCalls}} {{range .ToolCalls}}{{ . }}{{end}}{{end}}", | ||
|  | 			want:     "{", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			name:     "whitespace only in range", | ||
|  | 			template: "{{if .ToolCalls}}{{range .ToolCalls}}\n{{ . }}\n{{end}}{{end}}", | ||
|  | 			want:     "{", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			name:     "json objects", | ||
|  | 			template: `{{if .ToolCalls}}{{range .ToolCalls}}{"name": "{{ .Function.Name }}", "arguments": {{ .Function.Arguments }}}{{end}}{{end}}`, | ||
|  | 			want:     "{", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			name:     "json objects with whitespace", | ||
|  | 			template: "{{if .ToolCalls}}{{range .ToolCalls}}\n{\"name\": \"{{ .Function.Name }}\", \"arguments\": {{ .Function.Arguments }}}{{end}}{{end}}", | ||
|  | 			want:     "{", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			name:     "json objects with CRLF", | ||
|  | 			template: "{{if .ToolCalls}}{{range .ToolCalls}}\r\n{\"name\": \"{{ .Function.Name }}\", \"arguments\": {{ .Function.Arguments }}}{{end}}{{end}}", | ||
|  | 			want:     "{", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			name:     "json objects with whitespace before and after range", | ||
|  | 			template: "{{if .ToolCalls}}\n{{range .ToolCalls}}\n{\"name\": \"{{ .Function.Name }}\", \"arguments\": {{ .Function.Arguments }}}\r\n{{end}}\r\n{{end}}", | ||
|  | 			want:     "{", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			name:     "before and after range", | ||
|  | 			template: "{{if .ToolCalls}}<|tool▁calls▁begin|>{{range .ToolCalls}}<|tool▁call▁begin|>functionget_current_weather\n```json\n{\"location\": \"Tokyo\"}\n```<|tool▁call▁end|>\n{{end}}<|tool▁calls▁end|>{{end}}", | ||
|  | 			want:     "<|tool▁calls▁begin|>", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			name:     "after range", | ||
|  | 			template: "{{if .ToolCalls}}{{range .ToolCalls}}<tool_call>{\"name\": \"{{ .Function.Name }}\", \"arguments\": {{ .Function.Arguments }}}</tool_call>{{end}}{{end}}", | ||
|  | 			want:     "<tool_call>", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			name:     "after range with leading whitespace before range", | ||
|  | 			template: "{{if .ToolCalls}}\n{{range .ToolCalls}}<tool_call>{\"name\": \"{{ .Function.Name }}\", \"arguments\": {{ .Function.Arguments }}}</tool_call>{{end}}{{end}}", | ||
|  | 			want:     "<tool_call>", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			name:     "tool call in range with {", | ||
|  | 			template: `{{if .ToolCalls}}{{range .ToolCalls}}<tool_call>{"name": "{{ .Function.Name }}", "arguments": {{ .Function.Arguments }}}<tool_call>{{end}}{{end}}`, | ||
|  | 			want:     "<tool_call>", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			name:     "tool call with multiple text nodes", | ||
|  | 			template: "{{if .ToolCalls}}First text{{if .Something}}inner{{end}}Second text{{end}}", | ||
|  | 			want:     "First text", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			name:     "action tag", | ||
|  | 			template: "{{if .ToolCalls}}Action: ```json{{end}}", | ||
|  | 			want:     "Action: ```json", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			name:     "incomplete functools bracket", | ||
|  | 			template: "{{if .ToolCalls}}functools[{{end}}", | ||
|  | 			want:     "functools[", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			name:     "uppercase tool call with incomplete bracket", | ||
|  | 			template: "{{if .ToolCalls}}[TOOL_CALL] [{{end}}", | ||
|  | 			want:     "[TOOL_CALL] [", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			name:     "uppercase tool call with adjacent bracket", | ||
|  | 			template: "{{if .ToolCalls}}[TOOL_CALL][{{end}}", | ||
|  | 			want:     "[TOOL_CALL][", | ||
|  | 		}, | ||
|  | 	} | ||
|  | 
 | ||
|  | 	for _, tc := range cases { | ||
|  | 		t.Run(tc.name, func(t *testing.T) { | ||
|  | 			tmpl, err := template.New("test").Parse(tc.template) | ||
|  | 			if err != nil && tc.template != "" { | ||
|  | 				t.Fatalf("failed to parse template: %v", err) | ||
|  | 			} | ||
|  | 
 | ||
|  | 			got := parseTag(tmpl) | ||
|  | 			if got != tc.want { | ||
|  | 				t.Errorf("got text %q, want %q", got, tc.want) | ||
|  | 			} | ||
|  | 		}) | ||
|  | 	} | ||
|  | } |