Skip to content

Repository files navigation

OntoReview

Litigation prevention OS for US Food & CPG brands. Catch litigation risk hidden in customer reviews and FDA data before it reaches a courtroom.

Live: https://onto-review.vercel.app Status: MVP (active development)


The Problem

A single customer complaint about an undeclared allergen, a piece of foreign material in food, or a misleading nutrition label can be the seed of a multi-million-dollar product liability or class-action suit. Most US Food & CPG SMBs find out only after a legal notice lands — by which point the FDA recall is already public and discovery is underway.

OntoReview catches that signal in the customer-review and FDA-recall feed before it becomes a lawsuit.


How It Works

Customer reviews (CSV upload or sample set)
        ↓
  Risk classification
        ↓
  Matching to real FDA recall precedent (openFDA, live)
        ↓
  Rule-based exposure estimate (from openFDA fields)
        ↓
  Mitigation playbook
        ↓
  Append-only audit trail

Implemented with an LLM risk classifier and OWL ontology reasoning over an openFDA-backed precedent layer.

Every analyzed review surfaces:

Field Example
Risk Category Product Liability
Severity Score 8 / 10
Confidence 0.82
Matched FDA Precedent openFDA F-2182-2017 (BEYOND MEAT undeclared peanut allergen, Class I)
Estimated Legal Exposure Range $9.5M – $17.6M (rule-based, with breakdown)

Exposure estimate — how the number is built

Exposure is a transparent rule-based estimate derived from real openFDA fields (recall classification, distribution scope, recall reason), shown as a range with a visible breakdown — not a single precise figure. A settlement-data-backed model is on the roadmap.

The estimator multiplies a documented per-class base by openFDA-derived factors:

  • Classification base: Class I $5M · Class II $1.5M · Class III $400K
  • Distribution scope: 1–2 states 1.0× · 3–10 states 1.5× · 11–25 states 2.0× · Nationwide 3.0×
  • Reason category: Allergen / undeclared 1.5× · Pathogen contamination 1.4× · Foreign material 1.3× · Mislabeling 1.0×
  • Range: midpoint ± 30%

Every UI surface that shows a dollar number also shows which openFDA field produced which multiplier. Missing inputs → the estimate is hidden behind a "Data insufficient" guard rather than a fabricated number.

Implementation: backend/services/exposure_estimator.py


LLM Reasoning Engine

OpenAI gpt-4o-mini serves as the primary reasoning engine with Google's gemini-2.0-flash as automatic fallback. The LLM powers the analytical components:

  • Risk Classification — analyzes review text, assigns legal risk categories with severity scores (1–10) and confidence levels
  • Playbook Generation — context-aware mitigation strategies per risk category and severity
  • Compliance Analysis — evaluates risks against multi-jurisdiction regulations (US FDA, EU Consumer Protection, KR Fair Trade)
  • Agent Response Simulation — configurable AI agents with different autonomy levels for automated risk response

Note: Exposure dollar numbers come from the rule-based estimator above, not from the LLM. The LLM is used for classification and narrative — not for picking the settlement figure.

LLM Provider Architecture

The system uses a provider abstraction layer. Switch primary/fallback order via a single environment variable:

LLM_PROVIDER=openai    # OpenAI GPT-4o-mini → Gemini fallback (production default)
LLM_PROVIDER=google    # Gemini 2.0 Flash → OpenAI fallback (local dev)

OpenAI failures trigger automatic Gemini fallback. In Gemini-primary mode, 429 rate-limit errors trigger backoff retries before failing over to OpenAI.

Implementation: core/utils/openai_client.py


Data sources

  • openFDA Food Enforcement API (real, public, no key required) — live recall pull via POST /api/fda/recalls/ingest; curated samples served via GET /api/fda/recalls/featured.
  • User-uploaded CSVsPOST /api/data/upload accepts Ratings/Reviews columns; POST /api/analysis/run analyzes whatever the user uploaded.
  • Synthetic sample review setsGET /api/data/sample?industry=ecommerce returns 25 clearly-labeled Food/CPG synthetic reviews for demos/onboarding.
  • No arbitrary scraping — no Amazon/Reddit/etc. scrapers in production code. ToS and legal risk avoidance.

Color contract

The UI consistently uses two tones to signal data origin:

  • emerald = real / live data (openFDA pulls, user-uploaded CSVs, "Live · openFDA" badge)
  • amber = sample data or rule-based estimate ("Sample review set" badge, "Estimated range" exposure pill)

