VisionSeq / docs /ARCHITECTURE.md
Antigravity Agent
Update docs, readme, and add project context
dc0d7c0
|
Raw
History Blame Contribute Delete
6.05 kB
# System Architecture
VisionSeq is built as a decoupled, service-oriented system: a Next.js frontend, a FastAPI backend, and an in-process PyTorch inference service. This document describes each component, their responsibilities, and how a request flows end-to-end.
**Live system:** [huggingface.co/spaces/Hardik-25/VisionSeq](https://huggingface.co/spaces/Hardik-25/VisionSeq)
---
## Architecture Diagram
```mermaid
graph TD
User([User Client]) -->|Web UI| FE[Next.js Frontend]
FE -->|REST API Requests| BE[FastAPI Backend]
BE -->|Preprocessed Tensor| INF[PyTorch Inference Service]
INF -->|Model weights| Model[ResNet-18 Multi-Head Classifier]
Model -->|Logits| Dec[Character Decoder]
Dec -->|Prediction & Confidence| BE
BE -->|JSON Response| FE
```
## Design Principles
- **Separation of concerns:** the frontend never touches the model; it only speaks REST/JSON to the backend. The backend never renders UI; it only validates, orchestrates, and serves predictions.
- **Single model instance:** the model is loaded exactly once per process, not per request, eliminating repeated disk I/O and weight-loading latency from the hot path.
- **Non-blocking request handling:** CPU/GPU-bound work never runs directly on FastAPI's async event loop β€” it's offloaded to a threadpool so I/O-bound requests (like concurrent uploads) are never starved.
- **Fail fast, fail loud:** if model weights are missing or corrupted, the server refuses to start rather than serving broken predictions.
---
## Component Details
### 1. Frontend β€” Next.js + Tailwind CSS
- **Presentation:** Responsive dashboard with glassmorphism styling, Framer Motion micro-interactions, and a live canvas preview of the uploaded image.
- **API integration:** Async client with support for both single and batch prediction, rendering per-character confidence breakdowns alongside the decoded sequence.
- **Config:** Backend base URL is injected via `NEXT_PUBLIC_API_URL`, keeping the frontend environment-agnostic across local, staging, and production deployments.
### 2. Backend β€” FastAPI
- **API surface:** JSON endpoints for single prediction, batch prediction, model metadata, and health monitoring (see [API.md](API.md)).
- **Middleware:** A boundary-level `Content-Length` check rejects oversized uploads before the body is read, backed by a token-bucket rate limiter to protect compute resources from abuse.
- **Threadpool scheduling:** Preprocessing and inference are wrapped in `run_in_threadpool()`, keeping the event loop free to accept and parse concurrent requests while CPU/GPU work happens on background threads.
### 3. ML Inference Service β€” PyTorch & ResNet-18
- **Lifecycle:** Instantiated once at startup as a singleton (`InferenceService`), avoiding per-request model-loading overhead.
- **Execution mode:** Runs in `eval()` mode; a dummy `[1, 1, 100, 200]` tensor is pushed through the model during boot to trigger kernel compilation and CUDA context initialization ahead of the first real request.
- **Device selection:** Automatically targets `cuda` when available, with a transparent fallback to CPU β€” no configuration required to run in either environment.
- **Multi-position head:** CAPTCHA decoding is framed as 6 concurrent per-position classification tasks. A ResNet-18 backbone (modified for 1-channel input) is pooled to `[B, 512, 6]` and classified into a shared 31-class vocabulary per position β€” see the [Modeling Approach](../README.md#modeling-approach) section of the README for the full rationale.
---
## Request Data Pipeline
| Step | Stage | Detail |
|---|---|---|
| 1 | **Upload** | Client submits a distorted CAPTCHA image via `multipart/form-data`. |
| 2 | **Defensive check** | Middleware validates request size against `Content-Length` and checks the caller's rate-limit bucket. |
| 3 | **MIME verification** | API confirms the declared content type starts with `image/`. |
| 4 | **Streaming read** | Image bytes are read in 64KB chunks, aborting if the stream exceeds the 5MB limit β€” protects against missing/spoofed `Content-Length` headers. |
| 5 | **Structural integrity** | `PIL.Image.verify()` confirms the file is a physically valid image, not a disguised payload. |
| 6 | **Preprocessing** (threadpool) | Grayscale conversion, resize to `100Γ—200`, normalization, tensor conversion to `[1, 1, 100, 200]`. |
| 7 | **Inference** (threadpool) | ResNet-18 forward pass produces raw logits of shape `[1, 6, 31]`. |
| 8 | **Decoding** | Softmax per position; the argmax index at each of the 6 positions maps to a character in the 31-symbol vocabulary. |
| 9 | **Telemetry** | Uptime and request counters are updated; structured logs capture request ID, latency, and prediction. |
---
## Why This Design
**Per-position classification over CTC.** Because every label in this dataset is exactly 6 characters, there's no variable-length alignment problem for CTC to solve β€” a shared linear head over 6 pooled feature vectors directly optimizes the actual objective with less architectural and training complexity.
**Threadpool over async-native inference.** PyTorch's synchronous CPU/GPU execution doesn't natively play well with `asyncio`; rather than rewriting the inference stack around async primitives, offloading to a threadpool is the minimal-risk way to keep the event loop responsive under concurrent load.
**Singleton over per-request instantiation.** Model loading (deserializing ~44MB of weights, initializing CUDA context) is orders of magnitude slower than a single forward pass β€” doing it once at startup, rather than per request, is what makes sub-200ms latency achievable at all.
---
## Related Documentation
- [API Specification](API.md) β€” endpoint contracts and payloads
- [Hardening Notes](HARDENING.md) β€” security and reliability measures layered onto this architecture
- [Engineering Audit](AUDIT.md) β€” independent review of this design
- [Deployment Guide](DEPLOYMENT.md) β€” running this architecture locally, via Docker, or in the cloud