File size: 11,863 Bytes
bf76d9c
14940fe
bf76d9c
14940fe
 
 
 
bf76d9c
 
 
 
48f71e3
bf76d9c
 
 
 
 
 
 
 
 
 
 
 
 
 
14940fe
 
9fbe539
 
f8ea3d5
9fbe539
f8ea3d5
0310c6a
9fbe539
f8ea3d5
9fbe539
f8ea3d5
0310c6a
9fbe539
14940fe
 
bf76d9c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48f71e3
 
 
 
 
 
 
 
 
 
 
 
bf76d9c
 
4b78f37
 
bf76d9c
4b78f37
 
bf76d9c
4b78f37
 
bf76d9c
 
 
 
 
4b78f37
d0ed7bf
 
613377b
4b78f37
bf76d9c
4b78f37
bf76d9c
4b78f37
 
14940fe
4b78f37
bf76d9c
4b78f37
bf76d9c
4b78f37
 
bf76d9c
 
14940fe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ecf1a4e
14940fe
0310c6a
 
 
14940fe
e3f8f8d
0310c6a
 
 
 
14940fe
0310c6a
 
 
14940fe
 
 
 
 
 
e3f8f8d
 
 
75fe258
 
e3f8f8d
75fe258
 
14940fe
 
 
 
 
613377b
 
 
 
 
14940fe
0310c6a
14940fe
 
 
 
 
 
bf76d9c
14940fe
1804836
1bb49a9
 
 
 
 
1c6f397
1bb49a9
 
 
 
 
 
 
 
1804836
 
 
14940fe
b1c0057
 
93d5201
1804836
 
93d5201
 
9fbe539
f8ea3d5
9fbe539
 
 
14940fe
 
 
bf76d9c
 
14940fe
 
bf76d9c
 
14940fe
 
bf76d9c
 
14940fe
bf76d9c
 
14940fe
 
bf76d9c
14940fe
bf76d9c
 
14940fe
 
613377b
14940fe
 
48f71e3
 
 
 
 
 
 
 
 
 
 
 
bf76d9c
613377b
14940fe
 
613377b
14940fe
 
 
 
 
 
bf76d9c
 
14940fe
bf76d9c
 
 
14940fe
bf76d9c
48f71e3
bf76d9c
14940fe
bf76d9c
 
9fbe539
 
 
 
 
 
 
 
 
 
 
 
14940fe
4b78f37
bf76d9c
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
"""
ClaimReady — pre-submission claim checker (Gradio UI).

Pre-checks hospital insurance claim / pre-auth document sets against the Standard
Treatment Guidelines before they're uploaded to the government portal. OCR +
content verification run inside the Space on Gemma 3 12B via transformers (GPU),
with no cloud inference API.
"""

import gradio as gr

from llm import LLMConfigError, verify, _file_to_images
from packages import (
    STAGE_PREAUTH,
    STAGE_CLAIM,
    STAGE_LABELS,
    dropdown_choices,
    get_package,
    required_documents,
)

STAGE_RADIO_CHOICES = [
    (STAGE_LABELS[STAGE_PREAUTH], STAGE_PREAUTH),
    (STAGE_LABELS[STAGE_CLAIM], STAGE_CLAIM),
]

# Synthetic (fictional-patient) sample claims — safe to publish, let judges try
# the app in one click. NO real patient data.
# (label, package_code, stage, files) — synthetic, fictional-patient sample claims.
SAMPLES = [
    ("🤒 Enteric Fever · Pre-Auth · EN+हिन्दी", "MG006A", STAGE_PREAUTH,
     ["samples/enteric_clinical_notes.png", "samples/enteric_cbc.png"]),
    ("🤒 Enteric Fever · Claim · EN+हिन्दी", "MG006A", STAGE_CLAIM,
     ["samples/enteric_indoor_case.png", "samples/enteric_post_cbc.png",
      "samples/enteric_discharge.png"]),
    ("🩸 Severe Anemia · Pre-Auth · EN+తెలుగు", "MG064A", STAGE_PREAUTH,
     ["samples/anemia_clinical_notes.png", "samples/anemia_cbc_hb.png"]),
    ("🩸 Severe Anemia · Claim · EN+తెలుగు", "MG064A", STAGE_CLAIM,
     ["samples/anemia_indoor_case.png", "samples/anemia_post_cbc.png",
      "samples/anemia_discharge.png"]),
]


def package_info_md(code):
    pkg = get_package(code)
    if not pkg:
        return ""
    return (
        f"**{pkg['code']}{pkg['name']}**  \n"
        f"Procedure: {pkg.get('procedure', '—')}  \n"
        f"Specialty: {pkg.get('specialty', '—')}  \n"
        f"Package price: {pkg.get('price', '—')}"
    )


