{"id":50885936,"url":"https://github.com/kbennett2000/sourced","last_synced_at":"2026-06-15T17:01:36.288Z","repository":{"id":360991546,"uuid":"1252603395","full_name":"kbennett2000/sourced","owner":"kbennett2000","description":"A grounded answer engine demonstrating Brave Search's Web Search vs LLM Context endpoints — Next.js 15 + Vercel AI SDK + Claude","archived":false,"fork":false,"pushed_at":"2026-05-28T19:09:48.000Z","size":87,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-28T19:27:06.875Z","etag":null,"topics":["brave-search","claude","developer-relations","grounded-generation","nextjs","rag","vercel-ai-sdk"],"latest_commit_sha":null,"homepage":"https://sourced-zeta.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kbennett2000.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-28T17:27:13.000Z","updated_at":"2026-05-28T19:09:52.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/kbennett2000/sourced","commit_stats":null,"previous_names":["kbennett2000/sourced"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/kbennett2000/sourced","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbennett2000%2Fsourced","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbennett2000%2Fsourced/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbennett2000%2Fsourced/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbennett2000%2Fsourced/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kbennett2000","download_url":"https://codeload.github.com/kbennett2000/sourced/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbennett2000%2Fsourced/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34372130,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-15T02:00:07.085Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["brave-search","claude","developer-relations","grounded-generation","nextjs","rag","vercel-ai-sdk"],"created_at":"2026-06-15T17:01:34.324Z","updated_at":"2026-06-15T17:01:36.283Z","avatar_url":"https://github.com/kbennett2000.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/social-preview.png\" alt=\"Reference app for Brave RAG / agent use cases\" width=\"100%\" /\u003e\n\u003c/p\u003e\n\n# Sourced\n\nSourced is a working reference app for the [Brave Search API](https://brave.com/search/api/),\nbuilt for developers evaluating Brave for RAG and agent use cases. It exists to\nmake one tradeoff visible: Brave's two retrieval endpoints — **Web Search** and\n**LLM Context** — behind a single toggle, with source count and retrieval latency\nshown on every query.\n\n![Sourced — answering with citations](docs/images/02-answering.png)\n\n## Time to first call\n\nClone, add two keys, run — about five minutes. Requires Node 20+ and pnpm\n(`corepack enable pnpm` if you don't have it).\n\n```bash\ngit clone https://github.com/kbennett2000/sourced\ncd sourced\npnpm install\n\ncp .env.example .env.local      # then fill in the two keys (see below)\npnpm dev                        # http://localhost:3000\n```\n\n`.env.local` needs:\n\n| Variable | Where to get it |\n| --- | --- |\n| `BRAVE_API_KEY` | [Brave Search API dashboard](https://api-dashboard.search.brave.com) — subscription token |\n| `ANTHROPIC_API_KEY` | [Anthropic Console](https://console.anthropic.com) — `sk-ant-...` |\n\n\u003e The `Search` plan (from $5/1K, with a $5 free monthly credit) covers both\n\u003e endpoints the toggle uses — no separate subscription needed.\n\nOptional: `RATE_LIMIT_MAX` (default 30) and `RATE_LIMIT_WINDOW_MS` (default\n3600000 = 1h) tune the per-IP limit.\n\n## Two endpoints, one toggle\n\nBoth endpoints answer the same question; they differ in who does the work of\nturning the web into grounding context.\n\n- **Web Search** (`/res/v1/web/search`) — ranked links + snippets. *You* decide\n  how many results to pass and how to format them. Best when you want control\n  over the grounding, clean per-result snippets, and obvious source diversity.\n- **LLM Context** (`/res/v1/llm/context`) — relevance-ranked, model-ready content\n  in a single call, purpose-built for AI grounding. Best when you want Brave to\n  do the chunking/ranking for you. The tradeoff: you get more, denser context\n  with less control over formatting — some chunks arrive as raw JSON or tables\n  rather than prose (Sourced detects these and renders them collapsed).\n\nThe stats bar surfaces `source_count` and `retrieval_ms` per run so the\ndifference is visible rather than described.\n\n![The same question re-run on LLM Context](docs/images/03-llm-context.png)\n\n*The same question on each endpoint — the stats bar shows the difference in\nsource count and retrieval latency. That visible diff is the whole point.*\n\nLLM Context sometimes returns JSON or table chunks instead of prose; Sourced\ndetects these and collapses them behind a toggle rather than dumping raw markup\ninto a source card:\n\n![A non-prose LLM Context snippet, collapsed](docs/images/04-snippet-collapse.png)\n\n## How it works\n\n```\nBrowser → POST /api/answer { question, endpoint }\n  → validate (Zod) → rate-limit (per IP)\n  → retrieve via lib/brave.ts (Web Search or LLM Context) → normalized Source[]\n  → ground (lib/prompt.ts) → streamText(claude-sonnet-4-6)\n  → response: NDJSON prelude line { endpoint, retrieval_ms, source_count, sources }\n    followed by the streamed answer text\n```\n\nThe client reads the prelude (source cards + stats), then renders the streaming\nanswer, turning inline `[n]` markers into pills that scroll to the matching\nsource card. See [docs/architecture.md](docs/architecture.md) for the full shape\nand the [ADRs](docs/adr/) for key decisions (stack, stream transport, rate limit).\n\n## Tests\n\n\u003e Pure-function + mocked-route only — runs in ~1s and needs no API keys.\n\n```bash\npnpm test        # vitest, runs in ~1s\npnpm typecheck   # tsc --noEmit (strict)\npnpm lint        # eslint\n```\n\nCovered: the Brave client (normalization + error handling for both endpoints),\nthe stream prelude builder, the `[n]` citation parser, the snippet classifier\n(prose vs JSON/table/code), the rate limiter (cap + window + reset), and the\nanswer route (rate-limit headers + 429 shape, with the AI SDK mocked).\n\n## Deploy\n\nDeployed on Vercel. The app builds without keys (env validation is lazy, runtime\nonly), so the first deploy succeeds and the API returns 500 until you add the\ntwo secrets.\n\n```bash\npnpm dlx vercel link\npnpm dlx vercel --prod\n# add BRAVE_API_KEY and ANTHROPIC_API_KEY for Production (dashboard or\n# `vercel env add`), then redeploy so the deployment picks them up:\npnpm dlx vercel --prod\n```\n\nNo `vercel.json` is needed — the route declares `runtime = \"nodejs\"` itself.\n\n## What I'd add next\n\n- **Compare mode** — run both endpoints in parallel for one question and show the\n  answers + stats side by side (the toggle, taken to its logical conclusion).\n- **Durable rate limiting** — swap the in-memory limiter for Upstash Ratelimit so\n  limits are shared across serverless instances (see\n  [ADR 0003](docs/adr/0003-ratelimit-strategy.md)).\n- **MCP server** — expose the grounded-answer flow as an MCP tool; Brave is a\n  leading MCP search backend, so this is a natural fit.\n- **More Brave endpoints** — news, images, and video for richer answers.\n\nAnd if this were a real Brave-published reference, [docs/launch-playbook.md](docs/launch-playbook.md)\nsketches how I'd ship the surrounding content — blog, notebook, launch-day\nchannels, community norms, and the metrics that would actually matter.\n\nSee [CUSTOMER-ZERO.md](CUSTOMER-ZERO.md) for friction encountered while building\nthis, with suggested fixes.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkbennett2000%2Fsourced","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkbennett2000%2Fsourced","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkbennett2000%2Fsourced/lists"}