Upload a PDF, Word doc, Excel sheet, or plain text, and a 3-pass LLM pipeline reads it the way a process analyst would — finding triggers, tasks, decisions, actors, and outcomes — then emits a schema-validated, auto-repaired BPMN model that renders as an interactive swimlane diagram you can export to JSON or BPMN XML.
- 🧠 Three-pass extraction, not one prompt. Instead of begging a model for BPMN in a single shot, the pipeline runs Core Extraction → Structural Enhancement → Validation & Auto-Repair — and each pass is fed the raw document text plus the previous pass's JSON, so the model refines its own work rather than starting cold.
- 🛡️ Dual-schema validation + auto-repair. Every result is checked against both a hand-written JSON Schema (draft-07) and a Pydantic v2 model with custom validators (start/end events required, unique node IDs, flows must reference real nodes). A
repair_bpmn_json()step heals the most common LLM mistakes — missing start/end events, absent lanes, dropped arrays — before validation, so a slightly-off generation still becomes a valid diagram. - 🎨 A from-scratch SVG renderer. No diagramming library on the canvas —
WorkflowVisualization.tsxbuilds the whole BPMN diagram with rawcreateElementNScalls: color-coded swimlanes, circles for events, diamonds for gateways, rounded rectangles for tasks, an arrowhead marker, a Gaussian-blur glow filter, and click-to-highlight flow animation. - 📄 Ten document formats, one interface. PDF, Word, Excel, HTML, XML, JSON, plain text, and Markdown all flow through a single
UniversalDocumentProcessorthat normalizes text, tables, structure, and metadata. - 🔎 Evidence-traced output. Nodes can carry
evidence(page number + source quote) and businessrules, so every element of the model points back to where it came from in the document.
flowchart LR
A[📄 Upload document<br/>PDF · Word · Excel · HTML<br/>XML · JSON · TXT · MD] --> B[UniversalDocumentProcessor<br/>text · tables · structure · metadata]
B --> C[Complexity analysis<br/>+ workflow indicators]
C --> P1[Pass 1 · Core Extraction<br/>triggers · tasks · decisions · actors]
P1 -->|doc text + Pass 1 JSON| P2[Pass 2 · Structural Enhancement<br/>nodes · lanes · subprocesses · flows]
P2 -->|doc text + Pass 2 JSON| P3[Pass 3 · Validation & Auto-Repair<br/>lifecycle · evidence · rules]
P3 --> R[repair_bpmn_json<br/>heal common LLM gaps]
R --> V[validate_bpmn_json<br/>JSON Schema + Pydantic v2]
V --> M[(BPMN process model)]
M --> SVG[🎨 Interactive SVG diagram]
M --> EX[⬇️ Export · JSON · BPMN XML · Markdown]
style P1 fill:#6366f1,color:#fff
style P2 fill:#6366f1,color:#fff
style P3 fill:#6366f1,color:#fff
style V fill:#16a34a,color:#fff
style SVG fill:#0ea5e9,color:#fff
| Pass | Goal | What the model receives | What it produces |
|---|---|---|---|
| 1 · Core Extraction | Find candidate workflows | Document text (first 8k chars), tables, and pre-computed indicators (triggers / tasks / decisions / actors / outcomes) | Candidate workflows, actors, confidence |
| 2 · Structural Enhancement | Become real BPMN | Pass 1 JSON + the target schema | nodes, lanes, flows, subProcesses |
| 3 · Validation & Auto-Repair | Make it complete & valid | Pass 2 JSON | Connected lifecycle + evidence + rules |
Each pass has its own system prompt (a workflow-extraction expert, a BPMN-modeling expert, a BPMN-validation expert), and every pass degrades gracefully: if a call fails, a structured fallback keeps the pipeline producing a valid result instead of crashing.
repair_bpmn_json() runs first and is forgiving — it injects a default lane, prepends a startEvent, appends an endEvent, and backfills missing subProcesses / rules / evidence arrays. Then validate_bpmn_json() is strict — it runs the JSON Schema check and the Pydantic model, whose validators enforce real business rules:
# every process must have a start and an end, unique node ids,
# and every flow must connect nodes that actually exist
@validator('flows')
def validate_flows(cls, v, values):
node_ids = {n.id for n in values['nodes']}
for flow in v:
if flow.from_ not in node_ids:
raise ValueError(f'Flow source "{flow.from_}" not found in nodes')
if flow.to not in node_ids:
raise ValueError(f'Flow target "{flow.to}" not found in nodes')
return v- 3-pass LLM extraction with prior-pass context injection and per-pass fallbacks
- Dual-layer validation — JSON Schema (draft-07) + Pydantic v2 with custom validators
- Auto-repair of common LLM gaps before validation
- 10 input formats via a single universal document processor
- Hand-built interactive SVG swimlane diagram (no canvas library) with click-to-highlight
- Sync & async extraction — small files return immediately, large files run as background jobs with status polling
- Three export formats — JSON, BPMN 2.0 XML, and a human-readable Markdown report
- Evidence & rules linking each node back to the source document
- Polished React + Tailwind UI with live job progress and per-process drill-down
| Area | Tools |
|---|---|
| Backend | Python · FastAPI · Uvicorn · async background tasks |
| LLM | Ollama /api/generate (streaming) · gpt-oss:120b · forced JSON output |
| Parsing | PyPDF2 · python-docx · pandas / openpyxl · BeautifulSoup · lxml |
| Validation | Pydantic v2 · jsonschema (draft-07) |
| Frontend | React 18 · TypeScript · Create React App |
| UI | Tailwind CSS · Headless UI · Heroicons · react-hot-toast · axios |
| Visualization | Hand-written SVG via createElementNS |
- Python 3.8+ and Node.js 16+
- Access to an Ollama endpoint that serves
gpt-oss:120b— either Ollama's hosted API (with an API key) or a localollama serve(no key needed)
# from the repo root
pip install -r requirements.txt
# configure environment
cp env.example .env # then edit .env with your Ollama settings
# run the API (http://localhost:8000)
python run_backend.py
# …or directly:
uvicorn backend.app.main:app --reload --port 8000cd frontend
npm install
npm start # opens http://localhost:3000The dev server proxies API calls to http://localhost:8000. Open the app, drop in a document (try the ones in examples/), and watch the workflow render.
All backend config lives in .env (see env.example):
| Variable | Description | Default |
|---|---|---|
OLLAMA_BASE_URL |
Ollama API endpoint | https://ollama.com |
OLLAMA_MODEL |
Model to call | gpt-oss:120b |
OLLAMA_API_KEY |
Bearer token (required for the hosted API; leave blank for local Ollama) | – |
OLLAMA_FORMAT_JSON |
Force JSON-mode responses | true |
OLLAMA_NUM_CTX |
Context window | 32768 |
OLLAMA_TEMPERATURE |
Sampling temperature | 0.2 |
OLLAMA_TOP_P |
Nucleus sampling | 0.9 |
OLLAMA_REPEAT_PENALTY |
Repetition penalty | 1.1 |
EXTRACTION_TIMEOUT_SEC |
Per-request timeout | 150 |
Heads-up: never commit a real
OLLAMA_API_KEY..envis git-ignored; onlyenv.example(with placeholders) is tracked.
| Method | Endpoint | Purpose |
|---|---|---|
POST |
/extract |
Upload a document for async processing (returns a job_id) |
POST |
/extract-sync |
Upload a small file (< 5 MB) and get the result immediately |
GET |
/status/{job_id} |
Poll job progress |
GET |
/result/{job_id} |
Fetch the finished BPMN model |
GET |
/export/{job_id}/json |
Download the result as JSON |
GET |
/export/{job_id}/bpmn |
Download as BPMN 2.0 XML |
GET |
/jobs · DELETE /jobs/{job_id} |
List / clean up jobs |
GET |
/ |
Health check |
backend/
app/
main.py # FastAPI app: upload, jobs, exports, BPMN-XML serializer
extraction_pipeline.py # the 3-pass pipeline + per-pass prompts & fallbacks
document_processor.py # universal parser for 10 formats + indicators/complexity
ollama_client.py # streaming Ollama client (JSON mode)
schema.py # Pydantic v2 models, JSON Schema, validate + auto-repair
frontend/
src/
components/
WorkflowVisualization.tsx # from-scratch SVG BPMN renderer
DocumentUpload.tsx # drag-and-drop upload
JobStatus.tsx # live progress polling
ProcessDetails.tsx # nodes / lanes / rules / evidence drill-down
ExportPanel.tsx # JSON · BPMN XML · Markdown export
services/api.ts # typed axios client (smart sync/async upload)
types/bpmn.ts # shared TypeScript model types
examples/ # sample SRS + loan-approval policy documents
run_backend.py # convenience server launcher
requirements.txt
Two ready-to-try documents live in examples/:
sample_srs.md— a software requirements specloan_approval_policy.md— a business-process policy
curl -X POST "http://localhost:8000/extract-sync" -F "file=@examples/loan_approval_policy.md"MIT © 2026 Mohammed Abumtary