mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
Stacked on supabase/supabase#49415. ## Summary - collect completed assistant delete_notebook tool calls as deleted cache effects - evict deleted notebooks from the React Query cache and notebook state - cover collector, cache eviction, and the open-tab Notebook not found state ## Verification - pnpm test:studio -- notebook-cache-invalidation ExplorerNotebookTab.assistant-cache-invalidation - pnpm --filter studio typecheck - touched-file ESLint and Prettier checks <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved notebook deletion handling so removed notebooks no longer remain visible after deletion. * Navigation and cached notebook data now update promptly when a notebook is deleted. * Attempting to access a deleted notebook now displays a clear “Notebook not found” message. * **Tests** * Added coverage for notebook deletion, cache invalidation, navigation updates, and the resulting not-found state. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
76 lines
2.3 KiB
TypeScript
76 lines
2.3 KiB
TypeScript
import type { QueryClient } from '@tanstack/react-query'
|
|
import type { ToolUIPart, UIMessage } from 'ai'
|
|
|
|
import { notebookToolOutputSchema } from '@/components/ui/AIAssistantPanel/Message.utils'
|
|
import { contentKeys } from '@/data/content/keys'
|
|
import { evictNotebookFromCaches } from '@/data/content/notebooks/notebook-cache'
|
|
import { notebooksState } from '@/state/notebooks/notebooks-state'
|
|
|
|
export type NotebookCacheEffect =
|
|
| { _tag: 'upserted'; toolCallId: string; id: string }
|
|
| { _tag: 'deleted'; toolCallId: string; id: string }
|
|
|
|
const NOTEBOOK_MUTATION_TOOL_TYPES = new Set([
|
|
'tool-create_notebook',
|
|
'tool-update_notebook',
|
|
'tool-delete_notebook',
|
|
])
|
|
|
|
function isNotebookMutationPart(part: UIMessage['parts'][number]): part is ToolUIPart {
|
|
return NOTEBOOK_MUTATION_TOOL_TYPES.has(part.type)
|
|
}
|
|
|
|
export function collectNotebookCacheEffects(
|
|
messages: Array<UIMessage>,
|
|
processed: ReadonlySet<string>
|
|
): Array<NotebookCacheEffect> {
|
|
const effects: Array<NotebookCacheEffect> = []
|
|
|
|
for (const message of messages) {
|
|
if (message.role !== 'assistant') continue
|
|
|
|
for (const part of message.parts ?? []) {
|
|
if (!isNotebookMutationPart(part)) continue
|
|
if (part.state !== 'output-available') continue
|
|
if (processed.has(part.toolCallId)) continue
|
|
|
|
const result = notebookToolOutputSchema.safeParse(part.output)
|
|
if (!result.success) continue
|
|
|
|
effects.push({
|
|
_tag: part.type === 'tool-delete_notebook' ? 'deleted' : 'upserted',
|
|
toolCallId: part.toolCallId,
|
|
id: result.data.id,
|
|
})
|
|
}
|
|
}
|
|
|
|
return effects
|
|
}
|
|
|
|
export async function applyNotebookCacheEffects({
|
|
queryClient,
|
|
projectRef,
|
|
effects,
|
|
}: {
|
|
queryClient: QueryClient
|
|
projectRef: string
|
|
effects: Array<NotebookCacheEffect>
|
|
}): Promise<void> {
|
|
if (effects.length === 0) return
|
|
|
|
await Promise.all([
|
|
queryClient.invalidateQueries({ queryKey: contentKeys.allContentLists(projectRef) }),
|
|
queryClient.invalidateQueries({ queryKey: contentKeys.infiniteList(projectRef) }),
|
|
])
|
|
|
|
await Promise.all(
|
|
effects.map((effect) => {
|
|
const stateNotebook = notebooksState.notebooks[effect.id]
|
|
if (stateNotebook && stateNotebook.status !== 'saved') return
|
|
|
|
return evictNotebookFromCaches({ queryClient, projectRef, id: effect.id })
|
|
})
|
|
)
|
|
}
|