From d1bc3e429dd3a1c48f8ba06743525f7d7e98fbed Mon Sep 17 00:00:00 2001 From: Vincent Driessen Date: Tue, 5 Aug 2025 12:23:10 +0200 Subject: [PATCH] Fixes knowledge leaking bug in AiChat (#2570) --- CHANGELOG.md | 5 + .../app/dual-chat/page.tsx | 111 +++++++ .../app/knowledge/page.tsx | 17 +- e2e/next-ai-kitchen-sink/app/page.tsx | 3 + .../test/knowledge-isolation.test.ts | 287 ++++++++++++++++++ .../test/knowledge.test.ts | 71 ++--- .../test/simple-chat.test.ts | 48 ++- .../test/tool-calling.test.ts | 14 +- packages/liveblocks-core/src/ai.ts | 15 +- .../src/components/AiChat.tsx | 26 +- .../components/internal/AiChatComposer.tsx | 11 + 11 files changed, 523 insertions(+), 85 deletions(-) create mode 100644 e2e/next-ai-kitchen-sink/app/dual-chat/page.tsx create mode 100644 e2e/next-ai-kitchen-sink/test/knowledge-isolation.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index e7d1a737498..f355b664573 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## vNEXT (not yet published) +### `@liveblocks/react-ui` + +- Knowledge passed as a prop to `AiChat` no longer leaks that knowledge to other + instances of `AiChat` that are currently mounted on screen. + ## v3.2.1 ### `@liveblocks/react-ui` diff --git a/e2e/next-ai-kitchen-sink/app/dual-chat/page.tsx b/e2e/next-ai-kitchen-sink/app/dual-chat/page.tsx new file mode 100644 index 00000000000..23ceedbb18d --- /dev/null +++ b/e2e/next-ai-kitchen-sink/app/dual-chat/page.tsx @@ -0,0 +1,111 @@ +"use client"; + +import { LiveblocksProvider, RegisterAiKnowledge } from "@liveblocks/react"; +import { AiChat } from "@liveblocks/react-ui"; +import { useState } from "react"; + +function ChatWithLocalKnowledge() { + const [localKnowledge, setLocalKnowledge] = useState("Spaghetti Carbonara"); + + return ( +
+
+ + setLocalKnowledge(e.target.value)} + className="w-full p-2 border border-blue-300 rounded bg-blue-50" + data-testid="chat-a-knowledge-input" + /> +

+ This knowledge is passed via the knowledge prop to chat A + only. +

+
+ +

Chat A

+
+ +
+
+ ); +} + +function ChatWithoutLocalKnowledge() { + return ( +
+
+

+ No local knowledge +

+

+ This chat has no additional local knowledge passed via props. It can + only access the global knowledge. +

+
+ +

Chat B

+
+ +
+
+ ); +} + +export default function DualChatPage() { + const [globalKnowledge, setGlobalKnowledge] = useState("Tiramisu"); + + return ( + + {/* Global knowledge that should be accessible to ALL chat instances */} + + +
+
+

Knowledge Isolation Test

+
+ + setGlobalKnowledge(e.target.value)} + className="w-full p-2 border border-yellow-300 rounded bg-yellow-50" + data-testid="global-knowledge-input" + /> +

+ This knowledge is registered via{" "} + <RegisterAiKnowledge /> and should be + accessible to both chat A and B. +

+
+
+ +
+ + +
+
+
+ ); +} diff --git a/e2e/next-ai-kitchen-sink/app/knowledge/page.tsx b/e2e/next-ai-kitchen-sink/app/knowledge/page.tsx index fc95726597d..a22b80c7d78 100644 --- a/e2e/next-ai-kitchen-sink/app/knowledge/page.tsx +++ b/e2e/next-ai-kitchen-sink/app/knowledge/page.tsx @@ -28,6 +28,7 @@ function DarkModeToggle() {