File size: 12,868 Bytes
1fabaeb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
---
title: ReliefLens AI
emoji: 🚨
colorFrom: red
colorTo: blue
sdk: docker
app_port: 7860
pinned: false
---
# ReliefLens AI

ReliefLens AI is a multimodal, human-in-the-loop emergency triage demo built for the AMD Developer Hackathon. It accepts public field evidence such as text, image uploads, optional audio, browser location, map-selected location, and textual place references, then produces prioritized incidents with recommended resources, location confidence, and operational explanations.

## Public demo

- Hugging Face Space frontend: `https://lablab-ai-amd-developer-hackathon-relieflens-frontend.hf.space`
- AMD FastAPI backend: `http://129.212.185.232:8080`

## Application routes

- Public portal: `/emergencies`
- Private admin: `/admin`
- Admin login: `/admin/login`
- Admin incident queue: `/admin/incidents`
- Admin incident detail: `/admin/incidents/[id]`
- Admin telemetry: `/admin/telemetry`

The public portal is limited to evidence submission and public-safe confirmation details. Sensitive incident management stays behind authenticated admin routes.

## What the system does

- Ingests image, text, optional audio, and location hints
- Resolves location from the best available source without inventing exact coordinates
- Produces prioritized incidents with severity, findings, and recommended resources
- Separates public intake from private incident management
- Shows admin telemetry for backend health and AMD demo metrics

## Location resolution

ReliefLens does not pretend that a photo alone can reveal exact location. Location is resolved in this order:

1. Browser geolocation, if the user shares it
2. Manual map click, if the user selects or corrects a point
3. Image GPS EXIF, if present
4. Textual location hints such as `Santa Ana`
5. `Location requires human review` when evidence is insufficient

If there is no browser location, no map point, no EXIF GPS, and no usable text location, the incident stays unresolved and requires human review.

## Evidence intake behavior

The public `/emergencies` portal supports:

- image upload
- optional audio recording or upload
- `What is happening?`
- `Where is this happening?`
- `Use my current location`
- map click to adjust or set location
- `Analyze Evidence`

Current behavior uses deterministic, rule-based fallback analysis:

- text, filenames, and location hints drive incident classification
- audio files are accepted but returned as `received_not_transcribed` when no ASR model is connected
- images are inspected only for metadata and optional EXIF GPS
- the system does not claim real vision inference unless a model is explicitly connected later

## Qwen-powered incident analysis

Rescue Evidence Intelligence can analyze submitted evidence with a Qwen model through an OpenAI-compatible API.

Required backend variables:

- `QWEN_BASE_URL`
- `QWEN_API_KEY`
- `QWEN_MODEL`
- `QWEN_ENABLED`

Behavior:

- if `QWEN_ENABLED=false`, the backend uses the existing rule-based fallback classifier
- if `QWEN_ENABLED=true`, the backend attempts Qwen analysis first
- if the model server times out, returns malformed JSON, or is unavailable, the backend falls back to `rule_based_fallback`
- the frontend never receives the API key

The admin dashboard shows:

- incident type
- severity
- confidence
- detected risks
- evidence summary
- recommended resources
- requires human review
- analysis provider: `qwen` or `rule_based_fallback`

## Admin incident management

Authenticated admins can:

- sign in through `/admin/login`
- review the full incident queue
- inspect detailed evidence metadata
- update incident status, review state, notes, and assigned team
- inspect backend and AMD telemetry

Required backend environment variables:

- `ADMIN_USERNAME`
- `ADMIN_PASSWORD`
- `ADMIN_TOKEN_SECRET`
- `ADMIN_TOKEN_EXPIRE_MINUTES`

These values must stay on the backend. Do not expose them through `NEXT_PUBLIC_*` variables or commit secrets to git.

## Safety

- Synthetic demo data only
- Human-in-the-loop required
- Not connected to emergency services
- Not for real-world dispatch
- AI suggestions are advisory only

## Security limitations

- The hackathon MVP uses bearer token authentication for `/api/admin/*`
- The frontend stores the admin access token in `sessionStorage`
- This is acceptable for the demo, but production should use HTTPS end-to-end and secure HttpOnly cookies
- Admin credentials must be configured from backend environment variables, not frontend code

## Architecture

Local development:

`Next.js frontend -> /backend rewrite proxy -> FastAPI backend`

Hugging Face Space deployment:

`Hugging Face Docker Space -> nginx on :7860 -> Next.js frontend -> AMD FastAPI backend -> AMD Qwen OpenAI-compatible endpoint`

The browser does not call `localhost:8080`, `127.0.0.1:8080`, the AMD FastAPI public IP, or the AMD Qwen endpoint directly from client-side code. Local frontend requests use `/backend`. The Hugging Face Space build uses same-origin `/api`, which nginx proxies to the AMD FastAPI backend at runtime.

