**Phase 122 (image documents) — final verification pass: all green. No code changes were needed; defects found: none.**
**Verified (implementation already complete in working tree, reviewed end-to-end):**
- Toggle (`BOR_IMAGES`/`BOR_IMAGE_EXTENSIONS`/`BOR_IMAGE_DIR`, off by default) + `GET /api/config` `images` flag
- Ingest: bytes digest, `image_dir` persistent copy, `content = summary = vision description` (chat-model call; only text embedded), fail-soft skip + `images_failed` counter
- Serve/display: `/api/documents/{id}/image` route (404 matrix), viewer `<img>` + description, Sources 48px lazy thumbnails, chat inline source figure (alt = summary), agent `read` marker
- Prune guard: images-off syncs never prune `is_image` docs
**Test / lint / coverage (exact commands & outcomes):**
- `uv run pytest` → exit 0 (green; note: pytest 9.1.1 `-q` omits the final count line in output — exit code authoritative)
- `uv run pytest --cov=app --cov-report=term-missing` → **2715 passed, exit 0, TOTAL 99%** (>90% gate)
- `uv run ruff check . && uv run pyright` → "All checks passed!" / "0 errors, 0 warnings, 0 informations"
- `uv run pytest tests/e2e/test_image_documents.py -v --no-cov` → **4 passed, exit 0** (isolation)
**Completion criteria:** (1) images=true → described/embedded/displayed docs: ✅ (E2E + integration) · (2) images=false byte-identical + image docs survive sync: ✅ (E2E negative app + unit/integration) · (3) viewer + chat rendering with alt text; failed description skips + logs, sync completes: ✅ · (4) test/lint/coverage gates: ✅ · (5) commit + phase move: deferred to harness per this pass's rules (working tree left uncommitted).
**Notable deviation (pre-existing, documented in code):** image route uses `require_user` (phase-79 posture, same gate as the document content endpoint) rather than the phase text's "public" parenthetical — matches the endpoint it mirrors.
**Next pending phase:** `123_chat_image_questions`.
2.8 KiB
2.8 KiB
Task 01 — Image toggle: BOR_IMAGES + extensions + dir, off by default
Phase: 122_image_documents · Source: TODO.md:6 — "The user should be able to turn on and off image support in their .env depending on whether their model supports it."
Objective
The single env knob for image support exists and is surfaced — BOR_IMAGES (default false), BOR_IMAGE_EXTENSIONS, BOR_IMAGE_DIR — with GET /api/config exposing the flag for UI gating. Toggle off = byte-identical behavior to pre-phase.
Work
app/config.py— three newSettingsfields (house docstring style, theupload_dir/llm_retriesprecedents):images: bool = False—BOR_IMAGES,0/falseoff (LOCKED A3 default). Docstring: master switch for image-document indexing (phase 122) — off by default, enable only when the chat model supports vision (descriptions come from it).image_extensions: str = "png,jpg,jpeg,webp,gif,bmp"—BOR_IMAGE_EXTENSIONS, comma-separated, case-insensitive; a property/parse into a lowercased-dotted frozenset (theimport_extension_setprecedent) — the image set is SEPARATE fromimport_extension_set(images are never user-added viaBOR_IMPORT_EXTENSIONS).image_dir: str = "~/bor-sources/images"—BOR_IMAGE_DIR, raw string,Path.expanduser()applied by the importer (thesources_dir/upload_dirconvention) — the persistent home for image bytes (uploads are replaced, checkouts re-cloned — the copy must outlive the source file).
.env.example— the three entries with the comment block: off by default + the vision-model dependency note (LOCKED A3).app/api/config.py:30— theapp_configdict gains"images": settings.images(the dict isstr | bool-valued — bools already allowed). Extend the docstring: consumed by the chat composer (phase 123) to show/hide the attach control, optionally by the Sources page.- ASSUMPTION:
GET /api/configis already anonymous-readable (the UI gates on it pre-login in phase 123 — no auth change here).
Testing & Quality
- Unit:
tests/unit/test_image_documents.py(task 06 finalizes) — defaults (imagesFalse, extensions frozenset{".png", …}with the dotted form the matchers expect, dir default), env overrides, the frozenset parse is case-insensitive and trims spaces. - Integration: the existing
GET /api/configtest asserts the newimageskey (default false in the test env). - Coverage: >90% on the touched modules.
Completion Criteria
Settings()with no env:images is False, the extension set is the six defaults,image_diris the default path.GET /api/configreturnsimages: falsein the default test env (byte-check the other keys unchanged).uv run pytestgreen;uv run ruff check . && uv run pyrightclean.