Skip to content

Commit c57e28e

Browse files
fix(seer-explorer): Prevent optimistic state clearing on rethink with ame message (#111721)
+ When a user rethinks and re-sends the same message, the clearing effect was matching stale pre-truncation server blocks, causing deleted blocks to briefly reappear and the thinking indicator to vanish. Store the session updated_at as a baseline when setting optimistic state and skip the clearing effect until the server has actually processed the request. + Was introduced when fixing another bug in this PR: #111685 Co-authored-by: Claude Sonnet 4 <noreply@anthropic.com>
1 parent cef45a3 commit c57e28e

File tree

2 files changed

+57
-1
lines changed

2 files changed

+57
-1
lines changed

static/app/views/seerExplorer/hooks/useSeerExplorer.spec.tsx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,5 +222,55 @@ describe('useSeerExplorer', () => {
222222
const blocks = result.current.sessionData?.blocks ?? [];
223223
expect(blocks.some(b => b.message.role === 'assistant' && b.loading)).toBe(true);
224224
});
225+
226+
it('keeps optimistic state when rethinking with the same message', async () => {
227+
const chatUrl = `/organizations/${organization.slug}/seer/explorer-chat/`;
228+
const ts = '2024-01-01T00:00:00Z';
229+
230+
MockApiClient.addMockResponse({url: chatUrl, method: 'GET', body: {session: null}});
231+
MockApiClient.addMockResponse({
232+
url: `${chatUrl}789/`,
233+
method: 'GET',
234+
body: {
235+
session: {
236+
blocks: [
237+
{
238+
id: 'u0',
239+
message: {role: 'user', content: 'hello'},
240+
timestamp: ts,
241+
loading: false,
242+
},
243+
{
244+
id: 'a1',
245+
message: {role: 'assistant', content: 'Hi!'},
246+
timestamp: ts,
247+
loading: false,
248+
},
249+
],
250+
run_id: 789,
251+
status: 'completed',
252+
updated_at: ts,
253+
},
254+
},
255+
});
256+
MockApiClient.addMockResponse({
257+
url: `${chatUrl}789/`,
258+
method: 'POST',
259+
body: {run_id: 789},
260+
});
261+
262+
const {result} = renderHookWithProviders(() => useSeerExplorer(), {organization});
263+
264+
act(() => result.current.switchToRun(789));
265+
await waitFor(() => result.current.sessionData?.blocks?.length === 2);
266+
267+
act(() => result.current.deleteFromIndex(0));
268+
await act(async () => {
269+
await result.current.sendMessage('hello');
270+
});
271+
272+
expect(result.current.sessionData?.blocks?.some(b => b.loading)).toBe(true);
273+
expect(result.current.deletedFromIndex).toBe(0);
274+
});
225275
});
226276
});

static/app/views/seerExplorer/hooks/useSeerExplorer.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ export const useSeerExplorer = () => {
148148
const [optimistic, setOptimistic] = useState<{
149149
assistantBlockId: string;
150150
assistantContent: string;
151+
baselineUpdatedAt: string | undefined;
151152
insertIndex: number;
152153
userBlockId: string;
153154
userQuery: string;
@@ -231,6 +232,7 @@ export const useSeerExplorer = () => {
231232
calculatedInsertIndex + 1
232233
),
233234
assistantContent: assistantContent || 'Thinking...',
235+
baselineUpdatedAt: apiData?.session?.updated_at,
234236
});
235237

236238
try {
@@ -450,6 +452,10 @@ export const useSeerExplorer = () => {
450452
return undefined;
451453
}
452454

455+
if (apiData?.session?.updated_at === optimistic.baselineUpdatedAt) {
456+
return undefined;
457+
}
458+
453459
const serverBlocks = apiData?.session?.blocks || [];
454460
const blockAtInsert = serverBlocks[optimistic.insertIndex];
455461

@@ -471,7 +477,7 @@ export const useSeerExplorer = () => {
471477
}
472478

473479
return undefined;
474-
}, [apiData?.session?.blocks, optimistic]);
480+
}, [apiData?.session?.blocks, apiData?.session?.updated_at, optimistic]);
475481

476482
// Detect PR creation errors and show error messages
477483
useEffect(() => {

0 commit comments

Comments
 (0)