|
15 | 15 | */ |
16 | 16 | package io.agentscope.core.agui.adapter; |
17 | 17 |
|
18 | | -import static org.junit.jupiter.api.Assertions.assertEquals; |
19 | | -import static org.junit.jupiter.api.Assertions.assertInstanceOf; |
20 | | -import static org.junit.jupiter.api.Assertions.assertNotNull; |
21 | | -import static org.junit.jupiter.api.Assertions.assertTrue; |
22 | | -import static org.mockito.ArgumentMatchers.any; |
23 | | -import static org.mockito.ArgumentMatchers.anyList; |
24 | | -import static org.mockito.Mockito.mock; |
25 | | -import static org.mockito.Mockito.when; |
26 | | - |
27 | 18 | import io.agentscope.core.agent.Agent; |
28 | 19 | import io.agentscope.core.agent.Event; |
29 | 20 | import io.agentscope.core.agent.EventType; |
|
36 | 27 | import io.agentscope.core.message.TextBlock; |
37 | 28 | import io.agentscope.core.message.ToolResultBlock; |
38 | 29 | import io.agentscope.core.message.ToolUseBlock; |
39 | | -import java.util.List; |
40 | | -import java.util.Map; |
41 | 30 | import org.junit.jupiter.api.BeforeEach; |
42 | 31 | import org.junit.jupiter.api.Test; |
43 | 32 | import reactor.core.publisher.Flux; |
44 | 33 | import reactor.test.StepVerifier; |
45 | 34 |
|
| 35 | +import java.util.List; |
| 36 | +import java.util.Map; |
| 37 | + |
| 38 | +import static org.junit.jupiter.api.Assertions.assertEquals; |
| 39 | +import static org.junit.jupiter.api.Assertions.assertInstanceOf; |
| 40 | +import static org.junit.jupiter.api.Assertions.assertNotNull; |
| 41 | +import static org.junit.jupiter.api.Assertions.assertTrue; |
| 42 | +import static org.mockito.ArgumentMatchers.any; |
| 43 | +import static org.mockito.ArgumentMatchers.anyList; |
| 44 | +import static org.mockito.Mockito.mock; |
| 45 | +import static org.mockito.Mockito.when; |
| 46 | + |
46 | 47 | /** |
47 | 48 | * Unit tests for AguiAgentAdapter. |
48 | 49 | */ |
@@ -508,4 +509,67 @@ void testReactiveStreamCompletion() { |
508 | 509 | .expectNextMatches(e -> e instanceof AguiEvent.RunFinished) |
509 | 510 | .verifyComplete(); |
510 | 511 | } |
| 512 | + |
| 513 | + @Test |
| 514 | + void testTextMessageEndNotDuplicatedWhenLastEventAfterToolCall() { |
| 515 | + // Test that when a text message is interrupted by a tool call and then the last event |
| 516 | + // contains text blocks with the same message ID, only one TextMessageEnd is emitted |
| 517 | + String msgId = "msg-text"; |
| 518 | + Msg firstMsg = |
| 519 | + Msg.builder() |
| 520 | + .id(msgId) |
| 521 | + .role(MsgRole.ASSISTANT) |
| 522 | + .content( |
| 523 | + List.of( |
| 524 | + TextBlock.builder().text("first part").build())) |
| 525 | + .build(); |
| 526 | + |
| 527 | + Msg toolCall1 = |
| 528 | + Msg.builder() |
| 529 | + .id("msg-tc") |
| 530 | + .role(MsgRole.ASSISTANT) |
| 531 | + .content( |
| 532 | + ToolUseBlock.builder() |
| 533 | + .id("tc-1") |
| 534 | + .name("tool") |
| 535 | + .input(Map.of()) |
| 536 | + .build()) |
| 537 | + .build(); |
| 538 | + Msg lastMsg = |
| 539 | + Msg.builder() |
| 540 | + .id(msgId) |
| 541 | + .role(MsgRole.ASSISTANT) |
| 542 | + .content( |
| 543 | + List.of( |
| 544 | + TextBlock.builder().text("last part").build())) |
| 545 | + .build(); |
| 546 | + |
| 547 | + Event firstEvent = new Event(EventType.REASONING, firstMsg, false); |
| 548 | + Event toolCallEvent = new Event(EventType.REASONING, toolCall1, false); |
| 549 | + Event lastEvent = new Event(EventType.REASONING, lastMsg, true); |
| 550 | + when(mockAgent.stream(anyList(), any(StreamOptions.class))) |
| 551 | + .thenReturn(Flux.just(firstEvent, toolCallEvent, lastEvent)); |
| 552 | + |
| 553 | + RunAgentInput input = |
| 554 | + RunAgentInput.builder() |
| 555 | + .threadId("thread-1") |
| 556 | + .runId("run-1") |
| 557 | + .messages(List.of(AguiMessage.userMessage("msg-1", "Test"))) |
| 558 | + .build(); |
| 559 | + |
| 560 | + List<AguiEvent> events = adapter.run(input).collectList().block(); |
| 561 | + |
| 562 | + assertNotNull(events); |
| 563 | + |
| 564 | + // Should have exactly one TextMessageEnd for the same message ID |
| 565 | + long textEndCount = |
| 566 | + events.stream() |
| 567 | + .filter(e -> e instanceof AguiEvent.TextMessageEnd) |
| 568 | + .filter(e -> { |
| 569 | + AguiEvent.TextMessageEnd end = (AguiEvent.TextMessageEnd) e; |
| 570 | + return msgId.equals(end.messageId()); |
| 571 | + }) |
| 572 | + .count(); |
| 573 | + assertEquals(1, textEndCount, "Should have exactly 1 TextMessageEnd per message ID"); |
| 574 | + } |
511 | 575 | } |
0 commit comments