[ [ "message" => [ "role" => "assistant", "content" => null, "tool_calls" => [ [ "id" => "call_123", "function" => [ "name" => "fetch_message", "arguments" => "{\"md5\":\"abc123\"}", ], ], ], ], ], ], ]; $toolCalls = $parser->extract($response); $this->assertNotNull($toolCalls); $this->assertCount(1, $toolCalls); $this->assertEquals("fetch_message", $toolCalls[0]["name"]); $this->assertEquals(["md5" => "abc123"], $toolCalls[0]["arguments"]); } /** * Test extracting raw tool calls from content. */ public function testExtractRawToolCalls(): void { $parser = new ToolCallParser(ToolCallMode::RAW); // The actual content from SmolLM3 response $rawContent = chr(0xD9) . chr(0xA7) . "{\"name\": \"web_search\", \"arguments\": {\"query\": \"nvidia stock\"}}" . chr(0xD9) . chr(0xA7); $response = [ "choices" => [ [ "message" => [ "role" => "assistant", "content" => $rawContent, "tool_calls" => [], ], ], ], ]; $toolCalls = $parser->extract($response); $this->assertNotNull($toolCalls); $this->assertGreaterThanOrEqual(1, count($toolCalls)); $this->assertEquals("web_search", $toolCalls[0]["name"]); } /** * Test auto-detect mode picks native tool calls. */ public function testAutoDetectNative(): void { $parser = new ToolCallParser(ToolCallMode::AUTO); $response = [ "choices" => [ [ "message" => [ "role" => "assistant", "content" => null, "tool_calls" => [ [ "id" => "call_456", "function" => [ "name" => "fetch_message", "arguments" => "{\"md5\":\"def456\"}", ], ], ], ], ], ], ]; $detectedMode = $parser->detectMode($response); $this->assertEquals(ToolCallMode::NATIVE, $detectedMode); $toolCalls = $parser->extract($response); $this->assertNotNull($toolCalls); } /** * Test no tool calls returns null. */ public function testNoToolCalls(): void { $parser = new ToolCallParser(ToolCallMode::AUTO); $response = [ "choices" => [ [ "message" => [ "role" => "assistant", "content" => "Hello! How can I help you?", ], ], ], ]; $toolCalls = $parser->extract($response); $this->assertNull($toolCalls); } /** * Test hasToolCalls helper. */ public function testHasToolCalls(): void { $parser = new ToolCallParser(ToolCallMode::NATIVE); $withTools = [ "choices" => [ ["message" => ["tool_calls" => [["function" => ["name" => "test"]]]]], ], ]; $withoutTools = [ "choices" => [ ["message" => ["content" => "Hello"]], ], ]; $this->assertTrue($parser->hasToolCalls($withTools)); $this->assertFalse($parser->hasToolCalls($withoutTools)); } }