From 95ea22d9c5cc13766864c6e7966972d1a4dd968f Mon Sep 17 00:00:00 2001 From: Zeno Kapitein Date: Wed, 9 Apr 2025 09:59:35 +0200 Subject: [PATCH] Cache AI Page Link summary (#3106) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Samy Pessé --- .changeset/chilly-snakes-sell.md | 5 ++ .../components/Adaptive/AIPageLinkSummary.tsx | 58 ++++++++++++++++--- 2 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 .changeset/chilly-snakes-sell.md diff --git a/.changeset/chilly-snakes-sell.md b/.changeset/chilly-snakes-sell.md new file mode 100644 index 000000000..2056ab607 --- /dev/null +++ b/.changeset/chilly-snakes-sell.md @@ -0,0 +1,5 @@ +--- +"gitbook": patch +--- + +Cache AI Page Link summary diff --git a/packages/gitbook/src/components/Adaptive/AIPageLinkSummary.tsx b/packages/gitbook/src/components/Adaptive/AIPageLinkSummary.tsx index fed93e56c..5560e3f91 100644 --- a/packages/gitbook/src/components/Adaptive/AIPageLinkSummary.tsx +++ b/packages/gitbook/src/components/Adaptive/AIPageLinkSummary.tsx @@ -3,11 +3,32 @@ import { useLanguage } from '@/intl/client'; import { t } from '@/intl/translate'; import { Icon } from '@gitbook/icons'; import { useEffect, useState } from 'react'; +import { create } from 'zustand'; import { useVisitedPages } from '../Insights'; import { usePageContext } from '../PageContext'; import { Loading } from '../primitives'; import { streamLinkPageSummary } from './server-actions/streamLinkPageSummary'; +const useSummaries = create<{ + cache: Map; + setSummary: (key: string, summary: string) => void; +}>((set) => ({ + cache: new Map(), + setSummary: (key, summary) => + set((state) => { + const newCache = new Map(state.cache); + newCache.set(key, summary); + return { cache: newCache }; + }), +})); + +/** + * Get a unique cache key for a page summary + */ +function getCacheKey(targetSpaceId: string, targetPageId: string): string { + return `${targetSpaceId}:${targetPageId}`; +} + /** * Summarise a page's content for use in a link preview */ @@ -21,15 +42,26 @@ export function AIPageLinkSummary(props: { const { targetSpaceId, targetPageId, linkPreview, linkTitle, showTrademark = true } = props; const currentPage = usePageContext(); - const language = useLanguage(); const visitedPages = useVisitedPages((state) => state.pages); - const [highlight, setHighlight] = useState(''); + const [summary, setSummary] = useState(''); + const cacheKey = getCacheKey(targetSpaceId, targetPageId); + const { cachedSummary, setCachedSummary } = useSummaries((state) => { + return { + cachedSummary: state.cache.get(cacheKey) ?? '', + setCachedSummary: state.setSummary, + }; + }); useEffect(() => { let canceled = false; - setHighlight(''); + setSummary(''); + + if (cachedSummary) { + setSummary(cachedSummary); + return; + } (async () => { const stream = await streamLinkPageSummary({ @@ -43,9 +75,16 @@ export function AIPageLinkSummary(props: { visitedPages, }); + let generatedSummary = ''; for await (const highlight of stream) { if (canceled) return; - setHighlight(highlight ?? ''); + generatedSummary = highlight ?? ''; + setSummary(generatedSummary); + } + + // Cache the complete summary + if (generatedSummary) { + setCachedSummary(cacheKey, generatedSummary); } })(); @@ -61,6 +100,9 @@ export function AIPageLinkSummary(props: { linkPreview, linkTitle, visitedPages, + cachedSummary, + cacheKey, + setCachedSummary, ]); const shimmerBlocks = [ @@ -76,14 +118,14 @@ export function AIPageLinkSummary(props: {
{showTrademark ? ( - + ) : ( )}
{t(language, 'link_tooltip_ai_summary')}
- {highlight.length > 0 ? ( -

{highlight}

+ {summary.length > 0 ? ( +

{summary}

) : (
{shimmerBlocks.map((block, index) => ( @@ -94,7 +136,7 @@ export function AIPageLinkSummary(props: { ))}
)} - {highlight.length > 0 ? ( + {summary.length > 0 ? (
{t(language, 'link_tooltip_ai_summary_description')}