vector-auditor / BACKEND_API.md
AkshayM
production hardening: circuit breakers, graceful degradation, health checks, parallel uploads, HF Spaces deploy config
fc2fe9f
|
Raw
History Blame Contribute Delete
6.9 kB
# Backend API Contract β€” Frontend Reference
Base URL: `http://localhost:8000`
## Authentication
All endpoints except `/auth/*` and `/health`/`/ready` require:
```
Authorization: Bearer <access_token>
```
### Auth Endpoints
| Method | Path | Request | Response |
|--------|------|---------|----------|
| POST | `/auth/register` | `{ email, password, first_name?, last_name? }` | `UserResponse` |
| POST | `/auth/login` | `{ email, password }` | `{ access_token, user_id, roles }` |
| POST | `/auth/logout` | β€” | 204 |
| GET | `/auth/token/refresh` | β€” | `{ access_token }` |
| POST | `/auth/mfa/setup` | β€” | `{ secret, uri, qr_code_url }` |
| POST | `/auth/mfa/verify` | `{ code }` | `UserResponse` |
| GET | `/auth/oauth/config` | β€” | `{ github_client_id }` |
| POST | `/auth/oauth/github` | `{ code }` | `LoginResponse` |
## Documents
### Upload β€” `POST /documents`
```
Content-Type: multipart/form-data
Body: files[] = (binary PDF files)
```
Returns `{ uploaded_documents: [{ upload_id, document_id, filename, status }] }`
Documents process asynchronously. Poll `GET /uploads/{upload_id}` for status.
### List Documents β€” `GET /documents`
```
Response: [
{
id: string,
filename: string,
status: "processing" | "ready" | "failed",
has_pii: bool,
cloudinary_url: string | null, // PDF viewing URL
uploaded_by: string,
created_at: string | null
}
]
```
### Get Document β€” `GET /documents/{doc_id}`
Returns single `DocumentResponse` (same shape as list item).
### Delete Document β€” `DELETE /documents/{doc_id}`
204 No Content.
### Backfill Cloudinary β€” `POST /documents/backfill`
Re-uploads old docs to Cloudinary. Returns `{ backfilled: number, total: number }`.
### Upload Status β€” `GET /uploads/{upload_id}`
```
{
id, filename, stage, progress, error?, document_id?, user_id, created_at?, updated_at?
}
```
Stages: `uploading β†’ extracting β†’ chunking β†’ embedding β†’ indexing β†’ completed`
## Query
### Non-streaming β€” `POST /query`
```json
{
"question": "what is attention?",
"document_ids": ["uuid1", "uuid2"], // optional, null = all docs
"mode": "white_box", // "white_box" | "black_box"
"max_citations": 20, // optional, 1-100
"conversation_history": [ // optional, last 6 turns max
{ "role": "user", "content": "..." },
{ "role": "assistant", "content": "..." }
]
}
```
**Response:**
```json
{
"answer": "The Transformer uses multi-head attention... [1][2]",
"citations": [
{
"quote": "excerpt from the document",
"source": "attention_paper.pdf", // filename
"location": "page 3 / chunk 5",
"page": 3,
"document_id": "uuid"
}
],
"reasoning_path": ["Retrieved 6 citations across 2 hop(s)", "VERIFIED"],
"tokens_used": 450,
"cost_usd": 0.0009,
"query_id": "hex",
"verification": "VERIFIED" // white_box only, null for black_box
}
```
### Streaming β€” `POST /query/stream`
Same request body. Server-Sent Events (SSE):
```
data: {"type": "citations", "citations": [...], "query_id": "...", "reasoning_path": [...]}
data: {"type": "token", "content": "The"}
data: {"type": "token", "content": " Transformer"}
data: {"type": "token", "content": " uses..."}
data: {"type": "verification", "content": "VERIFIED"} // white_box only
data: {"type": "gap_analysis", "content": "missing X"} // white_box only, one per gap
data: {"type": "done", "tokens_used": 450, "cost_usd": 0.0009, "mode": "white_box", "query_id": "..."}
data: [DONE]
```
**Citation click:** Use `citation.document_id` β†’ fetch `GET /documents/{document_id}` β†’ read `cloudinary_url` β†’ open in PDF viewer at that URL.
## Analyze
### `POST /analyze`
```json
{
"question": "Compare their methodologies", // optional
"document_ids": ["uuid1", "uuid2"], // optional, null = error
"max_citations": 20
}
```
**Response:**
```json
{
"summary": "Both papers use Transformer-based architectures...",
"key_findings": ["Finding 1", "Finding 2", "..."],
"methodology": "Description of methods...",
"research_gaps": ["Gap 1", "Gap 2"],
"contradictions": ["Contradiction 1"],
"open_questions": ["Question 1"],
"limitations": "Only 3 documents analyzed",
"confidence": "high",
"citations": [/* same Citation shape as query */],
"documents_analyzed": ["doc-uuid-1", "doc-uuid-2"],
"cross_document_comparison": {
"common_themes": ["both use attention"],
"differences": ["doc1 uses BERT, doc2 uses GPT"],
"complementary_insights": ["doc1's findings on X support doc2's Y"]
}, // null when single doc
"per_document_summary": {
"paper1.pdf": "Introduces Transformer...",
"paper2.pdf": "Applies BERT to..."
} // null when single doc
}
```
## PDF Viewer Flow
1. Query returns citations with `document_id`
2. Frontend user clicks citation `[N]`
3. Frontend calls `GET /documents/{document_id}` β†’ gets `cloudinary_url`
4. Opens Cloudinary URL in PDF viewer (iframe or new tab)
**Cloudinary URL format:** `https://res.cloudinary.com/da1nocxo0/image/upload/v.../{user_id}/{doc_id}.pdf`
The `cloudinary_url` is always set for new uploads. For old docs, run `POST /documents/backfill` once.
## Sessions (Chat History)
| Method | Path | Notes |
|--------|------|-------|
| GET | `/sessions` | List all sessions |
| POST | `/sessions` | Create new session |
| GET | `/sessions/{sid}` | Session with messages |
| PUT | `/sessions/{sid}` | Update title |
| DELETE | `/sessions/{sid}` | Delete session |
| GET | `/sessions/{sid}/messages` | List messages |
| POST | `/sessions/{sid}/messages` | Add message |
| POST | `/sessions/{sid}/feedback` | Thumbs up/down |
## Query Modes
### White Box (default)
- Temperature: default (some variation)
- Max tokens: 3048
- Streams: `verification` + `gap_analysis` events
- Multi-hop retrieval (up to 3 rounds)
- Reasoning path included
- Structured answer with sections
### Black Box
- Temperature: 0 (deterministic)
- Max tokens: default (2048)
- No verification, no gap analysis, no reasoning
- Single-pass retrieval only
- Minimal answer β€” just the cited response, no commentary
## Greeting Detection
All three endpoints (`/query`, `/query/stream`, `/analyze`) detect greetings and return a friendly message without calling the LLM or searching documents:
```
"Hi there! What would you like to know about your documents?" // /query, /query/stream
"Hi there! Please select documents to analyze, or ask me a question about your documents." // /analyze
```
## Common Error Codes
| Code | Meaning |
|------|---------|
| 401 | Missing/invalid token |
| 400 | Guardrails blocked the query |
| 413 | File too large (max 50MB) |
| 502 | LLM provider unreachable |
| 429 | Rate limited (20/min for query, 10/min for analyze) |