diff --git a/frontend/src/features/chat/ChatPage.a11y.test.tsx b/frontend/src/features/chat/ChatPage.a11y.test.tsx index cb83259..4f23b6f 100644 --- a/frontend/src/features/chat/ChatPage.a11y.test.tsx +++ b/frontend/src/features/chat/ChatPage.a11y.test.tsx @@ -338,6 +338,18 @@ describe("ChatPage response announcements", () => { await waitFor(() => expect(announcer()).toHaveTextContent(/^Response complete$/)); }); + it("says a response was cut short when the context limit ended it, and shows why under the answer", async () => { + const user = userEvent.setup(); + const { api, finishWith } = harness(); + renderChat(api); + await sendPrompt(user); + + await finishWith("generation.completed", { assistant_message_id: "assistant-1" }, savedAnswer({ eval_count: 900, stop_reason: "length" })); + + await waitFor(() => expect(announcer()).toHaveTextContent(/^Response cut short by the context limit$/)); + expect(await screen.findByText(/This answer was cut short by the context limit/)).toBeVisible(); + }); + it("says a response stopped after the person stopped it and the answer was kept", async () => { const user = userEvent.setup(); const { api, finishWith } = harness(); diff --git a/frontend/src/features/chat/MessageCard.test.tsx b/frontend/src/features/chat/MessageCard.test.tsx index c831981..5c06fc0 100644 --- a/frontend/src/features/chat/MessageCard.test.tsx +++ b/frontend/src/features/chat/MessageCard.test.tsx @@ -92,4 +92,34 @@ describe("MessageCard", () => { expect(screen.queryByRole("region", { name: "Cortex suggests remembering" })).toBeNull(); expect(screen.getByText("Noted.")).toBeInTheDocument(); }); + + it("notes under an answer the context limit cut short, and says what helps", () => { + renderCard({ + id: "assistant-1", + role: "assistant", + content: "The three steps are: first, open the", + stats: { eval_count: 512, tokens_per_second: 24.5, stop_reason: "length" }, + }); + + const note = screen.getByRole("note"); + expect(note).toHaveTextContent("This answer was cut short by the context limit."); + expect(note).toHaveTextContent("Raising the Context window gives answers more room."); + // The partial answer and its usage are still shown as they were. + expect(screen.getByText("The three steps are: first, open the")).toBeInTheDocument(); + expect(screen.getByText(/512 tok/)).toBeInTheDocument(); + }); + + it("still says why when the limit left only reasoning and no answer", () => { + renderCard({ id: "assistant-1", role: "assistant", content: "", thoughts: "Let me think about this carefully.", stats: { stop_reason: "length" } }); + + expect(screen.getByRole("note")).toHaveTextContent("cut short by the context limit"); + }); + + it("adds no note to an answer that finished on its own, one with no reason recorded, or a person's message", () => { + renderCard({ id: "assistant-1", role: "assistant", content: "Done.", stats: { eval_count: 3, tokens_per_second: 30, stop_reason: "stop" } }); + renderCard({ id: "assistant-2", role: "assistant", content: "Older answer.", stats: { eval_count: 3, tokens_per_second: 30 } }); + renderCard({ id: "user-1", role: "user", content: "Question?", stats: { stop_reason: "length" } }); + + expect(screen.queryByText(/cut short by the context limit/)).toBeNull(); + }); }); diff --git a/frontend/src/features/chat/MessageCard.tsx b/frontend/src/features/chat/MessageCard.tsx index 48dd3c7..8398a83 100644 --- a/frontend/src/features/chat/MessageCard.tsx +++ b/frontend/src/features/chat/MessageCard.tsx @@ -74,6 +74,10 @@ export const MessageCard = memo(function MessageCard({ message, index, isFinalAs
{message.content}
:Couldn't translate this answer; showing the original.
} + {/* The runtime reports why the model stopped. "length" means it ran + into the context limit, and the answer ends wherever that fell: + without this it reads as though it finished. */} + {message.role === "assistant" && message.stats?.stop_reason === "length" &&This answer was cut short by the context limit. Raising the Context window gives answers more room.
} {message.sources && message.sources.length > 0 &&