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)) | |
} | |
} | |
}) | |
}) | |
}) | |