Mixing the two would let a GC mistake an estimate for a settlement. Don't.


Features

1. Risk Intelligence Dashboard — total legal exposure, critical risk count, severity distribution, openFDA-backed "Highest active litigation risk" card with breakdown popover.

2. Risk Response Playbook — AI-generated mitigation strategies tailored to each detected risk. Includes priority actions, timeline, and escalation paths.

3. Trust & Safety Audit — append-only audit log recording every scan, classification, FDA pull, and risk flag. Exportable as PDF for legal compliance (Duty of Care).

4. Domain Ontology Studio — custom OWL ontology rule editor for industry-specific risk classification.

5. Global Compliance Tracker — multi-jurisdiction regulation checking across US, EU, and KR.

6. Agent Communication Setup — configurable AI agent autonomy levels (1–5) for automated risk response, including simulation mode.

7. Per-tenant Authentication — WorkOS AuthKit with server-side code exchange. Tenant isolation is fail-closed: when AUTH_ENFORCED=true, every tenant-scoped endpoint requires a verified Bearer JWT; only an explicit allowlist (/api/health, /api/ready, /api/fda/recalls/{search,featured}) stays public.


Tech Stack

Layer Technology
LLM OpenAI GPT-4o-mini (primary) · Gemini 2.0 Flash (fallback)
Ontology OWL 2 (owlready2)
Backend Python, FastAPI
Frontend React, Tailwind CSS
Database PostgreSQL (prod) · SQLite (dev/CI only), SQLAlchemy, Alembic
Authentication WorkOS AuthKit (server-side code exchange), JWKS RS256 verification with client_id + iss pins
External data openFDA Food Enforcement API (public)
Legal precedent layer In-memory embedding cache + cosine similarity + keyword/TF fallback (no IDF weighting yet)
Deployment Vercel (frontend), Docker Compose home-server stack behind Cloudflare Tunnel (backend)

Project Structure

OntoReview/
├── backend/
│   ├── main.py                          # FastAPI application + lifespan
│   ├── routers/                         # API route handlers
│   │   ├── auth.py                      # WorkOS code-exchange endpoints
│   │   ├── risk.py                      # Risk analysis endpoints
│   │   ├── analysis.py                  # CSV-driven analysis
│   │   ├── data.py                      # Upload + sample review sets
│   │   ├── fda.py                       # openFDA recall ingest + featured
│   │   ├── agent.py                     # Agent communication setup
│   │   ├── compliance.py                # Global compliance tracker
│   │   ├── studio.py                    # Domain ontology studio
│   │   ├── audit.py                     # Trust & safety audit
│   │   └── ...
│   ├── dependencies/
│   │   ├── auth.py                      # JWKS-cached WorkOS JWT verifier
│   │   └── tenancy.py                   # `current_org_id` resolver (JWT → JIT)
│   ├── middleware/
│   │   ├── auth_enforcement.py          # Fail-closed gate; explicit public allowlist
│   │   ├── correlation_id.py            # X-Request-ID propagation
│   │   └── rate_limit.py                # Per-prefix daily caps (auth + LLM buckets)
│   ├── services/                        # Business logic
│   │   ├── auth_provisioning.py         # JIT user/org/membership provisioning
│   │   ├── risk_service.py              # Core risk analysis (LLM-powered)
│   │   ├── exposure_estimator.py        # Rule-based openFDA → $ range
│   │   ├── fda_service.py               # openFDA ingest + normalization
│   │   ├── ontology_engine.py           # OWL ontology reasoning
│   │   ├── legal_rag_service.py         # Legal precedent matching
│   │   ├── playbook_service.py          # Risk response playbook generation
│   │   └── ...
│   ├── data/
│   │   ├── legal_cases.json             # 20 curated US legal precedents
│   │   ├── openfda_featured_samples.json # Real openFDA records (verbatim snapshot)
│   │   └── mock_reviews_*.json          # Synthetic sample review sets
│   ├── database/
│   │   ├── database.py                  # Engine + SessionLocal (PG/SQLite)
│   │   └── models.py                    # SQLAlchemy models (audit trail)
│   └── alembic/
│       └── versions/                    # 0001 multitenant · 0002 schema hardening · 0003 WorkOS mapping
├── core/
│   ├── config.py                        # Environment & LLM configuration
│   └── utils/
│       └── openai_client.py             # Multi-provider LLM abstraction
├── frontend/
│   └── src/
│       ├── auth/
│       │   ├── session.js               # sessionStorage-backed token state
│       │   └── tokenStore.js            # axios interceptor bridge + refresh
│       ├── components/                  # React UI components
│       │   ├── RiskIntelligence.jsx
│       │   ├── FdaHighestRiskCard.jsx   # openFDA-driven hero card
│       │   ├── RealReviewAnalysisCard.jsx # Upload + sample entry
│       │   ├── RiskPlaybook.jsx
│       │   ├── AuditTimeline.jsx
│       │   └── ...
│       └── api/client.js                # API client (Bearer + 401-retry)
├── tests/                               # Unit + integration tests
├── CLAUDE.md                            # Development guide
└── requirements.txt

