Skip to content

Commit a447c4e

Browse files
authored
Merge pull request #80 from WordPress/fix/empty-tool-calls-parameters
Fix empty tool calls and arguments in OpenAI compatible models
2 parents 983d66e + 133e576 commit a447c4e

File tree

2 files changed

+73
-6
lines changed

2 files changed

+73
-6
lines changed

src/Providers/OpenAiCompatibleImplementation/AbstractOpenAiCompatibleTextGenerationModel.php

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -240,17 +240,24 @@ function (Message $message): array {
240240
'tool_call_id' => $functionResponse->getId(),
241241
];
242242
}
243-
return [
243+
$messageData = [
244244
'role' => $this->getMessageRoleString($message->getRole()),
245245
'content' => array_values(array_filter(array_map(
246246
[$this, 'getMessagePartContentData'],
247247
$messageParts
248248
))),
249-
'tool_calls' => array_values(array_filter(array_map(
250-
[$this, 'getMessagePartToolCallData'],
251-
$messageParts
252-
))),
253249
];
250+
251+
// Only include tool_calls if there are any (OpenAI rejects empty arrays).
252+
$toolCalls = array_values(array_filter(array_map(
253+
[$this, 'getMessagePartToolCallData'],
254+
$messageParts
255+
)));
256+
if (!empty($toolCalls)) {
257+
$messageData['tool_calls'] = $toolCalls;
258+
}
259+
260+
return $messageData;
254261
},
255262
$messages
256263
);
@@ -398,12 +405,26 @@ protected function getMessagePartToolCallData(MessagePart $part): ?array
398405
'The function call typed message part must contain a function call.'
399406
);
400407
}
408+
$args = $functionCall->getArgs();
409+
410+
/*
411+
* Ensure empty arrays become empty objects for JSON encoding.
412+
* While in theory the JSON schema could also dictate a type of
413+
* 'array', in practice function arguments are typically of type
414+
* 'object'. More importantly, the OpenAI API specification seems
415+
* to expect that, and does not support passing arrays as the root
416+
* value.
417+
*/
418+
if (is_array($args) && count($args) === 0) {
419+
$args = new \stdClass();
420+
}
421+
401422
return [
402423
'type' => 'function',
403424
'id' => $functionCall->getId(),
404425
'function' => [
405426
'name' => $functionCall->getName(),
406-
'arguments' => json_encode($functionCall->getArgs()),
427+
'arguments' => json_encode($args),
407428
],
408429
];
409430
}

tests/unit/Providers/OpenAiCompatibleImplementation/AbstractOpenAiCompatibleTextGenerationModelTest.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,26 @@ public function testPrepareMessagesParamModelMessageWithFunctionCall(): void
520520
);
521521
}
522522

523+
/**
524+
* Tests prepareMessagesParam with message having no function calls (tool_calls should not be included).
525+
*
526+
* @return void
527+
*/
528+
public function testPrepareMessagesParamNoToolCalls(): void
529+
{
530+
$message = new Message(
531+
MessageRoleEnum::model(),
532+
[new MessagePart('Hello, I am a simple text response.')]
533+
);
534+
535+
$model = $this->createModel();
536+
$prepared = $model->exposePrepareMessagesParam([$message], null);
537+
538+
$this->assertCount(1, $prepared);
539+
$this->assertEquals('assistant', $prepared[0]['role']);
540+
$this->assertArrayNotHasKey('tool_calls', $prepared[0]); // Should not have tool_calls field at all
541+
}
542+
523543
/**
524544
* Tests prepareMessagesParam() with function response.
525545
*
@@ -744,6 +764,32 @@ public function testGetMessagePartToolCallDataFunctionCallPart(): void
744764
], $data);
745765
}
746766

767+
/**
768+
* Tests getMessagePartToolCallData() with empty arguments (should encode as empty object).
769+
*
770+
* @return void
771+
*/
772+
public function testGetMessagePartToolCallDataEmptyArguments(): void
773+
{
774+
$functionCall = new FunctionCall(
775+
'call_1',
776+
'list_capabilities',
777+
[] // Empty arguments array
778+
);
779+
$part = new MessagePart($functionCall);
780+
$model = $this->createModel();
781+
$data = $model->exposeGetMessagePartToolCallData($part);
782+
783+
$this->assertEquals([
784+
'type' => 'function',
785+
'id' => 'call_1',
786+
'function' => [
787+
'name' => 'list_capabilities',
788+
'arguments' => '{}', // Should be empty object, not empty array
789+
],
790+
], $data);
791+
}
792+
747793
/**
748794
* Tests getMessagePartToolCallData() with text part (should return null).
749795
*

0 commit comments

Comments
 (0)