Content compression for AI agents. Extract once, render per query.
Project description
Brief
Reading the web is the most expensive thing your agent does, and the least of what it's good at.
Brief reads content so your agent doesn't have to. Give it a URL and a question, and it extracts, summarizes, and caches the answer. Webpages, videos, PDFs, Reddit, GitHub, all through one interface.
from brief import brief
# Is this page even relevant? (~1 sentence)
brief("https://fastapi.tiangolo.com/", "async support", depth=0)
# What do I need to know? (summary + key points)
brief("https://fastapi.tiangolo.com/", "async support", depth=1)
# Give me everything. (detailed analysis with examples)
brief("https://fastapi.tiangolo.com/", "async support", depth=2)
Every answer is cached as a plain .brief file. Ask once, reuse forever. A team of agents can share research without repeating work. One agent investigates, another reasons, another writes, and nobody re-reads the same source.
The more your system runs, the more it already knows.
# Agent A researches FastAPI (extraction + LLM call)
brief("https://fastapi.tiangolo.com/", "async support", depth=2)
# Minutes later, Agent B is writing a comparison doc.
# Same URL, same question. Instant. No fetch, no LLM, no tokens.
check_brief("https://fastapi.tiangolo.com/")
# → "async-support-deep.brief: FastAPI handles async natively..."
# Agent B goes deeper with a new question. One LLM call, no re-extraction.
brief("https://fastapi.tiangolo.com/", "error handling", depth=1)
Agent A paid the cost. Agent B got it free. Agents should spend tokens on reasoning, not searching.
Install
Requires Python 3.12+.
pip install getbrief
Brief uses any OpenAI-compatible LLM for summarization. Create a .env file:
BRIEF_LLM_API_KEY=sk-or-v1-your-key
BRIEF_LLM_BASE_URL=https://openrouter.ai/api/v1
BRIEF_LLM_MODEL=google/gemma-3-4b-it:free # any OpenRouter free or cheap model works
Free models work great. Also works with OpenAI, Ollama (local), and Groq. See .env.example for all options.
Common patterns
Triage many URLs, then go deep on what matters
from brief import brief_batch, brief
# Scan 10 URLs for pennies. Which ones are relevant?
headlines = brief_batch([
"https://docs.python.org/3/library/asyncio.html",
"https://fastapi.tiangolo.com/",
"https://flask.palletsprojects.com/",
], query="python async web framework", depth=0)
# Go deep on the one that matters
detail = brief("https://fastapi.tiangolo.com/", "async support", depth=2)
Compare sources
from brief import compare
# Briefs each source, then synthesizes a comparison
result = compare(
["https://fastapi.tiangolo.com/", "https://flask.palletsprojects.com/"],
query="how do they handle middleware",
depth=2,
)
Check what's already been researched
from brief import check_brief
# See what queries have already been answered for this URL
check_brief("https://fastapi.tiangolo.com/")
Depth levels
depth=0 headline one sentence, is this worth reading?
depth=1 summary 2-3 sentences + key points (default)
depth=2 deep dive detailed analysis with specifics, examples, trade-offs
Each (query, depth) pair produces its own .brief file.
Content types
Brief handles five content types with the same interface:
- Webpages — trafilatura strips navigation, ads, and scripts. Falls back to httpx, then optionally Playwright for bot-protected sites.
- Videos — yt-dlp fetches captions. If none exist, faster-whisper transcribes audio locally.
- PDFs — pymupdf extracts text page by page.
- Reddit — fetches post content and top comments via Reddit's JSON API.
- GitHub — fetches repo metadata, README, file tree, and open issues via GitHub's API.
Interfaces
Python
from brief import brief, brief_batch, compare, check_brief
CLI
brief --uri "https://example.com" --query "key takeaways"
brief --uri "https://example.com" --depth 0
brief --compare --batch "https://url1.com" --batch "https://url2.com" --query "compare"
brief --list
MCP
{
"mcpServers": {
"brief": {
"command": "uvx",
"args": ["--from", "getbrief", "brief-mcp"],
"env": {
"BRIEF_LLM_API_KEY": "sk-or-v1-your-key",
"BRIEF_LLM_BASE_URL": "https://openrouter.ai/api/v1",
"BRIEF_LLM_MODEL": "google/gemma-3-4b-it:free"
}
}
}
}
This gives your agent four tools:
- brief_content — brief a URL with a query at depth 0–2
- check_existing_brief — see what's already been asked about a URL
- list_briefs — show all cached briefs
- compare_sources — same question across multiple URLs, with synthesis
HTTP API
uvicorn brief.api:app --port 8080
# Brief a URL
curl -X POST http://localhost:8080/brief \
-H "Content-Type: application/json" \
-d '{"uri": "https://fastapi.tiangolo.com/", "query": "async support", "depth": 1}'
# List all briefs
curl http://localhost:8080/briefs
# Health check
curl http://localhost:8080/health
The .briefs/ folder
Every URL gets its own subdirectory. Each (query, depth) adds a new .brief file:
.briefs/
├── fastapi-tiangolo-com/
│ ├── _source.json raw extraction, no LLM output
│ ├── async-support.brief depth=1 answer
│ └── async-support-deep.brief depth=2 answer, same query, richer
├── _comparisons/ cached cross-source comparisons
└── _index.sqlite3 fast lookups
Each .brief file includes a TRAIL section at the bottom, listing sibling briefs for the same source:
─── TRAIL ──────────────────────────────────────
→ async-support.brief
→ error-handling-deep.brief
→ _source.json
When an agent opens any .brief file, it instantly sees what else has already been asked about that source. No API call, no index lookup, just read the file. This means agents can build on each other's research naturally.
Configuration
Brief uses any OpenAI-compatible provider. OPENAI_API_KEY also works as a fallback if BRIEF_LLM_API_KEY is not set.
For video transcription without captions:
pip install getbrief[transcribe] # installs faster-whisper
For bot-protected sites (Cloudflare, etc.):
pip install getbrief[playwright]
playwright install chromium
For GitHub repos, the public API is rate-limited to 60 requests/hour. Set a token for higher limits:
GITHUB_TOKEN=ghp_your-token
Troubleshooting
- Paywalled / auth-protected content — Brief returns a clear error for 401/403/429 responses. It cannot extract content behind logins or paywalls.
- Bot protection (Cloudflare, etc.) — Install Playwright:
pip install getbrief[playwright] && playwright install chromium - Stale or bad summary — Use
--forceto skip cache and re-extract:brief --uri <URL> --force - Clear all cached data — Delete the
.briefs/folder. - LLM not responding — Check your
.envfile has valid API keys. Brief falls back to a heuristic summary if the LLM is unavailable.
Contributing
Brief is designed to be easy to extend. New extractors live in brief/extractors/ and each one is a single file implementing one function:
def extract(uri: str) -> list[dict[str, Any]]:
"""Return a list of chunks with 'text' and 'start_sec' keys."""
Contributions welcome: new content types, better summarization, CLI improvements, or API enhancements.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file getbrief-0.6.0.tar.gz.
File metadata
- Download URL: getbrief-0.6.0.tar.gz
- Upload date:
- Size: 36.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d2a67a2e3a9e62ae6a592bdf43c43ed9075cff770692b41bd46eb01fd4a0e1e2
|
|
| MD5 |
f9d44249af44dfab4bf8eab32b0c842a
|
|
| BLAKE2b-256 |
e1af4b8ac66809af7865c1e7c659b049a0c4682394e425cbb396bc7ec62b7096
|
Provenance
The following attestation bundles were made for getbrief-0.6.0.tar.gz:
Publisher:
publish.yml on aulesy/brief
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
getbrief-0.6.0.tar.gz -
Subject digest:
d2a67a2e3a9e62ae6a592bdf43c43ed9075cff770692b41bd46eb01fd4a0e1e2 - Sigstore transparency entry: 987181132
- Sigstore integration time:
-
Permalink:
aulesy/brief@4bed64e5ed03203a0eecfa486ffe087ee327c268 -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/aulesy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4bed64e5ed03203a0eecfa486ffe087ee327c268 -
Trigger Event:
release
-
Statement type:
File details
Details for the file getbrief-0.6.0-py3-none-any.whl.
File metadata
- Download URL: getbrief-0.6.0-py3-none-any.whl
- Upload date:
- Size: 39.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dd9399a21a7c2e8e595261e80bd772622639fa330c65998a1b7e61a25353107f
|
|
| MD5 |
8a7e6e85301751762c2391135996ee77
|
|
| BLAKE2b-256 |
094124a9fa664f8ac0761e197e071939b7489f83f51739a50bb43ec50c26ae34
|
Provenance
The following attestation bundles were made for getbrief-0.6.0-py3-none-any.whl:
Publisher:
publish.yml on aulesy/brief
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
getbrief-0.6.0-py3-none-any.whl -
Subject digest:
dd9399a21a7c2e8e595261e80bd772622639fa330c65998a1b7e61a25353107f - Sigstore transparency entry: 987181170
- Sigstore integration time:
-
Permalink:
aulesy/brief@4bed64e5ed03203a0eecfa486ffe087ee327c268 -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/aulesy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4bed64e5ed03203a0eecfa486ffe087ee327c268 -
Trigger Event:
release
-
Statement type: