Spaces:
Running
Running
File size: 6,904 Bytes
fc2fe9f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | # 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) |
|