diff --git a/src/Providers/Groq/Handlers/Text.php b/src/Providers/Groq/Handlers/Text.php index a88c3007e..b81c86e23 100644 --- a/src/Providers/Groq/Handlers/Text.php +++ b/src/Providers/Groq/Handlers/Text.php @@ -148,10 +148,28 @@ protected function addStep(array $data, Request $request, ClientResponse $client */ protected function mapToolCalls(array $toolCalls): array { - return array_map(fn (array $toolCall): ToolCall => new ToolCall( - id: data_get($toolCall, 'id'), - name: data_get($toolCall, 'function.name'), - arguments: data_get($toolCall, 'function.arguments'), - ), $toolCalls); + return array_map(function (array $toolCall): ToolCall { + $name = data_get($toolCall, 'function.name', ''); + $arguments = data_get($toolCall, 'function.arguments', '{}'); + + // Some Llama models (e.g. llama-3.3-70b-versatile on Groq) embed + // the arguments JSON directly in the function name field, separated + // by a comma: "tool_name,{\"arg\":\"val\"}". Groq then rejects the + // follow-up turn because the mangled name is not in request.tools. + // Detect this pattern and split the name from the inline arguments. + if (is_string($name) && str_contains($name, ',{')) { + [$extractedName, $inlineArgs] = explode(',', $name, 2); + if (json_decode($inlineArgs) !== null) { + $name = $extractedName; + $arguments = $inlineArgs; + } + } + + return new ToolCall( + id: data_get($toolCall, 'id'), + name: $name, + arguments: $arguments ?: '{}', + ); + }, $toolCalls); } }