## Backend endpoints

- `GET /health`
- `GET /api/demo/incidents`
- `GET /api/demo/scenario`
- `POST /api/demo/run`
- `POST /api/evidence/intake`
- `GET /api/evidence/status/{tracking_code}`
- `POST /api/demo/analyze-image`
- `POST /api/admin/login`
- `GET /api/admin/me`
- `GET /api/admin/incidents`
- `GET /api/admin/incidents/{incident_id}`
- `PATCH /api/admin/incidents/{incident_id}`
- `GET /api/admin/telemetry`
- `GET /api/amd/performance`

## Local development

### Backend

```bash
cd backend
set ADMIN_USERNAME=admin
set ADMIN_PASSWORD=admin123
set ADMIN_TOKEN_SECRET=local-dev-secret-change-me
set ADMIN_TOKEN_EXPIRE_MINUTES=720
set QWEN_ENABLED=false
python -m pytest tests -v
python -m uvicorn main:app --host 0.0.0.0 --port 8080 --reload
```

Run locally with Qwen disabled:

- keep `QWEN_ENABLED=false`
- the backend will use the rule-based fallback pipeline

Run locally with Qwen through vLLM or another OpenAI-compatible server:

```bash
cd backend
set QWEN_BASE_URL=http://127.0.0.1:8000/v1
set QWEN_API_KEY=token-abc123
set QWEN_MODEL=Qwen/Qwen2.5-7B-Instruct
set QWEN_ENABLED=true
python -m uvicorn main:app --host 0.0.0.0 --port 8080 --reload
```

### Frontend

```bash
cd frontend
npm install
npm run build
npm run dev
```

Local frontend environment file:

```env
NEXT_PUBLIC_API_URL=/backend
NEXT_PUBLIC_API_BASE_URL=/backend
NEXT_PUBLIC_BACKEND_URL=/backend
BACKEND_ORIGIN=http://localhost:8080
```

Local proxy checks:

- `http://localhost:3000/backend/health`
- `http://localhost:3000/admin/login`

Local admin login test credentials:

- username: `admin`
- password: `admin123`

## Deployment to AMD Cloud

Recommended topology:

- AMD VM: FastAPI backend on `:8080`
- AMD VM: external Qwen OpenAI-compatible endpoint on `:8000`
- Hugging Face Space: public container on `:7860`

On the AMD VM:

```bash
git clone https://github.com/<your-org-or-user>/ReliefLensAI.git
cd ReliefLensAI
bash scripts/deploy_backend_amd.sh
cp backend/.env.example backend/.env
```

Edit `backend/.env` for your VM. Minimum values:

```env
APP_ENV=production
DEBUG=false
DEMO_MODE=false
STORAGE_PATH=./data
CORS_ORIGINS=https://YOUR_SPACE.hf.space

QWEN_ENABLED=true
QWEN_BASE_URL=http://127.0.0.1:8000/v1
QWEN_API_KEY=amd-qwen-demo-key
QWEN_MODEL=Qwen/Qwen2-7B-Instruct

ADMIN_USERNAME=admin
ADMIN_PASSWORD=admin123
ADMIN_TOKEN_SECRET=relieflens-demo-secret-change-this
ADMIN_TOKEN_EXPIRE_MINUTES=720
```

Then start the backend:

```bash
bash scripts/start_backend_amd.sh
curl http://127.0.0.1:8080/health
curl http://129.212.185.232:8080/health
```

If Qwen is running separately on the same VM, verify it first:

```bash
curl http://127.0.0.1:8000/v1/models -H "Authorization: Bearer amd-qwen-demo-key"
```

## Deployment to Hugging Face Docker Space

The repository now includes a root `Dockerfile` for a single Hugging Face Docker Space container.

Container behavior:

- Next.js frontend runs internally on `127.0.0.1:3000`
- `nginx` exposes only port `7860`
- `/` routes to the Next.js frontend
- `/api/*` routes to the AMD FastAPI backend via `BACKEND_ORIGIN`
- `/health` routes to `${BACKEND_ORIGIN}/health`

Public browser behavior:

- the browser calls same-origin `/api` in the Hugging Face Space build
- no Qwen secret is exposed to the frontend bundle
- the Space does not call Qwen directly
- Qwen credentials stay only on the AMD backend environment

Space variables:

- `NEXT_PUBLIC_API_URL=/api`
- `NEXT_PUBLIC_API_BASE_URL=/api`
- `NEXT_PUBLIC_BACKEND_URL=/api`
- `BACKEND_ORIGIN=http://129.212.185.232:8080`

Do not put `QWEN_API_KEY`, admin credentials, or token secrets in any `NEXT_PUBLIC_*` variable or Hugging Face frontend variable.

Build and push flow:

