Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/toolbox/weather-event.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
// Add tool call result listener to enforce chain exits direct with structured response for weather tools
$eventDispatcher->addListener(ToolCallsExecuted::class, function (ToolCallsExecuted $event): void {
foreach ($event->toolResults as $toolCallResult) {
if (str_starts_with($toolCallResult->toolCall->name, 'weather_')) {
if (str_starts_with($toolCallResult->toolCall->getName(), 'weather_')) {
$event->result = new ObjectResult($toolCallResult->result);
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/agent/src/Toolbox/Exception/ToolExecutionException.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ final class ToolExecutionException extends \RuntimeException implements ToolExec

public static function executionFailed(ToolCall $toolCall, \Throwable $previous): self
{
$exception = new self(\sprintf('Execution of tool "%s" failed with error: %s', $toolCall->name, $previous->getMessage()), previous: $previous);
$exception = new self(\sprintf('Execution of tool "%s" failed with error: %s', $toolCall->getName(), $previous->getMessage()), previous: $previous);
$exception->toolCall = $toolCall;

return $exception;
}

public function getToolCallResult(): string
{
return \sprintf('An error occurred while executing tool "%s".', $this->toolCall->name);
return \sprintf('An error occurred while executing tool "%s".', $this->toolCall->getName());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ final class ToolNotFoundException extends \RuntimeException implements Exception

public static function notFoundForToolCall(ToolCall $toolCall): self
{
$exception = new self(\sprintf('Tool not found for call: %s.', $toolCall->name));
$exception = new self(\sprintf('Tool not found for call: %s.', $toolCall->getName()));
$exception->toolCall = $toolCall;

return $exception;
Expand Down
2 changes: 1 addition & 1 deletion src/agent/src/Toolbox/FaultTolerantToolbox.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public function execute(ToolCall $toolCall): mixed
} catch (ToolNotFoundException) {
$names = array_map(fn (Tool $metadata) => $metadata->name, $this->getTools());

return \sprintf('Tool "%s" was not found, please use one of these: %s', $toolCall->name, implode(', ', $names));
return \sprintf('Tool "%s" was not found, please use one of these: %s', $toolCall->getName(), implode(', ', $names));
}
}
}
6 changes: 3 additions & 3 deletions src/agent/src/Toolbox/ToolCallArgumentResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,14 @@ public function resolveArguments(Tool $metadata, ToolCall $toolCall): array
$arguments = [];

foreach ($parameters as $name => $reflectionParameter) {
if (!\array_key_exists($name, $toolCall->arguments)) {
if (!\array_key_exists($name, $toolCall->getArguments())) {
if (!$reflectionParameter->isOptional()) {
throw new ToolException(\sprintf('Parameter "%s" is mandatory for tool "%s".', $name, $toolCall->name));
throw new ToolException(\sprintf('Parameter "%s" is mandatory for tool "%s".', $name, $toolCall->getName()));
}
continue;
}

$value = $toolCall->arguments[$name];
$value = $toolCall->getArguments()[$name];
$parameterType = $this->typeResolver->resolve($reflectionParameter);
$dimensions = '';
while ($parameterType instanceof CollectionType) {
Expand Down
6 changes: 3 additions & 3 deletions src/agent/src/Toolbox/Toolbox.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public function execute(ToolCall $toolCall): mixed
$tool = $this->getExecutable($metadata);

try {
$this->logger->debug(\sprintf('Executing tool "%s".', $toolCall->name), $toolCall->arguments);
$this->logger->debug(\sprintf('Executing tool "%s".', $toolCall->getName()), $toolCall->getArguments());

$arguments = $this->argumentResolver->resolveArguments($metadata, $toolCall);
$this->eventDispatcher?->dispatch(new ToolCallArgumentsResolved($tool, $metadata, $arguments));
Expand All @@ -88,7 +88,7 @@ public function execute(ToolCall $toolCall): mixed
$this->eventDispatcher?->dispatch(new ToolCallFailed($tool, $metadata, $arguments ?? [], $e));
throw $e;
} catch (\Throwable $e) {
$this->logger->warning(\sprintf('Failed to execute tool "%s".', $toolCall->name), ['exception' => $e]);
$this->logger->warning(\sprintf('Failed to execute tool "%s".', $toolCall->getName()), ['exception' => $e]);
$this->eventDispatcher?->dispatch(new ToolCallFailed($tool, $metadata, $arguments ?? [], $e));
throw ToolExecutionException::executionFailed($toolCall, $e);
}
Expand All @@ -99,7 +99,7 @@ public function execute(ToolCall $toolCall): mixed
private function getMetadata(ToolCall $toolCall): Tool
{
foreach ($this->getTools() as $metadata) {
if ($metadata->name === $toolCall->name) {
if ($metadata->name === $toolCall->getName()) {
return $metadata;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ public function normalize(mixed $data, ?string $format = null, array $context =
'content' => $data->hasToolCalls() ? array_map(static function (ToolCall $toolCall) {
return [
'type' => 'tool_use',
'id' => $toolCall->id,
'name' => $toolCall->name,
'input' => [] !== $toolCall->arguments ? $toolCall->arguments : new \stdClass(),
'id' => $toolCall->getId(),
'name' => $toolCall->getName(),
'input' => [] !== $toolCall->getArguments() ? $toolCall->getArguments() : new \stdClass(),
];
}, $data->toolCalls) : $data->content,
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public function normalize(mixed $data, ?string $format = null, array $context =
'content' => [
[
'type' => 'tool_result',
'tool_use_id' => $data->toolCall->id,
'tool_use_id' => $data->toolCall->getId(),
'content' => $data->content,
],
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ public function normalize(mixed $data, ?string $format = null, array $context =
'content' => array_map(static function (ToolCall $toolCall) {
return [
'toolUse' => [
'toolUseId' => $toolCall->id,
'name' => $toolCall->name,
'input' => [] !== $toolCall->arguments ? $toolCall->arguments : new \stdClass(),
'toolUseId' => $toolCall->getId(),
'name' => $toolCall->getName(),
'input' => [] !== $toolCall->getArguments() ? $toolCall->getArguments() : new \stdClass(),
],
];
}, $data->toolCalls),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public function normalize(mixed $data, ?string $format = null, array $context =
'content' => [
[
'toolResult' => [
'toolUseId' => $data->toolCall->id,
'toolUseId' => $data->toolCall->getId(),
'content' => [['json' => $data->content]],
],
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ public function normalize(mixed $data, ?string $format = null, array $context =

if (isset($data->toolCalls[0])) {
$normalized['functionCall'] = [
'id' => $data->toolCalls[0]->id,
'name' => $data->toolCalls[0]->name,
'id' => $data->toolCalls[0]->getId(),
'name' => $data->toolCalls[0]->getName(),
];

if ($data->toolCalls[0]->arguments) {
$normalized['functionCall']['args'] = $data->toolCalls[0]->arguments;
if ($data->toolCalls[0]->getArguments()) {
$normalized['functionCall']['args'] = $data->toolCalls[0]->getArguments();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ public function normalize(mixed $data, ?string $format = null, array $context =

return [[
'functionResponse' => array_filter([
'id' => $data->toolCall->id,
'name' => $data->toolCall->name,
'id' => $data->toolCall->getId(),
'name' => $data->toolCall->getName(),
'response' => \is_array($resultContent) ? $resultContent : [
'rawResponse' => $resultContent, // Gemini expects the response to be an object, but not everyone uses objects as their responses.
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ public function normalize(mixed $data, ?string $format = null, array $context =
return [
'type' => 'function',
'function' => [
'name' => $message->name,
'name' => $message->getName(),
// stdClass forces empty object
'arguments' => [] === $message->arguments ? new \stdClass() : $message->arguments,
'arguments' => [] === $message->getArguments() ? new \stdClass() : $message->getArguments(),
],
];
}, $data->toolCalls ?? [])),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ public function normalize(mixed $data, ?string $format = null, array $context =

if (isset($data->toolCalls[0])) {
$normalized['functionCall'] = [
'name' => $data->toolCalls[0]->name,
'name' => $data->toolCalls[0]->getName(),
];

if ($data->toolCalls[0]->arguments) {
$normalized['functionCall']['args'] = $data->toolCalls[0]->arguments;
if ($data->toolCalls[0]->getArguments()) {
$normalized['functionCall']['args'] = $data->toolCalls[0]->getArguments();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public function normalize(mixed $data, ?string $format = null, array $context =

return [[
'functionResponse' => array_filter([
'name' => $data->toolCall->name,
'name' => $data->toolCall->getName(),
'response' => \is_array($resultContent) ? $resultContent : [
'rawResponse' => $resultContent,
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public function normalize(mixed $data, ?string $format = null, array $context =
return [
'role' => $data->getRole()->value,
'content' => $this->normalizer->normalize($data->content, $format, $context),
'tool_call_id' => $data->toolCall->id,
'tool_call_id' => $data->toolCall->getId(),
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ public function getSupportedTypes(?string $format): array
public function normalize(mixed $data, ?string $format = null, array $context = []): array
{
return [
'id' => $data->id,
'id' => $data->getId(),
'type' => 'function',
'function' => [
'name' => $data->name,
'arguments' => json_encode($data->arguments),
'name' => $data->getName(),
'arguments' => json_encode($data->getArguments()),
],
];
}
Expand Down
24 changes: 21 additions & 3 deletions src/platform/src/Result/ToolCall.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,30 @@
* @param array<string, mixed> $arguments
*/
public function __construct(
public string $id,
public string $name,
public array $arguments = [],
private string $id,
private string $name,
private array $arguments = [],
) {
}

public function getId(): string
{
return $this->id;
}

public function getName(): string
{
return $this->name;
}

/**
* @return array<string, mixed>
*/
public function getArguments(): array
{
return $this->arguments;
}

/**
* @return array{
* id: string,
Expand Down
6 changes: 3 additions & 3 deletions src/platform/tests/Bridge/Anthropic/ResultConverterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ public function testConvertThrowsExceptionWhenContentIsToolUseAndLacksText()
$result = $handler->convert(new RawHttpResult($httpResponse));
$this->assertInstanceOf(ToolCallResult::class, $result);
$this->assertCount(1, $result->getContent());
$this->assertSame('toolu_01UM4PcTjC1UDiorSXVHSVFM', $result->getContent()[0]->id);
$this->assertSame('xxx_tool', $result->getContent()[0]->name);
$this->assertSame(['action' => 'get_data'], $result->getContent()[0]->arguments);
$this->assertSame('toolu_01UM4PcTjC1UDiorSXVHSVFM', $result->getContent()[0]->getId());
$this->assertSame('xxx_tool', $result->getContent()[0]->getName());
$this->assertSame(['action' => 'get_data'], $result->getContent()[0]->getArguments());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ public function testConvertToolCallResult()
$this->assertInstanceOf(ToolCallResult::class, $result);
$toolCalls = $result->getContent();
$this->assertCount(1, $toolCalls);
$this->assertSame('toolu_01UM4PcTjC1UDiorSXVHSVFM', $toolCalls[0]->id);
$this->assertSame('get_weather', $toolCalls[0]->name);
$this->assertSame(['location' => 'Paris'], $toolCalls[0]->arguments);
$this->assertSame('toolu_01UM4PcTjC1UDiorSXVHSVFM', $toolCalls[0]->getId());
$this->assertSame('get_weather', $toolCalls[0]->getName());
$this->assertSame(['location' => 'Paris'], $toolCalls[0]->getArguments());
}

#[TestDox('Converts response with multiple tool calls to ToolCallResult')]
Expand Down Expand Up @@ -116,13 +116,13 @@ public function testConvertMultipleToolCalls()
$toolCalls = $result->getContent();
$this->assertCount(2, $toolCalls);

$this->assertSame('toolu_01', $toolCalls[0]->id);
$this->assertSame('get_weather', $toolCalls[0]->name);
$this->assertSame(['location' => 'Paris'], $toolCalls[0]->arguments);
$this->assertSame('toolu_01', $toolCalls[0]->getId());
$this->assertSame('get_weather', $toolCalls[0]->getName());
$this->assertSame(['location' => 'Paris'], $toolCalls[0]->getArguments());

$this->assertSame('toolu_02', $toolCalls[1]->id);
$this->assertSame('get_time', $toolCalls[1]->name);
$this->assertSame(['timezone' => 'UTC'], $toolCalls[1]->arguments);
$this->assertSame('toolu_02', $toolCalls[1]->getId());
$this->assertSame('get_time', $toolCalls[1]->getName());
$this->assertSame(['timezone' => 'UTC'], $toolCalls[1]->getArguments());
}

#[TestDox('Prioritizes tool calls over text in mixed content')]
Expand Down Expand Up @@ -153,7 +153,7 @@ public function testConvertMixedContentWithToolUse()
$this->assertInstanceOf(ToolCallResult::class, $result);
$toolCalls = $result->getContent();
$this->assertCount(1, $toolCalls);
$this->assertSame('toolu_01', $toolCalls[0]->id);
$this->assertSame('toolu_01', $toolCalls[0]->getId());
}

#[TestDox('Throws RuntimeException when response has no content')]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,9 @@ public function testConvertToolCallResult()
$this->assertInstanceOf(ToolCallResult::class, $result);
$toolCalls = $result->getContent();
$this->assertCount(1, $toolCalls);
$this->assertSame('nova-tool-123', $toolCalls[0]->id);
$this->assertSame('calculate', $toolCalls[0]->name);
$this->assertSame(['expression' => '2+2'], $toolCalls[0]->arguments);
$this->assertSame('nova-tool-123', $toolCalls[0]->getId());
$this->assertSame('calculate', $toolCalls[0]->getName());
$this->assertSame(['expression' => '2+2'], $toolCalls[0]->getArguments());
}

#[TestDox('Converts response with multiple tool calls to ToolCallResult')]
Expand Down Expand Up @@ -134,13 +134,13 @@ public function testConvertMultipleToolCalls()
$toolCalls = $result->getContent();
$this->assertCount(2, $toolCalls);

$this->assertSame('nova-tool-1', $toolCalls[0]->id);
$this->assertSame('get_weather', $toolCalls[0]->name);
$this->assertSame(['location' => 'New York'], $toolCalls[0]->arguments);
$this->assertSame('nova-tool-1', $toolCalls[0]->getId());
$this->assertSame('get_weather', $toolCalls[0]->getName());
$this->assertSame(['location' => 'New York'], $toolCalls[0]->getArguments());

$this->assertSame('nova-tool-2', $toolCalls[1]->id);
$this->assertSame('get_time', $toolCalls[1]->name);
$this->assertSame(['timezone' => 'EST'], $toolCalls[1]->arguments);
$this->assertSame('nova-tool-2', $toolCalls[1]->getId());
$this->assertSame('get_time', $toolCalls[1]->getName());
$this->assertSame(['timezone' => 'EST'], $toolCalls[1]->getArguments());
}

#[TestDox('Prioritizes tool calls over text in mixed content')]
Expand Down Expand Up @@ -175,7 +175,7 @@ public function testConvertMixedContentWithToolUse()
$this->assertInstanceOf(ToolCallResult::class, $result);
$toolCalls = $result->getContent();
$this->assertCount(1, $toolCalls);
$this->assertSame('nova-tool-123', $toolCalls[0]->id);
$this->assertSame('nova-tool-123', $toolCalls[0]->getId());
}

#[TestDox('Throws RuntimeException when response has no output')]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public function testReturnsToolCallEvenIfMultipleContentPartsAreGiven()
$this->assertCount(1, $result->getContent());
$toolCall = $result->getContent()[0];
$this->assertInstanceOf(ToolCall::class, $toolCall);
$this->assertSame('1234', $toolCall->id);
$this->assertSame('1234', $toolCall->getId());
}

public function testConvertsInlineDataToBinaryResult()
Expand Down
18 changes: 9 additions & 9 deletions src/platform/tests/Bridge/Ollama/OllamaResultConverterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ public function testConvertToolCallResponse()
$this->assertInstanceOf(ToolCallResult::class, $result);
$toolCalls = $result->getContent();
$this->assertCount(1, $toolCalls);
$this->assertSame('0', $toolCalls[0]->id); // ID is the array index as a string
$this->assertSame('test_function', $toolCalls[0]->name);
$this->assertSame(['arg1' => 'value1'], $toolCalls[0]->arguments);
$this->assertSame('0', $toolCalls[0]->getId()); // ID is the array index as a string
$this->assertSame('test_function', $toolCalls[0]->getName());
$this->assertSame(['arg1' => 'value1'], $toolCalls[0]->getArguments());
}

public function testConvertMultipleToolCallsResponse()
Expand Down Expand Up @@ -103,13 +103,13 @@ public function testConvertMultipleToolCallsResponse()
$toolCalls = $result->getContent();
$this->assertCount(2, $toolCalls);

$this->assertSame('0', $toolCalls[0]->id);
$this->assertSame('function1', $toolCalls[0]->name);
$this->assertSame(['param1' => 'value1'], $toolCalls[0]->arguments);
$this->assertSame('0', $toolCalls[0]->getId());
$this->assertSame('function1', $toolCalls[0]->getName());
$this->assertSame(['param1' => 'value1'], $toolCalls[0]->getArguments());

$this->assertSame('1', $toolCalls[1]->id);
$this->assertSame('function2', $toolCalls[1]->name);
$this->assertSame(['param2' => 'value2'], $toolCalls[1]->arguments);
$this->assertSame('1', $toolCalls[1]->getId());
$this->assertSame('function2', $toolCalls[1]->getName());
$this->assertSame(['param2' => 'value2'], $toolCalls[1]->getArguments());
}

public function testThrowsExceptionWhenNoMessage()
Expand Down
Loading