conflens 0.1.14__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- conflens-0.1.14/.dockerignore +12 -0
- conflens-0.1.14/.env.example +41 -0
- conflens-0.1.14/.github/workflows/lint.yml +31 -0
- conflens-0.1.14/.github/workflows/release.yml +100 -0
- conflens-0.1.14/.github/workflows/test.yml +33 -0
- conflens-0.1.14/.gitignore +10 -0
- conflens-0.1.14/.python-version +1 -0
- conflens-0.1.14/.readthedocs.yaml +26 -0
- conflens-0.1.14/AGENTS.md +116 -0
- conflens-0.1.14/CLAUDE.md +33 -0
- conflens-0.1.14/Dockerfile +43 -0
- conflens-0.1.14/LICENSE +373 -0
- conflens-0.1.14/PKG-INFO +349 -0
- conflens-0.1.14/README.md +325 -0
- conflens-0.1.14/conflens/__init__.py +8 -0
- conflens-0.1.14/conflens/app.py +788 -0
- conflens-0.1.14/conflens/bibtex.py +56 -0
- conflens-0.1.14/conflens/cache.py +36 -0
- conflens-0.1.14/conflens/classifier.py +174 -0
- conflens-0.1.14/conflens/cli.py +75 -0
- conflens-0.1.14/conflens/dedup.py +100 -0
- conflens-0.1.14/conflens/llm.py +336 -0
- conflens-0.1.14/conflens/models.py +116 -0
- conflens-0.1.14/conflens/pipeline.py +207 -0
- conflens-0.1.14/conflens/pptx_export.py +225 -0
- conflens-0.1.14/conflens/scraper.py +277 -0
- conflens-0.1.14/conflens/sources.py +839 -0
- conflens-0.1.14/conflens/topics.py +365 -0
- conflens-0.1.14/conflens/view.py +188 -0
- conflens-0.1.14/docker-compose.yml +42 -0
- conflens-0.1.14/docs/ARCHITECTURE.md +222 -0
- conflens-0.1.14/pyproject.toml +64 -0
- conflens-0.1.14/run.py +10 -0
- conflens-0.1.14/start_docker.sh +17 -0
- conflens-0.1.14/tests/__init__.py +0 -0
- conflens-0.1.14/tests/test_bibtex_cache.py +43 -0
- conflens-0.1.14/tests/test_classifier_topics.py +115 -0
- conflens-0.1.14/tests/test_cli.py +34 -0
- conflens-0.1.14/tests/test_dblp_source.py +81 -0
- conflens-0.1.14/tests/test_dedup.py +41 -0
- conflens-0.1.14/tests/test_emnlp_source.py +42 -0
- conflens-0.1.14/tests/test_ijcai_source.py +40 -0
- conflens-0.1.14/tests/test_llm.py +88 -0
- conflens-0.1.14/tests/test_matching.py +63 -0
- conflens-0.1.14/tests/test_naacl_source.py +42 -0
- conflens-0.1.14/tests/test_openreview_source.py +125 -0
- conflens-0.1.14/tests/test_pscc_source.py +66 -0
- conflens-0.1.14/tests/test_result_roundtrip.py +72 -0
- conflens-0.1.14/tests/test_scraper.py +42 -0
- conflens-0.1.14/tests/test_view.py +60 -0
- conflens-0.1.14/uv.lock +2838 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Conference Paper Analyzer — environment variables
|
|
2
|
+
#
|
|
3
|
+
# Copy this file to `.env` and fill in the key(s) for the provider you use:
|
|
4
|
+
#
|
|
5
|
+
# cp .env.example .env
|
|
6
|
+
#
|
|
7
|
+
# The app loads `.env` automatically on startup. Every value is optional — set
|
|
8
|
+
# only what your chosen LLM provider needs. The in-app "API key" / "LLM endpoint"
|
|
9
|
+
# fields override these at runtime.
|
|
10
|
+
|
|
11
|
+
# ── Anthropic / Claude (default provider) ───────────────────────────────────
|
|
12
|
+
ANTHROPIC_API_KEY=
|
|
13
|
+
# Optional: custom Anthropic-compatible base URL
|
|
14
|
+
# ANTHROPIC_BASE_URL=
|
|
15
|
+
|
|
16
|
+
# ── OpenAI (install: uv sync --extra openai) ───────────────────────────────
|
|
17
|
+
OPENAI_API_KEY=
|
|
18
|
+
# Optional: OpenAI-compatible base URL (the OpenAI SDK reads this automatically;
|
|
19
|
+
# you can also set it per-run in the UI's "LLM endpoint" field)
|
|
20
|
+
# OPENAI_BASE_URL=
|
|
21
|
+
|
|
22
|
+
# ── LiteLLM (install: uv sync --extra litellm) ─────────────────────────────
|
|
23
|
+
# Key for your LiteLLM proxy/endpoint. If unset, LITELLM falls back to OPENAI_API_KEY.
|
|
24
|
+
# Set the endpoint itself in the UI's "LLM endpoint" field.
|
|
25
|
+
# LITELLM_API_KEY=
|
|
26
|
+
#
|
|
27
|
+
# LiteLLM also reads provider-native keys from the environment when it routes to
|
|
28
|
+
# a given backend, e.g.:
|
|
29
|
+
# AZURE_API_KEY=
|
|
30
|
+
# AZURE_API_BASE=
|
|
31
|
+
# GEMINI_API_KEY=
|
|
32
|
+
# COHERE_API_KEY=
|
|
33
|
+
|
|
34
|
+
# ── OpenReview source (ICLR / NeurIPS) ──────────────────────────────────────
|
|
35
|
+
# Usually required. OpenReview now gates anonymous note queries behind an
|
|
36
|
+
# anti-bot challenge (HTTP 403), so ICLR / NeurIPS venues need a logged-in
|
|
37
|
+
# session. Provide credentials to authenticate (this also enables private
|
|
38
|
+
# access). Use EITHER a pre-obtained token OR username + password.
|
|
39
|
+
# OPENREVIEW_TOKEN=
|
|
40
|
+
# OPENREVIEW_USERNAME=
|
|
41
|
+
# OPENREVIEW_PASSWORD=
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Lint
|
|
2
|
+
|
|
3
|
+
# Ruff on every pushed commit (any branch) and every pull request. The
|
|
4
|
+
# concurrency group keys on the branch name — shared by a branch's push and
|
|
5
|
+
# pull_request events — so cancel-in-progress collapses the two into a single
|
|
6
|
+
# run per commit and supersedes stale runs.
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
pull_request:
|
|
10
|
+
|
|
11
|
+
concurrency:
|
|
12
|
+
group: ${{ github.workflow }}-${{ github.head_ref || github.ref_name }}
|
|
13
|
+
cancel-in-progress: true
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
ruff:
|
|
17
|
+
name: ruff
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Install uv
|
|
23
|
+
uses: astral-sh/setup-uv@v6
|
|
24
|
+
with:
|
|
25
|
+
enable-cache: true
|
|
26
|
+
|
|
27
|
+
- name: Sync dependencies (base + dev)
|
|
28
|
+
run: uv sync
|
|
29
|
+
|
|
30
|
+
- name: Lint (ruff)
|
|
31
|
+
run: uv run ruff check .
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# When a pull request is merged into main, bump the version in pyproject.toml
|
|
4
|
+
# (and keep uv.lock in sync), commit it back to main, create a matching tag,
|
|
5
|
+
# then build and publish the package to PyPI.
|
|
6
|
+
#
|
|
7
|
+
# Bump level is patch by default; add a "minor" or "major" label to the PR to
|
|
8
|
+
# bump those instead. The version-bump commit and tag are pushed with the
|
|
9
|
+
# built-in GITHUB_TOKEN, which by design does NOT re-trigger workflows — so this
|
|
10
|
+
# never loops (the `[skip ci]` marker is belt-and-suspenders). Publishing runs
|
|
11
|
+
# in this same job (a separate tag-triggered workflow would never fire, since
|
|
12
|
+
# GITHUB_TOKEN-pushed tags don't trigger workflows).
|
|
13
|
+
#
|
|
14
|
+
# PyPI publishing requires a repo secret PYPI_API_TOKEN (a PyPI API token; set
|
|
15
|
+
# TWINE_USERNAME=__token__). Without it the publish step fails; the bump/tag
|
|
16
|
+
# still succeed.
|
|
17
|
+
on:
|
|
18
|
+
pull_request:
|
|
19
|
+
types: [closed]
|
|
20
|
+
branches: [main]
|
|
21
|
+
|
|
22
|
+
permissions:
|
|
23
|
+
contents: write
|
|
24
|
+
|
|
25
|
+
concurrency:
|
|
26
|
+
group: release
|
|
27
|
+
cancel-in-progress: false
|
|
28
|
+
|
|
29
|
+
jobs:
|
|
30
|
+
tag:
|
|
31
|
+
if: github.event.pull_request.merged == true
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
env:
|
|
34
|
+
BUMP: >-
|
|
35
|
+
${{ contains(github.event.pull_request.labels.*.name, 'major') && 'major'
|
|
36
|
+
|| contains(github.event.pull_request.labels.*.name, 'minor') && 'minor'
|
|
37
|
+
|| 'patch' }}
|
|
38
|
+
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
39
|
+
PR_TITLE: ${{ github.event.pull_request.title }}
|
|
40
|
+
steps:
|
|
41
|
+
- name: Checkout main
|
|
42
|
+
uses: actions/checkout@v4
|
|
43
|
+
with:
|
|
44
|
+
ref: main
|
|
45
|
+
fetch-depth: 0
|
|
46
|
+
fetch-tags: true
|
|
47
|
+
|
|
48
|
+
- name: Install uv
|
|
49
|
+
uses: astral-sh/setup-uv@v6
|
|
50
|
+
with:
|
|
51
|
+
enable-cache: true
|
|
52
|
+
|
|
53
|
+
- name: Configure git identity
|
|
54
|
+
run: |
|
|
55
|
+
git config user.name "github-actions[bot]"
|
|
56
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
57
|
+
|
|
58
|
+
- name: Bump version and create tag
|
|
59
|
+
id: bump
|
|
60
|
+
run: |
|
|
61
|
+
set -euo pipefail
|
|
62
|
+
CUR=$(grep -m1 -E '^version *= *"' pyproject.toml | sed -E 's/.*"([^"]+)".*/\1/')
|
|
63
|
+
IFS='.' read -r MA MI PA <<< "$CUR"
|
|
64
|
+
case "$BUMP" in
|
|
65
|
+
major) MA=$((MA + 1)); MI=0; PA=0 ;;
|
|
66
|
+
minor) MI=$((MI + 1)); PA=0 ;;
|
|
67
|
+
*) PA=$((PA + 1)) ;;
|
|
68
|
+
esac
|
|
69
|
+
NEW="${MA}.${MI}.${PA}"
|
|
70
|
+
TAG="v${NEW}"
|
|
71
|
+
echo "PR #${PR_NUMBER} merged — bumping ${CUR} -> ${NEW} (${BUMP})"
|
|
72
|
+
|
|
73
|
+
if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then
|
|
74
|
+
echo "Tag ${TAG} already exists — nothing to do."
|
|
75
|
+
echo "released=false" >> "$GITHUB_OUTPUT"
|
|
76
|
+
exit 0
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
sed -i -E "s/^version *= *\"[0-9]+\.[0-9]+\.[0-9]+\"/version = \"${NEW}\"/" pyproject.toml
|
|
80
|
+
# Keep the lockfile's project version in step with pyproject.toml.
|
|
81
|
+
uv lock
|
|
82
|
+
git add pyproject.toml uv.lock
|
|
83
|
+
git commit -m "chore: release ${TAG} [skip ci]"
|
|
84
|
+
git tag -a "${TAG}" -m "Release ${TAG} (PR #${PR_NUMBER}: ${PR_TITLE})"
|
|
85
|
+
git push origin HEAD:main
|
|
86
|
+
git push origin "${TAG}"
|
|
87
|
+
echo "released=true" >> "$GITHUB_OUTPUT"
|
|
88
|
+
echo "Released ${TAG}"
|
|
89
|
+
|
|
90
|
+
- name: Build distributions
|
|
91
|
+
if: steps.bump.outputs.released == 'true'
|
|
92
|
+
run: uv build # builds from the just-bumped pyproject.toml -> dist/*
|
|
93
|
+
|
|
94
|
+
- name: Publish to PyPI
|
|
95
|
+
if: steps.bump.outputs.released == 'true'
|
|
96
|
+
env:
|
|
97
|
+
TWINE_USERNAME: __token__
|
|
98
|
+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
|
|
99
|
+
# --skip-existing keeps re-runs idempotent if the version is already up.
|
|
100
|
+
run: uvx twine upload --non-interactive --skip-existing dist/*
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
# pytest + package build on every pushed commit (any branch) and every pull
|
|
4
|
+
# request. Concurrency keys on the branch name so a branch's push and
|
|
5
|
+
# pull_request events collapse into a single run and stale runs are superseded.
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
pull_request:
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: ${{ github.workflow }}-${{ github.head_ref || github.ref_name }}
|
|
12
|
+
cancel-in-progress: true
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
pytest:
|
|
16
|
+
name: pytest + build
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v6
|
|
23
|
+
with:
|
|
24
|
+
enable-cache: true
|
|
25
|
+
|
|
26
|
+
- name: Sync dependencies (base + dev)
|
|
27
|
+
run: uv sync
|
|
28
|
+
|
|
29
|
+
- name: Tests (pytest)
|
|
30
|
+
run: uv run pytest -q
|
|
31
|
+
|
|
32
|
+
- name: Build package
|
|
33
|
+
run: uv build
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
# Read the Docs configuration file
|
|
4
|
+
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
|
5
|
+
|
|
6
|
+
# Required
|
|
7
|
+
version: 2
|
|
8
|
+
|
|
9
|
+
# Set the OS, Python version, and other tools you might need
|
|
10
|
+
build:
|
|
11
|
+
os: ubuntu-24.04
|
|
12
|
+
tools:
|
|
13
|
+
python: "3.13"
|
|
14
|
+
|
|
15
|
+
# Build documentation in the "docs/" directory with Sphinx
|
|
16
|
+
sphinx:
|
|
17
|
+
configuration: docs/conf.py
|
|
18
|
+
|
|
19
|
+
# Optionally, but recommended,
|
|
20
|
+
# declare the Python requirements required to build your documentation
|
|
21
|
+
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
|
22
|
+
# python:
|
|
23
|
+
# install:
|
|
24
|
+
# - requirements: docs/requirements.txt
|
|
25
|
+
|
|
26
|
+
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Guidance for coding agents (and humans) working in this repository. See
|
|
4
|
+
[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for diagrams.
|
|
5
|
+
|
|
6
|
+
## What this is
|
|
7
|
+
|
|
8
|
+
**ConfLens** — a NiceGUI web app that browses conference papers (ACL Anthology,
|
|
9
|
+
EMNLP, NAACL, IJCAI, OpenReview / ICLR · NeurIPS, PSCC, and DBLP-indexed venues
|
|
10
|
+
such as ISGT Europe), classifies them against a theme with an LLM, discovers
|
|
11
|
+
topics, and synthesises per-topic findings. It also
|
|
12
|
+
flags near-duplicate titles and offers a fully client-side results view
|
|
13
|
+
(live confidence re-threshold, keyword search + highlight, sort/author facets,
|
|
14
|
+
save/load a run). Python 3.13, managed with **uv**.
|
|
15
|
+
|
|
16
|
+
## Setup & run
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
uv sync # base install (Anthropic, OpenAI, LiteLLM + NiceGUI)
|
|
20
|
+
uv sync --extra bertopic # + BERTopic topic backend (heavy)
|
|
21
|
+
|
|
22
|
+
cp .env.example .env # add provider key(s); loaded automatically
|
|
23
|
+
uv run conflens # NiceGUI GUI on http://localhost:6868
|
|
24
|
+
uv run conflens --clear-cache # wipe the on-disk cache
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The GUI (`app.py`) is a thin **NiceGUI** presentation layer; all non-UI logic
|
|
28
|
+
lives in `view.py` + `pipeline.py` — put behaviour changes there.
|
|
29
|
+
|
|
30
|
+
Docker: `docker compose up --build` (see the README).
|
|
31
|
+
|
|
32
|
+
## Checks before you commit
|
|
33
|
+
|
|
34
|
+
Run the full gate — CI (GitHub Actions) runs the same three on every push/PR:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
uv run ruff check . # lint (also `ruff check --fix .` to auto-fix imports)
|
|
38
|
+
uv run pytest -q # test suite — network- and API-free, fast
|
|
39
|
+
uv build # wheel + sdist build
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The tests live in `tests/` and are deterministic: parsers run on HTML/JSON
|
|
43
|
+
fixtures and the LLM stages use a fake `LLMClient` (a class with a
|
|
44
|
+
`structured()` method), so **no network or API keys are needed**. When you add
|
|
45
|
+
behaviour, add a test next to the matching `tests/test_*.py`.
|
|
46
|
+
|
|
47
|
+
Beyond the automated gate, for changes with real runtime surface:
|
|
48
|
+
|
|
49
|
+
- **Boot the app:** `uv run conflens`, confirm `GET /` returns 200
|
|
50
|
+
and the logs are clean.
|
|
51
|
+
- **Scraper/source changes:** also validate against the live source (counts, a
|
|
52
|
+
sample record's title/authors/abstract). ACL listing pages are large and may
|
|
53
|
+
truncate mid-download — `_robust_get` retries for a complete read; don't cache
|
|
54
|
+
a partial. OpenReview's v2 API may challenge anonymous requests from some IPs —
|
|
55
|
+
set `OPENREVIEW_USERNAME`/`OPENREVIEW_PASSWORD` or `OPENREVIEW_TOKEN` to
|
|
56
|
+
authenticate (see `.env.example`).
|
|
57
|
+
|
|
58
|
+
Keep changes ASCII-clean and match the surrounding style (dataclasses, targeted
|
|
59
|
+
regexes over heavyweight parsers, small focused modules).
|
|
60
|
+
|
|
61
|
+
## Where things live
|
|
62
|
+
|
|
63
|
+
| Area | Module |
|
|
64
|
+
|------|--------|
|
|
65
|
+
| UI (NiceGUI) | `app.py` |
|
|
66
|
+
| View logic | `view.py` (filter/sort/highlight/compute_view/exports) |
|
|
67
|
+
| Exports | `pptx_export.py`, `bibtex.py` |
|
|
68
|
+
| Orchestration | `pipeline.py` (`AnalysisConfig`, `run_analysis`) |
|
|
69
|
+
| Sources | `sources.py` (registry + `make_source`; `IJCAISource`, `OpenReviewSource`, `PSCCSource`, `DBLPSource`), `scraper.py` (`AnthologyScraper` — also serves EMNLP/NAACL) |
|
|
70
|
+
| LLM providers | `llm.py` (`LLMClient`, `make_client`) |
|
|
71
|
+
| Classify / topics | `classifier.py`, `topics.py` |
|
|
72
|
+
| Near-duplicate detection | `dedup.py` (`annotate_duplicates`) |
|
|
73
|
+
| Cache / models | `cache.py`, `models.py` |
|
|
74
|
+
|
|
75
|
+
## Conventions
|
|
76
|
+
|
|
77
|
+
- **All LLM calls go through `llm.py`'s `structured()`** — never call a provider
|
|
78
|
+
SDK directly from `classifier.py` / `topics.py`.
|
|
79
|
+
- **Add a conference**: implement `resolve_url` / `list_papers` /
|
|
80
|
+
`enrich_abstracts` in `sources.py`, then register it in `SOURCES`.
|
|
81
|
+
- **Add an LLM provider**: add an `LLMClient` subclass + a branch in
|
|
82
|
+
`make_client()`. Everything downstream uses the same interface.
|
|
83
|
+
- **Anything expensive should be cached** under `cache.default_cache_dir()`,
|
|
84
|
+
keyed so a re-run with the same inputs is free (see the caching table in
|
|
85
|
+
ARCHITECTURE.md). Respect the `force_refresh` flag.
|
|
86
|
+
- **Never hardcode API keys.** Read from env / `.env` / the in-app field.
|
|
87
|
+
|
|
88
|
+
## LLM / model notes
|
|
89
|
+
|
|
90
|
+
- Default model is `gpt-5.4` (OpenAI); structured output uses
|
|
91
|
+
`output_config.format` (Anthropic) or JSON-object mode + schema-in-prompt
|
|
92
|
+
(OpenAI/LiteLLM).
|
|
93
|
+
- The `effort` parameter is only sent to models that support it (e.g. **not**
|
|
94
|
+
Haiku 4.5 — it 400s). See `_EFFORT_MODELS` in `llm.py`.
|
|
95
|
+
- OpenAI/LiteLLM calls fall back gracefully (drop `response_format`, then
|
|
96
|
+
`temperature`) for models/endpoints that reject them.
|
|
97
|
+
|
|
98
|
+
## Git workflow
|
|
99
|
+
|
|
100
|
+
- **Do not commit to `main` directly.** Create a feature branch per change and
|
|
101
|
+
open a PR into `main`. Name the branch with a `feat/…` or `fix/…` prefix, and
|
|
102
|
+
**the branch pushed to GitHub (the PR's source) must not contain "claude"** in
|
|
103
|
+
its name (see [CLAUDE.md](CLAUDE.md)).
|
|
104
|
+
- **No AI-assistant / Claude Code attribution** anywhere — no `Co-Authored-By`,
|
|
105
|
+
`Claude-Session`, `🤖 Generated with …`, or `claude.ai/code` in commit
|
|
106
|
+
messages or PR descriptions, and don't attribute commit authorship to an
|
|
107
|
+
assistant. See [CLAUDE.md](CLAUDE.md). (The Anthropic *LLM provider* stays —
|
|
108
|
+
it's a feature.)
|
|
109
|
+
- `.env`, `.venv`, `__pycache__`, `~/.cache/...` and build artefacts are
|
|
110
|
+
gitignored — never commit them.
|
|
111
|
+
- **CI** is one workflow per concern under `.github/workflows/`: `lint.yml`
|
|
112
|
+
(ruff on every commit), `test.yml` (pytest + build), `release.yml`
|
|
113
|
+
(version bump + tag on PR merge). Keep lint and tests green.
|
|
114
|
+
- **Releases are automated** (`.github/workflows/release.yml`): merging a PR into
|
|
115
|
+
`main` bumps the version and tags it. Don't bump `version` in `pyproject.toml`
|
|
116
|
+
by hand — control the bump with a `minor`/`major` PR label (default patch).
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# ConfLens — project rules for agents
|
|
2
|
+
|
|
3
|
+
See [AGENTS.md](AGENTS.md) for contributor/agent guidance and
|
|
4
|
+
[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the design.
|
|
5
|
+
|
|
6
|
+
## Branch naming (persistent — applies to every session and agent)
|
|
7
|
+
|
|
8
|
+
Develop each change on a feature branch named `feat/…` or `fix/…` and open a PR
|
|
9
|
+
into `main`; never commit directly to `main`. **The branch pushed to GitHub (the
|
|
10
|
+
PR's source branch) must not contain "claude" in its name** — this holds even if
|
|
11
|
+
a session's setup designates a `claude/…` branch.
|
|
12
|
+
|
|
13
|
+
## Attribution policy (persistent — applies to every session and agent)
|
|
14
|
+
|
|
15
|
+
Do **not** add AI-assistant / "Claude Code" attribution anywhere in this
|
|
16
|
+
repository or its GitHub artifacts:
|
|
17
|
+
|
|
18
|
+
- **No** `Co-Authored-By:` trailers, `Claude-Session:` lines, `🤖 Generated
|
|
19
|
+
with …` footers, or `claude.ai/code` links in **commit messages** or
|
|
20
|
+
**pull-request descriptions**.
|
|
21
|
+
- Keep source, docs, and config free of Claude Code / assistant references.
|
|
22
|
+
- Do not attribute commit authorship to an AI assistant.
|
|
23
|
+
|
|
24
|
+
**Exception:** Anthropic / Claude *as an LLM provider* is a genuine product
|
|
25
|
+
feature (the `anthropic` provider and `claude-*` model ids in `llm.py`) and
|
|
26
|
+
stays — this policy is about tooling attribution, not the app's providers.
|
|
27
|
+
|
|
28
|
+
## Front-end (NiceGUI)
|
|
29
|
+
|
|
30
|
+
The app has a single **NiceGUI** GUI in `conflens/app.py` (console script
|
|
31
|
+
`conflens`). All non-UI logic (filtering, sorting, highlighting, the
|
|
32
|
+
computed view, exports) lives in **`conflens/view.py`** and the pipeline — put
|
|
33
|
+
behaviour changes there so the UI stays a thin presentation layer.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
|
|
3
|
+
# uv-managed image pinned to the project's Python (3.13).
|
|
4
|
+
FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim
|
|
5
|
+
|
|
6
|
+
# Which optional extras to install (e.g. bertopic).
|
|
7
|
+
# Override at build time, e.g. --build-arg EXTRAS="--extra bertopic".
|
|
8
|
+
ARG EXTRAS=""
|
|
9
|
+
|
|
10
|
+
ENV PYTHONUNBUFFERED=1 \
|
|
11
|
+
UV_COMPILE_BYTECODE=1 \
|
|
12
|
+
UV_LINK_MODE=copy \
|
|
13
|
+
UV_PROJECT_ENVIRONMENT=/app/.venv
|
|
14
|
+
|
|
15
|
+
WORKDIR /app
|
|
16
|
+
|
|
17
|
+
# 1) Install dependencies in a cached layer (no project source yet).
|
|
18
|
+
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
19
|
+
--mount=type=bind,source=uv.lock,target=uv.lock \
|
|
20
|
+
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
|
21
|
+
uv sync --frozen --no-install-project ${EXTRAS}
|
|
22
|
+
|
|
23
|
+
# 2) Copy the project and install it (the conflens entry point).
|
|
24
|
+
COPY . /app
|
|
25
|
+
COPY --link .env* /app/
|
|
26
|
+
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
27
|
+
uv sync --frozen ${EXTRAS}
|
|
28
|
+
|
|
29
|
+
ENV PATH="/app/.venv/bin:${PATH}"
|
|
30
|
+
|
|
31
|
+
# Run as a non-root user; keep the on-disk cache on a persistable path.
|
|
32
|
+
RUN useradd --create-home --uid 1000 app \
|
|
33
|
+
&& mkdir -p /home/app/.cache/conflens \
|
|
34
|
+
&& chown -R app:app /app /home/app
|
|
35
|
+
USER app
|
|
36
|
+
ENV HOME=/home/app
|
|
37
|
+
|
|
38
|
+
EXPOSE 6868
|
|
39
|
+
|
|
40
|
+
# ENTRYPOINT + CMD: default launches the server; `docker run <image> --clear-cache`
|
|
41
|
+
# (or any other flags) is appended to `conflens`.
|
|
42
|
+
ENTRYPOINT ["conflens"]
|
|
43
|
+
CMD ["--host", "0.0.0.0", "--port", "6868"]
|