From 5d31e5269db45986ae2a3ebf26dddc338db2e4ae Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Fri, 31 May 2024 22:52:02 +0200 Subject: [PATCH] feat(functions): allow `response_regex` to be a list (#2447) feat(functions): allow regex match to be a list Signed-off-by: Ettore Di Giacinto --- .../content/docs/features/openai-functions.md | 5 ++-- pkg/functions/parse.go | 28 ++++++++++--------- pkg/functions/parse_test.go | 2 +- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/docs/content/docs/features/openai-functions.md b/docs/content/docs/features/openai-functions.md index feb8bc74f12c..cb667815e938 100644 --- a/docs/content/docs/features/openai-functions.md +++ b/docs/content/docs/features/openai-functions.md @@ -93,8 +93,9 @@ parameters: function: # set to true to not use grammars no_grammar: true - # set a regex to extract the function tool arguments from the LLM response - response_regex: "(?P\w+)\s*\((?P.*)\)" + # set one or more regexes used to extract the function tool arguments from the LLM response + response_regex: + - "(?P\w+)\s*\((?P.*)\)" ``` The response regex have to be a regex with named parameters to allow to scan the function name and the arguments. For instance, consider: diff --git a/pkg/functions/parse.go b/pkg/functions/parse.go index ff8357b1f14f..1be681c02e42 100644 --- a/pkg/functions/parse.go +++ b/pkg/functions/parse.go @@ -52,7 +52,7 @@ type FunctionsConfig struct { NoActionDescriptionName string `yaml:"no_action_description_name"` // ResponseRegex is a named regex to extract the function name and arguments from the response - ResponseRegex string `yaml:"response_regex"` + ResponseRegex []string `yaml:"response_regex"` // JSONRegexMatch is a regex to extract the JSON object from the response JSONRegexMatch []string `yaml:"json_regex_match"` @@ -228,24 +228,26 @@ func ParseFunctionCall(llmresult string, functionConfig FunctionsConfig) []FuncC } } - if functionConfig.ResponseRegex != "" { + if len(functionConfig.ResponseRegex) > 0 { // We use named regexes here to extract the function name and arguments // obviously, this expects the LLM to be stable and return correctly formatted JSON // TODO: optimize this and pre-compile it - var respRegex = regexp.MustCompile(functionConfig.ResponseRegex) - matches := respRegex.FindAllStringSubmatch(llmresult, -1) - for _, match := range matches { - for i, name := range respRegex.SubexpNames() { - if i != 0 && name != "" && len(match) > i { - result[name] = match[i] + for _, r := range functionConfig.ResponseRegex { + var respRegex = regexp.MustCompile(r) + matches := respRegex.FindAllStringSubmatch(llmresult, -1) + for _, match := range matches { + for i, name := range respRegex.SubexpNames() { + if i != 0 && name != "" && len(match) > i { + result[name] = match[i] + } } - } - functionName := result[functionNameKey] - if functionName == "" { - return results + functionName := result[functionNameKey] + if functionName == "" { + return results + } + results = append(results, FuncCallResults{Name: result[functionNameKey], Arguments: result["arguments"]}) } - results = append(results, FuncCallResults{Name: result[functionNameKey], Arguments: result["arguments"]}) } } else { if len(llmResults) == 0 { diff --git a/pkg/functions/parse_test.go b/pkg/functions/parse_test.go index 01d8469f94f4..dd58069fd9e4 100644 --- a/pkg/functions/parse_test.go +++ b/pkg/functions/parse_test.go @@ -28,7 +28,7 @@ var _ = Describe("LocalAI function parse tests", func() { Context("when not using grammars and regex is needed", func() { It("should extract function name and arguments from the regex", func() { input := `add({"x":5,"y":3})` - functionConfig.ResponseRegex = `(?P\w+)\s*\((?P.*)\)` + functionConfig.ResponseRegex = []string{`(?P\w+)\s*\((?P.*)\)`} results := ParseFunctionCall(input, functionConfig) Expect(results).To(HaveLen(1))