([]);
+ const { data: suggestionsConfig } = useSuggestionsConfig();
const [followupsHidden, setFollowupsHidden] = useState(false);
const [followupsLoading, setFollowupsLoading] = useState(false);
const [textareaFocused, setTextareaFocused] = useState(false);
@@ -524,6 +526,9 @@ export function InputBox({
if (!lastAiId || lastAiId === lastGeneratedForAiIdRef.current) {
return;
}
+ if (suggestionsConfig === undefined) {
+ return;
+ }
lastGeneratedForAiIdRef.current = lastAiId;
const recent = messagesRef.current
@@ -540,6 +545,11 @@ export function InputBox({
return;
}
+ if (!suggestionsConfig?.enabled) {
+ setFollowups([]);
+ return;
+ }
+
const controller = new AbortController();
setFollowupsHidden(false);
setFollowupsLoading(true);
@@ -576,7 +586,14 @@ export function InputBox({
});
return () => controller.abort();
- }, [context.model_name, disabled, isMock, status, threadId]);
+ }, [
+ context.model_name,
+ disabled,
+ isMock,
+ status,
+ threadId,
+ suggestionsConfig?.enabled,
+ ]);
return (
{
+ const response = await fetch(`${getBackendBaseURL()}/api/suggestions/config`);
+ if (!response.ok) {
+ if (response.status === 404) {
+ // Fallback to true if the backend is older
+ return { enabled: true };
+ }
+ throw new Error(
+ `Failed to load suggestions config: ${response.statusText}`,
+ );
+ }
+ return response.json() as Promise;
+}
diff --git a/frontend/src/core/suggestions/hooks.ts b/frontend/src/core/suggestions/hooks.ts
new file mode 100644
index 000000000..250d1bee8
--- /dev/null
+++ b/frontend/src/core/suggestions/hooks.ts
@@ -0,0 +1,11 @@
+import { useQuery } from "@tanstack/react-query";
+
+import { loadSuggestionsConfig } from "./api";
+
+export function useSuggestionsConfig() {
+ return useQuery({
+ queryKey: ["suggestionsConfig"],
+ queryFn: loadSuggestionsConfig,
+ staleTime: Infinity,
+ });
+}
diff --git a/frontend/tests/e2e/chat.spec.ts b/frontend/tests/e2e/chat.spec.ts
index 50ab3c871..237a80844 100644
--- a/frontend/tests/e2e/chat.spec.ts
+++ b/frontend/tests/e2e/chat.spec.ts
@@ -310,4 +310,47 @@ test.describe("Chat workspace", () => {
});
await expect(promptForm.getByText("report.docx")).toBeHidden();
});
+
+ test("does not fetch follow-up suggestions when disabled in config", async ({
+ page,
+ }) => {
+ await page.route("**/api/suggestions/config", (route) => {
+ return route.fulfill({
+ status: 200,
+ contentType: "application/json",
+ body: JSON.stringify({ enabled: false }),
+ });
+ });
+
+ let suggestionsFetched = false;
+ await page.route("**/api/threads/*/suggestions", (route) => {
+ suggestionsFetched = true;
+ return route.fulfill({
+ status: 200,
+ contentType: "application/json",
+ body: JSON.stringify({ suggestions: [] }),
+ });
+ });
+
+ let streamCalled = false;
+ await page.route("**/runs/stream", (route) => {
+ streamCalled = true;
+ return handleRunStream(route);
+ });
+
+ await page.goto("/workspace/chats/new");
+
+ const textarea = page.getByPlaceholder(/how can i assist you/i);
+ await expect(textarea).toBeVisible({ timeout: 15_000 });
+
+ await textarea.fill("Hello");
+ await textarea.press("Enter");
+
+ await expect.poll(() => streamCalled, { timeout: 10_000 }).toBeTruthy();
+ await expect(page.getByText("Hello from DeerFlow!")).toBeVisible({
+ timeout: 10_000,
+ });
+ await page.waitForTimeout(1000);
+ expect(suggestionsFetched).toBe(false);
+ });
});