Files
FabledScribe/src/fabledassistant/models/image_cache.py
T
bvandeusen efe0a15b8c Add image search with local cache (Phase 23)
Images found via SearXNG are fetched server-side, stored on disk, and
served from /api/images/<id> — the user's browser never contacts the
original image host.  Original URLs are preserved for citation.

New files:
- alembic/versions/0016_add_image_cache.py  — image_cache table
- src/fabledassistant/models/image_cache.py — SQLAlchemy model
- src/fabledassistant/services/images.py    — fetch/store/serve logic
- src/fabledassistant/routes/images.py      — GET /api/images/<id>

Modified:
- config.py: IMAGE_CACHE_DIR (/data/images), IMAGE_MAX_BYTES (5 MB)
- research.py: _search_searxng_images() — SearXNG categories=images
- tools.py: _IMAGE_TOOLS def + search_images branch in execute_tool
- intent.py: search_images routing rule (explicit visual language only)
- app.py: register images_bp
- docker-compose.yml: image_cache named volume mounted at /data/images
- ToolCallCard.vue: "image_search" label

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 12:55:10 -05:00

26 lines
1.0 KiB
Python

from datetime import datetime, timezone
from sqlalchemy import DateTime, Integer, Text
from sqlalchemy.orm import Mapped, mapped_column
from fabledassistant.models import Base
class ImageCache(Base):
"""Metadata for an image fetched from the web and stored locally on disk."""
__tablename__ = "image_cache"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
url_hash: Mapped[str] = mapped_column(Text, unique=True, nullable=False, index=True)
original_url: Mapped[str] = mapped_column(Text, nullable=False)
source_domain: Mapped[str | None] = mapped_column(Text, nullable=True)
title: Mapped[str | None] = mapped_column(Text, nullable=True)
content_type: Mapped[str] = mapped_column(Text, nullable=False, default="image/jpeg")
file_size: Mapped[int | None] = mapped_column(Integer, nullable=True)
file_ext: Mapped[str] = mapped_column(Text, nullable=False, default="jpg")
fetched_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
default=lambda: datetime.now(timezone.utc),
)