Inspiration
Everyone else asks if ancient medicine works.
That's the wrong question. It's been the wrong question for a thousand years.
The right question is: what is actually happening at the molecular level inside a 1,000-year-old recipe? Nobody has ever answered it — not because it's mystical, but because it's tedious. Cross-referencing 18,000 compounds against 50,000 protein targets, for 1,035 formulations, by hand, is a job no human has time for.
Al-Razi had the same problem in the 9th century. He solved it with the Aqrabādhīn — a book that catalogs every compound medicine known to his world. It was the reference standard from Córdoba to Delhi for 800 years. Then modern medicine arrived and the book stopped being updated.
Not because it was wrong. Because it became unreadable.
We built Itqan because the bottleneck is finally solvable. IMPPAT 3.0 released the structured Ayurvedic data. Free-tier vision models can read Devanagari. Free-tier LLMs can reason over 200,000-edge molecular graphs. For the first time in history, the question is answerable in seconds, by anyone, for free.
We're not here to prove Ayurveda works. We're here to make it testable.
That's the difference between tradition and science.
What it does
You give Itqan an ancient polyherbal formulation — from a formulary or a photographed manuscript.
It gives you back a ranked list of cross-plant molecular findings, each one written as a research brief.
The pipeline:
1. Read. Gemini 3.8 Flash reads the manuscript image and extracts the recipe text. An LLM-powered extractor pulls ingredient names out of the OCR text. A 10,149-alias resolver matches each traditional name to a botanical species.
2. Analyze. The analysis engine expands the formulation into its molecular graph — plants → compounds → protein targets. It finds pairs of compounds from different plants that hit the same biological target. It filters out promiscuous targets. It deduplicates pairs. It ranks by potency and shared-target count.
3. Interpret. Groq's Qwen 3.8 reads each finding, grounds itself strictly in the measured evidence, and writes a hedged pharmacological interpretation with a mechanistic hypothesis and three specific experiments to test it.
What comes out, for one formulation — Kaphaketu rasa, three ingredients:
491 findings. 66.7% coverage. Every finding traced to a measured database row.
Example — Finding #1: Piperlonguminine (from Piper longum) + Shogaol (from Zingiber officinale) → TRPV1 receptor Measured potency: 6.31 nM vs 290 nM. Shared targets: 2. Score: 2.7.
LLM interpretation, generated in 1.6 seconds: "Both may act as ligands for the Vanilloid receptor, with Piperlonguminine potentially exhibiting higher affinity... Testable predictions: (1) Competitive binding assay to determine binding site overlap. (2) Calcium flux assay in TRPV1-transfected cells. (3) Cryo-EM structural modeling of both binding poses."
That's not a wellness claim. It's a research proposal.
How we built it
FastAPI + PostgreSQL + Celery, containerized with Docker Compose, deployed on three free-tier services. Zero infrastructure cost.
The schema was the hardest part. The external_identifiers table is what makes this universal — any source (IMPPAT, TCMSP, KNApSAcK, Dr. Duke's) plugs in without a schema change. Every evidence edge carries two columns that most projects skip: evidence_tier (MEASURED / CURATED / INFERRED / PREDICTED) and manifest_id (which loader run produced this row). Nothing in this database can silently conflate a prediction with a measurement.
The ETL pipeline loads IMPPAT 3.0 — 4,154 plants, 18,304 compounds, 196,181 plant-compound edges, 1,035 formulations, 13,860 formulation ingredients, 4,150 targets, 56,577 compound-target edges. Every load is batched, idempotent, and fully traced.
The analysis engine runs in one pass over the molecular graph. It ranks by score × √(shared_targets) — potency times corroboration. Nothing is ranked on score alone.
The LLM layer is Groq's Qwen 3.8 with strict JSON mode and a system prompt that bans efficacy claims outright. The prompt says: "Use ONLY the evidence provided. NEVER claim a compound treats or cures any disease. Frame every claim as a testable hypothesis."
The OCR layer is Gemini 3.8 Flash with a four-model fallback chain that survives 503 throttling. When the primary model is rate-limited, it tries the next one automatically.
The frontend — Next.js 16, React 19, TanStack Query/Table, Tailwind v4 — was built by a teammate against the PRD's exact Zod contract. When the schemas didn't match the backend, we rewrote the backend adapters, not the frontend.
What we didn't build: a fine-tuned model. We measured, we tested, and we concluded the general models were good enough. Fine-tuning is a v2 optimization, not a v1 requirement.
Challenges we ran into
The PostgreSQL parameter limit. Our first backfill tried to insert 18,304 rows in a single query. PostgreSQL's ceiling is 65,535 parameters. We hit it, the transaction rolled back, and we learned to batch in 500-row chunks — which we now do everywhere.
Groq deprecated our model mid-build. We had working code against llama-3.3-70b-versatile. Then Groq shut it down on 16 August 2026 and returned model_not_found. The fix was one line, but we had to prove the new model (qwen/qwen3.8-27b) actually parsed JSON before we trusted it.
Gemini's free tier throttled us with 503s. We built a four-model fallback chain — gemini-3.8-flash → gemini-3.5-flash → gemini-flash-latest → gemini-2.5-flash — so a single model being busy doesn't kill the request. That's not defensive coding; that's the reality of free-tier infrastructure.
UniProt complexes in a single column. IMPPAT stores protein complexes as O94806|P05129|P05771|... in a VARCHAR(32) column. That broke every insert. We truncate to the first UniProt ID and store the full string in a notes field. The schema was designed for the data we assumed; the loader had to be rewritten for the data we actually got.
Same-compound self-pairs. Our first findings run produced 500 results, many of which were beta-Caryophyllene + beta-Caryophyllene — because two different plants both contained the same molecule. That's not synergy, that's one molecule counted twice. A one-line filter kills it. But you have to notice it first.
Cell lines treated as protein targets. DU-145 and MCF-7 are cancer cell lines, not receptors. IMPPAT's bioactivity file mixes phenotypic readouts with molecular targets. Our first run ranked "both kill DU-145" as a molecular finding. Wrong category. We built a noise-target filter with 19 prefixes and 10 exact matches.
A frontend-backend contract mismatch we didn't see for weeks. The frontend was built against a PRD, not against a working API. The Zod schemas expected period where the backend returned era. The findings endpoint returned {items, total} where the frontend expected a plain array. Every screen was "working" — and showed nothing. The fix wasn't a frontend fix or a backend fix. It was one adapter file that translated between them.
Accomplishments we're proud of
The database actually works. 4,154 plants, 18,304 compounds, 196,181 measured plant-compound edges, 56,577 compound-target edges with 17,734 measured potencies. Every row traces to a manifest. Every edge carries an evidence tier. No shortcuts.
The analysis engine produces real pharmacology. Finding #1 on Kaphaketu rasa — Piperlonguminine + Shogaol → TRPV1 — is not a false positive. Both compounds are documented TRPV1 agonists. The engine found a real, published, testable relationship from the data alone, in 2 seconds.
The LLM never claims more than the data. Every interpretation is hedged, grounded, and includes a limitations section. When we tested it, the model voluntarily wrote: "The evidence is limited to in vitro binding measurements and does not confirm that these compounds reach the receptor at effective concentrations in vivo." That's the model being more honest than we asked it to be.
Zero cost. Free-tier Groq for NLP, free-tier Gemini for vision, free-tier PostgreSQL on Neon, free-tier FastAPI on Render. The entire system runs on infrastructure that costs nothing. That's not a hack — that's a deliberate design decision. The pharmacologist who needs this most is the one with no budget.
The schema is universal. Add TCMSP tomorrow? New loader, zero migrations. Add KNApSAcK? Same. The external_identifiers table has one job and it does it. That's what "extensible" actually means — not a buzzword, a specific design that lets you add a source without rewriting the past.
The OCR pipeline works end-to-end with zero external dependency. We tested it with a generated image: image → Gemini → Devanagari text → Groq extraction → ingredient candidates → resolver → formulation. Every stage ran. No OCR engineer. No fine-tuned model. Just a free API and a fallback chain.
What we learned
Data quality is the product. We could have built this in a weekend with fabricated numbers. It would have looked perfect and been worthless. The reason our findings are credible is that every number we use was measured, cited, and traced. The engineering was harder than the idea. That's always true, and it's the thing most hackathon teams don't say out loud.
Fine-tuning is usually the wrong answer. We spent time considering fine-tuned SLMs for both OCR and NLP. The research said constrained LLM inference beats supervised fine-tuning on structured extraction tasks. We tested it. The research was right. A 70B model with JSON mode and a good prompt did what a fine-tuned 3B model would have done — for less money, in less time, with no training data.
Never assume what the API will return. Every single bug we hit was because we assumed the data shape. Activities were = 900000.0 nM, not 900000. UniProt IDs came as pipe-separated complexes. Status values were uppercase PENDING, not lowercase pending. The only assumption that survived contact with reality was "nothing is what you expect."
A contract is a document. The frontend and backend have to agree on the exact JSON shape — not a description of it, the exact shape. We learned that the hard way. Now there's an adapter file whose sole job is to be the source of truth. If it's not written down, it's not a contract. It's a hope.
A finding without provenance is a rumor. We could have surfaced findings without source citations. It would have been faster to build. It would also have been useless — a researcher can't test a claim with no chain back to the evidence. The traceability is the science. Not the ML. Not the models. The chain of evidence.
You can't validate what you don't test. We assumed the compounds loaded because the table existed. It was empty for days. We assumed the frontend worked because the pages returned 200. It was showing an error state on every screen. The lesson is boring and true: run the query. Check the count. Open the browser. Every time.
What's next for ITQAN
Immediate (next 2 weeks):
- Graph endpoint.
GET /graph?analysis_id={id}— the 3D knowledge graph screen in the frontend is already built. It just needs real data. - Deploy. Neon for Postgres, Render for FastAPI. A URL a pharmacologist can open from anywhere.
- Design pass. The palette is functional but too cold. Warm it up. Add a botanical illustration system. Make it look like what it is — a serious scientific tool with a soul.
- Real manuscript test. We tested with generated English and screenshot Devanagari. Next is a real Ayurvedic manuscript scan. That's where we find out if Gemini is enough or if a fine-tuned model is required.
Short term (next 3 months):
- TCMSP integration. The universal schema is ready. Load the Chinese tradition. Two traditions, then ten. "Universal" stops being a promise and becomes a fact.
- ChEMBL bioactivity. Real IC50/Ki data from a mature source. Replaces the thin bioactivity layer from IMPPAT.
- Dr. Duke's ethnobotany. Populate
plant_uses. Now the tool knows not only which plants were used, but what they were used for, in which culture, in which region. That's what makes this worldwide instead of Ayurvedic. - API rate limits and auth. A public URL needs a key. Right now it's open. That changes before anything ships.
Medium term (next 6–12 months):
- Fine-tuned OCR model. If Gemini's accuracy on historical manuscripts is under 85%, the fine-tuned model becomes necessary. We'll have 500 labeled manuscripts by then. That's a 4-week training project, not a research project.
- KEGG and Reactome pathway integration. The findings are currently target-level. With pathways, they become mechanism-level. A finding that says "both compounds hit CYP3A4" is interesting. One that says "one hits the upstream enzyme, the other hits the downstream receptor, in the same metabolic pathway" — that's a discovery.
- Open submission to the AIIA-iCAINE Ayurveda AI Challenge. We're building for it. The submission is ready the day the frontend is verified in the browser.
Long term (12+ months):
- The first published paper. Not "we built a tool." A real paper. "Cross-plant molecular convergence in the Ayurvedic Formulary of India." The data is there. The engine works. What's missing is the validation — a computational biologist to review the top 100 findings against the literature. That's a collaboration, not a code problem.
- Partnerships with a wet lab. A finding is a hypothesis. A hypothesis is only worth what someone tests. Find one lab willing to test ten findings, and the entire thesis becomes provable.
- Global expansion. Africa, South America, Southeast Asia. Traditional medicine is not an Indian problem. It's a human problem. The schema is ready. The engine is ready. The data is not — yet. That's the next three years.
Itqan is not a company yet. It's a proof.
A proof that ancient knowledge is not unreadable. A proof that the pharmacy of the past can be read by the pharmacology of the present. A proof that a solo founder with free-tier APIs and a ruthless commitment to honesty can build something a research lab would pay for.
The next aqrabādhīn is not written by hand. It's written by whoever builds the tool that reads the last one.
That's what we built. That's what comes next.
Built With
- alembic
- api
- async
- celery
- docker
- httpx
- next.js
- panda
- pillow
- postgresql
- python
- react
- redis
- smiles-drawer
- sqlalchemy
- tailwind
- tanstack
- three.js
- typescript
- unidecode