Skip to content

demo: hospital readmission data and a more visual page - #87

Merged
amrit110 merged 3 commits into
mainfrom
demo/readmission-and-visual-ui
Oct 31, 2026
Merged

amrit110 merged 3 commits into
mainfrom
demo/readmission-and-visual-ui

Conversation

@amrit110

@amrit110 amrit110 commented Oct 31, 2026 •

Copy link
Copy Markdown
Member

What

The tabctx-mcp agent demo (examples/agent_demo/) moves from telecom churn to 30-day hospital readmission, and its page loses most of its text in favour of visuals.

Data: Diabetes 130-US Hospitals (UCI; OpenML #4541)

  • The canonical hospital readmission table: 101,766 stays of diabetic patients, 1999-2008. The task (rank today's discharges by risk, find drivers, test care changes, recommend a program) reads for hospitals, and as rank-by-risk-then-test-an-intervention for financial institutions (credit, claims, fraud).
  • Prepared as in Strack et al. 2014: stays ending in death or hospice dropped, ICD-9 codes grouped into chapters, near-empty or constant columns dropped. Admission and discharge codes are named ("Home health service", not 6), and "None" lab results become "Not measured" (CSV readers took them for missing).
  • 30,000 past stays and 2,000 current patients, one stay each, who appear nowhere in the history. Current patients are picked with a chance in proportion to their number of stays, as the history samples stays, so both sides have the same mix: 11.0% readmitted on each. (Picking patients uniformly gave a healthier current set, 9.6% against 11.6%; taking each patient's latest stay gave 4.7%.)
  • task.py names the files and columns, the outcome labels (positive and negative class, window) and the page's step icons; prepare_data.py, score.py, run.sh (via python3 task.py files) and the page read it. Another dataset means changing task.py, prepare_data.py and PROMPT.md.

Page: visual over text

  • The gateway-key paragraph is an ⓘ tooltip; the hero lost its paragraph and footer (the data card's lock already says the outcome is hidden).
  • Agent cards: name, score badge and icon chips (MCP, no shell); model and tools on hover.
  • Task: five step icons (Compare, Rank, Drivers, What-if, Report) with the prompt folded (open by default only locally, where it is editable).
  • A failed tool call shows its first clause; the rest (e.g. a column list) on hover.
  • The predict card shows the expected split (mean probability). For a rare outcome almost no row crosses 50%, so the old "No 100% / Yes 0%" said nothing.
  • What-if rows: the new values, with the columns they go in beneath in small type.
  • Tooltips escape untrusted values twice (tipAttr): data-tip is unescaped by the browser and then rendered as HTML.
  • Score cards say "readmitted" from the dataset's labels; score.json keys are now positive_in_top_10/100 (fractions).

Fixes

  • run.sh claude failed on macOS bash 3.2 without CLAUDE_MODEL (empty array "unbound" under set -u). The Docker image uses bash 5, so the hosted demo was not affected.
  • make_showcase.py masks the login name (Codex's ls -la output showed it).

Results (prod gateway, real agents)

Agent (LLM) tabctx calls Wall time ROC AUC on real outcomes Readmitted in top 10 / top 100
Claude Code 2.1 (Opus 5.5) 10 160 s 0.674 80% / 36%
Codex 0.137 (gpt-5 via gateway) 9 188 s 0.674 80% / 36%
pi 0.73 (Sonnet 5.5 via gateway) 10 134 s 0.674 80% / 36%
  • Base rate 11.0%: a random top 10 holds about 1; the top 100 is 3.3x the base rate. Readmission is hard to predict from administrative data (published scores reach AUC ~0.65-0.72), so these are honest, not churn-sized, numbers.
  • evaluate on 7,500 held-out stays: tabpfn-3.5 0.697, tabicl-v2 0.690, gradient boosting 0.683. Fit 30,000 rows in 20-23 s; predict 2,000 in ~1.5 s; explain (50,000 rows in one predict) ~30 s.
  • All three used every tool, dropped patient_id, chose tabpfn-3.5, and found the same drivers (prior inpatient stays, discharge destination, emergency visits, primary diagnosis). For the three riskiest patients going home, home health service lowered predicted risk by 2-10 points; medication and A1C changes by about 1 point or less. All three reports state these are learned associations, not proven effects, and recommend a risk-targeted transitional-care program measured against a comparison group.
  • In an earlier run, Codex stopped after predicting with a progress message and no report (gpt-5 in codex exec sometimes ends its turn early); running it again finished.

Review

A review agent found one blocking issue and two should-fixes, all fixed in 483c70e:

  • Blocking, XSS: data-tip values were escaped once, but the browser unescapes the attribute and the tooltip sets it as innerHTML, so an agent-supplied what-if value or column name could inject HTML on hover (older cards had the same pattern). Fixed with tipAttr(); checked in Chrome with an onerror payload in a column name, value, error and class label: no element created.
  • Should-fix: the current set was healthier than the history (9.6% vs 11.6%); now stay-weighted (11.0% each), with a test. Showcase runs redone on the new split.
  • Should-fix: a dataset swap still needed JS edits; the negative class, outcome window and step icons moved to task.py.
  • Nits fixed: "Not scored" for a score.json without top-k keys, a clear error when task.py can't run in run.sh, stable sort in scoring, login masked only as an ls -l owner, dead counter and a churn comment removed.
  • Verified with no change needed: the code-to-name maps against the dataset's IDs_mapping, the "None" → "Not measured" handling, run.sh in bash 3.2 and 5, the Docker image's imports, and every README/CHANGELOG number against the showcase files.

Tests

  • New tests/unit/test_agent_demo_data.py: ICD-9 grouping, the patient-disjoint and stay-weighted split, scoring (top-k, base rate, row coverage), showcase runs matching the task, task.py files, and transcript sanitizing (paths, login name).
  • ruff check, ruff format --check, and the unit suite (759 passed) pass locally.
  • Checked in Chrome: local and hosted modes, live replays of the new runs, light and dark.

Deploy after merge: GCP_ACCOUNT=amritk@vectorinstitute.ai examples/agent_demo/deploy.sh.

🤖 Generated with Claude Code

The agent demo moves from telecom churn to 30-day hospital readmission
(Diabetes 130-US Hospitals, OpenML #4541), which reads for hospitals
and, as a rank-by-risk-then-test-an-intervention task, for financial
institutions too. Admission and discharge codes are named and diagnosis
codes grouped, current patients are kept out of the history, and
task.py holds the files and columns the runner, scoring and page read.
New showcase runs: Claude Code, Codex and pi all reach ROC AUC 0.692,
6/10 of their top 10 readmitted (base rate 9.6%).

The page drops text in favour of visuals: key note as a tooltip, agent
cards as icon chips, the task as five step icons with the prompt
folded, one-line tool errors, expected (mean-probability) split on
predict, and what-if rows labelled by value with the column beneath.

Also fixes run.sh on macOS bash 3.2 without CLAUDE_MODEL, and masks the
login name in showcase transcripts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov-commenter

codecov-commenter commented Oct 31, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.78%. Comparing base (270b8f1) to head (9d2e3c7).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main      #87      +/-   ##
==========================================
+ Coverage   84.74%   84.78%   +0.03%     
==========================================
  Files          56       56              
  Lines        5021     5021              
==========================================
+ Hits         4255     4257       +2     
+ Misses        766      764       -2     
Flag Coverage Δ
integration 37.22% <ø> (ø)
unit 81.87% <ø> (+0.03%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.
see 1 file with indirect coverage changes

Impacted file tree graph

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

amrit110 and others added 2 commits October 31, 2026 18:38
- Tooltips: data-tip is unescaped once by the browser and then set as
  innerHTML, so tipAttr() escapes the tooltip HTML again and every
  untrusted part (column names, class labels, what-if values, tool and
  model errors) is esc()-ed inside it. Checked in Chrome with an
  onerror payload: no element is created.
- Current patients are picked in proportion to their number of stays,
  like the history's stays, so both sides have the same mix (11.0%
  readmitted each; uniform picking gave 9.6% against 11.6%). New
  showcase runs on the new split: ROC AUC 0.674, 8/10 and 36% of the
  top 10/100 readmitted, for all three agents.
- task.py also holds the negative class, the outcome window and the
  page's step icons, so a dataset swap needs no JS edit.
- A score.json without the top-k keys shows "Not scored"; run.sh fails
  clearly when task.py can't run; ties sort stably in score.py; the
  login is masked only as an `ls -l` owner; dead counters and a churn
  comment are gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@amrit110
amrit110 merged commit 0306e2c into main Oct 31, 2026
3 checks passed
@amrit110
amrit110 deleted the demo/readmission-and-visual-ui branch October 31, 2026 22:41
@amrit110 amrit110 mentioned this pull request Nov 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants