From f253b092876c88922241c51428a519dc5ce4704c Mon Sep 17 00:00:00 2001 From: Gracjan Kubicki Date: Tue, 19 May 2026 12:43:55 +0200 Subject: [PATCH] Fix stored tool conversation replay order --- src/Storage/DatabaseConversationStore.php | 6 ++- .../Storage/DatabaseConversationStoreTest.php | 45 ++++++++++++++++++- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/Storage/DatabaseConversationStore.php b/src/Storage/DatabaseConversationStore.php index 2b31b4fc7..b426595d4 100644 --- a/src/Storage/DatabaseConversationStore.php +++ b/src/Storage/DatabaseConversationStore.php @@ -159,7 +159,7 @@ public function getLatestConversationMessages(string $conversationId, int $limit $messages = []; $messages[] = new AssistantMessage( - $record->content ?: '', + '', $toolCalls->map(fn ($toolCall) => new ToolCall( id: $toolCall['id'], name: $toolCall['name'], @@ -182,6 +182,10 @@ public function getLatestConversationMessages(string $conversationId, int $limit ); } + if ($record->content !== '') { + $messages[] = new AssistantMessage($record->content); + } + return $messages; } diff --git a/tests/Feature/Storage/DatabaseConversationStoreTest.php b/tests/Feature/Storage/DatabaseConversationStoreTest.php index 81df3099a..5a7978f7f 100644 --- a/tests/Feature/Storage/DatabaseConversationStoreTest.php +++ b/tests/Feature/Storage/DatabaseConversationStoreTest.php @@ -169,11 +169,52 @@ $messages = $store->getLatestConversationMessages($conversationId, 10); - expect($messages)->toHaveCount(2) + expect($messages)->toHaveCount(3) ->and($messages[0])->toBeInstanceOf(AssistantMessage::class) ->and($messages[0]->toolCalls->keys()->all())->toBe([0, 1]) ->and($messages[1])->toBeInstanceOf(ToolResultMessage::class) - ->and($messages[1]->toolResults->keys()->all())->toBe([0, 1]); + ->and($messages[1]->toolResults->keys()->all())->toBe([0, 1]) + ->and($messages[2])->toBeInstanceOf(AssistantMessage::class) + ->and($messages[2]->content)->toBe('The order has shipped.'); +}); + +test('it replays stored tool conversations before the final assistant response', function () { + $store = new DatabaseConversationStore; + $conversationId = $store->storeConversation(1, 'Tool conversation'); + + DB::table('agent_conversation_messages')->insert([ + 'id' => 'message-1', + 'conversation_id' => $conversationId, + 'user_id' => 1, + 'agent' => ToolUsingAgent::class, + 'role' => 'assistant', + 'content' => 'The order has shipped.', + 'attachments' => '[]', + 'tool_calls' => json_encode([ + ['id' => 'call-1', 'name' => 'lookup_order', 'arguments' => ['id' => 1], 'result_id' => 'result-1'], + ]), + 'tool_results' => json_encode([ + ['id' => 'call-1', 'name' => 'lookup_order', 'arguments' => ['id' => 1], 'result' => ['status' => 'shipped'], 'result_id' => 'result-1'], + ]), + 'usage' => '[]', + 'meta' => '[]', + 'created_at' => now(), + 'updated_at' => now(), + ]); + + $messages = $store->getLatestConversationMessages($conversationId, 10); + + expect($messages)->toHaveCount(3) + ->and($messages[0])->toBeInstanceOf(AssistantMessage::class) + ->and($messages[0]->content)->toBe('') + ->and($messages[0]->toolCalls)->toHaveCount(1) + ->and($messages[0]->toolCalls[0]->resultId)->toBe('result-1') + ->and($messages[1])->toBeInstanceOf(ToolResultMessage::class) + ->and($messages[1]->toolResults)->toHaveCount(1) + ->and($messages[1]->toolResults[0]->resultId)->toBe('result-1') + ->and($messages[2])->toBeInstanceOf(AssistantMessage::class) + ->and($messages[2]->content)->toBe('The order has shipped.') + ->and($messages[2]->toolCalls)->toBeEmpty(); }); test('user messages with stored attachments are rehydrated as UserMessage', function () {