def checklist_md(code, stage):
    if not code or not stage:
        return "_Select a package and stage to see the required documents._"
    pkg = get_package(code)
    docs = required_documents(code, stage)
    if not docs:
        return "_No documents configured for this selection._"
    lines = [
        f"### Required documents — {STAGE_LABELS[stage]}",
        f"_{pkg['name']} ({pkg['code']})_",
        "",
    ]
    for i, doc in enumerate(docs, 1):
        line = f"{i}. **{doc['label']}** — {doc['verify']}"
        if doc.get("note"):
            line += f"  \n   _Note: {doc['note']}_"
        lines.append(line)
    rules = pkg.get("rules", [])
    if rules:
        lines.append("")
        lines.append("**Compliance checks (content):**")
        for r in rules:
            lines.append(f"- {r['check']}")
    return "\n".join(lines)


def on_package_change(code, stage):
    return package_info_md(code), checklist_md(code, stage)


def on_stage_change(code, stage):
    return checklist_md(code, stage)


def show_documents(files):
    """Render the uploaded/sample files (PDF pages too) into gallery images."""
    images = []
    for f in files or []:
        path = getattr(f, "name", f)
        try:
            images.extend(_file_to_images(path))
        except Exception:
            pass
    return images


def run_verification(code, stage, files):
    if not code:
        yield "⚠️ Please select a package code first."
        return
    if not stage:
        yield "⚠️ Please select a stage (Pre-Auth or Claim)."
        return
    if not files:
        yield "⚠️ Please upload at least one document to verify."
        return

    pkg = get_package(code)
    docs = required_documents(code, stage)
    paths = [getattr(f, "name", f) for f in files]

    yield (
        f"⏳ **Analyzing {len(paths)} document(s)…** "
        "Reading each page and checking it against the guidelines — "
        "the assistive review will appear here shortly."
    )
    try:
        result = verify(pkg, STAGE_LABELS[stage], docs, pkg.get("rules", []), paths)
    except LLMConfigError as e:
        yield f"## ⚠️ Configuration error\n\n{e}"
        return
    except Exception as e:
        yield (
            "## ❌ Verification failed\n\n"
            f"The model did not complete: `{type(e).__name__}: {e}`"
        )
        return
    yield result


# --- Styling ------------------------------------------------------------------

THEME = gr.themes.Soft(
    primary_hue=gr.themes.colors.teal,
    secondary_hue=gr.themes.colors.blue,
    neutral_hue=gr.themes.colors.slate,
    font=[gr.themes.GoogleFont("Inter"), "system-ui", "sans-serif"],
).set(
    body_background_fill="#eef2f6",
    block_background_fill="#ffffff",
    block_border_width="1px",
    block_shadow="0 1px 3px rgba(16,24,40,.06)",
    block_radius="14px",
)

CSS = """
.gradio-container {max-width: 1500px !important; width: 96% !important; margin: auto !important;}
#hero {
  background: linear-gradient(135deg, #2563eb 0%, #06b6d4 100%);
  color: #fff; border-radius: 16px; padding: 28px 32px; margin-bottom: 6px;
  box-shadow: 0 10px 26px rgba(37,99,235,.28);
}
#hero h1 {margin: 0 0 8px 0; font-size: 31px; font-weight: 800;
  text-shadow: 0 1px 2px rgba(0,0,0,.18);}
#hero p {margin: 0; color:#f0f9ff; opacity: 1; font-size: 15.5px; line-height: 1.55;}
#hero p b {color:#ffffff;}
#hero .pills {margin-top: 16px;}
#hero .pill {
  display:inline-block; background: rgba(255,255,255,.22); border:1px solid rgba(255,255,255,.45);
  color:#fff; padding: 5px 13px; border-radius: 999px; font-size: 12.5px;
  margin: 0 8px 6px 0; font-weight: 500;
}
#run-btn {font-weight: 700; font-size: 16px;}
#result-card .prose {font-size: 15px;}
.step-label {font-weight: 700; color:#0f4c81; margin-bottom: 2px;}
footer {display: none !important;}
#foot {text-align:center; color:#64748b; font-size:12.5px; margin-top:10px;}
/* keep text + list markers off the edge so the first glyph never clips */
.prose, .md, .prose *, .md * {overflow: visible !important;}
.prose {padding: 2px 6px !important;}
.prose ul, .prose ol {padding-left: 1.6em !important; margin-left: 0 !important;}
.prose li {line-height: 1.6; margin: 4px 0;}
.prose p, .prose h1, .prose h2, .prose h3, .prose h4 {line-height: 1.55;}
#problem-sidebar {padding: 6px 18px 16px 22px !important;}
#result-card {padding: 2px 18px !important;}
"""

