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]);
}