Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,22 @@
# Copy this file to .env and paste your real key after the equals sign.
OPENROUTER_API_KEY=
GEMINI_API_KEY=
LLM_PROVIDER=openrouter
OPENROUTER_MODEL=google/gemini-3.5-flash
GEMINI_MODEL=gemini-2.0-flash
PORT=8501
IS_QUESTIONS_NEED_CSV=Yes
BIHAR_WORKERS=2
BIHAR_RENDER_DPI=200
BIHAR_SOURCE_SPREAD_COUNT=6
BIHAR_LOGICAL_PAGE_COUNT=12
BIHAR_REFERENCE_MASTER_PDF=../reference_master.pdf
BIHAR_SPREAD_GUTTER_FRACTION=0.018
BIHAR_MIN_IMAGE_QUALITY=0.35
BIHAR_REVIEW_CONFIDENCE_THRESHOLD=0.72
BIHAR_MAX_RETRIES=3
BIHAR_RETRY_BACKOFF_SECONDS=1.5
BIHAR_REQUEST_TIMEOUT_SECONDS=120
BIHAR_MAX_COMPLETION_TOKENS=20000
BIHAR_OUTPUT_WORKBOOK=questionnaire_results.xlsx
BIHAR_QUESTION_CATALOG=questions.txt
BIHAR_STATE_FILE=processing_state.json
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ __pycache__/
.env
output/
.DS_Store
results/
env/
test_runs/
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,35 @@ master before generating answer crops.
```powershell
python -m venv .venv; .\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
$env:GEMINI_API_KEY = "..." # optional; deterministic local extraction still runs
$env:OPENROUTER_API_KEY = "..."
$env:OPENROUTER_MODEL = "google/gemini-3.5-flash"
$env:GEMINI_API_KEY = "..."
$env:GEMINI_MODEL = "gemini-2.0-flash"
$env:LLM_PROVIDER = "openrouter" # or "gemini"
$env:PORT = "8501"
python main.py process --input C:\scans --output C:\results
or
python3 main.py process --input input_pdfs --output results
streamlit run core/review/streamlit_review.py -- --run-dir C:\results
```

The calibrated answer regions are saved once in `config/pages.json`; they are
not edited for individual student PDFs. The system deliberately supports only
this format. It records intermediate crops and JSON audit records in the output
directory and can resume completed PDFs.

All application configuration is loaded from environment variables. Copy
`.env.example` to `.env` and adjust values there; `.env` is excluded from Git.
Set `IS_QUESTIONS_NEED_CSV=Yes` to add `Q1 — <question text>` headers to the
response report; set it to `No` to use compact `Q1`–`Q34` headers only.
Set `BIHAR_REQUEST_TIMEOUT_SECONDS` to bound each LiteLLM request, including
each attempt made by the retry policy.
Set `LLM_PROVIDER=gemini` with `GEMINI_API_KEY` to use direct Gemini, or leave
it as `openrouter` with `OPENROUTER_API_KEY` to use OpenRouter.
The Gemini LiteLLM route uses LiteLLM's chat-completion interface; LiteLLM
model names are qualified automatically (for example `gemini/<model>` when
using the LiteLLM Gemini route). The OpenRouter route sends the value of
`OPENROUTER_MODEL` directly to OpenRouter — do not prefix this value with
`openrouter/` in your `.env` file, since that exact string will be used.
At startup, the application validates all `BIHAR_*` settings, `PORT`, the
selected model, and the credentials required by the selected provider.
82 changes: 71 additions & 11 deletions config/prompts.yaml
Original file line number Diff line number Diff line change
@@ -1,14 +1,74 @@
system: |
You are the answer-extraction engine for one fixed Hindi questionnaire.
Each image is preceded by its fixed question ID, type, and allowed options.
Never infer question IDs, never return question text, and never invent an answer.
For a single_choice question, return exactly one allowed code only when one checkbox is clearly marked.
If zero or multiple boxes are marked, or the mark is ambiguous, return an empty selected_codes array and set review_required true.
For multiple_choice, return every clearly marked allowed code; uncertainty requires review_required true.
For short_text and long_text, transcribe only legible handwritten Hindi/Devanagari exactly as written.
Do not translate Hindi to English. Preserve names, numbers, and punctuation. If handwriting cannot be read,
return answer_text as an empty string and set review_required true.

