Sentinel

Sentinel

Proactive health companion that merges environmental risk (map + media + air/weather) with wearable context (sleep, stress, recovery) so people can act before they feel sick—not only after.

Team

Name LinkedIn Profile Link
Ngoc Viet Tien https://www.linkedin.com/in/viettienngoc/
Tran Thi Hong Hanh https://www.linkedin.com/in/hanh-thi-hong-tran-0b886212a/

The problem

Health signals live in silos: news, air quality, and your watch rarely answer “Given what’s happening outside, how should I treat my body today?” Products default to reaction (symptoms, sick days). Prevention is vastly cheaper than treatment, but most apps show up only after the crash.

What we built

One narrative, six pillars—each is detailed again in Features & techniques (models and libraries).

# Feature In one sentence
1 Sentinel Map MapLibre view of incidents across France: disease-related signals from French RSS (LLM-extracted), WAQI air quality, OpenWeather temperature grid.
2 Ingest & DB Cron jobs pull sources, normalize into Neon Postgres (incidents, biometrics, summaries, nudges, chat threads).
3 Thryve wearables 🚀 Daily + epoch sync → rolling bio summary + rule-based flags (@acme/validators)—sleep, stress, recovery-style context, not diagnosis.
4 Companion assistant 🚀 Mistral streaming chat with four tools (incidents, weather + live OpenWeather in France, Thryve context, Wrapped aggregates)—answers must follow tool output + Zod schemas.
5 Proactive nudges After ingest, geo + dedupe + LLM structured decision → inbox + optional browser notifications when outside risk and inside readiness overlap.
6 Personalized Wrapped (Alan Play demo) Synthetic employer-wellness dataset → deterministic aggregates → /wrapped story cards + optional Mistral narrative bundle; assistant exposes get_my_wrapped_context so chat and UI stay aligned.

Features & techniques

Feature What users see Models, APIs & techniques
Sentinel Map Heatmap / markers: disease, pollution, pollen, weather-style incidents MapLibre GL; Postgres via Drizzle; bbox filters shared with tools
RSS health ingest New incidents from French media on the map rss-parser + generateText (Mistral) + Output.object + newsIncidentExtractionSchema (Zod); dedupe rawRef
Environment grid Per-city AQI + temperature incidents WAQI + OpenWeather over FRANCE_ENV_GRID; heuristic severity; upsertIncidentByRawRef
Thryve ingest Bio rows + rolling summary in DB Thryve /v5/dailyDynamicValues & /v5/dynamicEpochValues; parsers + buildBioSummary + flags
Companion Streaming chat, tool-backed replies, JSON UI streamText, tool() ×4, stepCountIs(7), validateUIMessages, pipeJsonRender; IP-approx geo; conversation persist
Proactive nudges In-app + optional browser notifications Haversine ~220 km, severity gates, dedupe; generateText + proactiveNudgeDecisionSchema; rate limits
Wrapped /wrapped cards, week/month, share Synthetic JSON, buildWrappedAggregates, generateText + wrappedStoryBundleSchema; Web Share / clipboard; assistant get_my_wrapped_context

System overview

Data → ingest → Neon → models (batch generateText vs interactive streamText + tools) → Map, Assistant, Wrapped, notifications.

flowchart TB
  subgraph sources["Data sources"]
    RSS["French RSS feeds"]
    WAQI["WAQI AQI"]
    OWM["OpenWeather"]
    TH["Thryve Web API"]
  end

  subgraph ingest["Ingest pipelines"]
    IN1["RSS + Mistral structured extraction\n(Zod newsIncidentExtractionSchema)"]
    IN2["France grid → WAQI + OWM\nheuristic severity → incidents"]
    IN3["Thryve daily + epoch\nparse + rule-based bio summary"]
  end

  subgraph store["Persistence"]
    DB[("Neon Postgres\nincidents · biometrics · summaries\nnudges · assistant threads")]
  end

  subgraph intelligence["Model use"]
    M1["Mistral generateText + Output.object\nnews · nudges · wrapped stories"]
    M2["Mistral streamText + tools\nassistant multi-step"]
  end

  subgraph surfaces["Product surfaces"]
    MAP["Sentinel Map\nMapLibre"]
    ASST["Companion\nstreaming + JSON render"]
    WRAP["Wrapped\nstory cards"]
    NUDGE["Inbox + browser notifications"]
  end

  RSS --> IN1
  IN1 --> DB
  WAQI --> IN2
  OWM --> IN2
  IN2 --> DB
  TH --> IN3
  IN3 --> DB

  DB --> MAP
  DB --> M2
  M2 --> ASST
  DB --> M1
  M1 --> NUDGE
  DB --> WRAP
  M1 --> WRAP

Tech stack

Layer Technologies
Monorepo Turborepo, pnpm, create-t3-turbo base
Web Next.js App Router, React
API & types tRPC, Zod
Database PostgreSQL · Neon 🚀 · Drizzle
Auth Better Auth
Map MapLibre GL
AI Mistral 🚀 · Vercel AI SDK (streamText, generateText + Output.object, json-render)
Wearables Thryve Web API 🚀
Hosting Vercel (cron ingest + nudge evaluation)
Mobile (scaffold) Expo

Hackathon tracks

Wrapped is pillar 6 of Sentinel (employer-style recap + same assistant tools). The long standalone block duplicated the feature table; submission detail is summarized here and in Quick demo: Wrapped.

Are you submitting to a special track?

  • [ ] Alan Play: Living Avatars
  • [ ] Alan Play: Mo Studios
  • [x] Alan Play: Personalized Wrapped — synthetic dataset, /wrapped, get_my_wrapped_context (not live Alan production data).
  • [ ] Alan Play: Health App in a Prompt
  • [x] Alan Precision — tool-grounded chat, structured Output.object pipelines, Zod + deterministic aggregates; no invented biometrics.
Track What we highlight
Personalized Wrapped Narrative cards, team/league aggregates, chat Q&A via tool.
Precision RSS / nudge / Wrapped schemas; validation and rule-based bio flags alongside the LLM.

Quick demo: Wrapped

  1. Sign in (seeded personas: pnpm -F @acme/nextjs seed:personas — see apps/nextjs/scripts/seed-persona-users.ts; unmapped users get a stable synthetic member id).
  2. MISTRAL_API_KEY in repo root .env.
  3. Open /wrapped or use the app chrome linking to Wrapped.
  4. Optional: regenerate synthetic data — pnpm -F @acme/nextjs wrapped:gen-dataset

What we'd do next

  • Prevention score / single briefing screen — Map risk + wearable readiness in one glance.
  • Deep links — Incident on map → assistant with context; stronger notification CTAs.
  • B2B view — Anonymized office/regional signals for HR / insurers.
  • Mobile push — Expo + same nudge API.
  • Voice — Speech-to-text into assistant.

Built With

Share this project:

Updates