feat: frontend UI, batch LLM extraction, dynamic PDF labels, API hardening#210
Open
utkarshqz wants to merge 1 commit intofireform-core:mainfrom
Open
feat: frontend UI, batch LLM extraction, dynamic PDF labels, API hardening#210utkarshqz wants to merge 1 commit intofireform-core:mainfrom
utkarshqz wants to merge 1 commit intofireform-core:mainfrom
Conversation
This was referenced Mar 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat: frontend UI, batch LLM extraction, dynamic PDF labels, API hardening
Summary
This PR delivers the complete FireForm pipeline working end-to-end for the first time. It adds the missing browser frontend, fixes the root cause of the LLM hallucination bug, hardens the API, and provides full documentation.
All changes in this PR are tightly interconnected — they cannot be separated without creating a broken intermediate state. The dependency chain is:
Every file changed is a direct dependency of the frontend working correctly. This is why they are submitted together.
Closes / Fixes
Closes #1
Closes #102
Closes #160
Closes #162
Closes #165
Closes #173
Closes #196
Closes #205
Fixes #135
Fixes #145
Fixes #149
Fixes #187
Addresses #206
Type of change
What changed and why
1. 🧠 Root cause fix — LLM hallucination (#173)
This was the most impactful bug in the codebase. Every PDF came out with the same value repeated in every field.
Root cause — traced through 3 layers:
templates.py{"textbox_0_0": ""}— empty values, meaningless to any LLM{"JobTitle": "Job Title"}llm.pytextbox_0_0— Mistral had no idea what the field meant"What is textbox_0_0?"7 times → guessed the same name each timePerformance improvement: 7 Ollama API calls → 1 Ollama API call (O(N) → O(1))
The exact prompt now sent to Mistral:
Real Mistral output (verified locally, 09/03/2026):
{ "NAME/SID": "John Smith; EMP-2024-789", "JobTitle": "Firefighter Paramedic", "Department": "Emergency Medical Services", "Phone Number": "916-555-0147", "email": null, "Date7_af_date": null, "signature": null }4 fields correctly extracted with distinct values. 3 fields correctly null (email, date, signature were not in the input transcript). Zero hallucination.
2. 🖥️ Frontend UI — first working browser interface (#1, #196)
FireForm had no UI. Users had to write curl commands or use Postman to interact with it. This PR adds a complete single-file browser interface at
frontend/index.html.What the UI does:
Additional UI features:
python -m http.server 3000Screenshots (from local testing):
3. 📄 Dynamic PDF field label extraction (#162, #173)
The old template system stored every PDF field as an empty string, throwing away the label information that was sitting right there in the PDF file.
Old
templates.py:New
templates.py:This works with any PDF regardless of how it was created — whether it has proper tooltip labels or just internal names.
4. 🔌 API hardening (#135, #145, #149, #165, #187, #205)
api/main.py— three critical additions:api/routes/forms.py:fieldstype mismatch —template.fieldsis stored asdictbutController.fill_form()expects alist— addedisinstanceguard withlist(fields.keys())conversion for backward compatibilityConnectionError→ returns 503 with "Ollama not running" message (not a cryptic 500)os.path.exists()check before DB insert — prevents saving records pointing to missing filesGET /forms/download/{submission_id}— serves filled PDF as file download ([FEAT]: Add GET /forms/download/{submission_id} endpoint #205)GET /forms/{submission_id}— retrieve submission record by IDapi/routes/templates.py:GET /templateswith limit/offset pagination ([FEAT]: Add GET /templates/{template_id} endpoint #160)GET /templates/{id}([FEAT]: Add GET /templates endpoint (list + pagination) #162)src/inputs/(stable path, survives restarts)/TUand/TPDF annotations for field labelsapi/db/repositories.py:get_all_templates(session, limit, offset)— was missing entirely, causedImportError: cannot import name 'get_all_templates'on every startup ([BUG]: 'Union' not defined in main.py #135, [BUG]: NameError on startup due to missing Union import in main.py #187)get_form(session, submission_id)— needed for download and submission retrievalsrc/llm.py:dictandlistfortarget_fields—isinstancecheck ensures compatibility with both old list-based and new dict-based field formats\``json...```` → clean JSON)src/main.py:from backend import Fill→Controller) which caused silent PDF generation failuresos.fspath()normalization for PDF paths on Windows5. 📚 Documentation (#102)
docs/SETUP.md— complete setup guide for Windows, Linux, and macOS covering:docs/frontend.md— frontend architecture and API integration referencedocs/demo/— real screenshots and filled PDF from local testing on 09/03/2026How Has This Been Tested?
Manual end-to-end test (verified locally, 09/03/2026):
Unit tests (PR #209): 52/52 passing
Environment:
Checklist