The first page contains student information.
The remaining pages contain questionnaire responses.

Extract both the student information and the questionnaire answers.

Never invent values.
Never guess unreadable handwriting.
If a field is blank or unreadable, return an empty string ("").

For questionnaire answers:

- Never infer question IDs.
- Never return question text.
- Never invent an answer.
- For a single_choice question, return exactly one allowed code only when one checkbox is clearly marked.
- If zero or multiple boxes are marked, or the mark is ambiguous, return an empty selected_codes array and set review_required to true.
- For multiple_choice, return every clearly marked allowed code. If uncertain, set review_required to true.
- For short_text and long_text, transcribe only legible handwritten Hindi/Devanagari exactly as written.
- Do not translate Hindi to English.
- Preserve names, numbers and punctuation exactly.
- If handwriting cannot be read, return answer_text as an empty string and set review_required to true.
- Every answer object must always include all six fields shown in the JSON
template below, with exactly those names, even when a field does not
apply to that question. For example, single_choice and multiple_choice
questions have no free text: still return answer_text as "" rather than
omitting the key.

instruction: |
Return a JSON array only. Each item must contain exactly question_id, selected_codes,
answer_text, confidence, review_required, raw_observations. selected_codes is an array.
Use only allowed option codes. confidence must be a number from 0 to 1.
Return ONLY one valid JSON object.

The JSON object must have exactly two top-level keys:

{
"student": {
"student_name": "",
"gender": "",
"school_name": "",
"school_udise": "",
"crc_name": "",
"crc_udise": "",
"block": "",
"district": "",
"grade": "",
"meena_manch_participation": ""
},
"answers": [
{
"question_id": "",
"selected_codes": [],
"answer_text": "",
"confidence": 0.0,
"review_required": false,
"raw_observations": ""
}
]
}

Rules:

- Return ONLY valid JSON.
- Do not include markdown.
- Do not include explanations.
- student contains information extracted only from the first page.
- If any student field is blank or unreadable, return an empty string.
- answers must contain exactly one object for every detected questionnaire answer.
- Every answer object must contain all six keys from the template above
(question_id, selected_codes, answer_text, confidence, review_required,
raw_observations), never fewer. Use "" or [] as the empty value, never omit the key.
- selected_codes must always be an array.
- Use only the allowed option codes.
- confidence must be a number between 0 and 1.
109 changes: 109 additions & 0 deletions config/questions.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
1.
अनीता के विद्यालय का पानी का नल अक्सर टूट जाता है। समस्या हल करने का पहला कदम क्या होगा?

2.
किसी समस्या के दो समाधान चुने गए हैं। उन्हें चुनने से पहले क्या करना चाहिए?

3.
यदि दो लोगों की जानकारी अलग है, तो क्या करना चाहिए?

4.
यदि कोई योजना काम नहीं करती उसे बेहतर तरीके से सोचने का क्या तरीका है?

5.
यदि किसी नई बीमारी की अफवाह फैलती है तो हमें सबसे पहले क्या करना चाहिए?

6.
आपके गाँव में लड़कियों को किन समस्याओं का सामना करना पड़ता है, और ऐसा क्यों होता है?
खुला प्रश्न — लिखित उत्तर के लिए स्थान दिया गया है।

7.
अच्छा संचार का मतलब है:

8.
यदि कोई आपको नहीं समझता, तो आपको क्या करना चाहिए?

9.
समूह में विचार साझा करते समय सबसे मददगार क्या है?

10.
मीना मंच बैठक में लड़कियाँ विद्यालय की सफाई पर चर्चा कर रही हैं, लेकिन कुछ लड़कियाँ बोलने में संकोच करती हैं। चर्चा को प्रोत्साहित करने का सबसे अच्छा तरीका क्या है?

11.
हम यह कैसे सुनिश्चित कर सकते हैं कि हमारी स्कूल में लड़कियाँ विद्यालय में सुरक्षित रहें और अच्छी तरह से पढ़ाई कर सकें?
खुला प्रश्न — लिखित उत्तर के लिए स्थान दिया गया है।

12.
आपका मीना समूह चाहता है कि लड़कियाँ नियमित रूप से विद्यालय जाएँ। इसके लिए सबसे अच्छा तरीका क्या होगा?

