diff --git a/qbit-assistant/src/test/java/com/kingsrook/qbits/assistant/engine/TurnEngineTest.java b/qbit-assistant/src/test/java/com/kingsrook/qbits/assistant/engine/TurnEngineTest.java index 7660df2..d2c7ee2 100644 --- a/qbit-assistant/src/test/java/com/kingsrook/qbits/assistant/engine/TurnEngineTest.java +++ b/qbit-assistant/src/test/java/com/kingsrook/qbits/assistant/engine/TurnEngineTest.java @@ -162,6 +162,34 @@ class TurnEngineTest extends BaseAssistantTest + /******************************************************************************* + ** The live thread after a tool-using turn: when message.end arrives the panel + ** reloads, and must find the real title, the question, the answer and the tool + ** steps - not the transcript redactor's placeholders (changed:v1, [], {}). + *******************************************************************************/ + @Test + void testLiveThreadIsNotPlaceholdersAtMessageEnd() throws Exception + { + provider.thenToolUse("count_records", input("table", "blog_post", "filter", filter("AND", "status", "EQUALS", List.of("PUBLISHED")))) + .thenText("Four posts are published."); + + TurnRunner.Outcome outcome = runner.run(EDITOR, "how many published posts?"); + assertThat(outcome.end()).containsEntry("status", "COMPLETE"); + + assertThat(runner.conversationAtEnd().getTitle()).isEqualTo("how many published posts?"); + List stored = runner.messagesAtEnd(); + assertThat(stored).extracting(AssistantMessage::getPartsJson).noneMatch(parts -> parts.equals("[]") || parts.contains("changed:v1")); + assertThat(stored.get(0).getPartsJson()).contains("how many published posts?"); + assertThat(stored.get(stored.size() - 1).getPartsJson()).contains("Four posts are published."); + assertThat(runner.toolCallsAtEnd()).isNotEmpty().allSatisfy(call -> + { + assertThat(call.getInputJson()).isNotEqualTo("{}"); + assertThat(call.getResultSummary()).doesNotContain("changed:v1"); + }); + } + + + /******************************************************************************* ** Parallel tool calls: all results in ONE user message, in order. *******************************************************************************/ diff --git a/qbit-assistant/src/test/java/com/kingsrook/qbits/assistant/testing/TurnRunner.java b/qbit-assistant/src/test/java/com/kingsrook/qbits/assistant/testing/TurnRunner.java index 00f17ac..fe5d3e9 100644 --- a/qbit-assistant/src/test/java/com/kingsrook/qbits/assistant/testing/TurnRunner.java +++ b/qbit-assistant/src/test/java/com/kingsrook/qbits/assistant/testing/TurnRunner.java @@ -27,6 +27,7 @@ import com.kingsrook.qbits.assistant.AssistantRuntime; import com.kingsrook.qbits.assistant.api.AssistantApi; import com.kingsrook.qbits.assistant.engine.RunningTurn; import com.kingsrook.qbits.assistant.engine.TurnEvent; +import com.kingsrook.qbits.assistant.model.AssistantConversation; import com.kingsrook.qbits.assistant.model.AssistantMessage; import com.kingsrook.qbits.assistant.model.AssistantToolCall; import com.kingsrook.qbits.assistant.persistence.AssistantStore; @@ -49,6 +50,8 @@ public class TurnRunner private final AssistantQBitConfig config; private List messagesAtEnd = List.of(); + private AssistantConversation conversationAtEnd; + private List toolCallsAtEnd = List.of(); @@ -175,6 +178,8 @@ public class TurnRunner if(batch.stream().anyMatch(e -> TurnEvent.MESSAGE_END.equals(e.getType()))) { messagesAtEnd = messagesOf(userId, convId); + conversationAtEnd = conversationOf(userId, convId); + toolCallsAtEnd = toolCalls(userId, running.getTurnId()); } if(!batch.isEmpty()) { @@ -213,6 +218,44 @@ public class TurnRunner + /******************************************************************************* + ** The conversation row as stored the moment message.end was seen. + *******************************************************************************/ + public AssistantConversation conversationAtEnd() + { + return (conversationAtEnd); + } + + + + /******************************************************************************* + ** The audited tool calls as stored the moment message.end was seen. + *******************************************************************************/ + public List toolCallsAtEnd() + { + return (toolCallsAtEnd); + } + + + + /******************************************************************************* + ** One conversation of the user, read as the user. + *******************************************************************************/ + private AssistantConversation conversationOf(String userId, Integer conversationId) throws Exception + { + QContext.init(qInstance, AssistantTestFixture.ownerSession(userId, config)); + try + { + return (new AssistantStore().getConversation(userId, conversationId)); + } + finally + { + QContext.clear(); + } + } + + + /******************************************************************************* ** The stored messages of a conversation (read as the user). *******************************************************************************/