rohitsar567 Claude Opus 4.7 (1M context) commited on
Commit
de473cb
·
1 Parent(s): 6d5684e

fix(frontend): PDF upload — in-chat ack + inline scorecard card + proceed-choice prompt

Browse files

Reported symptom: user uploaded a PDF and saw no acknowledgment in
chat — only a transient 8-second banner above the composer, which is
easy to miss. The chat transcript itself showed no trace of the
upload, so the user had no signal the indexing succeeded or that
they could now ask the bot about the document.

CHANGES
─────────────────────────────────────────────────────────────────────
handleFile (frontend/src/app/page.tsx):

1. pushUser("📎 Uploaded: <filename>") — breadcrumb so the chat
transcript records the action AND so the next /api/chat turn's
chat_history includes it (helps the brain disambiguate "this
policy" references).

2. On success: pushAssistant with the upload-ack text AND a
`citations` array carrying the uploaded policy_id. The existing
chat renderer reads citations from an assistant message and
fires getScorecard(policy_id, session_id) per cited policy, so
the scorecard card now appears INLINE under the bubble — same
treatment as a recommendation card.

3. Followed by a second pushAssistant carrying the proceed-choice
prompt: "Finish your profile" vs "Dive into the PDF first",
with the explicit note that a fuller profile makes the
discussion more useful.

4. On error: pushAssistant the error text (was banner-only;
equally easy to miss as the success case).

frontend/src/lib/i18n.ts — three new keys (en + hi):
- upload.user_msg "📎 Uploaded: ${name}"
- upload.chat_ack "Got it — I've indexed **${name}**…
Here's how it grades against what we know
about you so far:"
- upload.chat_choice proceed-choice prompt

BACKEND
─────────────────────────────────────────────────────────────────────
Unchanged. The /api/upload-policy endpoint already returns policy_id
+ policy_name + chunks_added + pages_indexed + elapsed_ms; retrieval
already pulls from the per-session quarantine collection via
rag.retrieve(session_id=...); /api/policies/{id}/scorecard already
honors uploaded policies (registered in UPLOADED_DOCS_DIR + included
in /api/policies/all).

VERIFY
─────────────────────────────────────────────────────────────────────
- Typecheck clean (npx tsc --noEmit, no errors)
- Live audit deferred to deploy commit — Playwright drives a file
upload + asserts (a) ack message appears in chat, (b) scorecard
card renders inline, (c) proceed-choice prompt appears.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

frontend/src/app/page.tsx CHANGED
@@ -1440,6 +1440,10 @@ export default function Page() {
1440
  async function handleFile(ev: React.ChangeEvent<HTMLInputElement>) {
1441
  const f = ev.target.files?.[0];
1442
  if (!f) return;
 
 
 
 
1443
  setUploadStatus(t("upload.indexing", { name: f.name }));
1444
  try {
1445
  // Pass the live chat session so the backend scopes the uploaded doc
@@ -1454,14 +1458,45 @@ export default function Page() {
1454
  secs: (r.elapsed_ms / 1000).toFixed(1),
1455
  }),
1456
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1457
  // Refresh coverage so the uploaded doc shows up
1458
  getCoverage().then(setCoverage).catch(() => {});
1459
  } catch (e: unknown) {
1460
- setUploadStatus(
1461
- t("upload.error", {
1462
- err: e instanceof Error ? e.message : String(e),
1463
- }),
1464
- );
1465
  } finally {
1466
  if (fileInputRef.current) fileInputRef.current.value = "";
1467
  setTimeout(() => setUploadStatus(null), 8000);
 
1440
  async function handleFile(ev: React.ChangeEvent<HTMLInputElement>) {
1441
  const f = ev.target.files?.[0];
1442
  if (!f) return;
1443
+ // Push a user-side breadcrumb so the chat transcript shows the upload
1444
+ // happened (helps the user track context — and it's part of the
1445
+ // history the next /api/chat turn sends back to the brain).
1446
+ pushUser(t("upload.user_msg", { name: f.name }));
1447
  setUploadStatus(t("upload.indexing", { name: f.name }));
1448
  try {
1449
  // Pass the live chat session so the backend scopes the uploaded doc
 
1458
  secs: (r.elapsed_ms / 1000).toFixed(1),
1459
  }),
1460
  );
1461
+ // ── In-chat acknowledgment + inline scorecard card ──────────────
1462
+ // Push two assistant messages:
1463
+ // 1. The "got it, here's the card" ack with a `citations` array
1464
+ // carrying the uploaded policy_id. The existing chat renderer
1465
+ // reads citations from an assistant message and fires
1466
+ // `getScorecard(policy_id, session_id)` per cited policy, so
1467
+ // the scorecard card appears inline under the bubble — same
1468
+ // treatment as a recommendation card.
1469
+ // 2. The proceed-choice prompt — telling the user they can
1470
+ // finish their profile OR dive into the PDF, and noting that
1471
+ // a fuller profile makes the policy discussion more useful.
1472
+ const ackText = t("upload.chat_ack", {
1473
+ name: r.policy_name,
1474
+ chunks: r.chunks_added,
1475
+ pages: r.pages_indexed,
1476
+ secs: (r.elapsed_ms / 1000).toFixed(1),
1477
+ });
1478
+ pushAssistant(ackText, {
1479
+ citations: [
1480
+ {
1481
+ policy_id: r.policy_id,
1482
+ policy_name: r.policy_name,
1483
+ insurer_slug: "user-upload",
1484
+ page_start: 1,
1485
+ page_end: r.pages_indexed,
1486
+ source_url: "",
1487
+ score: 1.0,
1488
+ },
1489
+ ],
1490
+ });
1491
+ pushAssistant(t("upload.chat_choice"));
1492
  // Refresh coverage so the uploaded doc shows up
1493
  getCoverage().then(setCoverage).catch(() => {});
1494
  } catch (e: unknown) {
1495
+ const errMsg = e instanceof Error ? e.message : String(e);
1496
+ setUploadStatus(t("upload.error", { err: errMsg }));
1497
+ // Surface the failure in chat too — a transient banner alone is
1498
+ // easy to miss (originally reported as "no acknowledgment").
1499
+ pushAssistant(t("upload.error", { err: errMsg }));
1500
  } finally {
1501
  if (fileInputRef.current) fileInputRef.current.value = "";
1502
  setTimeout(() => setUploadStatus(null), 8000);
frontend/src/lib/i18n.ts CHANGED
@@ -41,6 +41,9 @@ export const UI_STRINGS = {
41
  "upload.indexing": "Indexing ${name}…",
42
  "upload.success": "✓ Indexed “${name}” — ${chunks} chunks from ${pages} pages (${secs}s). It's now searchable in this chat. Ask me about it.",
43
  "upload.error": "✗ Upload failed: ${err}",
 
 
 
44
 
45
  // Marketplace panel
46
  "mp.heading": "Health insurance marketplace",
@@ -159,6 +162,9 @@ export const UI_STRINGS = {
159
  "upload.indexing": "${name} index हो रही है…",
160
  "upload.success": "✓ “${name}” index हो गई — ${pages} पेज से ${chunks} chunks (${secs}s)। अब यह इसी chat में search हो सकती है। इसके बारे में पूछिए।",
161
  "upload.error": "✗ Upload विफल: ${err}",
 
 
 
162
 
163
  "mp.heading": "स्वास्थ्य बीमा बाज़ार",
164
  "mp.summary": "${total} पॉलिसियाँ, ${insurers} प्रमुख भारतीय बीमाकर्ताओं से। पूरी रेटिंग और source document के लिए किसी भी पॉलिसी पर click करें।",
 
41
  "upload.indexing": "Indexing ${name}…",
42
  "upload.success": "✓ Indexed “${name}” — ${chunks} chunks from ${pages} pages (${secs}s). It's now searchable in this chat. Ask me about it.",
43
  "upload.error": "✗ Upload failed: ${err}",
44
+ "upload.user_msg": "📎 Uploaded: ${name}",
45
+ "upload.chat_ack": "Got it — I've indexed **${name}** (${chunks} chunks from ${pages} pages, in ${secs}s). Here's how it grades against what we know about you so far:",
46
+ "upload.chat_choice": "How would you like to proceed?\n\n• **Tell me more about yourself** — finish the short profile (age, family, location, budget, health) so I can speak to this policy more personally.\n• **Dive into the PDF first** — ask questions about coverage, waiting periods, exclusions, anything in the document.\n\nEither works. The more I know about you, the more useful the discussion of this policy will be.",
47
 
48
  // Marketplace panel
49
  "mp.heading": "Health insurance marketplace",
 
162
  "upload.indexing": "${name} index हो रही है…",
163
  "upload.success": "✓ “${name}” index हो गई — ${pages} पेज से ${chunks} chunks (${secs}s)। अब यह इसी chat में search हो सकती है। इसके बारे में पूछिए।",
164
  "upload.error": "✗ Upload विफल: ${err}",
165
+ "upload.user_msg": "📎 Upload किया: ${name}",
166
+ "upload.chat_ack": "मिल गया — **${name}** index हो गई (${pages} पेज से ${chunks} chunks, ${secs}s में)। यह policy आपके profile के हिसाब से कैसी है:",
167
+ "upload.chat_choice": "आगे कैसे बढ़ें?\n\n• **अपने बारे में बताएं** — short profile पूरा करें (उम्र, परिवार, location, बजट, health) ताकि मैं इस policy पर आपको personally बात कर सकूं।\n• **पहले PDF पर बात करें** — coverage, waiting periods, exclusions — कुछ भी पूछें।\n\nदोनों ठीक हैं। जितना मैं आपके बारे में जानूंगा, इस policy की चर्चा उतनी useful होगी।",
168
 
169
  "mp.heading": "स्वास्थ्य बीमा बाज़ार",
170
  "mp.summary": "${total} पॉलिसियाँ, ${insurers} प्रमुख भारतीय बीमाकर्ताओं से। पूरी रेटिंग और source document के लिए किसी भी पॉलिसी पर click करें।",