carepath-api / docs /deploy.md
tranth3truong's picture
Deploy CP-UX-17: public demo hub, same-origin API, one design world
aacc29a
|
Raw
History Blame Contribute Delete
8.41 kB
# CarePath Unified Deploy
Use `https://carepath-medicaltranslation.vercel.app` as the public Scribe site. One Hugging
Face Space runs the Scribe tool at `/ghi-chep-lam-sang/`, the Scribe API at
`/api/v1/*`, and the retained Interpreter API at `/api/*` + `/ws/*`.
`/phien-dich-y-khoa/*` and `/console/*` are intentionally public 404s while
the Interpreter remains in development.
## Hugging Face Space
Reuse the existing Docker Space
[`tranth3truong/carepath-api`](https://huggingface.co/spaces/tranth3truong/carepath-api),
served at `https://tranth3truong-carepath-api.hf.space`. Deploy this unified
repository there; it retains the Interpreter API but does not serve the
unfinished Interpreter browser workflow.
1. Rebuild the existing `tranth3truong/carepath-api` Space with **Docker** as
the SDK. Create a replacement only if that Space is no longer available.
2. Copy `README.hf-space.md` to the Space as `README.md` so the Space has:
```yaml
---
title: CarePath
sdk: docker
app_port: 7860
---
```
3. Push this repo to the Space. The root `Dockerfile` builds `scribe/frontend/`
in a node stage (that build enforces the Vietnamese diacritics gate),
installs both API packages, pre-downloads the Gipformer int8 ONNX files, and
starts Uvicorn on port `7860`.
4. Set these Space secrets:
```text
LLM_PROVIDER=ckey
LLM_API_KEY=<your CKey key>
LLM_MODEL=gpt-5.4
TEAM_CODE=<shared internal demo code>
SOAP_RATE_LIMIT_PER_IP_HOUR=3
SOAP_RATE_LIMIT_PER_IP_DAY=10
SOAP_RATE_LIMIT_GLOBAL_DAY=100
APP_ENV=prod
CORS_ORIGINS=https://carepath-medicaltranslation.vercel.app
```
The interpreter defaults to mock mode, which returns `[vi->en] …` echoes. For
`/kham-song-ngu/` to actually translate on the deployed Space, add one more
secret:
```text
PROVIDER_MODE=ckey
```
It reuses `LLM_BASE_URL`, `LLM_API_KEY` and `LLM_MODEL` above — one CKey
account configures both modules. Measured latency across 50 turns: median 15s,
p90 54s, max 206s, so a live visitor waits. `PROVIDER_MODE=demo` is the
offline scripted scenario for the pitch laptop, not for a public URL, because
anything off-script falls back to a visible placeholder.
The public demo hub at `/thu-nghiem/` is built for both cases and reads
`GET /api/health` to decide what it may honestly offer:
| `provider_mode` | What the hub shows |
| --- | --- |
| `ckey` | Everything: the scripted sample, own-document upload, and the two-way conversation. |
| `demo` | The scripted sample only. Own-upload and the conversation panel are hidden, because in this mode `read_document` ignores the uploaded bytes and the canned map covers nothing a visitor would actually type. This is what makes `demo` safe on a public URL. |
| `mock` or unreachable | Nothing runnable, and a notice saying so. It never invents output. |
The sample path sends `X-CarePath-Sample: 1`, which forces scripted mode for
that one request, so samples stay instant and free even on `ckey`. Own-uploads
and conversation turns on `ckey` bill real tokens, capped at five runs per IP
per day by the `/api/demo/*` functions.
The `cloud` mode (Anthropic/OpenAI direct) additionally needs
`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and a non-default `ADMIN_TOKEN`
(startup refuses `change-me` in cloud mode).
The Vercel site uploads Scribe audio directly to the Space, so its exact origin
must be present in `CORS_ORIGINS`. Add local Vite origins as comma-separated
values only when testing local frontends against the deployed API.
Requests with header `X-Team-Code: <TEAM_CODE>` bypass the SOAP rate limits
for internal doctor-comparison runs. Limited requests return HTTP `429`,
`Retry-After`, and JSON body
`{"detail":{"message":"...","retry_after_seconds":3600}}` — the Scribe tool
surfaces that message to the user.
## Local Docker Check
```powershell
docker build -t carepath .
docker run --rm -p 7860:7860 `
-e LLM_PROVIDER=ckey `
-e "LLM_API_KEY=$env:LLM_API_KEY" `
-e LLM_MODEL=gpt-5.4 `
-e APP_ENV=prod `
carepath
```
Keyless variant (mock ASR + offline LLM + mock interpreter — must also work):
```powershell
docker run --rm -p 7860:7860 `
-e ASR_PROVIDER=mock -e ALLOW_MOCK_ASR=true -e LLM_PROVIDER=offline `
carepath
```
In another terminal:
```powershell
curl.exe http://127.0.0.1:7860/api/v1/health
curl.exe http://127.0.0.1:7860/api/health
curl.exe -o NUL -w "%{http_code}`n" http://127.0.0.1:7860/
curl.exe -o NUL -w "%{http_code}`n" http://127.0.0.1:7860/ghi-chep-lam-sang/
curl.exe -o NUL -w "%{http_code}`n" http://127.0.0.1:7860/phien-dich-y-khoa/
curl.exe -X POST http://127.0.0.1:7860/api/v1/soap-notes `
-F "audio=@C:\path\to\demo.wav" `
-F "encounter_context=Phòng khám nội tổng quát"
```
## Canonical Vercel site
The `scribe/frontend/` directory is the static marketing deployment at
`https://carepath-medicaltranslation.vercel.app`:
1. In the Vercel project settings, change **Root Directory** from the removed
`apps/web-next` to `scribe/frontend` (framework/build/output come from
`scribe/frontend/vercel.json`).
2. Set these Vercel environment variables:
```text
VITE_API_BASE=
VITE_WS_BASE=https://tranth3truong-carepath-api.hf.space
DEMO_API_BASE=https://tranth3truong-carepath-api.hf.space
VITE_LEAD_ENDPOINT=<optional lead endpoint>
VITE_LEAD_EMAIL=<pilot contact email>
```
`VITE_API_BASE` is **empty on purpose** (DEC-0021). `vercel.json` rewrites
`/api/*` to the Space, so the browser calls its own origin and no
`CORS_ORIGINS` entry stands between the site and the backend. Setting it to an
absolute origin restores the cross-origin coupling that took both tool routes
down when the domain moved to `carepath-medicaltranslation.vercel.app` while
the Space still allowed only `carepath-omega.vercel.app`.
`VITE_WS_BASE` is required whenever `VITE_API_BASE` is empty: a Vercel rewrite
will not carry a websocket upgrade to an external host, so `/ws/*` still goes
direct. That is safe — the origin check in `scribe/carepath/main.py` is HTTP
middleware and never runs for a websocket scope.
`DEMO_API_BASE` is read server-side by the `/api/demo/*` functions, so the API
host no longer appears in the client bundle.
3. Earlier guidance here said not to add Vercel API rewrites because the Space
owns those routes. That is superseded: the `/api/*` rewrite is what makes the
API same-origin. It excludes `/api/demo/*`, which are Vercel functions.
The Vercel build runs `npm run validate:deploy` before compiling. It fails when
`VITE_API_BASE` is set but invalid, does not use HTTPS, contains a
query/fragment, or is not the bare `/` pathname; when both `VITE_API_BASE` and
`VITE_WS_BASE` are empty; or when an SPA route
in `src/App.tsx` has no matching rewrite in `vercel.json` — without one, a direct
visit to that route is a hard 404. Local and combined-service builds still use
same-origin fallbacks because the normal `npm run build` skips this
deployment-only check.
The interpreter console was deleted once `/kham-song-ngu/` replaced it; its
backend stays on the Space for the WebSocket API, and `/phien-dich-y-khoa/` and
`/console/` return an explicit 404. The pilot form remains client-side unless
`VITE_LEAD_ENDPOINT` is configured.
## Keep-Alive
The workflow in `.github/workflows/keepalive.yml` pings the Space every 12
hours. Set the GitHub repository variable:
```text
SPACE_URL=https://tranth3truong-carepath-api.hf.space
```
Then run **Actions > Keep HF Space Awake > Run workflow** once and confirm the
job succeeds.
## Final Smoke
1. Open the Space URL: the landing renders, Vietnamese by default, and the
English resolves under each prescription line within about two seconds.
2. Open `/kham-song-ngu/`, consent, start a visit, and type a Vietnamese dose.
Confirm the turn is gated and the patient pane shows nothing until you
confirm it.
3. Open `/ghi-chep-lam-sang/`, upload a short clip, and verify the SOAP draft renders
with the review banner.
4. Confirm `/phien-dich-y-khoa/` and `/console/` return HTTP 404.
5. Confirm `GET /api/v1/health` reports `llm_provider: ckey` and
`asr_ready: true`, and `GET /api/health` reports `provider_mode: ckey`.
Note the deploy commit shape: the Space's `README.md` carries the
`sdk: docker` frontmatter, so a plain branch push would overwrite it with the
repository README and break the Space's build configuration. Deploy by
building a commit that copies `README.hf-space.md` over `README.md` first.