phase: 123_chat_image_questions
All gates green. Verification complete. **Phase 123 — final verification pass (all 4 tasks already in `complete/`)** - Verified the full implementation is in the working tree: `app/api/chat_images.py` (upload/serve pair), `ChatRequest.image`/`ChatMessage.image` (path-validated, omitted-when-None), toggle-off + stale-file hinted error frames, `build_user_content` multimodal build at both sites (chat.py deflected branch + `run_agent`), config-gated composer attach/preview/upload-then-send, restore + shared rendering, CSP `img-src 'self' data:` carve-out, mock-LLM capture buffer. - `uv run pytest` → **2796 passed**, exit 0 (unit + integration). - `uv run pytest --cov=app --cov-report=term-missing` → **TOTAL 99%** (29/4615 missed; phase-123 modules 99–100%). - `uv run pytest tests/e2e/test_chat_image_questions.py -v --no-cov` → **5 passed** in isolation. - `uv run ruff check . && uv run pyright` → clean (0 errors). **Completion criteria:** (1) attach→send→multimodal text+image to the model, bubble/reload/shared all render it, saved chat stores the PATH with `"base64" not in json.dumps(stored)` — **verified** (E2E tests 1–4 + integration round-trip); (2) `BOR_IMAGES=false` — control hidden, exact hinted error frame, zero model calls / no query_log row — **verified** (E2E test 5 + integration); (3) text-only byte-identical (`content` stays a plain `str`) — **verified** (unit + integration); (4) all gates green — **verified**; (5) commit + phase move — left to the harness per pipeline rules (no `git add`/`commit` run). No defects found; no live-infrastructure changes (repo + local dev DB only). **Next pending phase: none** — 123 is the last phase in `todo/`.
This commit is contained in:
@@ -396,6 +396,22 @@ class Settings(BaseSettings):
|
||||
#: upload): the served copy must outlive the source file.
|
||||
image_dir: str = "~/bor-sources/images"
|
||||
|
||||
# --- Chat image questions (phase 123: attach an image to a question) ---
|
||||
#: Where a user's question image bytes are stored (phase 123,
|
||||
#: ``BOR_CHAT_IMAGE_DIR`` — the ``image_dir`` convention: a sibling of
|
||||
#: phase 122's document-image dir, separate because question-images
|
||||
#: are per-conversation, not per-source). Raw string —
|
||||
#: ``Path.expanduser()`` is applied by the upload/serve routes, not
|
||||
#: here. Files land as ``<uuid4().hex>.<ext>`` — the uuid is the
|
||||
#: credential (no enumeration value; the saved/shared chat record
|
||||
#: carries the served path, never base64, LOCKED A5).
|
||||
chat_image_dir: str = "~/bor-sources/chat-images"
|
||||
#: Cap in MiB for one question-image upload (phase 123, LOCKED A5 —
|
||||
#: the ~10 MB cap; ``BOR_CHAT_IMAGE_MAX_MB``). ``<= 0`` would reject
|
||||
#: every upload — a typo, so the validator fails loudly at startup
|
||||
#: (the ``upload_max_mb`` pattern).
|
||||
chat_image_max_mb: int = 10
|
||||
|
||||
# --- Docs push (phase 59: save a chat answer as documentation) ---
|
||||
#: The git repo a saved chat answer is committed to (phase 59, D3):
|
||||
#: **any** remote — a URL (``https://``, ``ssh://``, ``git@``) or a
|
||||
@@ -574,6 +590,15 @@ class Settings(BaseSettings):
|
||||
raise ValueError("upload_max_mb must be > 0 (MiB)")
|
||||
return v
|
||||
|
||||
@field_validator("chat_image_max_mb")
|
||||
@classmethod
|
||||
def _chat_image_max_mb_positive(cls, v: int) -> int:
|
||||
"""``0``/negative would reject every question-image upload — fail
|
||||
loud at startup (the ``upload_max_mb`` precedent, phase 123)."""
|
||||
if v <= 0:
|
||||
raise ValueError("chat_image_max_mb must be > 0 (MiB)")
|
||||
return v
|
||||
|
||||
@field_validator("history_max_turns")
|
||||
@classmethod
|
||||
def _history_max_turns_non_negative(cls, v: int) -> int:
|
||||
|
||||
Reference in New Issue
Block a user