Make Regex replacement stable, drop lookaheads

Signed-off-by: mudler <mudler@localai.io>
This commit is contained in:
mudler 2024-05-19 00:42:51 +02:00
parent a348b15921
commit 3e6f1e50e3
2 changed files with 36 additions and 22 deletions

View File

@ -3,10 +3,10 @@ package functions
import (
"encoding/json"
"regexp"
"strings"
"github.com/go-skynet/LocalAI/pkg/utils"
"github.com/rs/zerolog/log"
"gopkg.in/yaml.v2"
)
// FunctionsConfig is the configuration for the tool/function call.
@ -44,7 +44,7 @@ type FunctionsConfig struct {
GrammarPrefix string `yaml:"grammar_prefix"`
// ReplaceResults allow to replace strings in the results before parsing them
ReplaceResults map[string]string `yaml:"replace_results"`
ReplaceResults yaml.MapSlice `yaml:"replace_results"`
// FunctionName enable the LLM to return { "name": "function_name", "arguments": { "arg1": "value1", "arg2": "value2" } }
// instead of { "function": "function_name", "arguments": { "arg1": "value1", "arg2": "value2" } }.
@ -60,11 +60,12 @@ type FuncCallResults struct {
func ParseFunctionCall(llmresult string, functionConfig FunctionsConfig) []FuncCallResults {
log.Debug().Msgf("LLM result: %s", llmresult)
for k, v := range functionConfig.ReplaceResults {
log.Debug().Msgf("Replacing %s with %s", k, v)
re := regexp.MustCompile(k)
llmresult = re.ReplaceAllString(llmresult, v)
}
for _, item := range functionConfig.ReplaceResults {
k, v := item.Key.(string), item.Value.(string)
log.Debug().Msgf("Replacing %s with %s", k, v)
re := regexp.MustCompile(k)
llmresult = re.ReplaceAllString(llmresult, v)
}
log.Debug().Msgf("LLM result(processed): %s", llmresult)

View File

@ -4,6 +4,7 @@ import (
. "github.com/go-skynet/LocalAI/pkg/functions"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"gopkg.in/yaml.v2"
)
var _ = Describe("LocalAI function parse tests", func() {
@ -119,9 +120,10 @@ Some text before the JSON
{"function": "add", "arguments": {"x": 5, "y": 3}}
Some text after the JSON
`
functionConfig.ReplaceResults = map[string]string{
`(?s)^[^\\{]*(?=\\{)`: "",
`(?s)\\}[^{}]+$`: "",
functionConfig.ReplaceResults = yaml.MapSlice{
{Key: `(?s)^[^{\[]*`, Value: ""},
{Key: `(?s)[^}\]]*$`, Value: ""},
}
results := ParseFunctionCall(input, functionConfig)
@ -136,9 +138,9 @@ Some text before the JSON
[{"function": "add", "arguments": {"x": 5, "y": 3}}, {"function": "subtract", "arguments": {"x": 10, "y": 7}}]
Some text after the JSON
`
functionConfig.ReplaceResults = map[string]string{
`(?s)^[^\\[]{*(?=\\[)`: "",
`(?s)\\][^]]+$`: "",
functionConfig.ReplaceResults = yaml.MapSlice{
{Key: `(?s)^[^{\[]*`, Value: ""},
{Key: `(?s)[^}\]]*$`, Value: ""},
}
results := ParseFunctionCall(input, functionConfig)
@ -152,21 +154,32 @@ Some text after the JSON
It("should convert single-quoted key-value pairs to double-quoted and escape double quotes within values", func() {
input := `
Some text before the JSON
{'function': '"add"', 'arguments': {'x': 5, 'y': '"value"'}}
{'function': '"add"', 'arguments': {'x': 5, 'z': '"v"', 'y': 'v"value"'}}
Some text after the JSON
`
functionConfig.ReplaceResults = map[string]string{
`(?s)^[^\\{]*(?=\\{)`: "",
`(?s)\\}[^{}]+$`: "",
`'([^']+)':\s*'([^']*)'`: "\"$1\": \"$2\"",
`(?<!\\)"`: "\\\"",
// Regex to match non-JSON characters before the JSON structure
//reBefore := regexp.MustCompile(`(?s)^.*?(?=\{|\[)`)
// Regex to match non-JSON characters after the JSON structure
//reAfter := regexp.MustCompile(`(?s)(?<=\}|\]).*$`)
functionConfig.ReplaceResults = yaml.MapSlice{
{Key: `(?s)^[^{\[]*`, Value: ""},
{Key: `(?s)[^}\]]*$`, Value: ""},
// Regex pattern to match single quotes around keys and values
// Step 1: Replace single quotes around keys and values with double quotes
{Key: `'([^']*?)'`, Value: `_DQUOTE_${1}_DQUOTE_`},
// Step 2: Replace double quotes inside values with placeholders
{Key: `\\"`, Value: `__TEMP_QUOTE__`},
{Key: `"`, Value: `\"`},
{Key: `\'`, Value: `'`},
{Key: `_DQUOTE_`, Value: `"`},
{Key: `__TEMP_QUOTE__`, Value: `"`},
}
results := ParseFunctionCall(input, functionConfig)
Expect(results).To(HaveLen(1))
Expect(results[0].Name).To(Equal("\\\"add\\\""))
Expect(results[0].Arguments).To(Equal(`{"x":5,"y":"\\\"value\\\""}`))
Expect(results[0].Name).To(Equal("\"add\""))
Expect(results[0].Arguments).To(Equal(`{"x":5,"y":"v\"value\"","z":"\"v\""}`))
})
})
})