Update summary.md for session: image cache, suggested notes improvements
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+30
-3
@@ -12,7 +12,7 @@
|
||||
> Include file-level details in the commit body when the change is non-trivial.
|
||||
|
||||
## Last Updated
|
||||
2026-02-28 — Quick-capture endpoint for Android/external clients; bug fixes: create_note list-title crash, AI Assist 400 error, note picker persistent context, intent routing
|
||||
2026-03-01 — Image search with local cache (Phase 23); suggested notes improvements (limit 8, threshold 0.45, relevance scores); suggested notes UX; quick-capture endpoint
|
||||
|
||||
## Project Overview
|
||||
Fabled Assistant is a self-hosted note-taking and task-tracking application with
|
||||
@@ -276,6 +276,7 @@ fabledassistant/
|
||||
│ │ ├── auth.py # /api/auth blueprint: register, login, logout, me, password, email change, status, password reset, invitation registration, OAuth login+callback — rate limiting, LOCAL_AUTH_ENABLED guards, _client_ip() helper
|
||||
│ │ ├── admin.py # /api/admin blueprint: backup, restore, user management, registration toggle, invitations, base URL, SMTP (admin only)
|
||||
│ │ ├── chat.py # /api/chat blueprint: conversations CRUD, SSE message streaming (all @login_required)
|
||||
│ │ ├── images.py # /api/images/<id> GET — serve locally-cached images (no auth required; IDs are opaque)
|
||||
│ │ ├── notes.py # /api/notes CRUD + wikilinks + backlinks + tag suggestions (all @login_required)
|
||||
│ │ ├── quick_capture.py # /api/quick-capture POST — mobile/external single-shot item creation (session auth)
|
||||
│ │ ├── tasks.py # /api/tasks CRUD + PATCH status (all @login_required)
|
||||
@@ -290,8 +291,9 @@ fabledassistant/
|
||||
│ │ ├── generation_buffer.py # In-memory SSE event buffer with cancel_event, reconnect support, auto-cleanup; supports chat (int keys) and assist (string keys)
|
||||
│ │ ├── generation_task.py # Background asyncio tasks: run_generation (chat, DB flush, titles, intent-first pipeline + tool loop) + run_assist_generation (lightweight, no DB); _INTENT_TRIGGER_WORDS + _should_skip_intent() for skipping intent on conversational messages
|
||||
│ │ ├── intent.py # Intent routing: classify_intent() makes fast non-streaming LLM call (num_ctx=4096); IntentResult has ack field (one-sentence acknowledgment streamed as TTFT)
|
||||
│ │ ├── tools.py # LLM tool definitions (create/delete note+task, update_note w/tag management, get_note, list_notes, search_notes w/type filter, list_tasks, full CalDAV suite incl. search_todos, search_web, research_topic when SearXNG enabled) + execute_tool dispatcher
|
||||
│ │ ├── research.py # SearXNG research pipeline: parallel query/fetch, streaming synthesis; run_research_pipeline() → Note; constants SEARXNG_QUERIES=5, PAGES_PER_QUERY=3, MAX_SYNTHESIS_SOURCES=12, CHARS_PER_SOURCE=2000
|
||||
│ │ ├── images.py # Image cache service: fetch_and_store_image (SHA-256 dedup, content-type validation, 5MB cap), get_image_record, get_image_path
|
||||
│ │ ├── tools.py # LLM tool definitions (create/delete note+task, update_note w/tag management, get_note, list_notes, search_notes w/type filter, list_tasks, full CalDAV suite incl. search_todos, search_web, research_topic, search_images when SearXNG enabled) + execute_tool dispatcher
|
||||
│ │ ├── research.py # SearXNG research pipeline: parallel query/fetch, streaming synthesis; run_research_pipeline() → Note; _search_searxng_images() for image category search; constants SEARXNG_QUERIES=5, PAGES_PER_QUERY=3, MAX_SYNTHESIS_SOURCES=12, CHARS_PER_SOURCE=2000
|
||||
│ │ ├── tag_suggestions.py # LLM-powered tag suggestions: suggest_tags() builds prompt with existing tags, calls generate_completion, parses JSON response
|
||||
│ │ ├── caldav.py # CalDAV integration: full event lifecycle (create/list/search/update/delete), todos (create/list/search/update/complete/delete), list_calendars, timezone (ZoneInfo), reminders (VALARM), attendees, multi-calendar search
|
||||
│ │ ├── settings.py # Settings CRUD with user_id isolation: get_setting, set_setting, set_settings_batch, get_all_settings
|
||||
@@ -669,6 +671,31 @@ When adding a new migration, follow these conventions:
|
||||
brainstorming requests, game design, writing help, or when the user references existing notes.
|
||||
Creative/ideation requests ("think of", "come up with", "brainstorm") always route to null (chat).
|
||||
`search_web` is only for genuinely new real-time facts not in the user's notes.
|
||||
- **Image search with local cache (Phase 23):** `search_images` tool (SearXNG `categories=images`) fetches
|
||||
images server-side, stores them on disk, and serves them from `/api/images/<id>` — the user's browser
|
||||
never contacts the original image host. Original URLs preserved for citation.
|
||||
- New migration `0016_add_image_cache.py`: `image_cache` table (`id`, `url_hash`, `original_url`,
|
||||
`source_domain`, `title`, `content_type`, `file_size`, `file_ext`, `fetched_at`)
|
||||
- `services/images.py`: `fetch_and_store_image()` — SHA-256 dedup, content-type validation (`image/*`
|
||||
only), 5 MB cap, same-origin `Referer` header to bypass hotlink protection, `IntegrityError` race
|
||||
handling. `get_image_record()`, `get_image_path()`.
|
||||
- `routes/images.py`: `GET /api/images/<id>` — no auth (IDs are opaque SHA-256-derived); `Cache-Control:
|
||||
max-age=86400`; 404 if file missing from disk.
|
||||
- `research.py`: `_search_searxng_images(query)` — SearXNG with `categories=images`, returns
|
||||
`[{img_src, page_url, title, source_domain}]` with same 429-retry logic as text search.
|
||||
- `tools.py`: `_IMAGE_TOOLS` list + `search_images` branch in `execute_tool` — fetches top 2 images,
|
||||
returns `{type: "image_search", data: {images: [{local_url, page_url, source_domain, title}]}}`.
|
||||
- `intent.py`: routing rule — only triggers on explicit visual language ("show me", "what does X look
|
||||
like", "picture of", "photo of"). Factual/description questions stay as null (chat).
|
||||
- Config: `IMAGE_CACHE_DIR` (default `/data/images`), `IMAGE_MAX_BYTES` (default 5 MB).
|
||||
- Docker: named volume `app_data` mounted at `/data` (covers all future persistent storage, not just
|
||||
images). Bind-mount alternative `./data:/data` documented in compose comments.
|
||||
- No CSP changes needed — `/api/images/` is same-origin, already covered by `img-src 'self'`.
|
||||
- **Suggested notes improvements:** `semantic_search_notes` now returns `list[tuple[float, Note]]`
|
||||
(was `list[Note]`); scores threaded through `context_meta["auto_notes"]` to frontend. Sidebar shows
|
||||
`%` badge per note: green ≥75%, amber 60–74%, muted 45–59%. Threshold raised 0.30 → 0.45 (only
|
||||
genuinely relevant notes shown). Limit raised 3 → 8 (no added compute — threshold is the real gate).
|
||||
Keyword fallback notes carry `score: null` (no badge shown).
|
||||
- **Quick-capture endpoint:** `POST /api/quick-capture` in `routes/quick_capture.py` (session auth via
|
||||
cookie). Takes `{"text": "natural language string"}`, runs `classify_intent` filtered to creation-only
|
||||
tools (`create_note`, `create_task`, `create_event`, `create_todo`), calls `execute_tool`, and returns
|
||||
|
||||
Reference in New Issue
Block a user