1. Push this repository to the Hugging Face Space repo.
2. Configure the Space variables above.
3. Wait for the Docker build to finish.
4. Test:
   - `https://YOUR_SPACE.hf.space/health`
   - `https://YOUR_SPACE.hf.space/api/demo/incidents`
   - `https://YOUR_SPACE.hf.space/emergencies`

The Space container uses the root `Dockerfile` and serves the full app on port `7860`.

Deployment flow:

1. Create a Docker Space on Hugging Face.
2. Push this repository root to the Space repository.
3. Configure these Variables:
   - `NEXT_PUBLIC_API_URL=/api`
   - `NEXT_PUBLIC_API_BASE_URL=/api`
   - `NEXT_PUBLIC_BACKEND_URL=/api`
   - `BACKEND_ORIGIN=http://129.212.185.232:8080`
4. Wait for the Docker build.
5. Validate:
   - `https://YOUR_SPACE.hf.space/health`
   - `https://YOUR_SPACE.hf.space/api/demo/incidents`
   - `https://YOUR_SPACE.hf.space/admin/login`

## Hugging Face Space + AMD Qwen Deployment

This deployment mode keeps both Qwen and FastAPI on AMD infrastructure while Hugging Face hosts only the public UI and reverse proxy.

Architecture:

`Hugging Face Docker Space -> nginx on :7860 -> Next.js on :3000 -> AMD FastAPI on :8080 -> AMD Qwen OpenAI-compatible endpoint`

Required AMD backend runtime variables:

- `QWEN_ENABLED=true`
- `QWEN_BASE_URL=http://127.0.0.1:8000/v1`
- `QWEN_MODEL=Qwen/Qwen2-7B-Instruct`
- `QWEN_API_KEY=amd-qwen-demo-key`

Required Hugging Face Space variables:

- `NEXT_PUBLIC_API_URL=/api`
- `NEXT_PUBLIC_API_BASE_URL=/api`
- `NEXT_PUBLIC_BACKEND_URL=/api`
- `BACKEND_ORIGIN=http://129.212.185.232:8080`

Optional local Docker smoke test before pushing to Hugging Face:

```bash
docker build -t relieflens-space .
docker run --rm -p 7860:7860 \
  -e NEXT_PUBLIC_API_URL=/api \
  -e NEXT_PUBLIC_API_BASE_URL=/api \
  -e NEXT_PUBLIC_BACKEND_URL=/api \
  -e BACKEND_ORIGIN=http://129.212.185.232:8080 \
  relieflens-space
```

Then test:

- `http://localhost:7860/health`
- `http://localhost:7860/api/demo/incidents`
- `http://localhost:7860/admin/login`

## Final deployment checklist

1. Verify Qwen on AMD:
   - `curl http://127.0.0.1:8000/v1/models -H "Authorization: Bearer amd-qwen-demo-key"`
2. Start AMD backend:
   - `bash scripts/start_backend_amd.sh`
3. Verify AMD backend locally:
   - `curl http://127.0.0.1:8080/health`
4. Verify AMD backend publicly:
   - `curl http://129.212.185.232:8080/health`
5. Configure Hugging Face Space variables:
   - `NEXT_PUBLIC_API_URL=/api`
   - `NEXT_PUBLIC_API_BASE_URL=/api`
   - `NEXT_PUBLIC_BACKEND_URL=/api`
   - `BACKEND_ORIGIN=http://129.212.185.232:8080`
6. Push to a new Docker Space.
7. Test:
   - `https://SPACE.hf.space/health`
   - `https://SPACE.hf.space/api/demo/incidents`
   - `https://SPACE.hf.space/emergencies`
8. Submit the seeded flood report and verify the admin dashboard shows `analysis_provider=qwen`.

## Known limitations

- Qwen analysis depends on a reachable OpenAI-compatible model server returning valid JSON
- If Qwen is disabled or fails, the system falls back to rule-based analysis
- Audio is accepted but not transcribed unless an ASR model is connected later
- EXIF GPS is only available for images that actually contain GPS metadata
- Text-location resolution is intentionally conservative and does not invent precise coordinates
- Map clicks are user-provided hints, not verified GPS
- Human review remains mandatory for operational use

## Flood demo case

To test the seeded flood case in the admin dashboard or API, use evidence like:

- Evidence text:
  `A severe flash flood is affecting a residential area. Streets are partially flooded, several vehicles are stuck, and people may be trapped inside nearby buildings. Water level appears to be rising quickly after heavy rainfall.`
- Image findings:
  `Flooded urban street, muddy water reaching residential entrances, stuck cars, people moving to higher ground.`
- Audio transcript:
  `No audio uploaded.`
- Location:
  browser geolocation with `confidence=0.90`

Expected result:

- `incident_type=flood`
- `severity=critical`
- `priority=P0`
- `requires_human_review=true`
- provider `qwen` when available, otherwise `rule_based_fallback`