DEV Community

Cover image for Tender Radar: finding government tenders for small contractors, entirely on their own laptop
Pranesh Nikhar
Pranesh Nikhar

Posted on

Tender Radar: finding government tenders for small contractors, entirely on their own laptop

Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle Submission 💸

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle

What I Built

Tender Radar is a local-first government tender radar for small contractors.

A small electrical contractor bids on public tenders. Notices land on GeM, state portals and department boards, and the office has to answer three questions fast: which notices matter, is the firm actually eligible (turnover, similar work, licences), and how do we assemble the compliance paperwork without missing a clause. Miss one certificate and the bid is rejected on technical grounds; the preparation cost is gone. Many MSME contractors pay commission agents for this filtering, or simply miss tenders they would have won.

Tender Radar answers all three on the contractor's own machine:

  • Extracts the eligibility clauses from each notice (turnover, EMD, experience, licences, certifications)
  • Matches every clause against the firm's own documents (past bids, completion certificates, licence and ISO scans, audited financials), with a quote behind every claim
  • Flags risky terms in plain language (an EMD at 9% of value, a deadline three days out, a job ten times anything the firm has built)
  • Estimates win probability from the firm's historical bids with TabPFN
  • Drafts the covering letter, compliance statement and capability summary behind a citation guard that deletes any sentence its own documents cannot support

Who would pay: MSME contractors who pay agents per bid, or lose tenders they never saw. One won tender a year covers a year of tooling.

Demo

The board, ranked by fit and win probability:

Tender Radar board

One tender, with the evidence quote and document citation behind every requirement:

Tender detail with evidence

The print-ready bid pack it produces:

Bid pack

Everything above was computed locally, on purpose: the whole point is that it runs on the machine that holds the sensitive documents. Five commands take you from clone to board (full list in the README): install, generate the demo corpus, load it, scan 8 tenders, open the dashboard. The corpus is a synthetic but realistic electrical MSME (Jaipur, Class-A licence, about INR 3.7 crore average turnover), clearly labelled; live portal ingestion is scaffolded behind a key.

Code

github.com/praneshnikhar/tender-radar, built during the challenge window. scripts/eval_summary.py reproduces every number below.

How I Built It

Extraction, verdicts and drafting all run on Gemma 4 (gemma4:latest, 4.6B Q4) via Ollama, fully local. Amounts are parsed deterministically in code, because money must never depend on a model's mood. Matching is hybrid retrieval (a built-in TF-IDF embedder plus BM25) followed by one Gemma verdict pass per tender. Win probability is TabPFN v3.5 with thinking mode.

The citation guard is the part I am proud of, and it exists because a 4B model is not a frontier model. I caught it marking a clause "met" while its own rationale argued the firm fell short. So every "met" carries a quote, verified before display: exact match, or a fuzzy match where numbers are compared numerically ("INR 84.5 lakh" passes against "INR 84.50 lakh"; "two crore" fails against "2 crore"). A deterministic amount cross-check runs independently of the model.

Measured on an M3 laptop with 8 GB RAM, everything local: 51 clauses extracted across 8 tenders, every one with a parsed amount; verdicts 24 met, 5 partial, 19 missing, 3 unknown; the quote guard rejected 3 model citations (downgraded, reason recorded) and the cross-check caught 4 numeric conflicts; drafting removed 8 unsupported sentences across 16 sections; 21 LLM calls, about 83k tokens, US$0.00; 22 minutes for 8 tenders.

Win model ablation on the same time-ordered split (147 submitted bids): sklearn baseline AUC 0.731 / Brier 0.260 vs TabPFN v3.5 + thinking: AUC 0.752 / Brier 0.209. A should-I-bid number lives or dies by calibration.

Reading that shaped the guard: Your AI cited a real file. It still lied to you. and Making RAG admit when it is guessing.

Why Does Open Innovation Matter?

  1. The data is competitively sensitive. A contractor's audited financials, past bids and pricing are the most sensitive documents the firm owns. Every document-reading and document-writing step is a local open-weight model. The only network call in the system is the TabPFN win model, and it sends features (value, distance, competitor count), never documents.
  2. Zero marginal cost changes behaviour. The alternatives cost money per bid (agents) or per seat (SaaS). Local inference makes scanning one more notice free, so the firm checks every notice instead of the ones it can afford to check.
  3. Small-data problems need small-data models, and a fallback. Win probability is modelled over about 220 historical bids; TabPFN is built for that regime, unlike closed AutoML that wants thousands of rows. The pipeline also degrades gracefully to sklearn, so it has zero mandatory external dependencies.