Getting Started

Prerequisites

  • Python 3.11+
  • Node.js 20+
  • OpenAI API key (primary LLM)
  • Google AI Studio API key (Gemini fallback, optional but recommended)

Setup

# Clone
git clone https://github.com/heeoneie/OntoReview.git
cd OntoReview

# Backend
python -m venv venv
source venv/bin/activate        # Windows: venv\Scripts\activate
pip install -r requirements.txt

# Frontend
cd frontend
npm install
cd ..

Environment Variables

Create a .env file in the project root:

# Primary LLM (Required)
OPENAI_API_KEY=your_openai_key
LLM_PROVIDER=openai

# Fallback LLM (Optional but recommended for production)
GOOGLE_API_KEY=your_google_key

# openFDA API key (optional — public API works without one;
# the key only raises the per-IP rate limit)
OPENFDA_API_KEY=your_openfda_key

# ── WorkOS AuthKit (required for AUTH_ENFORCED=true) ──
# Dev default: AUTH_ENFORCED=false. Production MUST flip it on
# (see Deployment below). Frontend Vite reads VITE_* from this
# same root .env via `envDir: '..'` in `frontend/vite.config.js`.
AUTH_ENFORCED=false
WORKOS_API_KEY=sk_test_...           # WorkOS dashboard → API keys
WORKOS_CLIENT_ID=client_...          # WorkOS dashboard → AuthKit
WORKOS_JWKS_URL=                     # Optional override; defaults to
                                     # https://api.workos.com/sso/jwks/<CLIENT_ID>
WORKOS_ORG_CLAIM_KEY=org_id          # Confirmed via real-token inspection

VITE_WORKOS_CLIENT_ID=client_...     # Mirror of WORKOS_CLIENT_ID
VITE_WORKOS_REDIRECT_URI=http://localhost:5173/callback

Run

# Backend (terminal 1)
uvicorn backend.main:app --reload

# Frontend (terminal 2)
cd frontend
npm run dev

Open http://localhost:5173

Production deploy notes

Authentication is enforced by AUTH_ENFORCED. The local default is false so dev / CI / live-demo paths work without a sign-in. Any public-internet deploy MUST set AUTH_ENFORCED=true in the service env vars BEFORE the cutover:

Env var Required for prod Source
AUTH_ENFORCED yes — true production .env consumed by compose.yaml (verify)
WORKOS_API_KEY yes WorkOS dashboard → API keys (sk_live_…)
WORKOS_CLIENT_ID yes WorkOS dashboard → AuthKit (client_…)
WORKOS_JWKS_URL optional Defaults to https://api.workos.com/sso/jwks/<CLIENT_ID>
RUN_MIGRATIONS_AT_STARTUP yes — false Run alembic upgrade head separately

Frontend Vite build needs VITE_WORKOS_CLIENT_ID and VITE_WORKOS_REDIRECT_URI baked in at build time. Without these the /auth/login page renders the guest fallback and no Bearer is attached to API calls — combined with AUTH_ENFORCED=true that's an effective lock-out, so verify both pairs are in place before promoting a build to prod.

Security & UX TODO — before first enterprise customer

The current sign-in flow is correct enough for MVP but must be hardened before any enterprise B2B customer who carries legal data. Each item below is tracked at the source with a TODO comment pointing back here.

Token storage (XSS hardening)frontend/src/auth/session.js:

  • Move access_token to an httpOnly, Secure, SameSite=Strict cookie set by POST /api/auth/callback. XSS can no longer read it.
  • Move refresh_token to the same cookie (or a sibling cookie with a longer Max-Age). The frontend never holds either token in JS state.
  • Add CSRF protection: backend reads X-CSRF-Token header sourced from a non-httpOnly companion cookie (double-submit). Required because cookies fire automatically on any same-site request.
  • Drop frontend/src/auth/session.js + tokenStore.js once tokens are cookie-only. The axios interceptor needs withCredentials: true.
  • Coordinate with backend/routers/auth.py/logout must clear the cookies AND call user_management.revoke_session so the WorkOS session itself dies.

