sentence-transformers 3.x imports SentenceTransformerTrainer at package level,
which drags in tensorflow, which fails due to a protobuf version conflict in
this environment. Pinned to 2.7.0 (last version without trainer import).
vectorstore.py now uses sentence-transformers all-MiniLM-L6-v2 as primary
(cosine similarity) with BM25 keyword retrieval as automatic fallback if the
model fails to load. Public API unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- core/vectorstore.py: switch to EphemeralClient (in-memory) — eliminates
ChromaDB 0.5.x tenant/SQLite compatibility error entirely; index rebuilds
on each evaluation run so persistence is not needed
- data/tender/real_tenders/: two real CRPF tender PDFs downloaded from
crpf.gov.in (Bhopal water tanks NIT-71 Aug-2025, J&K office repair Jan-2026)
- ui/tab_tender.py: pre-loaded tender selector with tabs (pre-loaded vs upload);
real tenders shown with green badge; active tender name always displayed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix ChromaDB tenant error on stale .chroma directory
When the .chroma directory exists from an older ChromaDB version, PersistentClient
raises ValueError about missing default_tenant. Catch it, wipe the directory,
and recreate — data is regenerated on the next evaluation run.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements specs/09_evaluator.md. Combined confidence formula weighs LLM
confidence against OCR tier quality. Safety rules prevent silent disqualification:
not_eligible at medium confidence (0.55-0.80) is downgraded to needs_review.
Falls back to precomputed on LLMUnavailable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Step 8: vector store and bidder processor — ChromaDB indexing and retrieval
Implements specs/06_vectorstore_and_bidder_processor.md. get_client cached
with st.cache_resource; upsert deduplicates via sha256 IDs. process_bidder
runs OCR pipeline per file, chunks, and indexes with bidder_id metadata.
gather_evidence queries top-k chunks for a criterion's query_hints.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Step 7: OCR pipeline — 3-tier extraction with caching
Implements specs/07_criteria_extractor.md. extract_criteria calls DeepSeek
with the full tender text and parses Criterion objects; falls back to
hardcoded precomputed criteria on LLMUnavailable. audit.py writes to SQLite;
fallback.py loads precomputed JSON or hardcoded defaults. Tab 2 renders
criteria cards with category/mandatory badges and rule details.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Step 5: pdf_utils and chunker — PyMuPDF extraction and text chunking
Implements specs/03_pdf_utils_and_chunker.md. extract_pages returns per-page
dicts; is_text_pdf uses avg-chars heuristic; render_page_to_image produces PIL
images for OCR. chunk_tender splits on clause headings up to 2000 chars;
chunk_bidder emits one chunk per ExtractedPage with full OCR metadata.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Step 4: LLM client — DeepSeek wrapper with retry and LLMUnavailable
Implements specs/02_llm_client.md. chat_json uses response_format=json_object
and retries on 5xx/connection errors with exponential backoff; chat_vision
base64-encodes images for multimodal calls. Raises LLMUnavailable on missing
key, auth failure, or exhausted retries.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Step 1: project skeleton — all stubs, directory structure, and entry point
Creates every file listed in specs/00_skeleton.md: app.py with 5-tab Streamlit
shell, all core/ and ui/ stubs importable without logic, data/scripts/assets
directory tree, requirements.txt, packages.txt, .env.example.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>