refactor(toolcall): enhance tool call extraction with multiple keywords and safety limits

- Add support for multiple keywords: tool_calls, function.name:, [tool_call_history]
- Add OOM protection with search limits in extractToolCallObjects
- Add max scan length limit in extractJSONObject to prevent OOM on unclosed objects
- Update tool_sieve to handle more tool call patterns
- Add loose JSON repair in parseToolCallPayload for better error recovery

This improves DeepSeek tool call parsing robustness.
This commit is contained in:
huangxun
2026-03-17 16:28:27 +08:00
parent 16216cc2ca
commit c9c59f2490
7 changed files with 95 additions and 39 deletions

View File

@@ -8,15 +8,15 @@ import (
)
func BuildChatCompletion(completionID, model, finalPrompt, finalThinking, finalText string, toolNames []string) map[string]any {
detected := util.ParseStandaloneToolCalls(finalText, toolNames)
detected := util.ParseStandaloneToolCallsDetailed(finalText, toolNames)
finishReason := "stop"
messageObj := map[string]any{"role": "assistant", "content": finalText}
if strings.TrimSpace(finalThinking) != "" {
messageObj["reasoning_content"] = finalThinking
}
if len(detected) > 0 {
if len(detected.Calls) > 0 {
finishReason = "tool_calls"
messageObj["tool_calls"] = util.FormatOpenAIToolCalls(detected)
messageObj["tool_calls"] = util.FormatOpenAIToolCalls(detected.Calls)
messageObj["content"] = nil
}

View File

@@ -13,12 +13,12 @@ import (
func BuildResponseObject(responseID, model, finalPrompt, finalThinking, finalText string, toolNames []string) map[string]any {
// Strict mode: only standalone, structured tool-call payloads are treated
// as executable tool calls.
detected := util.ParseStandaloneToolCalls(finalText, toolNames)
detected := util.ParseStandaloneToolCallsDetailed(finalText, toolNames)
exposedOutputText := finalText
output := make([]any, 0, 2)
if len(detected) > 0 {
if len(detected.Calls) > 0 {
exposedOutputText = ""
output = append(output, toResponsesFunctionCallItems(detected)...)
output = append(output, toResponsesFunctionCallItems(detected.Calls)...)
} else {
content := make([]any, 0, 2)
if finalThinking != "" {