Cache AI Page Link summary (#3106)

Co-authored-by: Samy Pessé <samypesse@gmail.com>
This commit is contained in:
Zeno Kapitein
2025-04-09 09:59:35 +02:00
committed by GitHub
parent f23023ddfa
commit 95ea22d9c5
2 changed files with 55 additions and 8 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"gitbook": patch
---
Cache AI Page Link summary
@@ -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<string, string>;
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: {
<div className="flex flex-col gap-1">
<div className="flex w-screen items-center gap-1 font-semibold text-tint text-xs uppercase leading-tight tracking-wide">
{showTrademark ? (
<Loading className="size-4" busy={!highlight || highlight.length === 0} />
<Loading className="size-4" busy={!summary || summary.length === 0} />
) : (
<Icon icon="sparkle" className="size-3" />
)}
<h6 className="text-tint">{t(language, 'link_tooltip_ai_summary')}</h6>
</div>
{highlight.length > 0 ? (
<p className="animate-fadeIn">{highlight}</p>
{summary.length > 0 ? (
<p className="animate-fadeIn">{summary}</p>
) : (
<div className="mt-2 flex flex-wrap gap-2">
{shimmerBlocks.map((block, index) => (
@@ -94,7 +136,7 @@ export function AIPageLinkSummary(props: {
))}
</div>
)}
{highlight.length > 0 ? (
{summary.length > 0 ? (
<div className="animate-fadeIn text-tint-subtle text-xs">
{t(language, 'link_tooltip_ai_summary_description')}
</div>