Guidance for coding agents (and humans) working in this repository. See docs/ARCHITECTURE.md for diagrams.
ConfLens — a NiceGUI web app that browses conference papers (ACL Anthology, EMNLP, NAACL, IJCAI, OpenReview / ICLR · NeurIPS, PSCC, and DBLP-indexed venues such as ISGT Europe), classifies them against a theme with an LLM, discovers topics, and synthesises per-topic findings. It also flags near-duplicate titles and offers a fully client-side results view (live confidence re-threshold, keyword search + highlight, sort/author facets, save/load a run). Python 3.13, managed with uv.
uv sync # base install (Anthropic, OpenAI, LiteLLM + NiceGUI)
uv sync --extra bertopic # + BERTopic topic backend (heavy)
cp .env.example .env # add provider key(s); loaded automatically
uv run conflens # NiceGUI GUI on http://localhost:6868
uv run conflens --clear-cache # wipe the on-disk cacheThe GUI (app.py) is a thin NiceGUI presentation layer; all non-UI logic
lives in view.py + pipeline.py — put behaviour changes there.
Docker: docker compose up --build (see the README).
Run the full gate — CI (GitHub Actions) runs the same three on every push/PR:
uv run ruff check . # lint (also `ruff check --fix .` to auto-fix imports)
uv run pytest -q # test suite — network- and API-free, fast
uv build # wheel + sdist buildThe tests live in tests/ and are deterministic: parsers run on HTML/JSON
fixtures and the LLM stages use a fake LLMClient (a class with a
structured() method), so no network or API keys are needed. When you add
behaviour, add a test next to the matching tests/test_*.py.
Beyond the automated gate, for changes with real runtime surface:
- Boot the app:
uv run conflens, confirmGET /returns 200 and the logs are clean. - Scraper/source changes: also validate against the live source (counts, a
sample record's title/authors/abstract). ACL listing pages are large and may
truncate mid-download —
_robust_getretries for a complete read; don't cache a partial. OpenReview's v2 API may challenge anonymous requests from some IPs — setOPENREVIEW_USERNAME/OPENREVIEW_PASSWORDorOPENREVIEW_TOKENto authenticate (see.env.example).
Keep changes ASCII-clean and match the surrounding style (dataclasses, targeted regexes over heavyweight parsers, small focused modules).
| Area | Module |
|---|---|
| UI (NiceGUI) | app.py |
| View logic | view.py (filter/sort/highlight/compute_view/exports) |
| Exports | pptx_export.py, bibtex.py |
| Orchestration | pipeline.py (AnalysisConfig, run_analysis) |
| Sources | sources.py (registry + make_source; IJCAISource, OpenReviewSource, PSCCSource, DBLPSource), scraper.py (AnthologyScraper — also serves EMNLP/NAACL) |
| LLM providers | llm.py (LLMClient, make_client) |
| Classify / topics | classifier.py, topics.py |
| Near-duplicate detection | dedup.py (annotate_duplicates) |
| Cache / models | cache.py, models.py |
- All LLM calls go through
llm.py'sstructured()— never call a provider SDK directly fromclassifier.py/topics.py. - Add a conference: implement
resolve_url/list_papers/enrich_abstractsinsources.py, then register it inSOURCES. - Add an LLM provider: add an
LLMClientsubclass + a branch inmake_client(). Everything downstream uses the same interface. - Anything expensive should be cached under
cache.default_cache_dir(), keyed so a re-run with the same inputs is free (see the caching table in ARCHITECTURE.md). Respect theforce_refreshflag. - Never hardcode API keys. Read from env /
.env/ the in-app field.
- Default model is
gpt-5.4(OpenAI); structured output usesoutput_config.format(Anthropic) or JSON-object mode + schema-in-prompt (OpenAI/LiteLLM). - The
effortparameter is only sent to models that support it (e.g. not Haiku 4.5 — it 400s). See_EFFORT_MODELSinllm.py. - OpenAI/LiteLLM calls fall back gracefully (drop
response_format, thentemperature) for models/endpoints that reject them.
- Do not commit to
maindirectly. Create a feature branch per change and open a PR intomain. Name the branch with afeat/…orfix/…prefix, and the branch pushed to GitHub (the PR's source) must not contain "claude" in its name (see CLAUDE.md). - No AI-assistant / Claude Code attribution anywhere — no
Co-Authored-By,Claude-Session,🤖 Generated with …, orclaude.ai/codein commit messages or PR descriptions, and don't attribute commit authorship to an assistant. See CLAUDE.md. (The Anthropic LLM provider stays — it's a feature.) .env,.venv,__pycache__,~/.cache/...and build artefacts are gitignored — never commit them.- CI is one workflow per concern under
.github/workflows/:lint.yml(ruff on every commit),test.yml(pytest + build),release.yml(version bump + tag on PR merge). Keep lint and tests green. - Releases are automated (
.github/workflows/release.yml): merging a PR intomainbumps the version and tags it. Don't bumpversioninpyproject.tomlby hand — control the bump with aminor/majorPR label (default patch).