use-chat-container-warm.ts1.7 KBView on GitHub import { useMutation } from '@tanstack/react-query';
import { useTRPC } from '@/providers/query-provider';
import { useEffect, useRef } from 'react';
const KEEPALIVE_INTERVAL_MS = 4 * 60 * 1000; // 4 minutes keeps the chat service warm
/**
* Keeps the chat container warm so the first message is always fast.
*
* - Warms on mount (initial page load)
* - Pings every 4 minutes while the tab is visible (prevents container going cold)
* - Re-warms immediately when the tab becomes visible after 4+ minutes hidden
*/
export function useChatContainerWarm() {
const trpc = useTRPC();
const hiddenAtRef = useRef<number | null>(null);
const { mutate: warmContainer } = useMutation(
trpc.mastra.chatWarm.mutationOptions({
onSuccess: (data) => {
if (data.status === 'warmed') {
console.log(`[chatWarm] Container warmed in ${data.warmMs}ms`);
}
},
}),
);
useEffect(() => {
warmContainer();
const interval = setInterval(() => {
if (document.visibilityState === 'visible') {
warmContainer();
}
}, KEEPALIVE_INTERVAL_MS);
const handleVisibilityChange = () => {
if (document.visibilityState === 'hidden') {
hiddenAtRef.current = Date.now();
} else if (document.visibilityState === 'visible') {
const hiddenMs = hiddenAtRef.current ? Date.now() - hiddenAtRef.current : 0;
if (hiddenMs >= KEEPALIVE_INTERVAL_MS) {
warmContainer();
}
hiddenAtRef.current = null;
}
};
document.addEventListener('visibilitychange', handleVisibilityChange);
return () => {
clearInterval(interval);
document.removeEventListener('visibilitychange', handleVisibilityChange);
};
}, [warmContainer]);
}