Inspiration# InsightAI · SuwaPath
Your Health. Our Path. Healthy life is not a choice. It is a mission.
Inspiration
Every one of us has a version of the same story. A parent with chest pain who did not know whether it was urgent. An aunt who spent a morning calling hospitals to find out which one had the right specialist free that week. A grandfather who sees a cardiologist, an endocrinologist and an eye surgeon, on three different days in three different towns, and tells his whole history from the beginning every time.
Sri Lanka has good doctors and good hospitals. What it lacks is the path between them.
- Channelling platforms list doctors, but they do not guide. A patient types "chest pain" and gets a list of names. Nobody asks how long it has lasted, whether it gets worse on the stairs, or whether this needs an emergency room today.
- Booking is not easy. Finding a doctor, a slot and a hospital that can run the test you need still means many calls and websites.
- Doctors start from zero. When a patient moves between specialists, the history, allergies and medication do not move with them, yet that is exactly what a diagnosis depends on.
- Nobody follows up. Guardians are not reminded when a parent's next clinic is due. Tablets that quietly stop go unnoticed. New mothers, newborns, pregnant women and elders need regular, coordinated specialist care, and they are the people least able to organise it.
- Sensitive care is avoided. HIV and sexual-health concerns are rising in Sri Lanka, and many people never ask for help because they are afraid their data will leak.
- Emergencies lose minutes, and hospital managers plan tomorrow without knowing what tomorrow will bring.
We built SuwaPath to close those gaps. InsightAI is SuwaPath rebuilt from the ground up in Jac, with a stronger security model, real AI agents with permissions, and the features we always wanted: same-day multi-specialist planning, care plans with guardian reminders, and live, transparent AI.
What it does
InsightAI turns a health concern into a tracked care journey:
$$ \text{concern} \rightarrow \text{history} \rightarrow \text{red-flag check} \rightarrow \text{specialist} \rightarrow \text{doctor} \rightarrow \text{booking} \rightarrow \text{handoff} \rightarrow \text{follow-up} $$
For patients
- A conversational assistant in English, Sinhala and Tamil, by text or by voice. It asks follow-up questions the way a doctor would, shows every step it takes live, and ends with a named specialist and the closest well-rated doctors with real upcoming clinics.
- Booking in one or two taps.
- A visit planner that finds one day when all the specialists you need are available, preferably in the same hospital, with travel time between visits, and books them all together or not at all.
- A health profile (allergies, conditions, medication, history) that travels to every doctor the patient sees, with consent.
- Care plans for recurring visits, medication reminders, and programmes for mothers, newborns, pregnancy and elder care.
- A PIN-protected private mode for HIV and sexual health. The chat is encrypted with a key derived from the PIN, stays on our own server, and never reaches a model provider with identifiers.
- One tap to call emergency services or the treating doctor, with spoken first-aid guidance.
For guardians: reminders before a dependent's clinic is due, alerts for missed medication or check-ins, and booking on their behalf when the patient allows it. Guardians see only what the patient has shared.
For doctors: a live queue, and a pre-consultation summary with the patient's own words, history, allergies, medication and reports. Doctors can send urgent messages that reach the patient immediately.
For hospital managers: demand forecasting, no-show risk, crowd management by hour, resource allocation and staff shift planning, all from the hospital's own appointments.
For administrators: provider verification, plans, a system monitor (latency, errors, model tokens and cost, agent outcomes, job runs) and a tamper-evident audit trail.
How we built it
One language, end to end
InsightAI is written in Jac on the Jaseci stack. The data model, business rules, AI agents, API and the entire browser UI are Jac: 92% of the repository by size. The rest is CSS and a little JavaScript.
| SuwaPath (before) | InsightAI (Jac) |
|---|---|
| SQLAlchemy models, 43 tables, migrations | node and edge archetypes on a persistent graph |
| FastAPI routers and Pydantic | def:protect functions served by Jac Scale, typed obj contracts |
| LangGraph and LangChain | byLLM by llm() functions with sem, behind our own gateway |
| APScheduler | @schedule functions |
| React and TypeScript SPA | jac-client: JSX inside .jac files |
The details are in JAC_AND_JASECI.md.
Security as a property of the data
Each patient's records live under that patient's own graph root. Another user's query cannot reach them, because they are not on that user's graph. Shared reference data (specialties, hospitals, doctors, clinics) lives on a shared root that every signed-in user can read and only the system can write. A notification is opened to exactly one recipient with allow_root.
On top of that, every endpoint calls one policy decision point. The same function authorises people and AI agents. An agent acting for a patient gets the intersection of its own permissions and what the patient delegated:
$$ \text{allowed}(a, p) = \text{perm}(a) \cap \text{delegated}(p \rightarrow a) $$
and it is re-checked at the moment the agent acts, not when it was planned.
The model never decides urgency
A deterministic red-flag engine runs over the whole conversation on every turn, before the assistant decides whether to ask another question or answer. The language model writes questions and explanations; it cannot lower or invent urgency. Every model call passes through one gateway that screens the input for prompt injection, removes fields the route does not need, replaces names with pseudonyms, blocks any payload that still carries an identifier, meters the tokens against the patient's plan and records the call.
Transparent AI
The assistant streams its work to the browser as Server-Sent Events. While it is thinking, the patient sees each step with its real duration: safety check, understanding the question, searching doctors. Follow-up questions arrive as tappable cards, and the answer ends with a swipeable deck of doctors to book.
Ranking that explains itself
Doctors are ranked by rating, distance from the patient's city and how soon the next clinic is:
$$ s_{\text{doctor}} = 0.40\,\frac{r}{5} + 0.35\,\max!\left(0,\ 1-\frac{d_{\text{km}}}{40}\right) + 0.25\,\sigma_{\text{soon}} $$
The visit planner searches every combination of one doctor per specialty whose clinic falls on a given day, orders the visits by time, adds travel time between hospitals and rejects any plan that overlaps. Plans are ranked to prefer one hospital, short travel and little waiting:
$$ s_{\text{plan}} = 0.5\,\mathbb{1}[\text{one hospital}] + 0.3\,\max!\left(0,\ 1-\frac{T_{\text{km}}}{30}\right) + 0.2\,\max!\left(0,\ 1-\frac{I_{\text{min}}}{240}\right) $$
With $k$ specialties and at most three candidate doctors each, the search per day is bounded by $3^k \le 27$ plans for $k \le 3$, so the whole five-week calendar is computed in one request.
Booking a plan is atomic and idempotent: every appointment is booked or none is, and a retried request with the same key never books twice.
Working as a team with AI agents
We split the work into lanes with frozen contracts: Lane A built the backend engines and agents, Lane B built the patient experience, and JacHammer built the leaf pages from a numbered prompt sequence. Typed contracts and contract stubs meant the UI could be built and tested against the real API shape while the engines behind it were still being written.
How we built InsightAI with Jac and Jaseci
InsightAI is SuwaPath rebuilt on the Jaseci stack. The original ran on FastAPI, SQLAlchemy and PostgreSQL, LangGraph, Qdrant and a React and TypeScript single-page app. The rebuild uses one language, Jac, from the data model to the browser. 92% of the repository is Jac (measured by tools/language_share.jac).
This document lists the Jac and Jaseci features we used and what each one replaced.
The stack at a glance
| Layer | SuwaPath (before) | InsightAI (Jac / Jaseci) |
|---|---|---|
| Data model | SQLAlchemy models, 43 tables, Alembic migrations | Jac node and edge archetypes on a persistent graph; no ORM and no migrations |
| API | FastAPI routers, Pydantic schemas | def:protect / def:pub functions served by Jac Scale at POST /function/<name>, typed obj contracts |
| Auth | JWT, bcrypt, hand-written RBAC | Built-in /user/register and /user/login, per-user roots, plus our policy decision point |
| AI orchestration | LangGraph StateGraph, LangChain calls |
byLLM by llm() functions with sem strings, wrapped by our LLM gateway |
| Streaming | FastAPI StreamingResponse |
A function that reports a generator streams Server-Sent Events |
| Background jobs | APScheduler | @schedule functions in main.jac |
| Frontend | React 19, TypeScript, Vite | jac-client: JSX inside .jac files, compiled to React, served by the same process |
| Database | PostgreSQL 16 server | Jac Scale's embedded PostgreSQL, started with the app |
| Tooling | pytest, ruff, mypy | jac check, jac test, jac format, version-matched jac guide skills, jac mcp |
| Hosted editor | none | JacHammer for leaf pages, previews and deploys |
Features we used
1. Graph data model: node, edge, roots
Every record is a node and every relationship is an edge: profiles, chat sessions and messages, doctors, hospitals, schedules, appointments, care plans, notifications, audit events, usage events and agent principals.
node Doctor(DirectoryNode) {
has code: str = "", name: str = "", specialty_code: str = "",
hospital_code: str = "", rating: float = 0.0, synthetic: bool = True;
}
edge HasSchedule {}
- Per-user roots isolate data with no extra code. A patient's sessions, appointments and care plans hang off that patient's own root, so another user's query cannot reach them.
root.sharedholds the reference directory (specialties, hospitals, doctors, schedules, agents). The access hook__jac_access__makes it readable by every signed-in user and writable only by the system.Jac.allow_rootshares a single node with a single other user. We use it to deliver a notification to exactly one recipient, and to share audit and security records with administrators only.allroots()lets the system monitor and the security log aggregate across users, behind an admin-only policy check.- Graph traversal (
[d ->:HasSchedule:->],[root.shared -->[?:Doctor]]) replaces SQL joins. The planner walks from each doctor to that doctor's clinics to find a day that works for everyone.
There are no migrations. Adding a field means adding a has line with a default.
2. Typed contracts with obj
Every request and response is a Jac obj in app/src/domain/contracts.jac, for example PlannerResponse, DayOption, QuestionSpec and RankedProvider. The compiler generates typed RPC stubs for the browser from the same definitions, so the client and server cannot drift apart. Contracts are frozen per lane, which is how two developers and JacHammer could work in parallel without breaking each other.
3. Endpoints with access levels: def:pub, def:protect, def:priv
def:protect plan_multi_visit(specialties: list[str], preferred_date: str = "",
city: str = "", window_days: int = 30) -> PlannerResponse { ... }
def:protectendpoints require a signed-in user. Jac Scale exposes them atPOST /function/<name>with a uniform{ok, data, error}envelope.def:pubis for anonymous health checks.def:privkeeps the scheduler heartbeat internal.
On top of that, every endpoint calls our policy decision point (app/src/policy/pdp.jac) before acting. The same function authorises humans and AI agents.
4. byLLM: by llm() and sem
Model calls are ordinary typed Jac functions:
def reply_to(message: str, context: dict[str, any]) -> str by llm();
sem reply_to = "Reply to the patient's message.";
semstrings describe intent and fields to the model, so prompts live next to the types they fill.- Typed outputs such as
SafetyVerdictcome back as real Jac objects, which the guardrails merge with deterministic rules. - One gateway. Agents never call
by llm()directly. They callinvoke_llmandrun_toolinapp/src/gateway/llm_gateway.jac. The gateway screens the input for prompt injection, minimises and pseudonymises patient data, runs an egress guard, meters tokens against the patient's plan, rehydrates names locally and writes anLlmCallEventthat administrators can review.
5. Streaming with generators
A function that reports a generator becomes a Server-Sent Events stream:
def frames -> Generator[dict[str, any], None, None] {
yield {"type": "stage", "node": "guard_input", "detail": "No urgent warning signs", "ms": 12};
yield {"type": "final", "answer": answer, "question": question, "providers": providers};
}
report frames();
The assistant streams session, routes, stage, tool, final and error events, the same event types as SuwaPath. Each stage carries a patient-safe description and a measured time, so the UI can show live reasoning, searching and tool steps while the answer is being prepared.
6. @schedule for background agents
@schedule(trigger=ScheduleTrigger.STATIC, interval=60)
def:priv scheduler_heartbeat -> None { ... }
Scheduled functions run inside the app process with no separate worker or queue. Care-plan reminders, medication adherence checks and follow-up detectors run this way, as the system reminder agent principal.
7. jac-client: the whole UI in Jac
The UI is written in .jac files with JSX:
def:pub Composer(value: str, onSend: any, busy: bool = False) -> JsxElement {
has listening: bool = False;
can with [value] entry { ... } # an effect
<div className="rounded-3xl border bg-surface">...</div>
}
hasdeclares component state, andcan with entryandcan with [deps] entrydeclare effects.- npm interop: we import
react-router-dom,react-error-boundaryandthinking-orbsdirectly from Jac. - Cross-tier calls: a client component calls
await plan_multi_visit(...)as if it were local. The compiler turns it into an authenticated RPC. - Tailwind v4 runs through the Jac Vite pipeline and loads SuwaPath's unchanged
tailwind.config.js, so the design tokens, fonts and components look exactly like SuwaPath. - Placement:
[placement] default = "server"and[placement.pins]injac.tomlkeep server-only modules (crypto, policy, gateway) out of the browser bundle.
This is the main reason the repository is over 90% Jac: the React SPA alone was 504 KB of TypeScript in SuwaPath.
8. Jac Scale: one process for everything
jac run main.jac starts the API, the embedded PostgreSQL store, the scheduler and the client dev server with hot reload. Production uses jac start. There is no separate web server, database server or job runner to install.
9. Tooling that kept a team in sync
jac checktype-checks the whole program, server and client, and runs in CI on every pull request.jac testrunstest "..." { }blocks next to the code. The suite covers policy, contracts, crypto, PHI handling, the gateway, guardrails, input and upload guards, the planner, notifications and billing.jac guide --export .claude/skillsexported version-matched language guides, so every coding agent wrote Jac for the exact pinned version, 0.37.25.jac mcpgave our coding agents the live compiler.- JacHammer builds the leaf pages (account, notifications, appointments, care plans, guardian, monitor) from a numbered prompt sequence in
docs/JACHAMMER_SPEC.md, then previews and deploys.
What Jac changed about the design
- Security by structure. Per-user roots and
allow_rootmean privacy is a property of where data lives, not a filter someone has to remember. - One contract, three tiers. The same
objtypes define the database payload, the API and the browser props. - Agents are principals. Because every endpoint is a typed function behind one policy check, an AI agent acting for a patient goes through exactly the same authorisation as the patient, intersected with what the patient delegated to it.
- Less glue. No ORM, no migrations, no separate SPA build, no task queue and no API client generator.
Lessons and pitfalls (Jac 0.37.25)
We recorded each of these in skills.md as we found it:
- Run scripts with
jac run --no-takeover, or the script replaces the app. @schedulefunctions register only when declared in the entry module.- Use
import typefor annotation-only imports in client code, or the bundle declares a class twice. - Inherited fields on contract
objs were not carried to the browser, so each response declaresok,errorandmetaitself. - Anchor-free modules can compile to the native codespace, whose
str()of an enum differs from Python's; default placement to server. - On the client, typed lists use
len(), not.length, and there is noremodule; we use the browser'sRegExpwhere needed. - Reserved words (
match,report,entry,own) cannot be variable names. - Nested helper functions inside a server function are not visible at runtime in every case; keep helpers at module level.
- Jac does not read
.envby itself; load it into the shell.
Challenges we ran into
- A new language under a deadline. Jac is young, and some behaviour differs from Python in ways the documentation does not yet cover. We pinned one version (0.37.25), exported version-matched language guides for our coding agents, and recorded every pitfall we hit in
skills.md: scripts must run with--no-takeover,@scheduleonly registers in the entry module, inherited fields on contract objects did not reach the browser, and modules without a placement anchor compiled to a codespace with differentstr()behaviour for enums. - Porting a React app without losing its look. We ported SuwaPath's UI screen by screen and kept its Tailwind configuration unchanged, then compared every screen side by side at desktop and phone sizes. A reviewer agent checked each port for parity.
- Streaming, voice and real time on the web. Dictation in browsers stops on its own, mobile keyboards push input boxes off screen, and a stop button that both cancels a reply and ends dictation confused people. We rebuilt the composer three times until a single, obvious control remained.
- A planner that respects geography. Our first planner happily suggested a 97 km trip across three cities in under two hours. It now keeps hospitals within reach of the patient's city, limits the distance between visits and adds travel time.
- Privacy that holds by structure, not by promise. It is easy to say "we do not leak data". It is harder to make leaking impossible. That is why the private chat key is derived from the PIN and never stored, why the model only ever sees pseudonyms, and why a planning step sees whether a lookup succeeded, never what it returned.
- Agents that act, safely. We wanted agents that do things, not just talk, without letting a model book, share or prescribe on its own. The answer was action tiers: reminders run immediately, bookings and sharing wait for one human tap, and diagnosis or prescribing has no path at all.
Accomplishments that we are proud of
- A complete patient journey, from "I have chest pain when I climb stairs" to booked doctors, with every step visible.
- Same-day multi-specialist planning with atomic booking, which no channelling platform in Sri Lanka offers.
- A security model where AI agents are first-class principals with audited, delegated permissions.
- 92% of the codebase in Jac, including the entire UI.
What we learned
- Graphs fit healthcare. Patients, guardians, doctors, hospitals, clinics and appointments are relationships first. Modelling them as nodes and edges removed a whole layer of joins and migrations.
- One language removes a class of bugs. When the server, the API contract and the UI props are the same Jac types, the client and server cannot quietly disagree.
- Deterministic where it matters, generative where it helps. Urgency, ranking and scheduling must be explainable and repeatable. Language is where a model shines.
- Show the work. People trust an assistant more when they can see what it checked and how long it took.
- Privacy must be designed into where data lives, not added as a filter afterwards.
Business model
- Hospital SaaS: hospitals license the operations and intelligence dashboard (demand forecasting, crowd management, resource and shift planning, no-show reduction).
- Booking commission: a small fee on each appointment channelled through the platform.
- Listing fees: hospitals and clinics pay to be verified and listed, with featured placement for capabilities such as diagnostics.
- Patient plans: a free core for everyone, with Plus and Family plans for more agent runs, care plans, guardian seats and multi-specialist planning.
As more patients book through InsightAI, hospitals that are not listed lose visibility, which makes listing and the hospital dashboard a natural purchase.
What's next
- Live integrations with hospital booking systems and SMS gateways across Sri Lanka.
- Pilots with partner hospitals for the operations dashboard.
Log in or sign up for Devpost to join the conversation.