Screenshot Folder Source
Seraph does not connect to a screenshot app or service. It can scan a local directory that contains ordinary screenshot image files.
The producing app is intentionally anonymous to Seraph. Seraph does not call a recorder, read recorder metadata, require manifests, or expect any service handshake. The contract is just .png, .jpg, and .jpeg files in a configured folder.
Boundary
The screenshot producer owns:
- screenshot capture
- operating-system permissions
- capture cadence and pause state
- folder selection
- retention and storage cleanup
- blocklists
- its own UI and activity state
Seraph owns:
- scanning a configured directory for image files
- computing Seraph-side image hashes
- duplicate detection
- local image artifact analysis and future provider-backed image analysis through Seraph settings
ScreenObservationpersistence- report generation
The screenshot producer must not write Seraph-specific sidecars, observations, analysis output, reports, or manifests for this path. Seraph must not require a live producer connection.
Configuration
Seraph resolves the screenshot folder in this order:
SERAPH_SCREENSHOT_FOLDER- Seraph settings key
screenshot_folder - Seraph workspace default
artifacts/screenshot-folder
The default is a generic Seraph-owned workspace folder so unconfigured Seraph never assumes a specific screenshot producer. To consume screenshots from another app, configure Seraph with that app's screenshot folder explicitly.
Seraph does not migrate or resolve producer-specific screenshot keys. API requests, stored settings, and environment configuration use only screenshot_folder or SERAPH_SCREENSHOT_FOLDER. artifact_root and producer-specific key names are not part of the current contract.
Folder Scan
Seraph exposes one on-demand local scan endpoint for its own UI and scheduler:
POST /api/observer/screenshot-folder/scan
Optional JSON body:
{
"screenshot_folder": "/path/to/screenshots",
"limit": 100
}
If screenshot_folder is omitted, Seraph uses the configured folder. For each new image, Seraph computes SHA-256 plus local image facts such as byte size, file format, and dimensions when detectable. Seraph stores those Seraph-owned facts in observation details, persists a ScreenObservation, and leaves analysis and report generation inside Seraph.
The request model is intentionally strict: legacy artifact_root or producer-specific fields are rejected instead of being treated as screenshot-folder aliases.
The artifact analysis endpoint returns Seraph-owned local image analysis, including source, hash, byte size, file format, dimensions when detectable, observation id, and report readiness. This analysis is computed from the image file in Seraph.
End-of-day reports consume screenshot-folder ScreenObservation rows through the same report builder as other screen observations. Seraph records the observation source as screenshot_folder from its own stored capture-artifact details and includes report-safe screenshot samples using filenames, format, dimensions, and size. Reports do not rely on recorder manifests, sidecars, or service metadata.
Configure a narrow, trusted screenshot directory. Seraph rejects obvious broad roots such as the filesystem root, home folder, Desktop, Downloads, and Seraph workspace root.
Settings Surface
The Seraph settings UI describes this as a local screenshot folder, not as Seraph-owned capture or a connected service. The folder status includes:
- configured screenshot folder
- configuration source
- image count
- latest image timestamp
- local image-file scan status
- manual local folder scan action
- editable saved folder when no env override is present
- inspection endpoint
- stored artifact type:
image
The settings panel saves screenshot_folder through /api/settings/screen-analysis. The manual scan action calls Seraph's local /api/observer/screenshot-folder/scan endpoint. Seraph can also run its own screenshot_folder_ingest scheduler job, controlled by SCREENSHOT_FOLDER_INGEST_ENABLED, SCREENSHOT_FOLDER_INGEST_INTERVAL_MIN, and SCREENSHOT_FOLDER_INGEST_LIMIT.
Both paths only read local image files from the configured folder. They do not start, connect to, or query any screenshot producer.
Remote VLM Analysis Target
Seraph can keep screenshot production separate from analysis while still using a GPU on another machine. The target shape is:
- A producer writes ordinary screenshot image files to the configured folder.
- Seraph scans that folder and owns observation/report persistence.
- A separate image-analysis service accepts image bytes and forwards them to a private LAN/VPN vision-language model backend.
The reusable service repo is public under the Seraph organization:
- repo: seraph-quest/vlm-screenshot-server
- purpose: Dockerized FastAPI screenshot analysis wrapper for OpenAI-compatible VLM backends
- endpoints:
POST /v1/analyze-filefor multipart uploads andPOST /v1/analyzefor base64 image payloads - run modes: API wrapper only, or API wrapper plus a GPU
vllm/vllm-openaibackend viadocker-compose.gpu.yml
For an RTX 3090 Ti 24 GB server, the current preferred Gemma-first target is Unsloth's Gemma 4 26B-A4B quantized GGUF/Dynamic 4-bit path. Unsloth's Gemma 4 docs list practical 4-bit memory footprints for this card class, including the 26B-A4B family in the high-teens GB range.
Example GPU-server setup with llama.cpp:
curl -LsSf https://llama.app/install.sh | sh
llama serve \
-hf unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q4_K_M \
--host 0.0.0.0 \
--port 8000 \
--ctx-size 32768 \
--chat-template-kwargs '{"enable_thinking":false}'
The VLM backend then exposes an OpenAI-compatible API at:
http://GPU_SERVER_IP:8000/v1
Run the screenshot-analysis wrapper:
git clone https://github.com/seraph-quest/vlm-screenshot-server.git
cd vlm-screenshot-server
cp .env.example .env
Use:
VLM_BASE_URL=http://GPU_SERVER_IP:8000/v1
VLM_MODEL=unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q4_K_M
VLM_API_KEY=
VLM_TIMEOUT_SECONDS=180
VLM_MAX_TOKENS=700
VLM_TEMPERATURE=0
REDACT_VISIBLE_TEXT=true
Then start the wrapper:
docker compose up --build
Test the wrapper with a screenshot:
curl -F "file=@/path/to/screenshot.png" \
http://GPU_SERVER_IP:8088/v1/analyze-file
Seraph-side first-class local-vlm wiring is still a follow-up implementation item. The intended future settings shape is:
SERAPH_SCREEN_ANALYSIS_PROVIDER=local-vlm
SERAPH_LOCAL_VLM_BASE_URL=http://GPU_SERVER_IP:8088
SERAPH_LOCAL_VLM_MODEL=unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q4_K_M
Until that provider exists, this section is an operator target and integration contract, not a claim that current Seraph releases can call the wrapper automatically.
Current model notes:
- Gemma 4 26B-A4B quantized via Unsloth is the preferred 24 GB GPU benchmark target.
- Gemma 4 12B QAT/Q4 is the safer fallback if 26B-A4B is too slow or unavailable.
- MiniCPM-V 4.5 quantized variants remain the strongest small non-Gemma comparison.
- Qwen2.5-VL-32B-Instruct-AWQ is the known Qwen VLM stress test for quality, but it is memory-sensitive on 24 GB.
- Qwen3 3.6B or "Qwen 3.6" should not be used for screenshot analysis unless the exact checkpoint is confirmed as a vision-language model. Text-only Qwen3 checkpoints do not replace a VLM.
Sources checked June 30, 2026:
Verification
This branch verifies the image-source path with:
cd /Users/bigcube/Desktop/repos/seraph/backend
UV_CACHE_DIR=/tmp/seraph-uv-cache uv run pytest tests/test_observer_screen_artifacts.py tests/test_settings_api.py::test_artifact_storage_exposes_screenshot_folder_status tests/test_screenshot_folder_ingest_job.py
cd /Users/bigcube/Desktop/repos/seraph/frontend
npm run test -- ArtifactStoragePanel.test.tsx
cd /Users/bigcube/Desktop/repos/seraph
PYTHONPYCACHEPREFIX=/tmp/seraph-pycache python3 -m py_compile backend/src/observer/screenshot_folder_source.py backend/src/scheduler/jobs/screenshot_folder_ingest.py backend/src/scheduler/engine.py backend/src/api/observer.py backend/src/api/settings.py
git diff --check