fix(functions): do not duplicate function when valid JSON is inside XML tags (#8043)

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
Ettore Di Giacinto
2026-01-14 23:42:00 +01:00
committed by GitHub
parent cbaa793520
commit d20a113aef
2 changed files with 29 additions and 4 deletions

View File

@@ -1492,7 +1492,7 @@ func ParseFunctionCall(llmresult string, functionConfig FunctionsConfig) []FuncC
results := []FuncCallResults{}
llmResults := []string{}
returnResult := func(results []string) (result []FuncCallResults, e error) {
extractJSON := func(results []string) (result []FuncCallResults, e error) {
// As we have to change the result before processing, we can't stream the answer token-by-token (yet?)
result = make([]FuncCallResults, 0)
@@ -1593,7 +1593,7 @@ func ParseFunctionCall(llmresult string, functionConfig FunctionsConfig) []FuncC
if len(llmResults) == 0 {
llmResults = append(llmResults, llmresult)
}
results, _ = returnResult(llmResults)
results, _ = extractJSON(llmResults)
}
// Determine which XML format to use (if any)
@@ -1632,8 +1632,16 @@ func ParseFunctionCall(llmresult string, functionConfig FunctionsConfig) []FuncC
// But skip if JSONRegexMatch or ResponseRegex was used (they already extracted the content)
xmlResults, err := ParseXML(llmresult, xmlFormat)
if err == nil && len(xmlResults) > 0 {
xlog.Debug("Found additional XML tool calls alongside JSON", "xml_count", len(xmlResults))
results = append(results, xmlResults...)
// Check if JSON is inside XML tags, if so, skip it
for _, result := range xmlResults {
jsonResults, _ := extractJSON([]string{result.Name})
if len(jsonResults) > 0 {
xlog.Debug("Found valid JSON inside XML tags, skipping XML parsing", "json_count", len(jsonResults))
} else {
xlog.Debug("Found additional XML tool calls alongside JSON", "xml_count", len(xmlResults))
results = append(results, xmlResults...)
}
}
}
}

View File

@@ -820,6 +820,23 @@ Final text`
Expect(results[0].Name).To(Equal("first"))
Expect(results[1].Name).To(Equal("second"))
})
It("should not duplicate parse JSON inside tool_call tags", func() {
// This test reproduces a bug where JSON inside <tool_call> tags
// gets parsed twice: once as JSON (correctly) and once as XML (incorrectly)
// The XML parser should not run when JSON parsing already found valid results
input := `<tool_call>
{"name": "get_current_weather", "arguments": {"location": "Beijing", "unit": "celsius"}}
</tool_call>`
results := ParseFunctionCall(input, functionConfig)
// Should only have 1 result, not 2 (one correct + one malformed)
Expect(results).To(HaveLen(1), "Should not create duplicate entries when JSON is inside XML tags")
Expect(results[0].Name).To(Equal("get_current_weather"))
Expect(results[0].Arguments).To(Equal(`{"location":"Beijing","unit":"celsius"}`))
// Verify the name is not the entire JSON object (which would indicate malformed XML parsing)
Expect(results[0].Name).NotTo(ContainSubstring(`{"name"`), "Function name should not contain JSON object")
})
})
Context("Iterative Parser (ChatMsgParser)", func() {