Org provisioning UXbackend/services/auth_provisioning.py

  • frontend/src/App.jsx::NeedsOrganizationPage:
  • Sync Organization.name from WorkOS — currently new orgs land as "Organization org_01XYZ" because the JIT path doesn't call workos.organizations.get_organization(). An enterprise customer would see this string in the dashboard.
  • Replace NeedsOrganizationPage with a self-serve "Create your first organization" flow that calls user_management.create_organization and adds the current user as the first member. Today the page is a static "contact your admin" guidance screen.
  • Decide on an invite-link flow vs admin-only org creation for new sign-ups. Probably invite-link based on the GC target persona.

Tracked at the source: each module has a top-of-file or inline TODO block linking back to this section.


End-to-end Flow

  1. Source review data — upload a CSV (POST /api/data/upload) or load the synthetic Food/CPG sample (GET /api/data/sample?industry=ecommerce).
  2. Optionally pull live FDA recallsPOST /api/fda/recalls/ingest matching a brand (e.g. Beyond Meat) for live precedent grounding.
  3. AnalyzePOST /api/analysis/run classifies legal risks with severity scores via GPT-4o-mini.
  4. Match — risk flags are paired with the closest real openFDA recall (GET /api/fda/recalls/featured).
  5. Estimate — the rule-based estimator produces a dollar RANGE per matched precedent, with rationale.
  6. Dashboard — Risk Intelligence shows total legal exposure, top issues, and the openFDA precedent link.
  7. Playbook — LLM generates risk mitigation strategies per finding.
  8. Audit — append-only audit trail records every classification, FDA pull, and mitigation action for compliance.

Risk Categories

  • Product Liability
  • Regulatory Risk
  • False Advertising
  • Consumer Safety
  • Class Action Risk

Risk Score Formula

RiskScore = Σ(severity_i × confidence_i)

severity_i is the LLM-generated risk severity (1–10), confidence_i is classification confidence (0–1). The total legal exposure shown on the dashboard is the sum of the rule-based range midpoints described in the Exposure estimate section above.


API Endpoints

Method Endpoint Description
GET /api/auth/authorize-url Build the WorkOS hosted login URL for the given redirect_uri
POST /api/auth/callback Exchange a WorkOS authorization code for {access_token, refresh_token, user, organization_id, needs_organization}
POST /api/auth/refresh Rotate the access token using a refresh token
POST /api/auth/logout Frontend-driven logout (no-op today; see Security TODO)
POST /api/data/upload Upload a CSV of customer reviews (Ratings/Reviews columns)
GET /api/data/sample Load a curated sample review set (?industry=ecommerce)
POST /api/analysis/run Run the analysis pipeline on the most-recent uploaded/sample CSV
POST /api/fda/recalls/ingest Pull live openFDA recalls for a brand into the analysis store
GET /api/fda/recalls/featured Curated openFDA recalls + per-record exposure estimate
POST /api/risk/demo Run risk analysis on built-in demo reviews
POST /api/risk/playbook/generate Generate a risk mitigation playbook
GET /api/risk/ontology/graph Get the OWL ontology knowledge graph
POST /api/compliance/check Multi-jurisdiction compliance check
GET /api/compliance/regulations List available regulations
POST /api/agent/simulate Simulate AI agent response
GET /api/audit/events Retrieve audit trail (filterable by action)
POST /api/audit/log Append a user-action audit row
POST /api/discovery/search Web discovery engine search
GET /api/kpi/summary Dashboard KPI summary
GET /api/health Health check

Deployment

Frontend — Vercel Backend — Docker Compose stack (compose.yaml: api + Celery worker/beat + Postgres + Redis) on a home server, exposed through a Cloudflare Tunnel.

Secrets and deploy flags (AUTH_ENFORCED=true, WorkOS keys, LLM keys, ALLOWED_ORIGINS) live in the server's .env, which compose.yaml loads via env_file. DATABASE_URL (Postgres) and REDIS_URL are injected by compose.yaml itself. Recovery steps after a host reboot are in docs/reboot-checklist.md; backup/restore in docs/ops/backup.md.


License

MIT License

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages