HSE-AI Insight Platform is a full-stack Oil & Gas safety analytics application that converts free-text HSE incident reports into structured data and actionable insights. The platform uses a local open-source LLM served by Ollama to extract entities, classify events, infer severity, and feed a dark-themed operational dashboard for proactive risk management.
- Frontend: Next.js, React, TypeScript, CSS Modules, Recharts
- Backend API: NestJS, Node.js, TypeScript
- Worker: Node.js, TypeScript
- Data layer: PostgreSQL
- Messaging: RabbitMQ
- Search and analytics: OpenSearch
- AI runtime: Ollama with
qwen2:7b - Containers and local orchestration: Docker, Docker Compose
- CI: GitHub Actions
This project follows Specification-Driven Development (SDD).
Every feature begins with a formal specification, not with code. Specifications are the single source of truth for contracts between services, and they live in the specs/ directory alongside the implementation.
specs/
├── api/
│ └── openapi.yaml ← REST API contract (OpenAPI 3.0)
├── messaging/
│ └── asyncapi.yaml ← RabbitMQ message contract (AsyncAPI 2.6)
└── ai/
└── extraction-schema.json ← AI extraction output schema (JSON Schema draft-07)
1. Write or update the spec → specs/api/openapi.yaml (or messaging, ai)
2. Implement to satisfy it → src/ code, DTOs, services
3. Write spec-driven tests → test/*.spec.ts — assertions reference the spec
4. CI validates everything → build + test jobs on every push
| Contract | File | Validates |
|---|---|---|
| REST API | specs/api/openapi.yaml |
All request/response shapes |
| RabbitMQ messages | specs/messaging/asyncapi.yaml |
Queue payload schema |
| AI extraction output | specs/ai/extraction-schema.json |
LLM/fallback JSON output |
When the backend is running, the live OpenAPI docs are available at:
http://localhost:3001/api/docs
The Swagger UI is generated directly from the NestJS decorators and stays in sync with the specs/api/openapi.yaml contract.
- Update or add the relevant spec file under
specs/. - Implement the feature so it satisfies the spec.
- Add tests in
apps/backend/test/that assert the response shape matches the spec. - Run
npm testfromapps/backendto verify.
This repository includes a GitHub Actions workflow at .github/workflows/ci.yml.
The pipeline:
- installs dependencies for
backend,worker, andfrontend - runs the build step for each app
- runs spec-driven tests for the backend
- validates the
docker-compose.ymlfile syntax
The README uses a static high-resolution preview because GitHub renders PNG images much more sharply than large animated GIFs. This keeps the product walkthrough clear, readable, and professional.
What the walkthrough shows:
- A user pastes a free-text HSE incident report.
- The frontend submits the report to the NestJS API.
- The API queues the job in RabbitMQ for asynchronous processing.
- The worker uses Ollama + Qwen2 to extract entities and classify severity.
- The dashboard refreshes with KPIs, tags, and recent incident records.
A static preview is used here intentionally to avoid blurred frames, oversized captions, and poor readability inside the GitHub README.
HSE data in Oil & Gas environments is often captured as unstructured text. Valuable signals about unsafe conditions, recurring root causes, and injury patterns stay buried in narrative reports. This project turns those narratives into searchable, analyzable, and operationally useful data.
- Submit free-text incident reports from the UI.
- Process reports asynchronously through RabbitMQ.
- Use Ollama + Qwen2 for structured AI extraction.
- Persist original and normalized incident data in PostgreSQL.
- Index reports in OpenSearch for text search and analytics.
- Display KPIs, trends, recent incidents, and extracted tags in a modern dashboard.
- Run the full development stack locally with Docker Compose.
Next.js SPA
|
v
NestJS API ---> RabbitMQ ---> Worker ---> Ollama (Qwen2)
| | |
| v |
+--------------------------> PostgreSQL <----+
|
v
OpenSearch
hse-ai-insight-platform/
├── .github/
│ └── workflows/
│ └── ci.yml
├── apps/
│ ├── backend/
│ │ └── test/ ← spec-driven tests
│ ├── frontend/
│ └── worker/
├── docs/
│ └── demo/
├── infrastructure/
│ └── postgres/
├── specs/
│ ├── api/
│ │ └── openapi.yaml ← REST API contract
│ ├── messaging/
│ │ └── asyncapi.yaml ← RabbitMQ message contract
│ └── ai/
│ └── extraction-schema.json ← AI extraction output schema
├── docker-compose.yml
├── .env.example
├── Makefile
└── README.md
- An operator pastes a free-text HSE report into the left control panel.
- The frontend sends the payload to the NestJS ingestion endpoint.
- The API stores the report with status
QUEUEDand publishes a message to RabbitMQ. - The worker consumes the message and calls Ollama with a structured extraction prompt.
- The worker updates PostgreSQL with classification, severity, tags, summary, and entities.
- The worker indexes the enriched document into OpenSearch.
- The dashboard refreshes and shows updated KPIs, recent incidents, trends, and extracted tags.
cp .env.example .envdocker compose up --build -ddocker compose exec ollama ollama pull qwen2:7b- Frontend:
http://localhost:3000 - Backend API:
http://localhost:3001 - API docs (Swagger UI):
http://localhost:3001/api/docs - RabbitMQ Management:
http://localhost:15672 - OpenSearch:
http://localhost:9200 - OpenSearch Dashboards:
http://localhost:5601 - Ollama API:
http://localhost:11434
Default RabbitMQ credentials:
- Username:
guest - Password:
guest
For local demos, AI_FALLBACK_ENABLED=true is enabled by default. If Ollama is running but the model is not yet available, or if the LLM response is invalid, the worker falls back to a deterministic rules-based extractor so the platform still behaves end-to-end.
To force only LLM-based processing, set:
AI_FALLBACK_ENABLED=falseFull contract: specs/api/openapi.yaml
POST /api/reports
Content-Type: application/json
{
"reportText": "Worker observed gas detector alarm near compressor station C2 after valve inspection. No injury, area isolated, likely loose fitting."
}GET /api/dashboard/summaryGET /api/dashboard/trends?days=14GET /api/reports/recent?limit=10GET /api/reports/:idDefined in specs/ai/extraction-schema.json. The worker asks the model to produce JSON with fields similar to these:
{
"classification": "Near Miss",
"severity": "Medium",
"equipment": "Mud pump skid",
"location": "Well pad A17",
"injuryType": "Minor wrist pain",
"probableRootCause": "Poor housekeeping / slippery surface",
"summary": "Short operational summary",
"tags": ["slip", "maintenance", "housekeeping"]
}cd apps/backend
npm install
npm testTests in apps/backend/test/ are spec-driven: each test file references the spec it validates and asserts that responses conform to the shapes defined in specs/api/openapi.yaml.
The UI is implemented as a dark-mode single-page dashboard with two main areas:
- Left panel: free-text input, analyze button, latest extracted tags.
- Right panel: KPI cards, line chart for incident trend, severity distribution, and recent incident table.
Each app can also run independently:
cd apps/backend && npm install && npm run start:dev
cd apps/worker && npm install && npm run dev
cd apps/frontend && npm install && npm run devYou still need PostgreSQL, RabbitMQ, OpenSearch, and Ollama available locally.
- Add authentication and role-based access control.
- Add report attachments and image support.
- Add vector search for semantically similar incidents.
- Add WebSocket or Server-Sent Events for real-time UI updates.
- Add Prometheus and Grafana for observability.
- Add incident recommendation workflows and corrective action tracking.
- Add contract testing with Pact to validate the RabbitMQ AsyncAPI contract at runtime.
- Add OpenAPI response validation middleware to enforce the spec at the HTTP boundary.
This project uses a stack aligned with the current official documentation for NestJS, Next.js App Router, OpenSearch local Docker deployments, and the Ollama local generation API with qwen2:7b.
MIT