13.
यदि समूह (group) के दो सदस्यों के बीच असहमति हो, तो सबसे अच्छा क्या है?

14.
समूह में छात्र छात्राएँ अलग-अलग भाषाएँ बोलते हैं। टीमवर्क में सबसे अधिक मददगार क्या है?

15.
समूह कार्य में आपकी भूमिका स्पष्ट नहीं है। इसके लिए आपको क्या करना चाहिए?

16.
आप और आपके दोस्त विद्यालय की किसी समस्या को हल करने में विद्यालय की कैसे मदद कर सकते हैं?
खुला प्रश्न — लिखित उत्तर के लिए स्थान दिया गया है।

17.
रचनात्मकता का मतलब है:

18.
आपका मीना समूह चाहता है कि लड़कियाँ नियमित रूप से विद्यालय जाएँ। इसके लिए कौन-सा विचार सबसे अधिक रचनात्मक है?

19.
गाँव की लड़कियाँ विद्यालय जाते समय असुरक्षित महसूस करती हैं। सबसे रचनात्मक समाधान क्या होगा?

20.
एक नया विचार लिखें जिससे आप अपने विद्यालय या गाँव को बेहतर बना सकते हैं।
खुला प्रश्न — लिखित उत्तर के लिए स्थान दिया गया है।

21.
अच्छा नेता कौन होता है?

22.
समूह का नेतृत्व करते समय क्या महत्वपूर्ण है?

23.
यदि आपकी टीम भ्रमित है, तो नेता को क्या करना चाहिए?

24.
मीना मंच बैठक में केवल दो लड़कियाँ बोल रही हैं और बाकी चुप हैं। एक अच्छा नेता क्या करेगा?

25.
एक ऐसी स्थिति का वर्णन करें जिसमें आपने जिम्मेदारी ली हो या दूसरों का नेतृत्व करने में मदद की हो।
खुला प्रश्न — लिखित उत्तर के लिए स्थान दिया गया है।

26.
कुछ छात्र एक लड़की के उच्चारण पर हँसते हैं। आपको क्या करना चाहिए?

27.
यदि आप सम्मानपूर्वक असहमत होते हैं, तो आप क्या दिखा रहे हैं?

28.
चुनौती का सामना करते समय सशक्त लड़कियों को क्या करना चाहिए?

29.
आपके पड़ोस की एक लड़की विद्यालय आना बंद कर देती है। सबसे पहले आप क्या कर सकते हैं?

30.
आपके सपने क्या हैं? बड़े होकर आप क्या बनना चाहते हैं?
खुला प्रश्न — लिखित उत्तर के लिए स्थान दिया गया है।

31.
पिछले सप्ताह में आप कितने दिन स्कूल गए?

32.
पिछले एक महीने में आप कितनी बार स्कूल से अनुपस्थित रहे हैं?

33.
स्कूल न जाने का आपका मुख्य कारण क्या है? (एक या अधिक विकल्प चुनें)

34.
अगर आप स्कूल नहीं जाते हैं, तो आप आमतौर पर क्या करते हैं?


13 changes: 0 additions & 13 deletions config/settings.yaml

This file was deleted.

24 changes: 20 additions & 4 deletions core/ai/confidence.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
"""Fuse independent quality indicators into a reviewable confidence score."""
# """Fuse independent quality indicators into a reviewable confidence score."""
# class ConfidenceEngine:
# def calculate(self, llm: float, checkbox: float, image: float, handwriting: float, rules_ok: bool) -> float:
# visual=max(checkbox, handwriting)
# return round(max(0.,min(1., .45*llm+.25*visual+.20*image+.10*(1. if rules_ok else 0.))),3)

class ConfidenceEngine:
def calculate(self, gemini: float, checkbox: float, image: float, handwriting: float, rules_ok: bool) -> float:
visual=max(checkbox, handwriting)
return round(max(0.,min(1., .45*gemini+.25*visual+.20*image+.10*(1. if rules_ok else 0.))),3)
def calculate(
self,
llm: float,
image: float,
rules_ok: bool,
) -> float:

score = (
0.70 * llm +
0.20 * image +
0.10 * (1.0 if rules_ok else 0.0)
)

return round(max(0.0, min(1.0, score)), 3)
56 changes: 0 additions & 56 deletions core/ai/gemini_provider.py

This file was deleted.

Loading