The honest trade-off: a 4B local model is slower and rougher at prose than a frontier API on a per-tender basis. The guard makes that safe, and the privacy and cost properties make it worth it. A closed API would have been easier to build and impossible to ship to this customer.

My Agent Session

Tender Radar: building a local-first government tender radar with Gemma 4, TabPFN, and a citation guard
You

Side Hustle challenge: build with open-source AI at its core. Government tender radar for small contractors. What it does: scrapes tender portals such as GeM, matches tenders to a small firm's past work and certifications, builds an eligibility checklist, and drafts the compliance sections. Open stack: a multi-agent pipeline with a vector index of the firm's documents, running locally. Who pays: MSME contractors who currently pay agents commissions or miss tenders entirely. Why open wins: their financials and past bids are competitively sensitive. Locked constraints: week 3 only for now, budget 0, electrical contractor persona. Create a new folder and start making everything, first plan.

Agent

Plan first, then build. Key decisions from environment reconnaissance:

Decisions: SQLite as default store (fully local, ACID, brute-force cosine search is instant at demo scale) with an optional Postgres+pgvector path documented; open-weight LLM via Ollama; built-in hashed TF-IDF embedder as fallback so nothing needs downloading; TabPFN for the win-probability model with sklearn fallback. Architecture: notices -> extract -> match -> score -> draft -> bid pack. Prize targeting: Gemma (core LLM), TabPFN (win model), honest about everything else.

Agent

Scaffolded the project: pyproject, db/schema.sql, and the core modules.

You

dont pull any models, choose any model which i already have, but i will be using api key

Agent

Understood - using only gemma4:latest locally for LLM work, API key for TabPFN, and the built-in embedder. Smoke-tested Gemma 4 on clause extraction: it returned clean structured JSON with all five requirement types correctly segmented; a full generated tender notice read like a real state portal document.

Agent

Two real bugs found and fixed during the build worth recording:

Agent

Full measured run on 8 tenders, all local on the M3/8GB: 51 clauses extracted, every one with a parsed amount. Verdicts: 24 met / 5 partial / 19 missing / 3 unknown. The guard rejected 3 model citations (downgraded to unknown with reason) and the deterministic amount cross-check caught 4 numeric conflicts, including one where the model marked a clause 'met' while its own rationale argued the firm fell short. Draft guard removed 8 unsupported sentences across 16 sections. 21 LLM calls, ~83k tokens total, $0.00. Took screenshots of the board, detail page, and print-ready bid pack.

You

tabpfn_sk_REDACTED - this is the tabpfn key

Agent

Installed tabpfn-client and found the auth subtlety: the library reads TABPFN_TOKEN, not TABPFN_API_KEY, so the config now accepts both and calls set_access_token explicitly. A/B tested checkpoints on the same time-ordered 75/25 split over 147 submitted bids:

Thinking mode became the default. Final eval: sklearn baseline AUC 0.731 / Brier 0.260 vs TabPFN v3.5+thinking AUC 0.752 / Brier 0.209 - better on both, and calibration is what a 'should I bid?' number needs.

You

use the devrelay tool to make a submission for week 2 (Side Hustle), as this is a week 2 project

Agent

Submission workflow: pushed the repo to GitHub (praneshnikhar/tender-radar, built within the challenge window), generated web-sized screenshots for the post, extracted the exact DEV submission template from the challenge page, saved this session (scrubbed, no keys or local paths), and published the submission post with the required devchallenge and hf26challenge tags, entering Best Use of Gemma and Best Use of TabPFN.

Prize Categories

  • Best Use of Gemma: Gemma 4 does all clause extraction, verdicts and drafting, running locally via Ollama.
  • Best Use of TabPFN: TabPFN v3.5 with thinking mode is the win-probability model, with a same-split ablation against an sklearn baseline.

Built in a day for Hacktoberfest Week 2. The firm's data never leaves the laptop; the bills never arrive.

Top comments (0)