Spaces:
Configuration error
Configuration error
| package grammars_test | |
| import ( | |
| "strings" | |
| . "github.com/mudler/LocalAI/pkg/functions" | |
| . "github.com/mudler/LocalAI/pkg/functions/grammars" | |
| . "github.com/onsi/ginkgo/v2" | |
| . "github.com/onsi/gomega" | |
| ) | |
| var testFunctions = []Item{ | |
| { | |
| Type: "object", | |
| Properties: createFunction( | |
| "function", | |
| "arguments", | |
| "create_event", | |
| map[string]interface{}{ | |
| "title": map[string]string{"type": "string"}, | |
| "date": map[string]string{"type": "string"}, | |
| "time": map[string]string{"type": "string"}, | |
| }, | |
| ), | |
| }, | |
| { | |
| Type: "object", | |
| Properties: createFunction( | |
| "function", | |
| "arguments", | |
| "search", | |
| map[string]interface{}{ | |
| "query": map[string]string{"type": "string"}, | |
| }), | |
| }, | |
| } | |
| var testFunctionsName = []Item{ | |
| { | |
| Type: "object", | |
| Properties: createFunction( | |
| "name", | |
| "arguments", | |
| "create_event", | |
| map[string]interface{}{ | |
| "title": map[string]string{"type": "string"}, | |
| "date": map[string]string{"type": "string"}, | |
| "time": map[string]string{"type": "string"}, | |
| }, | |
| ), | |
| }, | |
| { | |
| Type: "object", | |
| Properties: createFunction( | |
| "name", | |
| "arguments", | |
| "search", | |
| map[string]interface{}{ | |
| "query": map[string]string{"type": "string"}, | |
| }), | |
| }, | |
| } | |
| func rootResult(s string) string { | |
| return `root-0-name ::= "\"create_event\"" | |
| freestring ::= ( | |
| [^"\\] | | |
| "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F]) | |
| )* space | |
| root-0 ::= "{" space "\"arguments\"" space ":" space root-0-arguments "," space "\"name\"" space ":" space root-0-name "}" space | |
| root-1-arguments ::= "{" space "\"query\"" space ":" space string "}" space | |
| realvalue ::= root-0 | root-1 | |
| root ::= ` + s + ` | |
| space ::= " "? | |
| root-0-arguments ::= "{" space "\"date\"" space ":" space string "," space "\"time\"" space ":" space string "," space "\"title\"" space ":" space string "}" space | |
| root-1 ::= "{" space "\"arguments\"" space ":" space root-1-arguments "," space "\"name\"" space ":" space root-1-name "}" space | |
| string ::= "\"" ( | |
| [^"\\] | | |
| "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F]) | |
| )* "\"" space | |
| arr ::= | |
| "[\n" ( | |
| realvalue | |
| (",\n" realvalue)* | |
| )? "]" | |
| root-1-name ::= "\"search\""` | |
| } | |
| const ( | |
| testInput1 = ` | |
| { | |
| "oneOf": [ | |
| { | |
| "type": "object", | |
| "properties": { | |
| "function": {"const": "create_event"}, | |
| "arguments": { | |
| "type": "object", | |
| "properties": { | |
| "title": {"type": "string"}, | |
| "date": {"type": "string"}, | |
| "time": {"type": "string"} | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "type": "object", | |
| "properties": { | |
| "function": {"const": "search"}, | |
| "arguments": { | |
| "type": "object", | |
| "properties": { | |
| "query": {"type": "string"} | |
| } | |
| } | |
| } | |
| } | |
| ] | |
| }` | |
| inputResult1 = `root-0-function ::= "\"create_event\"" | |
| freestring ::= ( | |
| [^"\\] | | |
| "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F]) | |
| )* space | |
| root-0 ::= "{" space "\"arguments\"" space ":" space root-0-arguments "," space "\"function\"" space ":" space root-0-function "}" space | |
| root-1-arguments ::= "{" space "\"query\"" space ":" space string "}" space | |
| root ::= root-0 | root-1 | |
| space ::= " "? | |
| root-0-arguments ::= "{" space "\"date\"" space ":" space string "," space "\"time\"" space ":" space string "," space "\"title\"" space ":" space string "}" space | |
| root-1 ::= "{" space "\"arguments\"" space ":" space root-1-arguments "," space "\"function\"" space ":" space root-1-function "}" space | |
| string ::= "\"" ( | |
| [^"\\] | | |
| "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F]) | |
| )* "\"" space | |
| root-1-function ::= "\"search\""` | |
| inputResult2 = `root-0-function ::= "\"create_event\"" | |
| freestring ::= ( | |
| [^"\\] | | |
| "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F]) | |
| )* space | |
| root-0 ::= "{" space "\"arguments\"" space ":" space root-0-arguments "," space "\"function\"" space ":" space root-0-function "}" space | |
| root-1-arguments ::= "{" space "\"query\"" space ":" space string "}" space | |
| realvalue ::= root-0 | root-1 | |
| root ::= arr | realvalue | |
| space ::= " "? | |
| root-0-arguments ::= "{" space "\"date\"" space ":" space string "," space "\"time\"" space ":" space string "," space "\"title\"" space ":" space string "}" space | |
| root-1 ::= "{" space "\"arguments\"" space ":" space root-1-arguments "," space "\"function\"" space ":" space root-1-function "}" space | |
| string ::= "\"" ( | |
| [^"\\] | | |
| "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F]) | |
| )* "\"" space | |
| arr ::= | |
| "[\n" ( | |
| realvalue | |
| (",\n" realvalue)* | |
| )? "]" | |
| root-1-function ::= "\"search\""` | |
| testInput2 = ` | |
| { | |
| "oneOf": [ | |
| { | |
| "type": "object", | |
| "properties": { | |
| "name": {"const": "create_event"}, | |
| "arguments": { | |
| "type": "object", | |
| "properties": { | |
| "title": {"type": "string"}, | |
| "date": {"type": "string"}, | |
| "time": {"type": "string"} | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "type": "object", | |
| "properties": { | |
| "name": {"const": "search"}, | |
| "arguments": { | |
| "type": "object", | |
| "properties": { | |
| "query": {"type": "string"} | |
| } | |
| } | |
| } | |
| } | |
| ] | |
| }` | |
| inputResult3 = `root-0-name ::= "\"create_event\"" | |
| freestring ::= ( | |
| [^"\\] | | |
| "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F]) | |
| )* space | |
| root-0 ::= "{" space "\"arguments\"" space ":" space root-0-arguments "," space "\"name\"" space ":" space root-0-name "}" space | |
| root-1-arguments ::= "{" space "\"query\"" space ":" space string "}" space | |
| root ::= root-0 | root-1 | |
| space ::= " "? | |
| root-0-arguments ::= "{" space "\"date\"" space ":" space string "," space "\"time\"" space ":" space string "," space "\"title\"" space ":" space string "}" space | |
| root-1 ::= "{" space "\"arguments\"" space ":" space root-1-arguments "," space "\"name\"" space ":" space root-1-name "}" space | |
| string ::= "\"" ( | |
| [^"\\] | | |
| "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F]) | |
| )* "\"" space | |
| root-1-name ::= "\"search\""` | |
| inputResult4 = `root-0-name ::= "\"create_event\"" | |
| freestring ::= ( | |
| [^"\\] | | |
| "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F]) | |
| )* space | |
| root-0 ::= "{" space "\"arguments\"" space ":" space root-0-arguments "," space "\"name\"" space ":" space root-0-name "}" space | |
| root-1-arguments ::= "{" space "\"query\"" space ":" space string "}" space | |
| realvalue ::= root-0 | root-1 | |
| root ::= arr | realvalue | |
| space ::= " "? | |
| root-0-arguments ::= "{" space "\"date\"" space ":" space string "," space "\"time\"" space ":" space string "," space "\"title\"" space ":" space string "}" space | |
| root-1 ::= "{" space "\"arguments\"" space ":" space root-1-arguments "," space "\"name\"" space ":" space root-1-name "}" space | |
| string ::= "\"" ( | |
| [^"\\] | | |
| "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F]) | |
| )* "\"" space | |
| arr ::= | |
| "[\n" ( | |
| realvalue | |
| (",\n" realvalue)* | |
| )? "]" | |
| root-1-name ::= "\"search\""` | |
| ) | |
| var _ = Describe("JSON schema grammar tests", func() { | |
| Context("JSON", func() { | |
| It("generates a valid grammar from JSON schema", func() { | |
| grammar, err := NewJSONSchemaConverter("").GrammarFromBytes([]byte(testInput1)) | |
| Expect(err).To(BeNil()) | |
| results := strings.Split(inputResult1, "\n") | |
| for _, r := range results { | |
| if r != "" { | |
| Expect(grammar).To(ContainSubstring(r)) | |
| } | |
| } | |
| Expect(len(results)).To(Equal(len(strings.Split(grammar, "\n")))) | |
| }) | |
| It("generates a valid grammar from JSON schema", func() { | |
| grammar, err := NewJSONSchemaConverter("").GrammarFromBytes([]byte(testInput2)) | |
| Expect(err).To(BeNil()) | |
| results := strings.Split(inputResult3, "\n") | |
| for _, r := range results { | |
| if r != "" { | |
| Expect(grammar).To(ContainSubstring(r)) | |
| } | |
| } | |
| Expect(len(results)).To(Equal(len(strings.Split(grammar, "\n")))) | |
| }) | |
| It("generates a valid grammar from JSON Objects", func() { | |
| structuredGrammar := JSONFunctionStructure{ | |
| OneOf: testFunctions} | |
| grammar, err := structuredGrammar.Grammar() | |
| Expect(err).To(BeNil()) | |
| results := strings.Split(inputResult1, "\n") | |
| for _, r := range results { | |
| if r != "" { | |
| Expect(grammar).To(ContainSubstring(r)) | |
| } | |
| } | |
| Expect(len(results)).To(Equal(len(strings.Split(grammar, "\n")))) | |
| }) | |
| It("generates a valid grammar from JSON Objects for multiple function return", func() { | |
| structuredGrammar := JSONFunctionStructure{ | |
| OneOf: testFunctions} | |
| grammar, err := structuredGrammar.Grammar(EnableMaybeArray) | |
| Expect(err).To(BeNil()) | |
| results := strings.Split( | |
| strings.Join([]string{ | |
| inputResult2, | |
| "mixedstring ::= freestring | freestring arr | freestring realvalue"}, "\n"), | |
| "\n") | |
| for _, r := range results { | |
| if r != "" { | |
| Expect(grammar).To(ContainSubstring(r)) | |
| } | |
| } | |
| Expect(len(results)).To(Equal(len(strings.Split(grammar, "\n"))), grammar) | |
| }) | |
| It("generates a valid grammar from JSON Objects for multiple function return", func() { | |
| structuredGrammar := JSONFunctionStructure{ | |
| OneOf: testFunctionsName} | |
| grammar, err := structuredGrammar.Grammar(EnableMaybeArray) | |
| Expect(err).To(BeNil()) | |
| results := strings.Split( | |
| strings.Join([]string{ | |
| inputResult4, | |
| "mixedstring ::= freestring | freestring arr | freestring realvalue"}, "\n"), | |
| "\n") | |
| for _, r := range results { | |
| if r != "" { | |
| Expect(grammar).To(ContainSubstring(r)) | |
| } | |
| } | |
| Expect(len(results)).To(Equal(len(strings.Split(grammar, "\n"))), grammar) | |
| }) | |
| It("generates a valid grammar from JSON Objects for multiple function return with a suffix and array", func() { | |
| structuredGrammar := JSONFunctionStructure{ | |
| OneOf: testFunctionsName} | |
| grammar, err := structuredGrammar.Grammar( | |
| SetPrefix("suffix"), | |
| EnableMaybeArray, | |
| ) | |
| Expect(err).To(BeNil()) | |
| results := strings.Split( | |
| strings.Join([]string{ | |
| rootResult(`"suffix" arr | realvalue`), | |
| "mixedstring ::= freestring | freestring arr | freestring realvalue"}, "\n"), | |
| "\n") | |
| for _, r := range results { | |
| if r != "" { | |
| Expect(grammar).To(ContainSubstring(r)) | |
| } | |
| } | |
| Expect(len(results)).To(Equal(len(strings.Split(grammar, "\n"))), grammar) | |
| }) | |
| It("generates a valid grammar from JSON Objects with a suffix", func() { | |
| structuredGrammar := JSONFunctionStructure{ | |
| OneOf: testFunctionsName} | |
| grammar, err := structuredGrammar.Grammar(SetPrefix("suffix")) | |
| Expect(err).To(BeNil()) | |
| results := strings.Split( | |
| strings.Join([]string{ | |
| rootResult(`"suffix" realvalue`), | |
| "mixedstring ::= freestring | freestring realvalue"}, "\n"), | |
| "\n") | |
| for _, r := range results { | |
| if r != "" { | |
| Expect(grammar).To(ContainSubstring(r)) | |
| } | |
| } | |
| Expect(len(results)).To(Equal(len(strings.Split(grammar, "\n"))), grammar) | |
| }) | |
| It("generates a valid grammar from JSON Objects with a suffix and could return string", func() { | |
| structuredGrammar := JSONFunctionStructure{ | |
| OneOf: testFunctionsName} | |
| grammar, err := structuredGrammar.Grammar(SetPrefix("suffix"), EnableMaybeString) | |
| Expect(err).To(BeNil()) | |
| results := strings.Split( | |
| strings.Join([]string{ | |
| rootResult(`( "suffix" realvalue | mixedstring )`), | |
| "mixedstring ::= freestring | freestring realvalue"}, "\n"), | |
| "\n") | |
| for _, r := range results { | |
| if r != "" { | |
| Expect(grammar).To(ContainSubstring(r)) | |
| } | |
| } | |
| Expect(len(results)).To(Equal(len(strings.Split(grammar, "\n"))), grammar) | |
| }) | |
| It("generates a valid grammar from JSON Objects with a suffix that could return text or an array of tools", func() { | |
| structuredGrammar := JSONFunctionStructure{ | |
| OneOf: testFunctionsName} | |
| grammar, err := structuredGrammar.Grammar(SetPrefix("suffix"), EnableMaybeString, EnableMaybeArray) | |
| Expect(err).To(BeNil()) | |
| results := strings.Split( | |
| strings.Join([]string{ | |
| rootResult(`( "suffix" (arr | realvalue) | mixedstring )`), | |
| "mixedstring ::= freestring | freestring arr | freestring realvalue"}, "\n"), | |
| "\n") | |
| for _, r := range results { | |
| if r != "" { | |
| Expect(grammar).To(ContainSubstring(r)) | |
| } | |
| } | |
| Expect(len(results)).To(Equal(len(strings.Split(grammar, "\n"))), grammar) | |
| }) | |
| It("generates a valid grammar from JSON Objects without a suffix that could return text or an array of tools or just string", func() { | |
| structuredGrammar := JSONFunctionStructure{ | |
| OneOf: testFunctionsName} | |
| grammar, err := structuredGrammar.Grammar(EnableMaybeString, EnableMaybeArray) | |
| Expect(err).To(BeNil()) | |
| results := strings.Split( | |
| strings.Join([]string{ | |
| rootResult(`mixedstring | arr | realvalue`), | |
| "mixedstring ::= freestring | freestring arr | freestring realvalue"}, "\n"), | |
| "\n") | |
| for _, r := range results { | |
| if r != "" { | |
| Expect(grammar).To(ContainSubstring(r)) | |
| } | |
| } | |
| Expect(len(results)).To(Equal(len(strings.Split(grammar, "\n"))), grammar) | |
| }) | |
| It("generates a valid grammar from JSON Objects without a suffix that could return text or an array of tools or just string. Disables mixedstring", func() { | |
| structuredGrammar := JSONFunctionStructure{ | |
| OneOf: testFunctionsName} | |
| grammar, err := structuredGrammar.Grammar(EnableMaybeString, EnableMaybeArray, NoMixedFreeString) | |
| Expect(err).To(BeNil()) | |
| results := strings.Split( | |
| strings.Join([]string{ | |
| rootResult(`freestring | arr | realvalue`), | |
| "mixedstring ::= freestring | freestring arr | freestring realvalue"}, "\n"), | |
| "\n") | |
| for _, r := range results { | |
| if r != "" { | |
| Expect(grammar).To(ContainSubstring(r)) | |
| } | |
| } | |
| Expect(len(results)).To(Equal(len(strings.Split(grammar, "\n"))), grammar) | |
| }) | |
| It("generates parallel tools without newlines in JSON", func() { | |
| structuredGrammar := JSONFunctionStructure{ | |
| OneOf: testFunctionsName} | |
| content := `arr ::= | |
| "[" ( | |
| realvalue | |
| ("," realvalue)* | |
| )? "]"` | |
| grammar, err := structuredGrammar.Grammar(EnableMaybeString, EnableMaybeArray, DisableParallelNewLines) | |
| Expect(err).To(BeNil()) | |
| results := strings.Split(content, "\n") | |
| for _, r := range results { | |
| if r != "" { | |
| Expect(grammar).To(ContainSubstring(r)) | |
| } | |
| } | |
| }) | |
| }) | |
| }) | |