尚未收集仓库信息。
尚未检测到更新。GitHub 发布版本后会显示在这里。
制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗Upload the case Excel file as-is and every applicant gets one decision (Approved / Waitlisted / On hold / Not approved), the reasons with numbers, what to do next, and, if approved, a dated onboarding checklist and a training plan in their language. A chat panel answers "why" questions and accepts new facts ("APP-012 has now found a ground-floor space"), re-runs the whole engine and lists every knock-on change. No login.
Rules decide, AI explains. Every decision comes from a deterministic rule engine (same input → same output). The LLM is used only to read the two free-text columns into fixed fields and to phrase chat answers / parse chat updates. With no API key the app works fully: keyword rules read the free text and template sentences explain the stored reasons.
pip install -r requirements-dev.txt
uvicorn app:app --reload # web app at http://localhost:8000
pytest -q # 65 tests, no API key needed
app.py is a FastAPI app (JSON API under /api/*) serving the single-page front end in web/index.html.
An older Streamlit UI is kept as streamlit_app.py (pip install -r requirements-streamlit.txt, then
streamlit run streamlit_app.py).
Optional: ANTHROPIC_API_KEY (and ANTHROPIC_MODEL, default claude-sonnet-5-5) as environment variables. The key
stays on the server.
Import the GitHub repository in Vercel. It detects the FastAPI app in app.py and installs requirements.txt; no
build settings are needed. Add ANTHROPIC_API_KEY under Project → Settings → Environment Variables (optional) and
redeploy. To make the link public, turn off Deployment Protection (Settings → Deployment Protection → Vercel
Authentication) or share the production domain.
The server keeps no state between requests: the browser holds the uploaded data and sends it with each request, and the engine re-runs deterministically. So several judges can use the link at once without interfering, and a page refresh keeps your session (stored in the tab's sessionStorage).
parse → validate (Gate A) → derive (territory, distance, cost, payback) → Gate B (location) → Gate C (readiness)
→ score (100 pts) → allocate per territory → reasons, next steps, checklist, training → invariant checks
| Gate | Check | Outcome |
|---|---|---|
| A – data | missing ID / city / pin / investment / space; invalid pin; city ≠ pin prefix; city/state not allowed; Owned/Rented without size or floor; notes contradict the space field | On hold, with the exact fix |
| B – location | pin in no territory; pin already has a brand store | Not approved (other open pins / territories offered) |
| C – readiness | funds < 80% of required | Not approved |
| funds 80–100% of required | On hold ("add ₹X lakh or choose a space up to N sq ft") | |
| no space, upper floor (non-mall), < 300 sq ft, funding not confirmed in the notes | On hold |
If several gates fail, all reasons are recorded. Precedence: withdrawn/duplicate → Not approved; an unreliable location (bad pin, city/pin mismatch) → On hold (Gate B can't be judged yet); any Not-approved reason → Not approved; any other blocking issue → On hold.
config.yaml| ₹ | Tier 1 | Tier 2 | Tier 3 |
|---|---|---|---|
| Franchise fee | 5,00,000 | 4,00,000 | 3,00,000 |
| Fit-out per sq ft | 3,000 | 2,500 | 2,000 |
| Initial inventory | 8,00,000 | 6,00,000 | 5,00,000 |
| Working capital (3 months) | 4,00,000 | 3,00,000 | 2,00,000 |
| Rent per sq ft per month (rented only; deposit = 6 months) | 150 | 80 | 50 |
required = fee + fit-out × size + inventory + working capital + rent deposit (size = actual, or 400 sq ft if unknown).
| Worked example | Required |
|---|---|
| Tier 2, 400 sq ft, rented | 4 + 10 + 6 + 3 + 1.92 = ₹24.9 lakh |
| Tier 1, 400 sq ft, rented | 5 + 12 + 8 + 4 + 3.6 = ₹32.6 lakh |
| Tier 3, 400 sq ft, owned | 3 + 8 + 5 + 2 + 0 = ₹18.0 lakh |
| Tier 2, 600 sq ft, rented | 4 + 15 + 6 + 3 + 2.88 = ₹30.9 lakh |
Payback = required ÷ (revenue per sq ft × size × 15% margin); revenue ₹2,500 / 2,000 / 1,500 per sq ft per month by tier. Example: Tier 2, 400 sq ft rented → ₹1.2 lakh profit a month → about 20.8 months.
| Factor | Max | Rule |
|---|---|---|
| Investment adequacy | 25 | min(25, 10 + 30 × (ratio − 1)), ratio = offered ÷ required. More money for the same store → higher score; same money for a bigger store → lower score. |
| Retail experience | 15 | 0 yrs 0 · 1–2 yrs 7 · 3–5 yrs 12 · 6+ yrs 15 |
| Business background | 5 | runs/ran a shop or customer-facing business 5 · other business 2 · none/unclear 0 |
| Space quality | 15 | Owned 8 / Rented 5 + Ground 5 / Mall 4 + size 300–600: 2, 600–800: 1, > 800: 0 |
| Territory priority | 15 | High 15 · Medium 8 · Low 3 |
| Location & cannibalisation | 15 | nearest brand store > 3 km 15 · 1.5–3 km 8 · < 1.5 km 0 (flagged) · no store in city 15 · unknown 8 |
| Timeline fit | 5 | target ≥ realistic 5 · 1 month short 2 · more 0 · no target 2 |
| Payback | 5 | ≤ 18 months 5 · 18–30 3 · > 30 0 |
Tie-break (fixed order, unique result): score → investment ratio → retail experience → shorter time to open → lower Application ID. The level that decided a tie is written into both applicants' reasons.
Allocation per territory: walk the ranking; pin already won (or holds a store) → Waitlisted; slots full → Waitlisted; otherwise Approved and the pin is taken. Invariants (no shared pin, no store pin, slots respected, one decision + reason each, checklist + training for every approval) are checked after every run, including chat re-runs.
Every On hold applicant gets a ready-to-send email built from the stored reasons: what we found, exactly what to update, and a reply-by date (30 days). On the dashboard's "Needs follow-up" list and on the applicant profile, Draft in Gmail opens Gmail's compose window with the email filled in; you review it and press Send. Nothing is sent automatically, and no Google account setup or API key is needed. "Other email app" uses a mailto link, and "Copy" puts the text on the clipboard. The Excel export has a "Follow-up emails" sheet with every draft for mail merge. If the uploaded Applications sheet has an optional Email column, the recipient is filled in; otherwise type it in the draft window (it is remembered for the session).
data/pin_coords.csv – built once by scripts/build_pin_coords.py from the Department of Posts All India Pincode
Directory (data.gov.in, Government Open Data Licence; GitHub mirror). 427 pins in the 7 cities. No internet at runtime.data/existing_stores.csv, data/city_reference.csv – Sheet 3 and the allowed values. data/template.xlsx – the official template.tests/test_data.xlsx – generated by scripts/make_test_file.py; 27 applicants covering every tricky case
(see the case map at the top of that script).config.yaml.withdrawn, funds_confirmed, space_confirmed.temperature; repeat answers stay identical because LLM results are cached by input hash.fallbacks: "default"); if unavailable they fall back to a plain call, then to rules.