HERO = """
<div id="hero">
  <h1>🏥 ClaimReady</h1>
  <p>An <b>assistive pre-check</b> for hospital insurance <b>claim</b> and
  <b>pre-authorization</b> document sets. Before you upload to the government portal,
  ClaimReady reads each document and checks it against the Standard Treatment
  Guidelines, <b>highlighting</b> missing papers and possible compliance gaps so your
  team can fix them early and reduce rejections.</p>
  <div class="pills">
    <span class="pill">🩺 Decision-support</span>
    <span class="pill">🔒 No cloud API — runs in the Space</span>
    <span class="pill">🤖 Gemma 3 12B (≤32B open model)</span>
    <span class="pill">⚡ GPU-accelerated</span>
  </div>
</div>
"""


PROBLEM = """
Health-insurance claims — for example under India's **Ayushman Bharat / PMJAY** scheme — require a
specific set of **supporting documents** that must satisfy the applicable **clinical / treatment
guidelines** for each procedure and stage (pre-authorization / claim).

**The problem**
- Every claim must include **all mandatory documents** and meet defined **content conditions** for the procedure and stage.
- A missing document — or a value that doesn't meet a condition — can lead to **claim rejection**, delays and rework.

**What ClaimReady offers**
- 📄 Reads every uploaded document with **on-device OCR** — images *and* PDFs.
- ✅ Verifies the set against the **required-document checklist** for the selected package and stage.
- 🔎 Evaluates **content rules** (thresholds, conditions) against the values it actually reads.
- 🌐 Handles **mixed-language** documents (e.g. English + Hindi / Telugu).
- ⚠️ Surfaces **missing documents and unmet conditions early**, with supporting evidence — as an **assistive** pre-check.
"""


with gr.Blocks(theme=THEME, css=CSS, title="ClaimReady — Claim Submission Check") as demo:
    with gr.Sidebar(label="ℹ️ The problem", open=False, position="left", width=400,
                    elem_id="problem-sidebar"):
        gr.Markdown("### The problem ClaimReady solves")
        gr.Markdown(PROBLEM)

    gr.HTML(HERO)

    gr.Markdown("**▶  New here? Load a sample claim** &nbsp;·&nbsp; "
                "_synthetic data, no real patients · mixed English + हिन्दी / తెలుగు_")
    with gr.Row():
        sample_buttons = [gr.Button(s[0], size="sm") for s in SAMPLES]

    with gr.Row(equal_height=False):
        with gr.Column(scale=5):
            gr.Markdown("#### 1 · Select the package", elem_classes="step-label")
            package_dd = gr.Dropdown(
                choices=dropdown_choices(),
                label="Package code",
                info="The PMJAY Health Benefit Package being claimed",
            )
            package_info = gr.Markdown()

            gr.Markdown("#### 2 · Select the stage", elem_classes="step-label")
            stage_radio = gr.Radio(
                choices=STAGE_RADIO_CHOICES,
                label="Stage",
                info="Pre-auth = before treatment · Claim = after treatment",
            )

            gr.Markdown("#### 3 · Upload the documents", elem_classes="step-label")
            files_in = gr.File(
                label="Claim documents (images or PDF)",
                file_count="multiple",
                file_types=["image", ".pdf"],
                height=160,
            )
            verify_btn = gr.Button("🔎  Run Assistive Check", variant="primary", elem_id="run-btn")

        with gr.Column(scale=5):
            with gr.Tabs():
                with gr.Tab("📋 Required documents & checks"):
                    checklist_out = gr.Markdown(
                        "_Select a package and stage to see the required documents._"
                    )
                with gr.Tab("📎 View uploaded documents"):
                    doc_gallery = gr.Gallery(
                        label="Click any page to enlarge",
                        columns=2,
                        height=560,
                        object_fit="contain",
                    )

    gr.Markdown("#### 🔎 Assistive review", elem_classes="step-label")
    with gr.Group(elem_id="result-card"):
        result_out = gr.Markdown(
            "_Run a check (or click a sample claim above) to see the assistive review here._"
        )

    gr.HTML(
        "<div id='foot'>ClaimReady · runs entirely on an open ≤32B model inside this "
        "Space, no cloud inference API · built for the HuggingFace Build Small hackathon</div>"
    )

    package_dd.change(
        on_package_change, inputs=[package_dd, stage_radio],
        outputs=[package_info, checklist_out],
    )
    stage_radio.change(
        on_stage_change, inputs=[package_dd, stage_radio], outputs=[checklist_out],
    )
    files_in.change(show_documents, inputs=[files_in], outputs=[doc_gallery])
    verify_btn.click(
        run_verification, inputs=[package_dd, stage_radio, files_in], outputs=[result_out],
    )

    def _load_sample(code, stage, files):
        return (
            code, stage, files,
            package_info_md(code), checklist_md(code, stage), show_documents(files),
        )

    for _btn, _s in zip(sample_buttons, SAMPLES):
        _btn.click(
            (lambda c=_s[1], st=_s[2], fl=_s[3]: _load_sample(c, st, fl)),
            outputs=[package_dd, stage_radio, files_in, package_info, checklist_out, doc_gallery],
        )

demo.queue()

if __name__ == "__main__":
    demo.launch()