Imported from Attafii/Distiller (
AGENTS.md). Install upstream withnpx skills add Attafii/Distiller. Copyright stays with the author.
AGENTS.md — Distiller
Next.js 15 App Router app that fetches news via NewsAPI, grounds articles with NVIDIA Build embeddings + RAG, and renders 3-bullet AI summaries.
Dev commands
npm run dev # Start dev server
npm run build # Production build
npm run lint # ESLint via eslint-config-next
No test runner is configured. No typecheck script — tsc runs during next build.
Environment variables
Copy .env.example to .env.local and fill in:
NEWSAPI_KEY— required for live news; app falls back to demo articles if missingNEWSAPI_BASE_URL— defaults tohttps://newsapi.org/v2NEWS_COUNTRY— defaults tousNVIDIA_BUILD_API_KEY— required for embeddings + summarizationNVIDIA_BUILD_BASE_URL— defaults tohttps://integrate.api.nvidia.com/v1NVIDIA_BUILD_MODEL_FAST— defaults tonvidia/llama-3.1-nemotron-nano-8b-v1NVIDIA_BUILD_MODEL_BALANCED— defaults tonvidia/llama-3.3-nemotron-super-49b-v1NVIDIA_BUILD_MODEL_DEEP— defaults tonvidia/llama-3.1-nemotron-ultra-253b-v1NVIDIA_BUILD_MODEL_CHAT— defaults tonvidia/llama-3.3-nemotron-super-49b-v1(falls back to BALANCED)NVIDIA_BUILD_EMBED_MODEL— defaults tonvidia/nv-embedqa-e5-v5DISTILL_BATCH_SIZE— defaults to3NEXT_PUBLIC_SITE_URL— your deployment URL (e.g.,https://distiller.attafii.dev)
Architecture
- Pages:
/(landing),/RefinedFeed(feed with search/filters/infinite scroll),/article/[id](article SEO page with NewsArticle structured data) - API routes:
GET /api/feed— fetches + distills articles in batchesPOST /api/news/chat— article-specific Q&A via LLMGET /api/debug/runtime— health check for both APIsGET /feed.xml— RSS 2.0 feed with latest AI summaries
- Server-only modules:
lib/ai.ts,lib/rag.ts,services/newsapi.tsall import"server-only". Never import them into client components.
Key implementation details
RAG pipeline (lib/rag.ts)
- Chunks text at ~900 chars with 120-char overlap
- If token estimate ≤ 220, skips embeddings and returns full text
- Otherwise calls NVIDIA Build embeddings endpoint, ranks by cosine similarity + lexical score
- Falls back to lexical-only ranking if embedding call fails
Model tier routing (lib/ai.ts)
- Tiers:
fast(< 180 tokens),balanced(< 650 tokens),deep(dense/long),chat(Q&A) - Each tier has a cascade of hardcoded fallback models if the env-specified one fails
- Summaries are validated with Zod; falls back to line-parsing, then sentence-parsing, then static fallback
- Chat uses the
chattier (512 max_tokens), which falls back tobalancedtier models
NewsAPI integration (services/newsapi.ts)
/top-headlinesfor country-specific feeds/everythingfor Global (country=global) and Tunisia (country=tn) with query terms- Demo articles are served for all 12 categories when key is missing or API fails
- Date filtering happens client-side after fetch
UI components
- Custom primitives in
components/ui/(button, card, badge) — not generated by shadcn CLI - Uses Tailwind CSS v3 with dark mode class strategy
- Fonts: Space Grotesk (sans) + IBM Plex Mono (mono) via
next/font/google - Consistent zinc-based dark palette; no light mode
Style conventions
- Prefer
functiondeclarations over arrow functions for components - Use
cn()fromlib/utils.tsfor Tailwind class merging - Client components marked with
"use client"at top - Use
z.coercefor query param parsing in API routes - Prefer
typeoverinterfacefor simple shapes