File size: 8,408 Bytes
aacc29a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# 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.