med-scribe-alliance-ts-sdk 2.0.8 → 2.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +247 -77
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -4,8 +4,7 @@ TypeScript SDK for the [MedScribe Alliance Protocol](https://github.com/MedScrib
4
4
 
5
5
  ## npm package link
6
6
 
7
- https://www.npmjs.com/package/med-scribe-alliance-ts-sdk
8
-
7
+ https://www.npmjs.com/package/med-scribe-alliance-ts-sdk
9
8
 
10
9
  ## Installation
11
10
 
@@ -19,57 +18,269 @@ Peer dependencies (installed automatically):
19
18
  - `@breezystack/lamejs` — MP3 encoding
20
19
  - `zod` — Schema validation
21
20
 
22
- ## Quick Start
21
+ ---
22
+
23
+ ## Integration Guide (Step-by-Step)
24
+
25
+ ### Step 1: Create the Client
26
+
27
+ The `baseUrl` is **required** — all API calls (session creation, upload, status polling) go through this URL.
23
28
 
24
29
  ```ts
25
30
  import { ScribeClient } from 'med-scribe-alliance-ts-sdk';
26
31
 
27
32
  const client = new ScribeClient({
28
- baseUrl: 'https://api.example.com/voice/api/v2',
33
+ baseUrl: 'https://api.eka.care/voice/api/v2', // your scribe service URL
29
34
  accessToken: 'your-bearer-token',
35
+ debug: true, // optional: logs SDK activity to console
30
36
  });
37
+ ```
38
+
39
+ ### Step 2: Initialize (Discovery)
40
+
41
+ `init()` fetches the discovery document from the server. This tells the SDK what the server supports (models, languages, upload methods, audio formats, etc.).
42
+
43
+ ```ts
44
+ const initResult = await client.init();
45
+ if (!initResult.success) {
46
+ console.error('Init failed:', initResult.error.message);
47
+ return;
48
+ }
49
+ ```
50
+
51
+ > `startRecording()` calls `init()` automatically if not already initialized. You can skip this step if you go directly to recording.
52
+
53
+ ### Step 3: Register Callbacks
54
+
55
+ Register callbacks **before** starting a recording. These are how you receive events from the SDK.
31
56
 
32
- // Register callbacks
57
+ ```ts
58
+ // Upload progress
33
59
  client.registerCallback('onUploadEvent', (event) => {
34
60
  if (event.type === 'progress') {
35
61
  console.log(`Uploaded ${event.data.successCount}/${event.data.totalCount}`);
36
62
  }
37
63
  });
38
64
 
65
+ // Recording state changes
66
+ client.registerCallback('onRecordingStateChange', (event) => {
67
+ console.log('Recording state:', event.type); // 'started' | 'paused' | 'resumed' | 'ended'
68
+ });
69
+
70
+ // Errors (VAD failures, network issues, validation)
39
71
  client.registerCallback('onError', (event) => {
40
72
  console.error(`[${event.error.code}] ${event.error.message}`);
41
73
  });
42
74
 
43
- // Start recording creates session, starts mic, begins chunked upload
75
+ // Auto token refresh on 401
76
+ client.registerCallback('onTokenRequired', async (event) => {
77
+ const newToken = await refreshMyAuthToken();
78
+ event.resolve(newToken);
79
+ });
80
+ ```
81
+
82
+ ### Step 4: Start Recording
83
+
84
+ Creates a session, starts the microphone, and begins chunked upload in one call.
85
+
86
+ ```ts
44
87
  const result = await client.startRecording({
45
- templates: ['soap', 'prescription'],
46
- uploadType: 'chunked',
88
+ templates: ['soap'], // required: template IDs for extraction
89
+ uploadType: 'chunked', // 'chunked' (default) | 'single' | 'stream'
90
+ sessionMode: 'consultation', // optional: 'consultation' | 'dictation'
91
+ transcriptLanguage: 'en', // optional: language code for transcript output
92
+ languageHint: ['en', 'hi'], // optional: language codes for audio input
93
+ patientDetails: { // optional
94
+ name: 'John Doe',
95
+ age: '45',
96
+ gender: 'male',
97
+ },
98
+ additionalData: {}, // optional: any extra data for the session
99
+ txnId: 'your-transaction-id', // optional: external transaction ID
47
100
  });
48
101
 
49
102
  if (!result.success) {
50
- console.error(result.error);
103
+ console.error('Failed to start:', result.error.message);
104
+ return;
51
105
  }
52
106
 
53
- // ... user speaks ...
107
+ const sessionId = result.data.session_id;
108
+ ```
109
+
110
+ #### Pause / Resume
111
+
112
+ ```ts
113
+ client.pauseRecording(); // pauses VAD — mic stays open, no new chunks created
114
+ client.resumeRecording(); // resumes VAD processing
115
+ ```
116
+
117
+ ### Step 5: End Recording
118
+
119
+ Stops the microphone, flushes the last audio chunk, waits for all uploads to complete, and tells the server the session has ended (triggers server-side processing).
54
120
 
55
- // Stop recording — flushes remaining audio, waits for uploads, ends session
121
+ ```ts
56
122
  const stopResult = await client.endRecording();
57
123
 
58
124
  if (stopResult.success) {
59
125
  console.log(`${stopResult.data.totalFiles} files uploaded`);
60
126
  console.log(`${stopResult.data.failedUploads.length} failed`);
61
127
  }
128
+ ```
62
129
 
63
- // Poll for results
64
- const status = await client.getSessionStatus(result.data.session_id, {
130
+ ### Step 6: Poll for Results
131
+
132
+ After ending the recording, poll the server until processing is complete.
133
+
134
+ ```ts
135
+ const abortController = new AbortController();
136
+
137
+ const status = await client.getSessionStatus(sessionId, {
65
138
  poll: {
66
139
  maxAttempts: 60,
67
140
  intervalMs: 2000,
68
- onProgress: (s) => console.log(`Status: ${s.status}`),
141
+ signal: abortController.signal, // optional: abort polling early
142
+ onProgress: (s) => {
143
+ console.log(`Status: ${s.status}`);
144
+ if (s.templates) {
145
+ console.log('Templates:', s.templates);
146
+ }
147
+ },
69
148
  },
70
149
  });
150
+
151
+ if (status.success) {
152
+ console.log('Final status:', status.data.status);
153
+ console.log('Templates:', status.data.templates);
154
+ console.log('Transcript:', status.data.transcript);
155
+ }
156
+ ```
157
+
158
+ ### Step 7: Clean Up
159
+
160
+ ```ts
161
+ await client.reset(); // stops recording if active, clears all state and caches
162
+ ```
163
+
164
+ ### Flow Diagram
165
+
166
+ ```
167
+ new ScribeClient({ baseUrl, accessToken })
168
+
169
+
170
+ init() ────────── Fetches discovery (auto-called by startRecording)
171
+
172
+
173
+ registerCallback() ── Set up event handlers before recording
174
+
175
+
176
+ startRecording() ──── Creates session → starts mic → begins upload
177
+
178
+ pause / resume ──── Optional during recording
179
+
180
+
181
+ endRecording() ────── Stops mic → flushes audio → ends session → triggers processing
182
+
183
+
184
+ getSessionStatus() ── Poll until completed/failed
185
+
186
+
187
+ Read results ──────── templates, transcript, errors
188
+ ```
189
+
190
+ ---
191
+
192
+ ## Important Notes
193
+
194
+ - **`baseUrl` is the root for all API calls.** Session creation, audio upload, status polling — everything uses this URL. Make sure it's correct and accessible.
195
+ - **`accessToken` must be a valid Bearer token.** All API requests include `Authorization: Bearer <token>`. If it expires, register `onTokenRequired` to auto-refresh.
196
+ - **Register callbacks before `startRecording()`.** Events fire immediately once recording starts — if callbacks aren't registered, you'll miss upload progress and errors.
197
+ - **`endRecording()` triggers server processing.** Once you call it, the server begins processing the uploaded audio. Use `cancelSession()` instead if you don't want processing to happen.
198
+ - **`cancelSession()` does NOT trigger processing.** It stops the recorder locally, cleans up state, and tells the server the session is cancelled. No `endSession` call is made to the backend.
199
+ - **All async methods return `SDKResult<T>`, never throw.** Always check `result.success` before accessing `result.data`. Errors are in `result.error`.
200
+ - **The SDK validates inputs against the discovery document.** If the server doesn't support an upload type, language, or model you requested, you'll get a `ValidationError` before the API call is made.
201
+ - **SharedWorker is optional.** If you provide `workerScriptUrl`, the SDK offloads MP3 compression and upload to a SharedWorker. If the worker fails to load, it silently falls back to main-thread processing.
202
+ - **Microphone permission is requested on `startRecording()`.** The browser will prompt the user for mic access. If denied, you'll get an error via `onError` callback.
203
+ - **`reset()` is a full teardown.** It destroys the transport, clears discovery cache, removes all callbacks, and sets the client back to uninitialized state. You'll need to call `init()` (or `startRecording()`) again after reset.
204
+ - **Polling supports `AbortSignal`.** Pass `signal` in poll options to cancel polling early (e.g. when the user navigates away).
205
+
206
+ ---
207
+
208
+ ## Other Operations
209
+
210
+ ### Cancel a Session
211
+
212
+ Stops the recorder locally **without** triggering server-side processing, then tells the server the session is cancelled.
213
+
214
+ ```ts
215
+ await client.cancelSession(); // cancels the current active session
216
+ await client.cancelSession('specific-session-id'); // or by ID
217
+ ```
218
+
219
+ ### Update a Session (Patch)
220
+
221
+ Update session properties after creation.
222
+
223
+ ```ts
224
+ await client.updateSession({
225
+ patient_details: { name: 'Jane Doe', age: '30', gender: 'female' },
226
+ additional_data: { notes: 'Follow-up visit' },
227
+ templates: ['soap', 'prescription'],
228
+ });
229
+ ```
230
+
231
+ ### Process a Specific Template
232
+
233
+ Trigger server-side processing for a specific template.
234
+
235
+ ```ts
236
+ await client.processTemplate('soap');
237
+ await client.processTemplate('soap', 'session-id'); // with explicit session ID
238
+ ```
239
+
240
+ ### Two-Step Flow (Create Session + Record Separately)
241
+
242
+ ```ts
243
+ // Step 1: Create session
244
+ const session = await client.createSession({
245
+ templates: ['soap'],
246
+ upload_type: 'chunked',
247
+ communication_protocol: 'http',
248
+ session_mode: 'consultation',
249
+ });
250
+
251
+ if (!session.success) return;
252
+
253
+ // Step 2: Start recording with the existing session
254
+ await client.startRecordingWithSession(session.data, {
255
+ uploadType: 'chunked',
256
+ });
257
+ ```
258
+
259
+ ### Get Status for a Specific Template
260
+
261
+ ```ts
262
+ const status = await client.getSessionStatus(sessionId, {
263
+ templateId: 'soap',
264
+ });
265
+ ```
266
+
267
+ ### Retry Failed Uploads
268
+
269
+ ```ts
270
+ if (client.hasFailedUploads()) {
271
+ const retryResult = await client.retryFailedUploads();
272
+ console.log(`Retried: ${retryResult.data.retried}, Succeeded: ${retryResult.data.succeeded}`);
273
+ }
274
+ ```
275
+
276
+ ### Update Auth Token
277
+
278
+ ```ts
279
+ client.setAccessToken('new-bearer-token');
71
280
  ```
72
281
 
282
+ ---
283
+
73
284
  ## Configuration
74
285
 
75
286
  ```ts
@@ -100,6 +311,24 @@ interface ScribeSDKConfig {
100
311
  }
101
312
  ```
102
313
 
314
+ ## Recording Options
315
+
316
+ ```ts
317
+ interface RecordingOptions {
318
+ templates: string[]; // Template IDs for extraction (required)
319
+ model?: string; // Model ID from discovery
320
+ languageHint?: string[]; // Language codes for audio input
321
+ transcriptLanguage?: string; // Language code for transcript output
322
+ uploadType?: string; // 'chunked' | 'single' | 'stream' (default: 'chunked')
323
+ communicationProtocol?: string; // 'http' | 'websocket' (default: 'http')
324
+ additionalData?: Record<string, any>; // Extra data for the session
325
+ deviceId?: string; // Specific microphone device ID
326
+ sessionMode?: string; // 'consultation' | 'dictation'
327
+ patientDetails?: PatientDetails; // Patient info
328
+ txnId?: string; // External transaction ID
329
+ }
330
+ ```
331
+
103
332
  ## API Reference
104
333
 
105
334
  ### Lifecycle
@@ -123,42 +352,16 @@ interface ScribeSDKConfig {
123
352
  | `retryFailedUploads()` | `SDKResult<RetryUploadResult>` | Retry uploads that failed during the last recording. |
124
353
  | `hasFailedUploads()` | `boolean` | Whether there are retryable failed uploads. |
125
354
 
126
- #### Recording Options
127
-
128
- ```ts
129
- interface RecordingOptions {
130
- templates: string[]; // Template IDs for extraction (required)
131
- model?: string; // Model ID from discovery
132
- languageHint?: string[]; // Language codes for audio input
133
- transcriptLanguage?: string[];// Language codes for transcript output
134
- uploadType?: string; // 'chunked' | 'single' (default: 'chunked')
135
- communicationProtocol?: string;// 'http' | 'websocket' (default: 'http')
136
- additionalData?: Record<string, any>;
137
- deviceId?: string; // Specific microphone device ID
138
- }
139
- ```
140
-
141
355
  ### Session
142
356
 
143
357
  | Method | Returns | Description |
144
358
  |---|---|---|
145
359
  | `createSession(request)` | `SDKResult<CreateSessionResponse>` | Create a session without starting a recording. |
146
- | `getSessionStatus(sessionId?, options?)` | `SDKResult<GetSessionStatusResponse>` | Get status. Pass `{ poll: PollOptions }` to poll until completion. |
360
+ | `getSessionStatus(sessionId?, options?)` | `SDKResult<GetSessionStatusResponse>` | Get status. Supports `poll` and `templateId` options. |
147
361
  | `getCurrentSession()` | `CreateSessionResponse \| null` | Get the active session if any. |
148
-
149
- #### Polling
150
-
151
- Pass `poll` options to `getSessionStatus` to poll until the session reaches a terminal state:
152
-
153
- ```ts
154
- const result = await client.getSessionStatus(sessionId, {
155
- poll: {
156
- maxAttempts: 60,
157
- intervalMs: 2000,
158
- onProgress: (status) => console.log(status.status),
159
- },
160
- });
161
- ```
362
+ | `updateSession(request, sessionId?)` | `SDKResult<PatchSessionResponse>` | Patch session (patient details, status, etc.). |
363
+ | `processTemplate(templateId, sessionId?)` | `SDKResult<ProcessTemplateResponse>` | Trigger processing for a specific template. |
364
+ | `cancelSession(sessionId?)` | `SDKResult<PatchSessionResponse>` | Cancel session (stops recorder, no server processing). |
162
365
 
163
366
  ### Discovery
164
367
 
@@ -225,19 +428,6 @@ console.log(result.data.session_id);
225
428
  | `WorkerError` | — | SharedWorker failure |
226
429
  | `UploadError` | — | Audio upload failure |
227
430
 
228
- ### Auto Token Refresh
229
-
230
- When a 401 is received, the SDK dispatches `onTokenRequired`. Supply a new token to retry the request:
231
-
232
- ```ts
233
- client.registerCallback('onTokenRequired', async (event) => {
234
- const newToken = await refreshMyAuthToken();
235
- event.resolve(newToken);
236
- });
237
- ```
238
-
239
- Concurrent 401s are deduplicated — only one callback fires regardless of how many requests failed simultaneously.
240
-
241
431
  ## SharedWorker Support
242
432
 
243
433
  The SDK offloads MP3 compression and upload to a SharedWorker for better main-thread performance. The worker is bundled separately as `dist/worker.bundle.js`.
@@ -299,26 +489,6 @@ const client = new ScribeClient({
299
489
 
300
490
  IPC mode always uses main-thread compression (SharedWorker can't access the IPC bridge).
301
491
 
302
- ## Two-Step Flow
303
-
304
- For apps that need to create the session separately from recording:
305
-
306
- ```ts
307
- // Step 1: Create session
308
- const session = await client.createSession({
309
- templates: ['soap'],
310
- upload_type: 'chunked',
311
- communication_protocol: 'http',
312
- });
313
-
314
- if (!session.success) return;
315
-
316
- // Step 2: Start recording with the existing session
317
- await client.startRecordingWithSession(session.data, {
318
- uploadType: 'chunked',
319
- });
320
- ```
321
-
322
492
  ## Building from Source
323
493
 
324
494
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "med-scribe-alliance-ts-sdk",
3
- "version": "2.0.8",
3
+ "version": "2.0.9",
4
4
  "description": "TypeScript SDK for the MedScribe Alliance Protocol",
5
5
  "main": "dist/index.mjs",
6
6
  "module": "dist/index.mjs",