import React, { useEffect, useMemo, useState } from 'react'; import { recomaze_ai_personalization_env } from '../env'; import { fetchLlmHits, fetchLlmStatus, generateLlmNow, } from '../service/llm/llm.service'; import type { ILlmHitsResponse, ILlmStatus, } from '../service/llm/llm.interface'; import LineChart from '../components/charts/LineChart'; const LlmTxtPage = (): JSX.Element => { const [status, setStatus] = useState(null); const [hits, setHits] = useState(null); const [loading, setLoading] = useState(false); const [loadingHits, setLoadingHits] = useState(false); const [error, setError] = useState(null); const [hitsError, setHitsError] = useState(null); const [successMessage, setSuccessMessage] = useState(null); const loadStatus = async () => { setError(null); setLoading(true); try { const data = await fetchLlmStatus(); setStatus(data); } catch (err: any) { setError(err?.message || 'Unable to load llm.txt status.'); } finally { setLoading(false); } }; const loadHits = async () => { setHitsError(null); setLoadingHits(true); try { const data = await fetchLlmHits(7); setHits(data); } catch (err: any) { setHitsError(err?.message || 'Unable to load llm.txt hits.'); } finally { setLoadingHits(false); } }; useEffect(() => { loadStatus(); loadHits(); }, []); const handleGenerate = async () => { setError(null); setSuccessMessage(null); setLoading(true); try { const data = await generateLlmNow(); setStatus(data); await loadHits(); const products = data?.counts?.products ?? 0; const pages = data?.counts?.pages ?? 0; setSuccessMessage( `Successfully generated llm.txt with ${products} products and ${pages} pages.` ); setTimeout(() => setSuccessMessage(null), 5000); } catch (err: any) { setError(err?.message || 'Generation failed.'); } finally { setLoading(false); } }; const llmUrl = status?.llm_url || recomaze_ai_personalization_env?.llm_url || recomaze_ai_personalization_env?.wp_api_url?.replace(/\/$/, '') + '/llm.txt'; const chartData = useMemo( () => ({ labels: hits?.hits_by_day?.map(d => d.label) || [], datasets: [ { label: 'Requests', data: hits?.hits_by_day?.map(d => d.count) || [], fill: true, tension: 0.35, borderColor: '#111827', backgroundColor: 'rgba(17, 24, 39, 0.05)', }, ], }), [hits?.hits_by_day] ); const totalHits = useMemo( () => hits?.hits_by_day?.reduce((sum, d) => sum + (d?.count || 0), 0) || 0, [hits?.hits_by_day] ); return (

LLM.txt Configuration

Manage the feed used by GPT, Claude, and Perplexity bots to index your catalog.

Open llm.txt
{successMessage && (

{successMessage}

)} {error && (

{error}

)}

Current Status

Last Generated {status?.generated_at ? new Date(status.generated_at).toLocaleString() : 'Never'}
{status?.cached && ( Cached )}

Products

{status?.counts?.products ?? '—'}

Pages

{status?.counts?.pages ?? '—'}

Total URLs

{status?.counts?.total ?? '—'}

{status?.warnings && status.warnings.length > 0 && (

Warnings

    {status.warnings.map((w, idx) => (
  • {w}
  • ))}
)}

About llm.txt

llm.txt exposes a clean list of products and pages for LLM crawlers (e.g., GPTBot, ClaudeBot) in a simple text format. Data is generated from your catalog, sanitized from HTML, and cached for performance.

TYPE | URL | TITLE | SUMMARY

Best Practices

  • Ensure GPTBot and ClaudeBot are not blocked in your{' '} robots.txt .
  • Keep the storefront publicly accessible (no passwords) so URLs can be indexed.
  • Regenerate this file when you make significant changes to your catalog.

Traffic Analysis (Last 7 Days)

{hitsError && ( {hitsError} )}
{loadingHits ? (
Loading traffic data...
) : hits?.hits_by_day?.length ? (
) : (
No hits recorded in the last 7 days.
)}

Recent Crawler Activity

{hits?.recent && hits.recent.length > 0 ? (
{hits.recent.map(hit => ( ))}
Timestamp Bot Name User Agent Status
{new Date(hit.created_at).toLocaleString()} {hit.bot_name} {hit.user_agent} {hit.status}
) : (
No recent crawler activity found.
)}
); }; export default LlmTxtPage;